diff --git a/src/tests/web-boot-node24.test.ts b/src/tests/web-boot-node24.test.ts index f103070cf..dd587aefa 100644 --- a/src/tests/web-boot-node24.test.ts +++ b/src/tests/web-boot-node24.test.ts @@ -151,3 +151,26 @@ test("boot route returns { error } JSON on handler failure", async () => { "boot route must return status 500 on error", ) }) + +// --------------------------------------------------------------------------- +// Bug 4 — bridge-service must import readdirSync for session listing (#1936) +// --------------------------------------------------------------------------- + +test("bridge-service imports readdirSync from node:fs (#1936)", async () => { + // The boot payload calls listProjectSessions which uses readdirSync. + // A missing import causes ReferenceError → HTTP 500 on /api/boot. + const { readFileSync } = await import("node:fs") + const { join } = await import("node:path") + + const bridgeSource = readFileSync( + join(process.cwd(), "src", "web", "bridge-service.ts"), + "utf-8", + ) + + assert.match( + bridgeSource, + /import\s*\{[^}]*readdirSync[^}]*\}\s*from\s*["']node:fs["']/, + "bridge-service.ts must import readdirSync from node:fs — " + + "removing it breaks /api/boot with ReferenceError (see #1936)", + ) +}) diff --git a/src/tests/web-bridge-contract.test.ts b/src/tests/web-bridge-contract.test.ts index 1f29ad4ab..cf85c2d85 100644 --- a/src/tests/web-bridge-contract.test.ts +++ b/src/tests/web-bridge-contract.test.ts @@ -659,3 +659,77 @@ test("bridge command/runtime failures are inspectable and redact secret material fixture.cleanup(); } }); + +// --------------------------------------------------------------------------- +// Bug — readdirSync must be available in bridge-service for session listing +// (Fixes #1936: /api/boot returns 500 when readdirSync is missing) +// --------------------------------------------------------------------------- + +test("/api/boot lists sessions from the real filesystem via readdirSync (#1936)", async () => { + const fixture = makeWorkspaceFixture(); + const sessionPath = createSessionFile(fixture.projectCwd, fixture.sessionsDir, "sess-fs", "FS Session"); + const harness = createHarness((command, current) => { + if (command.type === "get_state") { + current.emit({ + id: command.id, + type: "response", + command: "get_state", + success: true, + data: { + sessionId: "sess-fs", + sessionFile: sessionPath, + thinkingLevel: "off", + isStreaming: false, + isCompacting: false, + steeringMode: "all", + followUpMode: "all", + autoCompactionEnabled: false, + autoRetryEnabled: false, + retryInProgress: false, + retryAttempt: 0, + messageCount: 0, + pendingMessageCount: 0, + }, + }); + return; + } + assert.fail(`unexpected command during boot: ${command.type}`); + }); + + // Deliberately omit listSessions so the real listProjectSessions (which + // calls readdirSync) is exercised. If readdirSync is missing from the + // bridge-service node:fs import, this test will throw ReferenceError. + bridge.configureBridgeServiceForTests({ + env: { + ...process.env, + GSD_WEB_PROJECT_CWD: fixture.projectCwd, + GSD_WEB_PROJECT_SESSIONS_DIR: fixture.sessionsDir, + GSD_WEB_PACKAGE_ROOT: repoRoot, + }, + spawn: harness.spawn, + indexWorkspace: async () => fakeWorkspaceIndex(), + getAutoDashboardData: () => fakeAutoDashboardData(), + getOnboardingNeeded: () => false, + }); + + try { + const response = await bootRoute.GET(); + assert.equal(response.status, 200, "/api/boot must not return 500 — readdirSync must be available"); + const payload = await response.json() as any; + + // The real listProjectSessions should have found the session file via readdirSync + assert.ok( + Array.isArray(payload.resumableSessions), + "boot payload must include resumableSessions array", + ); + assert.equal( + payload.resumableSessions.length, + 1, + "readdirSync-based session listing must find the test session file", + ); + assert.equal(payload.resumableSessions[0].id, "sess-fs"); + } finally { + await bridge.resetBridgeServiceForTests(); + fixture.cleanup(); + } +});