"""Golden-case conformance + anti-leak for the PUBLIC wire view. The PUBLIC case the client receives must (a) match the prototype shape and (b) never carry the sealed solution or any per-question/per-evidence suspicion delta (the deltas alone would reveal the killer). """ from __future__ import annotations import json import pytest from case_zero.api.public_view import PublicCase, golden_to_public from case_zero.persistence.golden import load_golden GOLDEN_ID = "GRAYMOOR-3107" # Quoted JSON keys (not bare substrings): "iris"/"kill" appear in public prose, the KEYS must not. SEALED_KEY_TOKENS = ('"sealed"', '"killer"', '"correctMotive"', '"keyEvidence"', '"present"', '"default"') @pytest.fixture(scope="module") def public_dump() -> tuple[PublicCase, dict]: pub = golden_to_public(load_golden(GOLDEN_ID)) return pub, pub.model_dump(by_alias=True) def test_golden_loads_and_validates(public_dump: tuple[PublicCase, dict]) -> None: pub, _ = public_dump assert isinstance(pub, PublicCase) assert pub.id == GOLDEN_ID assert len(pub.suspects) == 4 assert len(pub.evidence) == 6 assert len(pub.motives) == 4 assert len(pub.timeline) == 9 assert pub.flashback.title def test_camel_case_wire_keys(public_dump: tuple[PublicCase, dict]) -> None: _, d = public_dump assert "bootLines" in d s0 = d["suspects"][0] assert "baselineSuspicion" in s0 assert "suggestedQuestions" in s0 thread_ev = next(e for e in d["evidence"] if e["type"] == "PHONE") assert thread_ev["thread"][0]["from"] in ("me", "them") # JS "from" alias preserved def test_no_sealed_fields_leak(public_dump: tuple[PublicCase, dict]) -> None: _, d = public_dump blob = json.dumps(d) for token in SEALED_KEY_TOKENS: assert token not in blob, f"sealed token leaked into public view: {token}" def test_suggested_questions_expose_only_text(public_dump: tuple[PublicCase, dict]) -> None: _, d = public_dump for s in d["suspects"]: for q in s["suggestedQuestions"]: assert set(q.keys()) == {"id", "q"}, "scripted answer/delta must not be exposed" def test_no_suspicion_delta_survives(public_dump: tuple[PublicCase, dict]) -> None: # The sealed golden carries "d": on each scripted answer (Iris/keycard is +26). # No "d" key exists in the public model, so none must appear in the serialized output. _, d = public_dump assert '"d":' not in json.dumps(d)