Files
breakpilot-compliance/ai-compliance-sdk/internal/ucca/financial_policy_test.go
Benjamin Boenisch 4435e7ea0a 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>
2026-02-11 23:47:28 +01:00

619 lines
15 KiB
Go

package ucca
import (
"testing"
)
// ============================================================================
// Financial Policy Engine Tests
// ============================================================================
func TestFinancialPolicyEngine_NewEngine(t *testing.T) {
// Try to load the financial policy engine
engine, err := NewFinancialPolicyEngineFromPath("../../policies/financial_regulations_policy.yaml")
if err != nil {
t.Skipf("Skipping test - policy file not found: %v", err)
}
if engine == nil {
t.Fatal("Engine should not be nil")
}
version := engine.GetPolicyVersion()
if version == "" {
t.Error("Policy version should not be empty")
}
}
func TestFinancialPolicyEngine_IsApplicable(t *testing.T) {
engine := createTestFinancialEngine(t)
if engine == nil {
return
}
tests := []struct {
name string
domain Domain
expected bool
}{
{"Banking domain is applicable", DomainBanking, true},
{"Finance domain is applicable", DomainFinance, true},
{"Insurance domain is applicable", DomainInsurance, true},
{"Investment domain is applicable", DomainInvestment, true},
{"Healthcare is not applicable", DomainHealthcare, false},
{"Retail is not applicable", DomainRetail, false},
{"Education is not applicable", DomainEducation, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
intake := &UseCaseIntake{
Domain: tt.domain,
}
result := engine.IsApplicable(intake)
if result != tt.expected {
t.Errorf("IsApplicable() = %v, want %v", result, tt.expected)
}
})
}
}
func TestFinancialPolicyEngine_Evaluate_NonApplicableDomain(t *testing.T) {
engine := createTestFinancialEngine(t)
if engine == nil {
return
}
intake := &UseCaseIntake{
Domain: DomainHealthcare,
FinancialContext: &FinancialContext{
FinancialEntity: FinancialEntity{
Type: FinancialEntityCreditInstitution,
Regulated: true,
},
},
}
result := engine.Evaluate(intake)
if result.IsApplicable {
t.Error("Should not be applicable for healthcare domain")
}
if len(result.TriggeredRules) > 0 {
t.Error("No rules should trigger for non-applicable domain")
}
}
func TestFinancialPolicyEngine_Evaluate_MissingContext(t *testing.T) {
engine := createTestFinancialEngine(t)
if engine == nil {
return
}
intake := &UseCaseIntake{
Domain: DomainBanking,
FinancialContext: nil, // No financial context provided
}
result := engine.Evaluate(intake)
if !result.IsApplicable {
t.Error("Should be applicable for banking domain")
}
if !result.MissingContext {
t.Error("Should indicate missing financial context")
}
}
func TestFinancialPolicyEngine_Evaluate_RegulatedBank(t *testing.T) {
engine := createTestFinancialEngine(t)
if engine == nil {
return
}
intake := &UseCaseIntake{
Domain: DomainBanking,
FinancialContext: &FinancialContext{
FinancialEntity: FinancialEntity{
Type: FinancialEntityCreditInstitution,
Regulated: true,
SizeCategory: SizeCategoryLessSignificant,
},
ICTService: ICTService{
IsCritical: false,
IsOutsourced: false,
ProviderLocation: ProviderLocationEU,
},
AIApplication: FinancialAIApplication{
AffectsCustomerDecisions: false,
RiskAssessment: false,
},
},
}
result := engine.Evaluate(intake)
if !result.IsApplicable {
t.Error("Should be applicable for banking domain")
}
if result.MissingContext {
t.Error("Context should not be missing")
}
// Should trigger basic DORA/BAIT rules for regulated banks
if len(result.TriggeredRules) == 0 {
t.Error("Should trigger at least basic regulatory rules")
}
// Check for DORA control requirements
hasDORAControl := false
for _, ctrl := range result.RequiredControls {
if ctrl.Category == "DORA" || ctrl.Category == "BAIT" {
hasDORAControl = true
break
}
}
if !hasDORAControl {
t.Error("Should require DORA or BAIT controls for regulated bank")
}
}
func TestFinancialPolicyEngine_Evaluate_CriticalICTOutsourcing(t *testing.T) {
engine := createTestFinancialEngine(t)
if engine == nil {
return
}
intake := &UseCaseIntake{
Domain: DomainBanking,
FinancialContext: &FinancialContext{
FinancialEntity: FinancialEntity{
Type: FinancialEntityCreditInstitution,
Regulated: true,
},
ICTService: ICTService{
IsCritical: true,
IsOutsourced: true,
ProviderLocation: ProviderLocationEU,
},
},
}
result := engine.Evaluate(intake)
// Should have elevated risk for critical ICT outsourcing
if result.RiskScore == 0 {
t.Error("Risk score should be elevated for critical ICT outsourcing")
}
// Should require TPP risk management control
hasTPPControl := false
for _, ctrl := range result.RequiredControls {
if ctrl.ID == "CTRL-DORA-TPP-RISK-MANAGEMENT" || ctrl.ID == "CTRL-MARISK-OUTSOURCING" {
hasTPPControl = true
break
}
}
if !hasTPPControl {
t.Error("Should require TPP risk management for critical outsourcing")
}
// Should trigger escalation
if result.EscalationLevel == "" {
t.Error("Should trigger escalation for critical ICT outsourcing")
}
}
func TestFinancialPolicyEngine_Evaluate_UnvalidatedRiskModel(t *testing.T) {
engine := createTestFinancialEngine(t)
if engine == nil {
return
}
intake := &UseCaseIntake{
Domain: DomainBanking,
FinancialContext: &FinancialContext{
FinancialEntity: FinancialEntity{
Type: FinancialEntityCreditInstitution,
Regulated: true,
},
ICTService: ICTService{
IsCritical: false,
IsOutsourced: false,
},
AIApplication: FinancialAIApplication{
RiskAssessment: true,
ModelValidationDone: false, // Not validated!
},
},
}
result := engine.Evaluate(intake)
// Should block unvalidated risk models
if result.Feasibility != FeasibilityNO {
t.Error("Should block use case with unvalidated risk model")
}
// Should require model validation control
hasValidationControl := false
for _, ctrl := range result.RequiredControls {
if ctrl.ID == "CTRL-MARISK-MODEL-VALIDATION" {
hasValidationControl = true
break
}
}
if !hasValidationControl {
t.Error("Should require MaRisk model validation control")
}
}
func TestFinancialPolicyEngine_Evaluate_ValidatedRiskModel(t *testing.T) {
engine := createTestFinancialEngine(t)
if engine == nil {
return
}
intake := &UseCaseIntake{
Domain: DomainBanking,
FinancialContext: &FinancialContext{
FinancialEntity: FinancialEntity{
Type: FinancialEntityCreditInstitution,
Regulated: true,
},
ICTService: ICTService{
IsCritical: false,
IsOutsourced: false,
},
AIApplication: FinancialAIApplication{
RiskAssessment: true,
ModelValidationDone: true, // Validated!
},
},
}
result := engine.Evaluate(intake)
// Should not block validated risk models
if result.Feasibility == FeasibilityNO {
t.Error("Should not block use case with validated risk model")
}
}
func TestFinancialPolicyEngine_Evaluate_AlgorithmicTrading(t *testing.T) {
engine := createTestFinancialEngine(t)
if engine == nil {
return
}
intake := &UseCaseIntake{
Domain: DomainInvestment,
FinancialContext: &FinancialContext{
FinancialEntity: FinancialEntity{
Type: FinancialEntityInvestmentFirm,
Regulated: true,
},
AIApplication: FinancialAIApplication{
AlgorithmicTrading: true,
},
},
}
result := engine.Evaluate(intake)
// Should require algorithmic trading control
hasAlgoControl := false
for _, ctrl := range result.RequiredControls {
if ctrl.ID == "CTRL-FIN-ALGO-TRADING" {
hasAlgoControl = true
break
}
}
if !hasAlgoControl {
t.Error("Should require algorithmic trading control")
}
// Should trigger highest escalation
if result.EscalationLevel != "E3" {
t.Errorf("Should trigger E3 escalation for algo trading, got %s", result.EscalationLevel)
}
}
func TestFinancialPolicyEngine_Evaluate_CustomerDecisions(t *testing.T) {
engine := createTestFinancialEngine(t)
if engine == nil {
return
}
intake := &UseCaseIntake{
Domain: DomainBanking,
FinancialContext: &FinancialContext{
FinancialEntity: FinancialEntity{
Type: FinancialEntityCreditInstitution,
Regulated: true,
},
AIApplication: FinancialAIApplication{
AffectsCustomerDecisions: true,
},
},
}
result := engine.Evaluate(intake)
// Should require explainability control
hasExplainControl := false
for _, ctrl := range result.RequiredControls {
if ctrl.ID == "CTRL-FIN-AI-EXPLAINABILITY" {
hasExplainControl = true
break
}
}
if !hasExplainControl {
t.Error("Should require AI explainability control for customer decisions")
}
// Should trigger E2 escalation
if result.EscalationLevel != "E2" {
t.Errorf("Should trigger E2 escalation for customer decisions, got %s", result.EscalationLevel)
}
}
func TestFinancialPolicyEngine_Evaluate_AMLKYC(t *testing.T) {
engine := createTestFinancialEngine(t)
if engine == nil {
return
}
intake := &UseCaseIntake{
Domain: DomainBanking,
FinancialContext: &FinancialContext{
FinancialEntity: FinancialEntity{
Type: FinancialEntityCreditInstitution,
Regulated: true,
},
AIApplication: FinancialAIApplication{
AMLKYC: true,
},
},
}
result := engine.Evaluate(intake)
// Should require AML control
hasAMLControl := false
for _, ctrl := range result.RequiredControls {
if ctrl.ID == "CTRL-FIN-AML-AI" {
hasAMLControl = true
break
}
}
if !hasAMLControl {
t.Error("Should require AML AI control for KYC/AML use cases")
}
}
func TestFinancialPolicyEngine_Evaluate_ThirdCountryCritical(t *testing.T) {
engine := createTestFinancialEngine(t)
if engine == nil {
return
}
intake := &UseCaseIntake{
Domain: DomainBanking,
FinancialContext: &FinancialContext{
FinancialEntity: FinancialEntity{
Type: FinancialEntityCreditInstitution,
Regulated: true,
},
ICTService: ICTService{
IsCritical: true,
IsOutsourced: true,
ProviderLocation: ProviderLocationThirdCountry,
},
},
}
result := engine.Evaluate(intake)
// Should be conditional at minimum
if result.Feasibility == FeasibilityYES {
t.Error("Should not allow critical ICT in third country without conditions")
}
// Should have elevated risk
if result.RiskScore < 30 {
t.Error("Should have elevated risk score for third country critical ICT")
}
}
func TestFinancialPolicyEngine_Evaluate_ConcentrationRisk(t *testing.T) {
engine := createTestFinancialEngine(t)
if engine == nil {
return
}
intake := &UseCaseIntake{
Domain: DomainBanking,
FinancialContext: &FinancialContext{
FinancialEntity: FinancialEntity{
Type: FinancialEntityCreditInstitution,
Regulated: true,
},
ICTService: ICTService{
IsOutsourced: true,
ConcentrationRisk: true,
},
},
}
result := engine.Evaluate(intake)
// Should trigger escalation for concentration risk
if result.EscalationLevel == "" {
t.Error("Should trigger escalation for concentration risk")
}
// Should add risk
if result.RiskScore == 0 {
t.Error("Should add risk for concentration risk")
}
}
func TestFinancialPolicyEngine_Evaluate_InsuranceCompany(t *testing.T) {
engine := createTestFinancialEngine(t)
if engine == nil {
return
}
intake := &UseCaseIntake{
Domain: DomainInsurance,
FinancialContext: &FinancialContext{
FinancialEntity: FinancialEntity{
Type: FinancialEntityInsuranceCompany,
Regulated: true,
},
},
}
result := engine.Evaluate(intake)
if !result.IsApplicable {
t.Error("Should be applicable for insurance domain")
}
}
func TestFinancialPolicyEngine_GetAllControls(t *testing.T) {
engine := createTestFinancialEngine(t)
if engine == nil {
return
}
controls := engine.GetAllControls()
if len(controls) == 0 {
t.Error("Should have controls defined")
}
// Check for key DORA controls
keyControls := []string{
"CTRL-DORA-ICT-RISK-FRAMEWORK",
"CTRL-DORA-ICT-INCIDENT-MANAGEMENT",
"CTRL-DORA-TPP-RISK-MANAGEMENT",
"CTRL-MARISK-MODEL-VALIDATION",
"CTRL-BAIT-SDLC",
}
for _, key := range keyControls {
if _, ok := controls[key]; !ok {
t.Errorf("Should have control %s", key)
}
}
}
func TestFinancialPolicyEngine_GetAllGaps(t *testing.T) {
engine := createTestFinancialEngine(t)
if engine == nil {
return
}
gaps := engine.GetAllGaps()
if len(gaps) == 0 {
t.Error("Should have gaps defined")
}
// Check for key gaps
keyGaps := []string{
"GAP_DORA_NOT_IMPLEMENTED",
"GAP_MARISK_MODEL_NOT_VALIDATED",
}
for _, key := range keyGaps {
if _, ok := gaps[key]; !ok {
t.Errorf("Should have gap %s", key)
}
}
}
func TestFinancialPolicyEngine_GetAllStopLines(t *testing.T) {
engine := createTestFinancialEngine(t)
if engine == nil {
return
}
stopLines := engine.GetAllStopLines()
if len(stopLines) == 0 {
t.Error("Should have stop lines defined")
}
// Check for key stop lines
keyStopLines := []string{
"STOP_MARISK_UNVALIDATED_RISK_MODEL",
"STOP_ALGO_TRADING_WITHOUT_APPROVAL",
}
for _, key := range keyStopLines {
if _, ok := stopLines[key]; !ok {
t.Errorf("Should have stop line %s", key)
}
}
}
func TestFinancialPolicyEngine_Determinism(t *testing.T) {
engine := createTestFinancialEngine(t)
if engine == nil {
return
}
intake := &UseCaseIntake{
Domain: DomainBanking,
FinancialContext: &FinancialContext{
FinancialEntity: FinancialEntity{
Type: FinancialEntityCreditInstitution,
Regulated: true,
},
ICTService: ICTService{
IsCritical: true,
IsOutsourced: true,
},
AIApplication: FinancialAIApplication{
AffectsCustomerDecisions: true,
RiskAssessment: true,
ModelValidationDone: true,
},
},
}
// Run evaluation multiple times
var lastResult *FinancialAssessmentResult
for i := 0; i < 10; i++ {
result := engine.Evaluate(intake)
if lastResult != nil {
if result.Feasibility != lastResult.Feasibility {
t.Error("Feasibility should be deterministic")
}
if result.RiskScore != lastResult.RiskScore {
t.Error("Risk score should be deterministic")
}
if len(result.TriggeredRules) != len(lastResult.TriggeredRules) {
t.Error("Triggered rules should be deterministic")
}
if len(result.RequiredControls) != len(lastResult.RequiredControls) {
t.Error("Required controls should be deterministic")
}
}
lastResult = result
}
}
// ============================================================================
// Helper Functions
// ============================================================================
func createTestFinancialEngine(t *testing.T) *FinancialPolicyEngine {
engine, err := NewFinancialPolicyEngineFromPath("../../policies/financial_regulations_policy.yaml")
if err != nil {
t.Skipf("Skipping test - policy file not found: %v", err)
return nil
}
return engine
}