import { beforeEach, describe, expect, it, vi } from "vitest"; function requireDefined(value: T | null | undefined, name: string): T { if (value == null) { throw new Error(`${name} should be defined`); } return value; } const { deleteAccountFromConfigSectionMock, listHubAccountIdsMock, resolveDefaultHubAccountIdMock, resolveHubAccountMock, sendMessageHubMock, setAccountEnabledInConfigSectionMock, } = vi.hoisted(() => ({ deleteAccountFromConfigSectionMock: vi.fn(() => ({})), listHubAccountIdsMock: vi.fn(() => ["default"]), resolveDefaultHubAccountIdMock: vi.fn(() => "default"), resolveHubAccountMock: vi.fn(), sendMessageHubMock: vi.fn(), setAccountEnabledInConfigSectionMock: vi.fn(() => ({})), })); vi.mock("openclaw/plugin-sdk/compat", () => ({ buildBaseAccountStatusSnapshot: vi.fn(() => ({})), buildBaseChannelStatusSummary: vi.fn(() => ({})), buildChannelConfigSchema: vi.fn((schema: unknown) => schema), DEFAULT_ACCOUNT_ID: "default", deleteAccountFromConfigSection: deleteAccountFromConfigSectionMock, formatPairingApproveHint: vi.fn(() => "approve via hub"), PAIRING_APPROVED_MESSAGE: "approved", setAccountEnabledInConfigSection: setAccountEnabledInConfigSectionMock, })); vi.mock("./accounts.js", () => ({ listHubAccountIds: listHubAccountIdsMock, resolveDefaultHubAccountId: resolveDefaultHubAccountIdMock, resolveHubAccount: resolveHubAccountMock, })); vi.mock("./config-schema.js", () => ({ HubConfigSchema: { type: "object" }, })); vi.mock("./monitor.js", () => ({ monitorHubProvider: vi.fn(), })); vi.mock("./onboarding.js", () => ({ hubOnboardingAdapter: {}, })); vi.mock("./probe.js", () => ({ probeHub: vi.fn(), })); vi.mock("./runtime.js", () => ({ getHubRuntime: vi.fn(() => ({ channel: { activity: { record: vi.fn(), }, text: { chunkMarkdownText: vi.fn(), }, }, })), })); vi.mock("./send.js", () => ({ sendMessageHub: sendMessageHubMock, })); const { hubPlugin } = await import("./channel.js"); describe("hubPlugin normalization", () => { beforeEach(() => { sendMessageHubMock.mockReset(); sendMessageHubMock.mockResolvedValue({ messageId: "hub-1", target: "brain", }); resolveHubAccountMock.mockReset(); resolveHubAccountMock.mockReturnValue({ accountId: "default", name: "Hub", enabled: true, configured: true, url: "https://hub.example.test", agentId: "sender", secretSource: "inline", config: { allowFrom: [" hub:Brain ", "*", "CombinatorAgent ", "hub:"], defaultTo: " hub:TargetAgent ", dmPolicy: "open", }, }); }); it("normalizes outbound hub targets", () => { expect(hubPlugin.messaging?.normalizeTarget?.(" hub:Brain ")).toBe("Brain"); expect(hubPlugin.messaging?.normalizeTarget?.("CombinatorAgent")).toBe("CombinatorAgent"); expect(hubPlugin.messaging?.normalizeTarget?.("hub:")).toBeUndefined(); }); it("normalizes pairing allow entries and approval targets", async () => { const pairing = requireDefined(hubPlugin.pairing, "hubPlugin.pairing"); const notifyApproval = requireDefined( pairing.notifyApproval, "hubPlugin.pairing.notifyApproval", ); expect(pairing.normalizeAllowEntry?.(" hub:Brain ")).toBe("brain"); expect(pairing.normalizeAllowEntry?.("hub:*")).toBe("*"); await notifyApproval({ id: " hub:Brain " } as any); expect(sendMessageHubMock).toHaveBeenCalledWith("Brain", "approved"); }); it("normalizes config-derived allowFrom and defaultTo values", () => { const cfg = { channels: { hub: {} } }; const config = requireDefined(hubPlugin.config, "hubPlugin.config"); const resolveAllowFrom = requireDefined( config.resolveAllowFrom, "hubPlugin.config.resolveAllowFrom", ); const formatAllowFrom = requireDefined( config.formatAllowFrom, "hubPlugin.config.formatAllowFrom", ); const resolveDefaultTo = requireDefined( config.resolveDefaultTo, "hubPlugin.config.resolveDefaultTo", ); expect(resolveAllowFrom({ cfg, accountId: "default" } as any)).toEqual([ "brain", "*", "combinatoragent", ]); expect( formatAllowFrom({ allowFrom: [" hub:Brain ", "COMBINATORAGENT", "*", "hub:"], } as any), ).toEqual(["brain", "combinatoragent", "*"]); expect(resolveDefaultTo({ cfg, accountId: "default" } as any)).toBe("TargetAgent"); }); it("normalizes dm-policy entries, resolver ids, and directory peers", async () => { const cfg = { channels: { hub: {} } }; const account = resolveHubAccountMock.mock.results[0]?.value ?? resolveHubAccountMock(); const security = requireDefined(hubPlugin.security, "hubPlugin.security"); const resolveDmPolicy = requireDefined( security.resolveDmPolicy, "hubPlugin.security.resolveDmPolicy", ); const resolver = requireDefined(hubPlugin.resolver, "hubPlugin.resolver"); const directory = requireDefined(hubPlugin.directory, "hubPlugin.directory"); const listPeers = requireDefined(directory.listPeers, "hubPlugin.directory.listPeers"); const dmPolicy = requireDefined( resolveDmPolicy({ cfg, accountId: "default", account, } as any), "hubPlugin.security.resolveDmPolicy()", ); const normalizeEntry = requireDefined(dmPolicy.normalizeEntry, "dmPolicy.normalizeEntry"); expect(normalizeEntry(" hub:Brain ")).toBe("brain"); expect(normalizeEntry("hub:*")).toBe("*"); await expect( resolver.resolveTargets({ inputs: [" hub:Brain ", " hub: ", "CombinatorAgent"], } as any), ).resolves.toEqual([ { input: " hub:Brain ", resolved: true, id: "Brain", name: "Brain" }, { input: " hub: ", resolved: false, note: "empty target" }, { input: "CombinatorAgent", resolved: true, id: "CombinatorAgent", name: "CombinatorAgent", }, ]); await expect( listPeers({ cfg, accountId: "default", limit: 10, } as any), ).resolves.toEqual([ { kind: "user", id: "brain" }, { kind: "user", id: "combinatoragent" }, ]); }); });