test(telegram): inject media loader in delivery replies

This commit is contained in:
Vincent Koc
2026-03-19 06:30:38 -07:00
parent a0445b192e
commit 79d7fdce93
2 changed files with 20 additions and 7 deletions

View File

@@ -1,6 +1,8 @@
import { type Bot, GrammyError, InputFile } from "grammy";
import type { ReplyToMode } from "openclaw/plugin-sdk/config-runtime";
import type { MarkdownTableMode } from "openclaw/plugin-sdk/config-runtime";
import type { ReplyPayload } from "openclaw/plugin-sdk/reply-runtime";
import type { RuntimeEnv } from "openclaw/plugin-sdk/runtime-env";
import { type Bot, GrammyError, InputFile } from "grammy";
import { fireAndForgetHook } from "openclaw/plugin-sdk/hook-runtime";
import { createInternalHookEvent, triggerInternalHook } from "openclaw/plugin-sdk/hook-runtime";
import {
@@ -14,9 +16,7 @@ import { buildOutboundMediaLoadOptions } from "openclaw/plugin-sdk/media-runtime
import { isGifMedia, kindFromMime } from "openclaw/plugin-sdk/media-runtime";
import { getGlobalHookRunner } from "openclaw/plugin-sdk/plugin-runtime";
import { chunkMarkdownTextWithMode, type ChunkMode } from "openclaw/plugin-sdk/reply-runtime";
import type { ReplyPayload } from "openclaw/plugin-sdk/reply-runtime";
import { danger, logVerbose } from "openclaw/plugin-sdk/runtime-env";
import type { RuntimeEnv } from "openclaw/plugin-sdk/runtime-env";
import { loadWebMedia } from "openclaw/plugin-sdk/web-media";
import type { TelegramInlineButtons } from "../button-types.js";
import { splitTelegramCaption } from "../caption.js";
@@ -238,6 +238,7 @@ async function deliverMediaReply(params: {
tableMode?: MarkdownTableMode;
mediaLocalRoots?: readonly string[];
chunkText: ChunkTextFn;
mediaLoader: typeof loadWebMedia;
onVoiceRecording?: () => Promise<void> | void;
linkPreview?: boolean;
silent?: boolean;
@@ -252,7 +253,7 @@ async function deliverMediaReply(params: {
let pendingFollowUpText: string | undefined;
for (const mediaUrl of params.mediaList) {
const isFirstMedia = first;
const media = await loadWebMedia(
const media = await params.mediaLoader(
mediaUrl,
buildOutboundMediaLoadOptions({ mediaLocalRoots: params.mediaLocalRoots }),
);
@@ -569,12 +570,15 @@ export async function deliverReplies(params: {
silent?: boolean;
/** Optional quote text for Telegram reply_parameters. */
replyQuoteText?: string;
/** Override media loader (tests). */
mediaLoader?: typeof loadWebMedia;
}): Promise<{ delivered: boolean }> {
const progress: DeliveryProgress = {
hasReplied: false,
hasDelivered: false,
deliveredCount: 0,
};
const mediaLoader = params.mediaLoader ?? loadWebMedia;
const hookRunner = getGlobalHookRunner();
const hasMessageSendingHooks = hookRunner?.hasHooks("message_sending") ?? false;
const hasMessageSentHooks = hookRunner?.hasHooks("message_sent") ?? false;
@@ -663,6 +667,7 @@ export async function deliverReplies(params: {
tableMode: params.tableMode,
mediaLocalRoots: params.mediaLocalRoots,
chunkText,
mediaLoader,
onVoiceRecording: params.onVoiceRecording,
linkPreview: params.linkPreview,
silent: params.silent,

View File

@@ -1,9 +1,10 @@
import type { Bot } from "grammy";
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { RuntimeEnv } from "../../../../src/runtime.js";
import { deliverReplies } from "./delivery.js";
const loadWebMedia = vi.fn();
const { loadWebMedia } = vi.hoisted(() => ({
loadWebMedia: vi.fn(),
}));
const triggerInternalHook = vi.hoisted(() => vi.fn(async () => {}));
const messageHookRunner = vi.hoisted(() => ({
hasHooks: vi.fn<(name: string) => boolean>(() => false),
@@ -21,12 +22,15 @@ type DeliverWithParams = Omit<
DeliverRepliesParams,
"chatId" | "token" | "replyToMode" | "textLimit"
> &
Partial<Pick<DeliverRepliesParams, "replyToMode" | "textLimit">>;
Partial<Pick<DeliverRepliesParams, "replyToMode" | "textLimit" | "mediaLoader">>;
type RuntimeStub = Pick<RuntimeEnv, "error" | "log" | "exit">;
vi.mock("openclaw/plugin-sdk/web-media", () => ({
loadWebMedia: (...args: unknown[]) => loadWebMedia(...args),
}));
vi.mock("openclaw/plugin-sdk/web-media.js", () => ({
loadWebMedia: (...args: unknown[]) => loadWebMedia(...args),
}));
vi.mock("../../../../src/plugins/hook-runner-global.js", () => ({
getGlobalHookRunner: () => messageHookRunner,
@@ -42,6 +46,9 @@ vi.mock("../../../../src/hooks/internal-hooks.js", async () => {
};
});
vi.resetModules();
const { deliverReplies } = await import("./delivery.js");
vi.mock("grammy", () => ({
InputFile: class {
constructor(
@@ -70,6 +77,7 @@ async function deliverWith(params: DeliverWithParams) {
await deliverReplies({
...baseDeliveryParams,
...params,
mediaLoader: params.mediaLoader ?? loadWebMedia,
});
}