Prepare custody observation adapter pending authenticated owner delivery
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Container Image / build-and-push (push) Successful in 55s
Account journey acceptance / journeys (push) Successful in 11s

Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a1029d-2c93-7500-a0e0-e3e089668b29
This commit is contained in:
tegwick 2026-10-03 18:58:57 +02:00
parent 22eef7f902
commit c11c290290
3 changed files with 290 additions and 4 deletions

View file

@ -0,0 +1,130 @@
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"
try:
spec = importlib.util.spec_from_file_location("custody_canon", CANON / "validate.py")
policy = importlib.util.module_from_spec(spec)
spec.loader.exec_module(policy)
except (FileNotFoundError, ModuleNotFoundError):
policy = None
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.skipIf(policy is None, "optional sibling NetKingdom validator/jsonschema 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_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)