From 98a568ad50ab87d30c3b880826d323a8e6f36365 Mon Sep 17 00:00:00 2001 From: wenshao Date: Sun, 19 Jul 2026 18:54:31 +0800 Subject: [PATCH] test: raise timeout ceiling for I/O-bound tests flaky under CI contention MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The self-hosted CI runners are heavily oversubscribed (core runs maxThreads: 16), and a recurring class of tests blows vitest's 5s default timeout purely under that contention — not from any logic fault. Observed repeatedly across unrelated PRs (#7213, #7219, and noted in prior sessions): - packages/core/src/utils/shell-ast-parser-lazy.test.ts — fully mocked, but the dynamic import + async coordination exceeds 5s when 16 threads contend. - packages/cli/src/serve/workspace-registration-store.test.ts — tempdir round-trip. - packages/core/src/extension/github.test.ts > extractFile — its waitForFileData helper polled a FIXED 1_000 setImmediate turns, which elapse in <100ms while the tar extraction I/O is still catching up, throwing 'Timed out waiting for extracted data'. Fixes: - testTimeout: 15000 in the core and cli vitest configs — 3x the default. Assertions still fail instantly; only the timeout ceiling grows, so this masks no logic bug (a real hang still fails, just later, and the job timeout still bounds it). - waitForFileData now polls a real ~10s wall-clock budget (2_000 x 5ms) instead of a fixed iteration count, so a slow extraction is awaited rather than raced. Stays under the 15s ceiling. These are the deterministic root-cause fixes for the flake class the autofix loop and CI Failure Patrol were papering over with reruns. --- packages/cli/vitest.config.ts | 4 ++++ packages/core/src/extension/github.test.ts | 9 +++++++-- packages/core/vitest.config.ts | 6 ++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/packages/cli/vitest.config.ts b/packages/cli/vitest.config.ts index 20af92bc2df..aa34cadd821 100644 --- a/packages/cli/vitest.config.ts +++ b/packages/cli/vitest.config.ts @@ -112,6 +112,10 @@ export default defineConfig({ }, }, test: { + // See packages/core/vitest.config.ts: raise the per-test ceiling above + // vitest's 5s default so I/O-bound tests (e.g. the workspace registration + // store's tempdir round-trip) don't blow it purely under CI contention. + testTimeout: 15000, include: ['**/*.{test,spec}.?(c|m)[jt]s?(x)', 'config.test.ts'], exclude: ['**/node_modules/**', '**/dist/**', '**/cypress/**'], environment: 'jsdom', diff --git a/packages/core/src/extension/github.test.ts b/packages/core/src/extension/github.test.ts index 8188e5c3790..9296042cd0a 100644 --- a/packages/core/src/extension/github.test.ts +++ b/packages/core/src/extension/github.test.ts @@ -2075,9 +2075,14 @@ describe('git extension helpers', () => { } async function waitForFileData(filePath: string): Promise { - for (let attempt = 0; attempt < 1_000; attempt += 1) { + // Poll on a real wall-clock budget (~10s), not a fixed iteration count: + // setImmediate turns are sub-millisecond, so 1_000 of them could elapse + // in <100ms while the tar extraction I/O is still catching up on a + // contended runner — the source of the "Timed out waiting for extracted + // data" flake. Stays well under the 15s per-test ceiling. + for (let attempt = 0; attempt < 2_000; attempt += 1) { if ((await getFileSize(filePath)) > 0) return; - await new Promise((resolve) => setImmediate(resolve)); + await new Promise((resolve) => setTimeout(resolve, 5)); } throw new Error(`Timed out waiting for extracted data at ${filePath}`); } diff --git a/packages/core/vitest.config.ts b/packages/core/vitest.config.ts index b983891257f..85a298b6504 100644 --- a/packages/core/vitest.config.ts +++ b/packages/core/vitest.config.ts @@ -8,6 +8,12 @@ import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { + // Raise the per-test ceiling above vitest's 5s default: the self-hosted + // CI runners are heavily oversubscribed (maxThreads: 16 below), and I/O- + // or WASM-load-bound tests (e.g. the web-tree-sitter lazy runtime, tar + // extraction) blow 5s purely under contention, not from any logic fault. + // Assertions still fail instantly; only the timeout ceiling grows. + testTimeout: 15000, reporters: ['default', 'junit'], silent: true, setupFiles: ['./test-setup.ts'],