1import * as NodeServices from "@effect/platform-node/NodeServices";
2import { assert, describe, it } from "@effect/vitest";
3import * as Effect from "effect/Effect";
4import * as FileSystem from "effect/FileSystem";
5import * as Layer from "effect/Layer";
6import * as PlatformError from "effect/PlatformError";
7import { ProjectId } from "@t3tools/contracts";
8
9import * as ServerConfig from "../config.ts";
10import * as ProjectStore from "../orchestration-v2/ProjectStore.ts";
11import * as ServerSettings from "../serverSettings.ts";
12import * as GitVcsDriver from "../vcs/GitVcsDriver.ts";
13import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts";
14import * as ReviewService from "./ReviewService.ts";
15
16function layer(input: {
17 readonly workspaceRoot: string;
18 readonly baseDir: string;
19 readonly detectCalls?: Array<{ readonly cwd: string }>;
20 readonly worktreesDirectory?: string;
21 readonly previousWorktreesDirectories?: ReadonlyArray<string>;
22 readonly projectRoots?: ReadonlyArray<string> | "unavailable";
23}) {
24 const projectRoots = input.projectRoots ?? [];
25 return ReviewService.layer.pipe(
26 Layer.provide(
27 Layer.mock(ProjectStore.ProjectStoreV2)({
28 listShells: () =>
29 projectRoots === "unavailable"
30 ? Effect.fail(
31 new ProjectStore.ProjectStoreV2Error({ operation: "list", cause: "offline" }),
32 )
33 : Effect.succeed(
34 projectRoots.map((workspaceRoot, index) => ({
35 id: ProjectId.make(`project-${index}`),
36 title: `Project ${index}`,
37 workspaceRoot,
38 defaultModelSelection: null,
39 scripts: [],
40 createdAt: "2026-01-01T00:00:00.000Z",
41 updatedAt: "2026-01-01T00:00:00.000Z",
42 })),
43 ),
44 }),
45 ),
46 Layer.provide(
47 Layer.mock(VcsDriverRegistry.VcsDriverRegistry)({
48 get: () => Effect.die("unexpected VCS registry get"),
49 resolve: () => Effect.die("unexpected VCS registry resolve"),
50 detect: (request) =>
51 Effect.sync(() => {
52 input.detectCalls?.push({ cwd: request.cwd });
53 return null;
54 }),
55 }),
56 ),
57 Layer.provide(Layer.mock(GitVcsDriver.GitVcsDriver)({})),
58 Layer.provide(
59 ServerSettings.ServerSettingsService.layerTest({
60 worktreesDirectory: input.worktreesDirectory ?? "",
61 previousWorktreesDirectories: [...(input.previousWorktreesDirectories ?? [])],
62 }),
63 ),
64 Layer.provide(ServerConfig.layerTest(input.workspaceRoot, input.baseDir)),
65 Layer.provideMerge(NodeServices.layer),
66 );
67}
68
69describe("ReviewService", () => {
70 it.effect("rejects diff preview cwd outside the configured workspace roots", () =>
71 Effect.gen(function* () {
72 const fs = yield* FileSystem.FileSystem;
73 const workspaceRoot = yield* fs.makeTempDirectoryScoped({ prefix: "t3-review-workspace-" });
74 const outsideRoot = yield* fs.makeTempDirectoryScoped({ prefix: "t3-review-outside-" });
75 const baseDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-review-base-" });
76 const detectCalls: Array<{ readonly cwd: string }> = [];
77
78 const error = yield* Effect.gen(function* () {
79 const review = yield* ReviewService.ReviewService;
80 return yield* review.getDiffPreview({ cwd: outsideRoot }).pipe(Effect.flip);
81 }).pipe(Effect.provide(layer({ workspaceRoot, baseDir, detectCalls })));
82
83 assert.strictEqual(error._tag, "VcsRepositoryDetectionError");
84 assert.strictEqual(error.operation, "ReviewService.getDiffPreview");
85 assert.match(
86 "detail" in error ? error.detail : "",
87 /must stay within the configured workspace root/,
88 );
89 assert.deepStrictEqual(detectCalls, []);
90 }).pipe(Effect.provide(NodeServices.layer)),
91 );
92
93 it.effect("attributes file-content workspace violations to the file-content operation", () =>
94 Effect.gen(function* () {
95 const fs = yield* FileSystem.FileSystem;
96 const workspaceRoot = yield* fs.makeTempDirectoryScoped({ prefix: "t3-review-workspace-" });
97 const outsideRoot = yield* fs.makeTempDirectoryScoped({ prefix: "t3-review-outside-" });
98 const baseDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-review-base-" });
99 const detectCalls: Array<{ readonly cwd: string }> = [];
100
101 const error = yield* Effect.gen(function* () {
102 const review = yield* ReviewService.ReviewService;
103 return yield* review
104 .getDiffFileContents({
105 cwd: outsideRoot,
106 sourceKind: "working-tree",
107 changeType: "change",
108 baseRef: "HEAD",
109 headRef: null,
110 oldPath: "file.ts",
111 newPath: "file.ts",
112 })
113 .pipe(Effect.flip);
114 }).pipe(Effect.provide(layer({ workspaceRoot, baseDir, detectCalls })));
115
116 assert.strictEqual(error._tag, "VcsRepositoryDetectionError");
117 assert.strictEqual(error.operation, "ReviewService.getDiffFileContents");
118 assert.match(
119 "detail" in error ? error.detail : "",
120 /must stay within the configured workspace root/,
121 );
122 assert.deepStrictEqual(detectCalls, []);
123 }).pipe(Effect.provide(NodeServices.layer)),
124 );
125
126 it.effect("allows previous custom worktree locations but never a filesystem root", () =>
127 Effect.gen(function* () {
128 const fs = yield* FileSystem.FileSystem;
129 const workspaceRoot = yield* fs.makeTempDirectoryScoped({ prefix: "t3-review-workspace-" });
130 const baseDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-review-base-" });
131 const previous = yield* fs.makeTempDirectoryScoped({ prefix: "t3-review-old-worktrees-" });
132 const outsideRoot = yield* fs.makeTempDirectoryScoped({ prefix: "t3-review-outside-" });
133
134 const result = yield* Effect.gen(function* () {
135 const review = yield* ReviewService.ReviewService;
136 return yield* review.getDiffPreview({ cwd: previous });
137 }).pipe(
138 Effect.provide(
139 layer({
140 workspaceRoot,
141 baseDir,
142 worktreesDirectory: "/",
143 previousWorktreesDirectories: [previous],
144 }),
145 ),
146 );
147 assert.strictEqual(result.cwd, previous);
148
149 const rootLink = `${baseDir}/root-link`;
150 yield* fs.symlink("/", rootLink);
151 for (const worktreesDirectory of ["/", rootLink]) {
152 const error = yield* Effect.gen(function* () {
153 const review = yield* ReviewService.ReviewService;
154 return yield* review.getDiffPreview({ cwd: outsideRoot }).pipe(Effect.flip);
155 }).pipe(Effect.provide(layer({ workspaceRoot, baseDir, worktreesDirectory })));
156 assert.strictEqual(error._tag, "VcsRepositoryDetectionError");
157 }
158 }).pipe(Effect.provide(NodeServices.layer)),
159 );
160
161 it.effect("allows registered project roots outside the server cwd", () =>
162 Effect.gen(function* () {
163 const fs = yield* FileSystem.FileSystem;
164 const workspaceRoot = yield* fs.makeTempDirectoryScoped({ prefix: "t3-review-workspace-" });
165 const baseDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-review-base-" });
166 // Stands in for a project on another drive than the server's home cwd.
167 const projectRoot = yield* fs.makeTempDirectoryScoped({ prefix: "t3-review-project-" });
168 const projectChild = `${projectRoot}/packages/app`;
169 yield* fs.makeDirectory(projectChild, { recursive: true });
170 const sibling = `${projectRoot}-sibling`;
171 yield* fs.makeDirectory(sibling);
172 yield* Effect.addFinalizer(() => fs.remove(sibling, { recursive: true }).pipe(Effect.ignore));
173
174 const review = (cwd: string, projectRoots: ReadonlyArray<string> | "unavailable") =>
175 Effect.gen(function* () {
176 const service = yield* ReviewService.ReviewService;
177 return yield* service.getDiffPreview({ cwd });
178 }).pipe(Effect.provide(layer({ workspaceRoot, baseDir, projectRoots })));
179
180 assert.strictEqual((yield* review(projectRoot, [projectRoot])).cwd, projectRoot);
181 assert.strictEqual((yield* review(projectChild, [projectRoot])).cwd, projectChild);
182 for (const [cwd, projectRoots] of [
183 [sibling, [projectRoot]],
184 [projectRoot, []],
185 [projectRoot, "unavailable"],
186 ] as const) {
187 const error = yield* review(cwd, projectRoots).pipe(Effect.flip);
188 assert.strictEqual(error._tag, "VcsRepositoryDetectionError");
189 }
190 }).pipe(Effect.provide(NodeServices.layer)),
191 );
192
193 it.effect("allows diff preview cwd inside the configured workspace root", () =>
194 Effect.gen(function* () {
195 const fs = yield* FileSystem.FileSystem;
196 const workspaceRoot = yield* fs.makeTempDirectoryScoped({ prefix: "t3-review-workspace-" });
197 const baseDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-review-base-" });
198 const detectCalls: Array<{ readonly cwd: string }> = [];
199
200 const result = yield* Effect.gen(function* () {
201 const review = yield* ReviewService.ReviewService;
202 return yield* review.getDiffPreview({ cwd: workspaceRoot });
203 }).pipe(Effect.provide(layer({ workspaceRoot, baseDir, detectCalls })));
204
205 assert.strictEqual(result.cwd, workspaceRoot);
206 assert.deepStrictEqual(result.sources, []);
207 assert.deepStrictEqual(detectCalls, [{ cwd: workspaceRoot }]);
208 }).pipe(Effect.provide(NodeServices.layer)),
209 );
210
211 it.effect("preserves unexpected path-resolution failures", () =>
212 Effect.gen(function* () {
213 const fs = yield* FileSystem.FileSystem;
214 const workspaceRoot = yield* fs.makeTempDirectoryScoped({ prefix: "t3-review-workspace-" });
215 const baseDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-review-base-" });
216 const invalidCwd = `${workspaceRoot}\0invalid`;
217 const detectCalls: Array<{ readonly cwd: string }> = [];
218
219 const error = yield* Effect.gen(function* () {
220 const review = yield* ReviewService.ReviewService;
221 return yield* review.getDiffPreview({ cwd: invalidCwd }).pipe(Effect.flip);
222 }).pipe(Effect.provide(layer({ workspaceRoot, baseDir, detectCalls })));
223
224 assert.strictEqual(error._tag, "VcsRepositoryDetectionError");
225 if (error._tag !== "VcsRepositoryDetectionError") return;
226 assert.strictEqual(error.operation, "ReviewService.assertWorkspaceBoundCwd.canonicalizePath");
227 assert.strictEqual(error.cwd, invalidCwd);
228 assert.match(error.detail, /Failed to resolve a path/);
229 assert.instanceOf(error.cause, PlatformError.PlatformError);
230 assert.deepStrictEqual(detectCalls, []);
231 }).pipe(Effect.provide(NodeServices.layer)),
232 );
233});