@@ -45,6 +45,24 @@ describe('SecurityRules', () => {
4545  } ; 
4646  const  CREATE_TIME_UTC  =  'Fri, 08 Mar 2019 23:45:23 GMT' ; 
4747
48+   const  INVALID_RULESET_ERROR  =  new  FirebaseSecurityRulesError ( 
49+     'invalid-argument' , 
50+     'ruleset must be a non-empty name or a RulesetMetadata object.' , 
51+   ) ; 
52+   const  INVALID_RULESETS : any [ ]  =  [ null ,  undefined ,  '' ,  1 ,  true ,  { } ,  [ ] ,  { name : '' } ] ; 
53+ 
54+   const  INVALID_BUCKET_ERROR  =  new  FirebaseSecurityRulesError ( 
55+     'invalid-argument' , 
56+     'Bucket name not specified or invalid. Specify a default bucket name via the '  + 
57+     'storageBucket option when initializing the app, or specify the bucket name '  + 
58+     'explicitly when calling the rules API.' , 
59+     ) ; 
60+   const  INVALID_BUCKET_NAMES : any [ ]  =  [ null ,  '' ,  true ,  false ,  1 ,  0 ,  { } ,  [ ] ] ; 
61+ 
62+   const  INVALID_SOURCES : any [ ]  =  [ null ,  undefined ,  '' ,  1 ,  true ,  { } ,  [ ] ] ; 
63+   const  INVALID_SOURCE_ERROR  =  new  FirebaseSecurityRulesError ( 
64+     'invalid-argument' ,  'Source must be a non-empty string or a Buffer.' ) ; 
65+ 
4866  let  securityRules : SecurityRules ; 
4967  let  mockApp : FirebaseApp ; 
5068  let  mockCredentialApp : FirebaseApp ; 
@@ -67,6 +85,19 @@ describe('SecurityRules', () => {
6785    stubs  =  [ ] ; 
6886  } ) ; 
6987
88+   function  stubReleaseFromSource ( ) : [ sinon . SinonStub ,  sinon . SinonStub ]  { 
89+     const  createRuleset  =  sinon 
90+       . stub ( SecurityRulesApiClient . prototype ,  'createRuleset' ) 
91+       . resolves ( FIRESTORE_RULESET_RESPONSE ) ; 
92+     const  updateRelease  =  sinon 
93+       . stub ( SecurityRulesApiClient . prototype ,  'updateRelease' ) 
94+       . resolves ( { 
95+         rulesetName : 'projects/test-project/rulesets/foo' , 
96+       } ) ; 
97+     stubs . push ( createRuleset ,  updateRelease ) ; 
98+     return  [ createRuleset ,  updateRelease ] ; 
99+   } 
100+ 
70101  describe ( 'Constructor' ,  ( )  =>  { 
71102    const  invalidApps  =  [ null ,  NaN ,  0 ,  1 ,  true ,  false ,  '' ,  'a' ,  [ ] ,  [ 1 ,  'a' ] ,  { } ,  {  a : 1  } ,  _ . noop ] ; 
72103    invalidApps . forEach ( ( invalidApp )  =>  { 
@@ -239,17 +270,10 @@ describe('SecurityRules', () => {
239270  } ) ; 
240271
241272  describe ( 'getStorageRuleset' ,  ( )  =>  { 
242-     const  invalidBucketNames : any [ ]  =  [ null ,  '' ,  true ,  false ,  1 ,  0 ,  { } ,  [ ] ] ; 
243-     const  invalidBucketError  =  new  FirebaseSecurityRulesError ( 
244-       'invalid-argument' , 
245-       'Bucket name not specified or invalid. Specify a default bucket name via the '  + 
246-       'storageBucket option when initializing the app, or specify the bucket name '  + 
247-       'explicitly when calling the rules API.' , 
248-     ) ; 
249-     invalidBucketNames . forEach ( ( bucketName )  =>  { 
273+     INVALID_BUCKET_NAMES . forEach ( ( bucketName )  =>  { 
250274      it ( `should reject when called with: ${ JSON . stringify ( bucketName ) }  ,  ( )  =>  { 
251275        return  securityRules . getStorageRuleset ( bucketName ) 
252-           . should . eventually . be . rejected . and . deep . equal ( invalidBucketError ) ; 
276+           . should . eventually . be . rejected . and . deep . equal ( INVALID_BUCKET_ERROR ) ; 
253277      } ) ; 
254278    } ) ; 
255279
@@ -327,15 +351,10 @@ describe('SecurityRules', () => {
327351  } ) ; 
328352
329353  describe ( 'releaseFirestoreRuleset' ,  ( )  =>  { 
330-     const  invalidRulesetError  =  new  FirebaseSecurityRulesError ( 
331-       'invalid-argument' , 
332-       'ruleset must be a non-empty name or a RulesetMetadata object.' , 
333-     ) ; 
334-     const  invalidRulesets : any [ ]  =  [ null ,  undefined ,  '' ,  1 ,  true ,  { } ,  [ ] ,  { name : '' } ] ; 
335-     invalidRulesets . forEach ( ( invalidRuleset )  =>  { 
354+     INVALID_RULESETS . forEach ( ( invalidRuleset )  =>  { 
336355      it ( `should reject when called with: ${ JSON . stringify ( invalidRuleset ) }  ,  ( )  =>  { 
337356        return  securityRules . releaseFirestoreRuleset ( invalidRuleset ) 
338-           . should . eventually . be . rejected . and . deep . equal ( invalidRulesetError ) ; 
357+           . should . eventually . be . rejected . and . deep . equal ( INVALID_RULESET_ERROR ) ; 
339358      } ) ; 
340359    } ) ; 
341360
@@ -377,6 +396,213 @@ describe('SecurityRules', () => {
377396    } ) ; 
378397  } ) ; 
379398
399+   describe ( 'releaseFirestoreRulesetFromSource' ,  ( )  =>  { 
400+     const  RULES_FILE  =  { 
401+       name : 'firestore.rules' , 
402+       content : 'test source {}' , 
403+     } ; 
404+ 
405+     INVALID_SOURCES . forEach ( ( invalidSource )  =>  { 
406+       it ( `should reject when called with: ${ JSON . stringify ( invalidSource ) }  ,  ( )  =>  { 
407+         return  securityRules . releaseFirestoreRulesetFromSource ( invalidSource ) 
408+           . should . eventually . be . rejected . and . deep . equal ( INVALID_SOURCE_ERROR ) ; 
409+       } ) ; 
410+     } ) ; 
411+ 
412+     it ( 'should propagate API errors' ,  ( )  =>  { 
413+       const  stub  =  sinon 
414+         . stub ( SecurityRulesApiClient . prototype ,  'createRuleset' ) 
415+         . rejects ( EXPECTED_ERROR ) ; 
416+       stubs . push ( stub ) ; 
417+       return  securityRules . releaseFirestoreRulesetFromSource ( 'foo' ) 
418+         . should . eventually . be . rejected . and . deep . equal ( EXPECTED_ERROR ) ; 
419+     } ) ; 
420+ 
421+     const  sources : { [ key : string ] : string  |  Buffer }  =  { 
422+       string : RULES_FILE . content , 
423+       buffer : Buffer . from ( RULES_FILE . content ) , 
424+     } ; 
425+     Object . keys ( sources ) . forEach ( ( key )  =>  { 
426+       it ( `should resolve on success when source specified as a ${ key }  ,  ( )  =>  { 
427+         const  [ createRuleset ,  updateRelease ]  =  stubReleaseFromSource ( ) ; 
428+ 
429+         return  securityRules . releaseFirestoreRulesetFromSource ( sources [ key ] ) 
430+           . then ( ( ruleset )  =>  { 
431+             expect ( ruleset . name ) . to . equal ( 'foo' ) ; 
432+             expect ( ruleset . createTime ) . to . equal ( CREATE_TIME_UTC ) ; 
433+             expect ( ruleset . source . length ) . to . equal ( 1 ) ; 
434+ 
435+             const  file  =  ruleset . source [ 0 ] ; 
436+             expect ( file . name ) . equals ( 'firestore.rules' ) ; 
437+             expect ( file . content ) . equals ( 'service cloud.firestore{\n}\n' ) ; 
438+ 
439+             const  request : RulesetContent  =  { 
440+               source : { 
441+                 files : [ 
442+                   RULES_FILE , 
443+                 ] , 
444+               } , 
445+             } ; 
446+             expect ( createRuleset ) . to . have . been . called . calledOnce . and . calledWith ( request ) ; 
447+             expect ( updateRelease ) . to . have . been . calledOnce . and . calledWith ( 'cloud.firestore' ,  ruleset . name ) ; 
448+           } ) ; 
449+       } ) ; 
450+     } ) ; 
451+   } ) ; 
452+ 
453+   describe ( 'releaseStorageRuleset' ,  ( )  =>  { 
454+     INVALID_RULESETS . forEach ( ( invalidRuleset )  =>  { 
455+       it ( `should reject when called with: ${ JSON . stringify ( invalidRuleset ) }  ,  ( )  =>  { 
456+         return  securityRules . releaseStorageRuleset ( invalidRuleset ) 
457+           . should . eventually . be . rejected . and . deep . equal ( INVALID_RULESET_ERROR ) ; 
458+       } ) ; 
459+     } ) ; 
460+ 
461+     INVALID_BUCKET_NAMES . forEach ( ( bucketName )  =>  { 
462+       it ( `should reject when called with: ${ JSON . stringify ( bucketName ) }  ,  ( )  =>  { 
463+         return  securityRules . releaseStorageRuleset ( 'foo' ,  bucketName ) 
464+           . should . eventually . be . rejected . and . deep . equal ( INVALID_BUCKET_ERROR ) ; 
465+       } ) ; 
466+     } ) ; 
467+ 
468+     it ( 'should propagate API errors' ,  ( )  =>  { 
469+       const  stub  =  sinon 
470+         . stub ( SecurityRulesApiClient . prototype ,  'updateRelease' ) 
471+         . rejects ( EXPECTED_ERROR ) ; 
472+       stubs . push ( stub ) ; 
473+       return  securityRules . releaseStorageRuleset ( 'foo' ) 
474+         . should . eventually . be . rejected . and . deep . equal ( EXPECTED_ERROR ) ; 
475+     } ) ; 
476+ 
477+     it ( 'should resolve on success when the ruleset specified by name' ,  ( )  =>  { 
478+       const  stub  =  sinon 
479+         . stub ( SecurityRulesApiClient . prototype ,  'updateRelease' ) 
480+         . resolves ( { 
481+           rulesetName : 'projects/test-project/rulesets/foo' , 
482+         } ) ; 
483+       stubs . push ( stub ) ; 
484+ 
485+       return  securityRules . releaseStorageRuleset ( 'foo' ) 
486+         . then ( ( )  =>  { 
487+           expect ( stub ) . to . have . been . calledOnce . and . calledWith ( 
488+             'firebase.storage/bucketName.appspot.com' ,  'foo' ) ; 
489+         } ) ; 
490+     } ) ; 
491+ 
492+     it ( 'should resolve on success when a custom bucket name is specified' ,  ( )  =>  { 
493+       const  stub  =  sinon 
494+         . stub ( SecurityRulesApiClient . prototype ,  'updateRelease' ) 
495+         . resolves ( { 
496+           rulesetName : 'projects/test-project/rulesets/foo' , 
497+         } ) ; 
498+       stubs . push ( stub ) ; 
499+ 
500+       return  securityRules . releaseStorageRuleset ( 'foo' ,  'other.appspot.com' ) 
501+         . then ( ( )  =>  { 
502+           expect ( stub ) . to . have . been . calledOnce . and . calledWith ( 
503+             'firebase.storage/other.appspot.com' ,  'foo' ) ; 
504+         } ) ; 
505+     } ) ; 
506+ 
507+     it ( 'should resolve on success when the ruleset specified as an object' ,  ( )  =>  { 
508+       const  stub  =  sinon 
509+         . stub ( SecurityRulesApiClient . prototype ,  'updateRelease' ) 
510+         . resolves ( { 
511+           rulesetName : 'projects/test-project/rulesets/foo' , 
512+         } ) ; 
513+       stubs . push ( stub ) ; 
514+ 
515+       return  securityRules . releaseStorageRuleset ( { name : 'foo' ,  createTime : 'time' } ) 
516+         . then ( ( )  =>  { 
517+           expect ( stub ) . to . have . been . calledOnce . and . calledWith ( 
518+             'firebase.storage/bucketName.appspot.com' ,  'foo' ) ; 
519+         } ) ; 
520+     } ) ; 
521+   } ) ; 
522+ 
523+   describe ( 'releaseStorageRulesetFromSource' ,  ( )  =>  { 
524+     const  RULES_FILE  =  { 
525+       name : 'storage.rules' , 
526+       content : 'test source {}' , 
527+     } ; 
528+     const  RULES_CONTENT : RulesetContent  =  { 
529+       source : { 
530+         files : [ 
531+           RULES_FILE , 
532+         ] , 
533+       } , 
534+     } ; 
535+ 
536+     INVALID_SOURCES . forEach ( ( invalidSource )  =>  { 
537+       it ( `should reject when called with source: ${ JSON . stringify ( invalidSource ) }  ,  ( )  =>  { 
538+         return  securityRules . releaseStorageRulesetFromSource ( invalidSource ) 
539+           . should . eventually . be . rejected . and . deep . equal ( INVALID_SOURCE_ERROR ) ; 
540+       } ) ; 
541+     } ) ; 
542+ 
543+     INVALID_BUCKET_NAMES . forEach ( ( invalidBucket )  =>  { 
544+       it ( `should reject when called with bucket: ${ JSON . stringify ( invalidBucket ) }  ,  ( )  =>  { 
545+         return  securityRules . releaseStorageRulesetFromSource ( RULES_FILE . content ,  invalidBucket ) 
546+           . should . eventually . be . rejected . and . deep . equal ( INVALID_BUCKET_ERROR ) ; 
547+       } ) ; 
548+     } ) ; 
549+ 
550+     it ( 'should propagate API errors' ,  ( )  =>  { 
551+       const  stub  =  sinon 
552+         . stub ( SecurityRulesApiClient . prototype ,  'createRuleset' ) 
553+         . rejects ( EXPECTED_ERROR ) ; 
554+       stubs . push ( stub ) ; 
555+       return  securityRules . releaseStorageRulesetFromSource ( 'foo' ) 
556+         . should . eventually . be . rejected . and . deep . equal ( EXPECTED_ERROR ) ; 
557+     } ) ; 
558+ 
559+     const  sources : { [ key : string ] : string  |  Buffer }  =  { 
560+       string : RULES_FILE . content , 
561+       buffer : Buffer . from ( RULES_FILE . content ) , 
562+     } ; 
563+     Object . keys ( sources ) . forEach ( ( key )  =>  { 
564+       it ( `should resolve on success when source specified as a ${ key }  ,  ( )  =>  { 
565+         const  [ createRuleset ,  updateRelease ]  =  stubReleaseFromSource ( ) ; 
566+ 
567+         return  securityRules . releaseStorageRulesetFromSource ( sources [ key ] ) 
568+           . then ( ( ruleset )  =>  { 
569+             expect ( ruleset . name ) . to . equal ( 'foo' ) ; 
570+             expect ( ruleset . createTime ) . to . equal ( CREATE_TIME_UTC ) ; 
571+             expect ( ruleset . source . length ) . to . equal ( 1 ) ; 
572+ 
573+             const  file  =  ruleset . source [ 0 ] ; 
574+             expect ( file . name ) . equals ( 'firestore.rules' ) ; 
575+             expect ( file . content ) . equals ( 'service cloud.firestore{\n}\n' ) ; 
576+ 
577+             expect ( createRuleset ) . to . have . been . called . calledOnce . and . calledWith ( RULES_CONTENT ) ; 
578+             expect ( updateRelease ) . to . have . been . calledOnce . and . calledWith ( 
579+               'firebase.storage/bucketName.appspot.com' ,  ruleset . name ) ; 
580+           } ) ; 
581+       } ) ; 
582+     } ) ; 
583+ 
584+     Object . keys ( sources ) . forEach ( ( key )  =>  { 
585+       it ( `should resolve on success when source specified as a ${ key }  ,  ( )  =>  { 
586+         const  [ createRuleset ,  updateRelease ]  =  stubReleaseFromSource ( ) ; 
587+ 
588+         return  securityRules . releaseStorageRulesetFromSource ( sources [ key ] ,  'other.appspot.com' ) 
589+           . then ( ( ruleset )  =>  { 
590+             expect ( ruleset . name ) . to . equal ( 'foo' ) ; 
591+             expect ( ruleset . createTime ) . to . equal ( CREATE_TIME_UTC ) ; 
592+             expect ( ruleset . source . length ) . to . equal ( 1 ) ; 
593+ 
594+             const  file  =  ruleset . source [ 0 ] ; 
595+             expect ( file . name ) . equals ( 'firestore.rules' ) ; 
596+             expect ( file . content ) . equals ( 'service cloud.firestore{\n}\n' ) ; 
597+ 
598+             expect ( createRuleset ) . to . have . been . called . calledOnce . and . calledWith ( RULES_CONTENT ) ; 
599+             expect ( updateRelease ) . to . have . been . calledOnce . and . calledWith ( 
600+               'firebase.storage/other.appspot.com' ,  ruleset . name ) ; 
601+           } ) ; 
602+       } ) ; 
603+     } ) ; 
604+   } ) ; 
605+ 
380606  describe ( 'createRulesFileFromSource' ,  ( )  =>  { 
381607    const  INVALID_STRINGS : any [ ]  =  [ null ,  undefined ,  '' ,  1 ,  true ,  { } ,  [ ] ] ; 
382608
0 commit comments