package ucca import ( "encoding/base64" "strings" "testing" "time" "github.com/google/uuid" ) func TestPDFExporter_Creation(t *testing.T) { exporter := NewPDFExporter("de") if exporter == nil { t.Error("Expected exporter to be created") } // Default language exporter = NewPDFExporter("") if exporter.language != "de" { t.Errorf("Expected default language 'de', got '%s'", exporter.language) } } func TestPDFExporter_ExportMarkdown(t *testing.T) { overview := createTestOverview() exporter := NewPDFExporter("de") response, err := exporter.ExportMarkdown(overview) if err != nil { t.Fatalf("Failed to export markdown: %v", err) } if response.ContentType != "text/markdown" { t.Errorf("Expected content type 'text/markdown', got '%s'", response.ContentType) } if !strings.HasSuffix(response.Filename, ".md") { t.Errorf("Expected filename to end with .md, got '%s'", response.Filename) } // Check content contains expected sections if !strings.Contains(response.Content, "# Regulatorische Pflichten-Uebersicht") { t.Error("Expected markdown to contain title") } if !strings.Contains(response.Content, "Executive Summary") { t.Error("Expected markdown to contain executive summary") } if !strings.Contains(response.Content, "Anwendbare Regulierungen") { t.Error("Expected markdown to contain regulations section") } } func TestPDFExporter_ExportPDF(t *testing.T) { overview := createTestOverview() exporter := NewPDFExporter("de") response, err := exporter.ExportManagementMemo(overview) if err != nil { t.Fatalf("Failed to export PDF: %v", err) } if response.ContentType != "application/pdf" { t.Errorf("Expected content type 'application/pdf', got '%s'", response.ContentType) } if !strings.HasSuffix(response.Filename, ".pdf") { t.Errorf("Expected filename to end with .pdf, got '%s'", response.Filename) } // Check that content is valid base64 decoded, err := base64.StdEncoding.DecodeString(response.Content) if err != nil { t.Fatalf("Failed to decode base64 content: %v", err) } // Check PDF magic bytes if len(decoded) < 4 || string(decoded[:4]) != "%PDF" { t.Error("Expected content to start with PDF magic bytes") } } func TestPDFExporter_ExportEmptyOverview(t *testing.T) { overview := &ManagementObligationsOverview{ ID: uuid.New(), AssessmentDate: time.Now(), ApplicableRegulations: []ApplicableRegulation{}, Obligations: []Obligation{}, RequiredControls: []ObligationControl{}, IncidentDeadlines: []IncidentDeadline{}, ExecutiveSummary: ExecutiveSummary{ TotalRegulations: 0, TotalObligations: 0, ComplianceScore: 100, KeyRisks: []string{}, RecommendedActions: []string{}, }, SanctionsSummary: SanctionsSummary{ Summary: "Keine Sanktionen identifiziert.", }, } exporter := NewPDFExporter("de") // Test Markdown mdResponse, err := exporter.ExportMarkdown(overview) if err != nil { t.Fatalf("Failed to export empty overview as markdown: %v", err) } if mdResponse.Content == "" { t.Error("Expected non-empty markdown content") } // Test PDF pdfResponse, err := exporter.ExportManagementMemo(overview) if err != nil { t.Fatalf("Failed to export empty overview as PDF: %v", err) } if pdfResponse.Content == "" { t.Error("Expected non-empty PDF content") } } func TestPDFExporter_WithIncidentDeadlines(t *testing.T) { overview := createTestOverview() overview.IncidentDeadlines = []IncidentDeadline{ { RegulationID: "nis2", Phase: "Erstmeldung", Deadline: "24 Stunden", Recipient: "BSI", Content: "Unverzuegliche Meldung erheblicher Sicherheitsvorfaelle", }, { RegulationID: "dsgvo", Phase: "Meldung an Aufsichtsbehoerde", Deadline: "72 Stunden", Recipient: "Zustaendige Aufsichtsbehoerde", Content: "Meldung bei Datenschutzverletzung", }, } exporter := NewPDFExporter("de") // Test that incident deadlines are included mdResponse, err := exporter.ExportMarkdown(overview) if err != nil { t.Fatalf("Failed to export: %v", err) } if !strings.Contains(mdResponse.Content, "24 Stunden") { t.Error("Expected markdown to contain 24 hour deadline") } if !strings.Contains(mdResponse.Content, "72 Stunden") { t.Error("Expected markdown to contain 72 hour deadline") } } func createTestOverview() *ManagementObligationsOverview { deadline := time.Date(2025, 1, 17, 0, 0, 0, 0, time.UTC) return &ManagementObligationsOverview{ ID: uuid.New(), TenantID: uuid.New(), OrganizationName: "Test GmbH", AssessmentDate: time.Now(), CreatedAt: time.Now(), UpdatedAt: time.Now(), ApplicableRegulations: []ApplicableRegulation{ { ID: "nis2", Name: "NIS2-Richtlinie", Classification: "wichtige_einrichtung", Reason: "Anbieter digitaler Dienste", ObligationCount: 5, ControlCount: 3, }, { ID: "dsgvo", Name: "DSGVO", Classification: "controller", Reason: "Verarbeitung personenbezogener Daten", ObligationCount: 4, ControlCount: 2, }, }, Obligations: []Obligation{ { ID: "NIS2-OBL-001", RegulationID: "nis2", Title: "BSI-Registrierung", Description: "Registrierung beim BSI", LegalBasis: []LegalReference{{Norm: "ยง 33 BSIG-E"}}, Category: CategoryMeldepflicht, Responsible: RoleManagement, Deadline: &Deadline{Type: DeadlineAbsolute, Date: &deadline}, Sanctions: &SanctionInfo{MaxFine: "500.000 EUR"}, Priority: PriorityCritical, }, { ID: "DSGVO-OBL-001", RegulationID: "dsgvo", Title: "Verarbeitungsverzeichnis", Description: "Fuehrung eines VVT", LegalBasis: []LegalReference{{Norm: "Art. 30 DSGVO"}}, Category: CategoryGovernance, Responsible: RoleDSB, Priority: PriorityHigh, }, }, ExecutiveSummary: ExecutiveSummary{ TotalRegulations: 2, TotalObligations: 9, CriticalObligations: 1, UpcomingDeadlines: 2, OverdueObligations: 0, KeyRisks: []string{ "BSI-Registrierung faellig", "Persoenliche Haftung moeglich", }, RecommendedActions: []string{ "BSI-Registrierung durchfuehren", "ISMS aufbauen", }, ComplianceScore: 75, }, SanctionsSummary: SanctionsSummary{ MaxFinancialRisk: "10 Mio. EUR oder 2% Jahresumsatz", PersonalLiabilityRisk: true, CriminalLiabilityRisk: false, AffectedRegulations: []string{"nis2", "dsgvo"}, Summary: "Hohe Bussgelder moeglich. Persoenliche Haftung der Geschaeftsfuehrung bei Verstoessen.", }, } }