import { describe, expect, test } from "bun:test"; import { extensions, kindOfExtension } from "../src/kinds.ts"; import { RendererRegistry } from "../src/registry.ts"; import { resolve } from "../src/resolve.ts"; import { readBounded } from "../src/source.ts"; import { sniff } from "../src/sniff.ts"; const file = (name: string, bytes: string | number[], mime = "") => ({ name, blob: new Blob([typeof bytes === "string" ? bytes : new Uint8Array(bytes)], { type: mime }), }); describe("classification and signatures", () => { for (const [kind, exts] of Object.entries(extensions)) for (const ext of exts) { test(`classifies .${ext}`, () => expect(String(kindOfExtension(`.${ext.toUpperCase()}`))).toBe(kind)); } const signatures: [string, string | number[], string][] = [ ["png", [137, 80, 78, 71, 13, 10, 26, 10], "ImageViewer"], ["jpeg", [255, 216, 255], "ImageViewer"], ["gif", "GIF89a", "ImageViewer"], ["webp", "RIFF0000WEBP", "ImageViewer"], ["bmp", "BM00", "ImageViewer"], ["ico", [0, 0, 1, 0], "ImageViewer"], ["avif", "0000ftypavif", "ImageViewer"], ["pdf", "%PDF-1.7", "PdfViewer"], ["wav", "RIFF0000WAVE", "MediaViewer"], ["mp3", "ID3", "MediaViewer"], ["aac", [255, 241, 80, 128], "MediaViewer"], ["flac", "fLaC", "MediaViewer"], ["ogg", "OggS", "MediaViewer"], ["mp4", "0000ftypisom", "MediaViewer"], ["mov", "0000ftypqt ", "MediaViewer"], ["webm", [0x1a, 0x45, 0xdf, 0xa3], "MediaViewer"], ["txt", "hello", "CodeViewer"], ["js", "const value = 1;", "CodeViewer"], ["html", "", "HtmlViewer"], ]; for (const [ext, bytes, component] of signatures) test(`resolves ${ext}`, async () => { expect((await resolve(file(`test.${ext}`, bytes))).renderer.component).toBe(component); }); test("HTML masquerading as PNG is shown as inert source", async () => { const plan = await resolve( file("fake.png", "", "image/png"), ); expect(plan.kind).toBe("markup"); expect(plan.renderer.component).toBe("CodeViewer"); }); test("SVG masquerading as image never reaches native image rendering", async () => { const plan = await resolve(file("fake.png", '', "image/png")); expect(plan.kind).toBe("vector"); expect(plan.sandboxed).toBe(true); expect(plan.renderer.component).toBe("SvgViewer"); }); test("binary or executables cannot become text through MIME", async () => { expect((await resolve(file("fake.txt", [0, 1, 2], "text/plain"))).renderer.component).toBe( "DownloadCard", ); expect((await resolve(file("fake.png", "MZ123", "image/png"))).renderer.component).toBe( "DownloadCard", ); expect((await resolve(file("fake.exe", "hello"))).renderer.component).toBe("DownloadCard"); }); test("UTF-8 truncated at sniff boundary is still text", () => { expect(sniff(new Uint8Array([97, 0xe2, 0x82])).kind).toBe("text"); }); }); describe("degradation and network limits", () => { test("missing optional dependencies fall through to native renderer", async () => { const registry = new RendererRegistry(); registry.register({ kind: "pdf", exts: [".pdf"], tier: 2, component: "RichPdf", load: async () => { throw new Error("missing"); }, }); expect((await resolve(file("test.pdf", "%PDF-1.7"), { registry })).renderer.component).toBe( "PdfViewer", ); }); test("overrides can be removed and conflicting signatures bypass them", async () => { const registry = new RendererRegistry(); const unregister = registry.register({ kind: "text", exts: ["txt"], tier: 2, component: "Custom", }); expect((await resolve(file("test.txt", "hi"), { registry })).renderer.component).toBe("Custom"); expect((await resolve(file("test.txt", "%PDF-1.7"), { registry })).renderer.component).toBe( "PdfViewer", ); unregister(); expect((await resolve(file("test.txt", "hi"), { registry })).renderer.component).toBe( "CodeViewer", ); }); test("unavailable converters degrade", async () => { const registry = new RendererRegistry(); registry.register({ kind: "binary", exts: [".psd"], tier: 3, component: "Convert" }); expect((await resolve(file("image.psd", [0, 0, 0]), { registry })).renderer.component).toBe( "DownloadCard", ); }); test("oversized HEAD prevents body fetch", async () => { const methods: string[] = []; const request = (async (_url, init) => { methods.push(init?.method ?? "GET"); return new Response(null, { headers: { "content-length": "400000000" } }); }) as typeof fetch; const plan = await resolve( { name: "large.mp4", src: "https://example.test/file" }, { fetch: request }, ); expect(plan.renderer.component).toBe("DownloadCard"); expect(methods).toEqual(["HEAD"]); }); test("unknown-length or ignored Range stream is cancelled at prefix cap", async () => { let cancelled = false; const bytes = await readBounded( new Response( new ReadableStream({ start(c) { c.enqueue(new Uint8Array(8192)); }, cancel() { cancelled = true; }, }), ), 4096, true, ); expect(bytes.length).toBe(4096); expect(cancelled).toBe(true); }); test("full reads enforce actual bytes despite dishonest metadata", async () => { await expect( readBounded(new Response("123456", { headers: { "content-length": "1" } }), 3), ).rejects.toThrow("limit"); }); test("HEAD without support still performs bounded sniff", async () => { const request = (async (_url, init) => init?.method === "HEAD" ? new Response(null, { status: 405 }) : new Response("hello")) as typeof fetch; expect( (await resolve({ name: "text.txt", src: "/file" }, { fetch: request })).renderer.component, ).toBe("CodeViewer"); }); test("invalid URLs, rejected fetches and key-only sources degrade", async () => { expect((await resolve({ name: "a.txt", src: "javascript:alert(1)" })).renderer.component).toBe( "DownloadCard", ); expect((await resolve({ name: "a.txt", key: "private" })).renderer.component).toBe( "DownloadCard", ); const request = (async () => { throw new Error("offline"); }) as unknown as typeof fetch; expect( (await resolve({ name: "a.txt", src: "/file" }, { fetch: request })).renderer.component, ).toBe("DownloadCard"); }); }); describe("fallback reasons and unrenderable signatures", () => { test("a kind with no renderer reports why, not a size limit", async () => { const request = (async () => new Response("PK", { headers: { "content-length": "512", "content-type": "application/zip" }, })) as unknown as typeof fetch; const plan = await resolve( { name: "bundle.zip", src: "https://example.test/bundle.zip" }, { fetch: request }, ); expect(plan.kind).toBe("archive"); expect(plan.reason).toBe("Preview unavailable for this file"); }); test("design formats with ASCII-clean headers are not mistaken for text", async () => { const ascii = (signature: number[]) => { const bytes = new Uint8Array(4096).fill(0x41); bytes.set(signature); return bytes; }; expect(sniff(ascii([0x38, 0x42, 0x50, 0x53])).kind).toBe("binary"); expect(sniff(ascii([0x49, 0x49, 0x2a, 0x00])).kind).toBe("binary"); expect(sniff(ascii([0x4d, 0x4d, 0x00, 0x2a])).kind).toBe("binary"); expect( (await resolve({ name: "art.psd", blob: new Blob([ascii([0x38, 0x42, 0x50, 0x53])]) })) .renderer.component, ).toBe("DownloadCard"); }); });