fix: harden TaskTree CLI contracts
This commit is contained in:
@@ -31,8 +31,10 @@ test("HTTP command endpoint returns the same dispatcher DTO", async () => {
|
||||
|
||||
test("dispatcher keeps batch validation atomic and completion gated", async () => {
|
||||
let batchCalls = 0;
|
||||
let createCalls = 0;
|
||||
let updateCalls = 0;
|
||||
const store = {
|
||||
async createTask() { createCalls += 1; return null; },
|
||||
async createTasks() { batchCalls += 1; return []; },
|
||||
async updateTask() { updateCalls += 1; return null; },
|
||||
async deleteTask() { return false; }
|
||||
@@ -44,6 +46,17 @@ test("dispatcher keeps batch validation atomic and completion gated", async () =
|
||||
assert.equal(batch.error?.code, "invalid_title");
|
||||
assert.equal(batchCalls, 0);
|
||||
|
||||
const invalidRange = await dispatch({
|
||||
operation: "task.create",
|
||||
groupId: "tg_1",
|
||||
title: "Invalid range",
|
||||
startAt: "2026-07-19T00:00:00.000Z",
|
||||
dueAt: "2026-07-18T00:00:00.000Z"
|
||||
});
|
||||
assert.equal(invalidRange.ok, false);
|
||||
assert.equal(invalidRange.error?.code, "invalid_time_range");
|
||||
assert.equal(createCalls, 0);
|
||||
|
||||
const bypass = await dispatch({ operation: "task.update", taskId: "tt_1", status: "completed" });
|
||||
assert.equal(bypass.ok, false);
|
||||
assert.equal(bypass.error?.code, "completion_command_required");
|
||||
@@ -52,6 +65,10 @@ test("dispatcher keeps batch validation atomic and completion gated", async () =
|
||||
const missing = await dispatch({ operation: "task.delete", taskId: "tt_missing" });
|
||||
assert.equal(missing.ok, false);
|
||||
assert.equal(missing.error?.code, "task_not_found");
|
||||
|
||||
const workflow = await dispatch({ operation: "workflow.start", taskId: "tt_1" });
|
||||
assert.equal(workflow.ok, false);
|
||||
assert.equal(workflow.error?.code, "temporal_address_required");
|
||||
});
|
||||
|
||||
test("native PostgreSQL store exposes overview and three task levels", async () => {
|
||||
@@ -59,6 +76,7 @@ test("native PostgreSQL store exposes overview and three task levels", async ()
|
||||
assert.ok(databaseUrl, "TASKTREE_DATABASE_URL is required for the TaskTree store integration test");
|
||||
const store = new TaskTreeStore(databaseUrl);
|
||||
const group = await store.createGroup(`TaskTree test ${Date.now()}`);
|
||||
const otherGroup = await store.createGroup(`TaskTree cross-group test ${Date.now()}`);
|
||||
try {
|
||||
const task = await store.createTask({
|
||||
groupId: group.id,
|
||||
@@ -71,6 +89,32 @@ test("native PostgreSQL store exposes overview and three task levels", async ()
|
||||
const batch = await store.createTasks({ groupId: group.id, parentId: task.id, titles: ["Batch one", "Batch two"] });
|
||||
assert.deepEqual(batch.map((item) => item.title), ["Batch one", "Batch two"]);
|
||||
await store.createMilestone({ groupId: group.id, taskId: task.id, title: "Review", occursAt: "2026-07-17T00:00:00.000Z" });
|
||||
await assert.rejects(
|
||||
store.createMilestone({ groupId: otherGroup.id, taskId: task.id, title: "Wrong group", occursAt: "2026-07-17T00:00:00.000Z" }),
|
||||
(error: any) => error?.code === "task_group_mismatch"
|
||||
);
|
||||
await assert.rejects(
|
||||
store.createMilestone({ groupId: "tg_missing", title: "Missing group", occursAt: "2026-07-17T00:00:00.000Z" }),
|
||||
(error: any) => error?.code === "group_not_found"
|
||||
);
|
||||
await assert.rejects(
|
||||
store.createMilestone({ groupId: group.id, taskId: "tt_missing", title: "Missing task", occursAt: "2026-07-17T00:00:00.000Z" }),
|
||||
(error: any) => error?.code === "task_not_found"
|
||||
);
|
||||
await assert.rejects(
|
||||
store.createTask({
|
||||
groupId: group.id,
|
||||
title: "Invalid range",
|
||||
startAt: "2026-07-19T00:00:00.000Z",
|
||||
dueAt: "2026-07-18T00:00:00.000Z"
|
||||
}),
|
||||
(error: any) => error?.code === "invalid_time_range"
|
||||
);
|
||||
await assert.rejects(
|
||||
store.updateTask(task.id, { dueAt: "2026-07-15T00:00:00.000Z" }),
|
||||
(error: any) => error?.code === "invalid_time_range"
|
||||
);
|
||||
assert.equal((await store.getTask(task.id))?.dueAt, "2026-07-18T00:00:00.000Z");
|
||||
await assert.rejects(
|
||||
store.completeTask(task.id),
|
||||
(error: any) => error?.code === "execution_report_required"
|
||||
@@ -117,6 +161,7 @@ test("native PostgreSQL store exposes overview and three task levels", async ()
|
||||
assert.equal(overview?.startAt, "2026-07-16T00:00:00.000Z");
|
||||
assert.equal(overview?.dueAt, "2026-07-18T00:00:00.000Z");
|
||||
} finally {
|
||||
await store.deleteGroup(otherGroup.id);
|
||||
await store.deleteGroup(group.id);
|
||||
await store.close();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user