perf(ci): isolate Docker tooling tests

This commit is contained in:
Vincent Koc 2026-06-11 18:13:36 +09:00
parent 0c4fc0a2e3
commit f1401b2cac
13 changed files with 149 additions and 14 deletions

View file

@ -717,6 +717,24 @@ const SPLIT_NODE_SHARDS = new Map([
],
],
["core-unit-security", []],
[
"core-tooling",
[
{
shardName: "core-tooling",
configs: [
"test/vitest/vitest.tooling.config.ts",
"test/vitest/vitest.tooling-isolated.config.ts",
],
requiresDist: false,
},
{
shardName: "core-tooling-docker",
configs: ["test/vitest/vitest.tooling-docker.config.ts"],
requiresDist: false,
},
],
],
[
"core-unit-support",
[

View file

@ -29,6 +29,7 @@ const VITEST_NO_OUTPUT_TIMEOUT_ENV_KEY = "OPENCLAW_VITEST_NO_OUTPUT_TIMEOUT_MS";
const VITEST_NO_OUTPUT_HEARTBEAT_ENV_KEY = "OPENCLAW_VITEST_NO_OUTPUT_HEARTBEAT_MS";
const UI_VITEST_CONFIG = "test/vitest/vitest.ui.config.ts";
const UNIT_UI_VITEST_CONFIG = "test/vitest/vitest.unit-ui.config.ts";
const TOOLING_DOCKER_VITEST_CONFIG = "test/vitest/vitest.tooling-docker.config.ts";
const TOOLING_VITEST_CONFIG = "test/vitest/vitest.tooling.config.ts";
const GATEWAY_VITEST_CONFIG = "test/vitest/vitest.gateway.config.ts";
const LONG_RUNNING_VITEST_CONFIGS = new Set([
@ -40,6 +41,7 @@ const LONG_RUNNING_VITEST_CONFIGS = new Set([
]);
const TOOLING_EXCLUDED_TESTS = new Set([
...boundaryTestFiles,
"test/scripts/docker-build-helper.test.ts",
"test/scripts/openclaw-e2e-instance.test.ts",
]);
const EXPLICIT_FILE_TARGET_RE = /\.(?:[cm]?[jt]sx?)$/u;
@ -677,6 +679,10 @@ function isToolingTestTarget(target) {
);
}
function isToolingDockerTestTarget(target) {
return target === "test/scripts/docker-build-helper.test.ts";
}
/**
* Resolves config defaults and explicit-file handling for wrapper-inferred runs.
*/
@ -687,6 +693,9 @@ export function resolveImplicitVitestArgs(argv, cwd = process.cwd()) {
const testTargets = argv
.filter((arg) => !arg.startsWith("-") && arg.endsWith(".test.ts"))
.map((arg) => toRepoRelativeArg(arg, cwd));
if (testTargets.length > 0 && testTargets.every(isToolingDockerTestTarget)) {
return withImplicitVitestConfig(argv, TOOLING_DOCKER_VITEST_CONFIG);
}
if (testTargets.length > 0 && testTargets.every(isToolingTestTarget)) {
return withImplicitVitestConfig(argv, TOOLING_VITEST_CONFIG);
}

View file

@ -273,8 +273,10 @@ const RUNTIME_CONFIG_VITEST_CONFIG = "test/vitest/vitest.runtime-config.config.t
const SECRETS_VITEST_CONFIG = "test/vitest/vitest.secrets.config.ts";
const SHARED_CORE_VITEST_CONFIG = "test/vitest/vitest.shared-core.config.ts";
const TASKS_VITEST_CONFIG = "test/vitest/vitest.tasks.config.ts";
const TOOLING_DOCKER_VITEST_CONFIG = "test/vitest/vitest.tooling-docker.config.ts";
const TOOLING_ISOLATED_VITEST_CONFIG = "test/vitest/vitest.tooling-isolated.config.ts";
const TOOLING_VITEST_CONFIG = "test/vitest/vitest.tooling.config.ts";
const TOOLING_DOCKER_TEST_TARGET = "test/scripts/docker-build-helper.test.ts";
const TOOLING_ISOLATED_TEST_TARGET = "test/scripts/openclaw-e2e-instance.test.ts";
const TUI_VITEST_CONFIG = "test/vitest/vitest.tui.config.ts";
const TUI_PTY_VITEST_CONFIG = "test/vitest/vitest.tui-pty.config.ts";
@ -367,6 +369,7 @@ const VITEST_CONFIG_BY_KIND = {
secrets: SECRETS_VITEST_CONFIG,
sharedCore: SHARED_CORE_VITEST_CONFIG,
tasks: TASKS_VITEST_CONFIG,
toolingDocker: TOOLING_DOCKER_VITEST_CONFIG,
toolingIsolated: TOOLING_ISOLATED_VITEST_CONFIG,
tooling: TOOLING_VITEST_CONFIG,
tui: TUI_VITEST_CONFIG,
@ -2011,6 +2014,9 @@ function classifyTarget(arg, cwd) {
if (relative === TOOLING_ISOLATED_TEST_TARGET) {
return "toolingIsolated";
}
if (relative === TOOLING_DOCKER_TEST_TARGET) {
return "toolingDocker";
}
if (
relative.startsWith("test/") ||
relative.startsWith("src/scripts/") ||
@ -2237,6 +2243,20 @@ export function buildVitestRunPlans(
groupedTargets.set(kind, current);
}
const toolingTargets = groupedTargets.get("tooling") ?? [];
if (
!watchMode &&
toolingTargets.some((targetArg) =>
includePatternMatchesAnyFile(toScopedIncludePattern(targetArg, cwd), [
TOOLING_DOCKER_TEST_TARGET,
]),
)
) {
const current = groupedTargets.get("toolingDocker") ?? [];
if (!current.includes(TOOLING_DOCKER_TEST_TARGET)) {
current.push(TOOLING_DOCKER_TEST_TARGET);
groupedTargets.set("toolingDocker", current);
}
}
if (
!watchMode &&
toolingTargets.some((targetArg) =>
@ -2263,6 +2283,7 @@ export function buildVitestRunPlans(
"unitFastFakeTimers",
"default",
"boundary",
"toolingDocker",
"toolingIsolated",
"tooling",
"contractsChannelSurface",

View file

@ -177,19 +177,30 @@ describe("scripts/lib/ci-node-test-plan.mjs", () => {
expect(requiresDistShardNames).toEqual(["core-support-boundary"]);
});
it("keeps tooling checks independent from built artifacts", () => {
const toolingShard = createNodeTestShards().find((shard) => shard.shardName === "core-tooling");
it("splits tooling checks independently from built artifacts", () => {
const toolingShards = createNodeTestShards().filter((shard) =>
shard.shardName.startsWith("core-tooling"),
);
expect(toolingShard).toEqual({
checkName: "checks-node-core-tooling",
configs: [
"test/vitest/vitest.tooling.config.ts",
"test/vitest/vitest.tooling-isolated.config.ts",
],
requiresDist: false,
runner: "blacksmith-8vcpu-ubuntu-2404",
shardName: "core-tooling",
});
expect(toolingShards).toEqual([
{
checkName: "checks-node-core-tooling",
configs: [
"test/vitest/vitest.tooling.config.ts",
"test/vitest/vitest.tooling-isolated.config.ts",
],
requiresDist: false,
runner: "blacksmith-8vcpu-ubuntu-2404",
shardName: "core-tooling",
},
{
checkName: "checks-node-core-tooling-docker",
configs: ["test/vitest/vitest.tooling-docker.config.ts"],
requiresDist: false,
runner: "blacksmith-8vcpu-ubuntu-2404",
shardName: "core-tooling-docker",
},
]);
});
it("assigns Blacksmith runners to every core node shard", () => {

View file

@ -180,6 +180,15 @@ describe("scripts/run-vitest", () => {
]);
});
it("routes explicit Docker helper tests through the Docker tooling config", () => {
expect(resolveImplicitVitestArgs(["run", "test/scripts/docker-build-helper.test.ts"])).toEqual([
"run",
"--config",
"test/vitest/vitest.tooling-docker.config.ts",
"test/scripts/docker-build-helper.test.ts",
]);
});
it("keeps tooling-excluded explicit tests on existing routing", () => {
const argv = ["run", "test/scripts/openclaw-e2e-instance.test.ts"];
expect(resolveImplicitVitestArgs(argv)).toBe(argv);

View file

@ -835,11 +835,16 @@ describe("scripts/test-projects changed-target routing", () => {
expect(findUnmatchedExplicitTestTargets(targets)).toEqual([]);
expect(buildVitestRunPlans(targets, process.cwd())).toEqual([
{
config: "test/vitest/vitest.tooling-docker.config.ts",
forwardedArgs: [],
includePatterns: ["test/scripts/docker-build-helper.test.ts"],
watchMode: false,
},
{
config: "test/vitest/vitest.tooling.config.ts",
forwardedArgs: [],
includePatterns: [
"test/scripts/docker-build-helper.test.ts",
"test/scripts/plugin-prerelease-test-plan.test.ts",
"test/scripts/kitchen-sink-rpc-walk.test.ts",
"test/scripts/openclaw-test-state.test.ts",
@ -911,6 +916,12 @@ describe("scripts/test-projects changed-target routing", () => {
it("includes the isolated tooling shard for broad shell helper targets", () => {
expect(buildVitestRunPlans(["test/scripts"], process.cwd())).toEqual([
{
config: "test/vitest/vitest.tooling-docker.config.ts",
forwardedArgs: [],
includePatterns: ["test/scripts/docker-build-helper.test.ts"],
watchMode: false,
},
{
config: "test/vitest/vitest.tooling-isolated.config.ts",
forwardedArgs: [],
@ -928,6 +939,12 @@ describe("scripts/test-projects changed-target routing", () => {
it("includes the isolated tooling shard for broad shell helper globs", () => {
expect(buildVitestRunPlans(["test/scripts/*.test.ts"], process.cwd())).toEqual([
{
config: "test/vitest/vitest.tooling-docker.config.ts",
forwardedArgs: [],
includePatterns: ["test/scripts/docker-build-helper.test.ts"],
watchMode: false,
},
{
config: "test/vitest/vitest.tooling-isolated.config.ts",
forwardedArgs: [],
@ -2271,6 +2288,7 @@ describe("scripts/test-projects full-suite sharding", () => {
"test/vitest/vitest.unit-support.config.ts",
"test/vitest/vitest.boundary.config.ts",
"test/vitest/vitest.tooling.config.ts",
"test/vitest/vitest.tooling-docker.config.ts",
"test/vitest/vitest.tooling-isolated.config.ts",
"test/vitest/vitest.contracts-channel-surface.config.ts",
"test/vitest/vitest.contracts-channel-config.config.ts",

View file

@ -85,6 +85,19 @@ describe("projects vitest config", () => {
);
});
it("keeps root watch projects aligned with dedicated tooling shard lanes", () => {
const toolingShard = fullSuiteVitestShards.find(
(shard) => shard.config === "test/vitest/vitest.full-core-tooling.config.ts",
);
expect(toolingShard?.projects).toEqual(
expect.arrayContaining(["test/vitest/vitest.tooling-docker.config.ts"]),
);
expect(rootVitestProjects).toEqual(
expect.arrayContaining(["test/vitest/vitest.tooling-docker.config.ts"]),
);
});
it("disables vite env-file loading for vitest lanes", () => {
expect(baseConfig.envFile).toBe(false);
expect(sharedVitestConfig.envFile).toBe(false);

View file

@ -61,6 +61,10 @@ import { createSecretsVitestConfig } from "./vitest/vitest.secrets.config.ts";
import { createSharedCoreVitestConfig } from "./vitest/vitest.shared-core.config.ts";
import { sharedVitestConfig } from "./vitest/vitest.shared.config.ts";
import { createTasksVitestConfig } from "./vitest/vitest.tasks.config.ts";
import {
createToolingDockerVitestConfig,
toolingDockerTestFiles,
} from "./vitest/vitest.tooling-docker.config.ts";
import { createToolingIsolatedVitestConfig } from "./vitest/vitest.tooling-isolated.config.ts";
import { createToolingVitestConfig } from "./vitest/vitest.tooling.config.ts";
import { createTuiVitestConfig } from "./vitest/vitest.tui.config.ts";
@ -454,6 +458,7 @@ describe("scoped vitest configs", () => {
const defaultAgentsConfig = createAgentsVitestConfig({});
const defaultPluginsConfig = createPluginsVitestConfig({});
const defaultProcessConfig = createProcessVitestConfig({});
const defaultToolingDockerConfig = createToolingDockerVitestConfig({});
const defaultToolingConfig = createToolingVitestConfig({});
const defaultTuiConfig = createTuiVitestConfig({});
const defaultUiConfig = createUiVitestConfig({});
@ -475,6 +480,7 @@ describe("scoped vitest configs", () => {
defaultAutoReplyCoreConfig,
defaultAutoReplyTopLevelConfig,
defaultAutoReplyReplyConfig,
defaultToolingDockerConfig,
defaultToolingConfig,
]) {
expectThreadedNonIsolatedRunner(config);
@ -974,10 +980,17 @@ describe("scoped vitest configs", () => {
it("keeps tooling tests in their own lane", () => {
const testConfig = requireTestConfig(defaultToolingConfig);
expect(testConfig.include).toEqual(["test/**/*.test.ts", "src/scripts/**/*.test.ts"]);
expect(testConfig.exclude).toEqual(expect.arrayContaining(toolingDockerTestFiles));
expect(testConfig.exclude).toContain("test/scripts/openclaw-e2e-instance.test.ts");
expect(testConfig.include).not.toContain("src/config/doc-baseline.integration.test.ts");
});
it("keeps Docker helper tooling tests in their own lane", () => {
const testConfig = requireTestConfig(defaultToolingDockerConfig);
expect(testConfig.include).toEqual(toolingDockerTestFiles);
expect(testConfig.fileParallelism).toBe(false);
});
it("runs shell helper tooling tests isolated from shared mocks", () => {
const testConfig = requireTestConfig(createToolingIsolatedVitestConfig({}));
expect(testConfig.include).toEqual(["test/scripts/openclaw-e2e-instance.test.ts"]);

View file

@ -50,6 +50,7 @@ export const rootVitestProjects = [
"test/vitest/vitest.media-understanding.config.ts",
"test/vitest/vitest.shared-core.config.ts",
"test/vitest/vitest.tasks.config.ts",
"test/vitest/vitest.tooling-docker.config.ts",
"test/vitest/vitest.tooling-isolated.config.ts",
"test/vitest/vitest.tooling.config.ts",
"test/vitest/vitest.tui.config.ts",

View file

@ -150,6 +150,7 @@ const SCOPED_PROJECT_GROUP_ORDER_BY_NAME = new Map(
"secrets",
"shared-core",
"tasks",
"tooling-docker",
"tooling-isolated",
"tooling",
"tui",

View file

@ -46,6 +46,7 @@ export const fullSuiteVitestShards = [
name: "core-tooling",
projects: [
"test/vitest/vitest.tooling.config.ts",
"test/vitest/vitest.tooling-docker.config.ts",
"test/vitest/vitest.tooling-isolated.config.ts",
],
},

View file

@ -0,0 +1,15 @@
// Vitest tooling Docker config isolates the slow Docker helper contract tests.
import { createScopedVitestConfig } from "./vitest.scoped-config.ts";
export const toolingDockerTestFiles = ["test/scripts/docker-build-helper.test.ts"];
export function createToolingDockerVitestConfig(env?: Record<string, string | undefined>) {
return createScopedVitestConfig(toolingDockerTestFiles, {
env,
fileParallelism: false,
name: "tooling-docker",
passWithNoTests: true,
});
}
export default createToolingDockerVitestConfig();

View file

@ -1,6 +1,7 @@
// Vitest tooling config wires the tooling test shard.
import { loadPatternListFromEnv } from "./vitest.pattern-file.ts";
import { createScopedVitestConfig } from "./vitest.scoped-config.ts";
import { toolingDockerTestFiles } from "./vitest.tooling-docker.config.ts";
import { boundaryTestFiles } from "./vitest.unit-paths.mjs";
export function loadIncludePatternsFromEnv(
@ -14,7 +15,11 @@ export function createToolingVitestConfig(env?: Record<string, string | undefine
loadIncludePatternsFromEnv(env) ?? ["test/**/*.test.ts", "src/scripts/**/*.test.ts"],
{
env,
exclude: [...boundaryTestFiles, "test/scripts/openclaw-e2e-instance.test.ts"],
exclude: [
...boundaryTestFiles,
...toolingDockerTestFiles,
"test/scripts/openclaw-e2e-instance.test.ts",
],
fileParallelism: false,
name: "tooling",
passWithNoTests: true,