package iace import "testing" // TestGetExtendedHazardPatterns_HasEntries verifies extended patterns exist. func TestGetExtendedHazardPatterns_HasEntries(t *testing.T) { patterns := GetExtendedHazardPatterns() if len(patterns) < 50 { t.Fatalf("GetExtendedHazardPatterns returned %d entries, want at least 50", len(patterns)) } } // TestGetExtendedHazardPatterns_UniqueIDs verifies all extended pattern IDs are unique. func TestGetExtendedHazardPatterns_UniqueIDs(t *testing.T) { seen := make(map[string]bool) for _, p := range GetExtendedHazardPatterns() { if p.ID == "" { t.Error("pattern with empty ID") continue } if seen[p.ID] { t.Errorf("duplicate pattern ID: %s", p.ID) } seen[p.ID] = true } } // TestGetExtendedHazardPatterns_NoConflictWithBuiltin verifies no ID overlap with builtin. func TestGetExtendedHazardPatterns_NoConflictWithBuiltin(t *testing.T) { builtinIDs := make(map[string]bool) for _, p := range GetBuiltinHazardPatterns() { builtinIDs[p.ID] = true } for _, p := range GetExtendedHazardPatterns() { if builtinIDs[p.ID] { t.Errorf("extended pattern %s conflicts with builtin pattern", p.ID) } } } // TestGetExtendedHazardPatterns_AllHaveRequiredFields verifies all fields are populated. func TestGetExtendedHazardPatterns_AllHaveRequiredFields(t *testing.T) { for _, p := range GetExtendedHazardPatterns() { if p.NameDE == "" { t.Errorf("pattern %s: NameDE is empty", p.ID) } if p.NameEN == "" { t.Errorf("pattern %s: NameEN is empty", p.ID) } if len(p.RequiredComponentTags) == 0 { t.Errorf("pattern %s: RequiredComponentTags is empty", p.ID) } if len(p.GeneratedHazardCats) == 0 { t.Errorf("pattern %s: GeneratedHazardCats is empty", p.ID) } if len(p.SuggestedMeasureIDs) == 0 { t.Errorf("pattern %s: SuggestedMeasureIDs is empty", p.ID) } if len(p.SuggestedEvidenceIDs) == 0 { t.Errorf("pattern %s: SuggestedEvidenceIDs is empty", p.ID) } if p.Priority <= 0 { t.Errorf("pattern %s: Priority is %d, want > 0", p.ID, p.Priority) } } } // TestGetExtendedHazardPatterns_ReferencedMeasuresExist verifies M-IDs exist. func TestGetExtendedHazardPatterns_ReferencedMeasuresExist(t *testing.T) { measureIDs := make(map[string]bool) for _, m := range GetProtectiveMeasureLibrary() { measureIDs[m.ID] = true } for _, p := range GetExtendedHazardPatterns() { for _, mid := range p.SuggestedMeasureIDs { if !measureIDs[mid] { t.Errorf("pattern %s references measure %s which does not exist", p.ID, mid) } } } } // TestGetExtendedHazardPatterns_ReferencedEvidenceExist verifies E-IDs exist. func TestGetExtendedHazardPatterns_ReferencedEvidenceExist(t *testing.T) { evidenceIDs := make(map[string]bool) for _, e := range GetEvidenceTypeLibrary() { evidenceIDs[e.ID] = true } for _, p := range GetExtendedHazardPatterns() { for _, eid := range p.SuggestedEvidenceIDs { if !evidenceIDs[eid] { t.Errorf("pattern %s references evidence %s which does not exist", p.ID, eid) } } } } // TestPatternEngine_CombinedCount verifies the engine has both builtin + extended. func TestPatternEngine_CombinedCount(t *testing.T) { engine := NewPatternEngine() builtinCount := len(GetBuiltinHazardPatterns()) extendedCount := len(GetExtendedHazardPatterns()) totalExpected := builtinCount + extendedCount if len(engine.patterns) != totalExpected { t.Errorf("engine has %d patterns, want %d (builtin %d + extended %d)", len(engine.patterns), totalExpected, builtinCount, extendedCount) } } // TestPatternEngine_ExtendedPatternsMatch verifies extended patterns fire correctly. func TestPatternEngine_ExtendedPatternsMatch(t *testing.T) { engine := NewPatternEngine() // Hydraulic hose + hydraulic pressure should match extended patterns output := engine.Match(MatchInput{ ComponentLibraryIDs: []string{"C045"}, // Hydraulikschlauch EnergySourceIDs: []string{"EN05"}, // Hydraulic }) hasExtended := false for _, pm := range output.MatchedPatterns { if pm.PatternID >= "HP045" { hasExtended = true break } } if !hasExtended && len(output.MatchedPatterns) > 0 { // Extended patterns may not fire if tags don't match exactly, // but we should at least have some matches t.Logf("No extended patterns fired for hydraulic hose, but %d total patterns matched", len(output.MatchedPatterns)) } } // TestPatternEngine_ExtendedAIPatterns verifies AI-related extended patterns. func TestPatternEngine_ExtendedAIPatterns(t *testing.T) { engine := NewPatternEngine() // Vision AI camera should trigger AI patterns output := engine.Match(MatchInput{ ComponentLibraryIDs: []string{"C089"}, // KI-Kamerasystem (has has_ai, sensor_part) EnergySourceIDs: []string{}, }) if len(output.MatchedPatterns) == 0 { t.Log("No patterns matched for AI camera — may need tag alignment") } // Check that AI hazard categories appear when AI components are used hasAI := false for _, h := range output.SuggestedHazards { if h.Category == "ai_misclassification" || h.Category == "model_drift" || h.Category == "sensor_fault" { hasAI = true break } } if len(output.SuggestedHazards) > 0 && !hasAI { t.Logf("Expected AI-related hazard categories, got: %v", output.SuggestedHazards) } }