Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Increase test coverage for service account impersonation support #2047

Merged
merged 2 commits into from
Jan 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/app/credential-internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@ export class ImpersonatedServiceAccountCredential implements Credential {
* @param impersonatedServiceAccountPathOrObject - Impersonated Service account json object or
* path to a service account json file.
* @param httpAgent - Optional http.Agent to use when calling the remote token server.
* @param implicit - An optinal boolean indicating whether this credential was implicitly
* @param implicit - An optional boolean indicating whether this credential was implicitly
* discovered from the environment, as opposed to being explicitly specified by the developer.
*
* @constructor
Expand Down
70 changes: 70 additions & 0 deletions test/unit/app/credential-internal.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import {
import { HttpClient } from '../../../src/utils/api-request';
import { Agent } from 'https';
import { FirebaseAppError } from '../../../src/utils/error';
import { deepCopy } from '../../../src/utils/deep-copy';

chai.should();
chai.use(sinonChai);
Expand Down Expand Up @@ -420,6 +421,75 @@ describe('Credential', () => {
});
});

describe('ImpersonatedServiceAccountCredential', () => {
it('should throw if called with the path to an invalid file', () => {
const invalidPath = path.resolve(__dirname, '../../resources/unparsable.key.json');
expect(() => new ImpersonatedServiceAccountCredential(invalidPath))
.to.throw('Failed to parse impersonated service account file');
});

it('should throw given an object without a "clientId" property', () => {
const invalidCredential = deepCopy(MOCK_IMPERSONATED_TOKEN_CONFIG);
invalidCredential.source_credentials.client_id = '';
expect(() => new ImpersonatedServiceAccountCredential(invalidCredential as any))
.to.throw('Impersonated Service Account must contain a "source_credentials.client_id" property.');
});

it('should throw given an object without a "clientSecret" property', () => {
const invalidCredential = deepCopy(MOCK_IMPERSONATED_TOKEN_CONFIG);
invalidCredential.source_credentials.client_secret = '';
expect(() => new ImpersonatedServiceAccountCredential(invalidCredential as any))
.to.throw('Impersonated Service Account must contain a "source_credentials.client_secret" property.');
});

it('should throw given an object without a "refreshToken" property', () => {
const invalidCredential = deepCopy(MOCK_IMPERSONATED_TOKEN_CONFIG);
invalidCredential.source_credentials.refresh_token = '';
expect(() => new ImpersonatedServiceAccountCredential(invalidCredential as any))
.to.throw('Impersonated Service Account must contain a "source_credentials.refresh_token" property.');
});

it('should throw given an object without a "type" property', () => {
const invalidCredential = deepCopy(MOCK_IMPERSONATED_TOKEN_CONFIG);
invalidCredential.source_credentials.type = '';
expect(() => new ImpersonatedServiceAccountCredential(invalidCredential as any))
.to.throw('Impersonated Service Account must contain a "source_credentials.type" property.');
});

it('should return a Credential', () => {
const c = new ImpersonatedServiceAccountCredential(MOCK_IMPERSONATED_TOKEN_CONFIG);
expect(c).to.deep.include({
implicit: false,
});
});

it('should return an implicit Credential', () => {
const c = new ImpersonatedServiceAccountCredential(MOCK_IMPERSONATED_TOKEN_CONFIG, undefined, true);
expect(c).to.deep.include({
implicit: true,
});
});

it('should create access tokens', () => {
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 ImpersonatedServiceAccountCredential(MOCK_IMPERSONATED_TOKEN_CONFIG);
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);
});
});
});

describe('getApplicationDefault()', () => {
let fsStub: sinon.SinonStub;

Expand Down