mirror of
https://github.com/farcasclaudiu/openclaw.git
synced 2026-06-29 11:02:12 +03:00
perf(test): minimize gateway startup in vitest
This commit is contained in:
+67
-11
@@ -1,5 +1,6 @@
|
||||
import path from "node:path";
|
||||
import type { CanvasHostServer } from "../canvas-host/server.js";
|
||||
import type { PluginRegistry } from "../plugins/registry.js";
|
||||
import type { PluginServicesHandle } from "../plugins/services.js";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
import type { ControlUiRootState } from "./control-ui.js";
|
||||
@@ -31,7 +32,7 @@ import { isDiagnosticsEnabled } from "../infra/diagnostic-events.js";
|
||||
import { logAcceptedEnvOption } from "../infra/env.js";
|
||||
import { createExecApprovalForwarder } from "../infra/exec-approval-forwarder.js";
|
||||
import { onHeartbeatEvent } from "../infra/heartbeat-events.js";
|
||||
import { startHeartbeatRunner } from "../infra/heartbeat-runner.js";
|
||||
import { startHeartbeatRunner, type HeartbeatRunner } from "../infra/heartbeat-runner.js";
|
||||
import { getMachineDisplayName } from "../infra/machine-name.js";
|
||||
import { ensureOpenClawCliOnPath } from "../infra/path-env.js";
|
||||
import { setGatewaySigusr1RestartPolicy, setPreRestartDeferralCheck } from "../infra/restart.js";
|
||||
@@ -161,6 +162,9 @@ export async function startGatewayServer(
|
||||
port = 18789,
|
||||
opts: GatewayServerOptions = {},
|
||||
): Promise<GatewayServer> {
|
||||
const minimalTestGateway =
|
||||
process.env.VITEST === "1" && process.env.OPENCLAW_TEST_MINIMAL_GATEWAY === "1";
|
||||
|
||||
// Ensure all default port derivations (browser/canvas) see the actual runtime port.
|
||||
process.env.OPENCLAW_GATEWAY_PORT = String(port);
|
||||
logAcceptedEnvOption({
|
||||
@@ -235,7 +239,24 @@ export async function startGatewayServer(
|
||||
const defaultAgentId = resolveDefaultAgentId(cfgAtStart);
|
||||
const defaultWorkspaceDir = resolveAgentWorkspaceDir(cfgAtStart, defaultAgentId);
|
||||
const baseMethods = listGatewayMethods();
|
||||
const { pluginRegistry, gatewayMethods: baseGatewayMethods } = loadGatewayPlugins({
|
||||
const emptyPluginRegistry: PluginRegistry = {
|
||||
plugins: [],
|
||||
tools: [],
|
||||
hooks: [],
|
||||
typedHooks: [],
|
||||
channels: [],
|
||||
providers: [],
|
||||
gatewayHandlers: {},
|
||||
httpHandlers: [],
|
||||
httpRoutes: [],
|
||||
cliRegistrars: [],
|
||||
services: [],
|
||||
commands: [],
|
||||
diagnostics: [],
|
||||
};
|
||||
const { pluginRegistry, gatewayMethods: baseGatewayMethods } = minimalTestGateway
|
||||
? { pluginRegistry: emptyPluginRegistry, gatewayMethods: baseMethods }
|
||||
: loadGatewayPlugins({
|
||||
cfg: cfgAtStart,
|
||||
workspaceDir: defaultWorkspaceDir,
|
||||
log,
|
||||
@@ -402,6 +423,7 @@ export async function startGatewayServer(
|
||||
const { getRuntimeSnapshot, startChannels, startChannel, stopChannel, markChannelLoggedOut } =
|
||||
channelManager;
|
||||
|
||||
if (!minimalTestGateway) {
|
||||
const machineDisplayName = await getMachineDisplayName();
|
||||
const discovery = await startGatewayDiscovery({
|
||||
machineDisplayName,
|
||||
@@ -416,15 +438,20 @@ export async function startGatewayServer(
|
||||
logDiscovery,
|
||||
});
|
||||
bonjourStop = discovery.bonjourStop;
|
||||
}
|
||||
|
||||
if (!minimalTestGateway) {
|
||||
setSkillsRemoteRegistry(nodeRegistry);
|
||||
void primeRemoteSkillsCache();
|
||||
}
|
||||
// Debounce skills-triggered node probes to avoid feedback loops and rapid-fire invokes.
|
||||
// Skills changes can happen in bursts (e.g., file watcher events), and each probe
|
||||
// takes time to complete. A 30-second delay ensures we batch changes together.
|
||||
let skillsRefreshTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
const skillsRefreshDelayMs = 30_000;
|
||||
const skillsChangeUnsub = registerSkillsChangeListener((event) => {
|
||||
const skillsChangeUnsub = minimalTestGateway
|
||||
? () => {}
|
||||
: registerSkillsChangeListener((event) => {
|
||||
if (event.reason === "remote-node") {
|
||||
return;
|
||||
}
|
||||
@@ -438,7 +465,12 @@ export async function startGatewayServer(
|
||||
}, skillsRefreshDelayMs);
|
||||
});
|
||||
|
||||
const { tickInterval, healthInterval, dedupeCleanup } = startGatewayMaintenanceTimers({
|
||||
const noopInterval = () => setInterval(() => {}, 1 << 30);
|
||||
let tickInterval = noopInterval();
|
||||
let healthInterval = noopInterval();
|
||||
let dedupeCleanup = noopInterval();
|
||||
if (!minimalTestGateway) {
|
||||
({ tickInterval, healthInterval, dedupeCleanup } = startGatewayMaintenanceTimers({
|
||||
broadcast,
|
||||
nodeSendToAllSubscribed,
|
||||
getPresenceVersion,
|
||||
@@ -453,9 +485,12 @@ export async function startGatewayServer(
|
||||
removeChatRun,
|
||||
agentRunSeq,
|
||||
nodeSendToSession,
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
const agentUnsub = onAgentEvent(
|
||||
const agentUnsub = minimalTestGateway
|
||||
? null
|
||||
: onAgentEvent(
|
||||
createAgentEventHandler({
|
||||
broadcast,
|
||||
broadcastToConnIds,
|
||||
@@ -468,15 +503,25 @@ export async function startGatewayServer(
|
||||
}),
|
||||
);
|
||||
|
||||
const heartbeatUnsub = onHeartbeatEvent((evt) => {
|
||||
const heartbeatUnsub = minimalTestGateway
|
||||
? null
|
||||
: onHeartbeatEvent((evt) => {
|
||||
broadcast("heartbeat", evt, { dropIfSlow: true });
|
||||
});
|
||||
|
||||
let heartbeatRunner = startHeartbeatRunner({ cfg: cfgAtStart });
|
||||
let heartbeatRunner: HeartbeatRunner = minimalTestGateway
|
||||
? {
|
||||
stop: () => {},
|
||||
updateConfig: () => {},
|
||||
}
|
||||
: startHeartbeatRunner({ cfg: cfgAtStart });
|
||||
|
||||
if (!minimalTestGateway) {
|
||||
void cron.start().catch((err) => logCron.error(`failed to start: ${String(err)}`));
|
||||
}
|
||||
|
||||
// Recover pending outbound deliveries from previous crash/restart.
|
||||
if (!minimalTestGateway) {
|
||||
void (async () => {
|
||||
const { recoverPendingDeliveries } = await import("../infra/outbound/delivery-queue.js");
|
||||
const { deliverOutboundPayloads } = await import("../infra/outbound/deliver.js");
|
||||
@@ -487,6 +532,7 @@ export async function startGatewayServer(
|
||||
cfg: cfgAtStart,
|
||||
});
|
||||
})().catch((err) => log.error(`Delivery recovery failed: ${String(err)}`));
|
||||
}
|
||||
|
||||
const execApprovalManager = new ExecApprovalManager();
|
||||
const execApprovalForwarder = createExecApprovalForwarder();
|
||||
@@ -564,8 +610,12 @@ export async function startGatewayServer(
|
||||
log,
|
||||
isNixMode,
|
||||
});
|
||||
if (!minimalTestGateway) {
|
||||
scheduleGatewayUpdateCheck({ cfg: cfgAtStart, log, isNixMode });
|
||||
const tailscaleCleanup = await startGatewayTailscaleExposure({
|
||||
}
|
||||
const tailscaleCleanup = minimalTestGateway
|
||||
? null
|
||||
: await startGatewayTailscaleExposure({
|
||||
tailscaleMode,
|
||||
resetOnExit: tailscaleConfig.resetOnExit,
|
||||
port,
|
||||
@@ -574,6 +624,7 @@ export async function startGatewayServer(
|
||||
});
|
||||
|
||||
let browserControl: Awaited<ReturnType<typeof startBrowserControlServerIfEnabled>> = null;
|
||||
if (!minimalTestGateway) {
|
||||
({ browserControl, pluginServices } = await startGatewaySidecars({
|
||||
cfg: cfgAtStart,
|
||||
pluginRegistry,
|
||||
@@ -585,9 +636,10 @@ export async function startGatewayServer(
|
||||
logChannels,
|
||||
logBrowser,
|
||||
}));
|
||||
}
|
||||
|
||||
// Run gateway_start plugin hook (fire-and-forget)
|
||||
{
|
||||
if (!minimalTestGateway) {
|
||||
const hookRunner = getGlobalHookRunner();
|
||||
if (hookRunner?.hasHooks("gateway_start")) {
|
||||
void hookRunner.runGatewayStart({ port }, { port }).catch((err) => {
|
||||
@@ -596,6 +648,9 @@ export async function startGatewayServer(
|
||||
}
|
||||
}
|
||||
|
||||
const configReloader = minimalTestGateway
|
||||
? { stop: async () => {} }
|
||||
: (() => {
|
||||
const { applyHotReload, requestGatewayRestart } = createGatewayReloadHandlers({
|
||||
deps,
|
||||
broadcast,
|
||||
@@ -622,7 +677,7 @@ export async function startGatewayServer(
|
||||
logReload,
|
||||
});
|
||||
|
||||
const configReloader = startGatewayConfigReloader({
|
||||
return startGatewayConfigReloader({
|
||||
initialConfig: cfgAtStart,
|
||||
readSnapshot: readConfigFileSnapshot,
|
||||
onHotReload: applyHotReload,
|
||||
@@ -634,6 +689,7 @@ export async function startGatewayServer(
|
||||
},
|
||||
watchPath: CONFIG_PATH,
|
||||
});
|
||||
})();
|
||||
|
||||
const close = createGatewayCloseHandler({
|
||||
bonjourStop,
|
||||
|
||||
@@ -50,6 +50,10 @@ let previousSkipBrowserControl: string | undefined;
|
||||
let previousSkipGmailWatcher: string | undefined;
|
||||
let previousSkipCanvasHost: string | undefined;
|
||||
let previousBundledPluginsDir: string | undefined;
|
||||
let previousSkipChannels: string | undefined;
|
||||
let previousSkipProviders: string | undefined;
|
||||
let previousSkipCron: string | undefined;
|
||||
let previousMinimalGateway: string | undefined;
|
||||
let tempHome: string | undefined;
|
||||
let tempConfigRoot: string | undefined;
|
||||
|
||||
@@ -90,6 +94,10 @@ async function setupGatewayTestHome() {
|
||||
previousSkipGmailWatcher = process.env.OPENCLAW_SKIP_GMAIL_WATCHER;
|
||||
previousSkipCanvasHost = process.env.OPENCLAW_SKIP_CANVAS_HOST;
|
||||
previousBundledPluginsDir = process.env.OPENCLAW_BUNDLED_PLUGINS_DIR;
|
||||
previousSkipChannels = process.env.OPENCLAW_SKIP_CHANNELS;
|
||||
previousSkipProviders = process.env.OPENCLAW_SKIP_PROVIDERS;
|
||||
previousSkipCron = process.env.OPENCLAW_SKIP_CRON;
|
||||
previousMinimalGateway = process.env.OPENCLAW_TEST_MINIMAL_GATEWAY;
|
||||
tempHome = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-gateway-home-"));
|
||||
process.env.HOME = tempHome;
|
||||
process.env.USERPROFILE = tempHome;
|
||||
@@ -101,6 +109,10 @@ function applyGatewaySkipEnv() {
|
||||
process.env.OPENCLAW_SKIP_BROWSER_CONTROL_SERVER = "1";
|
||||
process.env.OPENCLAW_SKIP_GMAIL_WATCHER = "1";
|
||||
process.env.OPENCLAW_SKIP_CANVAS_HOST = "1";
|
||||
process.env.OPENCLAW_SKIP_CHANNELS = "1";
|
||||
process.env.OPENCLAW_SKIP_PROVIDERS = "1";
|
||||
process.env.OPENCLAW_SKIP_CRON = "1";
|
||||
process.env.OPENCLAW_TEST_MINIMAL_GATEWAY = "1";
|
||||
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = tempHome
|
||||
? path.join(tempHome, "openclaw-test-no-bundled-extensions")
|
||||
: "openclaw-test-no-bundled-extensions";
|
||||
@@ -203,6 +215,26 @@ async function cleanupGatewayTestHome(options: { restoreEnv: boolean }) {
|
||||
} else {
|
||||
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = previousBundledPluginsDir;
|
||||
}
|
||||
if (previousSkipChannels === undefined) {
|
||||
delete process.env.OPENCLAW_SKIP_CHANNELS;
|
||||
} else {
|
||||
process.env.OPENCLAW_SKIP_CHANNELS = previousSkipChannels;
|
||||
}
|
||||
if (previousSkipProviders === undefined) {
|
||||
delete process.env.OPENCLAW_SKIP_PROVIDERS;
|
||||
} else {
|
||||
process.env.OPENCLAW_SKIP_PROVIDERS = previousSkipProviders;
|
||||
}
|
||||
if (previousSkipCron === undefined) {
|
||||
delete process.env.OPENCLAW_SKIP_CRON;
|
||||
} else {
|
||||
process.env.OPENCLAW_SKIP_CRON = previousSkipCron;
|
||||
}
|
||||
if (previousMinimalGateway === undefined) {
|
||||
delete process.env.OPENCLAW_TEST_MINIMAL_GATEWAY;
|
||||
} else {
|
||||
process.env.OPENCLAW_TEST_MINIMAL_GATEWAY = previousMinimalGateway;
|
||||
}
|
||||
}
|
||||
if (options.restoreEnv && tempHome) {
|
||||
await fs.rm(tempHome, {
|
||||
|
||||
Reference in New Issue
Block a user