package ucca import "testing" func strPtr(s string) *string { return &s } func TestMapCompanyProfileToFacts_FullProfile(t *testing.T) { profile := &CompanyProfileInput{ CompanyName: "Test GmbH", LegalForm: "GmbH", Industry: "Gesundheitswesen", EmployeeCount: "50-249", AnnualRevenue: "10-50 Mio", HeadquartersCountry: "DE", IsDataController: true, IsDataProcessor: false, UsesAI: true, AIUseCases: []string{"Diagnostik"}, DPOName: strPtr("Dr. Datenschutz"), SubjectToNIS2: true, SubjectToAIAct: true, } facts := MapCompanyProfileToFacts(profile) if facts.Organization.Name != "Test GmbH" { t.Errorf("Name: got %q", facts.Organization.Name) } if facts.Organization.EmployeeCount != 150 { t.Errorf("EmployeeCount: got %d, want 150", facts.Organization.EmployeeCount) } if facts.Organization.AnnualRevenue != 30_000_000 { t.Errorf("AnnualRevenue: got %f", facts.Organization.AnnualRevenue) } if facts.Organization.Country != "DE" { t.Errorf("Country: got %q", facts.Organization.Country) } if !facts.Organization.EUMember { t.Error("expected EUMember=true for DE") } if facts.Sector.PrimarySector != "health" && facts.Sector.PrimarySector != "healthcare" { t.Errorf("Sector: got %q, want health or healthcare", facts.Sector.PrimarySector) } if !facts.DataProtection.IsController { t.Error("expected IsController=true") } if !facts.Personnel.HasDPO { t.Error("expected HasDPO=true") } if !facts.AIUsage.UsesAI { t.Error("expected UsesAI=true") } if !facts.DataProtection.RequiresDSBByLaw { t.Error("expected DPO requirement for DE with 150 employees") } } func TestMapCompanyProfileToFacts_NilProfile(t *testing.T) { facts := MapCompanyProfileToFacts(nil) if facts == nil { t.Fatal("expected non-nil UnifiedFacts for nil profile") } if facts.Organization.Country != "DE" { t.Errorf("expected default country DE, got %q", facts.Organization.Country) } } func TestParseEmployeeRangeGo(t *testing.T) { tests := []struct { input string expected int }{ {"1-9", 5}, {"10-49", 30}, {"50-249", 150}, {"250-999", 625}, {"1000+", 1500}, {"", 0}, {"unknown", 0}, } for _, tc := range tests { got := parseEmployeeRangeGo(tc.input) if got != tc.expected { t.Errorf("parseEmployeeRangeGo(%q) = %d, want %d", tc.input, got, tc.expected) } } } func TestParseRevenueRangeGo(t *testing.T) { tests := []struct { input string expected float64 }{ {"< 2 Mio", 1_000_000}, {"2-10 Mio", 6_000_000}, {"10-50 Mio", 30_000_000}, {"50+ Mio", 75_000_000}, {"", 0}, } for _, tc := range tests { got := parseRevenueRangeGo(tc.input) if got != tc.expected { t.Errorf("parseRevenueRangeGo(%q) = %f, want %f", tc.input, got, tc.expected) } } } func TestMergeCompanyFactsIntoIntakeFacts(t *testing.T) { company := NewUnifiedFacts() company.Organization.Name = "ACME" company.Organization.EmployeeCount = 200 company.Organization.Country = "DE" company.DataProtection.IsController = true company.Sector.PrimarySector = "health" intake := NewUnifiedFacts() intake.DataProtection.ProcessesPersonalData = true intake.DataProtection.Profiling = true intake.AIUsage.UsesAI = true intake.UCCAFacts = &UseCaseIntake{Domain: "hr"} merged := MergeCompanyFactsIntoIntakeFacts(company, intake) // Company-level fields should come from company if merged.Organization.Name != "ACME" { t.Errorf("expected company Name, got %q", merged.Organization.Name) } if merged.Organization.EmployeeCount != 200 { t.Errorf("expected company EmployeeCount=200, got %d", merged.Organization.EmployeeCount) } if merged.Sector.PrimarySector != "health" { t.Errorf("expected company sector=health, got %q", merged.Sector.PrimarySector) } // Use-case-level fields should come from intake if !merged.DataProtection.Profiling { t.Error("expected intake Profiling=true") } if !merged.AIUsage.UsesAI { t.Error("expected intake UsesAI=true") } if merged.UCCAFacts == nil || merged.UCCAFacts.Domain != "hr" { t.Error("expected intake UCCAFacts preserved") } // Company-level data protection preserved if !merged.DataProtection.IsController { t.Error("expected company IsController=true in merged") } } func TestMergeWithNilCompanyFacts(t *testing.T) { intake := NewUnifiedFacts() intake.AIUsage.UsesAI = true merged := MergeCompanyFactsIntoIntakeFacts(nil, intake) if !merged.AIUsage.UsesAI { t.Error("expected intake-only merge to preserve AIUsage") } } func TestNIS2ThresholdTriggered(t *testing.T) { profile := &CompanyProfileInput{ EmployeeCount: "50-249", AnnualRevenue: "10-50 Mio", HeadquartersCountry: "DE", Industry: "Energie", } facts := MapCompanyProfileToFacts(profile) if !facts.Organization.MeetsNIS2SizeThreshold() { t.Error("expected NIS2 size threshold met for 150 employees") } } func TestBDSG_DPOTriggered(t *testing.T) { profile := &CompanyProfileInput{ EmployeeCount: "10-49", HeadquartersCountry: "DE", IsDataController: true, } facts := MapCompanyProfileToFacts(profile) // 30 employees in DE processing personal data → DPO required if !facts.DataProtection.RequiresDSBByLaw { t.Error("expected BDSG DPO requirement for 30 employees in DE") } } func TestBDSG_DPONotTriggeredSmallCompany(t *testing.T) { profile := &CompanyProfileInput{ EmployeeCount: "1-9", HeadquartersCountry: "DE", IsDataController: true, } facts := MapCompanyProfileToFacts(profile) // 5 employees → DPO NOT required if facts.DataProtection.RequiresDSBByLaw { t.Error("expected no DPO requirement for 5 employees") } } func TestComputeEnrichmentHints_AllMissing(t *testing.T) { profile := &CompanyProfileInput{} hints := ComputeEnrichmentHints(profile) if len(hints) < 4 { t.Errorf("expected at least 4 hints for empty profile, got %d", len(hints)) } // Check high priority hints highCount := 0 for _, h := range hints { if h.Priority == "high" { highCount++ } } if highCount < 3 { t.Errorf("expected at least 3 high-priority hints, got %d", highCount) } } func TestComputeEnrichmentHints_Complete(t *testing.T) { profile := &CompanyProfileInput{ EmployeeCount: "50-249", AnnualRevenue: "10-50 Mio", Industry: "IT", HeadquartersCountry: "DE", DPOName: strPtr("Max Mustermann"), } hints := ComputeEnrichmentHints(profile) if len(hints) != 0 { t.Errorf("expected 0 hints for complete profile, got %d: %+v", len(hints), hints) } } func TestComputeEnrichmentHints_NilProfile(t *testing.T) { hints := ComputeEnrichmentHints(nil) if len(hints) < 4 { t.Errorf("expected all critical hints for nil profile, got %d", len(hints)) } } func TestIsEUCountry(t *testing.T) { if !isEUCountry("DE") { t.Error("DE should be EU") } if !isEUCountry("at") { t.Error("AT should be EU (case insensitive)") } if isEUCountry("US") { t.Error("US should not be EU") } if isEUCountry("CH") { t.Error("CH should not be EU") } } func TestBuildCompanyContext(t *testing.T) { profile := &CompanyProfileInput{ EmployeeCount: "250-999", AnnualRevenue: "50+ Mio", HeadquartersCountry: "DE", Industry: "Finanzdienstleistungen", SubjectToNIS2: true, } ctx := BuildCompanyContext(profile) if ctx == nil { t.Fatal("expected non-nil context") } if ctx.Country != "DE" { t.Errorf("Country: got %q", ctx.Country) } if !ctx.NIS2Applicable { t.Error("expected NIS2 applicable") } if !ctx.DPORequired { t.Error("expected DPO required for large DE company") } }