Files
cowork-local/tests/characterization/test_run_cowork.py
T

158 lines
5.5 KiB
Python

"""Characterization tests for core/chat_agent.py (run_chat and run_cowork runtime seams).
These tests capture existing behavior as an executable baseline specification,
ensuring that future refactoring to ConversationApplicationService does not alter
core turn semantics, event emissions, or file handling.
"""
from __future__ import annotations
from pathlib import Path
from typing import Any, Dict, List
from cowork_local.core import chat_agent
from cowork_local.tests.fakes.fake_provider import FakeProvider
def test_run_chat_characterization() -> None:
"""Capture baseline behavior of run_chat: system prompt insertion, streaming, and message persistence."""
provider = FakeProvider()
provider.queue_response(content="Hello there!", chunks=["Hello ", "there!"])
messages: List[Dict[str, Any]] = [{"role": "user", "content": "Hi assistant"}]
emitted_events: List[Dict[str, Any]] = []
def emit(event: Dict[str, Any]) -> None:
emitted_events.append(event)
result = chat_agent.run_chat(
provider=provider,
messages=messages,
emit=emit,
)
# 1. Verify system prompt was injected at position 0
assert messages[0]["role"] == "system"
assert "Cowork Local" in messages[0]["content"]
# 2. Verify returned assistant message
assert result["role"] == "assistant"
assert result["content"] == "Hello there!"
# 3. Verify assistant message was appended to messages list
assert messages[-1] == result
# 4. Verify emitted events sequence
text_deltas = [e["delta"] for e in emitted_events if e["type"] == "text"]
assert "".join(text_deltas) == "Hello there!"
assert any(e["type"] == "assistant_done" for e in emitted_events)
def test_run_cowork_save_file_characterization(tmp_path: Path) -> None:
"""Capture baseline behavior of run_cowork: tool execution loop and file production."""
output_dir = tmp_path / "output"
output_dir.mkdir(parents=True, exist_ok=True)
provider = FakeProvider()
# Step 1: Model requests save_file tool
provider.queue_response(
content="Saving your requested report.",
tool_calls=[{
"id": "call_save_1",
"name": "save_file",
"arguments": {
"filename": "report.md",
"content": "# Executive Summary\nAll systems nominal.",
},
}],
)
# Step 2: Model finishes after tool result
provider.queue_response(
content="I have created report.md in your output directory.",
chunks=["I have created report.md in your output directory."],
)
messages: List[Dict[str, Any]] = [{"role": "user", "content": "Export report to markdown file"}]
emitted_events: List[Dict[str, Any]] = []
def emit(event: Dict[str, Any]) -> None:
emitted_events.append(event)
final_messages = chat_agent.run_cowork(
provider=provider,
messages=messages,
output_dir=output_dir,
emit=emit,
enforce_rules=False,
)
# 1. Verify file was created in output directory with expected content
created_file = output_dir / "report.md"
assert created_file.exists()
assert created_file.read_text(encoding="utf-8") == "# Executive Summary\nAll systems nominal."
# 2. Verify message history contains user -> assistant (tool_calls) -> tool -> assistant
roles = [m["role"] for m in final_messages]
assert "system" in roles
assert "user" in roles
assert "tool" in roles
# 3. Verify tool result message content
tool_msg = next(m for m in final_messages if m["role"] == "tool")
assert tool_msg["name"] == "save_file"
assert "Saved report.md" in tool_msg["content"]
def test_run_cowork_cancellation_characterization(tmp_path: Path) -> None:
"""Capture cancellation behavior in run_cowork."""
output_dir = tmp_path / "output_cancel"
output_dir.mkdir(parents=True, exist_ok=True)
provider = FakeProvider()
provider.queue_response(content="Working...")
is_cancelled = True
def check_cancel() -> bool:
return is_cancelled
emitted_events: List[Dict[str, Any]] = []
messages: List[Dict[str, Any]] = [{"role": "user", "content": "Please start"}]
chat_agent.run_cowork(
provider=provider,
messages=messages,
output_dir=output_dir,
emit=lambda e: emitted_events.append(e),
cancel=check_cancel,
enforce_rules=False,
)
# Provider should not have executed turns if cancelled right away
assert provider.call_count == 0
def test_cleanup_turn_output_characterization(tmp_path: Path) -> None:
"""Capture behavior of temporary .scratch folder cleanup and artifact preservation."""
output_dir = tmp_path / "output_cleanup"
output_dir.mkdir(parents=True, exist_ok=True)
scratch_dir = output_dir / ".scratch"
scratch_dir.mkdir(parents=True, exist_ok=True)
# Create a generator script and a deliverable inside scratch
generator_script = scratch_dir / "gen.py"
generator_script.write_text("print('generating')", encoding="utf-8")
deliverable = scratch_dir / "data.csv"
deliverable.write_text("a,b,c\n1,2,3", encoding="utf-8")
before_snapshot = chat_agent._snapshot(output_dir)
removed, moved = chat_agent._cleanup_cowork_intermediates(output_dir, before_snapshot, cancelled=False)
# .scratch directory should be removed
assert not scratch_dir.exists()
# deliverable should be moved to output root
root_csv = output_dir / "data.csv"
assert root_csv.exists()
# script should not be in output root
assert not (output_dir / "gen.py").exists()