@webex/webex-core 3.12.0-next.9 → 3.12.0-webex-services-ready.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -34,6 +34,53 @@ describe('webex-core', () => {
34
34
  });
35
35
 
36
36
  describe('#initialize', () => {
37
+ it('listens for "loaded" event instead of "ready" to avoid deadlock', () => {
38
+ services.listenToOnce = sinon.stub();
39
+ services.initialize();
40
+
41
+ // Second listenToOnce call should be for 'loaded' event
42
+ assert.equal(services.listenToOnce.getCall(1).args[1], 'loaded');
43
+ });
44
+
45
+ it('services.ready starts as false', () => {
46
+ assert.isFalse(services.ready);
47
+ });
48
+
49
+ it('sets ready to true and triggers services:initialized when initialization succeeds with credentials', async () => {
50
+ services.listenToOnce = sinon.stub();
51
+ services.initServiceCatalogs = sinon.stub().returns(Promise.resolve());
52
+ services.trigger = sinon.stub();
53
+ services.webex.credentials = {
54
+ supertoken: {
55
+ access_token: 'token',
56
+ },
57
+ };
58
+
59
+ services.initialize();
60
+
61
+ // call the onLoaded callback
62
+ services.listenToOnce.getCall(1).args[2]();
63
+ await waitForAsync();
64
+
65
+ assert.isTrue(services.ready);
66
+ sinon.assert.calledWith(services.trigger, 'services:initialized');
67
+ });
68
+
69
+ it('sets ready to true and triggers services:initialized when initialization succeeds without credentials', async () => {
70
+ services.listenToOnce = sinon.stub();
71
+ services.collectPreauthCatalog = sinon.stub().returns(Promise.resolve());
72
+ services.trigger = sinon.stub();
73
+
74
+ services.initialize();
75
+
76
+ // call the onLoaded callback
77
+ services.listenToOnce.getCall(1).args[2]();
78
+ await waitForAsync();
79
+
80
+ assert.isTrue(services.ready);
81
+ sinon.assert.calledWith(services.trigger, 'services:initialized');
82
+ });
83
+
37
84
  it('initFailed is false when initialization succeeds and credentials are available', async () => {
38
85
  services.listenToOnce = sinon.stub();
39
86
  services.initServiceCatalogs = sinon.stub().returns(Promise.resolve());
@@ -45,7 +92,7 @@ describe('webex-core', () => {
45
92
 
46
93
  services.initialize();
47
94
 
48
- // call the onReady callback
95
+ // call the onLoaded callback
49
96
  services.listenToOnce.getCall(1).args[2]();
50
97
  await waitForAsync();
51
98
 
@@ -58,7 +105,7 @@ describe('webex-core', () => {
58
105
 
59
106
  services.initialize();
60
107
 
61
- // call the onReady callback
108
+ // call the onLoaded callback
62
109
  services.listenToOnce.getCall(1).args[2]();
63
110
  await waitForAsync();
64
111
 
@@ -69,7 +116,7 @@ describe('webex-core', () => {
69
116
  {error: new Error('failed'), expectedMessage: 'failed'},
70
117
  {error: undefined, expectedMessage: undefined},
71
118
  ])(
72
- 'sets initFailed to true when collectPreauthCatalog errors',
119
+ 'sets initFailed to true when collectPreauthCatalog errors but still sets ready to true',
73
120
  async ({error, expectedMessage}) => {
74
121
  services.collectPreauthCatalog = sinon.stub().callsFake(() => {
75
122
  return Promise.reject(error);
@@ -77,15 +124,18 @@ describe('webex-core', () => {
77
124
 
78
125
  services.listenToOnce = sinon.stub();
79
126
  services.logger.error = sinon.stub();
127
+ services.trigger = sinon.stub();
80
128
 
81
129
  services.initialize();
82
130
 
83
- // call the onReady callback
131
+ // call the onLoaded callback
84
132
  services.listenToOnce.getCall(1).args[2]();
85
133
 
86
134
  await waitForAsync();
87
135
 
88
136
  assert.isTrue(services.initFailed);
137
+ assert.isTrue(services.ready);
138
+ sinon.assert.calledWith(services.trigger, 'services:initialized');
89
139
  sinon.assert.calledWith(
90
140
  services.logger.error,
91
141
  `services: failed to init initial services when no credentials available, ${expectedMessage}`
@@ -97,7 +147,7 @@ describe('webex-core', () => {
97
147
  {error: new Error('failed'), expectedMessage: 'failed'},
98
148
  {error: undefined, expectedMessage: undefined},
99
149
  ])(
100
- 'sets initFailed to true when initServiceCatalogs errors',
150
+ 'sets initFailed to true when initServiceCatalogs errors but still sets ready to true',
101
151
  async ({error, expectedMessage}) => {
102
152
  services.initServiceCatalogs = sinon.stub().callsFake(() => {
103
153
  return Promise.reject(error);
@@ -110,21 +160,118 @@ describe('webex-core', () => {
110
160
 
111
161
  services.listenToOnce = sinon.stub();
112
162
  services.logger.error = sinon.stub();
163
+ services.trigger = sinon.stub();
113
164
 
114
165
  services.initialize();
115
166
 
116
- // call the onReady callback
167
+ // call the onLoaded callback
117
168
  services.listenToOnce.getCall(1).args[2]();
118
169
 
119
170
  await waitForAsync();
120
171
 
121
172
  assert.isTrue(services.initFailed);
173
+ assert.isTrue(services.ready);
174
+ sinon.assert.calledWith(services.trigger, 'services:initialized');
122
175
  sinon.assert.calledWith(
123
176
  services.logger.error,
124
177
  `services: failed to init initial services when credentials available, ${expectedMessage}`
125
178
  );
126
179
  }
127
180
  );
181
+
182
+ describe('when change:canAuthorize fires', () => {
183
+ it('calls initServiceCatalogs when canAuthorize becomes true and postauth catalog is not ready', async () => {
184
+ services.listenToOnce = sinon.stub();
185
+ services._loadCatalogFromCache = sinon.stub().returns(Promise.resolve(false));
186
+ services.collectPreauthCatalog = sinon.stub().returns(Promise.resolve());
187
+ services.initServiceCatalogs = sinon.stub().returns(Promise.resolve());
188
+ services.trigger = sinon.stub();
189
+ // Ensure no credentials so we go to the preauth path
190
+ services.webex.credentials = {};
191
+
192
+ services.initialize();
193
+
194
+ // Get the catalog after initialize creates it
195
+ const testCatalog = services._getCatalog();
196
+ testCatalog.status.postauth.ready = false;
197
+
198
+ // call the onLoaded callback (preauth path)
199
+ services.listenToOnce.getCall(1).args[2]();
200
+ await waitForAsync();
201
+
202
+ // Now set canAuthorize to true to simulate auth completing
203
+ services.webex.canAuthorize = true;
204
+
205
+ // call the change:canAuthorize callback (should be call 2)
206
+ services.listenToOnce.getCall(2).args[2]();
207
+ await waitForAsync();
208
+
209
+ sinon.assert.calledOnce(services.initServiceCatalogs);
210
+ assert.isTrue(testCatalog.isReady);
211
+ });
212
+
213
+ it('does not call initServiceCatalogs when postauth catalog is already ready', async () => {
214
+ services.listenToOnce = sinon.stub();
215
+ services._loadCatalogFromCache = sinon.stub().returns(Promise.resolve(false));
216
+ services.collectPreauthCatalog = sinon.stub().returns(Promise.resolve());
217
+ services.initServiceCatalogs = sinon.stub().returns(Promise.resolve());
218
+ services.trigger = sinon.stub();
219
+ // Ensure no credentials so we go to the preauth path
220
+ services.webex.credentials = {};
221
+
222
+ services.initialize();
223
+
224
+ // Get the catalog after initialize creates it
225
+ const testCatalog = services._getCatalog();
226
+
227
+ // call the onLoaded callback (preauth path)
228
+ services.listenToOnce.getCall(1).args[2]();
229
+ await waitForAsync();
230
+
231
+ // Set canAuthorize to true and postauth as ready BEFORE calling the callback
232
+ services.webex.canAuthorize = true;
233
+ testCatalog.status.postauth.ready = true;
234
+
235
+ // call the change:canAuthorize callback (should be call 2)
236
+ services.listenToOnce.getCall(2).args[2]();
237
+ await waitForAsync();
238
+
239
+ sinon.assert.notCalled(services.initServiceCatalogs);
240
+ });
241
+
242
+ it('logs an error when initServiceCatalogs fails after auth', async () => {
243
+ services.listenToOnce = sinon.stub();
244
+ services._loadCatalogFromCache = sinon.stub().returns(Promise.resolve(false));
245
+ services.collectPreauthCatalog = sinon.stub().returns(Promise.resolve());
246
+ services.initServiceCatalogs = sinon.stub().rejects(new Error('auth failed'));
247
+ services.logger.error = sinon.stub();
248
+ services.trigger = sinon.stub();
249
+ // Ensure no credentials so we go to the preauth path
250
+ services.webex.credentials = {};
251
+
252
+ services.initialize();
253
+
254
+ // Get the catalog after initialize creates it
255
+ const testCatalog = services._getCatalog();
256
+ testCatalog.status.postauth.ready = false;
257
+
258
+ // call the onLoaded callback (preauth path)
259
+ services.listenToOnce.getCall(1).args[2]();
260
+ await waitForAsync();
261
+
262
+ // Now set canAuthorize to true to simulate auth completing
263
+ services.webex.canAuthorize = true;
264
+
265
+ // call the change:canAuthorize callback (should be call 2)
266
+ services.listenToOnce.getCall(2).args[2]();
267
+ await waitForAsync();
268
+
269
+ sinon.assert.calledWith(
270
+ services.logger.error,
271
+ 'services: failed to init service catalogs after auth, auth failed'
272
+ );
273
+ });
274
+ });
128
275
  });
129
276
 
130
277
  describe('#initServiceCatalogs', () => {
@@ -278,7 +425,7 @@ describe('webex-core', () => {
278
425
 
279
426
  services.initServiceCatalogs = sinon.stub().returns(Promise.resolve());
280
427
  services.webex.credentials = {
281
- getOrgId: sinon.stub().returns('')
428
+ getOrgId: sinon.stub().returns(''),
282
429
  };
283
430
  catalog.status = {};
284
431
  });
@@ -319,7 +466,7 @@ describe('webex-core', () => {
319
466
  const serviceGroup = 'postauth';
320
467
  const hostmap = {services: [{hostmap: 'hostmap'}]};
321
468
 
322
- services._formatReceivedHostmap = sinon.stub().returns({services : [{some: 'hostmap'}]});
469
+ services._formatReceivedHostmap = sinon.stub().returns({services: [{some: 'hostmap'}]});
323
470
 
324
471
  catalog.updateServiceGroups = sinon.stub().returns(Promise.resolve([{some: 'value'}]));
325
472
 
@@ -335,7 +482,7 @@ describe('webex-core', () => {
335
482
  const serviceGroup = 'postauth';
336
483
  const hostmap = {};
337
484
 
338
- services._formatReceivedHostmap = sinon.stub().returns({services : undefined});
485
+ services._formatReceivedHostmap = sinon.stub().returns({services: undefined});
339
486
 
340
487
  catalog.updateServiceGroups = sinon.stub().returns(Promise.resolve([{some: 'value'}]));
341
488
 
@@ -574,13 +721,13 @@ describe('webex-core', () => {
574
721
  });
575
722
 
576
723
  describe('#invalidateCache', () => {
577
- beforeEach( () => {
724
+ beforeEach(() => {
578
725
  services.initServiceCatalogs = sinon.stub().returns(Promise.resolve());
579
726
  services.webex.credentials = {
580
- getOrgId: sinon.stub().returns('')
727
+ getOrgId: sinon.stub().returns(''),
581
728
  };
582
729
  catalog.status = {};
583
- })
730
+ });
584
731
  it('should log the timestamp parameter', async () => {
585
732
  const timestamp = '1234567890';
586
733
  services.logger.info = sinon.stub();
@@ -588,7 +735,11 @@ describe('webex-core', () => {
588
735
 
589
736
  await services.invalidateCache(timestamp);
590
737
 
591
- assert.calledWith(services.logger.info, 'services: invalidate cache, timestamp:', timestamp);
738
+ assert.calledWith(
739
+ services.logger.info,
740
+ 'services: invalidate cache, timestamp:',
741
+ timestamp
742
+ );
592
743
  });
593
744
 
594
745
  it('should call initServiceCatalogs when invalidate timestamp is newer than catalog timestamp', async () => {
@@ -715,30 +866,30 @@ describe('webex-core', () => {
715
866
  // Arrange: seed internal _services with mobius (including duplicate baseUrl)
716
867
  services._services = [
717
868
  {
718
- "id": "urn:TEAM:us-east-2_a:mobius",
719
- "serviceName": 'mobius',
720
- "serviceUrls": [
721
- {"baseUrl": 'https://mobius-us-east-2.prod.infra.webex.com/api/v1', "priority": 5},
722
- {"baseUrl": 'https://mobius-eu-central-1.prod.infra.webex.com/api/v1', "priority": 10},
723
- {"baseUrl": 'https://mobius-ap-southeast-2.prod.infra.webex.com/api/v1', "priority": 15}, // duplicate
869
+ id: 'urn:TEAM:us-east-2_a:mobius',
870
+ serviceName: 'mobius',
871
+ serviceUrls: [
872
+ {baseUrl: 'https://mobius-us-east-2.prod.infra.webex.com/api/v1', priority: 5},
873
+ {baseUrl: 'https://mobius-eu-central-1.prod.infra.webex.com/api/v1', priority: 10},
874
+ {baseUrl: 'https://mobius-ap-southeast-2.prod.infra.webex.com/api/v1', priority: 15}, // duplicate
724
875
  ],
725
876
  },
726
877
  {
727
- "id": "urn:TEAM:ap-southeast-2_m:mobius",
728
- "serviceName": "mobius",
729
- "serviceUrls": [
730
- {
731
- "baseUrl": "https://mobius-me-central-1.prod.infra.webex.com/api/v1",
732
- "priority": 5
733
- },
734
- {
735
- "baseUrl": "https://mobius-eu-central-1.prod.infra.webex.com/api/v1",
736
- "priority": 10
737
- },
738
- {
739
- "baseUrl": "https://mobius-ap-southeast-2.prod.infra.webex.com/api/v1",
740
- "priority": 15
741
- },
878
+ id: 'urn:TEAM:ap-southeast-2_m:mobius',
879
+ serviceName: 'mobius',
880
+ serviceUrls: [
881
+ {
882
+ baseUrl: 'https://mobius-me-central-1.prod.infra.webex.com/api/v1',
883
+ priority: 5,
884
+ },
885
+ {
886
+ baseUrl: 'https://mobius-eu-central-1.prod.infra.webex.com/api/v1',
887
+ priority: 10,
888
+ },
889
+ {
890
+ baseUrl: 'https://mobius-ap-southeast-2.prod.infra.webex.com/api/v1',
891
+ priority: 15,
892
+ },
742
893
  ],
743
894
  },
744
895
  // Non-mobius service should be ignored by getMobiusClusters
@@ -756,42 +907,64 @@ describe('webex-core', () => {
756
907
  assert.deepEqual(
757
908
  clusters.map(({host, id, ttl, priority}) => ({host, id, ttl, priority})),
758
909
  [
759
- {host: 'mobius-us-east-2.prod.infra.webex.com', id: 'urn:TEAM:us-east-2_a:mobius', ttl: 0, priority: 5},
760
- {host: 'mobius-eu-central-1.prod.infra.webex.com', id: 'urn:TEAM:us-east-2_a:mobius', ttl: 0, priority: 10},
761
- {host: 'mobius-ap-southeast-2.prod.infra.webex.com', id: 'urn:TEAM:us-east-2_a:mobius', ttl: 0, priority: 15},
762
- {host: 'mobius-me-central-1.prod.infra.webex.com', id: 'urn:TEAM:ap-southeast-2_m:mobius', ttl: 0, priority: 5},
910
+ {
911
+ host: 'mobius-us-east-2.prod.infra.webex.com',
912
+ id: 'urn:TEAM:us-east-2_a:mobius',
913
+ ttl: 0,
914
+ priority: 5,
915
+ },
916
+ {
917
+ host: 'mobius-eu-central-1.prod.infra.webex.com',
918
+ id: 'urn:TEAM:us-east-2_a:mobius',
919
+ ttl: 0,
920
+ priority: 10,
921
+ },
922
+ {
923
+ host: 'mobius-ap-southeast-2.prod.infra.webex.com',
924
+ id: 'urn:TEAM:us-east-2_a:mobius',
925
+ ttl: 0,
926
+ priority: 15,
927
+ },
928
+ {
929
+ host: 'mobius-me-central-1.prod.infra.webex.com',
930
+ id: 'urn:TEAM:ap-southeast-2_m:mobius',
931
+ ttl: 0,
932
+ priority: 5,
933
+ },
763
934
  ]
764
935
  );
765
936
  });
766
937
  });
767
-
938
+
768
939
  describe('#isValidHost', () => {
769
940
  beforeEach(() => {
770
941
  // Setting up a mock services list
771
- services._services = [{
772
- "id": "urn:IDENTITY:PC75:adminAudit",
773
- "serviceName": "adminAudit",
774
- "serviceUrls": [
775
- {
776
- "baseUrl": "https://audit-ci-r.wbx2.com/audit-ci/api/v2",
777
- "priority": 5
778
- },
779
- {
780
- "baseUrl": "https://audit-ci-t.wbx2.com/audit-ci/api/v2",
781
- "priority": 10
782
- }
783
- ]
784
- },
785
- {
786
- "id": "urn:IDENTITY:PC75:cdf",
787
- "serviceName": "cdf",
788
- "serviceUrls": [
789
- {
790
- "baseUrl": "https://wapdavis.webex.com/davis/api/v1",
791
- "priority": 5
792
- }
793
- ]
794
- }];
942
+ services._services = [
943
+ {
944
+ id: 'urn:IDENTITY:PC75:adminAudit',
945
+ serviceName: 'adminAudit',
946
+ serviceUrls: [
947
+ {
948
+ baseUrl: 'https://audit-ci-r.wbx2.com/audit-ci/api/v2',
949
+ priority: 5,
950
+ },
951
+ {
952
+ baseUrl: 'https://audit-ci-t.wbx2.com/audit-ci/api/v2',
953
+ priority: 10,
954
+ },
955
+ ],
956
+ },
957
+ {
958
+ id: 'urn:IDENTITY:PC75:cdf',
959
+ serviceName: 'cdf',
960
+ serviceUrls: [
961
+ {
962
+ baseUrl: 'https://wapdavis.webex.com/davis/api/v1',
963
+ priority: 5,
964
+ },
965
+ ],
966
+ },
967
+ ];
795
968
  });
796
969
  afterAll(() => {
797
970
  // Clean up the mock services list
@@ -397,6 +397,8 @@ describe('Webex', () => {
397
397
  return new Promise((resolve) => webex.once('loaded', resolve)).then(() => {
398
398
  assert.isFalse(webex.ready);
399
399
  assert.isFalse(webex.test.ready);
400
+ // Set services.ready to true since it now blocks webex.ready
401
+ webex.internal.services.ready = true;
400
402
  webex.test.ready = true;
401
403
  assert.isTrue(webex.test.ready);
402
404
  assert.isTrue(webex.ready);
@@ -82,6 +82,8 @@ describe('Webex', () => {
82
82
  assert.isFalse(webex.internal.test.ready);
83
83
  assert.isFalse(webex.internal.ready);
84
84
  assert.isFalse(webex.ready);
85
+ // Set services.ready to true since it now blocks webex.ready
86
+ webex.internal.services.ready = true;
85
87
  webex.internal.test.ready = true;
86
88
  assert.isTrue(webex.internal.ready);
87
89
  assert.isTrue(webex.ready);