import { describe, expect, it } from "vitest"; import { assertParent, collectSubtree, validateSiblingOrder, } from "./hierarchy"; const pages = [ { id: 1, parentId: -1, orderNum: 0 }, { id: 2, parentId: 1, orderNum: 1 }, { id: 3, parentId: 1, orderNum: 2 }, { id: 4, parentId: 2, orderNum: 1 }, ]; describe("catalog hierarchy commands", () => { it("rejects self, descendant, missing and malformed destinations", () => { for (const parent of [1, 2, 4, 999, 1.5, -2]) expect(() => assertParent(pages, 1, parent)).toThrow(); }); it("allows detaching a page from an inherited cycle", () => expect(() => assertParent( [ { id: 1, parentId: 2 }, { id: 2, parentId: 1 }, ], 1, -1, ), ).not.toThrow()); it("rejects a destination with an inherited parent cycle", () => expect(() => assertParent( [ { id: 1, parentId: -1 }, { id: 2, parentId: 3 }, { id: 3, parentId: 2 }, ], 1, 2, ), ).toThrow()); it("collects cyclic subtrees once without recursion", () => expect( collectSubtree( [ { id: 1, parentId: 2 }, { id: 2, parentId: 1 }, ], 1, ), ).toEqual([1, 2])); it("rejects stale or partial sibling lists before reordering", () => { expect(() => validateSiblingOrder(pages, 1, [2], [2, 3])).toThrow(); expect(() => validateSiblingOrder(pages, 1, [3, 2], [3, 2])).toThrow(); expect(() => validateSiblingOrder(pages, 1, [2, 2], [2, 3])).toThrow(); }); it("accepts a complete reorder matching the previous sibling order", () => expect(() => validateSiblingOrder(pages, 1, [3, 2], [2, 3])).not.toThrow()); }); it("orders mixed root conventions with a stable ID tie breaker", () => { const roots = [ { id: 3, parentId: 0, orderNum: 1 }, { id: 2, parentId: -1, orderNum: 1 }, ]; expect(() => validateSiblingOrder(roots, -1, [3, 2], [2, 3])).not.toThrow(); expect(() => validateSiblingOrder(roots, -1, [2, 3], [3, 2])).toThrow(); });