From ecd278b67b2df1d2f8eb7872580754865d8d65d9 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 23 Feb 2026 21:57:23 +0000 Subject: [PATCH] test: merge redundant telegram media path scenarios --- ...s-media-file-path-no-file-download.test.ts | 343 ++++++++---------- 1 file changed, 160 insertions(+), 183 deletions(-) diff --git a/src/telegram/bot.media.downloads-media-file-path-no-file-download.test.ts b/src/telegram/bot.media.downloads-media-file-path-no-file-download.test.ts index 47448cd0a..265e6a92e 100644 --- a/src/telegram/bot.media.downloads-media-file-path-no-file-download.test.ts +++ b/src/telegram/bot.media.downloads-media-file-path-no-file-download.test.ts @@ -112,35 +112,69 @@ describe("telegram inbound media", () => { const INBOUND_MEDIA_TEST_TIMEOUT_MS = process.platform === "win32" ? 120_000 : 90_000; it( - "downloads media via file_path (no file.download)", + "handles file_path media downloads and missing file_path safely", async () => { - const { handler, replySpy, runtimeError } = await createBotHandler(); - const fetchSpy = mockTelegramFileDownload({ - contentType: "image/jpeg", - bytes: new Uint8Array([0xff, 0xd8, 0xff, 0x00]), - }); - - await handler({ - message: { - message_id: 1, - chat: { id: 1234, type: "private" }, - photo: [{ file_id: "fid" }], - date: 1736380800, // 2025-01-09T00:00:00Z + for (const scenario of [ + { + name: "downloads via file_path", + getFile: async () => ({ file_path: "photos/1.jpg" }), + setupFetch: () => + mockTelegramFileDownload({ + contentType: "image/jpeg", + bytes: new Uint8Array([0xff, 0xd8, 0xff, 0x00]), + }), + assert: (params: { + fetchSpy: ReturnType; + replySpy: ReturnType; + runtimeError: ReturnType; + }) => { + expect(params.runtimeError).not.toHaveBeenCalled(); + expect(params.fetchSpy).toHaveBeenCalledWith( + "https://api.telegram.org/file/bottok/photos/1.jpg", + expect.objectContaining({ redirect: "manual" }), + ); + expect(params.replySpy).toHaveBeenCalledTimes(1); + const payload = params.replySpy.mock.calls[0][0]; + expect(payload.Body).toContain(""); + }, }, - me: { username: "openclaw_bot" }, - getFile: async () => ({ file_path: "photos/1.jpg" }), - }); + { + name: "skips when file_path is missing", + getFile: async () => ({}), + setupFetch: () => vi.spyOn(globalThis, "fetch"), + assert: (params: { + fetchSpy: ReturnType; + replySpy: ReturnType; + runtimeError: ReturnType; + }) => { + expect(params.fetchSpy).not.toHaveBeenCalled(); + expect(params.replySpy).not.toHaveBeenCalled(); + expect(params.runtimeError).not.toHaveBeenCalled(); + }, + }, + ]) { + const runtimeLog = vi.fn(); + const runtimeError = vi.fn(); + const { handler, replySpy } = await createBotHandlerWithOptions({ + runtimeLog, + runtimeError, + }); + const fetchSpy = scenario.setupFetch(); - expect(runtimeError).not.toHaveBeenCalled(); - expect(fetchSpy).toHaveBeenCalledWith( - "https://api.telegram.org/file/bottok/photos/1.jpg", - expect.objectContaining({ redirect: "manual" }), - ); - expect(replySpy).toHaveBeenCalledTimes(1); - const payload = replySpy.mock.calls[0][0]; - expect(payload.Body).toContain(""); + await handler({ + message: { + message_id: 1, + chat: { id: 1234, type: "private" }, + photo: [{ file_id: "fid" }], + date: 1736380800, // 2025-01-09T00:00:00Z + }, + me: { username: "openclaw_bot" }, + getFile: scenario.getFile, + }); - fetchSpy.mockRestore(); + scenario.assert({ fetchSpy, replySpy, runtimeError }); + fetchSpy.mockRestore(); + } }, INBOUND_MEDIA_TEST_TIMEOUT_MS, ); @@ -184,32 +218,6 @@ describe("telegram inbound media", () => { globalFetchSpy.mockRestore(); }); - it("handles missing file_path from getFile without crashing", async () => { - const runtimeLog = vi.fn(); - const runtimeError = vi.fn(); - const { handler, replySpy } = await createBotHandlerWithOptions({ - runtimeLog, - runtimeError, - }); - const fetchSpy = vi.spyOn(globalThis, "fetch"); - - await handler({ - message: { - message_id: 3, - chat: { id: 1234, type: "private" }, - photo: [{ file_id: "fid" }], - }, - me: { username: "openclaw_bot" }, - getFile: async () => ({}), - }); - - expect(fetchSpy).not.toHaveBeenCalled(); - expect(replySpy).not.toHaveBeenCalled(); - expect(runtimeError).not.toHaveBeenCalled(); - - fetchSpy.mockRestore(); - }); - it("captures pin and venue location payload fields", async () => { const { handler, replySpy } = await createBotHandler(); @@ -279,99 +287,87 @@ describe("telegram media groups", () => { const MEDIA_GROUP_FLUSH_MS = TELEGRAM_TEST_TIMINGS.mediaGroupFlushMs + 60; it( - "buffers messages with same media_group_id and processes them together", + "handles same-group buffering and separate-group independence", async () => { - const runtimeError = vi.fn(); - const { handler, replySpy } = await createBotHandlerWithOptions({ runtimeError }); - const fetchSpy = mockTelegramPngDownload(); - const first = handler({ - message: { - chat: { id: 42, type: "private" }, - message_id: 1, - caption: "Here are my photos", - date: 1736380800, - media_group_id: "album123", - photo: [{ file_id: "photo1" }], + for (const scenario of [ + { + messages: [ + { + chat: { id: 42, type: "private" as const }, + message_id: 1, + caption: "Here are my photos", + date: 1736380800, + media_group_id: "album123", + photo: [{ file_id: "photo1" }], + filePath: "photos/photo1.jpg", + }, + { + chat: { id: 42, type: "private" as const }, + message_id: 2, + date: 1736380801, + media_group_id: "album123", + photo: [{ file_id: "photo2" }], + filePath: "photos/photo2.jpg", + }, + ], + expectedReplyCount: 1, + assert: (replySpy: ReturnType) => { + const payload = replySpy.mock.calls[0]?.[0]; + expect(payload?.Body).toContain("Here are my photos"); + expect(payload?.MediaPaths).toHaveLength(2); + }, }, - me: { username: "openclaw_bot" }, - getFile: async () => ({ file_path: "photos/photo1.jpg" }), - }); - - const second = handler({ - message: { - chat: { id: 42, type: "private" }, - message_id: 2, - date: 1736380801, - media_group_id: "album123", - photo: [{ file_id: "photo2" }], + { + messages: [ + { + chat: { id: 42, type: "private" as const }, + message_id: 11, + caption: "Album A", + date: 1736380800, + media_group_id: "albumA", + photo: [{ file_id: "photoA1" }], + filePath: "photos/photoA1.jpg", + }, + { + chat: { id: 42, type: "private" as const }, + message_id: 12, + caption: "Album B", + date: 1736380801, + media_group_id: "albumB", + photo: [{ file_id: "photoB1" }], + filePath: "photos/photoB1.jpg", + }, + ], + expectedReplyCount: 2, + assert: () => {}, }, - me: { username: "openclaw_bot" }, - getFile: async () => ({ file_path: "photos/photo2.jpg" }), - }); + ]) { + const runtimeError = vi.fn(); + const { handler, replySpy } = await createBotHandlerWithOptions({ runtimeError }); + const fetchSpy = mockTelegramPngDownload(); - await first; - await second; + await Promise.all( + scenario.messages.map((message) => + handler({ + message, + me: { username: "openclaw_bot" }, + getFile: async () => ({ file_path: message.filePath }), + }), + ), + ); - expect(replySpy).not.toHaveBeenCalled(); - await vi.waitFor( - () => { - expect(replySpy).toHaveBeenCalledTimes(1); - }, - { timeout: MEDIA_GROUP_FLUSH_MS * 2, interval: 10 }, - ); + expect(replySpy).not.toHaveBeenCalled(); + await vi.waitFor( + () => { + expect(replySpy).toHaveBeenCalledTimes(scenario.expectedReplyCount); + }, + { timeout: MEDIA_GROUP_FLUSH_MS * 2, interval: 10 }, + ); - expect(runtimeError).not.toHaveBeenCalled(); - const payload = replySpy.mock.calls[0][0]; - expect(payload.Body).toContain("Here are my photos"); - expect(payload.MediaPaths).toHaveLength(2); - - fetchSpy.mockRestore(); - }, - MEDIA_GROUP_TEST_TIMEOUT_MS, - ); - - it( - "processes separate media groups independently", - async () => { - const { handler, replySpy } = await createBotHandler(); - const fetchSpy = mockTelegramPngDownload(); - const first = handler({ - message: { - chat: { id: 42, type: "private" }, - message_id: 1, - caption: "Album A", - date: 1736380800, - media_group_id: "albumA", - photo: [{ file_id: "photoA1" }], - }, - me: { username: "openclaw_bot" }, - getFile: async () => ({ file_path: "photos/photoA1.jpg" }), - }); - - const second = handler({ - message: { - chat: { id: 42, type: "private" }, - message_id: 2, - caption: "Album B", - date: 1736380801, - media_group_id: "albumB", - photo: [{ file_id: "photoB1" }], - }, - me: { username: "openclaw_bot" }, - getFile: async () => ({ file_path: "photos/photoB1.jpg" }), - }); - - await Promise.all([first, second]); - - expect(replySpy).not.toHaveBeenCalled(); - await vi.waitFor( - () => { - expect(replySpy).toHaveBeenCalledTimes(2); - }, - { timeout: MEDIA_GROUP_FLUSH_MS * 2, interval: 10 }, - ); - - fetchSpy.mockRestore(); + expect(runtimeError).not.toHaveBeenCalled(); + scenario.assert(replySpy); + fetchSpy.mockRestore(); + } }, MEDIA_GROUP_TEST_TIMEOUT_MS, ); @@ -556,53 +552,25 @@ describe("telegram stickers", () => { ); it( - "skips animated stickers (TGS format)", + "skips animated and video sticker formats that cannot be downloaded", async () => { - const { handler, replySpy, runtimeError } = await createBotHandler(); - const fetchSpy = vi.spyOn(globalThis, "fetch"); - - await handler({ - message: { - message_id: 101, - chat: { id: 1234, type: "private" }, + for (const scenario of [ + { + filePath: "stickers/animated.tgs", sticker: { file_id: "animated_sticker_id", file_unique_id: "animated_unique", type: "regular", width: 512, height: 512, - is_animated: true, // TGS format + is_animated: true, is_video: false, emoji: "😎", set_name: "AnimatedPack", }, - date: 1736380800, }, - me: { username: "openclaw_bot" }, - getFile: async () => ({ file_path: "stickers/animated.tgs" }), - }); - - // Should not attempt to download animated stickers - expect(fetchSpy).not.toHaveBeenCalled(); - // Should still process the message (as text-only, no media) - expect(replySpy).not.toHaveBeenCalled(); // No text content, so no reply generated - expect(runtimeError).not.toHaveBeenCalled(); - - fetchSpy.mockRestore(); - }, - STICKER_TEST_TIMEOUT_MS, - ); - - it( - "skips video stickers (WEBM format)", - async () => { - const { handler, replySpy, runtimeError } = await createBotHandler(); - const fetchSpy = vi.spyOn(globalThis, "fetch"); - - await handler({ - message: { - message_id: 102, - chat: { id: 1234, type: "private" }, + { + filePath: "stickers/video.webm", sticker: { file_id: "video_sticker_id", file_unique_id: "video_unique", @@ -610,22 +578,31 @@ describe("telegram stickers", () => { width: 512, height: 512, is_animated: false, - is_video: true, // WEBM format + is_video: true, emoji: "🎬", set_name: "VideoPack", }, - date: 1736380800, }, - me: { username: "openclaw_bot" }, - getFile: async () => ({ file_path: "stickers/video.webm" }), - }); + ]) { + const { handler, replySpy, runtimeError } = await createBotHandler(); + const fetchSpy = vi.spyOn(globalThis, "fetch"); - // Should not attempt to download video stickers - expect(fetchSpy).not.toHaveBeenCalled(); - expect(replySpy).not.toHaveBeenCalled(); - expect(runtimeError).not.toHaveBeenCalled(); + await handler({ + message: { + message_id: 101, + chat: { id: 1234, type: "private" }, + sticker: scenario.sticker, + date: 1736380800, + }, + me: { username: "openclaw_bot" }, + getFile: async () => ({ file_path: scenario.filePath }), + }); - fetchSpy.mockRestore(); + expect(fetchSpy).not.toHaveBeenCalled(); + expect(replySpy).not.toHaveBeenCalled(); + expect(runtimeError).not.toHaveBeenCalled(); + fetchSpy.mockRestore(); + } }, STICKER_TEST_TIMEOUT_MS, );