197 lines · 6.1 KB · TypeScript
RawDownload
import {  CheckpointRef,  DEFAULT_PROVIDER_INTERACTION_MODE,  ProjectId,  ThreadId,  TurnId,  type OrchestrationReadModel,} from "@t3tools/contracts";import { Effect, Layer } from "effect";import { describe, expect, it } from "vitest"; import { ProjectionSnapshotQuery } from "../../orchestration/Services/ProjectionSnapshotQuery.ts";import { checkpointRefForThreadTurn } from "../Utils.ts";import { CheckpointDiffQueryLive } from "./CheckpointDiffQuery.ts";import { CheckpointStore, type CheckpointStoreShape } from "../Services/CheckpointStore.ts";import { CheckpointDiffQuery } from "../Services/CheckpointDiffQuery.ts"; function makeSnapshot(input: {  readonly projectId: ProjectId;  readonly threadId: ThreadId;  readonly workspaceRoot: string;  readonly worktreePath: string | null;  readonly checkpointTurnCount: number;  readonly checkpointRef: CheckpointRef;}): OrchestrationReadModel {  return {    snapshotSequence: 0,    updatedAt: "2026-01-01T00:00:00.000Z",    projects: [      {        id: input.projectId,        title: "Project",        workspaceRoot: input.workspaceRoot,        defaultModel: null,        scripts: [],        createdAt: "2026-01-01T00:00:00.000Z",        updatedAt: "2026-01-01T00:00:00.000Z",        deletedAt: null,      },    ],    threads: [      {        id: input.threadId,        projectId: input.projectId,        title: "Thread",        model: "gpt-5-codex",        interactionMode: DEFAULT_PROVIDER_INTERACTION_MODE,        runtimeMode: "full-access",        branch: null,        worktreePath: input.worktreePath,        latestTurn: {          turnId: TurnId.makeUnsafe("turn-1"),          state: "completed",          requestedAt: "2026-01-01T00:00:00.000Z",          startedAt: "2026-01-01T00:00:00.000Z",          completedAt: "2026-01-01T00:00:00.000Z",          assistantMessageId: null,        },        createdAt: "2026-01-01T00:00:00.000Z",        updatedAt: "2026-01-01T00:00:00.000Z",        deletedAt: null,        messages: [],        activities: [],        proposedPlans: [],        checkpoints: [          {            turnId: TurnId.makeUnsafe("turn-1"),            checkpointTurnCount: input.checkpointTurnCount,            checkpointRef: input.checkpointRef,            status: "ready",            files: [],            assistantMessageId: null,            completedAt: "2026-01-01T00:00:00.000Z",          },        ],        session: null,      },    ],  };} describe("CheckpointDiffQueryLive", () => {  it("computes diffs using canonical turn-0 checkpoint refs", async () => {    const projectId = ProjectId.makeUnsafe("project-1");    const threadId = ThreadId.makeUnsafe("thread-1");    const toCheckpointRef = checkpointRefForThreadTurn(threadId, 1);    const hasCheckpointRefCalls: Array<CheckpointRef> = [];    const diffCheckpointsCalls: Array<{      readonly fromCheckpointRef: CheckpointRef;      readonly toCheckpointRef: CheckpointRef;      readonly cwd: string;    }> = [];     const snapshot = makeSnapshot({      projectId,      threadId,      workspaceRoot: "/tmp/workspace",      worktreePath: null,      checkpointTurnCount: 1,      checkpointRef: toCheckpointRef,    });     const checkpointStore: CheckpointStoreShape = {      isGitRepository: () => Effect.succeed(true),      captureCheckpoint: () => Effect.void,      hasCheckpointRef: ({ checkpointRef }) =>        Effect.sync(() => {          hasCheckpointRefCalls.push(checkpointRef);          return true;        }),      restoreCheckpoint: () => Effect.succeed(true),      diffCheckpoints: ({ fromCheckpointRef, toCheckpointRef, cwd }) =>        Effect.sync(() => {          diffCheckpointsCalls.push({ fromCheckpointRef, toCheckpointRef, cwd });          return "diff patch";        }),      deleteCheckpointRefs: () => Effect.void,    };     const layer = CheckpointDiffQueryLive.pipe(      Layer.provideMerge(Layer.succeed(CheckpointStore, checkpointStore)),      Layer.provideMerge(        Layer.succeed(ProjectionSnapshotQuery, {          getSnapshot: () => Effect.succeed(snapshot),        }),      ),    );     const result = await Effect.runPromise(      Effect.gen(function* () {        const query = yield* CheckpointDiffQuery;        return yield* query.getTurnDiff({          threadId,          fromTurnCount: 0,          toTurnCount: 1,        });      }).pipe(Effect.provide(layer)),    );     const expectedFromRef = checkpointRefForThreadTurn(threadId, 0);    expect(hasCheckpointRefCalls).toEqual([expectedFromRef, toCheckpointRef]);    expect(diffCheckpointsCalls).toEqual([      {        cwd: "/tmp/workspace",        fromCheckpointRef: expectedFromRef,        toCheckpointRef,      },    ]);    expect(result).toEqual({      threadId,      fromTurnCount: 0,      toTurnCount: 1,      diff: "diff patch",    });  });   it("fails when the thread is missing from the snapshot", async () => {    const threadId = ThreadId.makeUnsafe("thread-missing");     const checkpointStore: CheckpointStoreShape = {      isGitRepository: () => Effect.succeed(true),      captureCheckpoint: () => Effect.void,      hasCheckpointRef: () => Effect.succeed(true),      restoreCheckpoint: () => Effect.succeed(true),      diffCheckpoints: () => Effect.succeed(""),      deleteCheckpointRefs: () => Effect.void,    };     const layer = CheckpointDiffQueryLive.pipe(      Layer.provideMerge(Layer.succeed(CheckpointStore, checkpointStore)),      Layer.provideMerge(        Layer.succeed(ProjectionSnapshotQuery, {          getSnapshot: () =>            Effect.succeed({              snapshotSequence: 0,              projects: [],              threads: [],              updatedAt: "2026-01-01T00:00:00.000Z",            } satisfies OrchestrationReadModel),        }),      ),    );     await expect(      Effect.runPromise(        Effect.gen(function* () {          const query = yield* CheckpointDiffQuery;          return yield* query.getTurnDiff({            threadId,            fromTurnCount: 0,            toTurnCount: 1,          });        }).pipe(Effect.provide(layer)),      ),    ).rejects.toThrow("Thread 'thread-missing' not found.");  });});