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", ); }); });