import assert from "node:assert/strict"; import { test } from "bun:test"; import { createTaskTreeDispatcher } from "./dispatcher.ts"; import { createTaskTreeHttpApp } from "./http.ts"; import { TaskTreeStore } from "./store.ts"; test("HTTP command endpoint returns the same dispatcher DTO", async () => { const group = { id: "tg_contract", name: "Contract", description: "", createdAt: "2026-07-16T00:00:00.000Z", updatedAt: "2026-07-16T00:00:00.000Z" }; const store = { async listGroups() { return [group]; } }; const dispatch = createTaskTreeDispatcher({ store: store as TaskTreeStore }); const expected = await dispatch({ operation: "group.list" }); const app = createTaskTreeHttpApp({ dispatch }); const response = await app.fetch(new Request("http://tasktree/v1/tasktree/commands", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ operation: "group.list" }) })); assert.equal(response.status, 200); assert.deepEqual(await response.json(), expected); }); test("native PostgreSQL store exposes timeline DTO and rejects a third task level", async () => { const databaseUrl = process.env.TASKTREE_DATABASE_URL; 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()}`); try { const task = await store.createTask({ groupId: group.id, title: "Root task", startAt: "2026-07-16T00:00:00.000Z", dueAt: "2026-07-18T00:00:00.000Z" }); const subtask = await store.createTask({ groupId: group.id, parentId: task.id, title: "Subtask" }); await store.createMilestone({ groupId: group.id, taskId: task.id, title: "Review", occursAt: "2026-07-17T00:00:00.000Z" }); await store.createReport({ taskId: subtask.id, title: "Execution", body: "passed" }); await assert.rejects( store.createTask({ groupId: group.id, parentId: subtask.id, title: "Unsupported third level" }), (error: any) => error?.code === "maximum_depth_exceeded" ); const timeline = await store.timeline(group.id); assert.equal(timeline?.tasks.length, 2); assert.equal(timeline?.tasks[0]?.kind, "task"); assert.equal(timeline?.tasks[1]?.kind, "subtask"); assert.equal(timeline?.milestones.length, 1); assert.equal(timeline?.reports.length, 1); } finally { await store.deleteGroup(group.id); await store.close(); } });