-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathdelete.test.ts
More file actions
170 lines (134 loc) · 6.09 KB
/
delete.test.ts
File metadata and controls
170 lines (134 loc) · 6.09 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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
import { describe, it, expect, beforeEach, afterEach } from "vitest";
import { runCli } from "./index.js";
import type { CliTestFixture } from "./test-helpers.js";
import { createCliTestFixture, createTaskAndGetId } from "./test-helpers.js";
describe("delete command", () => {
let fixture: CliTestFixture;
beforeEach(() => {
fixture = createCliTestFixture();
});
afterEach(() => {
fixture.cleanup();
});
it("deletes a task with force flag", async () => {
const taskId = await createTaskAndGetId(fixture, "To delete");
await runCli(["delete", taskId, "-f"], { storage: fixture.storage });
expect(fixture.output.stdout.join("\n")).toContain("Deleted");
const tasks = await fixture.storage.readAsync();
expect(tasks.tasks).toHaveLength(0);
});
it("fails for nonexistent task", async () => {
await expect(
runCli(["delete", "nonexist", "-f"], { storage: fixture.storage }),
).rejects.toThrow("process.exit");
expect(fixture.output.stderr.join("\n")).toContain("not found");
});
it("requires task ID", async () => {
await expect(
runCli(["delete", "-f"], { storage: fixture.storage }),
).rejects.toThrow("process.exit");
expect(fixture.output.stderr.join("\n")).toContain("Task ID is required");
});
it("shows help with --help flag", async () => {
await runCli(["delete", "--help"], { storage: fixture.storage });
const out = fixture.output.stdout.join("\n");
expect(out).toContain("dex delete");
expect(out).toContain("--force");
});
describe("cascade deletion", () => {
it("deletes task and all its children", async () => {
const parentId = await createTaskAndGetId(fixture, "Parent task");
await createTaskAndGetId(fixture, "Child 1", { parent: parentId });
await createTaskAndGetId(fixture, "Child 2", { parent: parentId });
await runCli(["delete", parentId, "-f"], { storage: fixture.storage });
const tasks = await fixture.storage.readAsync();
expect(tasks.tasks).toHaveLength(0);
});
it("deletes nested descendants (grandchildren)", async () => {
const grandparentId = await createTaskAndGetId(fixture, "Grandparent");
const parentId = await createTaskAndGetId(fixture, "Parent", {
parent: grandparentId,
});
await createTaskAndGetId(fixture, "Child", { parent: parentId });
await runCli(["delete", grandparentId, "-f"], {
storage: fixture.storage,
});
const tasks = await fixture.storage.readAsync();
expect(tasks.tasks).toHaveLength(0);
});
it("only deletes descendants, not siblings", async () => {
const parentId = await createTaskAndGetId(fixture, "Parent");
const child1Id = await createTaskAndGetId(fixture, "Child 1", {
parent: parentId,
});
await createTaskAndGetId(fixture, "Child 2", { parent: parentId });
await runCli(["delete", child1Id, "-f"], { storage: fixture.storage });
const tasks = await fixture.storage.readAsync();
expect(tasks.tasks).toHaveLength(2);
expect(tasks.tasks.find((t) => t.name === "Parent")).toBeDefined();
expect(tasks.tasks.find((t) => t.name === "Child 2")).toBeDefined();
});
});
describe("blocking relationship cleanup", () => {
it("removes deleted task from blockedBy arrays of other tasks", async () => {
const blockerId = await createTaskAndGetId(fixture, "Blocker");
await createTaskAndGetId(fixture, "Blocked task", {
blockedBy: blockerId,
});
await runCli(["delete", blockerId, "-f"], { storage: fixture.storage });
const tasks = await fixture.storage.readAsync();
expect(tasks.tasks).toHaveLength(1);
expect(tasks.tasks[0].blockedBy).not.toContain(blockerId);
expect(tasks.tasks[0].blockedBy).toEqual([]);
});
it("removes deleted task from blocks arrays of other tasks", async () => {
const blockerId = await createTaskAndGetId(fixture, "Blocker");
const blockedId = await createTaskAndGetId(fixture, "Blocked task", {
blockedBy: blockerId,
});
await runCli(["delete", blockedId, "-f"], { storage: fixture.storage });
const tasks = await fixture.storage.readAsync();
expect(tasks.tasks).toHaveLength(1);
expect(tasks.tasks[0].id).toBe(blockerId);
expect(tasks.tasks[0].blocks).not.toContain(blockedId);
expect(tasks.tasks[0].blocks).toEqual([]);
});
it("removes deleted task from children arrays of parent tasks", async () => {
const parentId = await createTaskAndGetId(fixture, "Parent");
const childId = await createTaskAndGetId(fixture, "Child", {
parent: parentId,
});
// Verify parent has child in children array initially
let tasks = await fixture.storage.readAsync();
const parentBefore = tasks.tasks.find((t) => t.id === parentId);
expect(parentBefore?.children).toContain(childId);
// Create a three-level hierarchy then delete the middle
const grandparentId = await createTaskAndGetId(fixture, "Grandparent");
const middleId = await createTaskAndGetId(fixture, "Middle", {
parent: grandparentId,
});
await runCli(["delete", middleId, "-f"], { storage: fixture.storage });
tasks = await fixture.storage.readAsync();
const grandparent = tasks.tasks.find((t) => t.id === grandparentId);
expect(grandparent?.children).not.toContain(middleId);
});
});
it("returns the deleted task info in output", async () => {
const taskId = await createTaskAndGetId(fixture, "Task to delete");
await runCli(["delete", taskId, "-f"], { storage: fixture.storage });
const out = fixture.output.stdout.join("\n");
expect(out).toContain("Deleted");
expect(out).toContain(taskId);
});
it("fails when multiple positional arguments are provided", async () => {
const taskId = await createTaskAndGetId(fixture, "Task");
await expect(
runCli(["delete", taskId, "extra-arg", "-f"], {
storage: fixture.storage,
}),
).rejects.toThrow("process.exit");
expect(fixture.output.stderr.join("\n")).toContain(
"unexpected positional argument",
);
});
});