Files
clawdbot/src/index.commands.test.ts
2025-11-25 12:30:43 +01:00

140 lines
3.9 KiB
TypeScript

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();
});
});