254 lines
6.7 KiB
TypeScript
254 lines
6.7 KiB
TypeScript
import { afterAll, beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
import type { CliDeps } from "../cli/deps.js";
|
|
import type { RuntimeEnv } from "../runtime.js";
|
|
import { sendCommand } from "./send.js";
|
|
|
|
let testConfig: Record<string, unknown> = {};
|
|
vi.mock("../config/config.js", async (importOriginal) => {
|
|
const actual = await importOriginal<typeof import("../config/config.js")>();
|
|
return {
|
|
...actual,
|
|
loadConfig: () => testConfig,
|
|
};
|
|
});
|
|
|
|
const callGatewayMock = vi.fn();
|
|
vi.mock("../gateway/call.js", () => ({
|
|
callGateway: (...args: unknown[]) => callGatewayMock(...args),
|
|
randomIdempotencyKey: () => "idem-1",
|
|
}));
|
|
|
|
const originalTelegramToken = process.env.TELEGRAM_BOT_TOKEN;
|
|
const originalDiscordToken = process.env.DISCORD_BOT_TOKEN;
|
|
|
|
beforeEach(() => {
|
|
process.env.TELEGRAM_BOT_TOKEN = "token-abc";
|
|
process.env.DISCORD_BOT_TOKEN = "token-discord";
|
|
testConfig = {};
|
|
});
|
|
|
|
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> = {}): CliDeps => ({
|
|
sendMessageWhatsApp: vi.fn(),
|
|
sendMessageTelegram: vi.fn(),
|
|
sendMessageDiscord: vi.fn(),
|
|
sendMessageSlack: vi.fn(),
|
|
sendMessageSignal: vi.fn(),
|
|
sendMessageIMessage: vi.fn(),
|
|
...overrides,
|
|
});
|
|
|
|
describe("sendCommand", () => {
|
|
it("skips send on dry-run", async () => {
|
|
const deps = makeDeps();
|
|
await sendCommand(
|
|
{
|
|
to: "+1",
|
|
message: "hi",
|
|
dryRun: true,
|
|
},
|
|
deps,
|
|
runtime,
|
|
);
|
|
expect(deps.sendMessageWhatsApp).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("sends via gateway", async () => {
|
|
callGatewayMock.mockResolvedValueOnce({ messageId: "g1" });
|
|
const deps = makeDeps();
|
|
await sendCommand(
|
|
{
|
|
to: "+1",
|
|
message: "hi",
|
|
},
|
|
deps,
|
|
runtime,
|
|
);
|
|
expect(callGatewayMock).toHaveBeenCalled();
|
|
expect(runtime.log).toHaveBeenCalledWith(expect.stringContaining("g1"));
|
|
});
|
|
|
|
it("does not override remote gateway URL", async () => {
|
|
callGatewayMock.mockResolvedValueOnce({ messageId: "g2" });
|
|
testConfig = {
|
|
gateway: { mode: "remote", remote: { url: "wss://remote.example" } },
|
|
};
|
|
const deps = makeDeps();
|
|
await sendCommand(
|
|
{
|
|
to: "+1",
|
|
message: "hi",
|
|
},
|
|
deps,
|
|
runtime,
|
|
);
|
|
const args = callGatewayMock.mock.calls.at(-1)?.[0] as
|
|
| Record<string, unknown>
|
|
| undefined;
|
|
expect(args?.url).toBeUndefined();
|
|
});
|
|
|
|
it("passes gifPlayback to gateway send", async () => {
|
|
callGatewayMock.mockClear();
|
|
callGatewayMock.mockResolvedValueOnce({ messageId: "g1" });
|
|
const deps = makeDeps();
|
|
await sendCommand(
|
|
{
|
|
to: "+1",
|
|
message: "hi",
|
|
gifPlayback: true,
|
|
},
|
|
deps,
|
|
runtime,
|
|
);
|
|
expect(callGatewayMock).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
method: "send",
|
|
params: expect.objectContaining({ gifPlayback: true }),
|
|
}),
|
|
);
|
|
});
|
|
|
|
it("routes to telegram provider", async () => {
|
|
const deps = makeDeps({
|
|
sendMessageTelegram: vi
|
|
.fn()
|
|
.mockResolvedValue({ messageId: "t1", chatId: "123" }),
|
|
});
|
|
testConfig = { telegram: { botToken: "token-abc" } };
|
|
await sendCommand(
|
|
{ to: "123", message: "hi", provider: "telegram" },
|
|
deps,
|
|
runtime,
|
|
);
|
|
expect(deps.sendMessageTelegram).toHaveBeenCalledWith(
|
|
"123",
|
|
"hi",
|
|
expect.objectContaining({ accountId: "default", verbose: false }),
|
|
);
|
|
expect(deps.sendMessageWhatsApp).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("uses config token for telegram when env is missing", async () => {
|
|
process.env.TELEGRAM_BOT_TOKEN = "";
|
|
testConfig = { telegram: { botToken: "cfg-token" } };
|
|
const deps = makeDeps({
|
|
sendMessageTelegram: vi
|
|
.fn()
|
|
.mockResolvedValue({ messageId: "t1", chatId: "123" }),
|
|
});
|
|
await sendCommand(
|
|
{ to: "123", message: "hi", provider: "telegram" },
|
|
deps,
|
|
runtime,
|
|
);
|
|
expect(deps.sendMessageTelegram).toHaveBeenCalledWith(
|
|
"123",
|
|
"hi",
|
|
expect.objectContaining({ accountId: "default", verbose: false }),
|
|
);
|
|
});
|
|
|
|
it("routes to discord provider", async () => {
|
|
const deps = makeDeps({
|
|
sendMessageDiscord: vi
|
|
.fn()
|
|
.mockResolvedValue({ messageId: "d1", channelId: "chan" }),
|
|
});
|
|
await sendCommand(
|
|
{ to: "channel:chan", message: "hi", provider: "discord" },
|
|
deps,
|
|
runtime,
|
|
);
|
|
expect(deps.sendMessageDiscord).toHaveBeenCalledWith(
|
|
"channel:chan",
|
|
"hi",
|
|
expect.objectContaining({ verbose: false }),
|
|
);
|
|
expect(deps.sendMessageWhatsApp).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("routes to signal provider", async () => {
|
|
const deps = makeDeps({
|
|
sendMessageSignal: vi.fn().mockResolvedValue({ messageId: "s1" }),
|
|
});
|
|
await sendCommand(
|
|
{ to: "+15551234567", message: "hi", provider: "signal" },
|
|
deps,
|
|
runtime,
|
|
);
|
|
expect(deps.sendMessageSignal).toHaveBeenCalledWith(
|
|
"+15551234567",
|
|
"hi",
|
|
expect.objectContaining({ maxBytes: undefined }),
|
|
);
|
|
expect(deps.sendMessageWhatsApp).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("routes to slack provider", async () => {
|
|
const deps = makeDeps({
|
|
sendMessageSlack: vi
|
|
.fn()
|
|
.mockResolvedValue({ messageId: "s1", channelId: "C123" }),
|
|
});
|
|
await sendCommand(
|
|
{ to: "channel:C123", message: "hi", provider: "slack" },
|
|
deps,
|
|
runtime,
|
|
);
|
|
expect(deps.sendMessageSlack).toHaveBeenCalledWith(
|
|
"channel:C123",
|
|
"hi",
|
|
expect.objectContaining({ accountId: "default" }),
|
|
);
|
|
expect(deps.sendMessageWhatsApp).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("routes to imessage provider", async () => {
|
|
const deps = makeDeps({
|
|
sendMessageIMessage: vi.fn().mockResolvedValue({ messageId: "i1" }),
|
|
});
|
|
await sendCommand(
|
|
{ to: "chat_id:42", message: "hi", provider: "imessage" },
|
|
deps,
|
|
runtime,
|
|
);
|
|
expect(deps.sendMessageIMessage).toHaveBeenCalledWith(
|
|
"chat_id:42",
|
|
"hi",
|
|
expect.objectContaining({ maxBytes: undefined }),
|
|
);
|
|
expect(deps.sendMessageWhatsApp).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("emits json output", async () => {
|
|
callGatewayMock.mockResolvedValueOnce({ messageId: "direct2" });
|
|
const deps = makeDeps();
|
|
await sendCommand(
|
|
{
|
|
to: "+1",
|
|
message: "hi",
|
|
json: true,
|
|
},
|
|
deps,
|
|
runtime,
|
|
);
|
|
expect(runtime.log).toHaveBeenCalledWith(
|
|
expect.stringContaining('"provider": "whatsapp"'),
|
|
);
|
|
});
|
|
});
|