test(ci): isolate flaky shared-worker suites (#107780)

* test(ci): isolate flaky shared-worker suites

* test(ci): route isolated agent targets
This commit is contained in:
Peter Steinberger
2026-07-14 15:00:58 -07:00
committed by GitHub
parent 890f20be06
commit 86085563be
12 changed files with 133 additions and 8 deletions
+14 -2
View File
@@ -1,5 +1,6 @@
// Builds CI node/Vitest shard plans from the full suite configuration.
import { relative } from "node:path";
import { agentsCoreIsolatedTestFiles } from "../../test/vitest/vitest.agents-paths.mjs";
import { commandsLightTestFiles } from "../../test/vitest/vitest.commands-light-paths.mjs";
import { fullSuiteVitestShards } from "../../test/vitest/vitest.test-shards.mjs";
import { toolingIsolatedTestFiles } from "../../test/vitest/vitest.tooling-isolated-paths.mjs";
@@ -280,17 +281,18 @@ function resolveAgentCoreShardName(file) {
}
function createAgentCoreSplitShards() {
const isolatedTests = new Set(agentsCoreIsolatedTestFiles);
const groups = new Map();
for (const file of listTestFiles("src/agents")) {
const name = relative("src/agents", file).replaceAll("\\", "/");
if (name.includes("/")) {
if (name.includes("/") || isolatedTests.has(file)) {
continue;
}
const shardName = resolveAgentCoreShardName(file);
groups.set(shardName, [...(groups.get(shardName) ?? []), file]);
}
return [
const sharedShards = [
"agentic-agents-core-auth",
"agentic-agents-core-models",
"agentic-agents-core-tools",
@@ -305,6 +307,16 @@ function createAgentCoreSplitShards() {
shardName,
}))
.filter((shard) => shard.includePatterns.length > 0);
return [
...sharedShards,
{
configs: ["test/vitest/vitest.agents-core-isolated.config.ts"],
includePatterns: agentsCoreIsolatedTestFiles,
requiresDist: false,
shardName: "agentic-agents-core-isolated",
},
];
}
const GATEWAY_SERVER_BACKED_HTTP_TESTS = new Set([
+12
View File
@@ -6,6 +6,10 @@ import { randomUUID } from "node:crypto";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import {
agentsCoreIsolatedTestFiles,
isAgentsCoreIsolatedTestFile,
} from "../test/vitest/vitest.agents-paths.mjs";
import { isChannelSurfaceTestFile } from "../test/vitest/vitest.channel-paths.mjs";
import {
commandsLightTestFiles,
@@ -70,6 +74,7 @@ import {
} from "./run-vitest.mjs";
const DEFAULT_VITEST_CONFIG = "test/vitest/vitest.unit.config.ts";
const AGENTS_CORE_ISOLATED_VITEST_CONFIG = "test/vitest/vitest.agents-core-isolated.config.ts";
const AGENTS_CORE_VITEST_CONFIG = "test/vitest/vitest.agents-core.config.ts";
const AGENTS_EMBEDDED_AGENT_VITEST_CONFIG = "test/vitest/vitest.agents-embedded-agent.config.ts";
const AGENTS_SUPPORT_VITEST_CONFIG = "test/vitest/vitest.agents-support.config.ts";
@@ -314,6 +319,7 @@ const VITEST_CONFIG_BY_KIND = {
agentSupport: AGENTS_SUPPORT_VITEST_CONFIG,
agentTools: AGENTS_TOOLS_VITEST_CONFIG,
agent: AGENTS_VITEST_CONFIG,
agentsCoreIsolated: AGENTS_CORE_ISOLATED_VITEST_CONFIG,
agentsCore: AGENTS_CORE_VITEST_CONFIG,
agentsSupport: AGENTS_SUPPORT_VITEST_CONFIG,
agentsTools: AGENTS_TOOLS_VITEST_CONFIG,
@@ -2563,6 +2569,7 @@ function listUnitFastFullSuiteTestTargets() {
}
function listAgentsCoreFullSuiteTestTargets(cwd) {
const isolatedTests = new Set(agentsCoreIsolatedTestFiles);
const agentsDir = path.join(cwd, "src/agents");
if (!fs.existsSync(agentsDir)) {
return [];
@@ -2571,6 +2578,7 @@ function listAgentsCoreFullSuiteTestTargets(cwd) {
.readdirSync(agentsDir, { withFileTypes: true })
.filter((entry) => entry.isFile() && entry.name.endsWith(".test.ts"))
.map((entry) => `src/agents/${entry.name}`)
.filter((file) => !isolatedTests.has(file))
.toSorted((left, right) => left.localeCompare(right));
}
@@ -3758,6 +3766,9 @@ function classifyTarget(arg, cwd) {
if (configTargetKind) {
return configTargetKind;
}
if (isAgentsCoreIsolatedTestFile(relative)) {
return "agentsCoreIsolated";
}
if (isControlUiE2eTarget(relative)) {
return "uiE2e";
}
@@ -4205,6 +4216,7 @@ export function buildVitestRunPlans(
"agentSupport",
"agentTools",
"agent",
"agentsCoreIsolated",
"agentsCore",
"agentsSupport",
"agentsTools",
+10 -2
View File
@@ -184,8 +184,8 @@ describe("scripts/lib/ci-node-test-plan.mjs", () => {
compact: true,
});
expect(compact).toHaveLength(15);
expect(compact.filter((shard) => !shard.requiresDist)).toHaveLength(14);
expect(compact).toHaveLength(16);
expect(compact.filter((shard) => !shard.requiresDist)).toHaveLength(15);
expect(compact.every((shard) => Array.isArray(shard.groups))).toBe(true);
expect(compact.some((shard) => shard.requiresDist)).toBe(true);
expect(
@@ -874,6 +874,14 @@ describe("scripts/lib/ci-node-test-plan.mjs", () => {
runner: DEFAULT_NODE_TEST_RUNNER,
shardName: "agentic-agents-core-runtime",
},
{
checkName: "checks-node-agentic-agents-core-isolated",
configs: ["test/vitest/vitest.agents-core-isolated.config.ts"],
includePatterns: agentShards[6]?.includePatterns,
requiresDist: false,
runner: DEFAULT_NODE_TEST_RUNNER,
shardName: "agentic-agents-core-isolated",
},
{
checkName: "checks-node-agentic-agents-embedded",
configs: ["test/vitest/vitest.agents-embedded-agent.config.ts"],
+41 -3
View File
@@ -31,6 +31,7 @@ import {
} from "../../scripts/test-projects.test-support.mjs";
import { captureReaddirSyncCallsDuring } from "../../src/test-utils/fs-scan-assertions.js";
import { toRepoPath } from "../../src/test-utils/repo-files.js";
import { agentsCoreIsolatedTestFiles } from "../vitest/vitest.agents-paths.mjs";
import {
channelConfigContractPatterns,
channelRegistryContractPatterns,
@@ -2466,6 +2467,34 @@ describe("scripts/test-projects changed-target routing", () => {
]);
});
it.each([
"test/scripts/check-extension-package-tsc-boundary.test.ts",
"test/scripts/control-ui-i18n.test.ts",
])("routes process-group test %s to the isolated tooling shard", (testFile) => {
expect(buildVitestRunPlans([testFile])).toEqual([
{
config: "test/vitest/vitest.tooling-isolated.config.ts",
forwardedArgs: [],
includePatterns: [testFile],
watchMode: false,
},
]);
});
it.each(agentsCoreIsolatedTestFiles)(
"routes isolated agent test %s to the isolated agents-core shard",
(testFile) => {
expect(buildVitestRunPlans([testFile])).toEqual([
{
config: "test/vitest/vitest.agents-core-isolated.config.ts",
forwardedArgs: [],
includePatterns: [testFile],
watchMode: false,
},
]);
},
);
it("routes Docker E2E script targets to their owner tooling tests", () => {
const targets = [
"scripts/e2e/kitchen-sink-plugin-docker.sh",
@@ -2693,7 +2722,11 @@ describe("scripts/test-projects changed-target routing", () => {
{
config: "test/vitest/vitest.tooling-isolated.config.ts",
forwardedArgs: [],
includePatterns: ["test/scripts/openclaw-e2e-instance.test.ts"],
includePatterns: [
"test/scripts/check-extension-package-tsc-boundary.test.ts",
"test/scripts/control-ui-i18n.test.ts",
"test/scripts/openclaw-e2e-instance.test.ts",
],
watchMode: false,
},
]);
@@ -2837,7 +2870,11 @@ describe("scripts/test-projects changed-target routing", () => {
{
config: "test/vitest/vitest.tooling-isolated.config.ts",
forwardedArgs: [],
includePatterns: ["test/scripts/openclaw-e2e-instance.test.ts"],
includePatterns: [
"test/scripts/check-extension-package-tsc-boundary.test.ts",
"test/scripts/control-ui-i18n.test.ts",
"test/scripts/openclaw-e2e-instance.test.ts",
],
watchMode: false,
},
]);
@@ -3404,7 +3441,7 @@ describe("scripts/test-projects changed-target routing", () => {
]),
).toEqual([
{
config: "test/vitest/vitest.tooling.config.ts",
config: "test/vitest/vitest.tooling-isolated.config.ts",
forwardedArgs: [],
includePatterns: ["test/scripts/control-ui-i18n.test.ts"],
watchMode: false,
@@ -4461,6 +4498,7 @@ describe("scripts/test-projects full-suite sharding", () => {
"test/vitest/vitest.cli.config.ts",
"test/vitest/vitest.commands-light.config.ts",
"test/vitest/vitest.commands.config.ts",
"test/vitest/vitest.agents-core-isolated.config.ts",
...agentsCorePlans.map(() => agentsCoreConfig),
"test/vitest/vitest.agents-embedded-agent.config.ts",
"test/vitest/vitest.agents-support.config.ts",
+18
View File
@@ -7,6 +7,9 @@ import { describe, expect, it } from "vitest";
import { cleanupTempDirs, makeTempDir } from "./helpers/temp-dir.js";
import { normalizeConfigPath, normalizeConfigPaths } from "./helpers/vitest-config-paths.js";
import { createAcpVitestConfig } from "./vitest/vitest.acp.config.ts";
import { createAgentsCoreIsolatedVitestConfig } from "./vitest/vitest.agents-core-isolated.config.ts";
import { createAgentsCoreVitestConfig } from "./vitest/vitest.agents-core.config.ts";
import { agentsCoreIsolatedTestFiles } from "./vitest/vitest.agents-paths.mjs";
import { createAgentsVitestConfig } from "./vitest/vitest.agents.config.ts";
import { createAutoReplyCoreVitestConfig } from "./vitest/vitest.auto-reply-core.config.ts";
import { createAutoReplyReplyVitestConfig } from "./vitest/vitest.auto-reply-reply.config.ts";
@@ -566,6 +569,8 @@ describe("scoped vitest configs", () => {
const defaultAutoReplyTopLevelConfig = createAutoReplyTopLevelVitestConfig({});
const defaultAutoReplyReplyConfig = createAutoReplyReplyVitestConfig({});
const defaultAgentsConfig = createAgentsVitestConfig({});
const defaultAgentsCoreConfig = createAgentsCoreVitestConfig({});
const defaultAgentsCoreIsolatedConfig = createAgentsCoreIsolatedVitestConfig({});
const defaultPluginsConfig = createPluginsVitestConfig({});
const defaultProcessConfig = createProcessVitestConfig({});
const defaultToolingDockerConfig = createToolingDockerVitestConfig({});
@@ -638,6 +643,19 @@ describe("scoped vitest configs", () => {
);
});
it("isolates media task status suites from conflicting task-runtime mocks", () => {
const sharedConfig = requireTestConfig(defaultAgentsCoreConfig);
const isolatedConfig = requireTestConfig(defaultAgentsCoreIsolatedConfig);
const scopedIsolatedFiles = agentsCoreIsolatedTestFiles.map((file) =>
file.replace("src/agents/", ""),
);
expect(sharedConfig.exclude).toEqual(expect.arrayContaining(scopedIsolatedFiles));
expect(isolatedConfig.include).toEqual(scopedIsolatedFiles);
expect(isolatedConfig.isolate).toBe(true);
expect(isolatedConfig.runner).toBeUndefined();
});
it("keeps selected plugin-sdk and commands light lanes off the openclaw runtime setup", () => {
expect(normalizeConfigPaths(requireTestConfig(defaultPluginSdkLightConfig).setupFiles)).toEqual(
["test/setup.ts"],
@@ -0,0 +1,16 @@
// Vitest agents core isolated config separates suites with conflicting module mocks.
import { agentsCoreIsolatedTestFiles } from "./vitest.agents-paths.mjs";
import { createScopedVitestConfig } from "./vitest.scoped-config.ts";
export function createAgentsCoreIsolatedVitestConfig(env?: Record<string, string | undefined>) {
return createScopedVitestConfig(agentsCoreIsolatedTestFiles, {
dir: "src/agents",
env,
isolate: true,
name: "agents-core-isolated",
passWithNoTests: true,
useNonIsolatedRunner: false,
});
}
export default createAgentsCoreIsolatedVitestConfig();
+2 -1
View File
@@ -1,11 +1,12 @@
// Vitest agents core config wires the agents core test shard.
import { agentsCoreTestPatterns } from "./vitest.agents-paths.mjs";
import { agentsCoreIsolatedTestFiles, agentsCoreTestPatterns } from "./vitest.agents-paths.mjs";
import { createScopedVitestConfig } from "./vitest.scoped-config.ts";
export function createAgentsCoreVitestConfig(env?: Record<string, string | undefined>) {
return createScopedVitestConfig(agentsCoreTestPatterns, {
dir: "src/agents",
env,
exclude: agentsCoreIsolatedTestFiles,
fileParallelism: false,
name: "agents-core",
});
+2
View File
@@ -1,4 +1,6 @@
export const agentsAllTestPatterns: string[];
export const agentsCoreIsolatedTestFiles: string[];
export function isAgentsCoreIsolatedTestFile(file: string): boolean;
export const agentsCoreTestPatterns: string[];
export const agentsEmbeddedTestPatterns: string[];
export const agentsToolsTestPatterns: string[];
+14
View File
@@ -1,6 +1,20 @@
// Test routing globs for agent core, embedded-agent, tool, and support suites.
export const agentsAllTestPatterns = ["src/agents/**/*.test.ts"];
// These suites install different mocks for the same task-runtime module. Keep
// their module graphs separate from the shared agents-core worker.
export const agentsCoreIsolatedTestFiles = [
"src/agents/image-generation-task-status.test.ts",
"src/agents/media-generation-task-status-shared.test.ts",
"src/agents/video-generation-task-status.test.ts",
];
const agentsCoreIsolatedTestFileSet = new Set(agentsCoreIsolatedTestFiles);
export function isAgentsCoreIsolatedTestFile(value) {
return agentsCoreIsolatedTestFileSet.has(value.replaceAll("\\", "/"));
}
export const agentsCoreTestPatterns = ["src/agents/*.test.ts"];
export const agentsEmbeddedTestPatterns = ["src/agents/embedded-agent-runner/**/*.test.ts"];
+1
View File
@@ -32,6 +32,7 @@ export const rootVitestProjects = [
"test/vitest/vitest.commands-light.config.ts",
"test/vitest/vitest.commands.config.ts",
"test/vitest/vitest.auto-reply.config.ts",
"test/vitest/vitest.agents-core-isolated.config.ts",
"test/vitest/vitest.agents-core.config.ts",
"test/vitest/vitest.agents-embedded-agent.config.ts",
"test/vitest/vitest.agents-support.config.ts",
+1
View File
@@ -98,6 +98,7 @@ export const fullSuiteVitestShards = [
"test/vitest/vitest.cli.config.ts",
"test/vitest/vitest.commands-light.config.ts",
"test/vitest/vitest.commands.config.ts",
"test/vitest/vitest.agents-core-isolated.config.ts",
"test/vitest/vitest.agents-core.config.ts",
"test/vitest/vitest.agents-embedded-agent.config.ts",
"test/vitest/vitest.agents-support.config.ts",
@@ -1,6 +1,8 @@
// Tooling tests that need fresh module or process state instead of the shared serial worker.
export const toolingIsolatedTestFiles = [
"test/plugins/bundled-provider-auth-literal-parity.test.ts",
"test/scripts/check-extension-package-tsc-boundary.test.ts",
"test/scripts/control-ui-i18n.test.ts",
"test/scripts/openclaw-e2e-instance.test.ts",
];