This repository has been archived on 2026-08-04. You can view files and clone it, but cannot push or open issues or pull requests.
orchestrator/agent-team/tests/test_dashboard.py
Adam Moussa e3137e33e6 test(agent-team): cover transitions, topology, dashboard API + rework status_page tests
Add test_transitions.py (recorder idempotency under replay, terminal close,
fail-soft, the B4 _instrument signature-preservation guarantee, end-to-end graph
drive), test_topology.py (meta coverage, tree grouping, edge classification,
phase->node map), and test_dashboard.py (/api/state contract + per-node state,
/api/topology, /api/task timeline+cost+partial+thread_id validation, all via
TestClient, fastapi-skipif guarded). Rework test_status_page.py to the data layer
(drop retired render_html/SVG tests). conftest exposes tests/ for sibling imports.
Frontend: Vitest+RTL for layout, TaskList filters/selection, TaskDrawer timeline.
2026-06-23 17:15:31 -04:00

118 lines
4.1 KiB
Python

"""Tests for the read-only dashboard FastAPI app (``agent_team.dashboard``).
Exercises the three JSON endpoints against a seeded temp ledger via FastAPI's
TestClient: /api/state (contract preserved + per-node live state), /api/topology,
and /api/task/{id} (timeline assembly, thread_id validation, partial fallback,
cost join). Guarded so the suite still runs where FastAPI is absent.
"""
from __future__ import annotations
import importlib.util
from pathlib import Path
import pytest
from agent_team.db import TransitionRecorder, connect, init_db
from agent_team.dashboard import task_detail
# Reuse the seeded-ledger helper from the status-page tests (sibling module,
# importable by bare name under pytest's default prepend import mode).
from test_status_page import seed_ledger
_HAS_FASTAPI = importlib.util.find_spec("fastapi") is not None
pytestmark = pytest.mark.skipif(_HAS_FASTAPI is False, reason="fastapi not installed")
def _client(db: Path):
from fastapi.testclient import TestClient
from agent_team.dashboard import make_dashboard_app
return TestClient(make_dashboard_app(db_path=db))
def _seed_budget(db: Path, thread_id: str, stage: str, usd: float) -> None:
conn = connect(db)
try:
conn.execute(
"INSERT INTO budget_ledger (thread_id, stage, model, billing_mode, "
"usd_cost, recorded_at, day_bucket) VALUES (?,?,?,?,?,?,?)",
(
thread_id,
stage,
"claude",
"subscription",
usd,
"2026-06-23T00:00:00+00:00",
"2026-06-23",
),
)
finally:
conn.close()
def test_api_state_contract_and_nodes(tmp_path: Path) -> None:
db = tmp_path / "agent_team.sqlite"
seed_ledger(db)
r = _client(db).get("/api/state")
assert r.status_code == 200
payload = r.json()
# Existing contract preserved.
assert payload["ok"] is True
assert {"stages", "tasks", "summary", "budget"} <= set(payload)
# New per-node live state, keyed by topology node id.
assert "nodes" in payload
# thread-waiting is at clarify and awaiting human; thread-active at plan.
assert payload["nodes"]["clarify"]["state"] == "awaiting_human"
assert payload["nodes"]["plan"]["state"] == "active"
def test_api_topology(tmp_path: Path) -> None:
db = tmp_path / "agent_team.sqlite"
init_db(db)
payload = _client(db).get("/api/topology").json()
ids = {n["id"] for n in payload["nodes"]}
assert {"intake", "clarify", "plan", "review"} <= ids
assert payload["edges"]
def test_api_task_timeline_and_cost(tmp_path: Path) -> None:
db = tmp_path / "agent_team.sqlite"
seed_ledger(db)
rec = TransitionRecorder(db)
rec.record_entry(thread_id="thread-active", to_phase="intake", status="active")
rec.record_entry(thread_id="thread-active", to_phase="plan", status="active")
_seed_budget(db, "thread-active", "plan", 0.5)
payload = _client(db).get("/api/task/thread-active").json()
assert payload["ok"] is True
assert payload["partial"] is False
steps = {s["to_phase"]: s for s in payload["timeline"]}
assert "intake" in steps and "plan" in steps
assert steps["plan"]["cost_usd"] == pytest.approx(0.5)
assert payload["total_usd"] == pytest.approx(0.5)
def test_api_task_partial_when_no_transitions(tmp_path: Path) -> None:
db = tmp_path / "agent_team.sqlite"
seed_ledger(db) # checkpoints exist, but no task_transitions rows
payload = _client(db).get("/api/task/thread-active").json()
assert payload["ok"] is True
assert payload["partial"] is True
# Best-effort single step reconstructed from the checkpoint phase.
assert payload["timeline"]
assert payload["timeline"][0]["note"]
def test_api_task_rejects_bad_thread_id(tmp_path: Path) -> None:
db = tmp_path / "agent_team.sqlite"
init_db(db)
cli = _client(db)
assert cli.get("/api/task/has space").status_code == 400
assert cli.get("/api/task/" + "x" * 65).status_code == 400
def test_task_detail_missing_ledger_is_failsafe(tmp_path: Path) -> None:
out = task_detail(tmp_path / "nope.sqlite", "abc")
assert out["ok"] is False