import { afterAll, beforeEach, describe, expect, it, vi } from "vitest"; import type { CliDeps } from "../cli/deps.js"; import type { RuntimeEnv } from "../runtime.js"; import { messageCommand } from "./message.js"; let testConfig: Record = {}; vi.mock("../config/config.js", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, loadConfig: () => testConfig, }; }); const callGatewayMock = vi.fn(); vi.mock("../gateway/call.js", () => ({ callGateway: (...args: unknown[]) => callGatewayMock(...args), randomIdempotencyKey: () => "idem-1", })); const webAuthExists = vi.fn(async () => false); vi.mock("../web/session.js", () => ({ webAuthExists: (...args: unknown[]) => webAuthExists(...args), })); const handleDiscordAction = vi.fn(async () => ({ details: { ok: true } })); vi.mock("../agents/tools/discord-actions.js", () => ({ handleDiscordAction: (...args: unknown[]) => handleDiscordAction(...args), })); const handleSlackAction = vi.fn(async () => ({ details: { ok: true } })); vi.mock("../agents/tools/slack-actions.js", () => ({ handleSlackAction: (...args: unknown[]) => handleSlackAction(...args), })); const handleTelegramAction = vi.fn(async () => ({ details: { ok: true } })); vi.mock("../agents/tools/telegram-actions.js", () => ({ handleTelegramAction: (...args: unknown[]) => handleTelegramAction(...args), })); const handleWhatsAppAction = vi.fn(async () => ({ details: { ok: true } })); vi.mock("../agents/tools/whatsapp-actions.js", () => ({ handleWhatsAppAction: (...args: unknown[]) => handleWhatsAppAction(...args), })); const originalTelegramToken = process.env.TELEGRAM_BOT_TOKEN; const originalDiscordToken = process.env.DISCORD_BOT_TOKEN; beforeEach(() => { process.env.TELEGRAM_BOT_TOKEN = ""; process.env.DISCORD_BOT_TOKEN = ""; testConfig = {}; callGatewayMock.mockReset(); webAuthExists.mockReset().mockResolvedValue(false); handleDiscordAction.mockReset(); handleSlackAction.mockReset(); handleTelegramAction.mockReset(); handleWhatsAppAction.mockReset(); }); afterAll(() => { process.env.TELEGRAM_BOT_TOKEN = originalTelegramToken; process.env.DISCORD_BOT_TOKEN = originalDiscordToken; }); const runtime: RuntimeEnv = { log: vi.fn(), error: vi.fn(), exit: vi.fn(() => { throw new Error("exit"); }), }; const makeDeps = (overrides: Partial = {}): CliDeps => ({ sendMessageWhatsApp: vi.fn(), sendMessageTelegram: vi.fn(), sendMessageDiscord: vi.fn(), sendMessageSlack: vi.fn(), sendMessageSignal: vi.fn(), sendMessageIMessage: vi.fn(), ...overrides, }); describe("messageCommand", () => { it("defaults channel when only one configured", async () => { process.env.TELEGRAM_BOT_TOKEN = "token-abc"; const deps = makeDeps(); await messageCommand( { target: "123456", message: "hi", }, deps, runtime, ); expect(handleTelegramAction).toHaveBeenCalled(); }); it("requires channel when multiple configured", async () => { process.env.TELEGRAM_BOT_TOKEN = "token-abc"; process.env.DISCORD_BOT_TOKEN = "token-discord"; const deps = makeDeps(); await expect( messageCommand( { target: "123", message: "hi", }, deps, runtime, ), ).rejects.toThrow(/Channel is required/); }); it("sends via gateway for WhatsApp", async () => { callGatewayMock.mockResolvedValueOnce({ messageId: "g1" }); const deps = makeDeps(); await messageCommand( { action: "send", channel: "whatsapp", target: "+15551234567", message: "hi", }, deps, runtime, ); expect(callGatewayMock).toHaveBeenCalled(); }); it("routes discord polls through message action", async () => { const deps = makeDeps(); await messageCommand( { action: "poll", channel: "discord", target: "channel:123456789", pollQuestion: "Snack?", pollOption: ["Pizza", "Sushi"], }, deps, runtime, ); expect(handleDiscordAction).toHaveBeenCalledWith( expect.objectContaining({ action: "poll", to: "channel:123456789", }), expect.any(Object), ); }); });