fix: reliably dispatch and return background tasks
Rebuild & Deploy / rebuild (push) Successful in 47s
Rebuild & Deploy / rebuild (push) Successful in 47s
This commit is contained in:
1 parent
746ca8dca9
commit
321421e37f
7 files changed
+310
-27
No files matched your search
@@ -0,0 +1,116 @@
|
||||
"""Regression tests for background-task routing and event isolation."""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import sys
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parents[1] / "agent"))
|
||||
from task_registry import TaskRegistry # noqa: E402
|
||||
from task_router import should_force_background_task # noqa: E402
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"utterance",
|
||||
[
|
||||
"What are the top news stories today?",
|
||||
"Compare the PS5 Pro and Xbox Series X for me.",
|
||||
"Plan a weekend trip to Denver.",
|
||||
"Write a short birthday message for my sister.",
|
||||
"Write a birthday note for Sam.",
|
||||
"Look up the current price of a PlayStation 5.",
|
||||
"Research the latest solid state battery developments.",
|
||||
"Tell me about quantum computing.",
|
||||
],
|
||||
)
|
||||
def test_nontrivial_requests_force_dispatch(utterance):
|
||||
assert should_force_background_task(utterance)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"utterance",
|
||||
[
|
||||
"What's my name?",
|
||||
"Remember that I prefer tea.",
|
||||
"Read my saved notes.",
|
||||
"What's the weather in Raleigh?",
|
||||
"What time is it?",
|
||||
"Hello there.",
|
||||
],
|
||||
)
|
||||
def test_direct_requests_do_not_force_dispatch(utterance):
|
||||
assert not should_force_background_task(utterance)
|
||||
|
||||
|
||||
def test_task_event_directories_are_isolated(tmp_path):
|
||||
first = TaskRegistry(tmp_path / "room-one")
|
||||
second = TaskRegistry(tmp_path / "room-two")
|
||||
|
||||
task = first.create("private task")
|
||||
first.complete(task.id, "private result")
|
||||
|
||||
first_events = asyncio.run(first.poll_events())
|
||||
second_events = asyncio.run(second.poll_events())
|
||||
|
||||
assert [event[1]["event"] for event in first_events] == ["created", "completed"]
|
||||
assert second_events == []
|
||||
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_dispatch_mcp_task_reaches_terminal_event():
|
||||
"""The MCP request returns immediately while its child task stays alive."""
|
||||
from conftest import AGENT_DIR_IN_CONTAINER, CONTAINER_PYTHON, docker_exec_python
|
||||
|
||||
task_dir = f"/tmp/task-test-{uuid.uuid4().hex}"
|
||||
code = f"""
|
||||
import asyncio, json, os
|
||||
from mcp import ClientSession, StdioServerParameters
|
||||
from mcp.client.stdio import stdio_client
|
||||
|
||||
async def main():
|
||||
task_dir = {task_dir!r}
|
||||
os.makedirs(task_dir, exist_ok=True)
|
||||
env = {{**os.environ, "TASKS_DIR": task_dir, "TASK_TIMEOUT_SECONDS": "60"}}
|
||||
params = StdioServerParameters(
|
||||
command={CONTAINER_PYTHON!r},
|
||||
args=[{AGENT_DIR_IN_CONTAINER + '/dispatch_mcp.py'!r}],
|
||||
env=env,
|
||||
)
|
||||
async with stdio_client(params) as (read, write):
|
||||
async with ClientSession(read, write) as session:
|
||||
await session.initialize()
|
||||
result = await session.call_tool(
|
||||
"dispatch_task",
|
||||
{{"description": "Write a warm two-sentence birthday greeting for Sam."}},
|
||||
)
|
||||
assert not result.isError
|
||||
for _ in range(60):
|
||||
await asyncio.sleep(0.5)
|
||||
with open(os.path.join(task_dir, "events.jsonl"), encoding="utf-8") as f:
|
||||
events = [json.loads(line) for line in f if line.strip()]
|
||||
terminal = [e for e in events if e.get("event") in ("completed", "failed")]
|
||||
if terminal:
|
||||
print(json.dumps(terminal[-1]))
|
||||
return
|
||||
raise RuntimeError("background task produced no terminal event")
|
||||
|
||||
asyncio.run(main())
|
||||
"""
|
||||
try:
|
||||
proc = docker_exec_python(code, timeout=90, check=False)
|
||||
assert proc.returncode == 0, proc.stderr[-1200:]
|
||||
terminal = json.loads(proc.stdout.strip().splitlines()[-1])
|
||||
assert terminal["event"] == "completed", terminal
|
||||
assert terminal.get("result")
|
||||
finally:
|
||||
# Best-effort cleanup; a failed container connection should not mask the assertion.
|
||||
try:
|
||||
from conftest import docker_exec
|
||||
|
||||
docker_exec("rm", "-rf", task_dir, check=False)
|
||||
except Exception:
|
||||
pass
|
||||
Reference in new issue
Block a user