Initial commit: breakpilot-compliance - Compliance SDK Platform
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>
This commit is contained in:
446
ai-compliance-sdk/internal/ucca/escalation_test.go
Normal file
446
ai-compliance-sdk/internal/ucca/escalation_test.go
Normal file
@@ -0,0 +1,446 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user