Files
kandev/apps/web/lib/api/domains/queue-api.plan-comments.test.ts
T
2026-09-14 19:19:09 +01:00

280 lines
9.2 KiB
TypeScript

/* eslint-disable sonarjs/no-duplicate-string -- Wire actions and fixture IDs are intentionally repeated for readability. */
import { beforeEach, describe, expect, it, vi } from "vitest";
const getWebSocketClientMock = vi.hoisted(() => vi.fn());
const QUEUE_ADD_ACTION = "message.queue.add";
const PRIMARY_SESSION_ID = "session-primary";
const CLIENT_QUEUE_ID = "client-queue-1";
const INCARNATION_ID = "incarnation-1";
vi.mock("@/lib/ws/connection", () => ({
getWebSocketClient: getWebSocketClientMock,
}));
import { queueMessage } from "./queue-api";
beforeEach(() => {
getWebSocketClientMock.mockReset();
});
// eslint-disable-next-line max-lines-per-function -- Admission retries share one wire-contract fixture.
describe("task plan queue admission", () => {
it("forwards idempotent task plan comment admission fields", async () => {
const request = vi.fn().mockResolvedValue({ id: "q-1" });
getWebSocketClientMock.mockReturnValue({ request });
await queueMessage({
session_id: PRIMARY_SESSION_ID,
session_incarnation_id: INCARNATION_ID,
task_id: "task-1",
client_queue_id: CLIENT_QUEUE_ID,
content: "",
plan_mode: true,
plan_comment_refs: [{ id: "comment-1", version: 2 }],
require_primary_session: true,
});
expect(request).toHaveBeenCalledWith(
QUEUE_ADD_ACTION,
{
session_id: PRIMARY_SESSION_ID,
session_incarnation_id: INCARNATION_ID,
task_id: "task-1",
client_queue_id: CLIENT_QUEUE_ID,
content: "",
plan_mode: true,
plan_comment_refs: [{ id: "comment-1", version: 2 }],
require_primary_session: true,
},
10000,
);
});
it("reconciles a timed-out comment queue admission from queue state", async () => {
const queued = {
id: CLIENT_QUEUE_ID,
session_id: PRIMARY_SESSION_ID,
task_id: "task-1",
content: "resolved",
plan_mode: true,
queued_at: "2026-09-02T00:00:00Z",
};
const request = vi.fn(async (action: string) => {
if (action === QUEUE_ADD_ACTION) throw new Error("WebSocket request timed out");
if (action === "message.queue.get") {
return { entries: [queued], count: 1, max: 10, merge_enabled: true };
}
return undefined;
});
getWebSocketClientMock.mockReturnValue({ request });
await expect(
queueMessage({
session_id: PRIMARY_SESSION_ID,
session_incarnation_id: INCARNATION_ID,
task_id: "task-1",
client_queue_id: CLIENT_QUEUE_ID,
content: "",
plan_comment_refs: [{ id: "comment-1", version: 2 }],
}),
).resolves.toEqual(queued);
expect(request).toHaveBeenCalledWith("message.queue.get", {
task_id: "task-1",
session_id: PRIMARY_SESSION_ID,
session_incarnation_id: INCARNATION_ID,
});
});
it("reconciles an already-drained admission from transcript metadata", async () => {
const request = vi.fn(async (action: string) => {
if (action === QUEUE_ADD_ACTION) throw new Error("WebSocket request timed out");
if (action === "message.queue.get") {
return { entries: [], count: 0, max: 10, merge_enabled: true };
}
if (action === "message.list") {
return {
messages: [
{
id: "message-1",
session_id: PRIMARY_SESSION_ID,
task_id: "task-1",
content: "resolved",
metadata: { client_queue_id: CLIENT_QUEUE_ID },
created_at: "2026-09-02T00:00:00Z",
},
],
};
}
return undefined;
});
getWebSocketClientMock.mockReturnValue({ request });
await expect(
queueMessage({
session_id: PRIMARY_SESSION_ID,
session_incarnation_id: INCARNATION_ID,
task_id: "task-1",
client_queue_id: CLIENT_QUEUE_ID,
content: "",
plan_mode: true,
plan_comment_refs: [{ id: "comment-1", version: 2 }],
}),
).resolves.toMatchObject({ id: CLIENT_QUEUE_ID, content: "resolved" });
});
it("reconciles an admission folded into a Send Now transcript", async () => {
const request = vi.fn(async (action: string) => {
if (action === QUEUE_ADD_ACTION) throw new Error("WebSocket request timed out");
if (action === "message.queue.get") {
return { entries: [], count: 0, max: 10, merge_enabled: true };
}
if (action === "message.list") {
return {
messages: [
{
id: "message-1",
session_id: PRIMARY_SESSION_ID,
task_id: "task-1",
content: "ordinary\n\nresolved",
metadata: {
send_now_sources: [
{ id: "ordinary", metadata: {} },
{ id: CLIENT_QUEUE_ID, metadata: { client_queue_id: CLIENT_QUEUE_ID } },
],
},
created_at: "2026-09-02T00:00:00Z",
},
],
};
}
return undefined;
});
getWebSocketClientMock.mockReturnValue({ request });
await expect(
queueMessage({
session_id: PRIMARY_SESSION_ID,
session_incarnation_id: INCARNATION_ID,
task_id: "task-1",
client_queue_id: CLIENT_QUEUE_ID,
content: "",
plan_comment_refs: [{ id: "comment-1", version: 2 }],
}),
).resolves.toMatchObject({ id: CLIENT_QUEUE_ID, content: "ordinary\n\nresolved" });
});
it("searches older transcript pages for an already-drained admission", async () => {
const request = vi.fn(async (action: string, params?: { before?: string }) => {
if (action === QUEUE_ADD_ACTION) throw new Error("WebSocket request timed out");
if (action === "message.queue.get") {
return { entries: [], count: 0, max: 10, merge_enabled: true };
}
if (action === "message.list" && !params?.before) {
return { messages: [], has_more: true, cursor: "message-100" };
}
if (action === "message.list" && params?.before === "message-100") {
return {
messages: [
{
id: "message-older",
session_id: PRIMARY_SESSION_ID,
task_id: "task-1",
content: "resolved earlier",
metadata: { client_queue_id: CLIENT_QUEUE_ID },
created_at: "2026-09-02T00:00:00Z",
},
],
has_more: false,
};
}
return undefined;
});
getWebSocketClientMock.mockReturnValue({ request });
await expect(
queueMessage({
session_id: PRIMARY_SESSION_ID,
session_incarnation_id: INCARNATION_ID,
task_id: "task-1",
client_queue_id: CLIENT_QUEUE_ID,
content: "",
plan_comment_refs: [{ id: "comment-1", version: 2 }],
}),
).resolves.toMatchObject({ id: CLIENT_QUEUE_ID, content: "resolved earlier" });
expect(request).toHaveBeenCalledWith(
"message.list",
expect.objectContaining({ before: "message-100" }),
5000,
);
});
it("retries an uncertain admission after reconciliation finds no record", async () => {
const queued = {
id: CLIENT_QUEUE_ID,
session_id: PRIMARY_SESSION_ID,
task_id: "task-1",
content: "resolved",
queued_at: "2026-09-02T00:00:00Z",
};
let addCalls = 0;
const request = vi.fn(async (action: string) => {
if (action === QUEUE_ADD_ACTION) {
addCalls++;
if (addCalls === 1) throw new Error("WebSocket request timed out");
return queued;
}
if (action === "message.queue.get") return { entries: [], count: 0, max: 10 };
if (action === "message.list") return { messages: [], has_more: false };
return undefined;
});
getWebSocketClientMock.mockReturnValue({ request });
await expect(
queueMessage({
session_id: PRIMARY_SESSION_ID,
session_incarnation_id: INCARNATION_ID,
task_id: "task-1",
client_queue_id: CLIENT_QUEUE_ID,
content: "",
plan_comment_refs: [{ id: "comment-1", version: 2 }],
}),
).resolves.toEqual(queued);
expect(addCalls).toBe(2);
});
it("reconciles acceptance after the uncertain retry also loses its response", async () => {
const queued = {
id: CLIENT_QUEUE_ID,
session_id: PRIMARY_SESSION_ID,
task_id: "task-1",
content: "resolved",
queued_at: "2026-09-02T00:00:00Z",
};
let queueReads = 0;
const request = vi.fn(async (action: string) => {
if (action === QUEUE_ADD_ACTION) throw new Error("WebSocket request timed out");
if (action === "message.queue.get") {
queueReads++;
return queueReads === 1
? { entries: [], count: 0, max: 10 }
: { entries: [queued], count: 1, max: 10 };
}
if (action === "message.list") return { messages: [], has_more: false };
return undefined;
});
getWebSocketClientMock.mockReturnValue({ request });
await expect(
queueMessage({
session_id: PRIMARY_SESSION_ID,
session_incarnation_id: INCARNATION_ID,
task_id: "task-1",
client_queue_id: CLIENT_QUEUE_ID,
content: "",
plan_comment_refs: [{ id: "comment-1", version: 2 }],
}),
).resolves.toEqual(queued);
expect(request).toHaveBeenCalledTimes(5);
});
});