@openobserve/browser-core 0.2.12-beta.26 → 0.2.12-beta.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/cjs/boot/init.js +1 -1
  2. package/cjs/domain/configuration/endpointBuilder.js +1 -9
  3. package/cjs/domain/configuration/endpointBuilder.js.map +1 -1
  4. package/cjs/domain/tags.js +1 -1
  5. package/cjs/domain/telemetry/telemetry.js +1 -1
  6. package/cjs/transport/httpRequest.js +0 -4
  7. package/cjs/transport/httpRequest.js.map +1 -1
  8. package/esm/boot/init.js +1 -1
  9. package/esm/domain/configuration/endpointBuilder.js +1 -9
  10. package/esm/domain/configuration/endpointBuilder.js.map +1 -1
  11. package/esm/domain/tags.js +1 -1
  12. package/esm/domain/telemetry/telemetry.js +1 -1
  13. package/esm/transport/httpRequest.js +0 -4
  14. package/esm/transport/httpRequest.js.map +1 -1
  15. package/package.json +2 -2
  16. package/src/boot/displayAlreadyInitializedError.spec.ts +18 -0
  17. package/src/boot/init.spec.ts +50 -0
  18. package/src/browser/addEventListener.spec.ts +141 -0
  19. package/src/browser/cookie.spec.ts +53 -0
  20. package/src/browser/fetchObservable.spec.ts +334 -0
  21. package/src/browser/pageMayExitObservable.spec.ts +56 -0
  22. package/src/browser/xhrObservable.spec.ts +405 -0
  23. package/src/domain/allowedTrackingOrigins.spec.ts +210 -0
  24. package/src/domain/bufferedData.spec.ts +34 -0
  25. package/src/domain/configuration/configuration.spec.ts +236 -0
  26. package/src/domain/configuration/endpointBuilder.spec.ts +173 -0
  27. package/src/domain/configuration/endpointBuilder.ts +1 -9
  28. package/src/domain/configuration/transportConfiguration.spec.ts +130 -0
  29. package/src/domain/connectivity/connectivity.spec.ts +50 -0
  30. package/src/domain/console/consoleObservable.spec.ts +151 -0
  31. package/src/domain/context/contextManager.spec.ts +152 -0
  32. package/src/domain/context/contextUtils.spec.ts +26 -0
  33. package/src/domain/context/storeContextManager.spec.ts +103 -0
  34. package/src/domain/contexts/accountContext.spec.ts +99 -0
  35. package/src/domain/contexts/globalContext.spec.ts +88 -0
  36. package/src/domain/contexts/userContext.spec.ts +156 -0
  37. package/src/domain/error/error.spec.ts +294 -0
  38. package/src/domain/error/trackRuntimeError.spec.ts +343 -0
  39. package/src/domain/eventRateLimiter/createEventRateLimiter.spec.ts +119 -0
  40. package/src/domain/extension/extensionUtils.spec.ts +47 -0
  41. package/src/domain/report/reportObservable.spec.ts +71 -0
  42. package/src/domain/session/oldCookiesMigration.spec.ts +76 -0
  43. package/src/domain/session/sessionManager.spec.ts +684 -0
  44. package/src/domain/session/sessionState.spec.ts +88 -0
  45. package/src/domain/session/sessionStore.spec.ts +647 -0
  46. package/src/domain/session/sessionStoreOperations.spec.ts +234 -0
  47. package/src/domain/session/storeStrategies/sessionInCookie.spec.ts +228 -0
  48. package/src/domain/session/storeStrategies/sessionInLocalStorage.spec.ts +75 -0
  49. package/src/domain/synthetics/syntheticsWorkerValues.spec.ts +82 -0
  50. package/src/domain/tags.spec.ts +74 -0
  51. package/src/domain/telemetry/telemetry.spec.ts +494 -0
  52. package/src/domain/trackingConsent.spec.ts +44 -0
  53. package/src/tools/abstractHooks.spec.ts +76 -0
  54. package/src/tools/abstractLifeCycle.spec.ts +46 -0
  55. package/src/tools/boundedBuffer.spec.ts +40 -0
  56. package/src/tools/catchUserErrors.spec.ts +20 -0
  57. package/src/tools/encoder.spec.ts +112 -0
  58. package/src/tools/experimentalFeatures.spec.ts +60 -0
  59. package/src/tools/getZoneJsOriginalValue.spec.ts +36 -0
  60. package/src/tools/instrumentMethod.spec.ts +381 -0
  61. package/src/tools/matchOption.spec.ts +41 -0
  62. package/src/tools/mergeInto.spec.ts +198 -0
  63. package/src/tools/monitor.spec.ts +172 -0
  64. package/src/tools/observable.spec.ts +259 -0
  65. package/src/tools/queueMicrotask.spec.ts +24 -0
  66. package/src/tools/readBytesFromStream.spec.ts +62 -0
  67. package/src/tools/requestIdleCallback.spec.ts +65 -0
  68. package/src/tools/serialisation/jsonStringify.spec.ts +76 -0
  69. package/src/tools/serialisation/sanitize.spec.ts +284 -0
  70. package/src/tools/stackTrace/capturedExceptions.specHelper.ts +316 -0
  71. package/src/tools/stackTrace/computeStackTrace.spec.ts +1007 -0
  72. package/src/tools/stackTrace/handlingStack.spec.ts +20 -0
  73. package/src/tools/taskQueue.spec.ts +60 -0
  74. package/src/tools/timer.spec.ts +76 -0
  75. package/src/tools/utils/browserDetection.spec.ts +120 -0
  76. package/src/tools/utils/byteUtils.spec.ts +29 -0
  77. package/src/tools/utils/functionUtils.spec.ts +229 -0
  78. package/src/tools/utils/numberUtils.spec.ts +27 -0
  79. package/src/tools/utils/stringUtils.spec.ts +74 -0
  80. package/src/tools/utils/typeUtils.spec.ts +25 -0
  81. package/src/tools/utils/urlPolyfill.spec.ts +55 -0
  82. package/src/tools/valueHistory.spec.ts +180 -0
  83. package/src/transport/batch.spec.ts +261 -0
  84. package/src/transport/eventBridge.spec.ts +90 -0
  85. package/src/transport/flushController.spec.ts +267 -0
  86. package/src/transport/httpRequest.spec.ts +400 -0
  87. package/src/transport/httpRequest.ts +3 -7
  88. package/src/transport/sendWithRetryStrategy.spec.ts +393 -0
@@ -0,0 +1,393 @@
1
+ import { mockClock, setNavigatorOnLine } from '../../test'
2
+ import type { Clock } from '../../test'
3
+ import { ErrorSource } from '../domain/error/error.types'
4
+ import { Observable } from '../tools/observable'
5
+ import { ONE_MEBI_BYTE } from '../tools/utils/byteUtils'
6
+ import type { RetryState } from './sendWithRetryStrategy'
7
+ import {
8
+ newRetryState,
9
+ sendWithRetryStrategy,
10
+ MAX_ONGOING_BYTES_COUNT,
11
+ MAX_ONGOING_REQUESTS,
12
+ MAX_QUEUE_BYTES_COUNT,
13
+ INITIAL_BACKOFF_TIME,
14
+ } from './sendWithRetryStrategy'
15
+ import type { Payload, HttpResponse, HttpRequestEvent } from './httpRequest'
16
+
17
+ describe('sendWithRetryStrategy', () => {
18
+ const ENDPOINT_TYPE = 'logs'
19
+ let sendMock: ReturnType<typeof mockSend>
20
+ let state: RetryState<Payload>
21
+ let sendRequest: (payload?: Partial<Payload>) => Payload
22
+ let clock: Clock
23
+ let reportErrorSpy: jasmine.Spy<jasmine.Func>
24
+ const observedEvents: HttpRequestEvent[] = []
25
+
26
+ function mockSend() {
27
+ const requests: Array<(r: HttpResponse) => void> = []
28
+ return {
29
+ sendStrategy: (_: Payload, onResponse: (r: HttpResponse) => void) => {
30
+ requests.push(onResponse)
31
+ },
32
+ respondWith: (index: number, r: HttpResponse) => {
33
+ requests[index](r)
34
+ requests[index] = () => {
35
+ throw new Error('response already handled')
36
+ }
37
+ },
38
+ }
39
+ }
40
+
41
+ function latestEvents() {
42
+ const events = [...observedEvents]
43
+ observedEvents.length = 0
44
+ return events
45
+ }
46
+
47
+ beforeEach(() => {
48
+ sendMock = mockSend()
49
+ state = newRetryState()
50
+ clock = mockClock()
51
+ reportErrorSpy = jasmine.createSpy('reportError')
52
+ const observable = new Observable<HttpRequestEvent>()
53
+ observable.subscribe((event) => observedEvents.push(event))
54
+ sendRequest = (payload) => {
55
+ const effectivePayload = {
56
+ data: payload?.data ?? 'a',
57
+ bytesCount: payload?.bytesCount ?? 1,
58
+ }
59
+ sendWithRetryStrategy(effectivePayload, state, sendMock.sendStrategy, ENDPOINT_TYPE, reportErrorSpy, observable)
60
+ return effectivePayload
61
+ }
62
+ })
63
+
64
+ afterEach(() => {
65
+ observedEvents.length = 0
66
+ })
67
+
68
+ describe('nominal cases:', () => {
69
+ it('should send request when no bandwidth limit reached', () => {
70
+ const payload = sendRequest()
71
+ expect(state.bandwidthMonitor.ongoingRequestCount).toBe(1)
72
+ expect(state.queuedPayloads.size()).toBe(0)
73
+ expect(latestEvents()).toEqual([])
74
+
75
+ sendMock.respondWith(0, { status: 200 })
76
+ expect(state.bandwidthMonitor.ongoingRequestCount).toBe(0)
77
+ expect(state.queuedPayloads.size()).toBe(0)
78
+ expect(latestEvents()).toEqual([
79
+ { type: 'success', bandwidth: { ongoingByteCount: 0, ongoingRequestCount: 0 }, payload },
80
+ ])
81
+ })
82
+
83
+ it('should allow to send request payload greater than bandwidth limit', () => {
84
+ sendRequest({ bytesCount: MAX_ONGOING_BYTES_COUNT + 10 })
85
+ expect(state.bandwidthMonitor.ongoingRequestCount).toBe(1)
86
+ expect(state.queuedPayloads.size()).toBe(0)
87
+ expect(latestEvents()).toEqual([])
88
+ })
89
+
90
+ it('should send concurrent requests ', () => {
91
+ const payload0 = sendRequest()
92
+ const payload1 = sendRequest()
93
+ const payload2 = sendRequest()
94
+ expect(state.bandwidthMonitor.ongoingRequestCount).toBe(3)
95
+ expect(state.queuedPayloads.size()).toBe(0)
96
+ expect(latestEvents()).toEqual([])
97
+
98
+ sendMock.respondWith(0, { status: 200 })
99
+ expect(state.bandwidthMonitor.ongoingRequestCount).toBe(2)
100
+ expect(latestEvents()).toEqual([
101
+ { type: 'success', bandwidth: { ongoingByteCount: 2, ongoingRequestCount: 2 }, payload: payload0 },
102
+ ])
103
+
104
+ sendMock.respondWith(1, { status: 200 })
105
+ expect(state.bandwidthMonitor.ongoingRequestCount).toBe(1)
106
+ expect(latestEvents()).toEqual([
107
+ { type: 'success', bandwidth: { ongoingByteCount: 1, ongoingRequestCount: 1 }, payload: payload1 },
108
+ ])
109
+
110
+ sendMock.respondWith(2, { status: 200 })
111
+ expect(state.bandwidthMonitor.ongoingRequestCount).toBe(0)
112
+ expect(latestEvents()).toEqual([
113
+ { type: 'success', bandwidth: { ongoingByteCount: 0, ongoingRequestCount: 0 }, payload: payload2 },
114
+ ])
115
+ })
116
+ })
117
+
118
+ describe('bandwidth limitation:', () => {
119
+ it('should queue request when its payload would overflow bytes limit', () => {
120
+ sendRequest({ bytesCount: MAX_ONGOING_BYTES_COUNT - 10 })
121
+ expect(state.bandwidthMonitor.ongoingRequestCount).toBe(1)
122
+ expect(state.queuedPayloads.size()).toBe(0)
123
+ expect(latestEvents()).toEqual([])
124
+
125
+ sendRequest({ bytesCount: 11 })
126
+ expect(state.bandwidthMonitor.ongoingRequestCount).toBe(1)
127
+ expect(state.queuedPayloads.size()).toBe(1)
128
+ expect(latestEvents()).toEqual([])
129
+ })
130
+
131
+ it('should queue request when too much ongoing requests', () => {
132
+ for (let i = 1; i <= MAX_ONGOING_REQUESTS; i++) {
133
+ sendRequest()
134
+ }
135
+ expect(state.bandwidthMonitor.ongoingRequestCount).toBe(MAX_ONGOING_REQUESTS)
136
+ expect(state.queuedPayloads.size()).toBe(0)
137
+ expect(latestEvents()).toEqual([])
138
+
139
+ sendRequest()
140
+ expect(state.queuedPayloads.size()).toBe(1)
141
+ expect(latestEvents()).toEqual([])
142
+ })
143
+ })
144
+
145
+ describe('queue limitation:', () => {
146
+ it('should stop queueing new payloads when queue bytes limit is reached', () => {
147
+ sendRequest({ bytesCount: MAX_ONGOING_BYTES_COUNT })
148
+ sendRequest({ bytesCount: MAX_QUEUE_BYTES_COUNT - 20 })
149
+ sendRequest({ bytesCount: 30 })
150
+ expect(state.queuedPayloads.size()).toBe(2)
151
+ expect(state.queuedPayloads.bytesCount).toBe(MAX_QUEUE_BYTES_COUNT + 10)
152
+ expect(latestEvents()).toEqual([])
153
+
154
+ const payload = sendRequest({ bytesCount: 1 })
155
+ expect(state.queuedPayloads.size()).toBe(2)
156
+ expect(state.queuedPayloads.bytesCount).toBe(MAX_QUEUE_BYTES_COUNT + 10)
157
+ expect(latestEvents()).toEqual([
158
+ { type: 'queue-full', bandwidth: { ongoingByteCount: 81920, ongoingRequestCount: 1 }, payload },
159
+ ])
160
+ })
161
+
162
+ it('should report a single error when queue is full after request success', () => {
163
+ const payload0 = sendRequest({ bytesCount: MAX_ONGOING_BYTES_COUNT })
164
+ const payload1 = sendRequest({ bytesCount: MAX_QUEUE_BYTES_COUNT })
165
+ expect(state.queuedPayloads.isFull()).toBe(true)
166
+ expect(latestEvents()).toEqual([])
167
+
168
+ sendMock.respondWith(0, { status: 200 })
169
+ expect(reportErrorSpy).toHaveBeenCalled()
170
+ expect(reportErrorSpy.calls.argsFor(0)[0]).toEqual(
171
+ jasmine.objectContaining({
172
+ message: `Reached max logs events size queued for upload: ${MAX_QUEUE_BYTES_COUNT / ONE_MEBI_BYTE}MiB`,
173
+ source: ErrorSource.AGENT,
174
+ })
175
+ )
176
+ reportErrorSpy.calls.reset()
177
+ expect(latestEvents()).toEqual([
178
+ { type: 'success', bandwidth: { ongoingByteCount: 0, ongoingRequestCount: 0 }, payload: payload0 },
179
+ ])
180
+
181
+ sendRequest({ bytesCount: MAX_QUEUE_BYTES_COUNT })
182
+ expect(state.queuedPayloads.isFull()).toBe(true)
183
+ expect(latestEvents()).toEqual([])
184
+
185
+ sendMock.respondWith(1, { status: 200 })
186
+ expect(reportErrorSpy).not.toHaveBeenCalled()
187
+ expect(latestEvents()).toEqual([
188
+ { type: 'success', bandwidth: { ongoingByteCount: 0, ongoingRequestCount: 0 }, payload: payload1 },
189
+ ])
190
+ })
191
+
192
+ it('should not report error when queue is full after resuming transport', () => {
193
+ const payload = sendRequest()
194
+ sendMock.respondWith(0, { status: 500 })
195
+ expect(latestEvents()).toEqual([
196
+ { type: 'failure', bandwidth: { ongoingByteCount: 0, ongoingRequestCount: 0 }, payload },
197
+ ])
198
+
199
+ sendRequest({ bytesCount: MAX_QUEUE_BYTES_COUNT })
200
+ expect(state.queuedPayloads.isFull()).toBe(true)
201
+ expect(latestEvents()).toEqual([])
202
+
203
+ clock.tick(INITIAL_BACKOFF_TIME)
204
+ sendMock.respondWith(1, { status: 200 })
205
+ expect(latestEvents()).toEqual([
206
+ {
207
+ type: 'success',
208
+ bandwidth: { ongoingByteCount: 0, ongoingRequestCount: 0 },
209
+ payload: { ...payload, retry: { count: 1, lastFailureStatus: 500 } },
210
+ },
211
+ ])
212
+
213
+ expect(reportErrorSpy).not.toHaveBeenCalled()
214
+ })
215
+ })
216
+
217
+ describe('dequeue:', () => {
218
+ it('should send as much queued request as possible after a successful request', () => {
219
+ const payload = sendRequest({ bytesCount: MAX_ONGOING_BYTES_COUNT })
220
+ for (let i = 1; i <= MAX_ONGOING_REQUESTS; i++) {
221
+ sendRequest()
222
+ }
223
+ expect(state.bandwidthMonitor.ongoingRequestCount).toBe(1)
224
+ expect(state.queuedPayloads.size()).toBe(MAX_ONGOING_REQUESTS)
225
+ expect(latestEvents()).toEqual([])
226
+
227
+ sendMock.respondWith(0, { status: 200 })
228
+ expect(state.bandwidthMonitor.ongoingRequestCount).toBe(MAX_ONGOING_REQUESTS)
229
+ expect(state.queuedPayloads.size()).toBe(0)
230
+ expect(latestEvents()).toEqual([
231
+ { type: 'success', bandwidth: { ongoingByteCount: 0, ongoingRequestCount: 0 }, payload },
232
+ ])
233
+ })
234
+
235
+ it('should respect request order', () => {
236
+ const payload0 = sendRequest({ bytesCount: MAX_ONGOING_BYTES_COUNT - 10 })
237
+ const payload1 = sendRequest({ bytesCount: 20 })
238
+ sendRequest({ bytesCount: MAX_ONGOING_BYTES_COUNT - 15 })
239
+ sendRequest({ bytesCount: 10 })
240
+ expect(state.queuedPayloads.size()).toBe(3)
241
+ expect(state.queuedPayloads.bytesCount).toBe(20 + (MAX_ONGOING_BYTES_COUNT - 15) + 10)
242
+ expect(latestEvents()).toEqual([])
243
+
244
+ sendMock.respondWith(0, { status: 200 })
245
+ expect(state.queuedPayloads.size()).toBe(2)
246
+ expect(state.queuedPayloads.bytesCount).toBe(MAX_ONGOING_BYTES_COUNT - 15 + 10)
247
+ expect(latestEvents()).toEqual([
248
+ { type: 'success', bandwidth: { ongoingByteCount: 0, ongoingRequestCount: 0 }, payload: payload0 },
249
+ ])
250
+
251
+ sendMock.respondWith(1, { status: 200 })
252
+ expect(state.queuedPayloads.size()).toBe(0)
253
+ expect(latestEvents()).toEqual([
254
+ { type: 'success', bandwidth: { ongoingByteCount: 0, ongoingRequestCount: 0 }, payload: payload1 },
255
+ ])
256
+ })
257
+ })
258
+ ;[
259
+ { expectRetry: true, description: 'when the intake returns error:', status: 500 },
260
+ { expectRetry: true, description: 'when the intake returns too many request:', status: 429 },
261
+ { expectRetry: true, description: 'when the intake returns request timeout:', status: 408 },
262
+ { expectRetry: true, description: 'when network error while offline:', status: 0, offLine: true },
263
+ { expectRetry: false, description: 'when network error while online:', status: 0 },
264
+ { expectRetry: false, description: 'when the intake returns opaque response:', status: 0, type: 'opaque' as const },
265
+ ].forEach(({ expectRetry, description, status, type, offLine }) => {
266
+ describe(description, () => {
267
+ beforeEach(() => {
268
+ setNavigatorOnLine(!offLine)
269
+ })
270
+
271
+ if (expectRetry) {
272
+ it('should start queueing following requests', () => {
273
+ const payload = sendRequest()
274
+ sendMock.respondWith(0, { status, type })
275
+ expect(state.queuedPayloads.size()).toBe(1)
276
+ expect(latestEvents()).toEqual([
277
+ { type: 'failure', bandwidth: { ongoingByteCount: 0, ongoingRequestCount: 0 }, payload },
278
+ ])
279
+
280
+ sendRequest()
281
+ expect(state.queuedPayloads.size()).toBe(2)
282
+ sendRequest()
283
+ expect(state.queuedPayloads.size()).toBe(3)
284
+ expect(latestEvents()).toEqual([])
285
+ })
286
+
287
+ it('should send queued requests if another ongoing request succeed', () => {
288
+ const payload0 = sendRequest()
289
+ const payload1 = sendRequest()
290
+ sendMock.respondWith(0, { status, type })
291
+ expect(state.bandwidthMonitor.ongoingRequestCount).toBe(1)
292
+ expect(state.queuedPayloads.size()).toBe(1)
293
+ expect(latestEvents()).toEqual([
294
+ { type: 'failure', bandwidth: { ongoingByteCount: 1, ongoingRequestCount: 1 }, payload: payload0 },
295
+ ])
296
+
297
+ sendRequest()
298
+ expect(state.bandwidthMonitor.ongoingRequestCount).toBe(1)
299
+ expect(state.queuedPayloads.size()).toBe(2)
300
+ expect(latestEvents()).toEqual([])
301
+
302
+ sendMock.respondWith(1, { status: 200 })
303
+ expect(state.bandwidthMonitor.ongoingRequestCount).toBe(2)
304
+ expect(state.queuedPayloads.size()).toBe(0)
305
+ expect(latestEvents()).toEqual([
306
+ { type: 'success', bandwidth: { ongoingByteCount: 0, ongoingRequestCount: 0 }, payload: payload1 },
307
+ ])
308
+ })
309
+
310
+ it('should add retry info to payloads', () => {
311
+ const payload = sendRequest()
312
+
313
+ sendMock.respondWith(0, { status, type })
314
+ expect(state.queuedPayloads.first().retry).toEqual({ count: 1, lastFailureStatus: status })
315
+ expect(latestEvents()).toEqual([
316
+ { type: 'failure', bandwidth: { ongoingByteCount: 0, ongoingRequestCount: 0 }, payload },
317
+ ])
318
+
319
+ clock.tick(INITIAL_BACKOFF_TIME)
320
+
321
+ sendMock.respondWith(1, { status, type })
322
+ expect(state.queuedPayloads.first().retry).toEqual({ count: 2, lastFailureStatus: status })
323
+ expect(latestEvents()).toEqual([
324
+ { type: 'failure', bandwidth: { ongoingByteCount: 0, ongoingRequestCount: 0 }, payload },
325
+ ])
326
+ })
327
+ } else {
328
+ it('should not queue the payload for retry', () => {
329
+ const payload = sendRequest()
330
+ sendMock.respondWith(0, { status, type })
331
+ expect(state.queuedPayloads.size()).toBe(0)
332
+ expect(latestEvents()).toEqual([
333
+ { type: 'success', bandwidth: { ongoingByteCount: 0, ongoingRequestCount: 0 }, payload },
334
+ ])
335
+ })
336
+ }
337
+ })
338
+ })
339
+
340
+ describe('when transport down:', () => {
341
+ it('should regularly try to send first queued request', () => {
342
+ const payload = sendRequest()
343
+ sendMock.respondWith(0, { status: 500 })
344
+ expect(state.bandwidthMonitor.ongoingRequestCount).toBe(0)
345
+ expect(latestEvents()).toEqual([
346
+ { type: 'failure', bandwidth: { ongoingByteCount: 0, ongoingRequestCount: 0 }, payload },
347
+ ])
348
+
349
+ clock.tick(INITIAL_BACKOFF_TIME)
350
+ expect(state.bandwidthMonitor.ongoingRequestCount).toBe(1)
351
+ sendMock.respondWith(1, { status: 500 })
352
+ expect(latestEvents()).toEqual([
353
+ { type: 'failure', bandwidth: { ongoingByteCount: 0, ongoingRequestCount: 0 }, payload },
354
+ ])
355
+
356
+ clock.tick(2 * INITIAL_BACKOFF_TIME)
357
+ expect(state.bandwidthMonitor.ongoingRequestCount).toBe(1)
358
+ sendMock.respondWith(2, { status: 500 })
359
+ expect(latestEvents()).toEqual([
360
+ { type: 'failure', bandwidth: { ongoingByteCount: 0, ongoingRequestCount: 0 }, payload },
361
+ ])
362
+
363
+ clock.tick(4 * INITIAL_BACKOFF_TIME)
364
+ expect(state.bandwidthMonitor.ongoingRequestCount).toBe(1)
365
+ expect(latestEvents()).toEqual([])
366
+ })
367
+
368
+ it('should send queued requests after first successful request', () => {
369
+ const payload = sendRequest()
370
+ sendMock.respondWith(0, { status: 500 })
371
+ expect(latestEvents()).toEqual([
372
+ { type: 'failure', bandwidth: { ongoingByteCount: 0, ongoingRequestCount: 0 }, payload },
373
+ ])
374
+
375
+ sendRequest()
376
+ sendRequest()
377
+ sendRequest()
378
+ expect(state.bandwidthMonitor.ongoingRequestCount).toBe(0)
379
+ expect(state.queuedPayloads.size()).toBe(4)
380
+ expect(latestEvents()).toEqual([])
381
+
382
+ clock.tick(INITIAL_BACKOFF_TIME)
383
+ expect(state.bandwidthMonitor.ongoingRequestCount).toBe(1)
384
+ sendMock.respondWith(1, { status: 200 })
385
+ expect(latestEvents()).toEqual([
386
+ { type: 'success', bandwidth: { ongoingByteCount: 0, ongoingRequestCount: 0 }, payload },
387
+ ])
388
+
389
+ expect(state.bandwidthMonitor.ongoingRequestCount).toBe(3)
390
+ expect(state.queuedPayloads.size()).toBe(0)
391
+ })
392
+ })
393
+ })