mirror of
https://github.com/moltbot/moltbot.git
synced 2026-04-24 07:01:49 +00:00
perf(test): restore scoped vitest routing
This commit is contained in:
@@ -24,7 +24,7 @@ Most days:
|
||||
|
||||
- Full gate (expected before push): `pnpm build && pnpm check && pnpm test`
|
||||
- Faster local full-suite run on a roomy machine: `pnpm test:max`
|
||||
- Direct Vitest watch loop (modern projects config): `pnpm test:watch`
|
||||
- Direct Vitest watch loop: `pnpm test:watch`
|
||||
- Direct file targeting now routes extension/channel paths too: `pnpm test extensions/discord/src/monitor/message-handler.preflight.test.ts`
|
||||
|
||||
When you touch tests or want extra confidence:
|
||||
@@ -57,8 +57,9 @@ Think of the suites as “increasing realism” (and increasing flakiness/cost):
|
||||
- No real keys required
|
||||
- Should be fast and stable
|
||||
- Projects note:
|
||||
- `pnpm test`, `pnpm test:watch`, and `pnpm test:changed` all use the same native Vitest root `projects` config now.
|
||||
- Direct file filters route natively through the root project graph, so `pnpm test extensions/discord/src/monitor/message-handler.preflight.test.ts` works without a custom wrapper.
|
||||
- Untargeted `pnpm test` still uses the native Vitest root `projects` config.
|
||||
- `pnpm test`, `pnpm test:watch`, and `pnpm test:perf:imports` route explicit file/directory targets through scoped lanes first, so `pnpm test extensions/discord/src/monitor/message-handler.preflight.test.ts` avoids paying the full root project startup tax.
|
||||
- `pnpm test:changed` expands changed git paths into the same scoped lanes when the diff only touches routable source/test files; config/setup edits still fall back to the broad root-project rerun.
|
||||
- Embedded runner note:
|
||||
- When you change message-tool discovery inputs or compaction runtime context,
|
||||
keep both levels of coverage.
|
||||
@@ -77,8 +78,8 @@ Think of the suites as “increasing realism” (and increasing flakiness/cost):
|
||||
- `pnpm test` inherits the same `threads` + `isolate: false` defaults from the root `vitest.config.ts` projects config.
|
||||
- The shared `scripts/run-vitest.mjs` launcher now also adds `--no-maglev` for Vitest child Node processes by default to reduce V8 compile churn during big local runs. Set `OPENCLAW_VITEST_ENABLE_MAGLEV=1` if you need to compare against stock V8 behavior.
|
||||
- Fast-local iteration note:
|
||||
- `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:changed` routes through scoped lanes when the changed paths map cleanly to a smaller suite.
|
||||
- `pnpm test:max` and `pnpm test:changed:max` keep the same routing behavior, just with a higher worker cap.
|
||||
- Local worker auto-scaling is intentionally conservative now and also backs off when the host load average is already high, so multiple concurrent Vitest runs do less damage by default.
|
||||
- The base Vitest config marks the projects/config files as `forceRerunTriggers` so changed-mode reruns stay correct when test wiring changes.
|
||||
- The config keeps `OPENCLAW_VITEST_FS_MODULE_CACHE` enabled on supported hosts; set `OPENCLAW_VITEST_FS_MODULE_CACHE_PATH=/abs/path` if you want one explicit cache location for direct profiling.
|
||||
|
||||
@@ -12,13 +12,13 @@ title: "Tests"
|
||||
- `pnpm test:force`: Kills any lingering gateway process holding the default control port, then runs the full Vitest suite with an isolated gateway port so server tests don’t collide with a running instance. Use this when a prior gateway run left port 18789 occupied.
|
||||
- `pnpm test:coverage`: Runs the unit suite with V8 coverage (via `vitest.unit.config.ts`). Global thresholds are 70% lines/branches/functions/statements. Coverage excludes integration-heavy entrypoints (CLI wiring, gateway/telegram bridges, webchat static server) to keep the target focused on unit-testable logic.
|
||||
- `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`: runs the native Vitest root projects config directly. File filters work natively across the configured projects.
|
||||
- `pnpm test:changed`: expands changed git paths into scoped Vitest lanes when the diff only touches routable source/test files. Config/setup changes still fall back to the native root projects run so wiring edits rerun broadly when needed.
|
||||
- `pnpm test`: routes explicit file/directory targets through scoped Vitest lanes, but still falls back to the native root projects run when you do a full untargeted sweep.
|
||||
- Base Vitest config now defaults to `pool: "threads"` and `isolate: false`, with the shared non-isolated runner enabled across the repo configs.
|
||||
- `pnpm test:channels` runs `vitest.channels.config.ts`.
|
||||
- `pnpm test:extensions` runs `vitest.extensions.config.ts`.
|
||||
- `pnpm test:extensions`: runs extension/plugin suites.
|
||||
- `pnpm test:perf:imports`: enables Vitest import-duration + import-breakdown reporting for the native root projects run.
|
||||
- `pnpm test:perf:imports`: enables Vitest import-duration + import-breakdown reporting, while still using scoped lane routing for explicit file/directory targets.
|
||||
- `pnpm test:perf:imports:changed`: same import profiling, but only for files changed since `origin/main`.
|
||||
- `pnpm test:perf:profile:main`: writes a CPU profile for the Vitest main thread (`.artifacts/vitest-main-profile`).
|
||||
- `pnpm test:perf:profile:runner`: writes CPU + heap profiles for the unit runner (`.artifacts/vitest-runner-profile`).
|
||||
|
||||
14
package.json
14
package.json
@@ -1120,13 +1120,13 @@
|
||||
"runtime-sidecars:gen": "node --import tsx scripts/generate-runtime-sidecar-paths-baseline.ts --write",
|
||||
"stage:bundled-plugin-runtime-deps": "node scripts/stage-bundled-plugin-runtime-deps.mjs",
|
||||
"start": "node scripts/run-node.mjs",
|
||||
"test": "node scripts/run-vitest.mjs run --config vitest.config.ts",
|
||||
"test": "node scripts/test-projects.mjs",
|
||||
"test:all": "pnpm lint && pnpm build && pnpm test && pnpm test:e2e && pnpm test:live && pnpm test:docker:all",
|
||||
"test:auth:compat": "node scripts/run-vitest.mjs run --config vitest.gateway.config.ts src/gateway/server.auth.compat-baseline.test.ts src/gateway/client.test.ts src/gateway/reconnect-gating.test.ts src/gateway/protocol/connect-error-details.test.ts",
|
||||
"test:build:singleton": "node scripts/test-built-plugin-singleton.mjs",
|
||||
"test:bundled": "node scripts/run-vitest.mjs run --config vitest.bundled.config.ts",
|
||||
"test:changed": "node scripts/run-vitest.mjs run --config vitest.config.ts --changed origin/main",
|
||||
"test:changed:max": "OPENCLAW_VITEST_MAX_WORKERS=8 node scripts/run-vitest.mjs run --config vitest.config.ts --changed origin/main",
|
||||
"test:changed": "node scripts/test-projects.mjs --changed origin/main",
|
||||
"test:changed:max": "OPENCLAW_VITEST_MAX_WORKERS=8 node scripts/test-projects.mjs --changed origin/main",
|
||||
"test:channels": "node scripts/run-vitest.mjs run --config vitest.channels.config.ts",
|
||||
"test:contracts": "pnpm test:contracts:channels && pnpm test:contracts:plugins",
|
||||
"test:contracts:channels": "node scripts/run-vitest.mjs run --config vitest.contracts.config.ts --maxWorkers=1 src/channels/plugins/contracts",
|
||||
@@ -1165,15 +1165,15 @@
|
||||
"test:live:cache": "bun scripts/check-live-cache.ts",
|
||||
"test:live:gateway-profiles": "node scripts/test-live.mjs -- src/gateway/gateway-models.profiles.live.test.ts",
|
||||
"test:live:models-profiles": "node scripts/test-live.mjs -- src/agents/models.profiles.live.test.ts",
|
||||
"test:max": "OPENCLAW_VITEST_MAX_WORKERS=8 node scripts/run-vitest.mjs run --config vitest.config.ts",
|
||||
"test:max": "OPENCLAW_VITEST_MAX_WORKERS=8 node scripts/test-projects.mjs",
|
||||
"test:parallels:linux": "bash scripts/e2e/parallels-linux-smoke.sh",
|
||||
"test:parallels:macos": "bash scripts/e2e/parallels-macos-smoke.sh",
|
||||
"test:parallels:npm-update": "bash scripts/e2e/parallels-npm-update-smoke.sh",
|
||||
"test:parallels:windows": "bash scripts/e2e/parallels-windows-smoke.sh",
|
||||
"test:perf:budget": "node scripts/test-perf-budget.mjs",
|
||||
"test:perf:hotspots": "node scripts/test-hotspots.mjs",
|
||||
"test:perf:imports": "OPENCLAW_VITEST_IMPORT_DURATIONS=1 OPENCLAW_VITEST_PRINT_IMPORT_BREAKDOWN=1 node scripts/run-vitest.mjs run --config vitest.config.ts",
|
||||
"test:perf:imports:changed": "OPENCLAW_VITEST_IMPORT_DURATIONS=1 OPENCLAW_VITEST_PRINT_IMPORT_BREAKDOWN=1 node scripts/run-vitest.mjs run --config vitest.config.ts --changed origin/main",
|
||||
"test:perf:imports": "OPENCLAW_VITEST_IMPORT_DURATIONS=1 OPENCLAW_VITEST_PRINT_IMPORT_BREAKDOWN=1 node scripts/test-projects.mjs",
|
||||
"test:perf:imports:changed": "OPENCLAW_VITEST_IMPORT_DURATIONS=1 OPENCLAW_VITEST_PRINT_IMPORT_BREAKDOWN=1 node scripts/test-projects.mjs --changed origin/main",
|
||||
"test:perf:profile:main": "node scripts/run-vitest-profile.mjs main",
|
||||
"test:perf:profile:runner": "node scripts/run-vitest-profile.mjs runner",
|
||||
"test:sectriage": "node scripts/run-vitest.mjs run --config vitest.gateway.config.ts && node scripts/run-vitest.mjs run --config vitest.unit.config.ts --exclude src/daemon/launchd.integration.test.ts --exclude src/process/exec.test.ts",
|
||||
@@ -1186,7 +1186,7 @@
|
||||
"test:startup:memory": "node scripts/check-cli-startup-memory.mjs",
|
||||
"test:ui": "pnpm ui:i18n:check && pnpm lint:ui:no-raw-window-open && pnpm --dir ui test",
|
||||
"test:voicecall:closedloop": "node scripts/test-voicecall-closedloop.mjs",
|
||||
"test:watch": "node scripts/run-vitest.mjs --config vitest.config.ts",
|
||||
"test:watch": "node scripts/test-projects.mjs --watch",
|
||||
"tool-display:check": "node --import tsx scripts/tool-display.ts --check",
|
||||
"tool-display:write": "node --import tsx scripts/tool-display.ts --write",
|
||||
"ts-topology": "node --import tsx scripts/ts-topology.ts",
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import fs from "node:fs";
|
||||
import { acquireLocalHeavyCheckLockSync } from "./lib/local-heavy-check-runtime.mjs";
|
||||
import { spawnPnpmRunner } from "./pnpm-runner.mjs";
|
||||
import { resolveVitestCliEntry, resolveVitestNodeArgs } from "./run-vitest.mjs";
|
||||
import {
|
||||
createVitestRunSpecs,
|
||||
parseTestProjectsArgs,
|
||||
resolveChangedTargetArgs,
|
||||
writeVitestIncludeFile,
|
||||
} from "./test-projects.test-support.mjs";
|
||||
|
||||
@@ -66,7 +68,9 @@ function createRootVitestRunSpec(args) {
|
||||
includePatterns: null,
|
||||
pnpmArgs: [
|
||||
"exec",
|
||||
"vitest",
|
||||
"node",
|
||||
...resolveVitestNodeArgs(process.env),
|
||||
resolveVitestCliEntry(),
|
||||
...(watchMode ? [] : ["run"]),
|
||||
"--config",
|
||||
"vitest.config.ts",
|
||||
@@ -79,8 +83,10 @@ function createRootVitestRunSpec(args) {
|
||||
async function main() {
|
||||
const args = process.argv.slice(2);
|
||||
const { targetArgs } = parseTestProjectsArgs(args, process.cwd());
|
||||
const changedTargetArgs =
|
||||
targetArgs.length === 0 ? resolveChangedTargetArgs(args, process.cwd()) : null;
|
||||
const runSpecs =
|
||||
targetArgs.length === 0
|
||||
targetArgs.length === 0 && changedTargetArgs === null
|
||||
? [createRootVitestRunSpec(args)]
|
||||
: createVitestRunSpecs(args, {
|
||||
baseEnv: process.env,
|
||||
|
||||
@@ -23,7 +23,17 @@ export function parseTestProjectsArgs(
|
||||
watchMode: boolean;
|
||||
};
|
||||
|
||||
export function buildVitestRunPlans(args: string[], cwd?: string): VitestRunPlan[];
|
||||
export function buildVitestRunPlans(
|
||||
args: string[],
|
||||
cwd?: string,
|
||||
listChangedPaths?: (baseRef: string, cwd: string) => string[],
|
||||
): VitestRunPlan[];
|
||||
|
||||
export function resolveChangedTargetArgs(
|
||||
args: string[],
|
||||
cwd?: string,
|
||||
listChangedPaths?: (baseRef: string, cwd: string) => string[],
|
||||
): string[] | null;
|
||||
|
||||
export function createVitestRunSpecs(
|
||||
args: string[],
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { execFileSync } from "node:child_process";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
@@ -18,6 +19,7 @@ import { isVoiceCallExtensionRoot } from "../vitest.extension-voice-call-paths.m
|
||||
import { isWhatsAppExtensionRoot } from "../vitest.extension-whatsapp-paths.mjs";
|
||||
import { isZaloExtensionRoot } from "../vitest.extension-zalo-paths.mjs";
|
||||
import { isBoundaryTestFile, isBundledPluginDependentUnitTestFile } from "../vitest.unit-paths.mjs";
|
||||
import { resolveVitestCliEntry, resolveVitestNodeArgs } from "./run-vitest.mjs";
|
||||
|
||||
const DEFAULT_VITEST_CONFIG = "vitest.unit.config.ts";
|
||||
const AGENTS_VITEST_CONFIG = "vitest.agents.config.ts";
|
||||
@@ -68,6 +70,15 @@ const UI_VITEST_CONFIG = "vitest.ui.config.ts";
|
||||
const UTILS_VITEST_CONFIG = "vitest.utils.config.ts";
|
||||
const WIZARD_VITEST_CONFIG = "vitest.wizard.config.ts";
|
||||
const INCLUDE_FILE_ENV_KEY = "OPENCLAW_VITEST_INCLUDE_FILE";
|
||||
const CHANGED_ARGS_PATTERN = /^--changed(?:=(.+))?$/u;
|
||||
const BROAD_CHANGED_RERUN_PATTERNS = [
|
||||
/^package\.json$/u,
|
||||
/^pnpm-lock\.yaml$/u,
|
||||
/^test\/setup(?:\.shared|\.extensions|-openclaw-runtime)?\.ts$/u,
|
||||
/^vitest(?:\..+)?\.(?:config\.ts|paths\.mjs)$/u,
|
||||
/^scripts\/run-vitest\.mjs$/u,
|
||||
/^scripts\/test-projects(?:\.test-support)?\.mjs$/u,
|
||||
];
|
||||
|
||||
function normalizePathPattern(value) {
|
||||
return value.replaceAll("\\", "/");
|
||||
@@ -93,6 +104,10 @@ function isFileLikeTarget(arg) {
|
||||
return /\.(?:test|spec)\.[cm]?[jt]sx?$/u.test(arg);
|
||||
}
|
||||
|
||||
function isLikelyFileTarget(arg) {
|
||||
return /(?:^|\/)[^/]+\.[A-Za-z0-9]+$/u.test(arg);
|
||||
}
|
||||
|
||||
function isPathLikeTargetArg(arg, cwd) {
|
||||
if (!arg || arg === "--" || arg.startsWith("-")) {
|
||||
return false;
|
||||
@@ -113,13 +128,86 @@ function toScopedIncludePattern(arg, cwd) {
|
||||
if (isGlobTarget(relative) || isFileLikeTarget(relative)) {
|
||||
return relative;
|
||||
}
|
||||
if (isExistingFileTarget(arg, cwd)) {
|
||||
if (isExistingFileTarget(arg, cwd) || isLikelyFileTarget(relative)) {
|
||||
const directory = normalizePathPattern(path.posix.dirname(relative));
|
||||
return directory === "." ? "**/*.test.ts" : `${directory}/**/*.test.ts`;
|
||||
}
|
||||
return `${relative.replace(/\/+$/u, "")}/**/*.test.ts`;
|
||||
}
|
||||
|
||||
function listChangedPathsFromGit(baseRef, cwd) {
|
||||
return execFileSync("git", ["diff", "--name-only", `${baseRef}...HEAD`], {
|
||||
cwd,
|
||||
encoding: "utf8",
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
})
|
||||
.split("\n")
|
||||
.map((line) => normalizePathPattern(line.trim()))
|
||||
.filter((line) => line.length > 0);
|
||||
}
|
||||
|
||||
function extractChangedBaseRef(args) {
|
||||
for (let index = 0; index < args.length; index += 1) {
|
||||
const arg = args[index];
|
||||
const match = arg.match(CHANGED_ARGS_PATTERN);
|
||||
if (!match) {
|
||||
continue;
|
||||
}
|
||||
if (match[1]) {
|
||||
return match[1];
|
||||
}
|
||||
const nextArg = args[index + 1];
|
||||
return nextArg && nextArg !== "--" && !nextArg.startsWith("-") ? nextArg : "HEAD";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function stripChangedArgs(args) {
|
||||
const strippedArgs = [];
|
||||
for (let index = 0; index < args.length; index += 1) {
|
||||
const arg = args[index];
|
||||
const match = arg.match(CHANGED_ARGS_PATTERN);
|
||||
if (!match) {
|
||||
strippedArgs.push(arg);
|
||||
continue;
|
||||
}
|
||||
if (!match[1]) {
|
||||
const nextArg = args[index + 1];
|
||||
if (nextArg && nextArg !== "--" && !nextArg.startsWith("-")) {
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return strippedArgs;
|
||||
}
|
||||
|
||||
function shouldKeepBroadChangedRun(changedPaths) {
|
||||
return changedPaths.some((changedPath) =>
|
||||
BROAD_CHANGED_RERUN_PATTERNS.some((pattern) => pattern.test(changedPath)),
|
||||
);
|
||||
}
|
||||
|
||||
function isRoutableChangedTarget(changedPath) {
|
||||
return /^(?:src|test|extensions|ui|packages|apps)(?:\/|$)/u.test(changedPath);
|
||||
}
|
||||
|
||||
export function resolveChangedTargetArgs(
|
||||
args,
|
||||
cwd = process.cwd(),
|
||||
listChangedPaths = listChangedPathsFromGit,
|
||||
) {
|
||||
const baseRef = extractChangedBaseRef(args);
|
||||
if (!baseRef) {
|
||||
return null;
|
||||
}
|
||||
const changedPaths = listChangedPaths(baseRef, cwd);
|
||||
if (changedPaths.length === 0 || shouldKeepBroadChangedRun(changedPaths)) {
|
||||
return null;
|
||||
}
|
||||
const routablePaths = changedPaths.filter(isRoutableChangedTarget);
|
||||
return routablePaths.length > 0 ? [...new Set(routablePaths)] : null;
|
||||
}
|
||||
|
||||
function classifyTarget(arg, cwd) {
|
||||
const relative = toRepoRelativeTarget(arg, cwd);
|
||||
if (relative.endsWith(".e2e.test.ts")) {
|
||||
@@ -278,7 +366,9 @@ function classifyTarget(arg, cwd) {
|
||||
function createVitestArgs(params) {
|
||||
return [
|
||||
"exec",
|
||||
"vitest",
|
||||
"node",
|
||||
...resolveVitestNodeArgs(params.env),
|
||||
resolveVitestCliEntry(),
|
||||
...(params.watchMode ? [] : ["run"]),
|
||||
"--config",
|
||||
params.config,
|
||||
@@ -308,13 +398,21 @@ export function parseTestProjectsArgs(args, cwd = process.cwd()) {
|
||||
return { forwardedArgs, targetArgs, watchMode };
|
||||
}
|
||||
|
||||
export function buildVitestRunPlans(args, cwd = process.cwd()) {
|
||||
export function buildVitestRunPlans(
|
||||
args,
|
||||
cwd = process.cwd(),
|
||||
listChangedPaths = listChangedPathsFromGit,
|
||||
) {
|
||||
const { forwardedArgs, targetArgs, watchMode } = parseTestProjectsArgs(args, cwd);
|
||||
if (targetArgs.length === 0) {
|
||||
const changedTargetArgs =
|
||||
targetArgs.length === 0 ? resolveChangedTargetArgs(args, cwd, listChangedPaths) : null;
|
||||
const activeTargetArgs = changedTargetArgs ?? targetArgs;
|
||||
const activeForwardedArgs = changedTargetArgs ? stripChangedArgs(forwardedArgs) : forwardedArgs;
|
||||
if (activeTargetArgs.length === 0) {
|
||||
return [
|
||||
{
|
||||
config: DEFAULT_VITEST_CONFIG,
|
||||
forwardedArgs,
|
||||
forwardedArgs: activeForwardedArgs,
|
||||
includePatterns: null,
|
||||
watchMode,
|
||||
},
|
||||
@@ -322,7 +420,7 @@ export function buildVitestRunPlans(args, cwd = process.cwd()) {
|
||||
}
|
||||
|
||||
const groupedTargets = new Map();
|
||||
for (const targetArg of targetArgs) {
|
||||
for (const targetArg of activeTargetArgs) {
|
||||
const kind = classifyTarget(targetArg, cwd);
|
||||
const current = groupedTargets.get(kind) ?? [];
|
||||
current.push(targetArg);
|
||||
@@ -335,7 +433,7 @@ export function buildVitestRunPlans(args, cwd = process.cwd()) {
|
||||
);
|
||||
}
|
||||
|
||||
const nonTargetArgs = forwardedArgs.filter((arg) => !targetArgs.includes(arg));
|
||||
const nonTargetArgs = activeForwardedArgs.filter((arg) => !activeTargetArgs.includes(arg));
|
||||
const orderedKinds = [
|
||||
"default",
|
||||
"boundary",
|
||||
@@ -502,11 +600,14 @@ export function buildVitestRunPlans(args, cwd = process.cwd()) {
|
||||
"extension"
|
||||
? EXTENSIONS_VITEST_CONFIG
|
||||
: DEFAULT_VITEST_CONFIG;
|
||||
const includePatterns =
|
||||
kind === "default" || kind === "e2e"
|
||||
? null
|
||||
: grouped.map((targetArg) => toScopedIncludePattern(targetArg, cwd));
|
||||
const scopedTargetArgs = kind === "default" || kind === "e2e" ? grouped : [];
|
||||
const useCliTargetArgs =
|
||||
kind === "e2e" ||
|
||||
(kind === "default" &&
|
||||
grouped.every((targetArg) => isFileLikeTarget(toRepoRelativeTarget(targetArg, cwd))));
|
||||
const includePatterns = useCliTargetArgs
|
||||
? null
|
||||
: grouped.map((targetArg) => toScopedIncludePattern(targetArg, cwd));
|
||||
const scopedTargetArgs = useCliTargetArgs ? grouped : [];
|
||||
plans.push({
|
||||
config,
|
||||
forwardedArgs: [...nonTargetArgs, ...scopedTargetArgs],
|
||||
|
||||
70
test/scripts/test-projects.test.ts
Normal file
70
test/scripts/test-projects.test.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
buildVitestRunPlans,
|
||||
resolveChangedTargetArgs,
|
||||
} from "../../scripts/test-projects.test-support.mjs";
|
||||
|
||||
describe("scripts/test-projects changed-target routing", () => {
|
||||
it("maps changed source files into scoped lane targets", () => {
|
||||
expect(
|
||||
resolveChangedTargetArgs(["--changed", "origin/main"], process.cwd(), () => [
|
||||
"src/shared/string-normalization.ts",
|
||||
"src/utils/provider-utils.ts",
|
||||
]),
|
||||
).toEqual(["src/shared/string-normalization.ts", "src/utils/provider-utils.ts"]);
|
||||
});
|
||||
|
||||
it("keeps the broad changed run for Vitest wiring edits", () => {
|
||||
expect(
|
||||
resolveChangedTargetArgs(["--changed", "origin/main"], process.cwd(), () => [
|
||||
"vitest.shared.config.ts",
|
||||
"src/utils/provider-utils.ts",
|
||||
]),
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it("ignores changed files that cannot map to test lanes", () => {
|
||||
expect(
|
||||
resolveChangedTargetArgs(["--changed", "origin/main"], process.cwd(), () => [
|
||||
"docs/help/testing.md",
|
||||
]),
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it("narrows default-lane changed source files to include globs", () => {
|
||||
const plans = buildVitestRunPlans(["--changed", "origin/main"], process.cwd(), () => [
|
||||
"packages/sdk/src/index.ts",
|
||||
]);
|
||||
|
||||
expect(plans).toEqual([
|
||||
{
|
||||
config: "vitest.unit.config.ts",
|
||||
forwardedArgs: [],
|
||||
includePatterns: ["packages/sdk/src/**/*.test.ts"],
|
||||
watchMode: false,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("routes changed utils and shared files to their light scoped lanes", () => {
|
||||
const plans = buildVitestRunPlans(["--changed", "origin/main"], process.cwd(), () => [
|
||||
"src/shared/string-normalization.ts",
|
||||
"src/utils/provider-utils.ts",
|
||||
]);
|
||||
|
||||
expect(plans).toEqual([
|
||||
{
|
||||
config: "vitest.shared-core.config.ts",
|
||||
forwardedArgs: [],
|
||||
includePatterns: ["src/shared/**/*.test.ts"],
|
||||
watchMode: false,
|
||||
},
|
||||
{
|
||||
config: "vitest.utils.config.ts",
|
||||
forwardedArgs: [],
|
||||
includePatterns: ["src/utils/**/*.test.ts"],
|
||||
watchMode: false,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -101,6 +101,25 @@ describe("createScopedVitestConfig", () => {
|
||||
expect(config.test?.passWithNoTests).toBe(true);
|
||||
});
|
||||
|
||||
it("loads scoped include overrides from OPENCLAW_VITEST_INCLUDE_FILE", () => {
|
||||
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-vitest-scoped-"));
|
||||
try {
|
||||
const includeFile = path.join(tempDir, "include.json");
|
||||
fs.writeFileSync(includeFile, JSON.stringify(["src/utils/utils-misc.test.ts"]), "utf8");
|
||||
|
||||
const config = createScopedVitestConfig(["src/utils/**/*.test.ts"], {
|
||||
dir: "src",
|
||||
env: {
|
||||
OPENCLAW_VITEST_INCLUDE_FILE: includeFile,
|
||||
},
|
||||
});
|
||||
|
||||
expect(config.test?.include).toEqual(["utils/utils-misc.test.ts"]);
|
||||
} finally {
|
||||
fs.rmSync(tempDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it("overrides setup files when a scoped config requests them", () => {
|
||||
const config = createScopedVitestConfig(["src/example.test.ts"], {
|
||||
env: {},
|
||||
@@ -508,6 +527,7 @@ describe("scoped vitest configs", () => {
|
||||
it("normalizes shared-core include patterns relative to the scoped dir", () => {
|
||||
expect(defaultSharedCoreConfig.test?.dir).toBe("src");
|
||||
expect(defaultSharedCoreConfig.test?.include).toEqual(["shared/**/*.test.ts"]);
|
||||
expect(defaultSharedCoreConfig.test?.setupFiles).toEqual(["test/setup.ts"]);
|
||||
});
|
||||
|
||||
it("normalizes process include patterns relative to the scoped dir", () => {
|
||||
@@ -585,5 +605,6 @@ describe("scoped vitest configs", () => {
|
||||
it("normalizes utils include patterns relative to the scoped dir", () => {
|
||||
expect(defaultUtilsConfig.test?.dir).toBe("src");
|
||||
expect(defaultUtilsConfig.test?.include).toEqual(["utils/**/*.test.ts"]);
|
||||
expect(defaultUtilsConfig.test?.setupFiles).toEqual(["test/setup.ts"]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { defineConfig } from "vitest/config";
|
||||
import { narrowIncludePatternsForCli } from "./vitest.pattern-file.ts";
|
||||
import { loadPatternListFromEnv, narrowIncludePatternsForCli } from "./vitest.pattern-file.ts";
|
||||
import { sharedVitestConfig } from "./vitest.shared.config.ts";
|
||||
|
||||
function normalizePathPattern(value: string): string {
|
||||
@@ -55,6 +55,8 @@ export function createScopedVitestConfig(
|
||||
const base = sharedVitestConfig as Record<string, unknown>;
|
||||
const baseTest = sharedVitestConfig.test ?? {};
|
||||
const scopedDir = options?.dir;
|
||||
const env = options?.env;
|
||||
const includeFromEnv = loadPatternListFromEnv("OPENCLAW_VITEST_INCLUDE_FILE", env);
|
||||
const cliInclude = narrowIncludePatternsForCli(include, options?.argv);
|
||||
const exclude = relativizeScopedPatterns(
|
||||
[...(baseTest.exclude ?? []), ...(options?.exclude ?? [])],
|
||||
@@ -82,7 +84,7 @@ export function createScopedVitestConfig(
|
||||
...(runner ? { runner } : { runner: undefined }),
|
||||
setupFiles,
|
||||
...(scopedDir ? { dir: scopedDir } : {}),
|
||||
include: relativizeScopedPatterns(cliInclude ?? include, scopedDir),
|
||||
include: relativizeScopedPatterns(includeFromEnv ?? cliInclude ?? include, scopedDir),
|
||||
exclude,
|
||||
...(options?.pool ? { pool: options.pool } : {}),
|
||||
...(options?.passWithNoTests !== undefined || cliInclude !== null
|
||||
|
||||
@@ -4,6 +4,7 @@ export function createSharedCoreVitestConfig(env?: Record<string, string | undef
|
||||
return createScopedVitestConfig(["src/shared/**/*.test.ts"], {
|
||||
dir: "src",
|
||||
env,
|
||||
includeOpenClawRuntimeSetup: false,
|
||||
name: "shared-core",
|
||||
passWithNoTests: true,
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@ export function createUtilsVitestConfig(env?: Record<string, string | undefined>
|
||||
return createScopedVitestConfig(["src/utils/**/*.test.ts"], {
|
||||
dir: "src",
|
||||
env,
|
||||
includeOpenClawRuntimeSetup: false,
|
||||
name: "utils",
|
||||
passWithNoTests: true,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user