86d1473a6a
Consumes the cross-session contract obligations/obligation_join_keys.json (47 obligation_ids). Interim bridge = citation_unit (our source_norm <-> registry citation_units), to be hardened to the stable obligation_id (field now optional on ControlMapping). ComputeObligationCoverage joins the 47 registry obligations to our accepted control mappings: covered=2 (user_authentication_required, firmware_software_ authentication), mapped_rejected=3 ((2)(e) -> our OWASP mappings rejected, route via NIST/BSI), uncovered=42. This coverage signal is the feedback to the Obligation session for what to cut/refine next. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
62 lines
2.0 KiB
Go
62 lines
2.0 KiB
Go
package ucca
|
|
|
|
import "testing"
|
|
|
|
func TestCitationUnitKey_Join(t *testing.T) {
|
|
// our source_norm and the registry citation_unit must collapse to the SAME key.
|
|
if citationUnitKey("CRA Annex I Part I (2)(c) — Schutz vor unbefugtem Zugriff") != citationUnitKey("Annex I (2)(c)") {
|
|
t.Errorf("interim join broken: %q vs %q",
|
|
citationUnitKey("CRA Annex I Part I (2)(c)"), citationUnitKey("Annex I (2)(c)"))
|
|
}
|
|
// Part II must NOT collide with Part I.
|
|
if citationUnitKey("Annex I Part II (1)") == citationUnitKey("CRA Annex I Part I (2)(c)") {
|
|
t.Error("Part II must not join to Part I")
|
|
}
|
|
}
|
|
|
|
func TestLoadObligationJoinKeys(t *testing.T) {
|
|
o, err := LoadObligationJoinKeys("../../../obligations/obligation_join_keys.json")
|
|
if err != nil {
|
|
t.Fatalf("load: %v", err)
|
|
}
|
|
if o.Count != len(o.ObligationIDs) {
|
|
t.Errorf("count %d != len %d", o.Count, len(o.ObligationIDs))
|
|
}
|
|
if len(o.ObligationIDs) == 0 {
|
|
t.Fatal("empty contract")
|
|
}
|
|
if got := o.ObligationsForCitation("CRA Annex I Part I (2)(c)"); len(got) == 0 {
|
|
t.Error("expected an obligation joined to (2)(c)")
|
|
}
|
|
}
|
|
|
|
func TestObligationCoverage_Report(t *testing.T) {
|
|
joins, err := LoadObligationJoinKeys("../../../obligations/obligation_join_keys.json")
|
|
if err != nil {
|
|
t.Fatalf("join keys: %v", err)
|
|
}
|
|
maps, err := LoadControlMappings("../../data/control_mappings")
|
|
if err != nil {
|
|
t.Fatalf("mappings: %v", err)
|
|
}
|
|
ev, err := LoadEvidenceRequirements("../../data/evidence_requirements")
|
|
if err != nil {
|
|
t.Fatalf("evidence: %v", err)
|
|
}
|
|
cov := ComputeObligationCoverage(joins, maps, ev)
|
|
if len(cov) == 0 {
|
|
t.Fatal("no coverage computed")
|
|
}
|
|
byStatus := map[string]int{}
|
|
for _, c := range cov {
|
|
byStatus[c.Status]++
|
|
}
|
|
t.Logf("COVERAGE: %d Obligations | covered=%d mapped_rejected=%d uncovered=%d",
|
|
len(cov), byStatus["covered"], byStatus["mapped_rejected"], byStatus["uncovered"])
|
|
for _, c := range cov {
|
|
if c.Status != "uncovered" {
|
|
t.Logf(" %-15s %-36s controls=%v evidence=%d", c.Status, c.ObligationID, c.AcceptedControls, c.EvidenceCount)
|
|
}
|
|
}
|
|
}
|