import copy import importlib.util import json from pathlib import Path import unittest from datetime import datetime, timedelta, timezone from user_engine.custody import CustodyBaseline, CustodyObservationAdapter, CustodyRead CANON = Path(__file__).resolve().parents[2] / "net-kingdom/tools/custody-record" from user_engine.custody_canon import policy class Baselines: def __init__(self): self.value = None self.conflict = False def load(self): return copy.deepcopy(self.value) def compare_and_store(self, previous, current): if self.conflict or previous != self.value: return False self.value = copy.deepcopy(current) return True @unittest.skipUnless(CANON.exists(), "optional sibling NetKingdom fixtures absent") class CustodyAdapterTests(unittest.TestCase): def setUp(self): self.document = json.loads((CANON / "fixtures/valid/grant.json").read_text()) self.now = datetime(2026, 10, 1, 10, tzinfo=timezone.utc) self.read_at = self.now self.source = "fixture-authenticated-owner" self.baselines = Baselines() self.reads = 0 self.failure = False self.adapter = CustodyObservationAdapter( reader=self, baselines=self.baselines, policy=policy, source_id=self.source, record_id=self.document["record_id"], allow_initial_baseline=True, ) self.subject = dict(self.document["entries"][0]["subject"]) def read(self): self.reads += 1 if self.failure: raise RuntimeError("sensitive error must never escape") return CustodyRead(self.source, self.document, self.read_at) def observe(self, **overrides): return self.adapter.observe(**{**self.subject, "tenant": "platform", "now": self.now, **overrides}) def test_exact_mapping_and_fresh_read_each_time(self): result = self.observe() self.assertEqual(result["subject"], self.subject) self.assertEqual(result["source"], {"record_id": self.document["record_id"], "revision": 1}) result["subject"]["subject"] = "changed" self.assertEqual(self.observe()["subject"], self.subject) self.assertEqual(self.reads, 2) self.assertIsNone(self.observe(issuer=self.subject["issuer"] + "/")) self.assertIsNone(self.observe(subject="unknown")) self.assertIsNone(self.observe(tenant="other")) def test_failure_and_stale_read_never_refresh_old_grant(self): self.assertIsNotNone(self.observe()) baseline = self.baselines.load() self.failure = True self.assertIsNone(self.observe()) self.failure = False self.now += timedelta(seconds=901) self.assertIsNone(self.observe()) self.assertEqual(self.baselines.load(), baseline) def test_revocation_retained_and_rollback_denied_across_restart(self): grant = copy.deepcopy(self.document) self.assertIsNotNone(self.observe()) self.document = json.loads((CANON / "fixtures/valid/revocation.json").read_text()) self.now = self.read_at = datetime(2026, 10, 3, 12, tzinfo=timezone.utc) self.assertIsNone(self.observe()) self.assertEqual(self.baselines.value.document["revision"], 2) self.adapter = CustodyObservationAdapter(reader=self, baselines=self.baselines, policy=policy, source_id=self.source, record_id=grant["record_id"]) self.document = grant self.assertIsNone(self.observe()) self.assertEqual(self.baselines.value.document["revision"], 2) def test_same_revision_mutation_and_source_change_denied(self): self.assertIsNotNone(self.observe()) self.document["entries"][0]["granted"]["reason"] = "rewritten" self.assertIsNone(self.observe()) self.source = "other-owner" self.assertIsNone(self.observe()) def test_invalid_projection_and_future_clock_denied(self): self.now -= timedelta(seconds=1) self.assertIsNone(self.observe()) self.now = self.read_at self.document = json.loads((CANON / "fixtures/invalid/projection-only-grant.json").read_text()) self.assertIsNone(self.observe()) self.assertIsNone(self.baselines.value) def test_check_clock_sampled_after_owner_read(self): self.adapter.clock = lambda: self.now + timedelta(seconds=2) self.read_at = self.now + timedelta(seconds=1) result = self.adapter.observe(**self.subject, tenant="platform") self.assertIsNotNone(result) self.assertEqual(result["observed_at"], self.read_at.isoformat().replace("+00:00", "Z")) def test_bootstrap_and_persistence_conflict_deny(self): self.adapter.allow_initial_baseline = False self.assertIsNone(self.observe()) self.assertEqual(self.reads, 0) self.adapter.allow_initial_baseline = True self.baselines.conflict = True self.assertIsNone(self.observe()) def test_empty_and_supersession_revisions_are_retained(self): self.document["entries"] = [] self.assertIsNone(self.observe()) self.assertEqual(self.baselines.value.document["revision"], 1) # Separate approved bootstrap to the grant fixture, then handover. self.baselines.value = None self.document = json.loads((CANON / "fixtures/valid/grant.json").read_text()) self.assertIsNotNone(self.observe()) self.document = json.loads((CANON / "fixtures/valid/supersession.json").read_text()) self.now = self.read_at = datetime(2026, 10, 3, 12, tzinfo=timezone.utc) self.assertIsNone(self.observe()) successor = self.document["entries"][1]["subject"] self.assertIsNotNone(self.observe(**successor)) self.assertEqual(self.baselines.value.document["revision"], 2)