import test from "node:test"; import assert from "node:assert/strict"; import { mkdir, readFile } from "node:fs/promises"; import path from "node:path"; import { setupOpsCorpus, callPost } from "../_testCorpus"; // Run with: // pnpm -C editor exec tsx --test "app/api/ops/remote-listing/route.test.ts" // // The refusals before any job, and one listing run end to end against the e2e // fake yt-dlp (it lists fake00000001…05 for any channel URL): the job's last // line carries the diff, and nothing is written into the channel. const corpus = await setupOpsCorpus(null); await corpus.writeChannelConfig("demo-odysee", { handling: "transcribe", platform: "odysee", url: "https://odysee.com/@demo:0", }); await corpus.writeChannelConfig("demo-yt", { url: "https://www.youtube.com/@demo" }); await corpus.writeChannelConfig("demo-nourl", {}); const DATA = path.join(corpus.transcripts, "channels", "demo-odysee", "data"); // Held: one id the fake lists, one it does not. await mkdir(path.join(DATA, "fake00000002"), { recursive: true }); await mkdir(path.join(DATA, "imported-elsewhere"), { recursive: true }); const { POST } = await import("./route"); const { getRegistry } = await import("yt-dlp-transcript-common/jobs/registry"); const { getPaths } = await import("yt-dlp-transcript-common/lib/paths"); test.after(() => corpus.cleanup()); test("refusals before any job: keys, slug, channel, platform, url", async () => { const cases: [Record, RegExp][] = [ [{}, /"slug" is required/], [{ slug: "demo-odysee", full: true }, /unknown key\(s\): full/], [{ slug: "../x" }, /not a valid channel slug/], [{ slug: "no-such" }, /Channel "no-such" not found/], [{ slug: "demo-yt" }, /reads an Odysee or BitChute channel; "demo-yt" is on youtube/], [{ slug: "demo-nourl" }, /no `url` configured/], ]; for (const [body, re] of cases) { const r = await callPost(POST, body); assert.equal(r.status, 400, JSON.stringify(body)); assert.match(r.body.error ?? "", re, JSON.stringify(body)); } assert.deepEqual(await corpus.listJobIds(), []); }); test("a listing is a job whose last line is the diff against what is held", async () => { const r = await callPost(POST, { slug: "demo-odysee" }); assert.equal(r.status, 200, JSON.stringify(r.body)); const jobId = r.body.jobId as string; for (let i = 0; i < 200; i++) { const s = getRegistry().get(jobId)?.status; if (s === "done" || s === "failed") break; await new Promise((res) => setTimeout(res, 50)); } const log = await readFile(path.join(getPaths().jobsDir, `${jobId}.log`), "utf8"); assert.equal(getRegistry().get(jobId)?.status, "done", log); const line = log.split("\n").find((l) => l.startsWith("@@remote-listing ")); assert.ok(line, log); const result = JSON.parse(line!.slice("@@remote-listing ".length)); assert.equal(result.platform, "odysee"); assert.equal(result.listed, 5); assert.equal(result.held, 1); assert.deepEqual( result.notHeld.map((n: { id: string }) => n.id), ["fake00000001", "fake00000003", "fake00000004", "fake00000005"], ); assert.deepEqual(result.heldNotListed, ["imported-elsewhere"]); // Read-only: no roster, no playlist, no maybe-missing written. const { readdir } = await import("node:fs/promises"); const top = await readdir(path.join(corpus.transcripts, "channels", "demo-odysee")); assert.deepEqual(top.filter((f) => !["config.json", "data", "fake-ytdlp.invocations"].includes(f)), []); });