mirror of
https://github.com/moltbot/moltbot.git
synced 2026-05-13 15:47:28 +00:00
test: default vitest root projects to threads
This commit is contained in:
@@ -206,7 +206,7 @@
|
|||||||
- Agents MUST NOT modify baseline, inventory, ignore, snapshot, or expected-failure files to silence failing checks without explicit approval in this chat.
|
- Agents MUST NOT modify baseline, inventory, ignore, snapshot, or expected-failure files to silence failing checks without explicit approval in this chat.
|
||||||
- For targeted/local debugging, use the native root-project entrypoint: `pnpm test <path-or-filter> [vitest args...]` (for example `pnpm test src/commands/onboard-search.test.ts -t "shows registered plugin providers"`); do not default to raw `pnpm vitest run ...` because it bypasses the repo's default config/profile/pool routing.
|
- For targeted/local debugging, use the native root-project entrypoint: `pnpm test <path-or-filter> [vitest args...]` (for example `pnpm test src/commands/onboard-search.test.ts -t "shows registered plugin providers"`); do not default to raw `pnpm vitest run ...` because it bypasses the repo's default config/profile/pool routing.
|
||||||
- Do not set test workers above 16; tried already.
|
- Do not set test workers above 16; tried already.
|
||||||
- Keep Vitest on `forks` only. Do not introduce or reintroduce any non-`forks` Vitest pool or alternate execution mode in configs, wrapper scripts, or default test commands without explicit approval in this chat. This includes `threads`, `vmThreads`, `vmForks`, and any future/nonstandard pool variant.
|
- Vitest now defaults to native root-project `threads`, with hard `forks` exceptions for `gateway`, `agents`, and `commands`. Keep new pool changes explicit and justified; use `OPENCLAW_VITEST_POOL=forks` for full local fork debugging.
|
||||||
- If local Vitest runs cause memory pressure, the default worker budget now derives from host capabilities (CPU, memory band, current load). For a conservative explicit override during land/gate runs, use `OPENCLAW_VITEST_MAX_WORKERS=1 pnpm test`.
|
- If local Vitest runs cause memory pressure, the default worker budget now derives from host capabilities (CPU, memory band, current load). For a conservative explicit override during land/gate runs, use `OPENCLAW_VITEST_MAX_WORKERS=1 pnpm test`.
|
||||||
- Live tests (real keys): `OPENCLAW_LIVE_TEST=1 pnpm test:live` (OpenClaw-only) or `LIVE=1 pnpm test:live` (includes provider live tests). Docker: `pnpm test:docker:live-models`, `pnpm test:docker:live-gateway`. Onboarding Docker E2E: `pnpm test:docker:onboard`.
|
- Live tests (real keys): `OPENCLAW_LIVE_TEST=1 pnpm test:live` (OpenClaw-only) or `LIVE=1 pnpm test:live` (includes provider live tests). Docker: `pnpm test:docker:live-models`, `pnpm test:docker:live-gateway`. Onboarding Docker E2E: `pnpm test:docker:onboard`.
|
||||||
- `pnpm test:live` defaults quiet now. Keep `[live]` progress; suppress profile/gateway chatter. Full logs: `OPENCLAW_LIVE_TEST_QUIET=0 pnpm test:live`.
|
- `pnpm test:live` defaults quiet now. Keep `[live]` progress; suppress profile/gateway chatter. Full logs: `OPENCLAW_LIVE_TEST_QUIET=0 pnpm test:live`.
|
||||||
|
|||||||
@@ -71,13 +71,14 @@ Think of the suites as “increasing realism” (and increasing flakiness/cost):
|
|||||||
through the real `run.ts` / `compact.ts` paths; helper-only tests are not a
|
through the real `run.ts` / `compact.ts` paths; helper-only tests are not a
|
||||||
sufficient substitute for those integration paths.
|
sufficient substitute for those integration paths.
|
||||||
- Pool note:
|
- Pool note:
|
||||||
- Base Vitest config still defaults to `forks`.
|
- Base Vitest config now defaults to `threads`.
|
||||||
- Unit and boundary projects stay on `forks`.
|
- Hard thread exceptions stay on `forks`: `gateway`, `agents`, and `commands`.
|
||||||
- Channel, extension, and gateway configs also stay on `forks`.
|
- The root UI lane now mirrors the dedicated UI setup more closely: `jsdom`, isolated files, and the standard Vitest runner.
|
||||||
- Unit, channel, and extension configs default to `isolate: false` for faster file startup.
|
- Unit, channel, and extension configs default to `isolate: false` for faster file startup.
|
||||||
- `pnpm test` inherits the isolation defaults from the root `vitest.config.ts` projects config.
|
- `pnpm test` inherits the isolation defaults from the root `vitest.config.ts` projects config.
|
||||||
- Opt back into unit-file isolation with `OPENCLAW_TEST_ISOLATE=1 pnpm test`.
|
- Opt back into unit-file isolation with `OPENCLAW_TEST_ISOLATE=1 pnpm test`.
|
||||||
- `OPENCLAW_TEST_NO_ISOLATE=0` or `OPENCLAW_TEST_NO_ISOLATE=false` also force isolated runs.
|
- `OPENCLAW_TEST_NO_ISOLATE=0` or `OPENCLAW_TEST_NO_ISOLATE=false` also force isolated runs.
|
||||||
|
- `OPENCLAW_VITEST_POOL=forks` (or `OPENCLAW_TEST_POOL=forks`) forces a full local fork run when debugging thread-sensitive behavior.
|
||||||
- Fast-local iteration note:
|
- Fast-local iteration note:
|
||||||
- `pnpm test:changed` runs the native projects config with `--changed origin/main`.
|
- `pnpm test:changed` runs the native projects config with `--changed origin/main`.
|
||||||
- `pnpm test:max` and `pnpm test:changed:max` keep the same native projects config, just with a higher worker cap.
|
- `pnpm test:max` and `pnpm test:changed:max` keep the same native projects config, just with a higher worker cap.
|
||||||
|
|||||||
@@ -14,7 +14,9 @@ title: "Tests"
|
|||||||
- `pnpm test:coverage:changed`: Runs unit coverage only for files changed since `origin/main`.
|
- `pnpm test:coverage:changed`: Runs unit coverage only for files changed since `origin/main`.
|
||||||
- `pnpm test:changed`: runs the native Vitest projects config with `--changed origin/main`. The base config treats the projects/config files as `forceRerunTriggers` so wiring changes still rerun broadly when needed.
|
- `pnpm test:changed`: runs the native Vitest projects config with `--changed origin/main`. The base config treats the projects/config files as `forceRerunTriggers` so wiring changes still rerun broadly when needed.
|
||||||
- `pnpm test`: runs the native Vitest root projects config directly. File filters work natively across the configured projects.
|
- `pnpm test`: runs the native Vitest root projects config directly. File filters work natively across the configured projects.
|
||||||
- Unit, channel, and extension configs default to `pool: "forks"`.
|
- Base Vitest config now defaults to `pool: "threads"`.
|
||||||
|
- Hard thread exceptions stay on `pool: "forks"` for stability: `gateway`, `agents`, and `commands`.
|
||||||
|
- Use `OPENCLAW_VITEST_POOL=forks pnpm test` (or `OPENCLAW_TEST_POOL=forks pnpm test`) when you want a full local fork run for debugging.
|
||||||
- `pnpm test:channels` runs `vitest.channels.config.ts`.
|
- `pnpm test:channels` runs `vitest.channels.config.ts`.
|
||||||
- `pnpm test:extensions` runs `vitest.extensions.config.ts`.
|
- `pnpm test:extensions` runs `vitest.extensions.config.ts`.
|
||||||
- `pnpm test:extensions`: runs extension/plugin suites.
|
- `pnpm test:extensions`: runs extension/plugin suites.
|
||||||
@@ -40,6 +42,7 @@ For local PR land/gate checks, run:
|
|||||||
If `pnpm test` flakes on a loaded host, rerun once before treating it as a regression, then isolate with `pnpm test <path/to/test>`. For memory-constrained hosts, use:
|
If `pnpm test` flakes on a loaded host, rerun once before treating it as a regression, then isolate with `pnpm test <path/to/test>`. For memory-constrained hosts, use:
|
||||||
|
|
||||||
- `OPENCLAW_VITEST_MAX_WORKERS=1 pnpm test`
|
- `OPENCLAW_VITEST_MAX_WORKERS=1 pnpm test`
|
||||||
|
- `OPENCLAW_VITEST_POOL=forks OPENCLAW_VITEST_MAX_WORKERS=1 pnpm test`
|
||||||
- `OPENCLAW_VITEST_FS_MODULE_CACHE_PATH=/tmp/openclaw-vitest-cache pnpm test:changed`
|
- `OPENCLAW_VITEST_FS_MODULE_CACHE_PATH=/tmp/openclaw-vitest-cache pnpm test:changed`
|
||||||
|
|
||||||
## Model latency bench (local keys)
|
## Model latency bench (local keys)
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import { readFileSync } from "node:fs";
|
import { readFileSync } from "node:fs";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import baseConfig, { resolveLocalVitestMaxWorkers } from "../../vitest.config.ts";
|
import baseConfig, {
|
||||||
|
resolveDefaultVitestPool,
|
||||||
|
resolveLocalVitestMaxWorkers,
|
||||||
|
} from "../../vitest.config.ts";
|
||||||
|
|
||||||
describe("resolveLocalVitestMaxWorkers", () => {
|
describe("resolveLocalVitestMaxWorkers", () => {
|
||||||
it("uses a moderate local worker cap on larger hosts", () => {
|
it("uses a moderate local worker cap on larger hosts", () => {
|
||||||
@@ -89,6 +92,19 @@ describe("resolveLocalVitestMaxWorkers", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("base vitest config", () => {
|
describe("base vitest config", () => {
|
||||||
|
it("defaults the base pool to threads", () => {
|
||||||
|
expect(resolveDefaultVitestPool()).toBe("threads");
|
||||||
|
expect(baseConfig.test?.pool).toBe("threads");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("lets OPENCLAW_VITEST_POOL force forks for local debugging", () => {
|
||||||
|
expect(
|
||||||
|
resolveDefaultVitestPool({
|
||||||
|
OPENCLAW_VITEST_POOL: "forks",
|
||||||
|
}),
|
||||||
|
).toBe("forks");
|
||||||
|
});
|
||||||
|
|
||||||
it("excludes fixture trees from test collection", () => {
|
it("excludes fixture trees from test collection", () => {
|
||||||
expect(baseConfig.test?.exclude).toContain("test/fixtures/**");
|
expect(baseConfig.test?.exclude).toContain("test/fixtures/**");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -146,7 +146,9 @@ describe("scripts/test-extension.mjs", () => {
|
|||||||
resolveExtensionTestPlan({ cwd: process.cwd(), targetArg: extensionId }).hasTests,
|
resolveExtensionTestPlan({ cwd: process.cwd(), targetArg: extensionId }).hasTests,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(uniqueAssigned.toSorted((left, right) => left.localeCompare(right))).toEqual(expected);
|
expect(uniqueAssigned.toSorted((left, right) => left.localeCompare(right))).toEqual(
|
||||||
|
expected.toSorted((left, right) => left.localeCompare(right)),
|
||||||
|
);
|
||||||
expect(assigned).toHaveLength(expected.length);
|
expect(assigned).toHaveLength(expected.length);
|
||||||
|
|
||||||
const totals = shards.map((shard) => shard.testFileCount);
|
const totals = shards.map((shard) => shard.testFileCount);
|
||||||
|
|||||||
@@ -1,8 +1,26 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { createAgentsVitestConfig } from "../vitest.agents.config.ts";
|
||||||
|
import { createCommandsVitestConfig } from "../vitest.commands.config.ts";
|
||||||
import baseConfig, { rootVitestProjects } from "../vitest.config.ts";
|
import baseConfig, { rootVitestProjects } from "../vitest.config.ts";
|
||||||
|
import { createGatewayVitestConfig } from "../vitest.gateway.config.ts";
|
||||||
|
import { createUiVitestConfig } from "../vitest.ui.config.ts";
|
||||||
|
|
||||||
describe("projects vitest config", () => {
|
describe("projects vitest config", () => {
|
||||||
it("defines the native root project list for all non-live Vitest lanes", () => {
|
it("defines the native root project list for all non-live Vitest lanes", () => {
|
||||||
expect(baseConfig.test?.projects).toEqual([...rootVitestProjects]);
|
expect(baseConfig.test?.projects).toEqual([...rootVitestProjects]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("keeps hard thread exceptions on forks", () => {
|
||||||
|
expect(createGatewayVitestConfig().test.pool).toBe("forks");
|
||||||
|
expect(createAgentsVitestConfig().test.pool).toBe("forks");
|
||||||
|
expect(createCommandsVitestConfig().test.pool).toBe("forks");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps the root ui lane aligned with the jsdom ui project setup", () => {
|
||||||
|
const config = createUiVitestConfig();
|
||||||
|
expect(config.test.environment).toBe("jsdom");
|
||||||
|
expect(config.test.isolate).toBe(true);
|
||||||
|
expect(config.test.runner).toBeUndefined();
|
||||||
|
expect(config.test.setupFiles).toContain("ui/src/test-helpers/lit-warnings.setup.ts");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ export function createAgentsVitestConfig(env?: Record<string, string | undefined
|
|||||||
dir: "src/agents",
|
dir: "src/agents",
|
||||||
env,
|
env,
|
||||||
name: "agents",
|
name: "agents",
|
||||||
|
pool: "forks",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ export function createCommandsVitestConfig(env?: Record<string, string | undefin
|
|||||||
dir: "src/commands",
|
dir: "src/commands",
|
||||||
env,
|
env,
|
||||||
name: "commands",
|
name: "commands",
|
||||||
|
pool: "forks",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
import { defineConfig } from "vitest/config";
|
import { defineConfig } from "vitest/config";
|
||||||
import { resolveLocalVitestMaxWorkers, sharedVitestConfig } from "./vitest.shared.config.ts";
|
import {
|
||||||
|
resolveDefaultVitestPool,
|
||||||
|
resolveLocalVitestMaxWorkers,
|
||||||
|
sharedVitestConfig,
|
||||||
|
} from "./vitest.shared.config.ts";
|
||||||
|
|
||||||
export { resolveLocalVitestMaxWorkers };
|
export { resolveDefaultVitestPool, resolveLocalVitestMaxWorkers };
|
||||||
|
|
||||||
export const rootVitestProjects = [
|
export const rootVitestProjects = [
|
||||||
"vitest.unit.config.ts",
|
"vitest.unit.config.ts",
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ export function createGatewayVitestConfig(env?: Record<string, string | undefine
|
|||||||
dir: "src/gateway",
|
dir: "src/gateway",
|
||||||
env,
|
env,
|
||||||
name: "gateway",
|
name: "gateway",
|
||||||
|
pool: "forks",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,11 +42,14 @@ export function createScopedVitestConfig(
|
|||||||
options?: {
|
options?: {
|
||||||
dir?: string;
|
dir?: string;
|
||||||
env?: Record<string, string | undefined>;
|
env?: Record<string, string | undefined>;
|
||||||
|
environment?: string;
|
||||||
exclude?: string[];
|
exclude?: string[];
|
||||||
|
isolate?: boolean;
|
||||||
name?: string;
|
name?: string;
|
||||||
pool?: "threads" | "forks";
|
pool?: "threads" | "forks";
|
||||||
passWithNoTests?: boolean;
|
passWithNoTests?: boolean;
|
||||||
setupFiles?: string[];
|
setupFiles?: string[];
|
||||||
|
useNonIsolatedRunner?: boolean;
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
const base = sharedVitestConfig as Record<string, unknown>;
|
const base = sharedVitestConfig as Record<string, unknown>;
|
||||||
@@ -56,16 +59,26 @@ export function createScopedVitestConfig(
|
|||||||
[...(baseTest.exclude ?? []), ...(options?.exclude ?? [])],
|
[...(baseTest.exclude ?? []), ...(options?.exclude ?? [])],
|
||||||
scopedDir,
|
scopedDir,
|
||||||
);
|
);
|
||||||
const isolate = resolveVitestIsolation(options?.env);
|
const isolate = options?.isolate ?? resolveVitestIsolation(options?.env);
|
||||||
|
const setupFiles = [
|
||||||
|
...new Set([
|
||||||
|
...(baseTest.setupFiles ?? []),
|
||||||
|
...(options?.setupFiles ?? []),
|
||||||
|
"test/setup-openclaw-runtime.ts",
|
||||||
|
]),
|
||||||
|
];
|
||||||
|
|
||||||
return defineConfig({
|
return defineConfig({
|
||||||
...base,
|
...base,
|
||||||
test: {
|
test: {
|
||||||
...baseTest,
|
...baseTest,
|
||||||
...(options?.name ? { name: options.name } : {}),
|
...(options?.name ? { name: options.name } : {}),
|
||||||
|
...(options?.environment ? { environment: options.environment } : {}),
|
||||||
isolate,
|
isolate,
|
||||||
runner: "./test/non-isolated-runner.ts",
|
...(options?.useNonIsolatedRunner === false
|
||||||
setupFiles: [...new Set([...(baseTest.setupFiles ?? []), "test/setup-openclaw-runtime.ts"])],
|
? {}
|
||||||
|
: { runner: "./test/non-isolated-runner.ts" }),
|
||||||
|
setupFiles,
|
||||||
...(scopedDir ? { dir: scopedDir } : {}),
|
...(scopedDir ? { dir: scopedDir } : {}),
|
||||||
include: relativizeScopedPatterns(include, scopedDir),
|
include: relativizeScopedPatterns(include, scopedDir),
|
||||||
exclude,
|
exclude,
|
||||||
@@ -73,7 +86,6 @@ export function createScopedVitestConfig(
|
|||||||
...(options?.passWithNoTests !== undefined
|
...(options?.passWithNoTests !== undefined
|
||||||
? { passWithNoTests: options.passWithNoTests }
|
? { passWithNoTests: options.passWithNoTests }
|
||||||
: {}),
|
: {}),
|
||||||
...(options?.setupFiles ? { setupFiles: options.setupFiles } : {}),
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ type VitestHostInfo = {
|
|||||||
totalMemoryBytes?: number;
|
totalMemoryBytes?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type OpenClawVitestPool = "threads" | "forks";
|
||||||
|
|
||||||
function detectVitestHostInfo(): Required<VitestHostInfo> {
|
function detectVitestHostInfo(): Required<VitestHostInfo> {
|
||||||
return {
|
return {
|
||||||
cpuCount:
|
cpuCount:
|
||||||
@@ -68,11 +70,22 @@ export function resolveLocalVitestMaxWorkers(
|
|||||||
return clamp(inferred, 1, 16);
|
return clamp(inferred, 1, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function resolveDefaultVitestPool(
|
||||||
|
env: Record<string, string | undefined> = process.env,
|
||||||
|
): OpenClawVitestPool {
|
||||||
|
const configuredPool = (env.OPENCLAW_VITEST_POOL ?? env.OPENCLAW_TEST_POOL)?.trim();
|
||||||
|
if (configuredPool === "threads" || configuredPool === "forks") {
|
||||||
|
return configuredPool;
|
||||||
|
}
|
||||||
|
return "threads";
|
||||||
|
}
|
||||||
|
|
||||||
const repoRoot = path.dirname(fileURLToPath(import.meta.url));
|
const repoRoot = path.dirname(fileURLToPath(import.meta.url));
|
||||||
const isCI = process.env.CI === "true" || process.env.GITHUB_ACTIONS === "true";
|
const isCI = process.env.CI === "true" || process.env.GITHUB_ACTIONS === "true";
|
||||||
const isWindows = process.platform === "win32";
|
const isWindows = process.platform === "win32";
|
||||||
const localWorkers = resolveLocalVitestMaxWorkers();
|
const localWorkers = resolveLocalVitestMaxWorkers();
|
||||||
const ciWorkers = isWindows ? 2 : 3;
|
const ciWorkers = isWindows ? 2 : 3;
|
||||||
|
const defaultPool = resolveDefaultVitestPool();
|
||||||
|
|
||||||
export const sharedVitestConfig = {
|
export const sharedVitestConfig = {
|
||||||
resolve: {
|
resolve: {
|
||||||
@@ -96,7 +109,7 @@ export const sharedVitestConfig = {
|
|||||||
hookTimeout: isWindows ? 180_000 : 120_000,
|
hookTimeout: isWindows ? 180_000 : 120_000,
|
||||||
unstubEnvs: true,
|
unstubEnvs: true,
|
||||||
unstubGlobals: true,
|
unstubGlobals: true,
|
||||||
pool: "forks" as const,
|
pool: defaultPool,
|
||||||
maxWorkers: isCI ? ciWorkers : localWorkers,
|
maxWorkers: isCI ? ciWorkers : localWorkers,
|
||||||
forceRerunTriggers: [
|
forceRerunTriggers: [
|
||||||
"package.json",
|
"package.json",
|
||||||
|
|||||||
@@ -3,8 +3,12 @@ import { createScopedVitestConfig } from "./vitest.scoped-config.ts";
|
|||||||
export function createUiVitestConfig(env?: Record<string, string | undefined>) {
|
export function createUiVitestConfig(env?: Record<string, string | undefined>) {
|
||||||
return createScopedVitestConfig(["ui/src/ui/**/*.test.ts"], {
|
return createScopedVitestConfig(["ui/src/ui/**/*.test.ts"], {
|
||||||
dir: "ui/src/ui",
|
dir: "ui/src/ui",
|
||||||
|
environment: "jsdom",
|
||||||
env,
|
env,
|
||||||
|
isolate: true,
|
||||||
name: "ui",
|
name: "ui",
|
||||||
|
setupFiles: ["ui/src/test-helpers/lit-warnings.setup.ts"],
|
||||||
|
useNonIsolatedRunner: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user