From fae9282f544ca59d064b923656d1d343fbeacaf0 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Tue, 18 Aug 2026 18:37:24 -0700 Subject: [PATCH] test(wsl): account for the route quarantine in stalled-mount tests (#15408) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * test(wsl): account for the route quarantine in stalled-mount tests #15381 added a route-level quarantine: a timed-out task blocks new admissions on that route for a back-off window, so the next real task probes recovery instead of hammering a hung mount. It merged with four tests already failing on main, and they still fail. Two causes, both test-side: - The recovery half of three tests read again immediately after releasing the stall, which the quarantine now refuses. They wait out the window, which is what a real caller does — resetting the gate would skip the admission the recovery assertions exist to prove. - `blockedRoutes` is module state that no test cleared, so a stall in one test refused an unrelated read in the next. Both files now reset the gate in beforeEach. Production behavior is unchanged and is working as designed: eviction still happens, only the immediate re-read is deferred by the back-off. * test(wsl): match the sibling suites' quarantine idiom #15381 applied this same fix to session-scanner-core-parser-wsl-stall, session-scanner-discovery-wsl-gate, and opencode-usage/scanner-wsl-gate, and missed these two files. Adopt that established shape rather than a parallel one: fold the back-off wait into releaseAndSettle instead of a separate helper at each call site, and fake performance explicitly, since performance.now drives the quarantine clock. The explicit toFake is not strictly required today — vitest's default already fakes performance — but it documents the dependency and stops a default change from silently unfaking the quarantine clock. --- .../session-scanner-index-cache-wsl-stall.test.ts | 14 +++++++++++--- .../session-scanner-parse-wsl-stall.test.ts | 14 +++++++++++--- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/main/ai-vault/session-scanner-index-cache-wsl-stall.test.ts b/src/main/ai-vault/session-scanner-index-cache-wsl-stall.test.ts index 1a5da65f1a9..73216dcb0f5 100644 --- a/src/main/ai-vault/session-scanner-index-cache-wsl-stall.test.ts +++ b/src/main/ai-vault/session-scanner-index-cache-wsl-stall.test.ts @@ -27,8 +27,10 @@ import { } from './session-scanner-kimi-paths' import { readJsonObjectIfExists } from './session-scanner-values' import { + WSL_TRANSCRIPT_FS_ROUTE_QUARANTINE_BASE_MS, WSL_TRANSCRIPT_FS_SCAN_TIMEOUT_MS, - WslTranscriptFsError + WslTranscriptFsError, + resetWslTranscriptFsGateForTests } from '../native-chat/wsl-transcript-fs-gate' // Identity must not change between the refused and the recovered read, or the @@ -55,13 +57,18 @@ function servingHandle(body: string) { } } +// A result that lands past the deadline never lifts the route quarantine, so +// recovery waits out the back-off window the same way production does. async function releaseAndSettle(): Promise { releaseStall?.() releaseStall = undefined - await vi.advanceTimersByTimeAsync(0) + await vi.advanceTimersByTimeAsync(WSL_TRANSCRIPT_FS_ROUTE_QUARANTINE_BASE_MS) } beforeEach(() => { + // blockedRoutes is persistent gate state: a prior stall must not quarantine + // this test's route. + resetWslTranscriptFsGateForTests() resetCodexSessionIndexTitleCacheForTests() clearKimiSessionIndexCache() mocks.stat.mockReset() @@ -69,7 +76,8 @@ beforeEach(() => { mocks.readFile.mockReset() mocks.stat.mockResolvedValue(INDEX_STATS) releaseStall = undefined - vi.useFakeTimers() + // performance.now drives the route quarantine clock, so it must be faked too. + vi.useFakeTimers({ toFake: ['setTimeout', 'clearTimeout', 'Date', 'performance'] }) }) afterEach(async () => { diff --git a/src/main/ai-vault/session-scanner-parse-wsl-stall.test.ts b/src/main/ai-vault/session-scanner-parse-wsl-stall.test.ts index 9eea5176c66..f41c36a2b05 100644 --- a/src/main/ai-vault/session-scanner-parse-wsl-stall.test.ts +++ b/src/main/ai-vault/session-scanner-parse-wsl-stall.test.ts @@ -20,8 +20,10 @@ import { resetSessionParseCacheForTests } from './session-scanner-parse-cache' import { + WSL_TRANSCRIPT_FS_ROUTE_QUARANTINE_BASE_MS, WSL_TRANSCRIPT_FS_SCAN_TIMEOUT_MS, - WslTranscriptFsError + WslTranscriptFsError, + resetWslTranscriptFsGateForTests } from '../native-chat/wsl-transcript-fs-gate' type ReadResult = { bytesRead: number; buffer: Buffer } @@ -77,19 +79,25 @@ function candidate(path: string, bytes: Buffer, mtimeMs: number): SessionFileCan } } +// A result that lands past the deadline never lifts the route quarantine, so +// recovery waits out the back-off window the same way production does. async function releaseAndSettle(): Promise { releaseStall?.() releaseStall = undefined - await vi.advanceTimersByTimeAsync(0) + await vi.advanceTimersByTimeAsync(WSL_TRANSCRIPT_FS_ROUTE_QUARANTINE_BASE_MS) } beforeEach(() => { + // blockedRoutes is persistent gate state: a prior stall must not quarantine + // this test's route. + resetWslTranscriptFsGateForTests() resetSessionParseCacheForTests() mocks.open.mockReset() mocks.readdir.mockReset() mocks.readdir.mockResolvedValue([]) releaseStall = undefined - vi.useFakeTimers() + // performance.now drives the route quarantine clock, so it must be faked too. + vi.useFakeTimers({ toFake: ['setTimeout', 'clearTimeout', 'Date', 'performance'] }) }) afterEach(async () => {