From 1e90b3afcd9b334d21c5752cac8242c1b39b8a7b Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 4 Apr 2026 02:05:37 +0100 Subject: [PATCH] perf: split extension channel vitest lane --- scripts/test-projects.test-support.mjs | 16 ++++++++++------ src/scripts/test-projects.test.ts | 2 +- test/vitest-scoped-config.test.ts | 20 ++++++++++++++++++++ vitest.channel-paths.mjs | 14 ++++++++++++++ vitest.channels.config.ts | 6 +++--- vitest.extension-channels.config.ts | 18 ++++++++++++++++++ vitest.shared.config.ts | 1 + 7 files changed, 67 insertions(+), 10 deletions(-) create mode 100644 vitest.extension-channels.config.ts diff --git a/scripts/test-projects.test-support.mjs b/scripts/test-projects.test-support.mjs index 541b896fb4a..7fc5b7c9a75 100644 --- a/scripts/test-projects.test-support.mjs +++ b/scripts/test-projects.test-support.mjs @@ -14,6 +14,7 @@ const CHANNEL_VITEST_CONFIG = "vitest.channels.config.ts"; const COMMANDS_VITEST_CONFIG = "vitest.commands.config.ts"; const CONTRACTS_VITEST_CONFIG = "vitest.contracts.config.ts"; const E2E_VITEST_CONFIG = "vitest.e2e.config.ts"; +const EXTENSION_CHANNELS_VITEST_CONFIG = "vitest.extension-channels.config.ts"; const EXTENSIONS_VITEST_CONFIG = "vitest.extensions.config.ts"; const GATEWAY_VITEST_CONFIG = "vitest.gateway.config.ts"; const UI_VITEST_CONFIG = "vitest.ui.config.ts"; @@ -76,7 +77,7 @@ function classifyTarget(arg, cwd) { return "e2e"; } if (relative.startsWith("extensions/")) { - return isChannelSurfaceTestFile(relative) ? "channel" : "extension"; + return isChannelSurfaceTestFile(relative) ? "extensionChannel" : "extension"; } if (isChannelSurfaceTestFile(relative)) { return "channel"; @@ -191,6 +192,7 @@ export function buildVitestRunPlans(args, cwd = process.cwd()) { "agent", "ui", "e2e", + "extensionChannel", "channel", "extension", ]; @@ -221,11 +223,13 @@ export function buildVitestRunPlans(args, cwd = process.cwd()) { ? UI_VITEST_CONFIG : kind === "e2e" ? E2E_VITEST_CONFIG - : kind === "channel" - ? CHANNEL_VITEST_CONFIG - : kind === "extension" - ? EXTENSIONS_VITEST_CONFIG - : DEFAULT_VITEST_CONFIG; + : kind === "extensionChannel" + ? EXTENSION_CHANNELS_VITEST_CONFIG + : kind === "channel" + ? CHANNEL_VITEST_CONFIG + : kind === "extension" + ? EXTENSIONS_VITEST_CONFIG + : DEFAULT_VITEST_CONFIG; const includePatterns = kind === "default" || kind === "e2e" ? null diff --git a/src/scripts/test-projects.test.ts b/src/scripts/test-projects.test.ts index 8635d9c9a2d..f7f9f147b8e 100644 --- a/src/scripts/test-projects.test.ts +++ b/src/scripts/test-projects.test.ts @@ -241,7 +241,7 @@ describe("test-projects args", () => { buildVitestRunPlans(["extensions/discord/src/monitor/message-handler.preflight.test.ts"]), ).toEqual([ { - config: "vitest.channels.config.ts", + config: "vitest.extension-channels.config.ts", forwardedArgs: [], includePatterns: ["extensions/discord/src/monitor/message-handler.preflight.test.ts"], watchMode: false, diff --git a/test/vitest-scoped-config.test.ts b/test/vitest-scoped-config.test.ts index 7c489cc9780..2b8748e7894 100644 --- a/test/vitest-scoped-config.test.ts +++ b/test/vitest-scoped-config.test.ts @@ -7,6 +7,7 @@ import { createAgentsVitestConfig } from "../vitest.agents.config.ts"; import { createAutoReplyVitestConfig } from "../vitest.auto-reply.config.ts"; import { createChannelsVitestConfig } from "../vitest.channels.config.ts"; import { createCommandsVitestConfig } from "../vitest.commands.config.ts"; +import { createExtensionChannelsVitestConfig } from "../vitest.extension-channels.config.ts"; import { createExtensionsVitestConfig } from "../vitest.extensions.config.ts"; import { createGatewayVitestConfig } from "../vitest.gateway.config.ts"; import { createScopedVitestConfig, resolveVitestIsolation } from "../vitest.scoped-config.ts"; @@ -69,6 +70,7 @@ describe("scoped vitest configs", () => { const defaultChannelsConfig = createChannelsVitestConfig({}); const defaultAcpConfig = createAcpVitestConfig({}); const defaultExtensionsConfig = createExtensionsVitestConfig({}); + const defaultExtensionChannelsConfig = createExtensionChannelsVitestConfig({}); const defaultGatewayConfig = createGatewayVitestConfig({}); const defaultCommandsConfig = createCommandsVitestConfig({}); const defaultAutoReplyConfig = createAutoReplyVitestConfig({}); @@ -80,6 +82,13 @@ describe("scoped vitest configs", () => { expect(defaultChannelsConfig.test?.pool).toBe("forks"); }); + it("keeps the core channel lane limited to non-extension roots", () => { + expect(defaultChannelsConfig.test?.include).toEqual([ + "src/browser/**/*.test.ts", + "src/line/**/*.test.ts", + ]); + }); + it("loads channel include overrides from OPENCLAW_VITEST_INCLUDE_FILE", () => { const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-vitest-channels-")); try { @@ -112,6 +121,17 @@ describe("scoped vitest configs", () => { expect(defaultExtensionsConfig.test?.pool).toBe("forks"); }); + it("normalizes extension channel include patterns relative to the scoped dir", () => { + expect(defaultExtensionChannelsConfig.test?.dir).toBe("extensions"); + expect(defaultExtensionChannelsConfig.test?.include).toEqual([ + "discord/**/*.test.ts", + "whatsapp/**/*.test.ts", + "slack/**/*.test.ts", + "signal/**/*.test.ts", + "imessage/**/*.test.ts", + ]); + }); + it("normalizes extension include patterns relative to the scoped dir", () => { expect(defaultExtensionsConfig.test?.dir).toBe("extensions"); expect(defaultExtensionsConfig.test?.include).toEqual(["**/*.test.ts"]); diff --git a/vitest.channel-paths.mjs b/vitest.channel-paths.mjs index efa3efd72d9..47ff73ed105 100644 --- a/vitest.channel-paths.mjs +++ b/vitest.channel-paths.mjs @@ -20,8 +20,18 @@ export const channelTestRoots = [ "src/line", ]; +export const extensionChannelTestRoots = channelTestRoots.filter((root) => + root.startsWith(BUNDLED_PLUGIN_PATH_PREFIX), +); +export const coreChannelTestRoots = channelTestRoots.filter( + (root) => !root.startsWith(BUNDLED_PLUGIN_PATH_PREFIX), +); export const channelTestPrefixes = channelTestRoots.map((root) => `${root}/`); export const channelTestInclude = channelTestRoots.map((root) => `${root}/**/*.test.ts`); +export const extensionChannelTestInclude = extensionChannelTestRoots.map( + (root) => `${root}/**/*.test.ts`, +); +export const coreChannelTestInclude = coreChannelTestRoots.map((root) => `${root}/**/*.test.ts`); export const channelTestExclude = channelTestRoots.map((root) => `${root}/**`); const extensionChannelRootOverrideBasenames = new Map(); @@ -53,6 +63,10 @@ export const extensionExcludedChannelTestGlobs = channelTestRoots return `${relativeRoot}/**/!(${alternation}).test.ts`; }); +export const extensionChannelOverrideExcludeGlobs = extensionRoutedChannelTestFiles + .filter((file) => file.startsWith(BUNDLED_PLUGIN_PATH_PREFIX)) + .map((file) => file.slice(BUNDLED_PLUGIN_PATH_PREFIX.length)); + export function isChannelSurfaceTestFile(filePath) { const normalizedFile = normalizeRepoPath(filePath); return ( diff --git a/vitest.channels.config.ts b/vitest.channels.config.ts index c287a28a58f..b9d9026cd29 100644 --- a/vitest.channels.config.ts +++ b/vitest.channels.config.ts @@ -1,4 +1,4 @@ -import { channelTestInclude, extensionRoutedChannelTestFiles } from "./vitest.channel-paths.mjs"; +import { coreChannelTestInclude } from "./vitest.channel-paths.mjs"; import { loadPatternListFromEnv } from "./vitest.pattern-file.ts"; import { createScopedVitestConfig } from "./vitest.scoped-config.ts"; @@ -9,9 +9,9 @@ export function loadIncludePatternsFromEnv( } export function createChannelsVitestConfig(env?: Record) { - return createScopedVitestConfig(loadIncludePatternsFromEnv(env) ?? channelTestInclude, { + return createScopedVitestConfig(loadIncludePatternsFromEnv(env) ?? coreChannelTestInclude, { env, - exclude: ["src/gateway/**", ...extensionRoutedChannelTestFiles], + exclude: ["src/gateway/**"], passWithNoTests: true, }); } diff --git a/vitest.extension-channels.config.ts b/vitest.extension-channels.config.ts new file mode 100644 index 00000000000..aaa78addaef --- /dev/null +++ b/vitest.extension-channels.config.ts @@ -0,0 +1,18 @@ +import { + extensionChannelOverrideExcludeGlobs, + extensionChannelTestInclude, +} from "./vitest.channel-paths.mjs"; +import { createScopedVitestConfig } from "./vitest.scoped-config.ts"; + +export function createExtensionChannelsVitestConfig( + env: Record = process.env, +) { + return createScopedVitestConfig(extensionChannelTestInclude, { + dir: "extensions", + env, + exclude: extensionChannelOverrideExcludeGlobs, + passWithNoTests: true, + }); +} + +export default createExtensionChannelsVitestConfig(); diff --git a/vitest.shared.config.ts b/vitest.shared.config.ts index 0de69fcee3e..7e3e6f4be30 100644 --- a/vitest.shared.config.ts +++ b/vitest.shared.config.ts @@ -101,6 +101,7 @@ export const sharedVitestConfig = { "vitest.config.ts", "vitest.contracts.config.ts", "vitest.e2e.config.ts", + "vitest.extension-channels.config.ts", "vitest.extensions.config.ts", "vitest.gateway.config.ts", "vitest.live.config.ts",