fischer-agentkit/tests/unit/test_server_routes.py

294 lines
9.2 KiB
Python

"""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"] in ("ok", "healthy", "degraded")
assert data["version"] == "2.0.0"
assert "checks" in data
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