mirror of
https://github.com/saltbo/agent-kanban.git
synced 2026-09-19 09:01:47 +08:00
96fdd70d2c
* 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>
42 lines
1.5 KiB
TypeScript
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),
|
|
});
|
|
});
|
|
});
|