"""Tests for AgentTool - 将 Agent 包装为 Tool""" import pytest from unittest.mock import AsyncMock, MagicMock from agentkit.tools.agent_tool import AgentTool from agentkit.core.protocol import TaskStatus class TestAgentToolInit: """AgentTool 初始化测试""" def test_default_attributes(self): tool = AgentTool( name="my_agent_tool", description="Wraps an agent", agent_name="target_agent", task_type="analyze", ) assert tool.name == "my_agent_tool" assert tool.description == "Wraps an agent" assert tool.agent_name == "target_agent" assert tool.task_type == "analyze" assert tool.input_mapping == {} assert tool.output_mapping == {} assert tool.timeout_seconds == 300 assert tool.version == "1.0.0" assert tool.tags == ["agent"] assert tool._dispatcher is None def test_custom_attributes(self): tool = AgentTool( name="tool", description="desc", agent_name="agent_a", task_type="translate", input_mapping={"text": "content"}, output_mapping={"result": "translation"}, timeout_seconds=60, version="2.0.0", tags=["agent", "nlp"], ) assert tool.input_mapping == {"text": "content"} assert tool.output_mapping == {"result": "translation"} assert tool.timeout_seconds == 60 assert tool.version == "2.0.0" assert tool.tags == ["agent", "nlp"] def test_set_dispatcher_returns_self(self): tool = AgentTool( name="t", description="d", agent_name="a", task_type="t" ) dispatcher = MagicMock() result = tool.set_dispatcher(dispatcher) assert result is tool assert tool._dispatcher is dispatcher class TestAgentToolExecute: """AgentTool.execute 异步执行测试""" async def test_execute_without_dispatcher_raises(self): tool = AgentTool( name="t", description="d", agent_name="a", task_type="t" ) with pytest.raises(RuntimeError, match="has no dispatcher configured"): await tool.execute(query="hello") async def test_execute_dispatches_task(self): dispatcher = AsyncMock() dispatcher.get_task_status.return_value = { "status": "completed", "output_data": {"answer": "world"}, } tool = AgentTool( name="t", description="d", agent_name="target", task_type="ask" ) tool.set_dispatcher(dispatcher) result = await tool.execute(query="hello") assert result == {"answer": "world"} dispatcher.dispatch.assert_awaited_once() dispatched_task = dispatcher.dispatch.call_args[0][0] assert dispatched_task.agent_name == "target" assert dispatched_task.task_type == "ask" async def test_execute_with_input_mapping(self): dispatcher = AsyncMock() dispatcher.get_task_status.return_value = { "status": "completed", "output_data": {"text": "result"}, } tool = AgentTool( name="t", description="d", agent_name="a", task_type="t", input_mapping={"content": "query"}, ) tool.set_dispatcher(dispatcher) await tool.execute(query="hello") dispatched_task = dispatcher.dispatch.call_args[0][0] assert dispatched_task.input_data == {"content": "hello"} async def test_execute_without_input_mapping_passes_all_kwargs(self): dispatcher = AsyncMock() dispatcher.get_task_status.return_value = { "status": "completed", "output_data": {}, } tool = AgentTool( name="t", description="d", agent_name="a", task_type="t" ) tool.set_dispatcher(dispatcher) await tool.execute(x=1, y=2) dispatched_task = dispatcher.dispatch.call_args[0][0] assert dispatched_task.input_data == {"x": 1, "y": 2} async def test_execute_with_output_mapping(self): dispatcher = AsyncMock() dispatcher.get_task_status.return_value = { "status": "completed", "output_data": {"translation": "bonjour", "confidence": 0.9}, } tool = AgentTool( name="t", description="d", agent_name="a", task_type="t", output_mapping={"result": "translation"}, ) tool.set_dispatcher(dispatcher) result = await tool.execute(text="hello") assert result == {"result": "bonjour"} async def test_execute_output_mapping_skips_missing_keys(self): dispatcher = AsyncMock() dispatcher.get_task_status.return_value = { "status": "completed", "output_data": {"translation": "bonjour"}, } tool = AgentTool( name="t", description="d", agent_name="a", task_type="t", output_mapping={"result": "translation", "score": "confidence"}, ) tool.set_dispatcher(dispatcher) result = await tool.execute(text="hello") assert result == {"result": "bonjour"} async def test_execute_failed_status_raises(self): dispatcher = AsyncMock() dispatcher.get_task_status.return_value = { "status": "failed", "error_message": "OOM", } tool = AgentTool( name="t", description="d", agent_name="a", task_type="t" ) tool.set_dispatcher(dispatcher) with pytest.raises(RuntimeError, match="failed: OOM"): await tool.execute() async def test_execute_cancelled_returns_empty(self): dispatcher = AsyncMock() dispatcher.get_task_status.return_value = { "status": "cancelled", } tool = AgentTool( name="t", description="d", agent_name="a", task_type="t" ) tool.set_dispatcher(dispatcher) result = await tool.execute() assert result == {} async def test_execute_completed_no_output_data_returns_empty(self): dispatcher = AsyncMock() dispatcher.get_task_status.return_value = { "status": "completed", "output_data": None, } tool = AgentTool( name="t", description="d", agent_name="a", task_type="t" ) tool.set_dispatcher(dispatcher) result = await tool.execute() assert result == {} async def test_execute_timeout_raises(self): dispatcher = AsyncMock() # Always return running status to simulate timeout dispatcher.get_task_status.return_value = {"status": "running"} tool = AgentTool( name="t", description="d", agent_name="a", task_type="t", timeout_seconds=1, ) tool.set_dispatcher(dispatcher) with pytest.raises(TimeoutError, match="timed out after 1s"): await tool.execute() async def test_execute_waits_for_completion(self): dispatcher = AsyncMock() call_count = 0 async def mock_status(task_id): nonlocal call_count call_count += 1 if call_count < 3: return {"status": "running"} return {"status": "completed", "output_data": {"done": True}} dispatcher.get_task_status.side_effect = mock_status tool = AgentTool( name="t", description="d", agent_name="a", task_type="t", timeout_seconds=10, ) tool.set_dispatcher(dispatcher) result = await tool.execute() assert result == {"done": True} async def test_execute_input_mapping_only_maps_matched_keys(self): dispatcher = AsyncMock() dispatcher.get_task_status.return_value = { "status": "completed", "output_data": {}, } tool = AgentTool( name="t", description="d", agent_name="a", task_type="t", input_mapping={"content": "query", "extra": "missing_key"}, ) tool.set_dispatcher(dispatcher) await tool.execute(query="hello", other="world") dispatched_task = dispatcher.dispatch.call_args[0][0] assert dispatched_task.input_data == {"content": "hello"}