File size: 4,394 Bytes
fc93158 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | import { beforeEach, describe, expect, it, vi } from "vitest";
import type { ResolvedIMessageAccount } from "./accounts.js";
import type { IMessageRpcClient } from "./client.js";
import { sendMessageIMessage } from "./send.js";
const requestMock = vi.fn();
const stopMock = vi.fn();
const defaultAccount: ResolvedIMessageAccount = {
accountId: "default",
enabled: true,
configured: false,
config: {},
};
function createClient(): IMessageRpcClient {
return {
request: (...args: unknown[]) => requestMock(...args),
stop: (...args: unknown[]) => stopMock(...args),
} as unknown as IMessageRpcClient;
}
async function sendWithDefaults(
to: string,
text: string,
opts: Parameters<typeof sendMessageIMessage>[2] = {},
) {
return await sendMessageIMessage(to, text, {
account: defaultAccount,
config: {},
client: createClient(),
...opts,
});
}
function getSentParams() {
return requestMock.mock.calls[0]?.[1] as Record<string, unknown>;
}
describe("sendMessageIMessage", () => {
beforeEach(() => {
requestMock.mockClear().mockResolvedValue({ ok: true });
stopMock.mockClear().mockResolvedValue(undefined);
});
it("sends to chat_id targets", async () => {
await sendWithDefaults("chat_id:123", "hi");
const params = getSentParams();
expect(requestMock).toHaveBeenCalledWith("send", expect.any(Object), expect.any(Object));
expect(params.chat_id).toBe(123);
expect(params.text).toBe("hi");
});
it("applies sms service prefix", async () => {
await sendWithDefaults("sms:+1555", "hello");
const params = getSentParams();
expect(params.service).toBe("sms");
expect(params.to).toBe("+1555");
});
it("adds file attachment with placeholder text", async () => {
await sendWithDefaults("chat_id:7", "", {
mediaUrl: "http://x/y.jpg",
resolveAttachmentImpl: async () => ({
path: "/tmp/imessage-media.jpg",
contentType: "image/jpeg",
}),
});
const params = getSentParams();
expect(params.file).toBe("/tmp/imessage-media.jpg");
expect(params.text).toBe("<media:image>");
});
it("normalizes mixed-case parameterized MIME for attachment placeholder text", async () => {
await sendWithDefaults("chat_id:7", "", {
mediaUrl: "http://x/voice",
resolveAttachmentImpl: async () => ({
path: "/tmp/imessage-media.ogg",
contentType: " Audio/Ogg; codecs=opus ",
}),
});
const params = getSentParams();
expect(params.file).toBe("/tmp/imessage-media.ogg");
expect(params.text).toBe("<media:audio>");
});
it("returns message id when rpc provides one", async () => {
requestMock.mockResolvedValue({ ok: true, id: 123 });
const result = await sendWithDefaults("chat_id:7", "hello");
expect(result.messageId).toBe("123");
});
it("prepends reply tag as the first token when replyToId is provided", async () => {
await sendWithDefaults("chat_id:123", " hello\nworld", {
replyToId: "abc-123",
});
const params = getSentParams();
expect(params.text).toBe("[[reply_to:abc-123]] hello\nworld");
});
it("rewrites an existing leading reply tag to keep the requested id first", async () => {
await sendWithDefaults("chat_id:123", " [[reply_to:old-id]] hello", {
replyToId: "new-id",
});
const params = getSentParams();
expect(params.text).toBe("[[reply_to:new-id]] hello");
});
it("sanitizes replyToId before writing the leading reply tag", async () => {
await sendWithDefaults("chat_id:123", "hello", {
replyToId: " [ab]\n\u0000c\td ] ",
});
const params = getSentParams();
expect(params.text).toBe("[[reply_to:abcd]] hello");
});
it("skips reply tagging when sanitized replyToId is empty", async () => {
await sendWithDefaults("chat_id:123", "hello", {
replyToId: "[]\u0000\n\r",
});
const params = getSentParams();
expect(params.text).toBe("hello");
});
it("normalizes string message_id values from rpc result", async () => {
requestMock.mockResolvedValue({ ok: true, message_id: " guid-1 " });
const result = await sendWithDefaults("chat_id:7", "hello");
expect(result.messageId).toBe("guid-1");
});
it("does not stop an injected client", async () => {
await sendWithDefaults("chat_id:123", "hello");
expect(stopMock).not.toHaveBeenCalled();
});
});
|