Use same lock values as mobile clients
[ProtonMail-WebClient.git] / packages / metrics / tests / MetricsRequestService.test.ts
blob84d5d00b9ba0585ce2da0f69042f1f5f8adc6597
1 import MetricsApi from '../lib/MetricsApi';
2 import MetricsRequestService from '../lib/MetricsRequestService';
3 import type MetricsRequest from '../lib/types/MetricsRequest';
5 jest.mock('../lib/MetricsApi');
6 const metricsApi = new MetricsApi();
8 const getMetricsRequest = (name: string): MetricsRequest => ({
9     Name: name,
10     Version: 1,
11     Timestamp: 1,
12     Data: {},
13 });
15 const defaultMetricsRequest = getMetricsRequest('Name');
17 describe('MetricsRequestService', () => {
18     beforeAll(() => {
19         jest.useFakeTimers();
20     });
22     beforeEach(() => {
23         jest.clearAllTimers();
24     });
26     afterAll(() => {
27         jest.useRealTimers();
28     });
30     it('makes construct correct request', () => {
31         const requestService = new MetricsRequestService(metricsApi, { reportMetrics: true });
33         requestService.report(defaultMetricsRequest);
35         expect(metricsApi.fetch).toHaveBeenCalledTimes(1);
36         expect(metricsApi.fetch).toHaveBeenCalledWith('api/data/v1/metrics', {
37             method: 'post',
38             body: JSON.stringify({ Metrics: [defaultMetricsRequest] }),
39         });
40     });
42     describe('report metrics', () => {
43         it('allows api calls if reportMetrics was set to true in constructor', () => {
44             const requestService = new MetricsRequestService(metricsApi, { reportMetrics: true });
46             requestService.report(defaultMetricsRequest);
48             expect(metricsApi.fetch).toHaveBeenCalledTimes(1);
49         });
51         it('disallows api calls if reportMetrics was set to false in constructor', () => {
52             const requestService = new MetricsRequestService(metricsApi, { reportMetrics: false });
54             requestService.report(defaultMetricsRequest);
56             expect(metricsApi.fetch).not.toHaveBeenCalled();
57         });
59         describe('setter', () => {
60             it('allows api calls if reportMetrics is set to true via setter', () => {
61                 const requestService = new MetricsRequestService(metricsApi, { reportMetrics: false });
63                 requestService.setReportMetrics(true);
64                 requestService.report(defaultMetricsRequest);
66                 expect(metricsApi.fetch).toHaveBeenCalledTimes(1);
67             });
69             it('disallows api calls if reportMetrics is set to false via setter', () => {
70                 const requestService = new MetricsRequestService(metricsApi, { reportMetrics: true });
72                 requestService.setReportMetrics(false);
73                 requestService.report(defaultMetricsRequest);
75                 expect(metricsApi.fetch).not.toHaveBeenCalled();
76             });
77         });
78     });
80     describe('batching', () => {
81         describe('batch parameters', () => {
82             it('does not batch if frequency is 0', () => {
83                 const requestService = new MetricsRequestService(metricsApi, {
84                     reportMetrics: true,
85                     batch: {
86                         frequency: 0,
87                         size: 1,
88                     },
89                 });
91                 requestService.report(defaultMetricsRequest);
93                 expect(metricsApi.fetch).toHaveBeenCalledTimes(1);
94             });
96             it('does not batch if frequency is negative', () => {
97                 const requestService = new MetricsRequestService(metricsApi, {
98                     reportMetrics: true,
99                     batch: {
100                         frequency: -1,
101                         size: 1,
102                     },
103                 });
105                 requestService.report(defaultMetricsRequest);
107                 expect(metricsApi.fetch).toHaveBeenCalledTimes(1);
108             });
110             it('does not batch if size is 0', () => {
111                 const requestService = new MetricsRequestService(metricsApi, {
112                     reportMetrics: true,
113                     batch: {
114                         frequency: 1,
115                         size: 0,
116                     },
117                 });
119                 requestService.report(defaultMetricsRequest);
121                 expect(metricsApi.fetch).toHaveBeenCalledTimes(1);
122             });
124             it('does not batch if size is negative', () => {
125                 const requestService = new MetricsRequestService(metricsApi, {
126                     reportMetrics: true,
127                     batch: {
128                         frequency: 1,
129                         size: -1,
130                     },
131                 });
133                 requestService.report(defaultMetricsRequest);
135                 expect(metricsApi.fetch).toHaveBeenCalledTimes(1);
136             });
138             it('batches if frequency and size are positive', () => {
139                 const requestService = new MetricsRequestService(metricsApi, {
140                     reportMetrics: true,
141                     batch: {
142                         frequency: 1,
143                         size: 1,
144                     },
145                 });
147                 requestService.report(getMetricsRequest('Request 1'));
148                 requestService.report(getMetricsRequest('Request 2'));
149                 expect(metricsApi.fetch).not.toHaveBeenCalled();
151                 jest.advanceTimersByTime(1);
152                 expect(metricsApi.fetch).toHaveBeenCalledWith('api/data/v1/metrics', {
153                     method: 'post',
154                     body: JSON.stringify({ Metrics: [getMetricsRequest('Request 1')] }),
155                 });
157                 jest.advanceTimersByTime(1);
158                 expect(metricsApi.fetch).toHaveBeenCalledWith('api/data/v1/metrics', {
159                     method: 'post',
160                     body: JSON.stringify({ Metrics: [getMetricsRequest('Request 2')] }),
161                 });
162             });
163         });
165         it('delays api calls until frequency timeout has expired', () => {
166             const batchFrequency = 100;
167             const batchSize = 5;
168             const requestService = new MetricsRequestService(metricsApi, {
169                 reportMetrics: true,
170                 batch: {
171                     frequency: batchFrequency,
172                     size: batchSize,
173                 },
174             });
176             requestService.report(defaultMetricsRequest);
177             expect(metricsApi.fetch).not.toHaveBeenCalled();
179             // fast-forward time until 1 millisecond before it should be executed
180             jest.advanceTimersByTime(batchFrequency - 1);
181             expect(metricsApi.fetch).not.toHaveBeenCalled();
183             // fast-forward until 1st call should be executed
184             jest.advanceTimersByTime(1);
185             expect(metricsApi.fetch).toHaveBeenCalledTimes(1);
186         });
188         it('does not call api if there are no items to process', () => {
189             const batchFrequency = 100;
190             const batchSize = 5;
191             const requestService = new MetricsRequestService(metricsApi, {
192                 reportMetrics: true,
193                 batch: {
194                     frequency: batchFrequency,
195                     size: batchSize,
196                 },
197             });
198             requestService.startBatchingProcess();
200             jest.advanceTimersByTime(batchFrequency);
201             expect(metricsApi.fetch).not.toHaveBeenCalled();
202         });
204         it('batches calls in order they were reported', () => {
205             const batchFrequency = 100;
206             const batchSize = 2;
207             const requestService = new MetricsRequestService(metricsApi, {
208                 reportMetrics: true,
209                 batch: {
210                     frequency: batchFrequency,
211                     size: batchSize,
212                 },
213             });
215             requestService.report(getMetricsRequest('Request 1'));
216             requestService.report(getMetricsRequest('Request 2'));
217             requestService.report(getMetricsRequest('Request 3'));
218             requestService.report(getMetricsRequest('Request 4'));
219             expect(metricsApi.fetch).not.toHaveBeenCalled();
221             jest.advanceTimersByTime(batchFrequency);
222             expect(metricsApi.fetch).toHaveBeenCalledWith('api/data/v1/metrics', {
223                 method: 'post',
224                 body: JSON.stringify({ Metrics: [getMetricsRequest('Request 1'), getMetricsRequest('Request 2')] }),
225             });
227             jest.advanceTimersByTime(batchFrequency);
228             expect(metricsApi.fetch).toHaveBeenCalledWith('api/data/v1/metrics', {
229                 method: 'post',
230                 body: JSON.stringify({ Metrics: [getMetricsRequest('Request 3'), getMetricsRequest('Request 4')] }),
231             });
232         });
234         it('handles partial batches', () => {
235             const batchFrequency = 100;
236             const batchSize = 2;
237             const requestService = new MetricsRequestService(metricsApi, {
238                 reportMetrics: true,
239                 batch: {
240                     frequency: batchFrequency,
241                     size: batchSize,
242                 },
243             });
245             requestService.report(getMetricsRequest('Request 1'));
246             requestService.report(getMetricsRequest('Request 2'));
247             requestService.report(getMetricsRequest('Request 3'));
248             expect(metricsApi.fetch).not.toHaveBeenCalled();
250             jest.advanceTimersByTime(batchFrequency);
251             expect(metricsApi.fetch).toHaveBeenCalledWith('api/data/v1/metrics', {
252                 method: 'post',
253                 body: JSON.stringify({ Metrics: [getMetricsRequest('Request 1'), getMetricsRequest('Request 2')] }),
254             });
256             jest.advanceTimersByTime(batchFrequency);
257             expect(metricsApi.fetch).toHaveBeenCalledWith('api/data/v1/metrics', {
258                 method: 'post',
259                 body: JSON.stringify({ Metrics: [getMetricsRequest('Request 3')] }),
260             });
261         });
262     });
264     describe('startBatchingProcess', () => {
265         it('starts batching interval on first report call if batch params are defined', () => {
266             const requestService = new MetricsRequestService(metricsApi, {
267                 reportMetrics: true,
268                 batch: {
269                     frequency: 1,
270                     size: 1,
271                 },
272             });
274             requestService.report(defaultMetricsRequest);
275             expect(metricsApi.fetch).not.toHaveBeenCalled();
277             jest.advanceTimersByTime(1);
278             expect(metricsApi.fetch).toHaveBeenCalledTimes(1);
279         });
281         it('does not set batching interval if no batch params are set', () => {
282             const requestService = new MetricsRequestService(metricsApi, {
283                 reportMetrics: true,
284             });
286             requestService.report(defaultMetricsRequest);
287             expect(metricsApi.fetch).toHaveBeenCalledTimes(1);
289             /**
290              * Advance timers to when call should have been
291              * performed if batching was enabled and ensure
292              * no extra calls have been made
293              */
294             jest.advanceTimersByTime(1);
295             expect(metricsApi.fetch).toHaveBeenCalledTimes(1);
296         });
297     });
299     describe('stopBatchingProcess', () => {
300         it('stops any further batch requests from occurring until startBatchingProcess is called', () => {
301             const requestService = new MetricsRequestService(metricsApi, {
302                 reportMetrics: true,
303                 batch: {
304                     frequency: 1,
305                     size: 1,
306                 },
307             });
309             requestService.report(defaultMetricsRequest);
310             requestService.report(defaultMetricsRequest);
311             expect(metricsApi.fetch).not.toHaveBeenCalled();
313             jest.advanceTimersByTime(1);
314             expect(metricsApi.fetch).toHaveBeenCalledTimes(1);
316             requestService.stopBatchingProcess();
318             jest.advanceTimersByTime(4);
319             expect(metricsApi.fetch).toHaveBeenCalledTimes(1);
321             requestService.startBatchingProcess();
323             jest.advanceTimersByTime(1);
324             expect(metricsApi.fetch).toHaveBeenCalledTimes(2);
325         });
326     });
328     describe('processAllRequests', () => {
329         it('does not make a request if there is no queue', async () => {
330             const batchFrequency = 10;
331             const requestService = new MetricsRequestService(metricsApi, {
332                 reportMetrics: true,
333                 batch: {
334                     frequency: batchFrequency,
335                     size: 1,
336                 },
337             });
339             await requestService.processAllRequests();
340             expect(metricsApi.fetch).not.toHaveBeenCalled();
341         });
343         it('processes all requests in queue regardless of batch size', async () => {
344             const batchFrequency = 10;
345             const requestService = new MetricsRequestService(metricsApi, {
346                 reportMetrics: true,
347                 batch: {
348                     frequency: batchFrequency,
349                     size: 1,
350                 },
351             });
353             requestService.report(getMetricsRequest('Request 1'));
354             requestService.report(getMetricsRequest('Request 2'));
355             requestService.report(getMetricsRequest('Request 3'));
356             expect(metricsApi.fetch).not.toHaveBeenCalled();
358             jest.advanceTimersByTime(batchFrequency);
359             expect(metricsApi.fetch).toHaveBeenCalledWith('api/data/v1/metrics', {
360                 method: 'post',
361                 body: JSON.stringify({ Metrics: [getMetricsRequest('Request 1')] }),
362             });
364             jest.advanceTimersByTime(1);
365             await requestService.processAllRequests();
367             expect(metricsApi.fetch).toHaveBeenCalledTimes(2);
368             expect(metricsApi.fetch).toHaveBeenCalledWith('api/data/v1/metrics', {
369                 method: 'post',
370                 body: JSON.stringify({ Metrics: [getMetricsRequest('Request 2'), getMetricsRequest('Request 3')] }),
371             });
372         });
374         it('clears queue', async () => {
375             const batchFrequency = 10;
376             const requestService = new MetricsRequestService(metricsApi, {
377                 reportMetrics: true,
378                 batch: {
379                     frequency: batchFrequency,
380                     size: 1,
381                 },
382             });
384             requestService.report(getMetricsRequest('Request 1'));
385             requestService.report(getMetricsRequest('Request 2'));
386             requestService.report(getMetricsRequest('Request 3'));
387             expect(metricsApi.fetch).not.toHaveBeenCalled();
389             await requestService.processAllRequests();
390             expect(metricsApi.fetch).toHaveBeenCalledTimes(1);
392             jest.advanceTimersByTime(batchFrequency);
393             expect(metricsApi.fetch).toHaveBeenCalledTimes(1);
394         });
395     });
397     describe('clearQueue', () => {
398         it('removes all items from queue', () => {
399             const batchFrequency = 10;
400             const requestService = new MetricsRequestService(metricsApi, {
401                 reportMetrics: true,
402                 batch: {
403                     frequency: batchFrequency,
404                     size: 1,
405                 },
406             });
408             requestService.report(getMetricsRequest('Request 1'));
409             requestService.report(getMetricsRequest('Request 2'));
410             requestService.report(getMetricsRequest('Request 3'));
411             expect(metricsApi.fetch).not.toHaveBeenCalled();
413             jest.advanceTimersByTime(batchFrequency);
414             expect(metricsApi.fetch).toHaveBeenCalledTimes(1);
416             requestService.clearQueue();
418             jest.advanceTimersByTime(batchFrequency);
419             expect(metricsApi.fetch).toHaveBeenCalledTimes(1);
420         });
421     });