test: speed up cli and command suites

This commit is contained in:
Peter Steinberger
2026-03-31 02:12:23 +01:00
parent 6b6ddcd2a6
commit 3f1d6fe147
83 changed files with 1161 additions and 1054 deletions

View File

@@ -2,7 +2,7 @@ import { Command } from "commander";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { withEnvOverride } from "../config/test-helpers.js";
import { GatewayLockError } from "../infra/gateway-lock.js";
import { createCliRuntimeCapture } from "./test-runtime-capture.js";
import { registerGatewayCli } from "./gateway-cli.js";
type DiscoveredBeacon = Awaited<
ReturnType<typeof import("../infra/bonjour-discovery.js").discoverGatewayBeacons>
@@ -30,8 +30,31 @@ const gatewayStatusCommand = vi.fn<(opts: unknown) => Promise<void>>(async () =>
const inspectPortUsage = vi.fn(async (_port: number) => ({ status: "free" as const }));
const formatPortDiagnostics = vi.fn((_diagnostics: unknown) => [] as string[]);
const { runtimeLogs, runtimeErrors, defaultRuntime, resetRuntimeCapture } =
createCliRuntimeCapture();
const mocks = vi.hoisted(() => {
const runtimeLogs: string[] = [];
const runtimeErrors: string[] = [];
const stringifyArgs = (args: unknown[]) => args.map((value) => String(value)).join(" ");
const defaultRuntime = {
log: vi.fn((...args: unknown[]) => {
runtimeLogs.push(stringifyArgs(args));
}),
error: vi.fn((...args: unknown[]) => {
runtimeErrors.push(stringifyArgs(args));
}),
writeStdout: vi.fn((value: string) => {
defaultRuntime.log(value.endsWith("\n") ? value.slice(0, -1) : value);
}),
writeJson: vi.fn((value: unknown, space = 2) => {
defaultRuntime.log(JSON.stringify(value, null, space > 0 ? space : undefined));
}),
exit: vi.fn((code: number) => {
throw new Error(`__exit__:${code}`);
}),
};
return { runtimeLogs, runtimeErrors, defaultRuntime };
});
const { runtimeLogs, runtimeErrors, defaultRuntime } = mocks;
vi.mock(
new URL("../../gateway/call.ts", new URL("./gateway-cli/call.ts", import.meta.url)).href,
@@ -53,7 +76,7 @@ vi.mock("../globals.js", () => ({
vi.mock("../runtime.js", async (importOriginal) => ({
...(await importOriginal<typeof import("../runtime.js")>()),
defaultRuntime,
defaultRuntime: mocks.defaultRuntime,
}));
vi.mock("./ports.js", () => ({
@@ -96,7 +119,6 @@ vi.mock("../infra/ports.js", () => ({
formatPortDiagnostics: (diagnostics: unknown) => formatPortDiagnostics(diagnostics),
}));
const { registerGatewayCli } = await import("./gateway-cli.js");
let gatewayProgram: Command;
function createGatewayProgram() {
@@ -117,12 +139,18 @@ async function expectGatewayExit(args: string[]) {
describe("gateway-cli coverage", () => {
beforeEach(() => {
gatewayProgram = createGatewayProgram();
runtimeLogs.length = 0;
runtimeErrors.length = 0;
defaultRuntime.log.mockClear();
defaultRuntime.error.mockClear();
defaultRuntime.writeStdout.mockClear();
defaultRuntime.writeJson.mockClear();
defaultRuntime.exit.mockClear();
inspectPortUsage.mockClear();
formatPortDiagnostics.mockClear();
});
it("registers call/health commands and routes to callGateway", async () => {
resetRuntimeCapture();
callGateway.mockClear();
await runGatewayCommand(["gateway", "call", "health", "--params", '{"x":1}', "--json"]);
@@ -132,7 +160,6 @@ describe("gateway-cli coverage", () => {
});
it("registers gateway probe and routes to gatewayStatusCommand", async () => {
resetRuntimeCapture();
gatewayStatusCommand.mockClear();
await runGatewayCommand(["gateway", "probe", "--json"]);
@@ -141,7 +168,6 @@ describe("gateway-cli coverage", () => {
});
it("registers gateway discover and prints json output", async () => {
resetRuntimeCapture();
discoverGatewayBeacons.mockClear();
discoverGatewayBeacons.mockResolvedValueOnce([
{
@@ -166,7 +192,6 @@ describe("gateway-cli coverage", () => {
});
it("validates gateway discover timeout", async () => {
resetRuntimeCapture();
discoverGatewayBeacons.mockClear();
await expectGatewayExit(["gateway", "discover", "--timeout", "0"]);
@@ -175,7 +200,6 @@ describe("gateway-cli coverage", () => {
});
it("fails gateway call on invalid params JSON", async () => {
resetRuntimeCapture();
callGateway.mockClear();
await expectGatewayExit(["gateway", "call", "status", "--params", "not-json"]);
@@ -184,8 +208,6 @@ describe("gateway-cli coverage", () => {
});
it("validates gateway ports and handles force/start errors", async () => {
resetRuntimeCapture();
// Invalid port
await expectGatewayExit(["gateway", "--port", "0", "--token", "test-token"]);
@@ -243,7 +265,6 @@ describe("gateway-cli coverage", () => {
OPENCLAW_SERVICE_KIND: undefined,
},
async () => {
resetRuntimeCapture();
serviceIsLoaded.mockResolvedValue(true);
startGatewayServer.mockRejectedValueOnce(
new GatewayLockError("another gateway instance is already listening"),
@@ -260,7 +281,8 @@ describe("gateway-cli coverage", () => {
});
it("keeps exit 1 for gateway bind failures wrapped as GatewayLockError", async () => {
resetRuntimeCapture();
runtimeLogs.length = 0;
runtimeErrors.length = 0;
serviceIsLoaded.mockResolvedValue(true);
startGatewayServer.mockRejectedValueOnce(
new GatewayLockError("failed to bind gateway socket on ws://127.0.0.1:18789: Error: boom"),
@@ -272,7 +294,8 @@ describe("gateway-cli coverage", () => {
});
it("keeps exit 1 for gateway lock acquisition failures", async () => {
resetRuntimeCapture();
runtimeLogs.length = 0;
runtimeErrors.length = 0;
serviceIsLoaded.mockResolvedValue(true);
startGatewayServer.mockRejectedValueOnce(
new GatewayLockError("failed to acquire gateway lock at /tmp/openclaw/gateway.lock"),
@@ -285,7 +308,8 @@ describe("gateway-cli coverage", () => {
it("uses env/config port when --port is omitted", async () => {
await withEnvOverride({ OPENCLAW_GATEWAY_PORT: "19001" }, async () => {
resetRuntimeCapture();
runtimeLogs.length = 0;
runtimeErrors.length = 0;
startGatewayServer.mockClear();
startGatewayServer.mockRejectedValueOnce(new Error("nope"));