feat: strengthen task workflow and compaction recovery

This commit is contained in:
lesetong
2026-07-23 01:23:10 +08:00
parent fe05b278a4
commit 7d876735b2
13 changed files with 380 additions and 81 deletions
+43 -3
View File
@@ -170,9 +170,49 @@ test("loads the latest valid mutation snapshot from a session branch", () => {
assert.deepEqual(state.list().map((task) => task.name), ["First", "Second"]);
});
test("rejects multiline names while allowing multiline descriptions", () => {
test("supports optional descriptions and rejects invalid provided values", () => {
const state = new TaskState();
assert.throws(() => state.add([{ name: "Bad\nname", description: "Valid" }]), /single line/);
state.add([{ name: "Valid", description: "Line one\nLine two" }]);
assert.equal(state.list()[0]?.description, "Line one\nLine two");
assert.throws(() => state.add([{ name: "Blank description", description: " " }]), /must not be empty/);
state.add([
{ name: "No description" },
{ name: "Detailed", description: "Line one\nLine two" },
]);
assert.equal(state.list()[0]?.description, undefined);
assert.equal(state.list()[1]?.description, "Line one\nLine two");
});
test("set append rejects force and still requires at least one task", () => {
const state = new TaskState();
assert.throws(() => state.set("append", [], undefined), /tasks must not be empty/);
assert.throws(() => state.set("append", [{ name: "Task" }], false), /force is allowed only in replace mode/);
assert.deepEqual(state.snapshot(), { tasks: [], nextId: 1 });
});
test("set replace is atomic, guarded, and resets IDs", () => {
const state = new TaskState();
state.add([{ name: "Old", status: "in-progress" }]);
const before = state.snapshot();
assert.throws(() => state.set("replace", [{ name: "New" }]), /requires force/);
assert.throws(
() => state.set("replace", [{ name: "Invalid parent", parentId: 1 }], true),
/parentId is not allowed in replace mode/,
);
assert.deepEqual(state.snapshot(), before);
const replaced = state.set("replace", [{ name: "New", children: [{ name: "Child" }] }], true);
assert.deepEqual(replaced.map((task) => task.id), [1, 2]);
assert.deepEqual(state.list().map((task) => task.name), ["New", "Child"]);
assert.equal(state.snapshot().nextId, 3);
state.set("replace", [], true);
assert.deepEqual(state.snapshot(), { tasks: [], nextId: 1 });
});
test("set replace does not require force after all existing tasks are done", () => {
const state = new TaskState();
state.add([{ name: "Done", status: "done" }]);
state.set("replace", [{ name: "Fresh" }]);
assert.deepEqual(state.list().map((task) => [task.id, task.name]), [[1, "Fresh"]]);
});