// A project's render scratch on a media root (release 17): the roots in // lib/paths.mjs, ensureOutDir / ensureWriteDir, the two movers, and the walk's // skip rules for a media drive that is not there. // // Every call here names its own roots (`reportsRoot`, `mediaRoot`), so nothing // depends on, or touches, the process's REPORTS_ROOT. The env-derived roots are // tested in a child process, where the module is evaluated fresh. // // Run with: pnpm test:scripts import assert from "node:assert/strict"; import { execFileSync } from "node:child_process"; import { lstat, mkdir, mkdtemp, readFile, readdir, readlink, rename, rm, symlink, writeFile } from "node:fs/promises"; import { existsSync } from "node:fs"; import { homedir, tmpdir } from "node:os"; import path from "node:path"; import test from "node:test"; import { fileURLToPath } from "node:url"; import { mediaMirror } from "../paths.mjs"; import { SKIP_DIRS, skipsDir } from "../projects/kinds.mjs"; import { walkProjects } from "../projects/walk.mjs"; import { ensureOutDir, ensureWriteDir, mediaRootProblem, moveDirToLocal, moveDirToMedia, outDirState, } from "./storage.mjs"; const HERE = path.dirname(fileURLToPath(import.meta.url)); /** A reports root with one project, and a media root beside it (not inside). */ async function world({ withOut = true } = {}) { const base = await mkdtemp(path.join(tmpdir(), "umtool-storage-")); const reportsRoot = path.join(base, "reports"); const mediaRoot = path.join(base, "media"); const projectDir = path.join(reportsRoot, "folder", "proj"); await mkdir(projectDir, { recursive: true }); await mkdir(mediaRoot); await writeFile(path.join(projectDir, "video.manifest.json"), "{}\n"); if (withOut) { await mkdir(path.join(projectDir, "out", "clips-raw"), { recursive: true }); await writeFile(path.join(projectDir, "out", "proj.mp4"), Buffer.alloc(4096, 1)); await writeFile(path.join(projectDir, "out", "clips-raw", "v_0-9.mp4"), Buffer.alloc(2048, 2)); } const roots = { reportsRoot, mediaRoot }; const mirror = path.join(mediaRoot, "folder", "proj"); return { base, reportsRoot, mediaRoot, projectDir, roots, mirror, done: () => rm(base, { recursive: true, force: true }) }; } const kind = async (p) => { const st = await lstat(p).catch(() => null); if (!st) return "missing"; return st.isSymbolicLink() ? "link" : st.isDirectory() ? "dir" : "file"; }; // --------------------------------------------------------------------------- // The roots // --------------------------------------------------------------------------- /** lib/paths.mjs's values under a given environment, evaluated fresh. */ function rootsUnder(envPatch) { const env = { ...process.env }; for (const k of ["UMTOOL_MEDIA_DIR", "UMTOOL_CACHE_DIR", "UMTOOL_INDEX_DIR", "XDG_CACHE_HOME", "MIX_ROOTS", "MIX_WRITE_ROOTS"]) delete env[k]; Object.assign(env, { REPORTS_DIR: "/r/reports", SONG_DIR: "/r/song-data-that-is-not-there" }, envPatch); const code = "const m = await import(process.argv[1]);" + "console.log(JSON.stringify({ media: m.MEDIA_ROOT, tiered: m.MEDIA_TIERED, reports: m.REPORTS_ROOT, cache: m.CACHE_DIR," + " old: m.OLD_CACHE_DIR, index: m.INDEX_DIR, read: m.READ_ROOTS, write: m.WRITE_ROOTS }));"; const out = execFileSync(process.execPath, ["--input-type=module", "-e", code, path.join(HERE, "..", "paths.mjs")], { env, encoding: "utf8", }); return JSON.parse(out); } test("MEDIA_ROOT unset is REPORTS_ROOT: nothing tiered, no new root", () => { const r = rootsUnder({}); assert.equal(r.media, "/r/reports"); assert.equal(r.tiered, false); assert.equal(r.read.filter((p) => p === "/r/reports").length, 1); }); test("UMTOOL_MEDIA_DIR is a READ root and never a write root", () => { const r = rootsUnder({ UMTOOL_MEDIA_DIR: "/m/umtool" }); assert.equal(r.media, "/m/umtool"); assert.equal(r.tiered, true); assert.ok(r.read.includes("/m/umtool")); assert.ok(!r.write.includes("/m/umtool")); }); test("CACHE_DIR: UMTOOL_CACHE_DIR, else XDG_CACHE_HOME/archilyzer/umtool, else ~/.cache — never SONG_DATA", () => { assert.equal(rootsUnder({ UMTOOL_CACHE_DIR: "/c/u" }).cache, "/c/u"); assert.equal(rootsUnder({ XDG_CACHE_HOME: "/x" }).cache, "/x/archilyzer/umtool"); const plain = rootsUnder({ XDG_CACHE_HOME: "" }); assert.equal(plain.cache, path.join(homedir(), ".cache", "archilyzer", "umtool")); assert.equal(plain.index, path.join(plain.cache, "index")); assert.equal(plain.old, "/r/song-data-that-is-not-there/.cache/umtool"); assert.ok(!plain.cache.startsWith("/r/song-data")); }); test("mediaMirror: the project-relative path under the media root, or null outside the reports root", () => { const roots = { reportsRoot: "/r", mediaRoot: "/m" }; assert.equal(mediaMirror("/r/a/b", roots), "/m/a/b"); assert.equal(mediaMirror("/r", roots), null); assert.equal(mediaMirror("/elsewhere/p", roots), null); assert.equal(mediaMirror("/r-sibling/p", roots), null); }); // --------------------------------------------------------------------------- // ensureOutDir / ensureWriteDir // --------------------------------------------------------------------------- test("ensureOutDir, not tiered: a plain directory, as every writer made it", async () => { const w = await world({ withOut: false }); try { const out = await ensureOutDir(w.projectDir, { reportsRoot: w.reportsRoot, mediaRoot: w.reportsRoot }); assert.equal(await kind(out), "dir"); assert.deepEqual(await readdir(w.mediaRoot), []); } finally { await w.done(); } }); test("ensureOutDir, tiered and absent: the mirror is made under the media root and linked", async () => { const w = await world({ withOut: false }); try { const out = await ensureOutDir(w.projectDir, w.roots); assert.equal(await kind(out), "link"); assert.equal(await readlink(out), path.join(w.mirror, "out")); assert.equal(await kind(path.join(w.mirror, "out")), "dir"); assert.deepEqual(await outDirState(w.projectDir), { state: "link", target: path.join(w.mirror, "out") }); // Again: the link is kept as it is. assert.equal(await ensureOutDir(w.projectDir, w.roots), out); } finally { await w.done(); } }); test("ensureOutDir keeps an existing real out/ even when tiered (move-out moves it, not the writer)", async () => { const w = await world(); try { await ensureOutDir(w.projectDir, w.roots); assert.equal(await kind(path.join(w.projectDir, "out")), "dir"); assert.deepEqual(await readdir(w.mediaRoot), []); } finally { await w.done(); } }); test("ensureOutDir never creates the media root: an unplugged drive refuses and nothing is made", async () => { const w = await world({ withOut: false }); try { await rm(w.mediaRoot, { recursive: true }); await assert.rejects(ensureOutDir(w.projectDir, w.roots), /is not there — is its drive mounted\? Nothing was created/); assert.equal(existsSync(w.mediaRoot), false); assert.equal(await kind(path.join(w.projectDir, "out")), "missing"); } finally { await w.done(); } }); test("a dangling out link refuses loudly; nothing is materialised in its place or under it", async () => { const w = await world({ withOut: false }); try { await ensureOutDir(w.projectDir, w.roots); await rename(w.mediaRoot, `${w.mediaRoot}.unplugged`); assert.equal((await outDirState(w.projectDir)).state, "dangling"); await assert.rejects(ensureOutDir(w.projectDir, w.roots), /is the media drive mounted\?/); await assert.rejects(ensureWriteDir(path.join(w.projectDir, "out", "sourced", "segments")), /is the media drive mounted\?/); // And a plain recursive mkdir through the link fails too (ENOTDIR), making nothing. await assert.rejects(mkdir(path.join(w.projectDir, "out", "clips-raw"), { recursive: true })); assert.equal(await kind(path.join(w.projectDir, "out")), "link"); assert.equal(existsSync(w.mediaRoot), false); } finally { await w.done(); } }); test("a media root inside the reports root (or around it) is refused", async () => { const w = await world({ withOut: false }); try { const inner = { reportsRoot: w.reportsRoot, mediaRoot: path.join(w.reportsRoot, "media") }; await mkdir(inner.mediaRoot); assert.match(await mediaRootProblem(inner), /must be outside the reports root/); assert.match(await mediaRootProblem({ reportsRoot: w.reportsRoot, mediaRoot: w.base }), /must be outside/); assert.equal(await mediaRootProblem(w.roots), null); assert.equal(await mediaRootProblem({ reportsRoot: w.reportsRoot, mediaRoot: w.reportsRoot }), null); } finally { await w.done(); } }); test("ensureWriteDir makes a project's out/ through ensureOutDir before anything under it", async () => { const w = await world({ withOut: false }); try { // ensureWriteDir uses the process's roots; under the default (no // UMTOOL_MEDIA_DIR in the test environment) it is a plain directory, the // old behaviour. The tiered case is ensureOutDir's, tested above, and the // storage e2e drives the pipeline scripts through it. const deep = path.join(w.projectDir, "out", "sourced", "chrome", "deck-stills"); assert.equal(await ensureWriteDir(deep), deep); assert.equal(await kind(deep), "dir"); // A directory that is not under any out/ is made as it always was. const other = path.join(w.base, "elsewhere", "x"); await ensureWriteDir(other); assert.equal(await kind(other), "dir"); } finally { await w.done(); } }); // --------------------------------------------------------------------------- // The movers // --------------------------------------------------------------------------- test("moveDirToMedia: copy, verify, link; the bytes are the same and nothing is left parked", async () => { const w = await world(); try { const logs = []; const r = await moveDirToMedia(w.projectDir, "out", { ...w.roots, log: (m) => logs.push(m) }); assert.equal(r.state, "moved"); assert.equal(r.files, 2); assert.equal(r.bytes, 4096 + 2048); const out = path.join(w.projectDir, "out"); assert.equal(await kind(out), "link"); assert.equal(await readlink(out), path.join(w.mirror, "out")); assert.deepEqual(await readFile(path.join(out, "proj.mp4")), Buffer.alloc(4096, 1)); assert.deepEqual((await readdir(w.projectDir)).sort(), ["out", "video.manifest.json"]); assert.ok(logs.some((l) => l.includes("--delete")), "the mirror pass ran"); // Again: nothing to do. assert.equal((await moveDirToMedia(w.projectDir, "out", w.roots)).state, "already"); } finally { await w.done(); } }); test("moveDirToMedia --dry-run measures and changes nothing", async () => { const w = await world(); try { const r = await moveDirToMedia(w.projectDir, "out", { ...w.roots, dryRun: true }); assert.equal(r.state, "would-move"); assert.equal(r.bytes, 6144); assert.equal(await kind(path.join(w.projectDir, "out")), "dir"); assert.deepEqual(await readdir(w.mediaRoot), []); } finally { await w.done(); } }); test("moveDirToMedia refuses without a media root, and on a link that is not its own", async () => { const w = await world(); try { await assert.rejects( moveDirToMedia(w.projectDir, "out", { reportsRoot: w.reportsRoot, mediaRoot: w.reportsRoot }), /UMTOOL_MEDIA_DIR is not set/, ); await assert.rejects(moveDirToMedia(w.projectDir, "../x", w.roots), /not a project directory name/); const other = path.join(w.base, "other"); await mkdir(other); await symlink(other, path.join(w.projectDir, "clips")); await assert.rejects(moveDirToMedia(w.projectDir, "clips", w.roots), /already a link, to .* not to/); assert.equal((await moveDirToMedia(w.projectDir, "share-none", w.roots)).state, "absent"); } finally { await w.done(); } }); test("moveDirToMedia: a failed copy leaves the source untouched", async () => { const w = await world(); try { await assert.rejects(moveDirToMedia(w.projectDir, "out", { ...w.roots, rsyncBin: "false" }), /rsync failed/); assert.equal(await kind(path.join(w.projectDir, "out")), "dir"); assert.deepEqual(await readFile(path.join(w.projectDir, "out", "proj.mp4")), Buffer.alloc(4096, 1)); } finally { await w.done(); } }); test("moveDirToMedia finishes a run cut between the park and the link", async () => { const w = await world(); try { // What a cut leaves: the verified copy on the media root, the source parked. await mkdir(path.join(w.mirror), { recursive: true }); execFileSync("cp", ["-a", path.join(w.projectDir, "out"), path.join(w.mirror, "out")]); await rename(path.join(w.projectDir, "out"), path.join(w.projectDir, "out.moved-20261001T000000Z")); const r = await moveDirToMedia(w.projectDir, "out", w.roots); assert.equal(r.state, "moved"); assert.equal(r.resumed, true); assert.equal(await kind(path.join(w.projectDir, "out")), "link"); assert.deepEqual((await readdir(w.projectDir)).sort(), ["out", "video.manifest.json"]); } finally { await w.done(); } }); test("moveDirToMedia removes a parked copy left by a cut after the link", async () => { const w = await world(); try { await moveDirToMedia(w.projectDir, "out", w.roots); await mkdir(path.join(w.projectDir, "out.moved-20261001T000000Z")); assert.equal((await moveDirToMedia(w.projectDir, "out", w.roots)).state, "already"); assert.deepEqual((await readdir(w.projectDir)).sort(), ["out", "video.manifest.json"]); } finally { await w.done(); } }); test("moveDirToLocal: a real directory again, the media copy and its empty parents gone, the root kept", async () => { const w = await world(); try { await moveDirToMedia(w.projectDir, "out", w.roots); const r = await moveDirToLocal(w.projectDir, "out", w.roots); assert.equal(r.state, "moved"); assert.equal(r.bytes, 6144); const out = path.join(w.projectDir, "out"); assert.equal(await kind(out), "dir"); assert.deepEqual(await readFile(path.join(out, "clips-raw", "v_0-9.mp4")), Buffer.alloc(2048, 2)); assert.deepEqual((await readdir(w.projectDir)).sort(), ["out", "video.manifest.json"]); assert.equal(existsSync(path.join(w.mediaRoot, "folder")), false); assert.equal(existsSync(w.mediaRoot), true); assert.equal((await moveDirToLocal(w.projectDir, "out", w.roots)).state, "already"); } finally { await w.done(); } }); test("moveDirToLocal refuses a dangling link and finishes a cut rename", async () => { const w = await world(); try { await moveDirToMedia(w.projectDir, "out", w.roots); await rename(w.mediaRoot, `${w.mediaRoot}.unplugged`); await assert.rejects(moveDirToLocal(w.projectDir, "out", w.roots), /is the media drive mounted\? Nothing moved/); assert.equal(await kind(path.join(w.projectDir, "out")), "link"); await rename(`${w.mediaRoot}.unplugged`, w.mediaRoot); // A cut between removing the link and renaming the verified copy. The link // is gone, so what was copied cannot be told: the mirror is reported, kept. execFileSync("cp", ["-a", path.join(w.mirror, "out"), path.join(w.projectDir, "out.incoming")]); await rm(path.join(w.projectDir, "out")); const r = await moveDirToLocal(w.projectDir, "out", w.roots); assert.equal(r.state, "moved"); assert.equal(r.resumed, true); assert.equal(await kind(path.join(w.projectDir, "out")), "dir"); assert.equal(r.mediaCopyLeft, path.join(w.mirror, "out")); assert.equal(existsSync(path.join(w.mirror, "out")), true); // And from then on "already" keeps saying so (review L4). const again = await moveDirToLocal(w.projectDir, "out", w.roots); assert.equal(again.state, "already"); assert.equal(again.mediaCopyLeft, path.join(w.mirror, "out")); } finally { await w.done(); } }); // --------------------------------------------------------------------------- // The review's fixes (L3, L5, N5) // --------------------------------------------------------------------------- test("move-back without a media root never deletes what the link pointed at (L3)", async () => { const w = await world({ withOut: false }); try { // Another project's REAL out/, and a hand-made link to it. const other = path.join(w.reportsRoot, "other", "out"); await mkdir(other, { recursive: true }); await writeFile(path.join(other, "keep.mp4"), Buffer.alloc(1024, 3)); await symlink(other, path.join(w.projectDir, "out")); const untiered = { reportsRoot: w.reportsRoot, mediaRoot: w.reportsRoot }; const r = await moveDirToLocal(w.projectDir, "out", untiered); assert.equal(r.state, "moved"); assert.equal(r.mediaCopyLeft, other); assert.equal(await kind(path.join(w.projectDir, "out")), "dir"); assert.deepEqual(await readFile(path.join(other, "keep.mp4")), Buffer.alloc(1024, 3)); } finally { await w.done(); } }); test("a tiered move-back of a link that is not the project's own mirror leaves the target (L3)", async () => { const w = await world({ withOut: false }); try { const elsewhere = path.join(w.mediaRoot, "someone-else", "out"); await mkdir(elsewhere, { recursive: true }); await writeFile(path.join(elsewhere, "x.mp4"), Buffer.alloc(512, 4)); await symlink(elsewhere, path.join(w.projectDir, "out")); const r = await moveDirToLocal(w.projectDir, "out", w.roots); assert.equal(r.mediaCopyLeft, elsewhere); assert.equal(existsSync(path.join(elsewhere, "x.mp4")), true); } finally { await w.done(); } }); test("a cut move's leftovers are a guard: no fresh out/, no move over them (L5)", async () => { const w = await world({ withOut: false }); try { // A move-out cut after the park: out/ is missing, the parked copy is the data. const parked = path.join(w.projectDir, "out.moved-20261001T000000Z"); await mkdir(parked); await writeFile(path.join(parked, "proj.mp4"), Buffer.alloc(4096, 1)); for (const roots of [w.roots, { reportsRoot: w.reportsRoot, mediaRoot: w.reportsRoot }]) { await assert.rejects(ensureOutDir(w.projectDir, roots), /was cut \(left: out\.moved-20261001T000000Z\).*move-out .*nothing was created/); } assert.equal(await kind(path.join(w.projectDir, "out")), "missing"); // A writer that made a fresh out/ anyway (an older binary): move-out refuses to mirror it over. await mkdir(path.join(w.projectDir, "out")); // Both exist: neither move sends the person to the other, which would only // refuse again (re-review R1); the sentence says what to do by hand. for (const move of [moveDirToMedia, moveDirToLocal]) { await assert.rejects( move(w.projectDir, "out", w.roots), /out\/ and out\.moved-20261001T000000Z both exist .*The leftover holds the moved data\. Keep one and remove the other by hand, then run the move; nothing moved/, ); } assert.deepEqual(await readdir(w.mediaRoot), []); await rm(path.join(w.projectDir, "out"), { recursive: true }); await rm(parked, { recursive: true }); // A move-back cut after the unlink: move-out refuses; move-back finishes it. await mkdir(path.join(w.projectDir, "out.incoming")); await assert.rejects(moveDirToMedia(w.projectDir, "out", w.roots), /move-back .*nothing moved/); await assert.rejects(ensureOutDir(w.projectDir, w.roots), /left: out\.incoming/); assert.equal((await moveDirToLocal(w.projectDir, "out", w.roots)).state, "moved"); assert.equal(await kind(path.join(w.projectDir, "out")), "dir"); } finally { await w.done(); } }); test("ensureOutDir for a project that does not exist leaves no empty mirror (N5)", async () => { const w = await world({ withOut: false }); try { const ghost = path.join(w.reportsRoot, "ghost"); await assert.rejects(ensureOutDir(ghost, w.roots), /is not a directory/); assert.deepEqual(await readdir(w.mediaRoot), []); // A project directory that is itself a link is a directory (re-review R2). const real = path.join(w.base, "elsewhere-proj"); await mkdir(real); const linked = path.join(w.reportsRoot, "linked"); await symlink(real, linked); assert.equal(await kind(await ensureOutDir(linked, w.roots)), "link"); } finally { await w.done(); } }); // --------------------------------------------------------------------------- // The walk never descends what may sit on the media drive // --------------------------------------------------------------------------- test("the project walk skips out, clips and share-* (a link into an unplugged drive is never stat'd)", async () => { assert.ok(SKIP_DIRS.has("out") && SKIP_DIRS.has("clips")); assert.ok(skipsDir("share-emancipation") && skipsDir("clips") && !skipsDir("shares") && !skipsDir("project")); assert.ok(skipsDir("out.moved-20261001T000000Z") && skipsDir("out.incoming") && !skipsDir("incoming")); assert.ok(skipsDir("share-x.incoming") && !skipsDir("drafts.incoming") && !skipsDir("old.moved-2026")); const w = await world(); try { for (const hidden of ["clips", "share-x"]) { const d = path.join(w.reportsRoot, hidden, "inner"); await mkdir(d, { recursive: true }); await writeFile(path.join(d, "video.manifest.json"), "{}\n"); } const ids = (await walkProjects(w.reportsRoot)).map((p) => p.id); assert.deepEqual(ids, ["folder/proj"]); } finally { await w.done(); } });