160 lines
5.7 KiB
Python
160 lines
5.7 KiB
Python
#!/usr/bin/env python3
|
|
"""RED-first contract tests for verbatim-hashed normative fragments."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import importlib.util
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
|
|
MODULE_PATH = Path(__file__).parents[2] / "framework/tools/lease-broker/normative_fragments.py"
|
|
|
|
|
|
def shipped_module():
|
|
# Each test reaches the shipped implementation; no test doubles or local
|
|
# reimplementation of construction are allowed on this admission surface.
|
|
assert MODULE_PATH.is_file(), f"shipped construction module is missing: {MODULE_PATH}"
|
|
spec = importlib.util.spec_from_file_location("normative_fragments", MODULE_PATH)
|
|
if spec is None or spec.loader is None:
|
|
raise RuntimeError("unable to load normative fragment construction")
|
|
module = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(module)
|
|
return module
|
|
|
|
|
|
def fragment(module, source_id: str, content: bytes):
|
|
return module.NormativeFragment(
|
|
source_id=source_id,
|
|
content=content,
|
|
expected_sha256=hashlib.sha256(content).hexdigest(),
|
|
)
|
|
|
|
|
|
def valid_fragments(module):
|
|
return [
|
|
fragment(module, "authority/constitution", b"Constitution\n"),
|
|
fragment(module, "authority/runtime", b"Runtime\n"),
|
|
]
|
|
|
|
|
|
class NormativeFragmentsTest(unittest.TestCase):
|
|
def test_t24_claude_and_pi_builders_are_byte_identical_and_one_way(self) -> None:
|
|
module = shipped_module()
|
|
fragments = valid_fragments(module)
|
|
|
|
claude = module.build_for_claude(
|
|
manifest_version=1,
|
|
generator_version="wi4-test",
|
|
fragments=fragments,
|
|
)
|
|
pi = module.build_for_pi(
|
|
manifest_version=1,
|
|
generator_version="wi4-test",
|
|
fragments=fragments,
|
|
)
|
|
|
|
self.assertEqual(claude.injectionDecision, "ACCEPTED")
|
|
self.assertTrue(claude.promotion)
|
|
self.assertEqual(claude.b_payload, pi.b_payload)
|
|
self.assertEqual(claude.h_payload, pi.h_payload)
|
|
self.assertEqual(
|
|
claude.h_payload,
|
|
hashlib.sha256(module.HASH_DOMAIN_SEPARATOR + module.length_frame([claude.b_payload])).hexdigest(),
|
|
)
|
|
self.assertNotIn(b"h_payload", claude.b_payload)
|
|
|
|
mutated_fragment = module.build_for_claude(
|
|
manifest_version=1,
|
|
generator_version="wi4-test",
|
|
fragments=[
|
|
fragment(module, "authority/constitution", b"Constitution changed\n"),
|
|
fragment(module, "authority/runtime", b"Runtime\n"),
|
|
],
|
|
)
|
|
mutated_metadata = module.build_for_claude(
|
|
manifest_version=2,
|
|
generator_version="wi4-test",
|
|
fragments=fragments,
|
|
)
|
|
self.assertNotEqual(claude.h_payload, mutated_fragment.h_payload)
|
|
self.assertNotEqual(claude.h_payload, mutated_metadata.h_payload)
|
|
|
|
def test_length_framing_and_domain_separation_prevent_ambiguous_construction(self) -> None:
|
|
module = shipped_module()
|
|
left = module.length_frame([b"ab", b"c"])
|
|
right = module.length_frame([b"a", b"bc"])
|
|
|
|
self.assertNotEqual(left, right)
|
|
self.assertNotEqual(
|
|
hashlib.sha256(module.HASH_DOMAIN_SEPARATOR + left).hexdigest(),
|
|
hashlib.sha256(module.HASH_DOMAIN_SEPARATOR + right).hexdigest(),
|
|
)
|
|
self.assertNotEqual(
|
|
hashlib.sha256(module.HASH_DOMAIN_SEPARATOR + left).hexdigest(),
|
|
hashlib.sha256(b"other-context\x00" + left).hexdigest(),
|
|
)
|
|
|
|
def test_source_invalidation_missing_refuses_real_construction_and_promotion(self) -> None:
|
|
module = shipped_module()
|
|
missing = module.NormativeFragment(
|
|
source_id="authority/missing",
|
|
content=None,
|
|
expected_sha256=hashlib.sha256(b"missing").hexdigest(),
|
|
)
|
|
|
|
result = module.build_for_claude(
|
|
manifest_version=1,
|
|
generator_version="wi4-test",
|
|
fragments=[missing],
|
|
)
|
|
|
|
self.assertEqual(result.injectionDecision, "REFUSED")
|
|
self.assertFalse(result.promotion)
|
|
self.assertEqual(result.source_reason, "missing")
|
|
self.assertIsNone(result.b_payload)
|
|
self.assertIsNone(result.h_payload)
|
|
|
|
def test_source_invalidation_oversize_refuses_real_construction_and_promotion(self) -> None:
|
|
module = shipped_module()
|
|
content = b"x" * (module.MAX_FRAGMENT_BYTES + 1)
|
|
oversize = fragment(module, "authority/oversize", content)
|
|
|
|
result = module.build_for_pi(
|
|
manifest_version=1,
|
|
generator_version="wi4-test",
|
|
fragments=[oversize],
|
|
)
|
|
|
|
self.assertEqual(result.injectionDecision, "REFUSED")
|
|
self.assertFalse(result.promotion)
|
|
self.assertEqual(result.source_reason, "oversize")
|
|
self.assertIsNone(result.b_payload)
|
|
self.assertIsNone(result.h_payload)
|
|
|
|
def test_source_invalidation_hash_mismatch_refuses_real_construction_and_promotion(self) -> None:
|
|
module = shipped_module()
|
|
mismatch = module.NormativeFragment(
|
|
source_id="authority/hash-mismatch",
|
|
content=b"trusted bytes",
|
|
expected_sha256=hashlib.sha256(b"different bytes").hexdigest(),
|
|
)
|
|
|
|
result = module.build_for_claude(
|
|
manifest_version=1,
|
|
generator_version="wi4-test",
|
|
fragments=[mismatch],
|
|
)
|
|
|
|
self.assertEqual(result.injectionDecision, "REFUSED")
|
|
self.assertFalse(result.promotion)
|
|
self.assertEqual(result.source_reason, "hash-mismatch")
|
|
self.assertIsNone(result.b_payload)
|
|
self.assertIsNone(result.h_payload)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|