160 lines · 5.3 KB · TypeScript
RawDownload
import { ProjectId, type OrchestrationProject } from "@t3tools/contracts";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";import * as Option from "effect/Option";import { describe, expect, it, vi } from "vitest"; import { ProjectionSnapshotQuery } from "../../orchestration/Services/ProjectionSnapshotQuery.ts";import { TerminalManager } from "../../terminal/Services/Manager.ts";import { ProjectSetupScriptRunner } from "../Services/ProjectSetupScriptRunner.ts";import { ProjectSetupScriptRunnerLive } from "./ProjectSetupScriptRunner.ts"; const makeProject = (scripts: OrchestrationProject["scripts"]): OrchestrationProject => ({  id: ProjectId.make("project-1"),  title: "Project",  workspaceRoot: "/repo/project",  defaultModelSelection: null,  scripts,  createdAt: "2026-01-01T00:00:00.000Z",  updatedAt: "2026-01-01T00:00:00.000Z",  deletedAt: null,}); const makeProjectionSnapshotQueryLayer = (project: OrchestrationProject) =>  Layer.succeed(ProjectionSnapshotQuery, {    getCommandReadModel: () => Effect.die("unused"),    getSnapshot: () => Effect.die("unused"),    getShellSnapshot: () => Effect.die("unused"),    getArchivedShellSnapshot: () => Effect.die("unused"),    getSnapshotSequence: () => Effect.succeed({ snapshotSequence: 1 }),    getCounts: () => Effect.die("unused"),    getActiveProjectByWorkspaceRoot: (workspaceRoot) =>      Effect.succeed(        workspaceRoot === project.workspaceRoot ? Option.some(project) : Option.none(),      ),    getProjectShellById: (projectId) =>      Effect.succeed(projectId === project.id ? Option.some(project) : Option.none()),    getFirstActiveThreadIdByProjectId: () => Effect.die("unused"),    getThreadCheckpointContext: () => Effect.die("unused"),    getFullThreadDiffContext: () => Effect.die("unused"),    getThreadShellById: () => Effect.die("unused"),    getThreadDetailById: () => Effect.die("unused"),  }); describe("ProjectSetupScriptRunner", () => {  it("returns no-script when no setup script exists", async () => {    const open = vi.fn();    const write = vi.fn();    const project = makeProject([]);    const runner = await Effect.runPromise(      Effect.service(ProjectSetupScriptRunner).pipe(        Effect.provide(          ProjectSetupScriptRunnerLive.pipe(            Layer.provideMerge(makeProjectionSnapshotQueryLayer(project)),            Layer.provideMerge(              Layer.succeed(TerminalManager, {                open,                write,                resize: () => Effect.void,                clear: () => Effect.void,                restart: () => Effect.die(new Error("unused")),                close: () => Effect.void,                subscribe: () => Effect.succeed(() => undefined),              }),            ),          ),        ),      ),    );     const result = await Effect.runPromise(      runner.runForThread({        threadId: "thread-1",        projectId: "project-1",        worktreePath: "/repo/worktrees/a",      }),    );     expect(result).toEqual({ status: "no-script" });    expect(open).not.toHaveBeenCalled();    expect(write).not.toHaveBeenCalled();  });   it("opens the deterministic setup terminal with worktree env and writes the command", async () => {    const open = vi.fn(() =>      Effect.succeed({        threadId: "thread-1",        terminalId: "setup-setup",        cwd: "/repo/worktrees/a",        worktreePath: "/repo/worktrees/a",        status: "running" as const,        pid: 123,        history: "",        exitCode: null,        exitSignal: null,        updatedAt: "2026-01-01T00:00:00.000Z",      }),    );    const write = vi.fn(() => Effect.void);    const project = makeProject([      {        id: "setup",        name: "Setup",        command: "bun install",        icon: "configure",        runOnWorktreeCreate: true,      },    ]);    const runner = await Effect.runPromise(      Effect.service(ProjectSetupScriptRunner).pipe(        Effect.provide(          ProjectSetupScriptRunnerLive.pipe(            Layer.provideMerge(makeProjectionSnapshotQueryLayer(project)),            Layer.provideMerge(              Layer.succeed(TerminalManager, {                open,                write,                resize: () => Effect.void,                clear: () => Effect.void,                restart: () => Effect.die(new Error("unused")),                close: () => Effect.void,                subscribe: () => Effect.succeed(() => undefined),              }),            ),          ),        ),      ),    );     const result = await Effect.runPromise(      runner.runForThread({        threadId: "thread-1",        projectCwd: "/repo/project",        worktreePath: "/repo/worktrees/a",      }),    );     expect(result).toEqual({      status: "started",      scriptId: "setup",      scriptName: "Setup",      terminalId: "setup-setup",      cwd: "/repo/worktrees/a",    });    expect(open).toHaveBeenCalledWith({      threadId: "thread-1",      terminalId: "setup-setup",      cwd: "/repo/worktrees/a",      worktreePath: "/repo/worktrees/a",      env: {        T3CODE_PROJECT_ROOT: "/repo/project",        T3CODE_WORKTREE_PATH: "/repo/worktrees/a",      },    });    expect(write).toHaveBeenCalledWith({      threadId: "thread-1",      terminalId: "setup-setup",      data: "bun install\r",    });  });});