Services: Admin-Compliance, Backend-Compliance, AI-Compliance-SDK, Consent-SDK, Developer-Portal, PCA-Platform, DSMS Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
447 lines
12 KiB
Go
447 lines
12 KiB
Go
package ucca
|
|
|
|
import (
|
|
"testing"
|
|
)
|
|
|
|
// ============================================================================
|
|
// EscalationTrigger Tests
|
|
// ============================================================================
|
|
|
|
func TestDetermineEscalationLevel_E0_LowRiskInfoOnly(t *testing.T) {
|
|
trigger := DefaultEscalationTrigger()
|
|
|
|
result := &AssessmentResult{
|
|
Feasibility: FeasibilityYES,
|
|
RiskLevel: RiskLevelMINIMAL,
|
|
RiskScore: 10,
|
|
TriggeredRules: []TriggeredRule{
|
|
{Code: "R-INFO-001", Severity: "INFO", Description: "Informative Regel"},
|
|
},
|
|
DSFARecommended: false,
|
|
Art22Risk: false,
|
|
}
|
|
|
|
level, reason := trigger.DetermineEscalationLevel(result)
|
|
|
|
if level != EscalationLevelE0 {
|
|
t.Errorf("Expected E0 for low-risk case, got %s", level)
|
|
}
|
|
if reason == "" {
|
|
t.Error("Expected non-empty reason")
|
|
}
|
|
}
|
|
|
|
func TestDetermineEscalationLevel_E1_WarnRules(t *testing.T) {
|
|
trigger := DefaultEscalationTrigger()
|
|
|
|
result := &AssessmentResult{
|
|
Feasibility: FeasibilityCONDITIONAL,
|
|
RiskLevel: RiskLevelLOW,
|
|
RiskScore: 25,
|
|
TriggeredRules: []TriggeredRule{
|
|
{Code: "R-WARN-001", Severity: "WARN", Description: "Warnung"},
|
|
},
|
|
DSFARecommended: false,
|
|
Art22Risk: false,
|
|
}
|
|
|
|
level, reason := trigger.DetermineEscalationLevel(result)
|
|
|
|
if level != EscalationLevelE1 {
|
|
t.Errorf("Expected E1 for WARN rule, got %s", level)
|
|
}
|
|
if reason == "" {
|
|
t.Error("Expected non-empty reason")
|
|
}
|
|
}
|
|
|
|
func TestDetermineEscalationLevel_E1_RiskScore20to40(t *testing.T) {
|
|
trigger := DefaultEscalationTrigger()
|
|
|
|
result := &AssessmentResult{
|
|
Feasibility: FeasibilityCONDITIONAL,
|
|
RiskLevel: RiskLevelLOW,
|
|
RiskScore: 35,
|
|
TriggeredRules: []TriggeredRule{},
|
|
DSFARecommended: false,
|
|
Art22Risk: false,
|
|
}
|
|
|
|
level, _ := trigger.DetermineEscalationLevel(result)
|
|
|
|
if level != EscalationLevelE1 {
|
|
t.Errorf("Expected E1 for risk score 35, got %s", level)
|
|
}
|
|
}
|
|
|
|
func TestDetermineEscalationLevel_E2_Article9Data(t *testing.T) {
|
|
trigger := DefaultEscalationTrigger()
|
|
|
|
result := &AssessmentResult{
|
|
Feasibility: FeasibilityCONDITIONAL,
|
|
RiskLevel: RiskLevelMEDIUM,
|
|
RiskScore: 45,
|
|
TriggeredRules: []TriggeredRule{
|
|
{Code: "R-002", Severity: "WARN", Description: "Art. 9 Daten"},
|
|
},
|
|
DSFARecommended: false,
|
|
Art22Risk: false,
|
|
}
|
|
|
|
level, reason := trigger.DetermineEscalationLevel(result)
|
|
|
|
if level != EscalationLevelE2 {
|
|
t.Errorf("Expected E2 for Art. 9 data, got %s", level)
|
|
}
|
|
if reason == "" {
|
|
t.Error("Expected non-empty reason mentioning Art. 9")
|
|
}
|
|
}
|
|
|
|
func TestDetermineEscalationLevel_E2_DSFARecommended(t *testing.T) {
|
|
trigger := DefaultEscalationTrigger()
|
|
|
|
result := &AssessmentResult{
|
|
Feasibility: FeasibilityCONDITIONAL,
|
|
RiskLevel: RiskLevelMEDIUM,
|
|
RiskScore: 42,
|
|
TriggeredRules: []TriggeredRule{},
|
|
DSFARecommended: true,
|
|
Art22Risk: false,
|
|
}
|
|
|
|
level, reason := trigger.DetermineEscalationLevel(result)
|
|
|
|
if level != EscalationLevelE2 {
|
|
t.Errorf("Expected E2 for DSFA recommended, got %s", level)
|
|
}
|
|
if reason == "" {
|
|
t.Error("Expected reason to mention DSFA")
|
|
}
|
|
}
|
|
|
|
func TestDetermineEscalationLevel_E3_BlockRule(t *testing.T) {
|
|
trigger := DefaultEscalationTrigger()
|
|
|
|
result := &AssessmentResult{
|
|
Feasibility: FeasibilityNO,
|
|
RiskLevel: RiskLevelHIGH,
|
|
RiskScore: 75,
|
|
TriggeredRules: []TriggeredRule{
|
|
{Code: "R-BLOCK-001", Severity: "BLOCK", Description: "Blockierung"},
|
|
},
|
|
DSFARecommended: true,
|
|
Art22Risk: false,
|
|
}
|
|
|
|
level, reason := trigger.DetermineEscalationLevel(result)
|
|
|
|
if level != EscalationLevelE3 {
|
|
t.Errorf("Expected E3 for BLOCK rule, got %s", level)
|
|
}
|
|
if reason == "" {
|
|
t.Error("Expected reason to mention BLOCK")
|
|
}
|
|
}
|
|
|
|
func TestDetermineEscalationLevel_E3_Art22Risk(t *testing.T) {
|
|
trigger := DefaultEscalationTrigger()
|
|
|
|
result := &AssessmentResult{
|
|
Feasibility: FeasibilityCONDITIONAL,
|
|
RiskLevel: RiskLevelHIGH,
|
|
RiskScore: 55,
|
|
TriggeredRules: []TriggeredRule{},
|
|
DSFARecommended: false,
|
|
Art22Risk: true,
|
|
}
|
|
|
|
level, reason := trigger.DetermineEscalationLevel(result)
|
|
|
|
if level != EscalationLevelE3 {
|
|
t.Errorf("Expected E3 for Art. 22 risk, got %s", level)
|
|
}
|
|
if reason == "" {
|
|
t.Error("Expected reason to mention Art. 22")
|
|
}
|
|
}
|
|
|
|
func TestDetermineEscalationLevel_E3_HighRiskScore(t *testing.T) {
|
|
trigger := DefaultEscalationTrigger()
|
|
|
|
result := &AssessmentResult{
|
|
Feasibility: FeasibilityCONDITIONAL,
|
|
RiskLevel: RiskLevelHIGH,
|
|
RiskScore: 70, // Above E3 threshold
|
|
TriggeredRules: []TriggeredRule{},
|
|
DSFARecommended: false,
|
|
Art22Risk: false,
|
|
}
|
|
|
|
level, _ := trigger.DetermineEscalationLevel(result)
|
|
|
|
if level != EscalationLevelE3 {
|
|
t.Errorf("Expected E3 for risk score 70, got %s", level)
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// SLA Tests
|
|
// ============================================================================
|
|
|
|
func TestGetDefaultSLA_E0(t *testing.T) {
|
|
response, resolution := GetDefaultSLA(EscalationLevelE0)
|
|
|
|
if response != 0 || resolution != 0 {
|
|
t.Errorf("E0 should have no SLA, got response=%d, resolution=%d", response, resolution)
|
|
}
|
|
}
|
|
|
|
func TestGetDefaultSLA_E1(t *testing.T) {
|
|
response, resolution := GetDefaultSLA(EscalationLevelE1)
|
|
|
|
if response != 24 {
|
|
t.Errorf("E1 should have 24h response SLA, got %d", response)
|
|
}
|
|
if resolution != 72 {
|
|
t.Errorf("E1 should have 72h resolution SLA, got %d", resolution)
|
|
}
|
|
}
|
|
|
|
func TestGetDefaultSLA_E2(t *testing.T) {
|
|
response, resolution := GetDefaultSLA(EscalationLevelE2)
|
|
|
|
if response != 8 {
|
|
t.Errorf("E2 should have 8h response SLA, got %d", response)
|
|
}
|
|
if resolution != 48 {
|
|
t.Errorf("E2 should have 48h resolution SLA, got %d", resolution)
|
|
}
|
|
}
|
|
|
|
func TestGetDefaultSLA_E3(t *testing.T) {
|
|
response, resolution := GetDefaultSLA(EscalationLevelE3)
|
|
|
|
if response != 4 {
|
|
t.Errorf("E3 should have 4h response SLA, got %d", response)
|
|
}
|
|
if resolution != 24 {
|
|
t.Errorf("E3 should have 24h resolution SLA, got %d", resolution)
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Role Assignment Tests
|
|
// ============================================================================
|
|
|
|
func TestGetRoleForLevel_E0(t *testing.T) {
|
|
role := GetRoleForLevel(EscalationLevelE0)
|
|
if role != "" {
|
|
t.Errorf("E0 should have no role, got %s", role)
|
|
}
|
|
}
|
|
|
|
func TestGetRoleForLevel_E1(t *testing.T) {
|
|
role := GetRoleForLevel(EscalationLevelE1)
|
|
if role != "team_lead" {
|
|
t.Errorf("E1 should require team_lead, got %s", role)
|
|
}
|
|
}
|
|
|
|
func TestGetRoleForLevel_E2(t *testing.T) {
|
|
role := GetRoleForLevel(EscalationLevelE2)
|
|
if role != "dsb" {
|
|
t.Errorf("E2 should require dsb, got %s", role)
|
|
}
|
|
}
|
|
|
|
func TestGetRoleForLevel_E3(t *testing.T) {
|
|
role := GetRoleForLevel(EscalationLevelE3)
|
|
if role != "dsb" {
|
|
t.Errorf("E3 should require dsb (primary), got %s", role)
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Default Trigger Configuration Tests
|
|
// ============================================================================
|
|
|
|
func TestDefaultEscalationTrigger_Thresholds(t *testing.T) {
|
|
trigger := DefaultEscalationTrigger()
|
|
|
|
if trigger.E1RiskThreshold != 20 {
|
|
t.Errorf("E1 threshold should be 20, got %d", trigger.E1RiskThreshold)
|
|
}
|
|
if trigger.E2RiskThreshold != 40 {
|
|
t.Errorf("E2 threshold should be 40, got %d", trigger.E2RiskThreshold)
|
|
}
|
|
if trigger.E3RiskThreshold != 60 {
|
|
t.Errorf("E3 threshold should be 60, got %d", trigger.E3RiskThreshold)
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Edge Case Tests
|
|
// ============================================================================
|
|
|
|
func TestDetermineEscalationLevel_BoundaryRiskScores(t *testing.T) {
|
|
trigger := DefaultEscalationTrigger()
|
|
|
|
tests := []struct {
|
|
name string
|
|
riskScore int
|
|
expectedLevel EscalationLevel
|
|
}{
|
|
{"Risk 0 → E0", 0, EscalationLevelE0},
|
|
{"Risk 19 → E0", 19, EscalationLevelE0},
|
|
{"Risk 20 → E0 (boundary)", 20, EscalationLevelE0},
|
|
{"Risk 21 → E1", 21, EscalationLevelE1},
|
|
{"Risk 39 → E1", 39, EscalationLevelE1},
|
|
{"Risk 40 → E1 (boundary)", 40, EscalationLevelE1},
|
|
{"Risk 41 → E2", 41, EscalationLevelE2},
|
|
{"Risk 59 → E2", 59, EscalationLevelE2},
|
|
{"Risk 60 → E2 (boundary)", 60, EscalationLevelE2},
|
|
{"Risk 61 → E3", 61, EscalationLevelE3},
|
|
{"Risk 100 → E3", 100, EscalationLevelE3},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := &AssessmentResult{
|
|
RiskScore: tt.riskScore,
|
|
TriggeredRules: []TriggeredRule{},
|
|
DSFARecommended: false,
|
|
Art22Risk: false,
|
|
}
|
|
|
|
level, _ := trigger.DetermineEscalationLevel(result)
|
|
if level != tt.expectedLevel {
|
|
t.Errorf("Expected %s for risk score %d, got %s", tt.expectedLevel, tt.riskScore, level)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDetermineEscalationLevel_CombinedFactors(t *testing.T) {
|
|
trigger := DefaultEscalationTrigger()
|
|
|
|
// Multiple E3 factors should still result in E3
|
|
result := &AssessmentResult{
|
|
RiskScore: 80,
|
|
TriggeredRules: []TriggeredRule{
|
|
{Code: "R-BLOCK-001", Severity: "BLOCK", Description: "Block 1"},
|
|
{Code: "R-BLOCK-002", Severity: "BLOCK", Description: "Block 2"},
|
|
},
|
|
DSFARecommended: true,
|
|
Art22Risk: true,
|
|
}
|
|
|
|
level, reason := trigger.DetermineEscalationLevel(result)
|
|
|
|
if level != EscalationLevelE3 {
|
|
t.Errorf("Expected E3 for multiple high-risk factors, got %s", level)
|
|
}
|
|
|
|
// Reason should mention multiple factors
|
|
if reason == "" {
|
|
t.Error("Expected comprehensive reason for multiple factors")
|
|
}
|
|
}
|
|
|
|
func TestDetermineEscalationLevel_EmptyRules(t *testing.T) {
|
|
trigger := DefaultEscalationTrigger()
|
|
|
|
result := &AssessmentResult{
|
|
RiskScore: 5,
|
|
TriggeredRules: []TriggeredRule{},
|
|
DSFARecommended: false,
|
|
Art22Risk: false,
|
|
}
|
|
|
|
level, _ := trigger.DetermineEscalationLevel(result)
|
|
|
|
if level != EscalationLevelE0 {
|
|
t.Errorf("Expected E0 for empty rules and low risk, got %s", level)
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Constants Validation Tests
|
|
// ============================================================================
|
|
|
|
func TestEscalationLevelConstants(t *testing.T) {
|
|
levels := []EscalationLevel{
|
|
EscalationLevelE0,
|
|
EscalationLevelE1,
|
|
EscalationLevelE2,
|
|
EscalationLevelE3,
|
|
}
|
|
|
|
expected := []string{"E0", "E1", "E2", "E3"}
|
|
|
|
for i, level := range levels {
|
|
if string(level) != expected[i] {
|
|
t.Errorf("Expected %s, got %s", expected[i], level)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestEscalationStatusConstants(t *testing.T) {
|
|
statuses := map[EscalationStatus]string{
|
|
EscalationStatusPending: "pending",
|
|
EscalationStatusAssigned: "assigned",
|
|
EscalationStatusInReview: "in_review",
|
|
EscalationStatusApproved: "approved",
|
|
EscalationStatusRejected: "rejected",
|
|
EscalationStatusReturned: "returned",
|
|
}
|
|
|
|
for status, expected := range statuses {
|
|
if string(status) != expected {
|
|
t.Errorf("Expected status %s, got %s", expected, status)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestEscalationDecisionConstants(t *testing.T) {
|
|
decisions := map[EscalationDecision]string{
|
|
EscalationDecisionApprove: "approve",
|
|
EscalationDecisionReject: "reject",
|
|
EscalationDecisionModify: "modify",
|
|
EscalationDecisionEscalate: "escalate",
|
|
}
|
|
|
|
for decision, expected := range decisions {
|
|
if string(decision) != expected {
|
|
t.Errorf("Expected decision %s, got %s", expected, decision)
|
|
}
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Helper Function Tests
|
|
// ============================================================================
|
|
|
|
func TestJoinReasons_Empty(t *testing.T) {
|
|
result := joinReasons([]string{}, "Prefix: ")
|
|
if result != "Prefix: " {
|
|
t.Errorf("Expected 'Prefix: ', got '%s'", result)
|
|
}
|
|
}
|
|
|
|
func TestJoinReasons_Single(t *testing.T) {
|
|
result := joinReasons([]string{"Reason 1"}, "Test: ")
|
|
if result != "Test: Reason 1" {
|
|
t.Errorf("Expected 'Test: Reason 1', got '%s'", result)
|
|
}
|
|
}
|
|
|
|
func TestJoinReasons_Multiple(t *testing.T) {
|
|
result := joinReasons([]string{"Reason 1", "Reason 2", "Reason 3"}, "Test: ")
|
|
expected := "Test: Reason 1; Reason 2; Reason 3"
|
|
if result != expected {
|
|
t.Errorf("Expected '%s', got '%s'", expected, result)
|
|
}
|
|
}
|