import test from "node:test"; import assert from "node:assert/strict"; import { mkdir, mkdtemp, readdir, rm, writeFile } from "node:fs/promises"; import os from "node:os"; import path from "node:path"; // Run with: // pnpm -C editor exec tsx --test "app/api/ops/refresh-metadata/route.test.ts" // // The body's shape and the refusals that come before any job: every case here // is answered from the disk alone, so a temp corpus with one YouTube channel // is the whole world — no job is queued and no yt-dlp runs. const ROOT = await mkdtemp(path.join(os.tmpdir(), "refresh-metadata-route-")); const SLUG = "demo-yt"; const CHANNEL = path.join(ROOT, "channels", SLUG); // Set before the route (and getPaths, which caches) is first imported. process.env.WORKER_TOKEN = "test-token"; process.env.TRANSCRIPTS_DIR = ROOT; process.env.SETTINGS_FILE = path.join(ROOT, "settings.json"); await mkdir(path.join(CHANNEL, "data"), { recursive: true }); await writeFile( path.join(CHANNEL, "config.json"), JSON.stringify({ handling: "youtube", name: "Demo", url: "https://www.youtube.com/@demo/videos", }), ); await writeFile( path.join(CHANNEL, "playlist"), "https://www.youtube.com/watch?v=listed00001\n", ); const { POST } = await import("./route"); test.after(() => rm(ROOT, { recursive: true, force: true })); async function post(body: Record): Promise<{ status: number; error: string }> { const res = await POST( new Request("http://localhost/api/ops/refresh-metadata", { method: "POST", headers: { authorization: "Bearer test-token", "content-type": "application/json", }, body: JSON.stringify(body), }), ); return { status: res.status, error: ((await res.json()) as { error?: string }).error ?? "" }; } test("the body is { slug, id, queueKey? }: a missing id and an unknown key are refused", async () => { const missing = await post({ slug: SLUG }); assert.equal(missing.status, 400); assert.match(missing.error, /"id" is required/); const unknown = await post({ slug: SLUG, id: "listed00001", videoId: "x" }); assert.equal(unknown.status, 400); assert.match(unknown.error, /unknown key\(s\): videoId — this route accepts slug, id, queueKey/); }); test("a traversing id or slug is refused at the door", async () => { const id = await post({ slug: SLUG, id: "../escape" }); assert.equal(id.status, 400); assert.match(id.error, /is not a video id \(one path segment\)/); const slug = await post({ slug: "../../escape", id: "abc" }); assert.equal(slug.status, 400); assert.match(slug.error, /is not a valid channel slug/); }); test("an id with no data// is refused, even one the playlist lists; no job, no directory", async () => { for (const id of ["nope0000000", "listed00001"]) { const stray = await post({ slug: SLUG, id }); assert.equal(stray.status, 400, id); assert.equal( stray.error, `"${id}" has not been fetched into ${SLUG} yet — sync, import or download it first; a refresh only re-reads a video already archived.`, ); } assert.deepEqual(await readdir(path.join(CHANNEL, "data")), []); const channel = await post({ slug: "no-such-channel", id: "nope0000000" }); assert.equal(channel.status, 400); assert.equal(channel.error, 'Channel "no-such-channel" not found'); assert.deepEqual(await readdir(path.join(ROOT, ".jobs")).catch(() => []), []); });