Spaces:
Running
Running
Case Zero - initial public release (fully local: Qwen2.5-1.5B via llama.cpp + Supertonic, custom pixel-noir SPA via gradio.Server)
414dc55 | """End-to-end run flow over the scripted golden engine: interrogate -> suspicion moves, | |
| present the breaking exhibit -> the alibi cracks (without a confession), accuse -> graded | |
| verdict. Plus anti-leak on the live (pre-verdict) endpoints. | |
| """ | |
| from __future__ import annotations | |
| from starlette.testclient import TestClient | |
| from case_zero.api import build_server | |
| def _new_run() -> tuple[TestClient, str]: | |
| c = TestClient(build_server()) | |
| r = c.post("/api/case", json={"caseId": "GRAYMOOR-3107"}) | |
| return c, r.json()["runId"] | |
| def test_interrogate_moves_suspicion() -> None: | |
| c, run = _new_run() | |
| r = c.post(f"/api/run/{run}/interrogate/iris", json={"questionId": "q3"}) | |
| assert r.status_code == 200 | |
| body = r.json() | |
| assert body["reply"] | |
| assert body["suspicionDelta"] == 18 | |
| assert body["suspicion"] == 25 + 18 # iris baseline 25 + q3 delta | |
| def test_breaking_evidence_cracks_without_confession() -> None: | |
| c, run = _new_run() | |
| r = c.post(f"/api/run/{run}/interrogate/iris", json={"presentEvidenceId": "keycard"}).json() | |
| assert r["flags"]["rattled"] and r["flags"]["cornered"] # +26 is the strongest tell | |
| assert r["suspicion"] == 25 + 26 | |
| low = r["reply"].lower() | |
| # cracks, but never confesses | |
| assert "i did it" not in low | |
| assert "i killed" not in low | |
| def test_accuse_correct_is_case_closed() -> None: | |
| c, run = _new_run() | |
| v = c.post( | |
| f"/api/run/{run}/accuse", | |
| json={"suspectId": "iris", "motiveId": "m_credit", "evidenceIds": ["keycard", "voicemail", "cctv"]}, | |
| ).json() | |
| assert v["correct"] is True | |
| assert v["verdict"]["stamp"] == "CASE CLOSED" | |
| assert v["verdict"]["killerName"] == "IRIS CALLOWAY" | |
| assert v["score"]["points"] == 100 | |
| def test_accuse_wrong_is_mistrial() -> None: | |
| c, run = _new_run() | |
| v = c.post( | |
| f"/api/run/{run}/accuse", | |
| json={"suspectId": "wexler", "motiveId": "m_money", "evidenceIds": ["receipt"]}, | |
| ).json() | |
| assert v["correct"] is False | |
| assert v["verdict"]["stamp"] == "MISTRIAL" | |
| assert v["score"]["points"] < 100 | |
| def test_live_endpoints_do_not_leak_sealed() -> None: | |
| c, run = _new_run() | |
| turn = c.post(f"/api/run/{run}/interrogate/iris", json={"questionId": "q0"}).text | |
| hint = c.get(f"/api/run/{run}/hint", params={"screen": "interro"}).text | |
| for blob in (turn, hint): | |
| for token in ('"killer"', '"correctMotive"', '"keyEvidence"', '"d":'): | |
| assert token not in blob | |
| def test_hint_is_screen_aware_and_spoiler_safe() -> None: | |
| c, run = _new_run() | |
| h = c.get(f"/api/run/{run}/hint", params={"screen": "accuse"}).json()["hint"] | |
| assert h | |
| assert "iris" not in h.lower() # never names the killer | |
| def test_unknown_run_404() -> None: | |
| c = TestClient(build_server()) | |
| assert c.post("/api/run/nope/interrogate/iris", json={"questionId": "q0"}).status_code == 404 | |