This repository has been archived on 2026-02-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
breakpilot-pwa/admin-v2/lib/sdk/dsfa/__tests__/api.test.ts
Benjamin Admin 83e32dc289 feat(sdk): Add DSFA (Art. 35 DSGVO) Editor and API Client
Implements comprehensive Data Protection Impact Assessment tooling:
- 5-section wizard following Art. 35 DSGVO structure
- Interactive risk matrix with likelihood/impact scoring
- Mitigation management linked to risks
- DPO approval workflow (draft → in_review → approved/rejected)
- UCCA integration for auto-triggering DSFA from assessments
- Full TypeScript types and API client with 42 test cases

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-09 07:23:46 +01:00

356 lines
9.2 KiB
TypeScript

import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
// Mock fetch globally
const mockFetch = vi.fn()
global.fetch = mockFetch
describe('DSFA API Client', () => {
beforeEach(() => {
mockFetch.mockClear()
})
afterEach(() => {
vi.restoreAllMocks()
})
describe('listDSFAs', () => {
it('should fetch DSFAs without status filter', async () => {
const mockDSFAs = [
{ id: 'dsfa-1', name: 'Test DSFA 1', status: 'draft' },
{ id: 'dsfa-2', name: 'Test DSFA 2', status: 'approved' },
]
mockFetch.mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve({ dsfas: mockDSFAs }),
})
const { listDSFAs } = await import('../api')
const result = await listDSFAs()
expect(mockFetch).toHaveBeenCalledTimes(1)
expect(result).toHaveLength(2)
expect(result[0].name).toBe('Test DSFA 1')
})
it('should fetch DSFAs with status filter', async () => {
const mockDSFAs = [{ id: 'dsfa-1', name: 'Draft DSFA', status: 'draft' }]
mockFetch.mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve({ dsfas: mockDSFAs }),
})
const { listDSFAs } = await import('../api')
const result = await listDSFAs('draft')
expect(mockFetch).toHaveBeenCalledTimes(1)
const calledUrl = mockFetch.mock.calls[0][0]
expect(calledUrl).toContain('status=draft')
})
it('should return empty array when no DSFAs', async () => {
mockFetch.mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve({ dsfas: null }),
})
const { listDSFAs } = await import('../api')
const result = await listDSFAs()
expect(result).toEqual([])
})
})
describe('getDSFA', () => {
it('should fetch a single DSFA by ID', async () => {
const mockDSFA = {
id: 'dsfa-123',
name: 'Test DSFA',
status: 'draft',
risks: [],
mitigations: [],
}
mockFetch.mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve(mockDSFA),
})
const { getDSFA } = await import('../api')
const result = await getDSFA('dsfa-123')
expect(mockFetch).toHaveBeenCalledTimes(1)
expect(result.id).toBe('dsfa-123')
expect(result.name).toBe('Test DSFA')
})
it('should throw error for non-existent DSFA', async () => {
mockFetch.mockResolvedValueOnce({
ok: false,
status: 404,
text: () => Promise.resolve('{"error": "DSFA not found"}'),
})
const { getDSFA } = await import('../api')
await expect(getDSFA('non-existent')).rejects.toThrow()
})
})
describe('createDSFA', () => {
it('should create a new DSFA', async () => {
const newDSFA = {
name: 'New DSFA',
description: 'Test description',
processing_purpose: 'Testing',
}
const createdDSFA = {
id: 'dsfa-new',
...newDSFA,
status: 'draft',
}
mockFetch.mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve(createdDSFA),
})
const { createDSFA } = await import('../api')
const result = await createDSFA(newDSFA)
expect(mockFetch).toHaveBeenCalledTimes(1)
expect(result.id).toBe('dsfa-new')
expect(result.name).toBe('New DSFA')
})
})
describe('updateDSFA', () => {
it('should update an existing DSFA', async () => {
const updates = {
name: 'Updated DSFA Name',
processing_purpose: 'Updated purpose',
}
const updatedDSFA = {
id: 'dsfa-123',
...updates,
status: 'draft',
}
mockFetch.mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve(updatedDSFA),
})
const { updateDSFA } = await import('../api')
const result = await updateDSFA('dsfa-123', updates)
expect(mockFetch).toHaveBeenCalledTimes(1)
expect(result.name).toBe('Updated DSFA Name')
})
})
describe('deleteDSFA', () => {
it('should delete a DSFA', async () => {
mockFetch.mockResolvedValueOnce({
ok: true,
})
const { deleteDSFA } = await import('../api')
await deleteDSFA('dsfa-123')
expect(mockFetch).toHaveBeenCalledTimes(1)
const calledConfig = mockFetch.mock.calls[0][1]
expect(calledConfig.method).toBe('DELETE')
})
it('should throw error when deletion fails', async () => {
mockFetch.mockResolvedValueOnce({
ok: false,
statusText: 'Not Found',
})
const { deleteDSFA } = await import('../api')
await expect(deleteDSFA('non-existent')).rejects.toThrow()
})
})
describe('updateDSFASection', () => {
it('should update a specific section', async () => {
const sectionData = {
processing_purpose: 'Updated purpose',
data_categories: ['personal_data', 'contact_data'],
}
const updatedDSFA = {
id: 'dsfa-123',
section_progress: {
section_1_complete: true,
},
}
mockFetch.mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve(updatedDSFA),
})
const { updateDSFASection } = await import('../api')
const result = await updateDSFASection('dsfa-123', 1, sectionData)
expect(mockFetch).toHaveBeenCalledTimes(1)
const calledUrl = mockFetch.mock.calls[0][0]
expect(calledUrl).toContain('/sections/1')
})
})
describe('submitDSFAForReview', () => {
it('should submit DSFA for review', async () => {
const response = {
message: 'DSFA submitted for review',
dsfa: {
id: 'dsfa-123',
status: 'in_review',
},
}
mockFetch.mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve(response),
})
const { submitDSFAForReview } = await import('../api')
const result = await submitDSFAForReview('dsfa-123')
expect(mockFetch).toHaveBeenCalledTimes(1)
expect(result.dsfa.status).toBe('in_review')
})
})
describe('approveDSFA', () => {
it('should approve a DSFA', async () => {
mockFetch.mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve({ message: 'DSFA approved' }),
})
const { approveDSFA } = await import('../api')
const result = await approveDSFA('dsfa-123', {
dpo_opinion: 'Approved after review',
approved: true,
})
expect(mockFetch).toHaveBeenCalledTimes(1)
expect(result.message).toBe('DSFA approved')
})
it('should reject a DSFA', async () => {
mockFetch.mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve({ message: 'DSFA rejected' }),
})
const { approveDSFA } = await import('../api')
const result = await approveDSFA('dsfa-123', {
dpo_opinion: 'Needs more details',
approved: false,
})
expect(result.message).toBe('DSFA rejected')
})
})
describe('getDSFAStats', () => {
it('should fetch DSFA statistics', async () => {
const stats = {
total: 10,
status_stats: {
draft: 4,
in_review: 2,
approved: 3,
rejected: 1,
},
risk_stats: {
low: 3,
medium: 4,
high: 2,
very_high: 1,
},
}
mockFetch.mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve(stats),
})
const { getDSFAStats } = await import('../api')
const result = await getDSFAStats()
expect(mockFetch).toHaveBeenCalledTimes(1)
expect(result.total).toBe(10)
expect(result.status_stats.approved).toBe(3)
})
})
describe('createDSFAFromAssessment', () => {
it('should create DSFA from UCCA assessment', async () => {
const response = {
dsfa: {
id: 'dsfa-new',
name: 'AI Chatbot DSFA',
status: 'draft',
},
prefilled: true,
message: 'DSFA created from assessment',
}
mockFetch.mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve(response),
})
const { createDSFAFromAssessment } = await import('../api')
const result = await createDSFAFromAssessment('assessment-123')
expect(mockFetch).toHaveBeenCalledTimes(1)
expect(result.prefilled).toBe(true)
expect(result.dsfa.id).toBe('dsfa-new')
})
})
describe('getDSFAByAssessment', () => {
it('should return DSFA linked to assessment', async () => {
const dsfa = {
id: 'dsfa-123',
assessment_id: 'assessment-123',
name: 'Linked DSFA',
}
mockFetch.mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve(dsfa),
})
const { getDSFAByAssessment } = await import('../api')
const result = await getDSFAByAssessment('assessment-123')
expect(result?.id).toBe('dsfa-123')
})
it('should return null when no DSFA exists for assessment', async () => {
mockFetch.mockResolvedValueOnce({
ok: false,
status: 404,
text: () => Promise.resolve('Not found'),
})
const { getDSFAByAssessment } = await import('../api')
const result = await getDSFAByAssessment('no-dsfa-assessment')
expect(result).toBeNull()
})
})
})