commit c80cd066474b267503fd2a2c8cb640b4e4dd2885
parent 3caac3c614b6d18a13acb0abd41d5cb5d3fb6f7c
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Tue, 29 Sep 2026 21:16:37 -0400
common(test): review M1 — case (i): the drive is lost mid-walk (after the scan's first metadata stat); the second look holds the channel, 0 removed, records and pages unchanged (without that look, 3 of 4 removed)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Diffstat:
1 file changed, 51 insertions(+), 0 deletions(-)
diff --git a/common/controller/buildIndex.test.ts b/common/controller/buildIndex.test.ts
@@ -77,6 +77,7 @@ const COMMON = fileURLToPath(new URL("..", import.meta.url));
// sync and callback, synced into the named ESM imports the code under test
// holds. The last case reads it.
const writes: string[] = [];
+let afterStat: ((p: string) => void) | null = null;
{
const req = createRequire(import.meta.url);
const fsCjs = req("node:fs") as Record<string, unknown>;
@@ -111,6 +112,15 @@ const writes: string[] = [];
wrap(fsCjs, "open", "open");
wrap(fsCjs, "openSync", "open");
wrap(fsCjs, "createWriteStream", "write");
+ // Case (i)'s hook: the drive is lost DURING the scan's walk. node:fs/promises
+ // `stat` calls `afterStat` with each path it has just answered for.
+ const stat = fspCjs.stat as (...a: unknown[]) => Promise<unknown>;
+ fspCjs.stat = async function (this: unknown, ...args: unknown[]) {
+ const result = await stat.apply(this, args);
+ const p = asPath(args[0]);
+ if (p !== null) afterStat?.(path.resolve(p));
+ return result;
+ };
syncBuiltinESMExports();
}
@@ -584,6 +594,47 @@ test("(h) a curated-tag change while a channel is held reaches its pages when th
assert.equal(pagesPending(), false);
});
+test("(i) the drive lost MID-WALK: the second look holds the channel instead of dropping the rest of it", async () => {
+ resetCorpus();
+ seedVideo("local");
+ seedDriveChannel();
+ for (const id of ["d3", "d4"]) seedVideo(id, DRIVE_CHANNEL);
+ const drive = [...DRIVE_VIDEOS, `${DRIVE_CHANNEL}/d3`, `${DRIVE_CHANNEL}/d4`];
+ await runIndex();
+ assert.deepEqual(indexed(), [...drive, `${CHANNEL}/local`]);
+ const pagesBefore = sharedTranscripts();
+ const subsBefore = sharedSubs();
+
+ // The first look before the walk finds the drive, and readdir lists all four
+ // videos. Then the drive goes, right after the walk's first metadata stat:
+ // every later stat in the channel is ENOENT, which on its own reads as "no
+ // metadata yet" and would drop the rest of the channel as gone.
+ let lost = false;
+ afterStat = (p) => {
+ if (lost || !p.endsWith(`${path.sep}metadata.info.json`)) return;
+ if (!p.includes(`${path.sep}${DRIVE_CHANNEL}${path.sep}data${path.sep}`)) return;
+ lost = true;
+ unmount();
+ };
+ seedVideo("local2"); // so the build is not a no-op
+ const { res, log } = await runIndex().finally(() => {
+ afterStat = null;
+ });
+ assert.ok(lost, "the drive went away inside the walk");
+ assert.deepEqual(res.heldChannels, [DRIVE_CHANNEL]);
+ assert.equal(res.removed, 0, log.join("\n"));
+ assert.equal(res.added, 1);
+ assert.deepEqual(indexed(), [...drive, `${CHANNEL}/local`, `${CHANNEL}/local2`]);
+ assert.deepEqual(sharedTranscripts(), pagesBefore);
+ assert.deepEqual(sharedSubs(), subsBefore);
+ assert.ok(
+ log.some((l) =>
+ l.startsWith(`Channel ${DRIVE_CHANNEL}: its media is not reachable (drive not mounted?), on location "USB drive"; its 4 indexed video(s) are kept`),
+ ),
+ log.join("\n"),
+ );
+});
+
test("(z) no write this file caused landed outside its temp root", () => {
// LMDB writes natively, past the spy: its file must be under the root too.
assert.ok(paths.lmdbPath.startsWith(ROOT + path.sep), paths.lmdbPath);