240 lines
7.9 KiB
Python
240 lines
7.9 KiB
Python
"""Server E2E 集成测试 - 完整流程"""
|
||
|
||
import pytest
|
||
from unittest.mock import AsyncMock
|
||
from fastapi.testclient import TestClient
|
||
|
||
from agentkit.core.protocol import AgentStatus
|
||
from agentkit.llm.gateway import LLMGateway
|
||
from agentkit.llm.protocol import LLMProvider, LLMRequest, 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
|
||
|
||
|
||
class MockLLMProvider(LLMProvider):
|
||
"""Mock LLM Provider for integration tests"""
|
||
|
||
def __init__(self):
|
||
self.call_count = 0
|
||
|
||
async def chat(self, request: LLMRequest) -> LLMResponse:
|
||
self.call_count += 1
|
||
return LLMResponse(
|
||
content='{"result": "integration test output", "content": "This is the generated content from the skill"}',
|
||
model="mock-model",
|
||
usage=TokenUsage(prompt_tokens=50, completion_tokens=100),
|
||
)
|
||
|
||
|
||
@pytest.fixture
|
||
def llm_gateway():
|
||
gw = LLMGateway()
|
||
gw.register_provider("mock", MockLLMProvider())
|
||
return gw
|
||
|
||
|
||
@pytest.fixture
|
||
def skill_registry():
|
||
return SkillRegistry()
|
||
|
||
|
||
@pytest.fixture
|
||
def tool_registry():
|
||
return ToolRegistry()
|
||
|
||
|
||
@pytest.fixture
|
||
def app(llm_gateway, skill_registry, tool_registry):
|
||
return create_app(
|
||
llm_gateway=llm_gateway,
|
||
skill_registry=skill_registry,
|
||
tool_registry=tool_registry,
|
||
)
|
||
|
||
|
||
@pytest.fixture
|
||
def client(app):
|
||
return TestClient(app)
|
||
|
||
|
||
class TestFullFlow:
|
||
"""完整流程:register skill → create agent → submit task → get result"""
|
||
|
||
def test_register_skill_create_agent_submit_task(self, client):
|
||
# Step 1: Register a skill
|
||
skill_response = client.post(
|
||
"/api/v1/skills",
|
||
json={
|
||
"config": {
|
||
"name": "content_writer",
|
||
"agent_type": "content_generation",
|
||
"task_mode": "llm_generate",
|
||
"description": "Content writing skill",
|
||
"prompt": {
|
||
"identity": "You are a content writer",
|
||
"instructions": "Write high-quality content",
|
||
"output_format": "JSON",
|
||
},
|
||
"intent": {
|
||
"keywords": ["write", "content", "article"],
|
||
"description": "Content writing and generation",
|
||
},
|
||
"quality_gate": {
|
||
"required_fields": ["content"],
|
||
"min_word_count": 5,
|
||
},
|
||
}
|
||
},
|
||
)
|
||
assert skill_response.status_code == 201
|
||
|
||
# Step 2: Create agent from skill
|
||
agent_response = client.post(
|
||
"/api/v1/agents",
|
||
json={"skill_name": "content_writer"},
|
||
)
|
||
assert agent_response.status_code == 201
|
||
agent_data = agent_response.json()
|
||
assert agent_data["name"] == "content_writer"
|
||
|
||
# Step 3: Verify agent is listed
|
||
list_response = client.get("/api/v1/agents")
|
||
assert list_response.status_code == 200
|
||
agents = list_response.json()
|
||
assert len(agents) == 1
|
||
assert agents[0]["name"] == "content_writer"
|
||
|
||
# Step 4: Submit task using skill_name
|
||
task_response = client.post(
|
||
"/api/v1/tasks",
|
||
json={
|
||
"input_data": {"query": "Write an article about AI"},
|
||
"skill_name": "content_writer",
|
||
},
|
||
)
|
||
assert task_response.status_code == 200
|
||
task_data = task_response.json()
|
||
# Result should contain standardized output
|
||
assert "skill_name" in task_data or "data" in task_data or "output" in task_data
|
||
|
||
# Step 5: Verify skill is listed
|
||
skills_response = client.get("/api/v1/skills")
|
||
assert skills_response.status_code == 200
|
||
skills = skills_response.json()
|
||
assert len(skills) >= 1
|
||
|
||
def test_submit_task_auto_routes_to_skill(self, client):
|
||
"""Intent Router 自动路由到正确的 skill"""
|
||
# Register two skills with different keywords
|
||
client.post(
|
||
"/api/v1/skills",
|
||
json={
|
||
"config": {
|
||
"name": "translator",
|
||
"agent_type": "translation",
|
||
"task_mode": "llm_generate",
|
||
"prompt": {"identity": "Translator", "instructions": "Translate text"},
|
||
"intent": {
|
||
"keywords": ["translate", "翻译"],
|
||
"description": "Translation skill",
|
||
},
|
||
}
|
||
},
|
||
)
|
||
client.post(
|
||
"/api/v1/skills",
|
||
json={
|
||
"config": {
|
||
"name": "summarizer",
|
||
"agent_type": "summarization",
|
||
"task_mode": "llm_generate",
|
||
"prompt": {"identity": "Summarizer", "instructions": "Summarize text"},
|
||
"intent": {
|
||
"keywords": ["summarize", "摘要"],
|
||
"description": "Summarization skill",
|
||
},
|
||
}
|
||
},
|
||
)
|
||
|
||
# Submit task with keyword matching "translate"
|
||
response = client.post(
|
||
"/api/v1/tasks",
|
||
json={
|
||
"input_data": {"query": "Please translate this text to English"},
|
||
},
|
||
)
|
||
# Should route to translator skill via keyword matching
|
||
assert response.status_code == 200
|
||
|
||
def test_delete_agent_then_submit_task_error(self, client):
|
||
"""Delete agent → submit task → appropriate error"""
|
||
# Register skill and create agent
|
||
client.post(
|
||
"/api/v1/skills",
|
||
json={
|
||
"config": {
|
||
"name": "deletable_skill",
|
||
"agent_type": "deletable_type",
|
||
"task_mode": "llm_generate",
|
||
"prompt": {"identity": "Deletable"},
|
||
"intent": {"keywords": ["delete"], "description": "Deletable skill"},
|
||
}
|
||
},
|
||
)
|
||
client.post(
|
||
"/api/v1/agents",
|
||
json={"skill_name": "deletable_skill"},
|
||
)
|
||
|
||
# Delete the agent
|
||
delete_response = client.delete("/api/v1/agents/deletable_skill")
|
||
assert delete_response.status_code == 204
|
||
|
||
# Submit task referencing deleted agent
|
||
task_response = client.post(
|
||
"/api/v1/tasks",
|
||
json={
|
||
"input_data": {"query": "test"},
|
||
"agent_name": "deletable_skill",
|
||
},
|
||
)
|
||
# Should return 404 since agent was deleted
|
||
assert task_response.status_code == 404
|
||
|
||
def test_health_check_in_flow(self, client):
|
||
"""Health check works during full flow"""
|
||
response = client.get("/api/v1/health")
|
||
assert response.status_code == 200
|
||
data = response.json()
|
||
assert data["status"] in ("ok", "healthy")
|
||
|
||
def test_llm_usage_after_tasks(self, client):
|
||
"""LLM usage stats available after task execution"""
|
||
# Register skill and submit a task
|
||
client.post(
|
||
"/api/v1/skills",
|
||
json={
|
||
"config": {
|
||
"name": "usage_skill",
|
||
"agent_type": "usage_type",
|
||
"task_mode": "llm_generate",
|
||
"prompt": {"identity": "Usage Skill"},
|
||
"intent": {"keywords": ["usage"], "description": "Usage skill"},
|
||
}
|
||
},
|
||
)
|
||
client.post(
|
||
"/api/v1/tasks",
|
||
json={
|
||
"input_data": {"query": "test usage"},
|
||
"skill_name": "usage_skill",
|
||
},
|
||
)
|
||
|
||
# Check usage
|
||
response = client.get("/api/v1/llm/usage")
|
||
assert response.status_code == 200
|