@webex/internal-plugin-metrics 3.8.1-web-workers-keepalive.1 → 3.9.0-multipleLLM.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js +44 -15
- package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js.map +1 -1
- package/dist/call-diagnostic/call-diagnostic-metrics.js +160 -27
- package/dist/call-diagnostic/call-diagnostic-metrics.js.map +1 -1
- package/dist/call-diagnostic/call-diagnostic-metrics.util.js +15 -0
- package/dist/call-diagnostic/call-diagnostic-metrics.util.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/metrics.js +1 -1
- package/dist/metrics.types.js.map +1 -1
- package/dist/types/call-diagnostic/call-diagnostic-metrics-latencies.d.ts +8 -1
- package/dist/types/call-diagnostic/call-diagnostic-metrics.d.ts +35 -3
- package/dist/types/index.d.ts +2 -2
- package/dist/types/metrics.types.d.ts +2 -1
- package/package.json +11 -12
- package/src/call-diagnostic/call-diagnostic-metrics-latencies.ts +47 -15
- package/src/call-diagnostic/call-diagnostic-metrics.ts +149 -0
- package/src/call-diagnostic/call-diagnostic-metrics.util.ts +14 -0
- package/src/index.ts +2 -0
- package/src/metrics.types.ts +4 -1
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts +88 -0
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts +439 -1
- package/test/unit/spec/prelogin-metrics-batcher.ts +71 -3
package/src/index.ts
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
SubmitBusinessEvent,
|
|
19
19
|
SubmitMQE,
|
|
20
20
|
PreComputedLatencies,
|
|
21
|
+
SubmitFeatureEvent,
|
|
21
22
|
} from './metrics.types';
|
|
22
23
|
import * as CALL_DIAGNOSTIC_CONFIG from './call-diagnostic/config';
|
|
23
24
|
import * as CallDiagnosticUtils from './call-diagnostic/call-diagnostic-metrics.util';
|
|
@@ -61,4 +62,5 @@ export type {
|
|
|
61
62
|
SubmitOperationalEvent,
|
|
62
63
|
SubmitBusinessEvent,
|
|
63
64
|
PreComputedLatencies,
|
|
65
|
+
SubmitFeatureEvent,
|
|
64
66
|
};
|
package/src/metrics.types.ts
CHANGED
|
@@ -138,6 +138,7 @@ export type SubmitClientEventOptions = {
|
|
|
138
138
|
triggeredTime?: string;
|
|
139
139
|
emailInput?: ClientEmailInput;
|
|
140
140
|
userNameInput?: ClientUserNameInput;
|
|
141
|
+
vendorId?: string;
|
|
141
142
|
};
|
|
142
143
|
|
|
143
144
|
export type SubmitMQEOptions = {
|
|
@@ -160,7 +161,9 @@ export type InternalEvent = {
|
|
|
160
161
|
| 'internal.client.meeting.interstitial-window.showed'
|
|
161
162
|
| 'internal.client.interstitial-window.click.joinbutton'
|
|
162
163
|
| 'internal.client.add-media.turn-discovery.start'
|
|
163
|
-
| 'internal.client.add-media.turn-discovery.end'
|
|
164
|
+
| 'internal.client.add-media.turn-discovery.end'
|
|
165
|
+
| 'internal.client.share.initiated'
|
|
166
|
+
| 'internal.client.share.stopped';
|
|
164
167
|
|
|
165
168
|
payload?: never;
|
|
166
169
|
options?: never;
|
|
@@ -130,6 +130,82 @@ describe('internal-plugin-metrics', () => {
|
|
|
130
130
|
assert.deepEqual(res2, undefined);
|
|
131
131
|
});
|
|
132
132
|
|
|
133
|
+
describe('getDiffBetweenTimestamps with clamping', () => {
|
|
134
|
+
it('should return diff without clamping when no clampValues provided', () => {
|
|
135
|
+
cdl.saveTimestamp({key: 'client.alert.displayed', value: 10});
|
|
136
|
+
cdl.saveTimestamp({key: 'client.alert.removed', value: 50});
|
|
137
|
+
const res = cdl.getDiffBetweenTimestamps('client.alert.displayed', 'client.alert.removed');
|
|
138
|
+
assert.deepEqual(res, 40);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('should return diff without clamping when value is within range', () => {
|
|
142
|
+
cdl.saveTimestamp({key: 'client.alert.displayed', value: 10});
|
|
143
|
+
cdl.saveTimestamp({key: 'client.alert.removed', value: 50});
|
|
144
|
+
const res = cdl.getDiffBetweenTimestamps('client.alert.displayed', 'client.alert.removed', {
|
|
145
|
+
minimum: 0,
|
|
146
|
+
maximum: 100
|
|
147
|
+
});
|
|
148
|
+
assert.deepEqual(res, 40);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('should clamp to minimum when diff is below minimum', () => {
|
|
152
|
+
cdl.saveTimestamp({key: 'client.alert.displayed', value: 50});
|
|
153
|
+
cdl.saveTimestamp({key: 'client.alert.removed', value: 45});
|
|
154
|
+
const res = cdl.getDiffBetweenTimestamps('client.alert.displayed', 'client.alert.removed', {
|
|
155
|
+
minimum: 10,
|
|
156
|
+
maximum: 100
|
|
157
|
+
});
|
|
158
|
+
assert.deepEqual(res, 10);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('should clamp to maximum when diff is above maximum', () => {
|
|
162
|
+
cdl.saveTimestamp({key: 'client.alert.displayed', value: 10});
|
|
163
|
+
cdl.saveTimestamp({key: 'client.alert.removed', value: 210});
|
|
164
|
+
const res = cdl.getDiffBetweenTimestamps('client.alert.displayed', 'client.alert.removed', {
|
|
165
|
+
minimum: 0,
|
|
166
|
+
maximum: 100
|
|
167
|
+
});
|
|
168
|
+
assert.deepEqual(res, 100);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('should use default minimum of 0 when only maximum is specified', () => {
|
|
172
|
+
cdl.saveTimestamp({key: 'client.alert.displayed', value: 50});
|
|
173
|
+
cdl.saveTimestamp({key: 'client.alert.removed', value: 45});
|
|
174
|
+
const res = cdl.getDiffBetweenTimestamps('client.alert.displayed', 'client.alert.removed', {
|
|
175
|
+
maximum: 100
|
|
176
|
+
});
|
|
177
|
+
assert.deepEqual(res, 0);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('should not clamp maximum when maximum is undefined', () => {
|
|
181
|
+
cdl.saveTimestamp({key: 'client.alert.displayed', value: 10});
|
|
182
|
+
cdl.saveTimestamp({key: 'client.alert.removed', value: 2000});
|
|
183
|
+
const res = cdl.getDiffBetweenTimestamps('client.alert.displayed', 'client.alert.removed', {
|
|
184
|
+
minimum: 5
|
|
185
|
+
});
|
|
186
|
+
assert.deepEqual(res, 1990);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('should handle negative differences correctly with clamping', () => {
|
|
190
|
+
cdl.saveTimestamp({key: 'client.alert.displayed', value: 100});
|
|
191
|
+
cdl.saveTimestamp({key: 'client.alert.removed', value: 50});
|
|
192
|
+
const res = cdl.getDiffBetweenTimestamps('client.alert.displayed', 'client.alert.removed', {
|
|
193
|
+
minimum: 10,
|
|
194
|
+
maximum: 1000
|
|
195
|
+
});
|
|
196
|
+
assert.deepEqual(res, 10);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('should return undefined when timestamps are missing even with clamping', () => {
|
|
200
|
+
cdl.saveTimestamp({key: 'client.alert.displayed', value: 10});
|
|
201
|
+
const res = cdl.getDiffBetweenTimestamps('client.alert.displayed', 'client.alert.removed', {
|
|
202
|
+
minimum: 0,
|
|
203
|
+
maximum: 100
|
|
204
|
+
});
|
|
205
|
+
assert.deepEqual(res, undefined);
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
|
|
133
209
|
it('calculates getMeetingInfoReqResp correctly', () => {
|
|
134
210
|
cdl.saveTimestamp({key: 'internal.client.meetinginfo.request', value: 10});
|
|
135
211
|
cdl.saveTimestamp({key: 'internal.client.meetinginfo.response', value: 20});
|
|
@@ -918,6 +994,18 @@ describe('internal-plugin-metrics', () => {
|
|
|
918
994
|
assert.deepEqual(cdl.getInterstitialToMediaOKJMT(), 10);
|
|
919
995
|
});
|
|
920
996
|
|
|
997
|
+
it('calculates getShareDuration correctly', () => {
|
|
998
|
+
cdl.saveTimestamp({
|
|
999
|
+
key: 'internal.client.share.initiated',
|
|
1000
|
+
value: 5,
|
|
1001
|
+
});
|
|
1002
|
+
cdl.saveTimestamp({
|
|
1003
|
+
key: 'internal.client.share.stopped',
|
|
1004
|
+
value: 7,
|
|
1005
|
+
});
|
|
1006
|
+
assert.deepEqual(cdl.getShareDuration(), 2);
|
|
1007
|
+
});
|
|
1008
|
+
|
|
921
1009
|
describe('calculates getU2CTime correctly', () => {
|
|
922
1010
|
it('returns undefined when no precomputed value available', () => {
|
|
923
1011
|
assert.deepEqual(cdl.getU2CTime(), undefined);
|
|
@@ -264,6 +264,41 @@ describe('internal-plugin-metrics', () => {
|
|
|
264
264
|
});
|
|
265
265
|
});
|
|
266
266
|
|
|
267
|
+
it('should build origin correctly and vendorId can be passed in options', () => {
|
|
268
|
+
sinon.stub(CallDiagnosticUtils, 'anonymizeIPAddress').returns('1.1.1.1');
|
|
269
|
+
|
|
270
|
+
//@ts-ignore
|
|
271
|
+
const res = cd.getOrigin(
|
|
272
|
+
{
|
|
273
|
+
subClientType: 'WEB_APP',
|
|
274
|
+
clientType: 'TEAMS_CLIENT',
|
|
275
|
+
clientLaunchMethod: 'url-handler',
|
|
276
|
+
vendorId: 'GoogleMeet',
|
|
277
|
+
},
|
|
278
|
+
fakeMeeting.id
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
assert.deepEqual(res, {
|
|
282
|
+
clientInfo: {
|
|
283
|
+
browser: getBrowserName(),
|
|
284
|
+
browserVersion: getBrowserVersion(),
|
|
285
|
+
clientType: 'TEAMS_CLIENT',
|
|
286
|
+
clientVersion: 'webex-js-sdk/webex-version',
|
|
287
|
+
publicNetworkPrefix: '1.1.1.1',
|
|
288
|
+
localNetworkPrefix: '1.1.1.1',
|
|
289
|
+
os: getOSNameInternal(),
|
|
290
|
+
osVersion: getOSVersion() || 'unknown',
|
|
291
|
+
subClientType: 'WEB_APP',
|
|
292
|
+
clientLaunchMethod: 'url-handler',
|
|
293
|
+
vendorId: 'GoogleMeet',
|
|
294
|
+
},
|
|
295
|
+
environment: 'meeting_evn',
|
|
296
|
+
name: 'endpoint',
|
|
297
|
+
networkType: 'unknown',
|
|
298
|
+
userAgent,
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
|
|
267
302
|
it('should build origin correctly with browserLaunchMethod', () => {
|
|
268
303
|
sinon.stub(CallDiagnosticUtils, 'anonymizeIPAddress').returns('1.1.1.1');
|
|
269
304
|
|
|
@@ -2431,7 +2466,7 @@ describe('internal-plugin-metrics', () => {
|
|
|
2431
2466
|
);
|
|
2432
2467
|
});
|
|
2433
2468
|
|
|
2434
|
-
it('should
|
|
2469
|
+
it('should record failure metric when meetingId is provided but meeting is undefined', () => {
|
|
2435
2470
|
webex.meetings.getBasicMeetingInformation = sinon.stub().returns(undefined);
|
|
2436
2471
|
|
|
2437
2472
|
cd.submitClientEvent({name: 'client.alert.displayed', options: {meetingId: 'meetingId'}});
|
|
@@ -2465,6 +2500,228 @@ describe('internal-plugin-metrics', () => {
|
|
|
2465
2500
|
assert.calledWith(cd.submitToCallDiagnosticsPreLogin, testEvent);
|
|
2466
2501
|
assert.notCalled(cd.submitToCallDiagnostics);
|
|
2467
2502
|
});
|
|
2503
|
+
|
|
2504
|
+
describe('Limiting repeated events', () => {
|
|
2505
|
+
beforeEach(() => {
|
|
2506
|
+
cd.clearEventLimits();
|
|
2507
|
+
});
|
|
2508
|
+
|
|
2509
|
+
const createEventLimitRegex = (eventName: string, eventType: string) => {
|
|
2510
|
+
const escapedEventName = eventName.replace(/\./g, '\\.');
|
|
2511
|
+
return new RegExp(`Event limit reached for ${escapedEventName} for ${eventType}`);
|
|
2512
|
+
};
|
|
2513
|
+
|
|
2514
|
+
it('should always send events that are not in the limiting switch cases', () => {
|
|
2515
|
+
const options = {
|
|
2516
|
+
meetingId: fakeMeeting.id,
|
|
2517
|
+
};
|
|
2518
|
+
const submitToCallDiagnosticsStub = sinon.stub(cd, 'submitToCallDiagnostics');
|
|
2519
|
+
|
|
2520
|
+
const baselineCallCount = webex.logger.log.callCount;
|
|
2521
|
+
cd.submitClientEvent({
|
|
2522
|
+
name: 'client.alert.displayed',
|
|
2523
|
+
options,
|
|
2524
|
+
});
|
|
2525
|
+
|
|
2526
|
+
cd.submitClientEvent({
|
|
2527
|
+
name: 'client.alert.displayed',
|
|
2528
|
+
options,
|
|
2529
|
+
});
|
|
2530
|
+
|
|
2531
|
+
cd.submitClientEvent({
|
|
2532
|
+
name: 'client.alert.displayed',
|
|
2533
|
+
options,
|
|
2534
|
+
});
|
|
2535
|
+
|
|
2536
|
+
assert.calledThrice(submitToCallDiagnosticsStub);
|
|
2537
|
+
});
|
|
2538
|
+
|
|
2539
|
+
([
|
|
2540
|
+
['client.media.render.start'],
|
|
2541
|
+
['client.media.render.stop'],
|
|
2542
|
+
['client.media.rx.start'],
|
|
2543
|
+
['client.media.rx.stop'],
|
|
2544
|
+
['client.media.tx.start'],
|
|
2545
|
+
['client.media.tx.stop']
|
|
2546
|
+
] as const).forEach(([name]) => {
|
|
2547
|
+
it(`should only send ${name} once per mediaType`, () => {
|
|
2548
|
+
const options = {
|
|
2549
|
+
meetingId: fakeMeeting.id,
|
|
2550
|
+
};
|
|
2551
|
+
const payload = {
|
|
2552
|
+
mediaType: 'video' as const,
|
|
2553
|
+
};
|
|
2554
|
+
const submitToCallDiagnosticsStub = sinon.stub(cd, 'submitToCallDiagnostics');
|
|
2555
|
+
|
|
2556
|
+
const baselineCallCount = webex.logger.log.callCount;
|
|
2557
|
+
// Send first event
|
|
2558
|
+
cd.submitClientEvent({
|
|
2559
|
+
name,
|
|
2560
|
+
payload,
|
|
2561
|
+
options,
|
|
2562
|
+
});
|
|
2563
|
+
|
|
2564
|
+
assert.calledOnce(submitToCallDiagnosticsStub);
|
|
2565
|
+
submitToCallDiagnosticsStub.resetHistory();
|
|
2566
|
+
|
|
2567
|
+
// Send second event of same type
|
|
2568
|
+
cd.submitClientEvent({
|
|
2569
|
+
name,
|
|
2570
|
+
payload,
|
|
2571
|
+
options,
|
|
2572
|
+
});
|
|
2573
|
+
|
|
2574
|
+
assert.notCalled(submitToCallDiagnosticsStub);
|
|
2575
|
+
assert.calledWith(
|
|
2576
|
+
webex.logger.log,
|
|
2577
|
+
'call-diagnostic-events -> ',
|
|
2578
|
+
sinon.match(createEventLimitRegex(name, 'mediaType video'))
|
|
2579
|
+
);
|
|
2580
|
+
webex.logger.log.resetHistory();
|
|
2581
|
+
|
|
2582
|
+
// Send third event of same type
|
|
2583
|
+
cd.submitClientEvent({
|
|
2584
|
+
name,
|
|
2585
|
+
payload,
|
|
2586
|
+
options,
|
|
2587
|
+
});
|
|
2588
|
+
|
|
2589
|
+
assert.notCalled(submitToCallDiagnosticsStub);
|
|
2590
|
+
assert.neverCalledWithMatch(webex.logger.log,
|
|
2591
|
+
'call-diagnostic-events -> ',
|
|
2592
|
+
sinon.match(createEventLimitRegex(name, 'mediaType video'))
|
|
2593
|
+
);
|
|
2594
|
+
|
|
2595
|
+
// Send fourth event with a different mediaType
|
|
2596
|
+
cd.submitClientEvent({
|
|
2597
|
+
name,
|
|
2598
|
+
payload: {mediaType: 'audio'},
|
|
2599
|
+
options,
|
|
2600
|
+
});
|
|
2601
|
+
|
|
2602
|
+
assert.calledOnce(submitToCallDiagnosticsStub);
|
|
2603
|
+
});
|
|
2604
|
+
|
|
2605
|
+
it(`should handle share media type with shareInstanceId correctly for ${name}`, () => {
|
|
2606
|
+
const options = {
|
|
2607
|
+
meetingId: fakeMeeting.id,
|
|
2608
|
+
};
|
|
2609
|
+
const payload = {
|
|
2610
|
+
mediaType: 'share' as const,
|
|
2611
|
+
shareInstanceId: 'instance-1',
|
|
2612
|
+
};
|
|
2613
|
+
const submitToCallDiagnosticsStub = sinon.stub(cd, 'submitToCallDiagnostics');
|
|
2614
|
+
|
|
2615
|
+
const baselineCallCount = webex.logger.log.callCount;
|
|
2616
|
+
// Send first event
|
|
2617
|
+
cd.submitClientEvent({
|
|
2618
|
+
name,
|
|
2619
|
+
payload,
|
|
2620
|
+
options,
|
|
2621
|
+
});
|
|
2622
|
+
|
|
2623
|
+
// Send second event with same shareInstanceId
|
|
2624
|
+
cd.submitClientEvent({
|
|
2625
|
+
name,
|
|
2626
|
+
payload,
|
|
2627
|
+
options,
|
|
2628
|
+
});
|
|
2629
|
+
|
|
2630
|
+
// Send event with different shareInstanceId
|
|
2631
|
+
cd.submitClientEvent({
|
|
2632
|
+
name,
|
|
2633
|
+
payload: { ...payload, shareInstanceId: 'instance-2' },
|
|
2634
|
+
options,
|
|
2635
|
+
});
|
|
2636
|
+
|
|
2637
|
+
assert.calledTwice(submitToCallDiagnosticsStub);
|
|
2638
|
+
});
|
|
2639
|
+
});
|
|
2640
|
+
|
|
2641
|
+
([
|
|
2642
|
+
['client.roap-message.received'],
|
|
2643
|
+
['client.roap-message.sent']
|
|
2644
|
+
] as const).forEach(([name]) => {
|
|
2645
|
+
it(`should not send third event of same type and not log warning again for ${name}`, () => {
|
|
2646
|
+
const options = {
|
|
2647
|
+
meetingId: fakeMeeting.id,
|
|
2648
|
+
};
|
|
2649
|
+
const payload = {
|
|
2650
|
+
roap: {
|
|
2651
|
+
messageType: 'OFFER' as const,
|
|
2652
|
+
},
|
|
2653
|
+
};
|
|
2654
|
+
const submitToCallDiagnosticsStub = sinon.stub(cd, 'submitToCallDiagnostics');
|
|
2655
|
+
|
|
2656
|
+
// Clear any existing call history to get accurate counts
|
|
2657
|
+
webex.logger.log.resetHistory();
|
|
2658
|
+
|
|
2659
|
+
// Send first event
|
|
2660
|
+
cd.submitClientEvent({
|
|
2661
|
+
name,
|
|
2662
|
+
payload,
|
|
2663
|
+
options,
|
|
2664
|
+
});
|
|
2665
|
+
|
|
2666
|
+
assert.calledOnce(submitToCallDiagnosticsStub);
|
|
2667
|
+
submitToCallDiagnosticsStub.resetHistory();
|
|
2668
|
+
|
|
2669
|
+
// Send second event (should trigger warning)
|
|
2670
|
+
cd.submitClientEvent({
|
|
2671
|
+
name,
|
|
2672
|
+
payload,
|
|
2673
|
+
options,
|
|
2674
|
+
});
|
|
2675
|
+
|
|
2676
|
+
assert.notCalled(submitToCallDiagnosticsStub);
|
|
2677
|
+
assert.calledWith(
|
|
2678
|
+
webex.logger.log,
|
|
2679
|
+
'call-diagnostic-events -> ',
|
|
2680
|
+
sinon.match(createEventLimitRegex(name, 'ROAP type OFFER'))
|
|
2681
|
+
);
|
|
2682
|
+
webex.logger.log.resetHistory();
|
|
2683
|
+
|
|
2684
|
+
cd.submitClientEvent({
|
|
2685
|
+
name,
|
|
2686
|
+
payload,
|
|
2687
|
+
options,
|
|
2688
|
+
});
|
|
2689
|
+
|
|
2690
|
+
assert.notCalled(submitToCallDiagnosticsStub);
|
|
2691
|
+
assert.neverCalledWithMatch(
|
|
2692
|
+
webex.logger.log,
|
|
2693
|
+
'call-diagnostic-events -> ',
|
|
2694
|
+
sinon.match(createEventLimitRegex(name, 'ROAP type OFFER'))
|
|
2695
|
+
);
|
|
2696
|
+
});
|
|
2697
|
+
|
|
2698
|
+
it(`should handle roap.type instead of roap.messageType for ${name}`, () => {
|
|
2699
|
+
const options = {
|
|
2700
|
+
meetingId: fakeMeeting.id,
|
|
2701
|
+
};
|
|
2702
|
+
const payload = {
|
|
2703
|
+
roap: {
|
|
2704
|
+
type: 'ANSWER' as const,
|
|
2705
|
+
},
|
|
2706
|
+
};
|
|
2707
|
+
const submitToCallDiagnosticsStub = sinon.stub(cd, 'submitToCallDiagnostics');
|
|
2708
|
+
|
|
2709
|
+
cd.submitClientEvent({
|
|
2710
|
+
name,
|
|
2711
|
+
payload,
|
|
2712
|
+
options,
|
|
2713
|
+
});
|
|
2714
|
+
|
|
2715
|
+
cd.submitClientEvent({
|
|
2716
|
+
name,
|
|
2717
|
+
payload,
|
|
2718
|
+
options,
|
|
2719
|
+
});
|
|
2720
|
+
|
|
2721
|
+
assert.calledOnce(submitToCallDiagnosticsStub);
|
|
2722
|
+
});
|
|
2723
|
+
});
|
|
2724
|
+
});
|
|
2468
2725
|
});
|
|
2469
2726
|
|
|
2470
2727
|
describe('#submitToCallDiagnostics', () => {
|
|
@@ -4022,5 +4279,186 @@ describe('internal-plugin-metrics', () => {
|
|
|
4022
4279
|
assert.notCalled(submitFeatureEventSpy);
|
|
4023
4280
|
});
|
|
4024
4281
|
});
|
|
4282
|
+
|
|
4283
|
+
describe('#clearEventLimitsForCorrelationId', () => {
|
|
4284
|
+
beforeEach(() => {
|
|
4285
|
+
cd.clearEventLimits();
|
|
4286
|
+
});
|
|
4287
|
+
|
|
4288
|
+
it('should clear event limits for specific correlationId only', () => {
|
|
4289
|
+
// Use the actual correlationIds from our fakeMeeting fixtures
|
|
4290
|
+
const correlationId1 = fakeMeeting.correlationId; // e.g. 'correlationId1'
|
|
4291
|
+
const correlationId2 = fakeMeeting2.correlationId; // e.g. 'correlationId2'
|
|
4292
|
+
const options1 = { meetingId: fakeMeeting.id };
|
|
4293
|
+
const options2 = { meetingId: fakeMeeting2.id };
|
|
4294
|
+
const payload = { mediaType: 'video' as const };
|
|
4295
|
+
|
|
4296
|
+
// Set up events for both correlations to trigger limits
|
|
4297
|
+
cd.submitClientEvent({ name: 'client.media.render.start', payload, options: options1 });
|
|
4298
|
+
cd.submitClientEvent({ name: 'client.media.render.start', payload, options: options2 });
|
|
4299
|
+
cd.submitClientEvent({ name: 'client.media.render.start', payload, options: options1 });
|
|
4300
|
+
cd.submitClientEvent({ name: 'client.media.render.start', payload, options: options2 });
|
|
4301
|
+
assert.isTrue(cd.eventLimitTracker.size > 0);
|
|
4302
|
+
assert.isTrue(cd.eventLimitWarningsLogged.size > 0);
|
|
4303
|
+
|
|
4304
|
+
// Clear limits for only correlationId1 (present)
|
|
4305
|
+
cd.clearEventLimitsForCorrelationId(correlationId1);
|
|
4306
|
+
|
|
4307
|
+
const remainingTrackerKeys = Array.from(cd.eventLimitTracker.keys());
|
|
4308
|
+
const remainingWarningKeys = Array.from(cd.eventLimitWarningsLogged.keys());
|
|
4309
|
+
|
|
4310
|
+
// Should have no keys with correlationId1
|
|
4311
|
+
assert.isFalse(remainingTrackerKeys.some(key => key.split(':')[1] === correlationId1));
|
|
4312
|
+
assert.isFalse(remainingWarningKeys.some(key => key.split(':')[1] === correlationId1));
|
|
4313
|
+
|
|
4314
|
+
// Should still have keys with correlationId2
|
|
4315
|
+
assert.isTrue(remainingTrackerKeys.some(key => key.split(':')[1] === correlationId2));
|
|
4316
|
+
assert.isTrue(remainingWarningKeys.some(key => key.split(':')[1] === correlationId2));
|
|
4317
|
+
});
|
|
4318
|
+
|
|
4319
|
+
it('should handle empty correlationId gracefully', () => {
|
|
4320
|
+
const options = { meetingId: fakeMeeting.id };
|
|
4321
|
+
const payload = { mediaType: 'video' as const };
|
|
4322
|
+
|
|
4323
|
+
// Set up some tracking data
|
|
4324
|
+
cd.submitClientEvent({
|
|
4325
|
+
name: 'client.media.render.start',
|
|
4326
|
+
payload,
|
|
4327
|
+
options,
|
|
4328
|
+
});
|
|
4329
|
+
|
|
4330
|
+
cd.submitClientEvent({
|
|
4331
|
+
name: 'client.media.render.start',
|
|
4332
|
+
payload,
|
|
4333
|
+
options,
|
|
4334
|
+
});
|
|
4335
|
+
|
|
4336
|
+
const initialTrackerSize = cd.eventLimitTracker.size;
|
|
4337
|
+
const initialWarningsSize = cd.eventLimitWarningsLogged.size;
|
|
4338
|
+
|
|
4339
|
+
// Should not clear anything for empty correlationId
|
|
4340
|
+
cd.clearEventLimitsForCorrelationId('');
|
|
4341
|
+
cd.clearEventLimitsForCorrelationId(null as any);
|
|
4342
|
+
cd.clearEventLimitsForCorrelationId(undefined as any);
|
|
4343
|
+
|
|
4344
|
+
assert.equal(cd.eventLimitTracker.size, initialTrackerSize);
|
|
4345
|
+
assert.equal(cd.eventLimitWarningsLogged.size, initialWarningsSize);
|
|
4346
|
+
});
|
|
4347
|
+
|
|
4348
|
+
it('should handle non-existent correlationId gracefully', () => {
|
|
4349
|
+
const options = { meetingId: fakeMeeting.id };
|
|
4350
|
+
const payload = { mediaType: 'video' as const };
|
|
4351
|
+
|
|
4352
|
+
// Set up some tracking data
|
|
4353
|
+
cd.submitClientEvent({
|
|
4354
|
+
name: 'client.media.render.start',
|
|
4355
|
+
payload,
|
|
4356
|
+
options,
|
|
4357
|
+
});
|
|
4358
|
+
|
|
4359
|
+
const initialTrackerSize = cd.eventLimitTracker.size;
|
|
4360
|
+
const initialWarningsSize = cd.eventLimitWarningsLogged.size;
|
|
4361
|
+
|
|
4362
|
+
// Should not clear anything for non-existent correlationId
|
|
4363
|
+
cd.clearEventLimitsForCorrelationId('nonExistentCorrelationId');
|
|
4364
|
+
|
|
4365
|
+
assert.equal(cd.eventLimitTracker.size, initialTrackerSize);
|
|
4366
|
+
assert.equal(cd.eventLimitWarningsLogged.size, initialWarningsSize);
|
|
4367
|
+
});
|
|
4368
|
+
|
|
4369
|
+
it('should clear multiple event types for the same correlationId', () => {
|
|
4370
|
+
const correlationId = fakeMeeting.correlationId;
|
|
4371
|
+
const options = { meetingId: fakeMeeting.id };
|
|
4372
|
+
const videoPayload = { mediaType: 'video' as const };
|
|
4373
|
+
const audioPayload = { mediaType: 'audio' as const };
|
|
4374
|
+
const roapPayload = { roap: { messageType: 'OFFER' as const } };
|
|
4375
|
+
|
|
4376
|
+
// Set up multiple event types for the same correlation
|
|
4377
|
+
cd.submitClientEvent({
|
|
4378
|
+
name: 'client.media.render.start',
|
|
4379
|
+
payload: videoPayload,
|
|
4380
|
+
options,
|
|
4381
|
+
});
|
|
4382
|
+
|
|
4383
|
+
cd.submitClientEvent({
|
|
4384
|
+
name: 'client.media.render.start',
|
|
4385
|
+
payload: audioPayload,
|
|
4386
|
+
options,
|
|
4387
|
+
});
|
|
4388
|
+
|
|
4389
|
+
cd.submitClientEvent({
|
|
4390
|
+
name: 'client.roap-message.sent',
|
|
4391
|
+
payload: roapPayload,
|
|
4392
|
+
options,
|
|
4393
|
+
});
|
|
4394
|
+
|
|
4395
|
+
// Trigger limits
|
|
4396
|
+
cd.submitClientEvent({
|
|
4397
|
+
name: 'client.media.render.start',
|
|
4398
|
+
payload: videoPayload,
|
|
4399
|
+
options,
|
|
4400
|
+
});
|
|
4401
|
+
|
|
4402
|
+
cd.submitClientEvent({
|
|
4403
|
+
name: 'client.media.render.start',
|
|
4404
|
+
payload: audioPayload,
|
|
4405
|
+
options,
|
|
4406
|
+
});
|
|
4407
|
+
|
|
4408
|
+
cd.submitClientEvent({
|
|
4409
|
+
name: 'client.roap-message.sent',
|
|
4410
|
+
payload: roapPayload,
|
|
4411
|
+
options,
|
|
4412
|
+
});
|
|
4413
|
+
|
|
4414
|
+
assert.isTrue(cd.eventLimitTracker.size > 0);
|
|
4415
|
+
assert.isTrue(cd.eventLimitWarningsLogged.size > 0);
|
|
4416
|
+
|
|
4417
|
+
// Clear all limits for this correlationId
|
|
4418
|
+
cd.clearEventLimitsForCorrelationId(correlationId);
|
|
4419
|
+
|
|
4420
|
+
// Should clear all tracking data for this correlationId
|
|
4421
|
+
assert.equal(cd.eventLimitTracker.size, 0);
|
|
4422
|
+
assert.equal(cd.eventLimitWarningsLogged.size, 0);
|
|
4423
|
+
});
|
|
4424
|
+
|
|
4425
|
+
it('should allow events to be sent again after clearing limits for correlationId', () => {
|
|
4426
|
+
const correlationId = fakeMeeting.correlationId;
|
|
4427
|
+
const options = { meetingId: fakeMeeting.id };
|
|
4428
|
+
const payload = { mediaType: 'video' as const };
|
|
4429
|
+
const submitToCallDiagnosticsStub = sinon.stub(cd, 'submitToCallDiagnostics');
|
|
4430
|
+
|
|
4431
|
+
// Send first event (should succeed)
|
|
4432
|
+
cd.submitClientEvent({
|
|
4433
|
+
name: 'client.media.render.start',
|
|
4434
|
+
payload,
|
|
4435
|
+
options,
|
|
4436
|
+
});
|
|
4437
|
+
|
|
4438
|
+
assert.calledOnce(submitToCallDiagnosticsStub);
|
|
4439
|
+
submitToCallDiagnosticsStub.resetHistory();
|
|
4440
|
+
|
|
4441
|
+
// Send second event (should be blocked)
|
|
4442
|
+
cd.submitClientEvent({
|
|
4443
|
+
name: 'client.media.render.start',
|
|
4444
|
+
payload,
|
|
4445
|
+
options,
|
|
4446
|
+
});
|
|
4447
|
+
|
|
4448
|
+
assert.notCalled(submitToCallDiagnosticsStub);
|
|
4449
|
+
|
|
4450
|
+
// Clear limits for this correlationId
|
|
4451
|
+
cd.clearEventLimitsForCorrelationId(correlationId);
|
|
4452
|
+
|
|
4453
|
+
// Send event again (should succeed after clearing)
|
|
4454
|
+
cd.submitClientEvent({
|
|
4455
|
+
name: 'client.media.render.start',
|
|
4456
|
+
payload,
|
|
4457
|
+
options,
|
|
4458
|
+
});
|
|
4459
|
+
|
|
4460
|
+
assert.calledOnce(submitToCallDiagnosticsStub);
|
|
4461
|
+
});
|
|
4462
|
+
});
|
|
4025
4463
|
});
|
|
4026
4464
|
});
|