test(telegram): scope fake timers in probe retry tests

This commit is contained in:
Peter Steinberger
2026-02-22 00:05:16 +00:00
parent b3c5b532ad
commit 4a42bc64af

View File

@@ -1,13 +1,18 @@
import { type Mock, describe, expect, it, vi, beforeEach, afterEach } from "vitest"; import { type Mock, describe, expect, it, vi } from "vitest";
import { withFetchPreconnect } from "../test-utils/fetch-mock.js"; import { withFetchPreconnect } from "../test-utils/fetch-mock.js";
import { probeTelegram } from "./probe.js"; import { probeTelegram } from "./probe.js";
describe("probeTelegram retry logic", () => { describe("probeTelegram retry logic", () => {
const token = "test-token"; const token = "test-token";
const timeoutMs = 5000; const timeoutMs = 5000;
let fetchMock: Mock;
function mockGetMeSuccess() { const installFetchMock = (): Mock => {
const fetchMock = vi.fn();
global.fetch = withFetchPreconnect(fetchMock);
return fetchMock;
};
function mockGetMeSuccess(fetchMock: Mock) {
fetchMock.mockResolvedValueOnce({ fetchMock.mockResolvedValueOnce({
ok: true, ok: true,
json: vi.fn().mockResolvedValue({ json: vi.fn().mockResolvedValue({
@@ -17,14 +22,14 @@ describe("probeTelegram retry logic", () => {
}); });
} }
function mockGetWebhookInfoSuccess() { function mockGetWebhookInfoSuccess(fetchMock: Mock) {
fetchMock.mockResolvedValueOnce({ fetchMock.mockResolvedValueOnce({
ok: true, ok: true,
json: vi.fn().mockResolvedValue({ ok: true, result: { url: "" } }), json: vi.fn().mockResolvedValue({ ok: true, result: { url: "" } }),
}); });
} }
async function expectSuccessfulProbe(expectedCalls: number, retryCount = 0) { async function expectSuccessfulProbe(fetchMock: Mock, expectedCalls: number, retryCount = 0) {
const probePromise = probeTelegram(token, timeoutMs); const probePromise = probeTelegram(token, timeoutMs);
if (retryCount > 0) { if (retryCount > 0) {
await vi.advanceTimersByTimeAsync(retryCount * 1000); await vi.advanceTimersByTimeAsync(retryCount * 1000);
@@ -36,17 +41,6 @@ describe("probeTelegram retry logic", () => {
expect(result.bot?.username).toBe("test_bot"); expect(result.bot?.username).toBe("test_bot");
} }
beforeEach(() => {
vi.useFakeTimers();
fetchMock = vi.fn();
global.fetch = withFetchPreconnect(fetchMock);
});
afterEach(() => {
vi.useRealTimers();
vi.restoreAllMocks();
});
it.each([ it.each([
{ {
errors: [], errors: [],
@@ -64,32 +58,45 @@ describe("probeTelegram retry logic", () => {
retryCount: 2, retryCount: 2,
}, },
])("succeeds after retry pattern %#", async ({ errors, expectedCalls, retryCount }) => { ])("succeeds after retry pattern %#", async ({ errors, expectedCalls, retryCount }) => {
for (const message of errors) { const fetchMock = installFetchMock();
fetchMock.mockRejectedValueOnce(new Error(message)); vi.useFakeTimers();
} try {
for (const message of errors) {
fetchMock.mockRejectedValueOnce(new Error(message));
}
mockGetMeSuccess(); mockGetMeSuccess(fetchMock);
mockGetWebhookInfoSuccess(); mockGetWebhookInfoSuccess(fetchMock);
await expectSuccessfulProbe(expectedCalls, retryCount); await expectSuccessfulProbe(fetchMock, expectedCalls, retryCount);
} finally {
vi.useRealTimers();
}
}); });
it("should fail after 3 unsuccessful attempts", async () => { it("should fail after 3 unsuccessful attempts", async () => {
const fetchMock = installFetchMock();
vi.useFakeTimers();
const errorMsg = "Final network error"; const errorMsg = "Final network error";
fetchMock.mockRejectedValue(new Error(errorMsg)); try {
fetchMock.mockRejectedValue(new Error(errorMsg));
const probePromise = probeTelegram(token, timeoutMs); const probePromise = probeTelegram(token, timeoutMs);
// Fast-forward for all retries // Fast-forward for all retries
await vi.advanceTimersByTimeAsync(2000); await vi.advanceTimersByTimeAsync(2000);
const result = await probePromise; const result = await probePromise;
expect(result.ok).toBe(false); expect(result.ok).toBe(false);
expect(result.error).toBe(errorMsg); expect(result.error).toBe(errorMsg);
expect(fetchMock).toHaveBeenCalledTimes(3); // 3 attempts at getMe expect(fetchMock).toHaveBeenCalledTimes(3); // 3 attempts at getMe
} finally {
vi.useRealTimers();
}
}); });
it("should NOT retry if getMe returns a 401 Unauthorized", async () => { it("should NOT retry if getMe returns a 401 Unauthorized", async () => {
const fetchMock = installFetchMock();
const mockResponse = { const mockResponse = {
ok: false, ok: false,
status: 401, status: 401,