Skip to content

Commit 1896500

Browse files
chore: Increase test coverage for service account impersonation support (#2047)
* chore: Increase test coverage for service account impersonation support * fix typo
1 parent dd98704 commit 1896500

File tree

2 files changed

+71
-1
lines changed

2 files changed

+71
-1
lines changed

src/app/credential-internal.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,7 @@ export class ImpersonatedServiceAccountCredential implements Credential {
380380
* @param impersonatedServiceAccountPathOrObject - Impersonated Service account json object or
381381
* path to a service account json file.
382382
* @param httpAgent - Optional http.Agent to use when calling the remote token server.
383-
* @param implicit - An optinal boolean indicating whether this credential was implicitly
383+
* @param implicit - An optional boolean indicating whether this credential was implicitly
384384
* discovered from the environment, as opposed to being explicitly specified by the developer.
385385
*
386386
* @constructor

test/unit/app/credential-internal.spec.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import {
4141
import { HttpClient } from '../../../src/utils/api-request';
4242
import { Agent } from 'https';
4343
import { FirebaseAppError } from '../../../src/utils/error';
44+
import { deepCopy } from '../../../src/utils/deep-copy';
4445

4546
chai.should();
4647
chai.use(sinonChai);
@@ -420,6 +421,75 @@ describe('Credential', () => {
420421
});
421422
});
422423

424+
describe('ImpersonatedServiceAccountCredential', () => {
425+
it('should throw if called with the path to an invalid file', () => {
426+
const invalidPath = path.resolve(__dirname, '../../resources/unparsable.key.json');
427+
expect(() => new ImpersonatedServiceAccountCredential(invalidPath))
428+
.to.throw('Failed to parse impersonated service account file');
429+
});
430+
431+
it('should throw given an object without a "clientId" property', () => {
432+
const invalidCredential = deepCopy(MOCK_IMPERSONATED_TOKEN_CONFIG);
433+
invalidCredential.source_credentials.client_id = '';
434+
expect(() => new ImpersonatedServiceAccountCredential(invalidCredential as any))
435+
.to.throw('Impersonated Service Account must contain a "source_credentials.client_id" property.');
436+
});
437+
438+
it('should throw given an object without a "clientSecret" property', () => {
439+
const invalidCredential = deepCopy(MOCK_IMPERSONATED_TOKEN_CONFIG);
440+
invalidCredential.source_credentials.client_secret = '';
441+
expect(() => new ImpersonatedServiceAccountCredential(invalidCredential as any))
442+
.to.throw('Impersonated Service Account must contain a "source_credentials.client_secret" property.');
443+
});
444+
445+
it('should throw given an object without a "refreshToken" property', () => {
446+
const invalidCredential = deepCopy(MOCK_IMPERSONATED_TOKEN_CONFIG);
447+
invalidCredential.source_credentials.refresh_token = '';
448+
expect(() => new ImpersonatedServiceAccountCredential(invalidCredential as any))
449+
.to.throw('Impersonated Service Account must contain a "source_credentials.refresh_token" property.');
450+
});
451+
452+
it('should throw given an object without a "type" property', () => {
453+
const invalidCredential = deepCopy(MOCK_IMPERSONATED_TOKEN_CONFIG);
454+
invalidCredential.source_credentials.type = '';
455+
expect(() => new ImpersonatedServiceAccountCredential(invalidCredential as any))
456+
.to.throw('Impersonated Service Account must contain a "source_credentials.type" property.');
457+
});
458+
459+
it('should return a Credential', () => {
460+
const c = new ImpersonatedServiceAccountCredential(MOCK_IMPERSONATED_TOKEN_CONFIG);
461+
expect(c).to.deep.include({
462+
implicit: false,
463+
});
464+
});
465+
466+
it('should return an implicit Credential', () => {
467+
const c = new ImpersonatedServiceAccountCredential(MOCK_IMPERSONATED_TOKEN_CONFIG, undefined, true);
468+
expect(c).to.deep.include({
469+
implicit: true,
470+
});
471+
});
472+
473+
it('should create access tokens', () => {
474+
const scope = nock('https://www.googleapis.com')
475+
.post('/oauth2/v4/token')
476+
.reply(200, {
477+
access_token: 'token',
478+
token_type: 'Bearer',
479+
expires_in: 60 * 60,
480+
}, {
481+
'cache-control': 'no-cache, no-store, max-age=0, must-revalidate',
482+
});
483+
mockedRequests.push(scope);
484+
485+
const c = new ImpersonatedServiceAccountCredential(MOCK_IMPERSONATED_TOKEN_CONFIG);
486+
return c.getAccessToken().then((token) => {
487+
expect(token.access_token).to.be.a('string').and.to.not.be.empty;
488+
expect(token.expires_in).to.greaterThan(FIVE_MINUTES_IN_SECONDS);
489+
});
490+
});
491+
});
492+
423493
describe('getApplicationDefault()', () => {
424494
let fsStub: sinon.SinonStub;
425495

0 commit comments

Comments
 (0)