import { expect, test } from "bun:test"; import { mkdtempSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { serveMedia } from "../src"; const dir = mkdtempSync(join(tmpdir(), "stc-media-")); const file = join(dir, "clip.mp4"); const bytes = new Uint8Array(1000).map((_, i) => i % 251); writeFileSync(file, bytes); const req = (range?: string) => new Request("http://x/m", { headers: range ? { Range: range } : {} }); test("full file gives 200", async () => { const r = await serveMedia(req(), file); expect(r.status).toBe(200); expect(r.headers.get("content-type")).toBe("video/mp4"); expect(r.headers.get("accept-ranges")).toBe("bytes"); expect(r.headers.get("content-length")).toBe("1000"); expect((await r.arrayBuffer()).byteLength).toBe(1000); }); test("bytes=0-99 gives 206 with Content-Range", async () => { const r = await serveMedia(req("bytes=0-99"), file); expect(r.status).toBe(206); expect(r.headers.get("content-range")).toBe("bytes 0-99/1000"); expect(r.headers.get("content-length")).toBe("100"); expect(new Uint8Array(await r.arrayBuffer())).toEqual(bytes.slice(0, 100)); }); test("open-ended, suffix and clamped ranges", async () => { let r = await serveMedia(req("bytes=900-"), file); expect(r.status).toBe(206); expect(r.headers.get("content-range")).toBe("bytes 900-999/1000"); r = await serveMedia(req("bytes=-10"), file); expect(r.headers.get("content-range")).toBe("bytes 990-999/1000"); expect(new Uint8Array(await r.arrayBuffer())).toEqual(bytes.slice(990)); r = await serveMedia(req("bytes=990-5000"), file); expect(r.headers.get("content-range")).toBe("bytes 990-999/1000"); }); test("unsatisfiable range gives 416", async () => { const r = await serveMedia(req("bytes=999999999-"), file); expect(r.status).toBe(416); expect(r.headers.get("content-range")).toBe("bytes */1000"); }); test("non-ASCII filename gets a valid filename*", async () => { const f = join(dir, "Æble notes.pdf"); writeFileSync(f, "pdf"); const r = await serveMedia(req(), f, { download: true }); expect(r.headers.get("content-type")).toBe("application/pdf"); const cd = r.headers.get("content-disposition")!; expect(cd).toContain("filename*=UTF-8''%C3%86ble%20notes.pdf"); expect(cd).toContain('filename="_ble notes.pdf"'); }); test("missing file is 404", async () => { expect((await serveMedia(req(), join(dir, "nope.mp4"))).status).toBe(404); }); test("every response is nosniff; script-capable types get a sandbox CSP", async () => { const d = mkdtempSync(join(tmpdir(), "stc-media-csp-")); const { MEDIA_SANDBOX_CSP } = await import("../src"); for (const [name, body, sandboxed] of [ ["x.svg", "", true], ["page.html", "", true], ["Page.HTM", "

x

", true], ["code.js", "alert(1)", true], ["mod.mjs", "export {}", true], ["notes.md", "# hi", false], ["clip.mp4", "x", false], ] as const) { writeFileSync(join(d, name), body); for (const r of [await serveMedia(req(), join(d, name)), await serveMedia(req("bytes=0-0"), join(d, name))]) { expect(r.headers.get("x-content-type-options")).toBe("nosniff"); expect([name, r.headers.get("content-security-policy")]).toEqual([name, sandboxed ? MEDIA_SANDBOX_CSP : null]); } } expect(MEDIA_SANDBOX_CSP).toMatch(/^sandbox;/); expect(MEDIA_SANDBOX_CSP).not.toContain("allow-scripts"); }); test("a range on an empty file is 416, never a negative Content-Range", async () => { const d = mkdtempSync(join(tmpdir(), "stc-media-empty-")); writeFileSync(join(d, "empty.txt"), ""); for (const range of ["bytes=0-", "bytes=-5", "bytes=0-0"]) { const r = await serveMedia(req(range), join(d, "empty.txt")); expect(r.status).toBe(416); expect(r.headers.get("content-range")).toBe("bytes */0"); } expect((await serveMedia(req(), join(d, "empty.txt"))).status).toBe(200); });