Files
breakpilot-lehrer/admin-lehrer/lib/sdk/__tests__/export.test.ts
Benjamin Boenisch 5a31f52310 Initial commit: breakpilot-lehrer - Lehrer KI Platform
Services: Admin-Lehrer, Backend-Lehrer, Studio v2, Website,
Klausur-Service, School-Service, Voice-Service, Geo-Service,
BreakPilot Drive, Agent-Core

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 23:47:26 +01:00

325 lines
8.6 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from 'vitest'
import { exportToPDF, exportToZIP, downloadExport } from '../export'
import type { SDKState } from '../types'
// Mock jsPDF as a class
vi.mock('jspdf', () => {
return {
default: class MockJsPDF {
internal = {
pageSize: { getWidth: () => 210, getHeight: () => 297 },
}
setFillColor = vi.fn().mockReturnThis()
setDrawColor = vi.fn().mockReturnThis()
setTextColor = vi.fn().mockReturnThis()
setFontSize = vi.fn().mockReturnThis()
setFont = vi.fn().mockReturnThis()
setLineWidth = vi.fn().mockReturnThis()
text = vi.fn().mockReturnThis()
line = vi.fn().mockReturnThis()
rect = vi.fn().mockReturnThis()
roundedRect = vi.fn().mockReturnThis()
circle = vi.fn().mockReturnThis()
addPage = vi.fn().mockReturnThis()
setPage = vi.fn().mockReturnThis()
getNumberOfPages = vi.fn(() => 5)
splitTextToSize = vi.fn((text: string) => [text])
output = vi.fn(() => new Blob(['mock-pdf'], { type: 'application/pdf' }))
},
}
})
// Mock JSZip as a class
vi.mock('jszip', () => {
return {
default: class MockJSZip {
private mockFolder = {
file: vi.fn().mockReturnThis(),
folder: vi.fn(() => this.mockFolder),
}
folder = vi.fn(() => this.mockFolder)
generateAsync = vi.fn(() => Promise.resolve(new Blob(['mock-zip'], { type: 'application/zip' })))
},
}
})
const createMockState = (overrides: Partial<SDKState> = {}): SDKState => ({
version: '1.0.0',
lastModified: new Date('2024-01-15'),
tenantId: 'test-tenant',
userId: 'test-user',
subscription: 'PROFESSIONAL',
currentPhase: 1,
currentStep: 'use-case-workshop',
completedSteps: ['use-case-workshop', 'screening'],
checkpoints: {
'CP-UC': {
checkpointId: 'CP-UC',
passed: true,
validatedAt: new Date(),
validatedBy: 'SYSTEM',
errors: [],
warnings: [],
},
},
useCases: [
{
id: 'uc-1',
name: 'Test Use Case',
description: 'A test use case for testing',
category: 'Marketing',
stepsCompleted: 3,
steps: [
{ id: 's1', name: 'Step 1', completed: true, data: {} },
{ id: 's2', name: 'Step 2', completed: true, data: {} },
{ id: 's3', name: 'Step 3', completed: true, data: {} },
],
assessmentResult: null,
createdAt: new Date(),
updatedAt: new Date(),
},
],
activeUseCase: 'uc-1',
screening: null,
modules: [],
requirements: [],
controls: [
{
id: 'ctrl-1',
name: 'Test Control',
description: 'A test control',
type: 'TECHNICAL',
category: 'Access Control',
implementationStatus: 'IMPLEMENTED',
effectiveness: 'HIGH',
evidence: [],
owner: 'Test Owner',
dueDate: null,
},
],
evidence: [],
checklist: [],
risks: [
{
id: 'risk-1',
title: 'Test Risk',
description: 'A test risk',
category: 'Security',
likelihood: 3,
impact: 4,
severity: 'HIGH',
inherentRiskScore: 12,
residualRiskScore: 6,
status: 'MITIGATED',
mitigation: [
{
id: 'mit-1',
description: 'Test mitigation',
type: 'MITIGATE',
status: 'COMPLETED',
effectiveness: 80,
controlId: 'ctrl-1',
},
],
owner: 'Risk Owner',
relatedControls: ['ctrl-1'],
relatedRequirements: [],
},
],
aiActClassification: null,
obligations: [],
dsfa: null,
toms: [],
retentionPolicies: [],
vvt: [],
documents: [],
cookieBanner: null,
consents: [],
dsrConfig: null,
escalationWorkflows: [],
sbom: null,
securityIssues: [],
securityBacklog: [],
commandBarHistory: [],
recentSearches: [],
preferences: {
language: 'de',
theme: 'light',
compactMode: false,
showHints: true,
autoSave: true,
autoValidate: true,
allowParallelWork: true,
},
...overrides,
})
describe('exportToPDF', () => {
it('should return a Blob', async () => {
const state = createMockState()
const result = await exportToPDF(state)
expect(result).toBeInstanceOf(Blob)
})
it('should create a PDF with the correct type', async () => {
const state = createMockState()
const result = await exportToPDF(state)
expect(result.type).toBe('application/pdf')
})
it('should handle empty state', async () => {
const emptyState = createMockState({
useCases: [],
risks: [],
controls: [],
completedSteps: [],
})
const result = await exportToPDF(emptyState)
expect(result).toBeInstanceOf(Blob)
})
it('should handle state with multiple risks of different severities', async () => {
const state = createMockState({
risks: [
{
id: 'risk-1',
title: 'Critical Risk',
description: 'Critical',
category: 'Security',
likelihood: 5,
impact: 5,
severity: 'CRITICAL',
inherentRiskScore: 25,
residualRiskScore: 15,
status: 'IDENTIFIED',
mitigation: [],
owner: null,
relatedControls: [],
relatedRequirements: [],
},
{
id: 'risk-2',
title: 'Low Risk',
description: 'Low',
category: 'Operational',
likelihood: 1,
impact: 1,
severity: 'LOW',
inherentRiskScore: 1,
residualRiskScore: 1,
status: 'ACCEPTED',
mitigation: [],
owner: null,
relatedControls: [],
relatedRequirements: [],
},
],
})
const result = await exportToPDF(state)
expect(result).toBeInstanceOf(Blob)
})
})
describe('exportToZIP', () => {
it('should return a Blob', async () => {
const state = createMockState()
const result = await exportToZIP(state)
expect(result).toBeInstanceOf(Blob)
})
it('should create a ZIP with the correct type', async () => {
const state = createMockState()
const result = await exportToZIP(state)
expect(result.type).toBe('application/zip')
})
it('should handle empty state', async () => {
const emptyState = createMockState({
useCases: [],
risks: [],
controls: [],
completedSteps: [],
})
const result = await exportToZIP(emptyState)
expect(result).toBeInstanceOf(Blob)
})
it('should respect includeEvidence option', async () => {
const state = createMockState()
const result = await exportToZIP(state, { includeEvidence: false })
expect(result).toBeInstanceOf(Blob)
})
it('should respect includeDocuments option', async () => {
const state = createMockState()
const result = await exportToZIP(state, { includeDocuments: false })
expect(result).toBeInstanceOf(Blob)
})
})
describe('downloadExport', () => {
let mockCreateElement: ReturnType<typeof vi.spyOn>
let mockAppendChild: ReturnType<typeof vi.spyOn>
let mockRemoveChild: ReturnType<typeof vi.spyOn>
let mockLink: { href: string; download: string; click: ReturnType<typeof vi.fn> }
beforeEach(() => {
mockLink = {
href: '',
download: '',
click: vi.fn(),
}
mockCreateElement = vi.spyOn(document, 'createElement').mockReturnValue(mockLink as unknown as HTMLElement)
mockAppendChild = vi.spyOn(document.body, 'appendChild').mockImplementation(() => mockLink as unknown as HTMLElement)
mockRemoveChild = vi.spyOn(document.body, 'removeChild').mockImplementation(() => mockLink as unknown as HTMLElement)
})
it('should download JSON format', async () => {
const state = createMockState()
await downloadExport(state, 'json')
expect(mockLink.download).toContain('.json')
expect(mockLink.click).toHaveBeenCalled()
})
it('should download PDF format', async () => {
const state = createMockState()
await downloadExport(state, 'pdf')
expect(mockLink.download).toContain('.pdf')
expect(mockLink.click).toHaveBeenCalled()
})
it('should download ZIP format', async () => {
const state = createMockState()
await downloadExport(state, 'zip')
expect(mockLink.download).toContain('.zip')
expect(mockLink.click).toHaveBeenCalled()
})
it('should include date in filename', async () => {
const state = createMockState()
await downloadExport(state, 'json')
// Check that filename contains a date pattern
expect(mockLink.download).toMatch(/ai-compliance-sdk-\d{4}-\d{2}-\d{2}\.json/)
})
it('should throw error for unknown format', async () => {
const state = createMockState()
await expect(downloadExport(state, 'unknown' as any)).rejects.toThrow('Unknown export format')
})
})