@webex/calling 3.11.0 → 3.12.0

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.
Files changed (188) hide show
  1. package/dist/CallHistory/CallHistory.js +596 -0
  2. package/dist/CallHistory/CallHistory.js.map +1 -0
  3. package/dist/CallHistory/CallHistory.test.js +916 -0
  4. package/dist/CallHistory/CallHistory.test.js.map +1 -0
  5. package/dist/CallHistory/callHistoryFixtures.js +650 -0
  6. package/dist/CallHistory/callHistoryFixtures.js.map +1 -0
  7. package/dist/CallHistory/constants.js +38 -0
  8. package/dist/CallHistory/constants.js.map +1 -0
  9. package/dist/CallHistory/types.js +7 -0
  10. package/dist/CallHistory/types.js.map +1 -0
  11. package/dist/CallSettings/CallSettings.js +311 -0
  12. package/dist/CallSettings/CallSettings.js.map +1 -0
  13. package/dist/CallSettings/CallSettings.test.js +122 -0
  14. package/dist/CallSettings/CallSettings.test.js.map +1 -0
  15. package/dist/CallSettings/UcmBackendConnector.js +308 -0
  16. package/dist/CallSettings/UcmBackendConnector.js.map +1 -0
  17. package/dist/CallSettings/UcmBackendConnector.test.js +411 -0
  18. package/dist/CallSettings/UcmBackendConnector.test.js.map +1 -0
  19. package/dist/CallSettings/WxCallBackendConnector.js +597 -0
  20. package/dist/CallSettings/WxCallBackendConnector.js.map +1 -0
  21. package/dist/CallSettings/WxCallBackendConnector.test.js +904 -0
  22. package/dist/CallSettings/WxCallBackendConnector.test.js.map +1 -0
  23. package/dist/CallSettings/constants.js +31 -0
  24. package/dist/CallSettings/constants.js.map +1 -0
  25. package/dist/CallSettings/testFixtures.js +68 -0
  26. package/dist/CallSettings/testFixtures.js.map +1 -0
  27. package/dist/CallSettings/types.js +7 -0
  28. package/dist/CallSettings/types.js.map +1 -0
  29. package/dist/CallingClient/CallingClient.js +1119 -0
  30. package/dist/CallingClient/CallingClient.js.map +1 -0
  31. package/dist/CallingClient/CallingClient.test.js +1266 -0
  32. package/dist/CallingClient/CallingClient.test.js.map +1 -0
  33. package/dist/CallingClient/callRecordFixtures.js +101 -0
  34. package/dist/CallingClient/callRecordFixtures.js.map +1 -0
  35. package/dist/CallingClient/calling/CallerId/index.js +275 -0
  36. package/dist/CallingClient/calling/CallerId/index.js.map +1 -0
  37. package/dist/CallingClient/calling/CallerId/index.test.js +275 -0
  38. package/dist/CallingClient/calling/CallerId/index.test.js.map +1 -0
  39. package/dist/CallingClient/calling/CallerId/types.js +7 -0
  40. package/dist/CallingClient/calling/CallerId/types.js.map +1 -0
  41. package/dist/CallingClient/calling/call.js +3486 -0
  42. package/dist/CallingClient/calling/call.js.map +1 -0
  43. package/dist/CallingClient/calling/call.test.js +3612 -0
  44. package/dist/CallingClient/calling/call.test.js.map +1 -0
  45. package/dist/CallingClient/calling/callManager.js +460 -0
  46. package/dist/CallingClient/calling/callManager.js.map +1 -0
  47. package/dist/CallingClient/calling/callManager.test.js +741 -0
  48. package/dist/CallingClient/calling/callManager.test.js.map +1 -0
  49. package/dist/CallingClient/calling/index.js +30 -0
  50. package/dist/CallingClient/calling/index.js.map +1 -0
  51. package/dist/CallingClient/calling/types.js +74 -0
  52. package/dist/CallingClient/calling/types.js.map +1 -0
  53. package/dist/CallingClient/callingClientFixtures.js +143 -0
  54. package/dist/CallingClient/callingClientFixtures.js.map +1 -0
  55. package/dist/CallingClient/constants.js +243 -0
  56. package/dist/CallingClient/constants.js.map +1 -0
  57. package/dist/CallingClient/line/index.js +332 -0
  58. package/dist/CallingClient/line/index.js.map +1 -0
  59. package/dist/CallingClient/line/line.test.js +327 -0
  60. package/dist/CallingClient/line/line.test.js.map +1 -0
  61. package/dist/CallingClient/line/types.js +21 -0
  62. package/dist/CallingClient/line/types.js.map +1 -0
  63. package/dist/CallingClient/registration/index.js +19 -0
  64. package/dist/CallingClient/registration/index.js.map +1 -0
  65. package/dist/CallingClient/registration/register.js +1686 -0
  66. package/dist/CallingClient/registration/register.js.map +1 -0
  67. package/dist/CallingClient/registration/register.test.js +1885 -0
  68. package/dist/CallingClient/registration/register.test.js.map +1 -0
  69. package/dist/CallingClient/registration/registerFixtures.js +36 -0
  70. package/dist/CallingClient/registration/registerFixtures.js.map +1 -0
  71. package/dist/CallingClient/registration/types.js +7 -0
  72. package/dist/CallingClient/registration/types.js.map +1 -0
  73. package/dist/CallingClient/registration/webWorker.js +130 -0
  74. package/dist/CallingClient/registration/webWorker.js.map +1 -0
  75. package/dist/CallingClient/registration/webWorker.test.js +303 -0
  76. package/dist/CallingClient/registration/webWorker.test.js.map +1 -0
  77. package/dist/CallingClient/registration/webWorkerStr.js +15 -0
  78. package/dist/CallingClient/registration/webWorkerStr.js.map +1 -0
  79. package/dist/CallingClient/types.js +7 -0
  80. package/dist/CallingClient/types.js.map +1 -0
  81. package/dist/CallingClient/windowsChromiumIceWarmupUtils.js +142 -0
  82. package/dist/CallingClient/windowsChromiumIceWarmupUtils.js.map +1 -0
  83. package/dist/Contacts/ContactsClient.js +1206 -0
  84. package/dist/Contacts/ContactsClient.js.map +1 -0
  85. package/dist/Contacts/ContactsClient.test.js +1004 -0
  86. package/dist/Contacts/ContactsClient.test.js.map +1 -0
  87. package/dist/Contacts/constants.js +40 -0
  88. package/dist/Contacts/constants.js.map +1 -0
  89. package/dist/Contacts/contactFixtures.js +430 -0
  90. package/dist/Contacts/contactFixtures.js.map +1 -0
  91. package/dist/Contacts/types.js +43 -0
  92. package/dist/Contacts/types.js.map +1 -0
  93. package/dist/Errors/catalog/CallError.js +89 -0
  94. package/dist/Errors/catalog/CallError.js.map +1 -0
  95. package/dist/Errors/catalog/CallingDeviceError.js +83 -0
  96. package/dist/Errors/catalog/CallingDeviceError.js.map +1 -0
  97. package/dist/Errors/catalog/ExtendedError.js +42 -0
  98. package/dist/Errors/catalog/ExtendedError.js.map +1 -0
  99. package/dist/Errors/catalog/LineError.js +85 -0
  100. package/dist/Errors/catalog/LineError.js.map +1 -0
  101. package/dist/Errors/index.js +28 -0
  102. package/dist/Errors/index.js.map +1 -0
  103. package/dist/Errors/types.js +59 -0
  104. package/dist/Errors/types.js.map +1 -0
  105. package/dist/Events/impl/index.js +79 -0
  106. package/dist/Events/impl/index.js.map +1 -0
  107. package/dist/Events/types.js +107 -0
  108. package/dist/Events/types.js.map +1 -0
  109. package/dist/Logger/index.js +228 -0
  110. package/dist/Logger/index.js.map +1 -0
  111. package/dist/Logger/index.test.js +87 -0
  112. package/dist/Logger/index.test.js.map +1 -0
  113. package/dist/Logger/types.js +34 -0
  114. package/dist/Logger/types.js.map +1 -0
  115. package/dist/Metrics/index.js +534 -0
  116. package/dist/Metrics/index.js.map +1 -0
  117. package/dist/Metrics/index.test.js +463 -0
  118. package/dist/Metrics/index.test.js.map +1 -0
  119. package/dist/Metrics/types.js +64 -0
  120. package/dist/Metrics/types.js.map +1 -0
  121. package/dist/SDKConnector/index.js +102 -0
  122. package/dist/SDKConnector/index.js.map +1 -0
  123. package/dist/SDKConnector/index.test.js +9 -0
  124. package/dist/SDKConnector/index.test.js.map +1 -0
  125. package/dist/SDKConnector/types.js +7 -0
  126. package/dist/SDKConnector/types.js.map +1 -0
  127. package/dist/SDKConnector/utils.js +39 -0
  128. package/dist/SDKConnector/utils.js.map +1 -0
  129. package/dist/SDKConnector/utils.test.js +9 -0
  130. package/dist/SDKConnector/utils.test.js.map +1 -0
  131. package/dist/Voicemail/BroadworksBackendConnector.js +699 -0
  132. package/dist/Voicemail/BroadworksBackendConnector.js.map +1 -0
  133. package/dist/Voicemail/BroadworksBackendConnector.test.js +820 -0
  134. package/dist/Voicemail/BroadworksBackendConnector.test.js.map +1 -0
  135. package/dist/Voicemail/UcmBackendConnector.js +628 -0
  136. package/dist/Voicemail/UcmBackendConnector.js.map +1 -0
  137. package/dist/Voicemail/UcmBackendConnector.test.js +738 -0
  138. package/dist/Voicemail/UcmBackendConnector.test.js.map +1 -0
  139. package/dist/Voicemail/Voicemail.js +472 -0
  140. package/dist/Voicemail/Voicemail.js.map +1 -0
  141. package/dist/Voicemail/Voicemail.test.js +391 -0
  142. package/dist/Voicemail/Voicemail.test.js.map +1 -0
  143. package/dist/Voicemail/WxCallBackendConnector.js +657 -0
  144. package/dist/Voicemail/WxCallBackendConnector.js.map +1 -0
  145. package/dist/Voicemail/WxCallBackendConnector.test.js +1225 -0
  146. package/dist/Voicemail/WxCallBackendConnector.test.js.map +1 -0
  147. package/dist/Voicemail/constants.js +61 -0
  148. package/dist/Voicemail/constants.js.map +1 -0
  149. package/dist/Voicemail/types.js +7 -0
  150. package/dist/Voicemail/types.js.map +1 -0
  151. package/dist/Voicemail/voicemailFixture.js +524 -0
  152. package/dist/Voicemail/voicemailFixture.js.map +1 -0
  153. package/dist/api.js +157 -0
  154. package/dist/api.js.map +1 -0
  155. package/dist/common/Utils.js +1483 -0
  156. package/dist/common/Utils.js.map +1 -0
  157. package/dist/common/Utils.test.js +1989 -0
  158. package/dist/common/Utils.test.js.map +1 -0
  159. package/dist/common/constants.js +62 -0
  160. package/dist/common/constants.js.map +1 -0
  161. package/dist/common/index.js +19 -0
  162. package/dist/common/index.js.map +1 -0
  163. package/dist/common/testUtil.js +983 -0
  164. package/dist/common/testUtil.js.map +1 -0
  165. package/dist/common/types.js +75 -0
  166. package/dist/common/types.js.map +1 -0
  167. package/dist/index.js +321 -0
  168. package/dist/index.js.map +1 -0
  169. package/dist/module/CallHistory/CallHistory.js +28 -17
  170. package/dist/module/CallingClient/CallingClient.js +43 -1
  171. package/dist/module/CallingClient/calling/call.js +3 -2
  172. package/dist/module/CallingClient/constants.js +2 -0
  173. package/dist/module/CallingClient/registration/register.js +8 -0
  174. package/dist/types/CallHistory/CallHistory.d.ts.map +1 -1
  175. package/dist/types/CallingClient/CallingClient.d.ts +2 -1
  176. package/dist/types/CallingClient/CallingClient.d.ts.map +1 -1
  177. package/dist/types/CallingClient/calling/call.d.ts.map +1 -1
  178. package/dist/types/CallingClient/constants.d.ts +2 -0
  179. package/dist/types/CallingClient/constants.d.ts.map +1 -1
  180. package/dist/types/CallingClient/registration/register.d.ts +2 -1
  181. package/dist/types/CallingClient/registration/register.d.ts.map +1 -1
  182. package/dist/types/CallingClient/registration/types.d.ts +2 -1
  183. package/dist/types/CallingClient/registration/types.d.ts.map +1 -1
  184. package/dist/types/CallingClient/types.d.ts +2 -1
  185. package/dist/types/CallingClient/types.d.ts.map +1 -1
  186. package/dist/types/common/types.d.ts +4 -0
  187. package/dist/types/common/types.d.ts.map +1 -1
  188. package/package.json +4 -4
@@ -0,0 +1,1885 @@
1
+ "use strict";
2
+
3
+ var _typeof = require("@babel/runtime-corejs2/helpers/typeof");
4
+ var _Object$keys2 = 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$defineProperty = 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 _slicedToArray2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/slicedToArray"));
14
+ var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/asyncToGenerator"));
15
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/defineProperty"));
16
+ var _promise = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/promise"));
17
+ var _now = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/date/now"));
18
+ var _stringify = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/json/stringify"));
19
+ var _keys = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/object/keys"));
20
+ var _values = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/object/values"));
21
+ var _asyncMutex = require("async-mutex");
22
+ var _register = require("./register");
23
+ var _testUtil = require("../../common/testUtil");
24
+ var _types = require("../../common/types");
25
+ var utils = _interopRequireWildcard(require("../../common/Utils"));
26
+ var _Logger = _interopRequireDefault(require("../../Logger"));
27
+ var _types2 = require("../../Logger/types");
28
+ var _registerFixtures = require("./registerFixtures");
29
+ var _common = require("../../common");
30
+ var _types3 = require("../../Errors/types");
31
+ var _constants = require("../constants");
32
+ var _types4 = require("../line/types");
33
+ var _LineError = require("../../Errors/catalog/LineError");
34
+ var _types5 = require("../../Metrics/types");
35
+ 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 _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = _Object$defineProperty) && _Object$getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
36
+ function ownKeys(e, r) { var t = _Object$keys2(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; }
37
+ 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$defineProperty(e, r, _Object$getOwnPropertyDescriptor(t, r)); }); } return e; } /* eslint-disable @typescript-eslint/no-shadow */
38
+ var webex = (0, _testUtil.getTestUtilsWebex)();
39
+ var MockServiceData = {
40
+ indicator: _types.ServiceIndicator.CALLING,
41
+ domain: ''
42
+ };
43
+ var logSpy = jest.spyOn(_Logger.default, 'log');
44
+ var infoSpy = jest.spyOn(_Logger.default, 'info');
45
+ var warnSpy = jest.spyOn(_Logger.default, 'warn');
46
+ var handleErrorSpy = jest.spyOn(utils, 'handleRegistrationErrors');
47
+ jest.spyOn(utils, 'uploadLogs').mockResolvedValue();
48
+ describe('Registration Tests', function () {
49
+ var originalProcessNextTick = process.nextTick;
50
+ function flushPromises() {
51
+ return new _promise.default(function (resolve) {
52
+ originalProcessNextTick(resolve);
53
+ });
54
+ }
55
+ var lineEmitter = jest.fn();
56
+ var mobiusUris = (0, _common.filterMobiusUris)((0, _testUtil.getMobiusDiscoveryResponse)(), _registerFixtures.URL);
57
+ var mockResponse = _objectSpread(_objectSpread({}, (0, _testUtil.getMockRequestTemplate)()), {}, {
58
+ uri: "".concat(mobiusUris.primary[0], "device"),
59
+ body: {
60
+ userId: webex.internal.device.userId,
61
+ clientDeviceUri: webex.internal.device.url,
62
+ serviceData: {
63
+ domain: '',
64
+ indicator: 'calling'
65
+ }
66
+ }
67
+ });
68
+ var ccMockResponse = _objectSpread(_objectSpread({}, mockResponse), {}, {
69
+ body: _objectSpread(_objectSpread({}, mockResponse.body), {}, {
70
+ serviceData: {
71
+ domain: '',
72
+ indicator: 'contactcenter'
73
+ }
74
+ })
75
+ });
76
+ var failurePayload = {
77
+ statusCode: 500,
78
+ body: _registerFixtures.mockPostResponse,
79
+ headers: {
80
+ trackingid: 'webex-js-sdk_06bafdd0-2f9b-4cd7-b438-9c0d95ecec9b_15'
81
+ }
82
+ };
83
+ var failurePayload429One = {
84
+ statusCode: 429,
85
+ body: _registerFixtures.mockPostResponse,
86
+ headers: {
87
+ 'retry-after': 42
88
+ }
89
+ };
90
+ var failurePayload429Two = {
91
+ statusCode: 429,
92
+ body: _registerFixtures.mockPostResponse,
93
+ headers: {
94
+ 'retry-after': 33
95
+ }
96
+ };
97
+ var failurePayload429Three = {
98
+ statusCode: 429,
99
+ body: _registerFixtures.mockPostResponse,
100
+ headers: {
101
+ 'retry-after': 136
102
+ }
103
+ };
104
+ var failurePayload429Four = {
105
+ statusCode: 429,
106
+ body: _registerFixtures.mockPostResponse,
107
+ headers: {
108
+ 'retry-after': 81
109
+ }
110
+ };
111
+ var successPayload = {
112
+ statusCode: 200,
113
+ body: _registerFixtures.mockPostResponse,
114
+ headers: {
115
+ trackingid: 'webex-js-sdk_06bafdd0-2f9b-4cd7-b438-9c0d95ecec9b_15'
116
+ }
117
+ };
118
+ var reg;
119
+ var restartSpy;
120
+ var restoreSpy;
121
+ var postRegistrationSpy;
122
+ var deregisterSpy;
123
+ var failoverSpy;
124
+ var retry429Spy;
125
+ var metricSpy;
126
+ var setupRegistration = function setupRegistration(mockServiceData) {
127
+ var mutex = new _asyncMutex.Mutex();
128
+ reg = (0, _register.createRegistration)(webex, mockServiceData, mutex, lineEmitter, _types2.LOGGER.INFO);
129
+ reg.setMobiusServers(mobiusUris.primary, mobiusUris.backup);
130
+ jest.clearAllMocks();
131
+ restartSpy = jest.spyOn(reg, 'restartRegistration');
132
+ restoreSpy = jest.spyOn(reg, 'restorePreviousRegistration');
133
+ postRegistrationSpy = jest.spyOn(reg, 'postRegistration');
134
+ deregisterSpy = jest.spyOn(reg, 'deregister');
135
+ failoverSpy = jest.spyOn(reg, 'startFailoverTimer');
136
+ retry429Spy = jest.spyOn(reg, 'handle429Retry');
137
+ metricSpy = jest.spyOn(reg.metricManager, 'submitRegistrationMetric');
138
+ };
139
+ beforeEach(function () {
140
+ setupRegistration(MockServiceData);
141
+ });
142
+ afterEach(function () {
143
+ webex.request = jest.fn();
144
+ jest.clearAllTimers();
145
+ jest.clearAllMocks();
146
+ jest.useRealTimers();
147
+ localStorage.clear();
148
+ });
149
+ it('verify successful registration', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee() {
150
+ return _regenerator.default.wrap(function (_context) {
151
+ while (1) switch (_context.prev = _context.next) {
152
+ case 0:
153
+ webex.request.mockReturnValueOnce({
154
+ body: _registerFixtures.mockPostResponse,
155
+ headers: {
156
+ trackingid: 'webex-js-sdk_06bafdd0-2f9b-4cd7-b438-9c0d95ecec9b_15'
157
+ }
158
+ });
159
+ _context.next = 1;
160
+ return reg.triggerRegistration();
161
+ case 1:
162
+ expect(webex.request).toBeCalledOnceWith(_objectSpread(_objectSpread({}, mockResponse), {}, {
163
+ method: 'POST'
164
+ }));
165
+ expect(reg.getStatus()).toEqual(_types.RegistrationStatus.ACTIVE);
166
+ expect(lineEmitter).toBeCalledTimes(2);
167
+ expect(lineEmitter).toBeCalledWith(_types4.LINE_EVENTS.CONNECTING);
168
+ expect(lineEmitter).toBeCalledWith(_types4.LINE_EVENTS.REGISTERED, _registerFixtures.mockPostResponse);
169
+
170
+ // Check that log.log was called for successful registration
171
+ expect(logSpy).toBeCalledWith("Registration successful for deviceId: ".concat(_registerFixtures.mockPostResponse.device.deviceId, " userId: ").concat(_registerFixtures.mockPostResponse.userId, " responseTrackingId: webex-js-sdk_06bafdd0-2f9b-4cd7-b438-9c0d95ecec9b_15"), expect.objectContaining({
172
+ file: _constants.REGISTRATION_FILE,
173
+ method: 'register'
174
+ }));
175
+ expect(metricSpy).toBeCalledWith(_types5.METRIC_EVENT.REGISTRATION, _types5.REG_ACTION.REGISTER, _types5.METRIC_TYPE.BEHAVIORAL, _constants.REGISTRATION_UTIL, 'PRIMARY', 'webex-js-sdk_06bafdd0-2f9b-4cd7-b438-9c0d95ecec9b_15', undefined, undefined);
176
+ case 2:
177
+ case "end":
178
+ return _context.stop();
179
+ }
180
+ }, _callee);
181
+ })));
182
+ it('verify failure registration', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee2() {
183
+ var error;
184
+ return _regenerator.default.wrap(function (_context2) {
185
+ while (1) switch (_context2.prev = _context2.next) {
186
+ case 0:
187
+ webex.request.mockRejectedValue({
188
+ body: _registerFixtures.mockPostResponse,
189
+ statusCode: 401,
190
+ headers: {}
191
+ });
192
+ _context2.next = 1;
193
+ return reg.triggerRegistration();
194
+ case 1:
195
+ expect(webex.request).toBeCalledOnceWith(_objectSpread(_objectSpread({}, mockResponse), {}, {
196
+ method: 'POST'
197
+ }));
198
+ error = (0, _LineError.createLineError)('User is unauthorized due to an expired token. Sign out, then sign back in.', {}, _types3.ERROR_TYPE.TOKEN_ERROR, _types.RegistrationStatus.INACTIVE);
199
+ expect(reg.getStatus()).toEqual(_types.RegistrationStatus.INACTIVE);
200
+ expect(lineEmitter).toBeCalledTimes(2);
201
+ expect(lineEmitter).nthCalledWith(1, _types4.LINE_EVENTS.CONNECTING);
202
+ expect(lineEmitter).nthCalledWith(2, _types4.LINE_EVENTS.ERROR, undefined, error);
203
+ expect(metricSpy).toBeCalledWith(_types5.METRIC_EVENT.REGISTRATION_ERROR, _types5.REG_ACTION.REGISTER, _types5.METRIC_TYPE.BEHAVIORAL, _constants.REGISTRATION_UTIL, 'PRIMARY', '', undefined, error);
204
+ case 2:
205
+ case "end":
206
+ return _context2.stop();
207
+ }
208
+ }, _callee2);
209
+ })));
210
+ it('verify failure registration 403-101', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee3() {
211
+ return _regenerator.default.wrap(function (_context3) {
212
+ while (1) switch (_context3.prev = _context3.next) {
213
+ case 0:
214
+ webex.request.mockRejectedValueOnce({
215
+ body: {
216
+ userId: _registerFixtures.mockPostResponse.userId,
217
+ errorCode: 101,
218
+ devices: [_registerFixtures.mockPostResponse.device]
219
+ },
220
+ statusCode: 403
221
+ }).mockResolvedValueOnce({
222
+ statusCode: 200,
223
+ body: _registerFixtures.mockPostResponse,
224
+ headers: {
225
+ trackingid: 'webex-js-sdk_06bafdd0-2f9b-4cd7-b438-9c0d95ecec9b_15'
226
+ }
227
+ });
228
+ global.fetch = jest.fn(function () {
229
+ return _promise.default.resolve({
230
+ json: function json() {
231
+ return _registerFixtures.mockDeleteResponse;
232
+ }
233
+ });
234
+ });
235
+ expect(reg.getStatus()).toEqual(_types.RegistrationStatus.IDLE);
236
+ _context3.next = 1;
237
+ return reg.triggerRegistration();
238
+ case 1:
239
+ expect(webex.request).toBeCalledTimes(2);
240
+ expect(webex.request).toBeCalledWith(_objectSpread(_objectSpread({}, mockResponse), {}, {
241
+ method: 'POST'
242
+ }));
243
+ expect(global.fetch).toBeCalledOnceWith(_registerFixtures.mockPostResponse.device.uri, {
244
+ method: 'DELETE',
245
+ headers: expect.anything()
246
+ });
247
+ expect(warnSpy).toBeCalledWith('User device limit exceeded', expect.anything());
248
+ expect(infoSpy).toBeCalledWith('Registration restoration in progress.', expect.anything());
249
+ expect(infoSpy).toBeCalledWith('Registration restored successfully.', expect.anything());
250
+ expect(reg.getStatus()).toEqual(_types.RegistrationStatus.ACTIVE);
251
+ expect(lineEmitter).toBeCalledTimes(4);
252
+ expect(lineEmitter).nthCalledWith(1, _types4.LINE_EVENTS.CONNECTING);
253
+ expect(lineEmitter).nthCalledWith(2, _types4.LINE_EVENTS.UNREGISTERED);
254
+ expect(lineEmitter).nthCalledWith(3, _types4.LINE_EVENTS.CONNECTING);
255
+ expect(lineEmitter).nthCalledWith(4, _types4.LINE_EVENTS.REGISTERED, _registerFixtures.mockPostResponse);
256
+ expect(metricSpy).toBeCalledWith(_types5.METRIC_EVENT.REGISTRATION, _types5.REG_ACTION.REGISTER, _types5.METRIC_TYPE.BEHAVIORAL, _constants.REGISTRATION_UTIL, 'UNKNOWN', 'webex-js-sdk_06bafdd0-2f9b-4cd7-b438-9c0d95ecec9b_15', undefined, undefined);
257
+ case 2:
258
+ case "end":
259
+ return _context3.stop();
260
+ }
261
+ }, _callee3);
262
+ })));
263
+ describe('429 handling tests', function () {
264
+ var loggerContext = {
265
+ file: _constants.REGISTRATION_FILE,
266
+ method: _constants.FAILOVER_UTIL
267
+ };
268
+ var logSpy = jest.spyOn(_Logger.default, 'log');
269
+ beforeEach(function () {
270
+ mobiusUris.backup.pop();
271
+ });
272
+ afterEach(function () {
273
+ mobiusUris.backup.push(_registerFixtures.URL);
274
+ jest.clearAllMocks();
275
+ });
276
+ it('handle 429 received during initial registration failure and first attempt with primary', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee4() {
277
+ return _regenerator.default.wrap(function (_context4) {
278
+ while (1) switch (_context4.prev = _context4.next) {
279
+ case 0:
280
+ jest.useFakeTimers();
281
+ logSpy.mockClear();
282
+ webex.request.mockRejectedValueOnce(failurePayload429One).mockRejectedValueOnce(failurePayload429Two).mockRejectedValueOnce(failurePayload);
283
+ _context4.next = 1;
284
+ return reg.triggerRegistration();
285
+ case 1:
286
+ /* Initial registration failed with 429 with higher retyrAfter, interval should be updtaed with retryAfter.
287
+ * The first attempt to register with primary should be made after retryAfter seconds.
288
+ */
289
+
290
+ expect(webex.request).toHaveBeenNthCalledWith(1, _objectSpread(_objectSpread({}, mockResponse), {}, {
291
+ method: 'POST',
292
+ uri: "".concat(mobiusUris.primary[0], "device")
293
+ }));
294
+ expect(reg.getStatus()).toEqual(_types.RegistrationStatus.INACTIVE);
295
+ expect(retry429Spy).toBeCalledOnceWith(failurePayload429One.headers['retry-after'], 'triggerRegistration');
296
+ expect(reg.retryAfter).toEqual(failurePayload429One.headers['retry-after']);
297
+ expect(failoverSpy).toBeCalledOnceWith();
298
+ expect(logSpy).toBeCalledWith("Scheduled retry with primary in ".concat(failurePayload429One.headers['retry-after'], " seconds, number of attempts : 1"), loggerContext);
299
+ retry429Spy.mockClear();
300
+ failoverSpy.mockClear();
301
+ jest.advanceTimersByTime(Number(failurePayload429One.headers['retry-after']) * _constants.SEC_TO_MSEC_MFACTOR);
302
+ _context4.next = 2;
303
+ return flushPromises();
304
+ case 2:
305
+ /* The first attempt to register with primary failed with 429 with lower retryAfter, interval should remain the same.
306
+ * The second attempt to register with primary will be scheduled as per the interval calculated.
307
+ */
308
+
309
+ expect(webex.request).toHaveBeenNthCalledWith(2, _objectSpread(_objectSpread({}, mockResponse), {}, {
310
+ method: 'POST',
311
+ uri: "".concat(mobiusUris.primary[0], "device")
312
+ }));
313
+ expect(retry429Spy).toBeCalledOnceWith(failurePayload429Two.headers['retry-after'], 'startFailoverTimer');
314
+ expect(reg.retryAfter).toEqual(failurePayload429Two.headers['retry-after']);
315
+ expect(failoverSpy).toBeCalledOnceWith(2, failurePayload429One.headers['retry-after']);
316
+ retry429Spy.mockClear();
317
+ failoverSpy.mockClear();
318
+ jest.advanceTimersByTime(43 * _constants.SEC_TO_MSEC_MFACTOR);
319
+ _context4.next = 3;
320
+ return flushPromises();
321
+ case 3:
322
+ /* The second attempt to register with primary failed with 500, the retryAfter should be undefined.
323
+ * The third attempt to register with primary will be scheduled as per the interval calculated.
324
+ */
325
+ expect(webex.request).toHaveBeenNthCalledWith(3, _objectSpread(_objectSpread({}, mockResponse), {}, {
326
+ method: 'POST',
327
+ uri: "".concat(mobiusUris.primary[0], "device")
328
+ }));
329
+ expect(retry429Spy).not.toBeCalled();
330
+ expect(reg.retryAfter).toEqual(undefined);
331
+ expect(failoverSpy).toBeCalledOnceWith(3, 85);
332
+ case 4:
333
+ case "end":
334
+ return _context4.stop();
335
+ }
336
+ }, _callee4);
337
+ })));
338
+ it('handle 429 received with higher retryAfter than the interval when interval with elapsedTime is already reaching threshold timer so we failover immediately', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee5() {
339
+ return _regenerator.default.wrap(function (_context5) {
340
+ while (1) switch (_context5.prev = _context5.next) {
341
+ case 0:
342
+ reg.isCCFlow = true;
343
+ jest.spyOn(reg, 'getRegRetryInterval').mockReturnValueOnce(33).mockReturnValueOnce(40).mockReturnValueOnce(47).mockReturnValueOnce(52);
344
+ jest.useFakeTimers();
345
+ webex.request.mockRejectedValueOnce(failurePayload).mockRejectedValueOnce(failurePayload).mockRejectedValueOnce(failurePayload429One).mockResolvedValueOnce(successPayload);
346
+ _context5.next = 1;
347
+ return reg.triggerRegistration();
348
+ case 1:
349
+ expect(webex.request).toHaveBeenNthCalledWith(1, _objectSpread(_objectSpread({}, mockResponse), {}, {
350
+ method: 'POST',
351
+ uri: "".concat(mobiusUris.primary[0], "device")
352
+ }));
353
+ expect(reg.getStatus()).toEqual(_types.RegistrationStatus.INACTIVE);
354
+ expect(retry429Spy).not.toBeCalled();
355
+ expect(failoverSpy).toBeCalledOnceWith();
356
+ expect(logSpy).toBeCalledWith("Scheduled retry with primary in 33 seconds, number of attempts : 1", loggerContext);
357
+ failoverSpy.mockClear();
358
+ jest.advanceTimersByTime(33 * _constants.SEC_TO_MSEC_MFACTOR);
359
+ _context5.next = 2;
360
+ return flushPromises();
361
+ case 2:
362
+ expect(webex.request).toHaveBeenNthCalledWith(2, _objectSpread(_objectSpread({}, mockResponse), {}, {
363
+ method: 'POST',
364
+ uri: "".concat(mobiusUris.primary[0], "device")
365
+ }));
366
+ expect(retry429Spy).not.toBeCalled();
367
+ expect(failoverSpy).toBeCalledOnceWith(2, 33);
368
+ expect(logSpy).toBeCalledWith("Scheduled retry with primary in 40 seconds, number of attempts : 2", loggerContext);
369
+ logSpy.mockClear();
370
+ failoverSpy.mockClear();
371
+ jest.advanceTimersByTime(40 * _constants.SEC_TO_MSEC_MFACTOR);
372
+ _context5.next = 3;
373
+ return flushPromises();
374
+ case 3:
375
+ expect(webex.request).toHaveBeenNthCalledWith(3, _objectSpread(_objectSpread({}, mockResponse), {}, {
376
+ method: 'POST',
377
+ uri: "".concat(mobiusUris.primary[0], "device")
378
+ }));
379
+ expect(retry429Spy).toBeCalledOnceWith(failurePayload429One.headers['retry-after'], 'startFailoverTimer');
380
+ expect(failoverSpy).toBeCalledOnceWith(3, 73);
381
+ expect(logSpy).not.toBeCalledWith("Scheduled retry with primary in ".concat(failurePayload429One.headers['retry-after'], " seconds, number of attempts : 3"), loggerContext);
382
+ expect(infoSpy).toBeCalledWith("Failing over to backup servers.", loggerContext);
383
+ expect(webex.request).toHaveBeenNthCalledWith(4, _objectSpread(_objectSpread({}, mockResponse), {}, {
384
+ method: 'POST',
385
+ uri: "".concat(mobiusUris.backup[0], "device")
386
+ }));
387
+ expect(reg.getStatus()).toEqual(_types.RegistrationStatus.ACTIVE);
388
+ case 4:
389
+ case "end":
390
+ return _context5.stop();
391
+ }
392
+ }, _callee5);
393
+ })));
394
+ it('handle 429 received while the last attempt for primary', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee6() {
395
+ return _regenerator.default.wrap(function (_context6) {
396
+ while (1) switch (_context6.prev = _context6.next) {
397
+ case 0:
398
+ reg.isCCFlow = true;
399
+ jest.spyOn(reg, 'getRegRetryInterval').mockReturnValueOnce(33).mockReturnValueOnce(40).mockReturnValueOnce(47).mockReturnValueOnce(52);
400
+ jest.useFakeTimers();
401
+ webex.request.mockRejectedValueOnce(failurePayload).mockRejectedValueOnce(failurePayload).mockRejectedValueOnce(failurePayload).mockRejectedValueOnce(failurePayload429One).mockResolvedValueOnce(successPayload);
402
+ _context6.next = 1;
403
+ return reg.triggerRegistration();
404
+ case 1:
405
+ /* Initial registration and first 2 attempts with primary failed with non-final 5xx error responses.
406
+ * Last attempt with primary failed with 429, the retryAfter should be used to schedule the next attempt but
407
+ * the failover is triggered before the scheduling logic kicks in.
408
+ */
409
+ expect(webex.request).toHaveBeenNthCalledWith(1, _objectSpread(_objectSpread({}, mockResponse), {}, {
410
+ method: 'POST',
411
+ uri: "".concat(mobiusUris.primary[0], "device")
412
+ }));
413
+ expect(reg.getStatus()).toEqual(_types.RegistrationStatus.INACTIVE);
414
+ expect(retry429Spy).not.toBeCalled();
415
+ expect(failoverSpy).toBeCalledOnceWith();
416
+ expect(logSpy).toBeCalledWith("Scheduled retry with primary in 33 seconds, number of attempts : 1", loggerContext);
417
+ failoverSpy.mockClear();
418
+ jest.advanceTimersByTime(33 * _constants.SEC_TO_MSEC_MFACTOR);
419
+ _context6.next = 2;
420
+ return flushPromises();
421
+ case 2:
422
+ expect(webex.request).toHaveBeenNthCalledWith(2, _objectSpread(_objectSpread({}, mockResponse), {}, {
423
+ method: 'POST',
424
+ uri: "".concat(mobiusUris.primary[0], "device")
425
+ }));
426
+ expect(retry429Spy).not.toBeCalled();
427
+ expect(failoverSpy).toBeCalledOnceWith(2, 33);
428
+ expect(logSpy).toBeCalledWith("Scheduled retry with primary in 40 seconds, number of attempts : 2", loggerContext);
429
+ logSpy.mockClear();
430
+ failoverSpy.mockClear();
431
+ jest.advanceTimersByTime(40 * _constants.SEC_TO_MSEC_MFACTOR);
432
+ _context6.next = 3;
433
+ return flushPromises();
434
+ case 3:
435
+ expect(webex.request).toHaveBeenNthCalledWith(3, _objectSpread(_objectSpread({}, mockResponse), {}, {
436
+ method: 'POST',
437
+ uri: "".concat(mobiusUris.primary[0], "device")
438
+ }));
439
+ expect(retry429Spy).not.toBeCalled();
440
+ expect(failoverSpy).toBeCalledOnceWith(3, 73);
441
+ expect(logSpy).toBeCalledWith("Scheduled retry with primary in 41 seconds, number of attempts : 3", loggerContext);
442
+ failoverSpy.mockClear();
443
+ jest.advanceTimersByTime(41 * _constants.SEC_TO_MSEC_MFACTOR);
444
+ _context6.next = 4;
445
+ return flushPromises();
446
+ case 4:
447
+ expect(webex.request).toHaveBeenNthCalledWith(4, _objectSpread(_objectSpread({}, mockResponse), {}, {
448
+ method: 'POST',
449
+ uri: "".concat(mobiusUris.primary[0], "device")
450
+ }));
451
+ expect(retry429Spy).toBeCalledOnceWith(failurePayload429One.headers['retry-after'], 'startFailoverTimer');
452
+ expect(failoverSpy).toBeCalledOnceWith(4, 114);
453
+ expect(infoSpy).toBeCalledWith("Failing over to backup servers.", loggerContext);
454
+ expect(webex.request).toHaveBeenNthCalledWith(5, _objectSpread(_objectSpread({}, mockResponse), {}, {
455
+ method: 'POST',
456
+ uri: "".concat(mobiusUris.backup[0], "device")
457
+ }));
458
+ expect(reg.getStatus()).toEqual(_types.RegistrationStatus.ACTIVE);
459
+ case 5:
460
+ case "end":
461
+ return _context6.stop();
462
+ }
463
+ }, _callee6);
464
+ })));
465
+ it('handle 429 received while failing over to backup server for CC flow', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee7() {
466
+ return _regenerator.default.wrap(function (_context7) {
467
+ while (1) switch (_context7.prev = _context7.next) {
468
+ case 0:
469
+ reg.isCCFlow = true;
470
+ jest.useFakeTimers();
471
+ webex.request.mockRejectedValueOnce(failurePayload).mockRejectedValueOnce(failurePayload).mockRejectedValueOnce(failurePayload429One).mockResolvedValueOnce(successPayload);
472
+ _context7.next = 1;
473
+ return reg.triggerRegistration();
474
+ case 1:
475
+ jest.advanceTimersByTime(_constants.REG_TRY_BACKUP_TIMER_VAL_IN_SEC * _constants.SEC_TO_MSEC_MFACTOR);
476
+ _context7.next = 2;
477
+ return flushPromises();
478
+ case 2:
479
+ expect(webex.request).toBeCalledTimes(3);
480
+ expect(webex.request).toHaveBeenNthCalledWith(1, _objectSpread(_objectSpread({}, mockResponse), {}, {
481
+ method: 'POST',
482
+ uri: "".concat(mobiusUris.primary[0], "device")
483
+ }));
484
+ expect(webex.request).toHaveBeenNthCalledWith(2, _objectSpread(_objectSpread({}, mockResponse), {}, {
485
+ method: 'POST',
486
+ uri: "".concat(mobiusUris.primary[0], "device")
487
+ }));
488
+
489
+ /* Failover to backup server failed with 429, the retryAfter is used to schedule the next attempt with backup server.
490
+ * Interval will be updated with retryAfter as interval calculated is less than the retryAfter.
491
+ */
492
+ expect(webex.request).toHaveBeenNthCalledWith(3, _objectSpread(_objectSpread({}, mockResponse), {}, {
493
+ method: 'POST',
494
+ uri: "".concat(mobiusUris.backup[0], "device")
495
+ }));
496
+ expect(retry429Spy).toBeCalledOnceWith(failurePayload429One.headers['retry-after'], 'startFailoverTimer');
497
+ expect(logSpy).toBeCalledWith("Scheduled retry with backup servers in ".concat(failurePayload429One.headers['retry-after'], " seconds."), loggerContext);
498
+ webex.request.mockClear();
499
+ jest.advanceTimersByTime(Number(failurePayload429One.headers['retry-after']) * _constants.SEC_TO_MSEC_MFACTOR);
500
+ _context7.next = 3;
501
+ return flushPromises();
502
+ case 3:
503
+ expect(webex.request).toBeCalledOnceWith(_objectSpread(_objectSpread({}, mockResponse), {}, {
504
+ method: 'POST',
505
+ uri: "".concat(mobiusUris.backup[0], "device")
506
+ }));
507
+ expect(reg.getStatus()).toEqual(_types.RegistrationStatus.ACTIVE);
508
+ case 4:
509
+ case "end":
510
+ return _context7.stop();
511
+ }
512
+ }, _callee7);
513
+ })));
514
+ it('checking the retryAfter exceeding the threshold timers in first attempt itself', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee8() {
515
+ return _regenerator.default.wrap(function (_context8) {
516
+ while (1) switch (_context8.prev = _context8.next) {
517
+ case 0:
518
+ reg.isCCFlow = true;
519
+ jest.useFakeTimers();
520
+ jest.spyOn(reg, 'getRegRetryInterval').mockReturnValueOnce(40);
521
+ webex.request.mockRejectedValueOnce(failurePayload429Three);
522
+ _context8.next = 1;
523
+ return reg.triggerRegistration();
524
+ case 1:
525
+ expect(webex.request).toHaveBeenNthCalledWith(1, _objectSpread(_objectSpread({}, mockResponse), {}, {
526
+ method: 'POST',
527
+ uri: "".concat(mobiusUris.primary[0], "device")
528
+ }));
529
+ expect(reg.getStatus()).toEqual(_types.RegistrationStatus.INACTIVE);
530
+ expect(failoverSpy).toBeCalledOnceWith();
531
+ expect(infoSpy).toBeCalledWith("Failing over to backup servers.", loggerContext);
532
+ expect(logSpy).not.toBeCalledWith("Scheduled retry with primary in 40 seconds, number of attempts : 1", loggerContext);
533
+ expect(logSpy).not.toBeCalledWith("Scheduled retry with primary in ".concat(failurePayload429Three.headers['retry-after'], " seconds, number of attempts : 1"), loggerContext);
534
+ expect(webex.request).toHaveBeenNthCalledWith(2, _objectSpread(_objectSpread({}, mockResponse), {}, {
535
+ method: 'POST',
536
+ uri: "".concat(mobiusUris.backup[0], "device")
537
+ }));
538
+ case 2:
539
+ case "end":
540
+ return _context8.stop();
541
+ }
542
+ }, _callee8);
543
+ })));
544
+ it('checking the retryAfter exceeding the threshold timers in later attempts', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee9() {
545
+ return _regenerator.default.wrap(function (_context9) {
546
+ while (1) switch (_context9.prev = _context9.next) {
547
+ case 0:
548
+ reg.isCCFlow = true;
549
+ jest.useFakeTimers();
550
+ jest.spyOn(reg, 'getRegRetryInterval').mockReturnValueOnce(39).mockReturnValueOnce(43);
551
+ webex.request.mockRejectedValueOnce(failurePayload429One).mockRejectedValueOnce(failurePayload429Four).mockResolvedValueOnce(successPayload);
552
+ _context9.next = 1;
553
+ return reg.triggerRegistration();
554
+ case 1:
555
+ expect(webex.request).toHaveBeenNthCalledWith(1, _objectSpread(_objectSpread({}, mockResponse), {}, {
556
+ method: 'POST',
557
+ uri: "".concat(mobiusUris.primary[0], "device")
558
+ }));
559
+ expect(reg.getStatus()).toEqual(_types.RegistrationStatus.INACTIVE);
560
+ expect(failoverSpy).toBeCalledOnceWith();
561
+ expect(logSpy).toBeCalledWith("Scheduled retry with primary in ".concat(failurePayload429One.headers['retry-after'], " seconds, number of attempts : 1"), loggerContext);
562
+ failoverSpy.mockClear();
563
+ jest.advanceTimersByTime(Number(failurePayload429One.headers['retry-after']) * _constants.SEC_TO_MSEC_MFACTOR);
564
+ _context9.next = 2;
565
+ return flushPromises();
566
+ case 2:
567
+ expect(webex.request).toHaveBeenNthCalledWith(2, _objectSpread(_objectSpread({}, mockResponse), {}, {
568
+ method: 'POST',
569
+ uri: "".concat(mobiusUris.primary[0], "device")
570
+ }));
571
+ expect(failoverSpy).toBeCalledOnceWith(2, failurePayload429One.headers['retry-after']);
572
+ expect(logSpy).not.toBeCalledWith("Scheduled retry with primary in 43 seconds, number of attempts : 2", loggerContext);
573
+ expect(infoSpy).toBeCalledWith("Failing over to backup servers.", loggerContext);
574
+ expect(logSpy).not.toBeCalledWith("Scheduled retry with primary in ".concat(failurePayload429Four.headers['retry-after'], " seconds, number of attempts : 2"), loggerContext);
575
+ expect(infoSpy).toBeCalledWith("Failing over to backup servers.", loggerContext);
576
+ expect(webex.request).toHaveBeenNthCalledWith(3, _objectSpread(_objectSpread({}, mockResponse), {}, {
577
+ method: 'POST',
578
+ uri: "".concat(mobiusUris.backup[0], "device")
579
+ }));
580
+ expect(reg.getStatus()).toEqual(_types.RegistrationStatus.ACTIVE);
581
+ case 3:
582
+ case "end":
583
+ return _context9.stop();
584
+ }
585
+ }, _callee9);
586
+ })));
587
+ });
588
+ describe('restorePreviousRegistration 429 handling tests', function () {
589
+ beforeEach(function () {
590
+ jest.useFakeTimers();
591
+ });
592
+ afterEach(function () {
593
+ jest.clearAllTimers();
594
+ jest.clearAllMocks();
595
+ jest.useRealTimers();
596
+ });
597
+ it('should schedule retry when 429 with retry-after < 60 seconds during reconnect', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee0() {
598
+ var failurePayload429Small;
599
+ return _regenerator.default.wrap(function (_context0) {
600
+ while (1) switch (_context0.prev = _context0.next) {
601
+ case 0:
602
+ restartSpy = jest.spyOn(reg, 'restartRegistration');
603
+ reg.setActiveMobiusUrl(mobiusUris.primary[0]);
604
+ failurePayload429Small = {
605
+ statusCode: 429,
606
+ body: _constants.RECONNECT_ON_FAILURE_UTIL,
607
+ headers: {
608
+ 'retry-after': 30
609
+ }
610
+ };
611
+ webex.request.mockRejectedValueOnce(failurePayload429Small).mockResolvedValueOnce(successPayload);
612
+ _context0.next = 1;
613
+ return reg.reconnectOnFailure(_constants.RECONNECT_ON_FAILURE_UTIL);
614
+ case 1:
615
+ // This call is being used to set the retry-after value
616
+ // Verify restore is invoked first and retry-after captured before scheduling
617
+ expect(restoreSpy).toBeCalledOnceWith(_constants.RECONNECT_ON_FAILURE_UTIL);
618
+ expect(restartSpy).not.toBeCalled();
619
+ expect(reg.retryAfter).toEqual(undefined); // Clear retryAfter after 429 retry
620
+
621
+ jest.advanceTimersByTime(40 * _constants.SEC_TO_MSEC_MFACTOR);
622
+ _context0.next = 2;
623
+ return flushPromises();
624
+ case 2:
625
+ expect(restartSpy).toHaveBeenCalledTimes(1);
626
+ expect(restartSpy).toHaveBeenCalledWith(_constants.RECONNECT_ON_FAILURE_UTIL);
627
+ case 3:
628
+ case "end":
629
+ return _context0.stop();
630
+ }
631
+ }, _callee0);
632
+ })));
633
+ it('should try backup servers when 429 with retry-after >= 60 seconds on primary during reconnect', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee1() {
634
+ var attemptRegistrationWithServersSpy, failurePayload429Small;
635
+ return _regenerator.default.wrap(function (_context1) {
636
+ while (1) switch (_context1.prev = _context1.next) {
637
+ case 0:
638
+ // Setup: Register successfully with primary first
639
+ attemptRegistrationWithServersSpy = jest.spyOn(reg, 'attemptRegistrationWithServers');
640
+ reg.setActiveMobiusUrl(mobiusUris.primary[0]);
641
+ failurePayload429Small = {
642
+ statusCode: 429,
643
+ body: _constants.RECONNECT_ON_FAILURE_UTIL,
644
+ headers: {
645
+ 'retry-after': 100
646
+ }
647
+ };
648
+ webex.request.mockRejectedValueOnce(failurePayload429Small).mockResolvedValueOnce(successPayload);
649
+ _context1.next = 1;
650
+ return reg.reconnectOnFailure(_constants.RECONNECT_ON_FAILURE_UTIL);
651
+ case 1:
652
+ // This call is being used to trigger the retry
653
+ // Verify restore gets invoked, 429 with retry-after is observed and captured
654
+ expect(restoreSpy).toBeCalledOnceWith(_constants.RECONNECT_ON_FAILURE_UTIL);
655
+ expect(retry429Spy).toBeCalledOnceWith(100, _constants.RECONNECT_ON_FAILURE_UTIL);
656
+ jest.advanceTimersByTime(40 * _constants.SEC_TO_MSEC_MFACTOR);
657
+ _context1.next = 2;
658
+ return flushPromises();
659
+ case 2:
660
+ expect(attemptRegistrationWithServersSpy).toHaveBeenCalledTimes(2);
661
+ expect(attemptRegistrationWithServersSpy).toHaveBeenNthCalledWith(1, _constants.RECONNECT_ON_FAILURE_UTIL, [mobiusUris.primary[0]]);
662
+ // Immediately try backup servers when retry-after >= 60 seconds on primary
663
+ expect(attemptRegistrationWithServersSpy).toHaveBeenNthCalledWith(2, _constants.RECONNECT_ON_FAILURE_UTIL, mobiusUris.backup);
664
+ expect(restartSpy).not.toHaveBeenCalledTimes(1);
665
+ expect(restartSpy).not.toHaveBeenCalledWith(_constants.RECONNECT_ON_FAILURE_UTIL);
666
+ expect(reg.retryAfter).toEqual(undefined); // Clear retryAfter after 429 retry
667
+ case 3:
668
+ case "end":
669
+ return _context1.stop();
670
+ }
671
+ }, _callee1);
672
+ })));
673
+ it('should restart registration with primary if we get 429 while on backup', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee10() {
674
+ var attemptRegistrationWithServersSpy, failurePayload429Small;
675
+ return _regenerator.default.wrap(function (_context10) {
676
+ while (1) switch (_context10.prev = _context10.next) {
677
+ case 0:
678
+ // Setup: Register successfully with primary first
679
+ restartSpy = jest.spyOn(reg, 'restartRegistration');
680
+ attemptRegistrationWithServersSpy = jest.spyOn(reg, 'attemptRegistrationWithServers');
681
+ reg.setActiveMobiusUrl(mobiusUris.backup[0]);
682
+ failurePayload429Small = {
683
+ statusCode: 429,
684
+ body: _constants.RECONNECT_ON_FAILURE_UTIL,
685
+ headers: {
686
+ 'retry-after': 100
687
+ }
688
+ };
689
+ webex.request.mockRejectedValueOnce(failurePayload429Small).mockResolvedValueOnce(successPayload).mockRejectedValueOnce(failurePayload429Small).mockResolvedValueOnce(successPayload);
690
+ _context10.next = 1;
691
+ return reg.reconnectOnFailure(_constants.RECONNECT_ON_FAILURE_UTIL);
692
+ case 1:
693
+ // This call is being used to trigger the retry
694
+ // Verify restore path taken first and 429 handling captured
695
+ expect(restoreSpy).toBeCalledOnceWith(_constants.RECONNECT_ON_FAILURE_UTIL);
696
+ expect(retry429Spy).toBeCalledOnceWith(100, _constants.RECONNECT_ON_FAILURE_UTIL);
697
+ // No failover scheduling expected in this path
698
+ expect(failoverSpy).not.toBeCalled();
699
+ jest.advanceTimersByTime(40 * _constants.SEC_TO_MSEC_MFACTOR);
700
+ _context10.next = 2;
701
+ return flushPromises();
702
+ case 2:
703
+ expect(attemptRegistrationWithServersSpy).toHaveBeenCalledTimes(2);
704
+ expect(attemptRegistrationWithServersSpy).toHaveBeenNthCalledWith(1, _constants.RECONNECT_ON_FAILURE_UTIL, [mobiusUris.backup[0]]);
705
+ // Immediately try primary servers when retry-after >= 60 seconds on backup
706
+ expect(restartSpy).toHaveBeenCalledTimes(1);
707
+ expect(restartSpy).toHaveBeenCalledWith(_constants.RECONNECT_ON_FAILURE_UTIL);
708
+ expect(attemptRegistrationWithServersSpy).toHaveBeenNthCalledWith(2, _constants.RECONNECT_ON_FAILURE_UTIL, [mobiusUris.primary[0]]);
709
+ case 3:
710
+ case "end":
711
+ return _context10.stop();
712
+ }
713
+ }, _callee10);
714
+ })));
715
+ });
716
+ describe('handleConnectionRestoration 429 handling tests', function () {
717
+ beforeEach(function () {
718
+ jest.useFakeTimers();
719
+ });
720
+ afterEach(function () {
721
+ jest.clearAllTimers();
722
+ jest.clearAllMocks();
723
+ jest.useRealTimers();
724
+ });
725
+ it('should schedule retry when 429 with retry-after < 60 seconds during handleConnectionRestoration', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee11() {
726
+ var failurePayload429Small;
727
+ return _regenerator.default.wrap(function (_context11) {
728
+ while (1) switch (_context11.prev = _context11.next) {
729
+ case 0:
730
+ reg.setActiveMobiusUrl(mobiusUris.primary[0]);
731
+ failurePayload429Small = {
732
+ statusCode: 429,
733
+ body: _constants.RECONNECT_ON_FAILURE_UTIL,
734
+ headers: {
735
+ 'retry-after': 30
736
+ }
737
+ };
738
+ webex.request.mockRejectedValueOnce(failurePayload429Small).mockResolvedValueOnce(successPayload).mockRejectedValueOnce(failurePayload429Small).mockResolvedValueOnce(successPayload);
739
+ _context11.next = 1;
740
+ return reg.handleConnectionRestoration(true);
741
+ case 1:
742
+ expect(restoreSpy).toBeCalledOnceWith(_constants.METHODS.HANDLE_CONNECTION_RESTORATION);
743
+ expect(restartSpy).not.toBeCalled();
744
+ expect(reg.retryAfter).toEqual(undefined); // Clear retryAfter after 429 retry
745
+
746
+ jest.advanceTimersByTime(40 * _constants.SEC_TO_MSEC_MFACTOR);
747
+ _context11.next = 2;
748
+ return flushPromises();
749
+ case 2:
750
+ expect(restartSpy).toHaveBeenCalledTimes(1);
751
+ expect(restartSpy).toHaveBeenCalledWith(_constants.METHODS.HANDLE_CONNECTION_RESTORATION);
752
+ case 3:
753
+ case "end":
754
+ return _context11.stop();
755
+ }
756
+ }, _callee11);
757
+ })));
758
+ it('should try backup servers when 429 with retry-after >= 60 seconds on primary during handleConnectionRestoration', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee12() {
759
+ var attemptRegistrationWithServersSpy, failurePayload429Small;
760
+ return _regenerator.default.wrap(function (_context12) {
761
+ while (1) switch (_context12.prev = _context12.next) {
762
+ case 0:
763
+ attemptRegistrationWithServersSpy = jest.spyOn(reg, 'attemptRegistrationWithServers');
764
+ reg.setActiveMobiusUrl(mobiusUris.primary[0]);
765
+ failurePayload429Small = {
766
+ statusCode: 429,
767
+ body: _constants.RECONNECT_ON_FAILURE_UTIL,
768
+ headers: {
769
+ 'retry-after': 100
770
+ }
771
+ };
772
+ webex.request.mockRejectedValueOnce(failurePayload429Small).mockResolvedValueOnce(successPayload).mockRejectedValueOnce(failurePayload429Small).mockResolvedValueOnce(successPayload);
773
+ _context12.next = 1;
774
+ return reg.handleConnectionRestoration(true);
775
+ case 1:
776
+ expect(restoreSpy).toBeCalledOnceWith(_constants.METHODS.HANDLE_CONNECTION_RESTORATION);
777
+ expect(retry429Spy).toBeCalledOnceWith(100, _constants.METHODS.HANDLE_CONNECTION_RESTORATION);
778
+ jest.advanceTimersByTime(40 * _constants.SEC_TO_MSEC_MFACTOR);
779
+ _context12.next = 2;
780
+ return flushPromises();
781
+ case 2:
782
+ expect(attemptRegistrationWithServersSpy).toHaveBeenCalledTimes(2);
783
+ expect(attemptRegistrationWithServersSpy).toHaveBeenNthCalledWith(1, _constants.METHODS.HANDLE_CONNECTION_RESTORATION, [mobiusUris.primary[0]]);
784
+ // Immediately try backup servers when retry-after >= 60 seconds on primary
785
+ expect(attemptRegistrationWithServersSpy).toHaveBeenNthCalledWith(2, _constants.METHODS.HANDLE_CONNECTION_RESTORATION, mobiusUris.backup);
786
+ expect(restartSpy).not.toHaveBeenCalledTimes(1);
787
+ expect(restartSpy).not.toHaveBeenCalledWith(_constants.METHODS.HANDLE_CONNECTION_RESTORATION);
788
+ case 3:
789
+ case "end":
790
+ return _context12.stop();
791
+ }
792
+ }, _callee12);
793
+ })));
794
+ });
795
+ describe('Registration failover tests', function () {
796
+ it('persists failover state in localStorage when primary retry is scheduled', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee13() {
797
+ var key, raw, state;
798
+ return _regenerator.default.wrap(function (_context13) {
799
+ while (1) switch (_context13.prev = _context13.next) {
800
+ case 0:
801
+ jest.useFakeTimers();
802
+ // Force initial registration to fail to schedule failover
803
+ webex.request.mockRejectedValueOnce(failurePayload);
804
+ expect(reg.getStatus()).toEqual(_types.RegistrationStatus.IDLE);
805
+ _context13.next = 1;
806
+ return reg.triggerRegistration();
807
+ case 1:
808
+ // A failover timer should be scheduled; verify localStorage contains state
809
+ key = "wxc-failover-state.".concat(webex.internal.device.userId);
810
+ raw = localStorage.getItem(key);
811
+ expect(raw).toBeTruthy();
812
+ state = JSON.parse(raw);
813
+ expect(state).toEqual(expect.objectContaining({
814
+ attempt: 1,
815
+ timeElapsed: 0,
816
+ retryScheduledTime: expect.any(Number),
817
+ serverType: 'primary'
818
+ }));
819
+ case 2:
820
+ case "end":
821
+ return _context13.stop();
822
+ }
823
+ }, _callee13);
824
+ })));
825
+ it('resumes failover from localStorage on triggerRegistration', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee14() {
826
+ var key, now, failoverSpy, _failoverSpy$mock$cal, attemptArg, timeElapsedArg;
827
+ return _regenerator.default.wrap(function (_context14) {
828
+ while (1) switch (_context14.prev = _context14.next) {
829
+ case 0:
830
+ jest.useFakeTimers();
831
+ key = "wxc-failover-state.".concat(webex.internal.device.userId);
832
+ now = Math.floor((0, _now.default)() / 1000); // Seed a cached state indicating a retry should have already occurred 5s ago
833
+ localStorage.setItem(key, (0, _stringify.default)({
834
+ attempt: 3,
835
+ timeElapsed: 12,
836
+ retryScheduledTime: now - 5,
837
+ serverType: 'primary'
838
+ }));
839
+ failoverSpy = jest.spyOn(reg, 'startFailoverTimer');
840
+ webex.request.mockResolvedValueOnce(successPayload);
841
+ _context14.next = 1;
842
+ return reg.triggerRegistration();
843
+ case 1:
844
+ expect(failoverSpy).toHaveBeenCalledTimes(1);
845
+ _failoverSpy$mock$cal = (0, _slicedToArray2.default)(failoverSpy.mock.calls[0], 2), attemptArg = _failoverSpy$mock$cal[0], timeElapsedArg = _failoverSpy$mock$cal[1];
846
+ expect(attemptArg).toBe(3);
847
+ expect(timeElapsedArg).toBeGreaterThanOrEqual(12);
848
+ case 2:
849
+ case "end":
850
+ return _context14.stop();
851
+ }
852
+ }, _callee14);
853
+ })));
854
+ it('verify unreachable primary with reachable backup servers', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee15() {
855
+ return _regenerator.default.wrap(function (_context15) {
856
+ while (1) switch (_context15.prev = _context15.next) {
857
+ case 0:
858
+ jest.useFakeTimers();
859
+ // try the primary twice and register successfully with backup servers
860
+ webex.request.mockRejectedValueOnce(failurePayload).mockRejectedValueOnce(failurePayload).mockResolvedValueOnce(successPayload);
861
+ expect(reg.getStatus()).toEqual(_types.RegistrationStatus.IDLE);
862
+ _context15.next = 1;
863
+ return reg.triggerRegistration();
864
+ case 1:
865
+ jest.advanceTimersByTime(_constants.REG_TRY_BACKUP_TIMER_VAL_IN_SEC * _constants.SEC_TO_MSEC_MFACTOR);
866
+ _context15.next = 2;
867
+ return flushPromises();
868
+ case 2:
869
+ expect(webex.request).toBeCalledTimes(3);
870
+ expect(webex.request).toBeCalledWith(_objectSpread(_objectSpread({}, mockResponse), {}, {
871
+ method: 'POST',
872
+ uri: "".concat(mobiusUris.primary[0], "device")
873
+ }));
874
+ expect(webex.request).toBeCalledWith(_objectSpread(_objectSpread({}, mockResponse), {}, {
875
+ method: 'POST',
876
+ uri: "".concat(mobiusUris.backup[0], "device")
877
+ }));
878
+ expect(reg.getStatus()).toEqual(_types.RegistrationStatus.ACTIVE);
879
+ /* Active Url must match with the backup url as per the test */
880
+ expect(reg.getActiveMobiusUrl()).toEqual(mobiusUris.backup[0]);
881
+ expect(metricSpy).toHaveBeenNthCalledWith(3, _types5.METRIC_EVENT.REGISTRATION, _types5.REG_ACTION.REGISTER, _types5.METRIC_TYPE.BEHAVIORAL, _constants.FAILOVER_UTIL, 'BACKUP', 'webex-js-sdk_06bafdd0-2f9b-4cd7-b438-9c0d95ecec9b_15', undefined, undefined);
882
+ case 3:
883
+ case "end":
884
+ return _context15.stop();
885
+ }
886
+ }, _callee15);
887
+ })));
888
+ it('cc: verify unreachable primary with reachable backup server', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee16() {
889
+ return _regenerator.default.wrap(function (_context16) {
890
+ while (1) switch (_context16.prev = _context16.next) {
891
+ case 0:
892
+ setupRegistration(_objectSpread(_objectSpread({}, MockServiceData), {}, {
893
+ indicator: _types.ServiceIndicator.CONTACT_CENTER
894
+ }));
895
+ jest.useFakeTimers();
896
+ webex.request.mockRejectedValueOnce(failurePayload).mockRejectedValueOnce(failurePayload).mockResolvedValueOnce(successPayload);
897
+ expect(reg.getStatus()).toEqual(_types.RegistrationStatus.IDLE);
898
+ _context16.next = 1;
899
+ return reg.triggerRegistration();
900
+ case 1:
901
+ jest.advanceTimersByTime(_constants.REG_TRY_BACKUP_TIMER_VAL_IN_SEC * _constants.SEC_TO_MSEC_MFACTOR);
902
+ _context16.next = 2;
903
+ return flushPromises();
904
+ case 2:
905
+ expect(webex.request).toBeCalledTimes(3);
906
+ expect(webex.request).toBeCalledWith(_objectSpread(_objectSpread({}, ccMockResponse), {}, {
907
+ method: 'POST',
908
+ uri: "".concat(mobiusUris.primary[0], "device")
909
+ }));
910
+ expect(webex.request).toBeCalledWith(_objectSpread(_objectSpread({}, ccMockResponse), {}, {
911
+ method: 'POST',
912
+ uri: "".concat(mobiusUris.backup[0], "device")
913
+ }));
914
+ expect(reg.getStatus()).toEqual(_types.RegistrationStatus.ACTIVE);
915
+ /* Active Url must match with the backup url as per the test */
916
+ expect(reg.getActiveMobiusUrl()).toEqual(mobiusUris.backup[0]);
917
+ case 3:
918
+ case "end":
919
+ return _context16.stop();
920
+ }
921
+ }, _callee16);
922
+ })));
923
+ it('verify unreachable primary and backup servers', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee17() {
924
+ return _regenerator.default.wrap(function (_context17) {
925
+ while (1) switch (_context17.prev = _context17.next) {
926
+ case 0:
927
+ jest.useFakeTimers();
928
+ // try the primary twice and register successfully with backup servers
929
+ webex.request.mockRejectedValue(failurePayload);
930
+ expect(reg.getStatus()).toEqual(_types.RegistrationStatus.IDLE);
931
+ _context17.next = 1;
932
+ return reg.triggerRegistration();
933
+ case 1:
934
+ jest.advanceTimersByTime(_constants.REG_TRY_BACKUP_TIMER_VAL_IN_SEC * _constants.SEC_TO_MSEC_MFACTOR);
935
+ _context17.next = 2;
936
+ return flushPromises();
937
+ case 2:
938
+ jest.advanceTimersByTime(_constants.REG_TRY_BACKUP_TIMER_VAL_IN_SEC * _constants.SEC_TO_MSEC_MFACTOR);
939
+ _context17.next = 3;
940
+ return flushPromises();
941
+ case 3:
942
+ /*
943
+ * 2 calls for primary -> initial and after timer expiry.
944
+ * 2 calls for each backup entry -> 2 * 2 = 4.
945
+ * So a total of 6 calls to webex.request and handleErrors
946
+ */
947
+ expect(webex.request).toBeCalledTimes(6);
948
+ expect(handleErrorSpy).toBeCalledTimes(6);
949
+ expect(webex.request).toBeCalledWith(_objectSpread(_objectSpread({}, mockResponse), {}, {
950
+ method: 'POST',
951
+ uri: "".concat(mobiusUris.primary[0], "device")
952
+ }));
953
+ expect(webex.request).toBeCalledWith(_objectSpread(_objectSpread({}, mockResponse), {}, {
954
+ method: 'POST',
955
+ uri: "".concat(mobiusUris.backup[0], "device")
956
+ }));
957
+ expect(webex.request).toBeCalledWith(_objectSpread(_objectSpread({}, mockResponse), {}, {
958
+ method: 'POST',
959
+ uri: "".concat(mobiusUris.backup[1], "device")
960
+ }));
961
+ expect(reg.getStatus()).toEqual(_types.RegistrationStatus.INACTIVE);
962
+ case 4:
963
+ case "end":
964
+ return _context17.stop();
965
+ }
966
+ }, _callee17);
967
+ })));
968
+ });
969
+ describe('Registration failback tests', function () {
970
+ var isPrimaryActiveSpy;
971
+ beforeEach(/*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee18() {
972
+ return _regenerator.default.wrap(function (_context18) {
973
+ while (1) switch (_context18.prev = _context18.next) {
974
+ case 0:
975
+ isPrimaryActiveSpy = jest.spyOn(reg, 'isPrimaryActive');
976
+ isPrimaryActiveSpy.mockReturnValue(true);
977
+ /* keep keepalive as active so that it wont interfere with the failback tests */
978
+ jest.useFakeTimers();
979
+ postRegistrationSpy.mockRejectedValueOnce(failurePayload).mockRejectedValueOnce(failurePayload).mockResolvedValueOnce(successPayload);
980
+ _context18.next = 1;
981
+ return reg.triggerRegistration();
982
+ case 1:
983
+ jest.advanceTimersByTime(_constants.REG_TRY_BACKUP_TIMER_VAL_IN_SEC * _constants.SEC_TO_MSEC_MFACTOR);
984
+ _context18.next = 2;
985
+ return flushPromises();
986
+ case 2:
987
+ reg.rehomingIntervalMin = _constants.DEFAULT_REHOMING_INTERVAL_MIN;
988
+ reg.rehomingIntervalMax = _constants.DEFAULT_REHOMING_INTERVAL_MAX;
989
+
990
+ /* These 2 calls to handleErrorSpy are for primary after which it fails over to backup */
991
+ expect(handleErrorSpy).toBeCalledTimes(2);
992
+
993
+ /* Active Url must match with the backup url as per the test */
994
+ expect(reg.getActiveMobiusUrl()).toStrictEqual(mobiusUris.backup[0]);
995
+ expect(reg.getStatus()).toEqual(_types.RegistrationStatus.ACTIVE);
996
+ case 3:
997
+ case "end":
998
+ return _context18.stop();
999
+ }
1000
+ }, _callee18);
1001
+ })));
1002
+ afterEach(function () {
1003
+ jest.clearAllTimers();
1004
+ jest.clearAllMocks();
1005
+ });
1006
+ it('verify 429 error with failback to primary after initial registration with backup: Restore failure', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee19() {
1007
+ return _regenerator.default.wrap(function (_context19) {
1008
+ while (1) switch (_context19.prev = _context19.next) {
1009
+ case 0:
1010
+ // delete should be successful
1011
+ global.fetch = jest.fn(function () {
1012
+ return _promise.default.resolve({
1013
+ json: function json() {
1014
+ return _registerFixtures.mockDeleteResponse;
1015
+ }
1016
+ });
1017
+ });
1018
+
1019
+ // Mock to fail twice with 429 (once for executeFailback, once for restorePreviousRegistration)
1020
+ postRegistrationSpy.mockRejectedValueOnce(failurePayload429Two).mockRejectedValueOnce(failurePayload429Two);
1021
+
1022
+ /* Wait for failback to be triggered. */
1023
+ jest.advanceTimersByTime(reg.rehomingIntervalMax * _constants.MINUTES_TO_SEC_MFACTOR * _constants.SEC_TO_MSEC_MFACTOR);
1024
+ _context19.next = 1;
1025
+ return flushPromises();
1026
+ case 1:
1027
+ expect(infoSpy).toBeCalledWith("Attempting failback to primary.", {
1028
+ method: 'executeFailback',
1029
+ file: _constants.REGISTRATION_FILE
1030
+ });
1031
+ jest.advanceTimersByTime(10000);
1032
+ _context19.next = 2;
1033
+ return flushPromises();
1034
+ case 2:
1035
+ expect(retry429Spy).toBeCalledWith(failurePayload429Two.headers['retry-after'], 'executeFailback');
1036
+ // After handling 429 during failback, the counter is incremented to 1
1037
+ expect(reg.failback429RetryAttempts).toBe(1);
1038
+ expect(reg.getStatus()).toBe(_types.RegistrationStatus.INACTIVE);
1039
+ expect(restoreSpy).toBeCalledOnceWith(_constants.REG_429_RETRY_UTIL);
1040
+ // restartRegistration is not called immediately because 429 with retry-after < 60
1041
+ // schedules a delayed retry instead
1042
+ expect(restartSpy).not.toBeCalled();
1043
+ expect(reg.failbackTimer).not.toBe(undefined); // Timer is set in handle429Retry
1044
+ expect(reg.rehomingIntervalMin).toBe(_constants.DEFAULT_REHOMING_INTERVAL_MIN);
1045
+ expect(reg.rehomingIntervalMax).toBe(_constants.DEFAULT_REHOMING_INTERVAL_MAX);
1046
+ case 3:
1047
+ case "end":
1048
+ return _context19.stop();
1049
+ }
1050
+ }, _callee19);
1051
+ })));
1052
+ it('verify unsuccessful failback to primary after initial registration with backup: Restore failure', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee20() {
1053
+ return _regenerator.default.wrap(function (_context20) {
1054
+ while (1) switch (_context20.prev = _context20.next) {
1055
+ case 0:
1056
+ postRegistrationSpy.mockRejectedValue(failurePayload);
1057
+
1058
+ /* Wait for failback to be triggered. */
1059
+ jest.advanceTimersByTime(reg.rehomingIntervalMax * _constants.MINUTES_TO_SEC_MFACTOR * _constants.SEC_TO_MSEC_MFACTOR);
1060
+ _context20.next = 1;
1061
+ return flushPromises();
1062
+ case 1:
1063
+ expect(infoSpy).toBeCalledWith("Attempting failback to primary.", {
1064
+ method: 'executeFailback',
1065
+ file: _constants.REGISTRATION_FILE
1066
+ });
1067
+ expect(reg.getStatus()).toBe(_types.RegistrationStatus.INACTIVE);
1068
+ expect(restoreSpy).toBeCalledOnceWith(_constants.FAILBACK_UTIL);
1069
+ expect(reg.getStatus()).toBe(_types.RegistrationStatus.INACTIVE);
1070
+ expect(restartSpy).toBeCalledOnceWith(_constants.FAILBACK_UTIL);
1071
+ expect(reg.rehomingIntervalMin).toBe(_constants.DEFAULT_REHOMING_INTERVAL_MIN);
1072
+ expect(reg.rehomingIntervalMax).toBe(_constants.DEFAULT_REHOMING_INTERVAL_MAX);
1073
+ case 2:
1074
+ case "end":
1075
+ return _context20.stop();
1076
+ }
1077
+ }, _callee20);
1078
+ })));
1079
+ it('verify unsuccessful failback to primary after initial registration with backup: Restore failure with final error', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee21() {
1080
+ var finalErrorPayload;
1081
+ return _regenerator.default.wrap(function (_context21) {
1082
+ while (1) switch (_context21.prev = _context21.next) {
1083
+ case 0:
1084
+ finalErrorPayload = {
1085
+ statusCode: 401,
1086
+ body: _registerFixtures.mockPostResponse
1087
+ };
1088
+ postRegistrationSpy.mockClear();
1089
+ postRegistrationSpy.mockRejectedValue(finalErrorPayload).mockRejectedValueOnce(failurePayload);
1090
+ /* Wait for failback to be triggered. */
1091
+ jest.advanceTimersByTime(reg.rehomingIntervalMax * _constants.MINUTES_TO_SEC_MFACTOR * _constants.SEC_TO_MSEC_MFACTOR);
1092
+ _context21.next = 1;
1093
+ return flushPromises();
1094
+ case 1:
1095
+ expect(infoSpy).toBeCalledWith("Attempting failback to primary.", {
1096
+ method: 'executeFailback',
1097
+ file: _constants.REGISTRATION_FILE
1098
+ });
1099
+ expect(reg.getStatus()).toBe(_types.RegistrationStatus.INACTIVE);
1100
+ expect(restoreSpy).toBeCalledOnceWith(_constants.FAILBACK_UTIL);
1101
+ expect(restartSpy).not.toBeCalled();
1102
+ expect(reg.failbackTimer).toBe(undefined);
1103
+ expect(reg.rehomingIntervalMin).toBe(_constants.DEFAULT_REHOMING_INTERVAL_MIN);
1104
+ expect(reg.rehomingIntervalMax).toBe(_constants.DEFAULT_REHOMING_INTERVAL_MAX);
1105
+ case 2:
1106
+ case "end":
1107
+ return _context21.stop();
1108
+ }
1109
+ }, _callee21);
1110
+ })));
1111
+ it('verify unsuccessful failback to primary after initial registration with backup: Restore success', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee22() {
1112
+ return _regenerator.default.wrap(function (_context22) {
1113
+ while (1) switch (_context22.prev = _context22.next) {
1114
+ case 0:
1115
+ postRegistrationSpy.mockRejectedValueOnce(failurePayload).mockResolvedValue(successPayload);
1116
+
1117
+ /* Wait for failback to be triggered. */
1118
+ jest.advanceTimersByTime(reg.rehomingIntervalMax * _constants.MINUTES_TO_SEC_MFACTOR * _constants.SEC_TO_MSEC_MFACTOR);
1119
+ _context22.next = 1;
1120
+ return flushPromises();
1121
+ case 1:
1122
+ expect(infoSpy).toBeCalledWith("Attempting failback to primary.", {
1123
+ method: 'executeFailback',
1124
+ file: _constants.REGISTRATION_FILE
1125
+ });
1126
+ /* Active Url should still match backup url */
1127
+ expect(reg.getActiveMobiusUrl()).toStrictEqual(mobiusUris.backup[0]);
1128
+ expect(reg.getStatus()).toBe(_types.RegistrationStatus.ACTIVE);
1129
+ expect(restoreSpy).toBeCalledOnceWith(_constants.FAILBACK_UTIL);
1130
+ expect(restartSpy).not.toBeCalled();
1131
+ expect(reg.rehomingIntervalMin).toBe(_constants.DEFAULT_REHOMING_INTERVAL_MIN);
1132
+ expect(reg.rehomingIntervalMax).toBe(_constants.DEFAULT_REHOMING_INTERVAL_MAX);
1133
+ case 2:
1134
+ case "end":
1135
+ return _context22.stop();
1136
+ }
1137
+ }, _callee22);
1138
+ })));
1139
+ it('verify successful failback to primary after initial registration with backup', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee23() {
1140
+ return _regenerator.default.wrap(function (_context23) {
1141
+ while (1) switch (_context23.prev = _context23.next) {
1142
+ case 0:
1143
+ postRegistrationSpy.mockResolvedValue(successPayload);
1144
+
1145
+ /* Wait for failback to be triggered. */
1146
+ jest.advanceTimersByTime(reg.rehomingIntervalMax * _constants.MINUTES_TO_SEC_MFACTOR * _constants.SEC_TO_MSEC_MFACTOR);
1147
+ _context23.next = 1;
1148
+ return flushPromises();
1149
+ case 1:
1150
+ expect(infoSpy).toBeCalledWith("Attempting failback to primary.", {
1151
+ method: 'executeFailback',
1152
+ file: _constants.REGISTRATION_FILE
1153
+ });
1154
+
1155
+ /* Active Url must now match with the primary url */
1156
+ expect(deregisterSpy).toBeCalledOnceWith();
1157
+ expect(reg.getActiveMobiusUrl()).toStrictEqual(mobiusUris.primary[0]);
1158
+ expect(reg.getStatus()).toBe(_types.RegistrationStatus.ACTIVE);
1159
+ expect(reg.failbackTimer).toBe(undefined);
1160
+ expect(restoreSpy).not.toBeCalled();
1161
+ expect(reg.rehomingIntervalMin).toBe(_registerFixtures.mockPostResponse.rehomingIntervalMin);
1162
+ expect(reg.rehomingIntervalMax).toBe(_registerFixtures.mockPostResponse.rehomingIntervalMax);
1163
+ case 2:
1164
+ case "end":
1165
+ return _context23.stop();
1166
+ }
1167
+ }, _callee23);
1168
+ })));
1169
+ it('verify unsuccessful failback attempt due to active call', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee24() {
1170
+ return _regenerator.default.wrap(function (_context24) {
1171
+ while (1) switch (_context24.prev = _context24.next) {
1172
+ case 0:
1173
+ /** create a new call */
1174
+ reg.callManager.createCall();
1175
+ expect((0, _keys.default)(reg.callManager.getActiveCalls()).length).toBe(1);
1176
+ postRegistrationSpy.mockRejectedValueOnce(failurePayload).mockResolvedValue(successPayload);
1177
+
1178
+ /* Wait for failback to be triggered. */
1179
+ jest.advanceTimersByTime(reg.rehomingIntervalMax * _constants.MINUTES_TO_SEC_MFACTOR * _constants.SEC_TO_MSEC_MFACTOR);
1180
+ _context24.next = 1;
1181
+ return flushPromises();
1182
+ case 1:
1183
+ expect(infoSpy).toBeCalledWith("Active calls present or primary Mobius is down, deferring failback to next cycle.", {
1184
+ method: 'executeFailback',
1185
+ file: _constants.REGISTRATION_FILE
1186
+ });
1187
+
1188
+ /* Active Url should still match backup url */
1189
+ expect(reg.getActiveMobiusUrl()).toStrictEqual(mobiusUris.backup[0]);
1190
+ expect(reg.getStatus()).toBe(_types.RegistrationStatus.ACTIVE);
1191
+ expect(deregisterSpy).not.toBeCalled();
1192
+ expect(restoreSpy).not.toBeCalled();
1193
+ expect(restartSpy).not.toBeCalled();
1194
+ expect(infoSpy).toBeCalledWith('Active calls present or primary Mobius is down, deferring failback to next cycle.', {
1195
+ file: _constants.REGISTRATION_FILE,
1196
+ method: _constants.FAILBACK_UTIL
1197
+ });
1198
+ expect(reg.rehomingIntervalMin).toBe(_constants.DEFAULT_REHOMING_INTERVAL_MIN);
1199
+ expect(reg.rehomingIntervalMax).toBe(_constants.DEFAULT_REHOMING_INTERVAL_MAX);
1200
+ case 2:
1201
+ case "end":
1202
+ return _context24.stop();
1203
+ }
1204
+ }, _callee24);
1205
+ })));
1206
+ it('verify unsuccessful failback attempt due to primary server being down', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee25() {
1207
+ return _regenerator.default.wrap(function (_context25) {
1208
+ while (1) switch (_context25.prev = _context25.next) {
1209
+ case 0:
1210
+ isPrimaryActiveSpy.mockReturnValue(false);
1211
+
1212
+ /* Wait for failback to be triggered. */
1213
+ jest.advanceTimersByTime(reg.rehomingIntervalMax * _constants.MINUTES_TO_SEC_MFACTOR * _constants.SEC_TO_MSEC_MFACTOR);
1214
+ _context25.next = 1;
1215
+ return flushPromises();
1216
+ case 1:
1217
+ expect(infoSpy).toBeCalledWith("Active calls present or primary Mobius is down, deferring failback to next cycle.", {
1218
+ method: 'executeFailback',
1219
+ file: _constants.REGISTRATION_FILE
1220
+ });
1221
+
1222
+ /* Active Url should still match backup url */
1223
+ expect(deregisterSpy).not.toBeCalled();
1224
+ expect(reg.getActiveMobiusUrl()).toStrictEqual(mobiusUris.backup[0]);
1225
+ expect(reg.getStatus()).toBe(_types.RegistrationStatus.ACTIVE);
1226
+ case 2:
1227
+ case "end":
1228
+ return _context25.stop();
1229
+ }
1230
+ }, _callee25);
1231
+ })));
1232
+ });
1233
+
1234
+ // Keep-alive related test cases
1235
+ describe('Keep-alive Tests', function () {
1236
+ var beforeEachSetupForKeepalive = /*#__PURE__*/function () {
1237
+ var _ref26 = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee26() {
1238
+ return _regenerator.default.wrap(function (_context26) {
1239
+ while (1) switch (_context26.prev = _context26.next) {
1240
+ case 0:
1241
+ postRegistrationSpy.mockResolvedValueOnce(successPayload);
1242
+ jest.useFakeTimers();
1243
+ _context26.next = 1;
1244
+ return reg.triggerRegistration();
1245
+ case 1:
1246
+ expect(reg.getStatus()).toBe(_types.RegistrationStatus.ACTIVE);
1247
+ expect(reg.webWorker).toBeDefined();
1248
+ case 2:
1249
+ case "end":
1250
+ return _context26.stop();
1251
+ }
1252
+ }, _callee26);
1253
+ }));
1254
+ return function beforeEachSetupForKeepalive() {
1255
+ return _ref26.apply(this, arguments);
1256
+ };
1257
+ }();
1258
+ afterEach(function () {
1259
+ jest.clearAllTimers();
1260
+ jest.clearAllMocks();
1261
+ reg.clearKeepaliveTimer();
1262
+ reg.reconnectPending = false;
1263
+ var calls = (0, _values.default)(reg.callManager.getActiveCalls());
1264
+ calls.forEach(function (call) {
1265
+ call.end();
1266
+ });
1267
+ });
1268
+ it('verify successful keep-alive cases', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee27() {
1269
+ var postMessageSpy;
1270
+ return _regenerator.default.wrap(function (_context27) {
1271
+ while (1) switch (_context27.prev = _context27.next) {
1272
+ case 0:
1273
+ postMessageSpy = jest.spyOn(Worker.prototype, 'postMessage');
1274
+ _context27.next = 1;
1275
+ return beforeEachSetupForKeepalive();
1276
+ case 1:
1277
+ expect(reg.webWorker).toBeDefined();
1278
+ expect(postMessageSpy).toHaveBeenCalledWith(expect.objectContaining({
1279
+ type: 'START_KEEPALIVE',
1280
+ accessToken: expect.any(String),
1281
+ deviceUrl: expect.any(String),
1282
+ interval: expect.any(Number),
1283
+ retryCountThreshold: expect.any(Number),
1284
+ url: expect.any(String)
1285
+ }));
1286
+ reg.webWorker.onmessage({
1287
+ data: {
1288
+ type: 'KEEPALIVE_SUCCESS',
1289
+ statusCode: 200
1290
+ }
1291
+ });
1292
+ expect(lineEmitter).toBeCalledWith(_types4.LINE_EVENTS.RECONNECTED);
1293
+ case 2:
1294
+ case "end":
1295
+ return _context27.stop();
1296
+ }
1297
+ }, _callee27);
1298
+ })));
1299
+ it('verify failure keep-alive cases: Retry Success', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee28() {
1300
+ var worker;
1301
+ return _regenerator.default.wrap(function (_context28) {
1302
+ while (1) switch (_context28.prev = _context28.next) {
1303
+ case 0:
1304
+ _context28.next = 1;
1305
+ return beforeEachSetupForKeepalive();
1306
+ case 1:
1307
+ worker = reg.webWorker;
1308
+ lineEmitter.mockClear();
1309
+ worker.onmessage({
1310
+ data: {
1311
+ type: 'KEEPALIVE_FAILURE',
1312
+ err: {
1313
+ statusCode: 503
1314
+ },
1315
+ keepAliveRetryCount: 1
1316
+ }
1317
+ });
1318
+ worker.onmessage({
1319
+ data: {
1320
+ type: 'KEEPALIVE_SUCCESS',
1321
+ statusCode: 200
1322
+ }
1323
+ });
1324
+ expect(lineEmitter).toHaveBeenCalledWith(_types4.LINE_EVENTS.RECONNECTED);
1325
+ case 2:
1326
+ case "end":
1327
+ return _context28.stop();
1328
+ }
1329
+ }, _callee28);
1330
+ })));
1331
+ it('verify failure keep-alive cases: Restore failure', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee29() {
1332
+ var reconnectSpy, restoreSpy, restartRegSpy, RETRY_COUNT_THRESHOLD, failureEvent;
1333
+ return _regenerator.default.wrap(function (_context29) {
1334
+ while (1) switch (_context29.prev = _context29.next) {
1335
+ case 0:
1336
+ _context29.next = 1;
1337
+ return beforeEachSetupForKeepalive();
1338
+ case 1:
1339
+ reconnectSpy = jest.spyOn(reg, 'reconnectOnFailure');
1340
+ restoreSpy = jest.spyOn(reg, 'restorePreviousRegistration');
1341
+ restartRegSpy = jest.spyOn(reg, 'restartRegistration'); // Clear previous event emissions
1342
+ lineEmitter.mockClear();
1343
+
1344
+ // Assume registration is active
1345
+ expect(reg.getStatus()).toEqual(_types.RegistrationStatus.ACTIVE);
1346
+
1347
+ // Use fake timers to trigger keepalive initialization
1348
+ jest.useFakeTimers();
1349
+ jest.advanceTimersByTime(_registerFixtures.mockPostResponse.keepaliveInterval * _constants.SEC_TO_MSEC_MFACTOR);
1350
+
1351
+ // Simulate the worker sending a KEEPALIVE_FAILURE message with retry count at threshold.
1352
+ RETRY_COUNT_THRESHOLD = reg.isCCFlow ? 4 : 5;
1353
+ failureEvent = {
1354
+ data: {
1355
+ type: _types.WorkerMessageType.KEEPALIVE_FAILURE,
1356
+ err: {
1357
+ statusCode: 503
1358
+ },
1359
+ keepAliveRetryCount: RETRY_COUNT_THRESHOLD
1360
+ }
1361
+ };
1362
+ reg.webWorker.onmessage(failureEvent);
1363
+ _context29.next = 2;
1364
+ return flushPromises();
1365
+ case 2:
1366
+ expect(reg.getStatus()).toEqual(_types.RegistrationStatus.INACTIVE);
1367
+ expect(lineEmitter).toHaveBeenCalledWith(_types4.LINE_EVENTS.UNREGISTERED);
1368
+ expect(reconnectSpy).toBeCalledOnceWith(_constants.RECONNECT_ON_FAILURE_UTIL);
1369
+ expect(restoreSpy).toBeCalledOnceWith(_constants.RECONNECT_ON_FAILURE_UTIL);
1370
+ expect(restartRegSpy).toBeCalledOnceWith(_constants.RECONNECT_ON_FAILURE_UTIL);
1371
+ jest.useRealTimers();
1372
+ expect(warnSpy).toHaveBeenCalledWith('Keep-alive missed 5 times. Status -> 503 ', expect.objectContaining({
1373
+ file: _constants.REGISTRATION_FILE,
1374
+ method: 'startKeepaliveTimer'
1375
+ }));
1376
+ case 3:
1377
+ case "end":
1378
+ return _context29.stop();
1379
+ }
1380
+ }, _callee29);
1381
+ })));
1382
+ it('verify failure keep-alive cases: Restore Success', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee30() {
1383
+ var reconnectSpy, url;
1384
+ return _regenerator.default.wrap(function (_context30) {
1385
+ while (1) switch (_context30.prev = _context30.next) {
1386
+ case 0:
1387
+ _context30.next = 1;
1388
+ return beforeEachSetupForKeepalive();
1389
+ case 1:
1390
+ expect(reg.webWorker).toBeDefined();
1391
+ reconnectSpy = jest.spyOn(reg, 'reconnectOnFailure');
1392
+ url = 'https://mobius-dfw.webex.com/api/v1/calling/web/';
1393
+ reg.webWorker.onmessage({
1394
+ data: {
1395
+ type: _types.WorkerMessageType.KEEPALIVE_FAILURE,
1396
+ err: {
1397
+ statusCode: 503
1398
+ },
1399
+ keepAliveRetryCount: 5
1400
+ }
1401
+ });
1402
+ jest.advanceTimersByTime(1000);
1403
+ _context30.next = 2;
1404
+ return flushPromises();
1405
+ case 2:
1406
+ expect(reg.webWorker).toBeUndefined();
1407
+ expect(reconnectSpy).toBeCalledOnceWith(_constants.RECONNECT_ON_FAILURE_UTIL);
1408
+ localStorage.clear();
1409
+ webex.request.mockResolvedValueOnce(successPayload);
1410
+ _context30.next = 3;
1411
+ return reg.triggerRegistration();
1412
+ case 3:
1413
+ _context30.next = 4;
1414
+ return flushPromises();
1415
+ case 4:
1416
+ expect(reg.webWorker).toBeDefined();
1417
+ reg.webWorker.onmessage({
1418
+ data: {
1419
+ type: _types.WorkerMessageType.KEEPALIVE_SUCCESS,
1420
+ statusCode: 200
1421
+ }
1422
+ });
1423
+
1424
+ // Advance timers and flush any remaining promises.
1425
+ jest.advanceTimersByTime(1000);
1426
+ _context30.next = 5;
1427
+ return flushPromises();
1428
+ case 5:
1429
+ expect(reg.getStatus()).toEqual(_types.RegistrationStatus.ACTIVE);
1430
+ // reconnectSpy should have been called only once.
1431
+ expect(reconnectSpy).toBeCalledTimes(1);
1432
+ expect(restoreSpy).toBeCalledOnceWith(_constants.RECONNECT_ON_FAILURE_UTIL);
1433
+ expect(restartSpy).toBeCalledOnceWith(_constants.RECONNECT_ON_FAILURE_UTIL);
1434
+ // Active Mobius URL should remain unchanged.
1435
+ expect(reg.getActiveMobiusUrl()).toStrictEqual(url);
1436
+ case 6:
1437
+ case "end":
1438
+ return _context30.stop();
1439
+ }
1440
+ }, _callee30);
1441
+ })));
1442
+ it('verify failure followed by recovery of keepalive', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee31() {
1443
+ return _regenerator.default.wrap(function (_context31) {
1444
+ while (1) switch (_context31.prev = _context31.next) {
1445
+ case 0:
1446
+ _context31.next = 1;
1447
+ return beforeEachSetupForKeepalive();
1448
+ case 1:
1449
+ expect(reg.getStatus()).toBe(_types.RegistrationStatus.ACTIVE);
1450
+ expect(reg.webWorker).toBeDefined();
1451
+ webex.request.mockRejectedValueOnce(failurePayload).mockRejectedValueOnce(failurePayload).mockResolvedValue(successPayload);
1452
+ reg.webWorker.onmessage({
1453
+ data: {
1454
+ type: _types.WorkerMessageType.KEEPALIVE_FAILURE,
1455
+ err: failurePayload,
1456
+ keepAliveRetryCount: reg.isCCFlow ? 4 : 5
1457
+ }
1458
+ });
1459
+ _context31.next = 2;
1460
+ return flushPromises();
1461
+ case 2:
1462
+ expect(reg.webWorker).toBeUndefined();
1463
+ expect(handleErrorSpy).toBeCalledTimes(3);
1464
+ localStorage.clear();
1465
+ _context31.next = 3;
1466
+ return reg.triggerRegistration();
1467
+ case 3:
1468
+ _context31.next = 4;
1469
+ return flushPromises();
1470
+ case 4:
1471
+ expect(reg.webWorker).toBeDefined();
1472
+ reg.webWorker.onmessage({
1473
+ data: {
1474
+ type: _types.WorkerMessageType.KEEPALIVE_SUCCESS,
1475
+ statusCode: 200
1476
+ }
1477
+ });
1478
+ _context31.next = 5;
1479
+ return flushPromises();
1480
+ case 5:
1481
+ // In a complete failure‐then-recovery scenario, we expect another failure event to have been handled.
1482
+ // For that, simulate a second failure event on the new worker.
1483
+ reg.webWorker.onmessage({
1484
+ data: {
1485
+ type: _types.WorkerMessageType.KEEPALIVE_FAILURE,
1486
+ err: failurePayload,
1487
+ keepAliveRetryCount: reg.isCCFlow ? 4 : 5
1488
+ }
1489
+ });
1490
+ _context31.next = 6;
1491
+ return flushPromises();
1492
+ case 6:
1493
+ expect(handleErrorSpy).toBeCalledTimes(4);
1494
+
1495
+ // And then re-register successfully:
1496
+ _context31.next = 7;
1497
+ return reg.triggerRegistration();
1498
+ case 7:
1499
+ _context31.next = 8;
1500
+ return flushPromises();
1501
+ case 8:
1502
+ expect(reg.webWorker).toBeDefined();
1503
+ reg.webWorker.onmessage({
1504
+ data: {
1505
+ type: _types.WorkerMessageType.KEEPALIVE_SUCCESS,
1506
+ statusCode: 200
1507
+ }
1508
+ });
1509
+ _context31.next = 9;
1510
+ return flushPromises();
1511
+ case 9:
1512
+ expect(reg.getStatus()).toEqual(_types.RegistrationStatus.ACTIVE);
1513
+ expect(reg.webWorker).toBeDefined();
1514
+ case 10:
1515
+ case "end":
1516
+ return _context31.stop();
1517
+ }
1518
+ }, _callee31);
1519
+ })));
1520
+ it('cc: verify failover to backup server after 4 keep alive failure with primary server', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee32() {
1521
+ var clearKeepaliveSpy, reconnectSpy;
1522
+ return _regenerator.default.wrap(function (_context32) {
1523
+ while (1) switch (_context32.prev = _context32.next) {
1524
+ case 0:
1525
+ // Register with contact center service
1526
+ setupRegistration(_objectSpread(_objectSpread({}, MockServiceData), {}, {
1527
+ indicator: _types.ServiceIndicator.CONTACT_CENTER
1528
+ }));
1529
+ _context32.next = 1;
1530
+ return beforeEachSetupForKeepalive();
1531
+ case 1:
1532
+ webex.request.mockResolvedValueOnce(successPayload);
1533
+ _context32.next = 2;
1534
+ return reg.triggerRegistration();
1535
+ case 2:
1536
+ expect(reg.getStatus()).toEqual(_types.RegistrationStatus.ACTIVE);
1537
+ expect(reg.webWorker).toBeDefined();
1538
+
1539
+ // Spy on clearKeepaliveTimer and simulate reconnectOnFailure behavior
1540
+ clearKeepaliveSpy = jest.spyOn(reg, 'clearKeepaliveTimer');
1541
+ reconnectSpy = jest.spyOn(reg, 'reconnectOnFailure'); // Simulate a KEEPALIVE_FAILURE message from the worker with a retry count equal to threshold (4 for CC)
1542
+ reg.webWorker.onmessage({
1543
+ data: {
1544
+ type: _types.WorkerMessageType.KEEPALIVE_FAILURE,
1545
+ err: {
1546
+ statusCode: 503
1547
+ },
1548
+ keepAliveRetryCount: 4
1549
+ }
1550
+ });
1551
+
1552
+ // Wait for any asynchronous actions to complete
1553
+ _context32.next = 3;
1554
+ return flushPromises();
1555
+ case 3:
1556
+ // Verify that the keepalive timer was cleared and reconnectOnFailure was triggered
1557
+ expect(clearKeepaliveSpy).toHaveBeenCalled();
1558
+ expect(reconnectSpy).toHaveBeenCalledWith(_constants.RECONNECT_ON_FAILURE_UTIL);
1559
+
1560
+ // Verify that the active Mobius URL has been updated to the backup server and registration is active
1561
+ expect(reg.getActiveMobiusUrl()).toEqual(mobiusUris.backup[0]);
1562
+ expect(reg.getStatus()).toEqual(_types.RegistrationStatus.ACTIVE);
1563
+ case 4:
1564
+ case "end":
1565
+ return _context32.stop();
1566
+ }
1567
+ }, _callee32);
1568
+ })));
1569
+ it('verify failure keep-alive case with active call present: Restore Success after call ends', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee33() {
1570
+ var reconnectSpy, restoreSpy, restartRegSpy, clearTimerSpy, threshold, failureEvent;
1571
+ return _regenerator.default.wrap(function (_context33) {
1572
+ while (1) switch (_context33.prev = _context33.next) {
1573
+ case 0:
1574
+ _context33.next = 1;
1575
+ return beforeEachSetupForKeepalive();
1576
+ case 1:
1577
+ reconnectSpy = jest.spyOn(reg, 'reconnectOnFailure');
1578
+ restoreSpy = jest.spyOn(reg, 'restorePreviousRegistration');
1579
+ restartRegSpy = jest.spyOn(reg, 'restartRegistration'); // Simulate an active call.
1580
+ reg.callManager.createCall();
1581
+ expect((0, _keys.default)(reg.callManager.getActiveCalls()).length).toBe(1);
1582
+ clearTimerSpy = jest.spyOn(reg, 'clearKeepaliveTimer');
1583
+ threshold = reg.isCCFlow ? 4 : 5; // Simulate a KEEPALIVE_FAILURE event with a 503 error at threshold.
1584
+ failureEvent = {
1585
+ data: {
1586
+ type: _types.WorkerMessageType.KEEPALIVE_FAILURE,
1587
+ err: {
1588
+ statusCode: 503
1589
+ },
1590
+ keepAliveRetryCount: threshold
1591
+ }
1592
+ };
1593
+ reg.webWorker.onmessage(failureEvent);
1594
+ _context33.next = 2;
1595
+ return flushPromises();
1596
+ case 2:
1597
+ // At this point, clearKeepaliveTimer was called so the worker is terminated.
1598
+ expect(clearTimerSpy).toHaveBeenCalled();
1599
+ expect(reg.webWorker).toBeUndefined();
1600
+ expect(reg.getStatus()).toEqual(_types.RegistrationStatus.INACTIVE);
1601
+ expect(lineEmitter).lastCalledWith(_types4.LINE_EVENTS.UNREGISTERED);
1602
+ expect(reg.keepaliveTimer).toStrictEqual(undefined);
1603
+ expect(reg.failbackTimer).toStrictEqual(undefined);
1604
+ expect(reconnectSpy).toBeCalledOnceWith(_constants.RECONNECT_ON_FAILURE_UTIL);
1605
+ expect(restoreSpy).not.toBeCalled();
1606
+ expect(restartRegSpy).not.toBeCalled();
1607
+ expect(reg.reconnectPending).toStrictEqual(true);
1608
+ expect(infoSpy).toBeCalledWith('Active call(s) present, deferred reconnect till call cleanup.', {
1609
+ file: _constants.REGISTRATION_FILE,
1610
+ method: expect.any(String)
1611
+ });
1612
+ reconnectSpy.mockClear();
1613
+
1614
+ // Now simulate call cleanup.
1615
+ reg.callManager.callCollection = {};
1616
+ webex.request.mockResolvedValueOnce(successPayload);
1617
+
1618
+ // Call reconnectOnFailure manually. With no active calls, this should trigger re-registration.
1619
+ _context33.next = 3;
1620
+ return reg.reconnectOnFailure(_constants.CALLS_CLEARED_HANDLER_UTIL);
1621
+ case 3:
1622
+ _context33.next = 4;
1623
+ return flushPromises();
1624
+ case 4:
1625
+ expect((0, _keys.default)(reg.callManager.getActiveCalls()).length).toBe(0);
1626
+ // After re-registration, registration status becomes ACTIVE and a new worker is created.
1627
+ expect(reg.getStatus()).toEqual(_types.RegistrationStatus.ACTIVE);
1628
+ expect(reg.webWorker).toBeDefined();
1629
+ expect(reconnectSpy).toBeCalledOnceWith(_constants.CALLS_CLEARED_HANDLER_UTIL);
1630
+ expect(restoreSpy).toBeCalledOnceWith(_constants.CALLS_CLEARED_HANDLER_UTIL);
1631
+ expect(restartRegSpy).not.toBeCalled();
1632
+ expect(reg.reconnectPending).toStrictEqual(false);
1633
+ case 5:
1634
+ case "end":
1635
+ return _context33.stop();
1636
+ }
1637
+ }, _callee33);
1638
+ })));
1639
+ it('checks for keep-alive failure with final error: 404', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee34() {
1640
+ var reconnectSpy, restoreSpy, restartRegSpy, clearTimerSpy, handle404CbSpy, registerSpy;
1641
+ return _regenerator.default.wrap(function (_context34) {
1642
+ while (1) switch (_context34.prev = _context34.next) {
1643
+ case 0:
1644
+ _context34.next = 1;
1645
+ return beforeEachSetupForKeepalive();
1646
+ case 1:
1647
+ webex.request.mockResolvedValueOnce(successPayload);
1648
+ reconnectSpy = jest.spyOn(reg, 'reconnectOnFailure');
1649
+ restoreSpy = jest.spyOn(reg, 'restorePreviousRegistration');
1650
+ restartRegSpy = jest.spyOn(reg, 'restartRegistration');
1651
+ clearTimerSpy = jest.spyOn(reg, 'clearKeepaliveTimer');
1652
+ handle404CbSpy = jest.spyOn(reg, 'handle404KeepaliveFailure');
1653
+ registerSpy = jest.spyOn(reg, 'attemptRegistrationWithServers');
1654
+ reg.webWorker.onmessage({
1655
+ data: {
1656
+ type: _types.WorkerMessageType.KEEPALIVE_FAILURE,
1657
+ err: {
1658
+ statusCode: 404
1659
+ },
1660
+ keepAliveRetryCount: 1
1661
+ }
1662
+ });
1663
+ _context34.next = 2;
1664
+ return flushPromises();
1665
+ case 2:
1666
+ expect(warnSpy).toBeCalledWith('Keep-alive missed 1 times. Status -> 404 ', expect.objectContaining({
1667
+ file: _constants.REGISTRATION_FILE,
1668
+ method: 'startKeepaliveTimer'
1669
+ }));
1670
+ expect(handleErrorSpy).toBeCalledOnceWith({
1671
+ statusCode: 404
1672
+ }, expect.anything(), {
1673
+ file: _constants.REGISTRATION_FILE,
1674
+ method: _constants.KEEPALIVE_UTIL
1675
+ }, expect.anything());
1676
+ expect(lineEmitter).toHaveBeenCalledWith(_types4.LINE_EVENTS.ERROR, undefined, expect.anything());
1677
+ expect(lineEmitter).toHaveBeenCalledWith(_types4.LINE_EVENTS.UNREGISTERED);
1678
+ expect(clearTimerSpy).toBeCalledTimes(2);
1679
+ expect(reconnectSpy).not.toHaveBeenCalled();
1680
+ expect(restoreSpy).not.toHaveBeenCalled();
1681
+ expect(restartRegSpy).not.toHaveBeenCalled();
1682
+ expect(reg.reconnectPending).toStrictEqual(false);
1683
+ expect(handle404CbSpy).toBeCalledOnceWith(_constants.KEEPALIVE_UTIL);
1684
+ expect(registerSpy).toBeCalledOnceWith(_constants.KEEPALIVE_UTIL);
1685
+ case 3:
1686
+ case "end":
1687
+ return _context34.stop();
1688
+ }
1689
+ }, _callee34);
1690
+ })));
1691
+ it('checks for keep-alive failure with 429', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee35() {
1692
+ var _reg$deviceInfo$devic;
1693
+ var keepaliveSpy, postMessageSpy, clearTimerSpy, retry429Spy;
1694
+ return _regenerator.default.wrap(function (_context35) {
1695
+ while (1) switch (_context35.prev = _context35.next) {
1696
+ case 0:
1697
+ _context35.next = 1;
1698
+ return beforeEachSetupForKeepalive();
1699
+ case 1:
1700
+ keepaliveSpy = jest.spyOn(reg, 'startKeepaliveTimer');
1701
+ postMessageSpy = jest.spyOn(Worker.prototype, 'postMessage');
1702
+ clearTimerSpy = jest.spyOn(reg, 'clearKeepaliveTimer');
1703
+ retry429Spy = jest.spyOn(reg, 'handle429Retry');
1704
+ reg.webWorker.onmessage({
1705
+ data: {
1706
+ type: _types.WorkerMessageType.KEEPALIVE_FAILURE,
1707
+ err: {
1708
+ statusCode: 429,
1709
+ headers: {
1710
+ 'retry-after': 20
1711
+ }
1712
+ },
1713
+ keepAliveRetryCount: 1
1714
+ }
1715
+ });
1716
+ _context35.next = 2;
1717
+ return flushPromises();
1718
+ case 2:
1719
+ expect(warnSpy).toBeCalledWith('Keep-alive missed 1 times. Status -> 429 ', expect.objectContaining({
1720
+ file: _constants.REGISTRATION_FILE,
1721
+ method: 'startKeepaliveTimer'
1722
+ }));
1723
+ expect(handleErrorSpy).toBeCalledOnceWith({
1724
+ statusCode: 429,
1725
+ headers: {
1726
+ 'retry-after': 20
1727
+ }
1728
+ }, expect.anything(), {
1729
+ file: _constants.REGISTRATION_FILE,
1730
+ method: _constants.KEEPALIVE_UTIL
1731
+ }, expect.anything());
1732
+ expect(retry429Spy).toBeCalledOnceWith(20, 'startKeepaliveTimer');
1733
+ expect(clearTimerSpy).toBeCalledTimes(1);
1734
+ expect(reg.reconnectPending).toStrictEqual(false);
1735
+ expect(reg.keepaliveTimer).toBe(undefined);
1736
+ expect(reg.webWorker).toBeUndefined();
1737
+ jest.advanceTimersByTime(20 * _constants.SEC_TO_MSEC_MFACTOR);
1738
+ _context35.next = 3;
1739
+ return flushPromises();
1740
+ case 3:
1741
+ expect(keepaliveSpy).toBeCalledOnceWith((_reg$deviceInfo$devic = reg.deviceInfo.device) === null || _reg$deviceInfo$devic === void 0 ? void 0 : _reg$deviceInfo$devic.uri, reg.deviceInfo.keepaliveInterval, 'UNKNOWN');
1742
+ expect(logSpy).toBeCalledWith('Resuming keepalive after 20 seconds', {
1743
+ file: _constants.REGISTRATION_FILE,
1744
+ method: 'handle429Retry'
1745
+ });
1746
+ expect(reg.webWorker).toBeDefined();
1747
+ expect(postMessageSpy).toHaveBeenCalledWith(expect.objectContaining({
1748
+ type: 'START_KEEPALIVE',
1749
+ accessToken: expect.any(String),
1750
+ deviceUrl: expect.any(String),
1751
+ interval: expect.any(Number),
1752
+ retryCountThreshold: expect.any(Number),
1753
+ url: expect.any(String)
1754
+ }));
1755
+ case 4:
1756
+ case "end":
1757
+ return _context35.stop();
1758
+ }
1759
+ }, _callee35);
1760
+ })));
1761
+ it('ensure retryAfter is set when 429 occurs during failover retry', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee36() {
1762
+ var retry429Spy;
1763
+ return _regenerator.default.wrap(function (_context36) {
1764
+ while (1) switch (_context36.prev = _context36.next) {
1765
+ case 0:
1766
+ _context36.next = 1;
1767
+ return beforeEachSetupForKeepalive();
1768
+ case 1:
1769
+ // Simulate loss of registration so failover path attempts a new registration
1770
+ reg.clearKeepaliveTimer();
1771
+ reg.setStatus(_types.RegistrationStatus.INACTIVE);
1772
+ retry429Spy = jest.spyOn(reg, 'handle429Retry'); // Make the failover interval deterministic and simulate 429 on the failover attempt
1773
+ jest.spyOn(reg, 'getRegRetryInterval').mockReturnValueOnce(33);
1774
+ webex.request.mockRejectedValueOnce(failurePayload429One);
1775
+
1776
+ // Directly schedule the failover attempt
1777
+ _context36.next = 2;
1778
+ return reg.startFailoverTimer();
1779
+ case 2:
1780
+ jest.advanceTimersByTime(33 * _constants.SEC_TO_MSEC_MFACTOR);
1781
+ _context36.next = 3;
1782
+ return flushPromises();
1783
+ case 3:
1784
+ expect(retry429Spy).toBeCalledWith(failurePayload429One.headers['retry-after'], 'startFailoverTimer');
1785
+ expect(reg.retryAfter).toEqual(failurePayload429One.headers['retry-after']);
1786
+ jest.useRealTimers();
1787
+ case 4:
1788
+ case "end":
1789
+ return _context36.stop();
1790
+ }
1791
+ }, _callee36);
1792
+ })));
1793
+ it('sets retryAfter when reconnectOnFailure caller receives 429 after keepalive threshold miss', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee37() {
1794
+ var retry429Spy, threshold;
1795
+ return _regenerator.default.wrap(function (_context37) {
1796
+ while (1) switch (_context37.prev = _context37.next) {
1797
+ case 0:
1798
+ _context37.next = 1;
1799
+ return beforeEachSetupForKeepalive();
1800
+ case 1:
1801
+ retry429Spy = jest.spyOn(reg, 'handle429Retry'); // On reconnectOnFailure(RECONNECT_ON_FAILURE_UTIL) first restore attempt, make registration respond with 429 (retry-after: 20)
1802
+ webex.request.mockRejectedValueOnce({
1803
+ statusCode: 429,
1804
+ body: _registerFixtures.mockPostResponse,
1805
+ headers: {
1806
+ 'retry-after': 20
1807
+ }
1808
+ });
1809
+
1810
+ // Trigger the keepalive failure at threshold to route to reconnectOnFailure(RECONNECT_ON_FAILURE_UTIL)
1811
+ threshold = reg.isCCFlow ? 4 : 5;
1812
+ reg.webWorker.onmessage({
1813
+ data: {
1814
+ type: _types.WorkerMessageType.KEEPALIVE_FAILURE,
1815
+ err: {
1816
+ statusCode: 503
1817
+ },
1818
+ keepAliveRetryCount: threshold
1819
+ }
1820
+ });
1821
+ _context37.next = 2;
1822
+ return flushPromises();
1823
+ case 2:
1824
+ // handle429Retry is called with caller 'reconnectOnFailure' → else branch executes and sets retryAfter
1825
+ expect(retry429Spy).toBeCalledOnceWith(20, _constants.RECONNECT_ON_FAILURE_UTIL);
1826
+ expect(reg.retryAfter).toEqual(undefined); // Clear retryAfter after 429 retry
1827
+ case 3:
1828
+ case "end":
1829
+ return _context37.stop();
1830
+ }
1831
+ }, _callee37);
1832
+ })));
1833
+ });
1834
+ describe('Primary server status checks', function () {
1835
+ it('success: primary server status to be up', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee38() {
1836
+ var pingSuccessPayload, status;
1837
+ return _regenerator.default.wrap(function (_context38) {
1838
+ while (1) switch (_context38.prev = _context38.next) {
1839
+ case 0:
1840
+ pingSuccessPayload = {
1841
+ statusCode: 200
1842
+ };
1843
+ webex.request.mockResolvedValue(pingSuccessPayload);
1844
+ _context38.next = 1;
1845
+ return reg.isPrimaryActive();
1846
+ case 1:
1847
+ status = _context38.sent;
1848
+ expect(webex.request).toBeCalledWith(_objectSpread({
1849
+ method: 'GET',
1850
+ uri: "https://mobius-dfw.webex.com/api/v1/ping"
1851
+ }, (0, _testUtil.getMockRequestTemplate)()));
1852
+ expect(status).toEqual(true);
1853
+ case 2:
1854
+ case "end":
1855
+ return _context38.stop();
1856
+ }
1857
+ }, _callee38);
1858
+ })));
1859
+ it('failed: primary server status to be down', /*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee39() {
1860
+ var pingFailurePayload, status;
1861
+ return _regenerator.default.wrap(function (_context39) {
1862
+ while (1) switch (_context39.prev = _context39.next) {
1863
+ case 0:
1864
+ pingFailurePayload = {
1865
+ statusCode: 500
1866
+ };
1867
+ webex.request.mockResolvedValue(pingFailurePayload);
1868
+ _context39.next = 1;
1869
+ return reg.isPrimaryActive();
1870
+ case 1:
1871
+ status = _context39.sent;
1872
+ expect(webex.request).toBeCalledWith(_objectSpread({
1873
+ method: 'GET',
1874
+ uri: "https://mobius-dfw.webex.com/api/v1/ping"
1875
+ }, (0, _testUtil.getMockRequestTemplate)()));
1876
+ expect(status).toEqual(false);
1877
+ case 2:
1878
+ case "end":
1879
+ return _context39.stop();
1880
+ }
1881
+ }, _callee39);
1882
+ })));
1883
+ });
1884
+ });
1885
+ //# sourceMappingURL=register.test.js.map