Skip to content

Commit

Permalink
Added unit tests for verifyReplayProtection
Browse files Browse the repository at this point in the history
  • Loading branch information
lahirumaramba committed Apr 17, 2023
1 parent b7fe36b commit d7749c3
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 1 deletion.
4 changes: 4 additions & 0 deletions src/app-check/app-check-api-client-internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ export class AppCheckApiClient {
return this.httpClient.send(request);
})
.then((resp) => {
if (!validator.isBoolean(resp.data?.already_consumed)) {
throw new FirebaseAppCheckError(
'invalid-argument', '`already_consumed` must be a boolean value.');
}
return resp.data.already_consumed;
})
.catch((err) => {
Expand Down
115 changes: 114 additions & 1 deletion test/unit/app-check/app-check-api-client-internal.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,119 @@ describe('AppCheckApiClient', () => {
});
});

//describe verifyReplayProtection
describe('verifyReplayProtection', () => {
it('should reject when project id is not available', () => {
return clientWithoutProjectId.verifyReplayProtection(TEST_TOKEN_TO_EXCHANGE)
.should.eventually.be.rejectedWith(noProjectId);
});

it('should throw given no token', () => {
expect(() => {
(apiClient as any).verifyReplayProtection(undefined);
}).to.throw('`token` must be a non-empty string.');
});

[null, NaN, 0, 1, true, false, [], {}, { a: 1 }, _.noop].forEach((invalidToken) => {
it('should throw given a non-string token: ' + JSON.stringify(invalidToken), () => {
expect(() => {
apiClient.verifyReplayProtection(invalidToken as any);
}).to.throw('`token` must be a non-empty string.');
});
});

it('should throw given an empty string token', () => {
expect(() => {
apiClient.verifyReplayProtection('');
}).to.throw('`token` must be a non-empty string.');
});

it('should reject when a full platform error response is received', () => {
const stub = sinon
.stub(HttpClient.prototype, 'send')
.rejects(utils.errorFrom(ERROR_RESPONSE, 404));
stubs.push(stub);
const expected = new FirebaseAppCheckError('not-found', 'Requested entity not found');
return apiClient.verifyReplayProtection(TEST_TOKEN_TO_EXCHANGE)
.should.eventually.be.rejected.and.deep.include(expected);
});

it('should reject with unknown-error when error code is not present', () => {
const stub = sinon
.stub(HttpClient.prototype, 'send')
.rejects(utils.errorFrom({}, 404));
stubs.push(stub);
const expected = new FirebaseAppCheckError('unknown-error', 'Unknown server error: {}');
return apiClient.verifyReplayProtection(TEST_TOKEN_TO_EXCHANGE)
.should.eventually.be.rejected.and.deep.include(expected);
});

it('should reject with unknown-error for non-json response', () => {
const stub = sinon
.stub(HttpClient.prototype, 'send')
.rejects(utils.errorFrom('not json', 404));
stubs.push(stub);
const expected = new FirebaseAppCheckError(
'unknown-error', 'Unexpected response with status: 404 and body: not json');
return apiClient.verifyReplayProtection(TEST_TOKEN_TO_EXCHANGE)
.should.eventually.be.rejected.and.deep.include(expected);
});

it('should reject when rejected with a FirebaseAppError', () => {
const expected = new FirebaseAppError('network-error', 'socket hang up');
const stub = sinon
.stub(HttpClient.prototype, 'send')
.rejects(expected);
stubs.push(stub);
return apiClient.verifyReplayProtection(TEST_TOKEN_TO_EXCHANGE)
.should.eventually.be.rejected.and.deep.include(expected);
});

['', 'abc', '3s2', 'sssa', '3.000000001', '3.2', null, NaN, [], {}, 100, 1.2, -200, -2.4]
.forEach((invalidAlreadyConsumed) => {
it(`should throw if the returned already_consumed value is: ${invalidAlreadyConsumed}`, () => {
const response = { already_consumed: invalidAlreadyConsumed };
const stub = sinon
.stub(HttpClient.prototype, 'send')
.resolves(utils.responseFrom(response, 200));
stubs.push(stub);
const expected = new FirebaseAppCheckError(
'invalid-argument', '`already_consumed` must be a boolean value.');
return apiClient.verifyReplayProtection(TEST_TOKEN_TO_EXCHANGE)
.should.eventually.be.rejected.and.deep.include(expected);
});
});

it('should resolve with the already_consumed status on success', () => {
const stub = sinon
.stub(HttpClient.prototype, 'send')
.resolves(utils.responseFrom({ already_consumed: true }, 200));
stubs.push(stub);
return apiClient.verifyReplayProtection(TEST_TOKEN_TO_EXCHANGE)
.then((alreadyConsumed) => {
expect(alreadyConsumed).to.equal(true);
expect(stub).to.have.been.calledOnce.and.calledWith({
method: 'POST',
url: 'https://firebaseappcheck.googleapis.com/v1beta/projects/test-project:verifyAppCheckToken',
headers: EXPECTED_HEADERS,
data: { token: TEST_TOKEN_TO_EXCHANGE }
});
});
});

[true, false].forEach((expectedAlreadyConsumed) => {
it(`should resolve with alreadyConsumed as ${expectedAlreadyConsumed} when already_consumed
from server is: ${expectedAlreadyConsumed}`, () => {
const response = { already_consumed: expectedAlreadyConsumed };
const stub = sinon
.stub(HttpClient.prototype, 'send')
.resolves(utils.responseFrom(response, 200));
stubs.push(stub);
return apiClient.verifyReplayProtection(TEST_TOKEN_TO_EXCHANGE)
.then((alreadyConsumed) => {
expect(alreadyConsumed).to.equal(expectedAlreadyConsumed);
});
});
});
});

});

0 comments on commit d7749c3

Please sign in to comment.