Files
multica/packages/core/chat/pending.test.ts
Multica Eve 73283556d7 MUL-5750: fix(chat): keep idle sends out of follow-up queue (#6418)
* fix(chat): keep idle sends out of follow-up queue

Co-authored-by: multica-agent <github@multica.ai>

* fix(chat): preserve the positional queue head

Co-authored-by: multica-agent <github@multica.ai>

* fix(chat): polish deferred queue states

Co-authored-by: multica-agent <github@multica.ai>

* fix(migrations): resolve pending index prefix collision

Co-authored-by: multica-agent <github@multica.ai>

---------

Co-authored-by: Eve <eve@multica-ai.local>
Co-authored-by: multica-agent <github@multica.ai>
2026-08-05 15:06:42 +08:00

294 lines
8.9 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
enqueuePendingChatTask,
hideQueuedChatMessages,
prioritizePendingChatTask,
promotePendingChatTask,
removePendingChatTask,
} from "./pending";
const task = (task_id: string, created_at: string) => ({
task_id,
status: "queued",
created_at,
content: `message ${task_id}`,
});
describe("pending chat queue", () => {
it("keeps the active task at the head and follow-ups in FIFO order", () => {
const active = { ...task("active", "2026-01-01T00:00:00Z"), status: "running" };
const withLater = enqueuePendingChatTask(active, task("later", "2026-01-01T00:00:02Z"));
const result = enqueuePendingChatTask(withLater, task("next", "2026-01-01T00:00:01Z"));
expect(result.task_id).toBe("active");
expect(result.queued_tasks?.map((item) => item.task_id)).toEqual(["next", "later"]);
});
it("treats the first accepted task as the current head before it starts running", () => {
const queued = {
...task("next", "2026-01-01T00:00:01Z"),
message_id: "message-next",
};
const pending = enqueuePendingChatTask(undefined, queued);
expect(pending.queued_tasks).toEqual([]);
expect(promotePendingChatTask(pending, "next", "running").queued_tasks).toEqual([]);
});
it("keeps an authoritative queued send hidden while its head is still loading", () => {
const queued = {
...task("next", "2026-01-01T00:00:01Z"),
message_id: "message-next",
};
expect(enqueuePendingChatTask(undefined, queued, true)).toEqual({
queued_tasks: [queued],
});
});
it("preserves a follow-up when its response arrives before the head response", () => {
const followUp = {
...task("next", "2026-01-01T00:00:01Z"),
message_id: "message-next",
};
const waitingForHead = enqueuePendingChatTask(undefined, followUp, true);
const head = {
...task("head", "2026-01-01T00:00:00Z"),
message_id: "message-head",
};
expect(enqueuePendingChatTask(waitingForHead, head, false)).toEqual({
...head,
queued_tasks: [followUp],
});
});
it("keeps a send response preview when a sparse queued event arrives later", () => {
const current = {
...task("active", "2026-01-01T00:00:00Z"),
status: "running",
queued_tasks: [
{
...task("next", "2026-01-01T00:00:01Z"),
message_id: "message-next",
content: "Keep this visible preview",
},
],
};
const result = enqueuePendingChatTask(current, {
task_id: "next",
status: "queued",
created_at: "2026-01-01T00:00:03Z",
});
expect(result.queued_tasks).toEqual([
expect.objectContaining({
task_id: "next",
created_at: "2026-01-01T00:00:01Z",
message_id: "message-next",
content: "Keep this visible preview",
}),
]);
});
it("enriches a sparse queued event when the send response arrives later", () => {
const current = {
...task("active", "2026-01-01T00:00:00Z"),
status: "running",
queued_tasks: [{
task_id: "next",
status: "queued",
created_at: "2026-01-01T00:00:01Z",
}],
};
const result = enqueuePendingChatTask(current, {
...task("next", "2026-01-01T00:00:01Z"),
message_id: "message-next",
content: "Visible accepted prompt",
});
expect(result.queued_tasks).toEqual([
expect.objectContaining({
task_id: "next",
message_id: "message-next",
content: "Visible accepted prompt",
}),
]);
});
it("does not downgrade a dispatched task when its send response arrives later", () => {
const current = {
...task("first", "2026-01-01T00:00:00Z"),
status: "dispatched",
queued_tasks: [],
};
const result = enqueuePendingChatTask(current, {
...task("first", "2026-01-01T00:00:00Z"),
message_id: "message-first",
content: "Keep the accepted prompt",
});
expect(result).toEqual(expect.objectContaining({
task_id: "first",
status: "dispatched",
message_id: "message-first",
content: "Keep the accepted prompt",
queued_tasks: [],
}));
});
it("promotes a queued task without losing later work", () => {
const current = {
...task("active", "2026-01-01T00:00:00Z"),
status: "running",
supports_queue: true,
queued_tasks: [
task("next", "2026-01-01T00:00:01Z"),
task("later", "2026-01-01T00:00:02Z"),
],
};
const result = promotePendingChatTask(current, "next", "running");
expect(result.task_id).toBe("next");
expect(result.status).toBe("running");
expect(result.supports_queue).toBe(true);
expect(result.queued_tasks?.map((item) => item.task_id)).toEqual(["later"]);
});
it("ignores a stale dispatch for an unknown task while another task is active", () => {
const current = {
...task("active", "2026-01-01T00:00:00Z"),
status: "running",
queued_tasks: [task("next", "2026-01-01T00:00:01Z")],
};
expect(promotePendingChatTask(current, "stale", "running")).toBe(current);
});
it("does not resurrect an unknown task after the pending query cleared", () => {
expect(promotePendingChatTask(undefined, "stale", "running")).toEqual({});
expect(promotePendingChatTask({ supports_queue: true }, "stale", "running")).toEqual({
supports_queue: true,
});
});
it("removes only the selected queued task", () => {
const current = {
...task("active", "2026-01-01T00:00:00Z"),
status: "running",
queued_tasks: [task("next", "2026-01-01T00:00:01Z")],
};
expect(removePendingChatTask(current, "next")).toEqual({
...current,
queued_tasks: [],
});
});
it("promotes the first queued task when the active task finishes", () => {
const current = {
...task("active", "2026-01-01T00:00:00Z"),
status: "running",
supports_queue: true,
queued_tasks: [
task("next", "2026-01-01T00:00:01Z"),
task("later", "2026-01-01T00:00:02Z"),
],
};
const result = removePendingChatTask(current, "active");
expect(result.task_id).toBe("next");
expect(result.supports_queue).toBe(true);
expect(result.queued_tasks?.map((item) => item.task_id)).toEqual(["later"]);
});
it("keeps queued prompts out of the settled transcript", () => {
const messages = [
{
id: "message-active",
chat_session_id: "session",
role: "user" as const,
content: "active",
task_id: "active",
created_at: "2026-01-01T00:00:00Z",
},
{
id: "message-next",
chat_session_id: "session",
role: "user" as const,
content: "next",
task_id: "next",
created_at: "2026-01-01T00:00:01Z",
},
];
const pending = {
...task("active", "2026-01-01T00:00:00Z"),
status: "running",
queued_tasks: [{ ...task("next", "2026-01-01T00:00:01Z"), message_id: "message-next" }],
};
expect(hideQueuedChatMessages(messages, pending)).toEqual([messages[0]]);
const waiting = removePendingChatTask(pending, "active");
expect(hideQueuedChatMessages(messages, waiting)).toEqual(messages);
expect(
hideQueuedChatMessages(messages, promotePendingChatTask(waiting, "next", "running")),
).toEqual(
messages,
);
});
it("keeps a queued retry's historical root prompt in the transcript", () => {
const root = {
id: "message-root",
chat_session_id: "session",
role: "user" as const,
content: "retry this",
task_id: "task-root",
created_at: "2026-01-01T00:00:00Z",
};
const pending = {
...task("task-retry", "2026-01-01T00:00:01Z"),
queued_tasks: [{
...task("task-retry", "2026-01-01T00:00:01Z"),
message_id: root.id,
}],
};
expect(hideQueuedChatMessages([root], pending)).toEqual([root]);
});
it("moves a send-now task to the front without disturbing the remaining queue", () => {
const current = {
...task("active", "2026-01-01T00:00:00Z"),
status: "running",
queued_tasks: [
task("next", "2026-01-01T00:00:01Z"),
task("send-now", "2026-01-01T00:00:02Z"),
task("later", "2026-01-01T00:00:03Z"),
],
};
expect(
prioritizePendingChatTask(current, "send-now").queued_tasks?.map((item) => item.task_id),
).toEqual(["send-now", "next", "later"]);
});
it("preserves send-now order when the active task is removed", () => {
const current = prioritizePendingChatTask({
...task("active", "2026-01-01T00:00:00Z"),
status: "running",
queued_tasks: [
task("next", "2026-01-01T00:00:01Z"),
task("send-now", "2026-01-01T00:00:02Z"),
],
}, "send-now");
const result = removePendingChatTask(current, "active");
expect(result.task_id).toBe("send-now");
expect(result.queued_tasks?.map((item) => item.task_id)).toEqual(["next"]);
});
});