| ... | @@ -370,9 +370,32 @@ const buildAppUnderTest = (options?: { | ... | @@ -370,9 +370,32 @@ const buildAppUnderTest = (options?: { |
| 370 | ...options?.config, | 370 | ...options?.config, |
| 371 | }; | 371 | }; |
| 372 | const layerConfig = Layer.succeed(ServerConfig, config); | 372 | const layerConfig = Layer.succeed(ServerConfig, config); |
| | 373 | const gitCoreLayer = Layer.mock(GitCore)({ |
| | 374 | isInsideWorkTree: () => Effect.succeed(false), |
| | 375 | listWorkspaceFiles: () => |
| | 376 | Effect.succeed({ |
| | 377 | paths: [], |
| | 378 | truncated: false, |
| | 379 | }), |
| | 380 | filterIgnoredPaths: (_cwd, relativePaths) => Effect.succeed(relativePaths), |
| | 381 | ...options?.layers?.gitCore, |
| | 382 | }); |
| 373 | const gitManagerLayer = Layer.mock(GitManager)({ | 383 | const gitManagerLayer = Layer.mock(GitManager)({ |
| 374 | ...options?.layers?.gitManager, | 384 | ...options?.layers?.gitManager, |
| 375 | }); | 385 | }); |
| | 386 | const workspaceEntriesLayer = WorkspaceEntriesLive.pipe( |
| | 387 | Layer.provide(WorkspacePathsLive), |
| | 388 | Layer.provideMerge(gitCoreLayer), |
| | 389 | ); |
| | 390 | const workspaceAndProjectServicesLayer = Layer.mergeAll( |
| | 391 | WorkspacePathsLive, |
| | 392 | workspaceEntriesLayer, |
| | 393 | WorkspaceFileSystemLive.pipe( |
| | 394 | Layer.provide(WorkspacePathsLive), |
| | 395 | Layer.provide(workspaceEntriesLayer), |
| | 396 | ), |
| | 397 | ProjectFaviconResolverLive, |
| | 398 | ); |
| 376 | const gitStatusBroadcasterLayer = options?.layers?.gitStatusBroadcaster | 399 | const gitStatusBroadcasterLayer = options?.layers?.gitStatusBroadcaster |
| 377 | ? Layer.mock(GitStatusBroadcaster)({ | 400 | ? Layer.mock(GitStatusBroadcaster)({ |
| 378 | ...options.layers.gitStatusBroadcaster, | 401 | ...options.layers.gitStatusBroadcaster, |
| ... | @@ -416,11 +439,7 @@ const buildAppUnderTest = (options?: { | ... | @@ -416,11 +439,7 @@ const buildAppUnderTest = (options?: { |
| 416 | ...options?.layers?.open, | 439 | ...options?.layers?.open, |
| 417 | }), | 440 | }), |
| 418 | ), | 441 | ), |
| 419 | Layer.provide( | 442 | Layer.provide(gitCoreLayer), |
| 420 | Layer.mock(GitCore)({ | | |
| 421 | ...options?.layers?.gitCore, | | |
| 422 | }), | | |
| 423 | ), | | |
| 424 | Layer.provide(gitManagerLayer), | 443 | Layer.provide(gitManagerLayer), |
| 425 | Layer.provideMerge(gitStatusBroadcasterLayer), | 444 | Layer.provideMerge(gitStatusBroadcasterLayer), |
| 426 | Layer.provide( | 445 | Layer.provide( |
| ... | @@ -2017,6 +2036,58 @@ it.layer(NodeServices.layer)("server router seam", (it) => { | ... | @@ -2017,6 +2036,58 @@ it.layer(NodeServices.layer)("server router seam", (it) => { |
| 2017 | }).pipe(Effect.provide(NodeHttpServer.layerTest)), | 2036 | }).pipe(Effect.provide(NodeHttpServer.layerTest)), |
| 2018 | ); | 2037 | ); |
| 2019 | | 2038 | |
| | 2039 | it.effect("routes websocket rpc projects.searchEntries excludes gitignored files", () => |
| | 2040 | Effect.gen(function* () { |
| | 2041 | const fs = yield* FileSystem.FileSystem; |
| | 2042 | const path = yield* Path.Path; |
| | 2043 | const workspaceDir = yield* fs.makeTempDirectoryScoped({ |
| | 2044 | prefix: "t3-ws-project-search-gitignored-", |
| | 2045 | }); |
| | 2046 | yield* fs.writeFileString(path.join(workspaceDir, ".gitignore"), ".venv/\n"); |
| | 2047 | yield* fs.makeDirectory(path.join(workspaceDir, ".venv", "lib"), { recursive: true }); |
| | 2048 | yield* fs.writeFileString( |
| | 2049 | path.join(workspaceDir, ".venv", "lib", "ignored-search-target.ts"), |
| | 2050 | "export const ignored = true;", |
| | 2051 | ); |
| | 2052 | yield* fs.makeDirectory(path.join(workspaceDir, "src"), { recursive: true }); |
| | 2053 | yield* fs.writeFileString( |
| | 2054 | path.join(workspaceDir, "src", "tracked.ts"), |
| | 2055 | "export const ok = 1;", |
| | 2056 | ); |
| | 2057 | |
| | 2058 | yield* buildAppUnderTest({ |
| | 2059 | layers: { |
| | 2060 | gitCore: { |
| | 2061 | isInsideWorkTree: () => Effect.succeed(true), |
| | 2062 | listWorkspaceFiles: () => |
| | 2063 | Effect.succeed({ |
| | 2064 | paths: ["src/tracked.ts"], |
| | 2065 | truncated: false, |
| | 2066 | }), |
| | 2067 | filterIgnoredPaths: (_cwd, relativePaths) => |
| | 2068 | Effect.succeed( |
| | 2069 | relativePaths.filter((relativePath) => !relativePath.startsWith(".venv/")), |
| | 2070 | ), |
| | 2071 | }, |
| | 2072 | }, |
| | 2073 | }); |
| | 2074 | |
| | 2075 | const wsUrl = yield* getWsServerUrl("/ws"); |
| | 2076 | const response = yield* Effect.scoped( |
| | 2077 | withWsRpcClient(wsUrl, (client) => |
| | 2078 | client[WS_METHODS.projectsSearchEntries]({ |
| | 2079 | cwd: workspaceDir, |
| | 2080 | query: "ignored-search-target", |
| | 2081 | limit: 10, |
| | 2082 | }), |
| | 2083 | ), |
| | 2084 | ); |
| | 2085 | |
| | 2086 | assert.equal(response.entries.length, 0); |
| | 2087 | assert.equal(response.truncated, false); |
| | 2088 | }).pipe(Effect.provide(NodeHttpServer.layerTest)), |
| | 2089 | ); |
| | 2090 | |
| 2020 | it.effect("routes websocket rpc projects.searchEntries errors", () => | 2091 | it.effect("routes websocket rpc projects.searchEntries errors", () => |
| 2021 | Effect.gen(function* () { | 2092 | Effect.gen(function* () { |
| 2022 | yield* buildAppUnderTest(); | 2093 | yield* buildAppUnderTest(); |