149 lines
5.0 KiB
TypeScript
149 lines
5.0 KiB
TypeScript
import { describe, expect, it } from "@suaveplan/testing/runner";
|
|
import { MEDIA_CODES, MediaError } from "../errors/errors.js";
|
|
import {
|
|
BUILTIN_SIGNATURES,
|
|
defaultSignatureRegistry,
|
|
SignatureRegistry,
|
|
} from "./signatures.js";
|
|
|
|
describe("BUILTIN_SIGNATURES", () => {
|
|
it("includes PNG, JPEG, MP3, FLAC, OGG, WebM, MP4, WEBP", {
|
|
timeout: 5000,
|
|
}, () => {
|
|
const labels = BUILTIN_SIGNATURES.map((s) => s.label);
|
|
for (const expected of [
|
|
"PNG",
|
|
"JPEG",
|
|
"MP3-ID3",
|
|
"FLAC",
|
|
"OGG",
|
|
"WebM/MKV",
|
|
"MP4/MOV",
|
|
"WEBP",
|
|
]) {
|
|
expect(labels).toContain(expected);
|
|
}
|
|
});
|
|
|
|
it("is frozen", { timeout: 5000 }, () => {
|
|
expect(Object.isFrozen(BUILTIN_SIGNATURES)).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("SignatureRegistry", () => {
|
|
it("detects PNG via default registry", { timeout: 5000 }, () => {
|
|
const png = new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x00]);
|
|
expect(defaultSignatureRegistry.detect(png)).toBe("image");
|
|
});
|
|
|
|
it("returns 'unknown' for unrecognised bytes", { timeout: 5000 }, () => {
|
|
const bytes = new Uint8Array([0x00, 0x01, 0x02]);
|
|
expect(defaultSignatureRegistry.detect(bytes)).toBe("unknown");
|
|
});
|
|
|
|
it("match returns the signature record", { timeout: 5000 }, () => {
|
|
const flac = new Uint8Array([0x66, 0x4c, 0x61, 0x43, 0x00]);
|
|
const sig = defaultSignatureRegistry.match(flac);
|
|
expect(sig?.label).toBe("FLAC");
|
|
expect(sig?.type).toBe("audio");
|
|
});
|
|
|
|
it("match returns null for empty input", { timeout: 5000 }, () => {
|
|
expect(defaultSignatureRegistry.match(new Uint8Array([]))).toBeNull();
|
|
});
|
|
|
|
it("honours non-zero offset (MP4 ftyp)", { timeout: 5000 }, () => {
|
|
// 4 padding bytes then 'ftyp' (0x66 0x74 0x79 0x70)
|
|
const mp4 = new Uint8Array([
|
|
0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x69, 0x73, 0x6f,
|
|
0x6d,
|
|
]);
|
|
expect(defaultSignatureRegistry.detect(mp4)).toBe("video");
|
|
});
|
|
|
|
it("ignores signature when input shorter than offset+bytes", {
|
|
timeout: 5000,
|
|
}, () => {
|
|
const short = new Uint8Array([0x00]);
|
|
expect(defaultSignatureRegistry.detect(short)).toBe("unknown");
|
|
});
|
|
|
|
it("can register custom signature", { timeout: 5000 }, () => {
|
|
const reg = new SignatureRegistry([]);
|
|
reg.register({ type: "image", label: "X", bytes: [0xde, 0xad] });
|
|
const detected = reg.detect(new Uint8Array([0xde, 0xad, 0xbe]));
|
|
expect(detected).toBe("image");
|
|
});
|
|
|
|
it("unregister removes signature and returns true", {
|
|
timeout: 5000,
|
|
}, () => {
|
|
const reg = new SignatureRegistry();
|
|
expect(reg.unregister("PNG")).toBe(true);
|
|
expect(reg.detect(new Uint8Array([0x89, 0x50, 0x4e, 0x47]))).toBe(
|
|
"unknown"
|
|
);
|
|
});
|
|
|
|
it("unregister returns false for unknown label", {
|
|
timeout: 5000,
|
|
}, () => {
|
|
const reg = new SignatureRegistry();
|
|
expect(reg.unregister("NOPE")).toBe(false);
|
|
});
|
|
|
|
it("list returns frozen snapshot", { timeout: 5000 }, () => {
|
|
const reg = new SignatureRegistry();
|
|
const snap = reg.list();
|
|
expect(Object.isFrozen(snap)).toBe(true);
|
|
expect(snap.length).toBe(BUILTIN_SIGNATURES.length);
|
|
});
|
|
|
|
it("register rejects empty byte array", { timeout: 5000 }, () => {
|
|
const reg = new SignatureRegistry([]);
|
|
expect(() =>
|
|
reg.register({ type: "image", label: "E", bytes: [] })
|
|
).toThrow(MediaError);
|
|
});
|
|
|
|
it("register rejects non-byte values", { timeout: 5000 }, () => {
|
|
const reg = new SignatureRegistry([]);
|
|
try {
|
|
reg.register({ type: "image", label: "N", bytes: [256] });
|
|
throw new Error("should have thrown");
|
|
} catch (err) {
|
|
expect(err).toBeInstanceOf(MediaError);
|
|
expect((err as MediaError).code).toBe(
|
|
MEDIA_CODES.INVALID_SIGNATURE
|
|
);
|
|
}
|
|
});
|
|
|
|
it("register rejects negative byte values", { timeout: 5000 }, () => {
|
|
const reg = new SignatureRegistry([]);
|
|
expect(() =>
|
|
reg.register({ type: "image", label: "N", bytes: [-1] })
|
|
).toThrow(MediaError);
|
|
});
|
|
|
|
it("register rejects non-integer byte values", { timeout: 5000 }, () => {
|
|
const reg = new SignatureRegistry([]);
|
|
expect(() =>
|
|
reg.register({ type: "image", label: "N", bytes: [1.5] })
|
|
).toThrow(MediaError);
|
|
});
|
|
|
|
it("rejects duplicate label", { timeout: 5000 }, () => {
|
|
const reg = new SignatureRegistry();
|
|
try {
|
|
reg.register({ type: "image", label: "PNG", bytes: [0x00] });
|
|
throw new Error("should have thrown");
|
|
} catch (err) {
|
|
expect(err).toBeInstanceOf(MediaError);
|
|
expect((err as MediaError).code).toBe(
|
|
MEDIA_CODES.DUPLICATE_SIGNATURE
|
|
);
|
|
}
|
|
});
|
|
});
|