153 lines
4.5 KiB
TypeScript
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-');
|
|
});
|
|
});
|