Files
saltbo__agent-kanban/tests/task-json-fields.test.ts
saltbo 53b75fe880 refactor!: adopt the Enbor agency contract
Move internal upstream integration code to generic Agency names, consume @realmroot/enbor-sdk 0.3.0, expose Enbor public errors and Runner UI, and migrate active integration state without compatibility readers.

BREAKING CHANGE: legacy AMA integration names and metadata contracts are removed.
2026-09-03 16:32:57 -04:00

165 lines
6.6 KiB
TypeScript

// @vitest-environment node
import { Miniflare } from "miniflare";
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { applyMigrations as applyAllMigrations, seedUser } from "./helpers/db";
let db: D1Database;
let mf: Miniflare;
beforeAll(async () => {
mf = new Miniflare({
modules: true,
script: "export default { fetch() { return new Response('ok'); } }",
d1Databases: { DB: "test-db" },
});
db = await mf.getD1Database("DB");
await applyAllMigrations(db);
});
afterAll(async () => {
await mf.dispose();
});
describe("task JSON field parsing (labels, input, metadata)", () => {
const ownerId = "user-json-task";
let boardId: string;
let taskId: string;
it("setup: create board", async () => {
await seedUser(db, ownerId, `${ownerId}@test.local`);
const { createBoard } = await import("../server/adapters/d1/boardRepo");
const board = await createBoard(db, ownerId, "json-test-board", "ops");
boardId = board.id;
await createBoard(db, ownerId, "json-empty-labels-board", "ops");
await db
.prepare("UPDATE boards SET labels = ? WHERE id = ?")
.bind(
JSON.stringify([
{ name: "bug", color: "#EF4444", description: "Bug fix" },
{ name: "urgent", color: "#EAB308", description: "Urgent work" },
{ name: "feature", color: "#22D3EE", description: "Feature work" },
]),
boardId,
)
.run();
});
it("[spec: tasks/structured-fields] createTask returns labels and input in typed representations", async () => {
const { createTask } = await import("../server/adapters/d1/taskRepo");
const task = await createTask(db, ownerId, {
title: "Test labels and input",
board_id: boardId,
labels: ["bug", "urgent"],
input: { prompt: "fix the thing", context: { file: "main.ts", line: 42 } },
metadata: { annotations: { "enbor.sessionId": "session_123", "enbor.dispatch.result": "accepted" } },
});
taskId = task.id;
expect(Array.isArray(task.labels)).toBe(true);
expect(task.labels).toEqual(["bug", "urgent"]);
expect(typeof task.input).toBe("object");
expect(task.input).toEqual({ prompt: "fix the thing", context: { file: "main.ts", line: 42 } });
expect(task.metadata).toEqual({ annotations: { "enbor.sessionId": "session_123", "enbor.dispatch.result": "accepted" } });
});
it("createTask rejects labels that are not defined on the board", async () => {
const { createTask } = await import("../server/adapters/d1/taskRepo");
await expect(
createTask(db, ownerId, {
title: "Unknown label",
board_id: boardId,
labels: ["missing"],
}),
).rejects.toThrow("Label not found: missing");
});
it("createTask with null labels/input returns null", async () => {
const { createTask } = await import("../server/adapters/d1/taskRepo");
const task = await createTask(db, ownerId, {
title: "Bare task",
board_id: boardId,
});
expect(task.labels).toBeNull();
expect(task.input).toBeNull();
expect(task.metadata).toEqual({});
});
it("listTasks returns parsed labels and input", async () => {
const { listTasks } = await import("../server/adapters/d1/taskRepo");
const tasks = await listTasks(db, ownerId, { board_id: boardId });
const task = tasks.find((t) => t.id === taskId)!;
expect(Array.isArray(task.labels)).toBe(true);
expect(task.labels).toEqual(["bug", "urgent"]);
expect(typeof task.input).toBe("object");
expect(task.input!.prompt).toBe("fix the thing");
expect(task.metadata).toEqual({ annotations: { "enbor.sessionId": "session_123", "enbor.dispatch.result": "accepted" } });
});
it("getTask returns parsed labels and input", async () => {
const { getTask } = await import("../server/adapters/d1/taskRepo");
const task = await getTask(db, taskId, ownerId);
expect(task).toBeTruthy();
expect(Array.isArray(task!.labels)).toBe(true);
expect(task!.labels).toEqual(["bug", "urgent"]);
expect(typeof task!.input).toBe("object");
expect(task!.input!.context).toEqual({ file: "main.ts", line: 42 });
expect(task!.metadata).toEqual({ annotations: { "enbor.sessionId": "session_123", "enbor.dispatch.result": "accepted" } });
});
it("updateTask accepts arrays/objects and returns parsed values", async () => {
const { updateTask } = await import("../server/adapters/d1/taskRepo");
const task = await updateTask(db, taskId, {
labels: ["feature"],
input: { prompt: "new prompt" },
metadata: { annotations: { "enbor.sessionId": "session_456", "enbor.dispatch.result": "resumed" } },
});
expect(task).toBeTruthy();
expect(task!.labels).toEqual(["feature"]);
expect(task!.input).toEqual({ prompt: "new prompt" });
expect(task!.metadata).toEqual({ annotations: { "enbor.sessionId": "session_456", "enbor.dispatch.result": "resumed" } });
});
it("updated values persist through getTask", async () => {
const { getTask } = await import("../server/adapters/d1/taskRepo");
const task = await getTask(db, taskId, ownerId);
expect(task!.labels).toEqual(["feature"]);
expect(task!.input).toEqual({ prompt: "new prompt" });
expect(task!.metadata).toEqual({ annotations: { "enbor.sessionId": "session_456", "enbor.dispatch.result": "resumed" } });
});
it("getBoard returns tasks with parsed labels and input", async () => {
const { getBoard } = await import("../server/adapters/d1/boardRepo");
const board = await getBoard(db, boardId, ownerId);
expect(board).toBeTruthy();
const task = board!.tasks.find((t) => t.id === taskId)!;
expect(Array.isArray(task.labels)).toBe(true);
expect(task.labels).toEqual(["feature"]);
expect(typeof task.input).toBe("object");
expect(task.input).toEqual({ prompt: "new prompt" });
expect(task.metadata).toEqual({ annotations: { "enbor.sessionId": "session_456", "enbor.dispatch.result": "resumed" } });
});
it("[spec: boards/labels] deleteBoardLabel removes the label from tasks on the same board", async () => {
const { createTask, getTask } = await import("../server/adapters/d1/taskRepo");
const { deleteBoardLabel } = await import("../server/adapters/d1/boardRepo");
const task = await createTask(db, ownerId, {
title: "Delete label propagation",
board_id: boardId,
labels: ["bug", "feature"],
});
const board = await deleteBoardLabel(db, boardId, ownerId, "bug");
const updated = await getTask(db, task.id, ownerId);
expect(board!.labels.map((label) => label.name)).not.toContain("bug");
expect(updated!.labels).toEqual(["feature"]);
});
});