From ac3f834cee66603c90dda0bc6e2789dbb1048a91 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 14 Feb 2026 21:01:39 +0000 Subject: [PATCH] perf(test): consolidate web auto-reply media e2e suites --- ...resses-common-formats-jpeg-cap.e2e.test.ts | 170 ++++++++++++++++ ...y.falls-back-text-media-send-fails.test.ts | 183 ------------------ 2 files changed, 170 insertions(+), 183 deletions(-) delete mode 100644 src/web/auto-reply.web-auto-reply.falls-back-text-media-send-fails.test.ts diff --git a/src/web/auto-reply.web-auto-reply.compresses-common-formats-jpeg-cap.e2e.test.ts b/src/web/auto-reply.web-auto-reply.compresses-common-formats-jpeg-cap.e2e.test.ts index 1e934667081..ef81bad3546 100644 --- a/src/web/auto-reply.web-auto-reply.compresses-common-formats-jpeg-cap.e2e.test.ts +++ b/src/web/auto-reply.web-auto-reply.compresses-common-formats-jpeg-cap.e2e.test.ts @@ -230,4 +230,174 @@ describe("web auto-reply", () => { fetchMock.mockRestore(); }); + + it("falls back to text when media send fails", async () => { + const sendMedia = vi.fn().mockRejectedValue(new Error("boom")); + const reply = vi.fn().mockResolvedValue(undefined); + const sendComposing = vi.fn(); + const resolver = vi.fn().mockResolvedValue({ + text: "hi", + mediaUrl: "https://example.com/img.png", + }); + + let capturedOnMessage: + | ((msg: import("./inbound.js").WebInboundMessage) => Promise) + | undefined; + const listenerFactory = async (opts: { + onMessage: (msg: import("./inbound.js").WebInboundMessage) => Promise; + }) => { + capturedOnMessage = opts.onMessage; + return { close: vi.fn() }; + }; + + const smallPng = await sharp({ + create: { + width: 64, + height: 64, + channels: 3, + background: { r: 0, g: 255, b: 0 }, + }, + }) + .png() + .toBuffer(); + const fetchMock = vi.spyOn(globalThis, "fetch").mockResolvedValue({ + ok: true, + body: true, + arrayBuffer: async () => + smallPng.buffer.slice(smallPng.byteOffset, smallPng.byteOffset + smallPng.byteLength), + headers: { get: () => "image/png" }, + status: 200, + } as Response); + + await monitorWebChannel(false, listenerFactory, false, resolver); + + expect(capturedOnMessage).toBeDefined(); + await capturedOnMessage?.({ + body: "hello", + from: "+1", + to: "+2", + id: "msg1", + sendComposing, + reply, + sendMedia, + }); + + expect(sendMedia).toHaveBeenCalledTimes(1); + const fallback = reply.mock.calls[0]?.[0] as string; + expect(fallback).toContain("hi"); + expect(fallback).toContain("Media failed"); + fetchMock.mockRestore(); + }); + it("returns a warning when remote media fetch 404s", async () => { + const sendMedia = vi.fn(); + const reply = vi.fn().mockResolvedValue(undefined); + const sendComposing = vi.fn(); + const resolver = vi.fn().mockResolvedValue({ + text: "caption", + mediaUrl: "https://example.com/missing.jpg", + }); + + let capturedOnMessage: + | ((msg: import("./inbound.js").WebInboundMessage) => Promise) + | undefined; + const listenerFactory = async (opts: { + onMessage: (msg: import("./inbound.js").WebInboundMessage) => Promise; + }) => { + capturedOnMessage = opts.onMessage; + return { close: vi.fn() }; + }; + + const fetchMock = vi.spyOn(globalThis, "fetch").mockResolvedValue({ + ok: false, + status: 404, + body: null, + arrayBuffer: async () => new ArrayBuffer(0), + headers: { get: () => "text/plain" }, + } as unknown as Response); + + await monitorWebChannel(false, listenerFactory, false, resolver); + expect(capturedOnMessage).toBeDefined(); + + await capturedOnMessage?.({ + body: "hello", + from: "+1", + to: "+2", + id: "msg1", + sendComposing, + reply, + sendMedia, + }); + + expect(sendMedia).not.toHaveBeenCalled(); + const fallback = reply.mock.calls[0]?.[0] as string; + expect(fallback).toContain("caption"); + expect(fallback).toContain("Media failed"); + expect(fallback).toContain("404"); + + fetchMock.mockRestore(); + }); + it("sends media with a caption when delivery succeeds", async () => { + const sendMedia = vi.fn().mockResolvedValue(undefined); + const reply = vi.fn().mockResolvedValue(undefined); + const sendComposing = vi.fn(); + const resolver = vi.fn().mockResolvedValue({ + text: "hi", + mediaUrl: "https://example.com/img.png", + }); + + let capturedOnMessage: + | ((msg: import("./inbound.js").WebInboundMessage) => Promise) + | undefined; + const listenerFactory = async (opts: { + onMessage: (msg: import("./inbound.js").WebInboundMessage) => Promise; + }) => { + capturedOnMessage = opts.onMessage; + return { close: vi.fn() }; + }; + + const png = await sharp({ + create: { + width: 64, + height: 64, + channels: 3, + background: { r: 0, g: 0, b: 255 }, + }, + }) + .png() + .toBuffer(); + + const fetchMock = vi.spyOn(globalThis, "fetch").mockResolvedValue({ + ok: true, + body: true, + arrayBuffer: async () => png.buffer.slice(png.byteOffset, png.byteOffset + png.byteLength), + headers: { get: () => "image/png" }, + status: 200, + } as Response); + + await monitorWebChannel(false, listenerFactory, false, resolver); + expect(capturedOnMessage).toBeDefined(); + + await capturedOnMessage?.({ + body: "hello", + from: "+1", + to: "+2", + id: "msg1", + sendComposing, + reply, + sendMedia, + }); + + expect(sendMedia).toHaveBeenCalledTimes(1); + const payload = sendMedia.mock.calls[0][0] as { + image: Buffer; + caption?: string; + mimetype?: string; + }; + expect(payload.caption).toBe("hi"); + expect(payload.image.length).toBeGreaterThan(0); + // Should not fall back to separate text reply because caption is used. + expect(reply).not.toHaveBeenCalled(); + + fetchMock.mockRestore(); + }); }); diff --git a/src/web/auto-reply.web-auto-reply.falls-back-text-media-send-fails.test.ts b/src/web/auto-reply.web-auto-reply.falls-back-text-media-send-fails.test.ts deleted file mode 100644 index 382d5ea0769..00000000000 --- a/src/web/auto-reply.web-auto-reply.falls-back-text-media-send-fails.test.ts +++ /dev/null @@ -1,183 +0,0 @@ -import sharp from "sharp"; -import { describe, expect, it, vi } from "vitest"; -import { monitorWebChannel } from "./auto-reply.js"; -import { - installWebAutoReplyTestHomeHooks, - installWebAutoReplyUnitTestHooks, -} from "./auto-reply.test-harness.js"; - -installWebAutoReplyTestHomeHooks(); - -describe("web auto-reply", () => { - installWebAutoReplyUnitTestHooks({ pinDns: true }); - - it("falls back to text when media send fails", async () => { - const sendMedia = vi.fn().mockRejectedValue(new Error("boom")); - const reply = vi.fn().mockResolvedValue(undefined); - const sendComposing = vi.fn(); - const resolver = vi.fn().mockResolvedValue({ - text: "hi", - mediaUrl: "https://example.com/img.png", - }); - - let capturedOnMessage: - | ((msg: import("./inbound.js").WebInboundMessage) => Promise) - | undefined; - const listenerFactory = async (opts: { - onMessage: (msg: import("./inbound.js").WebInboundMessage) => Promise; - }) => { - capturedOnMessage = opts.onMessage; - return { close: vi.fn() }; - }; - - const smallPng = await sharp({ - create: { - width: 64, - height: 64, - channels: 3, - background: { r: 0, g: 255, b: 0 }, - }, - }) - .png() - .toBuffer(); - const fetchMock = vi.spyOn(globalThis, "fetch").mockResolvedValue({ - ok: true, - body: true, - arrayBuffer: async () => - smallPng.buffer.slice(smallPng.byteOffset, smallPng.byteOffset + smallPng.byteLength), - headers: { get: () => "image/png" }, - status: 200, - } as Response); - - await monitorWebChannel(false, listenerFactory, false, resolver); - - expect(capturedOnMessage).toBeDefined(); - await capturedOnMessage?.({ - body: "hello", - from: "+1", - to: "+2", - id: "msg1", - sendComposing, - reply, - sendMedia, - }); - - expect(sendMedia).toHaveBeenCalledTimes(1); - const fallback = reply.mock.calls[0]?.[0] as string; - expect(fallback).toContain("hi"); - expect(fallback).toContain("Media failed"); - fetchMock.mockRestore(); - }); - it("returns a warning when remote media fetch 404s", async () => { - const sendMedia = vi.fn(); - const reply = vi.fn().mockResolvedValue(undefined); - const sendComposing = vi.fn(); - const resolver = vi.fn().mockResolvedValue({ - text: "caption", - mediaUrl: "https://example.com/missing.jpg", - }); - - let capturedOnMessage: - | ((msg: import("./inbound.js").WebInboundMessage) => Promise) - | undefined; - const listenerFactory = async (opts: { - onMessage: (msg: import("./inbound.js").WebInboundMessage) => Promise; - }) => { - capturedOnMessage = opts.onMessage; - return { close: vi.fn() }; - }; - - const fetchMock = vi.spyOn(globalThis, "fetch").mockResolvedValue({ - ok: false, - status: 404, - body: null, - arrayBuffer: async () => new ArrayBuffer(0), - headers: { get: () => "text/plain" }, - } as unknown as Response); - - await monitorWebChannel(false, listenerFactory, false, resolver); - expect(capturedOnMessage).toBeDefined(); - - await capturedOnMessage?.({ - body: "hello", - from: "+1", - to: "+2", - id: "msg1", - sendComposing, - reply, - sendMedia, - }); - - expect(sendMedia).not.toHaveBeenCalled(); - const fallback = reply.mock.calls[0]?.[0] as string; - expect(fallback).toContain("caption"); - expect(fallback).toContain("Media failed"); - expect(fallback).toContain("404"); - - fetchMock.mockRestore(); - }); - it("sends media with a caption when delivery succeeds", async () => { - const sendMedia = vi.fn().mockResolvedValue(undefined); - const reply = vi.fn().mockResolvedValue(undefined); - const sendComposing = vi.fn(); - const resolver = vi.fn().mockResolvedValue({ - text: "hi", - mediaUrl: "https://example.com/img.png", - }); - - let capturedOnMessage: - | ((msg: import("./inbound.js").WebInboundMessage) => Promise) - | undefined; - const listenerFactory = async (opts: { - onMessage: (msg: import("./inbound.js").WebInboundMessage) => Promise; - }) => { - capturedOnMessage = opts.onMessage; - return { close: vi.fn() }; - }; - - const png = await sharp({ - create: { - width: 64, - height: 64, - channels: 3, - background: { r: 0, g: 0, b: 255 }, - }, - }) - .png() - .toBuffer(); - - const fetchMock = vi.spyOn(globalThis, "fetch").mockResolvedValue({ - ok: true, - body: true, - arrayBuffer: async () => png.buffer.slice(png.byteOffset, png.byteOffset + png.byteLength), - headers: { get: () => "image/png" }, - status: 200, - } as Response); - - await monitorWebChannel(false, listenerFactory, false, resolver); - expect(capturedOnMessage).toBeDefined(); - - await capturedOnMessage?.({ - body: "hello", - from: "+1", - to: "+2", - id: "msg1", - sendComposing, - reply, - sendMedia, - }); - - expect(sendMedia).toHaveBeenCalledTimes(1); - const payload = sendMedia.mock.calls[0][0] as { - image: Buffer; - caption?: string; - mimetype?: string; - }; - expect(payload.caption).toBe("hi"); - expect(payload.image.length).toBeGreaterThan(0); - // Should not fall back to separate text reply because caption is used. - expect(reply).not.toHaveBeenCalled(); - - fetchMock.mockRestore(); - }); -});