import { describe, it, expect } from 'vitest'; import { pdfGenerator } from './pdf-generator'; const mockData = { exposureSummary: { totalExposures: 10, newExposures: 3, resolvedExposures: 2, criticalExposures: 1, warningExposures: 4, infoExposures: 5, exposuresBySource: {}, }, spamStats: { callsBlocked: 15, textsBlocked: 20, callsFlagged: 5, textsFlagged: 8, falsePositives: 2, totalSpamEvents: 35, }, voiceStats: { analysesRun: 50, threatsDetected: 3, enrollmentsActive: 2, syntheticDetections: 3, voiceMismatchEvents: 3, }, recommendations: [], protectionScore: 75, }; const pdfContext = { reportTitle: 'Monthly Protection Report — January 2025', periodStart: '2025-01-01T00:00:00.000Z', periodEnd: '2025-01-31T23:59:59.000Z', generatedAt: '2025-02-01T00:00:00.000Z', data: mockData, reportId: 'test-1', }; describe('PdfGenerator', () => { it('generates a non-empty PDF buffer', async () => { const pdf = await pdfGenerator.generate(pdfContext); expect(pdf).toBeInstanceOf(Buffer); expect(pdf.length).toBeGreaterThan(100); }); it('PDF starts with PDF magic bytes', async () => { const pdf = await pdfGenerator.generate(pdfContext); const header = pdf.subarray(0, 5).toString(); expect(header).toBe('%PDF-'); }); it('PDF ends with %%EOF', async () => { const pdf = await pdfGenerator.generate(pdfContext); const footer = pdf.subarray(-6).toString(); expect(footer).toContain('%%EOF'); }); it('PDF contains xref table', async () => { const pdf = await pdfGenerator.generate(pdfContext); const text = pdf.toString('utf8'); expect(text).toContain('xref'); expect(text).toContain('trailer'); expect(text).toContain('startxref'); }); it('PDF contains multiple pages', async () => { const pdf = await pdfGenerator.generate(pdfContext); const text = pdf.toString('utf8'); // PDFKit creates multiple pages for our report expect(text).toContain('/Count 3'); }); it('PDF registers both font families', async () => { const pdf = await pdfGenerator.generate(pdfContext); const text = pdf.toString('utf8'); expect(text).toContain('Helvetica'); expect(text).toContain('Helvetica-Bold'); }); it('generates PDF with home title section (more content)', async () => { const basePdf = await pdfGenerator.generate(pdfContext); const premiumPdf = await pdfGenerator.generate({ ...pdfContext, data: { ...mockData, homeTitleStats: { propertiesMonitored: 2, changesDetected: 1, alertsTriggered: 3, }, }, }); // Premium report with extra section should be larger expect(premiumPdf.length).toBeGreaterThan(basePdf.length); }); it('generates PDF with recommendations (more content)', async () => { const basePdf = await pdfGenerator.generate(pdfContext); const withRecs = await pdfGenerator.generate({ ...pdfContext, data: { ...mockData, recommendations: [ { category: 'dark_web', priority: 'high', title: 'Address Critical Exposures', description: '1 critical exposure detected.', }, ], }, }); // Report with recommendations should be larger expect(withRecs.length).toBeGreaterThan(basePdf.length); }); it('generates PDF with score change (more content)', async () => { const basePdf = await pdfGenerator.generate(pdfContext); const withChange = await pdfGenerator.generate({ ...pdfContext, data: { ...mockData, protectionScore: 80, previousProtectionScore: 70, }, }); // Report with score change text should be larger expect(withChange.length).toBeGreaterThan(basePdf.length); }); it('generates valid PDF with all required sections', async () => { const pdf = await pdfGenerator.generate(pdfContext); const text = pdf.toString('utf8'); // Structural validation expect(text).toContain('%PDF-'); expect(text).toContain('/Type /Catalog'); expect(text).toContain('/Type /Pages'); expect(text).toContain('/Type /Page'); expect(text).toContain('/ProcSet [/PDF /Text'); }); it('handles empty recommendations gracefully', async () => { const pdf = await pdfGenerator.generate(pdfContext); expect(pdf).toBeInstanceOf(Buffer); expect(pdf.length).toBeGreaterThan(100); const header = pdf.subarray(0, 5).toString(); expect(header).toBe('%PDF-'); }); });