History

refactor workspace reads to Effect FileSystem

Cursor Agent committed 3 months ago

Commit 217fbf11 parent ebe8afb2 files · +138 −118Browse files

Showing the changes to apps/server/src/workspace/WorkspaceFileSystem.test.ts (1 of 2 files). Show all

Unified · Split

apps/server/src/workspace/WorkspaceFileSystem.test.ts+51−29
@@ -1,9 +1,11 @@
1import * as NodeServices from "@effect/platform-node/NodeServices";1import * as NodeServices from "@effect/platform-node/NodeServices";
2import { it, describe, expect } from "@effect/vitest";2import { assert, describe, it } from "@effect/vitest";
3import * as Effect from "effect/Effect";3import * as Effect from "effect/Effect";
4import * as FileSystem from "effect/FileSystem";4import * as FileSystem from "effect/FileSystem";
5import * as Layer from "effect/Layer";5import * as Layer from "effect/Layer";
6import * as Option from "effect/Option";
6import * as Path from "effect/Path";7import * as Path from "effect/Path";
8import * as PlatformError from "effect/PlatformError";
7 9
8import * as ServerConfig from "../config.ts";10import * as ServerConfig from "../config.ts";
9import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts";11import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts";
@@ -64,7 +66,7 @@ it.layer(TestLayer, { excludeTestServices: true })("WorkspaceFileSystemLive", (i · 54 unmodified lines
64 relativePath: "src/index.ts",66 relativePath: "src/index.ts",
65 });67 });
66 68
67 expect(result).toEqual({69 assert.deepStrictEqual(result, {
68 relativePath: "src/index.ts",70 relativePath: "src/index.ts",
69 contents: "export const answer = 42;\n",71 contents: "export const answer = 42;\n",
70 byteLength: 26,72 byteLength: 26,
@@ -82,7 +84,8 @@ it.layer(TestLayer, { excludeTestServices: true })("WorkspaceFileSystemLive", (i · 11 unmodified lines
82 .readFile({ cwd, relativePath: "../escape.md" })84 .readFile({ cwd, relativePath: "../escape.md" })
83 .pipe(Effect.flip);85 .pipe(Effect.flip);
84 86
85 expect(error.message).toContain(87 assert.include(
88 error.message,
86 "Workspace file path must be relative to the project root: ../escape.md",89 "Workspace file path must be relative to the project root: ../escape.md",
87 );90 );
88 }),91 }),
@@ -107,14 +110,14 @@ it.layer(TestLayer, { excludeTestServices: true })("WorkspaceFileSystemLive", (i · 18 unmodified lines
107 const resolvedWorkspaceRoot = yield* fileSystem.realPath(cwd);110 const resolvedWorkspaceRoot = yield* fileSystem.realPath(cwd);
108 const resolvedPath = yield* fileSystem.realPath(path.join(outsideDir, "secret.txt"));111 const resolvedPath = yield* fileSystem.realPath(path.join(outsideDir, "secret.txt"));
109 112
110 expect(error).toBeInstanceOf(WorkspaceFileSystem.WorkspaceFilePathEscapeError);113 assert.strictEqual(error._tag, "WorkspaceFilePathEscapeError");
111 expect(error).toMatchObject({114 assert.deepInclude(error, {
112 workspaceRoot: cwd,115 workspaceRoot: cwd,
113 relativePath: "linked-secret.txt",116 relativePath: "linked-secret.txt",
114 resolvedWorkspaceRoot,117 resolvedWorkspaceRoot,
115 resolvedPath,118 resolvedPath,
116 });119 });
117 expect("cause" in error).toBe(false);120 assert.notProperty(error, "cause");
118 }),121 }),
119 );122 );
120 123
@@ -131,13 +134,13 @@ it.layer(TestLayer, { excludeTestServices: true })("WorkspaceFileSystemLive", (i · 10 unmodified lines
131 .pipe(Effect.flip);134 .pipe(Effect.flip);
132 const resolvedPath = yield* fileSystem.realPath(path.join(cwd, "src"));135 const resolvedPath = yield* fileSystem.realPath(path.join(cwd, "src"));
133 136
134 expect(error).toBeInstanceOf(WorkspaceFileSystem.WorkspacePathNotFileError);137 assert.strictEqual(error._tag, "WorkspacePathNotFileError");
135 expect(error).toMatchObject({138 assert.deepInclude(error, {
136 workspaceRoot: cwd,139 workspaceRoot: cwd,
137 relativePath: "src",140 relativePath: "src",
138 resolvedPath,141 resolvedPath,
139 });142 });
140 expect("cause" in error).toBe(false);143 assert.notProperty(error, "cause");
141 }),144 }),
142 );145 );
143 146
@@ -155,14 +158,31 @@ it.layer(TestLayer, { excludeTestServices: true })("WorkspaceFileSystemLive", (i · 11 unmodified lines
155 .pipe(Effect.flip);158 .pipe(Effect.flip);
156 const resolvedPath = yield* fileSystem.realPath(absolutePath);159 const resolvedPath = yield* fileSystem.realPath(absolutePath);
157 160
158 expect(error).toBeInstanceOf(WorkspaceFileSystem.WorkspaceBinaryFileError);161 assert.strictEqual(error._tag, "WorkspaceBinaryFileError");
159 expect(error).toMatchObject({162 assert.deepInclude(error, {
160 workspaceRoot: cwd,163 workspaceRoot: cwd,
161 relativePath: "asset.bin",164 relativePath: "asset.bin",
162 resolvedPath,165 resolvedPath,
163 });166 });
164 expect("cause" in error).toBe(false);167 assert.notProperty(error, "cause");
165 expect("contents" in error).toBe(false);168 assert.notProperty(error, "contents");
169 }),
170 );
171
172 it.effect("reads at most the preview limit", () =>
173 Effect.gen(function* () {
174 const workspaceFileSystem = yield* WorkspaceFileSystem.WorkspaceFileSystem;
175 const cwd = yield* makeTempDir;
176 yield* writeTextFile(cwd, "large.txt", "a".repeat(1024 * 1024 + 1));
177
178 const result = yield* workspaceFileSystem.readFile({
179 cwd,
180 relativePath: "large.txt",
181 });
182
183 assert.strictEqual(result.contents.length, 1024 * 1024);
184 assert.strictEqual(result.byteLength, 1024 * 1024 + 1);
185 assert.isTrue(result.truncated);
166 }),186 }),
167 );187 );
168 188
@@ -177,16 +197,19 @@ it.layer(TestLayer, { excludeTestServices: true })("WorkspaceFileSystemLive", (i · 8 unmodified lines
177 .readFile({ cwd, relativePath: "missing.txt" })197 .readFile({ cwd, relativePath: "missing.txt" })
178 .pipe(Effect.flip);198 .pipe(Effect.flip);
179 199
180 expect(error).toBeInstanceOf(WorkspaceFileSystem.WorkspaceFileSystemOperationError);200 assert.strictEqual(error._tag, "WorkspaceFileSystemOperationError");
181 expect(error).toMatchObject({201 assert.deepInclude(error, {
182 workspaceRoot: cwd,202 workspaceRoot: cwd,
183 relativePath: "missing.txt",203 relativePath: "missing.txt",
184 resolvedPath,204 resolvedPath,
185 operationPath: resolvedPath,205 operationPath: resolvedPath,
186 operation: "realpath-target",206 operation: "realpath-target",
187 });207 });
188 expect(error.cause).toBeInstanceOf(Error);208 assert.instanceOf(error.cause, PlatformError.PlatformError);
189 expect((error.cause as NodeJS.ErrnoException).code).toBe("ENOENT");209 assert.strictEqual(
210 (error.cause as PlatformError.PlatformError).reason._tag,
211 "NotFound",
212 );
190 }),213 }),
191 );214 );
192 });215 });
@@ -207,8 +230,8 @@ it.layer(TestLayer, { excludeTestServices: true })("WorkspaceFileSystemLive", (i · 14 unmodified lines
207 .readFileString(path.join(cwd, "plans/effect-rpc.md"))230 .readFileString(path.join(cwd, "plans/effect-rpc.md"))
208 .pipe(Effect.orDie);231 .pipe(Effect.orDie);
209 232
210 expect(result).toEqual({ relativePath: "plans/effect-rpc.md" });233 assert.deepStrictEqual(result, { relativePath: "plans/effect-rpc.md" });
211 expect(saved).toBe("# Plan\n");234 assert.strictEqual(saved, "# Plan\n");
212 }),235 }),
213 );236 );
214 237
@@ -220,8 +243,8 @@ it.layer(TestLayer, { excludeTestServices: true })("WorkspaceFileSystemLive", (i · 5 unmodified lines
220 yield* writeTextFile(cwd, "src/existing.ts", "export {};\n");243 yield* writeTextFile(cwd, "src/existing.ts", "export {};\n");
221 244
222 const beforeWrite = yield* workspaceEntries.list({ cwd });245 const beforeWrite = yield* workspaceEntries.list({ cwd });
223 expect(beforeWrite.entries.some((entry) => entry.path === "plans/effect-rpc.md")).toBe(246 assert.isFalse(
224 false,247 beforeWrite.entries.some((entry) => entry.path === "plans/effect-rpc.md"),
225 );248 );
226 249
227 yield* workspaceFileSystem.writeFile({250 yield* workspaceFileSystem.writeFile({
@@ -231,10 +254,10 @@ it.layer(TestLayer, { excludeTestServices: true })("WorkspaceFileSystemLive", (i · 3 unmodified lines
231 });254 });
232 255
233 const afterWrite = yield* workspaceEntries.list({ cwd });256 const afterWrite = yield* workspaceEntries.list({ cwd });
234 expect(afterWrite.entries).toEqual(257 assert.isTrue(
235 expect.arrayContaining([expect.objectContaining({ path: "plans/effect-rpc.md" })]),258 afterWrite.entries.some((entry) => entry.path === "plans/effect-rpc.md"),
236 );259 );
237 expect(afterWrite.truncated).toBe(false);260 assert.isFalse(afterWrite.truncated);
238 }),261 }),
239 );262 );
240 263
@@ -253,15 +276,14 @@ it.layer(TestLayer, { excludeTestServices: true })("WorkspaceFileSystemLive", (i · 12 unmodified lines
253 })276 })
254 .pipe(Effect.flip);277 .pipe(Effect.flip);
255 278
256 expect(error.message).toContain(279 assert.include(
280 error.message,
257 "Workspace file path must be relative to the project root: ../escape.md",281 "Workspace file path must be relative to the project root: ../escape.md",
258 );282 );
259 283
260 const escapedPath = path.resolve(cwd, "..", "escape.md");284 const escapedPath = path.resolve(cwd, "..", "escape.md");
261 const escapedStat = yield* fileSystem285 const escapedStat = yield* fileSystem.stat(escapedPath).pipe(Effect.option);
262 .stat(escapedPath)286 assert.isTrue(Option.isNone(escapedStat));
263 .pipe(Effect.orElseSucceed(() => null));
264 expect(escapedStat).toBeNull();
265 }),287 }),
266 );288 );
267 });289 });