import { beforeEach, describe, expect, it, vi } from "vitest"; const runMock = vi.hoisted(() => vi.fn()); const createTelegramBotMock = vi.hoisted(() => vi.fn()); const isRecoverableTelegramNetworkErrorMock = vi.hoisted(() => vi.fn(() => true)); const computeBackoffMock = vi.hoisted(() => vi.fn(() => 0)); const sleepWithAbortMock = vi.hoisted(() => vi.fn(async () => undefined)); vi.mock("@grammyjs/runner", () => ({ run: runMock, })); vi.mock("./bot.js", () => ({ createTelegramBot: createTelegramBotMock, })); vi.mock("./network-errors.js", () => ({ isRecoverableTelegramNetworkError: isRecoverableTelegramNetworkErrorMock, })); vi.mock("./api-logging.js", () => ({ withTelegramApiErrorLogging: async ({ fn }: { fn: () => Promise }) => await fn(), })); vi.mock("openclaw/plugin-sdk/infra-runtime", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, computeBackoff: computeBackoffMock, sleepWithAbort: sleepWithAbortMock, }; }); import { TelegramPollingSession } from "./polling-session.js"; describe("TelegramPollingSession", () => { beforeEach(() => { runMock.mockReset(); createTelegramBotMock.mockReset(); isRecoverableTelegramNetworkErrorMock.mockReset().mockReturnValue(true); computeBackoffMock.mockReset().mockReturnValue(0); sleepWithAbortMock.mockReset().mockResolvedValue(undefined); }); it("uses backoff helpers for recoverable polling retries", async () => { const abort = new AbortController(); const recoverableError = new Error("recoverable polling error"); const botStop = vi.fn(async () => undefined); const runnerStop = vi.fn(async () => undefined); const bot = { api: { deleteWebhook: vi.fn(async () => true), getUpdates: vi.fn(async () => []), config: { use: vi.fn() }, }, stop: botStop, }; createTelegramBotMock.mockReturnValue(bot); let firstCycle = true; runMock.mockImplementation(() => { if (firstCycle) { firstCycle = false; return { task: async () => { throw recoverableError; }, stop: runnerStop, isRunning: () => false, }; } return { task: async () => { abort.abort(); }, stop: runnerStop, isRunning: () => false, }; }); const session = new TelegramPollingSession({ token: "tok", config: {}, accountId: "default", runtime: undefined, proxyFetch: undefined, abortSignal: abort.signal, runnerOptions: {}, getLastUpdateId: () => null, persistUpdateId: async () => undefined, log: () => undefined, telegramTransport: undefined, }); await session.runUntilAbort(); expect(runMock).toHaveBeenCalledTimes(2); expect(computeBackoffMock).toHaveBeenCalledTimes(1); expect(sleepWithAbortMock).toHaveBeenCalledTimes(1); }); });