186 lines · 5.4 KB · TypeScript
RawDownload
import { describe, expect, it } from "vite-plus/test"; import { ProjectId, ProviderInstanceId, ThreadId } from "@t3tools/contracts";import type { OrchestrationShellSnapshot, OrchestrationShellStreamEvent } from "@t3tools/contracts"; import { applyShellStreamEvent } from "./shellReducer.ts"; const baseSnapshot: OrchestrationShellSnapshot = {  snapshotSequence: 0,  projects: [],  threads: [],  updatedAt: "2026-04-01T00:00:00.000Z",}; const stubProject = {  id: ProjectId.make("project-1"),  title: "Test Project",  workspaceRoot: "/workspace/test",  repositoryIdentity: null,  defaultModelSelection: null,  scripts: [],  createdAt: "2026-04-01T00:00:00.000Z",  updatedAt: "2026-04-01T00:00:00.000Z",} as const; const stubThread = {  id: ThreadId.make("thread-1"),  projectId: ProjectId.make("project-1"),  title: "Test Thread",  modelSelection: { instanceId: ProviderInstanceId.make("codex"), model: "gpt-5.4" },  runtimeMode: "full-access" as const,  interactionMode: "default" as const,  branch: null,  worktreePath: null,  latestTurn: null,  createdAt: "2026-04-01T00:00:00.000Z",  updatedAt: "2026-04-01T00:00:00.000Z",  archivedAt: null,  settledOverride: null,  settledAt: null,  pullRequests: [],  latestUserMessageAt: null,  hasPendingApprovals: false,  hasPendingUserInput: false,  hasActionableProposedPlan: false,  session: null,} as const; describe("applyShellStreamEvent", () => {  it("ignores stale project upserts without mutating the snapshot", () => {    const snapshotWithProject: OrchestrationShellSnapshot = {      ...baseSnapshot,      snapshotSequence: 4,      projects: [stubProject],    };     for (const sequence of [3, 4]) {      const next = applyShellStreamEvent(snapshotWithProject, {        kind: "project-upserted",        sequence,        project: { ...stubProject, title: "Stale Title" },      });       expect(next).toBe(snapshotWithProject);      expect(next.snapshotSequence).toBe(4);      expect(next.projects[0]?.title).toBe("Test Project");    }  });   describe("project-upserted", () => {    it("adds a new project", () => {      const event: OrchestrationShellStreamEvent = {        kind: "project-upserted",        sequence: 1,        project: stubProject,      };       const next = applyShellStreamEvent(baseSnapshot, event);       expect(next.projects).toHaveLength(1);      expect(next.projects[0]?.id).toBe("project-1");      expect(next.snapshotSequence).toBe(1);    });     it("updates an existing project", () => {      const snapshotWithProject: OrchestrationShellSnapshot = {        ...baseSnapshot,        projects: [stubProject],      };       const updatedProject = { ...stubProject, title: "Updated Title" };      const event: OrchestrationShellStreamEvent = {        kind: "project-upserted",        sequence: 2,        project: updatedProject,      };       const next = applyShellStreamEvent(snapshotWithProject, event);       expect(next.projects).toHaveLength(1);      expect(next.projects[0]?.title).toBe("Updated Title");      expect(next.snapshotSequence).toBe(2);    });  });   describe("project-removed", () => {    it("removes a project by id", () => {      const snapshotWithProject: OrchestrationShellSnapshot = {        ...baseSnapshot,        projects: [stubProject],      };       const event: OrchestrationShellStreamEvent = {        kind: "project-removed",        sequence: 3,        projectId: ProjectId.make("project-1"),      };       const next = applyShellStreamEvent(snapshotWithProject, event);       expect(next.projects).toHaveLength(0);      expect(next.snapshotSequence).toBe(3);    });  });   describe("thread-upserted", () => {    it("adds a new thread", () => {      const event: OrchestrationShellStreamEvent = {        kind: "thread-upserted",        sequence: 4,        thread: stubThread,      };       const next = applyShellStreamEvent(baseSnapshot, event);       expect(next.threads).toHaveLength(1);      expect(next.threads[0]?.id).toBe("thread-1");      expect(next.snapshotSequence).toBe(4);    });     it("updates an existing thread", () => {      const snapshotWithThread: OrchestrationShellSnapshot = {        ...baseSnapshot,        threads: [stubThread],      };       const updatedThread = { ...stubThread, title: "Updated Thread" };      const event: OrchestrationShellStreamEvent = {        kind: "thread-upserted",        sequence: 5,        thread: updatedThread,      };       const next = applyShellStreamEvent(snapshotWithThread, event);       expect(next.threads).toHaveLength(1);      expect(next.threads[0]?.title).toBe("Updated Thread");    });  });   describe("thread-removed", () => {    it("removes a thread by id", () => {      const snapshotWithThread: OrchestrationShellSnapshot = {        ...baseSnapshot,        threads: [stubThread],      };       const event: OrchestrationShellStreamEvent = {        kind: "thread-removed",        sequence: 6,        threadId: ThreadId.make("thread-1"),      };       const next = applyShellStreamEvent(snapshotWithThread, event);       expect(next.threads).toHaveLength(0);      expect(next.snapshotSequence).toBe(6);    });  });   it("returns original snapshot for unrecognized event kinds", () => {    const unknownEvent = { kind: "unknown-future-event", sequence: 99 } as any;    const next = applyShellStreamEvent(baseSnapshot, unknownEvent);    expect(next).toBe(baseSnapshot);  });});