import { describe, it, expect, vi, beforeEach } from "vitest"; /** * Pinning tests for audit finding M12: a shared Puppeteer browser that * disconnects between getBrowser()'s point-in-time `connected` check and the * actual render must be re-acquired once — instead of failing the request * with a 500 that an immediate retry would have served fine. */ const { launchMock } = vi.hoisted(() => ({ launchMock: vi.fn() })); vi.mock("puppeteer", () => ({ default: { launch: launchMock } })); function healthyBrowser(pdfBytes = new Uint8Array([1, 2, 3])) { return { connected: true, newPage: vi.fn(async () => ({ setContent: vi.fn(async () => undefined), pdf: vi.fn(async () => pdfBytes), close: vi.fn(async () => undefined), })), close: vi.fn(async () => undefined), }; } /** Browser whose connection dies the moment a page is requested — the * post-guard crash window from the finding. */ function dyingBrowser() { const b = { connected: true, newPage: vi.fn(async () => { b.connected = false; throw new Error("Protocol error (Target.createTarget): Session closed."); }), close: vi.fn(async () => undefined), }; return b; } beforeEach(async () => { vi.resetModules(); launchMock.mockReset(); }); describe("htmlToPdf browser re-acquire (audit M12)", () => { it("relaunches once and resolves when the cached browser died mid-acquire", async () => { launchMock .mockResolvedValueOnce(dyingBrowser()) .mockResolvedValueOnce(healthyBrowser()); const { htmlToPdf } = await import("../utils/html-to-pdf"); const pdf = await htmlToPdf("retry"); expect(Buffer.isBuffer(pdf)).toBe(true); expect(launchMock).toHaveBeenCalledTimes(2); }); it("does NOT retry a genuine render error on a healthy browser", async () => { const browser = healthyBrowser(); browser.newPage = vi.fn(async () => ({ setContent: vi.fn(async () => { throw new Error("Render timeout"); }), pdf: vi.fn(async () => new Uint8Array()), close: vi.fn(async () => undefined), })); launchMock.mockResolvedValue(browser); const { htmlToPdf } = await import("../utils/html-to-pdf"); await expect(htmlToPdf("")).rejects.toThrow("Render timeout"); expect(launchMock).toHaveBeenCalledTimes(1); }); });