import { describe, expect, it } from "vitest"; import { reconcileUserEdits } from "./media-ingest-cluster.js"; const computed = [ { key: "session:a1", assetIds: ["a1", "a2", "a3"] }, { key: "session:b1", assetIds: ["b1", "b2"] }, { key: "session:c1", assetIds: ["c1", "c2", "c3"] }, ]; const keys = (clusters: Array<{ key: string; assetIds: string[] }>) => clusters.map((cluster) => [cluster.key, cluster.assetIds]); describe("reconcileUserEdits", () => { it("passes computed clusters through when the user edited nothing", () => { expect(reconcileUserEdits(computed, [], "session")).toEqual(computed); }); it("never re-adds the assets of a merged collection to computed clusters", () => { // The user merged a1.. and b1.. into the a1 collection. const merged = { clusterKey: "session:a1", assetIds: ["a1", "a2", "a3", "b1", "b2"] }; expect(keys(reconcileUserEdits(computed, [merged], "session"))).toEqual([ ["session:c1", ["c1", "c2", "c3"]], ]); }); it("keeps both halves of a split and places new assets outside them", () => { // Split c1 | c2 c3; a later sync adds c4 to the computed session. const grown = [ ...computed.slice(0, 2), { key: "session:c1", assetIds: ["c1", "c2", "c3", "c4"] }, ]; const kept = { clusterKey: "session:c1", assetIds: ["c1"] }; const splitOff = { clusterKey: null, assetIds: ["c2", "c3"] }; expect(keys(reconcileUserEdits(grown, [kept, splitOff], "session"))).toEqual([ ["session:a1", ["a1", "a2", "a3"]], ["session:b1", ["b1", "b2"]], ["session:c1", ["c1", "c4"]], ]); // When the kept half is not a subset any more it freezes; c4 gets its own key. const moved = [...computed.slice(0, 2), { key: "session:c0", assetIds: ["c0", "c4"] }]; expect( keys(reconcileUserEdits(moved, [{ ...kept, assetIds: ["c1", "c9"] }], "session")), ).toEqual([ ["session:a1", ["a1", "a2", "a3"]], ["session:b1", ["b1", "b2"]], ["session:c0", ["c0", "c4"]], ]); }); it("lets a renamed collection follow its cluster, even when the cluster key changes", () => { const renamed = { clusterKey: "session:a1", assetIds: ["a1", "a2"] }; const earlier = [ { key: "session:a0", assetIds: ["a0", "a1", "a2", "a3"] }, ...computed.slice(1), ]; expect(keys(reconcileUserEdits(earlier, [renamed], "session"))).toEqual([ ["session:a1", ["a0", "a1", "a2", "a3"]], ["session:b1", ["b1", "b2"]], ["session:c1", ["c1", "c2", "c3"]], ]); }); it("re-keys a cluster that lost its first asset to a split-off collection", () => { const splitOff = { clusterKey: null, assetIds: ["a1"] }; expect(keys(reconcileUserEdits(computed, [splitOff], "theme"))).toEqual([ ["theme:a2", ["a2", "a3"]], ["session:b1", ["b1", "b2"]], ["session:c1", ["c1", "c2", "c3"]], ]); }); it("ignores empty user collections", () => { expect( reconcileUserEdits(computed, [{ clusterKey: "session:a1", assetIds: [] }], "session"), ).toEqual(computed); }); });