feat(iace): Sprint 4B — ISO 12100 Hazard/Situation/Harm Trennung

ISO 12100 trennt: Hazard (Quelle) → Hazardous Situation (Person exponiert) → Harm (Verletzung).
Bisher war alles in einem Hazard-Record vermischt.

Implementierung als abgeleitetes Feld (keine DB-Migration noetig):
- HazardType Feld auf Hazard Entity ("hazard"|"hazardous_situation"|"harm")
- DeriveHazardType() berechnet Typ aus Scenario/PossibleHarm/Category
- Explizites Override moeglich (HazardType direkt setzen)
- GeneratedHazardType auf HazardPattern fuer Pattern-gesteuerte Zuweisung
- Store: GetHazard/ListHazards setzen HazardType automatisch
- Init-Handler: Fuellt jetzt TriggerEvent, PossibleHarm, AffectedPerson, HazardousZone
  aus Pattern-Match-Daten (vorher leer gelassen)

6 neue Tests: ScenarioAndHarm, HarmOnly, CategoryOnly, ExplicitOverride,
EmptyFallback, PatternMatchField

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-05-10 20:55:26 +02:00
parent 6e995b52d1
commit d339d1edc7
7 changed files with 119 additions and 10 deletions
@@ -338,6 +338,62 @@ func TestEvidenceTypes_UniqueIDs(t *testing.T) {
}
}
// ══════════════════════════════════════════════════════════════════
// Sprint 4B: HazardType / ISO 12100 Trennung
// ══════════════════════════════════════════════════════════════════
func TestDeriveHazardType_WithScenarioAndHarm(t *testing.T) {
h := &Hazard{Scenario: "Person im Bewegungsraum", PossibleHarm: "Quetschung"}
if got := DeriveHazardType(h); got != HazardTypeHazardousSituation {
t.Errorf("expected hazardous_situation, got %s", got)
}
}
func TestDeriveHazardType_HarmOnly(t *testing.T) {
h := &Hazard{PossibleHarm: "Quetschung", Category: "mechanical"}
if got := DeriveHazardType(h); got != HazardTypeHarm {
t.Errorf("expected harm, got %s", got)
}
}
func TestDeriveHazardType_CategoryOnly(t *testing.T) {
h := &Hazard{Category: "mechanical_hazard"}
if got := DeriveHazardType(h); got != HazardTypeHazard {
t.Errorf("expected hazard, got %s", got)
}
}
func TestDeriveHazardType_ExplicitOverride(t *testing.T) {
h := &Hazard{HazardType: "harm", Scenario: "ignored", PossibleHarm: "ignored"}
if got := DeriveHazardType(h); got != "harm" {
t.Errorf("expected explicit harm, got %s", got)
}
}
func TestDeriveHazardType_EmptyFallback(t *testing.T) {
h := &Hazard{}
if got := DeriveHazardType(h); got != DefaultHazardType {
t.Errorf("expected default %s, got %s", DefaultHazardType, got)
}
}
func TestPatternMatch_GeneratedHazardType(t *testing.T) {
// Verify PatternMatch carries GeneratedHazardType
engine := NewPatternEngine()
result := engine.Match(MatchInput{
ComponentLibraryIDs: []string{"C001"},
EnergySourceIDs: []string{"EN01"},
})
// Most patterns don't set GeneratedHazardType (empty = default)
// Just verify the field exists and doesn't crash
for _, p := range result.MatchedPatterns {
_ = p.GeneratedHazardType // Should compile and not panic
}
if len(result.MatchedPatterns) == 0 {
t.Fatal("expected matched patterns")
}
}
func TestEvidenceTypes_SortOrder(t *testing.T) {
types := GetEvidenceTypeLibrary()
for i := 1; i < len(types); i++ {