@webex/calling 3.12.0-mobius-socket.16 → 3.12.0-mobius-socket.18

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.
@@ -0,0 +1,881 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime-corejs2/helpers/interopRequireDefault");
4
+ var _regenerator = _interopRequireDefault(require("@babel/runtime-corejs2/regenerator"));
5
+ var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/asyncToGenerator"));
6
+ var _uuid = require("uuid");
7
+ var _testUtil = require("../../common/testUtil");
8
+ var _types = require("../../common/types");
9
+ var _Logger = _interopRequireDefault(require("../../Logger"));
10
+ var _request = require("./request");
11
+ var _mobiusSocket = require("../../mobius-socket");
12
+ var _Metrics = require("../../Metrics");
13
+ var _types2 = require("../../Metrics/types");
14
+ var _wsFeatureFlag = require("./wsFeatureFlag");
15
+ var _constants = require("./constants");
16
+ // Mock dependencies
17
+ jest.mock('../../mobius-socket', function () {
18
+ return {
19
+ getMobiusSocketInstance: jest.fn()
20
+ };
21
+ });
22
+ jest.mock('../../Metrics', function () {
23
+ return {
24
+ getMetricManager: jest.fn()
25
+ };
26
+ });
27
+ jest.mock('./wsFeatureFlag', function () {
28
+ return {
29
+ isMobiusWssEnabled: jest.fn()
30
+ };
31
+ });
32
+ jest.mock('uuid', function () {
33
+ return {
34
+ v4: jest.fn()
35
+ };
36
+ });
37
+ describe('APIRequest', function () {
38
+ var webex;
39
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
40
+ var mockMobiusSocket;
41
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
42
+ var mockMetricManager;
43
+ var infoSpy;
44
+ var logSpy;
45
+ var warnSpy;
46
+ var errorSpy;
47
+ beforeEach(function () {
48
+ _request.APIRequest.resetInstance();
49
+ webex = (0, _testUtil.getTestUtilsWebex)();
50
+ jest.clearAllMocks();
51
+ mockMobiusSocket = {
52
+ connect: jest.fn().mockResolvedValue(undefined),
53
+ disconnect: jest.fn().mockResolvedValue(undefined),
54
+ sendWssRequest: jest.fn().mockResolvedValue({
55
+ statusCode: 200,
56
+ trackingId: 'test-tracking-id',
57
+ data: {
58
+ deviceId: 'test-device-id'
59
+ }
60
+ }),
61
+ isConnected: jest.fn().mockReturnValue(false),
62
+ getConnectedWebSocketUrl: jest.fn().mockReturnValue('wss://test.webex.com'),
63
+ on: jest.fn(),
64
+ off: jest.fn()
65
+ };
66
+ mockMetricManager = {
67
+ submitMobiusSocketMetric: jest.fn()
68
+ };
69
+ _mobiusSocket.getMobiusSocketInstance.mockReturnValue(mockMobiusSocket);
70
+ _Metrics.getMetricManager.mockReturnValue(mockMetricManager);
71
+ _wsFeatureFlag.isMobiusWssEnabled.mockReturnValue(false);
72
+ _uuid.v4.mockReturnValue('mock-uuid-12345');
73
+ infoSpy = jest.spyOn(_Logger.default, 'info');
74
+ logSpy = jest.spyOn(_Logger.default, 'log');
75
+ warnSpy = jest.spyOn(_Logger.default, 'warn');
76
+ errorSpy = jest.spyOn(_Logger.default, 'error');
77
+ });
78
+ afterEach(function () {
79
+ jest.clearAllMocks();
80
+ });
81
+ describe('Constructor and singleton pattern', function () {
82
+ it('should create instance with webex config and HTTP transport', function () {
83
+ var apiRequest = _request.APIRequest.getInstance({
84
+ webex: webex
85
+ });
86
+ expect(apiRequest).toBeDefined();
87
+ expect(_mobiusSocket.getMobiusSocketInstance).toHaveBeenCalledWith(webex);
88
+ expect(_Metrics.getMetricManager).toHaveBeenCalledWith(webex);
89
+ expect(_wsFeatureFlag.isMobiusWssEnabled).toHaveBeenCalledWith(webex);
90
+ expect(infoSpy).toHaveBeenCalledWith('APIRequest initialized with transport: HTTP', {
91
+ file: 'REQUEST',
92
+ method: 'constructor'
93
+ });
94
+ });
95
+ it('should create instance with webex config and WSS transport when feature flag enabled', function () {
96
+ _wsFeatureFlag.isMobiusWssEnabled.mockReturnValue(true);
97
+ var apiRequest = _request.APIRequest.getInstance({
98
+ webex: webex
99
+ });
100
+ expect(apiRequest).toBeDefined();
101
+ expect(infoSpy).toHaveBeenCalledWith('APIRequest initialized with transport: WSS', {
102
+ file: 'REQUEST',
103
+ method: 'constructor'
104
+ });
105
+ });
106
+ it('should throw error if webex is missing from config', function () {
107
+ expect(function () {
108
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
109
+ _request.APIRequest.getInstance({
110
+ webex: undefined
111
+ });
112
+ }).toThrow('WebexSDK instance is required');
113
+ expect(errorSpy).toHaveBeenCalledWith('APIRequest instantiation failed: WebexSDK instance is required', {
114
+ file: 'REQUEST',
115
+ method: 'constructor'
116
+ });
117
+ });
118
+ it('should return same instance on subsequent getInstance() calls', function () {
119
+ var instance1 = _request.APIRequest.getInstance({
120
+ webex: webex
121
+ });
122
+ var instance2 = _request.APIRequest.getInstance({
123
+ webex: webex
124
+ });
125
+ expect(instance1).toBe(instance2);
126
+ });
127
+ it('should clear singleton when resetInstance() is called', function () {
128
+ var instance1 = _request.APIRequest.getInstance({
129
+ webex: webex
130
+ });
131
+ _request.APIRequest.resetInstance();
132
+ var instance2 = _request.APIRequest.getInstance({
133
+ webex: webex
134
+ });
135
+ expect(instance1).not.toBe(instance2);
136
+ });
137
+ });
138
+ describe('createAPIRequest factory', function () {
139
+ it('should create APIRequest instance using factory function', function () {
140
+ var apiRequest = (0, _request.createAPIRequest)({
141
+ webex: webex
142
+ });
143
+ expect(apiRequest).toBeDefined();
144
+ expect(apiRequest).toBeInstanceOf(_request.APIRequest);
145
+ });
146
+ });
147
+ describe('isSocketEnabled', function () {
148
+ it('should return false when WSS is disabled', function () {
149
+ _wsFeatureFlag.isMobiusWssEnabled.mockReturnValue(false);
150
+ var apiRequest = _request.APIRequest.getInstance({
151
+ webex: webex
152
+ });
153
+ var result = apiRequest.isSocketEnabled();
154
+ expect(result).toBe(false);
155
+ });
156
+ it('should return true when WSS is enabled', function () {
157
+ _wsFeatureFlag.isMobiusWssEnabled.mockReturnValue(true);
158
+ var apiRequest = _request.APIRequest.getInstance({
159
+ webex: webex
160
+ });
161
+ var result = apiRequest.isSocketEnabled();
162
+ expect(result).toBe(true);
163
+ });
164
+ });
165
+ describe('connectToMobiusSocket', function () {
166
+ it('should return immediately if socket is already connected', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee() {
167
+ var apiRequest, result;
168
+ return _regenerator.default.wrap(function (_context) {
169
+ while (1) switch (_context.prev = _context.next) {
170
+ case 0:
171
+ mockMobiusSocket.isConnected.mockReturnValue(true);
172
+ mockMobiusSocket.getConnectedWebSocketUrl.mockReturnValue('wss://existing.webex.com');
173
+ apiRequest = _request.APIRequest.getInstance({
174
+ webex: webex
175
+ });
176
+ _context.next = 1;
177
+ return apiRequest.connectToMobiusSocket('wss://test.webex.com');
178
+ case 1:
179
+ result = _context.sent;
180
+ expect(result).toBe('wss://existing.webex.com');
181
+ expect(mockMobiusSocket.connect).not.toHaveBeenCalled();
182
+ expect(infoSpy).toHaveBeenCalledWith('Mobius WebSocket already connected', {
183
+ file: 'REQUEST',
184
+ method: 'connectToMobiusSocket'
185
+ });
186
+ expect(mockMetricManager.submitMobiusSocketMetric).not.toHaveBeenCalled();
187
+ case 2:
188
+ case "end":
189
+ return _context.stop();
190
+ }
191
+ }, _callee);
192
+ })));
193
+ it('should initiate new connection if not connected', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee2() {
194
+ var apiRequest, wssUrl, result;
195
+ return _regenerator.default.wrap(function (_context2) {
196
+ while (1) switch (_context2.prev = _context2.next) {
197
+ case 0:
198
+ mockMobiusSocket.isConnected.mockReturnValue(false);
199
+ apiRequest = _request.APIRequest.getInstance({
200
+ webex: webex
201
+ });
202
+ wssUrl = 'wss://test.webex.com/api/v1';
203
+ _context2.next = 1;
204
+ return apiRequest.connectToMobiusSocket(wssUrl);
205
+ case 1:
206
+ result = _context2.sent;
207
+ expect(mockMobiusSocket.connect).toHaveBeenCalledWith(wssUrl);
208
+ expect(result).toBe(wssUrl);
209
+ expect(infoSpy).toHaveBeenCalledWith('Mobius WebSocket not connected, initiating connection', {
210
+ file: 'REQUEST',
211
+ method: 'connectToMobiusSocket'
212
+ });
213
+ case 2:
214
+ case "end":
215
+ return _context2.stop();
216
+ }
217
+ }, _callee2);
218
+ })));
219
+ it('should submit success metric on successful connection', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee3() {
220
+ var apiRequest, wssUrl;
221
+ return _regenerator.default.wrap(function (_context3) {
222
+ while (1) switch (_context3.prev = _context3.next) {
223
+ case 0:
224
+ mockMobiusSocket.isConnected.mockReturnValue(false);
225
+ apiRequest = _request.APIRequest.getInstance({
226
+ webex: webex
227
+ });
228
+ wssUrl = 'wss://mobius.webex.com/api/v1';
229
+ _context3.next = 1;
230
+ return apiRequest.connectToMobiusSocket(wssUrl);
231
+ case 1:
232
+ expect(logSpy).toHaveBeenCalledWith('Mobius WebSocket connected successfully', {
233
+ file: 'REQUEST',
234
+ method: 'connectToMobiusSocket'
235
+ });
236
+ expect(mockMetricManager.submitMobiusSocketMetric).toHaveBeenCalledWith(_types2.METRIC_EVENT.MOBIUS_SOCKET, _types2.MOBIUS_SOCKET_ACTION.CONNECT, _types2.METRIC_TYPE.BEHAVIORAL, wssUrl);
237
+ case 2:
238
+ case "end":
239
+ return _context3.stop();
240
+ }
241
+ }, _callee3);
242
+ })));
243
+ it('should throw normalized error on connection failure', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee4() {
244
+ var connectionError, apiRequest, wssUrl;
245
+ return _regenerator.default.wrap(function (_context4) {
246
+ while (1) switch (_context4.prev = _context4.next) {
247
+ case 0:
248
+ connectionError = {
249
+ statusCode: 503,
250
+ statusMessage: 'Service Unavailable',
251
+ trackingId: 'error-tracking-id',
252
+ response: {
253
+ statusCode: 503,
254
+ trackingId: 'error-tracking-id',
255
+ data: {
256
+ error: 'Connection failed'
257
+ },
258
+ metadata: {
259
+ 'x-error': 'true'
260
+ }
261
+ }
262
+ };
263
+ mockMobiusSocket.isConnected.mockReturnValue(false);
264
+ mockMobiusSocket.connect.mockRejectedValue(connectionError);
265
+ apiRequest = _request.APIRequest.getInstance({
266
+ webex: webex
267
+ });
268
+ wssUrl = 'wss://mobius.webex.com/api/v1';
269
+ _context4.next = 1;
270
+ return expect(apiRequest.connectToMobiusSocket(wssUrl)).rejects.toEqual({
271
+ statusCode: 503,
272
+ body: {
273
+ error: 'Connection failed'
274
+ },
275
+ headers: {
276
+ trackingid: 'error-tracking-id',
277
+ 'x-error': 'true'
278
+ }
279
+ });
280
+ case 1:
281
+ expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('Mobius WebSocket connection failed'), {
282
+ file: 'REQUEST',
283
+ method: 'connectToMobiusSocket'
284
+ });
285
+ expect(mockMetricManager.submitMobiusSocketMetric).toHaveBeenCalledWith(_types2.METRIC_EVENT.MOBIUS_SOCKET_ERROR, _types2.MOBIUS_SOCKET_ACTION.CONNECT, _types2.METRIC_TYPE.BEHAVIORAL, wssUrl, undefined, expect.any(String));
286
+ case 2:
287
+ case "end":
288
+ return _context4.stop();
289
+ }
290
+ }, _callee4);
291
+ })));
292
+ it('should handle connection error with minimal error object', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee5() {
293
+ var minimalError, apiRequest;
294
+ return _regenerator.default.wrap(function (_context5) {
295
+ while (1) switch (_context5.prev = _context5.next) {
296
+ case 0:
297
+ minimalError = {
298
+ statusCode: 500
299
+ };
300
+ mockMobiusSocket.isConnected.mockReturnValue(false);
301
+ mockMobiusSocket.connect.mockRejectedValue(minimalError);
302
+ apiRequest = _request.APIRequest.getInstance({
303
+ webex: webex
304
+ });
305
+ _context5.next = 1;
306
+ return expect(apiRequest.connectToMobiusSocket('wss://test.webex.com')).rejects.toEqual({
307
+ statusCode: 500,
308
+ body: undefined,
309
+ headers: {
310
+ trackingid: ''
311
+ }
312
+ });
313
+ case 1:
314
+ case "end":
315
+ return _context5.stop();
316
+ }
317
+ }, _callee5);
318
+ })));
319
+ });
320
+ describe('disconnectFromMobiusSocket', function () {
321
+ it('should disconnect successfully', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee6() {
322
+ var apiRequest;
323
+ return _regenerator.default.wrap(function (_context6) {
324
+ while (1) switch (_context6.prev = _context6.next) {
325
+ case 0:
326
+ apiRequest = _request.APIRequest.getInstance({
327
+ webex: webex
328
+ });
329
+ _context6.next = 1;
330
+ return apiRequest.disconnectFromMobiusSocket();
331
+ case 1:
332
+ expect(mockMobiusSocket.disconnect).toHaveBeenCalledWith(undefined);
333
+ expect(infoSpy).toHaveBeenCalledWith('Disconnecting from Mobius WebSocket', {
334
+ file: 'REQUEST',
335
+ method: 'disconnectFromMobiusSocket'
336
+ });
337
+ expect(logSpy).toHaveBeenCalledWith('Mobius WebSocket disconnected successfully', {
338
+ file: 'REQUEST',
339
+ method: 'disconnectFromMobiusSocket'
340
+ });
341
+ case 2:
342
+ case "end":
343
+ return _context6.stop();
344
+ }
345
+ }, _callee6);
346
+ })));
347
+ it('should submit success metric after disconnect', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee7() {
348
+ var apiRequest;
349
+ return _regenerator.default.wrap(function (_context7) {
350
+ while (1) switch (_context7.prev = _context7.next) {
351
+ case 0:
352
+ apiRequest = _request.APIRequest.getInstance({
353
+ webex: webex
354
+ });
355
+ _context7.next = 1;
356
+ return apiRequest.disconnectFromMobiusSocket();
357
+ case 1:
358
+ expect(mockMetricManager.submitMobiusSocketMetric).toHaveBeenCalledWith(_types2.METRIC_EVENT.MOBIUS_SOCKET, _types2.MOBIUS_SOCKET_ACTION.DISCONNECT, _types2.METRIC_TYPE.BEHAVIORAL);
359
+ case 2:
360
+ case "end":
361
+ return _context7.stop();
362
+ }
363
+ }, _callee7);
364
+ })));
365
+ it('should disconnect with custom options', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee8() {
366
+ var apiRequest, options;
367
+ return _regenerator.default.wrap(function (_context8) {
368
+ while (1) switch (_context8.prev = _context8.next) {
369
+ case 0:
370
+ apiRequest = _request.APIRequest.getInstance({
371
+ webex: webex
372
+ });
373
+ options = {
374
+ code: 1000,
375
+ reason: 'Normal closure'
376
+ };
377
+ _context8.next = 1;
378
+ return apiRequest.disconnectFromMobiusSocket(options);
379
+ case 1:
380
+ expect(mockMobiusSocket.disconnect).toHaveBeenCalledWith(options);
381
+ case 2:
382
+ case "end":
383
+ return _context8.stop();
384
+ }
385
+ }, _callee8);
386
+ })));
387
+ it('should handle disconnect error silently and log warning', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee9() {
388
+ var apiRequest;
389
+ return _regenerator.default.wrap(function (_context9) {
390
+ while (1) switch (_context9.prev = _context9.next) {
391
+ case 0:
392
+ mockMobiusSocket.disconnect.mockRejectedValue(new Error('Disconnect failed'));
393
+ apiRequest = _request.APIRequest.getInstance({
394
+ webex: webex
395
+ });
396
+ _context9.next = 1;
397
+ return expect(apiRequest.disconnectFromMobiusSocket()).resolves.not.toThrow();
398
+ case 1:
399
+ expect(warnSpy).toHaveBeenCalledWith('Mobius WebSocket disconnection failed: Error: Disconnect failed', {
400
+ file: 'REQUEST',
401
+ method: 'disconnectFromMobiusSocket'
402
+ });
403
+ case 2:
404
+ case "end":
405
+ return _context9.stop();
406
+ }
407
+ }, _callee9);
408
+ })));
409
+ it('should submit error metric on disconnect failure', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee0() {
410
+ var apiRequest;
411
+ return _regenerator.default.wrap(function (_context0) {
412
+ while (1) switch (_context0.prev = _context0.next) {
413
+ case 0:
414
+ mockMobiusSocket.disconnect.mockRejectedValue(new Error('Disconnect failed'));
415
+ apiRequest = _request.APIRequest.getInstance({
416
+ webex: webex
417
+ });
418
+ _context0.next = 1;
419
+ return apiRequest.disconnectFromMobiusSocket();
420
+ case 1:
421
+ expect(mockMetricManager.submitMobiusSocketMetric).toHaveBeenCalledWith(_types2.METRIC_EVENT.MOBIUS_SOCKET_ERROR, _types2.MOBIUS_SOCKET_ACTION.DISCONNECT, _types2.METRIC_TYPE.BEHAVIORAL, undefined, undefined, 'Error: Disconnect failed');
422
+ case 2:
423
+ case "end":
424
+ return _context0.stop();
425
+ }
426
+ }, _callee0);
427
+ })));
428
+ });
429
+ describe('makeRequest - HTTP transport', function () {
430
+ it('should route through webex.request when WSS is disabled', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee1() {
431
+ var apiRequest, requestOptions, expectedResponse, result;
432
+ return _regenerator.default.wrap(function (_context1) {
433
+ while (1) switch (_context1.prev = _context1.next) {
434
+ case 0:
435
+ _wsFeatureFlag.isMobiusWssEnabled.mockReturnValue(false);
436
+ apiRequest = _request.APIRequest.getInstance({
437
+ webex: webex
438
+ });
439
+ requestOptions = {
440
+ uri: '/api/v1/calling/web/device',
441
+ method: _types.HTTP_METHODS.POST,
442
+ body: {
443
+ userId: 'user-123'
444
+ }
445
+ };
446
+ expectedResponse = {
447
+ statusCode: 200,
448
+ body: {
449
+ deviceId: 'device-456'
450
+ }
451
+ };
452
+ webex.request.mockResolvedValue(expectedResponse);
453
+ _context1.next = 1;
454
+ return apiRequest.makeRequest(requestOptions);
455
+ case 1:
456
+ result = _context1.sent;
457
+ expect(webex.request).toHaveBeenCalledWith(requestOptions);
458
+ expect(result).toEqual(expectedResponse);
459
+ expect(infoSpy).toHaveBeenCalledWith('Dispatching request via HTTP ', {
460
+ file: 'REQUEST',
461
+ method: 'makeRequest'
462
+ });
463
+ case 2:
464
+ case "end":
465
+ return _context1.stop();
466
+ }
467
+ }, _callee1);
468
+ })));
469
+ it('should pass request options unchanged to webex.request', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee10() {
470
+ var apiRequest, requestOptions;
471
+ return _regenerator.default.wrap(function (_context10) {
472
+ while (1) switch (_context10.prev = _context10.next) {
473
+ case 0:
474
+ _wsFeatureFlag.isMobiusWssEnabled.mockReturnValue(false);
475
+ apiRequest = _request.APIRequest.getInstance({
476
+ webex: webex
477
+ });
478
+ requestOptions = {
479
+ uri: '/api/v1/calling/web/devices/device-123',
480
+ method: _types.HTTP_METHODS.GET,
481
+ headers: {
482
+ 'x-custom-header': 'value'
483
+ }
484
+ };
485
+ webex.request.mockResolvedValue({
486
+ statusCode: 200
487
+ });
488
+ _context10.next = 1;
489
+ return apiRequest.makeRequest(requestOptions);
490
+ case 1:
491
+ expect(webex.request).toHaveBeenCalledWith(requestOptions);
492
+ case 2:
493
+ case "end":
494
+ return _context10.stop();
495
+ }
496
+ }, _callee10);
497
+ })));
498
+ it('should return webex.request result directly', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee11() {
499
+ var apiRequest, expectedResponse, result;
500
+ return _regenerator.default.wrap(function (_context11) {
501
+ while (1) switch (_context11.prev = _context11.next) {
502
+ case 0:
503
+ _wsFeatureFlag.isMobiusWssEnabled.mockReturnValue(false);
504
+ apiRequest = _request.APIRequest.getInstance({
505
+ webex: webex
506
+ });
507
+ expectedResponse = {
508
+ statusCode: 404,
509
+ body: {
510
+ error: 'Not found'
511
+ },
512
+ headers: {
513
+ 'x-tracking-id': 'track-123'
514
+ }
515
+ };
516
+ webex.request.mockResolvedValue(expectedResponse);
517
+ _context11.next = 1;
518
+ return apiRequest.makeRequest({
519
+ uri: '/api/v1/test',
520
+ method: _types.HTTP_METHODS.GET
521
+ });
522
+ case 1:
523
+ result = _context11.sent;
524
+ expect(result).toEqual(expectedResponse);
525
+ case 2:
526
+ case "end":
527
+ return _context11.stop();
528
+ }
529
+ }, _callee11);
530
+ })));
531
+ });
532
+ describe('makeRequest - WebSocket transport', function () {
533
+ beforeEach(function () {
534
+ _wsFeatureFlag.isMobiusWssEnabled.mockReturnValue(true);
535
+ });
536
+ it('should derive socket message type from URI and method', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee12() {
537
+ var apiRequest, requestOptions;
538
+ return _regenerator.default.wrap(function (_context12) {
539
+ while (1) switch (_context12.prev = _context12.next) {
540
+ case 0:
541
+ apiRequest = _request.APIRequest.getInstance({
542
+ webex: webex
543
+ });
544
+ requestOptions = {
545
+ uri: '/api/v1/calling/web/device',
546
+ method: _types.HTTP_METHODS.POST,
547
+ body: {
548
+ userId: 'user-123'
549
+ }
550
+ };
551
+ _context12.next = 1;
552
+ return apiRequest.makeRequest(requestOptions);
553
+ case 1:
554
+ expect(mockMobiusSocket.sendWssRequest).toHaveBeenCalledWith(expect.objectContaining({
555
+ type: _constants.MOBIUS_SOCKET_MESSAGE_TYPE.REGISTER
556
+ }));
557
+ expect(infoSpy).toHaveBeenCalledWith('Dispatching request via WSS ', {
558
+ file: 'REQUEST',
559
+ method: 'makeRequest'
560
+ });
561
+ case 2:
562
+ case "end":
563
+ return _context12.stop();
564
+ }
565
+ }, _callee12);
566
+ })));
567
+ it('should throw error if message type is UNKNOWN', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee13() {
568
+ var apiRequest, requestOptions;
569
+ return _regenerator.default.wrap(function (_context13) {
570
+ while (1) switch (_context13.prev = _context13.next) {
571
+ case 0:
572
+ apiRequest = _request.APIRequest.getInstance({
573
+ webex: webex
574
+ });
575
+ requestOptions = {
576
+ uri: '/api/v1/unrecognized/path',
577
+ method: _types.HTTP_METHODS.POST
578
+ };
579
+ _context13.next = 1;
580
+ return expect(apiRequest.makeRequest(requestOptions)).rejects.toThrow('Unknown Mobius Socket message type: UNKNOWN');
581
+ case 1:
582
+ expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('Unknown Mobius Socket message type'), {
583
+ file: 'REQUEST',
584
+ method: 'makeRequest'
585
+ });
586
+ case 2:
587
+ case "end":
588
+ return _context13.stop();
589
+ }
590
+ }, _callee13);
591
+ })));
592
+ it('should generate tracking ID with webex-js-sdk prefix', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee14() {
593
+ var apiRequest, requestOptions;
594
+ return _regenerator.default.wrap(function (_context14) {
595
+ while (1) switch (_context14.prev = _context14.next) {
596
+ case 0:
597
+ apiRequest = _request.APIRequest.getInstance({
598
+ webex: webex
599
+ });
600
+ requestOptions = {
601
+ uri: '/api/v1/calling/web/device',
602
+ method: _types.HTTP_METHODS.POST
603
+ };
604
+ _context14.next = 1;
605
+ return apiRequest.makeRequest(requestOptions);
606
+ case 1:
607
+ expect(_uuid.v4).toHaveBeenCalled();
608
+ expect(mockMobiusSocket.sendWssRequest).toHaveBeenCalledWith(expect.objectContaining({
609
+ trackingId: 'webex-js-sdk_mock-uuid-12345'
610
+ }));
611
+ case 2:
612
+ case "end":
613
+ return _context14.stop();
614
+ }
615
+ }, _callee14);
616
+ })));
617
+ it('should send WSS request with correct payload structure', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee15() {
618
+ var apiRequest, requestOptions;
619
+ return _regenerator.default.wrap(function (_context15) {
620
+ while (1) switch (_context15.prev = _context15.next) {
621
+ case 0:
622
+ apiRequest = _request.APIRequest.getInstance({
623
+ webex: webex
624
+ });
625
+ requestOptions = {
626
+ uri: '/api/v1/calling/web/device',
627
+ method: _types.HTTP_METHODS.POST,
628
+ body: {
629
+ userId: 'user-123'
630
+ },
631
+ headers: {
632
+ 'x-custom': 'header-value'
633
+ }
634
+ };
635
+ _context15.next = 1;
636
+ return apiRequest.makeRequest(requestOptions);
637
+ case 1:
638
+ expect(mockMobiusSocket.sendWssRequest).toHaveBeenCalledWith({
639
+ type: _constants.MOBIUS_SOCKET_MESSAGE_TYPE.REGISTER,
640
+ trackingId: 'webex-js-sdk_mock-uuid-12345',
641
+ metadata: {
642
+ 'x-custom': 'header-value',
643
+ userAgent: 'webex-calling/beta',
644
+ authorization: ''
645
+ },
646
+ data: {
647
+ userId: 'user-123'
648
+ }
649
+ });
650
+ case 2:
651
+ case "end":
652
+ return _context15.stop();
653
+ }
654
+ }, _callee15);
655
+ })));
656
+ it('should get user token for supplementary services (CALL_HOLD)', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee16() {
657
+ var apiRequest, requestOptions;
658
+ return _regenerator.default.wrap(function (_context16) {
659
+ while (1) switch (_context16.prev = _context16.next) {
660
+ case 0:
661
+ webex.credentials.getUserToken.mockResolvedValue('Bearer token-abc-123');
662
+ apiRequest = _request.APIRequest.getInstance({
663
+ webex: webex
664
+ });
665
+ requestOptions = {
666
+ uri: '/api/v1/calling/web/devices/device-123/services/callhold/hold',
667
+ method: _types.HTTP_METHODS.POST
668
+ };
669
+ _context16.next = 1;
670
+ return apiRequest.makeRequest(requestOptions);
671
+ case 1:
672
+ expect(webex.credentials.getUserToken).toHaveBeenCalled();
673
+ expect(mockMobiusSocket.sendWssRequest).toHaveBeenCalledWith(expect.objectContaining({
674
+ type: _constants.MOBIUS_SOCKET_MESSAGE_TYPE.CALL_HOLD,
675
+ metadata: expect.objectContaining({
676
+ authorization: 'Bearer token-abc-123'
677
+ })
678
+ }));
679
+ case 2:
680
+ case "end":
681
+ return _context16.stop();
682
+ }
683
+ }, _callee16);
684
+ })));
685
+ it('should not get user token for non-supplementary services', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee17() {
686
+ var apiRequest, requestOptions;
687
+ return _regenerator.default.wrap(function (_context17) {
688
+ while (1) switch (_context17.prev = _context17.next) {
689
+ case 0:
690
+ apiRequest = _request.APIRequest.getInstance({
691
+ webex: webex
692
+ });
693
+ requestOptions = {
694
+ uri: '/api/v1/calling/web/device',
695
+ method: _types.HTTP_METHODS.POST
696
+ };
697
+ _context17.next = 1;
698
+ return apiRequest.makeRequest(requestOptions);
699
+ case 1:
700
+ expect(webex.credentials.getUserToken).not.toHaveBeenCalled();
701
+ expect(mockMobiusSocket.sendWssRequest).toHaveBeenCalledWith(expect.objectContaining({
702
+ metadata: expect.objectContaining({
703
+ authorization: ''
704
+ })
705
+ }));
706
+ case 2:
707
+ case "end":
708
+ return _context17.stop();
709
+ }
710
+ }, _callee17);
711
+ })));
712
+ it('should normalize success response to WebexRequestPayload shape', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee18() {
713
+ var wsResponse, apiRequest, result;
714
+ return _regenerator.default.wrap(function (_context18) {
715
+ while (1) switch (_context18.prev = _context18.next) {
716
+ case 0:
717
+ wsResponse = {
718
+ type: 'register.response',
719
+ statusCode: 201,
720
+ statusMessage: 'Created',
721
+ trackingId: 'track-123',
722
+ data: {
723
+ deviceId: 'device-456',
724
+ correlationId: 'corr-789'
725
+ },
726
+ metadata: {
727
+ 'x-response-header': 'value'
728
+ }
729
+ };
730
+ mockMobiusSocket.sendWssRequest.mockResolvedValue(wsResponse);
731
+ apiRequest = _request.APIRequest.getInstance({
732
+ webex: webex
733
+ });
734
+ _context18.next = 1;
735
+ return apiRequest.makeRequest({
736
+ uri: '/api/v1/calling/web/device',
737
+ method: _types.HTTP_METHODS.POST
738
+ });
739
+ case 1:
740
+ result = _context18.sent;
741
+ expect(result).toEqual({
742
+ statusCode: 201,
743
+ body: {
744
+ deviceId: 'device-456',
745
+ correlationId: 'corr-789'
746
+ },
747
+ headers: {
748
+ trackingid: 'track-123',
749
+ 'x-response-header': 'value'
750
+ }
751
+ });
752
+ expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('WSS request succeeded'), {
753
+ file: 'REQUEST',
754
+ method: 'makeRequest'
755
+ });
756
+ case 2:
757
+ case "end":
758
+ return _context18.stop();
759
+ }
760
+ }, _callee18);
761
+ })));
762
+ it('should normalize error to WebexRequestPayload shape', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee19() {
763
+ var wsError, apiRequest;
764
+ return _regenerator.default.wrap(function (_context19) {
765
+ while (1) switch (_context19.prev = _context19.next) {
766
+ case 0:
767
+ wsError = {
768
+ statusCode: 400,
769
+ statusMessage: 'Bad Request',
770
+ trackingId: 'error-track-456',
771
+ response: {
772
+ statusCode: 400,
773
+ trackingId: 'error-track-456',
774
+ data: {
775
+ error: 'Invalid request'
776
+ },
777
+ metadata: {
778
+ 'x-error-code': 'ERR001'
779
+ }
780
+ }
781
+ };
782
+ mockMobiusSocket.sendWssRequest.mockRejectedValue(wsError);
783
+ apiRequest = _request.APIRequest.getInstance({
784
+ webex: webex
785
+ });
786
+ _context19.next = 1;
787
+ return expect(apiRequest.makeRequest({
788
+ uri: '/api/v1/calling/web/device',
789
+ method: _types.HTTP_METHODS.POST
790
+ })).rejects.toEqual({
791
+ statusCode: 400,
792
+ body: {
793
+ error: 'Invalid request'
794
+ },
795
+ headers: {
796
+ trackingid: 'error-track-456',
797
+ 'x-error-code': 'ERR001'
798
+ }
799
+ });
800
+ case 1:
801
+ expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('WSS request failed'), {
802
+ file: 'REQUEST',
803
+ method: 'makeRequest'
804
+ });
805
+ case 2:
806
+ case "end":
807
+ return _context19.stop();
808
+ }
809
+ }, _callee19);
810
+ })));
811
+ });
812
+ describe('registerMobiusSocketListener', function () {
813
+ it('should attach listener to event:async_event', function () {
814
+ var apiRequest = _request.APIRequest.getInstance({
815
+ webex: webex
816
+ });
817
+ var callback = jest.fn();
818
+ apiRequest.registerMobiusSocketListener(callback);
819
+ expect(mockMobiusSocket.on).toHaveBeenCalledWith('event:async_event', expect.any(Function));
820
+ expect(infoSpy).toHaveBeenCalledWith('Attaching Mobius async event listener', {
821
+ file: 'REQUEST',
822
+ method: 'registerMobiusSocketListener'
823
+ });
824
+ expect(logSpy).toHaveBeenCalledWith('Mobius async event listener attached', {
825
+ file: 'REQUEST',
826
+ method: 'registerMobiusSocketListener'
827
+ });
828
+ });
829
+ it('should invoke callback with event data when async event received', function () {
830
+ var apiRequest = _request.APIRequest.getInstance({
831
+ webex: webex
832
+ });
833
+ var callback = jest.fn();
834
+ var mockEvent = {
835
+ type: 'event:async_event',
836
+ eventId: 'evt-123',
837
+ trackingId: 'track-456',
838
+ data: {
839
+ eventType: 'registration.down'
840
+ }
841
+ };
842
+ apiRequest.registerMobiusSocketListener(callback);
843
+ var attachedCallback = mockMobiusSocket.on.mock.calls[0][1];
844
+ attachedCallback(mockEvent);
845
+ expect(callback).toHaveBeenCalledWith(mockEvent);
846
+ });
847
+ it('should submit LISTENER_REGISTERED metric', function () {
848
+ var apiRequest = _request.APIRequest.getInstance({
849
+ webex: webex
850
+ });
851
+ var callback = jest.fn();
852
+ apiRequest.registerMobiusSocketListener(callback);
853
+ expect(mockMetricManager.submitMobiusSocketMetric).toHaveBeenCalledWith(_types2.METRIC_EVENT.MOBIUS_SOCKET, _types2.MOBIUS_SOCKET_ACTION.LISTENER_REGISTERED, _types2.METRIC_TYPE.BEHAVIORAL);
854
+ });
855
+ });
856
+ describe('unregisterMobiusSocketListener', function () {
857
+ it('should detach listener from event:async_event', function () {
858
+ var apiRequest = _request.APIRequest.getInstance({
859
+ webex: webex
860
+ });
861
+ apiRequest.unregisterMobiusSocketListener();
862
+ expect(mockMobiusSocket.off).toHaveBeenCalledWith('event:async_event');
863
+ expect(infoSpy).toHaveBeenCalledWith('Detaching Mobius async event listener', {
864
+ file: 'REQUEST',
865
+ method: 'unregisterMobiusSocketListener'
866
+ });
867
+ expect(logSpy).toHaveBeenCalledWith('Mobius async event listener detached', {
868
+ file: 'REQUEST',
869
+ method: 'unregisterMobiusSocketListener'
870
+ });
871
+ });
872
+ it('should submit LISTENER_UNREGISTERED metric', function () {
873
+ var apiRequest = _request.APIRequest.getInstance({
874
+ webex: webex
875
+ });
876
+ apiRequest.unregisterMobiusSocketListener();
877
+ expect(mockMetricManager.submitMobiusSocketMetric).toHaveBeenCalledWith(_types2.METRIC_EVENT.MOBIUS_SOCKET, _types2.MOBIUS_SOCKET_ACTION.LISTENER_UNREGISTERED, _types2.METRIC_TYPE.BEHAVIORAL);
878
+ });
879
+ });
880
+ });
881
+ //# sourceMappingURL=request.test.js.map