import { test } from "node:test"; import assert from "node:assert/strict"; import { selectToFreeBytes, type FreeUpCandidate } from "./freeUpSelection"; // Run with: // pnpm --filter yt-dlp-transcript-common exec tsx --test views/freeUpSelection.test.ts const GB = 1024 ** 3; function c( slug: string, gb: number | null, inPlace = true, ): FreeUpCandidate { return { slug, bytes: gb === null ? null : gb * GB, inPlace }; } test("largest first, stopping at the first pick that clears the target", () => { const r = selectToFreeBytes( [c("small", 10), c("huge", 300), c("mid", 120), c("tiny", 1)], 200 * GB, ); assert.deepEqual(r.slugs, ["huge"]); assert.equal(r.bytes, 300 * GB); assert.equal(r.shortfall, 0); }); test("it keeps taking until the target is met", () => { const r = selectToFreeBytes([c("a", 80), c("b", 70), c("c", 60)], 200 * GB); assert.deepEqual(r.slugs, ["a", "b", "c"]); assert.equal(r.bytes, 210 * GB); assert.equal(r.shortfall, 0); }); // MOVING A CHANNEL THAT IS ALREADY ON THE PLATTER FREES NOTHING on the disk // being emptied — counting it would report progress the operator would not get. test("channels already on a location are never picked", () => { const r = selectToFreeBytes( [c("moved", 500, false), c("here", 50)], 200 * GB, ); assert.deepEqual(r.slugs, ["here"]); assert.equal(r.shortfall, 150 * GB); assert.match(r.note, /150\.0 GB short/); }); // A ZERO NOBODY MEASURED IS NOT A ZERO: ranking an unmeasured channel as empty // would leave the biggest thing on the disk at the bottom of the list. test("an unmeasured channel is excluded and counted, never ranked as empty", () => { const r = selectToFreeBytes([c("unknown", null), c("known", 30)], 200 * GB); assert.deepEqual(r.slugs, ["known"]); assert.equal(r.unmeasured, 1); assert.match(r.note, /1 channel\(s\) have no size/); // An unmeasured channel that is not even in place is not our problem. assert.equal( selectToFreeBytes([c("elsewhere", null, false)], 10 * GB).unmeasured, 0, ); }); test("a zero or negative target selects nothing", () => { for (const target of [0, -1]) { assert.deepEqual(selectToFreeBytes([c("a", 80)], target).slugs, []); } }); test("ties break by slug so the same target always proposes the same list", () => { const rows = [c("beta", 50), c("alpha", 50), c("gamma", 50)]; const first = selectToFreeBytes(rows, 100 * GB).slugs; const again = selectToFreeBytes([...rows].reverse(), 100 * GB).slugs; assert.deepEqual(first, ["alpha", "beta"]); assert.deepEqual(again, first); }); test("nothing measurable says so rather than proposing an empty selection", () => { const r = selectToFreeBytes([c("x", null), c("y", null)], 100 * GB); assert.deepEqual(r.slugs, []); assert.match(r.note, /Nothing in place has a measured size/); }); test("a channel mid-relocation is excluded and counted, not proposed", () => { // The bulk move refuses a channel whose media is in transition BY NAME, so // proposing one puts a guaranteed skip in the deck and counts its bytes // toward a total the Move will never deliver. const result = selectToFreeBytes( [ { slug: "moving", bytes: 500 * GB, inPlace: true, inTransition: true }, { slug: "still", bytes: 100 * GB, inPlace: true }, ], 200 * GB, ); assert.deepEqual(result.slugs, ["still"]); assert.equal(result.moving, 1); // And it is NOT counted as unmeasured — it has a size; it is unavailable. assert.equal(result.unmeasured, 0); assert.match(result.note, /mid-relocation/); assert.match(result.note, /still 100\.0 GB short/); }); test("an unmeasured channel that is also moving is counted once, as moving", () => { const result = selectToFreeBytes( [{ slug: "moving", bytes: null, inPlace: true, inTransition: true }], 10 * GB, ); assert.equal(result.moving, 1); assert.equal(result.unmeasured, 0); });