Showing the changes to apps/server/src/workspace/Layers/WorkspaceFileSystem.test.ts (1 of 62 files). Show all
Unified · Split
| @@ -54,6 +54,64 @@ const writeTextFile = Effect.fn("writeTextFile")(function* ( · 53 unmodified lines | ||
| 54 | 54 | }); |
| 55 | 55 | |
| 56 | 56 | it.layer(TestLayer)("WorkspaceFileSystemLive", (it) => { |
| 57 | + describe("readFile", () => { | |
| 58 | + it.effect("reads UTF-8 files relative to the workspace root", () => | |
| 59 | + Effect.gen(function* () { | |
| 60 | + const workspaceFileSystem = yield* WorkspaceFileSystem; | |
| 61 | + const cwd = yield* makeTempDir; | |
| 62 | + yield* writeTextFile(cwd, "src/index.ts", "export const answer = 42;\n"); | |
| 63 | + | |
| 64 | + const result = yield* workspaceFileSystem.readFile({ | |
| 65 | + cwd, | |
| 66 | + relativePath: "src/index.ts", | |
| 67 | + }); | |
| 68 | + | |
| 69 | + expect(result).toEqual({ | |
| 70 | + relativePath: "src/index.ts", | |
| 71 | + contents: "export const answer = 42;\n", | |
| 72 | + byteLength: 26, | |
| 73 | + truncated: false, | |
| 74 | + }); | |
| 75 | + }), | |
| 76 | + ); | |
| 77 | + | |
| 78 | + it.effect("rejects reads outside the workspace root", () => | |
| 79 | + Effect.gen(function* () { | |
| 80 | + const workspaceFileSystem = yield* WorkspaceFileSystem; | |
| 81 | + const cwd = yield* makeTempDir; | |
| 82 | + | |
| 83 | + const error = yield* workspaceFileSystem | |
| 84 | + .readFile({ cwd, relativePath: "../escape.md" }) | |
| 85 | + .pipe(Effect.flip); | |
| 86 | + | |
| 87 | + expect(error.message).toContain( | |
| 88 | + "Workspace file path must be relative to the project root: ../escape.md", | |
| 89 | + ); | |
| 90 | + }), | |
| 91 | + ); | |
| 92 | + | |
| 93 | + it.effect("rejects symlinks that resolve outside the workspace root", () => | |
| 94 | + Effect.gen(function* () { | |
| 95 | + const workspaceFileSystem = yield* WorkspaceFileSystem; | |
| 96 | + const fileSystem = yield* FileSystem.FileSystem; | |
| 97 | + const path = yield* Path.Path; | |
| 98 | + const cwd = yield* makeTempDir; | |
| 99 | + const outsideDir = yield* makeTempDir; | |
| 100 | + yield* writeTextFile(outsideDir, "secret.txt", "outside\n"); | |
| 101 | + yield* fileSystem.symlink( | |
| 102 | + path.join(outsideDir, "secret.txt"), | |
| 103 | + path.join(cwd, "linked-secret.txt"), | |
| 104 | + ); | |
| 105 | + | |
| 106 | + const error = yield* workspaceFileSystem | |
| 107 | + .readFile({ cwd, relativePath: "linked-secret.txt" }) | |
| 108 | + .pipe(Effect.flip); | |
| 109 | + | |
| 110 | + expect(error.message).toContain("resolves outside the project root"); | |
| 111 | + }), | |
| 112 | + ); | |
| 113 | + }); | |
| 114 | + | |
| 57 | 115 | describe("writeFile", () => { |
| 58 | 116 | it.effect("writes files relative to the workspace root", () => |
| 59 | 117 | Effect.gen(function* () { |