-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmind-map.test.ts
More file actions
80 lines (71 loc) · 2.83 KB
/
Copy pathmind-map.test.ts
File metadata and controls
80 lines (71 loc) · 2.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
// @vitest-environment node
import { describe, expect, it } from "vitest";
import { buildForest } from "@/app/(app)/workspaces/[wsId]/boards/[boardId]/mind-map-view";
import { wouldCreateParentCycle } from "@/lib/repositories/cards";
type C = { id: string; title: string; parentId: string | null; archived: boolean };
const c = (id: string, parentId: string | null = null, archived = false): C => ({
id,
title: id,
parentId,
archived,
});
describe("buildForest", () => {
it("nests children under their parent", () => {
const forest = buildForest([c("A"), c("B", "A"), c("C", "B")]);
expect(forest).toHaveLength(1);
expect(forest[0]!.id).toBe("A");
expect(forest[0]!.children[0]!.id).toBe("B");
expect(forest[0]!.children[0]!.children[0]!.id).toBe("C");
});
it("treats a missing or archived parent as a root", () => {
const missing = buildForest([c("X", "ghost")]);
expect(missing.map((n) => n.id)).toEqual(["X"]);
const archivedParent = buildForest([c("P", null, true), c("Q", "P")]);
// P is archived (dropped); Q's parent is gone → Q becomes a root.
expect(archivedParent.map((n) => n.id)).toEqual(["Q"]);
});
it("drops archived cards entirely", () => {
const forest = buildForest([c("A"), c("B", "A", true)]);
expect(forest).toHaveLength(1);
expect(forest[0]!.children).toHaveLength(0);
});
it("is cycle-safe (no infinite recursion)", () => {
// A↔B mutual parents — pathological data the repo normally prevents.
const forest = buildForest([c("A", "B"), c("B", "A")]);
// One is swept up as a root; the other nests once, then stops.
const ids = forest.map((n) => n.id);
expect(ids).toHaveLength(1);
expect(["A", "B"]).toContain(ids[0]);
});
});
describe("wouldCreateParentCycle", () => {
it("rejects self-parenting", () => {
expect(wouldCreateParentCycle({ cardId: "A", newParentId: "A", parentById: new Map() })).toBe(
true,
);
});
it("rejects making a descendant the parent", () => {
// B is a child of A; making A a child of B is a cycle.
const parentById = new Map<string, string | null>([
["A", null],
["B", "A"],
]);
expect(wouldCreateParentCycle({ cardId: "A", newParentId: "B", parentById })).toBe(true);
});
it("allows an unrelated parent", () => {
const parentById = new Map<string, string | null>([
["A", null],
["B", null],
["C", null],
]);
expect(wouldCreateParentCycle({ cardId: "C", newParentId: "A", parentById })).toBe(false);
});
it("does not loop forever on pre-existing cyclic data", () => {
const parentById = new Map<string, string | null>([
["X", "Y"],
["Y", "X"],
]);
// Z is unrelated to the X↔Y cycle → no new cycle, and it must terminate.
expect(wouldCreateParentCycle({ cardId: "Z", newParentId: "X", parentById })).toBe(false);
});
});