diff --git a/apps/server/src/review/ReviewService.test.ts b/apps/server/src/review/ReviewService.test.ts index eb8758b1282..839eb73b2bb 100644 --- a/apps/server/src/review/ReviewService.test.ts +++ b/apps/server/src/review/ReviewService.test.ts @@ -3,6 +3,7 @@ import { assert, describe, it } from "@effect/vitest"; import * as Effect from "effect/Effect"; import * as FileSystem from "effect/FileSystem"; import * as Layer from "effect/Layer"; +import * as PlatformError from "effect/PlatformError"; import { ServerConfig } from "../config.ts"; import * as GitVcsDriver from "../vcs/GitVcsDriver.ts"; @@ -73,4 +74,27 @@ describe("ReviewService", () => { assert.deepStrictEqual(detectCalls, [{ cwd: workspaceRoot }]); }).pipe(Effect.provide(NodeServices.layer)), ); + + it.effect("preserves unexpected path-resolution failures", () => + Effect.gen(function* () { + const fs = yield* FileSystem.FileSystem; + const workspaceRoot = yield* fs.makeTempDirectoryScoped({ prefix: "t3-review-workspace-" }); + const baseDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-review-base-" }); + const invalidCwd = `${workspaceRoot}\0invalid`; + const detectCalls: Array<{ readonly cwd: string }> = []; + + const error = yield* Effect.gen(function* () { + const review = yield* ReviewService.ReviewService; + return yield* review.getDiffPreview({ cwd: invalidCwd }).pipe(Effect.flip); + }).pipe(Effect.provide(makeLayer({ workspaceRoot, baseDir, detectCalls }))); + + assert.strictEqual(error._tag, "VcsRepositoryDetectionError"); + if (error._tag !== "VcsRepositoryDetectionError") return; + assert.strictEqual(error.operation, "ReviewService.assertWorkspaceBoundCwd.canonicalizePath"); + assert.strictEqual(error.cwd, invalidCwd); + assert.match(error.detail, /Failed to resolve a path/); + assert.instanceOf(error.cause, PlatformError.PlatformError); + assert.deepStrictEqual(detectCalls, []); + }).pipe(Effect.provide(NodeServices.layer)), + ); }); diff --git a/apps/server/src/review/ReviewService.ts b/apps/server/src/review/ReviewService.ts index 3f222bd520f..db1dc5bc8d2 100644 --- a/apps/server/src/review/ReviewService.ts +++ b/apps/server/src/review/ReviewService.ts @@ -33,8 +33,24 @@ export const make = Effect.gen(function* () { const vcsRegistry = yield* VcsDriverRegistry.VcsDriverRegistry; const git = yield* GitVcsDriver.GitVcsDriver; - const canonicalizePath = (value: string) => - fileSystem.realPath(path.resolve(value)).pipe(Effect.orElseSucceed(() => path.resolve(value))); + const canonicalizePath = (value: string) => { + const resolvedPath = path.resolve(value); + return fileSystem.realPath(resolvedPath).pipe( + Effect.catchTags({ + PlatformError: (cause) => + cause.reason._tag === "NotFound" + ? Effect.succeed(resolvedPath) + : Effect.fail( + new VcsRepositoryDetectionError({ + operation: "ReviewService.assertWorkspaceBoundCwd.canonicalizePath", + cwd: resolvedPath, + detail: "Failed to resolve a path while validating the review workspace.", + cause, + }), + ), + }), + ); + }; const isWithinRoot = (candidate: string, root: string) => { const relative = path.relative(root, candidate);