mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:40:44 +00:00
test: keep gateway suites minimal
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { isVitestRuntimeEnv } from "../infra/env.js";
|
||||
import { startHeartbeatRunner, type HeartbeatRunner } from "../infra/heartbeat-runner.js";
|
||||
import type { ChannelHealthMonitor } from "./channel-health-monitor.js";
|
||||
import { startChannelHealthMonitor } from "./channel-health-monitor.js";
|
||||
@@ -87,7 +88,7 @@ export function startGatewayRuntimeServices(params: {
|
||||
heartbeatRunner: createNoopHeartbeatRunner(),
|
||||
channelHealthMonitor,
|
||||
stopModelPricingRefresh:
|
||||
!params.minimalTestGateway && process.env.VITEST !== "1"
|
||||
!params.minimalTestGateway && !isVitestRuntimeEnv()
|
||||
? startGatewayModelPricingRefresh({ config: params.cfgAtStart })
|
||||
: () => {},
|
||||
};
|
||||
|
||||
@@ -15,7 +15,6 @@ import {
|
||||
} from "./server-session-events.js";
|
||||
|
||||
export function startGatewayEventSubscriptions(params: {
|
||||
minimalTestGateway: boolean;
|
||||
broadcast: (event: string, payload: unknown, opts?: { dropIfSlow?: boolean }) => void;
|
||||
broadcastToConnIds: (
|
||||
event: string,
|
||||
@@ -33,47 +32,39 @@ export function startGatewayEventSubscriptions(params: {
|
||||
sessionMessageSubscribers: SessionMessageSubscriberRegistry;
|
||||
chatAbortControllers: Map<string, unknown>;
|
||||
}) {
|
||||
const agentUnsub = params.minimalTestGateway
|
||||
? null
|
||||
: onAgentEvent(
|
||||
createAgentEventHandler({
|
||||
broadcast: params.broadcast,
|
||||
broadcastToConnIds: params.broadcastToConnIds,
|
||||
nodeSendToSession: params.nodeSendToSession,
|
||||
agentRunSeq: params.agentRunSeq,
|
||||
chatRunState: params.chatRunState,
|
||||
resolveSessionKeyForRun: params.resolveSessionKeyForRun,
|
||||
clearAgentRunContext: params.clearAgentRunContext,
|
||||
toolEventRecipients: params.toolEventRecipients,
|
||||
sessionEventSubscribers: params.sessionEventSubscribers,
|
||||
isChatSendRunActive: (runId) => params.chatAbortControllers.has(runId),
|
||||
}),
|
||||
);
|
||||
const agentUnsub = onAgentEvent(
|
||||
createAgentEventHandler({
|
||||
broadcast: params.broadcast,
|
||||
broadcastToConnIds: params.broadcastToConnIds,
|
||||
nodeSendToSession: params.nodeSendToSession,
|
||||
agentRunSeq: params.agentRunSeq,
|
||||
chatRunState: params.chatRunState,
|
||||
resolveSessionKeyForRun: params.resolveSessionKeyForRun,
|
||||
clearAgentRunContext: params.clearAgentRunContext,
|
||||
toolEventRecipients: params.toolEventRecipients,
|
||||
sessionEventSubscribers: params.sessionEventSubscribers,
|
||||
isChatSendRunActive: (runId) => params.chatAbortControllers.has(runId),
|
||||
}),
|
||||
);
|
||||
|
||||
const heartbeatUnsub = params.minimalTestGateway
|
||||
? null
|
||||
: onHeartbeatEvent((evt) => {
|
||||
params.broadcast("heartbeat", evt, { dropIfSlow: true });
|
||||
});
|
||||
const heartbeatUnsub = onHeartbeatEvent((evt) => {
|
||||
params.broadcast("heartbeat", evt, { dropIfSlow: true });
|
||||
});
|
||||
|
||||
const transcriptUnsub = params.minimalTestGateway
|
||||
? null
|
||||
: onSessionTranscriptUpdate(
|
||||
createTranscriptUpdateBroadcastHandler({
|
||||
broadcastToConnIds: params.broadcastToConnIds,
|
||||
sessionEventSubscribers: params.sessionEventSubscribers,
|
||||
sessionMessageSubscribers: params.sessionMessageSubscribers,
|
||||
}),
|
||||
);
|
||||
const transcriptUnsub = onSessionTranscriptUpdate(
|
||||
createTranscriptUpdateBroadcastHandler({
|
||||
broadcastToConnIds: params.broadcastToConnIds,
|
||||
sessionEventSubscribers: params.sessionEventSubscribers,
|
||||
sessionMessageSubscribers: params.sessionMessageSubscribers,
|
||||
}),
|
||||
);
|
||||
|
||||
const lifecycleUnsub = params.minimalTestGateway
|
||||
? null
|
||||
: onSessionLifecycleEvent(
|
||||
createLifecycleEventBroadcastHandler({
|
||||
broadcastToConnIds: params.broadcastToConnIds,
|
||||
sessionEventSubscribers: params.sessionEventSubscribers,
|
||||
}),
|
||||
);
|
||||
const lifecycleUnsub = onSessionLifecycleEvent(
|
||||
createLifecycleEventBroadcastHandler({
|
||||
broadcastToConnIds: params.broadcastToConnIds,
|
||||
sessionEventSubscribers: params.sessionEventSubscribers,
|
||||
}),
|
||||
);
|
||||
|
||||
return {
|
||||
agentUnsub,
|
||||
|
||||
@@ -310,33 +310,36 @@ export function registerControlUiAndPairingSuite(): void {
|
||||
});
|
||||
});
|
||||
|
||||
test("allows localhost control ui without device identity when insecure auth is enabled", async () => {
|
||||
test("allows localhost ui clients without device identity when insecure auth is enabled", async () => {
|
||||
testState.gatewayControlUi = { allowInsecureAuth: true };
|
||||
const { server, ws, prevToken } = await startControlUiServerWithClient("secret", {
|
||||
const { server, ws, port, prevToken } = await startControlUiServerWithClient("secret", {
|
||||
wsHeaders: { origin: "http://127.0.0.1" },
|
||||
});
|
||||
await connectControlUiWithoutDeviceAndExpectOk({ ws, token: "secret" });
|
||||
ws.close();
|
||||
await server.close();
|
||||
restoreGatewayToken(prevToken);
|
||||
});
|
||||
let tuiWs: WebSocket | undefined;
|
||||
try {
|
||||
await connectControlUiWithoutDeviceAndExpectOk({ ws, token: "secret" });
|
||||
|
||||
test("allows localhost tui without device identity when insecure auth is enabled", async () => {
|
||||
testState.gatewayControlUi = { allowInsecureAuth: true };
|
||||
const { server, ws, prevToken } = await startControlUiServerWithClient("secret");
|
||||
await connectControlUiWithoutDeviceAndExpectOk({
|
||||
ws,
|
||||
token: "secret",
|
||||
client: {
|
||||
id: GATEWAY_CLIENT_NAMES.TUI,
|
||||
version: "1.0.0",
|
||||
platform: "darwin",
|
||||
mode: GATEWAY_CLIENT_MODES.UI,
|
||||
},
|
||||
});
|
||||
ws.close();
|
||||
await server.close();
|
||||
restoreGatewayToken(prevToken);
|
||||
tuiWs = await openWs(port);
|
||||
await connectControlUiWithoutDeviceAndExpectOk({
|
||||
ws: tuiWs,
|
||||
token: "secret",
|
||||
client: {
|
||||
id: GATEWAY_CLIENT_NAMES.TUI,
|
||||
version: "1.0.0",
|
||||
platform: "darwin",
|
||||
mode: GATEWAY_CLIENT_MODES.UI,
|
||||
},
|
||||
});
|
||||
} finally {
|
||||
ws.close();
|
||||
tuiWs?.close();
|
||||
await Promise.all([
|
||||
waitForWsClose(ws, 1_000),
|
||||
...(tuiWs ? [waitForWsClose(tuiWs, 1_000)] : []),
|
||||
]);
|
||||
await server.close();
|
||||
restoreGatewayToken(prevToken);
|
||||
}
|
||||
});
|
||||
|
||||
test("allows control ui password-only auth on localhost when insecure auth is enabled", async () => {
|
||||
@@ -1322,16 +1325,35 @@ export function registerControlUiAndPairingSuite(): void {
|
||||
}
|
||||
});
|
||||
|
||||
test("allows local gateway backend shared-auth connections without device pairing", async () => {
|
||||
const { server, ws, prevToken } = await startControlUiServerWithClient("secret");
|
||||
test("allows gateway backend loopback shared-auth connections without device pairing", async () => {
|
||||
const { server, ws, port, prevToken } = await startControlUiServerWithClient("secret");
|
||||
const sockets = [ws];
|
||||
try {
|
||||
const localBackend = await connectReq(ws, {
|
||||
token: "secret",
|
||||
client: BACKEND_GATEWAY_CLIENT,
|
||||
});
|
||||
expect(localBackend.ok).toBe(true);
|
||||
const backendCases: Array<{
|
||||
name: string;
|
||||
headers?: Record<string, string>;
|
||||
socket?: WebSocket;
|
||||
}> = [
|
||||
{ name: "default host", socket: ws },
|
||||
{ name: "remote-looking host", headers: { host: "gateway.example" } },
|
||||
{ name: "private host", headers: { host: "172.17.0.2:18789" } },
|
||||
];
|
||||
|
||||
for (const backendCase of backendCases) {
|
||||
const socket = backendCase.socket ?? (await openWs(port, backendCase.headers));
|
||||
if (!backendCase.socket) {
|
||||
sockets.push(socket);
|
||||
}
|
||||
const backendConnect = await connectReq(socket, {
|
||||
token: "secret",
|
||||
client: BACKEND_GATEWAY_CLIENT,
|
||||
});
|
||||
expect(backendConnect.ok, backendCase.name).toBe(true);
|
||||
}
|
||||
} finally {
|
||||
ws.close();
|
||||
for (const socket of sockets) {
|
||||
socket.close();
|
||||
}
|
||||
await server.close();
|
||||
restoreGatewayToken(prevToken);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import { applyPluginAutoEnable } from "../config/plugin-auto-enable.js";
|
||||
import { resolveMainSessionKey } from "../config/sessions.js";
|
||||
import { clearAgentRunContext } from "../infra/agent-events.js";
|
||||
import { isDiagnosticsEnabled } from "../infra/diagnostic-events.js";
|
||||
import { logAcceptedEnvOption } from "../infra/env.js";
|
||||
import { isVitestRuntimeEnv, logAcceptedEnvOption } from "../infra/env.js";
|
||||
import { ensureOpenClawCliOnPath } from "../infra/path-env.js";
|
||||
import { setGatewaySigusr1RestartPolicy, setPreRestartDeferralCheck } from "../infra/restart.js";
|
||||
import { enqueueSystemEvent } from "../infra/system-events.js";
|
||||
@@ -208,7 +208,7 @@ export async function startGatewayServer(
|
||||
opts: GatewayServerOptions = {},
|
||||
): Promise<GatewayServer> {
|
||||
const minimalTestGateway =
|
||||
process.env.VITEST === "1" && process.env.OPENCLAW_TEST_MINIMAL_GATEWAY === "1";
|
||||
isVitestRuntimeEnv() && 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);
|
||||
@@ -599,7 +599,6 @@ export async function startGatewayServer(
|
||||
Object.assign(
|
||||
runtimeState,
|
||||
startGatewayEventSubscriptions({
|
||||
minimalTestGateway,
|
||||
broadcast,
|
||||
broadcastToConnIds,
|
||||
nodeSendToSession,
|
||||
|
||||
@@ -542,9 +542,11 @@ describe("gateway server misc", () => {
|
||||
"utf-8",
|
||||
);
|
||||
|
||||
const autoPort = await getFreePort();
|
||||
const autoServer = await startGatewayServer(autoPort);
|
||||
await autoServer.close();
|
||||
await withEnvAsync({ OPENCLAW_TEST_MINIMAL_GATEWAY: undefined }, async () => {
|
||||
const autoPort = await getFreePort();
|
||||
const autoServer = await startGatewayServer(autoPort);
|
||||
await autoServer.close();
|
||||
});
|
||||
|
||||
const updated = JSON.parse(await fs.readFile(configPath, "utf-8")) as Record<string, unknown>;
|
||||
const channels = updated.channels as Record<string, unknown> | undefined;
|
||||
|
||||
@@ -4,6 +4,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { WebSocket } from "ws";
|
||||
import { resolveMainSessionKeyFromConfig } from "../config/sessions.js";
|
||||
import { drainSystemEvents } from "../infra/system-events.js";
|
||||
import { withEnvAsync } from "../test-utils/env.js";
|
||||
import {
|
||||
TALK_TEST_PROVIDER_API_KEY_PATH,
|
||||
TALK_TEST_PROVIDER_ID,
|
||||
@@ -369,8 +370,16 @@ describe("gateway hot reload", () => {
|
||||
);
|
||||
}
|
||||
|
||||
async function withNonMinimalGatewayServer(
|
||||
fn: Parameters<typeof withGatewayServer>[0],
|
||||
): ReturnType<typeof withGatewayServer> {
|
||||
return await withEnvAsync({ OPENCLAW_TEST_MINIMAL_GATEWAY: undefined }, async () =>
|
||||
withGatewayServer(fn),
|
||||
);
|
||||
}
|
||||
|
||||
it("applies hot reload actions and emits restart signal", async () => {
|
||||
await withGatewayServer(async () => {
|
||||
await withNonMinimalGatewayServer(async () => {
|
||||
const onHotReload = hoisted.getOnHotReload();
|
||||
expect(onHotReload).toBeTypeOf("function");
|
||||
|
||||
@@ -473,7 +482,7 @@ describe("gateway hot reload", () => {
|
||||
await writeEnvRefConfig();
|
||||
process.env.OPENAI_API_KEY = "sk-startup"; // pragma: allowlist secret
|
||||
|
||||
await withGatewayServer(async () => {
|
||||
await withNonMinimalGatewayServer(async () => {
|
||||
const onHotReload = hoisted.getOnHotReload();
|
||||
expect(onHotReload).toBeTypeOf("function");
|
||||
const sessionKey = resolveMainSessionKeyFromConfig();
|
||||
|
||||
@@ -67,6 +67,16 @@ export function isTruthyEnvValue(value?: string): boolean {
|
||||
}
|
||||
}
|
||||
|
||||
export function isVitestRuntimeEnv(env: NodeJS.ProcessEnv = process.env): boolean {
|
||||
return (
|
||||
env.VITEST === "true" ||
|
||||
env.VITEST === "1" ||
|
||||
env.VITEST_POOL_ID !== undefined ||
|
||||
env.VITEST_WORKER_ID !== undefined ||
|
||||
env.NODE_ENV === "test"
|
||||
);
|
||||
}
|
||||
|
||||
export function normalizeEnv(): void {
|
||||
normalizeZaiEnv();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user