203 lines
7.1 KiB
Python
203 lines
7.1 KiB
Python
"""F5 测试:API 层"""
|
||
|
||
import json
|
||
import os
|
||
from pathlib import Path
|
||
|
||
import pytest
|
||
from fastapi.testclient import TestClient
|
||
|
||
from src.blackboard.operations import Blackboard
|
||
from src.blackboard.models import Task
|
||
from src.blackboard.registry import ProjectRegistry
|
||
from src.main import app
|
||
|
||
pytestmark = pytest.mark.integration
|
||
|
||
|
||
@pytest.fixture
|
||
def project_env(tmp_path):
|
||
"""创建临时项目环境"""
|
||
project_root = tmp_path / "projects"
|
||
project_root.mkdir()
|
||
os.environ["BLACKBOARD_ROOT"] = str(project_root)
|
||
|
||
# Create a test project with registry + DB
|
||
reg = ProjectRegistry(project_root)
|
||
reg.create_project("test-proj", "Test Project", agents=["agent1"])
|
||
|
||
bb = Blackboard(project_root / "test-proj" / "blackboard.db")
|
||
bb.create_task(Task(id="t1", title="Existing Task", task_type="coding"))
|
||
|
||
yield project_root
|
||
del os.environ["BLACKBOARD_ROOT"]
|
||
|
||
|
||
@pytest.fixture
|
||
def client():
|
||
return TestClient(app)
|
||
|
||
|
||
# ===================================================================
|
||
# Daemon API
|
||
# ===================================================================
|
||
|
||
class TestDaemonAPI:
|
||
def test_status(self, client):
|
||
resp = client.get("/api/daemon/status")
|
||
assert resp.status_code == 200
|
||
assert resp.json()["status"] == "running"
|
||
|
||
def test_manual_tick(self, client):
|
||
resp = client.post("/api/daemon/tick")
|
||
assert resp.status_code == 200
|
||
|
||
|
||
# ===================================================================
|
||
# Project API
|
||
# ===================================================================
|
||
|
||
class TestProjectAPI:
|
||
def test_list_projects(self, client, project_env):
|
||
resp = client.get("/api/projects")
|
||
assert resp.status_code == 200
|
||
assert "test-proj" in resp.json()["projects"]
|
||
|
||
def test_create_project(self, client, tmp_path):
|
||
root = tmp_path / "new_root"
|
||
root.mkdir()
|
||
os.environ["BLACKBOARD_ROOT"] = str(root)
|
||
try:
|
||
resp = client.post("/api/projects", json={
|
||
"id": "new-proj", "name": "New",
|
||
"agents": ["a1"], "description": "test",
|
||
})
|
||
assert resp.status_code == 200
|
||
assert resp.json()["ok"]
|
||
finally:
|
||
del os.environ["BLACKBOARD_ROOT"]
|
||
|
||
def test_create_duplicate(self, client, project_env):
|
||
resp = client.post("/api/projects", json={
|
||
"id": "test-proj", "name": "Dup",
|
||
})
|
||
assert resp.status_code == 409
|
||
|
||
def test_get_project(self, client, project_env):
|
||
resp = client.get("/api/projects/test-proj")
|
||
assert resp.status_code == 200
|
||
assert resp.json()["name"] == "Test Project"
|
||
|
||
def test_get_nonexistent(self, client, project_env):
|
||
resp = client.get("/api/projects/nope")
|
||
assert resp.status_code == 404
|
||
|
||
|
||
# ===================================================================
|
||
# Blackboard API
|
||
# ===================================================================
|
||
|
||
class TestBlackboardAPI:
|
||
def test_list_tasks(self, client, project_env):
|
||
resp = client.get("/api/projects/test-proj/tasks")
|
||
assert resp.status_code == 200
|
||
assert len(resp.json()["tasks"]) == 1
|
||
|
||
def test_get_task(self, client, project_env):
|
||
resp = client.get("/api/projects/test-proj/tasks/t1")
|
||
assert resp.status_code == 200
|
||
assert resp.json()["title"] == "Existing Task"
|
||
|
||
def test_get_task_404(self, client, project_env):
|
||
resp = client.get("/api/projects/test-proj/tasks/nope")
|
||
assert resp.status_code == 404
|
||
|
||
def test_create_task(self, client, project_env):
|
||
resp = client.post("/api/projects/test-proj/tasks", json={
|
||
"id": "t2", "title": "New Task", "task_type": "review",
|
||
})
|
||
assert resp.status_code == 200
|
||
assert resp.json()["task_id"] == "t2"
|
||
|
||
def test_claim_task(self, client, project_env):
|
||
resp = client.post("/api/projects/test-proj/tasks/t1/claim",
|
||
json={"agent": "agent1"})
|
||
assert resp.status_code == 200
|
||
|
||
def test_update_status(self, client, project_env):
|
||
# Claim first
|
||
client.post("/api/projects/test-proj/tasks/t1/claim",
|
||
json={"agent": "agent1"})
|
||
resp = client.post("/api/projects/test-proj/tasks/t1/status",
|
||
json={"status": "working", "agent": "agent1"})
|
||
assert resp.status_code == 200
|
||
|
||
def test_invalid_status_transition(self, client, project_env):
|
||
resp = client.post("/api/projects/test-proj/tasks/t1/status",
|
||
json={"status": "done"})
|
||
assert resp.status_code == 409
|
||
|
||
def test_add_comment(self, client, project_env):
|
||
resp = client.post("/api/projects/test-proj/tasks/t1/comments", json={
|
||
"author": "pangtong", "body": "Nice", "comment_type": "general",
|
||
"mentions": ["agent1"],
|
||
})
|
||
assert resp.status_code == 200
|
||
|
||
def test_get_comments(self, client, project_env):
|
||
client.post("/api/projects/test-proj/tasks/t1/comments", json={
|
||
"author": "a", "body": "Hello",
|
||
})
|
||
resp = client.get("/api/projects/test-proj/tasks/t1/comments")
|
||
assert resp.status_code == 200
|
||
assert len(resp.json()["comments"]) == 1
|
||
|
||
def test_write_output(self, client, project_env):
|
||
resp = client.post("/api/projects/test-proj/tasks/t1/outputs", json={
|
||
"agent": "agent1", "type": "code", "title": "main.py",
|
||
})
|
||
assert resp.status_code == 200
|
||
|
||
def test_add_decision(self, client, project_env):
|
||
resp = client.post("/api/projects/test-proj/tasks/t1/decisions", json={
|
||
"decider": "pangtong", "decision": "Use X",
|
||
"rationale": "Better",
|
||
})
|
||
assert resp.status_code == 200
|
||
|
||
def test_add_observation(self, client, project_env):
|
||
resp = client.post("/api/projects/test-proj/tasks/t1/observations", json={
|
||
"observer": "simayi", "body": "Warning!", "severity": "warning",
|
||
})
|
||
assert resp.status_code == 200
|
||
|
||
def test_add_review(self, client, project_env):
|
||
resp = client.post("/api/projects/test-proj/tasks/t1/reviews", json={
|
||
"id": "rev-1", "reviewer": "simayi",
|
||
"review_type": "output_review", "verdict": "approved",
|
||
"summary": "LGTM", "confidence": 0.9,
|
||
})
|
||
assert resp.status_code == 200
|
||
|
||
def test_get_events(self, client, project_env):
|
||
resp = client.get("/api/projects/test-proj/events")
|
||
assert resp.status_code == 200
|
||
assert "events" in resp.json()
|
||
|
||
def test_summary(self, client, project_env):
|
||
resp = client.get("/api/projects/test-proj/summary")
|
||
assert resp.status_code == 200
|
||
assert "pending" in resp.json()["summary"]
|
||
|
||
|
||
# ===================================================================
|
||
# SSE
|
||
# ===================================================================
|
||
|
||
class TestSSE:
|
||
def test_sse_endpoint_registered(self):
|
||
"""SSE 路由已注册(详细测试见 test_sse.py)"""
|
||
from src.main import app
|
||
routes = [r.path for r in app.routes]
|
||
assert "/api/events" in routes
|