Files
ShieldAI/packages/report/src/pdf-generator.test.ts

153 lines
4.5 KiB
TypeScript

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-');
});
});