568 lines · 18.0 KB · TypeScript
RawDownload
import { describe, expect, it } from "vite-plus/test"; import {  CheckpointRef,  EventId,  MessageId,  ProjectId,  ProviderInstanceId,  ThreadId,  TurnId,} from "@t3tools/contracts";import type { OrchestrationThread } from "@t3tools/contracts"; import { applyThreadDetailEvent } from "./threadDetailReducer.ts"; const baseEventFields = {  eventId: EventId.make("event-1"),  commandId: null,  causationEventId: null,  correlationId: null,  metadata: {},} as const; const baseThread: OrchestrationThread = {  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",  interactionMode: "default",  branch: null,  worktreePath: null,  latestTurn: null,  createdAt: "2026-04-01T00:00:00.000Z",  updatedAt: "2026-04-01T00:00:00.000Z",  archivedAt: null,  deletedAt: null,  messages: [],  proposedPlans: [],  activities: [],  checkpoints: [],  session: null,}; describe("applyThreadDetailEvent", () => {  describe("project events", () => {    it("returns unchanged for project.created", () => {      const result = applyThreadDetailEvent(baseThread, {        ...baseEventFields,        sequence: 1,        occurredAt: "2026-04-01T01:00:00.000Z",        aggregateKind: "project",        aggregateId: ProjectId.make("project-1"),        type: "project.created",        payload: {          projectId: ProjectId.make("project-1"),          title: "T3 Code",          workspaceRoot: "/repo",          repositoryIdentity: null,          defaultModelSelection: null,          scripts: [],          createdAt: "2026-04-01T01:00:00.000Z",          updatedAt: "2026-04-01T01:00:00.000Z",          deletedAt: null,        },      } as any);      expect(result.kind).toBe("unchanged");    });  });   describe("thread.created", () => {    it("creates a fresh thread", () => {      const result = applyThreadDetailEvent(baseThread, {        ...baseEventFields,        sequence: 1,        occurredAt: "2026-04-01T01:00:00.000Z",        aggregateKind: "thread",        aggregateId: ThreadId.make("thread-2"),        type: "thread.created",        payload: {          threadId: ThreadId.make("thread-2"),          projectId: ProjectId.make("project-1"),          title: "New Thread",          modelSelection: { instanceId: ProviderInstanceId.make("codex"), model: "gpt-5.4" },          runtimeMode: "full-access",          interactionMode: "default",          branch: "main",          worktreePath: null,          createdAt: "2026-04-01T01:00:00.000Z",          updatedAt: "2026-04-01T01:00:00.000Z",        },      });       expect(result.kind).toBe("updated");      if (result.kind === "updated") {        expect(result.thread.id).toBe("thread-2");        expect(result.thread.title).toBe("New Thread");        expect(result.thread.branch).toBe("main");        expect(result.thread.messages).toEqual([]);        expect(result.thread.session).toBeNull();      }    });  });   describe("thread.deleted", () => {    it("returns deleted signal", () => {      const result = applyThreadDetailEvent(baseThread, {        ...baseEventFields,        sequence: 2,        occurredAt: "2026-04-01T02:00:00.000Z",        aggregateKind: "thread",        aggregateId: ThreadId.make("thread-1"),        type: "thread.deleted",        payload: {          threadId: ThreadId.make("thread-1"),          deletedAt: "2026-04-01T02:00:00.000Z",        },      });      expect(result.kind).toBe("deleted");    });  });   describe("thread.archived / thread.unarchived", () => {    it("sets archivedAt", () => {      const result = applyThreadDetailEvent(baseThread, {        ...baseEventFields,        sequence: 3,        occurredAt: "2026-04-01T03:00:00.000Z",        aggregateKind: "thread",        aggregateId: ThreadId.make("thread-1"),        type: "thread.archived",        payload: {          threadId: ThreadId.make("thread-1"),          archivedAt: "2026-04-01T03:00:00.000Z",          updatedAt: "2026-04-01T03:00:00.000Z",        },      });       expect(result.kind).toBe("updated");      if (result.kind === "updated") {        expect(result.thread.archivedAt).toBe("2026-04-01T03:00:00.000Z");      }    });     it("clears archivedAt", () => {      const archivedThread = { ...baseThread, archivedAt: "2026-04-01T03:00:00.000Z" };      const result = applyThreadDetailEvent(archivedThread, {        ...baseEventFields,        sequence: 4,        occurredAt: "2026-04-01T04:00:00.000Z",        aggregateKind: "thread",        aggregateId: ThreadId.make("thread-1"),        type: "thread.unarchived",        payload: {          threadId: ThreadId.make("thread-1"),          updatedAt: "2026-04-01T04:00:00.000Z",        },      });       expect(result.kind).toBe("updated");      if (result.kind === "updated") {        expect(result.thread.archivedAt).toBeNull();      }    });  });   describe("thread.meta-updated", () => {    it("patches title and branch", () => {      const result = applyThreadDetailEvent(baseThread, {        ...baseEventFields,        sequence: 5,        occurredAt: "2026-04-01T05:00:00.000Z",        aggregateKind: "thread",        aggregateId: ThreadId.make("thread-1"),        type: "thread.meta-updated",        payload: {          threadId: ThreadId.make("thread-1"),          title: "Updated Title",          branch: "feature/demo",          updatedAt: "2026-04-01T05:00:00.000Z",        },      });       expect(result.kind).toBe("updated");      if (result.kind === "updated") {        expect(result.thread.title).toBe("Updated Title");        expect(result.thread.branch).toBe("feature/demo");        // Model selection should be unchanged since it wasn't in the payload        expect(result.thread.modelSelection).toEqual(baseThread.modelSelection);      }    });  });   describe("thread.message-sent", () => {    it("appends a new message", () => {      const result = applyThreadDetailEvent(baseThread, {        ...baseEventFields,        sequence: 6,        occurredAt: "2026-04-01T06:00:00.000Z",        aggregateKind: "thread",        aggregateId: ThreadId.make("thread-1"),        type: "thread.message-sent",        payload: {          threadId: ThreadId.make("thread-1"),          messageId: MessageId.make("msg-1"),          role: "user",          text: "Hello, world!",          turnId: null,          streaming: false,          createdAt: "2026-04-01T06:00:00.000Z",          updatedAt: "2026-04-01T06:00:00.000Z",        },      });       expect(result.kind).toBe("updated");      if (result.kind === "updated") {        expect(result.thread.messages).toHaveLength(1);        expect(result.thread.messages[0]?.text).toBe("Hello, world!");      }    });     it("appends text for streaming messages", () => {      const threadWithMessage: OrchestrationThread = {        ...baseThread,        messages: [          {            id: MessageId.make("msg-2"),            role: "assistant",            text: "Hello",            turnId: TurnId.make("turn-1"),            streaming: true,            createdAt: "2026-04-01T06:00:00.000Z",            updatedAt: "2026-04-01T06:00:00.000Z",          },        ],      };       const result = applyThreadDetailEvent(threadWithMessage, {        ...baseEventFields,        sequence: 7,        occurredAt: "2026-04-01T06:01:00.000Z",        aggregateKind: "thread",        aggregateId: ThreadId.make("thread-1"),        type: "thread.message-sent",        payload: {          threadId: ThreadId.make("thread-1"),          messageId: MessageId.make("msg-2"),          role: "assistant",          text: ", world!",          turnId: TurnId.make("turn-1"),          streaming: true,          createdAt: "2026-04-01T06:00:00.000Z",          updatedAt: "2026-04-01T06:01:00.000Z",        },      });       expect(result.kind).toBe("updated");      if (result.kind === "updated") {        expect(result.thread.messages).toHaveLength(1);        expect(result.thread.messages[0]?.text).toBe("Hello, world!");      }    });     it("updates latestTurn for assistant messages with a turn", () => {      const result = applyThreadDetailEvent(baseThread, {        ...baseEventFields,        sequence: 8,        occurredAt: "2026-04-01T07:00:00.000Z",        aggregateKind: "thread",        aggregateId: ThreadId.make("thread-1"),        type: "thread.message-sent",        payload: {          threadId: ThreadId.make("thread-1"),          messageId: MessageId.make("msg-3"),          role: "assistant",          text: "Done.",          turnId: TurnId.make("turn-1"),          streaming: false,          createdAt: "2026-04-01T07:00:00.000Z",          updatedAt: "2026-04-01T07:00:00.000Z",        },      });       expect(result.kind).toBe("updated");      if (result.kind === "updated") {        expect(result.thread.latestTurn?.turnId).toBe("turn-1");        expect(result.thread.latestTurn?.state).toBe("completed");        expect(result.thread.latestTurn?.assistantMessageId).toBe("msg-3");      }    });  });   describe("thread.session-set", () => {    it("updates session and latestTurn for a running session", () => {      const result = applyThreadDetailEvent(baseThread, {        ...baseEventFields,        sequence: 9,        occurredAt: "2026-04-01T08:00:00.000Z",        aggregateKind: "thread",        aggregateId: ThreadId.make("thread-1"),        type: "thread.session-set",        payload: {          threadId: ThreadId.make("thread-1"),          session: {            threadId: ThreadId.make("thread-1"),            status: "running",            providerName: "codex",            runtimeMode: "full-access",            activeTurnId: TurnId.make("turn-1"),            lastError: null,            updatedAt: "2026-04-01T08:00:00.000Z",          },        },      });       expect(result.kind).toBe("updated");      if (result.kind === "updated") {        expect(result.thread.session?.status).toBe("running");        expect(result.thread.latestTurn?.turnId).toBe("turn-1");        expect(result.thread.latestTurn?.state).toBe("running");      }    });  });   describe("thread.session-stop-requested", () => {    it("marks session as stopped", () => {      const threadWithSession: OrchestrationThread = {        ...baseThread,        session: {          threadId: ThreadId.make("thread-1"),          status: "running",          providerName: "codex",          runtimeMode: "full-access",          activeTurnId: TurnId.make("turn-1"),          lastError: null,          updatedAt: "2026-04-01T08:00:00.000Z",        },      };       const result = applyThreadDetailEvent(threadWithSession, {        ...baseEventFields,        sequence: 10,        occurredAt: "2026-04-01T09:00:00.000Z",        aggregateKind: "thread",        aggregateId: ThreadId.make("thread-1"),        type: "thread.session-stop-requested",        payload: {          threadId: ThreadId.make("thread-1"),          createdAt: "2026-04-01T09:00:00.000Z",        },      });       expect(result.kind).toBe("updated");      if (result.kind === "updated") {        expect(result.thread.session?.status).toBe("stopped");        expect(result.thread.session?.activeTurnId).toBeNull();      }    });     it("returns unchanged when no session exists", () => {      const result = applyThreadDetailEvent(baseThread, {        ...baseEventFields,        sequence: 10,        occurredAt: "2026-04-01T09:00:00.000Z",        aggregateKind: "thread",        aggregateId: ThreadId.make("thread-1"),        type: "thread.session-stop-requested",        payload: {          threadId: ThreadId.make("thread-1"),          createdAt: "2026-04-01T09:00:00.000Z",        },      });      expect(result.kind).toBe("unchanged");    });  });   describe("thread.proposed-plan-upserted", () => {    it("adds a proposed plan", () => {      const result = applyThreadDetailEvent(baseThread, {        ...baseEventFields,        sequence: 11,        occurredAt: "2026-04-01T10:00:00.000Z",        aggregateKind: "thread",        aggregateId: ThreadId.make("thread-1"),        type: "thread.proposed-plan-upserted",        payload: {          threadId: ThreadId.make("thread-1"),          proposedPlan: {            id: "plan-1",            turnId: TurnId.make("turn-1"),            planMarkdown: "## Plan\n- Do stuff",            implementedAt: null,            implementationThreadId: null,            createdAt: "2026-04-01T10:00:00.000Z",            updatedAt: "2026-04-01T10:00:00.000Z",          },        },      });       expect(result.kind).toBe("updated");      if (result.kind === "updated") {        expect(result.thread.proposedPlans).toHaveLength(1);        expect(result.thread.proposedPlans[0]?.id).toBe("plan-1");      }    });  });   describe("thread.activity-appended", () => {    it("adds an activity", () => {      const result = applyThreadDetailEvent(baseThread, {        ...baseEventFields,        sequence: 12,        occurredAt: "2026-04-01T11:00:00.000Z",        aggregateKind: "thread",        aggregateId: ThreadId.make("thread-1"),        type: "thread.activity-appended",        payload: {          threadId: ThreadId.make("thread-1"),          activity: {            id: EventId.make("activity-1"),            tone: "tool",            kind: "file-edit",            summary: "Edited src/index.ts",            payload: {},            turnId: TurnId.make("turn-1"),            createdAt: "2026-04-01T11:00:00.000Z",          },        },      });       expect(result.kind).toBe("updated");      if (result.kind === "updated") {        expect(result.thread.activities).toHaveLength(1);        expect(result.thread.activities[0]?.kind).toBe("file-edit");      }    });  });   describe("thread.turn-diff-completed", () => {    it("adds a checkpoint and updates latestTurn", () => {      const result = applyThreadDetailEvent(baseThread, {        ...baseEventFields,        sequence: 13,        occurredAt: "2026-04-01T12:00:00.000Z",        aggregateKind: "thread",        aggregateId: ThreadId.make("thread-1"),        type: "thread.turn-diff-completed",        payload: {          threadId: ThreadId.make("thread-1"),          turnId: TurnId.make("turn-1"),          checkpointTurnCount: 1,          checkpointRef: CheckpointRef.make("ref-1"),          status: "ready",          files: [],          assistantMessageId: MessageId.make("msg-3"),          completedAt: "2026-04-01T12:00:00.000Z",        },      });       expect(result.kind).toBe("updated");      if (result.kind === "updated") {        expect(result.thread.checkpoints).toHaveLength(1);        expect(result.thread.latestTurn?.turnId).toBe("turn-1");        expect(result.thread.latestTurn?.state).toBe("completed");      }    });  });   describe("thread.reverted", () => {    it("filters entities to retained turns", () => {      const threadWithData: OrchestrationThread = {        ...baseThread,        messages: [          {            id: MessageId.make("msg-1"),            role: "user",            text: "First",            turnId: null,            streaming: false,            createdAt: "2026-04-01T01:00:00.000Z",            updatedAt: "2026-04-01T01:00:00.000Z",          },          {            id: MessageId.make("msg-2"),            role: "assistant",            text: "Response 1",            turnId: TurnId.make("turn-1"),            streaming: false,            createdAt: "2026-04-01T02:00:00.000Z",            updatedAt: "2026-04-01T02:00:00.000Z",          },          {            id: MessageId.make("msg-3"),            role: "assistant",            text: "Response 2",            turnId: TurnId.make("turn-2"),            streaming: false,            createdAt: "2026-04-01T03:00:00.000Z",            updatedAt: "2026-04-01T03:00:00.000Z",          },

Lines 1–500 of 568.