mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-08-18 02:35:41 +02:00
68 lines
2.8 KiB
Python
68 lines
2.8 KiB
Python
"""Unit coverage for the extracted PostToolUse hook (tool_result_hook). The streaming harness
|
|
mocks claude_agent_sdk.query, so it never fires the SDK's PostToolUse hooks; this pins the
|
|
behavior directly: a tool result becomes a tool_result message, and an Agent tool spawns a
|
|
sub-session into the manager's LIVE registry (the InstanceOf[dict] sharing, the subtle bit)."""
|
|
|
|
import pytest
|
|
from unittest.mock import patch, AsyncMock
|
|
|
|
from backend.apps.agents.core.models import AgentSession
|
|
from backend.apps.agents.manager.streaming.hook_context import HookContext
|
|
from backend.apps.agents.manager.streaming import tool_result_hook
|
|
|
|
|
|
def _ctx(registry: dict) -> HookContext:
|
|
session = AgentSession(name="t", model="sonnet", dashboard_id="d")
|
|
registry[session.id] = session
|
|
return HookContext(
|
|
session=session,
|
|
session_id=session.id,
|
|
prompt="hi",
|
|
builtin_perms={},
|
|
policy_defaults={},
|
|
sessions=registry,
|
|
)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_normal_tool_result_appends_message_and_continues():
|
|
registry: dict = {}
|
|
ctx = _ctx(registry)
|
|
before = len(ctx.session.messages)
|
|
with patch.object(tool_result_hook.ws_manager, "send_to_session", new=AsyncMock()) as send:
|
|
out = await tool_result_hook.post_tool_hook(
|
|
ctx, {"tool_name": "Read", "tool_response": "file body", "tool_input": {"file_path": "/x"}}, "tu1", None
|
|
)
|
|
assert out == {"continue_": True}
|
|
assert len(ctx.session.messages) == before + 1
|
|
msg = ctx.session.messages[-1]
|
|
assert msg.role == "tool_result"
|
|
assert "file body" in str(msg.content)
|
|
send.assert_awaited() # the tool_result is broadcast to the UI
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_agent_tool_spawns_subsession_into_live_registry():
|
|
registry: dict = {}
|
|
ctx = _ctx(registry)
|
|
parent_id = ctx.session_id
|
|
raw = {
|
|
"content": [{"type": "text", "text": "sub-agent did the work"}],
|
|
"usage": {"input_tokens": 7, "output_tokens": 3},
|
|
"total_cost_usd": 0.01,
|
|
"model": "sonnet",
|
|
}
|
|
with patch.object(tool_result_hook.ws_manager, "send_to_session", new=AsyncMock()), \
|
|
patch.object(tool_result_hook.ws_manager, "broadcast_global", new=AsyncMock()):
|
|
out = await tool_result_hook.post_tool_hook(
|
|
ctx, {"tool_name": "Agent", "tool_response": raw, "tool_input": {"prompt": "do x"}}, "tu1", None
|
|
)
|
|
assert out == {"continue_": True}
|
|
# exactly one NEW session registered (besides the parent), parented correctly
|
|
children = [s for sid, s in registry.items() if sid != parent_id]
|
|
assert len(children) == 1
|
|
child = children[0]
|
|
assert child.parent_session_id == parent_id
|
|
assert child.active_mcps == [] # context-isolation invariant: no inherited activations
|
|
assert "sub-agent did the work" in str(child.messages[-1].content)
|