perf(tests): trim plugin and gateway hot paths

This commit is contained in:
Vincent Koc
2026-04-14 23:03:03 +01:00
parent 5977579da4
commit 9b25c8f8e1
7 changed files with 95 additions and 50 deletions

View File

@@ -2,7 +2,7 @@ import * as fs from "node:fs/promises";
import type { IncomingMessage, ServerResponse } from "node:http";
import * as os from "node:os";
import * as path from "node:path";
import { beforeEach, describe, expect, it, test, vi } from "vitest";
import { beforeAll, beforeEach, describe, expect, it, test, vi } from "vitest";
import { defaultVoiceWakeTriggers } from "../infra/voicewake.js";
import { handleControlUiHttpRequest } from "./control-ui.js";
import {
@@ -46,28 +46,15 @@ vi.mock("ws", () => ({
let GatewayClient: typeof import("./client.js").GatewayClient;
async function loadFreshGatewayClientModuleForTest() {
vi.resetModules();
vi.doMock("ws", () => ({
WebSocket: class MockWebSocket {
on = vi.fn();
close = vi.fn();
send = vi.fn();
constructor(url: unknown, opts: unknown) {
wsMockState.last = { url, opts };
}
},
}));
({ GatewayClient } = await import("./client.js"));
}
beforeEach(async () => {
wsMockState.last = null;
await loadFreshGatewayClientModuleForTest();
});
describe("GatewayClient", () => {
beforeAll(async () => {
({ GatewayClient } = await import("./client.js"));
});
beforeEach(() => {
wsMockState.last = null;
});
async function withControlUiRoot(
params: { faviconSvg?: string; indexHtml?: string },
run: (tmp: string) => Promise<void>,

View File

@@ -47,6 +47,11 @@ export type BundledPluginCompatibleActivationInputs = PluginActivationInputs & {
compatPluginIds: string[];
};
export type BundledPluginCompatibleLoadValues = Pick<
BundledPluginCompatibleActivationInputs,
"rawConfig" | "config" | "activationSourceConfig" | "autoEnabledReasons" | "compatPluginIds"
>;
export function withActivatedPluginIds(params: {
config?: OpenClawConfig;
pluginIds: readonly string[];
@@ -235,3 +240,70 @@ export function resolveBundledPluginCompatibleActivationInputs(params: {
compatPluginIds,
};
}
export function resolveBundledPluginCompatibleLoadValues(params: {
rawConfig?: OpenClawConfig;
resolvedConfig?: OpenClawConfig;
autoEnabledReasons?: Record<string, string[]>;
env?: NodeJS.ProcessEnv;
workspaceDir?: string;
onlyPluginIds?: readonly string[];
applyAutoEnable?: boolean;
compatMode: PluginActivationBundledCompatMode;
resolveCompatPluginIds: (params: {
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
onlyPluginIds?: readonly string[];
}) => string[];
}): BundledPluginCompatibleLoadValues {
const env = params.env ?? process.env;
const rawConfig = params.rawConfig ?? params.resolvedConfig;
let resolvedConfig = params.resolvedConfig ?? params.rawConfig;
let autoEnabledReasons = params.autoEnabledReasons ?? {};
if (params.applyAutoEnable && rawConfig !== undefined) {
const autoEnabled = applyPluginAutoEnable({
config: rawConfig,
env,
});
resolvedConfig = autoEnabled.config;
autoEnabledReasons = autoEnabled.autoEnabledReasons;
}
const allowlistCompatEnabled = params.compatMode.allowlist === true;
const shouldResolveCompatPluginIds =
allowlistCompatEnabled ||
params.compatMode.enablement === "always" ||
(params.compatMode.enablement === "allowlist" && allowlistCompatEnabled) ||
params.compatMode.vitest === true;
const compatPluginIds = shouldResolveCompatPluginIds
? params.resolveCompatPluginIds({
config: resolvedConfig,
workspaceDir: params.workspaceDir,
env,
onlyPluginIds: params.onlyPluginIds,
})
: [];
const config = applyPluginCompatibilityOverrides({
config: resolvedConfig,
compat: {
allowlistPluginIds: allowlistCompatEnabled ? compatPluginIds : undefined,
enablementPluginIds:
params.compatMode.enablement === "always" ||
(params.compatMode.enablement === "allowlist" && allowlistCompatEnabled)
? compatPluginIds
: undefined,
vitestPluginIds: params.compatMode.vitest ? compatPluginIds : undefined,
},
env,
});
return {
rawConfig,
config,
activationSourceConfig: rawConfig,
autoEnabledReasons,
compatPluginIds,
};
}

View File

@@ -1679,8 +1679,6 @@ module.exports = { id: "throws-after-import", register() {} };`,
});
it("can scope bundled provider loads to deepseek without hanging", () => {
resetPluginLoaderTestStateForTest();
const scoped = loadOpenClawPlugins({
cache: false,
activate: false,

View File

@@ -1,4 +1,3 @@
import { type NormalizedPluginsConfig } from "./config-state.js";
import type { PluginLoadOptions } from "./loader.js";
import type { PluginWebFetchProviderEntry } from "./types.js";
import {
@@ -26,7 +25,6 @@ export function resolveBundledWebFetchResolutionConfig(params: {
bundledAllowlistCompat?: boolean;
}): {
config: PluginLoadOptions["config"];
normalized: NormalizedPluginsConfig;
activationSourceConfig?: PluginLoadOptions["config"];
autoEnabledReasons: Record<string, string[]>;
} {

View File

@@ -2,13 +2,11 @@ import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
const mocks = vi.hoisted(() => ({
loadPluginManifestRegistry: vi.fn(),
resolveManifestContractPluginIds: vi.fn(),
}));
vi.mock("./manifest-registry.js", () => ({
loadPluginManifestRegistry: (...args: unknown[]) => mocks.loadPluginManifestRegistry(...args),
resolveManifestContractPluginIds: (...args: unknown[]) =>
mocks.resolveManifestContractPluginIds(...args),
resolveManifestContractPluginIds: vi.fn(),
}));
let resolveManifestDeclaredWebProviderCandidatePluginIds: typeof import("./web-provider-resolution-shared.js").resolveManifestDeclaredWebProviderCandidatePluginIds;
@@ -20,8 +18,6 @@ describe("resolveManifestDeclaredWebProviderCandidatePluginIds", () => {
});
beforeEach(() => {
mocks.resolveManifestContractPluginIds.mockReset();
mocks.resolveManifestContractPluginIds.mockReturnValue(["alpha"]);
mocks.loadPluginManifestRegistry.mockReset();
mocks.loadPluginManifestRegistry.mockReturnValue({
plugins: [
@@ -65,4 +61,14 @@ describe("resolveManifestDeclaredWebProviderCandidatePluginIds", () => {
}),
).toBeUndefined();
});
it("derives provider candidates from a single manifest-registry read", () => {
expect(
resolveManifestDeclaredWebProviderCandidatePluginIds({
contract: "webSearchProviders",
configKey: "webSearch",
}),
).toEqual(["alpha", "beta"]);
expect(mocks.loadPluginManifestRegistry).toHaveBeenCalledTimes(1);
});
});

View File

@@ -1,5 +1,4 @@
import { resolveBundledPluginCompatibleActivationInputs } from "./activation-context.js";
import type { NormalizedPluginsConfig } from "./config-state.js";
import { resolveBundledPluginCompatibleLoadValues } from "./activation-context.js";
import type { PluginLoadOptions } from "./loader.js";
import {
loadPluginManifestRegistry,
@@ -72,16 +71,6 @@ export function resolveManifestDeclaredWebProviderCandidatePluginIds(params: {
onlyPluginIds?: readonly string[];
origin?: PluginManifestRecord["origin"];
}): string[] | undefined {
const contractIds = new Set(
resolveManifestContractPluginIds({
contract: params.contract,
origin: params.origin,
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
onlyPluginIds: params.onlyPluginIds,
}),
);
const scopedPluginIds = normalizePluginIdScope(params.onlyPluginIds);
const onlyPluginIdSet = createPluginIdScopeSet(scopedPluginIds);
const ids = loadPluginManifestRegistry({
@@ -93,8 +82,7 @@ export function resolveManifestDeclaredWebProviderCandidatePluginIds(params: {
(plugin) =>
(!params.origin || plugin.origin === params.origin) &&
(!onlyPluginIdSet || onlyPluginIdSet.has(plugin.id)) &&
(contractIds.has(plugin.id) ||
pluginManifestDeclaresProviderConfig(plugin, params.configKey, params.contract)),
pluginManifestDeclaresProviderConfig(plugin, params.configKey, params.contract),
)
.map((plugin) => plugin.id)
.toSorted((left, right) => left.localeCompare(right));
@@ -127,11 +115,10 @@ export function resolveBundledWebProviderResolutionConfig(params: {
bundledAllowlistCompat?: boolean;
}): {
config: PluginLoadOptions["config"];
normalized: NormalizedPluginsConfig;
activationSourceConfig?: PluginLoadOptions["config"];
autoEnabledReasons: Record<string, string[]>;
} {
const activation = resolveBundledPluginCompatibleActivationInputs({
const activation = resolveBundledPluginCompatibleLoadValues({
rawConfig: params.config,
env: params.env,
workspaceDir: params.workspaceDir,
@@ -150,7 +137,6 @@ export function resolveBundledWebProviderResolutionConfig(params: {
return {
config: activation.config,
normalized: activation.normalized,
activationSourceConfig: activation.activationSourceConfig,
autoEnabledReasons: activation.autoEnabledReasons,
};

View File

@@ -1,4 +1,3 @@
import { type NormalizedPluginsConfig } from "./config-state.js";
import type { PluginLoadOptions } from "./loader.js";
import type { PluginWebSearchProviderEntry } from "./types.js";
import {
@@ -26,7 +25,6 @@ export function resolveBundledWebSearchResolutionConfig(params: {
bundledAllowlistCompat?: boolean;
}): {
config: PluginLoadOptions["config"];
normalized: NormalizedPluginsConfig;
activationSourceConfig?: PluginLoadOptions["config"];
autoEnabledReasons: Record<string, string[]>;
} {