import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { createMockTwilio } from "../test/mocks/twilio.js"; import { statusCommand } from "./commands/status.js"; import { createDefaultDeps } from "./index.js"; import * as providerWeb from "./provider-web.js"; import { defaultRuntime } from "./runtime.js"; vi.mock("twilio", () => { const { factory } = createMockTwilio(); return { default: factory }; }); import * as index from "./index.js"; import * as provider from "./provider-web.js"; beforeEach(() => { index.program.exitOverride(); process.env.TWILIO_ACCOUNT_SID = "AC123"; process.env.TWILIO_WHATSAPP_FROM = "whatsapp:+15551234567"; process.env.TWILIO_AUTH_TOKEN = "token"; vi.clearAllMocks(); }); afterEach(() => { vi.restoreAllMocks(); }); describe("CLI commands", () => { it("exposes login command", () => { const names = index.program.commands.map((c) => c.name()); expect(names).toContain("login"); }); it("send command routes to web provider", async () => { const sendWeb = vi.spyOn(provider, "sendMessageWeb").mockResolvedValue(); await index.program.parseAsync( [ "send", "--to", "+1555", "--message", "hi", "--provider", "web", "--wait", "0", ], { from: "user" }, ); expect(sendWeb).toHaveBeenCalled(); }); it("send command uses twilio path when provider=twilio", async () => { const twilio = (await import("twilio")).default; twilio._client.messages.create.mockResolvedValue({ sid: "SM1" }); const wait = vi.spyOn(index, "waitForFinalStatus").mockResolvedValue(); await index.program.parseAsync( ["send", "--to", "+1555", "--message", "hi", "--wait", "0"], { from: "user" }, ); expect(twilio._client.messages.create).toHaveBeenCalled(); expect(wait).not.toHaveBeenCalled(); }); it("send command supports dry-run and skips sending", async () => { const twilio = (await import("twilio")).default; const wait = vi.spyOn(index, "waitForFinalStatus").mockResolvedValue(); await index.program.parseAsync( ["send", "--to", "+1555", "--message", "hi", "--wait", "0", "--dry-run"], { from: "user" }, ); expect(twilio._client.messages.create).not.toHaveBeenCalled(); expect(wait).not.toHaveBeenCalled(); }); it("send command outputs JSON when requested", async () => { const twilio = (await import("twilio")).default; twilio._client.messages.create.mockResolvedValue({ sid: "SMJSON" }); const logSpy = vi.spyOn(defaultRuntime, "log"); await index.program.parseAsync( ["send", "--to", "+1555", "--message", "hi", "--wait", "0", "--json"], { from: "user" }, ); expect(logSpy).toHaveBeenCalledWith( expect.stringContaining('"sid": "SMJSON"'), ); }); it("login command calls web login", async () => { const spy = vi.spyOn(providerWeb, "loginWeb").mockResolvedValue(); await index.program.parseAsync(["login"], { from: "user" }); expect(spy).toHaveBeenCalled(); }); it("status command prints JSON", async () => { const twilio = (await import("twilio")).default; twilio._client.messages.list .mockResolvedValueOnce([ { sid: "1", status: "delivered", direction: "inbound", dateCreated: new Date("2024-01-01T00:00:00Z"), from: "a", to: "b", body: "hi", errorCode: null, errorMessage: null, }, ]) .mockResolvedValueOnce([ { sid: "2", status: "sent", direction: "outbound-api", dateCreated: new Date("2024-01-02T00:00:00Z"), from: "b", to: "a", body: "yo", errorCode: null, errorMessage: null, }, ]); const runtime = { ...defaultRuntime, log: vi.fn(), error: vi.fn(), exit: ((code: number) => { throw new Error(`exit ${code}`); }) as (code: number) => never, }; await statusCommand( { limit: "1", lookback: "10", json: true }, createDefaultDeps(), runtime, ); expect(runtime.log).toHaveBeenCalled(); }); });