retry.test.ts 1.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
  1. import { describe, expect, it, vi } from "vitest";
  2. import { withRetry } from "@/lib/webhook/utils/retry";
  3. describe("withRetry", () => {
  4. it("should return result on first success", async () => {
  5. const fn = vi.fn().mockResolvedValue("success");
  6. const result = await withRetry(fn, { maxRetries: 3 });
  7. expect(result).toBe("success");
  8. expect(fn).toHaveBeenCalledTimes(1);
  9. });
  10. it("should retry on failure and succeed", async () => {
  11. const fn = vi
  12. .fn()
  13. .mockRejectedValueOnce(new Error("fail 1"))
  14. .mockRejectedValueOnce(new Error("fail 2"))
  15. .mockResolvedValue("success");
  16. const result = await withRetry(fn, { maxRetries: 3, baseDelay: 1 });
  17. expect(result).toBe("success");
  18. expect(fn).toHaveBeenCalledTimes(3);
  19. });
  20. it("should throw after max retries", async () => {
  21. const fn = vi.fn().mockRejectedValue(new Error("always fail"));
  22. await expect(withRetry(fn, { maxRetries: 3, baseDelay: 1 })).rejects.toThrow("always fail");
  23. expect(fn).toHaveBeenCalledTimes(3);
  24. });
  25. it("should use exponential backoff", async () => {
  26. const delays: number[] = [];
  27. vi.spyOn(globalThis, "setTimeout").mockImplementation((fn: any, delay: number) => {
  28. delays.push(delay);
  29. fn();
  30. return 0 as any;
  31. });
  32. const fn = vi
  33. .fn()
  34. .mockRejectedValueOnce(new Error("fail"))
  35. .mockRejectedValueOnce(new Error("fail"))
  36. .mockResolvedValue("success");
  37. await withRetry(fn, { maxRetries: 3, baseDelay: 100 });
  38. expect(delays).toEqual([100, 200]); // 100 * 2^0, 100 * 2^1
  39. vi.restoreAllMocks();
  40. });
  41. });