Download src/cli/gateway-cli/register.option-collisions.test.ts from SaylorTwift/openclaw: direct link, hf CLI and curl.
- Browser
- Download file 16.8 kB
-
https://huggingface.co/SaylorTwift/openclaw/resolve/main/src/cli/gateway-cli/register.option-collisions.test.ts
- Command line
-
hf download hf://SaylorTwift/openclaw/src/cli/gateway-cli/register.option-collisions.test.ts
-
curl -L -o register.option-collisions.test.ts https://huggingface.co/SaylorTwift/openclaw/resolve/main/src/cli/gateway-cli/register.option-collisions.test.ts
16.8 kB
| // Gateway register option collision tests cover gateway command option registration. | |
| import { Command } from "commander"; | |
| import { beforeEach, describe, expect, it, vi } from "vitest"; | |
| import { registerGatewayCli } from "./register.js"; | |
| const mocks = vi.hoisted(() => ({ | |
| callGatewayCli: vi.fn(async (method: string, _opts: unknown, _params?: unknown) => { | |
| if (method === "gateway.suspend.prepare") { | |
| return { | |
| status: "ready", | |
| suspensionId: "suspension-1", | |
| expiresAtMs: 1_800_000_000_000, | |
| activeCount: 0, | |
| blockers: [], | |
| }; | |
| } | |
| if (method === "gateway.suspend.resume") { | |
| return { ok: true, status: "running", resumed: true }; | |
| } | |
| return { ok: true }; | |
| }), | |
| emitReachableGatewayAuthDiagnostic: vi.fn(async (_params: unknown) => false), | |
| formatHealthChannelLines: vi.fn(() => []), | |
| gatewayStatusCommand: vi.fn(async (_opts: unknown, _runtime: unknown) => {}), | |
| gatewayAuthTokenCommand: vi.fn(async (_runtime: unknown) => {}), | |
| defaultRuntime: { | |
| log: vi.fn(), | |
| error: vi.fn(), | |
| writeStdout: vi.fn(), | |
| writeJson: vi.fn(), | |
| exit: vi.fn(), | |
| }, | |
| })); | |
| const { callGatewayCli, emitReachableGatewayAuthDiagnostic, gatewayStatusCommand, defaultRuntime } = | |
| mocks; | |
| vi.mock("../cli-utils.js", () => ({ | |
| runCommandWithRuntime: async ( | |
| _runtime: unknown, | |
| action: () => Promise<void>, | |
| onError: (err: unknown) => void, | |
| ) => { | |
| try { | |
| await action(); | |
| } catch (err) { | |
| onError(err); | |
| } | |
| }, | |
| })); | |
| vi.mock("../../runtime.js", async () => ({ | |
| ...(await vi.importActual<typeof import("../../runtime.js")>("../../runtime.js")), | |
| defaultRuntime: mocks.defaultRuntime, | |
| })); | |
| vi.mock("../../commands/gateway-status.js", () => ({ | |
| gatewayStatusCommand: (opts: unknown, runtime: unknown) => | |
| mocks.gatewayStatusCommand(opts, runtime), | |
| })); | |
| vi.mock("../../commands/gateway-auth-token.js", () => ({ | |
| gatewayAuthTokenCommand: (runtime: unknown) => mocks.gatewayAuthTokenCommand(runtime), | |
| })); | |
| vi.mock("../gateway-rpc.js", async () => ({ | |
| ...(await vi.importActual<typeof import("../gateway-rpc.js")>("../gateway-rpc.js")), | |
| callGatewayFromCliWithTransport: (method: string, opts: unknown, params?: unknown) => | |
| mocks.callGatewayCli(method, opts, params), | |
| })); | |
| vi.mock("./run-command.js", () => ({ | |
| addGatewayRunCommand: (cmd: Command) => | |
| cmd | |
| .option("--port <port>", "Port for the gateway WebSocket") | |
| .option("--token <token>", "Gateway token") | |
| .option("--password <password>", "Gateway password"), | |
| })); | |
| vi.mock("../daemon-cli/register-service-commands.js", () => ({ | |
| addGatewayServiceCommands: () => undefined, | |
| })); | |
| vi.mock("../../commands/health.js", () => ({ | |
| emitReachableGatewayAuthDiagnostic: (params: unknown) => | |
| mocks.emitReachableGatewayAuthDiagnostic(params), | |
| formatHealthChannelLines: () => mocks.formatHealthChannelLines(), | |
| readNonObservingHealthConfig: async () => ({}), | |
| })); | |
| vi.mock("../../config/read-best-effort-config.runtime.js", () => ({ | |
| readBestEffortConfig: async () => ({}), | |
| readSourceConfigBestEffort: async () => ({}), | |
| })); | |
| vi.mock("../../infra/bonjour-discovery.js", () => ({ | |
| discoverGatewayBeacons: async () => [], | |
| })); | |
| vi.mock("../../infra/widearea-dns.js", () => ({ | |
| resolveWideAreaDiscoveryDomain: () => undefined, | |
| })); | |
| vi.mock("../../../packages/terminal-core/src/health-style.js", () => ({ | |
| styleHealthChannelLine: (line: string) => line, | |
| })); | |
| vi.mock("../../../packages/terminal-core/src/links.js", () => ({ | |
| formatDocsLink: () => "docs.openclaw.ai/cli/gateway", | |
| })); | |
| vi.mock("../../../packages/terminal-core/src/theme.js", () => ({ | |
| colorize: (_rich: boolean, _fn: (value: string) => string, value: string) => value, | |
| isRich: () => false, | |
| theme: { | |
| heading: (value: string) => value, | |
| muted: (value: string) => value, | |
| success: (value: string) => value, | |
| }, | |
| })); | |
| vi.mock("../../utils/usage-format.js", () => ({ | |
| formatTokenCount: () => "0", | |
| formatUsd: () => "$0.00", | |
| })); | |
| vi.mock("../help-format.js", () => ({ | |
| formatHelpExamples: () => "", | |
| })); | |
| vi.mock("../progress.js", () => ({ | |
| withProgress: async (_opts: unknown, fn: () => Promise<unknown>) => await fn(), | |
| })); | |
| vi.mock("./discover.js", () => ({ | |
| dedupeBeacons: (beacons: unknown[]) => beacons, | |
| parseDiscoverTimeoutMs: () => 2000, | |
| renderBeaconLines: () => [], | |
| })); | |
| function firstGatewayCall() { | |
| return callGatewayCli.mock.calls[0] ?? []; | |
| } | |
| function firstGatewayStatusCall() { | |
| return gatewayStatusCommand.mock.calls[0] ?? []; | |
| } | |
| function expectLocalGatewayCall(method: string, port: number, params?: unknown) { | |
| expect(defaultRuntime.error.mock.calls).toEqual([]); | |
| expect(callGatewayCli).toHaveBeenCalledTimes(1); | |
| const [actualMethod, opts, actualParams] = firstGatewayCall(); | |
| expect(actualMethod).toBe(method); | |
| if (params !== undefined) { | |
| expect(actualParams).toEqual(params); | |
| } | |
| const gatewayOpts = opts as { localPortOverride?: number } | undefined; | |
| expect(gatewayOpts?.localPortOverride).toBe(port); | |
| } | |
| describe("gateway register option collisions", () => { | |
| const sharedProgram: Command = new Command(); | |
| if (sharedProgram.commands.length === 0) { | |
| sharedProgram.exitOverride(); | |
| registerGatewayCli(sharedProgram); | |
| } | |
| beforeEach(() => { | |
| callGatewayCli.mockClear(); | |
| emitReachableGatewayAuthDiagnostic.mockClear(); | |
| mocks.formatHealthChannelLines.mockClear(); | |
| gatewayStatusCommand.mockClear(); | |
| mocks.gatewayAuthTokenCommand.mockClear(); | |
| defaultRuntime.log.mockClear(); | |
| defaultRuntime.error.mockClear(); | |
| defaultRuntime.writeStdout.mockClear(); | |
| defaultRuntime.writeJson.mockClear(); | |
| defaultRuntime.exit.mockClear(); | |
| }); | |
| it("requires explicit confirmation before revealing the Gateway token", async () => { | |
| await sharedProgram.parseAsync(["gateway", "auth-token"], { from: "user" }); | |
| expect(mocks.gatewayAuthTokenCommand).not.toHaveBeenCalled(); | |
| expect(defaultRuntime.error).toHaveBeenCalledWith( | |
| expect.stringContaining("Pass --show to confirm"), | |
| ); | |
| expect(defaultRuntime.exit).toHaveBeenCalledWith(1); | |
| }); | |
| it("routes an explicitly confirmed token reveal through the output runtime", async () => { | |
| await sharedProgram.parseAsync(["gateway", "auth-token", "--show"], { from: "user" }); | |
| expect(mocks.gatewayAuthTokenCommand).toHaveBeenCalledWith(defaultRuntime); | |
| expect(defaultRuntime.error).not.toHaveBeenCalled(); | |
| }); | |
| it.each([ | |
| { | |
| name: "forwards the expected endpoint without overriding configured routing", | |
| argv: ["gateway", "call", "chat.send", "--expect-url", "wss://gateway.example/ws", "--json"], | |
| assert: () => { | |
| expect(callGatewayCli).toHaveBeenCalledTimes(1); | |
| const [method, opts] = firstGatewayCall(); | |
| expect(method).toBe("chat.send"); | |
| expect(opts).toMatchObject({ expectUrl: "wss://gateway.example/ws" }); | |
| expect(opts).not.toHaveProperty("url"); | |
| }, | |
| }, | |
| { | |
| name: "forwards --token to gateway call when parent and child option names collide", | |
| argv: ["gateway", "call", "health", "--token", "tok_call", "--json"], | |
| assert: () => { | |
| expect(callGatewayCli).toHaveBeenCalledTimes(1); | |
| const [method, opts, params] = firstGatewayCall(); | |
| expect(method).toBe("health"); | |
| expect((opts as { token?: string } | undefined)?.token).toBe("tok_call"); | |
| expect(params).toEqual({}); | |
| }, | |
| }, | |
| { | |
| name: "gives setup detection enough transport grace", | |
| argv: ["gateway", "call", "openclaw.setup.detect", "--json"], | |
| assert: () => { | |
| const [method, opts] = firstGatewayCall(); | |
| expect(method).toBe("openclaw.setup.detect"); | |
| expect((opts as { timeout?: string } | undefined)?.timeout).toBe("40000"); | |
| }, | |
| }, | |
| { | |
| name: "projects gateway call --port into the local override", | |
| argv: ["gateway", "call", "health", "--port", "19084", "--json"], | |
| assert: () => { | |
| expectLocalGatewayCall("health", 19084, {}); | |
| }, | |
| }, | |
| { | |
| name: "inherits parent --port for gateway call", | |
| argv: ["gateway", "--port", "19085", "call", "health", "--json"], | |
| assert: () => { | |
| expectLocalGatewayCall("health", 19085); | |
| }, | |
| }, | |
| { | |
| name: "projects gateway suspend --port and request id", | |
| argv: ["gateway", "suspend", "--request-id", "host-operation", "--port", "19086", "--json"], | |
| assert: () => { | |
| expectLocalGatewayCall("gateway.suspend.prepare", 19086, { | |
| requestId: "host-operation", | |
| }); | |
| expect(defaultRuntime.writeJson).toHaveBeenCalledWith( | |
| expect.objectContaining({ status: "ready", requestId: "host-operation" }), | |
| ); | |
| }, | |
| }, | |
| { | |
| name: "preserves the custom suspend port in its human-readable resume hint", | |
| argv: ["gateway", "suspend", "--port", "19086"], | |
| assert: () => { | |
| expectLocalGatewayCall("gateway.suspend.prepare", 19086); | |
| expect(defaultRuntime.log).toHaveBeenCalledWith( | |
| "Resume with: openclaw gateway resume suspension-1 --port 19086", | |
| ); | |
| }, | |
| }, | |
| { | |
| name: "preserves an inherited suspend port in its human-readable resume hint", | |
| argv: ["gateway", "--port", "19087", "suspend"], | |
| assert: () => { | |
| expectLocalGatewayCall("gateway.suspend.prepare", 19087); | |
| expect(defaultRuntime.log).toHaveBeenCalledWith( | |
| "Resume with: openclaw gateway resume suspension-1 --port 19087", | |
| ); | |
| }, | |
| }, | |
| { | |
| name: "inherits parent --port for gateway resume", | |
| argv: ["gateway", "--port", "19087", "resume", "suspension-1", "--json"], | |
| assert: () => { | |
| expectLocalGatewayCall("gateway.suspend.resume", 19087, { | |
| suspensionId: "suspension-1", | |
| }); | |
| expect(defaultRuntime.writeJson).toHaveBeenCalledWith({ | |
| ok: true, | |
| status: "running", | |
| resumed: true, | |
| }); | |
| }, | |
| }, | |
| { | |
| name: "forwards --token to gateway probe when parent and child option names collide", | |
| argv: ["gateway", "probe", "--token", "tok_probe", "--json"], | |
| assert: () => { | |
| expect(gatewayStatusCommand).toHaveBeenCalledTimes(1); | |
| const [opts, runtime] = firstGatewayStatusCall(); | |
| expect((opts as { token?: string } | undefined)?.token).toBe("tok_probe"); | |
| expect(runtime).toBe(defaultRuntime); | |
| }, | |
| }, | |
| { | |
| name: "forwards --port to gateway probe", | |
| argv: ["gateway", "probe", "--port", "19080", "--json"], | |
| assert: () => { | |
| expect(gatewayStatusCommand).toHaveBeenCalledTimes(1); | |
| const [opts] = firstGatewayStatusCall(); | |
| expect((opts as { port?: string } | undefined)?.port).toBe("19080"); | |
| }, | |
| }, | |
| { | |
| name: "inherits parent --port for gateway probe", | |
| argv: ["gateway", "--port", "19082", "probe", "--json"], | |
| assert: () => { | |
| expect(gatewayStatusCommand).toHaveBeenCalledTimes(1); | |
| const [opts] = firstGatewayStatusCall(); | |
| expect((opts as { port?: string } | undefined)?.port).toBe("19082"); | |
| }, | |
| }, | |
| { | |
| name: "projects gateway health --port into the local override", | |
| argv: ["gateway", "health", "--port", "19081", "--json"], | |
| assert: () => { | |
| expectLocalGatewayCall("health", 19081); | |
| }, | |
| }, | |
| { | |
| name: "inherits parent --port for gateway health", | |
| argv: ["gateway", "--port", "19083", "health", "--json"], | |
| assert: () => { | |
| expectLocalGatewayCall("health", 19083); | |
| }, | |
| }, | |
| { | |
| name: "passes decimal usage-cost --days values", | |
| argv: ["gateway", "usage-cost", "--days", "7", "--json"], | |
| assert: () => { | |
| expect(callGatewayCli).toHaveBeenCalledTimes(1); | |
| const [method, _opts, params] = firstGatewayCall(); | |
| expect(method).toBe("usage.cost"); | |
| expect(params).toEqual({ days: 7 }); | |
| }, | |
| }, | |
| { | |
| name: "projects gateway usage-cost --port into the local override", | |
| argv: ["gateway", "usage-cost", "--port", "19088", "--json"], | |
| assert: () => { | |
| expectLocalGatewayCall("usage.cost", 19088, { days: 30 }); | |
| }, | |
| }, | |
| { | |
| name: "inherits parent --port for gateway usage-cost", | |
| argv: ["gateway", "--port", "19089", "usage-cost", "--json"], | |
| assert: () => { | |
| expectLocalGatewayCall("usage.cost", 19089, { days: 30 }); | |
| }, | |
| }, | |
| { | |
| name: "prefers the explicit usage-cost --port over the parent --port", | |
| argv: ["gateway", "--port", "19090", "usage-cost", "--port", "19091", "--json"], | |
| assert: () => { | |
| expectLocalGatewayCall("usage.cost", 19091, { days: 30 }); | |
| }, | |
| }, | |
| { | |
| name: "projects gateway stability --port into the local override", | |
| argv: ["gateway", "stability", "--port", "19092", "--json"], | |
| assert: () => { | |
| expectLocalGatewayCall("diagnostics.stability", 19092, { limit: 25 }); | |
| }, | |
| }, | |
| { | |
| name: "inherits parent --port for live gateway stability", | |
| argv: ["gateway", "--port", "19093", "stability", "--json"], | |
| assert: () => { | |
| expectLocalGatewayCall("diagnostics.stability", 19093, { limit: 25 }); | |
| }, | |
| }, | |
| { | |
| name: "prefers the explicit live stability --port over the parent --port", | |
| argv: ["gateway", "--port", "19094", "stability", "--port", "19095", "--json"], | |
| assert: () => { | |
| expectLocalGatewayCall("diagnostics.stability", 19095, { limit: 25 }); | |
| }, | |
| }, | |
| ])("$name", async ({ argv, assert }) => { | |
| await sharedProgram.parseAsync(argv, { from: "user" }); | |
| assert(); | |
| }); | |
| it("rejects non-decimal usage-cost --days values instead of silently defaulting", async () => { | |
| await sharedProgram.parseAsync(["gateway", "usage-cost", "--days", "1e3", "--json"], { | |
| from: "user", | |
| }); | |
| expect(callGatewayCli).not.toHaveBeenCalled(); | |
| expect(defaultRuntime.writeJson).toHaveBeenCalledWith({ | |
| ok: false, | |
| error: { type: "cli_error", message: expect.stringContaining("Invalid --days") }, | |
| }); | |
| expect(defaultRuntime.error).not.toHaveBeenCalled(); | |
| expect(defaultRuntime.exit).toHaveBeenCalledWith(1); | |
| }); | |
| it.each([ | |
| { | |
| name: "call", | |
| args: ["call", "health"], | |
| }, | |
| { | |
| name: "usage-cost", | |
| args: ["usage-cost"], | |
| }, | |
| { | |
| name: "live stability", | |
| args: ["stability"], | |
| }, | |
| ])("rejects combining --url and --port for gateway $name", async ({ args }) => { | |
| await sharedProgram.parseAsync( | |
| ["gateway", ...args, "--url", "ws://127.0.0.1:19084", "--port", "19084", "--json"], | |
| { from: "user" }, | |
| ); | |
| expect(callGatewayCli).not.toHaveBeenCalled(); | |
| expect(defaultRuntime.writeJson).toHaveBeenCalledWith({ | |
| ok: false, | |
| error: { type: "cli_error", message: "Use either --url or --port, not both." }, | |
| }); | |
| expect(defaultRuntime.error).not.toHaveBeenCalled(); | |
| expect(defaultRuntime.exit).toHaveBeenCalledWith(1); | |
| }); | |
| it("uses the effective local port and timeout for gateway health auth diagnostics", async () => { | |
| const authError = new Error("gateway auth required"); | |
| callGatewayCli.mockRejectedValueOnce(authError); | |
| emitReachableGatewayAuthDiagnostic.mockResolvedValueOnce(true); | |
| await sharedProgram.parseAsync( | |
| ["gateway", "health", "--port", "19081", "--timeout", "1234", "--json"], | |
| { from: "user" }, | |
| ); | |
| expect(emitReachableGatewayAuthDiagnostic).toHaveBeenCalledTimes(1); | |
| expect(emitReachableGatewayAuthDiagnostic).toHaveBeenCalledWith({ | |
| error: authError, | |
| config: {}, | |
| runtime: defaultRuntime, | |
| timeoutMs: 1234, | |
| token: undefined, | |
| password: undefined, | |
| localPortOverride: 19081, | |
| json: true, | |
| }); | |
| }); | |
| it("defers health presentation imports for successful JSON output", async () => { | |
| const program = new Command(); | |
| program.exitOverride(); | |
| const loadGatewayHealthModule = vi.fn(async () => ({ | |
| emitReachableGatewayAuthDiagnostic: mocks.emitReachableGatewayAuthDiagnostic, | |
| formatHealthChannelLines: mocks.formatHealthChannelLines, | |
| })); | |
| const loadHealthStyleModule = vi.fn(async () => ({ | |
| styleHealthChannelLine: (line: string) => line, | |
| })); | |
| registerGatewayCli(program, { | |
| loadGatewayHealthModule: loadGatewayHealthModule as never, | |
| loadHealthStyleModule: loadHealthStyleModule as never, | |
| }); | |
| await program.parseAsync(["node", "openclaw", "gateway", "health", "--json"]); | |
| expect(callGatewayCli).toHaveBeenCalledWith( | |
| "health", | |
| expect.objectContaining({ json: true }), | |
| undefined, | |
| ); | |
| expect(loadGatewayHealthModule).not.toHaveBeenCalled(); | |
| expect(loadHealthStyleModule).not.toHaveBeenCalled(); | |
| expect(defaultRuntime.writeJson).toHaveBeenCalledWith({ ok: true }); | |
| }); | |
| }); | |