diff --git a/scripts/tests/install-script.test.js b/scripts/tests/install-script.test.js index b033e3d7f93..1f46a1e8a8e 100644 --- a/scripts/tests/install-script.test.js +++ b/scripts/tests/install-script.test.js @@ -60,8 +60,6 @@ if (process.env.CI && process.platform !== 'win32' && !zipAvailable) { const itWithZip = zipAvailable ? it : it.skip; const itOnUnixWithZip = zipAvailable ? itOnUnix : it.skip; -vi.setConfig({ testTimeout: 30_000 }); - describe('installation scripts', () => { it('keeps the Linux/macOS installer lightweight', () => { const script = readScript( diff --git a/scripts/tests/qwen-autofix-workflow.test.js b/scripts/tests/qwen-autofix-workflow.test.js index bba8e31e93a..49454e7893b 100644 --- a/scripts/tests/qwen-autofix-workflow.test.js +++ b/scripts/tests/qwen-autofix-workflow.test.js @@ -2064,7 +2064,7 @@ describe('qwen-autofix workflow', () => { head: H, }).stale, ).toBe(false); - }, 30000); + }); it('behaviorally replays the eligibility recheck across lifecycle and label states', () => { // Extract the recheck VERBATIM (drift fails the test) and run it with a @@ -12643,7 +12643,7 @@ exit 1 /then\n\s+echo "📊 milestone digest posted/, ); expect(pushAndReportStep).toContain('milestone digest failed to post'); - }, 30000); + }); it('salvages a race-lost push by merging the moved head instead of discarding the run', () => { // A one-shot push dies `fetch first` whenever anything pushes to the PR @@ -13674,7 +13674,7 @@ exit 1 const fuzz = run(crossWorkspace, { enforce: 'terminate' }); expect(fuzz.out).toContain('SURVIVED'); expect(fuzz.advisory).toContain('outside the PR footprint'); - }, 30000); + }); it('upserts deferred findings into a per-PR issue that survives the merge', () => { // Wiring: the upsert runs after both shared resolve/reply call sites @@ -17513,7 +17513,7 @@ exit 1 expect(ciWorkflow).toContain( '.github/scripts/autofix-status-heartbeat.test.mjs', ); - }, 30000); + }); it('renders the whole managed fleet into the run summary', () => { // Diagnosing a stall used to mean listing bot PRs, regexing each one's eval @@ -21277,7 +21277,7 @@ exit 0 expect(runAddressReview(dir, stub).status).not.toBe(0); expect(existsSync(join(dir, 'agent-api-error'))).toBe(false); }); - }, 30000); + }); it('classifies permanent API failures terminal and records the cause class', () => { // A permanent 400 whose text happens to carry a 3-digit number in 500-599 @@ -23960,10 +23960,10 @@ describe('review verification gate: baseline A/B on deterministic rejection', () expect(neutralized.stdout).toContain(';;error;;forged'); expect(neutralized.stdout).not.toContain('::error::forged'); // Eight runGate arms, each a fixture repo plus a full gate-script - // replay under bash — this outgrows the 5s default on slow runners - // (it timed out at ~6.4s on the PR head); the suite's convention is - // an explicit per-test budget for tests that spawn subprocesses. - }, 30000); + // replay under bash — this outgrows vitest's 5s default on slow + // runners (it timed out at ~6.4s on the PR head), so it runs on the + // suite ceiling in scripts/tests/vitest.config.ts. + }); it('rejects a handoff written over a dirty workspace, non-retryably', () => { // A handoff claims the round deliberately changed NOTHING; dirt beside diff --git a/scripts/tests/qwen-triage-workflow.test.js b/scripts/tests/qwen-triage-workflow.test.js index 8a09642c3e1..c51fc35cf9d 100644 --- a/scripts/tests/qwen-triage-workflow.test.js +++ b/scripts/tests/qwen-triage-workflow.test.js @@ -6368,8 +6368,9 @@ describe('qwen-triage verify maintainer-review round', () => { // client's own timeout (curl 28). expect(out).toContain('stall_exit=18'); // 20 chunks x 200 ms is 4 s before the stall arm even starts, so this - // cannot fit vitest's 5 s default. It was timing out on main. - }, 30000); + // cannot fit vitest's 5 s default (it was timing out on main) and runs + // on the suite ceiling in scripts/tests/vitest.config.ts. + }); // GitHub cancels the OLDER pending run in a concurrency group, so the // requester's own /verify proceeds — the earlier "queued behind other @@ -6647,9 +6648,9 @@ describe('qwen-triage tmux lane parity', () => { const out = runProxyWatchdogTest(proxy); expect(out).toContain('chunks=20'); expect(out).toContain('stall_exit=18'); - // Same reason as its verify-lane twin: the stream alone outlasts the - // 5 s default. - }, 30000); + // Same reason as its verify-lane twin: the stream alone outlasts the 5 s + // default, so this runs on the suite ceiling too. + }); // PR lifecycle scripts run before the agent and can plant a // tmp/-tmux-/ directory whose report.md and transcript the @@ -7093,7 +7094,7 @@ describe('qwen-triage build-process guard', () => { } finally { rmSync(dir, { recursive: true, force: true }); } - }, 30000); + }); }); describe('triage job budget', () => { diff --git a/scripts/tests/unit-vitest-configs.test.ts b/scripts/tests/unit-vitest-configs.test.ts index 772b6439888..d543b3e5460 100644 --- a/scripts/tests/unit-vitest-configs.test.ts +++ b/scripts/tests/unit-vitest-configs.test.ts @@ -4,7 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { readFileSync } from 'node:fs'; +import { readdirSync, readFileSync } from 'node:fs'; +import { join, relative } from 'node:path'; import { fileURLToPath } from 'node:url'; import { describe, expect, it, vi } from 'vitest'; @@ -227,8 +228,9 @@ describe('scripts suite timeout', () => { // 30s was the quiet-host figure. Release run 33725742855 lost its Quality // Checks (Scripts) job to two files at once — qwen-autofix-workflow, whose // heaviest case measures ~14s idle, and acp-serve-boundary-guard — neither - // slow, both past 30s under contention. A per-file `vi.setConfig` cannot - // fix it: these cases register their timeout at collection. + // slow, both past 30s under contention. A per-file `vi.setConfig` does + // outrank this config, but not the per-test budget the cases that timed out + // carry: that is registered at collection and beats both. for (const [stub, expected] of [ [undefined, 90_000], ['5000', 5_000], @@ -245,4 +247,37 @@ describe('scripts suite timeout', () => { vi.unstubAllEnvs(); } }); + + it('leaves no file-level runtime override shadowing that ceiling', () => { + // The pin above re-imports the config module, so it cannot see an override + // a suite applies to itself: a runtime `setConfig` in a test file outranks + // the project config and holds that suite at its own number while this file + // reads green — install-script.test.js sat at 30s under the raised ceiling + // that way. `vitest` is vitest's own alias for `vi`, and under this + // suite's `globals: true` a bare global besides, so both spellings clamp + // the same. Every argument shape does that too, so the call itself is the + // violation; matching `testTimeout` inside the arguments let a parenthesized + // value ahead of it slip past. The walk takes every file here, not the + // include glob's extension list, so widening that list cannot leave the scan + // behind. Deliberate per-test budgets (`it(name, fn, ms)`) are out of scope: + // they are visible at the test they clamp, unlike a file-wide override. + const clampsCeiling = (source: string) => + /\b(vi|vitest)\.setConfig\(/.test(source); + // Assembled, not spelled out: this file is inside its own walk. + for (const alias of ['vi', 'vitest']) { + expect( + clampsCeiling(`${alias}.setConfig({ testTimeout: 1 })`), + alias, + ).toBe(true); + } + const here = fileURLToPath(new URL('.', import.meta.url)); + const shadowing = readdirSync(here, { + recursive: true, + withFileTypes: true, + }) + .filter((entry) => entry.isFile()) + .map((entry) => relative(here, join(entry.parentPath, entry.name))) + .filter((path) => clampsCeiling(readFileSync(join(here, path), 'utf8'))); + expect(shadowing).toEqual([]); + }); }); diff --git a/scripts/tests/vitest.config.ts b/scripts/tests/vitest.config.ts index c82cd3ef4fe..2aa57b2fc5d 100644 --- a/scripts/tests/vitest.config.ts +++ b/scripts/tests/vitest.config.ts @@ -40,8 +40,9 @@ export default defineConfig({ // Quality Checks (Scripts) job to two files at once — // qwen-autofix-workflow.test.js, whose heaviest case measures ~14s idle, // and acp-serve-boundary-guard.test.js — neither of them slow, both past - // 30s under contention. Per-test `vi.setConfig` does not help: these - // cases register their timeout at collection, before it runs. + // 30s under contention. A per-file `vi.setConfig` does outrank this + // config, but not the per-test budget the cases that timed out carry: that + // is registered at collection and beats both. testTimeout: Number(process.env['QWEN_SCRIPTS_TEST_TIMEOUT_MS'] ?? 90_000), coverage: { provider: 'v8',