Files
openclaw/src/cli/channel-auth.test.ts

143 lines
4.7 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
import { runChannelLogin, runChannelLogout } from "./channel-auth.js";
const mocks = vi.hoisted(() => ({
resolveChannelDefaultAccountId: vi.fn(),
getChannelPlugin: vi.fn(),
normalizeChannelId: vi.fn(),
loadConfig: vi.fn(),
resolveMessageChannelSelection: vi.fn(),
setVerbose: vi.fn(),
login: vi.fn(),
logoutAccount: vi.fn(),
resolveAccount: vi.fn(),
}));
vi.mock("../channels/plugins/helpers.js", () => ({
resolveChannelDefaultAccountId: mocks.resolveChannelDefaultAccountId,
}));
vi.mock("../channels/plugins/index.js", () => ({
getChannelPlugin: mocks.getChannelPlugin,
normalizeChannelId: mocks.normalizeChannelId,
}));
vi.mock("../config/config.js", () => ({
loadConfig: mocks.loadConfig,
}));
vi.mock("../infra/outbound/channel-selection.js", () => ({
resolveMessageChannelSelection: mocks.resolveMessageChannelSelection,
}));
vi.mock("../globals.js", () => ({
setVerbose: mocks.setVerbose,
}));
describe("channel-auth", () => {
const runtime = { log: vi.fn(), error: vi.fn(), exit: vi.fn() };
const plugin = {
auth: { login: mocks.login },
gateway: { logoutAccount: mocks.logoutAccount },
config: { resolveAccount: mocks.resolveAccount },
};
beforeEach(() => {
vi.clearAllMocks();
mocks.normalizeChannelId.mockReturnValue("whatsapp");
mocks.getChannelPlugin.mockReturnValue(plugin);
mocks.loadConfig.mockReturnValue({ channels: {} });
mocks.resolveMessageChannelSelection.mockResolvedValue({
channel: "whatsapp",
configured: ["whatsapp"],
});
mocks.resolveChannelDefaultAccountId.mockReturnValue("default-account");
mocks.resolveAccount.mockReturnValue({ id: "resolved-account" });
mocks.login.mockResolvedValue(undefined);
mocks.logoutAccount.mockResolvedValue(undefined);
});
it("runs login with explicit trimmed account and verbose flag", async () => {
await runChannelLogin({ channel: "wa", account: " acct-1 ", verbose: true }, runtime);
expect(mocks.setVerbose).toHaveBeenCalledWith(true);
expect(mocks.resolveChannelDefaultAccountId).not.toHaveBeenCalled();
expect(mocks.login).toHaveBeenCalledWith(
expect.objectContaining({
cfg: { channels: {} },
accountId: "acct-1",
runtime,
verbose: true,
channelInput: "wa",
}),
);
});
it("auto-picks the single configured channel when opts are empty", async () => {
await runChannelLogin({}, runtime);
expect(mocks.resolveMessageChannelSelection).toHaveBeenCalledWith({ cfg: { channels: {} } });
expect(mocks.normalizeChannelId).toHaveBeenCalledWith("whatsapp");
expect(mocks.login).toHaveBeenCalledWith(
expect.objectContaining({
channelInput: "whatsapp",
}),
);
});
it("propagates channel ambiguity when channel is omitted", async () => {
mocks.resolveMessageChannelSelection.mockRejectedValueOnce(
new Error("Channel is required when multiple channels are configured: telegram, slack"),
);
await expect(runChannelLogin({}, runtime)).rejects.toThrow("Channel is required");
expect(mocks.login).not.toHaveBeenCalled();
});
it("throws for unsupported channel aliases", async () => {
mocks.normalizeChannelId.mockReturnValueOnce(undefined);
await expect(runChannelLogin({ channel: "bad-channel" }, runtime)).rejects.toThrow(
"Unsupported channel: bad-channel",
);
expect(mocks.login).not.toHaveBeenCalled();
});
it("throws when channel does not support login", async () => {
mocks.getChannelPlugin.mockReturnValueOnce({
auth: {},
gateway: { logoutAccount: mocks.logoutAccount },
config: { resolveAccount: mocks.resolveAccount },
});
await expect(runChannelLogin({ channel: "whatsapp" }, runtime)).rejects.toThrow(
"Channel whatsapp does not support login",
);
});
it("runs logout with resolved account and explicit account id", async () => {
await runChannelLogout({ channel: "whatsapp", account: " acct-2 " }, runtime);
expect(mocks.resolveAccount).toHaveBeenCalledWith({ channels: {} }, "acct-2");
expect(mocks.logoutAccount).toHaveBeenCalledWith({
cfg: { channels: {} },
accountId: "acct-2",
account: { id: "resolved-account" },
runtime,
});
expect(mocks.setVerbose).not.toHaveBeenCalled();
});
it("throws when channel does not support logout", async () => {
mocks.getChannelPlugin.mockReturnValueOnce({
auth: { login: mocks.login },
gateway: {},
config: { resolveAccount: mocks.resolveAccount },
});
await expect(runChannelLogout({ channel: "whatsapp" }, runtime)).rejects.toThrow(
"Channel whatsapp does not support logout",
);
});
});