Merge branch 'feat/inda-383-daily-stat' into 'main'
[ProtonMail-WebClient.git] / packages / drive-store / utils / retryOnError.test.ts
blobb941ccd61d23055205568d84132f5578377520ca
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);
14     beforeEach(() => {
15         throwingFunction = jest.fn().mockImplementation(() => {
16             throw new Error(errorMessage);
17         });
18         throwingFunction2 = jest.fn().mockImplementation(() => {
19             throw new Error(errorMessage);
20         });
21         mockedWait.mockReset();
22     });
24     it("runs main function once if there's no error", () => {
25         const runFunction = jest.fn();
27         void retryOnError({
28             fn: runFunction,
29             shouldRetryBasedOnError: () => true,
30             maxRetriesNumber: 1000,
31         })();
33         expect(runFunction).toBeCalledTimes(1);
34     });
36     it('retries run function n times', async () => {
37         const promise = retryOnError<unknown>({
38             fn: throwingFunction,
39             shouldRetryBasedOnError: () => true,
40             maxRetriesNumber: 1,
41         })();
43         await expect(promise).rejects.toThrow();
44         expect(throwingFunction).toBeCalledTimes(2);
45         expect(retryOnError).toThrow();
46     });
48     it('validates incoming error', async () => {
49         const promise = retryOnError<unknown>({
50             fn: throwingFunction,
51             shouldRetryBasedOnError: (error: unknown) => {
52                 return (error as Error).message === errorMessage;
53             },
54             maxRetriesNumber: 1,
55         })();
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';
63             },
64             maxRetriesNumber: 1,
65         })();
66         expect(throwingFunction2).toBeCalledTimes(1);
67         await expect(promise2).rejects.toThrow();
68     });
70     it('executes preparation function on retry', async () => {
71         const preparationFunction = jest.fn();
72         const promise = retryOnError<unknown>({
73             fn: throwingFunction,
74             shouldRetryBasedOnError: (error: unknown) => {
75                 return (error as Error).message === errorMessage;
76             },
77             beforeRetryCallback: preparationFunction,
78             maxRetriesNumber: 1,
79         })();
80         expect(preparationFunction).toBeCalledTimes(1);
81         await expect(promise).rejects.toThrow();
82     });
84     it('returns value on successful retry attempt', async () => {
85         const returnValue = Symbol('returnValue');
86         let execCount = 0;
87         const runFunc = jest.fn().mockImplementation(() => {
88             if (execCount > 1) {
89                 return Promise.resolve(returnValue);
90             }
91             execCount++;
92             throw new Error();
93         });
95         const result = await retryOnError<unknown>({
96             fn: runFunc,
97             shouldRetryBasedOnError: () => true,
98             maxRetriesNumber: 2,
99         })().catch(() => {});
101         expect(result).toBe(returnValue);
102     });
104     it('retries run function n times with backoff', async () => {
105         const promise = retryOnError<unknown>({
106             fn: throwingFunction,
107             shouldRetryBasedOnError: () => true,
108             maxRetriesNumber: 4,
109             backoff: true,
110         })();
112         await expect(promise).rejects.toThrow();
113         expect(retryOnError).toThrow();
114         expect(mockedWait.mock.calls.flat()).toEqual([30000, 60000, 90000, 150000]);
115     });
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,
124             maxRetriesNumber: 1,
125         })();
126         await expect(promise).rejects.toThrow();
127         expect(throwingFunction).toHaveBeenCalledWith(params);
128     });
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,
136             maxRetriesNumber: 1,
137             backoff: true,
138         })();
139         await expect(promise).rejects.toThrow();
140         expect(throwingFunction).toHaveBeenCalledWith(params);
141         expect(mockedWait.mock.calls.flat()).toEqual([30000]);
142     });