Skip to content

Commit

Permalink
Refactoring unit tests to reduce dependency on credentials (#431)
Browse files Browse the repository at this point in the history
* Refactoring unit tests to reduce dependency on credentials

* Fixing refresh token test

* Adding missing return

* Added a helper function for stubbing getToken()

* Removed unused imports
  • Loading branch information
hiranya911 authored Jan 22, 2019
1 parent d52d133 commit 4e526ab
Show file tree
Hide file tree
Showing 14 changed files with 165 additions and 209 deletions.
13 changes: 10 additions & 3 deletions test/integration/project-management.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
* limitations under the License.
*/

import * as _ from 'lodash';
import * as chai from 'chai';
import * as admin from '../../lib/index';
import * as util from '../unit/utils';
import { projectId } from './setup';

const APP_NAMESPACE_PREFIX = 'com.adminsdkintegrationtest.a';
Expand Down Expand Up @@ -224,14 +224,14 @@ function deleteAllShaCertificates(androidApp: admin.projectManagement.AndroidApp
* @return {string} Dot-separated string that can be used as a unique package name or bundle ID.
*/
function generateUniqueAppNamespace() {
return APP_NAMESPACE_PREFIX + util.generateRandomString(APP_NAMESPACE_SUFFIX_LENGTH);
return APP_NAMESPACE_PREFIX + generateRandomString(APP_NAMESPACE_SUFFIX_LENGTH);
}

/**
* @return {string} Dot-separated string that can be used as a unique app display name.
*/
function generateUniqueAppDisplayName() {
return APP_DISPLAY_NAME_PREFIX + util.generateRandomString(APP_DISPLAY_NAME_SUFFIX_LENGTH);
return APP_DISPLAY_NAME_PREFIX + generateRandomString(APP_DISPLAY_NAME_SUFFIX_LENGTH);
}

/**
Expand All @@ -240,3 +240,10 @@ function generateUniqueAppDisplayName() {
function isIntegrationTestApp(appNamespace: string): boolean {
return (appNamespace.indexOf(APP_NAMESPACE_PREFIX) > -1);
}

/**
* @return {string} A randomly generated alphanumeric string, of the specified length.
*/
function generateRandomString(stringLength: number): string {
return _.times(stringLength, () => _.random(35).toString(36)).join('');
}
10 changes: 5 additions & 5 deletions test/unit/auth/auth-api-request.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

import * as _ from 'lodash';
import * as chai from 'chai';
import * as nock from 'nock';
import * as sinon from 'sinon';
import * as sinonChai from 'sinon-chai';
import * as chaiAsPromised from 'chai-as-promised';
Expand Down Expand Up @@ -722,8 +721,8 @@ describe('FIREBASE_AUTH_SIGN_UP_NEW_USER', () => {

describe('FirebaseAuthRequestHandler', () => {
let mockApp: FirebaseApp;
const mockedRequests: nock.Scope[] = [];
let stubs: sinon.SinonStub[] = [];
let getTokenStub: sinon.SinonStub;
const mockAccessToken: string = utils.generateRandomAccessToken();
const expectedHeaders: {[key: string]: string} = {
'X-Client-Version': 'Node/Admin/<XXX_SDK_VERSION_XXX>',
Expand All @@ -739,11 +738,13 @@ describe('FirebaseAuthRequestHandler', () => {
};
};

before(() => utils.mockFetchAccessTokenRequests(mockAccessToken));
before(() => {
getTokenStub = utils.stubGetAccessToken(mockAccessToken);
});

after(() => {
stubs = [];
nock.cleanAll();
getTokenStub.restore();
});

beforeEach(() => {
Expand All @@ -753,7 +754,6 @@ describe('FirebaseAuthRequestHandler', () => {

afterEach(() => {
_.forEach(stubs, (stub) => stub.restore());
_.forEach(mockedRequests, (mockedRequest) => mockedRequest.done());
return mockApp.delete();
});

Expand Down
8 changes: 3 additions & 5 deletions test/unit/auth/auth.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

import * as _ from 'lodash';
import * as chai from 'chai';
import * as nock from 'nock';
import * as sinon from 'sinon';
import * as sinonChai from 'sinon-chai';
import * as chaiAsPromised from 'chai-as-promised';
Expand Down Expand Up @@ -155,17 +154,15 @@ function getDecodedSessionCookie(uid: string, authTime: Date): DecodedIdToken {
describe('Auth', () => {
let auth: Auth;
let mockApp: FirebaseApp;
let getTokenStub: sinon.SinonStub;
let oldProcessEnv: NodeJS.ProcessEnv;
let nullAccessTokenAuth: Auth;
let malformedAccessTokenAuth: Auth;
let rejectedPromiseAccessTokenAuth: Auth;

before(() => utils.mockFetchAccessTokenRequests());

after(() => nock.cleanAll());

beforeEach(() => {
mockApp = mocks.app();
getTokenStub = utils.stubGetAccessToken(undefined, mockApp);
auth = new Auth(mockApp);

nullAccessTokenAuth = new Auth(mocks.appReturningNullAccessToken());
Expand All @@ -179,6 +176,7 @@ describe('Auth', () => {
});

afterEach(() => {
getTokenStub.restore();
process.env = oldProcessEnv;
return mockApp.delete();
});
Expand Down
80 changes: 34 additions & 46 deletions test/unit/auth/credential.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ chai.use(chaiAsPromised);

const expect = chai.expect;

let TEST_GCLOUD_CREDENTIALS: any;
const GCLOUD_CREDENTIAL_SUFFIX = 'gcloud/application_default_credentials.json';
const GCLOUD_CREDENTIAL_PATH = path.resolve(process.env.HOME, '.config', GCLOUD_CREDENTIAL_SUFFIX);
const MOCK_REFRESH_TOKEN_CONFIG = {
Expand All @@ -52,38 +51,6 @@ const MOCK_REFRESH_TOKEN_CONFIG = {
type: 'authorized_user',
refresh_token: 'test_token',
};
try {
TEST_GCLOUD_CREDENTIALS = JSON.parse(fs.readFileSync(GCLOUD_CREDENTIAL_PATH).toString());
} catch (error) {
// tslint:disable-next-line:no-console
console.log(
'WARNING: gcloud credentials not found. Run `gcloud beta auth application-default login`. ' +
'Relevant tests will be skipped.',
);
}

/**
* Logs a warning and returns true if no gcloud credentials are found, meaning the test which calls
* this will be skipped.
*
* The only thing that should ever skip these tests is continuous integration. When developing
* locally, these tests should be run.
*
* @return {boolean} Whether or not the caller should skip the current test.
*/
const skipAndLogWarningIfNoGcloud = () => {
if (typeof TEST_GCLOUD_CREDENTIALS === 'undefined') {
// tslint:disable-next-line:no-console
console.log(
'WARNING: Test being skipped because gcloud credentials not found. Run `gcloud beta auth ' +
'application-default login`.',
);

return true;
}

return false;
};

const ONE_HOUR_IN_SECONDS = 60 * 60;
const FIVE_MINUTES_IN_SECONDS = 5 * 60;
Expand All @@ -92,17 +59,32 @@ const FIVE_MINUTES_IN_SECONDS = 5 * 60;
describe('Credential', () => {
let mockCertificateObject: any;
let oldProcessEnv: NodeJS.ProcessEnv;
let getTokenScope: nock.Scope;
let mockedRequests: nock.Scope[] = [];

before(() => {
getTokenScope = nock('https://accounts.google.com')
.persist()
.post('/o/oauth2/token')
.reply(200, {
access_token: utils.generateRandomAccessToken(),
token_type: 'Bearer',
expires_in: 3600,
}, {
'cache-control': 'no-cache, no-store, max-age=0, must-revalidate',
});
});

before(() => utils.mockFetchAccessTokenRequests());

after(() => nock.cleanAll());
after(() => getTokenScope.done());

beforeEach(() => {
mockCertificateObject = _.clone(mocks.certificateObject);
oldProcessEnv = process.env;
});

afterEach(() => {
_.forEach(mockedRequests, (mockedRequest) => mockedRequest.done());
mockedRequests = [];
process.env = oldProcessEnv;
});

Expand Down Expand Up @@ -289,11 +271,18 @@ describe('Credential', () => {
});

it('should create access tokens', () => {
if (skipAndLogWarningIfNoGcloud()) {
return;
}
const scope = nock('https://www.googleapis.com')
.post('/oauth2/v4/token')
.reply(200, {
access_token: 'token',
token_type: 'Bearer',
expires_in: 60 * 60,
}, {
'cache-control': 'no-cache, no-store, max-age=0, must-revalidate',
});
mockedRequests.push(scope);

const c = new RefreshTokenCredential(TEST_GCLOUD_CREDENTIALS);
const c = new RefreshTokenCredential(mocks.refreshToken);
return c.getAccessToken().then((token) => {
expect(token.access_token).to.be.a('string').and.to.not.be.empty;
expect(token.expires_in).to.greaterThan(FIVE_MINUTES_IN_SECONDS);
Expand Down Expand Up @@ -328,16 +317,12 @@ describe('Credential', () => {
});

describe('ApplicationDefaultCredential', () => {
let credPath: string;
let fsStub: sinon.SinonStub;

beforeEach(() => credPath = process.env.GOOGLE_APPLICATION_CREDENTIALS);

afterEach(() => {
if (fsStub) {
fsStub.restore();
}
process.env.GOOGLE_APPLICATION_CREDENTIALS = credPath;
});

it('should return a CertCredential with GOOGLE_APPLICATION_CREDENTIALS set', () => {
Expand Down Expand Up @@ -370,10 +355,13 @@ describe('Credential', () => {
});

it('should return a RefreshTokenCredential with gcloud login', () => {
if (skipAndLogWarningIfNoGcloud()) {
if (!fs.existsSync(GCLOUD_CREDENTIAL_PATH)) {
// tslint:disable-next-line:no-console
console.log(
'WARNING: Test being skipped because gcloud credentials not found. Run `gcloud beta auth ' +
'application-default login`.');
return;
}

delete process.env.GOOGLE_APPLICATION_CREDENTIALS;
expect((new ApplicationDefaultCredential()).getCredential()).to.be.an.instanceof(RefreshTokenCredential);
});
Expand Down
10 changes: 3 additions & 7 deletions test/unit/auth/token-generator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import * as chai from 'chai';
import * as sinon from 'sinon';
import * as sinonChai from 'sinon-chai';
import * as chaiAsPromised from 'chai-as-promised';
import * as nock from 'nock';

import * as mocks from '../../resources/mocks';
import {FirebaseTokenGenerator, ServiceAccountSigner, IAMSigner} from '../../../src/auth/token-generator';
Expand Down Expand Up @@ -106,20 +105,17 @@ describe('CryptoSigner', () => {

describe('IAMSigner', () => {
let mockApp: FirebaseApp;
let getTokenStub: sinon.SinonStub;
const mockAccessToken: string = utils.generateRandomAccessToken();

before(() => {
utils.mockFetchAccessTokenRequests(mockAccessToken);
});

after(() => nock.cleanAll());

beforeEach(() => {
mockApp = mocks.app();
getTokenStub = utils.stubGetAccessToken(mockAccessToken, mockApp);
return mockApp.INTERNAL.getToken();
});

afterEach(() => {
getTokenStub.restore();
return mockApp.delete();
});

Expand Down
Loading

0 comments on commit 4e526ab

Please sign in to comment.