import json import unittest from pathlib import Path CANON = Path(__file__).parents[1] / "projects" / "slop_cannon" / "canon.json" class CanonLifecycleTests(unittest.TestCase): @classmethod def setUpClass(cls): cls.data = json.loads(CANON.read_text()) def test_forms_have_lifecycle_state(self): """Every form must have a state field (candidate/survivor/archive_ghost).""" for form in self.data["forms"]: self.assertIn("state", form, f"Form {form.get('id', '?')} missing lifecycle state") self.assertIn(form["state"], ["candidate", "survivor", "archive_ghost"]) def test_audience_choice_schema_defined(self): """Schema must define how a choice is structured so Episode 04 can parse it.""" schema = self.data.get("audience_choice_schema", {}) required = ["survivor_id", "stolen_trait", "source_episode", "source_issue"] for field in required: self.assertIn(field, schema, f"audience_choice_schema missing {field}") def test_receipt_schema_defined(self): """Receipts need provenance fields for path-proof binding.""" schema = self.data.get("receipt_schema", {}) required = ["episode_id", "form_id", "provenance", "signed_by"] for field in required: self.assertIn(field, schema, f"receipt_schema missing {field}") def test_receipt_schema_requires_agent_signing(self): """Signed_by must constrain to known actors — free text defeats path proofs.""" signed = self.data["receipt_schema"]["signed_by"] self.assertIn("agent:", signed, "signed_by should mention agent: prefix") def test_law_enforced_by_data(self): """Law 'Every firing leaves a power, scar, cost, and receipt' requires receipt_schema to exist.""" laws_text = " ".join(self.data["laws"]).lower() self.assertIn("receipt", laws_text) self.assertIn("receipt_schema", self.data, "Law mentions receipt but no schema enforces it") if __name__ == "__main__": unittest.main()