package layer_test import ( "os" "path/filepath" "runtime" "strings" "testing" "github.com/netkingdom/flex-auth/internal/layer" "gopkg.in/yaml.v3" ) func TestLayerDeclarationConforms(t *testing.T) { root := repoRoot(t) if err := layer.Check(root); err != nil { t.Fatalf("layer conformance: %v", err) } decl, err := layer.LoadDeclaration(filepath.Join(root, "INTENT.md")) if err != nil { t.Fatalf("LoadDeclaration: %v", err) } if decl.Layer != "Engine" { t.Fatalf("layer = %q; want Engine", decl.Layer) } if decl.Role != "PDP" { t.Fatalf("role = %q; want PDP", decl.Role) } if decl.Framework != "netkingdom-security-layer-model" { t.Fatalf("framework = %q", decl.Framework) } // The declaration is a boundary and must NOT pin a standard version: the // role does not change when the standard text is amended. Version-scoped // conformance state lives in the derived record named below. if decl.StandardVersion != "" { t.Fatalf("standard_version = %q; want empty (boundary, not version-scoped)", decl.StandardVersion) } if decl.ConformanceRecord == "" { t.Fatal("conformance_record is empty; version-stamped state must have a home") } if _, err := os.Stat(filepath.Join(repoRoot(t), decl.ConformanceRecord)); err != nil { t.Fatalf("conformance_record %q does not exist: %v", decl.ConformanceRecord, err) } // GH-DEC-2026-018: flex-auth is the §4 source of evidence for the decision // record and owes a per-event-class emission guarantee. The declaration // must say so and must name the published inventory. if decl.SourceOfEvidence == nil || !*decl.SourceOfEvidence { t.Fatal("source_of_evidence must be true: GH-DEC-2026-018 §2") } if decl.EmissionGuarantee == "" { t.Fatal("emission_guarantee is empty; §11 requires it of a §4 evidence source") } if _, err := os.Stat(filepath.Join(repoRoot(t), decl.EmissionGuarantee)); err != nil { t.Fatalf("emission_guarantee %q does not exist: %v", decl.EmissionGuarantee, err) } } // The per-class rule is the operative half of GH-DEC-2026-018 §3: a single // repository-level guarantee over a stream carrying both a high-volume allow // and a rare deny is an average, not a declaration. Assert the published // inventory actually classifies each class, so a later edit cannot collapse it // back into one number. func TestEmissionInventoryIsPerEventClass(t *testing.T) { root := repoRoot(t) body, err := os.ReadFile(filepath.Join(root, "cadence.yaml")) if err != nil { t.Fatal(err) } var doc struct { Source string `yaml:"source"` Classes map[string]struct { Action string `yaml:"action"` EvidenceClass string `yaml:"evidence_class"` Rarity string `yaml:"rarity"` RateMonitoring string `yaml:"rate_monitoring"` Detection []string `yaml:"detection"` } `yaml:"classes"` } if err := yaml.Unmarshal(body, &doc); err != nil { t.Fatal(err) } if len(doc.Classes) < 2 { t.Fatal("cadence.yaml declares fewer than two event classes; §11 requires the guarantee per class, not per repository") } for name, c := range doc.Classes { if c.Action == "" || c.EvidenceClass == "" || c.Rarity == "" { t.Errorf("class %q: action, evidence_class and rarity must all be published — a run may not infer them (§11)", name) } // A rare load-bearing class MUST carry heartbeat AND reconciliation and // MUST NOT be covered by rate monitoring. if c.EvidenceClass == "load-bearing" && c.Rarity == "rare" { if c.RateMonitoring != "forbidden" { t.Errorf("class %q is rare load-bearing; rate_monitoring must be forbidden", name) } var heartbeat, reconciliation bool for _, d := range c.Detection { heartbeat = heartbeat || d == "heartbeat" reconciliation = reconciliation || d == "reconciliation" } if !heartbeat || !reconciliation { t.Errorf("class %q is rare load-bearing; it must carry heartbeat AND reconciliation, not either alone", name) } } } } func TestVersionPinInDeclarationIsRejected(t *testing.T) { err := layer.ValidateDeclaration(layer.Declaration{ Layer: "Engine", Role: "PDP", ConformanceRecord: "docs/conformance/security-layer-conformance.md", StandardVersion: "0.8", }) if err == nil { t.Fatal("a standard_version pin in the boundary declaration was accepted") } } func TestEngineWithoutRoleIsRejected(t *testing.T) { err := layer.ValidateDeclaration(layer.Declaration{Layer: "Engine"}) if err == nil { t.Fatal("Engine without role was accepted") } } func TestUnknownLayerIsRejected(t *testing.T) { err := layer.ValidateDeclaration(layer.Declaration{Layer: "ControlPlane", Role: "PDP"}) if err == nil { t.Fatal("unknown layer was accepted") } } // The defect this validator carried: the vocabulary has FOUR tokens and this // set admitted three, omitting the layer the standard itself occupies. // railiance-master's `Taxonomy` was conforming and the checker was wrong. func TestVocabularyHasFourTokensIncludingTaxonomy(t *testing.T) { want := map[string]bool{"Taxonomy": true, "Tooling": true, "Engine": true, "Staff": true} got := layer.Vocabulary() if len(got) != len(want) { t.Fatalf("vocabulary = %v; want the four §3 tokens", got) } for _, tok := range got { if !want[tok] { t.Errorf("unexpected token %q", tok) } } if canon, ok := layer.CanonicalLayer("Taxonomy"); !ok || canon != "Taxonomy" { t.Fatal("Taxonomy was rejected: §3.1 defines it, §4 catalogues it twice, and the standard is an instance of it") } } // GH-DEC-2026-017 §2: comparison is ASCII case-insensitive and a run MUST fold // before comparing. A lowercase declaration is conforming, not tolerated. func TestVocabularyComparisonFoldsCase(t *testing.T) { for _, in := range []string{"engine", "ENGINE", "Engine", " engine "} { canon, ok := layer.CanonicalLayer(in) if !ok { t.Fatalf("%q was rejected; comparison must fold ASCII case", in) } // §4's column form is canonical, so the folded result reports as `Engine` // however the declaration spelled it. if canon != "Engine" { t.Fatalf("CanonicalLayer(%q) = %q; want the §4 column spelling Engine", in, canon) } } if err := layer.ValidateDeclaration(layer.Declaration{ Layer: "engine", Role: "PDP", ConformanceRecord: "docs/conformance/security-layer-conformance.md", SourceOfEvidence: boolPtr(true), EmissionGuarantee: "cadence.yaml", }); err != nil { t.Fatalf("a lowercase declaration was rejected: %v", err) } } // §3's table heading reads `Engines`, plural, while §4's column reads `Engine`. // A9 states the token once and it is §4's. A declaration of `Engines` is a // declaration of a token the vocabulary does not have. func TestPluralEnginesIsNotTheToken(t *testing.T) { if _, ok := layer.CanonicalLayer("Engines"); ok { t.Fatal("`Engines` was admitted; the token is `Engine`, as §4's Layer column carries it") } } // A §4 evidence source that names no emission guarantee is not conforming. func TestEvidenceSourceWithoutEmissionGuaranteeIsRejected(t *testing.T) { err := layer.ValidateDeclaration(layer.Declaration{ Layer: "Engine", Role: "PDP", ConformanceRecord: "docs/conformance/security-layer-conformance.md", SourceOfEvidence: boolPtr(true), }) if err == nil { t.Fatal("a marked evidence source with no emission_guarantee was accepted") } } func boolPtr(b bool) *bool { return &b } func repoRoot(t *testing.T) string { t.Helper() _, file, _, ok := runtime.Caller(0) if !ok { t.Fatal("runtime.Caller failed") } return filepath.Clean(filepath.Join(filepath.Dir(file), "..", "..")) } // A12 r2 reaches CONTENT, not a key name (GH-DEC-2026-020 §1, §2). Each of these // is a version of the standard or its companion that a key-name check on // `standard_version` could not see. If any of them comes back into a // declaration, this test fails. func TestVersionAnywhereInDeclarationIsFound(t *testing.T) { for name, doc := range map[string]string{ "versioned standard path": "layer: Engine\nstandard: net-kingdom/canon/standards/security-layer-model_v0.7.md\n", "versioned companion path": "layer: Engine\ncompanion: net-kingdom/SECURITY-COMPANION_v0.2.md\n", "companion_version": "layer: Engine\ncompanion_version: \"0.2\"\n", "standard_version": "layer: Engine\nstandard_version: \"0.8\"\n", "bare version under standard": "layer: Engine\nstandard: \"v0.8\"\n", "nested versioned path": "layer: Engine\nassented_by:\n - ref: security-layer-model_v0.8.md\n", "version in a list of sources": "layer: Engine\nsources: [net-kingdom/canon/standards/security-layer-model_v0.6.md]\n", "prose version under standard": "layer: Engine\nstandard: security-layer-model v0.7\n", "version under companion": "layer: Engine\ncompanion: SECURITY-COMPANION 0.2\n", "reviewed-version key": "layer: Engine\nstandard_version_reviewed: \"0.7\"\n", "at-version reference": "layer: Engine\nsource: net-kingdom@0.7\n", } { pins, err := layer.VersionPins(doc) if err != nil { t.Fatalf("%s: %v", name, err) } if len(pins) == 0 { t.Errorf("%s: version was not detected in %q", name, doc) } } } // What A12 r2 states it does NOT reach: comments, schema_version, and versions // that are not versions of the standard or its companion. Unversioned standard // and companion paths are the conforming form. func TestVersionPinsLeavesWhatA12DoesNotReach(t *testing.T) { doc := "# declared against security-layer-model_v0.7.md, kept as history\n" + "schema_version: \"0.2\"\n" + "intent_version: 0.1.0\n" + "layer: engine # v0.8 comment\n" + "standard: net-kingdom/canon/standards/security-layer-model\n" + "companion: net-kingdom/SECURITY-COMPANION.md\n" + "declared_at: \"2026-08-29\"\n" + "declared_by: decisions/decisions.md FLEX-DEC-2026-001\n" + "companion_version:\n" + // GH-DEC-2026-021 §1: a revision cited in prose is provenance, not a // pin, even under a standard_*-prefixed key that names no version. "standard_note: the standard's v0.5 scope rule\n" + "rationale: adopted under security-layer-model v0.7 and GH-DEC-2026-020\n" pins, err := layer.VersionPins(doc) if err != nil { t.Fatal(err) } if len(pins) != 0 { t.Fatalf("A12 r2 reached what it does not reach: %v", pins) } } // Check must fail on a declaration whose only pin is in the standard: path. // Stance, claims and classification maps SHOULD carry the version of the text // they answer; a run MUST NOT apply A12 to them (GH-DEC-2026-020 §3). func TestCheckRejectsVersionedStandardPathButNotStanceMaps(t *testing.T) { good := "---\nlayer: Engine\nrole: PDP\nconformance_record: record.md\nsource_of_evidence: false\n" + "standard: net-kingdom/canon/standards/security-layer-model\n---\n\n# x\n" dir := t.TempDir() for name, body := range map[string]string{ "INTENT.md": good, "record.md": "x\n", "pep-stance.yaml": "standard_version: \"0.8\"\n", "pip-claims.yaml": "standard_version: \"0.8\"\nstandard: security-layer-model_v0.8.md\n", "evidence-classification.yaml": "standard_version: \"0.8\"\n", } { if err := os.WriteFile(filepath.Join(dir, name), []byte(body), 0o644); err != nil { t.Fatal(err) } } if err := layer.Check(dir); err != nil { t.Fatalf("a version in a stance/claims/classification map failed the declaration check: %v", err) } for _, f := range []string{"pep-stance.yaml", "pip-claims.yaml", "evidence-classification.yaml"} { if _, err := layer.DeclarationVersionPins(filepath.Join(dir, f)); err == nil { t.Errorf("DeclarationVersionPins accepted %s; A12 r2 must not be applied to it", f) } } bad := strings.Replace(good, "security-layer-model\n", "security-layer-model_v0.7.md\n", 1) if err := os.WriteFile(filepath.Join(dir, "INTENT.md"), []byte(bad), 0o644); err != nil { t.Fatal(err) } if err := layer.Check(dir); err == nil { t.Fatal("a versioned standard: path in INTENT.md was accepted") } } // flex-auth's own declaration carries no version under any key (A12 r2). func TestOwnDeclarationCarriesNoVersionAnywhere(t *testing.T) { pins, err := layer.DeclarationVersionPins(filepath.Join(repoRoot(t), "INTENT.md")) if err != nil { t.Fatal(err) } if len(pins) != 0 { t.Fatalf("INTENT.md declaration carries a version: %v", pins) } if layer.ValidatedAgainst == "" { t.Fatal("the checker must state the version it validates against on every run") } }