mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-13 22:32:04 +00:00
The residual scan from the report is a CCITT Group 4 single-image PDF, the codec class pdfjs rasterization is known to fail on. Chain checks: a pdfjs open failure or throw already lands as render-failed with the error text annotated; the zero-pages shape now annotates too instead of returning silently. An end-to-end test drives the REAL rasterizer through prepareVisionInput and pins the rasterFailed mapping; the test header documents why a tiny in-repo CCITT fixture is deliberately not checked in (probed against this pdfjs version, it renders the page as blank instead of throwing, so the fixture cannot deterministically fail and would be flaky across upgrades). The detail view's raster-failed line now names scanned fax compression formats as a known cause, in all six locales. Refs #776
72 lines
2.9 KiB
TypeScript
72 lines
2.9 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
|
import { Buffer } from "node:buffer";
|
|
|
|
/**
|
|
* Refs #776 — the raster-failed chain, end to end through the REAL
|
|
* rasterizer: a PDF that pdfjs cannot open must surface as
|
|
* `{ ok: false, reason: "rasterFailed" }` from `prepareVisionInput` (for an
|
|
* image-only provider), which the index tree records as
|
|
* `lastIndexOutcome: "raster-failed"` — never silently, never mislabelled as
|
|
* "needs Anthropic".
|
|
*
|
|
* Fixture note (deliberate abstention, verified 2026-08-13): the reporter's
|
|
* residual scan is a CCITT Group 4 (`/CCITTFaxDecode`, `/K -1`) single-image
|
|
* PDF — a codec class pdfjs rasterization is known to fail on. A minimal
|
|
* programmatically generated CCITT-G4 PDF was probed against THIS repo's
|
|
* pdfjs: it renders the page as blank (`ok: true`, 1 page) rather than
|
|
* throwing, because pdfjs skips the undecodable image object — so a tiny
|
|
* in-repo CCITT fixture cannot deterministically go red on this version and
|
|
* would be flaky across pdfjs upgrades. The deterministic stand-in below (a
|
|
* PDF magic header over an unparseable body) exercises the same failure arm:
|
|
* pdfjs throws at open, `rasterizePdf` reports `render-failed` (with the
|
|
* pdfjs error text annotated), and the mapping to `rasterFailed` is what the
|
|
* outcome column persists.
|
|
*/
|
|
|
|
vi.mock("@/lib/documents/store", () => ({
|
|
decryptDocumentContent: vi.fn(),
|
|
}));
|
|
|
|
import { prepareVisionInput } from "../ai-route-support";
|
|
import { rasterizePdf } from "../rasterize-pdf";
|
|
import { decryptDocumentContent } from "@/lib/documents/store";
|
|
|
|
/** PDF magic bytes over a body pdfjs cannot parse. */
|
|
const UNRENDERABLE_PDF = Buffer.concat([
|
|
Buffer.from("%PDF-1.4\n"),
|
|
Buffer.from(Array.from({ length: 64 }, (_, i) => (i * 37) % 256)),
|
|
]);
|
|
|
|
const DOC = {
|
|
id: "doc-1",
|
|
kind: "OTHER",
|
|
contentEncrypted: new Uint8Array([1]),
|
|
contentCodec: "binary2",
|
|
mimeType: "application/pdf",
|
|
status: "STORED",
|
|
};
|
|
|
|
describe("raster-failed end to end (#776)", () => {
|
|
it("rasterizePdf reports render-failed for a PDF pdfjs cannot open", async () => {
|
|
const result = await rasterizePdf(UNRENDERABLE_PDF);
|
|
expect(result).toEqual({ ok: false, reason: "render-failed" });
|
|
});
|
|
|
|
it("prepareVisionInput maps it to rasterFailed for an image-only provider", async () => {
|
|
vi.mocked(decryptDocumentContent).mockReturnValue(
|
|
UNRENDERABLE_PDF as never,
|
|
);
|
|
// pdfSupported=false → the raster path runs for real.
|
|
const vision = await prepareVisionInput(DOC as never, false);
|
|
expect(vision).toEqual({ ok: false, reason: "rasterFailed" });
|
|
});
|
|
|
|
it("a PDF-capable provider never rasterizes, so the same bytes stay readable natively", async () => {
|
|
vi.mocked(decryptDocumentContent).mockReturnValue(
|
|
UNRENDERABLE_PDF as never,
|
|
);
|
|
const vision = await prepareVisionInput(DOC as never, true);
|
|
expect(vision).toMatchObject({ ok: true });
|
|
});
|
|
});
|