@webex/internal-plugin-metrics 3.12.0-next.4 → 3.12.0-next.40

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 (48) hide show
  1. package/dist/automated-user.js +19 -0
  2. package/dist/automated-user.js.map +1 -0
  3. package/dist/batcher.js +3 -0
  4. package/dist/batcher.js.map +1 -1
  5. package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js +638 -65
  6. package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js.map +1 -1
  7. package/dist/call-diagnostic/call-diagnostic-metrics.js +167 -76
  8. package/dist/call-diagnostic/call-diagnostic-metrics.js.map +1 -1
  9. package/dist/call-diagnostic/call-diagnostic-metrics.util.js +5 -0
  10. package/dist/call-diagnostic/call-diagnostic-metrics.util.js.map +1 -1
  11. package/dist/call-diagnostic/config.js +16 -3
  12. package/dist/call-diagnostic/config.js.map +1 -1
  13. package/dist/config.js +1 -0
  14. package/dist/config.js.map +1 -1
  15. package/dist/index.js +15 -0
  16. package/dist/index.js.map +1 -1
  17. package/dist/metrics.js +1 -1
  18. package/dist/metrics.types.js +4 -0
  19. package/dist/metrics.types.js.map +1 -1
  20. package/dist/new-metrics.js +16 -6
  21. package/dist/new-metrics.js.map +1 -1
  22. package/dist/types/automated-user.d.ts +2 -0
  23. package/dist/types/call-diagnostic/call-diagnostic-metrics-latencies.d.ts +208 -5
  24. package/dist/types/call-diagnostic/call-diagnostic-metrics.d.ts +67 -14
  25. package/dist/types/call-diagnostic/config.d.ts +4 -0
  26. package/dist/types/config.d.ts +1 -0
  27. package/dist/types/index.d.ts +5 -3
  28. package/dist/types/metrics.types.d.ts +4 -2
  29. package/dist/types/new-metrics.d.ts +4 -0
  30. package/package.json +12 -11
  31. package/src/automated-user.ts +16 -0
  32. package/src/batcher.js +4 -0
  33. package/src/call-diagnostic/call-diagnostic-metrics-latencies.ts +800 -73
  34. package/src/call-diagnostic/call-diagnostic-metrics.ts +101 -15
  35. package/src/call-diagnostic/call-diagnostic-metrics.util.ts +5 -0
  36. package/src/call-diagnostic/config.ts +14 -0
  37. package/src/config.js +1 -0
  38. package/src/index.ts +5 -0
  39. package/src/metrics.types.ts +16 -3
  40. package/src/new-metrics.ts +12 -4
  41. package/test/unit/spec/automated-user.ts +43 -0
  42. package/test/unit/spec/batcher.js +43 -0
  43. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-batcher.ts +14 -2
  44. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts +1434 -303
  45. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts +852 -159
  46. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts +27 -0
  47. package/test/unit/spec/new-metrics.ts +39 -2
  48. package/test/unit/spec/prelogin-metrics-batcher.ts +71 -36
@@ -5,11 +5,12 @@ import sinon from 'sinon';
5
5
  describe('internal-plugin-metrics', () => {
6
6
  describe('CallDiagnosticLatencies', () => {
7
7
  let cdl: CallDiagnosticLatencies;
8
+ let clock: ReturnType<typeof sinon.useFakeTimers>;
8
9
  var now = new Date();
9
10
 
10
11
  beforeEach(() => {
11
12
  sinon.createSandbox();
12
- sinon.useFakeTimers(now.getTime());
13
+ clock = sinon.useFakeTimers(now.getTime());
13
14
  const webex = {
14
15
  meetings: {
15
16
  getBasicMeetingInformation: (id: string) => {
@@ -51,17 +52,17 @@ describe('internal-plugin-metrics', () => {
51
52
 
52
53
  it('should overwrite latency when accumulate is false', () => {
53
54
  assert.deepEqual(cdl.precomputedLatencies.size, 0);
54
- cdl.saveLatency('internal.client.pageJMT', 10, false);
55
+ cdl.saveLatency('internal.client.pageJMT', 10, {accumulate: false});
55
56
  assert.deepEqual(cdl.precomputedLatencies.size, 1);
56
57
  assert.deepEqual(cdl.precomputedLatencies.get('internal.client.pageJMT'), 10);
57
- cdl.saveLatency('internal.client.pageJMT', 20, false);
58
+ cdl.saveLatency('internal.client.pageJMT', 20, {accumulate: false});
58
59
  assert.deepEqual(cdl.precomputedLatencies.size, 1);
59
60
  assert.deepEqual(cdl.precomputedLatencies.get('internal.client.pageJMT'), 20);
60
61
  });
61
62
 
62
63
  it('should save latency correctly when accumulate is true', () => {
63
64
  assert.deepEqual(cdl.precomputedLatencies.size, 0);
64
- cdl.saveLatency('internal.client.pageJMT', 10, true);
65
+ cdl.saveLatency('internal.client.pageJMT', 10, {accumulate: true});
65
66
  assert.deepEqual(cdl.precomputedLatencies.size, 1);
66
67
  assert.deepEqual(cdl.precomputedLatencies.get('internal.client.pageJMT'), 10);
67
68
  });
@@ -71,11 +72,31 @@ describe('internal-plugin-metrics', () => {
71
72
  cdl.saveLatency('internal.client.pageJMT', 10);
72
73
  assert.deepEqual(cdl.precomputedLatencies.size, 1);
73
74
  assert.deepEqual(cdl.precomputedLatencies.get('internal.client.pageJMT'), 10);
74
- cdl.saveLatency('internal.client.pageJMT', 10, true);
75
+ cdl.saveLatency('internal.client.pageJMT', 10, {accumulate: true});
75
76
  assert.deepEqual(cdl.precomputedLatencies.size, 1);
76
77
  assert.deepEqual(cdl.precomputedLatencies.get('internal.client.pageJMT'), 20);
77
78
  });
78
79
 
80
+ it('accumulates when accumulate is passed as the legacy boolean argument', () => {
81
+ assert.deepEqual(cdl.precomputedLatencies.size, 0);
82
+ // @ts-ignore - legacy boolean third argument from older (untyped) SDK/plugin consumers
83
+ cdl.saveLatency('internal.client.pageJMT', 10, true);
84
+ // @ts-ignore - legacy boolean third argument from older (untyped) SDK/plugin consumers
85
+ cdl.saveLatency('internal.client.pageJMT', 15, true);
86
+ assert.deepEqual(cdl.precomputedLatencies.size, 1);
87
+ assert.deepEqual(cdl.precomputedLatencies.get('internal.client.pageJMT'), 25);
88
+ });
89
+
90
+ it('overwrites when the legacy boolean argument is false', () => {
91
+ assert.deepEqual(cdl.precomputedLatencies.size, 0);
92
+ // @ts-ignore - legacy boolean third argument from older (untyped) SDK/plugin consumers
93
+ cdl.saveLatency('internal.client.pageJMT', 10, false);
94
+ // @ts-ignore - legacy boolean third argument from older (untyped) SDK/plugin consumers
95
+ cdl.saveLatency('internal.client.pageJMT', 15, false);
96
+ assert.deepEqual(cdl.precomputedLatencies.size, 1);
97
+ assert.deepEqual(cdl.precomputedLatencies.get('internal.client.pageJMT'), 15);
98
+ });
99
+
79
100
  it('should save only first timestamp correctly', () => {
80
101
  assert.deepEqual(cdl.latencyTimestamps.size, 0);
81
102
  cdl.saveFirstTimestampOnly('client.alert.displayed', 10);
@@ -108,11 +129,1174 @@ describe('internal-plugin-metrics', () => {
108
129
  assert.deepEqual(cdl.latencyTimestamps.size, 2);
109
130
  cdl.saveLatency('internal.api.fetch.intelligence.models', 42);
110
131
  assert.deepEqual(cdl.precomputedLatencies.size, 1);
132
+ cdl.saveTimestamp({
133
+ key: 'internal.client.locus.sync.start',
134
+ value: 1,
135
+ options: {
136
+ meetingId: 'meeting-1',
137
+ dataSetName: 'main',
138
+ randomBackoffTime: 10,
139
+ trackingId: 'sync-tracking-id',
140
+ },
141
+ });
142
+ assert.deepEqual(cdl.meetingLatencies.size, 1);
111
143
 
112
144
  cdl.clearTimestamps();
113
145
 
114
146
  assert.deepEqual(cdl.latencyTimestamps.size, 0);
115
147
  assert.deepEqual(cdl.precomputedLatencies.size, 0);
148
+ assert.deepEqual(cdl.meetingLatencies.size, 0);
149
+ });
150
+
151
+ describe('locus sync latencies', () => {
152
+ it('uses pending sync backoff latency when sync starts', () => {
153
+ cdl.saveLatency('internal.client.locus.sync.random.backoff', 10.4, {
154
+ meetingId: 'meeting-1',
155
+ dataSetName: 'main',
156
+ });
157
+ cdl.saveTimestamp({
158
+ key: 'internal.client.locus.sync.start',
159
+ value: 100,
160
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId: 'sync-tracking-id'},
161
+ });
162
+ cdl.saveTimestamp({
163
+ key: 'internal.client.locus.sync.request',
164
+ value: 110,
165
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId: 'sync-tracking-id'},
166
+ });
167
+ cdl.saveTimestamp({
168
+ key: 'internal.client.locus.sync.response',
169
+ value: 130,
170
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId: 'sync-tracking-id'},
171
+ });
172
+ cdl.saveTimestamp({
173
+ key: 'internal.client.locus.sync.message.received',
174
+ value: 140,
175
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId: 'sync-tracking-id'},
176
+ });
177
+
178
+ assert.deepEqual(cdl.getLocusSyncLatency('meeting-1', 'sync-tracking-id'), {
179
+ randomBackoffTime: 10,
180
+ hashtreePrepTime: 0,
181
+ hashtreeResponseTime: 0,
182
+ syncPrepTime: 10,
183
+ syncResponseTime: 20,
184
+ syncMessageReceiveTime: 30,
185
+ totalTime: 40,
186
+ });
187
+ });
188
+
189
+ it('calculates sync latency values from milestones', () => {
190
+ cdl.saveLatency('internal.client.locus.sync.random.backoff', 10.4, {
191
+ meetingId: 'meeting-1',
192
+ dataSetName: 'main',
193
+ });
194
+ cdl.saveTimestamp({
195
+ key: 'internal.client.locus.sync.start',
196
+ value: 100,
197
+ options: {
198
+ meetingId: 'meeting-1',
199
+ dataSetName: 'main',
200
+ trackingId: 'sync-tracking-id',
201
+ },
202
+ });
203
+ cdl.saveTimestamp({
204
+ key: 'internal.client.locus.hashtree.request',
205
+ value: 105,
206
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId: 'sync-tracking-id'},
207
+ });
208
+ cdl.saveTimestamp({
209
+ key: 'internal.client.locus.hashtree.response',
210
+ value: 125,
211
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId: 'sync-tracking-id'},
212
+ });
213
+ cdl.saveTimestamp({
214
+ key: 'internal.client.locus.sync.request',
215
+ value: 128,
216
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId: 'sync-tracking-id'},
217
+ });
218
+ cdl.saveTimestamp({
219
+ key: 'internal.client.locus.sync.response',
220
+ value: 143,
221
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId: 'sync-tracking-id'},
222
+ });
223
+ cdl.saveTimestamp({
224
+ key: 'internal.client.locus.sync.message.received',
225
+ value: 150,
226
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId: 'sync-tracking-id'},
227
+ });
228
+
229
+ assert.deepEqual(cdl.getLocusSyncLatency('meeting-1', 'sync-tracking-id'), {
230
+ randomBackoffTime: 10,
231
+ hashtreePrepTime: 5,
232
+ hashtreeResponseTime: 20,
233
+ syncPrepTime: 3,
234
+ syncResponseTime: 15,
235
+ syncMessageReceiveTime: 22,
236
+ totalTime: 50,
237
+ });
238
+ });
239
+
240
+ it('calculates sync latency values when hash tree request is skipped', () => {
241
+ cdl.saveTimestamp({
242
+ key: 'internal.client.locus.sync.start',
243
+ value: 100,
244
+ options: {
245
+ meetingId: 'meeting-1',
246
+ dataSetName: 'main',
247
+ randomBackoffTime: 0,
248
+ trackingId: 'sync-tracking-1',
249
+ },
250
+ });
251
+ cdl.saveTimestamp({
252
+ key: 'internal.client.locus.sync.request',
253
+ value: 110,
254
+ options: {
255
+ meetingId: 'meeting-1',
256
+ dataSetName: 'main',
257
+ trackingId: 'sync-tracking-1',
258
+ },
259
+ });
260
+ cdl.saveTimestamp({
261
+ key: 'internal.client.locus.sync.response',
262
+ value: 130,
263
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId: 'sync-tracking-1'},
264
+ });
265
+ cdl.saveTimestamp({
266
+ key: 'internal.client.locus.sync.message.received',
267
+ value: 140,
268
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId: 'sync-tracking-1'},
269
+ });
270
+
271
+ assert.deepEqual(cdl.getLocusSyncLatency('meeting-1', 'sync-tracking-1'), {
272
+ randomBackoffTime: 0,
273
+ hashtreePrepTime: 0,
274
+ hashtreeResponseTime: 0,
275
+ syncPrepTime: 10,
276
+ syncResponseTime: 20,
277
+ syncMessageReceiveTime: 30,
278
+ totalTime: 40,
279
+ });
280
+ });
281
+
282
+ it('associates sync response tracking id and completes the matching meeting record', () => {
283
+ clock.setSystemTime(150);
284
+
285
+ try {
286
+ cdl.saveTimestamp({
287
+ key: 'internal.client.locus.sync.start',
288
+ value: 100,
289
+ options: {
290
+ meetingId: 'meeting-1',
291
+ dataSetName: 'main',
292
+ randomBackoffTime: 0,
293
+ trackingId: 'our-sync-tracking-id',
294
+ },
295
+ });
296
+ cdl.saveTimestamp({
297
+ key: 'internal.client.locus.sync.request',
298
+ value: 110,
299
+ options: {
300
+ meetingId: 'meeting-1',
301
+ dataSetName: 'main',
302
+ trackingId: 'our-sync-tracking-id',
303
+ },
304
+ });
305
+ cdl.saveTimestamp({
306
+ key: 'internal.client.locus.sync.response',
307
+ value: 130,
308
+ options: {
309
+ meetingId: 'meeting-1',
310
+ dataSetName: 'main',
311
+ trackingId: 'our-sync-tracking-id',
312
+ },
313
+ });
314
+ // the LLM state-update message that gates the metric arrives for this sync
315
+ cdl.saveTimestamp({
316
+ key: 'internal.client.locus.sync.message.received',
317
+ value: 150,
318
+ options: {
319
+ meetingId: 'meeting-1',
320
+ dataSetName: 'main',
321
+ trackingId: 'our-sync-tracking-id',
322
+ },
323
+ });
324
+ cdl.saveLatency('internal.client.locus.sync.random.backoff', 99, {
325
+ meetingId: 'meeting-1',
326
+ dataSetName: 'main',
327
+ });
328
+
329
+ // eager completion: both the /sync response and the LLM message are present
330
+ assert.deepEqual(cdl.completeLocusSyncLatency('meeting-1', 'our-sync-tracking-id'), {
331
+ dataSet: 'main',
332
+ syncLatency: {
333
+ randomBackoffTime: 0,
334
+ hashtreePrepTime: 0,
335
+ hashtreeResponseTime: 0,
336
+ syncPrepTime: 10,
337
+ syncResponseTime: 20,
338
+ syncMessageReceiveTime: 40,
339
+ totalTime: 50,
340
+ },
341
+ });
342
+ // completing the metric removes the matching record, leaving only the pending
343
+ // backoff record created above for the next sync to consume
344
+ assert.lengthOf(cdl.meetingLatencies.get('meeting-1'), 1);
345
+
346
+ cdl.saveTimestamp({
347
+ key: 'internal.client.locus.sync.start',
348
+ value: 200,
349
+ options: {
350
+ meetingId: 'meeting-1',
351
+ dataSetName: 'main',
352
+ trackingId: 'next-sync-tracking-id',
353
+ },
354
+ });
355
+ assert.deepInclude(cdl.meetingLatencies.get('meeting-1'), {
356
+ locusSync: {
357
+ meetingId: 'meeting-1',
358
+ dataSetName: 'main',
359
+ randomBackoffTime: 99,
360
+ trackingId: 'next-sync-tracking-id',
361
+ syncStart: 200,
362
+ },
363
+ });
364
+ } finally {
365
+ clock.setSystemTime(now.getTime());
366
+ }
367
+ });
368
+
369
+ it('does not bind message.received when tracking id does not match any record', () => {
370
+ clock.setSystemTime(500);
371
+
372
+ try {
373
+ cdl.saveTimestamp({
374
+ key: 'internal.client.locus.sync.start',
375
+ value: 100,
376
+ options: {
377
+ meetingId: 'meeting-1',
378
+ dataSetName: 'main',
379
+ randomBackoffTime: 0,
380
+ trackingId: 'sync-tracking-id',
381
+ },
382
+ });
383
+ cdl.saveTimestamp({
384
+ key: 'internal.client.locus.sync.request',
385
+ value: 110,
386
+ options: {
387
+ meetingId: 'meeting-1',
388
+ dataSetName: 'main',
389
+ trackingId: 'sync-tracking-id',
390
+ },
391
+ });
392
+ cdl.saveTimestamp({
393
+ key: 'internal.client.locus.sync.response',
394
+ value: 130,
395
+ options: {
396
+ meetingId: 'meeting-1',
397
+ dataSetName: 'main',
398
+ trackingId: 'sync-tracking-id',
399
+ },
400
+ });
401
+ cdl.saveTimestamp({
402
+ key: 'internal.client.locus.sync.message.received',
403
+ value: 140,
404
+ options: {
405
+ meetingId: 'meeting-1',
406
+ dataSetName: 'main',
407
+ trackingId: 'llm-envelope-id',
408
+ },
409
+ });
410
+
411
+ assert.isUndefined(cdl.completeLocusSyncLatency('meeting-1', 'llm-envelope-id'));
412
+ assert.isUndefined(cdl.meetingLatencies.get('meeting-1')?.[0].locusSync.messageReceived);
413
+ } finally {
414
+ clock.setSystemTime(now.getTime());
415
+ }
416
+ });
417
+
418
+ it('does not allow totalTime to drop below sync response duration when message.received is stale', () => {
419
+ cdl.saveTimestamp({
420
+ key: 'internal.client.locus.sync.start',
421
+ value: 100,
422
+ options: {
423
+ meetingId: 'meeting-1',
424
+ dataSetName: 'main',
425
+ randomBackoffTime: 0,
426
+ trackingId: 'sync-tracking-id',
427
+ },
428
+ });
429
+ cdl.saveTimestamp({
430
+ key: 'internal.client.locus.sync.request',
431
+ value: 101,
432
+ options: {
433
+ meetingId: 'meeting-1',
434
+ dataSetName: 'main',
435
+ trackingId: 'sync-tracking-id',
436
+ },
437
+ });
438
+ cdl.saveTimestamp({
439
+ key: 'internal.client.locus.sync.response',
440
+ value: 475,
441
+ options: {
442
+ meetingId: 'meeting-1',
443
+ dataSetName: 'main',
444
+ trackingId: 'sync-tracking-id',
445
+ },
446
+ });
447
+ cdl.saveTimestamp({
448
+ key: 'internal.client.locus.sync.message.received',
449
+ value: 90,
450
+ options: {
451
+ meetingId: 'meeting-1',
452
+ dataSetName: 'main',
453
+ trackingId: 'sync-tracking-id',
454
+ },
455
+ });
456
+
457
+ assert.deepEqual(cdl.completeLocusSyncLatency('meeting-1', 'sync-tracking-id'), {
458
+ dataSet: 'main',
459
+ syncLatency: {
460
+ randomBackoffTime: 0,
461
+ hashtreePrepTime: 0,
462
+ hashtreeResponseTime: 0,
463
+ syncPrepTime: 1,
464
+ syncResponseTime: 374,
465
+ syncMessageReceiveTime: 0,
466
+ totalTime: 375,
467
+ },
468
+ });
469
+ });
470
+
471
+ it('drops and consumes a completed sync latency record when a segment exceeds the clock skew threshold', () => {
472
+ cdl.saveTimestamp({
473
+ key: 'internal.client.locus.sync.start',
474
+ value: 100,
475
+ options: {
476
+ meetingId: 'meeting-1',
477
+ dataSetName: 'main',
478
+ trackingId: 'sync-tracking-id',
479
+ },
480
+ });
481
+ cdl.saveTimestamp({
482
+ key: 'internal.client.locus.sync.request',
483
+ value: 110,
484
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId: 'sync-tracking-id'},
485
+ });
486
+ cdl.saveTimestamp({
487
+ key: 'internal.client.locus.sync.response',
488
+ value: 600111,
489
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId: 'sync-tracking-id'},
490
+ });
491
+ cdl.saveTimestamp({
492
+ key: 'internal.client.locus.sync.message.received',
493
+ value: 600112,
494
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId: 'sync-tracking-id'},
495
+ });
496
+
497
+ assert.isUndefined(cdl.completeLocusSyncLatency('meeting-1', 'sync-tracking-id'));
498
+ assert.isFalse(cdl.meetingLatencies.has('meeting-1'));
499
+ });
500
+
501
+ it('allows a completed sync latency record when the largest segment equals the clock skew threshold', () => {
502
+ cdl.saveTimestamp({
503
+ key: 'internal.client.locus.sync.start',
504
+ value: 100,
505
+ options: {
506
+ meetingId: 'meeting-1',
507
+ dataSetName: 'main',
508
+ trackingId: 'sync-tracking-id',
509
+ },
510
+ });
511
+ cdl.saveTimestamp({
512
+ key: 'internal.client.locus.sync.request',
513
+ value: 110,
514
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId: 'sync-tracking-id'},
515
+ });
516
+ cdl.saveTimestamp({
517
+ key: 'internal.client.locus.sync.response',
518
+ value: 600100,
519
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId: 'sync-tracking-id'},
520
+ });
521
+ cdl.saveTimestamp({
522
+ key: 'internal.client.locus.sync.message.received',
523
+ value: 600100,
524
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId: 'sync-tracking-id'},
525
+ });
526
+
527
+ assert.deepEqual(cdl.completeLocusSyncLatency('meeting-1', 'sync-tracking-id'), {
528
+ dataSet: 'main',
529
+ syncLatency: {
530
+ randomBackoffTime: 0,
531
+ hashtreePrepTime: 0,
532
+ hashtreeResponseTime: 0,
533
+ syncPrepTime: 10,
534
+ syncResponseTime: 599990,
535
+ syncMessageReceiveTime: 599990,
536
+ totalTime: 600000,
537
+ },
538
+ });
539
+ assert.isFalse(cdl.meetingLatencies.has('meeting-1'));
540
+ });
541
+
542
+ it('matches exact record by tracking id on first pass', () => {
543
+ clock.setSystemTime(300);
544
+
545
+ try {
546
+ cdl.saveTimestamp({
547
+ key: 'internal.client.locus.sync.start',
548
+ value: 100,
549
+ options: {
550
+ meetingId: 'meeting-1',
551
+ dataSetName: 'main',
552
+ randomBackoffTime: 0,
553
+ trackingId: 'sync-tracking-1',
554
+ },
555
+ });
556
+ cdl.saveTimestamp({
557
+ key: 'internal.client.locus.sync.request',
558
+ value: 110,
559
+ options: {
560
+ meetingId: 'meeting-1',
561
+ dataSetName: 'main',
562
+ trackingId: 'sync-tracking-1',
563
+ },
564
+ });
565
+ cdl.saveTimestamp({
566
+ key: 'internal.client.locus.sync.response',
567
+ value: 130,
568
+ options: {
569
+ meetingId: 'meeting-1',
570
+ dataSetName: 'main',
571
+ trackingId: 'sync-tracking-1',
572
+ },
573
+ });
574
+
575
+ cdl.saveTimestamp({
576
+ key: 'internal.client.locus.sync.start',
577
+ value: 200,
578
+ options: {
579
+ meetingId: 'meeting-1',
580
+ dataSetName: 'main',
581
+ randomBackoffTime: 0,
582
+ trackingId: 'sync-tracking-2',
583
+ },
584
+ });
585
+ cdl.saveTimestamp({
586
+ key: 'internal.client.locus.sync.request',
587
+ value: 210,
588
+ options: {
589
+ meetingId: 'meeting-1',
590
+ dataSetName: 'main',
591
+ trackingId: 'sync-tracking-2',
592
+ },
593
+ });
594
+ cdl.saveTimestamp({
595
+ key: 'internal.client.locus.sync.response',
596
+ value: 230,
597
+ options: {
598
+ meetingId: 'meeting-1',
599
+ dataSetName: 'main',
600
+ trackingId: 'sync-tracking-2',
601
+ },
602
+ });
603
+ cdl.saveTimestamp({
604
+ key: 'internal.client.locus.sync.message.received',
605
+ value: 240,
606
+ options: {
607
+ meetingId: 'meeting-1',
608
+ dataSetName: 'main',
609
+ trackingId: 'sync-tracking-2',
610
+ },
611
+ });
612
+
613
+ assert.deepEqual(cdl.completeLocusSyncLatency('meeting-1', 'sync-tracking-2'), {
614
+ dataSet: 'main',
615
+ syncLatency: {
616
+ randomBackoffTime: 0,
617
+ hashtreePrepTime: 0,
618
+ hashtreeResponseTime: 0,
619
+ syncPrepTime: 10,
620
+ syncResponseTime: 20,
621
+ syncMessageReceiveTime: 30,
622
+ totalTime: 40,
623
+ },
624
+ });
625
+ } finally {
626
+ clock.setSystemTime(now.getTime());
627
+ }
628
+ });
629
+
630
+ it('does not bind message.received when tracking id does not match any record', () => {
631
+ cdl.saveTimestamp({
632
+ key: 'internal.client.locus.sync.start',
633
+ value: 100,
634
+ options: {
635
+ meetingId: 'meeting-1',
636
+ dataSetName: 'main',
637
+ randomBackoffTime: 0,
638
+ trackingId: 'sync-tracking-1',
639
+ },
640
+ });
641
+ cdl.saveTimestamp({
642
+ key: 'internal.client.locus.sync.request',
643
+ value: 110,
644
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId: 'sync-tracking-1'},
645
+ });
646
+ cdl.saveTimestamp({
647
+ key: 'internal.client.locus.sync.response',
648
+ value: 130,
649
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId: 'sync-tracking-1'},
650
+ });
651
+
652
+ cdl.saveTimestamp({
653
+ key: 'internal.client.locus.sync.start',
654
+ value: 200,
655
+ options: {
656
+ meetingId: 'meeting-1',
657
+ dataSetName: 'main',
658
+ randomBackoffTime: 0,
659
+ trackingId: 'sync-tracking-2',
660
+ },
661
+ });
662
+ cdl.saveTimestamp({
663
+ key: 'internal.client.locus.sync.request',
664
+ value: 210,
665
+ options: {
666
+ meetingId: 'meeting-1',
667
+ dataSetName: 'main',
668
+ trackingId: 'sync-tracking-2',
669
+ },
670
+ });
671
+ cdl.saveTimestamp({
672
+ key: 'internal.client.locus.sync.response',
673
+ value: 230,
674
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId: 'sync-tracking-2'},
675
+ });
676
+
677
+ cdl.saveTimestamp({
678
+ key: 'internal.client.locus.sync.message.received',
679
+ value: 120,
680
+ options: {
681
+ meetingId: 'meeting-1',
682
+ dataSetName: 'main',
683
+ trackingId: 'llm-unknown-id',
684
+ },
685
+ });
686
+
687
+ const records = cdl.meetingLatencies.get('meeting-1');
688
+
689
+ assert.isDefined(records);
690
+ assert.lengthOf(records!, 2);
691
+ assert.isUndefined(records![0].locusSync.messageReceived);
692
+ assert.isUndefined(records![1].locusSync.messageReceived);
693
+ });
694
+
695
+ it('binds message.received when the tracking id matches the only record', () => {
696
+ cdl.saveTimestamp({
697
+ key: 'internal.client.locus.sync.start',
698
+ value: 100,
699
+ options: {
700
+ meetingId: 'meeting-1',
701
+ dataSetName: 'main',
702
+ randomBackoffTime: 0,
703
+ trackingId: 'sync-tracking-id',
704
+ },
705
+ });
706
+ cdl.saveTimestamp({
707
+ key: 'internal.client.locus.sync.request',
708
+ value: 110,
709
+ options: {
710
+ meetingId: 'meeting-1',
711
+ dataSetName: 'main',
712
+ trackingId: 'sync-tracking-id',
713
+ },
714
+ });
715
+ cdl.saveTimestamp({
716
+ key: 'internal.client.locus.sync.response',
717
+ value: 130,
718
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId: 'sync-tracking-id'},
719
+ });
720
+
721
+ cdl.saveTimestamp({
722
+ key: 'internal.client.locus.sync.message.received',
723
+ value: 140,
724
+ options: {
725
+ meetingId: 'meeting-1',
726
+ dataSetName: 'main',
727
+ trackingId: 'sync-tracking-id',
728
+ },
729
+ });
730
+
731
+ const records = cdl.meetingLatencies.get('meeting-1');
732
+
733
+ assert.isDefined(records);
734
+ assert.lengthOf(records!, 1);
735
+ assert.equal(records![0].locusSync.messageReceived, 140);
736
+ });
737
+
738
+ it('returns undefined when the tracking id does not match', () => {
739
+ clock.setSystemTime(150);
740
+
741
+ try {
742
+ cdl.saveTimestamp({
743
+ key: 'internal.client.locus.sync.start',
744
+ value: 100,
745
+ options: {
746
+ meetingId: 'meeting-1',
747
+ dataSetName: 'main',
748
+ randomBackoffTime: 0,
749
+ trackingId: 'our-sync-tracking-id',
750
+ },
751
+ });
752
+ cdl.saveTimestamp({
753
+ key: 'internal.client.locus.sync.request',
754
+ value: 110,
755
+ options: {
756
+ meetingId: 'meeting-1',
757
+ dataSetName: 'main',
758
+ trackingId: 'our-sync-tracking-id',
759
+ },
760
+ });
761
+ cdl.saveTimestamp({
762
+ key: 'internal.client.locus.sync.response',
763
+ value: 130,
764
+ options: {
765
+ meetingId: 'meeting-1',
766
+ dataSetName: 'main',
767
+ trackingId: 'our-sync-tracking-id',
768
+ },
769
+ });
770
+
771
+ assert.isUndefined(cdl.completeLocusSyncLatency('meeting-1', 'llm-envelope-tracking-id'));
772
+ } finally {
773
+ clock.setSystemTime(now.getTime());
774
+ }
775
+ });
776
+
777
+ it('does not clean up older never-completed record for the same dataset when newer sync completes', () => {
778
+ // first sync: all sync milestones recorded, but its completing LLM event never arrives
779
+ cdl.saveTimestamp({
780
+ key: 'internal.client.locus.sync.start',
781
+ value: 100,
782
+ options: {
783
+ meetingId: 'meeting-1',
784
+ dataSetName: 'main',
785
+ randomBackoffTime: 0,
786
+ trackingId: 'sync-tracking-1',
787
+ },
788
+ });
789
+ cdl.saveTimestamp({
790
+ key: 'internal.client.locus.sync.request',
791
+ value: 110,
792
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId: 'sync-tracking-1'},
793
+ });
794
+ cdl.saveTimestamp({
795
+ key: 'internal.client.locus.sync.response',
796
+ value: 130,
797
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId: 'sync-tracking-1'},
798
+ });
799
+
800
+ // a second sync for the same dataset starts before the first one completed.
801
+ // because the first record already has syncStart set, it is not reused and a
802
+ // brand new record is created for the second sync
803
+ cdl.saveTimestamp({
804
+ key: 'internal.client.locus.sync.start',
805
+ value: 200,
806
+ options: {
807
+ meetingId: 'meeting-1',
808
+ dataSetName: 'main',
809
+ randomBackoffTime: 0,
810
+ trackingId: 'sync-tracking-2',
811
+ },
812
+ });
813
+ cdl.saveTimestamp({
814
+ key: 'internal.client.locus.sync.request',
815
+ value: 210,
816
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId: 'sync-tracking-2'},
817
+ });
818
+ cdl.saveTimestamp({
819
+ key: 'internal.client.locus.sync.response',
820
+ value: 230,
821
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId: 'sync-tracking-2'},
822
+ });
823
+ cdl.saveTimestamp({
824
+ key: 'internal.client.locus.sync.message.received',
825
+ value: 240,
826
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId: 'sync-tracking-2'},
827
+ });
828
+
829
+ // both records exist before completion
830
+ assert.lengthOf(cdl.meetingLatencies.get('meeting-1')!, 2);
831
+
832
+ // completing the second sync removes only its own record; the older pending record is left
833
+ // in place because a late LLM message could still complete it (stale cleanup is handled by
834
+ // the TTL / capacity pruning, not by completion).
835
+ const completed = cdl.completeLocusSyncLatency('meeting-1', 'sync-tracking-2');
836
+
837
+ assert.isDefined(completed);
838
+ assert.equal(completed!.dataSet, 'main');
839
+
840
+ const remaining = cdl.meetingLatencies.get('meeting-1');
841
+
842
+ assert.isDefined(remaining);
843
+ assert.lengthOf(remaining!, 1);
844
+ assert.equal(remaining![0].locusSync.trackingId, 'sync-tracking-1');
845
+ });
846
+
847
+ it('prunes an incomplete record once it is older than the pending TTL', () => {
848
+ // an incomplete sync record that never received its LLM message
849
+ cdl.saveTimestamp({
850
+ key: 'internal.client.locus.sync.start',
851
+ value: 100,
852
+ options: {
853
+ meetingId: 'meeting-1',
854
+ dataSetName: 'main',
855
+ trackingId: 'stale-tracking-id',
856
+ },
857
+ });
858
+
859
+ // a brand new sync for the same dataset starts more than the TTL later
860
+ cdl.saveTimestamp({
861
+ key: 'internal.client.locus.sync.start',
862
+ value: 100 + 5 * 60 * 1000 + 1,
863
+ options: {
864
+ meetingId: 'meeting-1',
865
+ dataSetName: 'main',
866
+ trackingId: 'fresh-tracking-id',
867
+ },
868
+ });
869
+
870
+ const records = cdl.meetingLatencies.get('meeting-1');
871
+
872
+ assert.isDefined(records);
873
+ assert.lengthOf(records!, 1);
874
+ assert.equal(records![0].locusSync.trackingId, 'fresh-tracking-id');
875
+ });
876
+
877
+ it('keeps an incomplete record that is still within the pending TTL', () => {
878
+ cdl.saveTimestamp({
879
+ key: 'internal.client.locus.sync.start',
880
+ value: 100,
881
+ options: {
882
+ meetingId: 'meeting-1',
883
+ dataSetName: 'main',
884
+ trackingId: 'pending-tracking-id',
885
+ },
886
+ });
887
+
888
+ // a new sync for the same dataset starts well within the TTL window
889
+ cdl.saveTimestamp({
890
+ key: 'internal.client.locus.sync.start',
891
+ value: 100 + 1000,
892
+ options: {
893
+ meetingId: 'meeting-1',
894
+ dataSetName: 'main',
895
+ trackingId: 'fresh-tracking-id',
896
+ },
897
+ });
898
+
899
+ const records = cdl.meetingLatencies.get('meeting-1');
900
+
901
+ assert.isDefined(records);
902
+ assert.lengthOf(records!, 2);
903
+ assert.equal(records![0].locusSync.trackingId, 'pending-tracking-id');
904
+ assert.equal(records![1].locusSync.trackingId, 'fresh-tracking-id');
905
+ });
906
+
907
+ it('does not prune incomplete records from a different dataset', () => {
908
+ cdl.saveTimestamp({
909
+ key: 'internal.client.locus.sync.start',
910
+ value: 100,
911
+ options: {
912
+ meetingId: 'meeting-1',
913
+ dataSetName: 'other',
914
+ trackingId: 'other-tracking-id',
915
+ },
916
+ });
917
+
918
+ // a much later sync for a different dataset must not touch the "other" record
919
+ cdl.saveTimestamp({
920
+ key: 'internal.client.locus.sync.start',
921
+ value: 100 + 5 * 60 * 1000 + 1,
922
+ options: {
923
+ meetingId: 'meeting-1',
924
+ dataSetName: 'main',
925
+ trackingId: 'main-tracking-id',
926
+ },
927
+ });
928
+
929
+ const records = cdl.meetingLatencies.get('meeting-1');
930
+
931
+ assert.isDefined(records);
932
+ assert.lengthOf(records!, 2);
933
+ assert.equal(records![0].locusSync.trackingId, 'other-tracking-id');
934
+ assert.equal(records![1].locusSync.trackingId, 'main-tracking-id');
935
+ });
936
+
937
+ it('caps the number of incomplete records kept per dataset', () => {
938
+ // create more incomplete records than the per-dataset cap, all within the TTL window
939
+ for (let i = 0; i <= 20; i += 1) {
940
+ cdl.saveTimestamp({
941
+ key: 'internal.client.locus.sync.start',
942
+ value: 100 + i,
943
+ options: {
944
+ meetingId: 'meeting-1',
945
+ dataSetName: 'main',
946
+ trackingId: `sync-tracking-${i}`,
947
+ },
948
+ });
949
+ }
950
+
951
+ const records = cdl.meetingLatencies.get('meeting-1');
952
+ const trackingIds = records!.map(({locusSync}) => locusSync.trackingId);
953
+
954
+ assert.isDefined(records);
955
+ assert.lengthOf(records!, 20);
956
+ // the oldest record is dropped, the newest is kept
957
+ assert.notInclude(trackingIds, 'sync-tracking-0');
958
+ assert.include(trackingIds, 'sync-tracking-20');
959
+ });
960
+
961
+ it('does not clean up never-completed records for a different dataset', () => {
962
+ // never-completed sync for dataset "other"
963
+ cdl.saveTimestamp({
964
+ key: 'internal.client.locus.sync.start',
965
+ value: 100,
966
+ options: {
967
+ meetingId: 'meeting-1',
968
+ dataSetName: 'other',
969
+ randomBackoffTime: 0,
970
+ trackingId: 'other-tracking-1',
971
+ },
972
+ });
973
+ cdl.saveTimestamp({
974
+ key: 'internal.client.locus.sync.request',
975
+ value: 110,
976
+ options: {meetingId: 'meeting-1', dataSetName: 'other', trackingId: 'other-tracking-1'},
977
+ });
978
+ cdl.saveTimestamp({
979
+ key: 'internal.client.locus.sync.response',
980
+ value: 130,
981
+ options: {meetingId: 'meeting-1', dataSetName: 'other', trackingId: 'other-tracking-1'},
982
+ });
983
+
984
+ // completed sync for dataset "main"
985
+ cdl.saveTimestamp({
986
+ key: 'internal.client.locus.sync.start',
987
+ value: 200,
988
+ options: {
989
+ meetingId: 'meeting-1',
990
+ dataSetName: 'main',
991
+ randomBackoffTime: 0,
992
+ trackingId: 'main-tracking-1',
993
+ },
994
+ });
995
+ cdl.saveTimestamp({
996
+ key: 'internal.client.locus.sync.request',
997
+ value: 210,
998
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId: 'main-tracking-1'},
999
+ });
1000
+ cdl.saveTimestamp({
1001
+ key: 'internal.client.locus.sync.response',
1002
+ value: 230,
1003
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId: 'main-tracking-1'},
1004
+ });
1005
+ cdl.saveTimestamp({
1006
+ key: 'internal.client.locus.sync.message.received',
1007
+ value: 240,
1008
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId: 'main-tracking-1'},
1009
+ });
1010
+
1011
+ const completed = cdl.completeLocusSyncLatency('meeting-1', 'main-tracking-1');
1012
+
1013
+ assert.isDefined(completed);
1014
+
1015
+ // the "other" dataset record must remain untouched
1016
+ const records = cdl.meetingLatencies.get('meeting-1');
1017
+
1018
+ assert.isDefined(records);
1019
+ assert.lengthOf(records!, 1);
1020
+ assert.equal(records![0].locusSync.dataSetName, 'other');
1021
+ assert.equal(records![0].locusSync.trackingId, 'other-tracking-1');
1022
+ });
1023
+
1024
+ it('keys sync latency records by meeting id', () => {
1025
+ cdl.saveTimestamp({
1026
+ key: 'internal.client.locus.sync.start',
1027
+ value: 100,
1028
+ options: {
1029
+ meetingId: 'meeting-1',
1030
+ dataSetName: 'main',
1031
+ randomBackoffTime: 10,
1032
+ trackingId: 'sync-tracking-1',
1033
+ },
1034
+ });
1035
+ cdl.saveTimestamp({
1036
+ key: 'internal.client.locus.sync.request',
1037
+ value: 110,
1038
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId: 'sync-tracking-1'},
1039
+ });
1040
+
1041
+ cdl.saveTimestamp({
1042
+ key: 'internal.client.locus.sync.start',
1043
+ value: 200,
1044
+ options: {
1045
+ meetingId: 'meeting-2',
1046
+ dataSetName: 'main',
1047
+ randomBackoffTime: 23,
1048
+ trackingId: 'sync-tracking-2',
1049
+ },
1050
+ });
1051
+ cdl.saveTimestamp({
1052
+ key: 'internal.client.locus.sync.request',
1053
+ value: 217,
1054
+ options: {meetingId: 'meeting-2', dataSetName: 'main', trackingId: 'sync-tracking-2'},
1055
+ });
1056
+ cdl.saveTimestamp({
1057
+ key: 'internal.client.locus.sync.response',
1058
+ value: 241,
1059
+ options: {meetingId: 'meeting-2', dataSetName: 'main', trackingId: 'sync-tracking-2'},
1060
+ });
1061
+ cdl.saveTimestamp({
1062
+ key: 'internal.client.locus.sync.message.received',
1063
+ value: 268,
1064
+ options: {meetingId: 'meeting-2', dataSetName: 'main', trackingId: 'sync-tracking-2'},
1065
+ });
1066
+
1067
+ cdl.saveTimestamp({
1068
+ key: 'internal.client.locus.sync.response',
1069
+ value: 125,
1070
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId: 'sync-tracking-1'},
1071
+ });
1072
+ cdl.saveTimestamp({
1073
+ key: 'internal.client.locus.sync.message.received',
1074
+ value: 150,
1075
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId: 'sync-tracking-1'},
1076
+ });
1077
+
1078
+ assert.deepEqual(cdl.getLocusSyncLatency('meeting-1', 'sync-tracking-1'), {
1079
+ randomBackoffTime: 0,
1080
+ hashtreePrepTime: 0,
1081
+ hashtreeResponseTime: 0,
1082
+ syncPrepTime: 10,
1083
+ syncResponseTime: 15,
1084
+ syncMessageReceiveTime: 40,
1085
+ totalTime: 50,
1086
+ });
1087
+ assert.deepEqual(cdl.getLocusSyncLatency('meeting-2', 'sync-tracking-2'), {
1088
+ randomBackoffTime: 0,
1089
+ hashtreePrepTime: 0,
1090
+ hashtreeResponseTime: 0,
1091
+ syncPrepTime: 17,
1092
+ syncResponseTime: 24,
1093
+ syncMessageReceiveTime: 51,
1094
+ totalTime: 68,
1095
+ });
1096
+
1097
+ cdl.clearLocusSyncLatency('main', 'meeting-1');
1098
+
1099
+ assert.isUndefined(cdl.getLocusSyncLatency('meeting-1', 'sync-tracking-1'));
1100
+ assert.isDefined(cdl.getLocusSyncLatency('meeting-2', 'sync-tracking-2'));
1101
+ });
1102
+
1103
+ it('returns undefined and clears state when required milestones are missing', () => {
1104
+ cdl.saveTimestamp({
1105
+ key: 'internal.client.locus.sync.start',
1106
+ value: 100,
1107
+ options: {
1108
+ meetingId: 'meeting-1',
1109
+ dataSetName: 'main',
1110
+ randomBackoffTime: 0,
1111
+ trackingId: 'sync-tracking-id',
1112
+ },
1113
+ });
1114
+ cdl.saveTimestamp({
1115
+ key: 'internal.client.locus.sync.message.received',
1116
+ value: 140,
1117
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId: 'sync-tracking-id'},
1118
+ });
1119
+
1120
+ assert.isUndefined(cdl.getLocusSyncLatency('meeting-1', 'sync-tracking-id'));
1121
+ cdl.clearLocusSyncLatency('main', 'meeting-1');
1122
+ assert.isFalse(cdl.meetingLatencies.has('meeting-1'));
1123
+ });
1124
+
1125
+ it('clears only the record matching the tracking id when one is supplied', () => {
1126
+ const stampFullRecord = (trackingId: string, base: number) => {
1127
+ cdl.saveTimestamp({
1128
+ key: 'internal.client.locus.sync.start',
1129
+ value: base,
1130
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId},
1131
+ });
1132
+ cdl.saveTimestamp({
1133
+ key: 'internal.client.locus.sync.request',
1134
+ value: base + 10,
1135
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId},
1136
+ });
1137
+ cdl.saveTimestamp({
1138
+ key: 'internal.client.locus.sync.response',
1139
+ value: base + 25,
1140
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId},
1141
+ });
1142
+ cdl.saveTimestamp({
1143
+ key: 'internal.client.locus.sync.message.received',
1144
+ value: base + 50,
1145
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId},
1146
+ });
1147
+ };
1148
+
1149
+ // Two records for the same dataset/meeting but different tracking ids.
1150
+ stampFullRecord('sync-tracking-1', 100);
1151
+ stampFullRecord('sync-tracking-2', 200);
1152
+
1153
+ assert.isDefined(cdl.getLocusSyncLatency('meeting-1', 'sync-tracking-1'));
1154
+ assert.isDefined(cdl.getLocusSyncLatency('meeting-1', 'sync-tracking-2'));
1155
+
1156
+ // Clearing with a tracking id drops only that record, leaving the other intact.
1157
+ cdl.clearLocusSyncLatency('main', 'meeting-1', 'sync-tracking-1');
1158
+
1159
+ assert.isUndefined(cdl.getLocusSyncLatency('meeting-1', 'sync-tracking-1'));
1160
+ assert.isDefined(cdl.getLocusSyncLatency('meeting-1', 'sync-tracking-2'));
1161
+ });
1162
+
1163
+ describe('recordLocusSyncMessageReceived', () => {
1164
+ const startPendingSync = (trackingId: string) => {
1165
+ cdl.saveTimestamp({
1166
+ key: 'internal.client.locus.sync.start',
1167
+ value: 100,
1168
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId},
1169
+ });
1170
+ };
1171
+
1172
+ it('stamps messageReceived on the matching pending record using the current time', () => {
1173
+ clock.setSystemTime(500);
1174
+
1175
+ try {
1176
+ startPendingSync('sync-tracking-id');
1177
+ cdl.recordLocusSyncMessageReceived('meeting-1', 'sync-tracking-id');
1178
+
1179
+ assert.equal(cdl.meetingLatencies.get('meeting-1')![0].locusSync.messageReceived, 500);
1180
+ } finally {
1181
+ clock.setSystemTime(now.getTime());
1182
+ }
1183
+ });
1184
+
1185
+ it('does not overwrite an already recorded messageReceived', () => {
1186
+ clock.setSystemTime(999);
1187
+
1188
+ try {
1189
+ startPendingSync('sync-tracking-id');
1190
+ cdl.saveTimestamp({
1191
+ key: 'internal.client.locus.sync.message.received',
1192
+ value: 140,
1193
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId: 'sync-tracking-id'},
1194
+ });
1195
+ cdl.recordLocusSyncMessageReceived('meeting-1', 'sync-tracking-id');
1196
+
1197
+ assert.equal(cdl.meetingLatencies.get('meeting-1')![0].locusSync.messageReceived, 140);
1198
+ } finally {
1199
+ clock.setSystemTime(now.getTime());
1200
+ }
1201
+ });
1202
+
1203
+ it('is a no-op when no record matches the tracking id', () => {
1204
+ startPendingSync('sync-tracking-id');
1205
+ cdl.recordLocusSyncMessageReceived('meeting-1', 'some-other-tracking-id');
1206
+
1207
+ assert.isUndefined(cdl.meetingLatencies.get('meeting-1')![0].locusSync.messageReceived);
1208
+ });
1209
+
1210
+ it('is a no-op when trackingId is empty', () => {
1211
+ startPendingSync('sync-tracking-id');
1212
+
1213
+ assert.doesNotThrow(() => cdl.recordLocusSyncMessageReceived('meeting-1', ''));
1214
+ assert.isUndefined(cdl.meetingLatencies.get('meeting-1')![0].locusSync.messageReceived);
1215
+ });
1216
+ });
1217
+
1218
+ describe('completeLocusSyncLatency requires both milestones', () => {
1219
+ const recordSyncRequest = (trackingId: string) => {
1220
+ cdl.saveTimestamp({
1221
+ key: 'internal.client.locus.sync.start',
1222
+ value: 100,
1223
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId},
1224
+ });
1225
+ cdl.saveTimestamp({
1226
+ key: 'internal.client.locus.sync.request',
1227
+ value: 110,
1228
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId},
1229
+ });
1230
+ };
1231
+ const recordSyncResponse = (trackingId: string) =>
1232
+ cdl.saveTimestamp({
1233
+ key: 'internal.client.locus.sync.response',
1234
+ value: 130,
1235
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId},
1236
+ });
1237
+ const recordMessageReceived = (trackingId: string) =>
1238
+ cdl.saveTimestamp({
1239
+ key: 'internal.client.locus.sync.message.received',
1240
+ value: 150,
1241
+ options: {meetingId: 'meeting-1', dataSetName: 'main', trackingId},
1242
+ });
1243
+
1244
+ it('returns undefined and keeps the record when only the /sync response is present', () => {
1245
+ recordSyncRequest('sync-tracking-id');
1246
+ recordSyncResponse('sync-tracking-id');
1247
+
1248
+ assert.isUndefined(cdl.completeLocusSyncLatency('meeting-1', 'sync-tracking-id'));
1249
+ assert.lengthOf(cdl.meetingLatencies.get('meeting-1')!, 1);
1250
+ });
1251
+
1252
+ it('returns undefined and keeps the record when only the LLM message is present', () => {
1253
+ recordSyncRequest('sync-tracking-id');
1254
+ recordMessageReceived('sync-tracking-id');
1255
+
1256
+ assert.isUndefined(cdl.completeLocusSyncLatency('meeting-1', 'sync-tracking-id'));
1257
+ assert.lengthOf(cdl.meetingLatencies.get('meeting-1')!, 1);
1258
+ });
1259
+
1260
+ it('emits and consumes the record when the /sync response arrives after the LLM message', () => {
1261
+ recordSyncRequest('sync-tracking-id');
1262
+ // LLM message arrives first, before the HTTP response
1263
+ recordMessageReceived('sync-tracking-id');
1264
+ assert.isUndefined(cdl.completeLocusSyncLatency('meeting-1', 'sync-tracking-id'));
1265
+
1266
+ // /sync response lands second -> now both milestones are in
1267
+ recordSyncResponse('sync-tracking-id');
1268
+
1269
+ assert.deepEqual(cdl.completeLocusSyncLatency('meeting-1', 'sync-tracking-id'), {
1270
+ dataSet: 'main',
1271
+ syncLatency: {
1272
+ randomBackoffTime: 0,
1273
+ hashtreePrepTime: 0,
1274
+ hashtreeResponseTime: 0,
1275
+ syncPrepTime: 10,
1276
+ syncResponseTime: 20,
1277
+ syncMessageReceiveTime: 40,
1278
+ totalTime: 50,
1279
+ },
1280
+ });
1281
+ assert.isUndefined(cdl.meetingLatencies.get('meeting-1'));
1282
+ });
1283
+
1284
+ it('emits and consumes the record when the LLM message arrives after the /sync response', () => {
1285
+ recordSyncRequest('sync-tracking-id');
1286
+ // HTTP response arrives first
1287
+ recordSyncResponse('sync-tracking-id');
1288
+ assert.isUndefined(cdl.completeLocusSyncLatency('meeting-1', 'sync-tracking-id'));
1289
+
1290
+ // LLM message lands second
1291
+ recordMessageReceived('sync-tracking-id');
1292
+
1293
+ const completed = cdl.completeLocusSyncLatency('meeting-1', 'sync-tracking-id');
1294
+
1295
+ assert.isDefined(completed);
1296
+ assert.equal(completed!.dataSet, 'main');
1297
+ assert.isUndefined(cdl.meetingLatencies.get('meeting-1'));
1298
+ });
1299
+ });
116
1300
  });
117
1301
 
118
1302
  it('should calculate diff between timestamps correctly', () => {
@@ -235,20 +1419,12 @@ describe('internal-plugin-metrics', () => {
235
1419
  });
236
1420
 
237
1421
  describe('measureLatency', () => {
238
- let clock;
239
1422
  let saveLatencySpy;
240
1423
 
241
1424
  beforeEach(() => {
242
- clock = sinon.useFakeTimers();
243
-
244
1425
  saveLatencySpy = sinon.stub(cdl, 'saveLatency');
245
1426
  });
246
1427
 
247
- afterEach(() => {
248
- clock.restore();
249
- sinon.restore();
250
- });
251
-
252
1428
  it('checks measureLatency with accumulate false', async () => {
253
1429
  const key = 'internal.client.pageJMT';
254
1430
  const accumulate = false;
@@ -264,7 +1440,7 @@ describe('internal-plugin-metrics', () => {
264
1440
  const resolvedValue = await promise;
265
1441
  assert.deepEqual(resolvedValue, 'test');
266
1442
  assert.calledOnceWithExactly(callbackStub);
267
- assert.calledOnceWithExactly(saveLatencySpy, key, 50, accumulate);
1443
+ assert.calledOnceWithExactly(saveLatencySpy, key, 50, {accumulate});
268
1444
  });
269
1445
 
270
1446
  it('checks measureLatency with accumulate true', async () => {
@@ -280,7 +1456,7 @@ describe('internal-plugin-metrics', () => {
280
1456
  const resolvedValue = await promise;
281
1457
  assert.deepEqual(resolvedValue, 'test123');
282
1458
  assert.calledOnceWithExactly(callbackStub);
283
- assert.calledOnceWithExactly(saveLatencySpy, key, 20, accumulate);
1459
+ assert.calledOnceWithExactly(saveLatencySpy, key, 20, {accumulate});
284
1460
  });
285
1461
 
286
1462
  it('checks measureLatency when callBack rejects', async () => {
@@ -297,7 +1473,7 @@ describe('internal-plugin-metrics', () => {
297
1473
  const rejectedValue = await assert.isRejected(promise);
298
1474
  assert.deepEqual(rejectedValue, error);
299
1475
  assert.calledOnceWithExactly(callbackStub);
300
- assert.calledOnceWithExactly(saveLatencySpy, key, 50, accumulate);
1476
+ assert.calledOnceWithExactly(saveLatencySpy, key, 50, {accumulate});
301
1477
  });
302
1478
  });
303
1479
 
@@ -412,8 +1588,10 @@ describe('internal-plugin-metrics', () => {
412
1588
  });
413
1589
 
414
1590
  it('calculates getCallInitJoinReq correctly', () => {
1591
+ cdl.saveTimestamp({key: 'internal.client.meeting.interstitial-window.showed', value: 5});
415
1592
  cdl.saveTimestamp({key: 'internal.client.interstitial-window.click.joinbutton', value: 10});
416
1593
  cdl.saveTimestamp({key: 'client.locus.join.request', value: 20});
1594
+ // showedToJoinReq = 20-5 = 15, showInterstitialTime = 10-5 = 5, result = 15-5 = 10
417
1595
  assert.deepEqual(cdl.getCallInitJoinReq(), 10);
418
1596
  });
419
1597
 
@@ -509,7 +1687,7 @@ describe('internal-plugin-metrics', () => {
509
1687
 
510
1688
  it('calculates getStayLobbyTime correctly', () => {
511
1689
  cdl.saveTimestamp({
512
- key: 'client.locus.join.response',
1690
+ key: 'client.lobby.entered',
513
1691
  value: 10,
514
1692
  });
515
1693
  cdl.saveTimestamp({
@@ -519,6 +1697,50 @@ describe('internal-plugin-metrics', () => {
519
1697
  assert.deepEqual(cdl.getStayLobbyTime(), 10);
520
1698
  });
521
1699
 
1700
+ describe('getStayLobbyTimeCappedBy', () => {
1701
+ it('returns 0 when lobbyStartTimestamp is missing', () => {
1702
+ cdl.saveTimestamp({key: 'client.media-engine.ready', value: 100});
1703
+ assert.deepEqual(cdl.getStayLobbyTimeCappedBy('client.media-engine.ready'), 0);
1704
+ });
1705
+
1706
+ it('returns undefined when endTimestampKey is missing', () => {
1707
+ cdl.saveTimestamp({key: 'client.lobby.entered', value: 10});
1708
+ assert.deepEqual(cdl.getStayLobbyTimeCappedBy('client.media-engine.ready'), undefined);
1709
+ });
1710
+
1711
+ it('uses maximumEndTimestamp when lobby end does not exist', () => {
1712
+ cdl.saveTimestamp({key: 'client.lobby.entered', value: 10});
1713
+ cdl.saveTimestamp({key: 'client.media-engine.ready', value: 50});
1714
+ assert.deepEqual(cdl.getStayLobbyTimeCappedBy('client.media-engine.ready'), 40);
1715
+ });
1716
+
1717
+ it('uses lobby end when it is before maximumEndTimestamp', () => {
1718
+ cdl.saveTimestamp({key: 'client.lobby.entered', value: 10});
1719
+ cdl.saveTimestamp({key: 'client.lobby.exited', value: 30});
1720
+ cdl.saveTimestamp({key: 'client.media-engine.ready', value: 50});
1721
+ assert.deepEqual(cdl.getStayLobbyTimeCappedBy('client.media-engine.ready'), 20);
1722
+ });
1723
+
1724
+ it('uses maximumEndTimestamp when lobby end is after it', () => {
1725
+ cdl.saveTimestamp({key: 'client.lobby.entered', value: 10});
1726
+ cdl.saveTimestamp({key: 'client.lobby.exited', value: 60});
1727
+ cdl.saveTimestamp({key: 'client.media-engine.ready', value: 50});
1728
+ assert.deepEqual(cdl.getStayLobbyTimeCappedBy('client.media-engine.ready'), 40);
1729
+ });
1730
+
1731
+ it('clamps to 0 when result would be negative', () => {
1732
+ cdl.saveTimestamp({key: 'client.lobby.entered', value: 100});
1733
+ cdl.saveTimestamp({key: 'client.media-engine.ready', value: 50});
1734
+ assert.deepEqual(cdl.getStayLobbyTimeCappedBy('client.media-engine.ready'), 0);
1735
+ });
1736
+
1737
+ it('clamps to MAX_INTEGER when result is very large', () => {
1738
+ cdl.saveTimestamp({key: 'client.lobby.entered', value: 0});
1739
+ cdl.saveTimestamp({key: 'client.media-engine.ready', value: 2147483648});
1740
+ assert.deepEqual(cdl.getStayLobbyTimeCappedBy('client.media-engine.ready'), 2147483647);
1741
+ });
1742
+ });
1743
+
522
1744
  it('calculates getPageJMT correctly', () => {
523
1745
  cdl.saveLatency('internal.client.pageJMT', 10);
524
1746
  assert.deepEqual(cdl.getPageJMT(), 10);
@@ -530,76 +1752,40 @@ describe('internal-plugin-metrics', () => {
530
1752
  });
531
1753
 
532
1754
  it('calculates getClickToInterstitial correctly', () => {
533
- cdl.saveTimestamp({
534
- key: 'internal.client.meeting.click.joinbutton',
535
- value: 10,
536
- });
537
- cdl.saveTimestamp({
538
- key: 'internal.client.meeting.interstitial-window.showed',
539
- value: 20,
540
- });
541
- assert.deepEqual(cdl.getClickToInterstitial(), 10);
542
- });
543
-
544
- it('calculates getClickToInterstitial without join button timestamp', () => {
545
1755
  cdl.saveLatency('internal.click.to.interstitial', 5);
546
- cdl.saveTimestamp({
547
- key: 'internal.client.meeting.interstitial-window.showed',
548
- value: 20,
549
- });
550
1756
  assert.deepEqual(cdl.getClickToInterstitial(), 5);
551
1757
  });
552
1758
 
553
- it('calculates getClickToInterstitial without join button timestamp when it is 0', () => {
1759
+ it('calculates getClickToInterstitial correctly when it is 0', () => {
554
1760
  cdl.saveLatency('internal.click.to.interstitial', 0);
555
- cdl.saveTimestamp({
556
- key: 'internal.client.meeting.interstitial-window.showed',
557
- value: 20,
558
- });
559
1761
  assert.deepEqual(cdl.getClickToInterstitial(), 0);
560
1762
  });
561
1763
 
562
- it('calculates getClickToInterstitial without join button timestamp when it is greater than MAX_INTEGER', () => {
1764
+ it('calculates getClickToInterstitial correctly when it is greater than MAX_INTEGER', () => {
563
1765
  cdl.saveLatency('internal.click.to.interstitial', 2147483648);
564
1766
  assert.deepEqual(cdl.getClickToInterstitial(), 2147483647);
565
1767
  });
566
1768
 
567
1769
  it('calculates getClickToInterstitialWithUserDelay correctly', () => {
568
- cdl.saveTimestamp({
569
- key: 'internal.client.meeting.click.joinbutton',
570
- value: 10,
571
- });
572
- cdl.saveTimestamp({
573
- key: 'internal.client.meeting.interstitial-window.showed',
574
- value: 20,
575
- });
576
- assert.deepEqual(cdl.getClickToInterstitialWithUserDelay(), 10);
577
- });
578
-
579
- it('calculates getClickToInterstitialWithUserDelay without join button timestamp', () => {
580
1770
  cdl.saveLatency('internal.click.to.interstitial.with.user.delay', 5);
581
- cdl.saveTimestamp({
582
- key: 'internal.client.meeting.interstitial-window.showed',
583
- value: 20,
584
- });
585
1771
  assert.deepEqual(cdl.getClickToInterstitialWithUserDelay(), 5);
586
1772
  });
587
1773
 
588
- it('calculates getClickToInterstitialWithUserDelay without join button timestamp when it is 0', () => {
1774
+ it('calculates getClickToInterstitialWithUserDelay correctly when it is 0', () => {
589
1775
  cdl.saveLatency('internal.click.to.interstitial.with.user.delay', 0);
590
- cdl.saveTimestamp({
591
- key: 'internal.client.meeting.interstitial-window.showed',
592
- value: 20,
593
- });
594
1776
  assert.deepEqual(cdl.getClickToInterstitialWithUserDelay(), 0);
595
1777
  });
596
1778
 
597
- it('calculates getClickToInterstitialWithUserDelay without join button timestamp when it is greater than MAX_INTEGER', () => {
1779
+ it('calculates getClickToInterstitialWithUserDelay correctly when it is greater than MAX_INTEGER', () => {
598
1780
  cdl.saveLatency('internal.click.to.interstitial.with.user.delay', 2147483648);
599
1781
  assert.deepEqual(cdl.getClickToInterstitialWithUserDelay(), 2147483647);
600
1782
  });
601
1783
 
602
1784
  it('calculates getInterstitialToJoinOK correctly', () => {
1785
+ cdl.saveTimestamp({
1786
+ key: 'internal.client.meeting.interstitial-window.showed',
1787
+ value: 5,
1788
+ });
603
1789
  cdl.saveTimestamp({
604
1790
  key: 'internal.client.interstitial-window.click.joinbutton',
605
1791
  value: 10,
@@ -608,12 +1794,13 @@ describe('internal-plugin-metrics', () => {
608
1794
  key: 'client.locus.join.response',
609
1795
  value: 20,
610
1796
  });
1797
+ // showedToJoinResp = 20-5 = 15, showInterstitialTime = 10-5 = 5, result = 15-5 = 10
611
1798
  assert.deepEqual(cdl.getInterstitialToJoinOK(), 10);
612
1799
  });
613
1800
 
614
1801
  it('calculates getInterstitialToJoinOK correctly when one value is not a number', () => {
615
1802
  cdl.saveTimestamp({
616
- key: 'internal.client.interstitial-window.click.joinbutton',
1803
+ key: 'internal.client.meeting.interstitial-window.showed',
617
1804
  value: 'ten' as unknown as number,
618
1805
  });
619
1806
  cdl.saveTimestamp({
@@ -624,22 +1811,12 @@ describe('internal-plugin-metrics', () => {
624
1811
  });
625
1812
 
626
1813
  it('calculates getCallInitMediaEngineReady correctly', () => {
627
- cdl.saveTimestamp({
628
- key: 'internal.client.interstitial-window.click.joinbutton',
629
- value: 10,
630
- });
631
- cdl.saveTimestamp({
632
- key: 'client.media-engine.ready',
633
- value: 20,
634
- });
635
- assert.deepEqual(cdl.getCallInitMediaEngineReady(), 10);
1814
+ sinon.stub(cdl, 'getInterstitialToMediaOKJMT').returns(42);
1815
+ assert.deepEqual(cdl.getCallInitMediaEngineReady(), 42);
636
1816
  });
637
1817
 
638
1818
  it('calculates getTotalJMT correctly', () => {
639
- cdl.saveTimestamp({
640
- key: 'internal.client.interstitial-window.click.joinbutton',
641
- value: 5,
642
- });
1819
+ cdl.saveLatency('internal.click.to.interstitial', 10);
643
1820
  cdl.saveTimestamp({
644
1821
  key: 'internal.client.meeting.click.joinbutton',
645
1822
  value: 10,
@@ -648,29 +1825,39 @@ describe('internal-plugin-metrics', () => {
648
1825
  key: 'internal.client.meeting.interstitial-window.showed',
649
1826
  value: 20,
650
1827
  });
1828
+ cdl.saveTimestamp({
1829
+ key: 'internal.client.interstitial-window.click.joinbutton',
1830
+ value: 25,
1831
+ });
651
1832
  cdl.saveTimestamp({
652
1833
  key: 'client.locus.join.response',
653
1834
  value: 40,
654
1835
  });
655
- assert.deepEqual(cdl.getTotalJMT(), 45);
1836
+ // clickToInterstitial = 20-10 = 10
1837
+ // showedToJoinLocusResponse = 40-20 = 20
1838
+ // showInterstitialTime = 25-20 = 5
1839
+ // total = 10 + 20 - 5 = 25
1840
+ assert.deepEqual(cdl.getTotalJMT(), 25);
656
1841
  });
657
1842
 
658
1843
  it('calculates getTotalJMT correctly when clickToInterstitial is 0', () => {
659
1844
  cdl.saveLatency('internal.click.to.interstitial', 0);
660
1845
  cdl.saveTimestamp({
661
- key: 'internal.client.interstitial-window.click.joinbutton',
1846
+ key: 'internal.client.meeting.interstitial-window.showed',
662
1847
  value: 20,
663
1848
  });
664
1849
  cdl.saveTimestamp({
665
1850
  key: 'client.locus.join.response',
666
1851
  value: 40,
667
1852
  });
1853
+ // showedToJoinLocusResponse = 40-20 = 20, showInterstitialTime = 0
1854
+ // total = 0 + 20 - 0 = 20
668
1855
  assert.deepEqual(cdl.getTotalJMT(), 20);
669
1856
  });
670
1857
 
671
- it('calculates getTotalJMT correctly when interstitialToJoinOk is 0', () => {
1858
+ it('calculates getTotalJMT correctly when interstitialClickJoinToJoinLocusResponse is 0', () => {
672
1859
  cdl.saveTimestamp({
673
- key: 'internal.client.interstitial-window.click.joinbutton',
1860
+ key: 'internal.client.meeting.interstitial-window.showed',
674
1861
  value: 40,
675
1862
  });
676
1863
  cdl.saveLatency('internal.click.to.interstitial', 12);
@@ -678,12 +1865,14 @@ describe('internal-plugin-metrics', () => {
678
1865
  key: 'client.locus.join.response',
679
1866
  value: 40,
680
1867
  });
1868
+ // showedToJoinLocusResponse = 0, showInterstitialTime = 0
1869
+ // total = 12 + 0 - 0 = 12
681
1870
  assert.deepEqual(cdl.getTotalJMT(), 12);
682
1871
  });
683
1872
 
684
- it('calculates getTotalJMT correctly when both clickToInterstitial and interstitialToJoinOk are 0', () => {
1873
+ it('calculates getTotalJMT correctly when both clickToInterstitial and interstitialClickJoinToJoinLocusResponse are 0', () => {
685
1874
  cdl.saveTimestamp({
686
- key: 'internal.client.interstitial-window.click.joinbutton',
1875
+ key: 'internal.client.meeting.interstitial-window.showed',
687
1876
  value: 40,
688
1877
  });
689
1878
  cdl.saveLatency('internal.click.to.interstitial', 0);
@@ -696,7 +1885,7 @@ describe('internal-plugin-metrics', () => {
696
1885
 
697
1886
  it('calculates getTotalJMT correctly when both clickToInterstitial is not a number', () => {
698
1887
  cdl.saveTimestamp({
699
- key: 'internal.client.interstitial-window.click.joinbutton',
1888
+ key: 'internal.client.meeting.interstitial-window.showed',
700
1889
  value: 40,
701
1890
  });
702
1891
  cdl.saveLatency('internal.click.to.interstitial', 'eleven' as unknown as number);
@@ -709,7 +1898,7 @@ describe('internal-plugin-metrics', () => {
709
1898
 
710
1899
  it('calculates getTotalJMT correctly when it is greater than MAX_INTEGER', () => {
711
1900
  cdl.saveTimestamp({
712
- key: 'internal.client.interstitial-window.click.joinbutton',
1901
+ key: 'internal.client.meeting.interstitial-window.showed',
713
1902
  value: 5,
714
1903
  });
715
1904
  cdl.saveTimestamp({
@@ -721,14 +1910,7 @@ describe('internal-plugin-metrics', () => {
721
1910
  });
722
1911
 
723
1912
  it('calculates getTotalJMTWithUserDelay correctly', () => {
724
- cdl.saveTimestamp({
725
- key: 'internal.client.interstitial-window.click.joinbutton',
726
- value: 5,
727
- });
728
- cdl.saveTimestamp({
729
- key: 'internal.client.meeting.click.joinbutton',
730
- value: 10,
731
- });
1913
+ cdl.saveLatency('internal.click.to.interstitial.with.user.delay', 10);
732
1914
  cdl.saveTimestamp({
733
1915
  key: 'internal.client.meeting.interstitial-window.showed',
734
1916
  value: 20,
@@ -737,13 +1919,13 @@ describe('internal-plugin-metrics', () => {
737
1919
  key: 'client.locus.join.response',
738
1920
  value: 40,
739
1921
  });
740
- assert.deepEqual(cdl.getTotalJMTWithUserDelay(), 45);
1922
+ assert.deepEqual(cdl.getTotalJMTWithUserDelay(), 30);
741
1923
  });
742
1924
 
743
1925
  it('calculates getTotalJMTWithUserDelay correctly when clickToInterstitialWithUserDelay is 0', () => {
744
1926
  cdl.saveLatency('internal.click.to.interstitial.with.user.delay', 0);
745
1927
  cdl.saveTimestamp({
746
- key: 'internal.client.interstitial-window.click.joinbutton',
1928
+ key: 'internal.client.meeting.interstitial-window.showed',
747
1929
  value: 20,
748
1930
  });
749
1931
  cdl.saveTimestamp({
@@ -753,9 +1935,9 @@ describe('internal-plugin-metrics', () => {
753
1935
  assert.deepEqual(cdl.getTotalJMTWithUserDelay(), 20);
754
1936
  });
755
1937
 
756
- it('calculates getTotalJMTWithUserDelay correctly when interstitialToJoinOk is 0', () => {
1938
+ it('calculates getTotalJMTWithUserDelay correctly when interstitialShowedToJoinLocusResponse is 0', () => {
757
1939
  cdl.saveTimestamp({
758
- key: 'internal.client.interstitial-window.click.joinbutton',
1940
+ key: 'internal.client.meeting.interstitial-window.showed',
759
1941
  value: 40,
760
1942
  });
761
1943
  cdl.saveLatency('internal.click.to.interstitial.with.user.delay', 12);
@@ -766,9 +1948,9 @@ describe('internal-plugin-metrics', () => {
766
1948
  assert.deepEqual(cdl.getTotalJMTWithUserDelay(), 12);
767
1949
  });
768
1950
 
769
- it('calculates getTotalJMTWithUserDelay correctly when both clickToInterstitialWithUserDelay and interstitialToJoinOk are 0', () => {
1951
+ it('calculates getTotalJMTWithUserDelay correctly when both clickToInterstitialWithUserDelay and interstitialShowedToJoinLocusResponse are 0', () => {
770
1952
  cdl.saveTimestamp({
771
- key: 'internal.client.interstitial-window.click.joinbutton',
1953
+ key: 'internal.client.meeting.interstitial-window.showed',
772
1954
  value: 40,
773
1955
  });
774
1956
  cdl.saveLatency('internal.click.to.interstitial.with.user.delay', 0);
@@ -781,7 +1963,7 @@ describe('internal-plugin-metrics', () => {
781
1963
 
782
1964
  it('calculates getTotalJMTWithUserDelay correctly when both clickToInterstitialWithUserDelay is not a number', () => {
783
1965
  cdl.saveTimestamp({
784
- key: 'internal.client.interstitial-window.click.joinbutton',
1966
+ key: 'internal.client.meeting.interstitial-window.showed',
785
1967
  value: 40,
786
1968
  });
787
1969
  cdl.saveLatency(
@@ -798,7 +1980,7 @@ describe('internal-plugin-metrics', () => {
798
1980
  it('calculates getTotalJMTWithUserDelay correctly when it is greater than MAX_INTEGER', () => {
799
1981
  cdl.saveLatency('internal.click.to.interstitial.with.user.delay', 2147483648);
800
1982
  cdl.saveTimestamp({
801
- key: 'internal.client.interstitial-window.click.joinbutton',
1983
+ key: 'internal.client.meeting.interstitial-window.showed',
802
1984
  value: 20,
803
1985
  });
804
1986
  cdl.saveTimestamp({
@@ -808,215 +1990,111 @@ describe('internal-plugin-metrics', () => {
808
1990
  assert.deepEqual(cdl.getTotalJMTWithUserDelay(), 2147483647);
809
1991
  });
810
1992
 
811
- it('calculates getTotalMediaJMT correctly', () => {
812
- cdl.saveTimestamp({
813
- key: 'internal.client.meeting.click.joinbutton',
814
- value: 5,
815
- });
816
- cdl.saveTimestamp({
817
- key: 'internal.client.meeting.interstitial-window.showed',
818
- value: 8,
819
- });
820
- cdl.saveTimestamp({
821
- key: 'internal.client.interstitial-window.click.joinbutton',
822
- value: 10,
823
- });
824
- cdl.saveTimestamp({
825
- key: 'client.locus.join.request',
826
- value: 12,
827
- });
828
- cdl.saveTimestamp({
829
- key: 'client.locus.join.response',
830
- value: 20,
831
- });
832
- cdl.saveTimestamp({
833
- key: 'client.lobby.exited',
834
- value: 24,
835
- });
836
- cdl.saveTimestamp({
837
- key: 'client.ice.start',
838
- value: 30,
839
- });
840
- cdl.saveTimestamp({
841
- key: 'client.ice.end',
842
- value: 40,
843
- });
844
- assert.deepEqual(cdl.getTotalMediaJMT(), 27);
1993
+ it('calculates getTotalMediaJMT correctly with lobby exiting before media-engine.ready', () => {
1994
+ cdl.saveLatency('internal.click.to.interstitial', 3);
1995
+ // clickToInterstitial = 3
1996
+ cdl.saveTimestamp({key: 'internal.client.meeting.interstitial-window.showed', value: 8});
1997
+ cdl.saveTimestamp({key: 'internal.client.interstitial-window.click.joinbutton', value: 10});
1998
+ cdl.saveTimestamp({key: 'client.media-engine.ready', value: 50});
1999
+ // interstitialShowedToMediaEngineReady = 50 - 8 = 42
2000
+ // showInterstitialTime = 10 - 8 = 2
2001
+ cdl.saveTimestamp({key: 'client.lobby.entered', value: 20});
2002
+ cdl.saveTimestamp({key: 'client.lobby.exited', value: 30});
2003
+ // stayLobbyTimeCappedByMediaEngineReady = min(30, 50) - 20 = 10
2004
+ // total = 3 + 42 - 2 - 10 = 33
2005
+ assert.deepEqual(cdl.getTotalMediaJMT(), 33);
2006
+ });
2007
+
2008
+ it('calculates getTotalMediaJMT correctly without lobby', () => {
2009
+ cdl.saveLatency('internal.click.to.interstitial', 3);
2010
+ // clickToInterstitial = 3
2011
+ cdl.saveTimestamp({key: 'internal.client.meeting.interstitial-window.showed', value: 8});
2012
+ cdl.saveTimestamp({key: 'internal.client.interstitial-window.click.joinbutton', value: 10});
2013
+ cdl.saveTimestamp({key: 'client.media-engine.ready', value: 50});
2014
+ // interstitialShowedToMediaEngineReady = 50 - 8 = 42
2015
+ // showInterstitialTime = 10 - 8 = 2
2016
+ // no client.lobby.entered → stayLobbyTimeCappedByMediaEngineReady = 0
2017
+ // total = 3 + 42 - 2 - 0 = 43
2018
+ assert.deepEqual(cdl.getTotalMediaJMT(), 43);
2019
+ });
2020
+
2021
+ it('calculates getTotalMediaJMT correctly with lobby exiting after media-engine.ready', () => {
2022
+ cdl.saveLatency('internal.click.to.interstitial', 3);
2023
+ // clickToInterstitial = 3
2024
+ cdl.saveTimestamp({key: 'internal.client.meeting.interstitial-window.showed', value: 8});
2025
+ cdl.saveTimestamp({key: 'internal.client.interstitial-window.click.joinbutton', value: 10});
2026
+ cdl.saveTimestamp({key: 'client.media-engine.ready', value: 50});
2027
+ // interstitialShowedToMediaEngineReady = 50 - 8 = 42
2028
+ // showInterstitialTime = 10 - 8 = 2
2029
+ cdl.saveTimestamp({key: 'client.lobby.entered', value: 20});
2030
+ cdl.saveTimestamp({key: 'client.lobby.exited', value: 60});
2031
+ // stayLobbyTimeCappedByMediaEngineReady = min(60, 50) - 20 = 30
2032
+ // total = 3 + 42 - 2 - 30 = 13
2033
+ assert.deepEqual(cdl.getTotalMediaJMT(), 13);
845
2034
  });
846
2035
 
847
2036
  it('calculates getTotalMediaJMT correctly when it is greater than MAX_INTEGER', () => {
848
- cdl.saveTimestamp({
849
- key: 'internal.client.meeting.click.joinbutton',
850
- value: 5,
851
- });
852
- cdl.saveTimestamp({
853
- key: 'internal.client.meeting.interstitial-window.showed',
854
- value: 8,
855
- });
856
- cdl.saveTimestamp({
857
- key: 'internal.client.interstitial-window.click.joinbutton',
858
- value: 10,
859
- });
860
- cdl.saveTimestamp({
861
- key: 'client.locus.join.request',
862
- value: 12,
863
- });
864
- cdl.saveTimestamp({
865
- key: 'client.locus.join.response',
866
- value: 2147483700,
867
- });
868
- cdl.saveTimestamp({
869
- key: 'client.lobby.exited',
870
- value: 2147483800,
871
- });
872
- cdl.saveTimestamp({
873
- key: 'client.ice.start',
874
- value: 30,
875
- });
876
- cdl.saveTimestamp({
877
- key: 'client.ice.end',
878
- value: 100,
879
- });
2037
+ cdl.saveLatency('internal.click.to.interstitial', 5);
2038
+ cdl.saveTimestamp({key: 'internal.client.meeting.interstitial-window.showed', value: 10});
2039
+ cdl.saveTimestamp({key: 'internal.client.interstitial-window.click.joinbutton', value: 10});
2040
+ cdl.saveTimestamp({key: 'client.media-engine.ready', value: 4294967400});
2041
+ cdl.saveTimestamp({key: 'client.lobby.entered', value: 28});
2042
+ cdl.saveTimestamp({key: 'client.lobby.exited', value: 30});
880
2043
  assert.deepEqual(cdl.getTotalMediaJMT(), 2147483647);
881
2044
  });
882
2045
 
883
- it('calculates getTotalMediaJMT correctly with allowMediaInLobby true', () => {
884
- cdl.saveTimestamp({
885
- key: 'internal.client.meeting.click.joinbutton',
886
- value: 5,
887
- options: {meetingId: 'meeting-id'},
888
- });
889
- cdl.saveTimestamp({
890
- key: 'internal.client.meeting.interstitial-window.showed',
891
- value: 8,
892
- });
893
- cdl.saveTimestamp({
894
- key: 'internal.client.interstitial-window.click.joinbutton',
895
- value: 10,
896
- });
897
- cdl.saveTimestamp({
898
- key: 'client.locus.join.request',
899
- value: 12,
900
- });
901
- cdl.saveTimestamp({
902
- key: 'client.locus.join.response',
903
- value: 20,
904
- });
905
- cdl.saveTimestamp({
906
- key: 'client.lobby.exited',
907
- value: 24,
908
- });
909
- cdl.saveTimestamp({
910
- key: 'client.ice.start',
911
- value: 30,
912
- });
913
- cdl.saveTimestamp({
914
- key: 'client.ice.end',
915
- value: 40,
916
- });
917
- assert.deepEqual(cdl.getTotalMediaJMT(), 31);
2046
+ it('returns undefined for getTotalMediaJMT when media-engine.ready is missing', () => {
2047
+ cdl.saveLatency('internal.click.to.interstitial', 3);
2048
+ cdl.saveTimestamp({key: 'internal.client.meeting.interstitial-window.showed', value: 8});
2049
+ cdl.saveTimestamp({key: 'internal.client.interstitial-window.click.joinbutton', value: 10});
2050
+ cdl.saveTimestamp({key: 'client.locus.join.response', value: 20});
2051
+ assert.deepEqual(cdl.getTotalMediaJMT(), undefined);
918
2052
  });
919
2053
 
920
- it('calculates getTotalMediaJMT correctly with allowMediaInLobby true and it is greater than MAX_INTEGER', () => {
921
- cdl.saveTimestamp({
922
- key: 'internal.client.meeting.click.joinbutton',
923
- value: 5,
924
- options: {meetingId: 'meeting-id'},
925
- });
926
- cdl.saveTimestamp({
927
- key: 'internal.client.meeting.interstitial-window.showed',
928
- value: 100,
929
- });
930
- cdl.saveTimestamp({
931
- key: 'internal.client.interstitial-window.click.joinbutton',
932
- value: 1000,
933
- });
934
- cdl.saveTimestamp({
935
- key: 'client.locus.join.request',
936
- value: 2000,
937
- });
938
- cdl.saveTimestamp({
939
- key: 'client.locus.join.response',
940
- value: 2147483700,
941
- });
942
- cdl.saveTimestamp({
943
- key: 'client.lobby.exited',
944
- value: 2147483800,
945
- });
946
- cdl.saveTimestamp({
947
- key: 'client.ice.start',
948
- value: 2147483900,
949
- });
950
- cdl.saveTimestamp({
951
- key: 'client.ice.end',
952
- value: 4294967400,
953
- });
954
- assert.deepEqual(cdl.getTotalMediaJMT(), 2147483647);
2054
+ it('calculates getTotalMediaJMT correctly when there is no lobby and stayLobbyTime defaults to 0', () => {
2055
+ cdl.saveLatency('internal.click.to.interstitial', 3);
2056
+ // clickToInterstitial = 3
2057
+ cdl.saveTimestamp({key: 'internal.client.meeting.interstitial-window.showed', value: 8});
2058
+ cdl.saveTimestamp({key: 'internal.client.interstitial-window.click.joinbutton', value: 10});
2059
+ cdl.saveTimestamp({key: 'client.media-engine.ready', value: 50});
2060
+ // interstitialShowedToMediaEngineReady = 50 - 8 = 42
2061
+ // showInterstitialTime = 10 - 8 = 2
2062
+ // no client.lobby.entered → stayLobbyTimeCappedByMediaEngineReady = 0
2063
+ // total = 3 + 42 - 2 - 0 = 43
2064
+ assert.deepEqual(cdl.getTotalMediaJMT(), 43);
955
2065
  });
956
2066
 
957
2067
  it('calculates getTotalMediaJMTWithUserDelay correctly', () => {
958
2068
  cdl.saveLatency('internal.click.to.interstitial.with.user.delay', 7);
959
- cdl.saveTimestamp({
960
- key: 'internal.client.interstitial-window.click.joinbutton',
961
- value: 10,
962
- });
963
- cdl.saveTimestamp({
964
- key: 'client.locus.join.request',
965
- value: 12,
966
- });
967
- cdl.saveTimestamp({
968
- key: 'client.locus.join.response',
969
- value: 20,
970
- });
971
- cdl.saveTimestamp({
972
- key: 'internal.host.meeting.participant.admitted',
973
- value: 24,
974
- });
975
- cdl.saveTimestamp({
976
- key: 'client.ice.start',
977
- value: 30,
978
- });
979
- cdl.saveTimestamp({
980
- key: 'client.ice.end',
981
- value: 40,
982
- });
983
- assert.deepEqual(cdl.getTotalMediaJMTWithUserDelay(), 35);
2069
+ // clickToInterstitialWithUserDelay = 7
2070
+ cdl.saveTimestamp({key: 'internal.client.meeting.interstitial-window.showed', value: 10});
2071
+ cdl.saveTimestamp({key: 'client.media-engine.ready', value: 50});
2072
+ // interstitialShowedToMediaEngineReady = 50 - 10 = 40
2073
+ // total = 7 + 40 = 47
2074
+ assert.deepEqual(cdl.getTotalMediaJMTWithUserDelay(), 47);
984
2075
  });
985
2076
 
986
2077
  it('calculates getTotalMediaJMTWithUserDelay correctly for guest join', () => {
987
- cdl.saveTimestamp({
988
- key: 'internal.client.meeting.click.joinbutton',
989
- value: 5,
990
- });
991
- cdl.saveTimestamp({
992
- key: 'internal.client.meeting.interstitial-window.showed',
993
- value: 8,
994
- });
995
- cdl.saveTimestamp({
996
- key: 'internal.client.interstitial-window.click.joinbutton',
997
- value: 10,
998
- });
999
- cdl.saveTimestamp({
1000
- key: 'client.locus.join.request',
1001
- value: 12,
1002
- });
1003
- cdl.saveTimestamp({
1004
- key: 'client.locus.join.response',
1005
- value: 20,
1006
- });
1007
- cdl.saveTimestamp({
1008
- key: 'internal.host.meeting.participant.admitted',
1009
- value: 24,
1010
- });
1011
- cdl.saveTimestamp({
1012
- key: 'client.ice.start',
1013
- value: 30,
1014
- });
1015
- cdl.saveTimestamp({
1016
- key: 'client.ice.end',
1017
- value: 40,
1018
- });
1019
- assert.deepEqual(cdl.getTotalMediaJMTWithUserDelay(), 31);
2078
+ cdl.saveLatency('internal.click.to.interstitial.with.user.delay', 3);
2079
+ // clickToInterstitialWithUserDelay = 3
2080
+ cdl.saveTimestamp({key: 'internal.client.meeting.interstitial-window.showed', value: 8});
2081
+ cdl.saveTimestamp({key: 'client.media-engine.ready', value: 50});
2082
+ // interstitialShowedToMediaEngineReady = 50 - 8 = 42
2083
+ // total = 3 + 42 = 45
2084
+ assert.deepEqual(cdl.getTotalMediaJMTWithUserDelay(), 45);
2085
+ });
2086
+
2087
+ it('returns undefined for getTotalMediaJMTWithUserDelay when media-engine.ready is missing', () => {
2088
+ cdl.saveLatency('internal.click.to.interstitial.with.user.delay', 7);
2089
+ cdl.saveTimestamp({key: 'internal.client.meeting.interstitial-window.showed', value: 10});
2090
+ assert.deepEqual(cdl.getTotalMediaJMTWithUserDelay(), undefined);
2091
+ });
2092
+
2093
+ it('calculates getTotalMediaJMTWithUserDelay correctly when it is greater than MAX_INTEGER', () => {
2094
+ cdl.saveLatency('internal.click.to.interstitial.with.user.delay', 2147483648);
2095
+ cdl.saveTimestamp({key: 'internal.client.meeting.interstitial-window.showed', value: 10});
2096
+ cdl.saveTimestamp({key: 'client.media-engine.ready', value: 50});
2097
+ assert.deepEqual(cdl.getTotalMediaJMTWithUserDelay(), 2147483647);
1020
2098
  });
1021
2099
 
1022
2100
  it('calculates getJoinConfJMT correctly', () => {
@@ -1062,6 +2140,11 @@ describe('internal-plugin-metrics', () => {
1062
2140
  });
1063
2141
 
1064
2142
  it('calculates getClientJMT correctly', () => {
2143
+ cdl.saveLatency('internal.click.to.interstitial.for.client.jmt', 5);
2144
+ cdl.saveTimestamp({
2145
+ key: 'internal.client.meeting.interstitial-window.showed',
2146
+ value: 1,
2147
+ });
1065
2148
  cdl.saveTimestamp({
1066
2149
  key: 'internal.client.interstitial-window.click.joinbutton',
1067
2150
  value: 2,
@@ -1070,19 +2153,30 @@ describe('internal-plugin-metrics', () => {
1070
2153
  key: 'client.locus.join.request',
1071
2154
  value: 6,
1072
2155
  });
2156
+ // showedToLocusJoinRequest = 6-1 = 5, showInterstitialTime = 2-1 = 1
2157
+ // clickToInterstitialForClientJmt (5) + 5 - 1 = 9
2158
+ assert.deepEqual(cdl.getClientJMT(), 9);
2159
+ });
2160
+
2161
+ it('returns undefined for getClientJMT when clickToInterstitialForClientJmt is missing', () => {
1073
2162
  cdl.saveTimestamp({
1074
- key: 'client.locus.join.response',
1075
- value: 8,
2163
+ key: 'internal.client.meeting.interstitial-window.showed',
2164
+ value: 1,
1076
2165
  });
1077
2166
  cdl.saveTimestamp({
1078
- key: 'client.ice.start',
1079
- value: 10,
2167
+ key: 'internal.client.interstitial-window.click.joinbutton',
2168
+ value: 2,
1080
2169
  });
1081
2170
  cdl.saveTimestamp({
1082
- key: 'client.ice.end',
1083
- value: 11,
2171
+ key: 'client.locus.join.request',
2172
+ value: 6,
1084
2173
  });
1085
- assert.deepEqual(cdl.getClientJMT(), 3);
2174
+ assert.deepEqual(cdl.getClientJMT(), undefined);
2175
+ });
2176
+
2177
+ it('returns undefined for getClientJMT when interstitialJoinToLocusJoinRequest is missing', () => {
2178
+ cdl.saveLatency('internal.click.to.interstitial.for.client.jmt', 5);
2179
+ assert.deepEqual(cdl.getClientJMT(), undefined);
1086
2180
  });
1087
2181
 
1088
2182
  it('calculates getAudioJoinRespRxStart correctly', () => {
@@ -1134,12 +2228,16 @@ describe('internal-plugin-metrics', () => {
1134
2228
  });
1135
2229
 
1136
2230
  it('calculates getInterstitialToMediaOKJMT correctly', () => {
2231
+ cdl.saveTimestamp({
2232
+ key: 'internal.client.meeting.interstitial-window.showed',
2233
+ value: 2,
2234
+ });
1137
2235
  cdl.saveTimestamp({
1138
2236
  key: 'internal.client.interstitial-window.click.joinbutton',
1139
2237
  value: 4,
1140
2238
  });
1141
2239
  cdl.saveTimestamp({
1142
- key: 'client.locus.join.response',
2240
+ key: 'client.lobby.entered',
1143
2241
  value: 10,
1144
2242
  });
1145
2243
  cdl.saveTimestamp({
@@ -1150,21 +2248,28 @@ describe('internal-plugin-metrics', () => {
1150
2248
  key: 'client.ice.end',
1151
2249
  value: 14,
1152
2250
  });
2251
+ // showedToIceEnd = 14-2 = 12, showInterstitialTime = 4-2 = 2
2252
+ // stayLobbyTimeCappedByIceEnd = min(12,14)-10 = 2
2253
+ // result = 12 - 2 - 2 = 8
1153
2254
  assert.deepEqual(cdl.getInterstitialToMediaOKJMT(), 8);
1154
2255
  });
1155
2256
 
1156
2257
  it('calculates getInterstitialToMediaOKJMT correctly when it is greater than MAX_INTEGER', () => {
2258
+ cdl.saveTimestamp({
2259
+ key: 'internal.client.meeting.interstitial-window.showed',
2260
+ value: 4,
2261
+ });
1157
2262
  cdl.saveTimestamp({
1158
2263
  key: 'internal.client.interstitial-window.click.joinbutton',
1159
2264
  value: 4,
1160
2265
  });
1161
2266
  cdl.saveTimestamp({
1162
- key: 'client.locus.join.response',
2267
+ key: 'client.lobby.entered',
1163
2268
  value: 10,
1164
2269
  });
1165
2270
  cdl.saveTimestamp({
1166
2271
  key: 'client.lobby.exited',
1167
- value: 12,
2272
+ value: 10,
1168
2273
  });
1169
2274
  cdl.saveTimestamp({
1170
2275
  key: 'client.ice.end',
@@ -1174,6 +2279,29 @@ describe('internal-plugin-metrics', () => {
1174
2279
  });
1175
2280
 
1176
2281
  it('calculates getInterstitialToMediaOKJMT correctly without lobby', () => {
2282
+ cdl.saveTimestamp({
2283
+ key: 'internal.client.meeting.interstitial-window.showed',
2284
+ value: 2,
2285
+ });
2286
+ cdl.saveTimestamp({
2287
+ key: 'internal.client.interstitial-window.click.joinbutton',
2288
+ value: 4,
2289
+ });
2290
+ cdl.saveTimestamp({
2291
+ key: 'client.ice.end',
2292
+ value: 14,
2293
+ });
2294
+ // showedToIceEnd = 14-2 = 12, showInterstitialTime = 4-2 = 2
2295
+ // stayLobbyTimeCappedByIceEnd = 0 (no lobby)
2296
+ // result = 12 - 2 - 0 = 10
2297
+ assert.deepEqual(cdl.getInterstitialToMediaOKJMT(), 10);
2298
+ });
2299
+
2300
+ it('calculates getInterstitialToMediaOKJMT correctly when there is no lobby and stayLobbyTime defaults to 0', () => {
2301
+ cdl.saveTimestamp({
2302
+ key: 'internal.client.meeting.interstitial-window.showed',
2303
+ value: 2,
2304
+ });
1177
2305
  cdl.saveTimestamp({
1178
2306
  key: 'internal.client.interstitial-window.click.joinbutton',
1179
2307
  value: 4,
@@ -1182,6 +2310,9 @@ describe('internal-plugin-metrics', () => {
1182
2310
  key: 'client.ice.end',
1183
2311
  value: 14,
1184
2312
  });
2313
+ // showedToIceEnd = 14-2 = 12, showInterstitialTime = 4-2 = 2
2314
+ // stayLobbyTimeCappedByIceEnd = 0 (no lobby)
2315
+ // result = 12 - 2 - 0 = 10
1185
2316
  assert.deepEqual(cdl.getInterstitialToMediaOKJMT(), 10);
1186
2317
  });
1187
2318