mirror of
https://github.com/moltbot/moltbot.git
synced 2026-05-02 02:57:51 +00:00
test: merge redundant telegram media path scenarios
This commit is contained in:
@@ -112,35 +112,69 @@ describe("telegram inbound media", () => {
|
|||||||
const INBOUND_MEDIA_TEST_TIMEOUT_MS = process.platform === "win32" ? 120_000 : 90_000;
|
const INBOUND_MEDIA_TEST_TIMEOUT_MS = process.platform === "win32" ? 120_000 : 90_000;
|
||||||
|
|
||||||
it(
|
it(
|
||||||
"downloads media via file_path (no file.download)",
|
"handles file_path media downloads and missing file_path safely",
|
||||||
async () => {
|
async () => {
|
||||||
const { handler, replySpy, runtimeError } = await createBotHandler();
|
for (const scenario of [
|
||||||
const fetchSpy = mockTelegramFileDownload({
|
{
|
||||||
contentType: "image/jpeg",
|
name: "downloads via file_path",
|
||||||
bytes: new Uint8Array([0xff, 0xd8, 0xff, 0x00]),
|
getFile: async () => ({ file_path: "photos/1.jpg" }),
|
||||||
});
|
setupFetch: () =>
|
||||||
|
mockTelegramFileDownload({
|
||||||
await handler({
|
contentType: "image/jpeg",
|
||||||
message: {
|
bytes: new Uint8Array([0xff, 0xd8, 0xff, 0x00]),
|
||||||
message_id: 1,
|
}),
|
||||||
chat: { id: 1234, type: "private" },
|
assert: (params: {
|
||||||
photo: [{ file_id: "fid" }],
|
fetchSpy: ReturnType<typeof vi.spyOn>;
|
||||||
date: 1736380800, // 2025-01-09T00:00:00Z
|
replySpy: ReturnType<typeof vi.fn>;
|
||||||
|
runtimeError: ReturnType<typeof vi.fn>;
|
||||||
|
}) => {
|
||||||
|
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("<media:image>");
|
||||||
|
},
|
||||||
},
|
},
|
||||||
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<typeof vi.spyOn>;
|
||||||
|
replySpy: ReturnType<typeof vi.fn>;
|
||||||
|
runtimeError: ReturnType<typeof vi.fn>;
|
||||||
|
}) => {
|
||||||
|
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();
|
await handler({
|
||||||
expect(fetchSpy).toHaveBeenCalledWith(
|
message: {
|
||||||
"https://api.telegram.org/file/bottok/photos/1.jpg",
|
message_id: 1,
|
||||||
expect.objectContaining({ redirect: "manual" }),
|
chat: { id: 1234, type: "private" },
|
||||||
);
|
photo: [{ file_id: "fid" }],
|
||||||
expect(replySpy).toHaveBeenCalledTimes(1);
|
date: 1736380800, // 2025-01-09T00:00:00Z
|
||||||
const payload = replySpy.mock.calls[0][0];
|
},
|
||||||
expect(payload.Body).toContain("<media:image>");
|
me: { username: "openclaw_bot" },
|
||||||
|
getFile: scenario.getFile,
|
||||||
|
});
|
||||||
|
|
||||||
fetchSpy.mockRestore();
|
scenario.assert({ fetchSpy, replySpy, runtimeError });
|
||||||
|
fetchSpy.mockRestore();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
INBOUND_MEDIA_TEST_TIMEOUT_MS,
|
INBOUND_MEDIA_TEST_TIMEOUT_MS,
|
||||||
);
|
);
|
||||||
@@ -184,32 +218,6 @@ describe("telegram inbound media", () => {
|
|||||||
globalFetchSpy.mockRestore();
|
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 () => {
|
it("captures pin and venue location payload fields", async () => {
|
||||||
const { handler, replySpy } = await createBotHandler();
|
const { handler, replySpy } = await createBotHandler();
|
||||||
|
|
||||||
@@ -279,99 +287,87 @@ describe("telegram media groups", () => {
|
|||||||
const MEDIA_GROUP_FLUSH_MS = TELEGRAM_TEST_TIMINGS.mediaGroupFlushMs + 60;
|
const MEDIA_GROUP_FLUSH_MS = TELEGRAM_TEST_TIMINGS.mediaGroupFlushMs + 60;
|
||||||
|
|
||||||
it(
|
it(
|
||||||
"buffers messages with same media_group_id and processes them together",
|
"handles same-group buffering and separate-group independence",
|
||||||
async () => {
|
async () => {
|
||||||
const runtimeError = vi.fn();
|
for (const scenario of [
|
||||||
const { handler, replySpy } = await createBotHandlerWithOptions({ runtimeError });
|
{
|
||||||
const fetchSpy = mockTelegramPngDownload();
|
messages: [
|
||||||
const first = handler({
|
{
|
||||||
message: {
|
chat: { id: 42, type: "private" as const },
|
||||||
chat: { id: 42, type: "private" },
|
message_id: 1,
|
||||||
message_id: 1,
|
caption: "Here are my photos",
|
||||||
caption: "Here are my photos",
|
date: 1736380800,
|
||||||
date: 1736380800,
|
media_group_id: "album123",
|
||||||
media_group_id: "album123",
|
photo: [{ file_id: "photo1" }],
|
||||||
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<typeof vi.fn>) => {
|
||||||
|
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" }),
|
messages: [
|
||||||
});
|
{
|
||||||
|
chat: { id: 42, type: "private" as const },
|
||||||
const second = handler({
|
message_id: 11,
|
||||||
message: {
|
caption: "Album A",
|
||||||
chat: { id: 42, type: "private" },
|
date: 1736380800,
|
||||||
message_id: 2,
|
media_group_id: "albumA",
|
||||||
date: 1736380801,
|
photo: [{ file_id: "photoA1" }],
|
||||||
media_group_id: "album123",
|
filePath: "photos/photoA1.jpg",
|
||||||
photo: [{ file_id: "photo2" }],
|
},
|
||||||
|
{
|
||||||
|
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 Promise.all(
|
||||||
await second;
|
scenario.messages.map((message) =>
|
||||||
|
handler({
|
||||||
|
message,
|
||||||
|
me: { username: "openclaw_bot" },
|
||||||
|
getFile: async () => ({ file_path: message.filePath }),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
expect(replySpy).not.toHaveBeenCalled();
|
expect(replySpy).not.toHaveBeenCalled();
|
||||||
await vi.waitFor(
|
await vi.waitFor(
|
||||||
() => {
|
() => {
|
||||||
expect(replySpy).toHaveBeenCalledTimes(1);
|
expect(replySpy).toHaveBeenCalledTimes(scenario.expectedReplyCount);
|
||||||
},
|
},
|
||||||
{ timeout: MEDIA_GROUP_FLUSH_MS * 2, interval: 10 },
|
{ timeout: MEDIA_GROUP_FLUSH_MS * 2, interval: 10 },
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(runtimeError).not.toHaveBeenCalled();
|
expect(runtimeError).not.toHaveBeenCalled();
|
||||||
const payload = replySpy.mock.calls[0][0];
|
scenario.assert(replySpy);
|
||||||
expect(payload.Body).toContain("Here are my photos");
|
fetchSpy.mockRestore();
|
||||||
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();
|
|
||||||
},
|
},
|
||||||
MEDIA_GROUP_TEST_TIMEOUT_MS,
|
MEDIA_GROUP_TEST_TIMEOUT_MS,
|
||||||
);
|
);
|
||||||
@@ -556,53 +552,25 @@ describe("telegram stickers", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
it(
|
it(
|
||||||
"skips animated stickers (TGS format)",
|
"skips animated and video sticker formats that cannot be downloaded",
|
||||||
async () => {
|
async () => {
|
||||||
const { handler, replySpy, runtimeError } = await createBotHandler();
|
for (const scenario of [
|
||||||
const fetchSpy = vi.spyOn(globalThis, "fetch");
|
{
|
||||||
|
filePath: "stickers/animated.tgs",
|
||||||
await handler({
|
|
||||||
message: {
|
|
||||||
message_id: 101,
|
|
||||||
chat: { id: 1234, type: "private" },
|
|
||||||
sticker: {
|
sticker: {
|
||||||
file_id: "animated_sticker_id",
|
file_id: "animated_sticker_id",
|
||||||
file_unique_id: "animated_unique",
|
file_unique_id: "animated_unique",
|
||||||
type: "regular",
|
type: "regular",
|
||||||
width: 512,
|
width: 512,
|
||||||
height: 512,
|
height: 512,
|
||||||
is_animated: true, // TGS format
|
is_animated: true,
|
||||||
is_video: false,
|
is_video: false,
|
||||||
emoji: "😎",
|
emoji: "😎",
|
||||||
set_name: "AnimatedPack",
|
set_name: "AnimatedPack",
|
||||||
},
|
},
|
||||||
date: 1736380800,
|
|
||||||
},
|
},
|
||||||
me: { username: "openclaw_bot" },
|
{
|
||||||
getFile: async () => ({ file_path: "stickers/animated.tgs" }),
|
filePath: "stickers/video.webm",
|
||||||
});
|
|
||||||
|
|
||||||
// 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" },
|
|
||||||
sticker: {
|
sticker: {
|
||||||
file_id: "video_sticker_id",
|
file_id: "video_sticker_id",
|
||||||
file_unique_id: "video_unique",
|
file_unique_id: "video_unique",
|
||||||
@@ -610,22 +578,31 @@ describe("telegram stickers", () => {
|
|||||||
width: 512,
|
width: 512,
|
||||||
height: 512,
|
height: 512,
|
||||||
is_animated: false,
|
is_animated: false,
|
||||||
is_video: true, // WEBM format
|
is_video: true,
|
||||||
emoji: "🎬",
|
emoji: "🎬",
|
||||||
set_name: "VideoPack",
|
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
|
await handler({
|
||||||
expect(fetchSpy).not.toHaveBeenCalled();
|
message: {
|
||||||
expect(replySpy).not.toHaveBeenCalled();
|
message_id: 101,
|
||||||
expect(runtimeError).not.toHaveBeenCalled();
|
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,
|
STICKER_TEST_TIMEOUT_MS,
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user