perf(test): consolidate web auto-reply media e2e suites

This commit is contained in:
Peter Steinberger 2026-02-14 21:01:39 +00:00
parent 03ea99ec65
commit ac3f834cee
2 changed files with 170 additions and 183 deletions

View File

@ -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<void>)
| undefined;
const listenerFactory = async (opts: {
onMessage: (msg: import("./inbound.js").WebInboundMessage) => Promise<void>;
}) => {
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<void>)
| undefined;
const listenerFactory = async (opts: {
onMessage: (msg: import("./inbound.js").WebInboundMessage) => Promise<void>;
}) => {
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<void>)
| undefined;
const listenerFactory = async (opts: {
onMessage: (msg: import("./inbound.js").WebInboundMessage) => Promise<void>;
}) => {
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();
});
});

View File

@ -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<void>)
| undefined;
const listenerFactory = async (opts: {
onMessage: (msg: import("./inbound.js").WebInboundMessage) => Promise<void>;
}) => {
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<void>)
| undefined;
const listenerFactory = async (opts: {
onMessage: (msg: import("./inbound.js").WebInboundMessage) => Promise<void>;
}) => {
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<void>)
| undefined;
const listenerFactory = async (opts: {
onMessage: (msg: import("./inbound.js").WebInboundMessage) => Promise<void>;
}) => {
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();
});
});