Skip to content

Commit 99eef4a

Browse files
Adds integration tests for tenant management APIs. (#570)
* Adds integration tests for tenant management APIs. These tests are skipped by default as multi-tenancy is a paid feature on Google Cloud Identity Platform. To run these tests, --testMultiTenancy flag has to be added. Adds default email provider config when backend Auth server returns undefined for emailSignInConfig. * Updates listTenants integration test.
1 parent 835e08f commit 99eef4a

File tree

4 files changed

+176
-6
lines changed

4 files changed

+176
-6
lines changed

src/auth/tenant.ts

100644100755
Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ export interface TenantServerResponse {
4545
name: string;
4646
type?: TenantServerType;
4747
displayName?: string;
48-
allowPasswordSignup: boolean;
49-
enableEmailLinkSignin: boolean;
48+
allowPasswordSignup?: boolean;
49+
enableEmailLinkSignin?: boolean;
5050
}
5151

5252
/** The interface representing the listTenant API response. */
@@ -181,7 +181,10 @@ export class Tenant {
181181
try {
182182
this.emailSignInConfig = new EmailSignInConfig(response);
183183
} catch (e) {
184-
this.emailSignInConfig = undefined;
184+
// If allowPasswordSignup is undefined, it is disabled by default.
185+
this.emailSignInConfig = new EmailSignInConfig({
186+
allowPasswordSignup: false,
187+
});
185188
}
186189
}
187190

src/index.d.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,6 @@ declare namespace admin.auth {
507507
providerId: string;
508508

509509
/**
510-
511510
* @return A JSON-serializable representation of this object.
512511
*/
513512
toJSON(): Object;

test/integration/auth.spec.ts

100644100755
Lines changed: 155 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,17 @@ import * as scrypt from 'scrypt';
2323
import firebase from '@firebase/app';
2424
import '@firebase/auth';
2525
import {clone} from 'lodash';
26-
import {generateRandomString, projectId, apiKey, noServiceAccountApp} from './setup';
26+
import {
27+
generateRandomString, projectId, apiKey, noServiceAccountApp, cmdArgs,
28+
} from './setup';
2729
import url = require('url');
2830
import * as mocks from '../resources/mocks';
2931
import { AuthProviderConfig } from '../../src/auth/auth-config';
30-
import { deepExtend } from '../../src/utils/deep-copy';
32+
import { deepExtend, deepCopy } from '../../src/utils/deep-copy';
33+
34+
/* tslint:disable:no-var-requires */
35+
const chalk = require('chalk');
36+
/* tslint:enable:no-var-requires */
3137

3238
chai.should();
3339
chai.use(chaiAsPromised);
@@ -428,6 +434,153 @@ describe('admin.auth', () => {
428434
});
429435
});
430436

437+
describe('Tenant management operations', () => {
438+
// TODO: Add basic user management tests for multi-tenancy when Auth client SDK starts supporting it.
439+
let createdTenantId: string;
440+
const createdTenants: string[] = [];
441+
const tenantOptions: admin.auth.CreateTenantRequest = {
442+
displayName: 'testTenant1',
443+
type: 'lightweight',
444+
emailSignInConfig: {
445+
enabled: true,
446+
passwordRequired: true,
447+
},
448+
};
449+
const expectedCreatedTenant: any = {
450+
displayName: 'testTenant1',
451+
emailSignInConfig: {
452+
enabled: true,
453+
passwordRequired: true,
454+
},
455+
type: 'lightweight',
456+
};
457+
const expectedUpdatedTenant: any = {
458+
displayName: 'testTenantUpdated',
459+
emailSignInConfig: {
460+
enabled: false,
461+
passwordRequired: true,
462+
},
463+
type: 'lightweight',
464+
};
465+
const expectedUpdatedTenant2: any = {
466+
displayName: 'testTenantUpdated',
467+
emailSignInConfig: {
468+
enabled: true,
469+
passwordRequired: false,
470+
},
471+
type: 'lightweight',
472+
};
473+
474+
// https://mochajs.org/
475+
// Passing arrow functions (aka "lambdas") to Mocha is discouraged.
476+
// Lambdas lexically bind this and cannot access the Mocha context.
477+
before(function() {
478+
/* tslint:disable:no-console */
479+
if (!cmdArgs.testMultiTenancy) {
480+
// To enable, run: npm run test:integration -- --testMultiTenancy
481+
// By default we skip multi-tenancy as it is a Google Cloud Identity Platform
482+
// feature only and requires to be enabled via the Cloud Console.
483+
console.log(chalk.yellow(' Skipping multi-tenancy tests.'));
484+
this.skip();
485+
}
486+
/* tslint:enable:no-console */
487+
});
488+
489+
// Delete test tenants at the end of test suite.
490+
after(() => {
491+
const promises: Array<Promise<any>> = [];
492+
createdTenants.forEach((tenantId) => {
493+
promises.push(
494+
admin.auth().deleteTenant(tenantId).catch((error) => {/** Ignore. */}));
495+
});
496+
return Promise.all(promises);
497+
});
498+
499+
it('createTenant() should resolve with a new tenant', () => {
500+
return admin.auth().createTenant(tenantOptions)
501+
.then((actualTenant) => {
502+
createdTenantId = actualTenant.tenantId;
503+
createdTenants.push(createdTenantId);
504+
expectedCreatedTenant.tenantId = createdTenantId;
505+
expect(actualTenant.toJSON()).to.deep.equal(expectedCreatedTenant);
506+
});
507+
});
508+
509+
it('getTenant() should resolve with expected tenant', () => {
510+
return admin.auth().getTenant(createdTenantId)
511+
.then((actualTenant) => {
512+
expect(actualTenant.toJSON()).to.deep.equal(expectedCreatedTenant);
513+
});
514+
});
515+
516+
it('updateTenant() should resolve with the updated tenant', () => {
517+
expectedUpdatedTenant.tenantId = createdTenantId;
518+
expectedUpdatedTenant2.tenantId = createdTenantId;
519+
const updatedOptions: admin.auth.UpdateTenantRequest = {
520+
displayName: expectedUpdatedTenant.displayName,
521+
emailSignInConfig: {
522+
enabled: false,
523+
},
524+
};
525+
const updatedOptions2: admin.auth.UpdateTenantRequest = {
526+
emailSignInConfig: {
527+
enabled: true,
528+
passwordRequired: false,
529+
},
530+
};
531+
return admin.auth().updateTenant(createdTenantId, updatedOptions)
532+
.then((actualTenant) => {
533+
expect(actualTenant.toJSON()).to.deep.equal(expectedUpdatedTenant);
534+
return admin.auth().updateTenant(createdTenantId, updatedOptions2);
535+
})
536+
.then((actualTenant) => {
537+
expect(actualTenant.toJSON()).to.deep.equal(expectedUpdatedTenant2);
538+
});
539+
});
540+
541+
it('listTenants() should resolve with expected number of tenants', () => {
542+
const allTenantIds: string[] = [];
543+
const tenantOptions2 = deepCopy(tenantOptions);
544+
tenantOptions2.displayName = 'testTenant2';
545+
const listAllTenantIds = (tenantIds: string[], nextPageToken?: string): Promise<void> => {
546+
return admin.auth().listTenants(100, nextPageToken)
547+
.then((result) => {
548+
result.tenants.forEach((tenant) => {
549+
tenantIds.push(tenant.tenantId);
550+
});
551+
if (result.pageToken) {
552+
return listAllTenantIds(tenantIds, result.pageToken);
553+
}
554+
});
555+
};
556+
return admin.auth().createTenant(tenantOptions2)
557+
.then((actualTenant) => {
558+
createdTenants.push(actualTenant.tenantId);
559+
// Test listTenants returns the expected tenants.
560+
return listAllTenantIds(allTenantIds);
561+
})
562+
.then(() => {
563+
// All created tenants should be in the list of tenants.
564+
createdTenants.forEach((tenantId) => {
565+
expect(allTenantIds).to.contain(tenantId);
566+
});
567+
});
568+
});
569+
570+
it('deleteTenant() should successfully delete the provided tenant', () => {
571+
return admin.auth().deleteTenant(createdTenantId)
572+
.then(() => {
573+
return admin.auth().getTenant(createdTenantId);
574+
})
575+
.then((result) => {
576+
throw new Error('unexpected success');
577+
})
578+
.catch((error) => {
579+
expect(error.code).to.equal('auth/tenant-not-found');
580+
});
581+
});
582+
});
583+
431584
describe('SAML configuration operations', () => {
432585
const authProviderConfig1 = {
433586
providerId: 'saml.' + generateRandomString(5),

test/unit/auth/tenant.spec.ts

100644100755
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,21 @@ describe('Tenant', () => {
247247
expect(() => new Tenant(invalidOptions))
248248
.to.throw('INTERNAL ASSERT FAILED: Invalid tenant response');
249249
});
250+
251+
it('should set default EmailSignInConfig when allowPasswordSignup is undefined', () => {
252+
const serverResponse: TenantServerResponse = {
253+
name: 'projects/project1/tenants/TENANT_ID',
254+
displayName: 'TENANT_DISPLAY_NAME',
255+
};
256+
expect(() => {
257+
const tenantWithoutAllowPasswordSignup = new Tenant(serverResponse);
258+
259+
expect(tenantWithoutAllowPasswordSignup.displayName).to.equal(serverResponse.displayName);
260+
expect(tenantWithoutAllowPasswordSignup.tenantId).to.equal('TENANT_ID');
261+
expect(tenantWithoutAllowPasswordSignup.emailSignInConfig.enabled).to.be.false;
262+
expect(tenantWithoutAllowPasswordSignup.emailSignInConfig.passwordRequired).to.be.true;
263+
}).not.to.throw();
264+
});
250265
});
251266

252267
describe('toJSON()', () => {

0 commit comments

Comments
 (0)