import json from crisisops.planner import ADMMPlanner from crisisops.core import GraphOfContracts, Object, Morphism from crisisops.governance import GovernanceLedger from crisisops.replay import graph_snapshot, restore_graph def build_simple_goC(): a = Object(id="a1", type="resource", attributes={"qty": 10}) b = Object(id="b1", type="node", attributes={"cap": 5}) m = Morphism(id="m1", source_id=a.id, target_id=b.id, type="dispatch", signals={}) return GraphOfContracts(objects={a.id: a, b.id: b}, morphisms={m.id: m}) def test_planner_allocates_dispatch_actions(): gof = build_simple_goC() planner = ADMMPlanner() plan = planner.optimize(gof) assert isinstance(plan.actions, list) assert plan.actions[0]["action"] == "dispatch" assert plan.actions[0]["details"]["utility_score"] > 0 def test_graph_snapshot_is_deterministic_and_restorable(): gof_a = build_simple_goC() gof_b = GraphOfContracts( objects={"b1": gof_a.objects["b1"], "a1": gof_a.objects["a1"]}, morphisms={"m1": gof_a.morphisms["m1"]}, ) snapshot_a = graph_snapshot(gof_a) snapshot_b = graph_snapshot(gof_b) assert snapshot_a["snapshot_hash"] == snapshot_b["snapshot_hash"] restored = restore_graph(snapshot_a) assert restored.objects["a1"].attributes["qty"] == 10 assert restored.morphisms["m1"].target_id == "b1" def test_snapshot_hash_tracks_plan_changes(): gof = build_simple_goC() planner = ADMMPlanner() plan = planner.optimize(gof) snapshot = graph_snapshot(gof, plan=plan) assert snapshot["plan_delta"]["actions"][0]["details"]["utility_score"] > 0 assert snapshot["snapshot_hash"] def test_governance_ledger_canonicalizes_structured_details(): ledger = GovernanceLedger() ledger.log_plan(actor="planner-1", status="approved", plan_hash="abc123", details={"agency": "ngo-a"}) record = ledger.list_logs()[0] assert json.loads(record["details"]) == {"agency": "ngo-a", "plan_hash": "abc123"}