Merge branch 'renovate/all-minor-patch' into 'main'
[ProtonMail-WebClient.git] / packages / metrics / tests / MetricsRequestService.test.ts
blobe295cc7f822a80e72b0929f6daba44691fae3a6e
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         describe('jails', () => {
166             it('defaults jailMax to 3', async () => {
167                 metricsApi.fetch = jest.fn().mockImplementation(() => Promise.reject());
168                 const batchFrequency = 100;
169                 const batchSize = 1;
170                 const requestService = new MetricsRequestService(metricsApi, {
171                     reportMetrics: true,
172                     batch: {
173                         frequency: batchFrequency,
174                         size: batchSize,
175                     },
176                 });
178                 requestService.report(defaultMetricsRequest);
179                 requestService.report(defaultMetricsRequest);
180                 requestService.report(defaultMetricsRequest);
181                 requestService.report(defaultMetricsRequest);
183                 await jest.advanceTimersToNextTimerAsync();
184                 await jest.advanceTimersToNextTimerAsync();
185                 await jest.advanceTimersToNextTimerAsync();
186                 await jest.advanceTimersToNextTimerAsync();
188                 expect(metricsApi.fetch).toHaveBeenCalledTimes(3);
189             });
191             it('sets jailMax to value passed in constructor', async () => {
192                 metricsApi.fetch = jest.fn().mockImplementation(() => Promise.reject());
193                 const batchFrequency = 1;
194                 const batchSize = 2;
195                 const requestService = new MetricsRequestService(metricsApi, {
196                     reportMetrics: true,
197                     batch: {
198                         frequency: batchFrequency,
199                         size: batchSize,
200                     },
201                     jailMax: 1,
202                 });
204                 requestService.report(defaultMetricsRequest);
205                 requestService.report(defaultMetricsRequest);
207                 requestService.report(defaultMetricsRequest);
208                 requestService.report(defaultMetricsRequest);
210                 await jest.advanceTimersToNextTimerAsync();
211                 await jest.advanceTimersToNextTimerAsync();
213                 expect(metricsApi.fetch).toHaveBeenCalledTimes(1);
214             });
216             it('resets jails if successful call is made and queue is clear', async () => {
217                 metricsApi.fetch = jest
218                     .fn()
219                     .mockRejectedValueOnce(undefined)
220                     // Resolve second request
221                     .mockResolvedValueOnce(undefined)
222                     //
223                     .mockRejectedValueOnce(undefined)
224                     .mockRejectedValueOnce(undefined)
225                     .mockRejectedValueOnce(undefined);
227                 const batchFrequency = 1;
228                 const batchSize = 2;
229                 const requestService = new MetricsRequestService(metricsApi, {
230                     reportMetrics: true,
231                     batch: {
232                         frequency: batchFrequency,
233                         size: batchSize,
234                     },
235                     jailMax: 2,
236                 });
238                 requestService.report(defaultMetricsRequest);
239                 requestService.report(defaultMetricsRequest);
240                 // Will clear queue no items left in queue so jails will reset
241                 await jest.advanceTimersToNextTimerAsync();
242                 expect(metricsApi.fetch).toHaveBeenCalledTimes(1);
244                 requestService.report(defaultMetricsRequest);
245                 requestService.report(defaultMetricsRequest);
246                 await jest.advanceTimersToNextTimerAsync(batchFrequency);
247                 expect(metricsApi.fetch).toHaveBeenCalledTimes(2);
249                 requestService.report(defaultMetricsRequest);
250                 requestService.report(defaultMetricsRequest);
251                 await jest.advanceTimersToNextTimerAsync(batchFrequency);
252                 expect(metricsApi.fetch).toHaveBeenCalledTimes(3);
254                 requestService.report(defaultMetricsRequest);
255                 requestService.report(defaultMetricsRequest);
256                 await jest.advanceTimersToNextTimerAsync(batchFrequency);
257                 expect(metricsApi.fetch).toHaveBeenCalledTimes(4);
259                 requestService.report(defaultMetricsRequest);
260                 requestService.report(defaultMetricsRequest);
261                 await jest.advanceTimersToNextTimerAsync(batchFrequency);
262                 expect(metricsApi.fetch).toHaveBeenCalledTimes(4);
263             });
265             it('does not reset jails if there are items still in the queue', async () => {
266                 metricsApi.fetch = jest
267                     .fn()
268                     .mockRejectedValueOnce(undefined)
269                     // Resolve second request
270                     .mockResolvedValueOnce(undefined)
271                     //
272                     .mockRejectedValueOnce(undefined)
273                     .mockRejectedValueOnce(undefined);
275                 const batchFrequency = 1;
276                 const batchSize = 2;
277                 const requestService = new MetricsRequestService(metricsApi, {
278                     reportMetrics: true,
279                     batch: {
280                         frequency: batchFrequency,
281                         size: batchSize,
282                     },
283                     jailMax: 2,
284                 });
286                 requestService.report(defaultMetricsRequest);
287                 requestService.report(defaultMetricsRequest);
288                 requestService.report(defaultMetricsRequest);
289                 requestService.report(defaultMetricsRequest);
290                 requestService.report(defaultMetricsRequest);
292                 await jest.advanceTimersToNextTimerAsync();
293                 expect(metricsApi.fetch).toHaveBeenCalledTimes(1);
295                 await jest.advanceTimersToNextTimerAsync(batchFrequency);
296                 expect(metricsApi.fetch).toHaveBeenCalledTimes(2);
298                 await jest.advanceTimersToNextTimerAsync(batchFrequency);
299                 expect(metricsApi.fetch).toHaveBeenCalledTimes(3);
301                 await jest.advanceTimersToNextTimerAsync(batchFrequency);
302                 expect(metricsApi.fetch).toHaveBeenCalledTimes(3);
303             });
304         });
306         it('delays api calls until frequency timeout has expired', () => {
307             const batchFrequency = 100;
308             const batchSize = 5;
309             const requestService = new MetricsRequestService(metricsApi, {
310                 reportMetrics: true,
311                 batch: {
312                     frequency: batchFrequency,
313                     size: batchSize,
314                 },
315             });
317             requestService.report(defaultMetricsRequest);
318             expect(metricsApi.fetch).not.toHaveBeenCalled();
320             // fast-forward time until 1 millisecond before it should be executed
321             jest.advanceTimersByTime(batchFrequency - 1);
322             expect(metricsApi.fetch).not.toHaveBeenCalled();
324             // fast-forward until 1st call should be executed
325             jest.advanceTimersByTime(1);
326             expect(metricsApi.fetch).toHaveBeenCalledTimes(1);
327         });
329         it('does not call api if there are no items to process', () => {
330             const batchFrequency = 100;
331             const batchSize = 5;
332             const requestService = new MetricsRequestService(metricsApi, {
333                 reportMetrics: true,
334                 batch: {
335                     frequency: batchFrequency,
336                     size: batchSize,
337                 },
338             });
339             requestService.startBatchingProcess();
341             jest.advanceTimersByTime(batchFrequency);
342             expect(metricsApi.fetch).not.toHaveBeenCalled();
343         });
345         it('pauses batching if there are no items to process', async () => {
346             const batchFrequency = 100;
347             const batchSize = 1;
348             const requestService = new MetricsRequestService(metricsApi, {
349                 reportMetrics: true,
350                 batch: {
351                     frequency: batchFrequency,
352                     size: batchSize,
353                 },
354             });
355             requestService.stopBatchingProcess = jest.fn();
357             requestService.report(defaultMetricsRequest);
358             requestService.report(defaultMetricsRequest);
360             await jest.advanceTimersToNextTimerAsync();
361             expect(metricsApi.fetch).toHaveBeenCalledTimes(1);
362             expect(requestService.stopBatchingProcess).toHaveBeenCalledTimes(0);
364             await jest.advanceTimersToNextTimerAsync();
365             expect(metricsApi.fetch).toHaveBeenCalledTimes(2);
367             // Queue is empty, so we should stop batching
368             await jest.advanceTimersToNextTimerAsync();
369             expect(requestService.stopBatchingProcess).toHaveBeenCalledTimes(1);
370         });
372         it('batches calls in order they were reported', () => {
373             const batchFrequency = 100;
374             const batchSize = 2;
375             const requestService = new MetricsRequestService(metricsApi, {
376                 reportMetrics: true,
377                 batch: {
378                     frequency: batchFrequency,
379                     size: batchSize,
380                 },
381             });
383             requestService.report(getMetricsRequest('Request 1'));
384             requestService.report(getMetricsRequest('Request 2'));
385             requestService.report(getMetricsRequest('Request 3'));
386             requestService.report(getMetricsRequest('Request 4'));
387             expect(metricsApi.fetch).not.toHaveBeenCalled();
389             jest.advanceTimersByTime(batchFrequency);
390             expect(metricsApi.fetch).toHaveBeenCalledWith('/api/data/v1/metrics', {
391                 method: 'post',
392                 body: JSON.stringify({ Metrics: [getMetricsRequest('Request 1'), getMetricsRequest('Request 2')] }),
393             });
395             jest.advanceTimersByTime(batchFrequency);
396             expect(metricsApi.fetch).toHaveBeenCalledWith('/api/data/v1/metrics', {
397                 method: 'post',
398                 body: JSON.stringify({ Metrics: [getMetricsRequest('Request 3'), getMetricsRequest('Request 4')] }),
399             });
400         });
402         it('handles partial batches', () => {
403             const batchFrequency = 100;
404             const batchSize = 2;
405             const requestService = new MetricsRequestService(metricsApi, {
406                 reportMetrics: true,
407                 batch: {
408                     frequency: batchFrequency,
409                     size: batchSize,
410                 },
411             });
413             requestService.report(getMetricsRequest('Request 1'));
414             requestService.report(getMetricsRequest('Request 2'));
415             requestService.report(getMetricsRequest('Request 3'));
416             expect(metricsApi.fetch).not.toHaveBeenCalled();
418             jest.advanceTimersByTime(batchFrequency);
419             expect(metricsApi.fetch).toHaveBeenCalledWith('/api/data/v1/metrics', {
420                 method: 'post',
421                 body: JSON.stringify({ Metrics: [getMetricsRequest('Request 1'), getMetricsRequest('Request 2')] }),
422             });
424             jest.advanceTimersByTime(batchFrequency);
425             expect(metricsApi.fetch).toHaveBeenCalledWith('/api/data/v1/metrics', {
426                 method: 'post',
427                 body: JSON.stringify({ Metrics: [getMetricsRequest('Request 3')] }),
428             });
429         });
431         describe('progressive backoff', () => {
432             it('progressively backs off by a factor of the jail count if requests fail', async () => {
433                 metricsApi.fetch = jest.fn().mockImplementation(() => Promise.reject());
434                 const batchFrequency = 1;
435                 const batchSize = 2;
436                 const requestService = new MetricsRequestService(metricsApi, {
437                     reportMetrics: true,
438                     batch: {
439                         frequency: batchFrequency,
440                         size: batchSize,
441                     },
442                     jailMax: 4,
443                 });
445                 requestService.report(getMetricsRequest('Request 1'));
446                 requestService.report(getMetricsRequest('Request 2'));
447                 requestService.report(getMetricsRequest('Request 3'));
448                 requestService.report(getMetricsRequest('Request 4'));
449                 requestService.report(getMetricsRequest('Request 5'));
450                 requestService.report(getMetricsRequest('Request 6'));
452                 expect(metricsApi.fetch).not.toHaveBeenCalled();
454                 await jest.advanceTimersByTimeAsync(batchFrequency);
455                 expect(metricsApi.fetch).toHaveBeenCalledTimes(1);
456                 expect(metricsApi.fetch).toHaveBeenCalledWith('/api/data/v1/metrics', {
457                     method: 'post',
458                     body: JSON.stringify({ Metrics: [getMetricsRequest('Request 1'), getMetricsRequest('Request 2')] }),
459                 });
461                 // Requests will have failed, so should now take 2 times the batch frequency
462                 await jest.advanceTimersByTimeAsync(batchFrequency);
463                 expect(metricsApi.fetch).toHaveBeenCalledTimes(1);
465                 await jest.advanceTimersByTimeAsync(batchFrequency);
466                 expect(metricsApi.fetch).toHaveBeenCalledTimes(2);
467                 expect(metricsApi.fetch).toHaveBeenCalledWith('/api/data/v1/metrics', {
468                     method: 'post',
469                     body: JSON.stringify({ Metrics: [getMetricsRequest('Request 3'), getMetricsRequest('Request 4')] }),
470                 });
472                 // Failed again, so should now take 3 times the batch frequency
473                 await jest.advanceTimersByTimeAsync(batchFrequency);
474                 expect(metricsApi.fetch).toHaveBeenCalledTimes(2);
475                 await jest.advanceTimersByTimeAsync(batchFrequency);
476                 expect(metricsApi.fetch).toHaveBeenCalledTimes(2);
477                 await jest.advanceTimersByTimeAsync(batchFrequency);
479                 expect(metricsApi.fetch).toHaveBeenCalledTimes(3);
480                 expect(metricsApi.fetch).toHaveBeenCalledWith('/api/data/v1/metrics', {
481                     method: 'post',
482                     body: JSON.stringify({ Metrics: [getMetricsRequest('Request 3'), getMetricsRequest('Request 4')] }),
483                 });
484             });
485         });
486     });
488     describe('startBatchingProcess', () => {
489         it('starts batching interval on first report call if batch params are defined', () => {
490             const requestService = new MetricsRequestService(metricsApi, {
491                 reportMetrics: true,
492                 batch: {
493                     frequency: 1,
494                     size: 1,
495                 },
496             });
498             requestService.report(defaultMetricsRequest);
499             expect(metricsApi.fetch).not.toHaveBeenCalled();
501             jest.advanceTimersByTime(1);
502             expect(metricsApi.fetch).toHaveBeenCalledTimes(1);
503         });
505         it('does not set batching interval if no batch params are set', () => {
506             const requestService = new MetricsRequestService(metricsApi, {
507                 reportMetrics: true,
508             });
510             requestService.report(defaultMetricsRequest);
511             expect(metricsApi.fetch).toHaveBeenCalledTimes(1);
513             /**
514              * Advance timers to when call should have been
515              * performed if batching was enabled and ensure
516              * no extra calls have been made
517              */
518             jest.advanceTimersByTime(1);
519             expect(metricsApi.fetch).toHaveBeenCalledTimes(1);
520         });
521     });
523     describe('stopBatchingProcess', () => {
524         it('stops any further batch requests from occurring until startBatchingProcess is called', () => {
525             const requestService = new MetricsRequestService(metricsApi, {
526                 reportMetrics: true,
527                 batch: {
528                     frequency: 1,
529                     size: 1,
530                 },
531             });
533             requestService.report(defaultMetricsRequest);
534             requestService.report(defaultMetricsRequest);
535             expect(metricsApi.fetch).not.toHaveBeenCalled();
537             jest.advanceTimersByTime(1);
538             expect(metricsApi.fetch).toHaveBeenCalledTimes(1);
540             requestService.stopBatchingProcess();
542             jest.advanceTimersByTime(4);
543             expect(metricsApi.fetch).toHaveBeenCalledTimes(1);
545             requestService.startBatchingProcess();
547             jest.advanceTimersByTime(1);
548             expect(metricsApi.fetch).toHaveBeenCalledTimes(2);
549         });
550     });
552     describe('processAllRequests', () => {
553         it('does not make a request if there is no queue', async () => {
554             const batchFrequency = 10;
555             const requestService = new MetricsRequestService(metricsApi, {
556                 reportMetrics: true,
557                 batch: {
558                     frequency: batchFrequency,
559                     size: 1,
560                 },
561             });
563             await requestService.processAllRequests();
564             expect(metricsApi.fetch).not.toHaveBeenCalled();
565         });
567         it('processes all requests in queue regardless of batch size', async () => {
568             const batchFrequency = 10;
569             const requestService = new MetricsRequestService(metricsApi, {
570                 reportMetrics: true,
571                 batch: {
572                     frequency: batchFrequency,
573                     size: 1,
574                 },
575             });
577             requestService.report(getMetricsRequest('Request 1'));
578             requestService.report(getMetricsRequest('Request 2'));
579             requestService.report(getMetricsRequest('Request 3'));
580             expect(metricsApi.fetch).not.toHaveBeenCalled();
582             jest.advanceTimersByTime(batchFrequency);
583             expect(metricsApi.fetch).toHaveBeenCalledWith('/api/data/v1/metrics', {
584                 method: 'post',
585                 body: JSON.stringify({ Metrics: [getMetricsRequest('Request 1')] }),
586             });
588             jest.advanceTimersByTime(1);
589             await requestService.processAllRequests();
591             expect(metricsApi.fetch).toHaveBeenCalledTimes(2);
592             expect(metricsApi.fetch).toHaveBeenCalledWith('/api/data/v1/metrics', {
593                 method: 'post',
594                 body: JSON.stringify({ Metrics: [getMetricsRequest('Request 2'), getMetricsRequest('Request 3')] }),
595             });
596         });
598         it('clears queue', async () => {
599             const batchFrequency = 10;
600             const requestService = new MetricsRequestService(metricsApi, {
601                 reportMetrics: true,
602                 batch: {
603                     frequency: batchFrequency,
604                     size: 1,
605                 },
606             });
608             requestService.report(getMetricsRequest('Request 1'));
609             requestService.report(getMetricsRequest('Request 2'));
610             requestService.report(getMetricsRequest('Request 3'));
611             expect(metricsApi.fetch).not.toHaveBeenCalled();
613             await requestService.processAllRequests();
614             expect(metricsApi.fetch).toHaveBeenCalledTimes(1);
616             jest.advanceTimersByTime(batchFrequency);
617             expect(metricsApi.fetch).toHaveBeenCalledTimes(1);
618         });
620         describe('jails', () => {
621             it('defaults jailMax to 3', async () => {
622                 metricsApi.fetch = jest.fn().mockImplementation(() => Promise.reject());
623                 const batchFrequency = 100;
624                 const batchSize = 5;
625                 const requestService = new MetricsRequestService(metricsApi, {
626                     reportMetrics: true,
627                     batch: {
628                         frequency: batchFrequency,
629                         size: batchSize,
630                     },
631                 });
633                 requestService.report(defaultMetricsRequest);
634                 await requestService.processAllRequests();
635                 expect(metricsApi.fetch).toHaveBeenCalledTimes(1);
637                 requestService.report(defaultMetricsRequest);
638                 await requestService.processAllRequests();
639                 expect(metricsApi.fetch).toHaveBeenCalledTimes(2);
641                 requestService.report(defaultMetricsRequest);
642                 await requestService.processAllRequests();
643                 expect(metricsApi.fetch).toHaveBeenCalledTimes(3);
645                 requestService.report(defaultMetricsRequest);
646                 await requestService.processAllRequests();
647                 expect(metricsApi.fetch).toHaveBeenCalledTimes(3);
648             });
650             it('sets jailMax to value passed in constructor', async () => {
651                 metricsApi.fetch = jest.fn().mockImplementation(() => Promise.reject());
652                 const batchFrequency = 1;
653                 const batchSize = 5;
654                 const requestService = new MetricsRequestService(metricsApi, {
655                     reportMetrics: true,
656                     batch: {
657                         frequency: batchFrequency,
658                         size: batchSize,
659                     },
660                     jailMax: 1,
661                 });
663                 requestService.report(defaultMetricsRequest);
664                 await requestService.processAllRequests();
665                 expect(metricsApi.fetch).toHaveBeenCalledTimes(1);
667                 requestService.report(defaultMetricsRequest);
668                 await requestService.processAllRequests();
669                 expect(metricsApi.fetch).toHaveBeenCalledTimes(1);
670             });
671         });
672     });
674     describe('clearQueue', () => {
675         it('removes all items from queue', () => {
676             const batchFrequency = 10;
677             const requestService = new MetricsRequestService(metricsApi, {
678                 reportMetrics: true,
679                 batch: {
680                     frequency: batchFrequency,
681                     size: 1,
682                 },
683             });
685             requestService.report(getMetricsRequest('Request 1'));
686             requestService.report(getMetricsRequest('Request 2'));
687             requestService.report(getMetricsRequest('Request 3'));
688             expect(metricsApi.fetch).not.toHaveBeenCalled();
690             jest.advanceTimersByTime(batchFrequency);
691             expect(metricsApi.fetch).toHaveBeenCalledTimes(1);
693             requestService.clearQueue();
695             jest.advanceTimersByTime(batchFrequency);
696             expect(metricsApi.fetch).toHaveBeenCalledTimes(1);
697         });
698     });
700     describe('jailMax', () => {
701         it('defaults jailMax to 3', async () => {
702             metricsApi.fetch = jest.fn().mockImplementation(() => Promise.reject());
704             const requestService = new MetricsRequestService(metricsApi, {
705                 reportMetrics: true,
706             });
708             requestService.report(getMetricsRequest('Request 1'));
709             await jest.advanceTimersByTimeAsync(1);
710             requestService.report(getMetricsRequest('Request 2'));
711             await jest.advanceTimersByTimeAsync(1);
712             requestService.report(getMetricsRequest('Request 3'));
713             await jest.advanceTimersByTimeAsync(1);
714             requestService.report(getMetricsRequest('Request 4'));
716             expect(metricsApi.fetch).toHaveBeenCalledTimes(3);
717         });
719         it('sets jailMax to value passed in constructor', async () => {
720             metricsApi.fetch = jest.fn().mockImplementation(() => Promise.reject());
722             const requestService = new MetricsRequestService(metricsApi, {
723                 reportMetrics: true,
724                 jailMax: 1,
725             });
727             requestService.report(getMetricsRequest('Request 1'));
728             await jest.advanceTimersByTimeAsync(1);
729             requestService.report(getMetricsRequest('Request 2'));
730             await jest.advanceTimersByTimeAsync(1);
732             expect(metricsApi.fetch).toHaveBeenCalledTimes(1);
733         });
735         it('uses default if jailMax is 0', async () => {
736             metricsApi.fetch = jest.fn().mockImplementation(() => Promise.reject());
737             const requestService = new MetricsRequestService(metricsApi, {
738                 reportMetrics: true,
739                 jailMax: 0,
740             });
742             requestService.report(getMetricsRequest('Request 1'));
743             await jest.advanceTimersByTimeAsync(1);
744             requestService.report(getMetricsRequest('Request 2'));
745             await jest.advanceTimersByTimeAsync(1);
746             requestService.report(getMetricsRequest('Request 3'));
747             await jest.advanceTimersByTimeAsync(1);
748             requestService.report(getMetricsRequest('Request 4'));
750             expect(metricsApi.fetch).toHaveBeenCalledTimes(3);
751         });
753         it('uses default if jailMax is < 0', async () => {
754             metricsApi.fetch = jest.fn().mockImplementation(() => Promise.reject());
755             const requestService = new MetricsRequestService(metricsApi, {
756                 reportMetrics: true,
757                 jailMax: -1,
758             });
760             requestService.report(getMetricsRequest('Request 1'));
761             await jest.advanceTimersByTimeAsync(1);
762             requestService.report(getMetricsRequest('Request 2'));
763             await jest.advanceTimersByTimeAsync(1);
764             requestService.report(getMetricsRequest('Request 3'));
765             await jest.advanceTimersByTimeAsync(1);
766             requestService.report(getMetricsRequest('Request 4'));
768             expect(metricsApi.fetch).toHaveBeenCalledTimes(3);
769         });
771         it('resets jails if successful call is made', async () => {
772             metricsApi.fetch = jest
773                 .fn()
774                 .mockRejectedValueOnce(undefined)
775                 // Resolve second request
776                 .mockResolvedValueOnce(undefined)
777                 //
778                 .mockRejectedValueOnce(undefined)
779                 .mockRejectedValueOnce(undefined)
780                 .mockRejectedValueOnce(undefined);
781             const requestService = new MetricsRequestService(metricsApi, {
782                 reportMetrics: true,
783                 jailMax: 2,
784             });
786             requestService.report(getMetricsRequest('Request 1'));
787             await jest.advanceTimersByTimeAsync(1);
788             requestService.report(getMetricsRequest('Request 2'));
789             await jest.advanceTimersByTimeAsync(1);
790             requestService.report(getMetricsRequest('Request 3'));
791             await jest.advanceTimersByTimeAsync(1);
792             requestService.report(getMetricsRequest('Request 4'));
793             await jest.advanceTimersByTimeAsync(1);
794             requestService.report(getMetricsRequest('Request 5'));
795             await jest.advanceTimersByTimeAsync(1);
797             expect(metricsApi.fetch).toHaveBeenCalledTimes(4);
798         });
799     });