test(web): add regression tests for readdirSync in boot payload path (#2050)

Fixes #1936

The /api/boot endpoint relies on bridge-service.ts importing readdirSync
from node:fs to list session files. Without this import, listProjectSessions
throws ReferenceError and the route returns HTTP 500 on every request.

Add two guard tests:
- Source-level check that bridge-service.ts imports readdirSync
- Integration test that exercises the real filesystem session listing
  (no listSessions mock) to catch the 500 at runtime

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Tom Boucher 2026-03-23 11:51:05 -04:00 committed by GitHub
parent de332ed3c8
commit c25b57b922
2 changed files with 97 additions and 0 deletions

View file

@ -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)",
)
})

View file

@ -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();
}
});