Files
saltbo__agent-kanban/tests/unit/component/task-review-api.test.ts
realmroot[bot] 96fdd70d2c feat(api)!: redesign task resource operations (#265)
* feat(api)!: redesign task resource operations

Publish the complete resource-oriented OpenAPI contract, enforce scope-based authorization at the HTTP boundary, and replace lifecycle command routes with Task PATCH and nested Claim/Event resources.

Add server-side Task CAS, migration coverage, updated Agent Skills, and browser/API regression tests.

BREAKING CHANGE: remove the legacy task lifecycle endpoints and move bounded waits to /tasks/{taskId}/events.

* refactor(tasks)!: make claims create-only

Remove Task Claim retrieval and deletion routes, the release scope, live deletion logic, and release workflow guidance.

Keep historical release records readable for migration and audit compatibility.

BREAKING CHANGE: remove GET and DELETE /tasks/{taskId}/claims/{claimId} and the task:release scope.

* test(ci): align actor and projection contracts

Cover machine and service Task Action writes and update Agent detail fixtures to the canonical assignedTo query and paged lowerCamelCase response.

* fix(api): preserve merge patch fields and consume browser pagination

* test(ui): include terminal pagination in agent task fixture

---------

Co-authored-by: jarvis <jarvis@agents.realmroot.dev>
2026-09-05 02:40:42 +00:00

42 lines
1.5 KiB
TypeScript

// @vitest-environment node
import { V2_API_VERSION } from "@shared";
import { afterEach, describe, expect, it, vi } from "vitest";
import { api } from "../../../src/lib/api";
vi.mock("../../../src/lib/auth-client", () => ({
getCsrfToken: () => "csrf-token",
getSession: vi.fn(),
}));
afterEach(() => {
vi.unstubAllGlobals();
});
describe("Web Task review API", () => {
it.each([
["complete", { status: "done" }, "done"],
["reject", { status: "in-progress", statusReason: "needs changes" }, "in_progress"],
] as const)("sends a %s status merge patch without reading or supplying an ETag", async (action, patch, expectedStatus) => {
const fetchMock = vi
.fn<typeof fetch>()
.mockResolvedValueOnce(Response.json({ id: "task/one", status: patch.status }, { status: 200, headers: { ETag: '"next-task-version"' } }));
vi.stubGlobal("fetch", fetchMock);
const result = action === "complete" ? api.tasks.complete("task/one") : api.tasks.reject("task/one", "needs changes");
await expect(result).resolves.toMatchObject({ id: "task/one", status: expectedStatus });
expect(fetchMock).toHaveBeenCalledTimes(1);
expect(fetchMock).toHaveBeenCalledWith("/api/tasks/task%2Fone", {
method: "PATCH",
headers: {
"Content-Type": "application/merge-patch+json",
"API-Version": V2_API_VERSION,
"x-csrf-token": "csrf-token",
},
credentials: "include",
body: JSON.stringify(patch),
});
});
});