diff --git a/extensions/raft/src/channel.test.ts b/extensions/raft/src/channel.test.ts index 8a2c86da00f7..436b97232402 100644 --- a/extensions/raft/src/channel.test.ts +++ b/extensions/raft/src/channel.test.ts @@ -1,7 +1,19 @@ -import { describe, expect, it } from "vitest"; +import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts"; +import { beforeEach, describe, expect, it, vi } from "vitest"; import { raftPlugin } from "./channel.js"; +const detectBinaryMock = vi.hoisted(() => vi.fn()); + +vi.mock("openclaw/plugin-sdk/setup-tools", async (importOriginal) => ({ + ...(await importOriginal()), + detectBinary: detectBinaryMock, +})); + describe("Raft channel plugin", () => { + beforeEach(() => { + detectBinaryMock.mockReset(); + }); + it("declares a wake-only direct channel", () => { expect(raftPlugin.meta).toMatchObject({ id: "raft", @@ -13,4 +25,42 @@ describe("Raft channel plugin", () => { expect(raftPlugin.message).toBeUndefined(); expect(raftPlugin.outbound).toBeUndefined(); }); + + it.each([ + { + detected: true, + expected: { ok: true, cliFound: true, error: null }, + }, + { + detected: false, + expected: { + ok: false, + cliFound: false, + error: "Raft CLI not found on the Gateway PATH", + }, + }, + ])( + "maps CLI detection to a coherent status outcome when detected=$detected", + async ({ detected, expected }) => { + detectBinaryMock.mockResolvedValueOnce(detected); + const cfg = { + channels: { + raft: { + profile: "test-profile", + }, + }, + } as OpenClawConfig; + const account = raftPlugin.config.resolveAccount(cfg, "default"); + + const result = await raftPlugin.status!.probeAccount!({ + account, + timeoutMs: 1_000, + cfg, + }); + + expect(detectBinaryMock).toHaveBeenCalledOnce(); + expect(detectBinaryMock).toHaveBeenCalledWith("raft"); + expect(result).toEqual(expected); + }, + ); }); diff --git a/extensions/raft/src/channel.ts b/extensions/raft/src/channel.ts index 19952973de3c..1a3d863bbc5a 100644 --- a/extensions/raft/src/channel.ts +++ b/extensions/raft/src/channel.ts @@ -19,9 +19,9 @@ import { raftChannelConfigSchema } from "./config-schema.js"; import { startRaftGatewayAccount } from "./gateway.js"; import { raftSetupPlugin } from "./setup.js"; -type RaftProbe = { - cliFound: boolean; -}; +type RaftProbe = + | { ok: true; cliFound: true; error: null } + | { ok: false; cliFound: false; error: string }; export const raftPlugin: ChannelPlugin = createChatChannelPlugin({ base: { @@ -61,9 +61,16 @@ export const raftPlugin: ChannelPlugin = createC status: createComputedAccountStatusAdapter({ defaultRuntime: createDefaultChannelRuntimeState("default"), buildChannelSummary: ({ snapshot }) => buildBaseChannelStatusSummary(snapshot), - probeAccount: async () => ({ - cliFound: await detectBinary("raft"), - }), + probeAccount: async () => { + const cliFound = await detectBinary("raft"); + return cliFound + ? { ok: true, cliFound: true, error: null } + : { + ok: false, + cliFound: false, + error: "Raft CLI not found on the Gateway PATH", + }; + }, formatCapabilitiesProbe: ({ probe }) => [ { text: `Raft CLI: ${probe.cliFound ? "found" : "missing"}`,