78 lines
3.8 KiB
TypeScript
78 lines
3.8 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
||
import { mkdirSync, writeFileSync } from "node:fs";
|
||
import { join } from "node:path";
|
||
import { $ } from "bun";
|
||
import { gitignoreLine, isLargeMedia, LARGE_FILE_BYTES, mediaGitignore, parseMediaRefs, sha256File } from "../src/server/media-refs";
|
||
import { tempDir } from "./helpers";
|
||
|
||
describe("isLargeMedia", () => {
|
||
test("video and audio by extension, whatever the size", () => {
|
||
for (const p of ["a.mp4", "x/B.MOV", "c.webm", "d.mkv", "e.mp3", "f.m4a", "g.wav", "h.flac", "i.avi", "j.m4v"]) expect(isLargeMedia(p, 10)).toBe(true);
|
||
});
|
||
test("other files only above the size limit", () => {
|
||
expect(isLargeMedia("deck.pdf", 1000)).toBe(false);
|
||
expect(isLargeMedia("deck.pdf", LARGE_FILE_BYTES + 1)).toBe(true);
|
||
expect(isLargeMedia("lesson.md", LARGE_FILE_BYTES)).toBe(false);
|
||
});
|
||
});
|
||
|
||
describe("gitignoreLine", () => {
|
||
test("anchors and escapes", () => {
|
||
expect(gitignoreLine("media/a.mp4")).toBe("/media/a.mp4");
|
||
expect(gitignoreLine("media/[1] *?.mp4")).toBe("/media/\\[1\\] \\*\\?.mp4");
|
||
expect(gitignoreLine("a\\b.mp4")).toBe("/a\\\\b.mp4");
|
||
expect(gitignoreLine("trail.mp4 ")).toBe("/trail.mp4\\ \\ ");
|
||
});
|
||
test("refuses names a .gitignore cannot express", () => {
|
||
expect(() => gitignoreLine("a\nb.mp4")).toThrow();
|
||
expect(() => gitignoreLine("a\rb.mp4")).toThrow();
|
||
});
|
||
|
||
test("git ignores exactly the listed paths", async () => {
|
||
const dir = tempDir();
|
||
await $`git init -q ${dir}`.quiet();
|
||
const listed = ["media/01 Intro – Übersicht.mp4", "media/[draft] *star?.mp4", "#hash.mp4", "!bang.mp4", "media/trailing.mp4 ", "deep/a b/c.mkv"];
|
||
const siblings = ["media/01 Intro – Übersicht.mp4.txt", "media/d.mp4", "hash.mp4", "bang.mp4", "media/trailing.mp4", "deep/a b/c.mkv2", "sub/#hash.mp4"];
|
||
for (const p of [...listed, ...siblings]) {
|
||
mkdirSync(join(dir, p, ".."), { recursive: true });
|
||
writeFileSync(join(dir, p), "x");
|
||
}
|
||
writeFileSync(join(dir, ".gitignore"), mediaGitignore(listed, null));
|
||
const input = Buffer.from([...listed, ...siblings].join("\0") + "\0");
|
||
const r = await $`git -c core.quotepath=false check-ignore -z --no-index --stdin < ${input}`.cwd(dir).quiet().nothrow();
|
||
const ignored = r.stdout.toString().split("\0").filter(Boolean).sort();
|
||
expect(ignored).toEqual([...listed].sort());
|
||
});
|
||
|
||
test("an existing .gitignore is kept above the media block", () => {
|
||
const out = mediaGitignore(["a.mp4"], "node_modules/\n");
|
||
expect(out.startsWith("node_modules/\n")).toBe(true);
|
||
expect(out).toContain("/a.mp4\n");
|
||
expect(mediaGitignore(["a.mp4"], "dist")).toStartWith("dist\n");
|
||
});
|
||
});
|
||
|
||
test("sha256File streams the file", async () => {
|
||
const dir = tempDir();
|
||
const bytes = new Uint8Array(3 * 1024 * 1024 + 7).map((_, i) => i % 251);
|
||
writeFileSync(join(dir, "f.bin"), bytes);
|
||
expect(await sha256File(join(dir, "f.bin"))).toBe(new Bun.CryptoHasher("sha256").update(bytes).digest("hex"));
|
||
});
|
||
|
||
describe("parseMediaRefs", () => {
|
||
const sha = "a".repeat(64);
|
||
test("accepts the schema", () => {
|
||
expect(parseMediaRefs(JSON.stringify({ schema: 1, files: [{ path: "media/a.mp4", size: 3, sha256: sha }] }))).toEqual([{ path: "media/a.mp4", size: 3, sha256: sha }]);
|
||
});
|
||
test.each([
|
||
"not json", "null", "{}", JSON.stringify({ schema: 2, files: [] }),
|
||
JSON.stringify({ schema: 1, files: [{ path: "../x.mp4", size: 1, sha256: sha }] }),
|
||
JSON.stringify({ schema: 1, files: [{ path: "/abs.mp4", size: 1, sha256: sha }] }),
|
||
JSON.stringify({ schema: 1, files: [{ path: ".git/config", size: 1, sha256: sha }] }),
|
||
JSON.stringify({ schema: 1, files: [{ path: "a.mp4", size: -1, sha256: sha }] }),
|
||
JSON.stringify({ schema: 1, files: [{ path: "a.mp4", size: 1, sha256: "zz" }] }),
|
||
])("rejects %p", (text) => {
|
||
expect(parseMediaRefs(text)).toBeNull();
|
||
});
|
||
});
|