feat: Massnahmen-Bibliothek auf 200 erweitert (3-Stufen)

60 Design + 80 Schutz + 60 Information — alle mit Normenreferenzen.
Subtypes: geometry, force_energy, material, ergonomics, control_design,
fixed_guard, movable_guard, electro_sensitive, emergency_stop,
electrical/thermal/fluid protection, extraction, signage, manual,
training, ppe, organizational, marking.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-05-07 14:23:15 +02:00
parent 71802614cc
commit 3d7b09bcef
5 changed files with 376 additions and 14 deletions
@@ -73,7 +73,15 @@ func TestProtectiveMeasures_HazardCategoryNotEmpty(t *testing.T) {
}
}
// TestProtectiveMeasures_Count160 verifies at least 160 measures exist.
// 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 {
@@ -81,6 +89,76 @@ func TestProtectiveMeasures_Count160(t *testing.T) {
}
}
// 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)