import { describe, expect, it, vi } from "vitest"; const callGatewayMock = vi.hoisted(() => vi.fn()); const probeGatewayMock = vi.hoisted(() => vi.fn()); vi.mock("../../gateway/call.js", () => ({ callGateway: (...args: unknown[]) => callGatewayMock(...args), })); vi.mock("../../gateway/probe.js", () => ({ probeGateway: (...args: unknown[]) => probeGatewayMock(...args), })); vi.mock("../progress.js", () => ({ withProgress: async (_opts: unknown, fn: () => Promise) => await fn(), })); const { probeGatewayStatus } = await import("./probe.js"); describe("probeGatewayStatus", () => { it("uses lightweight token-only probing for daemon status", async () => { callGatewayMock.mockReset(); probeGatewayMock.mockResolvedValueOnce({ ok: true }); const result = await probeGatewayStatus({ url: "ws://127.0.0.1:19191", token: "temp-token", tlsFingerprint: "abc123", timeoutMs: 5_000, json: true, }); expect(result).toEqual({ ok: true }); expect(callGatewayMock).not.toHaveBeenCalled(); expect(probeGatewayMock).toHaveBeenCalledWith({ url: "ws://127.0.0.1:19191", auth: { token: "temp-token", password: undefined, }, tlsFingerprint: "abc123", timeoutMs: 5_000, includeDetails: false, }); }); it("uses a real status RPC when requireRpc is enabled", async () => { callGatewayMock.mockReset(); probeGatewayMock.mockReset(); callGatewayMock.mockResolvedValueOnce({ status: "ok" }); const result = await probeGatewayStatus({ url: "ws://127.0.0.1:19191", token: "temp-token", tlsFingerprint: "abc123", timeoutMs: 5_000, json: true, requireRpc: true, configPath: "/tmp/openclaw-daemon/openclaw.json", }); expect(result).toEqual({ ok: true }); expect(probeGatewayMock).not.toHaveBeenCalled(); expect(callGatewayMock).toHaveBeenCalledWith({ url: "ws://127.0.0.1:19191", token: "temp-token", password: undefined, tlsFingerprint: "abc123", method: "status", timeoutMs: 5_000, configPath: "/tmp/openclaw-daemon/openclaw.json", }); }); it("surfaces probe close details when the handshake fails", async () => { callGatewayMock.mockReset(); probeGatewayMock.mockReset(); probeGatewayMock.mockResolvedValueOnce({ ok: false, error: null, close: { code: 1008, reason: "pairing required" }, }); const result = await probeGatewayStatus({ url: "ws://127.0.0.1:19191", timeoutMs: 5_000, }); expect(result).toEqual({ ok: false, error: "gateway closed (1008): pairing required", }); }); it("surfaces status RPC errors when requireRpc is enabled", async () => { callGatewayMock.mockReset(); probeGatewayMock.mockReset(); callGatewayMock.mockRejectedValueOnce(new Error("missing scope: operator.admin")); const result = await probeGatewayStatus({ url: "ws://127.0.0.1:19191", token: "temp-token", timeoutMs: 5_000, requireRpc: true, }); expect(result).toEqual({ ok: false, error: "missing scope: operator.admin", }); expect(probeGatewayMock).not.toHaveBeenCalled(); }); });