import { describe, expect, it } from "vitest"; import type { CapabilityId, Cursor, EventId, ProjectId, ServerId, SessionId, Task, TaskId, Timestamp } from "../../protocol/model"; import { activityReducer, canCancelTask, filterEvents, initialActivityState, isTaskTransitionAllowed } from "./state"; const ids = { server_id: "server-1" as ServerId, project_id: "project-1" as ProjectId, session_id: "session-1" as SessionId }; const task = (state: Task["state"], cancellable = true): Task => ({ ...ids, task_id: "task-1" as TaskId, capability_id: "shell" as CapabilityId, state, cancellable, created_at: "2026-01-01T00:00:00Z" as Timestamp, updated_at: "2026-01-01T00:00:01Z" as Timestamp }); const event = (id: string, cursor: string, type = "TASK_UPDATED") => ({ event_id: id as EventId, type, timestamp: "2026-01-01T00:00:00Z" as Timestamp, cursor: cursor as Cursor, sequence: 1, context: ids, payload: {} }); describe("activity state", () => { it("仅接受合法任务状态转换并保护终态", () => { expect(isTaskTransitionAllowed("PENDING", "RUNNING")).toBe(true); expect(isTaskTransitionAllowed("SUCCEEDED", "RUNNING")).toBe(false); const finished = activityReducer(initialActivityState, { type: "task_received", task: task("SUCCEEDED") }); expect(activityReducer(finished, { type: "task_received", task: task("RUNNING") })).toBe(finished); }); it("只允许可取消的非终态任务取消", () => { expect(canCancelTask(task("RUNNING"))).toBe(true); expect(canCancelTask(task("RUNNING", false))).toBe(false); expect(canCancelTask(task("FAILED"))).toBe(false); }); it("记录最新事件游标、去重并保留恢复缺口", () => { const once = activityReducer(initialActivityState, { type: "event_received", event: event("e1", "c1") }); expect(activityReducer(once, { type: "event_received", event: event("e1", "c1") }).events).toHaveLength(1); const resumed = activityReducer(once, { type: "resume_completed", cursor: "c3" as Cursor, gap: { requested_cursor: "c1" as Cursor, resumed_cursor: "c3" as Cursor, message: "缺口" } }); expect(resumed).toMatchObject({ stream_phase: "streaming", last_cursor: "c3", gap: { message: "缺口" } }); }); it("按类型和固定上下文筛选事件", () => { expect(filterEvents([event("e1", "c1"), event("e2", "c2", "TASK_OUTPUT")], { types: ["TASK_OUTPUT"], project_id: ids.project_id })).toHaveLength(1); }); });