@webex/internal-plugin-mercury 3.8.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.
@@ -33,6 +33,8 @@ export default class Socket extends EventEmitter {
33
33
  this._domain = 'unknown-domain';
34
34
  this.onmessage = this.onmessage.bind(this);
35
35
  this.onclose = this.onclose.bind(this);
36
+ // Increase max listeners to avoid memory leak warning in tests
37
+ this.setMaxListeners(5);
36
38
  }
37
39
 
38
40
  /**
@@ -358,9 +360,20 @@ export default class Socket extends EventEmitter {
358
360
  return Promise.reject(new Error('`event.data.id` is required'));
359
361
  }
360
362
 
363
+ // Don't try to acknowledge if socket is not in open state
364
+ if (this.readyState !== 1) {
365
+ return Promise.resolve(); // Silently ignore acknowledgment for closed sockets
366
+ }
367
+
361
368
  return this.send({
362
369
  messageId: event.data.id,
363
370
  type: 'ack',
371
+ }).catch((error) => {
372
+ // Gracefully handle send errors (like INVALID_STATE_ERROR) to prevent test issues
373
+ if (error.message === 'INVALID_STATE_ERROR') {
374
+ return Promise.resolve(); // Socket was closed, ignore the acknowledgment
375
+ }
376
+ throw error; // Re-throw other errors
364
377
  });
365
378
  }
366
379
 
@@ -38,14 +38,31 @@ describe('plugin-mercury', () => {
38
38
  },
39
39
  timestamp: Date.now(),
40
40
  trackingId: `suffix_${uuid.v4()}_${Date.now()}`,
41
+ sessionId: 'mercury-default-session',
41
42
  };
42
43
 
43
44
  beforeEach(() => {
44
45
  clock = FakeTimers.install({now: Date.now()});
45
46
  });
46
47
 
47
- afterEach(() => {
48
+ afterEach(async () => {
48
49
  clock.uninstall();
50
+ // Clean up mercury socket and mockWebSocket
51
+ if (mercury && mercury.socket) {
52
+ try {
53
+ await mercury.socket.close();
54
+ } catch (e) {}
55
+ }
56
+ if (mockWebSocket && typeof mockWebSocket.close === 'function') {
57
+ mockWebSocket.close();
58
+ }
59
+ // Restore stubs
60
+ if (Socket.getWebSocketConstructor.restore) {
61
+ Socket.getWebSocketConstructor.restore();
62
+ }
63
+ if (socketOpenStub && socketOpenStub.restore) {
64
+ socketOpenStub.restore();
65
+ }
49
66
  });
50
67
 
51
68
  beforeEach(() => {
@@ -56,6 +73,7 @@ describe('plugin-mercury', () => {
56
73
  });
57
74
 
58
75
  webex.internal.metrics.submitClientMetrics = sinon.stub();
76
+ webex.internal.newMetrics.callDiagnosticMetrics.setMercuryConnectedStatus = sinon.stub();
59
77
  webex.trackingId = 'fakeTrackingId';
60
78
  webex.config.mercury = mercuryConfig.mercury;
61
79
 
@@ -75,6 +93,7 @@ describe('plugin-mercury', () => {
75
93
  });
76
94
 
77
95
  mercury = webex.internal.mercury;
96
+ mercury.defaultSessionId = 'mercury-default-session';
78
97
  });
79
98
 
80
99
  afterEach(() => {
@@ -300,7 +319,7 @@ describe('plugin-mercury', () => {
300
319
  })
301
320
  .then(() => {
302
321
  assert.called(offlineSpy);
303
- assert.calledWith(offlineSpy, {code, reason});
322
+ assert.calledWith(offlineSpy, {code, reason, sessionId: 'mercury-default-session'});
304
323
  switch (action) {
305
324
  case 'close':
306
325
  assert.called(permanentSpy);
@@ -77,6 +77,7 @@ describe('plugin-mercury', () => {
77
77
  markFailedUrl: sinon.stub().returns(Promise.resolve()),
78
78
  };
79
79
  webex.internal.metrics.submitClientMetrics = sinon.stub();
80
+ webex.internal.newMetrics.callDiagnosticMetrics.setMercuryConnectedStatus = sinon.stub();
80
81
  webex.trackingId = 'fakeTrackingId';
81
82
  webex.config.mercury = mercuryConfig.mercury;
82
83
 
@@ -96,9 +97,32 @@ describe('plugin-mercury', () => {
96
97
  });
97
98
 
98
99
  mercury = webex.internal.mercury;
100
+ mercury.defaultSessionId = 'mercury-default-session';
99
101
  });
100
102
 
101
- afterEach(() => {
103
+ afterEach(async () => {
104
+ // Clean up Mercury connections and internal state
105
+ if (mercury) {
106
+ try {
107
+ await mercury.disconnectAll();
108
+ } catch (e) {
109
+ // Ignore cleanup errors
110
+ }
111
+ // Clear any remaining connection promises
112
+ if (mercury._connectPromises) {
113
+ mercury._connectPromises.clear();
114
+ }
115
+ }
116
+
117
+ // Ensure mock socket is properly closed
118
+ if (mockWebSocket && typeof mockWebSocket.close === 'function') {
119
+ try {
120
+ mockWebSocket.close();
121
+ } catch (e) {
122
+ // Ignore cleanup errors
123
+ }
124
+ }
125
+
102
126
  if (socketOpenStub) {
103
127
  socketOpenStub.restore();
104
128
  }
@@ -106,21 +130,38 @@ describe('plugin-mercury', () => {
106
130
  if (Socket.getWebSocketConstructor.restore) {
107
131
  Socket.getWebSocketConstructor.restore();
108
132
  }
133
+
134
+ // Small delay to ensure all async operations complete
135
+ await new Promise(resolve => setTimeout(resolve, 10));
109
136
  });
110
137
 
111
138
  describe('#listen()', () => {
112
139
  it('proxies to #connect()', () => {
113
- sinon.stub(mercury, 'connect');
114
- mercury.listen();
115
- assert.called(mercury.connect);
140
+ const connectStub = sinon.stub(mercury, 'connect').callThrough();
141
+ return mercury.listen().then(() => {
142
+ assert.called(connectStub);
143
+ assert.calledWith(
144
+ webex.internal.newMetrics.callDiagnosticMetrics.setMercuryConnectedStatus,
145
+ true
146
+ );
147
+ });
116
148
  });
117
149
  });
118
150
 
119
151
  describe('#stopListening()', () => {
120
152
  it('proxies to #disconnect()', () => {
121
- sinon.stub(mercury, 'connect');
122
- mercury.listen();
123
- assert.called(mercury.connect);
153
+ return mercury.connect().then(() => {
154
+ webex.internal.newMetrics.callDiagnosticMetrics.setMercuryConnectedStatus.resetHistory();
155
+ const disconnectStub = sinon.stub(mercury, 'disconnect').callThrough();
156
+
157
+ mercury.stopListening();
158
+ assert.called(disconnectStub);
159
+ mockWebSocket.emit('close', {code: 1000, reason: 'test'});
160
+ assert.calledWith(
161
+ webex.internal.newMetrics.callDiagnosticMetrics.setMercuryConnectedStatus,
162
+ false
163
+ );
164
+ });
124
165
  });
125
166
  });
126
167
 
@@ -421,9 +462,13 @@ describe('plugin-mercury', () => {
421
462
 
422
463
  // skipping due to apparent bug with lolex in all browsers but Chrome.
423
464
  skipInBrowser(it)('does not continue attempting to connect', () => {
424
- mercury.connect();
465
+ const promise = mercury.connect();
425
466
 
426
- return promiseTick(2)
467
+ // Wait for the connection to be established before proceeding
468
+ mockWebSocket.open();
469
+
470
+ return promise.then(() =>
471
+ promiseTick(2)
427
472
  .then(() => {
428
473
  clock.tick(6 * webex.internal.mercury.config.backoffTimeReset);
429
474
 
@@ -431,7 +476,8 @@ describe('plugin-mercury', () => {
431
476
  })
432
477
  .then(() => {
433
478
  assert.calledOnce(Socket.prototype.open);
434
- });
479
+ })
480
+ );
435
481
  });
436
482
  });
437
483
 
@@ -506,11 +552,11 @@ describe('plugin-mercury', () => {
506
552
  });
507
553
 
508
554
  describe('#logout()', () => {
509
- it('calls disconnect and logs', () => {
555
+ it('calls disconnectAll and logs', () => {
510
556
  sinon.stub(mercury.logger, 'info');
511
- sinon.stub(mercury, 'disconnect');
557
+ sinon.stub(mercury, 'disconnectAll');
512
558
  mercury.logout();
513
- assert.called(mercury.disconnect);
559
+ assert.called(mercury.disconnectAll);
514
560
  assert.calledTwice(mercury.logger.info);
515
561
 
516
562
  assert.calledWith(mercury.logger.info.getCall(0), 'Mercury: logout() called');
@@ -522,24 +568,24 @@ describe('plugin-mercury', () => {
522
568
  });
523
569
 
524
570
  it('uses the config.beforeLogoutOptionsCloseReason to disconnect and will send code 3050 for logout', () => {
525
- sinon.stub(mercury, 'disconnect');
571
+ sinon.stub(mercury, 'disconnectAll');
526
572
  mercury.config.beforeLogoutOptionsCloseReason = 'done (permanent)';
527
573
  mercury.logout();
528
- assert.calledWith(mercury.disconnect, {code: 3050, reason: 'done (permanent)'});
574
+ assert.calledWith(mercury.disconnectAll, {code: 3050, reason: 'done (permanent)'});
529
575
  });
530
576
 
531
577
  it('uses the config.beforeLogoutOptionsCloseReason to disconnect and will send code 3050 for logout if the reason is different than standard', () => {
532
- sinon.stub(mercury, 'disconnect');
578
+ sinon.stub(mercury, 'disconnectAll');
533
579
  mercury.config.beforeLogoutOptionsCloseReason = 'test';
534
580
  mercury.logout();
535
- assert.calledWith(mercury.disconnect, {code: 3050, reason: 'test'});
581
+ assert.calledWith(mercury.disconnectAll, {code: 3050, reason: 'test'});
536
582
  });
537
583
 
538
584
  it('uses the config.beforeLogoutOptionsCloseReason to disconnect and will send undefined for logout if the reason is same as standard', () => {
539
- sinon.stub(mercury, 'disconnect');
585
+ sinon.stub(mercury, 'disconnectAll');
540
586
  mercury.config.beforeLogoutOptionsCloseReason = 'done (forced)';
541
587
  mercury.logout();
542
- assert.calledWith(mercury.disconnect, undefined);
588
+ assert.calledWith(mercury.disconnectAll, undefined);
543
589
  });
544
590
  });
545
591
 
@@ -645,12 +691,12 @@ describe('plugin-mercury', () => {
645
691
  return promiseTick(webex.internal.mercury.config.backoffTimeReset).then(() => {
646
692
  // By this time backoffCall and mercury socket should be defined by the
647
693
  // 'connect' call
648
- assert.isDefined(mercury.backoffCall, 'Mercury backoffCall is not defined');
694
+ assert.isDefined(mercury.backoffCalls.get('mercury-default-session'), 'Mercury backoffCall is not defined');
649
695
  assert.isDefined(mercury.socket, 'Mercury socket is not defined');
650
696
  // Calling disconnect will abort the backoffCall, close the socket, and
651
697
  // reject the connect
652
698
  mercury.disconnect();
653
- assert.isUndefined(mercury.backoffCall, 'Mercury backoffCall is still defined');
699
+ assert.isUndefined(mercury.backoffCalls.get('mercury-default-session'), 'Mercury backoffCall is still defined');
654
700
  // The socket will never be unset (which seems bad)
655
701
  assert.isDefined(mercury.socket, 'Mercury socket is not defined');
656
702
 
@@ -668,15 +714,15 @@ describe('plugin-mercury', () => {
668
714
 
669
715
  let reason;
670
716
 
671
- mercury.backoffCall = undefined;
672
- mercury._attemptConnection('ws://example.com', (_reason) => {
717
+ mercury.backoffCalls.clear();
718
+ mercury._attemptConnection('ws://example.com', 'mercury-default-session',(_reason) => {
673
719
  reason = _reason;
674
720
  });
675
721
 
676
722
  return promiseTick(webex.internal.mercury.config.backoffTimeReset).then(() => {
677
723
  assert.equal(
678
724
  reason.message,
679
- 'Mercury: prevent socket open when backoffCall no longer defined'
725
+ `Mercury: prevent socket open when backoffCall no longer defined for ${mercury.defaultSessionId}`
680
726
  );
681
727
  });
682
728
  });
@@ -698,7 +744,7 @@ describe('plugin-mercury', () => {
698
744
  return assert.isRejected(promise).then((error) => {
699
745
  const lastError = mercury.getLastError();
700
746
 
701
- assert.equal(error.message, 'Mercury Connection Aborted');
747
+ assert.equal(error.message, `Mercury Connection Aborted for ${mercury.defaultSessionId}`);
702
748
  assert.isDefined(lastError);
703
749
  assert.equal(lastError, realError);
704
750
  });
@@ -792,7 +838,7 @@ describe('plugin-mercury', () => {
792
838
  },
793
839
  };
794
840
  assert.isUndefined(mercury.mercuryTimeOffset);
795
- mercury._setTimeOffset(event);
841
+ mercury._setTimeOffset('mercury-default-session', event);
796
842
  assert.isDefined(mercury.mercuryTimeOffset);
797
843
  assert.isTrue(mercury.mercuryTimeOffset > 0);
798
844
  });
@@ -802,7 +848,7 @@ describe('plugin-mercury', () => {
802
848
  wsWriteTimestamp: Date.now() + 60000,
803
849
  },
804
850
  };
805
- mercury._setTimeOffset(event);
851
+ mercury._setTimeOffset('mercury-default-session', event);
806
852
  assert.isTrue(mercury.mercuryTimeOffset < 0);
807
853
  });
808
854
  it('handles invalid wsWriteTimestamp', () => {
@@ -813,7 +859,7 @@ describe('plugin-mercury', () => {
813
859
  wsWriteTimestamp: invalidTimestamp,
814
860
  },
815
861
  };
816
- mercury._setTimeOffset(event);
862
+ mercury._setTimeOffset('mercury-default-session', event);
817
863
  assert.isUndefined(mercury.mercuryTimeOffset);
818
864
  });
819
865
  });