fix: Restore all files lost during destructive rebase
A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.
This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).
Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
238
ai-compliance-sdk/internal/ucca/pdf_export_test.go
Normal file
238
ai-compliance-sdk/internal/ucca/pdf_export_test.go
Normal file
@@ -0,0 +1,238 @@
|
||||
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.",
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user