@webex/internal-plugin-metrics 3.12.0-next.34 → 3.12.0-next.35

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.
@@ -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