mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
154 lines
5.0 KiB
TypeScript
154 lines
5.0 KiB
TypeScript
/**
|
|||
|
|
* Expanded format detection tests covering uncovered magic byte branches:
|
||
|
|
* - CR3 (Canon ISOBMFF RAW with ftyp "crx ")
|
||
|
|
* - DPX reverse byte order
|
||
|
|
* - Cineon
|
||
|
|
* - Fujifilm RAF
|
||
|
|
* - Sigma X3F
|
||
|
|
* - Minolta MRW
|
||
|
|
* - RIFF with buffer too short for WEBP check
|
||
|
|
* - AVIF ftyp with buffer exactly 12 bytes (edge case)
|
||
|
|
* - CR3 ftyp with buffer too short
|
||
|
|
*/
|
||
|
|
import { detectFormat } from "@snapotter/image-engine";
|
||
|
|
import { describe, expect, it } from "vitest";
|
||
|
|
|
||
|
|
describe("detectFormat expanded magic byte branches", () => {
|
||
|
|
it("detects CR3 magic bytes (ftyp at offset 4 with crx brand)", async () => {
|
||
|
|
const buf = Buffer.alloc(16);
|
||
|
|
// ftyp at offset 4
|
||
|
|
buf[4] = 0x66;
|
||
|
|
buf[5] = 0x74;
|
||
|
|
buf[6] = 0x79;
|
||
|
|
buf[7] = 0x70;
|
||
|
|
// brand "crx " at offset 8
|
||
|
|
buf[8] = 0x63;
|
||
|
|
buf[9] = 0x72;
|
||
|
|
buf[10] = 0x78;
|
||
|
|
buf[11] = 0x20;
|
||
|
|
const format = await detectFormat(buf);
|
||
|
|
expect(format).toBe("cr3");
|
||
|
|
});
|
||
|
|
|
||
|
|
it("rejects ftyp box with non-CR3 brand when CR3 entry is checked", async () => {
|
||
|
|
const buf = Buffer.alloc(16);
|
||
|
|
buf[4] = 0x66;
|
||
|
|
buf[5] = 0x74;
|
||
|
|
buf[6] = 0x79;
|
||
|
|
buf[7] = 0x70;
|
||
|
|
// "mp41" brand -- not AVIF and not CR3
|
||
|
|
buf[8] = 0x6d;
|
||
|
|
buf[9] = 0x70;
|
||
|
|
buf[10] = 0x34;
|
||
|
|
buf[11] = 0x31;
|
||
|
|
const format = await detectFormat(buf);
|
||
|
|
// Should not be cr3 or avif
|
||
|
|
expect(format).not.toBe("cr3");
|
||
|
|
expect(format).not.toBe("avif");
|
||
|
|
});
|
||
|
|
|
||
|
|
it("rejects CR3 ftyp when buffer is too short for brand (< 12 bytes)", async () => {
|
||
|
|
const buf = Buffer.alloc(8);
|
||
|
|
buf[4] = 0x66;
|
||
|
|
buf[5] = 0x74;
|
||
|
|
buf[6] = 0x79;
|
||
|
|
buf[7] = 0x70;
|
||
|
|
const format = await detectFormat(buf);
|
||
|
|
expect(format).not.toBe("cr3");
|
||
|
|
});
|
||
|
|
|
||
|
|
it("detects DPX reverse byte order magic bytes", async () => {
|
||
|
|
const buf = Buffer.from([0x58, 0x50, 0x44, 0x53, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||
|
|
const format = await detectFormat(buf);
|
||
|
|
expect(format).toBe("dpx");
|
||
|
|
});
|
||
|
|
|
||
|
|
it("detects Cineon magic bytes", async () => {
|
||
|
|
const buf = Buffer.from([0x80, 0x2a, 0x5f, 0xd7, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||
|
|
const format = await detectFormat(buf);
|
||
|
|
expect(format).toBe("cin");
|
||
|
|
});
|
||
|
|
|
||
|
|
it("detects Fujifilm RAF magic bytes", async () => {
|
||
|
|
// "FUJIFILMCCD-RAW" = 15 bytes
|
||
|
|
const signature = Buffer.from("FUJIFILMCCD-RAW", "ascii");
|
||
|
|
const buf = Buffer.alloc(20);
|
||
|
|
signature.copy(buf, 0);
|
||
|
|
const format = await detectFormat(buf);
|
||
|
|
expect(format).toBe("raf");
|
||
|
|
});
|
||
|
|
|
||
|
|
it("detects Sigma X3F magic bytes (FOVb)", async () => {
|
||
|
|
const buf = Buffer.from([0x46, 0x4f, 0x56, 0x62, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||
|
|
const format = await detectFormat(buf);
|
||
|
|
expect(format).toBe("x3f");
|
||
|
|
});
|
||
|
|
|
||
|
|
it("detects Minolta MRW magic bytes", async () => {
|
||
|
|
const buf = Buffer.from([0x00, 0x4d, 0x52, 0x4d, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||
|
|
const format = await detectFormat(buf);
|
||
|
|
expect(format).toBe("mrw");
|
||
|
|
});
|
||
|
|
|
||
|
|
it("RIFF header in 8-byte buffer is handled (Sharp may detect or magic bytes skip)", async () => {
|
||
|
|
// RIFF header in only 8 bytes -- the WEBP magic byte check requires 12 bytes,
|
||
|
|
// but Sharp's metadata() may still detect it as webp from the RIFF header alone.
|
||
|
|
const buf = Buffer.alloc(8);
|
||
|
|
buf[0] = 0x52;
|
||
|
|
buf[1] = 0x49;
|
||
|
|
buf[2] = 0x46;
|
||
|
|
buf[3] = 0x46;
|
||
|
|
const format = await detectFormat(buf);
|
||
|
|
// Sharp may report "webp" from the RIFF header, or magic bytes may skip it.
|
||
|
|
// Either way the function should not throw.
|
||
|
|
expect(typeof format).toBe("string");
|
||
|
|
});
|
||
|
|
|
||
|
|
it("detects AVIF ftyp with exactly 12-byte buffer (minimum for brand check)", async () => {
|
||
|
|
const buf = Buffer.alloc(12);
|
||
|
|
buf[4] = 0x66;
|
||
|
|
buf[5] = 0x74;
|
||
|
|
buf[6] = 0x79;
|
||
|
|
buf[7] = 0x70;
|
||
|
|
buf[8] = 0x61;
|
||
|
|
buf[9] = 0x76;
|
||
|
|
buf[10] = 0x69;
|
||
|
|
buf[11] = 0x66;
|
||
|
|
const format = await detectFormat(buf);
|
||
|
|
expect(format).toBe("avif");
|
||
|
|
});
|
||
|
|
|
||
|
|
it("buffer with only magic byte prefix but too short for full match returns unknown", async () => {
|
||
|
|
// PNG magic needs 4 bytes but buffer is only 3
|
||
|
|
const buf = Buffer.from([0x89, 0x50, 0x4e]);
|
||
|
|
const format = await detectFormat(buf);
|
||
|
|
expect(format).toBe("unknown");
|
||
|
|
});
|
||
|
|
|
||
|
|
it("buffer with ICO-like bytes but too short", async () => {
|
||
|
|
// ICO needs 4 bytes: 00 00 01 00
|
||
|
|
const buf = Buffer.from([0x00, 0x00, 0x01]);
|
||
|
|
const format = await detectFormat(buf);
|
||
|
|
expect(format).toBe("unknown");
|
||
|
|
});
|
||
|
|
|
||
|
|
it("two-byte buffer matching only first two bytes of a 4-byte signature", async () => {
|
||
|
|
// BMP starts with 0x42 0x4D (2 bytes) but needs at least 2 for match
|
||
|
|
const buf = Buffer.from([0x42, 0x4d]);
|
||
|
|
const format = await detectFormat(buf);
|
||
|
|
expect(format).toBe("bmp");
|
||
|
|
});
|
||
|
|
|
||
|
|
it("FITS magic needs 6 bytes, 5-byte buffer is too short", async () => {
|
||
|
|
const buf = Buffer.from([0x53, 0x49, 0x4d, 0x50, 0x4c]);
|
||
|
|
const format = await detectFormat(buf);
|
||
|
|
expect(format).toBe("unknown");
|
||
|
|
});
|
||
|
|
|
||
|
|
it("EPS ASCII magic needs 10 bytes, 9-byte buffer is too short", async () => {
|
||
|
|
const buf = Buffer.from([0x25, 0x21, 0x50, 0x53, 0x2d, 0x41, 0x64, 0x6f, 0x62]);
|
||
|
|
const format = await detectFormat(buf);
|
||
|
|
expect(format).toBe("unknown");
|
||
|
|
});
|
||
|
|
});
|