224 lines
7.5 KiB
TypeScript
224 lines
7.5 KiB
TypeScript
import { describe, expect, it } from "@suaveplan/testing/runner";
|
|
import * as fc from "fast-check";
|
|
import { BUILTIN_DB } from "./builtin-db.js";
|
|
import type { MimeEntry } from "./mime-entry.types.js";
|
|
import {
|
|
_indexEntryForTesting,
|
|
getEntryByExtension,
|
|
getMimeEntry,
|
|
listMimeEntries,
|
|
registerMimeEntry,
|
|
} from "./registry.js";
|
|
|
|
describe("BUILTIN_DB", () => {
|
|
it("is a non-empty frozen array", { timeout: 5000 }, () => {
|
|
expect(Array.isArray(BUILTIN_DB)).toBe(true);
|
|
expect(BUILTIN_DB.length).toBeGreaterThan(50);
|
|
expect(Object.isFrozen(BUILTIN_DB)).toBe(true);
|
|
});
|
|
|
|
it("contains expected common MIME types", { timeout: 5000 }, () => {
|
|
const mimes = BUILTIN_DB.map((e) => e.mime);
|
|
expect(mimes).toContain("image/png");
|
|
expect(mimes).toContain("image/jpeg");
|
|
expect(mimes).toContain("application/json");
|
|
expect(mimes).toContain("text/html");
|
|
expect(mimes).toContain("video/mp4");
|
|
expect(mimes).toContain("audio/mpeg");
|
|
expect(mimes).toContain("font/woff2");
|
|
expect(mimes).toContain("application/pdf");
|
|
expect(mimes).toContain("application/zip");
|
|
});
|
|
|
|
it("every entry has valid shape", { timeout: 5000 }, () => {
|
|
for (const entry of BUILTIN_DB) {
|
|
expect(typeof entry.mime).toBe("string");
|
|
expect(entry.mime).toContain("/");
|
|
expect(Array.isArray(entry.extensions)).toBe(true);
|
|
expect(typeof entry.isBinary).toBe("boolean");
|
|
expect(entry.category).toBeTruthy();
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("getMimeEntry", () => {
|
|
it("finds image/png by exact MIME type", { timeout: 5000 }, () => {
|
|
const entry = getMimeEntry("image/png");
|
|
expect(entry).toBeDefined();
|
|
expect(entry?.mime).toBe("image/png");
|
|
expect(entry?.category).toBe("image");
|
|
});
|
|
|
|
it("is case-insensitive", { timeout: 5000 }, () => {
|
|
expect(getMimeEntry("IMAGE/PNG")).toEqual(getMimeEntry("image/png"));
|
|
});
|
|
|
|
it("returns undefined for unknown MIME type", { timeout: 5000 }, () => {
|
|
expect(getMimeEntry("x/nonexistent-mime-type-12345")).toBeUndefined();
|
|
});
|
|
|
|
it("finds application/json with correct charset", { timeout: 5000 }, () => {
|
|
const entry = getMimeEntry("application/json");
|
|
expect(entry?.charset).toBe("utf-8");
|
|
expect(entry?.compressible).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("getEntryByExtension", () => {
|
|
it("finds entry for 'png'", { timeout: 5000 }, () => {
|
|
const entry = getEntryByExtension("png");
|
|
expect(entry?.mime).toBe("image/png");
|
|
});
|
|
|
|
it("finds entry for '.png' (leading dot)", { timeout: 5000 }, () => {
|
|
const entry = getEntryByExtension(".png");
|
|
expect(entry?.mime).toBe("image/png");
|
|
});
|
|
|
|
it("is case-insensitive", { timeout: 5000 }, () => {
|
|
expect(getEntryByExtension("PNG")).toEqual(getEntryByExtension("png"));
|
|
});
|
|
|
|
it("returns undefined for unknown extension", { timeout: 5000 }, () => {
|
|
expect(getEntryByExtension("__not_a_real_extension__")).toBeUndefined();
|
|
});
|
|
|
|
it("finds 'jpg' extension", { timeout: 5000 }, () => {
|
|
expect(getEntryByExtension("jpg")?.mime).toBe("image/jpeg");
|
|
});
|
|
|
|
it("finds 'json' extension", { timeout: 5000 }, () => {
|
|
expect(getEntryByExtension("json")?.mime).toBe("application/json");
|
|
});
|
|
});
|
|
|
|
describe("listMimeEntries", () => {
|
|
it("returns at least as many entries as BUILTIN_DB", {
|
|
timeout: 5000,
|
|
}, () => {
|
|
const list = listMimeEntries();
|
|
expect(list.length).toBeGreaterThanOrEqual(BUILTIN_DB.length);
|
|
});
|
|
|
|
it("is a snapshot (different reference on each call)", {
|
|
timeout: 5000,
|
|
}, () => {
|
|
const a = listMimeEntries();
|
|
const b = listMimeEntries();
|
|
expect(a).not.toBe(b);
|
|
});
|
|
});
|
|
|
|
describe("registerMimeEntry", () => {
|
|
it("adds a new entry retrievable by mime type", { timeout: 5000 }, () => {
|
|
const custom: MimeEntry = {
|
|
mime: "application/x-test-custom-type-99",
|
|
extensions: ["xcustom99"],
|
|
category: "application",
|
|
isBinary: false,
|
|
};
|
|
registerMimeEntry(custom);
|
|
const found = getMimeEntry("application/x-test-custom-type-99");
|
|
expect(found).toEqual(custom);
|
|
});
|
|
|
|
it("registered entry is retrievable by extension", {
|
|
timeout: 5000,
|
|
}, () => {
|
|
const custom: MimeEntry = {
|
|
mime: "application/x-test-ext-lookup-99",
|
|
extensions: ["xextlookup99"],
|
|
category: "application",
|
|
isBinary: false,
|
|
};
|
|
registerMimeEntry(custom);
|
|
expect(getEntryByExtension("xextlookup99")?.mime).toBe(
|
|
"application/x-test-ext-lookup-99"
|
|
);
|
|
});
|
|
|
|
it("overrides an existing entry", { timeout: 5000 }, () => {
|
|
const override: MimeEntry = {
|
|
mime: "image/png",
|
|
extensions: ["png"],
|
|
category: "image",
|
|
isBinary: true,
|
|
compressible: true, // changed from false
|
|
};
|
|
registerMimeEntry(override);
|
|
const found = getMimeEntry("image/png");
|
|
expect(found?.compressible).toBe(true);
|
|
// Reset
|
|
registerMimeEntry({
|
|
mime: "image/png",
|
|
extensions: ["png"],
|
|
category: "image",
|
|
isBinary: true,
|
|
compressible: false,
|
|
});
|
|
});
|
|
|
|
it("appears in listMimeEntries", { timeout: 5000 }, () => {
|
|
const custom: MimeEntry = {
|
|
mime: "application/x-list-test-99",
|
|
extensions: [],
|
|
category: "application",
|
|
isBinary: true,
|
|
};
|
|
registerMimeEntry(custom);
|
|
const list = listMimeEntries();
|
|
expect(list.some((e) => e.mime === "application/x-list-test-99")).toBe(
|
|
true
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("_indexEntryForTesting (duplicate extension guard)", () => {
|
|
it("first-write-wins: duplicate extension does not override existing entry", {
|
|
timeout: 5000,
|
|
}, () => {
|
|
// "png" is already mapped to image/png in the builtin DB
|
|
const competitor: MimeEntry = {
|
|
mime: "image/x-fake-png",
|
|
extensions: ["png"],
|
|
category: "image",
|
|
isBinary: true,
|
|
};
|
|
_indexEntryForTesting(competitor);
|
|
// The original image/png entry should still own the "png" extension
|
|
const found = getEntryByExtension("png");
|
|
expect(found?.mime).toBe("image/png");
|
|
});
|
|
});
|
|
|
|
describe("registry round-trip (property-based)", () => {
|
|
it("every registered entry is retrievable by mime", {
|
|
timeout: 30000,
|
|
}, () => {
|
|
fc.assert(
|
|
fc.property(
|
|
fc.constantFrom(
|
|
"application",
|
|
"text",
|
|
"image",
|
|
"audio",
|
|
"video"
|
|
),
|
|
fc.stringMatching(/^[a-z][a-z0-9-]+$/),
|
|
(category, token) => {
|
|
const mime = `${category}/x-prop-test-${token}`;
|
|
const entry: MimeEntry = {
|
|
mime,
|
|
extensions: [token],
|
|
category: category as MimeEntry["category"],
|
|
isBinary: false,
|
|
};
|
|
registerMimeEntry(entry);
|
|
const found = getMimeEntry(mime);
|
|
expect(found?.mime).toBe(mime);
|
|
}
|
|
)
|
|
);
|
|
});
|
|
});
|