Files
6krrt/tests/test_admin_snapshot.py
adlee-was-taken 515fcf5fce fix(admin): decisions tile reports route decisions and client outcomes
Backend:
- metrics.decision_outcome_summary(conn, since_days=7): route_decisions
  count in 7d window + verifications kind='client_outcome' counts
  (total / succeeded / failed), parameterized like verdict_mix.
- admin_snapshot exposes it as "decision_outcomes" (zeros on empty DB).

Frontend (renderBoard Decisions tile):
- One guarded local `dout` (|| defaults) so a stale tab without the
  snapshot key cannot throw and blank the board.
- Headline = decisions in 7 days; qualifier "routed in 7 days".
- Client-outcome line when client_reports > 0:
  "<n> client reports (P%): <ok> ok, <failed> failed" where P is
  guarded against decisions===0 (percent omitted, counts kept) and the
  failed count gets the warn colour when failed/reports > 0.10
  (matching the Signal card's >10% threshold), guarded against
  client_reports===0.
- "no client reports in 7 days" when client_reports===0.
- The word "verified" no longer appears in the tile; renderLive keeps
  verdict_mix untouched.

Tests: unit tests for the summary (window exclusions, client_outcome
scoping, structural verdicts excluded), exhaustive snapshot key +
zeros assertion, top-level key list, and static renderBoard/renderLive
regex locks. Append-only.
2026-09-26 03:03:30 -04:00

201 lines
6.9 KiB
Python

"""Tests for GET /admin/api/snapshot: the full top-level key contract.
The snapshot is the admin dashboard's main data payload. This file locks the
complete key set (not just a hand-picked subset) so a dropped or renamed key
fails the suite instead of silently missing from the UI. It drives a real
TestClient against a seeded temp DB, mirroring ``test_admin_health.py``.
"""
from __future__ import annotations
import sqlite3
from datetime import datetime, timedelta, timezone
from pathlib import Path
import pytest
from starlette.testclient import TestClient
import dispatcher
from config import load_config
ROOT = Path(__file__).resolve().parent.parent
SCHEMA_SQL = (ROOT / "config" / "schema.sql").read_text()
CFG = load_config(str(ROOT / "config" / "config.yaml"))
EXHAUSTIVE_KEYS = (
"quota",
"coverage",
"recent_decisions",
"per_model",
"verdict_mix",
"decision_outcomes",
"top_proficiency",
"local_energy",
"health",
"pinch",
"generated_at",
)
def _now() -> datetime:
return datetime.now(timezone.utc)
def _make_db(tmp_path: Path) -> sqlite3.Connection:
conn = sqlite3.connect(str(tmp_path / "test.db"))
conn.row_factory = sqlite3.Row
conn.executescript(SCHEMA_SQL)
return conn
def _seed_models(conn: sqlite3.Connection) -> None:
for model_id, tier, context, cost, vision in (
("cheap", 2, 262128, 0.30, 1),
("dear", 2, 262128, 9.00, 0),
):
conn.execute(
"""
INSERT INTO models (
model_id, provider, base_model_id, tier, context_window,
effective_context_window, max_output_tokens,
cost_per_1m_prompt, cost_per_1m_completion,
supports_vision, supports_json_mode,
latency_class, reasoning_mode, context_variant,
access_level, availability, last_updated
) VALUES (?, 'neuralwatt', ?, ?, ?, 192500, 16384, ?, ?,
?, 1, 'standard', 'default', 'full', 'public', 'active',
'2026-08-22T00:00:00+00:00')
""",
(model_id, model_id, tier, context, cost, cost / 3, vision),
)
conn.commit()
def _seed_decision(conn: sqlite3.Connection) -> None:
conn.execute(
"""
INSERT INTO route_decisions (
observed_at, kind, task_category, task_tier, required_context_tokens,
confidence, classifier_ms, classification_source, latency_tolerance,
candidates_considered, selected_model, selected_provider,
runner_up_models, est_cost_usd, est_proficiency,
session_key, tools, images, json_mode, streamed,
flex_preference, flex_swapped, flex_forced
) VALUES (?, 'route', 'coding_general', 2, 100, 0.95, 200,
'classifier', 'interactive', 5, 'cheap', 'neuralwatt',
'[{"model_id":"dear","provider":"neuralwatt"}]',
0.001, 0.9, 'abc123', 0, 0, 0, 0,
'auto', 0, 1)
""",
(_now().isoformat(),),
)
conn.commit()
def _seed_energy(conn: sqlite3.Connection) -> None:
conn.execute(
"INSERT INTO energy_observations "
"(model_id, provider, task_category, completion_tokens, energy_kwh, "
"cost_usd, carbon_g_co2eq, attribution_ratio, observed_at) "
"VALUES ('cheap', 'neuralwatt', 'coding_general', 100, 5.0e-05, 0.001, "
"2.4e-03, 0.25, ?)",
((_now() - timedelta(days=2)).isoformat(),),
)
conn.commit()
def _seed_verification(conn: sqlite3.Connection) -> None:
conn.execute(
"INSERT INTO verifications (model_id, provider, kind, verdict, observed_at) "
"VALUES ('cheap', 'neuralwatt', 'structural', 'ok', ?)",
(_now().isoformat(),),
)
conn.commit()
def _seed_proficiency(conn: sqlite3.Connection) -> None:
conn.execute(
"INSERT INTO proficiency (model_id, provider, category, blended_score, "
"source, last_updated) "
"VALUES ('cheap', 'neuralwatt', 'coding_general', 0.9, "
"'self_eval_thin', '2026-01-01T00:00:00+00:00')",
)
conn.commit()
def _seed_local_energy(conn: sqlite3.Connection) -> None:
conn.execute(
"INSERT INTO local_energy_observations "
"(observed_at, model_id, call_type, avg_power_watts, duration_seconds, "
"energy_kwh, cost_usd, carbon_g_co2eq, meter) "
"VALUES (?, 'mistral-nemo:12b', 'classify', 150.0, 1.0, "
"0.00015, 1.8e-05, 0.06, 'nvidia-smi')",
(_now().isoformat(),),
)
conn.commit()
@pytest.fixture
def seeded_client(tmp_path, monkeypatch):
conn = _make_db(tmp_path)
_seed_models(conn)
for _ in range(3):
_seed_decision(conn)
_seed_energy(conn)
_seed_verification(conn)
_seed_proficiency(conn)
_seed_local_energy(conn)
conn.close()
monkeypatch.setattr(dispatcher.cfg.database, "path", str(tmp_path / "test.db"))
monkeypatch.setattr(dispatcher.cfg.verification, "local_llm_enabled", False)
monkeypatch.setattr(dispatcher.cfg.routing, "require_vision", False)
monkeypatch.setattr(dispatcher.cfg.local_energy, "enabled", False)
monkeypatch.setenv("NEURALWATT_API_KEY", "test-key")
with TestClient(dispatcher.app) as client:
yield client
def test_admin_snapshot_returns_exhaustive_top_level_keys(seeded_client):
"""Every documented top-level snapshot key is present in the response."""
resp = seeded_client.get("/admin/api/snapshot")
assert resp.status_code == 200
data = resp.json()
for key in EXHAUSTIVE_KEYS:
assert key in data, f"missing top-level snapshot key {key!r}"
assert data["local_energy"] is None
def test_admin_snapshot_generated_at_is_iso(seeded_client):
"""generated_at is an ISO-8601 timestamp and freshly generated."""
data = seeded_client.get("/admin/api/snapshot").json()
generated = datetime.fromisoformat(data["generated_at"])
assert generated.tzinfo is not None
delta = abs((_now() - generated).total_seconds())
assert delta < 60, "snapshot generated_at is stale"
def test_admin_snapshot_empty_db_still_returns_all_keys(tmp_path, monkeypatch):
"""An unseeded DB returns the same key set, with empty collections."""
conn = _make_db(tmp_path)
conn.close()
monkeypatch.setattr(dispatcher.cfg.database, "path", str(tmp_path / "test.db"))
monkeypatch.setattr(dispatcher.cfg.verification, "local_llm_enabled", False)
monkeypatch.setattr(dispatcher.cfg.routing, "require_vision", False)
monkeypatch.setenv("NEURALWATT_API_KEY", "test-key")
with TestClient(dispatcher.app) as client:
data = client.get("/admin/api/snapshot").json()
for key in EXHAUSTIVE_KEYS:
assert key in data, f"empty-DB snapshot missing key {key!r}"
assert data["per_model"] == []
assert data["recent_decisions"] == []
assert data["top_proficiency"] == []
assert data["decision_outcomes"] == {
"decisions": 0,
"client_reports": 0,
"client_ok": 0,
"client_failed": 0,
}