import { expect, test } from "@playwright/test"; import { appendClawdTaskProgress, isPendingClawdTaskStatus, normalizeClawdTaskMetadata, } from "@/lib/openclaw-chat/task-state"; import { getOpenClawTaskAnswer, getOpenClawTaskStatus, isOpenClawAwaitingUserInputTurnComplete, } from "@/lib/openclaw-chat/task-protocol"; test.describe("OpenClaw durable task state", () => { test("restores a persisted running task after the page remounts", () => { const task = normalizeClawdTaskMetadata({ composerMode: "clawd", source: "openclaw", taskKind: "clawd_chat", status: "running", userMessageId: "user-message-id", assistantMessageId: "assistant-message-id", processingHint: "正在处理", progressEvents: [{ event_id: 1, message: "请求已接收" }], upstreamTaskId: "task_test_123", upstreamTaskKey: "enc:v1:iv:tag:ciphertext", upstreamEventsEndpoint: "/api/chat/tasks/task_test_123/events", upstreamLastEventId: 18, createdAt: 100, updatedAt: 200, }); expect(task).not.toBeNull(); expect(task?.userMessageId).toBe("user-message-id"); expect(task && isPendingClawdTaskStatus(task.status)).toBe(true); expect(task?.progressEvents).toEqual([ { event_id: 1, message: "请求已接收" }, ]); expect(task?.upstreamTaskId).toBe("task_test_123"); expect(task?.upstreamLastEventId).toBe(18); }); test("deduplicates replayed progress events while keeping new steps", () => { const first = { event_id: 8, message: "任务已恢复" }; const second = { event_id: 9, message: "正在选择工具" }; let events = appendClawdTaskProgress([], first); events = appendClawdTaskProgress(events, first); events = appendClawdTaskProgress(events, second); expect(events).toEqual([first, second]); }); test("refreshes a heartbeat even when it reuses the current event id", () => { const first = { event_id: 8, message: "正在识别产品结构", created_at: "2026-09-06T01:00:00Z", }; const heartbeat = { event_id: 8, heartbeat: true, display_text: "正在生成包装排布", created_at: "2026-09-06T01:00:05Z", }; let events = appendClawdTaskProgress([], first); events = appendClawdTaskProgress(events, heartbeat); expect(events).toHaveLength(1); expect(events[0]).toMatchObject({ event_id: 8, heartbeat: true, message: "正在识别产品结构", display_text: "正在生成包装排布", created_at: "2026-09-06T01:00:05Z", }); }); test("does not classify completed tasks as pending", () => { expect(isPendingClawdTaskStatus("completed")).toBe(false); expect(isPendingClawdTaskStatus("failed")).toBe(false); }); test("recognizes a completed turn that is waiting for the next upload", () => { const response = { task: { status: "waiting" }, output: { answer: "请上传正视图、侧视图、开门图和尺寸图", turn_complete: true, awaiting_user_input: true, business_status: "awaiting_user_input", }, }; expect(getOpenClawTaskStatus(response)).toBe("waiting"); expect(isOpenClawAwaitingUserInputTurnComplete(response)).toBe(true); expect(getOpenClawTaskAnswer(response)).toContain("请上传正视图"); }); });