auto-sync: 2026-06-05 11:03:30
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user