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() }) }) })