import { assert, it, vi } from "@effect/vitest";import { CheckpointScopeId, RunId, ThreadId, type OrchestrationV2ThreadShell, type OrchestrationV2ThreadProjection,} from "@t3tools/contracts";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer"; import * as PullRequestService from "../pullRequest/PullRequestService.ts";import * as VcsStatusBroadcaster from "../vcs/VcsStatusBroadcaster.ts";import * as WorkspaceEntries from "../workspace/WorkspaceEntries.ts";import * as CheckpointCapture from "./CheckpointCaptureService.ts";import * as ProjectionStore from "./ProjectionStore.ts";import * as RunFinalization from "./RunFinalizationService.ts"; it.effect("captures the root checkpoint and refreshes workspace state", () => { const threadId = ThreadId.make("thread_finalize"); const runId = RunId.make("run_finalize"); const scopeId = CheckpointScopeId.make("scope_finalize"); const capture = vi.fn(() => Effect.void); const refresh = vi.fn(() => Effect.void); const projection = { checkpointScopes: [{ id: scopeId, cwd: "/repo" }], } as unknown as OrchestrationV2ThreadProjection; const layer = RunFinalization.layer.pipe( Layer.provide( Layer.mergeAll( Layer.mock(CheckpointCapture.CheckpointCaptureServiceV2)({ execute: capture }), Layer.mock(ProjectionStore.ProjectionStoreV2)({ getThreadProjection: () => Effect.succeed(projection), }), Layer.succeed(RunFinalization.RunFinalizationObserver, { refresh, refreshAfterTurn: Effect.void, }), ), ), ); return Effect.gen(function* () { const service = yield* RunFinalization.RunFinalizationService; yield* service.finalize({ threadId, runId, scopeId }); assert.equal(capture.mock.calls.length, 1); assert.deepEqual(refresh.mock.calls[0], [{ cwd: "/repo", threadId, runId }]); }).pipe(Effect.provide(layer));}); for (const scenario of [ { label: "discovers a new PR for the completed run's branch", branch: "feature", checkedOut: "feature", activeRun: null, expected: ["/repo"], }, { label: "leaves the default branch's PR cache alone", branch: "main", checkedOut: "main", activeRun: null, expected: [], }, { label: "does not refresh another thread's checkout", branch: "feature", checkedOut: "other", activeRun: null, expected: [], }, { label: "does not refresh during a newer active run", branch: "feature", checkedOut: "feature", activeRun: "newer-run", expected: [], },] as const) { it.effect(scenario.label, () => { const refreshed: string[] = []; const threadId = ThreadId.make("thread-pr-refresh"); const runId = RunId.make("completed-run"); const layer = RunFinalization.observerLive.pipe( Layer.provide( Layer.mergeAll( Layer.mock(WorkspaceEntries.WorkspaceEntries)({ refresh: () => Effect.void }), Layer.mock(PullRequestService.PullRequestService)({ refreshAfterTurn: Effect.void }), Layer.mock(VcsStatusBroadcaster.VcsStatusBroadcaster)({ refreshLocalStatus: () => Effect.succeed({ isRepo: true, hasPrimaryRemote: true, isDefaultRef: scenario.checkedOut === "main", refName: scenario.checkedOut, hasWorkingTreeChanges: false, workingTree: { files: [], insertions: 0, deletions: 0 }, }), refreshStatus: () => Effect.die("turn completion must preserve known PRs and lookup backoff"), refreshPullRequestStatus: (cwd) => Effect.sync(() => { refreshed.push(cwd); return null; }), }), Layer.mock(ProjectionStore.ProjectionStoreV2)({ getThreadShell: () => Effect.succeed({ id: threadId, branch: scenario.branch, activeRunId: scenario.activeRun === null ? null : RunId.make(scenario.activeRun), } as OrchestrationV2ThreadShell), }), ), ), ); return Effect.gen(function* () { const observer = yield* RunFinalization.RunFinalizationObserver; yield* observer.refresh({ cwd: "/repo", threadId, runId }); assert.deepEqual(refreshed, [...scenario.expected]); }).pipe(Effect.provide(layer)); });}
import { assert, it, vi } from "@effect/vitest";
import {
CheckpointScopeId,
RunId,
ThreadId,
type OrchestrationV2ThreadShell,
type OrchestrationV2ThreadProjection,
} from "@t3tools/contracts";
import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";
import * as PullRequestService from "../pullRequest/PullRequestService.ts";
import * as VcsStatusBroadcaster from "../vcs/VcsStatusBroadcaster.ts";
import * as WorkspaceEntries from "../workspace/WorkspaceEntries.ts";
import * as CheckpointCapture from "./CheckpointCaptureService.ts";
import * as ProjectionStore from "./ProjectionStore.ts";
import * as RunFinalization from "./RunFinalizationService.ts";
it.effect("captures the root checkpoint and refreshes workspace state", () => {
const threadId = ThreadId.make("thread_finalize");
const runId = RunId.make("run_finalize");
const scopeId = CheckpointScopeId.make("scope_finalize");
const capture = vi.fn(() => Effect.void);
const refresh = vi.fn(() => Effect.void);
const projection = {
checkpointScopes: [{ id: scopeId, cwd: "/repo" }],
} as unknown as OrchestrationV2ThreadProjection;
const layer = RunFinalization.layer.pipe(
Layer.provide(
Layer.mergeAll(
Layer.mock(CheckpointCapture.CheckpointCaptureServiceV2)({ execute: capture }),
Layer.mock(ProjectionStore.ProjectionStoreV2)({
getThreadProjection: () => Effect.succeed(projection),
}),
Layer.succeed(RunFinalization.RunFinalizationObserver, {
refresh,
refreshAfterTurn: Effect.void,
}),
),
),
);
return Effect.gen(function* () {
const service = yield* RunFinalization.RunFinalizationService;
yield* service.finalize({ threadId, runId, scopeId });
assert.equal(capture.mock.calls.length, 1);
assert.deepEqual(refresh.mock.calls[0], [{ cwd: "/repo", threadId, runId }]);
}).pipe(Effect.provide(layer));
});
for (const scenario of [
{
label: "discovers a new PR for the completed run's branch",
branch: "feature",
checkedOut: "feature",
activeRun: null,
expected: ["/repo"],
},
{
label: "leaves the default branch's PR cache alone",
branch: "main",
checkedOut: "main",
activeRun: null,
expected: [],
},
{
label: "does not refresh another thread's checkout",
branch: "feature",
checkedOut: "other",
activeRun: null,
expected: [],
},
{
label: "does not refresh during a newer active run",
branch: "feature",
checkedOut: "feature",
activeRun: "newer-run",
expected: [],
},
] as const) {
it.effect(scenario.label, () => {
const refreshed: string[] = [];
const threadId = ThreadId.make("thread-pr-refresh");
const runId = RunId.make("completed-run");
const layer = RunFinalization.observerLive.pipe(
Layer.provide(
Layer.mergeAll(
Layer.mock(WorkspaceEntries.WorkspaceEntries)({ refresh: () => Effect.void }),
Layer.mock(PullRequestService.PullRequestService)({ refreshAfterTurn: Effect.void }),
Layer.mock(VcsStatusBroadcaster.VcsStatusBroadcaster)({
refreshLocalStatus: () =>
Effect.succeed({
isRepo: true,
hasPrimaryRemote: true,
isDefaultRef: scenario.checkedOut === "main",
refName: scenario.checkedOut,
hasWorkingTreeChanges: false,
workingTree: { files: [], insertions: 0, deletions: 0 },
}),
refreshStatus: () =>
Effect.die("turn completion must preserve known PRs and lookup backoff"),
refreshPullRequestStatus: (cwd) =>
Effect.sync(() => {
refreshed.push(cwd);
return null;
}),
}),
Layer.mock(ProjectionStore.ProjectionStoreV2)({
getThreadShell: () =>
Effect.succeed({
id: threadId,
branch: scenario.branch,
activeRunId: scenario.activeRun === null ? null : RunId.make(scenario.activeRun),
} as OrchestrationV2ThreadShell),
}),
),
),
);
return Effect.gen(function* () {
const observer = yield* RunFinalization.RunFinalizationObserver;
yield* observer.refresh({ cwd: "/repo", threadId, runId });
assert.deepEqual(refreshed, [...scenario.expected]);
}).pipe(Effect.provide(layer));
});
}