@@ -235,4 +235,133 @@ describe('AppCheckApiClient', () => {
235235 } ) ;
236236 } ) ;
237237 } ) ;
238+
239+ describe ( 'verifyReplayProtection' , ( ) => {
240+ it ( 'should reject when project id is not available' , ( ) => {
241+ return clientWithoutProjectId . verifyReplayProtection ( TEST_TOKEN_TO_EXCHANGE )
242+ . should . eventually . be . rejectedWith ( noProjectId ) ;
243+ } ) ;
244+
245+ it ( 'should throw given no token' , ( ) => {
246+ expect ( ( ) => {
247+ ( apiClient as any ) . verifyReplayProtection ( undefined ) ;
248+ } ) . to . throw ( '`token` must be a non-empty string.' ) ;
249+ } ) ;
250+
251+ [ null , NaN , 0 , 1 , true , false , [ ] , { } , { a : 1 } , _ . noop ] . forEach ( ( invalidToken ) => {
252+ it ( 'should throw given a non-string token: ' + JSON . stringify ( invalidToken ) , ( ) => {
253+ expect ( ( ) => {
254+ apiClient . verifyReplayProtection ( invalidToken as any ) ;
255+ } ) . to . throw ( '`token` must be a non-empty string.' ) ;
256+ } ) ;
257+ } ) ;
258+
259+ it ( 'should throw given an empty string token' , ( ) => {
260+ expect ( ( ) => {
261+ apiClient . verifyReplayProtection ( '' ) ;
262+ } ) . to . throw ( '`token` must be a non-empty string.' ) ;
263+ } ) ;
264+
265+ it ( 'should reject when a full platform error response is received' , ( ) => {
266+ const stub = sinon
267+ . stub ( HttpClient . prototype , 'send' )
268+ . rejects ( utils . errorFrom ( ERROR_RESPONSE , 404 ) ) ;
269+ stubs . push ( stub ) ;
270+ const expected = new FirebaseAppCheckError ( 'not-found' , 'Requested entity not found' ) ;
271+ return apiClient . verifyReplayProtection ( TEST_TOKEN_TO_EXCHANGE )
272+ . should . eventually . be . rejected . and . deep . include ( expected ) ;
273+ } ) ;
274+
275+ it ( 'should reject with unknown-error when error code is not present' , ( ) => {
276+ const stub = sinon
277+ . stub ( HttpClient . prototype , 'send' )
278+ . rejects ( utils . errorFrom ( { } , 404 ) ) ;
279+ stubs . push ( stub ) ;
280+ const expected = new FirebaseAppCheckError ( 'unknown-error' , 'Unknown server error: {}' ) ;
281+ return apiClient . verifyReplayProtection ( TEST_TOKEN_TO_EXCHANGE )
282+ . should . eventually . be . rejected . and . deep . include ( expected ) ;
283+ } ) ;
284+
285+ it ( 'should reject with unknown-error for non-json response' , ( ) => {
286+ const stub = sinon
287+ . stub ( HttpClient . prototype , 'send' )
288+ . rejects ( utils . errorFrom ( 'not json' , 404 ) ) ;
289+ stubs . push ( stub ) ;
290+ const expected = new FirebaseAppCheckError (
291+ 'unknown-error' , 'Unexpected response with status: 404 and body: not json' ) ;
292+ return apiClient . verifyReplayProtection ( TEST_TOKEN_TO_EXCHANGE )
293+ . should . eventually . be . rejected . and . deep . include ( expected ) ;
294+ } ) ;
295+
296+ it ( 'should reject when rejected with a FirebaseAppError' , ( ) => {
297+ const expected = new FirebaseAppError ( 'network-error' , 'socket hang up' ) ;
298+ const stub = sinon
299+ . stub ( HttpClient . prototype , 'send' )
300+ . rejects ( expected ) ;
301+ stubs . push ( stub ) ;
302+ return apiClient . verifyReplayProtection ( TEST_TOKEN_TO_EXCHANGE )
303+ . should . eventually . be . rejected . and . deep . include ( expected ) ;
304+ } ) ;
305+
306+ [ '' , 'abc' , '3s2' , 'sssa' , '3.000000001' , '3.2' , null , NaN , [ ] , { } , 100 , 1.2 , - 200 , - 2.4 ]
307+ . forEach ( ( invalidAlreadyConsumed ) => {
308+ it ( `should throw if the returned alreadyConsumed value is: ${ invalidAlreadyConsumed } ` , ( ) => {
309+ const response = { alreadyConsumed : invalidAlreadyConsumed } ;
310+ const stub = sinon
311+ . stub ( HttpClient . prototype , 'send' )
312+ . resolves ( utils . responseFrom ( response , 200 ) ) ;
313+ stubs . push ( stub ) ;
314+ const expected = new FirebaseAppCheckError (
315+ 'invalid-argument' , '`alreadyConsumed` must be a boolean value.' ) ;
316+ return apiClient . verifyReplayProtection ( TEST_TOKEN_TO_EXCHANGE )
317+ . should . eventually . be . rejected . and . deep . include ( expected ) ;
318+ } ) ;
319+ } ) ;
320+
321+ it ( 'should resolve with the alreadyConsumed status on success' , ( ) => {
322+ const stub = sinon
323+ . stub ( HttpClient . prototype , 'send' )
324+ . resolves ( utils . responseFrom ( { alreadyConsumed : true } , 200 ) ) ;
325+ stubs . push ( stub ) ;
326+ return apiClient . verifyReplayProtection ( TEST_TOKEN_TO_EXCHANGE )
327+ . then ( ( alreadyConsumed ) => {
328+ expect ( alreadyConsumed ) . to . equal ( true ) ;
329+ expect ( stub ) . to . have . been . calledOnce . and . calledWith ( {
330+ method : 'POST' ,
331+ url : 'https://firebaseappcheck.googleapis.com/v1beta/projects/test-project:verifyAppCheckToken' ,
332+ headers : EXPECTED_HEADERS ,
333+ data : { app_check_token : TEST_TOKEN_TO_EXCHANGE }
334+ } ) ;
335+ } ) ;
336+ } ) ;
337+
338+ [ true , false ] . forEach ( ( expectedAlreadyConsumed ) => {
339+ it ( `should resolve with alreadyConsumed as ${ expectedAlreadyConsumed } when alreadyConsumed
340+ from server is: ${ expectedAlreadyConsumed } ` , ( ) => {
341+ const response = { alreadyConsumed : expectedAlreadyConsumed } ;
342+ const stub = sinon
343+ . stub ( HttpClient . prototype , 'send' )
344+ . resolves ( utils . responseFrom ( response , 200 ) ) ;
345+ stubs . push ( stub ) ;
346+ return apiClient . verifyReplayProtection ( TEST_TOKEN_TO_EXCHANGE )
347+ . then ( ( alreadyConsumed ) => {
348+ expect ( alreadyConsumed ) . to . equal ( expectedAlreadyConsumed ) ;
349+ } ) ;
350+ } ) ;
351+ } ) ;
352+
353+ it ( `should resolve with alreadyConsumed as false when alreadyConsumed
354+ from server is: undefined` , ( ) => {
355+ const response = { } ;
356+ const stub = sinon
357+ . stub ( HttpClient . prototype , 'send' )
358+ . resolves ( utils . responseFrom ( response , 200 ) ) ;
359+ stubs . push ( stub ) ;
360+ return apiClient . verifyReplayProtection ( TEST_TOKEN_TO_EXCHANGE )
361+ . then ( ( alreadyConsumed ) => {
362+ expect ( alreadyConsumed ) . to . equal ( false ) ;
363+ } ) ;
364+ } ) ;
365+ } ) ;
366+
238367} ) ;
0 commit comments