refactor: use snake case instead of camel case in types (#1894)
This commit is contained in:
parent
7080fcf1e6
commit
e94b386a13
7 changed files with 1063 additions and 1057 deletions
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue