refactor: use snake case instead of camel case in types (#1894)

This commit is contained in:
Marcelo Trylesinski 2026-01-16 15:51:27 +01:00 • committed by GitHub
commit e94b386a13
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 1063 additions and 1057 deletions

View file

@ -15,8 +15,8 @@ async def test_task_context_properties() -> None:
task = await store.create_task(metadata=TaskMetadata(ttl=60000))
ctx = TaskContext(task, store)
assert ctx.task_id == task.taskId
assert ctx.task.taskId == task.taskId
assert ctx.task_id == task.task_id
assert ctx.task.task_id == task.task_id
assert ctx.task.status == "working"
assert ctx.is_cancelled is False
@ -33,9 +33,9 @@ async def test_task_context_update_status() -> None:
await ctx.update_status("Processing step 1...")
# Check status message was updated
updated = await store.get_task(task.taskId)
updated = await store.get_task(task.task_id)
assert updated is not None
assert updated.statusMessage == "Processing step 1..."
assert updated.status_message == "Processing step 1..."
store.cleanup()
@ -51,12 +51,12 @@ async def test_task_context_complete() -> None:
await ctx.complete(result)
# Check task status
updated = await store.get_task(task.taskId)
updated = await store.get_task(task.task_id)
assert updated is not None
assert updated.status == "completed"
# Check result is stored
stored_result = await store.get_result(task.taskId)
stored_result = await store.get_result(task.task_id)
assert stored_result is not None
store.cleanup()
@ -72,10 +72,10 @@ async def test_task_context_fail() -> None:
await ctx.fail("Something went wrong!")
# Check task status
updated = await store.get_task(task.taskId)
updated = await store.get_task(task.task_id)
assert updated is not None
assert updated.status == "failed"
assert updated.statusMessage == "Something went wrong!"
assert updated.status_message == "Something went wrong!"
store.cleanup()
@ -101,13 +101,13 @@ def test_create_task_state_generates_id() -> None:
task1 = create_task_state(TaskMetadata(ttl=60000))
task2 = create_task_state(TaskMetadata(ttl=60000))
assert task1.taskId != task2.taskId
assert task1.task_id != task2.task_id
def test_create_task_state_uses_provided_id() -> None:
"""create_task_state uses the provided task ID."""
task = create_task_state(TaskMetadata(ttl=60000), task_id="my-task-123")
assert task.taskId == "my-task-123"
assert task.task_id == "my-task-123"
def test_create_task_state_null_ttl() -> None:
@ -119,7 +119,7 @@ def test_create_task_state_null_ttl() -> None:
def test_create_task_state_has_created_at() -> None:
"""create_task_state sets createdAt timestamp."""
task = create_task_state(TaskMetadata(ttl=60000))
assert task.createdAt is not None
assert task.created_at is not None
@pytest.mark.anyio
@ -148,7 +148,7 @@ async def test_task_execution_auto_fails_on_exception() -> None:
failed_task = await store.get_task("exec-fail-1")
assert failed_task is not None
assert failed_task.status == "failed"
assert "Oops!" in (failed_task.statusMessage or "")
assert "Oops!" in (failed_task.status_message or "")
store.cleanup()

View file

@ -81,11 +81,11 @@ async def test_task_lifecycle_with_task_execution() -> None:
Tool(
name="process_data",
description="Process data asynchronously",
inputSchema={
input_schema={
"type": "object",
"properties": {"input": {"type": "string"}},
},
execution=ToolExecution(taskSupport=TASK_REQUIRED),
execution=ToolExecution(task_support=TASK_REQUIRED),
)
]
@ -101,11 +101,11 @@ async def test_task_lifecycle_with_task_execution() -> None:
# 2. Create event to signal completion (for testing)
done_event = Event()
app.task_done_events[task.taskId] = done_event
app.task_done_events[task.task_id] = done_event
# 3. Define work function using task_execution for safety
async def do_work():
async with task_execution(task.taskId, app.store) as task_ctx:
async with task_execution(task.task_id, app.store) as task_ctx:
await task_ctx.update_status("Processing input...")
# Simulate work
input_value = arguments.get("input", "")
@ -126,16 +126,16 @@ async def test_task_lifecycle_with_task_execution() -> None:
@server.experimental.get_task()
async def handle_get_task(request: GetTaskRequest) -> GetTaskResult:
app = server.request_context.lifespan_context
task = await app.store.get_task(request.params.taskId)
assert task is not None, f"Test setup error: task {request.params.taskId} should exist"
task = await app.store.get_task(request.params.task_id)
assert task is not None, f"Test setup error: task {request.params.task_id} should exist"
return GetTaskResult(
taskId=task.taskId,
task_id=task.task_id,
status=task.status,
statusMessage=task.statusMessage,
createdAt=task.createdAt,
lastUpdatedAt=task.lastUpdatedAt,
status_message=task.status_message,
created_at=task.created_at,
last_updated_at=task.last_updated_at,
ttl=task.ttl,
pollInterval=task.pollInterval,
poll_interval=task.poll_interval,
)
@server.experimental.get_task_result()
@ -143,8 +143,8 @@ async def test_task_lifecycle_with_task_execution() -> None:
request: GetTaskPayloadRequest,
) -> GetTaskPayloadResult:
app = server.request_context.lifespan_context
result = await app.store.get_result(request.params.taskId)
assert result is not None, f"Test setup error: result for {request.params.taskId} should exist"
result = await app.store.get_result(request.params.task_id)
assert result is not None, f"Test setup error: result for {request.params.task_id} should exist"
assert isinstance(result, CallToolResult)
# Return as GetTaskPayloadResult (which accepts extra fields)
return GetTaskPayloadResult(**result.model_dump())
@ -205,22 +205,22 @@ async def test_task_lifecycle_with_task_execution() -> None:
assert isinstance(create_result, CreateTaskResult)
assert create_result.task.status == "working"
task_id = create_result.task.taskId
task_id = create_result.task.task_id
# === Step 2: Wait for task to complete ===
await app_context.task_done_events[task_id].wait()
task_status = await client_session.send_request(
ClientRequest(GetTaskRequest(params=GetTaskRequestParams(taskId=task_id))),
ClientRequest(GetTaskRequest(params=GetTaskRequestParams(task_id=task_id))),
GetTaskResult,
)
assert task_status.taskId == task_id
assert task_status.task_id == task_id
assert task_status.status == "completed"
# === Step 3: Retrieve the actual result ===
task_result = await client_session.send_request(
ClientRequest(GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(taskId=task_id))),
ClientRequest(GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(task_id=task_id))),
CallToolResult,
)
@ -245,7 +245,7 @@ async def test_task_auto_fails_on_exception() -> None:
Tool(
name="failing_task",
description="A task that fails",
inputSchema={"type": "object", "properties": {}},
input_schema={"type": "object", "properties": {}},
)
]
@ -260,10 +260,10 @@ async def test_task_auto_fails_on_exception() -> None:
# Create event to signal completion (for testing)
done_event = Event()
app.task_done_events[task.taskId] = done_event
app.task_done_events[task.task_id] = done_event
async def do_failing_work():
async with task_execution(task.taskId, app.store) as task_ctx:
async with task_execution(task.task_id, app.store) as task_ctx:
await task_ctx.update_status("About to fail...")
raise RuntimeError("Something went wrong!")
# Note: complete() is never called, but task_execution
@ -279,16 +279,16 @@ async def test_task_auto_fails_on_exception() -> None:
@server.experimental.get_task()
async def handle_get_task(request: GetTaskRequest) -> GetTaskResult:
app = server.request_context.lifespan_context
task = await app.store.get_task(request.params.taskId)
assert task is not None, f"Test setup error: task {request.params.taskId} should exist"
task = await app.store.get_task(request.params.task_id)
assert task is not None, f"Test setup error: task {request.params.task_id} should exist"
return GetTaskResult(
taskId=task.taskId,
task_id=task.task_id,
status=task.status,
statusMessage=task.statusMessage,
createdAt=task.createdAt,
lastUpdatedAt=task.lastUpdatedAt,
status_message=task.status_message,
created_at=task.created_at,
last_updated_at=task.last_updated_at,
ttl=task.ttl,
pollInterval=task.pollInterval,
poll_interval=task.poll_interval,
)
# Set up streams
@ -340,18 +340,18 @@ async def test_task_auto_fails_on_exception() -> None:
CreateTaskResult,
)
task_id = create_result.task.taskId
task_id = create_result.task.task_id
# Wait for task to complete (even though it fails)
await app_context.task_done_events[task_id].wait()
# Check that task was auto-failed
task_status = await client_session.send_request(
ClientRequest(GetTaskRequest(params=GetTaskRequestParams(taskId=task_id))),
ClientRequest(GetTaskRequest(params=GetTaskRequestParams(task_id=task_id))),
GetTaskResult,
)
assert task_status.status == "failed"
assert task_status.statusMessage == "Something went wrong!"
assert task_status.status_message == "Something went wrong!"
tg.cancel_scope.cancel()

View file

@ -69,8 +69,8 @@ async def test_run_task_basic_flow() -> None:
Tool(
name="simple_task",
description="A simple task",
inputSchema={"type": "object", "properties": {"input": {"type": "string"}}},
execution=ToolExecution(taskSupport=TASK_REQUIRED),
input_schema={"type": "object", "properties": {"input": {"type": "string"}}},
execution=ToolExecution(task_support=TASK_REQUIRED),
)
]
@ -119,7 +119,7 @@ async def test_run_task_basic_flow() -> None:
)
# Should get CreateTaskResult
task_id = result.task.taskId
task_id = result.task.task_id
assert result.task.status == "working"
# Wait for work to complete
@ -157,8 +157,8 @@ async def test_run_task_auto_fails_on_exception() -> None:
Tool(
name="failing_task",
description="A task that fails",
inputSchema={"type": "object"},
execution=ToolExecution(taskSupport=TASK_REQUIRED),
input_schema={"type": "object"},
execution=ToolExecution(task_support=TASK_REQUIRED),
)
]
@ -188,7 +188,7 @@ async def test_run_task_auto_fails_on_exception() -> None:
await client_session.initialize()
result = await client_session.experimental.call_tool_as_task("failing_task", {})
task_id = result.task.taskId
task_id = result.task.task_id
# Wait for work to fail
with anyio.fail_after(5):
@ -201,7 +201,7 @@ async def test_run_task_auto_fails_on_exception() -> None:
if task_status.status == "failed": # pragma: no branch
break
assert "Something went wrong" in (task_status.statusMessage or "")
assert "Something went wrong" in (task_status.status_message or "")
async with anyio.create_task_group() as tg:
tg.start_soon(run_server)
@ -363,8 +363,8 @@ async def test_run_task_with_model_immediate_response() -> None:
Tool(
name="task_with_immediate",
description="A task with immediate response",
inputSchema={"type": "object"},
execution=ToolExecution(taskSupport=TASK_REQUIRED),
input_schema={"type": "object"},
execution=ToolExecution(task_support=TASK_REQUIRED),
)
]
@ -422,8 +422,8 @@ async def test_run_task_doesnt_complete_if_already_terminal() -> None:
Tool(
name="manual_complete_task",
description="A task that manually completes",
inputSchema={"type": "object"},
execution=ToolExecution(taskSupport=TASK_REQUIRED),
input_schema={"type": "object"},
execution=ToolExecution(task_support=TASK_REQUIRED),
)
]
@ -457,7 +457,7 @@ async def test_run_task_doesnt_complete_if_already_terminal() -> None:
await client_session.initialize()
result = await client_session.experimental.call_tool_as_task("manual_complete_task", {})
task_id = result.task.taskId
task_id = result.task.task_id
with anyio.fail_after(5):
await work_completed.wait()
@ -488,8 +488,8 @@ async def test_run_task_doesnt_fail_if_already_terminal() -> None:
Tool(
name="manual_cancel_task",
description="A task that manually cancels then raises",
inputSchema={"type": "object"},
execution=ToolExecution(taskSupport=TASK_REQUIRED),
input_schema={"type": "object"},
execution=ToolExecution(task_support=TASK_REQUIRED),
)
]
@ -522,7 +522,7 @@ async def test_run_task_doesnt_fail_if_already_terminal() -> None:
await client_session.initialize()
result = await client_session.experimental.call_tool_as_task("manual_cancel_task", {})
task_id = result.task.taskId
task_id = result.task.task_id
with anyio.fail_after(5):
await work_completed.wait()
@ -535,7 +535,7 @@ async def test_run_task_doesnt_fail_if_already_terminal() -> None:
break
# Task should still be failed (from manual fail, not auto-fail from exception)
assert status.statusMessage == "Manually failed" # Not "This error should not change status"
assert status.status_message == "Manually failed" # Not "This error should not change status"
async with anyio.create_task_group() as tg:
tg.start_soon(run_server)

View file

@ -64,20 +64,20 @@ async def test_list_tasks_handler() -> None:
now = datetime.now(timezone.utc)
test_tasks = [
Task(
taskId="task-1",
task_id="task-1",
status="working",
createdAt=now,
lastUpdatedAt=now,
created_at=now,
last_updated_at=now,
ttl=60000,
pollInterval=1000,
poll_interval=1000,
),
Task(
taskId="task-2",
task_id="task-2",
status="completed",
createdAt=now,
lastUpdatedAt=now,
created_at=now,
last_updated_at=now,
ttl=60000,
pollInterval=1000,
poll_interval=1000,
),
]
@ -92,8 +92,8 @@ async def test_list_tasks_handler() -> None:
assert isinstance(result, ServerResult)
assert isinstance(result.root, ListTasksResult)
assert len(result.root.tasks) == 2
assert result.root.tasks[0].taskId == "task-1"
assert result.root.tasks[1].taskId == "task-2"
assert result.root.tasks[0].task_id == "task-1"
assert result.root.tasks[1].task_id == "task-2"
@pytest.mark.anyio
@ -105,24 +105,24 @@ async def test_get_task_handler() -> None:
async def handle_get_task(request: GetTaskRequest) -> GetTaskResult:
now = datetime.now(timezone.utc)
return GetTaskResult(
taskId=request.params.taskId,
task_id=request.params.task_id,
status="working",
createdAt=now,
lastUpdatedAt=now,
created_at=now,
last_updated_at=now,
ttl=60000,
pollInterval=1000,
poll_interval=1000,
)
handler = server.request_handlers[GetTaskRequest]
request = GetTaskRequest(
method="tasks/get",
params=GetTaskRequestParams(taskId="test-task-123"),
params=GetTaskRequestParams(task_id="test-task-123"),
)
result = await handler(request)
assert isinstance(result, ServerResult)
assert isinstance(result.root, GetTaskResult)
assert result.root.taskId == "test-task-123"
assert result.root.task_id == "test-task-123"
assert result.root.status == "working"
@ -138,7 +138,7 @@ async def test_get_task_result_handler() -> None:
handler = server.request_handlers[GetTaskPayloadRequest]
request = GetTaskPayloadRequest(
method="tasks/result",
params=GetTaskPayloadRequestParams(taskId="test-task-123"),
params=GetTaskPayloadRequestParams(task_id="test-task-123"),
)
result = await handler(request)
@ -155,23 +155,23 @@ async def test_cancel_task_handler() -> None:
async def handle_cancel_task(request: CancelTaskRequest) -> CancelTaskResult:
now = datetime.now(timezone.utc)
return CancelTaskResult(
taskId=request.params.taskId,
task_id=request.params.task_id,
status="cancelled",
createdAt=now,
lastUpdatedAt=now,
created_at=now,
last_updated_at=now,
ttl=60000,
)
handler = server.request_handlers[CancelTaskRequest]
request = CancelTaskRequest(
method="tasks/cancel",
params=CancelTaskRequestParams(taskId="test-task-123"),
params=CancelTaskRequestParams(task_id="test-task-123"),
)
result = await handler(request)
assert isinstance(result, ServerResult)
assert isinstance(result.root, CancelTaskResult)
assert result.root.taskId == "test-task-123"
assert result.root.task_id == "test-task-123"
assert result.root.status == "cancelled"
@ -232,20 +232,20 @@ async def test_tool_with_task_execution_metadata() -> None:
Tool(
name="quick_tool",
description="Fast tool",
inputSchema={"type": "object", "properties": {}},
execution=ToolExecution(taskSupport=TASK_FORBIDDEN),
input_schema={"type": "object", "properties": {}},
execution=ToolExecution(task_support=TASK_FORBIDDEN),
),
Tool(
name="long_tool",
description="Long running tool",
inputSchema={"type": "object", "properties": {}},
execution=ToolExecution(taskSupport=TASK_REQUIRED),
input_schema={"type": "object", "properties": {}},
execution=ToolExecution(task_support=TASK_REQUIRED),
),
Tool(
name="flexible_tool",
description="Can be either",
inputSchema={"type": "object", "properties": {}},
execution=ToolExecution(taskSupport=TASK_OPTIONAL),
input_schema={"type": "object", "properties": {}},
execution=ToolExecution(task_support=TASK_OPTIONAL),
),
]
@ -258,11 +258,11 @@ async def test_tool_with_task_execution_metadata() -> None:
tools = result.root.tools
assert tools[0].execution is not None
assert tools[0].execution.taskSupport == TASK_FORBIDDEN
assert tools[0].execution.task_support == TASK_FORBIDDEN
assert tools[1].execution is not None
assert tools[1].execution.taskSupport == TASK_REQUIRED
assert tools[1].execution.task_support == TASK_REQUIRED
assert tools[2].execution is not None
assert tools[2].execution.taskSupport == TASK_OPTIONAL
assert tools[2].execution.task_support == TASK_OPTIONAL
@pytest.mark.anyio
@ -277,8 +277,8 @@ async def test_task_metadata_in_call_tool_request() -> None:
Tool(
name="long_task",
description="A long running task",
inputSchema={"type": "object", "properties": {}},
execution=ToolExecution(taskSupport="optional"),
input_schema={"type": "object", "properties": {}},
execution=ToolExecution(task_support="optional"),
)
]
@ -361,7 +361,7 @@ async def test_task_metadata_is_task_property() -> None:
Tool(
name="test_tool",
description="Test tool",
inputSchema={"type": "object", "properties": {}},
input_schema={"type": "object", "properties": {}},
)
]
@ -513,33 +513,35 @@ async def test_default_task_handlers_via_enable_tasks() -> None:
ListTasksResult,
)
assert len(list_result.tasks) == 1
assert list_result.tasks[0].taskId == task.taskId
assert list_result.tasks[0].task_id == task.task_id
# Test get_task (default handler - found)
get_result = await client_session.send_request(
ClientRequest(GetTaskRequest(params=GetTaskRequestParams(taskId=task.taskId))),
ClientRequest(GetTaskRequest(params=GetTaskRequestParams(task_id=task.task_id))),
GetTaskResult,
)
assert get_result.taskId == task.taskId
assert get_result.task_id == task.task_id
assert get_result.status == "working"
# Test get_task (default handler - not found path)
with pytest.raises(McpError, match="not found"):
await client_session.send_request(
ClientRequest(GetTaskRequest(params=GetTaskRequestParams(taskId="nonexistent-task"))),
ClientRequest(GetTaskRequest(params=GetTaskRequestParams(task_id="nonexistent-task"))),
GetTaskResult,
)
# Create a completed task to test get_task_result
completed_task = await store.create_task(TaskMetadata(ttl=60000))
await store.store_result(
completed_task.taskId, CallToolResult(content=[TextContent(type="text", text="Test result")])
completed_task.task_id, CallToolResult(content=[TextContent(type="text", text="Test result")])
)
await store.update_task(completed_task.taskId, status="completed")
await store.update_task(completed_task.task_id, status="completed")
# Test get_task_result (default handler)
payload_result = await client_session.send_request(
ClientRequest(GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(taskId=completed_task.taskId))),
ClientRequest(
GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(task_id=completed_task.task_id))
),
GetTaskPayloadResult,
)
# The result should have the related-task metadata
@ -548,10 +550,10 @@ async def test_default_task_handlers_via_enable_tasks() -> None:
# Test cancel_task (default handler)
cancel_result = await client_session.send_request(
ClientRequest(CancelTaskRequest(params=CancelTaskRequestParams(taskId=task.taskId))),
ClientRequest(CancelTaskRequest(params=CancelTaskRequestParams(task_id=task.task_id))),
CancelTaskResult,
)
assert cancel_result.taskId == task.taskId
assert cancel_result.task_id == task.task_id
assert cancel_result.status == "cancelled"
tg.cancel_scope.cancel()
@ -576,7 +578,7 @@ async def test_build_elicit_form_request() -> None:
# Test without task_id
request = server_session._build_elicit_form_request(
message="Test message",
requestedSchema={"type": "object", "properties": {"answer": {"type": "string"}}},
requested_schema={"type": "object", "properties": {"answer": {"type": "string"}}},
)
assert request.method == "elicitation/create"
assert request.params is not None
@ -585,7 +587,7 @@ async def test_build_elicit_form_request() -> None:
# Test with related_task_id (adds related-task metadata)
request_with_task = server_session._build_elicit_form_request(
message="Task message",
requestedSchema={"type": "object"},
requested_schema={"type": "object"},
related_task_id="test-task-123",
)
assert request_with_task.method == "elicitation/create"

View file

@ -45,7 +45,7 @@ async def test_server_task_context_properties() -> None:
)
assert ctx.task_id == "test-123"
assert ctx.task.taskId == "test-123"
assert ctx.task.task_id == "test-123"
assert ctx.is_cancelled is False
store.cleanup()
@ -181,7 +181,7 @@ async def test_elicit_raises_when_client_lacks_capability() -> None:
)
with pytest.raises(McpError) as exc_info:
await ctx.elicit(message="Test?", requestedSchema={"type": "object"})
await ctx.elicit(message="Test?", requested_schema={"type": "object"})
assert "elicitation capability" in exc_info.value.error.message
mock_session.check_client_capability.assert_called_once()
@ -232,7 +232,7 @@ async def test_elicit_raises_without_handler() -> None:
)
with pytest.raises(RuntimeError, match="handler is required"):
await ctx.elicit(message="Test?", requestedSchema={"type": "object"})
await ctx.elicit(message="Test?", requested_schema={"type": "object"})
store.cleanup()
@ -320,22 +320,22 @@ async def test_elicit_queues_request_and_waits_for_response() -> None:
nonlocal elicit_result
elicit_result = await ctx.elicit(
message="Test?",
requestedSchema={"type": "object"},
requested_schema={"type": "object"},
)
async with anyio.create_task_group() as tg:
tg.start_soon(run_elicit)
# Wait for request to be queued
await queue.wait_for_message(task.taskId)
await queue.wait_for_message(task.task_id)
# Verify task is in input_required status
updated_task = await store.get_task(task.taskId)
updated_task = await store.get_task(task.task_id)
assert updated_task is not None
assert updated_task.status == "input_required"
# Dequeue and simulate response
msg = await queue.dequeue(task.taskId)
msg = await queue.dequeue(task.task_id)
assert msg is not None
assert msg.resolver is not None
@ -348,7 +348,7 @@ async def test_elicit_queues_request_and_waits_for_response() -> None:
assert elicit_result.content == {"name": "Alice"}
# Verify task is back to working
final_task = await store.get_task(task.taskId)
final_task = await store.get_task(task.task_id)
assert final_task is not None
assert final_task.status == "working"
@ -396,15 +396,15 @@ async def test_elicit_url_queues_request_and_waits_for_response() -> None:
tg.start_soon(run_elicit_url)
# Wait for request to be queued
await queue.wait_for_message(task.taskId)
await queue.wait_for_message(task.task_id)
# Verify task is in input_required status
updated_task = await store.get_task(task.taskId)
updated_task = await store.get_task(task.task_id)
assert updated_task is not None
assert updated_task.status == "input_required"
# Dequeue and simulate response
msg = await queue.dequeue(task.taskId)
msg = await queue.dequeue(task.task_id)
assert msg is not None
assert msg.resolver is not None
@ -416,7 +416,7 @@ async def test_elicit_url_queues_request_and_waits_for_response() -> None:
assert elicit_result.action == "accept"
# Verify task is back to working
final_task = await store.get_task(task.taskId)
final_task = await store.get_task(task.task_id)
assert final_task is not None
assert final_task.status == "working"
@ -463,15 +463,15 @@ async def test_create_message_queues_request_and_waits_for_response() -> None:
tg.start_soon(run_sampling)
# Wait for request to be queued
await queue.wait_for_message(task.taskId)
await queue.wait_for_message(task.task_id)
# Verify task is in input_required status
updated_task = await store.get_task(task.taskId)
updated_task = await store.get_task(task.task_id)
assert updated_task is not None
assert updated_task.status == "input_required"
# Dequeue and simulate response
msg = await queue.dequeue(task.taskId)
msg = await queue.dequeue(task.task_id)
assert msg is not None
assert msg.resolver is not None
@ -491,7 +491,7 @@ async def test_create_message_queues_request_and_waits_for_response() -> None:
assert sampling_result.model == "test-model"
# Verify task is back to working
final_task = await store.get_task(task.taskId)
final_task = await store.get_task(task.task_id)
assert final_task is not None
assert final_task.status == "working"
@ -534,7 +534,7 @@ async def test_elicit_restores_status_on_cancellation() -> None:
try:
await ctx.elicit(
message="Test?",
requestedSchema={"type": "object"},
requested_schema={"type": "object"},
)
except anyio.get_cancelled_exc_class():
cancelled_error_raised = True
@ -543,15 +543,15 @@ async def test_elicit_restores_status_on_cancellation() -> None:
tg.start_soon(do_elicit)
# Wait for request to be queued
await queue.wait_for_message(task.taskId)
await queue.wait_for_message(task.task_id)
# Verify task is in input_required status
updated_task = await store.get_task(task.taskId)
updated_task = await store.get_task(task.task_id)
assert updated_task is not None
assert updated_task.status == "input_required"
# Get the queued message and set cancellation exception on its resolver
msg = await queue.dequeue(task.taskId)
msg = await queue.dequeue(task.task_id)
assert msg is not None
assert msg.resolver is not None
@ -559,7 +559,7 @@ async def test_elicit_restores_status_on_cancellation() -> None:
msg.resolver.set_exception(asyncio.CancelledError())
# Verify task is back to working after cancellation
final_task = await store.get_task(task.taskId)
final_task = await store.get_task(task.task_id)
assert final_task is not None
assert final_task.status == "working"
assert cancelled_error_raised
@ -612,15 +612,15 @@ async def test_create_message_restores_status_on_cancellation() -> None:
tg.start_soon(do_sampling)
# Wait for request to be queued
await queue.wait_for_message(task.taskId)
await queue.wait_for_message(task.task_id)
# Verify task is in input_required status
updated_task = await store.get_task(task.taskId)
updated_task = await store.get_task(task.task_id)
assert updated_task is not None
assert updated_task.status == "input_required"
# Get the queued message and set cancellation exception on its resolver
msg = await queue.dequeue(task.taskId)
msg = await queue.dequeue(task.task_id)
assert msg is not None
assert msg.resolver is not None
@ -628,7 +628,7 @@ async def test_create_message_restores_status_on_cancellation() -> None:
msg.resolver.set_exception(asyncio.CancelledError())
# Verify task is back to working after cancellation
final_task = await store.get_task(task.taskId)
final_task = await store.get_task(task.task_id)
assert final_task is not None
assert final_task.status == "working"
assert cancelled_error_raised
@ -646,7 +646,7 @@ async def test_elicit_as_task_raises_without_handler() -> None:
# Create mock session with proper client capabilities
mock_session = Mock()
mock_session.client_params = InitializeRequestParams(
protocolVersion="2025-01-01",
protocol_version="2025-01-01",
capabilities=ClientCapabilities(
tasks=ClientTasksCapability(
requests=ClientTasksRequestsCapability(
@ -654,7 +654,7 @@ async def test_elicit_as_task_raises_without_handler() -> None:
)
)
),
clientInfo=Implementation(name="test", version="1.0"),
client_info=Implementation(name="test", version="1.0"),
)
ctx = ServerTaskContext(
@ -666,7 +666,7 @@ async def test_elicit_as_task_raises_without_handler() -> None:
)
with pytest.raises(RuntimeError, match="handler is required for elicit_as_task"):
await ctx.elicit_as_task(message="Test?", requestedSchema={"type": "object"})
await ctx.elicit_as_task(message="Test?", requested_schema={"type": "object"})
store.cleanup()
@ -681,15 +681,15 @@ async def test_create_message_as_task_raises_without_handler() -> None:
# Create mock session with proper client capabilities
mock_session = Mock()
mock_session.client_params = InitializeRequestParams(
protocolVersion="2025-01-01",
protocol_version="2025-01-01",
capabilities=ClientCapabilities(
tasks=ClientTasksCapability(
requests=ClientTasksRequestsCapability(
sampling=TasksSamplingCapability(createMessage=TasksCreateMessageCapability())
sampling=TasksSamplingCapability(create_message=TasksCreateMessageCapability())
)
)
),
clientInfo=Implementation(name="test", version="1.0"),
client_info=Implementation(name="test", version="1.0"),
)
ctx = ServerTaskContext(

View file

@ -24,13 +24,13 @@ async def test_create_and_get(store: InMemoryTaskStore) -> None:
"""Test InMemoryTaskStore create and get operations."""
task = await store.create_task(metadata=TaskMetadata(ttl=60000))
assert task.taskId is not None
assert task.task_id is not None
assert task.status == "working"
assert task.ttl == 60000
retrieved = await store.get_task(task.taskId)
retrieved = await store.get_task(task.task_id)
assert retrieved is not None
assert retrieved.taskId == task.taskId
assert retrieved.task_id == task.task_id
assert retrieved.status == "working"
@ -42,12 +42,12 @@ async def test_create_with_custom_id(store: InMemoryTaskStore) -> None:
task_id="my-custom-id",
)
assert task.taskId == "my-custom-id"
assert task.task_id == "my-custom-id"
assert task.status == "working"
retrieved = await store.get_task("my-custom-id")
assert retrieved is not None
assert retrieved.taskId == "my-custom-id"
assert retrieved.task_id == "my-custom-id"
@pytest.mark.anyio
@ -71,15 +71,15 @@ async def test_update_status(store: InMemoryTaskStore) -> None:
"""Test InMemoryTaskStore status updates."""
task = await store.create_task(metadata=TaskMetadata(ttl=60000))
updated = await store.update_task(task.taskId, status="completed", status_message="All done!")
updated = await store.update_task(task.task_id, status="completed", status_message="All done!")
assert updated.status == "completed"
assert updated.statusMessage == "All done!"
assert updated.status_message == "All done!"
retrieved = await store.get_task(task.taskId)
retrieved = await store.get_task(task.task_id)
assert retrieved is not None
assert retrieved.status == "completed"
assert retrieved.statusMessage == "All done!"
assert retrieved.status_message == "All done!"
@pytest.mark.anyio
@ -96,10 +96,10 @@ async def test_store_and_get_result(store: InMemoryTaskStore) -> None:
# Store result
result = CallToolResult(content=[TextContent(type="text", text="Result data")])
await store.store_result(task.taskId, result)
await store.store_result(task.task_id, result)
# Retrieve result
retrieved_result = await store.get_result(task.taskId)
retrieved_result = await store.get_result(task.task_id)
assert retrieved_result == result
@ -114,7 +114,7 @@ async def test_get_result_nonexistent_returns_none(store: InMemoryTaskStore) ->
async def test_get_result_no_result_returns_none(store: InMemoryTaskStore) -> None:
"""Test that getting result when none stored returns None."""
task = await store.create_task(metadata=TaskMetadata(ttl=60000))
result = await store.get_result(task.taskId)
result = await store.get_result(task.task_id)
assert result is None
@ -172,14 +172,14 @@ async def test_delete_task(store: InMemoryTaskStore) -> None:
"""Test InMemoryTaskStore delete operation."""
task = await store.create_task(metadata=TaskMetadata(ttl=60000))
deleted = await store.delete_task(task.taskId)
deleted = await store.delete_task(task.task_id)
assert deleted is True
retrieved = await store.get_task(task.taskId)
retrieved = await store.get_task(task.task_id)
assert retrieved is None
# Delete non-existent
deleted = await store.delete_task(task.taskId)
deleted = await store.delete_task(task.task_id)
assert deleted is False
@ -210,7 +210,7 @@ async def test_create_task_with_null_ttl(store: InMemoryTaskStore) -> None:
assert task.ttl is None
# Task should persist (not expire)
retrieved = await store.get_task(task.taskId)
retrieved = await store.get_task(task.task_id)
assert retrieved is not None
@ -221,19 +221,19 @@ async def test_task_expiration_cleanup(store: InMemoryTaskStore) -> None:
task = await store.create_task(metadata=TaskMetadata(ttl=1)) # 1ms TTL
# Manually force the expiry to be in the past
stored = store._tasks.get(task.taskId)
stored = store._tasks.get(task.task_id)
assert stored is not None
stored.expires_at = datetime.now(timezone.utc) - timedelta(seconds=10)
# Task should still exist in internal dict but be expired
assert task.taskId in store._tasks
assert task.task_id in store._tasks
# Any access operation should clean up expired tasks
# list_tasks triggers cleanup
tasks, _ = await store.list_tasks()
# Expired task should be cleaned up
assert task.taskId not in store._tasks
assert task.task_id not in store._tasks
assert len(tasks) == 0
@ -244,17 +244,17 @@ async def test_task_with_null_ttl_never_expires(store: InMemoryTaskStore) -> Non
task = await store.create_task(metadata=TaskMetadata(ttl=None))
# Verify internal storage has no expiry
stored = store._tasks.get(task.taskId)
stored = store._tasks.get(task.task_id)
assert stored is not None
assert stored.expires_at is None
# Access operations should NOT remove this task
await store.list_tasks()
await store.get_task(task.taskId)
await store.get_task(task.task_id)
# Task should still exist
assert task.taskId in store._tasks
retrieved = await store.get_task(task.taskId)
assert task.task_id in store._tasks
retrieved = await store.get_task(task.task_id)
assert retrieved is not None
@ -265,13 +265,13 @@ async def test_terminal_task_ttl_reset(store: InMemoryTaskStore) -> None:
task = await store.create_task(metadata=TaskMetadata(ttl=60000)) # 60s
# Get the initial expiry
stored = store._tasks.get(task.taskId)
stored = store._tasks.get(task.task_id)
assert stored is not None
initial_expiry = stored.expires_at
assert initial_expiry is not None
# Update to terminal state (completed)
await store.update_task(task.taskId, status="completed")
await store.update_task(task.task_id, status="completed")
# Expiry should be reset to a new time (from now + TTL)
new_expiry = stored.expires_at
@ -291,16 +291,16 @@ async def test_terminal_status_transition_rejected(store: InMemoryTaskStore) ->
task = await store.create_task(metadata=TaskMetadata(ttl=60000))
# Move to terminal state
await store.update_task(task.taskId, status=terminal_status)
await store.update_task(task.task_id, status=terminal_status)
# Attempting to transition to any other status should raise
with pytest.raises(ValueError, match="Cannot transition from terminal status"):
await store.update_task(task.taskId, status="working")
await store.update_task(task.task_id, status="working")
# Also test transitioning to another terminal state
other_terminal = "failed" if terminal_status != "failed" else "completed"
with pytest.raises(ValueError, match="Cannot transition from terminal status"):
await store.update_task(task.taskId, status=other_terminal)
await store.update_task(task.task_id, status=other_terminal)
@pytest.mark.anyio
@ -310,15 +310,15 @@ async def test_terminal_status_allows_same_status(store: InMemoryTaskStore) -> N
This is not a transition, so it should be allowed (no-op).
"""
task = await store.create_task(metadata=TaskMetadata(ttl=60000))
await store.update_task(task.taskId, status="completed")
await store.update_task(task.task_id, status="completed")
# Setting the same status should not raise
updated = await store.update_task(task.taskId, status="completed")
updated = await store.update_task(task.task_id, status="completed")
assert updated.status == "completed"
# Updating just the message should also work
updated = await store.update_task(task.taskId, status_message="Updated message")
assert updated.statusMessage == "Updated message"
updated = await store.update_task(task.task_id, status_message="Updated message")
assert updated.status_message == "Updated message"
@pytest.mark.anyio
@ -334,13 +334,13 @@ async def test_cancel_task_succeeds_for_working_task(store: InMemoryTaskStore) -
task = await store.create_task(metadata=TaskMetadata(ttl=60000))
assert task.status == "working"
result = await cancel_task(store, task.taskId)
result = await cancel_task(store, task.task_id)
assert result.taskId == task.taskId
assert result.task_id == task.task_id
assert result.status == "cancelled"
# Verify store is updated
retrieved = await store.get_task(task.taskId)
retrieved = await store.get_task(task.task_id)
assert retrieved is not None
assert retrieved.status == "cancelled"
@ -359,10 +359,10 @@ async def test_cancel_task_rejects_nonexistent_task(store: InMemoryTaskStore) ->
async def test_cancel_task_rejects_completed_task(store: InMemoryTaskStore) -> None:
"""Test cancel_task raises McpError with INVALID_PARAMS for completed task."""
task = await store.create_task(metadata=TaskMetadata(ttl=60000))
await store.update_task(task.taskId, status="completed")
await store.update_task(task.task_id, status="completed")
with pytest.raises(McpError) as exc_info:
await cancel_task(store, task.taskId)
await cancel_task(store, task.task_id)
assert exc_info.value.error.code == INVALID_PARAMS
assert "terminal state 'completed'" in exc_info.value.error.message
@ -372,10 +372,10 @@ async def test_cancel_task_rejects_completed_task(store: InMemoryTaskStore) -> N
async def test_cancel_task_rejects_failed_task(store: InMemoryTaskStore) -> None:
"""Test cancel_task raises McpError with INVALID_PARAMS for failed task."""
task = await store.create_task(metadata=TaskMetadata(ttl=60000))
await store.update_task(task.taskId, status="failed")
await store.update_task(task.task_id, status="failed")
with pytest.raises(McpError) as exc_info:
await cancel_task(store, task.taskId)
await cancel_task(store, task.task_id)
assert exc_info.value.error.code == INVALID_PARAMS
assert "terminal state 'failed'" in exc_info.value.error.message
@ -385,10 +385,10 @@ async def test_cancel_task_rejects_failed_task(store: InMemoryTaskStore) -> None
async def test_cancel_task_rejects_already_cancelled_task(store: InMemoryTaskStore) -> None:
"""Test cancel_task raises McpError with INVALID_PARAMS for already cancelled task."""
task = await store.create_task(metadata=TaskMetadata(ttl=60000))
await store.update_task(task.taskId, status="cancelled")
await store.update_task(task.task_id, status="cancelled")
with pytest.raises(McpError) as exc_info:
await cancel_task(store, task.taskId)
await cancel_task(store, task.task_id)
assert exc_info.value.error.code == INVALID_PARAMS
assert "terminal state 'cancelled'" in exc_info.value.error.message
@ -398,9 +398,9 @@ async def test_cancel_task_rejects_already_cancelled_task(store: InMemoryTaskSto
async def test_cancel_task_succeeds_for_input_required_task(store: InMemoryTaskStore) -> None:
"""Test cancel_task helper succeeds for a task in input_required status."""
task = await store.create_task(metadata=TaskMetadata(ttl=60000))
await store.update_task(task.taskId, status="input_required")
await store.update_task(task.task_id, status="input_required")
result = await cancel_task(store, task.taskId)
result = await cancel_task(store, task.task_id)
assert result.taskId == task.taskId
assert result.task_id == task.task_id
assert result.status == "cancelled"

View file

@ -53,13 +53,13 @@ async def test_handle_returns_result_for_completed_task(
"""Test that handle() returns the stored result for a completed task."""
task = await store.create_task(TaskMetadata(ttl=60000), task_id="test-task")
result = CallToolResult(content=[TextContent(type="text", text="Done!")])
await store.store_result(task.taskId, result)
await store.update_task(task.taskId, status="completed")
await store.store_result(task.task_id, result)
await store.update_task(task.task_id, status="completed")
mock_session = Mock()
mock_session.send_message = AsyncMock()
request = GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(taskId=task.taskId))
request = GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(task_id=task.task_id))
response = await handler.handle(request, mock_session, "req-1")
assert response is not None
@ -73,7 +73,7 @@ async def test_handle_raises_for_nonexistent_task(
) -> None:
"""Test that handle() raises McpError for nonexistent task."""
mock_session = Mock()
request = GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(taskId="nonexistent"))
request = GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(task_id="nonexistent"))
with pytest.raises(McpError) as exc_info:
await handler.handle(request, mock_session, "req-1")
@ -87,12 +87,12 @@ async def test_handle_returns_empty_result_when_no_result_stored(
) -> None:
"""Test that handle() returns minimal result when task completed without stored result."""
task = await store.create_task(TaskMetadata(ttl=60000), task_id="test-task")
await store.update_task(task.taskId, status="completed")
await store.update_task(task.task_id, status="completed")
mock_session = Mock()
mock_session.send_message = AsyncMock()
request = GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(taskId=task.taskId))
request = GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(task_id=task.task_id))
response = await handler.handle(request, mock_session, "req-1")
assert response is not None
@ -116,8 +116,8 @@ async def test_handle_delivers_queued_messages(
params={},
),
)
await queue.enqueue(task.taskId, queued_msg)
await store.update_task(task.taskId, status="completed")
await queue.enqueue(task.task_id, queued_msg)
await store.update_task(task.task_id, status="completed")
sent_messages: list[SessionMessage] = []
@ -127,7 +127,7 @@ async def test_handle_delivers_queued_messages(
mock_session = Mock()
mock_session.send_message = track_send
request = GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(taskId=task.taskId))
request = GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(task_id=task.task_id))
await handler.handle(request, mock_session, "req-1")
assert len(sent_messages) == 1
@ -143,7 +143,7 @@ async def test_handle_waits_for_task_completion(
mock_session = Mock()
mock_session.send_message = AsyncMock()
request = GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(taskId=task.taskId))
request = GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(task_id=task.task_id))
result_holder: list[GetTaskPayloadResult | None] = [None]
async def run_handle() -> None:
@ -153,11 +153,11 @@ async def test_handle_waits_for_task_completion(
tg.start_soon(run_handle)
# Wait for handler to start waiting (event gets created when wait starts)
while task.taskId not in store._update_events:
while task.task_id not in store._update_events:
await anyio.sleep(0)
await store.store_result(task.taskId, CallToolResult(content=[TextContent(type="text", text="Done")]))
await store.update_task(task.taskId, status="completed")
await store.store_result(task.task_id, CallToolResult(content=[TextContent(type="text", text="Done")]))
await store.update_task(task.task_id, status="completed")
assert result_holder[0] is not None
@ -248,12 +248,12 @@ async def test_deliver_registers_resolver_for_request_messages(
resolver=resolver,
original_request_id="inner-req-1",
)
await queue.enqueue(task.taskId, queued_msg)
await queue.enqueue(task.task_id, queued_msg)
mock_session = Mock()
mock_session.send_message = AsyncMock()
await handler._deliver_queued_messages(task.taskId, mock_session, "outer-req-1")
await handler._deliver_queued_messages(task.task_id, mock_session, "outer-req-1")
assert "inner-req-1" in handler._pending_requests
assert handler._pending_requests["inner-req-1"] is resolver
@ -278,12 +278,12 @@ async def test_deliver_skips_resolver_registration_when_no_original_id(
resolver=resolver,
original_request_id=None, # No original request ID
)
await queue.enqueue(task.taskId, queued_msg)
await queue.enqueue(task.task_id, queued_msg)
mock_session = Mock()
mock_session.send_message = AsyncMock()
await handler._deliver_queued_messages(task.taskId, mock_session, "outer-req-1")
await handler._deliver_queued_messages(task.task_id, mock_session, "outer-req-1")
# Resolver should NOT be registered since original_request_id is None
assert len(handler._pending_requests) == 0
@ -307,10 +307,10 @@ async def test_wait_for_task_update_handles_store_exception(
# Queue a message to unblock the race via the queue path
async def enqueue_later() -> None:
# Wait for queue to start waiting (event gets created when wait starts)
while task.taskId not in queue._events:
while task.task_id not in queue._events:
await anyio.sleep(0)
await queue.enqueue(
task.taskId,
task.task_id,
QueuedMessage(
type="notification",
message=JSONRPCRequest(
@ -325,7 +325,7 @@ async def test_wait_for_task_update_handles_store_exception(
async with anyio.create_task_group() as tg:
tg.start_soon(enqueue_later)
# This should complete via the queue path even though store raises
await handler._wait_for_task_update(task.taskId)
await handler._wait_for_task_update(task.task_id)
@pytest.mark.anyio
@ -344,11 +344,11 @@ async def test_wait_for_task_update_handles_queue_exception(
# Update the store to unblock the race via the store path
async def update_later() -> None:
# Wait for store to start waiting (event gets created when wait starts)
while task.taskId not in store._update_events:
while task.task_id not in store._update_events:
await anyio.sleep(0)
await store.update_task(task.taskId, status="completed")
await store.update_task(task.task_id, status="completed")
async with anyio.create_task_group() as tg:
tg.start_soon(update_later)
# This should complete via the store path even though queue raises
await handler._wait_for_task_update(task.taskId)
await handler._wait_for_task_update(task.task_id)