approval-engine/tests/test_examples.py
tegwick 7e756773de Implement GH-DEC-2026-008: declared PDP-path intent, enforced at issue
Gate House ruled binding.pdp_digest is the binding correspondence on the
GH-DEC-2026-003 path and is required there, having rejected a vocabulary
mapping for the reasons we gave. It asked this engine to record the PDP
digest at issue for approvals intended for that path, and to have the
claim state which approvals those are rather than leaving it to the
requester's memory.

Schema v3 adds approvals.pdp_path. create() refuses pdp_path true without
a pdp_digest, so an approval that would be unusable on the path fails at
issue rather than at the protected side effect. The claim exposes
binding.pdp_path, which makes it a guarantee rather than a hint: pdp_path
true implies pdp_digest is non-null.

Intent is declared and never inferred. A pdp_digest that happens to be
present is not a declaration anybody made, so a recorded digest alone
leaves pdp_path false, legacy rows migrate to false rather than being
back-filled from their digests, and a successor inherits its
predecessor's declaration. Approvals issued before the ruling stay usable
by consumers in this engine's own vocabulary and are simply not usable on
the PDP path -- the ruling's intended cost, stated as such.

Schema, both published examples, a v2-to-v3 migration test asserting
survivors keep their digest while declaring no path intent, and tests for
refusal at issue, claim exposure, non-inference, and successor
inheritance. 102 tests pass (8 new).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TvyJPAaVCGsVheVhcCwNND

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 411227@bnt-lap001
Assistant-Session: d566f6d3-bcaf-43c3-bc5e-3ddd0f64b535
2026-09-06 14:51:23 +02:00

54 lines
1.9 KiB
Python

"""The published examples must satisfy the published schema.
flex-auth shipped two defects in one day from fixtures that contradicted their
own contracts, and secrets-engine built a validator against one of them. A
contract whose examples contradict its prose will be implemented as its
examples, so the examples are tested rather than trusted.
"""
import json
from pathlib import Path
import pytest
jsonschema = pytest.importorskip("jsonschema")
ROOT = Path(__file__).resolve().parent.parent
SCHEMA = json.loads((ROOT / "schemas" / "approval_claim.schema.json").read_text())
EXAMPLES = sorted((ROOT / "examples").glob("claim.*.json"))
def test_examples_exist():
assert EXAMPLES, "no claim examples found to validate"
@pytest.mark.parametrize("path", EXAMPLES, ids=lambda p: p.name)
def test_example_matches_schema(path):
jsonschema.validate(json.loads(path.read_text()), SCHEMA)
@pytest.mark.parametrize("path", EXAMPLES, ids=lambda p: p.name)
def test_example_states_pdp_digest_explicitly(path):
"""Absence must be a stated null, never a missing key."""
assert "pdp_digest" in json.loads(path.read_text())["binding"]
def test_examples_cover_both_pdp_binding_states():
"""An implementer must see both shapes, not infer one from the other."""
states = {
json.loads(p.read_text())["binding"]["pdp_digest"] is None for p in EXAMPLES
}
assert states == {True, False}
def test_examples_cover_both_pdp_path_declarations():
states = {json.loads(p.read_text())["binding"]["pdp_path"] for p in EXAMPLES}
assert states == {True, False}
@pytest.mark.parametrize("path", EXAMPLES, ids=lambda p: p.name)
def test_pdp_path_examples_always_carry_a_digest(path):
"""GH-DEC-2026-008: pdp_path true guarantees pdp_digest non-null."""
binding = json.loads(path.read_text())["binding"]
if binding["pdp_path"]:
assert binding["pdp_digest"] is not None