fix: add provider retry policy

This commit is contained in:
Peter Steinberger
2026-01-07 17:48:19 +00:00
parent 8db522d6a6
commit de55f4e111
15 changed files with 779 additions and 101 deletions

View File

@@ -25,4 +25,80 @@ describe("retryAsync", () => {
await expect(retryAsync(fn, 2, 1)).rejects.toThrow("boom");
expect(fn).toHaveBeenCalledTimes(2);
});
it("stops when shouldRetry returns false", async () => {
const fn = vi.fn().mockRejectedValue(new Error("boom"));
await expect(
retryAsync(fn, { attempts: 3, shouldRetry: () => false }),
).rejects.toThrow("boom");
expect(fn).toHaveBeenCalledTimes(1);
});
it("calls onRetry before retrying", async () => {
const fn = vi
.fn()
.mockRejectedValueOnce(new Error("boom"))
.mockResolvedValueOnce("ok");
const onRetry = vi.fn();
const res = await retryAsync(fn, {
attempts: 2,
minDelayMs: 0,
maxDelayMs: 0,
onRetry,
});
expect(res).toBe("ok");
expect(onRetry).toHaveBeenCalledWith(
expect.objectContaining({ attempt: 1, maxAttempts: 2 }),
);
});
it("clamps attempts to at least 1", async () => {
const fn = vi.fn().mockRejectedValue(new Error("boom"));
await expect(
retryAsync(fn, { attempts: 0, minDelayMs: 0, maxDelayMs: 0 }),
).rejects.toThrow("boom");
expect(fn).toHaveBeenCalledTimes(1);
});
it("uses retryAfterMs when provided", async () => {
vi.useFakeTimers();
const fn = vi
.fn()
.mockRejectedValueOnce(new Error("boom"))
.mockResolvedValueOnce("ok");
const delays: number[] = [];
const promise = retryAsync(fn, {
attempts: 2,
minDelayMs: 0,
maxDelayMs: 1000,
jitter: 0,
retryAfterMs: () => 500,
onRetry: (info) => delays.push(info.delayMs),
});
await vi.runAllTimersAsync();
await expect(promise).resolves.toBe("ok");
expect(delays[0]).toBe(500);
vi.useRealTimers();
});
it("clamps retryAfterMs to maxDelayMs", async () => {
vi.useFakeTimers();
const fn = vi
.fn()
.mockRejectedValueOnce(new Error("boom"))
.mockResolvedValueOnce("ok");
const delays: number[] = [];
const promise = retryAsync(fn, {
attempts: 2,
minDelayMs: 0,
maxDelayMs: 100,
jitter: 0,
retryAfterMs: () => 500,
onRetry: (info) => delays.push(info.delayMs),
});
await vi.runAllTimersAsync();
await expect(promise).resolves.toBe("ok");
expect(delays[0]).toBe(100);
vi.useRealTimers();
});
});