commit 9818857c6c9a5970a80046be47f5dffa66140ec4
parent 649e885def5b4a6ac0ea548ec924725c88fd283b
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Fri, 2 Oct 2026 01:51:38 -0400
common: re-review — a mixed location's re-point rolls back as one
Two cases: the media channel rewritten first and the legacy one failing, and
the reverse; both channels' links and config keys restored, no media link
invented, the settings written 0 times.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Diffstat:
1 file changed, 40 insertions(+), 0 deletions(-)
diff --git a/common/controller/storageLocations.test.ts b/common/controller/storageLocations.test.ts
@@ -252,6 +252,46 @@ test("re-point moves a legacy channel the retired way beside a migrated one", as
});
});
+// A MIXED LOCATION ROLLS BACK AS ONE (the review's re-review): whichever of a
+// media channel and a legacy one is rewritten first, a failure on the other
+// puts both back — link, config key, no `media` link invented — and the
+// settings are never written.
+for (const [first, second, failing] of [
+ ["alpha", "old", "old"], // media channel rewritten, the legacy one fails
+ ["aaa", "zzz", "zzz"], // legacy rewritten first, the media one fails
+] as const) {
+ test(`mixed rollback: ${first} then ${second}, ${failing} fails`, async () => {
+ await withTmp(async (h) => {
+ const legacySlug = first === "alpha" ? second : first;
+ const mediaSlug = first === "alpha" ? first : second;
+ await seedRelocated(h, mediaSlug, h.rootA);
+ await seedLegacy(h, legacySlug, h.rootA);
+ await mkdir(path.join(h.rootB, mediaSlug, "media"), { recursive: true });
+ await mkdir(path.join(h.rootB, legacySlug, "data", "v1"), { recursive: true });
+ await setLocations(h, [loc("cold", h.rootA)]);
+ const failDir = path.join(h.paths.channelsDir, failing);
+ await chmod(failDir, 0o555);
+ try {
+ await assert.rejects(
+ repointStorageLocation({ paths: h.paths, locationId: "cold", newRoot: h.rootB, io: h.io }),
+ new RegExp(`${failing}: failed while`),
+ );
+ } finally {
+ await chmod(failDir, 0o755);
+ }
+ assert.equal(await readlink(path.join(h.paths.channelsDir, mediaSlug, "media")), path.join(h.rootA, mediaSlug, "media"));
+ assert.equal((await readChannelConfig(h.paths, mediaSlug))?.mediaDir, path.join(h.rootA, mediaSlug, "media"));
+ assert.equal(await readlink(path.join(h.paths.channelsDir, legacySlug, "data")), path.join(h.rootA, legacySlug, "data"));
+ const lc = await readChannelConfig(h.paths, legacySlug);
+ assert.equal(lc?.dataDir, path.join(h.rootA, legacySlug, "data"));
+ assert.equal(lc?.mediaDir, undefined);
+ await assert.rejects(() => readlink(path.join(h.paths.channelsDir, legacySlug, "media")));
+ assert.equal(h.settings().storage.locations[0].root, h.rootA);
+ assert.equal(h.writes.length, 0);
+ });
+ });
+}
+
test("a legacy channel whose retired tree is missing under the new root is named, and nothing moves", async () => {
await withTmp(async (h) => {
await seedRelocated(h, "alpha", h.rootA);