import { describe, expect, it } from "vitest"; import { assertMime, jpegWidthHeight, MimeMismatchError, pdfPageCount, pngWidthHeight, sniffMagicBytes, webpWidthHeight, imageDimensions } from "../lib/mime.js"; // --- test buffers ---------------------------------------------------------- const JPEG = Buffer.concat([ Buffer.from([0xff, 0xd8, 0xff, 0xe0]), Buffer.alloc(16) ]); const PNG = Buffer.concat([ Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]), Buffer.from([0, 0, 0, 13]), // IHDR length Buffer.from("IHDR"), Buffer.from([0, 0, 0, 0x78]), // width 120 Buffer.from([0, 0, 0, 0x80]) // height 128 ]); const WEBP = Buffer.concat([ Buffer.from("RIFF"), Buffer.from([0, 0, 0, 0]), Buffer.from("WEBP"), Buffer.from("VP8X"), Buffer.from([0]), // flags Buffer.alloc(3), // reserved Buffer.from([0x3f, 0, 0]), // width-1 = 63 → 64 Buffer.from([0x2f, 0, 0]) // height-1 = 47 → 48 ]); const GIF = Buffer.concat([Buffer.from("GIF89a"), Buffer.alloc(16)]); const PDF = Buffer.from("%PDF-1.7\n1 0 obj\n<< /Type /Page >>\nendobj\n<< /Type /Pages >>\n"); const ZIP = Buffer.concat([Buffer.from([0x50, 0x4b, 0x03, 0x04]), Buffer.alloc(16)]); const WEBM = Buffer.concat([Buffer.from([0x1a, 0x45, 0xdf, 0xa3]), Buffer.alloc(16)]); const MP4 = Buffer.concat([ Buffer.from([0, 0, 0, 24]), Buffer.from("ftypmp42"), Buffer.alloc(8) ]); const UTF16BE = Buffer.concat([Buffer.from([0xfe, 0xff]), Buffer.from([0x00, 0x68, 0x00, 0x69])]); const JSONBUF = Buffer.from('{"a":1}'); const XMLBUF = Buffer.from(''); const HTMLBUF = Buffer.from(""); /** Minimal JPEG with SOF0 → width 200, height 100. */ const JPEG_DIMS = Buffer.concat([ Buffer.from([0xff, 0xd8]), // SOI Buffer.from([0xff, 0xe0, 0x00, 0x10]), // APP0 with 14-byte payload Buffer.alloc(14), Buffer.from([0xff, 0xc0, 0x00, 0x0b, 0x08, 0, 100, 0, 200, 1, 1, 1, 0]) // SOF0 h=100 w=200 ]); describe("sniffMagicBytes", () => { it("image/jpeg", () => expect(sniffMagicBytes(JPEG)).toBe("image/jpeg")); it("image/png", () => expect(sniffMagicBytes(PNG)).toBe("image/png")); it("image/webp", () => expect(sniffMagicBytes(WEBP)).toBe("image/webp")); it("image/gif", () => expect(sniffMagicBytes(GIF)).toBe("image/gif")); it("application/pdf", () => expect(sniffMagicBytes(PDF)).toBe("application/pdf")); it("application/zip", () => expect(sniffMagicBytes(ZIP)).toBe("application/zip")); it("video/webm", () => expect(sniffMagicBytes(WEBM)).toBe("video/webm")); it("video/mp4", () => expect(sniffMagicBytes(MP4)).toBe("video/mp4")); it("text/utf16 (BOM)", () => expect(sniffMagicBytes(UTF16BE)).toBe("text/utf16")); it("application/json heuristic", () => expect(sniffMagicBytes(JSONBUF)).toBe("application/json")); it("text/xml", () => expect(sniffMagicBytes(XMLBUF)).toBe("text/xml")); it("text/html", () => expect(sniffMagicBytes(HTMLBUF)).toBe("text/html")); it("plain text → null", () => expect(sniffMagicBytes(Buffer.from("just some text"))).toBe(null)); it("empty buffer → null", () => expect(sniffMagicBytes(Buffer.alloc(0))).toBe(null)); it("truncated buffer (2 bytes) → null, no throw", () => expect(sniffMagicBytes(Buffer.from([0x50, 0x4b]))).toBe(null)); }); describe("assertMime", () => { it("exact match passes", () => { expect(() => assertMime("image/png", "image/png")).not.toThrow(); }); it("declared octet-stream always passes", () => { expect(() => assertMime("application/octet-stream", "application/pdf")).not.toThrow(); }); it("text/* family leniency: declared text/plain, sniffed null", () => { expect(() => assertMime("text/plain", null)).not.toThrow(); }); it("declared text/csv vs sniffed text/csv (family) passes", () => { expect(() => assertMime("text/csv", "text/csv")).not.toThrow(); }); it("json/xml declared with no magic passes", () => { expect(() => assertMime("application/json", null)).not.toThrow(); expect(() => assertMime("application/xml", null)).not.toThrow(); }); it("mismatch throws MimeMismatchError", () => { expect(() => assertMime("image/png", "application/zip")).toThrow(MimeMismatchError); try { assertMime("image/jpeg", "application/pdf"); expect.unreachable(); } catch (err) { expect(err).toBeInstanceOf(MimeMismatchError); expect((err as MimeMismatchError).declared).toBe("image/jpeg"); expect((err as MimeMismatchError).actual).toBe("application/pdf"); } }); it("binary declared but content is plain text → throws", () => { expect(() => assertMime("image/png", null)).toThrow(MimeMismatchError); }); }); describe("image dimension parsers", () => { it("pngWidthHeight reads IHDR", () => { expect(pngWidthHeight(PNG)).toEqual({ width: 120, height: 128 }); }); it("pngWidthHeight rejects non-PNG", () => { expect(pngWidthHeight(JPEG)).toBeNull(); }); it("jpegWidthHeight walks to SOF0", () => { expect(jpegWidthHeight(JPEG_DIMS)).toEqual({ width: 200, height: 100 }); }); it("jpegWidthHeight rejects non-JPEG", () => { expect(jpegWidthHeight(PNG)).toBeNull(); }); it("webpWidthHeight (VP8X extended)", () => { expect(webpWidthHeight(WEBP)).toEqual({ width: 64, height: 48 }); }); it("imageDimensions dispatches by mime", () => { expect(imageDimensions(PNG)).toEqual({ width: 120, height: 128 }); expect(imageDimensions(JPEG_DIMS)).toEqual({ width: 200, height: 100 }); expect(imageDimensions(WEBP)).toEqual({ width: 64, height: 48 }); expect(imageDimensions(GIF)).toBeNull(); // gif dims not implemented expect(imageDimensions(Buffer.from("text"))).toBeNull(); }); }); describe("pdfPageCount (approximate)", () => { it("counts /Type /Page but not /Type /Pages", () => { const pdf = Buffer.from( "%PDF-1.7\n<< /Type /Page >>\n<< /Type /Page >>\n<< /Type /Pages /Count 2 >>\n" ); expect(pdfPageCount(pdf)).toBe(2); }); it("returns null when no pages found", () => { expect(pdfPageCount(Buffer.from("%PDF-1.7\nnothing here"))).toBeNull(); }); });