@webex/webex-core 3.8.1-next.2 → 3.8.1-next.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1047 @@
1
+ // /*!
2
+ // * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
3
+ // */
4
+
5
+ import '@webex/internal-plugin-device';
6
+
7
+ import {assert} from '@webex/test-helper-chai';
8
+ import {flaky} from '@webex/test-helper-mocha';
9
+ import WebexCore, {
10
+ ServiceCatalogV2,
11
+ ServiceDetail,
12
+ serviceConstantsV2,
13
+ registerInternalPlugin,
14
+ Services,
15
+ ServiceInterceptor,
16
+ ServerErrorInterceptor,
17
+ ServicesV2,
18
+ ServiceInterceptorV2,
19
+ ServerErrorInterceptorV2,
20
+ } from '@webex/webex-core';
21
+ import testUsers from '@webex/test-helper-test-users';
22
+ import uuid from 'uuid';
23
+ import sinon from 'sinon';
24
+ import {formattedServiceHostmapEntryConv} from '../../../fixtures/host-catalog-v2';
25
+
26
+ // /* eslint-disable no-underscore-dangle */
27
+ describe('webex-core', () => {
28
+ describe('ServicesV2', () => {
29
+ let webexUser;
30
+ let webexUserEU;
31
+ let webex;
32
+ let webexEU;
33
+ let services;
34
+ let servicesEU;
35
+ let catalog;
36
+ let catalogEU;
37
+
38
+ before('create users', () =>
39
+ Promise.all([
40
+ testUsers.create({count: 1}),
41
+ testUsers.create({
42
+ count: 1,
43
+ config: {
44
+ orgId: process.env.EU_PRIMARY_ORG_ID,
45
+ },
46
+ }),
47
+ ]).then(
48
+ ([[user], [userEU]]) =>
49
+ new Promise((resolve) => {
50
+ setTimeout(() => {
51
+ webexUser = user;
52
+ webexUserEU = userEU;
53
+ resolve();
54
+ }, 1000);
55
+ })
56
+ )
57
+ );
58
+
59
+ beforeEach(() => {
60
+ registerInternalPlugin('services', ServicesV2, {
61
+ interceptors: {
62
+ ServiceInterceptor: ServiceInterceptorV2.create,
63
+ ServerErrorInterceptor: ServerErrorInterceptorV2.create,
64
+ },
65
+ replace: true,
66
+ });
67
+ webex = new WebexCore({credentials: {supertoken: webexUser.token}});
68
+ webexEU = new WebexCore({credentials: {supertoken: webexUserEU.token}});
69
+ services = webex.internal.services;
70
+ servicesEU = webexEU.internal.services;
71
+ catalog = services._getCatalog();
72
+ catalogEU = servicesEU._getCatalog();
73
+
74
+ return Promise.all([
75
+ services.waitForCatalog('postauth', 10),
76
+ servicesEU.waitForCatalog('postauth', 10),
77
+ ]).then(() =>
78
+ services.updateServices({
79
+ from: 'limited',
80
+ query: {userId: webexUser.id},
81
+ })
82
+ );
83
+ });
84
+
85
+ afterEach(() => {
86
+ registerInternalPlugin('services', Services, {
87
+ interceptors: {
88
+ ServiceInterceptor: ServiceInterceptor.create,
89
+ ServerErrorInterceptor: ServerErrorInterceptor.create,
90
+ },
91
+ replace: true,
92
+ });
93
+ services = webex.internal.services;
94
+ servicesEU = webexEU.internal.services;
95
+ catalog = services._getCatalog();
96
+ catalogEU = servicesEU._getCatalog();
97
+ });
98
+
99
+ describe('#_getCatalog()', () => {
100
+ it('returns a catalog', () => {
101
+ const localCatalog = services._getCatalog();
102
+
103
+ assert.equal(localCatalog.namespace, 'ServiceCatalog');
104
+ });
105
+ });
106
+
107
+ describe('#get()', () => {
108
+ let testDetailTemplate;
109
+ let testDetail;
110
+
111
+ beforeEach(() => {
112
+ testDetailTemplate = formattedServiceHostmapEntryConv;
113
+ testDetail = new ServiceDetail(testDetailTemplate);
114
+ catalog._loadServiceDetails('preauth', [testDetail]);
115
+ services._activeServices = {
116
+ [testDetailTemplate.serviceName]: testDetailTemplate.id,
117
+ };
118
+ });
119
+
120
+ afterEach(() => {
121
+ catalog._unloadServiceDetails('preauth', [testDetail]);
122
+ });
123
+
124
+ it('returns a valid string when name is specified', () => {
125
+ const url = services.get(testDetailTemplate.serviceName);
126
+
127
+ assert.typeOf(url, 'string');
128
+ assert.equal(url, testDetail.get());
129
+ });
130
+
131
+ it("returns undefined if url doesn't exist", () => {
132
+ const s = services.get('invalidUrl');
133
+
134
+ assert.typeOf(s, 'undefined');
135
+ });
136
+
137
+ it('gets a service from a specific serviceGroup', () => {
138
+ assert.isDefined(services.get(testDetailTemplate.serviceName, 'preauth'));
139
+ });
140
+
141
+ it("fails to get a service if serviceGroup isn't accurate", () => {
142
+ assert.isUndefined(services.get(testDetailTemplate.serviceName, 'discovery'));
143
+ });
144
+ });
145
+
146
+ describe('#getClusterId()', () => {
147
+ let testDetailTemplate;
148
+ let testDetail;
149
+
150
+ beforeEach(() => {
151
+ testDetailTemplate = formattedServiceHostmapEntryConv;
152
+ testDetail = new ServiceDetail(testDetailTemplate);
153
+ catalog._loadServiceDetails('preauth', [testDetail]);
154
+ });
155
+
156
+ it('returns a clusterId when found with url', () => {
157
+ assert.equal(services.getClusterId(testDetail.get()), testDetail.id);
158
+ });
159
+
160
+ it('returns a clusterId when found with resource-appended url', () => {
161
+ assert.equal(
162
+ services.getClusterId(`${testDetail.get()}example/resource/value`),
163
+ testDetail.id
164
+ );
165
+ });
166
+
167
+ it("returns undefined when the url doesn't exist in catalog", () => {
168
+ assert.isUndefined(services.getClusterId('http://not-a-known-url.com/'));
169
+ });
170
+
171
+ it("returns undefined when the string isn't a url", () => {
172
+ assert.isUndefined(services.getClusterId('not a url'));
173
+ });
174
+ });
175
+
176
+ describe('#getServiceFromClusterId()', () => {
177
+ let testDetailTemplate;
178
+ let testDetail;
179
+
180
+ beforeEach(() => {
181
+ testDetailTemplate = formattedServiceHostmapEntryConv;
182
+ testDetail = new ServiceDetail(testDetailTemplate);
183
+ catalog._loadServiceDetails('preauth', [testDetail]);
184
+ });
185
+
186
+ it('finds a valid service url from only a clusterId', () => {
187
+ const serviceFound = services.getServiceFromClusterId({
188
+ clusterId: testDetailTemplate.id,
189
+ });
190
+
191
+ assert.equal(serviceFound.name, testDetail.serviceName);
192
+ assert.equal(serviceFound.url, testDetail.get());
193
+ });
194
+
195
+ it('finds a valid service when a service group is defined', () => {
196
+ const serviceFound = catalog.findServiceFromClusterId({
197
+ clusterId: testDetailTemplate.id,
198
+ serviceGroup: 'preauth',
199
+ });
200
+
201
+ assert.equal(serviceFound.name, testDetail.serviceName);
202
+ assert.equal(serviceFound.url, testDetail.get());
203
+ });
204
+
205
+ it("fails to find a valid service when it's not in a group", () => {
206
+ assert.isUndefined(
207
+ services.getServiceFromClusterId({
208
+ clusterId: testDetailTemplate.id,
209
+ serviceGroup: 'signin',
210
+ })
211
+ );
212
+ });
213
+
214
+ it("returns undefined when service doesn't exist", () => {
215
+ assert.isUndefined(services.getServiceFromClusterId({clusterId: 'not a clusterId'}));
216
+ });
217
+ });
218
+
219
+ describe('#getServiceFromUrl()', () => {
220
+ let testDetailTemplate;
221
+ let testDetail;
222
+
223
+ beforeEach(() => {
224
+ testDetailTemplate = formattedServiceHostmapEntryConv;
225
+ testDetail = new ServiceDetail(testDetailTemplate);
226
+ catalog._loadServiceDetails('preauth', [testDetail]);
227
+ });
228
+
229
+ afterEach(() => {
230
+ catalog._unloadServiceDetails('preauth', [testDetail]);
231
+ });
232
+
233
+ it('gets a valid service object from an existing service', () => {
234
+ const serviceObject = services.getServiceFromUrl(testDetail.get());
235
+
236
+ assert.isDefined(serviceObject);
237
+ assert.hasAllKeys(serviceObject, ['name', 'defaultUrl', 'priorityUrl']);
238
+
239
+ assert.equal(testDetailTemplate.serviceName, serviceObject.name);
240
+ assert.equal(testDetail.get(true), serviceObject.defaultUrl);
241
+ assert.equal(testDetail.get(true), serviceObject.priorityUrl);
242
+ });
243
+
244
+ it("returns undefined when the service url doesn't exist", () => {
245
+ const serviceObject = services.getServiceFromUrl('http://www.not-real.com/');
246
+
247
+ assert.isUndefined(serviceObject);
248
+ });
249
+ });
250
+
251
+ describe('#initConfig()', () => {
252
+ it('should set the discovery catalog based on the provided links', () => {
253
+ const key = 'test';
254
+ const url = 'http://www.test.com/';
255
+
256
+ webex.config.services.discovery[key] = url;
257
+
258
+ services.initConfig();
259
+
260
+ assert.equal(services.get(key), url);
261
+ });
262
+
263
+ it('should set the override catalog based on the provided links', () => {
264
+ const key = 'testOverride';
265
+ const url = 'http://www.test-override.com/';
266
+
267
+ webex.config.services.override = {};
268
+ webex.config.services.override[key] = url;
269
+
270
+ services.initConfig();
271
+
272
+ assert.equal(services.get(key), url);
273
+ });
274
+
275
+ it('should set validate domains to true when provided true', () => {
276
+ webex.config.services.validateDomains = true;
277
+
278
+ services.initConfig();
279
+
280
+ assert.isTrue(services.validateDomains);
281
+ });
282
+
283
+ it('should set validate domains to false when provided false', () => {
284
+ webex.config.services.validateDomains = false;
285
+
286
+ services.initConfig();
287
+
288
+ assert.isFalse(services.validateDomains);
289
+ });
290
+
291
+ it('should set the allowed domains based on the provided domains', () => {
292
+ const allowedDomains = ['domain'];
293
+
294
+ webex.config.services.allowedDomains = allowedDomains;
295
+
296
+ services.initConfig();
297
+
298
+ const expectedResult = [
299
+ ...allowedDomains,
300
+ ...serviceConstantsV2.COMMERCIAL_ALLOWED_DOMAINS,
301
+ ];
302
+
303
+ assert.deepEqual(expectedResult, services._getCatalog().allowedDomains);
304
+ });
305
+ });
306
+
307
+ describe('#initialize()', () => {
308
+ it('should create a catalog', () =>
309
+ assert.instanceOf(services._getCatalog(), ServiceCatalogV2));
310
+
311
+ it('should call services#initConfig() when webex config changes', () => {
312
+ services.initConfig = sinon.spy();
313
+ services.initialize();
314
+ webex.trigger('change:config');
315
+ assert.called(services.initConfig);
316
+ assert.isTrue(catalog.isReady);
317
+ });
318
+
319
+ it('should call services#initServiceCatalogs() on webex ready', () => {
320
+ services.initServiceCatalogs = sinon.stub().resolves();
321
+ services.initialize();
322
+ webex.trigger('ready');
323
+ assert.called(services.initServiceCatalogs);
324
+ assert.isTrue(catalog.isReady);
325
+ });
326
+
327
+ it('should collect different catalogs based on OrgId region', () =>
328
+ assert.notDeepEqual(catalog._getAllServiceDetails(), catalogEU._getAllServiceDetails()));
329
+
330
+ it('should not attempt to collect catalogs without authorization', (done) => {
331
+ const otherWebex = new WebexCore();
332
+ const initServiceCatalogs = sinon.stub(otherWebex.internal.services, 'initServiceCatalogs');
333
+
334
+ setTimeout(() => {
335
+ assert.notCalled(initServiceCatalogs);
336
+ assert.isFalse(otherWebex.internal.services._getCatalog().isReady);
337
+ otherWebex.internal.services.initServiceCatalogs.restore();
338
+ done();
339
+ }, 2000);
340
+ });
341
+ });
342
+
343
+ describe('#initServiceCatalogs()', () => {
344
+ it('should reject if a OrgId cannot be retrieved', () => {
345
+ webex.credentials.getOrgId = sinon.stub().throws();
346
+
347
+ return assert.isRejected(services.initServiceCatalogs());
348
+ });
349
+
350
+ it('should call services#collectPreauthCatalog with the OrgId', () => {
351
+ services.collectPreauthCatalog = sinon.stub().resolves();
352
+
353
+ return services.initServiceCatalogs().then(() =>
354
+ assert.calledWith(
355
+ services.collectPreauthCatalog,
356
+ sinon.match({
357
+ orgId: webex.credentials.getOrgId(),
358
+ })
359
+ )
360
+ );
361
+ });
362
+
363
+ it('should not call services#updateServices() when not authed', () => {
364
+ services.updateServices = sinon.stub().resolves();
365
+
366
+ // Since credentials uses AmpState, we have to set the derived
367
+ // properties of the dependent properties to undefined.
368
+ webex.credentials.supertoken.access_token = undefined;
369
+ webex.credentials.supertoken.refresh_token = undefined;
370
+
371
+ webex.credentials.getOrgId = sinon.stub().returns(webexUser.orgId);
372
+
373
+ return (
374
+ services
375
+ .initServiceCatalogs()
376
+ // services#updateServices() gets called once by the limited catalog
377
+ // retrieval and should not be called again when not authorized.
378
+ .then(() => assert.calledOnce(services.updateServices))
379
+ );
380
+ });
381
+
382
+ it('should call services#updateServices() when authed', () => {
383
+ services.updateServices = sinon.stub().resolves();
384
+
385
+ return (
386
+ services
387
+ .initServiceCatalogs()
388
+ // services#updateServices() gets called once by the limited catalog
389
+ // retrieval and should get called again when authorized.
390
+ .then(() => assert.calledTwice(services.updateServices))
391
+ );
392
+ });
393
+ });
394
+
395
+ describe('#isAllowedDomainUrl()', () => {
396
+ let list;
397
+
398
+ beforeEach(() => {
399
+ catalog.setAllowedDomains(['some-domain-a', 'some-domain-b']);
400
+
401
+ list = catalog.getAllowedDomains();
402
+ });
403
+
404
+ it('returns a boolean', () => {
405
+ assert.isBoolean(services.isAllowedDomainUrl('https://not-a-domain/resource'));
406
+ });
407
+
408
+ it('returns true if the url contains an allowed domain', () => {
409
+ assert.isTrue(services.isAllowedDomainUrl(`https://${list[0]}/resource`));
410
+ });
411
+
412
+ it('returns false if the url does not contain an allowed domain', () => {
413
+ assert.isFalse(services.isAllowedDomainUrl('https://bad-domain/resource'));
414
+ });
415
+ });
416
+
417
+ describe('#convertUrlToPriorityUrl', () => {
418
+ let testDetail;
419
+ let testDetailTemplate;
420
+
421
+ beforeEach(() => {
422
+ testDetailTemplate = formattedServiceHostmapEntryConv;
423
+ testDetail = new ServiceDetail(testDetailTemplate);
424
+ catalog._loadServiceDetails('preauth', [testDetail]);
425
+ });
426
+
427
+ it('converts the url to a priority host url', () => {
428
+ const resource = 'path/to/resource';
429
+ const url = `${testDetailTemplate.serviceUrls[1].baseUrl}/${resource}`;
430
+
431
+ const convertUrl = services.convertUrlToPriorityHostUrl(url);
432
+
433
+ assert.isDefined(convertUrl);
434
+ assert.isTrue(convertUrl.includes(testDetail.get()));
435
+ });
436
+
437
+ it('throws an exception if not a valid service', () => {
438
+ assert.throws(services.convertUrlToPriorityHostUrl, Error);
439
+
440
+ assert.throws(
441
+ services.convertUrlToPriorityHostUrl.bind(services, 'not-a-valid-service'),
442
+ Error
443
+ );
444
+ });
445
+
446
+ afterEach(() => {
447
+ catalog._unloadServiceDetails('preauth', [testDetail]);
448
+ });
449
+ });
450
+
451
+ describe('#markFailedUrl()', () => {
452
+ let testDetailTemplate;
453
+ let testDetail;
454
+
455
+ beforeEach(() => {
456
+ catalog.clean();
457
+
458
+ testDetailTemplate = formattedServiceHostmapEntryConv;
459
+ testDetail = new ServiceDetail(testDetailTemplate);
460
+ catalog._loadServiceDetails('preauth', [testDetail]);
461
+ });
462
+
463
+ afterEach(() => {
464
+ catalog._unloadServiceDetails('preauth', [testDetail]);
465
+ });
466
+
467
+ it('marks a host as failed', () => {
468
+ const priorityServiceUrl = catalog._getServiceDetail(testDetailTemplate.id);
469
+ const priorityUrl = priorityServiceUrl._getPriorityHostUrl();
470
+
471
+ services.markFailedUrl(priorityUrl);
472
+
473
+ const failedHost = priorityServiceUrl.serviceUrls.find((host) => host.failed);
474
+
475
+ assert.isTrue(priorityUrl.includes(failedHost.host));
476
+ });
477
+
478
+ it('returns the next priority url', () => {
479
+ const priorityUrl = services.get(testDetailTemplate.id);
480
+
481
+ const nextPriorityUrl = services.markFailedUrl(priorityUrl);
482
+
483
+ assert.notEqual(priorityUrl, nextPriorityUrl);
484
+ });
485
+
486
+ it('should reset hosts once all hosts have been marked failed', () => {
487
+ const priorityServiceUrl = catalog._getServiceDetail(testDetailTemplate.id);
488
+ const firstPriorityUrl = priorityServiceUrl._getPriorityHostUrl();
489
+
490
+ priorityServiceUrl.serviceUrls.forEach(() => {
491
+ const priorityUrl = priorityServiceUrl._getPriorityHostUrl();
492
+
493
+ services.markFailedUrl(priorityUrl);
494
+ });
495
+
496
+ const lastPriorityUrl = priorityServiceUrl._getPriorityHostUrl();
497
+
498
+ assert.equal(firstPriorityUrl, lastPriorityUrl);
499
+ });
500
+ });
501
+
502
+ describe('#updateServices()', () => {
503
+ it('returns a Promise that and resolves on success', (done) => {
504
+ const servicesPromise = services.updateServices();
505
+
506
+ assert.typeOf(servicesPromise, 'Promise');
507
+
508
+ servicesPromise.then(() => {
509
+ services._services.forEach((service) => {
510
+ assert.typeOf(service.serviceName, 'string');
511
+ assert.typeOf(service.id, 'string');
512
+ assert.typeOf(service.serviceUrls, 'array');
513
+ });
514
+
515
+ done();
516
+ });
517
+ });
518
+
519
+ it('updates the services list', (done) => {
520
+ catalog.serviceGroups.postauth = [];
521
+
522
+ services.updateServices().then(() => {
523
+ assert.isAbove(catalog.serviceGroups.postauth.length, 0);
524
+ done();
525
+ });
526
+ });
527
+
528
+ it('updates query.email to be emailhash-ed using SHA256', (done) => {
529
+ const updateStub = sinon.stub(catalog, 'updateServiceGroups').returnsThis();
530
+ const fetchStub = sinon.stub(services, '_fetchNewServiceHostmap').resolves();
531
+
532
+ services
533
+ .updateServices({
534
+ from: 'limited',
535
+ query: {email: webexUser.email},
536
+ })
537
+ .then(() => {
538
+ assert.calledWith(
539
+ services._fetchNewServiceHostmap,
540
+ sinon.match.has('query', {emailhash: sinon.match(/\b[A-Fa-f0-9]{64}\b/)})
541
+ );
542
+ done();
543
+ })
544
+ .finally(() => {
545
+ updateStub.restore();
546
+ fetchStub.restore();
547
+ });
548
+ });
549
+
550
+ it('updates the limited catalog when email is provided', (done) => {
551
+ catalog.serviceGroups.preauth = [];
552
+
553
+ services
554
+ .updateServices({
555
+ from: 'limited',
556
+ query: {email: webexUser.email},
557
+ })
558
+ .then(() => {
559
+ assert.isAbove(catalog.serviceGroups.preauth.length, 0);
560
+ done();
561
+ });
562
+ });
563
+
564
+ it('updates the limited catalog when userId is provided', (done) => {
565
+ catalog.serviceGroups.preauth = [];
566
+
567
+ services
568
+ .updateServices({
569
+ from: 'limited',
570
+ query: {userId: webexUser.id},
571
+ })
572
+ .then(() => {
573
+ assert.isAbove(catalog.serviceGroups.preauth.length, 0);
574
+ done();
575
+ });
576
+ });
577
+
578
+ it('updates the limited catalog when orgId is provided', (done) => {
579
+ catalog.serviceGroups.preauth = [];
580
+
581
+ services
582
+ .updateServices({
583
+ from: 'limited',
584
+ query: {orgId: webexUser.orgId},
585
+ })
586
+ .then(() => {
587
+ assert.isAbove(catalog.serviceGroups.preauth.length, 0);
588
+ done();
589
+ });
590
+ });
591
+ it('updates the limited catalog when query param mode is provided', (done) => {
592
+ catalog.serviceGroups.preauth = [];
593
+
594
+ services
595
+ .updateServices({
596
+ from: 'limited',
597
+ query: {mode: 'DEFAULT_BY_PROXIMITY'},
598
+ })
599
+ .then(() => {
600
+ assert.isAbove(catalog.serviceGroups.preauth.length, 0);
601
+ done();
602
+ });
603
+ });
604
+ it('does not update the limited catalog when nothing is provided', () => {
605
+ catalog.serviceGroups.preauth = [];
606
+
607
+ return services
608
+ .updateServices({from: 'limited'})
609
+ .then(() => {
610
+ assert(false, 'resolved, should have thrown');
611
+ })
612
+ .catch(() => {
613
+ assert(true);
614
+ });
615
+ });
616
+
617
+ it('updates limited catalog and calls _fetchNewServiceHostmap with forceRefresh = true', (done) => {
618
+ const forceRefresh = true;
619
+ const fetchNewServiceHostmapSpy = sinon.spy(services, '_fetchNewServiceHostmap');
620
+
621
+ services
622
+ .updateServices({
623
+ from: 'limited',
624
+ query: {email: webexUser.email},
625
+ forceRefresh,
626
+ })
627
+ .then(() => {
628
+ assert.calledOnce(fetchNewServiceHostmapSpy);
629
+ assert.calledWith(
630
+ fetchNewServiceHostmapSpy,
631
+ sinon.match.has(
632
+ 'from',
633
+ 'limited',
634
+ 'query',
635
+ {emailhash: sinon.match(/\b[A-Fa-f0-9]{64}\b/)},
636
+ 'forceFresh',
637
+ forceRefresh
638
+ )
639
+ );
640
+
641
+ fetchNewServiceHostmapSpy.returnValues[0].then((res) => {
642
+ assert.isAbove(res.length, 0);
643
+ });
644
+ done();
645
+ });
646
+ });
647
+ });
648
+
649
+ describe('#fetchClientRegionInfo()', () => {
650
+ it('returns client region info', () =>
651
+ services.fetchClientRegionInfo().then((r) => {
652
+ assert.isDefined(r.regionCode);
653
+ assert.isDefined(r.clientAddress);
654
+ }));
655
+ });
656
+
657
+ describe('#validateUser()', () => {
658
+ const unauthWebex = new WebexCore();
659
+ const unauthServices = unauthWebex.internal.services;
660
+ let sandbox = null;
661
+
662
+ const getActivationRequest = (requestStub) => {
663
+ const requests = requestStub.args.filter(
664
+ ([request]) => request.service === 'license' && request.resource === 'users/activations'
665
+ );
666
+
667
+ assert.strictEqual(requests.length, 1);
668
+
669
+ return requests[0][0];
670
+ };
671
+
672
+ beforeEach(() => {
673
+ sandbox = sinon.createSandbox();
674
+ });
675
+
676
+ afterEach(() => {
677
+ sandbox.restore();
678
+ sandbox = null;
679
+ });
680
+
681
+ it('returns a rejected promise when no email is specified', () =>
682
+ unauthServices
683
+ .validateUser({})
684
+ .then(() => {
685
+ assert(false, 'resolved, should have thrown');
686
+ })
687
+ .catch(() => {
688
+ assert(true);
689
+ }));
690
+
691
+ it('validates an authorized user and webex instance', () =>
692
+ services.validateUser({email: webexUser.email}).then((r) => {
693
+ assert.hasAllKeys(r, ['activated', 'exists', 'user', 'details']);
694
+ assert.equal(r.activated, true);
695
+ assert.equal(r.exists, true);
696
+ }));
697
+
698
+ it('validates an authorized EU user and webex instance', () =>
699
+ servicesEU.validateUser({email: webexUserEU.email}).then((r) => {
700
+ assert.hasAllKeys(r, ['activated', 'exists', 'user', 'details']);
701
+ assert.equal(r.activated, true);
702
+ assert.equal(r.exists, true);
703
+ }));
704
+
705
+ it("returns a rejected promise if the provided email isn't valid", () =>
706
+ unauthServices
707
+ .validateUser({email: 'not an email'})
708
+ .then(() => {
709
+ assert(false, 'resolved, should have thrown');
710
+ })
711
+ .catch(() => {
712
+ assert(true);
713
+ }));
714
+
715
+ it('validates a non-existing user', () =>
716
+ unauthServices
717
+ .validateUser({email: `Collabctg+webex-js-sdk-${uuid.v4()}@gmail.com`})
718
+ .then((r) => {
719
+ assert.hasAllKeys(r, ['activated', 'exists', 'user', 'details']);
720
+ assert.equal(r.activated, false);
721
+ assert.equal(r.exists, false);
722
+ }));
723
+
724
+ it('validates new user with activationOptions suppressEmail false', () =>
725
+ unauthServices
726
+ .validateUser({
727
+ email: `Collabctg+webex-js-sdk-${uuid.v4()}@gmail.com`,
728
+ activationOptions: {suppressEmail: false},
729
+ })
730
+ .then((r) => {
731
+ assert.hasAllKeys(r, ['activated', 'exists', 'user', 'details']);
732
+ assert.equal(r.activated, false);
733
+ assert.equal(r.exists, false);
734
+ assert.equal(r.user.verificationEmailTriggered, true);
735
+ }));
736
+
737
+ it.skip('validates new user with activationOptions suppressEmail true', () =>
738
+ unauthServices
739
+ .validateUser({
740
+ email: `Collabctg+webex-js-sdk-${uuid.v4()}@gmail.com`,
741
+ activationOptions: {suppressEmail: true},
742
+ })
743
+ .then((r) => {
744
+ assert.hasAllKeys(r, ['activated', 'exists', 'user', 'details']);
745
+ assert.equal(r.activated, false);
746
+ assert.equal(r.exists, false);
747
+ assert.equal(r.user.verificationEmailTriggered, false);
748
+ }));
749
+
750
+ it('validates an inactive user', () => {
751
+ const inactive = 'webex.web.client+nonactivated@gmail.com';
752
+
753
+ return unauthServices
754
+ .validateUser({email: inactive, activationOptions: {suppressEmail: true}})
755
+ .then((r) => {
756
+ assert.hasAllKeys(r, ['activated', 'exists', 'user', 'details']);
757
+ assert.equal(r.activated, false, 'activated');
758
+ assert.equal(r.exists, true, 'exists');
759
+ })
760
+ .catch(() => {
761
+ assert(true);
762
+ });
763
+ });
764
+
765
+ it('validates an existing user', () =>
766
+ unauthServices.validateUser({email: webexUser.email}).then((r) => {
767
+ assert.hasAllKeys(r, ['activated', 'exists', 'user', 'details']);
768
+ assert.equal(r.activated, true);
769
+ assert.equal(r.exists, true);
770
+ }));
771
+
772
+ it('validates an existing EU user', () =>
773
+ unauthServices.validateUser({email: webexUserEU.email}).then((r) => {
774
+ assert.hasAllKeys(r, ['activated', 'exists', 'user', 'details']);
775
+ assert.equal(r.activated, true);
776
+ assert.equal(r.exists, true);
777
+ }));
778
+
779
+ it('sends the prelogin user id as undefined when not specified', () => {
780
+ const requestStub = sandbox.spy(unauthServices, 'request');
781
+
782
+ return unauthServices
783
+ .validateUser({
784
+ email: `Collabctg+webex-js-sdk-${uuid.v4()}@gmail.com`,
785
+ activationOptions: {suppressEmail: true},
786
+ })
787
+ .then(() => {
788
+ assert.isUndefined(getActivationRequest(requestStub).headers['x-prelogin-userid']);
789
+ });
790
+ });
791
+
792
+ it('sends the prelogin user id as provided when specified', () => {
793
+ const requestStub = sandbox.spy(unauthServices, 'request');
794
+ const preloginUserId = uuid.v4();
795
+
796
+ return unauthServices
797
+ .validateUser({
798
+ email: `Collabctg+webex-js-sdk-${uuid.v4()}@gmail.com`,
799
+ activationOptions: {suppressEmail: true},
800
+ preloginUserId,
801
+ })
802
+ .then(() => {
803
+ assert.strictEqual(
804
+ getActivationRequest(requestStub).headers['x-prelogin-userid'],
805
+ preloginUserId
806
+ );
807
+ });
808
+ });
809
+ });
810
+
811
+ describe('#waitForService()', () => {
812
+ let name;
813
+ let url;
814
+
815
+ describe('when the service exists', () => {
816
+ beforeEach(() => {
817
+ name = Object.keys(services._activeServices)[0];
818
+ const clusterId = services._activeServices[name];
819
+ url = catalog.get(clusterId);
820
+ });
821
+
822
+ describe('when using the name parameter property', () => {
823
+ it('should resolve to the appropriate url', () =>
824
+ services.waitForService({name}).then((foundUrl) => assert.equal(foundUrl, url)));
825
+ });
826
+
827
+ describe('when using the url parameter property', () => {
828
+ it('should resolve to the appropriate url', () =>
829
+ services.waitForService({url}).then((foundUrl) => assert.equal(foundUrl, url)));
830
+ });
831
+
832
+ describe('when using the url and name parameter properties', () => {
833
+ it('should resolve to the appropriate url', () =>
834
+ services.waitForService({name, url}).then((foundUrl) => assert.equal(foundUrl, url)));
835
+ });
836
+ });
837
+
838
+ describe('when the service does not exist', () => {
839
+ let timeout;
840
+
841
+ beforeEach(() => {
842
+ name = 'not a service';
843
+ url = 'http://not-a-service.com/resource';
844
+ timeout = 1;
845
+ });
846
+
847
+ describe('when using the url parameter property', () => {
848
+ it('should return a resolve promise', () =>
849
+ // const waitForService = services.waitForService({url, timeout});
850
+
851
+ services.waitForService({url, timeout}).then((foundUrl) => {
852
+ assert.equal(foundUrl, url);
853
+ assert.isTrue(catalog.isReady);
854
+ }));
855
+ });
856
+
857
+ describe('when using the name parameter property', () => {
858
+ afterEach(() => {
859
+ webex.internal.metrics.submitClientMetrics.restore();
860
+ });
861
+
862
+ it('should return a rejected promise', () => {
863
+ const submitMetrics = sinon.stub(webex.internal.metrics, 'submitClientMetrics');
864
+ const waitForService = services.waitForService({name, timeout});
865
+
866
+ assert.called(submitMetrics);
867
+ assert.isRejected(waitForService);
868
+ assert.isTrue(catalog.isReady);
869
+ });
870
+ });
871
+
872
+ describe('when using the name and url parameter properties', () => {
873
+ it('should return a rejected promise', () => {
874
+ const waitForService = services.waitForService({
875
+ name,
876
+ url,
877
+ timeout,
878
+ });
879
+
880
+ assert.isRejected(waitForService);
881
+ assert.isTrue(catalog.isReady);
882
+ });
883
+ });
884
+
885
+ describe('when the service will exist', () => {
886
+ beforeEach(() => {
887
+ name = 'metrics';
888
+ url = services.get(name, true);
889
+ catalog.clean();
890
+ catalog.isReady = false;
891
+ });
892
+
893
+ describe('when only the preauth (limited) catalog becomes available', () => {
894
+ describe('when using the name parameter property', () => {
895
+ it('should resolve to the appropriate url', () =>
896
+ Promise.all([
897
+ services.waitForService({name}),
898
+ services.collectPreauthCatalog(),
899
+ ]).then(([foundUrl]) => assert.equal(foundUrl, url)));
900
+ });
901
+
902
+ describe('when using the url parameter property', () => {
903
+ it('should resolve to the appropriate url', () =>
904
+ Promise.all([
905
+ services.waitForService({url}),
906
+ services.collectPreauthCatalog(),
907
+ ]).then(([foundUrl]) => assert.equal(foundUrl, url)));
908
+ });
909
+
910
+ describe('when using the name and url parameter property', () => {
911
+ it('should resolve to the appropriate url', () =>
912
+ Promise.all([
913
+ services.waitForService({name, url}),
914
+ services.collectPreauthCatalog(),
915
+ ]).then(([foundUrl]) => assert.equal(foundUrl, url)));
916
+ });
917
+ });
918
+
919
+ describe('when all catalogs become available', () => {
920
+ describe('when using the name parameter property', () => {
921
+ it('should resolve to the appropriate url', () =>
922
+ Promise.all([services.waitForService({name}), services.initServiceCatalogs()]).then(
923
+ ([foundUrl]) => assert.equal(foundUrl, url)
924
+ ));
925
+ });
926
+
927
+ describe('when using the url parameter property', () => {
928
+ it('should resolve to the appropriate url', () =>
929
+ Promise.all([services.waitForService({url}), services.initServiceCatalogs()]).then(
930
+ ([foundUrl]) => assert.equal(foundUrl, url)
931
+ ));
932
+ });
933
+
934
+ describe('when using the name and url parameter property', () => {
935
+ it('should resolve to the appropriate url', () =>
936
+ Promise.all([
937
+ services.waitForService({name, url}),
938
+ services.initServiceCatalogs(),
939
+ ]).then(([foundUrl]) => assert.equal(foundUrl, url)));
940
+ });
941
+ });
942
+ });
943
+ });
944
+ });
945
+
946
+ describe('#collectPreauthCatalog()', () => {
947
+ const unauthWebex = new WebexCore({config: {credentials: {federation: true}}});
948
+ const unauthServices = unauthWebex.internal.services;
949
+ const forceRefresh = true;
950
+
951
+ it('updates the preauth catalog with email along with additional timestamp to address cache control', (done) => {
952
+ const updateServiceSpy = sinon.spy(unauthServices, 'updateServices');
953
+ const fetchNewServiceHostmapSpy = sinon.spy(unauthServices, '_fetchNewServiceHostmap');
954
+
955
+ unauthServices.collectPreauthCatalog({email: webexUser.email}, forceRefresh).then(() => {
956
+ assert.calledOnce(updateServiceSpy);
957
+ assert.calledWith(
958
+ updateServiceSpy,
959
+ sinon.match.has(
960
+ 'from',
961
+ 'limited',
962
+ 'query',
963
+ {emailhash: sinon.match(/\b[A-Fa-f0-9]{64}\b/)},
964
+ 'forceRefresh',
965
+ forceRefresh
966
+ )
967
+ );
968
+
969
+ assert.calledOnce(fetchNewServiceHostmapSpy);
970
+ assert.calledWith(
971
+ fetchNewServiceHostmapSpy,
972
+ sinon.match.has(
973
+ 'from',
974
+ 'limited',
975
+ 'query',
976
+ {emailhash: sinon.match(/\b[A-Fa-f0-9]{64}\b/)},
977
+ 'forceRefresh',
978
+ forceRefresh
979
+ )
980
+ );
981
+
982
+ fetchNewServiceHostmapSpy.returnValues[0].then((res) => {
983
+ assert.isAbove(res.length, 0);
984
+ });
985
+ done();
986
+ });
987
+ });
988
+ });
989
+
990
+ describe('#collectSigninCatalog()', () => {
991
+ const unauthWebex = new WebexCore({config: {credentials: {federation: true}}});
992
+ const unauthServices = unauthWebex.internal.services;
993
+
994
+ it('requires an email as the parameter', () =>
995
+ unauthServices.collectSigninCatalog().catch((e) => {
996
+ assert(true, e);
997
+ }));
998
+
999
+ it('requires a token as the parameter', () =>
1000
+ unauthServices.collectSigninCatalog({email: 'email@website.com'}).catch((e) => {
1001
+ assert(true, e);
1002
+ }));
1003
+ });
1004
+
1005
+ flaky(describe, process.env.SKIP_FLAKY_TESTS)('#_fetchNewServiceHostmap()', () => {
1006
+ let fullRemoteHM;
1007
+ let limitedRemoteHM;
1008
+
1009
+ before('collect remote catalogs', () =>
1010
+ Promise.all([
1011
+ services._fetchNewServiceHostmap(),
1012
+ services._fetchNewServiceHostmap({
1013
+ from: 'limited',
1014
+ query: {userId: webexUser.id},
1015
+ }),
1016
+ ]).then(([fRHM, lRHM]) => {
1017
+ fullRemoteHM = fRHM;
1018
+ limitedRemoteHM = lRHM;
1019
+ })
1020
+ );
1021
+
1022
+ it('resolves to an authed u2c hostmap when no params specified', () => {
1023
+ assert.typeOf(fullRemoteHM, 'array');
1024
+ assert.isAbove(fullRemoteHM.length, 0);
1025
+ });
1026
+
1027
+ it('resolves to a limited u2c hostmap when params specified', () => {
1028
+ assert.typeOf(limitedRemoteHM, 'array');
1029
+ assert.isAbove(limitedRemoteHM.length, 0);
1030
+ });
1031
+
1032
+ it('rejects if the params provided are invalid', () =>
1033
+ services
1034
+ ._fetchNewServiceHostmap({
1035
+ from: 'limited',
1036
+ query: {userId: 'notValid'},
1037
+ })
1038
+ .then(() => {
1039
+ assert.isTrue(false, 'should have rejected');
1040
+ })
1041
+ .catch((e) => {
1042
+ assert.typeOf(e, 'Error');
1043
+ }));
1044
+ });
1045
+ });
1046
+ });
1047
+ // /* eslint-enable no-underscore-dangle */