Files
kandev/apps/web/lib/threads/thread-session-status.test.ts
T
Jérémy Coatelen 538813de02 feat(web): add Threads view for live agent conversations (#3112)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Carlos Florencio <carlosmflorencio@gmail.com>
2026-09-03 08:07:16 +00:00

86 lines
3.1 KiB
TypeScript

import { describe, expect, it } from "vitest";
import type { TaskSession } from "@/lib/types/http";
import { sessionId, taskId } from "@/lib/types/ids";
import { resolveThreadColumnStatus, resolveThreadSessionStatus } from "./thread-session-status";
function session(overrides: Partial<TaskSession> = {}): TaskSession {
return {
id: sessionId("session-1"),
task_id: taskId("task-1"),
state: "IDLE",
started_at: "2026-08-27T10:00:00Z",
updated_at: "2026-08-27T12:00:00Z",
...overrides,
};
}
describe("resolveThreadSessionStatus", () => {
it("gives permission the highest precedence", () => {
expect(
resolveThreadSessionStatus(session({ state: "RUNNING", pending_action: "permission" })),
).toMatchObject({ kind: "permission", labelKey: "threads:statusPermissionNeeded" });
});
it("uses a question status only for an explicit clarification", () => {
expect(
resolveThreadSessionStatus(
session({ state: "WAITING_FOR_INPUT", pending_action: "clarification" }),
),
).toMatchObject({ kind: "clarification", labelKey: "threads:statusQuestionFromAgent" });
});
it("does not turn plain WAITING_FOR_INPUT into a question", () => {
expect(resolveThreadSessionStatus(session({ state: "WAITING_FOR_INPUT" }))).toMatchObject({
kind: "waiting",
hasAttention: false,
});
});
it("distinguishes starting, foreground work, and a finished turn", () => {
expect(resolveThreadSessionStatus(session({ state: "STARTING" })).kind).toBe("starting");
expect(resolveThreadSessionStatus(session({ state: "RUNNING" })).kind).toBe("working");
expect(resolveThreadSessionStatus(session({ state: "IDLE" })).kind).toBe("finished");
});
it("lets a foreground activity keep an idle session working", () => {
expect(
resolveThreadSessionStatus(session({ state: "IDLE", foreground_activity: "background" }))
.kind,
).toBe("working");
});
it("keeps terminal errors distinct", () => {
expect(resolveThreadSessionStatus(session({ state: "FAILED" })).kind).toBe("failed");
expect(resolveThreadSessionStatus(session({ state: "CANCELLED" })).kind).toBe("cancelled");
});
});
describe("resolveThreadColumnStatus", () => {
it("shows review readiness only when no session needs explicit action", () => {
expect(
resolveThreadColumnStatus({
taskState: "REVIEW",
session: session({ state: "COMPLETED" }),
}),
).toMatchObject({ kind: "review-ready", labelKey: "threads:statusReadyForReview" });
});
it("keeps session attention ahead of review readiness", () => {
expect(
resolveThreadColumnStatus({
taskState: "REVIEW",
session: session({ state: "WAITING_FOR_INPUT", pending_action: "clarification" }),
}).kind,
).toBe("clarification");
});
it("keeps task-wide attention separate from the selected session action", () => {
expect(
resolveThreadColumnStatus({
taskPendingAction: "permission",
session: session({ state: "WAITING_FOR_INPUT", pending_action: null }),
}),
).toMatchObject({ kind: "needs-you", hasAttention: true });
});
});