"""Server Routes 单元测试 - 使用 FastAPI TestClient""" import pytest from unittest.mock import AsyncMock, MagicMock, patch from fastapi.testclient import TestClient from agentkit.core.agent_pool import AgentPool from agentkit.core.config_driven import AgentConfig from agentkit.core.protocol import AgentStatus from agentkit.llm.gateway import LLMGateway from agentkit.llm.protocol import LLMResponse, TokenUsage from agentkit.skills.base import Skill, SkillConfig from agentkit.skills.registry import SkillRegistry from agentkit.tools.registry import ToolRegistry from agentkit.server.app import create_app @pytest.fixture def mock_llm_gateway(): gateway = LLMGateway() # Register a mock provider so gateway.chat() works mock_provider = AsyncMock() mock_provider.chat.return_value = LLMResponse( content='{"result": "mocked output"}', model="test-model", usage=TokenUsage(prompt_tokens=10, completion_tokens=20), ) gateway.register_provider("test", mock_provider) return gateway @pytest.fixture def skill_registry(): return SkillRegistry() @pytest.fixture def tool_registry(): return ToolRegistry() @pytest.fixture def app(mock_llm_gateway, skill_registry, tool_registry): return create_app( llm_gateway=mock_llm_gateway, skill_registry=skill_registry, tool_registry=tool_registry, ) @pytest.fixture def client(app): return TestClient(app) class TestHealthRoute: """GET /api/v1/health""" def test_health_returns_ok(self, client): response = client.get("/api/v1/health") assert response.status_code == 200 data = response.json() assert data["status"] == "ok" assert data["version"] == "2.0.0" class TestAgentRoutes: """Agent CRUD 路由测试""" def test_create_agent_201(self, client): response = client.post( "/api/v1/agents", json={ "config": { "name": "test_agent", "agent_type": "test_type", "task_mode": "llm_generate", "prompt": {"identity": "Test", "instructions": "Do test"}, } }, ) assert response.status_code == 201 data = response.json() assert data["name"] == "test_agent" assert data["agent_type"] == "test_type" def test_create_agent_from_skill_201(self, client, skill_registry): skill_config = SkillConfig( name="my_skill", agent_type="skill_type", task_mode="llm_generate", prompt={"identity": "Skill Agent"}, intent={"keywords": ["skill"], "description": "A skill"}, ) skill = Skill(config=skill_config) skill_registry.register(skill) response = client.post( "/api/v1/agents", json={"skill_name": "my_skill"}, ) assert response.status_code == 201 data = response.json() assert data["name"] == "my_skill" def test_list_agents_empty(self, client): response = client.get("/api/v1/agents") assert response.status_code == 200 assert response.json() == [] def test_list_agents_after_create(self, client): client.post( "/api/v1/agents", json={ "config": { "name": "agent1", "agent_type": "type1", "task_mode": "llm_generate", "prompt": {"identity": "Agent 1"}, } }, ) response = client.get("/api/v1/agents") assert response.status_code == 200 data = response.json() assert len(data) == 1 assert data[0]["name"] == "agent1" def test_get_agent_detail(self, client): client.post( "/api/v1/agents", json={ "config": { "name": "detail_agent", "agent_type": "detail_type", "task_mode": "llm_generate", "prompt": {"identity": "Detail Agent"}, } }, ) response = client.get("/api/v1/agents/detail_agent") assert response.status_code == 200 data = response.json() assert data["name"] == "detail_agent" assert data["agent_type"] == "detail_type" def test_get_agent_not_found_404(self, client): response = client.get("/api/v1/agents/nonexistent") assert response.status_code == 404 def test_delete_agent_204(self, client): client.post( "/api/v1/agents", json={ "config": { "name": "to_delete", "agent_type": "del_type", "task_mode": "llm_generate", "prompt": {"identity": "Delete me"}, } }, ) response = client.delete("/api/v1/agents/to_delete") assert response.status_code == 204 # Verify agent is gone response = client.get("/api/v1/agents/to_delete") assert response.status_code == 404 class TestTaskRoutes: """Task 提交路由测试""" def test_submit_task_with_skill_name(self, client, skill_registry): # Register a skill first skill_config = SkillConfig( name="task_skill", agent_type="task_type", task_mode="llm_generate", prompt={"identity": "Task Skill", "instructions": "Handle tasks"}, intent={"keywords": ["task"], "description": "Task skill"}, ) skill = Skill(config=skill_config) skill_registry.register(skill) response = client.post( "/api/v1/tasks", json={ "input_data": {"query": "test query"}, "skill_name": "task_skill", }, ) assert response.status_code == 200 data = response.json() assert "skill_name" in data or "data" in data or "output" in data def test_submit_task_with_agent_name(self, client): # Create an agent first client.post( "/api/v1/agents", json={ "config": { "name": "task_agent", "agent_type": "task_type", "task_mode": "llm_generate", "prompt": {"identity": "Task Agent"}, } }, ) response = client.post( "/api/v1/tasks", json={ "input_data": {"query": "test query"}, "agent_name": "task_agent", }, ) assert response.status_code == 200 def test_submit_task_no_skill_no_agent_error(self, client): response = client.post( "/api/v1/tasks", json={ "input_data": {"query": "test query"}, }, ) # Should return 400 or 422 since no skill or agent specified and no skills registered assert response.status_code in (400, 422) def test_get_task_status_placeholder(self, client): response = client.get("/api/v1/tasks/some-task-id") # Placeholder implementation assert response.status_code in (200, 404) class TestSkillRoutes: """Skill 注册路由测试""" def test_register_skill_201(self, client): response = client.post( "/api/v1/skills", json={ "config": { "name": "new_skill", "agent_type": "skill_type", "task_mode": "llm_generate", "prompt": {"identity": "New Skill"}, "intent": {"keywords": ["new"], "description": "A new skill"}, } }, ) assert response.status_code == 201 data = response.json() assert data["name"] == "new_skill" def test_list_skills_empty(self, client): response = client.get("/api/v1/skills") assert response.status_code == 200 assert response.json() == [] def test_list_skills_after_register(self, client): client.post( "/api/v1/skills", json={ "config": { "name": "listed_skill", "agent_type": "skill_type", "task_mode": "llm_generate", "prompt": {"identity": "Listed Skill"}, "intent": {"keywords": ["listed"], "description": "A listed skill"}, } }, ) response = client.get("/api/v1/skills") assert response.status_code == 200 data = response.json() assert len(data) >= 1 names = [s["name"] for s in data] assert "listed_skill" in names class TestLLMRoute: """LLM Usage 路由测试""" def test_get_usage(self, client): response = client.get("/api/v1/llm/usage") assert response.status_code == 200 data = response.json() assert "total_tokens" in data or "total_cost" in data def test_get_usage_with_agent_name(self, client): response = client.get("/api/v1/llm/usage?agent_name=test_agent") assert response.status_code == 200