kings-guard/tests/test_cadence_declaration.py
tegwick 4b8b79dc03 Align cadence observer with canonical NetKingdom namespace
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a1029d-2c93-7500-a0e0-e3e089668b29
2026-10-03 22:59:54 +02:00

163 lines
6.5 KiB
Python

from pathlib import Path
import pytest
from kings_guard.cadence import load_qonto_assistant_source_cadence
ROOT = Path(__file__).resolve().parents[1]
DRAFT = ROOT / "specs" / "EmissionCadenceDeclaration.md"
QONTO_SOURCE_DECLARATION = Path("/home/worsch/qonto-assistant/specs/audit-emission-cadence.yaml")
def test_emission_cadence_draft_exists_for_taxonomy_handover() -> None:
text = DRAFT.read_text(encoding="utf-8")
assert DRAFT.is_file()
assert "owner: Taxonomy" in text
assert "drafter: kings-guard" in text
assert "qonto-assistant" in text
assert "GH-WP-0002-T04" in text
assert "approval-engine/cadence.yaml" in text
assert "expected rate" in text.lower() or "expected-rate" in text
assert "heartbeat" in text.lower()
assert "reconciliation" in text.lower()
assert "alongside the security genome" in text.lower() or "alongside the genome" in text.lower()
assert "security_genome" in text
def test_qonto_source_declaration_is_consumed_separately_from_the_taxonomy_draft() -> None:
cadence = load_qonto_assistant_source_cadence()
assert cadence.status == "source-declared"
assert cadence.owner == "qonto-assistant"
assert cadence.reference_instance == "QONTO-WP-0005"
assert cadence.forms() == {"heartbeat-or-reconciliation"}
assert cadence.rates == ()
assert cadence.heartbeats[0].interval.total_seconds() == 86400
@pytest.mark.skipif(
not QONTO_SOURCE_DECLARATION.is_file(),
reason="qonto-assistant source declaration checkout is unavailable",
)
def test_qonto_source_cadence_fixture_matches_the_source_owned_declaration() -> None:
yaml = pytest.importorskip("yaml")
document = yaml.safe_load(QONTO_SOURCE_DECLARATION.read_text(encoding="utf-8"))
source = document.get("audit_emission_cadence", document)
cadence = load_qonto_assistant_source_cadence()
assert cadence.source_system == source["source"]
if "sources" in source:
deny = next(item for item in source["sources"] if item["event_class"] == "audit.deny")
heartbeat = deny["heartbeat"]
assert cadence.heartbeats[0].event_class == heartbeat["event_class"]
from kings_guard.cadence import parse_interval
interval = heartbeat.get("interval_seconds", heartbeat.get("interval"))
assert cadence.heartbeats[0].interval == parse_interval(interval)
assert deny["form"] in cadence.forms()
assert deny["extensions"]["netkingdom"]["evidence_class"] == "load-bearing"
else:
# Historical owner envelope is checked only as provenance during migration.
# The runtime reader accepts the canonical namespace exclusively.
assert cadence.heartbeats[0].event_class == source["heartbeat"]["event_class"]
assert (
cadence.heartbeats[0].interval.total_seconds()
== source["heartbeat"]["default_interval_seconds"]
)
assert source["event_classes"]["audit.deny"]["form"] in cadence.forms()
assert source["event_classes"]["audit.allow"]["completeness_claimed"] is False
assert cadence.rates == ()
@pytest.mark.parametrize(
"filename",
[
"qonto_assistant_cadence.json",
"qonto_assistant_source_cadence.json",
],
)
def test_cadence_fixtures_conform_to_owner_schema(filename):
import json
import jsonschema
import yaml
schema_path = Path(
"/home/worsch/info-tech-canon/infospace/schemas/emission-cadence.schema.yaml"
)
if not schema_path.is_file():
pytest.skip("canonical InfoTechCanon schema checkout is unavailable")
schema = yaml.safe_load(schema_path.read_text())
payload = json.loads((ROOT / "src/kings_guard/fixtures" / filename).read_text())
jsonschema.Draft202012Validator(schema).validate(payload)
@pytest.mark.parametrize(("value", "seconds"), [("P1DT2H30M5S", 95405), ("P1D", 86400)])
def test_canonical_compound_durations_are_consumed(value, seconds):
from kings_guard.cadence import parse_interval
assert parse_interval(value).total_seconds() == seconds
def _canonical_cadence_document():
import json
return json.loads(
(ROOT / "src/kings_guard/fixtures/qonto_assistant_source_cadence.json").read_text()
)
def test_canonical_profile_namespace_preserves_source_classification():
from kings_guard.cadence import emission_cadence_from_dict
from kings_guard.contracts import EvidenceClass
payload = _canonical_cadence_document()
cadence = emission_cadence_from_dict(payload)
assert cadence.heartbeats[0].evidence_class is EvidenceClass.LOAD_BEARING
payload["sources"][0]["extensions"]["netkingdom"]["evidence_class"] = "attributive"
assert (
emission_cadence_from_dict(payload).heartbeats[0].evidence_class
is EvidenceClass.ATTRIBUTIVE
)
@pytest.mark.parametrize("retain_canonical", [False, True])
def test_legacy_profile_namespace_requires_explicit_owner_migration(retain_canonical):
from kings_guard.cadence import emission_cadence_from_dict
payload = _canonical_cadence_document()
extensions = payload["sources"][0]["extensions"]
extensions["net-kingdom"] = dict(extensions["netkingdom"])
if not retain_canonical:
del extensions["netkingdom"]
with pytest.raises(ValueError, match="legacy extensions.net-kingdom must migrate"):
emission_cadence_from_dict(payload)
@pytest.mark.parametrize("profile", [None, [], "load-bearing"])
def test_missing_or_malformed_profile_does_not_infer_classification(profile):
from kings_guard.cadence import emission_cadence_from_dict
payload = _canonical_cadence_document()
payload["sources"][0]["extensions"]["netkingdom"] = profile
with pytest.raises(ValueError, match="requires source-owned extensions.netkingdom"):
emission_cadence_from_dict(payload)
@pytest.mark.parametrize("classification", [None, True, "unknown"])
def test_invalid_evidence_classification_is_refused(classification):
from kings_guard.cadence import emission_cadence_from_dict
payload = _canonical_cadence_document()
payload["sources"][0]["extensions"]["netkingdom"] = {"evidence_class": classification}
with pytest.raises(ValueError, match="evidence_class must be source-declared"):
emission_cadence_from_dict(payload)
def test_malformed_entry_extensions_are_diagnosed():
from kings_guard.cadence import emission_cadence_from_dict
payload = _canonical_cadence_document()
payload["sources"][0]["extensions"] = []
with pytest.raises(ValueError, match="extensions must be an object"):
emission_cadence_from_dict(payload)