package ucca import ( "testing" ) func TestConditionEngine_NilNode(t *testing.T) { engine := NewObligationConditionEngine() facts := NewUnifiedFacts() if !engine.Evaluate(nil, facts) { t.Error("nil node should return true (always applies)") } } func TestConditionEngine_LeafEquals(t *testing.T) { engine := NewObligationConditionEngine() tests := []struct { name string field string value interface{} facts func() *UnifiedFacts expected bool }{ { name: "is_controller true", field: "data_protection.is_controller", value: true, facts: func() *UnifiedFacts { f := NewUnifiedFacts() f.DataProtection.IsController = true return f }, expected: true, }, { name: "is_controller false mismatch", field: "data_protection.is_controller", value: true, facts: func() *UnifiedFacts { return NewUnifiedFacts() }, expected: false, }, { name: "employee_count equals", field: "organization.employee_count", value: float64(50), facts: func() *UnifiedFacts { f := NewUnifiedFacts() f.Organization.EmployeeCount = 50 return f }, expected: true, }, { name: "uses_ai true", field: "ai_usage.uses_ai", value: true, facts: func() *UnifiedFacts { f := NewUnifiedFacts() f.AIUsage.UsesAI = true return f }, expected: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { node := &ConditionNode{ Field: tt.field, Operator: "EQUALS", Value: tt.value, } result := engine.Evaluate(node, tt.facts()) if result != tt.expected { t.Errorf("expected %v, got %v", tt.expected, result) } }) } } func TestConditionEngine_GreaterThan(t *testing.T) { engine := NewObligationConditionEngine() facts := NewUnifiedFacts() facts.Organization.EmployeeCount = 50 node := &ConditionNode{ Field: "organization.employee_count", Operator: "GREATER_THAN", Value: float64(19), } if !engine.Evaluate(node, facts) { t.Error("50 > 19 should be true") } facts.Organization.EmployeeCount = 10 if engine.Evaluate(node, facts) { t.Error("10 > 19 should be false") } } func TestConditionEngine_AllOf(t *testing.T) { engine := NewObligationConditionEngine() facts := NewUnifiedFacts() facts.DataProtection.IsController = true facts.DataProtection.ProcessesSpecialCategories = true node := &ConditionNode{ AllOf: []ConditionNode{ {Field: "data_protection.is_controller", Operator: "EQUALS", Value: true}, {Field: "data_protection.processes_special_categories", Operator: "EQUALS", Value: true}, }, } if !engine.Evaluate(node, facts) { t.Error("all_of with both true should be true") } facts.DataProtection.ProcessesSpecialCategories = false if engine.Evaluate(node, facts) { t.Error("all_of with one false should be false") } } func TestConditionEngine_AnyOf(t *testing.T) { engine := NewObligationConditionEngine() facts := NewUnifiedFacts() facts.DataProtection.RequiresDSBByLaw = false node := &ConditionNode{ AnyOf: []ConditionNode{ {Field: "data_protection.needs_dpo", Operator: "EQUALS", Value: true}, {Field: "organization.employee_count", Operator: "GREATER_THAN", Value: float64(19)}, }, } if engine.Evaluate(node, facts) { t.Error("any_of with both false should be false") } facts.Organization.EmployeeCount = 25 if !engine.Evaluate(node, facts) { t.Error("any_of with one true should be true") } } func TestConditionEngine_UnknownField(t *testing.T) { engine := NewObligationConditionEngine() facts := NewUnifiedFacts() node := &ConditionNode{ Field: "nonexistent.field", Operator: "EQUALS", Value: true, } if engine.Evaluate(node, facts) { t.Error("unknown field should return false") } } func TestConditionEngine_NotEquals(t *testing.T) { engine := NewObligationConditionEngine() facts := NewUnifiedFacts() facts.Organization.Country = "DE" node := &ConditionNode{ Field: "organization.country", Operator: "NOT_EQUALS", Value: "US", } if !engine.Evaluate(node, facts) { t.Error("DE != US should be true") } node.Value = "DE" if engine.Evaluate(node, facts) { t.Error("DE != DE should be false") } }