Files
clawdbot/src/telegram/monitor.test.ts
2026-01-09 15:35:39 +01:00

144 lines
3.7 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
import { monitorTelegramProvider } from "./monitor.js";
type MockCtx = {
message: {
chat: { id: number; type: string; title?: string };
text?: string;
caption?: string;
};
me?: { username: string };
getFile: () => Promise<unknown>;
};
// Fake bot to capture handler and API calls
const handlers: Record<string, (ctx: MockCtx) => Promise<void> | void> = {};
const api = {
sendMessage: vi.fn(),
sendPhoto: vi.fn(),
sendVideo: vi.fn(),
sendAudio: vi.fn(),
sendDocument: vi.fn(),
setWebhook: vi.fn(),
deleteWebhook: vi.fn(),
};
const { initSpy, runSpy, loadConfig } = vi.hoisted(() => ({
initSpy: vi.fn(async () => undefined),
runSpy: vi.fn(() => ({
task: () => Promise.resolve(),
stop: vi.fn(),
})),
loadConfig: vi.fn(() => ({
agents: { defaults: { maxConcurrent: 2 } },
telegram: {},
})),
}));
vi.mock("../config/config.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../config/config.js")>();
return {
...actual,
loadConfig,
};
});
vi.mock("./bot.js", () => ({
createTelegramBot: () => {
handlers.message = async (ctx: MockCtx) => {
const chatId = ctx.message.chat.id;
const isGroup = ctx.message.chat.type !== "private";
const text = ctx.message.text ?? ctx.message.caption ?? "";
if (isGroup && !text.includes("@mybot")) return;
if (!text.trim()) return;
await api.sendMessage(chatId, `echo:${text}`, { parse_mode: "HTML" });
};
return {
on: vi.fn(),
api,
me: { username: "mybot" },
init: initSpy,
stop: vi.fn(),
start: vi.fn(),
};
},
createTelegramWebhookCallback: vi.fn(),
}));
// Mock the grammyjs/runner to resolve immediately
vi.mock("@grammyjs/runner", () => ({
run: runSpy,
}));
vi.mock("../auto-reply/reply.js", () => ({
getReplyFromConfig: async (ctx: { Body?: string }) => ({
text: `echo:${ctx.Body}`,
}),
}));
describe("monitorTelegramProvider (grammY)", () => {
beforeEach(() => {
loadConfig.mockReturnValue({
agents: { defaults: { maxConcurrent: 2 } },
telegram: {},
});
initSpy.mockClear();
runSpy.mockClear();
});
it("processes a DM and sends reply", async () => {
Object.values(api).forEach((fn) => {
fn?.mockReset?.();
});
await monitorTelegramProvider({ token: "tok" });
expect(handlers.message).toBeDefined();
await handlers.message?.({
message: {
message_id: 1,
chat: { id: 123, type: "private" },
text: "hi",
},
me: { username: "mybot" },
getFile: vi.fn(async () => ({})),
});
expect(api.sendMessage).toHaveBeenCalledWith(123, "echo:hi", {
parse_mode: "HTML",
});
});
it("uses agent maxConcurrent for runner concurrency", async () => {
runSpy.mockClear();
loadConfig.mockReturnValue({
agents: { defaults: { maxConcurrent: 3 } },
telegram: {},
});
await monitorTelegramProvider({ token: "tok" });
expect(runSpy).toHaveBeenCalledWith(
expect.anything(),
expect.objectContaining({
sink: { concurrency: 3 },
runner: expect.objectContaining({ silent: true }),
}),
);
});
it("requires mention in groups by default", async () => {
Object.values(api).forEach((fn) => {
fn?.mockReset?.();
});
await monitorTelegramProvider({ token: "tok" });
await handlers.message?.({
message: {
message_id: 2,
chat: { id: -99, type: "supergroup", title: "G" },
text: "hello all",
},
me: { username: "mybot" },
getFile: vi.fn(async () => ({})),
});
expect(api.sendMessage).not.toHaveBeenCalled();
});
});