294 lines
9.2 KiB
Python
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
|