@webex/webex-core 3.12.0-next.41 → 3.12.0-next.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.js +9 -0
- package/dist/config.js.map +1 -1
- package/dist/lib/batcher.js +1 -1
- package/dist/lib/credentials/credentials.js +1 -1
- package/dist/lib/credentials/token.js +1 -1
- package/dist/lib/services/services.js +70 -42
- package/dist/lib/services/services.js.map +1 -1
- package/dist/lib/services-v2/services-v2.js +70 -42
- package/dist/lib/services-v2/services-v2.js.map +1 -1
- package/dist/plugins/logger.js +1 -1
- package/dist/webex-core.js +2 -2
- package/package.json +13 -13
- package/src/config.js +10 -0
- package/src/lib/services/services.js +55 -27
- package/src/lib/services-v2/services-v2.ts +55 -26
- package/test/integration/spec/services/services.js +148 -113
- package/test/integration/spec/services-v2/services-v2.js +134 -99
- package/test/unit/spec/services/services.js +135 -2
- package/test/unit/spec/services-v2/services-v2.ts +138 -2
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import '@webex/internal-plugin-device';
|
|
6
6
|
|
|
7
7
|
import {assert} from '@webex/test-helper-chai';
|
|
8
|
-
import {flaky} from '@webex/test-helper-mocha';
|
|
8
|
+
import {flaky, retryOn429} from '@webex/test-helper-mocha';
|
|
9
9
|
import WebexCore, {
|
|
10
10
|
ServiceCatalogV2,
|
|
11
11
|
ServiceDetail,
|
|
@@ -354,7 +354,10 @@ describe('webex-core', () => {
|
|
|
354
354
|
// Before init settles, webex.ready must be false because services.ready
|
|
355
355
|
// is a dependency and starts false in the gated path.
|
|
356
356
|
assert.isFalse(gatedWebex.internal.services.ready, 'services.ready should start false');
|
|
357
|
-
assert.isFalse(
|
|
357
|
+
assert.isFalse(
|
|
358
|
+
gatedWebex.ready,
|
|
359
|
+
'webex.ready should not fire while services.ready is false'
|
|
360
|
+
);
|
|
358
361
|
|
|
359
362
|
// Wait up to 30s for services init to complete and flip ready.
|
|
360
363
|
await new Promise((resolve, reject) => {
|
|
@@ -374,7 +377,10 @@ describe('webex-core', () => {
|
|
|
374
377
|
});
|
|
375
378
|
});
|
|
376
379
|
|
|
377
|
-
assert.isTrue(
|
|
380
|
+
assert.isTrue(
|
|
381
|
+
gatedWebex.internal.services.ready,
|
|
382
|
+
'services.ready should flip true after init settles'
|
|
383
|
+
);
|
|
378
384
|
assert.isTrue(gatedWebex.ready, 'webex.ready should fire once services.ready flips');
|
|
379
385
|
});
|
|
380
386
|
});
|
|
@@ -826,18 +832,22 @@ describe('webex-core', () => {
|
|
|
826
832
|
}));
|
|
827
833
|
|
|
828
834
|
it('validates an authorized user and webex instance', () =>
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
835
|
+
retryOn429(() =>
|
|
836
|
+
services.validateUser({email: webexUser.email}).then((r) => {
|
|
837
|
+
assert.hasAllKeys(r, ['activated', 'exists', 'user', 'details']);
|
|
838
|
+
assert.equal(r.activated, true);
|
|
839
|
+
assert.equal(r.exists, true);
|
|
840
|
+
})
|
|
841
|
+
));
|
|
834
842
|
|
|
835
843
|
it('validates an authorized EU user and webex instance', () =>
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
844
|
+
retryOn429(() =>
|
|
845
|
+
servicesEU.validateUser({email: webexUserEU.email}).then((r) => {
|
|
846
|
+
assert.hasAllKeys(r, ['activated', 'exists', 'user', 'details']);
|
|
847
|
+
assert.equal(r.activated, true);
|
|
848
|
+
assert.equal(r.exists, true);
|
|
849
|
+
})
|
|
850
|
+
));
|
|
841
851
|
|
|
842
852
|
it("returns a rejected promise if the provided email isn't valid", () =>
|
|
843
853
|
unauthServices
|
|
@@ -850,113 +860,134 @@ describe('webex-core', () => {
|
|
|
850
860
|
}));
|
|
851
861
|
|
|
852
862
|
it('validates a non-existing user', () =>
|
|
853
|
-
|
|
854
|
-
.validateUser({email: createActivationEmail()})
|
|
855
|
-
.then((r) => {
|
|
863
|
+
retryOn429(() =>
|
|
864
|
+
unauthServices.validateUser({email: createActivationEmail()}).then((r) => {
|
|
856
865
|
assert.hasAllKeys(r, ['activated', 'exists', 'user', 'details']);
|
|
857
866
|
assert.equal(r.activated, false);
|
|
858
867
|
assert.equal(r.exists, false);
|
|
859
|
-
})
|
|
868
|
+
})
|
|
869
|
+
));
|
|
860
870
|
|
|
861
871
|
it('validates new user with activationOptions suppressEmail false', () =>
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
872
|
+
retryOn429(() =>
|
|
873
|
+
unauthServices
|
|
874
|
+
.validateUser({
|
|
875
|
+
email: createActivationEmail(),
|
|
876
|
+
activationOptions: {suppressEmail: false},
|
|
877
|
+
})
|
|
878
|
+
.then((r) => {
|
|
879
|
+
assert.hasAllKeys(r, ['activated', 'exists', 'user', 'details']);
|
|
880
|
+
assert.equal(r.activated, false);
|
|
881
|
+
assert.equal(r.exists, false);
|
|
882
|
+
assert.equal(r.user.verificationEmailTriggered, true);
|
|
883
|
+
})
|
|
884
|
+
));
|
|
873
885
|
|
|
874
886
|
it('validates new user with activationOptions suppressEmail true', () =>
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
887
|
+
retryOn429(() =>
|
|
888
|
+
unauthServices
|
|
889
|
+
.validateUser({
|
|
890
|
+
email: createActivationEmail(),
|
|
891
|
+
activationOptions: {suppressEmail: true},
|
|
892
|
+
})
|
|
893
|
+
.then((r) => {
|
|
894
|
+
assert.hasAllKeys(r, ['activated', 'exists', 'user', 'details']);
|
|
895
|
+
assert.equal(r.activated, false);
|
|
896
|
+
assert.equal(r.exists, false);
|
|
897
|
+
assert.equal(r.user.verificationEmailTriggered, false);
|
|
898
|
+
})
|
|
899
|
+
));
|
|
886
900
|
|
|
887
901
|
it('validates an inactive user', () => {
|
|
888
902
|
const inactive = 'webex.web.client+nonactivated@gmail.com';
|
|
889
903
|
|
|
890
|
-
return
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
904
|
+
return retryOn429(() =>
|
|
905
|
+
unauthServices
|
|
906
|
+
.validateUser({email: inactive, activationOptions: {suppressEmail: true}})
|
|
907
|
+
.then((r) => {
|
|
908
|
+
assert.hasAllKeys(r, ['activated', 'exists', 'user', 'details']);
|
|
909
|
+
assert.equal(r.activated, false, 'activated');
|
|
910
|
+
assert.equal(r.exists, true, 'exists');
|
|
911
|
+
})
|
|
912
|
+
).catch(() => {
|
|
913
|
+
assert(true);
|
|
914
|
+
});
|
|
900
915
|
});
|
|
901
916
|
|
|
902
917
|
it('validates an existing user', () =>
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
918
|
+
retryOn429(() =>
|
|
919
|
+
unauthServices.validateUser({email: webexUser.email}).then((r) => {
|
|
920
|
+
assert.hasAllKeys(r, ['activated', 'exists', 'user', 'details']);
|
|
921
|
+
assert.equal(r.activated, true);
|
|
922
|
+
assert.equal(r.exists, true);
|
|
923
|
+
})
|
|
924
|
+
));
|
|
908
925
|
|
|
909
926
|
it('validates an existing EU user', () =>
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
927
|
+
retryOn429(() =>
|
|
928
|
+
unauthServices.validateUser({email: webexUserEU.email}).then((r) => {
|
|
929
|
+
assert.hasAllKeys(r, ['activated', 'exists', 'user', 'details']);
|
|
930
|
+
assert.equal(r.activated, true);
|
|
931
|
+
assert.equal(r.exists, true);
|
|
932
|
+
})
|
|
933
|
+
));
|
|
915
934
|
|
|
916
935
|
it('sends the prelogin user id as undefined when not specified', () => {
|
|
917
936
|
const requestStub = sandbox.spy(unauthServices, 'request');
|
|
918
937
|
|
|
919
|
-
return
|
|
920
|
-
.
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
938
|
+
return retryOn429(() => {
|
|
939
|
+
requestStub.resetHistory();
|
|
940
|
+
|
|
941
|
+
return unauthServices
|
|
942
|
+
.validateUser({
|
|
943
|
+
email: createActivationEmail(),
|
|
944
|
+
activationOptions: {suppressEmail: true},
|
|
945
|
+
})
|
|
946
|
+
.then(() => {
|
|
947
|
+
assert.isUndefined(getActivationRequest(requestStub).headers['x-prelogin-userid']);
|
|
948
|
+
});
|
|
949
|
+
});
|
|
927
950
|
});
|
|
928
951
|
|
|
929
952
|
it('sends the prelogin user id as provided when specified', () => {
|
|
930
953
|
const requestStub = sandbox.spy(unauthServices, 'request');
|
|
931
954
|
const preloginUserId = uuid.v4();
|
|
932
955
|
|
|
933
|
-
return
|
|
934
|
-
.
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
956
|
+
return retryOn429(() => {
|
|
957
|
+
requestStub.resetHistory();
|
|
958
|
+
|
|
959
|
+
return unauthServices
|
|
960
|
+
.validateUser({
|
|
961
|
+
email: createActivationEmail(),
|
|
962
|
+
activationOptions: {suppressEmail: true},
|
|
963
|
+
preloginUserId,
|
|
964
|
+
})
|
|
965
|
+
.then(() => {
|
|
966
|
+
assert.strictEqual(
|
|
967
|
+
getActivationRequest(requestStub).headers['x-prelogin-userid'],
|
|
968
|
+
preloginUserId
|
|
969
|
+
);
|
|
970
|
+
});
|
|
971
|
+
});
|
|
945
972
|
});
|
|
946
973
|
|
|
947
974
|
it('uses the license service by default', () => {
|
|
948
975
|
const requestStub = sandbox.spy(unauthServices, 'request');
|
|
949
976
|
|
|
950
|
-
return
|
|
951
|
-
.
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
977
|
+
return retryOn429(() => {
|
|
978
|
+
requestStub.resetHistory();
|
|
979
|
+
|
|
980
|
+
return unauthServices
|
|
981
|
+
.validateUser({
|
|
982
|
+
email: createActivationEmail(),
|
|
983
|
+
activationOptions: {suppressEmail: true},
|
|
984
|
+
})
|
|
985
|
+
.then(() => {
|
|
986
|
+
const request = getActivationRequest(requestStub, false);
|
|
987
|
+
assert.strictEqual(request.service, 'license');
|
|
988
|
+
assert.strictEqual(request.resource, 'users/activations');
|
|
989
|
+
});
|
|
990
|
+
});
|
|
960
991
|
});
|
|
961
992
|
|
|
962
993
|
it('uses the user-onboarding service when useUserOnboardingServiceForActivations config is true', () => {
|
|
@@ -970,16 +1001,20 @@ describe('webex-core', () => {
|
|
|
970
1001
|
const userOnboardingServices = userOnboardingWebex.internal.services;
|
|
971
1002
|
const requestStub = sandbox.spy(userOnboardingServices, 'request');
|
|
972
1003
|
|
|
973
|
-
return
|
|
974
|
-
.
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
1004
|
+
return retryOn429(() => {
|
|
1005
|
+
requestStub.resetHistory();
|
|
1006
|
+
|
|
1007
|
+
return userOnboardingServices
|
|
1008
|
+
.validateUser({
|
|
1009
|
+
email: createActivationEmail(),
|
|
1010
|
+
activationOptions: {suppressEmail: true},
|
|
1011
|
+
})
|
|
1012
|
+
.then(() => {
|
|
1013
|
+
const request = getActivationRequest(requestStub, true);
|
|
1014
|
+
assert.strictEqual(request.service, 'user-onboarding');
|
|
1015
|
+
assert.strictEqual(request.resource, 'api/v1/users/activations');
|
|
1016
|
+
});
|
|
1017
|
+
});
|
|
983
1018
|
});
|
|
984
1019
|
});
|
|
985
1020
|
|
|
@@ -149,9 +149,55 @@ describe('webex-core', () => {
|
|
|
149
149
|
const [, event] = services.listenToOnce.getCall(1).args;
|
|
150
150
|
assert.equal(event, 'ready', 'default path listens on webex ready');
|
|
151
151
|
});
|
|
152
|
+
|
|
153
|
+
it('skips preauth catalog collection when skipPreauthCatalogOnUnauthenticated is true and no credentials', async () => {
|
|
154
|
+
services.webex.config = {
|
|
155
|
+
...(services.webex.config || {}),
|
|
156
|
+
services: {skipPreauthCatalogOnUnauthenticated: true},
|
|
157
|
+
};
|
|
158
|
+
services.listenToOnce = sinon.stub();
|
|
159
|
+
services.collectPreauthCatalog = sinon.stub().returns(Promise.resolve());
|
|
160
|
+
services.logger.info = sinon.stub();
|
|
161
|
+
|
|
162
|
+
services.initialize();
|
|
163
|
+
services.listenToOnce.getCall(0).args[2]();
|
|
164
|
+
services.listenToOnce.getCall(1).args[2]();
|
|
165
|
+
await waitForAsync();
|
|
166
|
+
|
|
167
|
+
assert.isFalse(
|
|
168
|
+
services.collectPreauthCatalog.called,
|
|
169
|
+
'must not collect preauth catalog while unauthenticated when option is enabled'
|
|
170
|
+
);
|
|
171
|
+
assert.isFalse(services.initFailed);
|
|
172
|
+
sinon.assert.calledWith(
|
|
173
|
+
services.logger.info,
|
|
174
|
+
'services: skipping preauth catalog collection while unauthenticated as per the config'
|
|
175
|
+
);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('still collects preauth catalog when skipPreauthCatalogOnUnauthenticated is false (default)', async () => {
|
|
179
|
+
services.webex.config = {
|
|
180
|
+
...(services.webex.config || {}),
|
|
181
|
+
services: {skipPreauthCatalogOnUnauthenticated: false},
|
|
182
|
+
};
|
|
183
|
+
services.listenToOnce = sinon.stub();
|
|
184
|
+
services.collectPreauthCatalog = sinon.stub().returns(Promise.resolve());
|
|
185
|
+
|
|
186
|
+
services.initialize();
|
|
187
|
+
services.listenToOnce.getCall(0).args[2]();
|
|
188
|
+
services.listenToOnce.getCall(1).args[2]();
|
|
189
|
+
await waitForAsync();
|
|
190
|
+
|
|
191
|
+
assert.isTrue(
|
|
192
|
+
services.collectPreauthCatalog.called,
|
|
193
|
+
'must collect preauth catalog when option is disabled'
|
|
194
|
+
);
|
|
195
|
+
});
|
|
152
196
|
});
|
|
153
197
|
|
|
154
198
|
describe('#initialize (waitForCatalogInit=true)', () => {
|
|
199
|
+
let clock;
|
|
200
|
+
|
|
155
201
|
beforeEach(() => {
|
|
156
202
|
services.webex.config = {
|
|
157
203
|
...(services.webex.config || {}),
|
|
@@ -162,6 +208,15 @@ describe('webex-core', () => {
|
|
|
162
208
|
// true). We reset here so tests that call initialize() a second time
|
|
163
209
|
// with the flag enabled observe the gated behavior from a clean state.
|
|
164
210
|
services.ready = false;
|
|
211
|
+
// The gated path schedules a real 15s setTimeout via _makeInitTimeout()
|
|
212
|
+
// that is not cleared when the init race resolves. Fake only setTimeout/
|
|
213
|
+
// clearTimeout (leaving setImmediate real so waitForAsync still works)
|
|
214
|
+
// so restoring in afterEach clears the pending timer and Jest can exit.
|
|
215
|
+
clock = sinon.useFakeTimers({toFake: ['setTimeout', 'clearTimeout']});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
afterEach(() => {
|
|
219
|
+
clock.restore();
|
|
165
220
|
});
|
|
166
221
|
|
|
167
222
|
it('leaves services.ready=false after initialize (does not flip until finalize)', () => {
|
|
@@ -242,7 +297,6 @@ describe('webex-core', () => {
|
|
|
242
297
|
});
|
|
243
298
|
|
|
244
299
|
it('flips services.ready=true when init times out', async () => {
|
|
245
|
-
const clock = sinon.useFakeTimers();
|
|
246
300
|
services.listenToOnce = sinon.stub();
|
|
247
301
|
// Never resolves - the timeout must win the race.
|
|
248
302
|
services.initServiceCatalogs = sinon.stub().returns(new Promise(() => {}));
|
|
@@ -259,7 +313,6 @@ describe('webex-core', () => {
|
|
|
259
313
|
// rejected timeout can propagate through .catch and .finally into
|
|
260
314
|
// _finalizeReady().
|
|
261
315
|
await clock.tickAsync(15_001);
|
|
262
|
-
clock.restore();
|
|
263
316
|
|
|
264
317
|
assert.isTrue(services.initFailed);
|
|
265
318
|
assert.isTrue(services.ready);
|
|
@@ -382,6 +435,86 @@ describe('webex-core', () => {
|
|
|
382
435
|
});
|
|
383
436
|
});
|
|
384
437
|
|
|
438
|
+
describe('#initialize (waitForCatalogInit=true, skipPreauthCatalogOnUnauthenticated=true)', () => {
|
|
439
|
+
beforeEach(() => {
|
|
440
|
+
services.webex.config = {
|
|
441
|
+
...(services.webex.config || {}),
|
|
442
|
+
services: {
|
|
443
|
+
waitForCatalogInit: true,
|
|
444
|
+
catalogInitTimeout: 15000,
|
|
445
|
+
skipPreauthCatalogOnUnauthenticated: true,
|
|
446
|
+
},
|
|
447
|
+
};
|
|
448
|
+
services.ready = false;
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
it('skips preauth catalog collection but still finalizes services.ready when unauthenticated', async () => {
|
|
452
|
+
services.listenToOnce = sinon.stub();
|
|
453
|
+
services.collectPreauthCatalog = sinon.stub().returns(Promise.resolve());
|
|
454
|
+
services.logger.info = sinon.stub();
|
|
455
|
+
|
|
456
|
+
services.initialize();
|
|
457
|
+
services.listenToOnce.getCall(0).args[2]();
|
|
458
|
+
services.listenToOnce.getCall(1).args[2]();
|
|
459
|
+
await waitForAsync();
|
|
460
|
+
|
|
461
|
+
assert.isFalse(
|
|
462
|
+
services.collectPreauthCatalog.called,
|
|
463
|
+
'must not collect preauth catalog while unauthenticated when option is enabled'
|
|
464
|
+
);
|
|
465
|
+
assert.isTrue(services.ready, 'must still finalize ready so webex.ready is not stalled');
|
|
466
|
+
sinon.assert.calledWith(
|
|
467
|
+
services.logger.info,
|
|
468
|
+
'services: skipping preauth catalog collection while unauthenticated as per the config'
|
|
469
|
+
);
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
it('still registers a change:canAuthorize listener when skipping preauth (fresh-login path)', async () => {
|
|
473
|
+
services.listenToOnce = sinon.stub();
|
|
474
|
+
services.collectPreauthCatalog = sinon.stub().returns(Promise.resolve());
|
|
475
|
+
services.initServiceCatalogs = sinon.stub().returns(Promise.resolve());
|
|
476
|
+
|
|
477
|
+
services.initialize();
|
|
478
|
+
const currentCatalog = services._getCatalog();
|
|
479
|
+
services.listenToOnce.getCall(0).args[2]();
|
|
480
|
+
services.listenToOnce.getCall(1).args[2]();
|
|
481
|
+
await waitForAsync();
|
|
482
|
+
|
|
483
|
+
const freshLoginCall = services.listenToOnce
|
|
484
|
+
.getCalls()
|
|
485
|
+
.find((call) => call.args[1] === 'change:canAuthorize');
|
|
486
|
+
assert.isDefined(freshLoginCall, 'expected change:canAuthorize listener to be registered');
|
|
487
|
+
|
|
488
|
+
services.webex.canAuthorize = true;
|
|
489
|
+
currentCatalog.status.postauth.ready = false;
|
|
490
|
+
freshLoginCall.args[2]();
|
|
491
|
+
|
|
492
|
+
assert.isTrue(
|
|
493
|
+
services.initServiceCatalogs.called,
|
|
494
|
+
'expected postauth catalog init after canAuthorize flips'
|
|
495
|
+
);
|
|
496
|
+
await waitForAsync();
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
it('does not schedule an init timeout when skipping preauth (no stray timer/rejection)', async () => {
|
|
500
|
+
const clock = sinon.useFakeTimers();
|
|
501
|
+
services.listenToOnce = sinon.stub();
|
|
502
|
+
services.collectPreauthCatalog = sinon.stub().returns(Promise.resolve());
|
|
503
|
+
services.logger.error = sinon.stub();
|
|
504
|
+
|
|
505
|
+
services.initialize();
|
|
506
|
+
services.listenToOnce.getCall(0).args[2]();
|
|
507
|
+
services.listenToOnce.getCall(1).args[2]();
|
|
508
|
+
|
|
509
|
+
// Advance well past the configured init timeout; since no timeout was
|
|
510
|
+
// scheduled in skip mode, no timeout error should ever be logged.
|
|
511
|
+
await clock.tickAsync(30_000);
|
|
512
|
+
clock.restore();
|
|
513
|
+
|
|
514
|
+
sinon.assert.neverCalledWithMatch(services.logger.error, /services: init timed out/);
|
|
515
|
+
});
|
|
516
|
+
});
|
|
517
|
+
|
|
385
518
|
describe('#_finalizeReady', () => {
|
|
386
519
|
beforeEach(() => {
|
|
387
520
|
// Outer beforeEach construction already ran initialize() (ungated,
|
|
@@ -151,9 +151,55 @@ describe('webex-core', () => {
|
|
|
151
151
|
const [, event] = services.listenToOnce.getCall(1).args;
|
|
152
152
|
assert.equal(event, 'ready', 'default path listens on webex ready');
|
|
153
153
|
});
|
|
154
|
+
|
|
155
|
+
it('skips preauth catalog collection when skipPreauthCatalogOnUnauthenticated is true and no credentials', async () => {
|
|
156
|
+
services.webex.config = {
|
|
157
|
+
...(services.webex.config || {}),
|
|
158
|
+
services: {skipPreauthCatalogOnUnauthenticated: true},
|
|
159
|
+
};
|
|
160
|
+
services.listenToOnce = sinon.stub();
|
|
161
|
+
services.collectPreauthCatalog = sinon.stub().returns(Promise.resolve());
|
|
162
|
+
services.logger.info = sinon.stub();
|
|
163
|
+
|
|
164
|
+
services.initialize();
|
|
165
|
+
services.listenToOnce.getCall(0).args[2]();
|
|
166
|
+
services.listenToOnce.getCall(1).args[2]();
|
|
167
|
+
await waitForAsync();
|
|
168
|
+
|
|
169
|
+
assert.isFalse(
|
|
170
|
+
services.collectPreauthCatalog.called,
|
|
171
|
+
'must not collect preauth catalog while unauthenticated when option is enabled'
|
|
172
|
+
);
|
|
173
|
+
assert.isFalse(services.initFailed);
|
|
174
|
+
sinon.assert.calledWith(
|
|
175
|
+
services.logger.info,
|
|
176
|
+
'services: skipping preauth catalog collection while unauthenticated as per the config'
|
|
177
|
+
);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('still collects preauth catalog when skipPreauthCatalogOnUnauthenticated is false (default)', async () => {
|
|
181
|
+
services.webex.config = {
|
|
182
|
+
...(services.webex.config || {}),
|
|
183
|
+
services: {skipPreauthCatalogOnUnauthenticated: false},
|
|
184
|
+
};
|
|
185
|
+
services.listenToOnce = sinon.stub();
|
|
186
|
+
services.collectPreauthCatalog = sinon.stub().returns(Promise.resolve());
|
|
187
|
+
|
|
188
|
+
services.initialize();
|
|
189
|
+
services.listenToOnce.getCall(0).args[2]();
|
|
190
|
+
services.listenToOnce.getCall(1).args[2]();
|
|
191
|
+
await waitForAsync();
|
|
192
|
+
|
|
193
|
+
assert.isTrue(
|
|
194
|
+
services.collectPreauthCatalog.called,
|
|
195
|
+
'must collect preauth catalog when option is disabled'
|
|
196
|
+
);
|
|
197
|
+
});
|
|
154
198
|
});
|
|
155
199
|
|
|
156
200
|
describe('#initialize (waitForCatalogInit=true)', () => {
|
|
201
|
+
let clock;
|
|
202
|
+
|
|
157
203
|
beforeEach(() => {
|
|
158
204
|
services.webex.config = {
|
|
159
205
|
...(services.webex.config || {}),
|
|
@@ -164,6 +210,15 @@ describe('webex-core', () => {
|
|
|
164
210
|
// true). We reset here so tests that call initialize() a second time
|
|
165
211
|
// with the flag enabled observe the gated behavior from a clean state.
|
|
166
212
|
services.ready = false;
|
|
213
|
+
// The gated path schedules a real 15s setTimeout via _makeInitTimeout()
|
|
214
|
+
// that is not cleared when the init race resolves. Fake only setTimeout/
|
|
215
|
+
// clearTimeout (leaving setImmediate real so waitForAsync still works)
|
|
216
|
+
// so restoring in afterEach clears the pending timer and Jest can exit.
|
|
217
|
+
clock = sinon.useFakeTimers({toFake: ['setTimeout', 'clearTimeout']});
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
afterEach(() => {
|
|
221
|
+
clock.restore();
|
|
167
222
|
});
|
|
168
223
|
|
|
169
224
|
it('leaves services.ready=false after initialize (does not flip until finalize)', () => {
|
|
@@ -243,7 +298,6 @@ describe('webex-core', () => {
|
|
|
243
298
|
});
|
|
244
299
|
|
|
245
300
|
it('flips services.ready=true when init times out', async () => {
|
|
246
|
-
const clock = sinon.useFakeTimers();
|
|
247
301
|
services.listenToOnce = sinon.stub();
|
|
248
302
|
services.initServiceCatalogs = sinon.stub().returns(new Promise(() => {}));
|
|
249
303
|
services.webex.credentials = {
|
|
@@ -256,7 +310,6 @@ describe('webex-core', () => {
|
|
|
256
310
|
services.listenToOnce.getCall(1).args[2]();
|
|
257
311
|
|
|
258
312
|
await clock.tickAsync(15_001);
|
|
259
|
-
clock.restore();
|
|
260
313
|
|
|
261
314
|
assert.isTrue(services.initFailed);
|
|
262
315
|
assert.isTrue(services.ready);
|
|
@@ -376,6 +429,89 @@ describe('webex-core', () => {
|
|
|
376
429
|
});
|
|
377
430
|
});
|
|
378
431
|
|
|
432
|
+
describe('#initialize (waitForCatalogInit=true, skipPreauthCatalogOnUnauthenticated=true)', () => {
|
|
433
|
+
beforeEach(() => {
|
|
434
|
+
services.webex.config = {
|
|
435
|
+
...(services.webex.config || {}),
|
|
436
|
+
services: {
|
|
437
|
+
waitForCatalogInit: true,
|
|
438
|
+
catalogInitTimeout: 15000,
|
|
439
|
+
skipPreauthCatalogOnUnauthenticated: true,
|
|
440
|
+
},
|
|
441
|
+
};
|
|
442
|
+
services.ready = false;
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
it('skips preauth catalog collection but still finalizes services.ready when unauthenticated', async () => {
|
|
446
|
+
services.listenToOnce = sinon.stub();
|
|
447
|
+
services.collectPreauthCatalog = sinon.stub().returns(Promise.resolve());
|
|
448
|
+
services.logger.info = sinon.stub();
|
|
449
|
+
|
|
450
|
+
services.initialize();
|
|
451
|
+
services.listenToOnce.getCall(0).args[2]();
|
|
452
|
+
services.listenToOnce.getCall(1).args[2]();
|
|
453
|
+
await waitForAsync();
|
|
454
|
+
|
|
455
|
+
assert.isFalse(
|
|
456
|
+
services.collectPreauthCatalog.called,
|
|
457
|
+
'must not collect preauth catalog while unauthenticated when option is enabled'
|
|
458
|
+
);
|
|
459
|
+
assert.isTrue(services.ready, 'must still finalize ready so webex.ready is not stalled');
|
|
460
|
+
sinon.assert.calledWith(
|
|
461
|
+
services.logger.info,
|
|
462
|
+
'services: skipping preauth catalog collection while unauthenticated as per the config'
|
|
463
|
+
);
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
it('still registers a change:canAuthorize listener when skipping preauth (fresh-login path)', async () => {
|
|
467
|
+
services.listenToOnce = sinon.stub();
|
|
468
|
+
services.collectPreauthCatalog = sinon.stub().returns(Promise.resolve());
|
|
469
|
+
services.initServiceCatalogs = sinon.stub().returns(Promise.resolve());
|
|
470
|
+
|
|
471
|
+
services.initialize();
|
|
472
|
+
const currentCatalog = services._getCatalog();
|
|
473
|
+
services.listenToOnce.getCall(0).args[2]();
|
|
474
|
+
services.listenToOnce.getCall(1).args[2]();
|
|
475
|
+
await waitForAsync();
|
|
476
|
+
|
|
477
|
+
const freshLoginCall = services.listenToOnce
|
|
478
|
+
.getCalls()
|
|
479
|
+
.find((call: any) => call.args[1] === 'change:canAuthorize');
|
|
480
|
+
assert.isDefined(freshLoginCall, 'expected change:canAuthorize listener to be registered');
|
|
481
|
+
|
|
482
|
+
services.webex.canAuthorize = true;
|
|
483
|
+
currentCatalog.status.postauth.ready = false;
|
|
484
|
+
freshLoginCall!.args[2]();
|
|
485
|
+
|
|
486
|
+
assert.isTrue(
|
|
487
|
+
services.initServiceCatalogs.called,
|
|
488
|
+
'expected postauth catalog init after canAuthorize flips'
|
|
489
|
+
);
|
|
490
|
+
await waitForAsync();
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
it('does not schedule an init timeout when skipping preauth (no stray timer/rejection)', async () => {
|
|
494
|
+
const clock = sinon.useFakeTimers();
|
|
495
|
+
services.listenToOnce = sinon.stub();
|
|
496
|
+
services.collectPreauthCatalog = sinon.stub().returns(Promise.resolve());
|
|
497
|
+
services.logger.error = sinon.stub();
|
|
498
|
+
|
|
499
|
+
services.initialize();
|
|
500
|
+
services.listenToOnce.getCall(0).args[2]();
|
|
501
|
+
services.listenToOnce.getCall(1).args[2]();
|
|
502
|
+
|
|
503
|
+
// Advance well past the configured init timeout; since no timeout was
|
|
504
|
+
// scheduled in skip mode, no timeout error should ever be logged.
|
|
505
|
+
await clock.tickAsync(30_000);
|
|
506
|
+
clock.restore();
|
|
507
|
+
|
|
508
|
+
sinon.assert.neverCalledWithMatch(
|
|
509
|
+
services.logger.error,
|
|
510
|
+
/services: init timed out/
|
|
511
|
+
);
|
|
512
|
+
});
|
|
513
|
+
});
|
|
514
|
+
|
|
379
515
|
describe('#_finalizeReady', () => {
|
|
380
516
|
beforeEach(() => {
|
|
381
517
|
// Outer beforeEach construction already ran initialize() (ungated,
|