Files
breakpilot-compliance/ai-compliance-sdk/internal/iace/risk_suggestion_test.go
T
Benjamin Admin 77536f04b7
CI / detect-changes (push) Successful in 8s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / secret-scan (push) Has been skipped
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / build-sha-integrity (push) Failing after 4s
CI / validate-canonical-controls (push) Successful in 11s
CI / loc-budget (push) Successful in 14s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / nodejs-build (push) Has been skipped
CI / test-go (push) Failing after 38s
CI / iace-gt-coverage (push) Successful in 23s
CI / test-python-backend (push) Has been skipped
CI / test-python-document-crawler (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped
feat(iace): dual-model risk-suggestion endpoint for Risikobewertung tab
GET /projects/:id/hazards/:hid/risk-suggestion returns BreakPilot's justified
starting values for BOTH risk models per hazard:
- EN-62061-style F/W/P/S (the Excel format the professional knows)
- Fine-Kinney P/E/C (US-recognized)
each with a plain-language justification + the visible formula. Read-only and
computed from public-data anchors (ESAW/NIOSH/OSHA via the engine estimators) —
the professional adjusts the values; no norm table is stored or reproduced.

Adds EstimateFrequency (lifecycle -> 1-5) and BuildRiskSuggestion. Go SDK has no
OpenAPI baseline, so the only contract surface is the frontend consumer (the new
Risikobewertung tab, next).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-09 15:35:39 +02:00

45 lines
1.5 KiB
Go

package iace
import (
"testing"
"github.com/google/uuid"
)
func TestBuildRiskSuggestion_DualModel(t *testing.T) {
hz := &Hazard{
ID: uuid.New(),
Name: "Quetschung unter absenkender Hubplattform",
Scenario: "Bediener wird zwischen Plattform und Boden eingeklemmt (Quetschung)",
Category: "mechanical_hazard",
LifecyclePhase: "normal_operation, maintenance",
}
rs := BuildRiskSuggestion(hz)
if rs.ContactMode != "crushing" {
t.Errorf("contact mode = %q, want crushing", rs.ContactMode)
}
// EN-62061 side populated + formula exposed
if rs.EN62061.Severity.Value < 1 || rs.EN62061.Score < 1 || rs.EN62061.Level == "" {
t.Errorf("EN62061 not populated: %+v", rs.EN62061)
}
if rs.EN62061.Formula == "" || rs.FineKinney.Formula == "" {
t.Error("both formulas must be exposed for the professional")
}
// Fine-Kinney side populated, score == P*E*C
fk := rs.FineKinney
if fk.Probability.Value <= 0 || fk.Exposure.Value <= 0 || fk.Consequence.Value <= 0 {
t.Errorf("FK params not populated: %+v", fk)
}
if want := fk.Probability.Value * fk.Exposure.Value * fk.Consequence.Value; fk.Score != want {
t.Errorf("FK score = %v, want P*E*C = %v", fk.Score, want)
}
if fk.Band == "" {
t.Error("FK band must be set")
}
// Justifications are the whole point (nachvollziehbar)
if rs.EN62061.Probability.Justification == "" || fk.Consequence.Justification == "" {
t.Error("justifications must be present on both models")
}
}