test(imessage): dedupe send test scaffolding

This commit is contained in:
Peter Steinberger
2026-02-18 13:01:37 +00:00
parent 7f7fc523cf
commit 7b46f2c17f

View File

@@ -1,5 +1,6 @@
import { beforeEach, describe, expect, it, vi } from "vitest"; import { beforeEach, describe, expect, it, vi } from "vitest";
import type { ResolvedIMessageAccount } from "./accounts.js"; import type { ResolvedIMessageAccount } from "./accounts.js";
import type { IMessageRpcClient } from "./client.js";
import { sendMessageIMessage } from "./send.js"; import { sendMessageIMessage } from "./send.js";
const requestMock = vi.fn(); const requestMock = vi.fn();
@@ -12,6 +13,30 @@ const defaultAccount: ResolvedIMessageAccount = {
config: {}, 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", () => { describe("sendMessageIMessage", () => {
beforeEach(() => { beforeEach(() => {
requestMock.mockReset().mockResolvedValue({ ok: true }); requestMock.mockReset().mockResolvedValue({ ok: true });
@@ -19,119 +44,79 @@ describe("sendMessageIMessage", () => {
}); });
it("sends to chat_id targets", async () => { it("sends to chat_id targets", async () => {
await sendMessageIMessage("chat_id:123", "hi", { await sendWithDefaults("chat_id:123", "hi");
account: defaultAccount, const params = getSentParams();
config: {},
client: {
request: (...args: unknown[]) => requestMock(...args),
stop: (...args: unknown[]) => stopMock(...args),
} as unknown as import("./client.js").IMessageRpcClient,
});
const params = requestMock.mock.calls[0]?.[1] as Record<string, unknown>;
expect(requestMock).toHaveBeenCalledWith("send", expect.any(Object), expect.any(Object)); expect(requestMock).toHaveBeenCalledWith("send", expect.any(Object), expect.any(Object));
expect(params.chat_id).toBe(123); expect(params.chat_id).toBe(123);
expect(params.text).toBe("hi"); expect(params.text).toBe("hi");
}); });
it("applies sms service prefix", async () => { it("applies sms service prefix", async () => {
await sendMessageIMessage("sms:+1555", "hello", { await sendWithDefaults("sms:+1555", "hello");
account: defaultAccount, const params = getSentParams();
config: {},
client: {
request: (...args: unknown[]) => requestMock(...args),
stop: (...args: unknown[]) => stopMock(...args),
} as unknown as import("./client.js").IMessageRpcClient,
});
const params = requestMock.mock.calls[0]?.[1] as Record<string, unknown>;
expect(params.service).toBe("sms"); expect(params.service).toBe("sms");
expect(params.to).toBe("+1555"); expect(params.to).toBe("+1555");
}); });
it("adds file attachment with placeholder text", async () => { it("adds file attachment with placeholder text", async () => {
await sendMessageIMessage("chat_id:7", "", { await sendWithDefaults("chat_id:7", "", {
mediaUrl: "http://x/y.jpg", mediaUrl: "http://x/y.jpg",
account: defaultAccount,
config: {},
resolveAttachmentImpl: async () => ({ resolveAttachmentImpl: async () => ({
path: "/tmp/imessage-media.jpg", path: "/tmp/imessage-media.jpg",
contentType: "image/jpeg", contentType: "image/jpeg",
}), }),
client: {
request: (...args: unknown[]) => requestMock(...args),
stop: (...args: unknown[]) => stopMock(...args),
} as unknown as import("./client.js").IMessageRpcClient,
}); });
const params = requestMock.mock.calls[0]?.[1] as Record<string, unknown>; const params = getSentParams();
expect(params.file).toBe("/tmp/imessage-media.jpg"); expect(params.file).toBe("/tmp/imessage-media.jpg");
expect(params.text).toBe("<media:image>"); expect(params.text).toBe("<media:image>");
}); });
it("returns message id when rpc provides one", async () => { it("returns message id when rpc provides one", async () => {
requestMock.mockResolvedValue({ ok: true, id: 123 }); requestMock.mockResolvedValue({ ok: true, id: 123 });
const result = await sendMessageIMessage("chat_id:7", "hello", { const result = await sendWithDefaults("chat_id:7", "hello");
account: defaultAccount,
config: {},
client: {
request: (...args: unknown[]) => requestMock(...args),
stop: (...args: unknown[]) => stopMock(...args),
} as unknown as import("./client.js").IMessageRpcClient,
});
expect(result.messageId).toBe("123"); expect(result.messageId).toBe("123");
}); });
it("prepends reply tag as the first token when replyToId is provided", async () => { it("prepends reply tag as the first token when replyToId is provided", async () => {
await sendMessageIMessage("chat_id:123", " hello\nworld", { await sendWithDefaults("chat_id:123", " hello\nworld", {
replyToId: "abc-123", replyToId: "abc-123",
account: defaultAccount,
config: {},
client: {
request: (...args: unknown[]) => requestMock(...args),
stop: (...args: unknown[]) => stopMock(...args),
} as unknown as import("./client.js").IMessageRpcClient,
}); });
const params = requestMock.mock.calls[0]?.[1] as Record<string, unknown>; const params = getSentParams();
expect(params.text).toBe("[[reply_to:abc-123]] hello\nworld"); expect(params.text).toBe("[[reply_to:abc-123]] hello\nworld");
}); });
it("rewrites an existing leading reply tag to keep the requested id first", async () => { it("rewrites an existing leading reply tag to keep the requested id first", async () => {
await sendMessageIMessage("chat_id:123", " [[reply_to:old-id]] hello", { await sendWithDefaults("chat_id:123", " [[reply_to:old-id]] hello", {
replyToId: "new-id", replyToId: "new-id",
account: defaultAccount,
config: {},
client: {
request: (...args: unknown[]) => requestMock(...args),
stop: (...args: unknown[]) => stopMock(...args),
} as unknown as import("./client.js").IMessageRpcClient,
}); });
const params = requestMock.mock.calls[0]?.[1] as Record<string, unknown>; const params = getSentParams();
expect(params.text).toBe("[[reply_to:new-id]] hello"); expect(params.text).toBe("[[reply_to:new-id]] hello");
}); });
it("sanitizes replyToId before writing the leading reply tag", async () => { it("sanitizes replyToId before writing the leading reply tag", async () => {
await sendMessageIMessage("chat_id:123", "hello", { await sendWithDefaults("chat_id:123", "hello", {
replyToId: " [ab]\n\u0000c\td ] ", replyToId: " [ab]\n\u0000c\td ] ",
account: defaultAccount,
config: {},
client: {
request: (...args: unknown[]) => requestMock(...args),
stop: (...args: unknown[]) => stopMock(...args),
} as unknown as import("./client.js").IMessageRpcClient,
}); });
const params = requestMock.mock.calls[0]?.[1] as Record<string, unknown>; const params = getSentParams();
expect(params.text).toBe("[[reply_to:abcd]] hello"); expect(params.text).toBe("[[reply_to:abcd]] hello");
}); });
it("skips reply tagging when sanitized replyToId is empty", async () => { it("skips reply tagging when sanitized replyToId is empty", async () => {
await sendMessageIMessage("chat_id:123", "hello", { await sendWithDefaults("chat_id:123", "hello", {
replyToId: "[]\u0000\n\r", replyToId: "[]\u0000\n\r",
account: defaultAccount,
config: {},
client: {
request: (...args: unknown[]) => requestMock(...args),
stop: (...args: unknown[]) => stopMock(...args),
} as unknown as import("./client.js").IMessageRpcClient,
}); });
const params = requestMock.mock.calls[0]?.[1] as Record<string, unknown>; const params = getSentParams();
expect(params.text).toBe("hello"); 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();
});
}); });