1 import { wait } from '@proton/shared/lib/helpers/promise';
3 import retryOnError from './retryOnError';
5 jest.mock('@proton/shared/lib/helpers/promise');
7 const errorMessage = 'STRI-I-I-I-I-I-ING';
9 describe('retryOnError', () => {
10 let throwingFunction: () => Promise<void>;
11 let throwingFunction2: () => Promise<void>;
12 const mockedWait = jest.mocked(wait);
15 throwingFunction = jest.fn().mockImplementation(() => {
16 throw new Error(errorMessage);
18 throwingFunction2 = jest.fn().mockImplementation(() => {
19 throw new Error(errorMessage);
21 mockedWait.mockReset();
24 it("runs main function once if there's no error", () => {
25 const runFunction = jest.fn();
29 shouldRetryBasedOnError: () => true,
30 maxRetriesNumber: 1000,
33 expect(runFunction).toBeCalledTimes(1);
36 it('retries run function n times', async () => {
37 const promise = retryOnError<unknown>({
39 shouldRetryBasedOnError: () => true,
43 await expect(promise).rejects.toThrow();
44 expect(throwingFunction).toBeCalledTimes(2);
45 expect(retryOnError).toThrow();
48 it('validates incoming error', async () => {
49 const promise = retryOnError<unknown>({
51 shouldRetryBasedOnError: (error: unknown) => {
52 return (error as Error).message === errorMessage;
56 await expect(promise).rejects.toThrow();
57 expect(throwingFunction).toBeCalledTimes(2);
59 const promise2 = retryOnError<unknown>({
60 fn: throwingFunction2,
61 shouldRetryBasedOnError: (error: unknown) => {
62 return (error as Error).message === 'another string';
66 expect(throwingFunction2).toBeCalledTimes(1);
67 await expect(promise2).rejects.toThrow();
70 it('executes preparation function on retry', async () => {
71 const preparationFunction = jest.fn();
72 const promise = retryOnError<unknown>({
74 shouldRetryBasedOnError: (error: unknown) => {
75 return (error as Error).message === errorMessage;
77 beforeRetryCallback: preparationFunction,
80 expect(preparationFunction).toBeCalledTimes(1);
81 await expect(promise).rejects.toThrow();
84 it('returns value on successful retry attempt', async () => {
85 const returnValue = Symbol('returnValue');
87 const runFunc = jest.fn().mockImplementation(() => {
89 return Promise.resolve(returnValue);
95 const result = await retryOnError<unknown>({
97 shouldRetryBasedOnError: () => true,
101 expect(result).toBe(returnValue);
104 it('retries run function n times with backoff', async () => {
105 const promise = retryOnError<unknown>({
106 fn: throwingFunction,
107 shouldRetryBasedOnError: () => true,
112 await expect(promise).rejects.toThrow();
113 expect(retryOnError).toThrow();
114 expect(mockedWait.mock.calls.flat()).toEqual([30000, 60000, 90000, 150000]);
117 it('retries run function n times with new params', async () => {
118 const params = { params: 1 };
119 const preparationFunction = jest.fn().mockReturnValueOnce(params);
120 const promise = retryOnError<unknown>({
121 fn: throwingFunction,
122 shouldRetryBasedOnError: () => true,
123 beforeRetryCallback: preparationFunction,
126 await expect(promise).rejects.toThrow();
127 expect(throwingFunction).toHaveBeenCalledWith(params);
129 it('retries run function n times with new params and backoff', async () => {
130 const params = { params: 1 };
131 const preparationFunction = jest.fn().mockReturnValueOnce(params);
132 const promise = retryOnError<unknown>({
133 fn: throwingFunction,
134 shouldRetryBasedOnError: () => true,
135 beforeRetryCallback: preparationFunction,
139 await expect(promise).rejects.toThrow();
140 expect(throwingFunction).toHaveBeenCalledWith(params);
141 expect(mockedWait.mock.calls.flat()).toEqual([30000]);