"""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