@webex/plugin-meetings 3.12.0-next.88 → 3.12.0-next.89

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.
@@ -13,11 +13,15 @@ import {
13
13
  import testUtils from '../../../utils/testUtils';
14
14
  import {Defer} from '@webex/common';
15
15
  import {IP_VERSION} from '../../../../src/constants';
16
+ import Metrics from '../../../../src/metrics';
17
+ import BEHAVIORAL_METRICS from '../../../../src/metrics/constants';
16
18
 
17
19
  describe('LocusMediaRequest.send()', () => {
18
20
  let locusMediaRequest: LocusMediaRequest;
19
21
  let webexRequestStub;
20
22
  let mockWebex;
23
+ let sendBehavioralMetricStub;
24
+ const waitForSelfUrlChange = () => Promise.resolve();
21
25
 
22
26
  const fakeLocusResponse = {
23
27
  locus: {something: 'whatever'},
@@ -50,8 +54,16 @@ describe('LocusMediaRequest.send()', () => {
50
54
  },
51
55
  ipver: IP_VERSION.only_ipv4,
52
56
  reachability: {
53
- version: '1',
54
- result: 'some fake reachability result',
57
+ version: 1,
58
+ result: {
59
+ usedDiscoveryOptions: {
60
+ 'early-call-min-clusters': 3,
61
+ },
62
+ metrics: {
63
+ 'total-duration-ms': 10,
64
+ },
65
+ tests: {},
66
+ },
55
67
  },
56
68
  },
57
69
  };
@@ -78,8 +90,16 @@ describe('LocusMediaRequest.send()', () => {
78
90
  timeShot: '2023-05-23T08:03:49Z',
79
91
  },
80
92
  reachability: {
81
- version: '1',
82
- result: 'some fake reachability result',
93
+ version: 1,
94
+ result: {
95
+ usedDiscoveryOptions: {
96
+ 'early-call-min-clusters': 3,
97
+ },
98
+ metrics: {
99
+ 'total-duration-ms': 10,
100
+ },
101
+ tests: {},
102
+ },
83
103
  },
84
104
  },
85
105
  };
@@ -132,6 +152,7 @@ describe('LocusMediaRequest.send()', () => {
132
152
  submitClientEvent: sinon.stub(),
133
153
  },
134
154
  };
155
+ sendBehavioralMetricStub = sinon.stub(Metrics, 'sendBehavioralMetric');
135
156
 
136
157
  locusMediaRequest = new LocusMediaRequest(
137
158
  {
@@ -143,6 +164,8 @@ describe('LocusMediaRequest.send()', () => {
143
164
  correlationId: 'correlationId',
144
165
  meetingId: 'meetingId',
145
166
  preferTranscoding: true,
167
+ getCurrentSelfUrl: () => undefined,
168
+ waitForSelfUrlChange,
146
169
  },
147
170
  {
148
171
  parent: mockWebex,
@@ -151,6 +174,10 @@ describe('LocusMediaRequest.send()', () => {
151
174
  webexRequestStub = sinon.stub(locusMediaRequest, 'request').resolves(fakeLocusResponse);
152
175
  });
153
176
 
177
+ afterEach(() => {
178
+ sendBehavioralMetricStub.restore();
179
+ });
180
+
154
181
  const sendLocalMute = (muteOptions, overrides = {}) =>
155
182
  locusMediaRequest.send({...exampleLocalMuteRequestBody, ...overrides, muteOptions});
156
183
 
@@ -167,6 +194,7 @@ describe('LocusMediaRequest.send()', () => {
167
194
 
168
195
  webexRequestStub.resetHistory();
169
196
  mockWebex.internal.newMetrics.submitClientEvent.resetHistory();
197
+ sendBehavioralMetricStub.resetHistory();
170
198
  };
171
199
 
172
200
  it('sends a roap message', async () => {
@@ -183,6 +211,276 @@ describe('LocusMediaRequest.send()', () => {
183
211
  });
184
212
  });
185
213
 
214
+ it('uses the latest resolved selfUrl when a queued roap message is sent', async () => {
215
+ let currentSelfUrl = 'oldSelfUrl';
216
+ locusMediaRequest = new LocusMediaRequest(
217
+ {
218
+ device: {
219
+ url: 'deviceUrl',
220
+ deviceType: 'deviceType',
221
+ regionCode: 'regionCode',
222
+ },
223
+ correlationId: 'correlationId',
224
+ meetingId: 'meetingId',
225
+ preferTranscoding: true,
226
+ getCurrentSelfUrl: () => currentSelfUrl,
227
+ waitForSelfUrlChange,
228
+ },
229
+ {
230
+ parent: mockWebex,
231
+ }
232
+ );
233
+ webexRequestStub = sinon.stub(locusMediaRequest, 'request').resolves(fakeLocusResponse);
234
+
235
+ const request = cloneDeep(exampleRoapRequestBody);
236
+
237
+ request.selfUrl = 'oldSelfUrl';
238
+ currentSelfUrl = 'newSelfUrl';
239
+
240
+ const result = await locusMediaRequest.send(request);
241
+
242
+ assert.equal(result, fakeLocusResponse);
243
+ assert.calledOnce(webexRequestStub);
244
+ assert.equal(webexRequestStub.getCall(0).args[0].uri, 'newSelfUrl/media');
245
+ assert.calledOnceWithExactly(
246
+ sendBehavioralMetricStub,
247
+ BEHAVIORAL_METRICS.LOCUS_MEDIA_REQUEST_RETRY,
248
+ {
249
+ correlation_id: 'correlationId',
250
+ reason: 'selfUrlUpdatedBeforeMediaRequest',
251
+ }
252
+ );
253
+ });
254
+
255
+ it('retries a roap message with the latest resolved selfUrl after a conflict', async () => {
256
+ let currentSelfUrl = 'oldSelfUrl';
257
+ const conflictError = {statusCode: 409, message: 'conflict'};
258
+
259
+ locusMediaRequest = new LocusMediaRequest(
260
+ {
261
+ device: {
262
+ url: 'deviceUrl',
263
+ deviceType: 'deviceType',
264
+ regionCode: 'regionCode',
265
+ },
266
+ correlationId: 'correlationId',
267
+ meetingId: 'meetingId',
268
+ preferTranscoding: true,
269
+ getCurrentSelfUrl: () => currentSelfUrl,
270
+ waitForSelfUrlChange,
271
+ },
272
+ {
273
+ parent: mockWebex,
274
+ }
275
+ );
276
+ webexRequestStub = sinon.stub(locusMediaRequest, 'request');
277
+ webexRequestStub.onFirstCall().callsFake(() => {
278
+ currentSelfUrl = 'newSelfUrl';
279
+
280
+ return Promise.reject(conflictError);
281
+ });
282
+ webexRequestStub.onSecondCall().resolves(fakeLocusResponse);
283
+
284
+ const request = cloneDeep(exampleRoapRequestBody);
285
+
286
+ request.selfUrl = 'oldSelfUrl';
287
+ request.roapMessage.messageType = 'ANSWER';
288
+
289
+ const result = await locusMediaRequest.send(request);
290
+
291
+ assert.equal(result, fakeLocusResponse);
292
+ assert.calledTwice(webexRequestStub);
293
+ assert.equal(webexRequestStub.getCall(0).args[0].uri, 'oldSelfUrl/media');
294
+ assert.equal(webexRequestStub.getCall(1).args[0].uri, 'newSelfUrl/media');
295
+ assert.calledWith(sendBehavioralMetricStub, BEHAVIORAL_METRICS.LOCUS_MEDIA_REQUEST_RETRY, {
296
+ correlation_id: 'correlationId',
297
+ reason: 'selfUrlUpdatedBeforeMediaRequest',
298
+ });
299
+ // The updated selfUrl is detected twice: once while deciding to retry and
300
+ // again when the request is re-resolved for the actual re-send.
301
+ assert.calledTwice(sendBehavioralMetricStub);
302
+ });
303
+
304
+ it('does not retry a roap conflict when the resolved selfUrl has not changed', async () => {
305
+ locusMediaRequest = new LocusMediaRequest(
306
+ {
307
+ device: {
308
+ url: 'deviceUrl',
309
+ deviceType: 'deviceType',
310
+ regionCode: 'regionCode',
311
+ },
312
+ correlationId: 'correlationId',
313
+ meetingId: 'meetingId',
314
+ preferTranscoding: true,
315
+ getCurrentSelfUrl: () => 'sameSelfUrl',
316
+ waitForSelfUrlChange,
317
+ },
318
+ {
319
+ parent: mockWebex,
320
+ }
321
+ );
322
+ webexRequestStub = sinon.stub(locusMediaRequest, 'request').rejects({
323
+ statusCode: 409,
324
+ message: 'conflict',
325
+ });
326
+
327
+ const request = cloneDeep(exampleRoapRequestBody);
328
+
329
+ request.selfUrl = 'sameSelfUrl';
330
+ request.roapMessage.messageType = 'ANSWER';
331
+
332
+ await assert.isRejected(locusMediaRequest.send(request));
333
+
334
+ assert.calledOnce(webexRequestStub);
335
+ assert.equal(webexRequestStub.getCall(0).args[0].uri, 'sameSelfUrl/media');
336
+ assert.calledWith(sendBehavioralMetricStub, BEHAVIORAL_METRICS.LOCUS_MEDIA_REQUEST_RETRY, {
337
+ correlation_id: 'correlationId',
338
+ reason: 'selfUrlNotChangedAfterWait',
339
+ retryAttempt: 0,
340
+ roapMessageType: 'ANSWER',
341
+ });
342
+ });
343
+
344
+ it('retries a roap conflict when selfUrl changes after waiting', async () => {
345
+ let currentSelfUrl = 'sameSelfUrl';
346
+ const waitForSelfUrlChangeAndUpdate = async () => {
347
+ currentSelfUrl = 'newSelfUrlAfterWait';
348
+ };
349
+ locusMediaRequest = new LocusMediaRequest(
350
+ {
351
+ device: {
352
+ url: 'deviceUrl',
353
+ deviceType: 'deviceType',
354
+ regionCode: 'regionCode',
355
+ },
356
+ correlationId: 'correlationId',
357
+ meetingId: 'meetingId',
358
+ preferTranscoding: true,
359
+ getCurrentSelfUrl: () => currentSelfUrl,
360
+ waitForSelfUrlChange: waitForSelfUrlChangeAndUpdate,
361
+ },
362
+ {
363
+ parent: mockWebex,
364
+ }
365
+ );
366
+ webexRequestStub = sinon.stub(locusMediaRequest, 'request');
367
+ webexRequestStub.onFirstCall().rejects({statusCode: 409, message: 'conflict'});
368
+ webexRequestStub.onSecondCall().resolves(fakeLocusResponse);
369
+
370
+ const request = cloneDeep(exampleRoapRequestBody);
371
+
372
+ request.selfUrl = 'sameSelfUrl';
373
+ request.roapMessage.messageType = 'ANSWER';
374
+
375
+ const result = await locusMediaRequest.send(request);
376
+
377
+ assert.equal(result, fakeLocusResponse);
378
+ assert.calledTwice(webexRequestStub);
379
+ assert.equal(webexRequestStub.getCall(0).args[0].uri, 'sameSelfUrl/media');
380
+ assert.equal(webexRequestStub.getCall(1).args[0].uri, 'newSelfUrlAfterWait/media');
381
+ assert.calledWith(sendBehavioralMetricStub, BEHAVIORAL_METRICS.LOCUS_MEDIA_REQUEST_RETRY, {
382
+ correlation_id: 'correlationId',
383
+ reason: 'selfUrlChangedAfterWait',
384
+ retryAttempt: 0,
385
+ roapMessageType: 'ANSWER',
386
+ });
387
+ });
388
+
389
+ it('retries a local mute request with the latest resolved selfUrl after a conflict', async () => {
390
+ let currentSelfUrl = 'oldSelfUrl';
391
+
392
+ locusMediaRequest = new LocusMediaRequest(
393
+ {
394
+ device: {
395
+ url: 'deviceUrl',
396
+ deviceType: 'deviceType',
397
+ regionCode: 'regionCode',
398
+ },
399
+ correlationId: 'correlationId',
400
+ meetingId: 'meetingId',
401
+ preferTranscoding: true,
402
+ getCurrentSelfUrl: () => currentSelfUrl,
403
+ waitForSelfUrlChange,
404
+ },
405
+ {
406
+ parent: mockWebex,
407
+ }
408
+ );
409
+ webexRequestStub = sinon.stub(locusMediaRequest, 'request').resolves(fakeLocusResponse);
410
+
411
+ // Create the confluence on this instance, otherwise the LocalMute request
412
+ // short-circuits (resolves without sending) while confluence is "not created".
413
+ await ensureConfluenceCreated();
414
+
415
+ webexRequestStub.reset();
416
+ webexRequestStub.onFirstCall().callsFake(() => {
417
+ currentSelfUrl = 'newSelfUrl';
418
+
419
+ return Promise.reject({statusCode: 409, message: 'conflict'});
420
+ });
421
+ webexRequestStub.onSecondCall().resolves(fakeLocusResponse);
422
+
423
+ const result = await locusMediaRequest.send({
424
+ ...exampleLocalMuteRequestBody,
425
+ selfUrl: 'oldSelfUrl',
426
+ muteOptions: {audioMuted: false, videoMuted: true},
427
+ });
428
+
429
+ assert.equal(result, fakeLocusResponse);
430
+ assert.calledTwice(webexRequestStub);
431
+ assert.equal(webexRequestStub.getCall(0).args[0].uri, 'oldSelfUrl/media');
432
+ assert.equal(webexRequestStub.getCall(1).args[0].uri, 'newSelfUrl/media');
433
+ });
434
+
435
+ it('stops retrying roap conflicts after the selfUrl retry limit', async () => {
436
+ let currentSelfUrl = 'oldSelfUrl';
437
+ locusMediaRequest = new LocusMediaRequest(
438
+ {
439
+ device: {
440
+ url: 'deviceUrl',
441
+ deviceType: 'deviceType',
442
+ regionCode: 'regionCode',
443
+ },
444
+ correlationId: 'correlationId',
445
+ meetingId: 'meetingId',
446
+ preferTranscoding: true,
447
+ getCurrentSelfUrl: () => currentSelfUrl,
448
+ waitForSelfUrlChange,
449
+ },
450
+ {
451
+ parent: mockWebex,
452
+ }
453
+ );
454
+ webexRequestStub = sinon.stub(locusMediaRequest, 'request');
455
+ webexRequestStub.onFirstCall().callsFake(() => {
456
+ currentSelfUrl = 'secondSelfUrl';
457
+
458
+ return Promise.reject({statusCode: 409, message: 'conflict'});
459
+ });
460
+ webexRequestStub.onSecondCall().callsFake(() => {
461
+ currentSelfUrl = 'thirdSelfUrl';
462
+
463
+ return Promise.reject({statusCode: 409, message: 'conflict'});
464
+ });
465
+ webexRequestStub.onThirdCall().callsFake(() => {
466
+ currentSelfUrl = 'fourthSelfUrl';
467
+
468
+ return Promise.reject({statusCode: 409, message: 'conflict'});
469
+ });
470
+
471
+ const request = cloneDeep(exampleRoapRequestBody);
472
+
473
+ request.selfUrl = 'oldSelfUrl';
474
+ request.roapMessage.messageType = 'ANSWER';
475
+
476
+ await assert.isRejected(locusMediaRequest.send(request));
477
+
478
+ assert.calledThrice(webexRequestStub);
479
+ assert.equal(webexRequestStub.getCall(0).args[0].uri, 'oldSelfUrl/media');
480
+ assert.equal(webexRequestStub.getCall(1).args[0].uri, 'secondSelfUrl/media');
481
+ assert.equal(webexRequestStub.getCall(2).args[0].uri, 'thirdSelfUrl/media');
482
+ });
483
+
186
484
  it('sends correct metric event when roap message fails', async () => {
187
485
  webexRequestStub.rejects({code: 300, message: 'fake error'});
188
486
  await assert.isRejected(sendRoapMessage('OFFER'));