package iace import "testing" // TestControlsLibrary_UniqueIDs verifies all control IDs are unique. func TestControlsLibrary_UniqueIDs(t *testing.T) { seen := make(map[string]bool) for _, e := range GetControlsLibrary() { if e.ID == "" { t.Errorf("control has empty ID") continue } if seen[e.ID] { t.Errorf("duplicate control ID: %s", e.ID) } seen[e.ID] = true } } // TestProtectiveMeasures_HasExamples verifies measures have examples. func TestProtectiveMeasures_HasExamples(t *testing.T) { withExamples := 0 for _, e := range GetProtectiveMeasureLibrary() { if len(e.Examples) > 0 { withExamples++ } } total := len(GetProtectiveMeasureLibrary()) threshold := total * 80 / 100 if withExamples < threshold { t.Errorf("only %d/%d measures have examples, want at least %d", withExamples, total, threshold) } } // TestProtectiveMeasures_ThreeReductionTypesPresent verifies all 3 types exist. func TestProtectiveMeasures_ThreeReductionTypesPresent(t *testing.T) { types := make(map[string]int) for _, e := range GetProtectiveMeasureLibrary() { types[e.ReductionType]++ } // Accept both naming variants designCount := types["design"] protectiveCount := types["protective"] + types["protection"] infoCount := types["information"] if designCount == 0 { t.Error("no measures with reduction type design") } if protectiveCount == 0 { t.Error("no measures with reduction type protective/protection") } if infoCount == 0 { t.Error("no measures with reduction type information") } } // TestProtectiveMeasures_TagFieldAccessible verifies the Tags field is accessible. func TestProtectiveMeasures_TagFieldAccessible(t *testing.T) { measures := GetProtectiveMeasureLibrary() if len(measures) == 0 { t.Fatal("no measures returned") } // Tags field exists but may not be populated yet _ = measures[0].Tags } // TestProtectiveMeasures_HazardCategoryNotEmpty verifies HazardCategory is populated. func TestProtectiveMeasures_HazardCategoryNotEmpty(t *testing.T) { for _, e := range GetProtectiveMeasureLibrary() { if e.HazardCategory == "" { t.Errorf("measure %s (%s): HazardCategory is empty", e.ID, e.Name) } } } // TestProtectiveMeasures_Count200 verifies exactly 200 measures exist. func TestProtectiveMeasures_Count200(t *testing.T) { entries := GetProtectiveMeasureLibrary() if len(entries) != 200 { t.Fatalf("got %d protective measures, want exactly 200", len(entries)) } } // TestProtectiveMeasures_Count160 verifies at least 160 measures exist (legacy gate). func TestProtectiveMeasures_Count160(t *testing.T) { entries := GetProtectiveMeasureLibrary() if len(entries) < 160 { t.Fatalf("got %d protective measures, want at least 160", len(entries)) } } // TestProtectiveMeasures_NormReferencesPopulated verifies most measures have norm refs. func TestProtectiveMeasures_NormReferencesPopulated(t *testing.T) { withRefs := 0 for _, e := range GetProtectiveMeasureLibrary() { if len(e.NormReferences) > 0 { withRefs++ } } total := len(GetProtectiveMeasureLibrary()) threshold := total * 90 / 100 if withRefs < threshold { t.Errorf("only %d/%d measures have NormReferences, want at least %d", withRefs, total, threshold) } } // TestProtectiveMeasures_DesignProtectionInfoDistribution verifies balanced distribution. func TestProtectiveMeasures_DesignProtectionInfoDistribution(t *testing.T) { types := make(map[string]int) for _, e := range GetProtectiveMeasureLibrary() { types[e.ReductionType]++ } design := types["design"] protection := types["protection"] information := types["information"] if design < 50 { t.Errorf("design measures: %d, want >= 50", design) } if protection < 70 { t.Errorf("protection measures: %d, want >= 70", protection) } if information < 50 { t.Errorf("information measures: %d, want >= 50", information) } t.Logf("Distribution: design=%d, protection=%d, information=%d", design, protection, information) } // TestProtectiveMeasures_IDSequential verifies IDs run M001-M200 without gaps. func TestProtectiveMeasures_IDSequential(t *testing.T) { entries := GetProtectiveMeasureLibrary() for i, e := range entries { expected := "M" + padID(i+1) if e.ID != expected { t.Errorf("entries[%d]: got ID %q, want %q", i, e.ID, expected) } } } func padID(n int) string { if n < 10 { return "00" + itoa(n) } if n < 100 { return "0" + itoa(n) } return itoa(n) } func itoa(n int) string { if n == 0 { return "0" } s := "" for n > 0 { s = string(rune('0'+n%10)) + s n /= 10 } return s } // TestProtectiveMeasures_SubTypesPresent verifies subtypes are used. func TestProtectiveMeasures_SubTypesPresent(t *testing.T) { subtypes := make(map[string]int) for _, e := range GetProtectiveMeasureLibrary() { if e.SubType != "" { subtypes[e.SubType]++ } } if len(subtypes) < 3 { t.Errorf("expected at least 3 different subtypes, got %d: %v", len(subtypes), subtypes) } }