From e6a26401d2b878523f9d7265dc861e4e72276b1b Mon Sep 17 00:00:00 2001 From: hetaoBackend Date: Sat, 19 Sep 2026 11:55:51 +0800 Subject: [PATCH] test: bound Windows filesystem concurrency and runtime startup --- .agents/skills/testing-workflow/SKILL.md | 6 ++++++ scripts/run-vitest-suite.mjs | 4 ++++ test/smoke.test.mjs | 21 ++++++++++++++------ test/source-sync.test.mjs | 25 ++++++++++++++---------- 4 files changed, 40 insertions(+), 16 deletions(-) diff --git a/.agents/skills/testing-workflow/SKILL.md b/.agents/skills/testing-workflow/SKILL.md index cf44573..512bd7c 100644 --- a/.agents/skills/testing-workflow/SKILL.md +++ b/.agents/skills/testing-workflow/SKILL.md @@ -39,6 +39,12 @@ suites are outside this distribution's verification. Artifact-dependent tests require a current `pnpm build`. Keep test state and reports outside the repository, using synthetic data and temporary directories. +The declared Vitest gate runner serializes test files on Windows to limit +filesystem contention while retaining individual test deadlines. Run the owning +gate for platform acceptance; a direct Vitest invocation uses its default worker +count. Full-runtime smoke children have a 30-second Windows startup budget, +matching ACP; lightweight help and validation checks retain 15 seconds. + ## Delivery verification Run `git diff --check` and the relevant individual gates while editing. Review diff --git a/scripts/run-vitest-suite.mjs b/scripts/run-vitest-suite.mjs index 19f23f5..ffc0c89 100644 --- a/scripts/run-vitest-suite.mjs +++ b/scripts/run-vitest-suite.mjs @@ -26,6 +26,10 @@ const result = spawnSync( "run", "--config", "vitest.oss.config.mjs", + // These suites create many SQLite databases and watched directories. On + // Windows, concurrent files can exhaust the 5s test budget through I/O + // contention. Serialize files without relaxing individual test deadlines. + ...(process.platform === "win32" ? ["--maxWorkers", "1"] : []), ...files, ], { stdio: "inherit", cwd: repositoryRoot, env: environment }, diff --git a/test/smoke.test.mjs b/test/smoke.test.mjs index 81ae2d1..8dc2b40 100644 --- a/test/smoke.test.mjs +++ b/test/smoke.test.mjs @@ -9,6 +9,14 @@ import Database from "better-sqlite3"; const root = fileURLToPath(new URL("../", import.meta.url)); const cli = path.join(root, "dist/cli.js"); +// Full runtime startup can exceed 15s on Windows CI (ACP took 22s). +// Match the ACP startup budget; lightweight help/validation stays at 15s. +const runtimeTimeoutMs = process.platform === "win32" ? 30000 : 15000; +function assertSuccessfulChild(result) { + assert.equal(result.error, undefined, + `CLI spawn failed: ${result.error?.message}; signal=${result.signal}; stderr=${result.stderr}`); + assert.equal(result.status, 0, result.stderr); +} const version = JSON.parse( readFileSync(path.join(root, "packages/tui/package.json"), "utf8"), ).version; @@ -46,6 +54,7 @@ function fixture(t, environment = process.env) { MAVIS_DATA_DIR: dataDir, MCODE_TEST_NETWORK_AUDIT: audit, MCODE_TEST_MANAGED_OFFLINE: "1", + MCODE_TEST_PROCESS_PROBE: "1", NODE_OPTIONS: `--import=${new URL("./network-deny.mjs", import.meta.url).href}`, }, }; @@ -70,9 +79,9 @@ test("provider configuration loads from an isolated data directory", (t) => { const result = spawnSync(process.execPath, [cli, "provider", "list"], { ...fixture(t), encoding: "utf8", - timeout: 15000, + timeout: runtimeTimeoutMs, }); - assert.equal(result.status, 0, result.stderr); + assertSuccessfulChild(result); assert.match(result.stdout, /minimax/); assert.doesNotMatch(result.stdout, /custom_provider:/); @@ -97,9 +106,9 @@ test("offline smoke children ignore ambient proxy variables", async (t) => { const result = spawnSync(process.execPath, [cli, "provider", "list"], { ...options, encoding: "utf8", - timeout: 15000, + timeout: runtimeTimeoutMs, }); - assert.equal(result.status, 0, result.stderr); + assertSuccessfulChild(result); assert.match(result.stdout, /minimax/); assert.match( readFileSync(options.env.MCODE_TEST_NETWORK_AUDIT + ".managed", "utf8"), @@ -225,9 +234,9 @@ test("local plugin browsing remains available with managed services offline", (t const result = spawnSync(process.execPath, [cli, ...args], { ...options, encoding: "utf8", - timeout: 15000, + timeout: runtimeTimeoutMs, }); - assert.equal(result.status, 0, result.stderr); + assertSuccessfulChild(result); const output = JSON.parse(result.stdout); assert.ok(output !== null); assert.doesNotMatch(result.stdout, /"official"/); diff --git a/test/source-sync.test.mjs b/test/source-sync.test.mjs index b0caead..c1085e1 100644 --- a/test/source-sync.test.mjs +++ b/test/source-sync.test.mjs @@ -404,16 +404,21 @@ test('suite runner preserves gate arguments and canonicalizes Windows temporary writeFileSync(path.join(root, 'node_modules/vitest/cli.cjs'), 'console.log(JSON.stringify({args:process.argv.slice(2),temp:process.env.TEMP,tmp:process.env.TMP,cwd:process.cwd()})); process.exit(17);'); const alias = path.join(root, 'temporary-alias'); symlinkSync(path.join(root, 'temporary'), alias, process.platform === 'win32' ? 'junction' : 'dir'); - const result = spawnSync(process.execPath, [path.join(root, 'scripts/run-vitest-suite.mjs'), 'fixture'], { - encoding: 'utf8', env: { ...process.env, TEMP: alias, TMP: alias }, - }); - assert.equal(result.status, 17, result.stderr); - const child = JSON.parse(result.stdout); - assert.deepEqual(child.args, ['run', '--config', 'vitest.oss.config.mjs', 'test/example.test.ts']); - assert.equal(realpathSync(child.cwd), realpathSync(root)); - const expected = process.platform === 'win32' ? realpathSync.native(alias) : alias; - assert.equal(child.temp, expected); - assert.equal(child.tmp, expected); + for (const platform of ['linux', 'darwin', 'win32']) { + const preload = path.join(root, 'platform.cjs'); + writeFileSync(preload, `Object.defineProperty(process, 'platform', { value: ${JSON.stringify(platform)} });`); + const result = spawnSync(process.execPath, ['--require', preload, path.join(root, 'scripts/run-vitest-suite.mjs'), 'fixture'], { + encoding: 'utf8', env: { ...process.env, TEMP: alias, TMP: alias, TMPDIR: alias }, + }); + assert.equal(result.status, 17, result.stderr); + const child = JSON.parse(result.stdout); + assert.deepEqual(child.args, ['run', '--config', 'vitest.oss.config.mjs', + ...(platform === 'win32' ? ['--maxWorkers', '1'] : []), 'test/example.test.ts']); + assert.equal(realpathSync(child.cwd), realpathSync(root)); + const expected = platform === 'win32' ? realpathSync.native(alias) : alias; + assert.equal(child.temp, expected); + assert.equal(child.tmp, expected); + } }); test('public support forms preserve destination URLs and separate Desktop from CLI reports', () => {