import test from "node:test"; import assert from "node:assert/strict"; import { defaultLocationRoot, locationOfDataDir, migrateMediaRootToLocations, type StorageLocation, } from "./storageLocations"; function loc(id: string, root: string): StorageLocation { return { id, label: id, root, autoRepoint: false }; } const PLATTER = loc("platter", "/mnt/platter"); const ARCHIVE = loc("archive", "/mnt/platter/archive"); test("a channel is on the location its dataDir is under", () => { assert.equal( locationOfDataDir("/mnt/platter/alpha/data", [PLATTER])?.id, "platter", ); assert.equal(locationOfDataDir("/corpus/channels/alpha/data", [PLATTER]), null); assert.equal(locationOfDataDir("", [PLATTER]), null); // A sibling whose name merely starts the same is not under it. assert.equal(locationOfDataDir("/mnt/platter-old/alpha/data", [PLATTER]), null); // "Under" is strict: the root itself is not a channel's dataDir. assert.equal(locationOfDataDir("/mnt/platter", [PLATTER]), null); }); test("nested roots: the longest match wins, whatever the list order", () => { const deep = "/mnt/platter/archive/alpha/data"; assert.equal(locationOfDataDir(deep, [PLATTER, ARCHIVE])?.id, "archive"); assert.equal(locationOfDataDir(deep, [ARCHIVE, PLATTER])?.id, "archive"); // Still the outer one for a channel that is not in the nested root. assert.equal( locationOfDataDir("/mnt/platter/beta/data", [PLATTER, ARCHIVE])?.id, "platter", ); }); test("trailing slashes on either side are one root", () => { assert.equal( locationOfDataDir("/mnt/platter/alpha/data/", [loc("p", "/mnt/platter/")]) ?.id, "p", ); }); test("defaultLocationRoot resolves the id, or blanks", () => { assert.equal( defaultLocationRoot({ locations: [PLATTER, ARCHIVE], defaultLocationId: "archive", }), "/mnt/platter/archive", ); // A default naming nothing (an empty list, or an id the sanitizer would have // repaired) is "no default root", which every caller already handles. assert.equal( defaultLocationRoot({ locations: [], defaultLocationId: "" }), "", ); assert.equal( defaultLocationRoot({ locations: [PLATTER], defaultLocationId: "gone" }), "", ); }); test("migration rule 1: a file that already spells locations is untouched", () => { const already = { locations: [{ id: "cold", label: "Cold", root: "/mnt/cold" }], defaultLocationId: "cold", // A stale mediaRoot beside it is NOT merged in as a second location. mediaRoot: "/mnt/deleted", }; assert.deepEqual(migrateMediaRootToLocations(already), already); // An EMPTY list is also "already spelled" — the operator deleted them all. const empty = { locations: [], defaultLocationId: "", mediaRoot: "/mnt/x" }; assert.deepEqual(migrateMediaRootToLocations(empty), empty); }); test("migration rule 2: blank, missing or relative mediaRoot is no location", () => { const none = { locations: [], defaultLocationId: "" }; assert.deepEqual(migrateMediaRootToLocations({}), none); assert.deepEqual(migrateMediaRootToLocations({ mediaRoot: "" }), none); assert.deepEqual(migrateMediaRootToLocations({ mediaRoot: " " }), none); assert.deepEqual(migrateMediaRootToLocations({ mediaRoot: 7 }), none); assert.deepEqual(migrateMediaRootToLocations({ mediaRoot: "platter" }), none); assert.deepEqual(migrateMediaRootToLocations({ mediaRoot: "../up" }), none); // Not an object at all: handed straight back for the sanitizer to default. assert.equal(migrateMediaRootToLocations(undefined), undefined); assert.equal(migrateMediaRootToLocations("/mnt/platter"), "/mnt/platter"); }); test("migration rule 3: an absolute mediaRoot becomes the default location", () => { assert.deepEqual(migrateMediaRootToLocations({ mediaRoot: "/mnt/platter" }), { locations: [ { id: "default", label: "Default", root: "/mnt/platter", // Never armed by a migration: re-point rewrites every channel symlink // on the location and nobody asked for that. autoRepoint: false, }, ], defaultLocationId: "default", }); }); test("migration is idempotent", () => { const once = migrateMediaRootToLocations({ mediaRoot: "/mnt/platter" }); assert.deepEqual(migrateMediaRootToLocations(once), once); });