@webex/calling 3.12.0-mobius-socket.10 → 3.12.0-mobius-socket.12
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/CallingClient/CallingClient.test.js +2 -2
- package/dist/CallingClient/CallingClient.test.js.map +1 -1
- package/dist/CallingClient/calling/call.test.js +1 -1
- package/dist/CallingClient/calling/call.test.js.map +1 -1
- package/dist/CallingClient/line/line.test.js +2 -2
- package/dist/CallingClient/line/line.test.js.map +1 -1
- package/dist/CallingClient/registration/register.js +76 -43
- package/dist/CallingClient/registration/register.js.map +1 -1
- package/dist/CallingClient/registration/register.test.js +1 -1
- package/dist/CallingClient/registration/register.test.js.map +1 -1
- package/dist/CallingClient/registration/types.js.map +1 -1
- package/dist/CallingClient/utils/request.js +2 -2
- package/dist/CallingClient/utils/request.js.map +1 -1
- package/dist/common/Utils.js +70 -30
- package/dist/common/Utils.js.map +1 -1
- package/dist/mobius-socket/config.js +61 -0
- package/dist/mobius-socket/config.js.map +1 -0
- package/dist/mobius-socket/errors.js +106 -0
- package/dist/mobius-socket/errors.js.map +1 -0
- package/dist/mobius-socket/index.js +101 -0
- package/dist/mobius-socket/index.js.map +1 -0
- package/dist/mobius-socket/mobius-socket-events.test.js +511 -0
- package/dist/mobius-socket/mobius-socket-events.test.js.map +1 -0
- package/dist/mobius-socket/mobius-socket.js +1191 -0
- package/dist/mobius-socket/mobius-socket.js.map +1 -0
- package/dist/mobius-socket/mobius-socket.test.js +2107 -0
- package/dist/mobius-socket/mobius-socket.test.js.map +1 -0
- package/dist/mobius-socket/socket/constants.js +20 -0
- package/dist/mobius-socket/socket/constants.js.map +1 -0
- package/dist/mobius-socket/socket/index.js +15 -0
- package/dist/mobius-socket/socket/index.js.map +1 -0
- package/dist/mobius-socket/socket/socket-base.js +632 -0
- package/dist/mobius-socket/socket/socket-base.js.map +1 -0
- package/dist/mobius-socket/socket/socket.js +19 -0
- package/dist/mobius-socket/socket/socket.js.map +1 -0
- package/dist/mobius-socket/socket/socket.shim.js +36 -0
- package/dist/mobius-socket/socket/socket.shim.js.map +1 -0
- package/dist/mobius-socket/socket.test.js +752 -0
- package/dist/mobius-socket/socket.test.js.map +1 -0
- package/dist/mobius-socket/test/mocha-helpers.js +23 -0
- package/dist/mobius-socket/test/mocha-helpers.js.map +1 -0
- package/dist/mobius-socket/test/promise-tick.js +29 -0
- package/dist/mobius-socket/test/promise-tick.js.map +1 -0
- package/dist/module/CallingClient/registration/register.js +30 -7
- package/dist/module/CallingClient/utils/request.js +1 -1
- package/dist/module/common/Utils.js +17 -4
- package/dist/module/mobius-socket/config.js +18 -0
- package/dist/module/mobius-socket/errors.js +30 -0
- package/dist/module/mobius-socket/index.js +24 -0
- package/dist/module/mobius-socket/mobius-socket.js +761 -0
- package/dist/module/mobius-socket/socket/constants.js +10 -0
- package/dist/module/mobius-socket/socket/index.js +4 -0
- package/dist/module/mobius-socket/socket/socket-base.js +374 -0
- package/dist/module/mobius-socket/socket/socket.js +9 -0
- package/dist/module/mobius-socket/socket/socket.shim.js +24 -0
- package/dist/types/CallingClient/registration/register.d.ts.map +1 -1
- package/dist/types/CallingClient/registration/types.d.ts +1 -1
- package/dist/types/CallingClient/registration/types.d.ts.map +1 -1
- package/dist/types/common/Utils.d.ts +4 -1
- package/dist/types/common/Utils.d.ts.map +1 -1
- package/dist/types/mobius-socket/config.d.ts +15 -0
- package/dist/types/mobius-socket/config.d.ts.map +1 -0
- package/dist/types/mobius-socket/errors.d.ts +13 -0
- package/dist/types/mobius-socket/errors.d.ts.map +1 -0
- package/dist/types/mobius-socket/index.d.ts +9 -0
- package/dist/types/mobius-socket/index.d.ts.map +1 -0
- package/dist/types/mobius-socket/mobius-socket.d.ts +62 -0
- package/dist/types/mobius-socket/mobius-socket.d.ts.map +1 -0
- package/dist/types/mobius-socket/socket/constants.d.ts +11 -0
- package/dist/types/mobius-socket/socket/constants.d.ts.map +1 -0
- package/dist/types/mobius-socket/socket/index.d.ts +2 -0
- package/dist/types/mobius-socket/socket/index.d.ts.map +1 -0
- package/dist/types/mobius-socket/socket/socket-base.d.ts +38 -0
- package/dist/types/mobius-socket/socket/socket-base.d.ts.map +1 -0
- package/dist/types/mobius-socket/socket/socket.d.ts +3 -0
- package/dist/types/mobius-socket/socket/socket.d.ts.map +1 -0
- package/dist/types/mobius-socket/socket/socket.shim.d.ts +3 -0
- package/dist/types/mobius-socket/socket/socket.shim.d.ts.map +1 -0
- package/package.json +16 -3
- package/src/mobius-socket/socket/socket.js +13 -0
- package/src/mobius-socket/socket/socket.shim.js +31 -0
|
@@ -0,0 +1,2107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _typeof = require("@babel/runtime-corejs2/helpers/typeof");
|
|
4
|
+
var _Object$keys = require("@babel/runtime-corejs2/core-js/object/keys");
|
|
5
|
+
var _Object$getOwnPropertySymbols = require("@babel/runtime-corejs2/core-js/object/get-own-property-symbols");
|
|
6
|
+
var _Object$getOwnPropertyDescriptor = require("@babel/runtime-corejs2/core-js/object/get-own-property-descriptor");
|
|
7
|
+
var _Object$getOwnPropertyDescriptors = require("@babel/runtime-corejs2/core-js/object/get-own-property-descriptors");
|
|
8
|
+
var _Object$defineProperties = require("@babel/runtime-corejs2/core-js/object/define-properties");
|
|
9
|
+
var _Object$defineProperty2 = require("@babel/runtime-corejs2/core-js/object/define-property");
|
|
10
|
+
var _WeakMap = require("@babel/runtime-corejs2/core-js/weak-map");
|
|
11
|
+
var _interopRequireDefault = require("@babel/runtime-corejs2/helpers/interopRequireDefault");
|
|
12
|
+
var _regenerator = _interopRequireDefault(require("@babel/runtime-corejs2/regenerator"));
|
|
13
|
+
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/asyncToGenerator"));
|
|
14
|
+
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/defineProperty"));
|
|
15
|
+
var _construct2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/construct"));
|
|
16
|
+
var _defineProperty3 = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/object/define-property"));
|
|
17
|
+
var _stringify = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/json/stringify"));
|
|
18
|
+
var _now = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/date/now"));
|
|
19
|
+
var _promise = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/promise"));
|
|
20
|
+
var _apply = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/reflect/apply"));
|
|
21
|
+
var _crypto = require("crypto");
|
|
22
|
+
var _sinon = _interopRequireDefault(require("sinon"));
|
|
23
|
+
var _testHelperChai = require("@webex/test-helper-chai");
|
|
24
|
+
var _testHelperMockWebex = _interopRequireDefault(require("@webex/test-helper-mock-webex"));
|
|
25
|
+
var _testHelperMockWebSocket = _interopRequireDefault(require("@webex/test-helper-mock-web-socket"));
|
|
26
|
+
var _index = _interopRequireWildcard(require("./index"));
|
|
27
|
+
var _mochaHelpers = require("./test/mocha-helpers");
|
|
28
|
+
var _constants = require("./socket/constants");
|
|
29
|
+
var _promiseTick = _interopRequireDefault(require("./test/promise-tick"));
|
|
30
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof _WeakMap) var r = new _WeakMap(), n = new _WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t3 in e) "default" !== _t3 && {}.hasOwnProperty.call(e, _t3) && ((i = (o = _Object$defineProperty2) && _Object$getOwnPropertyDescriptor(e, _t3)) && (i.get || i.set) ? o(f, _t3, i) : f[_t3] = e[_t3]); return f; })(e, t); }
|
|
31
|
+
function ownKeys(e, r) { var t = _Object$keys(e); if (_Object$getOwnPropertySymbols) { var o = _Object$getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return _Object$getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
32
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors ? _Object$defineProperties(e, _Object$getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { _Object$defineProperty2(e, r, _Object$getOwnPropertyDescriptor(t, r)); }); } return e; } /*!
|
|
33
|
+
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
|
|
34
|
+
*/
|
|
35
|
+
if (!crypto.randomUUID) {
|
|
36
|
+
(0, _defineProperty3.default)(crypto, 'randomUUID', {
|
|
37
|
+
value: _crypto.randomUUID,
|
|
38
|
+
configurable: true
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
describe('plugin-mobius-socket', function () {
|
|
42
|
+
var createUuid = function createUuid() {
|
|
43
|
+
return crypto.randomUUID();
|
|
44
|
+
};
|
|
45
|
+
describe('getMobiusSocketInstance', function () {
|
|
46
|
+
afterEach(function () {
|
|
47
|
+
(0, _index.resetMobiusSocketInstance)();
|
|
48
|
+
});
|
|
49
|
+
it('uses package config when consumer config is not provided', function () {
|
|
50
|
+
var configuredWebex = new _testHelperMockWebex.default();
|
|
51
|
+
var instance = (0, _index.getMobiusSocketInstance)(configuredWebex);
|
|
52
|
+
_testHelperChai.assert.instanceOf(instance, _index.default);
|
|
53
|
+
_testHelperChai.assert.equal(instance.config, _index.config.mobiusSocket);
|
|
54
|
+
});
|
|
55
|
+
it('uses consumer config when it is provided', function () {
|
|
56
|
+
var configuredWebex = new _testHelperMockWebex.default();
|
|
57
|
+
var consumerConfig = {
|
|
58
|
+
initialConnectionMaxRetries: 0,
|
|
59
|
+
backoffTimeReset: 1234
|
|
60
|
+
};
|
|
61
|
+
var instance = (0, _index.getMobiusSocketInstance)(configuredWebex, consumerConfig);
|
|
62
|
+
_testHelperChai.assert.instanceOf(instance, _index.default);
|
|
63
|
+
_testHelperChai.assert.equal(instance.config, consumerConfig);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
describe('MobiusSocket', function () {
|
|
67
|
+
var mobiusSocket;
|
|
68
|
+
var mockWebSocket;
|
|
69
|
+
var socketOpenStub;
|
|
70
|
+
var usingFakeTimers;
|
|
71
|
+
var webex;
|
|
72
|
+
var statusStartTypingMessage = (0, _stringify.default)({
|
|
73
|
+
id: createUuid(),
|
|
74
|
+
data: {
|
|
75
|
+
eventType: 'status.start_typing',
|
|
76
|
+
actor: {
|
|
77
|
+
id: 'actorId'
|
|
78
|
+
},
|
|
79
|
+
conversationId: createUuid()
|
|
80
|
+
},
|
|
81
|
+
timestamp: (0, _now.default)(),
|
|
82
|
+
trackingId: "suffix_".concat(createUuid(), "_").concat((0, _now.default)())
|
|
83
|
+
});
|
|
84
|
+
var emitAuthResponse = function emitAuthResponse() {
|
|
85
|
+
var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
|
|
86
|
+
_ref$statusCode = _ref.statusCode,
|
|
87
|
+
statusCode = _ref$statusCode === void 0 ? 200 : _ref$statusCode,
|
|
88
|
+
_ref$statusMessage = _ref.statusMessage,
|
|
89
|
+
statusMessage = _ref$statusMessage === void 0 ? 'OK' : _ref$statusMessage;
|
|
90
|
+
var authRequest = JSON.parse(mockWebSocket.send.lastCall.args[0]);
|
|
91
|
+
mockWebSocket.emit('message', {
|
|
92
|
+
data: (0, _stringify.default)({
|
|
93
|
+
type: 'response_event',
|
|
94
|
+
subtype: _constants.MESSAGE_TYPES.AUTH,
|
|
95
|
+
trackingId: authRequest.trackingId,
|
|
96
|
+
statusCode: statusCode,
|
|
97
|
+
statusMessage: statusMessage
|
|
98
|
+
})
|
|
99
|
+
});
|
|
100
|
+
};
|
|
101
|
+
var createAsyncEvent = function createAsyncEvent(eventId) {
|
|
102
|
+
var eventType = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'custom.event';
|
|
103
|
+
return {
|
|
104
|
+
data: {
|
|
105
|
+
type: 'async_event',
|
|
106
|
+
eventId: eventId,
|
|
107
|
+
data: {
|
|
108
|
+
eventType: eventType
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
var createSessionSocket = function createSessionSocket() {
|
|
114
|
+
return {
|
|
115
|
+
close: _sinon.default.stub().returns(_promise.default.resolve()),
|
|
116
|
+
removeAllListeners: _sinon.default.stub()
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
var countGenericEventEmits = function countGenericEventEmits(emitSpy, sessionId) {
|
|
120
|
+
return emitSpy.getCalls().filter(function (call) {
|
|
121
|
+
return call.args[0] === sessionId && call.args[1] === 'event';
|
|
122
|
+
}).length;
|
|
123
|
+
};
|
|
124
|
+
beforeEach(function () {
|
|
125
|
+
jest.useFakeTimers({
|
|
126
|
+
doNotFake: ['nextTick']
|
|
127
|
+
});
|
|
128
|
+
usingFakeTimers = true;
|
|
129
|
+
});
|
|
130
|
+
beforeEach(function () {
|
|
131
|
+
webex = new _testHelperMockWebex.default();
|
|
132
|
+
webex.credentials = {
|
|
133
|
+
refresh: _sinon.default.stub().returns(_promise.default.resolve()),
|
|
134
|
+
getUserToken: _sinon.default.stub().returns(_promise.default.resolve({
|
|
135
|
+
toString: function toString() {
|
|
136
|
+
return 'Bearer FAKE';
|
|
137
|
+
}
|
|
138
|
+
}))
|
|
139
|
+
};
|
|
140
|
+
webex.internal.device = {
|
|
141
|
+
registered: true,
|
|
142
|
+
register: _sinon.default.stub().returns(_promise.default.resolve()),
|
|
143
|
+
refresh: _sinon.default.stub().returns(_promise.default.resolve()),
|
|
144
|
+
webSocketUrl: 'ws://example.com',
|
|
145
|
+
getWebSocketUrl: _sinon.default.stub().returns(_promise.default.resolve('ws://example-2.com')),
|
|
146
|
+
useServiceCatalogUrl: _sinon.default.stub().returns(_promise.default.resolve('https://service-catalog-url.com'))
|
|
147
|
+
};
|
|
148
|
+
webex.internal.services = {
|
|
149
|
+
convertUrlToPriorityHostUrl: _sinon.default.stub().returns(_promise.default.resolve('ws://example-2.com')),
|
|
150
|
+
markFailedUrl: _sinon.default.stub().returns(_promise.default.resolve()),
|
|
151
|
+
switchActiveClusterIds: _sinon.default.stub(),
|
|
152
|
+
invalidateCache: _sinon.default.stub(),
|
|
153
|
+
isValidHost: _sinon.default.stub().returns(_promise.default.resolve(true))
|
|
154
|
+
};
|
|
155
|
+
webex.internal.metrics.submitClientMetrics = _sinon.default.stub();
|
|
156
|
+
webex.trackingId = 'fakeTrackingId';
|
|
157
|
+
webex.logger = console;
|
|
158
|
+
_sinon.default.stub(_index.Socket, 'getWebSocketConstructor').callsFake(function () {
|
|
159
|
+
return function () {
|
|
160
|
+
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
161
|
+
args[_key] = arguments[_key];
|
|
162
|
+
}
|
|
163
|
+
mockWebSocket = (0, _construct2.default)(_testHelperMockWebSocket.default, args);
|
|
164
|
+
return mockWebSocket;
|
|
165
|
+
};
|
|
166
|
+
});
|
|
167
|
+
var origOpen = _index.Socket.prototype.open;
|
|
168
|
+
socketOpenStub = _sinon.default.stub(_index.Socket.prototype, 'open').callsFake(function () {
|
|
169
|
+
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
|
|
170
|
+
args[_key2] = arguments[_key2];
|
|
171
|
+
}
|
|
172
|
+
var promise = (0, _apply.default)(origOpen, this, args);
|
|
173
|
+
process.nextTick(function () {
|
|
174
|
+
mockWebSocket.open();
|
|
175
|
+
// Simulate Mobius auth response after socket open
|
|
176
|
+
process.nextTick(function () {
|
|
177
|
+
emitAuthResponse();
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
return promise;
|
|
181
|
+
});
|
|
182
|
+
mobiusSocket = new _index.default(webex, _objectSpread({}, _index.config.mobiusSocket));
|
|
183
|
+
mobiusSocket.defaultSessionId = 'mobius-websocket-session';
|
|
184
|
+
});
|
|
185
|
+
afterEach(/*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee() {
|
|
186
|
+
var _t;
|
|
187
|
+
return _regenerator.default.wrap(function (_context) {
|
|
188
|
+
while (1) switch (_context.prev = _context.next) {
|
|
189
|
+
case 0:
|
|
190
|
+
if (usingFakeTimers) {
|
|
191
|
+
jest.useRealTimers();
|
|
192
|
+
usingFakeTimers = false;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Clean up MobiusSocket connections and internal state
|
|
196
|
+
if (!mobiusSocket) {
|
|
197
|
+
_context.next = 5;
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
_context.prev = 1;
|
|
201
|
+
_context.next = 2;
|
|
202
|
+
return mobiusSocket.disconnectAll();
|
|
203
|
+
case 2:
|
|
204
|
+
_context.next = 4;
|
|
205
|
+
break;
|
|
206
|
+
case 3:
|
|
207
|
+
_context.prev = 3;
|
|
208
|
+
_t = _context["catch"](1);
|
|
209
|
+
case 4:
|
|
210
|
+
// Clear any remaining connection promises
|
|
211
|
+
if (mobiusSocket._connectPromises) {
|
|
212
|
+
mobiusSocket._connectPromises.clear();
|
|
213
|
+
}
|
|
214
|
+
case 5:
|
|
215
|
+
// Ensure mock socket is properly closed
|
|
216
|
+
if (mockWebSocket && typeof mockWebSocket.close === 'function') {
|
|
217
|
+
try {
|
|
218
|
+
mockWebSocket.close();
|
|
219
|
+
} catch (e) {
|
|
220
|
+
// Ignore cleanup errors
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (socketOpenStub) {
|
|
224
|
+
socketOpenStub.restore();
|
|
225
|
+
}
|
|
226
|
+
if (_index.Socket.getWebSocketConstructor.restore) {
|
|
227
|
+
_index.Socket.getWebSocketConstructor.restore();
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Small delay to ensure all async operations complete
|
|
231
|
+
_context.next = 6;
|
|
232
|
+
return new _promise.default(function (resolve) {
|
|
233
|
+
return setTimeout(resolve, 10);
|
|
234
|
+
});
|
|
235
|
+
case 6:
|
|
236
|
+
case "end":
|
|
237
|
+
return _context.stop();
|
|
238
|
+
}
|
|
239
|
+
}, _callee, null, [[1, 3]]);
|
|
240
|
+
})));
|
|
241
|
+
describe('#connect()', function () {
|
|
242
|
+
it('lazily registers the device', function () {
|
|
243
|
+
webex.internal.device.registered = false;
|
|
244
|
+
_testHelperChai.assert.notCalled(webex.internal.device.register);
|
|
245
|
+
var promise = mobiusSocket.connect();
|
|
246
|
+
return promise.then(function () {
|
|
247
|
+
_testHelperChai.assert.calledOnce(webex.internal.device.register);
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
it('connects to MobiusSocket using default url', function () {
|
|
251
|
+
webex.internal.feature.updateFeature = _sinon.default.stub();
|
|
252
|
+
var promise = mobiusSocket.connect();
|
|
253
|
+
var envelope = {
|
|
254
|
+
data: {
|
|
255
|
+
featureToggle: {
|
|
256
|
+
'feature-name': true
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
_testHelperChai.assert.isFalse(mobiusSocket.connected, 'MobiusSocket is not connected');
|
|
261
|
+
_testHelperChai.assert.isTrue(mobiusSocket.connecting, 'MobiusSocket is connecting');
|
|
262
|
+
mockWebSocket.open();
|
|
263
|
+
return promise.then(function () {
|
|
264
|
+
_testHelperChai.assert.isTrue(mobiusSocket.connected, 'MobiusSocket is connected');
|
|
265
|
+
_testHelperChai.assert.isFalse(mobiusSocket.connecting, 'MobiusSocket is not connecting');
|
|
266
|
+
_testHelperChai.assert.calledWith(socketOpenStub, 'ws://example.com', _sinon.default.match.any);
|
|
267
|
+
mobiusSocket.emit('event:featureToggle_update', envelope);
|
|
268
|
+
_testHelperChai.assert.calledOnceWithExactly(webex.internal.feature.updateFeature, envelope.data.featureToggle);
|
|
269
|
+
_sinon.default.restore();
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
it('connects to MobiusSocket but does not call updateFeature', function () {
|
|
273
|
+
webex.internal.feature.updateFeature = _sinon.default.stub();
|
|
274
|
+
var promise = mobiusSocket.connect();
|
|
275
|
+
var envelope = {};
|
|
276
|
+
return promise.then(function () {
|
|
277
|
+
mobiusSocket.emit('event:featureToggle_update', envelope);
|
|
278
|
+
_testHelperChai.assert.notCalled(webex.internal.feature.updateFeature);
|
|
279
|
+
_sinon.default.restore();
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
it('MobiusSocket emit event:ActiveClusterStatusEvent, call services switchActiveClusterIds', function () {
|
|
283
|
+
var promise = mobiusSocket.connect();
|
|
284
|
+
var activeClusterEventEnvelope = {
|
|
285
|
+
data: {
|
|
286
|
+
activeClusters: {
|
|
287
|
+
wdm: 'wdm-cluster-id.com'
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
mockWebSocket.open();
|
|
292
|
+
return promise.then(function () {
|
|
293
|
+
mobiusSocket.emit('event:ActiveClusterStatusEvent', activeClusterEventEnvelope);
|
|
294
|
+
_testHelperChai.assert.calledOnceWithExactly(webex.internal.services.switchActiveClusterIds, activeClusterEventEnvelope.data.activeClusters);
|
|
295
|
+
_sinon.default.restore();
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
it('MobiusSocket emit event:ActiveClusterStatusEvent with no data, not call services switchActiveClusterIds', function () {
|
|
299
|
+
webex.internal.feature.updateFeature = _sinon.default.stub();
|
|
300
|
+
var promise = mobiusSocket.connect();
|
|
301
|
+
var envelope = {};
|
|
302
|
+
return promise.then(function () {
|
|
303
|
+
mobiusSocket.emit('event:ActiveClusterStatusEvent', envelope);
|
|
304
|
+
_testHelperChai.assert.notCalled(webex.internal.services.switchActiveClusterIds);
|
|
305
|
+
_sinon.default.restore();
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
it('MobiusSocket emit event:u2c.cache-invalidation, call services invalidateCache', function () {
|
|
309
|
+
var promise = mobiusSocket.connect();
|
|
310
|
+
var u2cInvalidateEventEnvelope = {
|
|
311
|
+
data: {
|
|
312
|
+
timestamp: '1759289614'
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
mockWebSocket.open();
|
|
316
|
+
return promise.then(function () {
|
|
317
|
+
mobiusSocket.emit('event:u2c.cache-invalidation', u2cInvalidateEventEnvelope);
|
|
318
|
+
_testHelperChai.assert.calledOnceWithExactly(webex.internal.services.invalidateCache, u2cInvalidateEventEnvelope.data.timestamp);
|
|
319
|
+
_sinon.default.restore();
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
it('MobiusSocket emit event:u2c.cache-invalidation with no data, not call services switchActiveClusterIds', function () {
|
|
323
|
+
webex.internal.feature.updateFeature = _sinon.default.stub();
|
|
324
|
+
var promise = mobiusSocket.connect();
|
|
325
|
+
var envelope = {};
|
|
326
|
+
return promise.then(function () {
|
|
327
|
+
mobiusSocket.emit('event:u2c.cache-invalidation', envelope);
|
|
328
|
+
_testHelperChai.assert.notCalled(webex.internal.services.invalidateCache);
|
|
329
|
+
_sinon.default.restore();
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
describe('when `maxRetries` is set', function () {
|
|
333
|
+
var check = function check() {
|
|
334
|
+
socketOpenStub.restore();
|
|
335
|
+
socketOpenStub = _sinon.default.stub(_index.Socket.prototype, 'open');
|
|
336
|
+
socketOpenStub.returns(_promise.default.reject(new _index.ConnectionError()));
|
|
337
|
+
_testHelperChai.assert.notCalled(_index.Socket.prototype.open);
|
|
338
|
+
var promise = mobiusSocket.connect();
|
|
339
|
+
return (0, _promiseTick.default)(5).then(function () {
|
|
340
|
+
_testHelperChai.assert.calledOnce(_index.Socket.prototype.open);
|
|
341
|
+
return (0, _promiseTick.default)(5);
|
|
342
|
+
}).then(function () {
|
|
343
|
+
jest.advanceTimersByTime(mobiusSocket.config.backoffTimeReset);
|
|
344
|
+
return (0, _promiseTick.default)(5);
|
|
345
|
+
}).then(function () {
|
|
346
|
+
_testHelperChai.assert.calledTwice(_index.Socket.prototype.open);
|
|
347
|
+
jest.advanceTimersByTime(2 * mobiusSocket.config.backoffTimeReset);
|
|
348
|
+
return (0, _promiseTick.default)(5);
|
|
349
|
+
}).then(function () {
|
|
350
|
+
_testHelperChai.assert.calledThrice(_index.Socket.prototype.open);
|
|
351
|
+
jest.advanceTimersByTime(5 * mobiusSocket.config.backoffTimeReset);
|
|
352
|
+
return _testHelperChai.assert.isRejected(promise);
|
|
353
|
+
}).then(function () {
|
|
354
|
+
_testHelperChai.assert.calledThrice(_index.Socket.prototype.open);
|
|
355
|
+
});
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
// skipping due to apparent bug with lolex in all browsers but Chrome.
|
|
359
|
+
(0, _mochaHelpers.skipInBrowser)(it)('fails after configured `initialConnectionMaxRetries` attempts', function () {
|
|
360
|
+
mobiusSocket.config.maxRetries = 0;
|
|
361
|
+
mobiusSocket.config.initialConnectionMaxRetries = 2;
|
|
362
|
+
return check();
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
// skipping due to apparent bug with lolex in all browsers but Chrome.
|
|
366
|
+
// if initial retries is zero and mobiusSocket has never connected, do not retry
|
|
367
|
+
(0, _mochaHelpers.skipInBrowser)(it)('fails immediately when `initialConnectionMaxRetries` is 0', function () {
|
|
368
|
+
mobiusSocket.config.maxRetries = 2;
|
|
369
|
+
mobiusSocket.config.initialConnectionMaxRetries = 0;
|
|
370
|
+
socketOpenStub.restore();
|
|
371
|
+
socketOpenStub = _sinon.default.stub(_index.Socket.prototype, 'open');
|
|
372
|
+
socketOpenStub.returns(_promise.default.reject(new _index.ConnectionError()));
|
|
373
|
+
_testHelperChai.assert.notCalled(_index.Socket.prototype.open);
|
|
374
|
+
var promise = mobiusSocket.connect();
|
|
375
|
+
return (0, _promiseTick.default)(5).then(function () {
|
|
376
|
+
_testHelperChai.assert.calledOnce(_index.Socket.prototype.open);
|
|
377
|
+
return _testHelperChai.assert.isRejected(promise);
|
|
378
|
+
}).then(function () {
|
|
379
|
+
_testHelperChai.assert.calledOnce(_index.Socket.prototype.open);
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
// initial retries is non-zero so takes precedence over maxRetries when mobiusSocket has never connected
|
|
384
|
+
(0, _mochaHelpers.skipInBrowser)(it)('fails after `initialConnectionMaxRetries` attempts', function () {
|
|
385
|
+
mobiusSocket.config.maxRetries = 0;
|
|
386
|
+
mobiusSocket.config.initialConnectionMaxRetries = 2;
|
|
387
|
+
return check();
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
// initial retries is non-zero so takes precedence over maxRetries when mobiusSocket has never connected
|
|
391
|
+
(0, _mochaHelpers.skipInBrowser)(it)('fails after `initialConnectionMaxRetries` attempts', function () {
|
|
392
|
+
mobiusSocket.config.initialConnectionMaxRetries = 2;
|
|
393
|
+
mobiusSocket.config.maxRetries = 5;
|
|
394
|
+
return check();
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
// when mobiusSocket has connected maxRetries is used and the initialConnectionMaxRetries is ignored
|
|
398
|
+
(0, _mochaHelpers.skipInBrowser)(it)('fails after `initialConnectionMaxRetries` attempts', function () {
|
|
399
|
+
mobiusSocket.config.initialConnectionMaxRetries = 5;
|
|
400
|
+
mobiusSocket.config.maxRetries = 2;
|
|
401
|
+
mobiusSocket.hasEverConnected = true;
|
|
402
|
+
return check();
|
|
403
|
+
});
|
|
404
|
+
});
|
|
405
|
+
it('can safely be called multiple times', function () {
|
|
406
|
+
var promise = _promise.default.all([mobiusSocket.connect(), mobiusSocket.connect(), mobiusSocket.connect(), mobiusSocket.connect()]);
|
|
407
|
+
mockWebSocket.open();
|
|
408
|
+
return promise.then(function () {
|
|
409
|
+
_testHelperChai.assert.calledOnce(_index.Socket.prototype.open);
|
|
410
|
+
});
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
// skipping due to apparent bug with lolex in all browsers but Chrome.
|
|
414
|
+
(0, _mochaHelpers.skipInBrowser)(describe)('when the connection fails', function () {
|
|
415
|
+
it('backs off exponentially', function () {
|
|
416
|
+
mobiusSocket.config.initialConnectionMaxRetries = 2;
|
|
417
|
+
socketOpenStub.restore();
|
|
418
|
+
socketOpenStub = _sinon.default.stub(_index.Socket.prototype, 'open');
|
|
419
|
+
socketOpenStub.returns(_promise.default.reject(new _index.ConnectionError({
|
|
420
|
+
code: 4001
|
|
421
|
+
})));
|
|
422
|
+
// Note: onCall is zero-based
|
|
423
|
+
socketOpenStub.onCall(2).returns(_promise.default.resolve(new _testHelperMockWebSocket.default()));
|
|
424
|
+
_testHelperChai.assert.notCalled(_index.Socket.prototype.open);
|
|
425
|
+
var promise = mobiusSocket.connect();
|
|
426
|
+
return (0, _promiseTick.default)(5).then(function () {
|
|
427
|
+
_testHelperChai.assert.calledOnce(_index.Socket.prototype.open);
|
|
428
|
+
|
|
429
|
+
// I'm not sure why, but it's important the clock doesn't advance
|
|
430
|
+
// until a tick happens
|
|
431
|
+
return (0, _promiseTick.default)(5);
|
|
432
|
+
}).then(function () {
|
|
433
|
+
jest.advanceTimersByTime(mobiusSocket.config.backoffTimeReset);
|
|
434
|
+
return (0, _promiseTick.default)(5);
|
|
435
|
+
}).then(function () {
|
|
436
|
+
_testHelperChai.assert.calledTwice(_index.Socket.prototype.open);
|
|
437
|
+
jest.advanceTimersByTime(2 * mobiusSocket.config.backoffTimeReset);
|
|
438
|
+
return (0, _promiseTick.default)(5);
|
|
439
|
+
}).then(function () {
|
|
440
|
+
_testHelperChai.assert.calledThrice(_index.Socket.prototype.open);
|
|
441
|
+
jest.advanceTimersByTime(5 * mobiusSocket.config.backoffTimeReset);
|
|
442
|
+
return promise;
|
|
443
|
+
}).then(function () {
|
|
444
|
+
_testHelperChai.assert.calledThrice(_index.Socket.prototype.open);
|
|
445
|
+
jest.advanceTimersByTime(8 * mobiusSocket.config.backoffTimeReset);
|
|
446
|
+
return (0, _promiseTick.default)(5);
|
|
447
|
+
}).then(function () {
|
|
448
|
+
_testHelperChai.assert.calledThrice(_index.Socket.prototype.open);
|
|
449
|
+
});
|
|
450
|
+
});
|
|
451
|
+
describe('with `BadRequest`', function () {
|
|
452
|
+
it('fails permanently', function () {
|
|
453
|
+
jest.useRealTimers();
|
|
454
|
+
usingFakeTimers = false;
|
|
455
|
+
socketOpenStub.restore();
|
|
456
|
+
socketOpenStub = _sinon.default.stub(_index.Socket.prototype, 'open').returns(_promise.default.reject(new _index.BadRequest({
|
|
457
|
+
code: 4400
|
|
458
|
+
})));
|
|
459
|
+
return _testHelperChai.assert.isRejected(mobiusSocket.connect());
|
|
460
|
+
});
|
|
461
|
+
});
|
|
462
|
+
describe('with `UnknownResponse`', function () {
|
|
463
|
+
it('triggers a device refresh', function () {
|
|
464
|
+
mobiusSocket.config.initialConnectionMaxRetries = 1;
|
|
465
|
+
socketOpenStub.restore();
|
|
466
|
+
socketOpenStub = _sinon.default.stub(_index.Socket.prototype, 'open').returns(_promise.default.resolve());
|
|
467
|
+
socketOpenStub.onCall(0).returns(_promise.default.reject(new _index.UnknownResponse({
|
|
468
|
+
code: 4444
|
|
469
|
+
})));
|
|
470
|
+
_testHelperChai.assert.notCalled(webex.credentials.refresh);
|
|
471
|
+
_testHelperChai.assert.notCalled(webex.internal.device.refresh);
|
|
472
|
+
var promise = mobiusSocket.connect();
|
|
473
|
+
return (0, _promiseTick.default)(7).then(function () {
|
|
474
|
+
_testHelperChai.assert.notCalled(webex.credentials.refresh);
|
|
475
|
+
_testHelperChai.assert.called(webex.internal.device.refresh);
|
|
476
|
+
jest.advanceTimersByTime(1000);
|
|
477
|
+
return promise;
|
|
478
|
+
});
|
|
479
|
+
});
|
|
480
|
+
});
|
|
481
|
+
describe('with `NotAuthorized`', function () {
|
|
482
|
+
it('triggers a token refresh', function () {
|
|
483
|
+
mobiusSocket.config.initialConnectionMaxRetries = 1;
|
|
484
|
+
socketOpenStub.restore();
|
|
485
|
+
socketOpenStub = _sinon.default.stub(_index.Socket.prototype, 'open').returns(_promise.default.resolve());
|
|
486
|
+
socketOpenStub.onCall(0).returns(_promise.default.reject(new _index.NotAuthorized({
|
|
487
|
+
code: 4401
|
|
488
|
+
})));
|
|
489
|
+
_testHelperChai.assert.notCalled(webex.credentials.refresh);
|
|
490
|
+
_testHelperChai.assert.notCalled(webex.internal.device.refresh);
|
|
491
|
+
var promise = mobiusSocket.connect();
|
|
492
|
+
return (0, _promiseTick.default)(7).then(function () {
|
|
493
|
+
_testHelperChai.assert.called(webex.credentials.refresh);
|
|
494
|
+
_testHelperChai.assert.notCalled(webex.internal.device.refresh);
|
|
495
|
+
jest.advanceTimersByTime(1000);
|
|
496
|
+
return promise;
|
|
497
|
+
});
|
|
498
|
+
});
|
|
499
|
+
});
|
|
500
|
+
describe('with `Forbidden`', function () {
|
|
501
|
+
it('fails permanently', function () {
|
|
502
|
+
jest.useRealTimers();
|
|
503
|
+
usingFakeTimers = false;
|
|
504
|
+
socketOpenStub.restore();
|
|
505
|
+
socketOpenStub = _sinon.default.stub(_index.Socket.prototype, 'open').returns(_promise.default.reject(new _index.Forbidden({
|
|
506
|
+
code: 4403
|
|
507
|
+
})));
|
|
508
|
+
return _testHelperChai.assert.isRejected(mobiusSocket.connect());
|
|
509
|
+
});
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
// describe(`with \`NotFound\``, () => {
|
|
513
|
+
// it(`triggers a device refresh`, () => {
|
|
514
|
+
// socketOpenStub.restore();
|
|
515
|
+
// socketOpenStub = sinon.stub(Socket.prototype, `open`).returns(Promise.resolve());
|
|
516
|
+
// socketOpenStub.onCall(0).returns(Promise.reject(new NotFound({code: 4404})));
|
|
517
|
+
// assert.notCalled(webex.credentials.refresh);
|
|
518
|
+
// assert.notCalled(webex.internal.device.refresh);
|
|
519
|
+
// const promise = mobiusSocket.connect();
|
|
520
|
+
// return promiseTick(6)
|
|
521
|
+
// .then(() => {
|
|
522
|
+
// assert.notCalled(webex.credentials.refresh);
|
|
523
|
+
// assert.called(webex.internal.device.refresh);
|
|
524
|
+
// clock.tick(1000);
|
|
525
|
+
// return assert.isFulfilled(promise);
|
|
526
|
+
// });
|
|
527
|
+
// });
|
|
528
|
+
// });
|
|
529
|
+
});
|
|
530
|
+
describe('when connected', function () {
|
|
531
|
+
it('resolves immediately', function () {
|
|
532
|
+
return mobiusSocket.connect().then(function () {
|
|
533
|
+
_testHelperChai.assert.isTrue(mobiusSocket.connected, 'MobiusSocket is connected');
|
|
534
|
+
_testHelperChai.assert.isFalse(mobiusSocket.connecting, 'MobiusSocket is not connecting');
|
|
535
|
+
var promise = mobiusSocket.connect();
|
|
536
|
+
_testHelperChai.assert.isTrue(mobiusSocket.connected, 'MobiusSocket is connected');
|
|
537
|
+
_testHelperChai.assert.isFalse(mobiusSocket.connecting, 'MobiusSocket is not connecting');
|
|
538
|
+
return promise;
|
|
539
|
+
});
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
// skipping due to apparent bug with lolex in all browsers but Chrome.
|
|
543
|
+
(0, _mochaHelpers.skipInBrowser)(it)('does not continue attempting to connect', function () {
|
|
544
|
+
var promise = mobiusSocket.connect();
|
|
545
|
+
|
|
546
|
+
// Wait for the connection to be established before proceeding
|
|
547
|
+
mockWebSocket.open();
|
|
548
|
+
return promise.then(function () {
|
|
549
|
+
return (0, _promiseTick.default)(2).then(function () {
|
|
550
|
+
jest.advanceTimersByTime(6 * mobiusSocket.config.backoffTimeReset);
|
|
551
|
+
return (0, _promiseTick.default)(2);
|
|
552
|
+
}).then(function () {
|
|
553
|
+
_testHelperChai.assert.calledOnce(_index.Socket.prototype.open);
|
|
554
|
+
});
|
|
555
|
+
});
|
|
556
|
+
});
|
|
557
|
+
});
|
|
558
|
+
describe('when webSocketUrl is provided', function () {
|
|
559
|
+
it('connects to MobiusSocket with provided url', function () {
|
|
560
|
+
var webSocketUrl = 'ws://providedurl.com';
|
|
561
|
+
var promise = mobiusSocket.connect(webSocketUrl);
|
|
562
|
+
_testHelperChai.assert.isFalse(mobiusSocket.connected, 'MobiusSocket is not connected');
|
|
563
|
+
_testHelperChai.assert.isTrue(mobiusSocket.connecting, 'MobiusSocket is connecting');
|
|
564
|
+
mockWebSocket.open();
|
|
565
|
+
return promise.then(function () {
|
|
566
|
+
_testHelperChai.assert.isTrue(mobiusSocket.connected, 'MobiusSocket is connected');
|
|
567
|
+
_testHelperChai.assert.isFalse(mobiusSocket.connecting, 'MobiusSocket is not connecting');
|
|
568
|
+
_testHelperChai.assert.calledWith(_index.Socket.prototype.open, 'ws://providedurl.com', _sinon.default.match.any);
|
|
569
|
+
});
|
|
570
|
+
});
|
|
571
|
+
});
|
|
572
|
+
describe('when config.initialConnectionMaxRetries is set to 0', function () {
|
|
573
|
+
it('connects successfully through the shared backoff flow', function () {
|
|
574
|
+
var backoffSpy = _sinon.default.spy(mobiusSocket, '_connectWithBackoff');
|
|
575
|
+
mobiusSocket.config.initialConnectionMaxRetries = 0;
|
|
576
|
+
var promise = mobiusSocket.connect('ws://example.com');
|
|
577
|
+
_testHelperChai.assert.isTrue(mobiusSocket.connecting, 'MobiusSocket is connecting');
|
|
578
|
+
mockWebSocket.open();
|
|
579
|
+
return promise.then(function () {
|
|
580
|
+
var _backoffSpy$firstCall;
|
|
581
|
+
_testHelperChai.assert.isTrue(mobiusSocket.connected, 'MobiusSocket is connected');
|
|
582
|
+
_testHelperChai.assert.isFalse(mobiusSocket.connecting, 'MobiusSocket is not connecting');
|
|
583
|
+
_testHelperChai.assert.isTrue(mobiusSocket.hasEverConnected, 'hasEverConnected is true');
|
|
584
|
+
_testHelperChai.assert.calledOnce(_index.Socket.prototype.open);
|
|
585
|
+
_testHelperChai.assert.calledOnce(backoffSpy);
|
|
586
|
+
_testHelperChai.assert.isUndefined((_backoffSpy$firstCall = backoffSpy.firstCall.args[2]) === null || _backoffSpy$firstCall === void 0 ? void 0 : _backoffSpy$firstCall.initialConnectionMaxRetries);
|
|
587
|
+
backoffSpy.restore();
|
|
588
|
+
});
|
|
589
|
+
});
|
|
590
|
+
it('rejects immediately on failure without retrying', function () {
|
|
591
|
+
jest.useRealTimers();
|
|
592
|
+
usingFakeTimers = false;
|
|
593
|
+
socketOpenStub.restore();
|
|
594
|
+
socketOpenStub = _sinon.default.stub(_index.Socket.prototype, 'open').returns(_promise.default.reject(new _index.ConnectionError({
|
|
595
|
+
code: 4001
|
|
596
|
+
})));
|
|
597
|
+
mobiusSocket.config.initialConnectionMaxRetries = 0;
|
|
598
|
+
var promise = mobiusSocket.connect('ws://example.com');
|
|
599
|
+
return _testHelperChai.assert.isRejected(promise).then(function () {
|
|
600
|
+
_testHelperChai.assert.calledOnce(_index.Socket.prototype.open);
|
|
601
|
+
_testHelperChai.assert.isFalse(mobiusSocket.connected, 'MobiusSocket is not connected');
|
|
602
|
+
});
|
|
603
|
+
});
|
|
604
|
+
it('uses config-driven initial retry behavior in the shared backoff strategy', function () {
|
|
605
|
+
var backoffSpy = _sinon.default.spy(mobiusSocket, '_connectWithBackoff');
|
|
606
|
+
mobiusSocket.config.initialConnectionMaxRetries = 0;
|
|
607
|
+
var promise = mobiusSocket.connect('ws://example.com');
|
|
608
|
+
mockWebSocket.open();
|
|
609
|
+
return promise.then(function () {
|
|
610
|
+
var _backoffSpy$firstCall2;
|
|
611
|
+
_testHelperChai.assert.calledOnce(backoffSpy);
|
|
612
|
+
_testHelperChai.assert.isUndefined((_backoffSpy$firstCall2 = backoffSpy.firstCall.args[2]) === null || _backoffSpy$firstCall2 === void 0 ? void 0 : _backoffSpy$firstCall2.initialConnectionMaxRetries);
|
|
613
|
+
backoffSpy.restore();
|
|
614
|
+
});
|
|
615
|
+
});
|
|
616
|
+
it('treats a different explicit URL as a fresh initial connect', function () {
|
|
617
|
+
jest.useRealTimers();
|
|
618
|
+
usingFakeTimers = false;
|
|
619
|
+
mobiusSocket.hasEverConnected = true;
|
|
620
|
+
mobiusSocket.socketUrl = 'ws://old-url.com';
|
|
621
|
+
mobiusSocket.config.initialConnectionMaxRetries = 0;
|
|
622
|
+
mobiusSocket.config.maxRetries = 5;
|
|
623
|
+
socketOpenStub.restore();
|
|
624
|
+
socketOpenStub = _sinon.default.stub(_index.Socket.prototype, 'open').returns(_promise.default.reject(new _index.ConnectionError({
|
|
625
|
+
code: 4001
|
|
626
|
+
})));
|
|
627
|
+
var promise = mobiusSocket.connect('ws://new-url.com');
|
|
628
|
+
return _testHelperChai.assert.isRejected(promise).then(function () {
|
|
629
|
+
_testHelperChai.assert.calledOnce(_index.Socket.prototype.open);
|
|
630
|
+
_testHelperChai.assert.equal(mobiusSocket.socketUrl, 'ws://new-url.com');
|
|
631
|
+
_testHelperChai.assert.isFalse(mobiusSocket.hasEverConnected, 'hasEverConnected is false');
|
|
632
|
+
});
|
|
633
|
+
});
|
|
634
|
+
});
|
|
635
|
+
});
|
|
636
|
+
describe('Websocket proxy agent', function () {
|
|
637
|
+
afterEach(function () {
|
|
638
|
+
delete webex.config.defaultMobiusSocketOptions;
|
|
639
|
+
});
|
|
640
|
+
it('connects to MobiusSocket using proxy agent', function () {
|
|
641
|
+
var testProxyUrl = 'http://proxyurl.com:80';
|
|
642
|
+
webex.config.defaultMobiusSocketOptions = {
|
|
643
|
+
agent: {
|
|
644
|
+
proxy: {
|
|
645
|
+
href: testProxyUrl
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
};
|
|
649
|
+
var promise = mobiusSocket.connect();
|
|
650
|
+
_testHelperChai.assert.isFalse(mobiusSocket.connected, 'MobiusSocket is not connected');
|
|
651
|
+
_testHelperChai.assert.isTrue(mobiusSocket.connecting, 'MobiusSocket is connecting');
|
|
652
|
+
mockWebSocket.open();
|
|
653
|
+
return promise.then(function () {
|
|
654
|
+
_testHelperChai.assert.isTrue(mobiusSocket.connected, 'MobiusSocket is connected');
|
|
655
|
+
_testHelperChai.assert.isFalse(mobiusSocket.connecting, 'MobiusSocket is not connecting');
|
|
656
|
+
_testHelperChai.assert.calledWith(socketOpenStub, 'ws://example.com', _sinon.default.match.has('agent', _sinon.default.match.has('proxy', _sinon.default.match.has('href', testProxyUrl))));
|
|
657
|
+
});
|
|
658
|
+
});
|
|
659
|
+
it('connects to MobiusSocket without proxy agent', function () {
|
|
660
|
+
var promise = mobiusSocket.connect();
|
|
661
|
+
_testHelperChai.assert.isFalse(mobiusSocket.connected, 'MobiusSocket is not connected');
|
|
662
|
+
_testHelperChai.assert.isTrue(mobiusSocket.connecting, 'MobiusSocket is connecting');
|
|
663
|
+
mockWebSocket.open();
|
|
664
|
+
return promise.then(function () {
|
|
665
|
+
_testHelperChai.assert.isTrue(mobiusSocket.connected, 'MobiusSocket is connected');
|
|
666
|
+
_testHelperChai.assert.isFalse(mobiusSocket.connecting, 'MobiusSocket is not connecting');
|
|
667
|
+
_testHelperChai.assert.calledWith(socketOpenStub, 'ws://example.com', _sinon.default.match({
|
|
668
|
+
agent: undefined
|
|
669
|
+
}));
|
|
670
|
+
});
|
|
671
|
+
});
|
|
672
|
+
});
|
|
673
|
+
describe('#logout()', function () {
|
|
674
|
+
it('calls disconnectAll and logs', function () {
|
|
675
|
+
_sinon.default.stub(mobiusSocket.logger, 'info');
|
|
676
|
+
_sinon.default.stub(mobiusSocket, 'disconnectAll');
|
|
677
|
+
mobiusSocket.logout();
|
|
678
|
+
_testHelperChai.assert.called(mobiusSocket.disconnectAll);
|
|
679
|
+
_testHelperChai.assert.calledOnce(mobiusSocket.logger.info);
|
|
680
|
+
_testHelperChai.assert.calledWith(mobiusSocket.logger.info.getCall(0), 'MobiusSocket: logout() called');
|
|
681
|
+
});
|
|
682
|
+
it('uses the config.beforeLogoutOptionsCloseReason to disconnect and will send code 3050 for logout', function () {
|
|
683
|
+
_sinon.default.stub(mobiusSocket, 'disconnectAll');
|
|
684
|
+
mobiusSocket.config.beforeLogoutOptionsCloseReason = 'done (permanent)';
|
|
685
|
+
mobiusSocket.logout();
|
|
686
|
+
_testHelperChai.assert.calledWith(mobiusSocket.disconnectAll, {
|
|
687
|
+
code: 3050,
|
|
688
|
+
reason: 'done (permanent)'
|
|
689
|
+
});
|
|
690
|
+
});
|
|
691
|
+
it('uses the config.beforeLogoutOptionsCloseReason to disconnect and will send code 3050 for logout if the reason is different than standard', function () {
|
|
692
|
+
_sinon.default.stub(mobiusSocket, 'disconnectAll');
|
|
693
|
+
mobiusSocket.config.beforeLogoutOptionsCloseReason = 'test';
|
|
694
|
+
mobiusSocket.logout();
|
|
695
|
+
_testHelperChai.assert.calledWith(mobiusSocket.disconnectAll, {
|
|
696
|
+
code: 3050,
|
|
697
|
+
reason: 'test'
|
|
698
|
+
});
|
|
699
|
+
});
|
|
700
|
+
it('uses the config.beforeLogoutOptionsCloseReason to disconnect and will send undefined for logout if the reason is same as standard', function () {
|
|
701
|
+
_sinon.default.stub(mobiusSocket, 'disconnectAll');
|
|
702
|
+
mobiusSocket.config.beforeLogoutOptionsCloseReason = 'done (forced)';
|
|
703
|
+
mobiusSocket.logout();
|
|
704
|
+
_testHelperChai.assert.calledWith(mobiusSocket.disconnectAll, undefined);
|
|
705
|
+
});
|
|
706
|
+
});
|
|
707
|
+
describe('#disconnect()', function () {
|
|
708
|
+
it('disconnects the WebSocket', function () {
|
|
709
|
+
return mobiusSocket.connect().then(function () {
|
|
710
|
+
_testHelperChai.assert.isTrue(mobiusSocket.connected, 'MobiusSocket is connected');
|
|
711
|
+
_testHelperChai.assert.isFalse(mobiusSocket.connecting, 'MobiusSocket is not connecting');
|
|
712
|
+
var promise = mobiusSocket.disconnect();
|
|
713
|
+
mockWebSocket.emit('close', {
|
|
714
|
+
code: 1000,
|
|
715
|
+
reason: 'Done'
|
|
716
|
+
});
|
|
717
|
+
return promise;
|
|
718
|
+
}).then(function () {
|
|
719
|
+
_testHelperChai.assert.isFalse(mobiusSocket.connected, 'MobiusSocket is not connected');
|
|
720
|
+
_testHelperChai.assert.isFalse(mobiusSocket.connecting, 'MobiusSocket is not connecting');
|
|
721
|
+
_testHelperChai.assert.isUndefined(mobiusSocket.mockWebSocket, 'MobiusSocket does not have a mockWebSocket');
|
|
722
|
+
});
|
|
723
|
+
});
|
|
724
|
+
it('disconnects the WebSocket with code 3050', function () {
|
|
725
|
+
return mobiusSocket.connect().then(function () {
|
|
726
|
+
_testHelperChai.assert.isTrue(mobiusSocket.connected, 'MobiusSocket is connected');
|
|
727
|
+
_testHelperChai.assert.isFalse(mobiusSocket.connecting, 'MobiusSocket is not connecting');
|
|
728
|
+
var promise = mobiusSocket.disconnect();
|
|
729
|
+
mockWebSocket.emit('close', {
|
|
730
|
+
code: 3050,
|
|
731
|
+
reason: 'done (permanent)'
|
|
732
|
+
});
|
|
733
|
+
return promise;
|
|
734
|
+
}).then(function () {
|
|
735
|
+
_testHelperChai.assert.isFalse(mobiusSocket.connected, 'MobiusSocket is not connected');
|
|
736
|
+
_testHelperChai.assert.isFalse(mobiusSocket.connecting, 'MobiusSocket is not connecting');
|
|
737
|
+
_testHelperChai.assert.isUndefined(mobiusSocket.mockWebSocket, 'MobiusSocket does not have a mockWebSocket');
|
|
738
|
+
});
|
|
739
|
+
});
|
|
740
|
+
it('stops emitting message events', function () {
|
|
741
|
+
var spy = _sinon.default.spy();
|
|
742
|
+
mobiusSocket.on('event:status.start_typing', spy);
|
|
743
|
+
return mobiusSocket.connect().then(function () {
|
|
744
|
+
_testHelperChai.assert.isTrue(mobiusSocket.connected, 'MobiusSocket is connected');
|
|
745
|
+
_testHelperChai.assert.isFalse(mobiusSocket.connecting, 'MobiusSocket is not connecting');
|
|
746
|
+
_testHelperChai.assert.notCalled(spy);
|
|
747
|
+
mockWebSocket.readyState = 1;
|
|
748
|
+
mockWebSocket.emit('open');
|
|
749
|
+
mockWebSocket.emit('message', {
|
|
750
|
+
data: statusStartTypingMessage
|
|
751
|
+
});
|
|
752
|
+
}).then(function () {
|
|
753
|
+
_testHelperChai.assert.calledOnce(spy);
|
|
754
|
+
var promise = mobiusSocket.disconnect();
|
|
755
|
+
mockWebSocket.readyState = 1;
|
|
756
|
+
mockWebSocket.emit('open');
|
|
757
|
+
mockWebSocket.emit('message', {
|
|
758
|
+
data: statusStartTypingMessage
|
|
759
|
+
});
|
|
760
|
+
mockWebSocket.emit('close', {
|
|
761
|
+
code: 1000,
|
|
762
|
+
reason: 'Done'
|
|
763
|
+
});
|
|
764
|
+
mockWebSocket.emit('message', {
|
|
765
|
+
data: statusStartTypingMessage
|
|
766
|
+
});
|
|
767
|
+
return promise;
|
|
768
|
+
}).then(function () {
|
|
769
|
+
mockWebSocket.readyState = 1;
|
|
770
|
+
mockWebSocket.emit('open');
|
|
771
|
+
mockWebSocket.emit('message', {
|
|
772
|
+
data: statusStartTypingMessage
|
|
773
|
+
});
|
|
774
|
+
_testHelperChai.assert.calledOnce(spy);
|
|
775
|
+
});
|
|
776
|
+
});
|
|
777
|
+
describe('when there is a connection attempt inflight', function () {
|
|
778
|
+
it('stops the attempt when disconnect called', function () {
|
|
779
|
+
socketOpenStub.restore();
|
|
780
|
+
socketOpenStub = _sinon.default.stub(_index.Socket.prototype, 'open');
|
|
781
|
+
socketOpenStub.onCall(0).returns(
|
|
782
|
+
// Delay the opening of the socket so that disconnect is called while open
|
|
783
|
+
// is in progress
|
|
784
|
+
(0, _promiseTick.default)(2 * mobiusSocket.config.backoffTimeReset)
|
|
785
|
+
// Pretend the socket opened successfully. Failing should be fine too but
|
|
786
|
+
// it generates more console output.
|
|
787
|
+
.then(function () {
|
|
788
|
+
return _promise.default.resolve();
|
|
789
|
+
}));
|
|
790
|
+
var promise = mobiusSocket.connect();
|
|
791
|
+
|
|
792
|
+
// Wait for the connect call to setup
|
|
793
|
+
return (0, _promiseTick.default)(mobiusSocket.config.backoffTimeReset).then(/*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee2() {
|
|
794
|
+
return _regenerator.default.wrap(function (_context2) {
|
|
795
|
+
while (1) switch (_context2.prev = _context2.next) {
|
|
796
|
+
case 0:
|
|
797
|
+
// By this time backoffCall and mobiusSocket socket should be defined by the
|
|
798
|
+
// 'connect' call
|
|
799
|
+
_testHelperChai.assert.isDefined(mobiusSocket.backoffCalls.get('mobius-websocket-session'), 'MobiusSocket backoffCall is not defined');
|
|
800
|
+
_testHelperChai.assert.isDefined(mobiusSocket.socket, 'MobiusSocket socket is not defined');
|
|
801
|
+
// Calling disconnect will abort the backoffCall, close the socket, and
|
|
802
|
+
// reject the connect
|
|
803
|
+
mobiusSocket.disconnect();
|
|
804
|
+
_testHelperChai.assert.isUndefined(mobiusSocket.backoffCalls.get('mobius-websocket-session'), 'MobiusSocket backoffCall is still defined');
|
|
805
|
+
// The socket will never be unset (which seems bad)
|
|
806
|
+
_testHelperChai.assert.isDefined(mobiusSocket.socket, 'MobiusSocket socket is not defined');
|
|
807
|
+
_context2.next = 1;
|
|
808
|
+
return _testHelperChai.assert.isRejected(promise);
|
|
809
|
+
case 1:
|
|
810
|
+
// connection did not fail, so no last error
|
|
811
|
+
_testHelperChai.assert.isUndefined(mobiusSocket.getLastError());
|
|
812
|
+
case 2:
|
|
813
|
+
case "end":
|
|
814
|
+
return _context2.stop();
|
|
815
|
+
}
|
|
816
|
+
}, _callee2);
|
|
817
|
+
})));
|
|
818
|
+
});
|
|
819
|
+
it('stops the attempt when backoffCall is undefined', function () {
|
|
820
|
+
socketOpenStub.restore();
|
|
821
|
+
socketOpenStub = _sinon.default.stub(_index.Socket.prototype, 'open');
|
|
822
|
+
socketOpenStub.returns(_promise.default.resolve());
|
|
823
|
+
var reason;
|
|
824
|
+
mobiusSocket.backoffCalls.clear();
|
|
825
|
+
var promise = mobiusSocket._attemptConnection('ws://example.com', 'mobius-websocket-session', function (_reason) {
|
|
826
|
+
reason = _reason;
|
|
827
|
+
});
|
|
828
|
+
return (0, _promiseTick.default)(mobiusSocket.config.backoffTimeReset).then(function () {
|
|
829
|
+
_testHelperChai.assert.equal(reason.message, "MobiusSocket: prevent socket open when backoffCall no longer defined for ".concat(mobiusSocket.defaultSessionId));
|
|
830
|
+
|
|
831
|
+
// Ensure the promise was actually rejected (short-circuited)
|
|
832
|
+
return _testHelperChai.assert.isRejected(promise);
|
|
833
|
+
});
|
|
834
|
+
});
|
|
835
|
+
it('sets lastError when retrying', function () {
|
|
836
|
+
var realError = new Error('FORCED');
|
|
837
|
+
mobiusSocket.config.initialConnectionMaxRetries = 1;
|
|
838
|
+
socketOpenStub.restore();
|
|
839
|
+
socketOpenStub = _sinon.default.stub(_index.Socket.prototype, 'open');
|
|
840
|
+
socketOpenStub.onCall(0).returns(_promise.default.reject(realError));
|
|
841
|
+
var promise = mobiusSocket.connect();
|
|
842
|
+
|
|
843
|
+
// Wait for the connect call to setup
|
|
844
|
+
return (0, _promiseTick.default)(mobiusSocket.config.backoffTimeReset).then(/*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee3() {
|
|
845
|
+
var error, lastError;
|
|
846
|
+
return _regenerator.default.wrap(function (_context3) {
|
|
847
|
+
while (1) switch (_context3.prev = _context3.next) {
|
|
848
|
+
case 0:
|
|
849
|
+
// Calling disconnect will abort the backoffCall, close the socket, and
|
|
850
|
+
// reject the connect
|
|
851
|
+
mobiusSocket.disconnect();
|
|
852
|
+
_context3.next = 1;
|
|
853
|
+
return _testHelperChai.assert.isRejected(promise);
|
|
854
|
+
case 1:
|
|
855
|
+
error = _context3.sent;
|
|
856
|
+
lastError = mobiusSocket.getLastError();
|
|
857
|
+
_testHelperChai.assert.match(error.message, /MobiusSocket Connection Aborted/);
|
|
858
|
+
_testHelperChai.assert.isDefined(lastError);
|
|
859
|
+
_testHelperChai.assert.equal(lastError, realError);
|
|
860
|
+
case 2:
|
|
861
|
+
case "end":
|
|
862
|
+
return _context3.stop();
|
|
863
|
+
}
|
|
864
|
+
}, _callee3);
|
|
865
|
+
})));
|
|
866
|
+
});
|
|
867
|
+
});
|
|
868
|
+
});
|
|
869
|
+
describe('#sendWssRequest()', function () {
|
|
870
|
+
beforeEach(function () {
|
|
871
|
+
mobiusSocket.config.wssResponseTimeout = 100;
|
|
872
|
+
});
|
|
873
|
+
it('resolves when a matching response_event arrives', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee4() {
|
|
874
|
+
var requestPromise, requestPayload, response;
|
|
875
|
+
return _regenerator.default.wrap(function (_context4) {
|
|
876
|
+
while (1) switch (_context4.prev = _context4.next) {
|
|
877
|
+
case 0:
|
|
878
|
+
_context4.next = 1;
|
|
879
|
+
return mobiusSocket.connect();
|
|
880
|
+
case 1:
|
|
881
|
+
requestPromise = mobiusSocket.sendWssRequest({
|
|
882
|
+
type: 'auth',
|
|
883
|
+
data: {
|
|
884
|
+
token: 'test'
|
|
885
|
+
}
|
|
886
|
+
});
|
|
887
|
+
_context4.next = 2;
|
|
888
|
+
return (0, _promiseTick.default)();
|
|
889
|
+
case 2:
|
|
890
|
+
requestPayload = JSON.parse(mockWebSocket.send.lastCall.args[0]);
|
|
891
|
+
_testHelperChai.assert.equal(requestPayload.data.token, 'test');
|
|
892
|
+
mockWebSocket.emit('message', {
|
|
893
|
+
data: (0, _stringify.default)({
|
|
894
|
+
type: 'response_event',
|
|
895
|
+
subtype: 'auth',
|
|
896
|
+
trackingId: requestPayload.trackingId,
|
|
897
|
+
statusCode: 200,
|
|
898
|
+
statusMessage: 'OK'
|
|
899
|
+
})
|
|
900
|
+
});
|
|
901
|
+
_context4.next = 3;
|
|
902
|
+
return requestPromise;
|
|
903
|
+
case 3:
|
|
904
|
+
response = _context4.sent;
|
|
905
|
+
_testHelperChai.assert.equal(response.type, 'response_event');
|
|
906
|
+
_testHelperChai.assert.equal(response.subtype, 'auth');
|
|
907
|
+
_testHelperChai.assert.equal(response.trackingId, requestPayload.trackingId);
|
|
908
|
+
_testHelperChai.assert.equal(response.statusCode, 200);
|
|
909
|
+
case 4:
|
|
910
|
+
case "end":
|
|
911
|
+
return _context4.stop();
|
|
912
|
+
}
|
|
913
|
+
}, _callee4);
|
|
914
|
+
})));
|
|
915
|
+
it('strips the Bearer prefix from connect-time auth token', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee5() {
|
|
916
|
+
var authPayload;
|
|
917
|
+
return _regenerator.default.wrap(function (_context5) {
|
|
918
|
+
while (1) switch (_context5.prev = _context5.next) {
|
|
919
|
+
case 0:
|
|
920
|
+
_context5.next = 1;
|
|
921
|
+
return mobiusSocket.connect();
|
|
922
|
+
case 1:
|
|
923
|
+
authPayload = JSON.parse(mockWebSocket.send.firstCall.args[0]);
|
|
924
|
+
_testHelperChai.assert.equal(authPayload.type, _constants.MESSAGE_TYPES.AUTH);
|
|
925
|
+
_testHelperChai.assert.equal(authPayload.data.token, 'FAKE');
|
|
926
|
+
case 2:
|
|
927
|
+
case "end":
|
|
928
|
+
return _context5.stop();
|
|
929
|
+
}
|
|
930
|
+
}, _callee5);
|
|
931
|
+
})));
|
|
932
|
+
it('rejects when a matching response_event is non-2xx', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee6() {
|
|
933
|
+
var requestPromise, requestPayload, error;
|
|
934
|
+
return _regenerator.default.wrap(function (_context6) {
|
|
935
|
+
while (1) switch (_context6.prev = _context6.next) {
|
|
936
|
+
case 0:
|
|
937
|
+
_context6.next = 1;
|
|
938
|
+
return mobiusSocket.connect();
|
|
939
|
+
case 1:
|
|
940
|
+
requestPromise = mobiusSocket.sendWssRequest({
|
|
941
|
+
type: 'auth',
|
|
942
|
+
data: {
|
|
943
|
+
token: 'test'
|
|
944
|
+
}
|
|
945
|
+
});
|
|
946
|
+
_context6.next = 2;
|
|
947
|
+
return (0, _promiseTick.default)();
|
|
948
|
+
case 2:
|
|
949
|
+
requestPayload = JSON.parse(mockWebSocket.send.lastCall.args[0]);
|
|
950
|
+
mockWebSocket.emit('message', {
|
|
951
|
+
data: (0, _stringify.default)({
|
|
952
|
+
type: 'response_event',
|
|
953
|
+
subtype: 'auth',
|
|
954
|
+
trackingId: requestPayload.trackingId,
|
|
955
|
+
statusCode: 403,
|
|
956
|
+
statusMessage: 'Forbidden'
|
|
957
|
+
})
|
|
958
|
+
});
|
|
959
|
+
_context6.next = 3;
|
|
960
|
+
return _testHelperChai.assert.isRejected(requestPromise);
|
|
961
|
+
case 3:
|
|
962
|
+
error = _context6.sent;
|
|
963
|
+
_testHelperChai.assert.equal(error.name, 'MobiusSocketResponseError');
|
|
964
|
+
_testHelperChai.assert.equal(error.statusCode, 403);
|
|
965
|
+
_testHelperChai.assert.equal(error.statusMessage, 'Forbidden');
|
|
966
|
+
_testHelperChai.assert.equal(error.trackingId, requestPayload.trackingId);
|
|
967
|
+
case 4:
|
|
968
|
+
case "end":
|
|
969
|
+
return _context6.stop();
|
|
970
|
+
}
|
|
971
|
+
}, _callee6);
|
|
972
|
+
})));
|
|
973
|
+
it('rejects when the matching response does not arrive before timeout', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee7() {
|
|
974
|
+
var requestPromise, error;
|
|
975
|
+
return _regenerator.default.wrap(function (_context7) {
|
|
976
|
+
while (1) switch (_context7.prev = _context7.next) {
|
|
977
|
+
case 0:
|
|
978
|
+
_context7.next = 1;
|
|
979
|
+
return mobiusSocket.connect();
|
|
980
|
+
case 1:
|
|
981
|
+
requestPromise = mobiusSocket.sendWssRequest({
|
|
982
|
+
type: 'auth',
|
|
983
|
+
data: {
|
|
984
|
+
token: 'test'
|
|
985
|
+
}
|
|
986
|
+
});
|
|
987
|
+
jest.advanceTimersByTime(101);
|
|
988
|
+
_context7.next = 2;
|
|
989
|
+
return (0, _promiseTick.default)();
|
|
990
|
+
case 2:
|
|
991
|
+
_context7.next = 3;
|
|
992
|
+
return _testHelperChai.assert.isRejected(requestPromise);
|
|
993
|
+
case 3:
|
|
994
|
+
error = _context7.sent;
|
|
995
|
+
_testHelperChai.assert.equal(error.name, 'MobiusSocketResponseError');
|
|
996
|
+
_testHelperChai.assert.equal(error.statusCode, 408);
|
|
997
|
+
_testHelperChai.assert.equal(error.statusMessage, 'Mobius websocket response timed out');
|
|
998
|
+
case 4:
|
|
999
|
+
case "end":
|
|
1000
|
+
return _context7.stop();
|
|
1001
|
+
}
|
|
1002
|
+
}, _callee7);
|
|
1003
|
+
})));
|
|
1004
|
+
it('rejects with a clear error when the matching response is missing status code', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee8() {
|
|
1005
|
+
var requestPromise, requestPayload, error;
|
|
1006
|
+
return _regenerator.default.wrap(function (_context8) {
|
|
1007
|
+
while (1) switch (_context8.prev = _context8.next) {
|
|
1008
|
+
case 0:
|
|
1009
|
+
_context8.next = 1;
|
|
1010
|
+
return mobiusSocket.connect();
|
|
1011
|
+
case 1:
|
|
1012
|
+
requestPromise = mobiusSocket.sendWssRequest({
|
|
1013
|
+
type: 'auth',
|
|
1014
|
+
data: {
|
|
1015
|
+
token: 'test'
|
|
1016
|
+
}
|
|
1017
|
+
});
|
|
1018
|
+
_context8.next = 2;
|
|
1019
|
+
return (0, _promiseTick.default)();
|
|
1020
|
+
case 2:
|
|
1021
|
+
requestPayload = JSON.parse(mockWebSocket.send.lastCall.args[0]);
|
|
1022
|
+
mockWebSocket.emit('message', {
|
|
1023
|
+
data: (0, _stringify.default)({
|
|
1024
|
+
type: 'response_event',
|
|
1025
|
+
subtype: 'auth',
|
|
1026
|
+
trackingId: requestPayload.trackingId
|
|
1027
|
+
})
|
|
1028
|
+
});
|
|
1029
|
+
_context8.next = 3;
|
|
1030
|
+
return _testHelperChai.assert.isRejected(requestPromise);
|
|
1031
|
+
case 3:
|
|
1032
|
+
error = _context8.sent;
|
|
1033
|
+
_testHelperChai.assert.equal(error.name, 'MobiusSocketResponseError');
|
|
1034
|
+
_testHelperChai.assert.isUndefined(error.statusCode);
|
|
1035
|
+
_testHelperChai.assert.equal(error.statusMessage, 'Socket response missing status code');
|
|
1036
|
+
case 4:
|
|
1037
|
+
case "end":
|
|
1038
|
+
return _context8.stop();
|
|
1039
|
+
}
|
|
1040
|
+
}, _callee8);
|
|
1041
|
+
})));
|
|
1042
|
+
it('rejects pending requests when the active socket closes', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee9() {
|
|
1043
|
+
var requestPromise, error;
|
|
1044
|
+
return _regenerator.default.wrap(function (_context9) {
|
|
1045
|
+
while (1) switch (_context9.prev = _context9.next) {
|
|
1046
|
+
case 0:
|
|
1047
|
+
_context9.next = 1;
|
|
1048
|
+
return mobiusSocket.connect();
|
|
1049
|
+
case 1:
|
|
1050
|
+
requestPromise = mobiusSocket.sendWssRequest({
|
|
1051
|
+
type: 'auth',
|
|
1052
|
+
data: {
|
|
1053
|
+
token: 'test'
|
|
1054
|
+
}
|
|
1055
|
+
});
|
|
1056
|
+
mockWebSocket.emit('close', {
|
|
1057
|
+
code: 1003,
|
|
1058
|
+
reason: 'service rejected request'
|
|
1059
|
+
});
|
|
1060
|
+
_context9.next = 2;
|
|
1061
|
+
return _testHelperChai.assert.isRejected(requestPromise);
|
|
1062
|
+
case 2:
|
|
1063
|
+
error = _context9.sent;
|
|
1064
|
+
_testHelperChai.assert.instanceOf(error, _index.ConnectionError);
|
|
1065
|
+
_testHelperChai.assert.equal(error.code, 1003);
|
|
1066
|
+
_testHelperChai.assert.equal(error.reason, 'service rejected request');
|
|
1067
|
+
case 3:
|
|
1068
|
+
case "end":
|
|
1069
|
+
return _context9.stop();
|
|
1070
|
+
}
|
|
1071
|
+
}, _callee9);
|
|
1072
|
+
})));
|
|
1073
|
+
it('rejects array payloads', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee0() {
|
|
1074
|
+
var error;
|
|
1075
|
+
return _regenerator.default.wrap(function (_context0) {
|
|
1076
|
+
while (1) switch (_context0.prev = _context0.next) {
|
|
1077
|
+
case 0:
|
|
1078
|
+
_context0.next = 1;
|
|
1079
|
+
return mobiusSocket.connect();
|
|
1080
|
+
case 1:
|
|
1081
|
+
_context0.next = 2;
|
|
1082
|
+
return _testHelperChai.assert.isRejected(mobiusSocket.sendWssRequest([]));
|
|
1083
|
+
case 2:
|
|
1084
|
+
error = _context0.sent;
|
|
1085
|
+
_testHelperChai.assert.equal(error.message, '`payload` is required');
|
|
1086
|
+
case 3:
|
|
1087
|
+
case "end":
|
|
1088
|
+
return _context0.stop();
|
|
1089
|
+
}
|
|
1090
|
+
}, _callee0);
|
|
1091
|
+
})));
|
|
1092
|
+
});
|
|
1093
|
+
describe('#getConnectedWebSocketUrl()', function () {
|
|
1094
|
+
it('returns the connected websocket url for the session', function () {
|
|
1095
|
+
mobiusSocket.sockets.set(mobiusSocket.defaultSessionId, {
|
|
1096
|
+
connected: true,
|
|
1097
|
+
url: 'ws://connected-url.com'
|
|
1098
|
+
});
|
|
1099
|
+
_testHelperChai.assert.equal(mobiusSocket.getConnectedWebSocketUrl(), 'ws://connected-url.com');
|
|
1100
|
+
});
|
|
1101
|
+
it('returns undefined when the session is not connected', function () {
|
|
1102
|
+
mobiusSocket.sockets.set(mobiusSocket.defaultSessionId, {
|
|
1103
|
+
connected: false,
|
|
1104
|
+
url: 'ws://disconnected-url.com'
|
|
1105
|
+
});
|
|
1106
|
+
_testHelperChai.assert.isUndefined(mobiusSocket.getConnectedWebSocketUrl());
|
|
1107
|
+
});
|
|
1108
|
+
});
|
|
1109
|
+
describe('#_emit()', function () {
|
|
1110
|
+
it('emits Error-safe events and log the error with the call parameters', function () {
|
|
1111
|
+
var error = 'error';
|
|
1112
|
+
var event = {
|
|
1113
|
+
data: 'some data'
|
|
1114
|
+
};
|
|
1115
|
+
mobiusSocket.on('break', function () {
|
|
1116
|
+
throw error;
|
|
1117
|
+
});
|
|
1118
|
+
_sinon.default.stub(mobiusSocket.logger, 'error');
|
|
1119
|
+
return _promise.default.resolve(mobiusSocket._emit(mobiusSocket.defaultSessionId, 'break', event)).then(function (res) {
|
|
1120
|
+
_testHelperChai.assert.calledWith(mobiusSocket.logger.error, 'MobiusSocket: error occurred in event handler:', error, ' with args: ', [mobiusSocket.defaultSessionId, 'break', event]);
|
|
1121
|
+
return res;
|
|
1122
|
+
});
|
|
1123
|
+
});
|
|
1124
|
+
});
|
|
1125
|
+
describe('#_applyOverrides()', function () {
|
|
1126
|
+
var lastSeenActivityDate = 'Some date';
|
|
1127
|
+
var lastReadableActivityDate = 'Some other date';
|
|
1128
|
+
it('merges a single header field with data', function () {
|
|
1129
|
+
var envelope = {
|
|
1130
|
+
headers: {
|
|
1131
|
+
'data.activity.target.lastSeenActivityDate': lastSeenActivityDate
|
|
1132
|
+
},
|
|
1133
|
+
data: {
|
|
1134
|
+
activity: {}
|
|
1135
|
+
}
|
|
1136
|
+
};
|
|
1137
|
+
mobiusSocket._applyOverrides(envelope);
|
|
1138
|
+
_testHelperChai.assert.equal(envelope.data.activity.target.lastSeenActivityDate, lastSeenActivityDate);
|
|
1139
|
+
});
|
|
1140
|
+
it('merges a multiple header fields with data', function () {
|
|
1141
|
+
var envelope = {
|
|
1142
|
+
headers: {
|
|
1143
|
+
'data.activity.target.lastSeenActivityDate': lastSeenActivityDate,
|
|
1144
|
+
'data.activity.target.lastReadableActivityDate': lastReadableActivityDate
|
|
1145
|
+
},
|
|
1146
|
+
data: {
|
|
1147
|
+
activity: {}
|
|
1148
|
+
}
|
|
1149
|
+
};
|
|
1150
|
+
mobiusSocket._applyOverrides(envelope);
|
|
1151
|
+
_testHelperChai.assert.equal(envelope.data.activity.target.lastSeenActivityDate, lastSeenActivityDate);
|
|
1152
|
+
_testHelperChai.assert.equal(envelope.data.activity.target.lastReadableActivityDate, lastReadableActivityDate);
|
|
1153
|
+
});
|
|
1154
|
+
it('merges headers when MobiusSocket messages arrive', function () {
|
|
1155
|
+
var envelope = {
|
|
1156
|
+
headers: {
|
|
1157
|
+
'data.activity.target.lastSeenActivityDate': lastSeenActivityDate
|
|
1158
|
+
},
|
|
1159
|
+
data: {
|
|
1160
|
+
activity: {}
|
|
1161
|
+
}
|
|
1162
|
+
};
|
|
1163
|
+
mobiusSocket._applyOverrides(envelope);
|
|
1164
|
+
_testHelperChai.assert.equal(envelope.data.activity.target.lastSeenActivityDate, lastSeenActivityDate);
|
|
1165
|
+
});
|
|
1166
|
+
});
|
|
1167
|
+
describe('#_setTimeOffset', function () {
|
|
1168
|
+
it('sets mercuryTimeOffset based on the difference between wsWriteTimestamp and now', function () {
|
|
1169
|
+
var event = {
|
|
1170
|
+
data: {
|
|
1171
|
+
wsWriteTimestamp: (0, _now.default)() - 60000
|
|
1172
|
+
}
|
|
1173
|
+
};
|
|
1174
|
+
_testHelperChai.assert.isUndefined(mobiusSocket.mercuryTimeOffset);
|
|
1175
|
+
mobiusSocket._setTimeOffset('mobius-websocket-session', event);
|
|
1176
|
+
_testHelperChai.assert.isDefined(mobiusSocket.mercuryTimeOffset);
|
|
1177
|
+
_testHelperChai.assert.isTrue(mobiusSocket.mercuryTimeOffset > 0);
|
|
1178
|
+
});
|
|
1179
|
+
it('handles negative offsets', function () {
|
|
1180
|
+
var event = {
|
|
1181
|
+
data: {
|
|
1182
|
+
wsWriteTimestamp: (0, _now.default)() + 60000
|
|
1183
|
+
}
|
|
1184
|
+
};
|
|
1185
|
+
mobiusSocket._setTimeOffset('mobius-websocket-session', event);
|
|
1186
|
+
_testHelperChai.assert.isTrue(mobiusSocket.mercuryTimeOffset < 0);
|
|
1187
|
+
});
|
|
1188
|
+
it('handles invalid wsWriteTimestamp', function () {
|
|
1189
|
+
var invalidTimestamps = [null, -1, 'invalid', undefined];
|
|
1190
|
+
invalidTimestamps.forEach(function (invalidTimestamp) {
|
|
1191
|
+
var event = {
|
|
1192
|
+
data: {
|
|
1193
|
+
wsWriteTimestamp: invalidTimestamp
|
|
1194
|
+
}
|
|
1195
|
+
};
|
|
1196
|
+
mobiusSocket._setTimeOffset('mobius-websocket-session', event);
|
|
1197
|
+
_testHelperChai.assert.isUndefined(mobiusSocket.mercuryTimeOffset);
|
|
1198
|
+
});
|
|
1199
|
+
});
|
|
1200
|
+
});
|
|
1201
|
+
describe('#_prepareUrl()', function () {
|
|
1202
|
+
it('returns the provided URL as-is (no Mercury URL transforms)', function () {
|
|
1203
|
+
return mobiusSocket._prepareUrl('ws://provided.com').then(function (wsUrl) {
|
|
1204
|
+
_testHelperChai.assert.equal(wsUrl, 'ws://provided.com');
|
|
1205
|
+
});
|
|
1206
|
+
});
|
|
1207
|
+
it('falls back to device webSocketUrl when no URL is provided', function () {
|
|
1208
|
+
return mobiusSocket._prepareUrl().then(function (wsUrl) {
|
|
1209
|
+
_testHelperChai.assert.equal(wsUrl, 'ws://example.com');
|
|
1210
|
+
});
|
|
1211
|
+
});
|
|
1212
|
+
});
|
|
1213
|
+
describe('shutdown protocol', function () {
|
|
1214
|
+
describe('#_handleImminentShutdown()', function () {
|
|
1215
|
+
var connectWithBackoffStub;
|
|
1216
|
+
var sessionId = 'mobius-websocket-session';
|
|
1217
|
+
beforeEach(function () {
|
|
1218
|
+
mobiusSocket.connected = true;
|
|
1219
|
+
mobiusSocket.sockets.set(sessionId, {
|
|
1220
|
+
url: 'ws://old-socket.com',
|
|
1221
|
+
removeAllListeners: _sinon.default.stub()
|
|
1222
|
+
});
|
|
1223
|
+
mobiusSocket.socket = mobiusSocket.sockets.get(sessionId);
|
|
1224
|
+
connectWithBackoffStub = _sinon.default.stub(mobiusSocket, '_connectWithBackoff');
|
|
1225
|
+
connectWithBackoffStub.returns(_promise.default.resolve());
|
|
1226
|
+
_sinon.default.stub(mobiusSocket, '_emit');
|
|
1227
|
+
});
|
|
1228
|
+
afterEach(function () {
|
|
1229
|
+
connectWithBackoffStub.restore();
|
|
1230
|
+
mobiusSocket._emit.restore();
|
|
1231
|
+
mobiusSocket.sockets.clear();
|
|
1232
|
+
});
|
|
1233
|
+
it('should be idempotent - no-op if already in progress', function () {
|
|
1234
|
+
// Simulate an existing switchover in progress by seeding the backoff map
|
|
1235
|
+
mobiusSocket._shutdownSwitchoverBackoffCalls.set(sessionId, {
|
|
1236
|
+
placeholder: true
|
|
1237
|
+
});
|
|
1238
|
+
mobiusSocket._handleImminentShutdown(sessionId);
|
|
1239
|
+
_testHelperChai.assert.notCalled(connectWithBackoffStub);
|
|
1240
|
+
});
|
|
1241
|
+
it('should set switchover flags when called', function () {
|
|
1242
|
+
mobiusSocket._handleImminentShutdown(sessionId);
|
|
1243
|
+
_testHelperChai.assert.calledOnce(connectWithBackoffStub);
|
|
1244
|
+
var callArgs = connectWithBackoffStub.firstCall.args;
|
|
1245
|
+
_testHelperChai.assert.isUndefined(callArgs[0]); // webSocketUrl
|
|
1246
|
+
_testHelperChai.assert.equal(callArgs[1], sessionId); // sessionId
|
|
1247
|
+
_testHelperChai.assert.isObject(callArgs[2]); // context
|
|
1248
|
+
_testHelperChai.assert.isTrue(callArgs[2].isShutdownSwitchover);
|
|
1249
|
+
_testHelperChai.assert.isObject(callArgs[2].attemptOptions);
|
|
1250
|
+
_testHelperChai.assert.isTrue(callArgs[2].attemptOptions.isShutdownSwitchover);
|
|
1251
|
+
});
|
|
1252
|
+
it('should call _connectWithBackoff with correct parameters', function (done) {
|
|
1253
|
+
mobiusSocket._handleImminentShutdown(sessionId);
|
|
1254
|
+
process.nextTick(function () {
|
|
1255
|
+
_testHelperChai.assert.calledOnce(connectWithBackoffStub);
|
|
1256
|
+
var callArgs = connectWithBackoffStub.firstCall.args;
|
|
1257
|
+
_testHelperChai.assert.isUndefined(callArgs[0]); // webSocketUrl
|
|
1258
|
+
_testHelperChai.assert.equal(callArgs[1], sessionId); // sessionId
|
|
1259
|
+
_testHelperChai.assert.isObject(callArgs[2]); // context
|
|
1260
|
+
_testHelperChai.assert.isTrue(callArgs[2].isShutdownSwitchover);
|
|
1261
|
+
_testHelperChai.assert.isObject(callArgs[2].attemptOptions);
|
|
1262
|
+
_testHelperChai.assert.isTrue(callArgs[2].attemptOptions.isShutdownSwitchover);
|
|
1263
|
+
done();
|
|
1264
|
+
});
|
|
1265
|
+
});
|
|
1266
|
+
it('should handle exceptions during switchover', function () {
|
|
1267
|
+
connectWithBackoffStub.restore();
|
|
1268
|
+
_sinon.default.stub(mobiusSocket, '_connectWithBackoff').throws(new Error('Connection failed'));
|
|
1269
|
+
mobiusSocket._handleImminentShutdown(sessionId);
|
|
1270
|
+
|
|
1271
|
+
// When an exception happens synchronously, the placeholder entry
|
|
1272
|
+
// should be removed from the map.
|
|
1273
|
+
var switchoverCall = mobiusSocket._shutdownSwitchoverBackoffCalls.get(sessionId);
|
|
1274
|
+
_testHelperChai.assert.isUndefined(switchoverCall);
|
|
1275
|
+
mobiusSocket._connectWithBackoff.restore();
|
|
1276
|
+
});
|
|
1277
|
+
});
|
|
1278
|
+
describe('#_onmessage() with shutdown message', function () {
|
|
1279
|
+
beforeEach(function () {
|
|
1280
|
+
_sinon.default.stub(mobiusSocket, '_handleImminentShutdown');
|
|
1281
|
+
_sinon.default.stub(mobiusSocket, '_emit');
|
|
1282
|
+
_sinon.default.stub(mobiusSocket, '_setTimeOffset');
|
|
1283
|
+
});
|
|
1284
|
+
afterEach(function () {
|
|
1285
|
+
mobiusSocket._handleImminentShutdown.restore();
|
|
1286
|
+
mobiusSocket._emit.restore();
|
|
1287
|
+
mobiusSocket._setTimeOffset.restore();
|
|
1288
|
+
});
|
|
1289
|
+
it('should trigger _handleImminentShutdown on shutdown message', function () {
|
|
1290
|
+
var shutdownEvent = {
|
|
1291
|
+
data: {
|
|
1292
|
+
type: 'shutdown'
|
|
1293
|
+
}
|
|
1294
|
+
};
|
|
1295
|
+
var result = mobiusSocket._onmessage(mobiusSocket.defaultSessionId, shutdownEvent);
|
|
1296
|
+
_testHelperChai.assert.calledOnce(mobiusSocket._handleImminentShutdown);
|
|
1297
|
+
_testHelperChai.assert.calledWith(mobiusSocket._emit, mobiusSocket.defaultSessionId, 'event:mercury_shutdown_imminent', shutdownEvent.data);
|
|
1298
|
+
_testHelperChai.assert.instanceOf(result, _promise.default);
|
|
1299
|
+
});
|
|
1300
|
+
it('should handle shutdown message without additional data gracefully', function () {
|
|
1301
|
+
var shutdownEvent = {
|
|
1302
|
+
data: {
|
|
1303
|
+
type: 'shutdown'
|
|
1304
|
+
}
|
|
1305
|
+
};
|
|
1306
|
+
mobiusSocket._onmessage(mobiusSocket.defaultSessionId, shutdownEvent);
|
|
1307
|
+
_testHelperChai.assert.calledOnce(mobiusSocket._handleImminentShutdown);
|
|
1308
|
+
});
|
|
1309
|
+
it('should not trigger shutdown handling for non-shutdown messages', function () {
|
|
1310
|
+
var regularEvent = {
|
|
1311
|
+
data: {
|
|
1312
|
+
type: 'regular',
|
|
1313
|
+
data: {
|
|
1314
|
+
eventType: 'conversation.activity'
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
};
|
|
1318
|
+
mobiusSocket._onmessage(mobiusSocket.defaultSessionId, regularEvent);
|
|
1319
|
+
_testHelperChai.assert.notCalled(mobiusSocket._handleImminentShutdown);
|
|
1320
|
+
});
|
|
1321
|
+
});
|
|
1322
|
+
describe('#_onmessage() with missing data or eventType', function () {
|
|
1323
|
+
beforeEach(function () {
|
|
1324
|
+
_sinon.default.stub(mobiusSocket, '_emit');
|
|
1325
|
+
_sinon.default.stub(mobiusSocket, '_setTimeOffset');
|
|
1326
|
+
_sinon.default.stub(mobiusSocket, '_applyOverrides');
|
|
1327
|
+
});
|
|
1328
|
+
afterEach(function () {
|
|
1329
|
+
mobiusSocket._emit.restore();
|
|
1330
|
+
mobiusSocket._setTimeOffset.restore();
|
|
1331
|
+
mobiusSocket._applyOverrides.restore();
|
|
1332
|
+
});
|
|
1333
|
+
it('should not throw when envelope.data is undefined', function () {
|
|
1334
|
+
var event = {
|
|
1335
|
+
data: {
|
|
1336
|
+
type: 'someType'
|
|
1337
|
+
// no nested data property
|
|
1338
|
+
}
|
|
1339
|
+
};
|
|
1340
|
+
var result = mobiusSocket._onmessage(mobiusSocket.defaultSessionId, event);
|
|
1341
|
+
_testHelperChai.assert.instanceOf(result, _promise.default);
|
|
1342
|
+
_testHelperChai.assert.calledWith(mobiusSocket._emit, mobiusSocket.defaultSessionId, 'event', event.data);
|
|
1343
|
+
});
|
|
1344
|
+
it('should not throw when data.eventType is undefined', function () {
|
|
1345
|
+
var event = {
|
|
1346
|
+
data: {
|
|
1347
|
+
type: 'someType',
|
|
1348
|
+
data: {
|
|
1349
|
+
// no eventType property
|
|
1350
|
+
someField: 'value'
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
};
|
|
1354
|
+
var result = mobiusSocket._onmessage(mobiusSocket.defaultSessionId, event);
|
|
1355
|
+
_testHelperChai.assert.instanceOf(result, _promise.default);
|
|
1356
|
+
_testHelperChai.assert.calledWith(mobiusSocket._emit, mobiusSocket.defaultSessionId, 'event', event.data);
|
|
1357
|
+
});
|
|
1358
|
+
it('should emit generic event for messages without eventType (e.g. subscription responses)', function () {
|
|
1359
|
+
var event = {
|
|
1360
|
+
data: {
|
|
1361
|
+
id: 'msg-123',
|
|
1362
|
+
sequenceNumber: 5,
|
|
1363
|
+
data: {
|
|
1364
|
+
statusCode: 200
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
};
|
|
1368
|
+
var result = mobiusSocket._onmessage(mobiusSocket.defaultSessionId, event);
|
|
1369
|
+
_testHelperChai.assert.instanceOf(result, _promise.default);
|
|
1370
|
+
_testHelperChai.assert.calledOnce(mobiusSocket._emit);
|
|
1371
|
+
_testHelperChai.assert.calledWith(mobiusSocket._emit, mobiusSocket.defaultSessionId, 'event', event.data);
|
|
1372
|
+
});
|
|
1373
|
+
it('should still process messages with a valid eventType', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee1() {
|
|
1374
|
+
var event;
|
|
1375
|
+
return _regenerator.default.wrap(function (_context1) {
|
|
1376
|
+
while (1) switch (_context1.prev = _context1.next) {
|
|
1377
|
+
case 0:
|
|
1378
|
+
event = {
|
|
1379
|
+
data: {
|
|
1380
|
+
data: {
|
|
1381
|
+
eventType: 'conversation.activity'
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
};
|
|
1385
|
+
_context1.next = 1;
|
|
1386
|
+
return mobiusSocket._onmessage(mobiusSocket.defaultSessionId, event);
|
|
1387
|
+
case 1:
|
|
1388
|
+
// Normal flow emits namespace-specific events after processing handlers.
|
|
1389
|
+
// The early-return guard only emits 'event', so asserting these proves the normal path was taken.
|
|
1390
|
+
_testHelperChai.assert.calledWith(mobiusSocket._emit, mobiusSocket.defaultSessionId, 'event:conversation', event.data);
|
|
1391
|
+
_testHelperChai.assert.calledWith(mobiusSocket._emit, mobiusSocket.defaultSessionId, 'event:conversation.activity', event.data);
|
|
1392
|
+
case 2:
|
|
1393
|
+
case "end":
|
|
1394
|
+
return _context1.stop();
|
|
1395
|
+
}
|
|
1396
|
+
}, _callee1);
|
|
1397
|
+
})));
|
|
1398
|
+
});
|
|
1399
|
+
describe('#_onmessage() async_event deduplication', function () {
|
|
1400
|
+
var originalDedupCacheMaxSize;
|
|
1401
|
+
beforeEach(function () {
|
|
1402
|
+
originalDedupCacheMaxSize = mobiusSocket.config.dedupCacheMaxSize;
|
|
1403
|
+
});
|
|
1404
|
+
afterEach(function () {
|
|
1405
|
+
mobiusSocket.config.dedupCacheMaxSize = originalDedupCacheMaxSize;
|
|
1406
|
+
});
|
|
1407
|
+
it('suppresses duplicate async_event messages for the same session', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee10() {
|
|
1408
|
+
var emitSpy;
|
|
1409
|
+
return _regenerator.default.wrap(function (_context10) {
|
|
1410
|
+
while (1) switch (_context10.prev = _context10.next) {
|
|
1411
|
+
case 0:
|
|
1412
|
+
emitSpy = _sinon.default.spy(mobiusSocket, '_emit');
|
|
1413
|
+
_context10.next = 1;
|
|
1414
|
+
return mobiusSocket._onmessage('session-a', createAsyncEvent('evt-1'));
|
|
1415
|
+
case 1:
|
|
1416
|
+
_context10.next = 2;
|
|
1417
|
+
return mobiusSocket._onmessage('session-a', createAsyncEvent('evt-1'));
|
|
1418
|
+
case 2:
|
|
1419
|
+
_testHelperChai.assert.equal(countGenericEventEmits(emitSpy, 'session-a'), 1);
|
|
1420
|
+
emitSpy.restore();
|
|
1421
|
+
case 3:
|
|
1422
|
+
case "end":
|
|
1423
|
+
return _context10.stop();
|
|
1424
|
+
}
|
|
1425
|
+
}, _callee10);
|
|
1426
|
+
})));
|
|
1427
|
+
it('suppresses duplicate async_event messages across socket replacement without disconnect', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee11() {
|
|
1428
|
+
var sessionId, emitSpy;
|
|
1429
|
+
return _regenerator.default.wrap(function (_context11) {
|
|
1430
|
+
while (1) switch (_context11.prev = _context11.next) {
|
|
1431
|
+
case 0:
|
|
1432
|
+
sessionId = 'session-a';
|
|
1433
|
+
emitSpy = _sinon.default.spy(mobiusSocket, '_emit');
|
|
1434
|
+
mobiusSocket.sockets.set(sessionId, createSessionSocket());
|
|
1435
|
+
_context11.next = 1;
|
|
1436
|
+
return mobiusSocket._onmessage(sessionId, createAsyncEvent('evt-2'));
|
|
1437
|
+
case 1:
|
|
1438
|
+
mobiusSocket.sockets.set(sessionId, createSessionSocket());
|
|
1439
|
+
_context11.next = 2;
|
|
1440
|
+
return mobiusSocket._onmessage(sessionId, createAsyncEvent('evt-2'));
|
|
1441
|
+
case 2:
|
|
1442
|
+
_testHelperChai.assert.equal(countGenericEventEmits(emitSpy, sessionId), 1);
|
|
1443
|
+
emitSpy.restore();
|
|
1444
|
+
case 3:
|
|
1445
|
+
case "end":
|
|
1446
|
+
return _context11.stop();
|
|
1447
|
+
}
|
|
1448
|
+
}, _callee11);
|
|
1449
|
+
})));
|
|
1450
|
+
it('evicts only the oldest eventId when the dedup cache exceeds max size', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee12() {
|
|
1451
|
+
var emitSpy;
|
|
1452
|
+
return _regenerator.default.wrap(function (_context12) {
|
|
1453
|
+
while (1) switch (_context12.prev = _context12.next) {
|
|
1454
|
+
case 0:
|
|
1455
|
+
emitSpy = _sinon.default.spy(mobiusSocket, '_emit');
|
|
1456
|
+
mobiusSocket.config.dedupCacheMaxSize = 3;
|
|
1457
|
+
_context12.next = 1;
|
|
1458
|
+
return mobiusSocket._onmessage('session-a', createAsyncEvent('e1'));
|
|
1459
|
+
case 1:
|
|
1460
|
+
_context12.next = 2;
|
|
1461
|
+
return mobiusSocket._onmessage('session-a', createAsyncEvent('e2'));
|
|
1462
|
+
case 2:
|
|
1463
|
+
_context12.next = 3;
|
|
1464
|
+
return mobiusSocket._onmessage('session-a', createAsyncEvent('e3'));
|
|
1465
|
+
case 3:
|
|
1466
|
+
_context12.next = 4;
|
|
1467
|
+
return mobiusSocket._onmessage('session-a', createAsyncEvent('e4'));
|
|
1468
|
+
case 4:
|
|
1469
|
+
_context12.next = 5;
|
|
1470
|
+
return mobiusSocket._onmessage('session-a', createAsyncEvent('e2'));
|
|
1471
|
+
case 5:
|
|
1472
|
+
_context12.next = 6;
|
|
1473
|
+
return mobiusSocket._onmessage('session-a', createAsyncEvent('e1'));
|
|
1474
|
+
case 6:
|
|
1475
|
+
_testHelperChai.assert.equal(countGenericEventEmits(emitSpy, 'session-a'), 5);
|
|
1476
|
+
emitSpy.restore();
|
|
1477
|
+
case 7:
|
|
1478
|
+
case "end":
|
|
1479
|
+
return _context12.stop();
|
|
1480
|
+
}
|
|
1481
|
+
}, _callee12);
|
|
1482
|
+
})));
|
|
1483
|
+
it('clears the session dedup cache on disconnect', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee13() {
|
|
1484
|
+
var sessionId, emitSpy;
|
|
1485
|
+
return _regenerator.default.wrap(function (_context13) {
|
|
1486
|
+
while (1) switch (_context13.prev = _context13.next) {
|
|
1487
|
+
case 0:
|
|
1488
|
+
sessionId = 'session-a';
|
|
1489
|
+
emitSpy = _sinon.default.spy(mobiusSocket, '_emit');
|
|
1490
|
+
_context13.next = 1;
|
|
1491
|
+
return mobiusSocket._onmessage(sessionId, createAsyncEvent('evt-3'));
|
|
1492
|
+
case 1:
|
|
1493
|
+
mobiusSocket.sockets.set(sessionId, createSessionSocket());
|
|
1494
|
+
_context13.next = 2;
|
|
1495
|
+
return mobiusSocket.disconnect(undefined, sessionId);
|
|
1496
|
+
case 2:
|
|
1497
|
+
_context13.next = 3;
|
|
1498
|
+
return mobiusSocket._onmessage(sessionId, createAsyncEvent('evt-3'));
|
|
1499
|
+
case 3:
|
|
1500
|
+
_testHelperChai.assert.equal(countGenericEventEmits(emitSpy, sessionId), 2);
|
|
1501
|
+
emitSpy.restore();
|
|
1502
|
+
case 4:
|
|
1503
|
+
case "end":
|
|
1504
|
+
return _context13.stop();
|
|
1505
|
+
}
|
|
1506
|
+
}, _callee13);
|
|
1507
|
+
})));
|
|
1508
|
+
});
|
|
1509
|
+
describe('#_getEventHandlers()', function () {
|
|
1510
|
+
it('should return an empty array when eventType is undefined', function () {
|
|
1511
|
+
var result = mobiusSocket._getEventHandlers(undefined);
|
|
1512
|
+
_testHelperChai.assert.deepEqual(result, []);
|
|
1513
|
+
});
|
|
1514
|
+
it('should return an empty array when eventType is null', function () {
|
|
1515
|
+
var result = mobiusSocket._getEventHandlers(null);
|
|
1516
|
+
_testHelperChai.assert.deepEqual(result, []);
|
|
1517
|
+
});
|
|
1518
|
+
it('should return an empty array when eventType is an empty string', function () {
|
|
1519
|
+
var result = mobiusSocket._getEventHandlers('');
|
|
1520
|
+
_testHelperChai.assert.deepEqual(result, []);
|
|
1521
|
+
});
|
|
1522
|
+
it('should return an empty array when namespace is not registered', function () {
|
|
1523
|
+
var result = mobiusSocket._getEventHandlers('unknownNamespace.someEvent');
|
|
1524
|
+
_testHelperChai.assert.deepEqual(result, []);
|
|
1525
|
+
});
|
|
1526
|
+
});
|
|
1527
|
+
describe('#_onclose() with code 4001 (shutdown replacement)', function () {
|
|
1528
|
+
var mockSocket;
|
|
1529
|
+
var anotherSocket;
|
|
1530
|
+
beforeEach(function () {
|
|
1531
|
+
mockSocket = {
|
|
1532
|
+
url: 'ws://active-socket.com',
|
|
1533
|
+
removeAllListeners: _sinon.default.stub()
|
|
1534
|
+
};
|
|
1535
|
+
anotherSocket = {
|
|
1536
|
+
url: 'ws://old-socket.com',
|
|
1537
|
+
removeAllListeners: _sinon.default.stub()
|
|
1538
|
+
};
|
|
1539
|
+
mobiusSocket.socket = mockSocket;
|
|
1540
|
+
mobiusSocket.sockets.set(mobiusSocket.defaultSessionId, mockSocket);
|
|
1541
|
+
mobiusSocket.connected = true;
|
|
1542
|
+
_sinon.default.stub(mobiusSocket, '_emit');
|
|
1543
|
+
_sinon.default.stub(mobiusSocket, '_reconnect');
|
|
1544
|
+
});
|
|
1545
|
+
afterEach(function () {
|
|
1546
|
+
mobiusSocket._emit.restore();
|
|
1547
|
+
mobiusSocket._reconnect.restore();
|
|
1548
|
+
});
|
|
1549
|
+
it('should handle active socket close with 4001 - permanent failure', function () {
|
|
1550
|
+
var closeEvent = {
|
|
1551
|
+
code: 4001,
|
|
1552
|
+
reason: 'replaced during shutdown'
|
|
1553
|
+
};
|
|
1554
|
+
mobiusSocket._onclose(mobiusSocket.defaultSessionId, closeEvent, mockSocket);
|
|
1555
|
+
_testHelperChai.assert.calledWith(mobiusSocket._emit, mobiusSocket.defaultSessionId, 'offline.permanent', closeEvent);
|
|
1556
|
+
_testHelperChai.assert.notCalled(mobiusSocket._reconnect); // No reconnect for 4001 on active socket
|
|
1557
|
+
_testHelperChai.assert.isFalse(mobiusSocket.connected);
|
|
1558
|
+
});
|
|
1559
|
+
it('should handle non-active socket close with 4001 - no reconnect needed', function () {
|
|
1560
|
+
var closeEvent = {
|
|
1561
|
+
code: 4001,
|
|
1562
|
+
reason: 'replaced during shutdown'
|
|
1563
|
+
};
|
|
1564
|
+
mobiusSocket._onclose(mobiusSocket.defaultSessionId, closeEvent, anotherSocket);
|
|
1565
|
+
_testHelperChai.assert.calledWith(mobiusSocket._emit, mobiusSocket.defaultSessionId, 'offline.replaced', closeEvent);
|
|
1566
|
+
_testHelperChai.assert.notCalled(mobiusSocket._reconnect);
|
|
1567
|
+
_testHelperChai.assert.isTrue(mobiusSocket.connected); // Should remain connected
|
|
1568
|
+
_testHelperChai.assert.strictEqual(mobiusSocket.socket, mockSocket);
|
|
1569
|
+
});
|
|
1570
|
+
it('should distinguish between active and non-active socket closes', function () {
|
|
1571
|
+
var closeEvent = {
|
|
1572
|
+
code: 4001,
|
|
1573
|
+
reason: 'replaced during shutdown'
|
|
1574
|
+
};
|
|
1575
|
+
|
|
1576
|
+
// Test non-active socket
|
|
1577
|
+
mobiusSocket._onclose(mobiusSocket.defaultSessionId, closeEvent, anotherSocket);
|
|
1578
|
+
_testHelperChai.assert.calledWith(mobiusSocket._emit, mobiusSocket.defaultSessionId, 'offline.replaced', closeEvent);
|
|
1579
|
+
|
|
1580
|
+
// Reset the spy call history
|
|
1581
|
+
mobiusSocket._emit.resetHistory();
|
|
1582
|
+
|
|
1583
|
+
// Test active socket
|
|
1584
|
+
mobiusSocket.sockets.set(mobiusSocket.defaultSessionId, mockSocket);
|
|
1585
|
+
mobiusSocket._onclose(mobiusSocket.defaultSessionId, closeEvent, mockSocket);
|
|
1586
|
+
_testHelperChai.assert.calledWith(mobiusSocket._emit, mobiusSocket.defaultSessionId, 'offline.permanent', closeEvent);
|
|
1587
|
+
});
|
|
1588
|
+
it('should handle missing sourceSocket parameter (treats as non-active)', function () {
|
|
1589
|
+
var closeEvent = {
|
|
1590
|
+
code: 4001,
|
|
1591
|
+
reason: 'replaced during shutdown'
|
|
1592
|
+
};
|
|
1593
|
+
mobiusSocket._onclose(mobiusSocket.defaultSessionId, closeEvent); // No sourceSocket parameter
|
|
1594
|
+
|
|
1595
|
+
// With simplified logic, undefined !== this.socket, so isActiveSocket = false
|
|
1596
|
+
_testHelperChai.assert.calledWith(mobiusSocket._emit, mobiusSocket.defaultSessionId, 'offline.replaced', closeEvent);
|
|
1597
|
+
_testHelperChai.assert.notCalled(mobiusSocket._reconnect);
|
|
1598
|
+
});
|
|
1599
|
+
it('should clean up event listeners from non-active socket when it closes', function () {
|
|
1600
|
+
var closeEvent = {
|
|
1601
|
+
code: 4001,
|
|
1602
|
+
reason: 'replaced during shutdown'
|
|
1603
|
+
};
|
|
1604
|
+
|
|
1605
|
+
// Close non-active socket (not the active one)
|
|
1606
|
+
mobiusSocket._onclose(mobiusSocket.defaultSessionId, closeEvent, anotherSocket);
|
|
1607
|
+
|
|
1608
|
+
// Verify listeners were removed from the old socket
|
|
1609
|
+
// The _onclose method checks if sourceSocket !== this.socket (non-active)
|
|
1610
|
+
// and then calls removeAllListeners in the else branch
|
|
1611
|
+
_testHelperChai.assert.calledOnce(anotherSocket.removeAllListeners);
|
|
1612
|
+
});
|
|
1613
|
+
it('should not clean up listeners from active socket listeners until close handler runs', function () {
|
|
1614
|
+
var closeEvent = {
|
|
1615
|
+
code: 4001,
|
|
1616
|
+
reason: 'replaced during shutdown'
|
|
1617
|
+
};
|
|
1618
|
+
|
|
1619
|
+
// Close active socket
|
|
1620
|
+
mobiusSocket._onclose(mobiusSocket.defaultSessionId, closeEvent, mockSocket);
|
|
1621
|
+
|
|
1622
|
+
// Verify listeners were removed from active socket
|
|
1623
|
+
_testHelperChai.assert.calledOnce(mockSocket.removeAllListeners);
|
|
1624
|
+
});
|
|
1625
|
+
});
|
|
1626
|
+
describe('shutdown switchover with retry logic', function () {
|
|
1627
|
+
var connectWithBackoffStub;
|
|
1628
|
+
var sessionId = 'mobius-websocket-session';
|
|
1629
|
+
beforeEach(function () {
|
|
1630
|
+
mobiusSocket.connected = true;
|
|
1631
|
+
mobiusSocket.sockets.set(sessionId, {
|
|
1632
|
+
url: 'ws://old-socket.com',
|
|
1633
|
+
removeAllListeners: _sinon.default.stub()
|
|
1634
|
+
});
|
|
1635
|
+
mobiusSocket.socket = mobiusSocket.sockets.get(sessionId);
|
|
1636
|
+
connectWithBackoffStub = _sinon.default.stub(mobiusSocket, '_connectWithBackoff');
|
|
1637
|
+
_sinon.default.stub(mobiusSocket, '_emit');
|
|
1638
|
+
});
|
|
1639
|
+
afterEach(function () {
|
|
1640
|
+
connectWithBackoffStub.restore();
|
|
1641
|
+
mobiusSocket._emit.restore();
|
|
1642
|
+
mobiusSocket.sockets.clear();
|
|
1643
|
+
});
|
|
1644
|
+
it('should call _connectWithBackoff with shutdown switchover context', function (done) {
|
|
1645
|
+
connectWithBackoffStub.returns(_promise.default.resolve());
|
|
1646
|
+
mobiusSocket._handleImminentShutdown(sessionId);
|
|
1647
|
+
process.nextTick(function () {
|
|
1648
|
+
_testHelperChai.assert.calledOnce(connectWithBackoffStub);
|
|
1649
|
+
var callArgs = connectWithBackoffStub.firstCall.args;
|
|
1650
|
+
_testHelperChai.assert.isUndefined(callArgs[0]); // webSocketUrl
|
|
1651
|
+
_testHelperChai.assert.equal(callArgs[1], sessionId);
|
|
1652
|
+
_testHelperChai.assert.isObject(callArgs[2]);
|
|
1653
|
+
_testHelperChai.assert.isTrue(callArgs[2].isShutdownSwitchover);
|
|
1654
|
+
_testHelperChai.assert.isObject(callArgs[2].attemptOptions);
|
|
1655
|
+
_testHelperChai.assert.isTrue(callArgs[2].attemptOptions.isShutdownSwitchover);
|
|
1656
|
+
done();
|
|
1657
|
+
});
|
|
1658
|
+
});
|
|
1659
|
+
it('should set _shutdownSwitchoverInProgress flag during switchover', function () {
|
|
1660
|
+
// With the new behavior, "in progress" is represented by the presence
|
|
1661
|
+
// of an entry in _shutdownSwitchoverBackoffCalls.
|
|
1662
|
+
// Since _connectWithBackoff is stubbed in this suite, simulate its side-effect
|
|
1663
|
+
// of seeding the backoff-call map entry.
|
|
1664
|
+
connectWithBackoffStub.callsFake(function () {
|
|
1665
|
+
mobiusSocket._shutdownSwitchoverBackoffCalls.set(sessionId, {
|
|
1666
|
+
placeholder: true
|
|
1667
|
+
});
|
|
1668
|
+
return new _promise.default(function () {}); // Never resolves
|
|
1669
|
+
});
|
|
1670
|
+
mobiusSocket._handleImminentShutdown(sessionId);
|
|
1671
|
+
var switchoverBackoffCall = mobiusSocket._shutdownSwitchoverBackoffCalls.get(sessionId);
|
|
1672
|
+
_testHelperChai.assert.isOk(switchoverBackoffCall);
|
|
1673
|
+
});
|
|
1674
|
+
it('should emit success event when switchover completes', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee14() {
|
|
1675
|
+
var emitCalls, hasCompleteEvent;
|
|
1676
|
+
return _regenerator.default.wrap(function (_context14) {
|
|
1677
|
+
while (1) switch (_context14.prev = _context14.next) {
|
|
1678
|
+
case 0:
|
|
1679
|
+
connectWithBackoffStub.callsFake(function (url, sid, context) {
|
|
1680
|
+
if (context && context.attemptOptions && context.attemptOptions.onSuccess) {
|
|
1681
|
+
var mockSocket = {
|
|
1682
|
+
url: 'ws://new-socket.com'
|
|
1683
|
+
};
|
|
1684
|
+
context.attemptOptions.onSuccess(mockSocket, 'ws://new-socket.com');
|
|
1685
|
+
}
|
|
1686
|
+
return _promise.default.resolve();
|
|
1687
|
+
});
|
|
1688
|
+
mobiusSocket._handleImminentShutdown(sessionId);
|
|
1689
|
+
_context14.next = 1;
|
|
1690
|
+
return (0, _promiseTick.default)(50);
|
|
1691
|
+
case 1:
|
|
1692
|
+
emitCalls = mobiusSocket._emit.getCalls();
|
|
1693
|
+
hasCompleteEvent = emitCalls.some(function (call) {
|
|
1694
|
+
return call.args[0] === sessionId && call.args[1] === 'event:mercury_shutdown_switchover_complete';
|
|
1695
|
+
});
|
|
1696
|
+
_testHelperChai.assert.isTrue(hasCompleteEvent, 'Should emit switchover complete event');
|
|
1697
|
+
case 2:
|
|
1698
|
+
case "end":
|
|
1699
|
+
return _context14.stop();
|
|
1700
|
+
}
|
|
1701
|
+
}, _callee14);
|
|
1702
|
+
})));
|
|
1703
|
+
it('should emit failure event when switchover exhausts retries', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee15() {
|
|
1704
|
+
var testError, emitCalls, hasFailureEvent;
|
|
1705
|
+
return _regenerator.default.wrap(function (_context15) {
|
|
1706
|
+
while (1) switch (_context15.prev = _context15.next) {
|
|
1707
|
+
case 0:
|
|
1708
|
+
testError = new Error('Connection failed');
|
|
1709
|
+
connectWithBackoffStub.returns(_promise.default.reject(testError));
|
|
1710
|
+
mobiusSocket._handleImminentShutdown(sessionId);
|
|
1711
|
+
_context15.next = 1;
|
|
1712
|
+
return (0, _promiseTick.default)(50);
|
|
1713
|
+
case 1:
|
|
1714
|
+
emitCalls = mobiusSocket._emit.getCalls();
|
|
1715
|
+
hasFailureEvent = emitCalls.some(function (call) {
|
|
1716
|
+
return call.args[0] === sessionId && call.args[1] === 'event:mercury_shutdown_switchover_failed' && call.args[2] && call.args[2].reason === testError;
|
|
1717
|
+
});
|
|
1718
|
+
_testHelperChai.assert.isTrue(hasFailureEvent, 'Should emit switchover failed event');
|
|
1719
|
+
case 2:
|
|
1720
|
+
case "end":
|
|
1721
|
+
return _context15.stop();
|
|
1722
|
+
}
|
|
1723
|
+
}, _callee15);
|
|
1724
|
+
})));
|
|
1725
|
+
it('should allow old socket to be closed by server after switchover failure', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee16() {
|
|
1726
|
+
return _regenerator.default.wrap(function (_context16) {
|
|
1727
|
+
while (1) switch (_context16.prev = _context16.next) {
|
|
1728
|
+
case 0:
|
|
1729
|
+
connectWithBackoffStub.returns(_promise.default.reject(new Error('Failed')));
|
|
1730
|
+
mobiusSocket._handleImminentShutdown(sessionId);
|
|
1731
|
+
_context16.next = 1;
|
|
1732
|
+
return (0, _promiseTick.default)(50);
|
|
1733
|
+
case 1:
|
|
1734
|
+
_testHelperChai.assert.equal(mobiusSocket.socket.removeAllListeners.callCount, 0);
|
|
1735
|
+
case 2:
|
|
1736
|
+
case "end":
|
|
1737
|
+
return _context16.stop();
|
|
1738
|
+
}
|
|
1739
|
+
}, _callee16);
|
|
1740
|
+
})));
|
|
1741
|
+
});
|
|
1742
|
+
describe('#_prepareAndOpenSocket()', function () {
|
|
1743
|
+
var mockSocket;
|
|
1744
|
+
var prepareUrlStub;
|
|
1745
|
+
var getUserTokenStub;
|
|
1746
|
+
beforeEach(function () {
|
|
1747
|
+
mockSocket = {
|
|
1748
|
+
open: _sinon.default.stub().returns(_promise.default.resolve())
|
|
1749
|
+
};
|
|
1750
|
+
prepareUrlStub = _sinon.default.stub(mobiusSocket, '_prepareUrl').returns(_promise.default.resolve('ws://example.com'));
|
|
1751
|
+
getUserTokenStub = webex.credentials.getUserToken;
|
|
1752
|
+
getUserTokenStub.returns(_promise.default.resolve({
|
|
1753
|
+
toString: function toString() {
|
|
1754
|
+
return 'mock-token';
|
|
1755
|
+
}
|
|
1756
|
+
}));
|
|
1757
|
+
});
|
|
1758
|
+
afterEach(function () {
|
|
1759
|
+
prepareUrlStub.restore();
|
|
1760
|
+
});
|
|
1761
|
+
it('should prepare URL and get user token', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee17() {
|
|
1762
|
+
return _regenerator.default.wrap(function (_context17) {
|
|
1763
|
+
while (1) switch (_context17.prev = _context17.next) {
|
|
1764
|
+
case 0:
|
|
1765
|
+
_context17.next = 1;
|
|
1766
|
+
return mobiusSocket._prepareAndOpenSocket(mockSocket, 'ws://test.com', false);
|
|
1767
|
+
case 1:
|
|
1768
|
+
_testHelperChai.assert.calledOnce(prepareUrlStub);
|
|
1769
|
+
_testHelperChai.assert.calledWith(prepareUrlStub, 'ws://test.com');
|
|
1770
|
+
_testHelperChai.assert.calledOnce(getUserTokenStub);
|
|
1771
|
+
case 2:
|
|
1772
|
+
case "end":
|
|
1773
|
+
return _context17.stop();
|
|
1774
|
+
}
|
|
1775
|
+
}, _callee17);
|
|
1776
|
+
})));
|
|
1777
|
+
it('should open socket with correct options for normal connection', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee18() {
|
|
1778
|
+
var callArgs;
|
|
1779
|
+
return _regenerator.default.wrap(function (_context18) {
|
|
1780
|
+
while (1) switch (_context18.prev = _context18.next) {
|
|
1781
|
+
case 0:
|
|
1782
|
+
_context18.next = 1;
|
|
1783
|
+
return mobiusSocket._prepareAndOpenSocket(mockSocket, undefined, false);
|
|
1784
|
+
case 1:
|
|
1785
|
+
_testHelperChai.assert.calledOnce(mockSocket.open);
|
|
1786
|
+
callArgs = mockSocket.open.firstCall.args;
|
|
1787
|
+
_testHelperChai.assert.equal(callArgs[0], 'ws://example.com');
|
|
1788
|
+
_testHelperChai.assert.isObject(callArgs[1]);
|
|
1789
|
+
_testHelperChai.assert.equal(callArgs[1].token, 'mock-token');
|
|
1790
|
+
_testHelperChai.assert.isDefined(callArgs[1].forceCloseDelay);
|
|
1791
|
+
_testHelperChai.assert.isDefined(callArgs[1].wssResponseTimeout);
|
|
1792
|
+
case 2:
|
|
1793
|
+
case "end":
|
|
1794
|
+
return _context18.stop();
|
|
1795
|
+
}
|
|
1796
|
+
}, _callee18);
|
|
1797
|
+
})));
|
|
1798
|
+
it('should log with correct prefix for normal connection', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee19() {
|
|
1799
|
+
return _regenerator.default.wrap(function (_context19) {
|
|
1800
|
+
while (1) switch (_context19.prev = _context19.next) {
|
|
1801
|
+
case 0:
|
|
1802
|
+
_context19.next = 1;
|
|
1803
|
+
return mobiusSocket._prepareAndOpenSocket(mockSocket, undefined, false);
|
|
1804
|
+
case 1:
|
|
1805
|
+
// The method should complete successfully - we're testing it runs without error
|
|
1806
|
+
// Actual log message verification is complex due to existing stubs in parent scope
|
|
1807
|
+
_testHelperChai.assert.calledOnce(mockSocket.open);
|
|
1808
|
+
case 2:
|
|
1809
|
+
case "end":
|
|
1810
|
+
return _context19.stop();
|
|
1811
|
+
}
|
|
1812
|
+
}, _callee19);
|
|
1813
|
+
})));
|
|
1814
|
+
it('should log with shutdown prefix for shutdown connection', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee20() {
|
|
1815
|
+
return _regenerator.default.wrap(function (_context20) {
|
|
1816
|
+
while (1) switch (_context20.prev = _context20.next) {
|
|
1817
|
+
case 0:
|
|
1818
|
+
_context20.next = 1;
|
|
1819
|
+
return mobiusSocket._prepareAndOpenSocket(mockSocket, undefined, true);
|
|
1820
|
+
case 1:
|
|
1821
|
+
// The method should complete successfully with shutdown flag
|
|
1822
|
+
_testHelperChai.assert.calledOnce(mockSocket.open);
|
|
1823
|
+
case 2:
|
|
1824
|
+
case "end":
|
|
1825
|
+
return _context20.stop();
|
|
1826
|
+
}
|
|
1827
|
+
}, _callee20);
|
|
1828
|
+
})));
|
|
1829
|
+
it('should merge custom mobiusSocket options when provided', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee21() {
|
|
1830
|
+
var callArgs;
|
|
1831
|
+
return _regenerator.default.wrap(function (_context21) {
|
|
1832
|
+
while (1) switch (_context21.prev = _context21.next) {
|
|
1833
|
+
case 0:
|
|
1834
|
+
webex.config.defaultMobiusSocketOptions = {
|
|
1835
|
+
customOption: 'test-value',
|
|
1836
|
+
wssResponseTimeout: 99999
|
|
1837
|
+
};
|
|
1838
|
+
_context21.next = 1;
|
|
1839
|
+
return mobiusSocket._prepareAndOpenSocket(mockSocket, undefined, false);
|
|
1840
|
+
case 1:
|
|
1841
|
+
callArgs = mockSocket.open.firstCall.args;
|
|
1842
|
+
_testHelperChai.assert.equal(callArgs[1].customOption, 'test-value');
|
|
1843
|
+
_testHelperChai.assert.equal(callArgs[1].wssResponseTimeout, 99999); // Custom value overrides default
|
|
1844
|
+
case 2:
|
|
1845
|
+
case "end":
|
|
1846
|
+
return _context21.stop();
|
|
1847
|
+
}
|
|
1848
|
+
}, _callee21);
|
|
1849
|
+
})));
|
|
1850
|
+
it('should return the webSocketUrl after opening', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee22() {
|
|
1851
|
+
var result;
|
|
1852
|
+
return _regenerator.default.wrap(function (_context22) {
|
|
1853
|
+
while (1) switch (_context22.prev = _context22.next) {
|
|
1854
|
+
case 0:
|
|
1855
|
+
_context22.next = 1;
|
|
1856
|
+
return mobiusSocket._prepareAndOpenSocket(mockSocket, undefined, false);
|
|
1857
|
+
case 1:
|
|
1858
|
+
result = _context22.sent;
|
|
1859
|
+
_testHelperChai.assert.equal(result, 'ws://example.com');
|
|
1860
|
+
case 2:
|
|
1861
|
+
case "end":
|
|
1862
|
+
return _context22.stop();
|
|
1863
|
+
}
|
|
1864
|
+
}, _callee22);
|
|
1865
|
+
})));
|
|
1866
|
+
it('should handle errors during socket open', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee23() {
|
|
1867
|
+
var _t2;
|
|
1868
|
+
return _regenerator.default.wrap(function (_context23) {
|
|
1869
|
+
while (1) switch (_context23.prev = _context23.next) {
|
|
1870
|
+
case 0:
|
|
1871
|
+
mockSocket.open.returns(_promise.default.reject(new Error('Open failed')));
|
|
1872
|
+
_context23.prev = 1;
|
|
1873
|
+
_context23.next = 2;
|
|
1874
|
+
return mobiusSocket._prepareAndOpenSocket(mockSocket, undefined, false);
|
|
1875
|
+
case 2:
|
|
1876
|
+
_testHelperChai.assert.fail('Should have thrown an error');
|
|
1877
|
+
_context23.next = 4;
|
|
1878
|
+
break;
|
|
1879
|
+
case 3:
|
|
1880
|
+
_context23.prev = 3;
|
|
1881
|
+
_t2 = _context23["catch"](1);
|
|
1882
|
+
_testHelperChai.assert.equal(_t2.message, 'Open failed');
|
|
1883
|
+
case 4:
|
|
1884
|
+
case "end":
|
|
1885
|
+
return _context23.stop();
|
|
1886
|
+
}
|
|
1887
|
+
}, _callee23, null, [[1, 3]]);
|
|
1888
|
+
})));
|
|
1889
|
+
});
|
|
1890
|
+
describe('#_attemptConnection() with shutdown switchover', function () {
|
|
1891
|
+
var prepareAndOpenSocketStub;
|
|
1892
|
+
var callback;
|
|
1893
|
+
var sessionId = 'mobius-websocket-session';
|
|
1894
|
+
beforeEach(function () {
|
|
1895
|
+
prepareAndOpenSocketStub = _sinon.default.stub(mobiusSocket, '_prepareAndOpenSocket').returns(_promise.default.resolve('ws://new-socket.com'));
|
|
1896
|
+
callback = _sinon.default.stub();
|
|
1897
|
+
mobiusSocket._shutdownSwitchoverBackoffCalls.set(sessionId, {
|
|
1898
|
+
abort: _sinon.default.stub()
|
|
1899
|
+
});
|
|
1900
|
+
mobiusSocket.socket = {
|
|
1901
|
+
url: 'ws://test.com'
|
|
1902
|
+
};
|
|
1903
|
+
mobiusSocket.connected = true;
|
|
1904
|
+
_sinon.default.stub(mobiusSocket, '_emit');
|
|
1905
|
+
_sinon.default.stub(mobiusSocket, '_attachSocketEventListeners');
|
|
1906
|
+
});
|
|
1907
|
+
afterEach(function () {
|
|
1908
|
+
prepareAndOpenSocketStub.restore();
|
|
1909
|
+
mobiusSocket._emit.restore();
|
|
1910
|
+
mobiusSocket._attachSocketEventListeners.restore();
|
|
1911
|
+
mobiusSocket._shutdownSwitchoverBackoffCalls.clear();
|
|
1912
|
+
});
|
|
1913
|
+
it('should not set socket reference before opening for shutdown switchover', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee24() {
|
|
1914
|
+
var originalSocket;
|
|
1915
|
+
return _regenerator.default.wrap(function (_context24) {
|
|
1916
|
+
while (1) switch (_context24.prev = _context24.next) {
|
|
1917
|
+
case 0:
|
|
1918
|
+
originalSocket = mobiusSocket.socket;
|
|
1919
|
+
_context24.next = 1;
|
|
1920
|
+
return mobiusSocket._attemptConnection('ws://test.com', sessionId, callback, {
|
|
1921
|
+
isShutdownSwitchover: true,
|
|
1922
|
+
onSuccess: function onSuccess(newSocket, url) {
|
|
1923
|
+
_testHelperChai.assert.equal(mobiusSocket.socket, originalSocket);
|
|
1924
|
+
}
|
|
1925
|
+
});
|
|
1926
|
+
case 1:
|
|
1927
|
+
_testHelperChai.assert.equal(mobiusSocket.socket, originalSocket);
|
|
1928
|
+
case 2:
|
|
1929
|
+
case "end":
|
|
1930
|
+
return _context24.stop();
|
|
1931
|
+
}
|
|
1932
|
+
}, _callee24);
|
|
1933
|
+
})));
|
|
1934
|
+
it('should call onSuccess callback with new socket and URL for shutdown', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee25() {
|
|
1935
|
+
var onSuccessStub;
|
|
1936
|
+
return _regenerator.default.wrap(function (_context25) {
|
|
1937
|
+
while (1) switch (_context25.prev = _context25.next) {
|
|
1938
|
+
case 0:
|
|
1939
|
+
onSuccessStub = _sinon.default.stub();
|
|
1940
|
+
_context25.next = 1;
|
|
1941
|
+
return mobiusSocket._attemptConnection('ws://test.com', sessionId, callback, {
|
|
1942
|
+
isShutdownSwitchover: true,
|
|
1943
|
+
onSuccess: onSuccessStub
|
|
1944
|
+
});
|
|
1945
|
+
case 1:
|
|
1946
|
+
_testHelperChai.assert.calledOnce(onSuccessStub);
|
|
1947
|
+
_testHelperChai.assert.equal(onSuccessStub.firstCall.args[1], 'ws://new-socket.com');
|
|
1948
|
+
case 2:
|
|
1949
|
+
case "end":
|
|
1950
|
+
return _context25.stop();
|
|
1951
|
+
}
|
|
1952
|
+
}, _callee25);
|
|
1953
|
+
})));
|
|
1954
|
+
it('should emit shutdown switchover complete event', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee26() {
|
|
1955
|
+
return _regenerator.default.wrap(function (_context26) {
|
|
1956
|
+
while (1) switch (_context26.prev = _context26.next) {
|
|
1957
|
+
case 0:
|
|
1958
|
+
_context26.next = 1;
|
|
1959
|
+
return mobiusSocket._attemptConnection('ws://test.com', sessionId, callback, {
|
|
1960
|
+
isShutdownSwitchover: true,
|
|
1961
|
+
onSuccess: function onSuccess(newSocket, url) {
|
|
1962
|
+
mobiusSocket.socket = newSocket;
|
|
1963
|
+
mobiusSocket.connected = true;
|
|
1964
|
+
mobiusSocket._emit(sessionId, 'event:mercury_shutdown_switchover_complete', {
|
|
1965
|
+
url: url
|
|
1966
|
+
});
|
|
1967
|
+
}
|
|
1968
|
+
});
|
|
1969
|
+
case 1:
|
|
1970
|
+
_testHelperChai.assert.calledWith(mobiusSocket._emit, sessionId, 'event:mercury_shutdown_switchover_complete', _sinon.default.match.has('url', 'ws://new-socket.com'));
|
|
1971
|
+
case 2:
|
|
1972
|
+
case "end":
|
|
1973
|
+
return _context26.stop();
|
|
1974
|
+
}
|
|
1975
|
+
}, _callee26);
|
|
1976
|
+
})));
|
|
1977
|
+
it('should use simpler error handling for shutdown switchover failures', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee27() {
|
|
1978
|
+
return _regenerator.default.wrap(function (_context27) {
|
|
1979
|
+
while (1) switch (_context27.prev = _context27.next) {
|
|
1980
|
+
case 0:
|
|
1981
|
+
prepareAndOpenSocketStub.returns(_promise.default.reject(new Error('Connection failed')));
|
|
1982
|
+
_context27.next = 1;
|
|
1983
|
+
return mobiusSocket._attemptConnection('ws://test.com', sessionId, callback, {
|
|
1984
|
+
isShutdownSwitchover: true
|
|
1985
|
+
}).catch(function () {});
|
|
1986
|
+
case 1:
|
|
1987
|
+
_testHelperChai.assert.calledOnce(callback);
|
|
1988
|
+
_testHelperChai.assert.instanceOf(callback.firstCall.args[0], Error);
|
|
1989
|
+
case 2:
|
|
1990
|
+
case "end":
|
|
1991
|
+
return _context27.stop();
|
|
1992
|
+
}
|
|
1993
|
+
}, _callee27);
|
|
1994
|
+
})));
|
|
1995
|
+
it('should check _shutdownSwitchoverBackoffCall for shutdown connections', function () {
|
|
1996
|
+
mobiusSocket._shutdownSwitchoverBackoffCalls.clear();
|
|
1997
|
+
var result = mobiusSocket._attemptConnection('ws://test.com', sessionId, callback, {
|
|
1998
|
+
isShutdownSwitchover: true
|
|
1999
|
+
});
|
|
2000
|
+
return result.catch(function (err) {
|
|
2001
|
+
_testHelperChai.assert.instanceOf(err, Error);
|
|
2002
|
+
_testHelperChai.assert.match(err.message, /switchover backoff call/);
|
|
2003
|
+
});
|
|
2004
|
+
});
|
|
2005
|
+
});
|
|
2006
|
+
describe('#_connectWithBackoff() with shutdown switchover', function () {
|
|
2007
|
+
var sessionId = 'mobius-websocket-session';
|
|
2008
|
+
it('should use shutdown-specific parameters when called', function () {
|
|
2009
|
+
var connectWithBackoffStub = _sinon.default.stub(mobiusSocket, '_connectWithBackoff').returns(_promise.default.resolve());
|
|
2010
|
+
mobiusSocket._handleImminentShutdown(sessionId);
|
|
2011
|
+
_testHelperChai.assert.calledOnce(connectWithBackoffStub);
|
|
2012
|
+
var callArgs = connectWithBackoffStub.firstCall.args;
|
|
2013
|
+
_testHelperChai.assert.equal(callArgs[1], sessionId);
|
|
2014
|
+
_testHelperChai.assert.isObject(callArgs[2]);
|
|
2015
|
+
_testHelperChai.assert.isTrue(callArgs[2].isShutdownSwitchover);
|
|
2016
|
+
connectWithBackoffStub.restore();
|
|
2017
|
+
});
|
|
2018
|
+
it('should pass shutdown switchover options to _attemptConnection', function () {
|
|
2019
|
+
var attemptStub = _sinon.default.stub(mobiusSocket, '_attemptConnection');
|
|
2020
|
+
attemptStub.callsFake(function (url, sid, cb) {
|
|
2021
|
+
return cb();
|
|
2022
|
+
});
|
|
2023
|
+
var context = {
|
|
2024
|
+
isShutdownSwitchover: true,
|
|
2025
|
+
attemptOptions: {
|
|
2026
|
+
isShutdownSwitchover: true,
|
|
2027
|
+
onSuccess: function onSuccess() {}
|
|
2028
|
+
}
|
|
2029
|
+
};
|
|
2030
|
+
var promise = mobiusSocket._connectWithBackoff(undefined, sessionId, context);
|
|
2031
|
+
return promise.then(function () {
|
|
2032
|
+
_testHelperChai.assert.calledOnce(attemptStub);
|
|
2033
|
+
var callArgs = attemptStub.firstCall.args;
|
|
2034
|
+
_testHelperChai.assert.equal(callArgs[1], sessionId);
|
|
2035
|
+
_testHelperChai.assert.isObject(callArgs[3]);
|
|
2036
|
+
_testHelperChai.assert.isTrue(callArgs[3].isShutdownSwitchover);
|
|
2037
|
+
attemptStub.restore();
|
|
2038
|
+
});
|
|
2039
|
+
});
|
|
2040
|
+
it('should set and clear state flags appropriately', function () {
|
|
2041
|
+
_sinon.default.stub(mobiusSocket, '_attemptConnection').callsFake(function (url, sid, cb) {
|
|
2042
|
+
return cb();
|
|
2043
|
+
});
|
|
2044
|
+
mobiusSocket._shutdownSwitchoverBackoffCalls.set(sessionId, {
|
|
2045
|
+
placeholder: true
|
|
2046
|
+
});
|
|
2047
|
+
var promise = mobiusSocket._connectWithBackoff(undefined, sessionId, {
|
|
2048
|
+
isShutdownSwitchover: true,
|
|
2049
|
+
attemptOptions: {
|
|
2050
|
+
isShutdownSwitchover: true,
|
|
2051
|
+
onSuccess: function onSuccess() {}
|
|
2052
|
+
}
|
|
2053
|
+
});
|
|
2054
|
+
return promise.then(function () {
|
|
2055
|
+
_testHelperChai.assert.isUndefined(mobiusSocket._shutdownSwitchoverBackoffCalls.get(sessionId));
|
|
2056
|
+
mobiusSocket._attemptConnection.restore();
|
|
2057
|
+
});
|
|
2058
|
+
});
|
|
2059
|
+
});
|
|
2060
|
+
describe('#disconnect() with shutdown switchover in progress', function () {
|
|
2061
|
+
var abortStub;
|
|
2062
|
+
var sessionId = 'mobius-websocket-session';
|
|
2063
|
+
beforeEach(function () {
|
|
2064
|
+
mobiusSocket.sockets.clear();
|
|
2065
|
+
mobiusSocket.sockets.set(sessionId, {
|
|
2066
|
+
close: _sinon.default.stub().returns(_promise.default.resolve()),
|
|
2067
|
+
removeAllListeners: _sinon.default.stub()
|
|
2068
|
+
});
|
|
2069
|
+
abortStub = _sinon.default.stub();
|
|
2070
|
+
mobiusSocket._shutdownSwitchoverBackoffCalls.set(sessionId, {
|
|
2071
|
+
abort: abortStub
|
|
2072
|
+
});
|
|
2073
|
+
});
|
|
2074
|
+
it('should abort shutdown switchover backoff call on disconnect', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee28() {
|
|
2075
|
+
return _regenerator.default.wrap(function (_context28) {
|
|
2076
|
+
while (1) switch (_context28.prev = _context28.next) {
|
|
2077
|
+
case 0:
|
|
2078
|
+
_context28.next = 1;
|
|
2079
|
+
return mobiusSocket.disconnect(undefined, sessionId);
|
|
2080
|
+
case 1:
|
|
2081
|
+
_testHelperChai.assert.calledOnce(abortStub);
|
|
2082
|
+
case 2:
|
|
2083
|
+
case "end":
|
|
2084
|
+
return _context28.stop();
|
|
2085
|
+
}
|
|
2086
|
+
}, _callee28);
|
|
2087
|
+
})));
|
|
2088
|
+
it('should handle disconnect when no switchover is in progress', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee29() {
|
|
2089
|
+
return _regenerator.default.wrap(function (_context29) {
|
|
2090
|
+
while (1) switch (_context29.prev = _context29.next) {
|
|
2091
|
+
case 0:
|
|
2092
|
+
mobiusSocket._shutdownSwitchoverBackoffCalls.clear();
|
|
2093
|
+
_context29.next = 1;
|
|
2094
|
+
return mobiusSocket.disconnect(undefined, sessionId);
|
|
2095
|
+
case 1:
|
|
2096
|
+
_testHelperChai.assert.calledOnce(mobiusSocket.sockets.get(sessionId).close);
|
|
2097
|
+
case 2:
|
|
2098
|
+
case "end":
|
|
2099
|
+
return _context29.stop();
|
|
2100
|
+
}
|
|
2101
|
+
}, _callee29);
|
|
2102
|
+
})));
|
|
2103
|
+
});
|
|
2104
|
+
});
|
|
2105
|
+
});
|
|
2106
|
+
});
|
|
2107
|
+
//# sourceMappingURL=mobius-socket.test.js.map
|