launchdarkly-js-sdk-common 4.0.0 → 4.0.1

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 (67) hide show
  1. package/.circleci/config.yml +22 -0
  2. package/.eslintignore +4 -0
  3. package/.eslintrc.yaml +104 -0
  4. package/.github/ISSUE_TEMPLATE/bug_report.md +37 -0
  5. package/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  6. package/.github/pull_request_template.md +21 -0
  7. package/.ldrelease/config.yml +24 -0
  8. package/.prettierignore +1 -0
  9. package/.prettierrc +5 -0
  10. package/CHANGELOG.md +4 -0
  11. package/CONTRIBUTING.md +45 -0
  12. package/babel.config.js +18 -0
  13. package/docs/typedoc.js +11 -0
  14. package/jest.config.js +15 -0
  15. package/package.json +3 -29
  16. package/scripts/better-audit.sh +76 -0
  17. package/src/EventEmitter.js +60 -0
  18. package/src/EventProcessor.js +175 -0
  19. package/src/EventSender.js +87 -0
  20. package/src/EventSummarizer.js +84 -0
  21. package/src/Identity.js +26 -0
  22. package/src/InitializationState.js +83 -0
  23. package/src/PersistentFlagStore.js +50 -0
  24. package/src/PersistentStorage.js +81 -0
  25. package/src/Requestor.js +111 -0
  26. package/src/Stream.js +154 -0
  27. package/src/UserFilter.js +75 -0
  28. package/src/UserValidator.js +56 -0
  29. package/src/__tests__/.eslintrc.yaml +7 -0
  30. package/src/__tests__/EventProcessor-test.js +559 -0
  31. package/src/__tests__/EventSender-test.js +252 -0
  32. package/src/__tests__/EventSource-mock.js +61 -0
  33. package/src/__tests__/EventSummarizer-test.js +103 -0
  34. package/src/__tests__/LDClient-events-test.js +757 -0
  35. package/src/__tests__/LDClient-localstorage-test.js +179 -0
  36. package/src/__tests__/LDClient-streaming-test.js +683 -0
  37. package/src/__tests__/LDClient-test.js +761 -0
  38. package/src/__tests__/PersistentFlagStore-test.js +111 -0
  39. package/src/__tests__/Requestor-test.js +362 -0
  40. package/src/__tests__/Stream-test.js +299 -0
  41. package/src/__tests__/UserFilter-test.js +93 -0
  42. package/src/__tests__/UserValidator-test.js +57 -0
  43. package/src/__tests__/configuration-test.js +217 -0
  44. package/src/__tests__/diagnosticEvents-test.js +449 -0
  45. package/src/__tests__/loggers-test.js +149 -0
  46. package/src/__tests__/mockHttp.js +122 -0
  47. package/src/__tests__/promiseCoalescer-test.js +128 -0
  48. package/src/__tests__/stubPlatform.js +148 -0
  49. package/src/__tests__/testUtils.js +77 -0
  50. package/src/__tests__/utils-test.js +148 -0
  51. package/src/configuration.js +151 -0
  52. package/src/diagnosticEvents.js +269 -0
  53. package/src/errors.js +37 -0
  54. package/src/index.js +772 -0
  55. package/src/jest.setup.js +1 -0
  56. package/src/loggers.js +93 -0
  57. package/src/messages.js +217 -0
  58. package/src/promiseCoalescer.js +52 -0
  59. package/src/utils.js +214 -0
  60. package/test-types.ts +96 -0
  61. package/tsconfig.json +13 -0
  62. package/dist/ldclient-common.cjs.js +0 -2
  63. package/dist/ldclient-common.cjs.js.map +0 -1
  64. package/dist/ldclient-common.es.js +0 -2
  65. package/dist/ldclient-common.es.js.map +0 -1
  66. package/dist/ldclient-common.min.js +0 -2
  67. package/dist/ldclient-common.min.js.map +0 -1
@@ -0,0 +1,559 @@
1
+ import EventProcessor from '../EventProcessor';
2
+ import { DiagnosticsAccumulator } from '../diagnosticEvents';
3
+ import * as messages from '../messages';
4
+
5
+ import * as stubPlatform from './stubPlatform';
6
+ import { MockEventSender } from './testUtils';
7
+
8
+ // These tests verify that the event processor produces the expected event payload data for
9
+ // various inputs. The actual delivery of data is done by EventSender, which has its own
10
+ // tests; here, we use a mock EventSender.
11
+
12
+ describe('EventProcessor', () => {
13
+ const user = { key: 'userKey', name: 'Red' };
14
+ const filteredUser = { key: 'userKey', privateAttrs: ['name'] };
15
+ const eventsUrl = '/fake-url';
16
+ const envId = 'env';
17
+ const logger = stubPlatform.logger();
18
+ const defaultConfig = {
19
+ eventsUrl: eventsUrl,
20
+ eventCapacity: 100,
21
+ flushInterval: 2000,
22
+ samplingInterval: 0,
23
+ logger: logger,
24
+ };
25
+ const platform = stubPlatform.defaults();
26
+
27
+ async function withProcessorAndSender(config, asyncCallback) {
28
+ const sender = MockEventSender();
29
+ const ep = EventProcessor(platform, config, envId, null, null, sender);
30
+ try {
31
+ return await asyncCallback(ep, sender);
32
+ } finally {
33
+ ep.stop();
34
+ }
35
+ }
36
+
37
+ async function withDiagnosticProcessorAndSender(config, asyncCallback) {
38
+ const sender = MockEventSender();
39
+ const diagnosticAccumulator = DiagnosticsAccumulator(1000);
40
+ const ep = EventProcessor(platform, config, envId, diagnosticAccumulator, null, sender);
41
+ try {
42
+ return await asyncCallback(ep, sender, diagnosticAccumulator);
43
+ } finally {
44
+ ep.stop();
45
+ }
46
+ }
47
+
48
+ function checkUserInline(e, source, inlineUser) {
49
+ if (inlineUser) {
50
+ expect(e.user).toEqual(inlineUser);
51
+ expect(e.userKey).toBeUndefined();
52
+ } else {
53
+ expect(e.userKey).toEqual(source.user.key);
54
+ expect(e.user).toBeUndefined();
55
+ }
56
+ }
57
+
58
+ function checkFeatureEvent(e, source, debug, inlineUser) {
59
+ expect(e.kind).toEqual(debug ? 'debug' : 'feature');
60
+ expect(e.creationDate).toEqual(source.creationDate);
61
+ expect(e.key).toEqual(source.key);
62
+ expect(e.version).toEqual(source.version);
63
+ expect(e.variation).toEqual(source.variation);
64
+ expect(e.value).toEqual(source.value);
65
+ expect(e.default).toEqual(source.default);
66
+ expect(e.reason).toEqual(source.reason);
67
+ checkUserInline(e, source, inlineUser);
68
+ }
69
+
70
+ function checkCustomEvent(e, source, inlineUser) {
71
+ expect(e.kind).toEqual('custom');
72
+ expect(e.creationDate).toEqual(source.creationDate);
73
+ expect(e.key).toEqual(source.key);
74
+ expect(e.data).toEqual(source.data);
75
+ expect(e.metricValue).toEqual(source.metricValue);
76
+ checkUserInline(e, source, inlineUser);
77
+ }
78
+
79
+ function checkSummaryEvent(e) {
80
+ expect(e.kind).toEqual('summary');
81
+ }
82
+
83
+ it('should enqueue identify event', async () => {
84
+ await withProcessorAndSender(defaultConfig, async (ep, mockEventSender) => {
85
+ const event = { kind: 'identify', creationDate: 1000, key: user.key, user: user };
86
+ ep.enqueue(event);
87
+ await ep.flush();
88
+
89
+ expect(mockEventSender.calls.length()).toEqual(1);
90
+ expect((await mockEventSender.calls.take()).events).toEqual([event]);
91
+ });
92
+ });
93
+
94
+ it('filters user in identify event', async () => {
95
+ const config = { ...defaultConfig, allAttributesPrivate: true };
96
+ await withProcessorAndSender(config, async (ep, mockEventSender) => {
97
+ const event = { kind: 'identify', creationDate: 1000, key: user.key, user: user };
98
+ ep.enqueue(event);
99
+ await ep.flush();
100
+
101
+ expect(mockEventSender.calls.length()).toEqual(1);
102
+ expect((await mockEventSender.calls.take()).events).toEqual([
103
+ {
104
+ kind: 'identify',
105
+ creationDate: event.creationDate,
106
+ key: user.key,
107
+ user: filteredUser,
108
+ },
109
+ ]);
110
+ });
111
+ });
112
+
113
+ it('queues individual feature event', async () => {
114
+ await withProcessorAndSender(defaultConfig, async (ep, mockEventSender) => {
115
+ const event = {
116
+ kind: 'feature',
117
+ creationDate: 1000,
118
+ key: 'flagkey',
119
+ user: user,
120
+ trackEvents: true,
121
+ };
122
+ ep.enqueue(event);
123
+ await ep.flush();
124
+
125
+ expect(mockEventSender.calls.length()).toEqual(1);
126
+ const output = (await mockEventSender.calls.take()).events;
127
+ expect(output.length).toEqual(2);
128
+ checkFeatureEvent(output[0], event, false);
129
+ checkSummaryEvent(output[1]);
130
+ });
131
+ });
132
+
133
+ it('can include inline user in feature event', async () => {
134
+ const config = { ...defaultConfig, inlineUsersInEvents: true };
135
+ await withProcessorAndSender(config, async (ep, mockEventSender) => {
136
+ const event = {
137
+ kind: 'feature',
138
+ creationDate: 1000,
139
+ key: 'flagkey',
140
+ user: user,
141
+ trackEvents: true,
142
+ };
143
+ ep.enqueue(event);
144
+ await ep.flush();
145
+
146
+ expect(mockEventSender.calls.length()).toEqual(1);
147
+ const output = (await mockEventSender.calls.take()).events;
148
+ expect(output.length).toEqual(2);
149
+ checkFeatureEvent(output[0], event, false, user);
150
+ checkSummaryEvent(output[1]);
151
+ });
152
+ });
153
+
154
+ it('can include reason in feature event', async () => {
155
+ const config = { ...defaultConfig, inlineUsersInEvents: true };
156
+ const reason = { kind: 'FALLTHROUGH' };
157
+ await withProcessorAndSender(config, async (ep, mockEventSender) => {
158
+ const event = {
159
+ kind: 'feature',
160
+ creationDate: 1000,
161
+ key: 'flagkey',
162
+ user: user,
163
+ trackEvents: true,
164
+ reason: reason,
165
+ };
166
+ ep.enqueue(event);
167
+ await ep.flush();
168
+
169
+ expect(mockEventSender.calls.length()).toEqual(1);
170
+ const output = (await mockEventSender.calls.take()).events;
171
+ expect(output.length).toEqual(2);
172
+ checkFeatureEvent(output[0], event, false, user);
173
+ checkSummaryEvent(output[1]);
174
+ });
175
+ });
176
+
177
+ it('filters user in feature event', async () => {
178
+ const config = { ...defaultConfig, allAttributesPrivate: true, inlineUsersInEvents: true };
179
+ await withProcessorAndSender(config, async (ep, mockEventSender) => {
180
+ const event = {
181
+ kind: 'feature',
182
+ creationDate: 1000,
183
+ key: 'flagkey',
184
+ user: user,
185
+ trackEvents: true,
186
+ };
187
+ ep.enqueue(event);
188
+ await ep.flush();
189
+
190
+ expect(mockEventSender.calls.length()).toEqual(1);
191
+ const output = (await mockEventSender.calls.take()).events;
192
+ expect(output.length).toEqual(2);
193
+ checkFeatureEvent(output[0], event, false, filteredUser);
194
+ checkSummaryEvent(output[1]);
195
+ });
196
+ });
197
+
198
+ it('sets event kind to debug if event is temporarily in debug mode', async () => {
199
+ await withProcessorAndSender(defaultConfig, async (ep, mockEventSender) => {
200
+ const futureTime = new Date().getTime() + 1000000;
201
+ const e = {
202
+ kind: 'feature',
203
+ creationDate: 1000,
204
+ user: user,
205
+ key: 'flagkey',
206
+ version: 11,
207
+ variation: 1,
208
+ value: 'value',
209
+ trackEvents: false,
210
+ debugEventsUntilDate: futureTime,
211
+ };
212
+ ep.enqueue(e);
213
+ await ep.flush();
214
+
215
+ expect(mockEventSender.calls.length()).toEqual(1);
216
+ const output = (await mockEventSender.calls.take()).events;
217
+ expect(output.length).toEqual(2);
218
+ checkFeatureEvent(output[0], e, true, user);
219
+ checkSummaryEvent(output[1]);
220
+ });
221
+ });
222
+
223
+ it('filters user in debug event', async () => {
224
+ const config = { ...defaultConfig, allAttributesPrivate: true };
225
+ await withProcessorAndSender(config, async (ep, mockEventSender) => {
226
+ const futureTime = new Date().getTime() + 1000000;
227
+ const e = {
228
+ kind: 'feature',
229
+ creationDate: 1000,
230
+ user: user,
231
+ key: 'flagkey',
232
+ version: 11,
233
+ variation: 1,
234
+ value: 'value',
235
+ default: 'default',
236
+ trackEvents: false,
237
+ debugEventsUntilDate: futureTime,
238
+ };
239
+ ep.enqueue(e);
240
+ await ep.flush();
241
+
242
+ expect(mockEventSender.calls.length()).toEqual(1);
243
+ const output = (await mockEventSender.calls.take()).events;
244
+ expect(output.length).toEqual(2);
245
+ checkFeatureEvent(output[0], e, true, filteredUser);
246
+ checkSummaryEvent(output[1]);
247
+ });
248
+ });
249
+
250
+ it('can both track and debug an event', async () => {
251
+ await withProcessorAndSender(defaultConfig, async (ep, mockEventSender) => {
252
+ const futureTime = new Date().getTime() + 1000000;
253
+ const e = {
254
+ kind: 'feature',
255
+ creationDate: 1000,
256
+ user: user,
257
+ key: 'flagkey',
258
+ version: 11,
259
+ variation: 1,
260
+ value: 'value',
261
+ trackEvents: true,
262
+ debugEventsUntilDate: futureTime,
263
+ };
264
+ ep.enqueue(e);
265
+ await ep.flush();
266
+
267
+ expect(mockEventSender.calls.length()).toEqual(1);
268
+ const output = (await mockEventSender.calls.take()).events;
269
+ expect(output.length).toEqual(3);
270
+ checkFeatureEvent(output[0], e, false);
271
+ checkFeatureEvent(output[1], e, true, user);
272
+ checkSummaryEvent(output[2]);
273
+ });
274
+ });
275
+
276
+ it('expires debug mode based on client time if client time is later than server time', async () => {
277
+ await withProcessorAndSender(defaultConfig, async (ep, mockEventSender) => {
278
+ // Pick a server time that is somewhat behind the client time
279
+ const serverTime = new Date().getTime() - 20000;
280
+ mockEventSender.setServerTime(serverTime);
281
+
282
+ // Send and flush an event we don't care about, just to set the last server time
283
+ ep.enqueue({ kind: 'identify', user: { key: 'otherUser' } });
284
+ await ep.flush();
285
+
286
+ // Now send an event with debug mode on, with a "debug until" time that is further in
287
+ // the future than the server time, but in the past compared to the client.
288
+ const debugUntil = serverTime + 1000;
289
+ const e = {
290
+ kind: 'feature',
291
+ creationDate: 1000,
292
+ user: user,
293
+ key: 'flagkey',
294
+ version: 11,
295
+ variation: 1,
296
+ value: 'value',
297
+ trackEvents: false,
298
+ debugEventsUntilDate: debugUntil,
299
+ };
300
+ ep.enqueue(e);
301
+
302
+ // Should get a summary event only, not a full feature event
303
+ await ep.flush();
304
+ expect(mockEventSender.calls.length()).toEqual(2);
305
+ await mockEventSender.calls.take();
306
+ const output = (await mockEventSender.calls.take()).events;
307
+ expect(output.length).toEqual(1);
308
+ checkSummaryEvent(output[0]);
309
+ });
310
+ });
311
+
312
+ it('expires debug mode based on server time if server time is later than client time', async () => {
313
+ await withProcessorAndSender(defaultConfig, async (ep, mockEventSender) => {
314
+ // Pick a server time that is somewhat ahead of the client time
315
+ const serverTime = new Date().getTime() + 20000;
316
+ mockEventSender.setServerTime(serverTime);
317
+
318
+ // Send and flush an event we don't care about, just to set the last server time
319
+ ep.enqueue({ kind: 'identify', user: { key: 'otherUser' } });
320
+ await ep.flush();
321
+
322
+ // Now send an event with debug mode on, with a "debug until" time that is further in
323
+ // the future than the client time, but in the past compared to the server.
324
+ const debugUntil = serverTime - 1000;
325
+ const e = {
326
+ kind: 'feature',
327
+ creationDate: 1000,
328
+ user: user,
329
+ key: 'flagkey',
330
+ version: 11,
331
+ variation: 1,
332
+ value: 'value',
333
+ trackEvents: false,
334
+ debugEventsUntilDate: debugUntil,
335
+ };
336
+ ep.enqueue(e);
337
+
338
+ // Should get a summary event only, not a full feature event
339
+ await ep.flush();
340
+ expect(mockEventSender.calls.length()).toEqual(2);
341
+ await mockEventSender.calls.take();
342
+ const output = (await mockEventSender.calls.take()).events;
343
+ expect(output.length).toEqual(1);
344
+ checkSummaryEvent(output[0]);
345
+ });
346
+ });
347
+
348
+ it('summarizes nontracked events', async () => {
349
+ await withProcessorAndSender(defaultConfig, async (ep, mockEventSender) => {
350
+ function makeEvent(key, date, version, variation, value, defaultVal) {
351
+ return {
352
+ kind: 'feature',
353
+ creationDate: date,
354
+ user: user,
355
+ key: key,
356
+ version: version,
357
+ variation: variation,
358
+ value: value,
359
+ default: defaultVal,
360
+ trackEvents: false,
361
+ };
362
+ }
363
+ const e1 = makeEvent('flagkey1', 1000, 11, 1, 'value1', 'default1');
364
+ const e2 = makeEvent('flagkey2', 2000, 22, 1, 'value2', 'default2');
365
+ ep.enqueue(e1);
366
+ ep.enqueue(e2);
367
+ await ep.flush();
368
+
369
+ expect(mockEventSender.calls.length()).toEqual(1);
370
+ const output = (await mockEventSender.calls.take()).events;
371
+ expect(output.length).toEqual(1);
372
+ const se = output[0];
373
+ checkSummaryEvent(se);
374
+ expect(se.startDate).toEqual(1000);
375
+ expect(se.endDate).toEqual(2000);
376
+ expect(se.features).toEqual({
377
+ flagkey1: {
378
+ default: 'default1',
379
+ counters: [{ version: 11, variation: 1, value: 'value1', count: 1 }],
380
+ },
381
+ flagkey2: {
382
+ default: 'default2',
383
+ counters: [{ version: 22, variation: 1, value: 'value2', count: 1 }],
384
+ },
385
+ });
386
+ });
387
+ });
388
+
389
+ it('queues custom event', async () => {
390
+ await withProcessorAndSender(defaultConfig, async (ep, mockEventSender) => {
391
+ const e = {
392
+ kind: 'custom',
393
+ creationDate: 1000,
394
+ user: user,
395
+ key: 'eventkey',
396
+ data: { thing: 'stuff' },
397
+ metricValue: 1.5,
398
+ };
399
+ ep.enqueue(e);
400
+ await ep.flush();
401
+
402
+ expect(mockEventSender.calls.length()).toEqual(1);
403
+ const output = (await mockEventSender.calls.take()).events;
404
+ expect(output.length).toEqual(1);
405
+ checkCustomEvent(output[0], e);
406
+ });
407
+ });
408
+
409
+ it('can include inline user in custom event', async () => {
410
+ const config = { ...defaultConfig, inlineUsersInEvents: true };
411
+ await withProcessorAndSender(config, async (ep, mockEventSender) => {
412
+ const e = {
413
+ kind: 'custom',
414
+ creationDate: 1000,
415
+ user: user,
416
+ key: 'eventkey',
417
+ data: { thing: 'stuff' },
418
+ };
419
+ ep.enqueue(e);
420
+ await ep.flush();
421
+
422
+ expect(mockEventSender.calls.length()).toEqual(1);
423
+ const output = (await mockEventSender.calls.take()).events;
424
+ expect(output.length).toEqual(1);
425
+ checkCustomEvent(output[0], e, user);
426
+ });
427
+ });
428
+
429
+ it('filters user in custom event', async () => {
430
+ const config = { ...defaultConfig, allAttributesPrivate: true, inlineUsersInEvents: true };
431
+ await withProcessorAndSender(config, async (ep, mockEventSender) => {
432
+ const e = {
433
+ kind: 'custom',
434
+ creationDate: 1000,
435
+ user: user,
436
+ key: 'eventkey',
437
+ data: { thing: 'stuff' },
438
+ };
439
+ ep.enqueue(e);
440
+ await ep.flush();
441
+
442
+ expect(mockEventSender.calls.length()).toEqual(1);
443
+ const output = (await mockEventSender.calls.take()).events;
444
+ expect(output.length).toEqual(1);
445
+ checkCustomEvent(output[0], e, filteredUser);
446
+ });
447
+ });
448
+
449
+ it('enforces event capacity', async () => {
450
+ const config = { ...defaultConfig, eventCapacity: 1, logger: stubPlatform.logger() };
451
+ const e0 = { kind: 'custom', creationDate: 1000, user: user, key: 'key0' };
452
+ const e1 = { kind: 'custom', creationDate: 1001, user: user, key: 'key1' };
453
+ const e2 = { kind: 'custom', creationDate: 1002, user: user, key: 'key2' };
454
+ await withProcessorAndSender(config, async (ep, mockEventSender) => {
455
+ ep.enqueue(e0);
456
+ ep.enqueue(e1);
457
+ ep.enqueue(e2);
458
+ await ep.flush();
459
+
460
+ expect(mockEventSender.calls.length()).toEqual(1);
461
+ const output = (await mockEventSender.calls.take()).events;
462
+ expect(output.length).toEqual(1);
463
+ checkCustomEvent(output[0], e0);
464
+
465
+ expect(config.logger.output.warn).toEqual([messages.eventCapacityExceeded()]); // warning is not repeated for e2
466
+ });
467
+ });
468
+
469
+ it('sends nothing if there are no events to flush', async () => {
470
+ await withProcessorAndSender(defaultConfig, async (ep, mockEventSender) => {
471
+ await ep.flush();
472
+ expect(mockEventSender.calls.length()).toEqual(0);
473
+ });
474
+ });
475
+
476
+ async function verifyUnrecoverableHttpError(status) {
477
+ await withProcessorAndSender(defaultConfig, async (ep, mockEventSender) => {
478
+ const e = { kind: 'identify', creationDate: 1000, user: user };
479
+ ep.enqueue(e);
480
+ mockEventSender.setStatus(status);
481
+ await ep.flush();
482
+
483
+ expect(mockEventSender.calls.length()).toEqual(1);
484
+ ep.enqueue(e);
485
+ await ep.flush();
486
+
487
+ expect(mockEventSender.calls.length()).toEqual(1); // still the one from our first flush
488
+ });
489
+ }
490
+
491
+ async function verifyRecoverableHttpError(status) {
492
+ await withProcessorAndSender(defaultConfig, async (ep, mockEventSender) => {
493
+ const e = { kind: 'identify', creationDate: 1000, user: user };
494
+ ep.enqueue(e);
495
+ mockEventSender.setStatus(status);
496
+ await ep.flush();
497
+
498
+ expect(mockEventSender.calls.length()).toEqual(1);
499
+ ep.enqueue(e);
500
+ await ep.flush();
501
+
502
+ expect(mockEventSender.calls.length()).toEqual(2);
503
+ });
504
+ }
505
+
506
+ describe('stops sending events after unrecoverable HTTP error', () => {
507
+ [401, 403, 404].forEach(status => {
508
+ it('status ' + status, async () => await verifyUnrecoverableHttpError(status));
509
+ });
510
+ });
511
+
512
+ describe('continues sending events after recoverable HTTP error', () => {
513
+ [408, 429, 500].forEach(status => {
514
+ it('status ' + status, async () => await verifyRecoverableHttpError(status));
515
+ });
516
+ });
517
+
518
+ describe('interaction with diagnostic events', () => {
519
+ it('sets eventsInLastBatch on flush', async () => {
520
+ const e0 = { kind: 'custom', creationDate: 1000, user: user, key: 'key0' };
521
+ const e1 = { kind: 'custom', creationDate: 1001, user: user, key: 'key1' };
522
+ await withDiagnosticProcessorAndSender(defaultConfig, async (ep, mockEventSender, diagnosticAccumulator) => {
523
+ expect(diagnosticAccumulator.getProps().eventsInLastBatch).toEqual(0);
524
+
525
+ ep.enqueue(e0);
526
+ ep.enqueue(e1);
527
+ await ep.flush();
528
+
529
+ expect(mockEventSender.calls.length()).toEqual(1);
530
+ const output = (await mockEventSender.calls.take()).events;
531
+ expect(output.length).toEqual(2);
532
+
533
+ expect(diagnosticAccumulator.getProps().eventsInLastBatch).toEqual(2);
534
+ });
535
+ });
536
+
537
+ it('increments droppedEvents when capacity is exceeded', async () => {
538
+ const config = { ...defaultConfig, eventCapacity: 1, logger: stubPlatform.logger() };
539
+ const e0 = { kind: 'custom', creationDate: 1000, user: user, key: 'key0' };
540
+ const e1 = { kind: 'custom', creationDate: 1001, user: user, key: 'key1' };
541
+ const e2 = { kind: 'custom', creationDate: 1002, user: user, key: 'key2' };
542
+ await withDiagnosticProcessorAndSender(config, async (ep, mockEventSender, diagnosticAccumulator) => {
543
+ ep.enqueue(e0);
544
+ ep.enqueue(e1);
545
+ ep.enqueue(e2);
546
+ await ep.flush();
547
+
548
+ expect(mockEventSender.calls.length()).toEqual(1);
549
+ const output = (await mockEventSender.calls.take()).events;
550
+ expect(output.length).toEqual(1);
551
+ checkCustomEvent(output[0], e0);
552
+
553
+ expect(config.logger.output.warn).toEqual([messages.eventCapacityExceeded()]); // warning is not repeated for e2
554
+
555
+ expect(diagnosticAccumulator.getProps().droppedEvents).toEqual(2);
556
+ });
557
+ });
558
+ });
559
+ });