refactor: deduplicate reply payload helpers

This commit is contained in:
Peter Steinberger
2026-03-18 17:29:54 +00:00
parent 656679e6e0
commit 8d73bc77fa
67 changed files with 2246 additions and 1366 deletions

View File

@@ -1,5 +1,13 @@
import { describe, expect, it } from "vitest";
import { isNumericTargetId, sendPayloadWithChunkedTextAndMedia } from "./reply-payload.js";
import { describe, expect, it, vi } from "vitest";
import {
deliverFormattedTextWithAttachments,
deliverTextOrMediaReply,
isNumericTargetId,
resolveOutboundMediaUrls,
resolveTextChunksWithFallback,
sendMediaWithLeadingCaption,
sendPayloadWithChunkedTextAndMedia,
} from "./reply-payload.js";
describe("sendPayloadWithChunkedTextAndMedia", () => {
it("returns empty result when payload has no text and no media", async () => {
@@ -56,3 +64,155 @@ describe("sendPayloadWithChunkedTextAndMedia", () => {
expect(isNumericTargetId("")).toBe(false);
});
});
describe("resolveOutboundMediaUrls", () => {
it("prefers mediaUrls over the legacy single-media field", () => {
expect(
resolveOutboundMediaUrls({
mediaUrls: ["https://example.com/a.png", "https://example.com/b.png"],
mediaUrl: "https://example.com/legacy.png",
}),
).toEqual(["https://example.com/a.png", "https://example.com/b.png"]);
});
it("falls back to the legacy single-media field", () => {
expect(
resolveOutboundMediaUrls({
mediaUrl: "https://example.com/legacy.png",
}),
).toEqual(["https://example.com/legacy.png"]);
});
});
describe("resolveTextChunksWithFallback", () => {
it("returns existing chunks unchanged", () => {
expect(resolveTextChunksWithFallback("hello", ["a", "b"])).toEqual(["a", "b"]);
});
it("falls back to the full text when chunkers return nothing", () => {
expect(resolveTextChunksWithFallback("hello", [])).toEqual(["hello"]);
});
it("returns empty for empty text with no chunks", () => {
expect(resolveTextChunksWithFallback("", [])).toEqual([]);
});
});
describe("deliverTextOrMediaReply", () => {
it("sends media first with caption only on the first attachment", async () => {
const sendMedia = vi.fn(async () => undefined);
const sendText = vi.fn(async () => undefined);
await expect(
deliverTextOrMediaReply({
payload: { text: "hello", mediaUrls: ["https://a", "https://b"] },
text: "hello",
sendText,
sendMedia,
}),
).resolves.toBe("media");
expect(sendMedia).toHaveBeenNthCalledWith(1, {
mediaUrl: "https://a",
caption: "hello",
});
expect(sendMedia).toHaveBeenNthCalledWith(2, {
mediaUrl: "https://b",
caption: undefined,
});
expect(sendText).not.toHaveBeenCalled();
});
it("falls back to chunked text delivery when there is no media", async () => {
const sendMedia = vi.fn(async () => undefined);
const sendText = vi.fn(async () => undefined);
await expect(
deliverTextOrMediaReply({
payload: { text: "alpha beta gamma" },
text: "alpha beta gamma",
chunkText: () => ["alpha", "beta", "gamma"],
sendText,
sendMedia,
}),
).resolves.toBe("text");
expect(sendText).toHaveBeenCalledTimes(3);
expect(sendText).toHaveBeenNthCalledWith(1, "alpha");
expect(sendText).toHaveBeenNthCalledWith(2, "beta");
expect(sendText).toHaveBeenNthCalledWith(3, "gamma");
expect(sendMedia).not.toHaveBeenCalled();
});
it("returns empty when chunking produces no sendable text", async () => {
const sendMedia = vi.fn(async () => undefined);
const sendText = vi.fn(async () => undefined);
await expect(
deliverTextOrMediaReply({
payload: { text: " " },
text: " ",
chunkText: () => [],
sendText,
sendMedia,
}),
).resolves.toBe("empty");
expect(sendText).not.toHaveBeenCalled();
expect(sendMedia).not.toHaveBeenCalled();
});
});
describe("sendMediaWithLeadingCaption", () => {
it("passes leading-caption metadata to async error handlers", async () => {
const send = vi
.fn<({ mediaUrl, caption }: { mediaUrl: string; caption?: string }) => Promise<void>>()
.mockRejectedValueOnce(new Error("boom"))
.mockResolvedValueOnce(undefined);
const onError = vi.fn(async () => undefined);
await expect(
sendMediaWithLeadingCaption({
mediaUrls: ["https://example.com/a.png", "https://example.com/b.png"],
caption: "hello",
send,
onError,
}),
).resolves.toBe(true);
expect(onError).toHaveBeenCalledWith(
expect.objectContaining({
mediaUrl: "https://example.com/a.png",
caption: "hello",
index: 0,
isFirst: true,
}),
);
expect(send).toHaveBeenNthCalledWith(2, {
mediaUrl: "https://example.com/b.png",
caption: undefined,
});
});
});
describe("deliverFormattedTextWithAttachments", () => {
it("combines attachment links and forwards replyToId", async () => {
const send = vi.fn(async () => undefined);
await expect(
deliverFormattedTextWithAttachments({
payload: {
text: "hello",
mediaUrls: ["https://example.com/a.png", "https://example.com/b.png"],
replyToId: "r1",
},
send,
}),
).resolves.toBe(true);
expect(send).toHaveBeenCalledWith({
text: "hello\n\nAttachment: https://example.com/a.png\nAttachment: https://example.com/b.png",
replyToId: "r1",
});
});
});