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
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>
45 lines
1.5 KiB
Go
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")
|
|
}
|
|
}
|