import { spawnSync } from 'node:child_process'; import { tmpdir } from 'node:os'; import path from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; import { afterEach, describe, expect, it, vi } from 'vitest'; const mocks = vi.hoisted(() => ({ env: { PDF_REGENERATE: 'never' }, files: new Map(), })); vi.mock('../src/config/env.js', () => ({ env: mocks.env })); vi.mock('../src/pdf/render.js', () => ({ renderPdf: vi.fn(async (html) => Buffer.from(`pdf:${html}`)), })); vi.mock('../src/services/storage.js', () => ({ getObject: vi.fn(async (key) => { if (!mocks.files.has(key)) { throw new Error('missing'); } return mocks.files.get(key); }), putObject: vi.fn(async (key, body) => { mocks.files.set(key, Buffer.from(body)); }), })); const { refreshStoredPdf, rememberPdfSource } = await import('../src/services/pdfCache.js'); const { renderPdf } = await import('../src/pdf/render.js'); const KEY = 'applications/abc/consent.pdf'; describe('PDF_REGENERATE', () => { afterEach(() => { mocks.files.clear(); vi.mocked(renderPdf).mockClear(); }); it('never rebuilds a stored PDF by default', async () => { mocks.env.PDF_REGENERATE = 'never'; const build = vi.fn(async () => '
v2
'); expect(await refreshStoredPdf(KEY, build)).toBeNull(); expect(build).not.toHaveBeenCalled(); expect(renderPdf).not.toHaveBeenCalled(); }); it('rebuilds only when the html changed in changed mode', async () => { mocks.env.PDF_REGENERATE = 'changed'; await rememberPdfSource(KEY, 'v1
'); expect(await refreshStoredPdf(KEY, async () => 'v1
')).toBeNull(); expect(renderPdf).not.toHaveBeenCalled(); const rebuilt = await refreshStoredPdf(KEY, async () => 'v2
'); expect(String(rebuilt)).toBe('pdf:v2
'); expect(String(mocks.files.get(KEY))).toBe('pdf:v2
'); expect(await refreshStoredPdf(KEY, async () => 'v2
')).toBeNull(); expect(renderPdf).toHaveBeenCalledTimes(1); }); it('rebuilds a PDF with no recorded source in changed mode', async () => { mocks.env.PDF_REGENERATE = 'changed'; mocks.files.set(KEY, Buffer.from('old pdf')); expect(String(await refreshStoredPdf(KEY, async () => 'v1
'))).toBe('pdf:v1
'); }); it('rebuilds on every call in always mode', async () => { mocks.env.PDF_REGENERATE = 'always'; await refreshStoredPdf(KEY, async () => 'v1
'); await refreshStoredPdf(KEY, async () => 'v1
'); expect(renderPdf).toHaveBeenCalledTimes(2); }); it('shares one render between simultaneous requests for the same file', async () => { mocks.env.PDF_REGENERATE = 'always'; const build = vi.fn(async () => 'v1
'); await Promise.all([refreshStoredPdf(KEY, build), refreshStoredPdf(KEY, build)]); expect(build).toHaveBeenCalledTimes(1); expect(renderPdf).toHaveBeenCalledTimes(1); }); }); describe('PDF_REGENERATE in production', () => { const envModule = pathToFileURL( path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../src/config/env.js'), ).href; /** * Runs outside backend/ so a developer's backend/.env is not loaded. * @param {Record