From 89b531fd9d31316bb086bcd16f2e939056dfe29d Mon Sep 17 00:00:00 2001 From: Dhereal1 Date: Thu, 2 Apr 2026 23:00:11 +0100 Subject: [PATCH] test(bot): ensure init retries after failure and does not cache rejection --- app/bot/webhook.ensureBotInit.test.ts | 83 +++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 app/bot/webhook.ensureBotInit.test.ts diff --git a/app/bot/webhook.ensureBotInit.test.ts b/app/bot/webhook.ensureBotInit.test.ts new file mode 100644 index 00000000..848b400f --- /dev/null +++ b/app/bot/webhook.ensureBotInit.test.ts @@ -0,0 +1,83 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { Bot } from 'grammy'; + +test('ensureBotInit resets after init failure (no stuck rejected promise)', async (t) => { + const originalInit = Bot.prototype.init; + const originalHandleUpdate = Bot.prototype.handleUpdate; + + let initCalls = 0; + let handleUpdateCalls = 0; + + Bot.prototype.init = async function initMock(): Promise { + initCalls += 1; + if (initCalls === 1) throw new Error('init failed (simulated)'); + }; + + Bot.prototype.handleUpdate = async function handleUpdateMock(): Promise { + handleUpdateCalls += 1; + }; + + t.after(() => { + Bot.prototype.init = originalInit; + Bot.prototype.handleUpdate = originalHandleUpdate; + delete process.env.BOT_TOKEN; + delete process.env.TELEGRAM_BOT_TOKEN; + }); + + process.env.BOT_TOKEN = 'test-token'; + + // Import after env + prototype mocks so the module creates a bot using the patched methods. + const mod = await import('./webhook.ts'); + const handler = mod.default as ( + req: { method: string; body?: unknown }, + res: { + setHeader(name: string, value: string): void; + status(code: number): { json(data: unknown): void; end(): void }; + end(): void; + }, + ) => Promise; + + function createRes() { + let statusCode: number | null = null; + let jsonBody: unknown = undefined; + return { + get statusCode() { + return statusCode; + }, + get jsonBody() { + return jsonBody; + }, + res: { + setHeader() {}, + status(code: number) { + statusCode = code; + return { + json(data: unknown) { + jsonBody = data; + }, + end() {}, + }; + }, + end() {}, + }, + }; + } + + const update = JSON.stringify({ + update_id: 1, + message: { chat: { id: 123 }, text: 'hi' }, + }); + + const first = createRes(); + await handler({ method: 'POST', body: update }, first.res); + assert.equal(first.statusCode, 500); + + const second = createRes(); + await handler({ method: 'POST', body: update }, second.res); + assert.equal(second.statusCode, 200); + + assert.equal(initCalls, 2); + assert.equal(handleUpdateCalls, 1); +}); +