From 069670388ec47ae33ac305e7bd6f4d2571a6a71a Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 12 Feb 2026 17:59:44 +0000 Subject: [PATCH] perf(test): speed up test runs and harden temp cleanup --- scripts/test-parallel.mjs | 27 +++++++++++++++---- ...oard-non-interactive.provider-auth.test.ts | 19 ++++++++++++- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/scripts/test-parallel.mjs b/scripts/test-parallel.mjs index 4d4c928229..646c57c609 100644 --- a/scripts/test-parallel.mjs +++ b/scripts/test-parallel.mjs @@ -32,14 +32,22 @@ const shardCount = isWindowsCi : 2 : 1; const windowsCiArgs = isWindowsCi ? ["--dangerouslyIgnoreUnhandledErrors"] : []; +const silentArgs = + process.env.OPENCLAW_TEST_SHOW_PASSED_LOGS === "1" ? [] : ["--silent=passed-only"]; const rawPassthroughArgs = process.argv.slice(2); const passthroughArgs = rawPassthroughArgs[0] === "--" ? rawPassthroughArgs.slice(1) : rawPassthroughArgs; const overrideWorkers = Number.parseInt(process.env.OPENCLAW_TEST_WORKERS ?? "", 10); const resolvedOverride = Number.isFinite(overrideWorkers) && overrideWorkers > 0 ? overrideWorkers : null; -const parallelRuns = runs.filter((entry) => entry.name !== "gateway"); -const serialRuns = runs.filter((entry) => entry.name === "gateway"); +// Keep gateway serial by default to avoid resource contention with unit/extensions. +// Allow explicit opt-in parallel runs on non-Windows CI/local when requested. +const keepGatewaySerial = + isWindowsCi || + process.env.OPENCLAW_TEST_SERIAL_GATEWAY === "1" || + process.env.OPENCLAW_TEST_PARALLEL_GATEWAY !== "1"; +const parallelRuns = keepGatewaySerial ? runs.filter((entry) => entry.name !== "gateway") : runs; +const serialRuns = keepGatewaySerial ? runs.filter((entry) => entry.name === "gateway") : []; const localWorkers = Math.max(4, Math.min(16, os.cpus().length)); const defaultUnitWorkers = localWorkers; const defaultExtensionsWorkers = Math.max(1, Math.min(4, Math.floor(localWorkers / 4))); @@ -120,11 +128,12 @@ const runOnce = (entry, extraArgs = []) => ...entry.args, "--maxWorkers", String(maxWorkers), + ...silentArgs, ...reporterArgs, ...windowsCiArgs, ...extraArgs, ] - : [...entry.args, ...reporterArgs, ...windowsCiArgs, ...extraArgs]; + : [...entry.args, ...silentArgs, ...reporterArgs, ...windowsCiArgs, ...extraArgs]; const nodeOptions = process.env.NODE_OPTIONS ?? ""; const nextNodeOptions = WARNING_SUPPRESSION_FLAGS.reduce( (acc, flag) => (acc.includes(flag) ? acc : `${acc} ${flag}`.trim()), @@ -168,8 +177,16 @@ process.on("SIGTERM", () => shutdown("SIGTERM")); if (passthroughArgs.length > 0) { const maxWorkers = maxWorkersForRun("unit"); const args = maxWorkers - ? ["vitest", "run", "--maxWorkers", String(maxWorkers), ...windowsCiArgs, ...passthroughArgs] - : ["vitest", "run", ...windowsCiArgs, ...passthroughArgs]; + ? [ + "vitest", + "run", + "--maxWorkers", + String(maxWorkers), + ...silentArgs, + ...windowsCiArgs, + ...passthroughArgs, + ] + : ["vitest", "run", ...silentArgs, ...windowsCiArgs, ...passthroughArgs]; const nodeOptions = process.env.NODE_OPTIONS ?? ""; const nextNodeOptions = WARNING_SUPPRESSION_FLAGS.reduce( (acc, flag) => (acc.includes(flag) ? acc : `${acc} ${flag}`.trim()), diff --git a/src/commands/onboard-non-interactive.provider-auth.test.ts b/src/commands/onboard-non-interactive.provider-auth.test.ts index aeb64ff777..6a7f1a94f2 100644 --- a/src/commands/onboard-non-interactive.provider-auth.test.ts +++ b/src/commands/onboard-non-interactive.provider-auth.test.ts @@ -1,6 +1,7 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; +import { setTimeout as delay } from "node:timers/promises"; import { describe, expect, it, vi } from "vitest"; import { OPENAI_DEFAULT_MODEL } from "./openai-model-default.js"; @@ -29,6 +30,22 @@ type OnboardEnv = { runtime: RuntimeMock; }; +async function removeDirWithRetry(dir: string): Promise { + for (let attempt = 0; attempt < 5; attempt += 1) { + try { + await fs.rm(dir, { recursive: true, force: true }); + return; + } catch (error) { + const code = (error as NodeJS.ErrnoException).code; + const isTransient = code === "ENOTEMPTY" || code === "EBUSY" || code === "EPERM"; + if (!isTransient || attempt === 4) { + throw error; + } + await delay(25 * (attempt + 1)); + } + } +} + function captureEnv(): EnvSnapshot { return { home: process.env.HOME, @@ -102,7 +119,7 @@ async function withOnboardEnv( try { await run({ configPath, runtime }); } finally { - await fs.rm(tempHome, { recursive: true, force: true }); + await removeDirWithRetry(tempHome); restoreEnv(prev); } }