package iace import "testing" func TestPatternEngine_RobotArm_MechanicalHazards(t *testing.T) { engine := NewPatternEngine() result := engine.Match(MatchInput{ ComponentLibraryIDs: []string{"C001"}, // Roboterarm: moving_part, rotating_part, high_force EnergySourceIDs: []string{"EN01", "EN02"}, // kinetic translational + rotational }) if len(result.MatchedPatterns) == 0 { t.Fatal("expected matched patterns for robot arm + kinetic energy") } // Should match mechanical hazard patterns (HP001, HP002, HP006) hasMechHazard := false for _, h := range result.SuggestedHazards { if h.Category == "mechanical_hazard" { hasMechHazard = true break } } if !hasMechHazard { t.Error("expected mechanical_hazard in suggested hazards for robot arm") } } func TestPatternEngine_Schaltschrank_ElectricalHazards(t *testing.T) { engine := NewPatternEngine() result := engine.Match(MatchInput{ ComponentLibraryIDs: []string{"C061"}, // Schaltschrank: high_voltage, electrical_part EnergySourceIDs: []string{"EN04"}, // Elektrische Energie }) if len(result.MatchedPatterns) == 0 { t.Fatal("expected matched patterns for control cabinet + electrical energy") } hasElecHazard := false for _, h := range result.SuggestedHazards { if h.Category == "electrical_hazard" { hasElecHazard = true break } } if !hasElecHazard { t.Error("expected electrical_hazard in suggested hazards for Schaltschrank") } } func TestPatternEngine_SPS_Switch_SoftwareCyberHazards(t *testing.T) { engine := NewPatternEngine() result := engine.Match(MatchInput{ ComponentLibraryIDs: []string{"C071", "C111"}, // SPS + Switch: has_software, programmable, networked, it_component EnergySourceIDs: []string{"EN18"}, // Cyber/Data energy }) if len(result.MatchedPatterns) == 0 { t.Fatal("expected matched patterns for SPS + Switch + cyber energy") } categories := make(map[string]bool) for _, h := range result.SuggestedHazards { categories[h.Category] = true } if !categories["software_fault"] { t.Error("expected software_fault hazard for SPS") } if !categories["unauthorized_access"] { t.Error("expected unauthorized_access hazard for networked components + cyber energy") } } func TestPatternEngine_AIModule_AISpecificHazards(t *testing.T) { engine := NewPatternEngine() result := engine.Match(MatchInput{ ComponentLibraryIDs: []string{"C119"}, // KI-Inferenzmodul: has_ai, has_software, networked EnergySourceIDs: []string{"EN19", "EN18"}, // AI model + cyber energy }) if len(result.MatchedPatterns) == 0 { t.Fatal("expected matched patterns for AI inference module") } categories := make(map[string]bool) for _, h := range result.SuggestedHazards { categories[h.Category] = true } if !categories["false_classification"] { t.Error("expected false_classification hazard for AI module") } if !categories["model_drift"] { t.Error("expected model_drift hazard for AI module") } } func TestPatternEngine_EmptyInput_EmptyResults(t *testing.T) { engine := NewPatternEngine() result := engine.Match(MatchInput{}) if len(result.MatchedPatterns) != 0 { t.Errorf("expected no matched patterns for empty input, got %d", len(result.MatchedPatterns)) } if len(result.SuggestedHazards) != 0 { t.Errorf("expected no suggested hazards for empty input, got %d", len(result.SuggestedHazards)) } } func TestPatternEngine_ExclusionTags(t *testing.T) { engine := NewPatternEngine() // HP029 requires has_software+programmable, excludes has_ai // C071 (SPS) has has_software+programmable but NOT has_ai → should match HP029 result1 := engine.Match(MatchInput{ ComponentLibraryIDs: []string{"C071"}, // SPS }) hasHP029 := false for _, p := range result1.MatchedPatterns { if p.PatternID == "HP029" { hasHP029 = true break } } if !hasHP029 { t.Error("HP029 should match for SPS (has_software+programmable, no has_ai)") } // C119 (KI-Inferenzmodul) has has_ai → HP029 should be excluded result2 := engine.Match(MatchInput{ ComponentLibraryIDs: []string{"C119"}, // KI-Inferenzmodul: has_ai }) hasHP029_2 := false for _, p := range result2.MatchedPatterns { if p.PatternID == "HP029" { hasHP029_2 = true break } } if hasHP029_2 { t.Error("HP029 should NOT match for AI module (excluded by has_ai tag)") } } func TestPatternEngine_HydraulicPatterns(t *testing.T) { engine := NewPatternEngine() result := engine.Match(MatchInput{ ComponentLibraryIDs: []string{"C042"}, // Hydraulikzylinder EnergySourceIDs: []string{"EN05"}, // Hydraulische Energie }) hasHydraulicHazard := false for _, h := range result.SuggestedHazards { if h.Category == "pneumatic_hydraulic" { hasHydraulicHazard = true break } } if !hasHydraulicHazard { t.Error("expected pneumatic_hydraulic hazard for hydraulic cylinder + hydraulic energy") } } func TestPatternEngine_MeasuresAndEvidence(t *testing.T) { engine := NewPatternEngine() result := engine.Match(MatchInput{ ComponentLibraryIDs: []string{"C001"}, // Roboterarm EnergySourceIDs: []string{"EN01"}, // Kinetische Energie }) if len(result.SuggestedMeasures) == 0 { t.Error("expected suggested measures for robot arm") } if len(result.SuggestedEvidence) == 0 { t.Error("expected suggested evidence for robot arm") } } func TestPatternEngine_ResolvedTags(t *testing.T) { engine := NewPatternEngine() result := engine.Match(MatchInput{ ComponentLibraryIDs: []string{"C001"}, EnergySourceIDs: []string{"EN01"}, CustomTags: []string{"my_custom_tag"}, }) tagSet := toSet(result.ResolvedTags) if !tagSet["moving_part"] { t.Error("expected 'moving_part' in resolved tags") } if !tagSet["kinetic"] { t.Error("expected 'kinetic' in resolved tags") } if !tagSet["my_custom_tag"] { t.Error("expected 'my_custom_tag' in resolved tags") } } func TestPatternEngine_SafetyDevice_HighPriority(t *testing.T) { engine := NewPatternEngine() result := engine.Match(MatchInput{ ComponentLibraryIDs: []string{"C101"}, // Not-Halt-Taster: safety_device, emergency_stop }) hasSafetyFail := false for _, h := range result.SuggestedHazards { if h.Category == "safety_function_failure" { hasSafetyFail = true break } } if !hasSafetyFail { t.Error("expected safety_function_failure for Not-Halt-Taster") } }