@@ -134,7 +134,8 @@ describe('RemoteConfig', () => {
134134 variantValue : [
135135 { variantId : 'variant_A' , value : 'true' } ,
136136 { variantId : 'variant_B' , noChange : true }
137- ]
137+ ] ,
138+ exposurePercent : 25 ,
138139 }
139140 }
140141 } ,
@@ -233,7 +234,8 @@ describe('RemoteConfig', () => {
233234 variantValue : [
234235 { variantId : 'variant_A' , value : 'true' } ,
235236 { variantId : 'variant_B' , noChange : true }
236- ]
237+ ] ,
238+ exposurePercent : 25 ,
237239 }
238240 }
239241 } ,
@@ -607,6 +609,48 @@ describe('RemoteConfig', () => {
607609 } ) ;
608610 } ) ;
609611
612+ it ( 'should throw if experiment exposure percent is out of range' , ( ) => {
613+ sourceTemplate = deepCopy ( REMOTE_CONFIG_RESPONSE ) ;
614+ ( sourceTemplate . parameters as any ) . experiment_enabled
615+ . conditionalValues . ios . experimentValue . exposurePercent = 101 ;
616+ const jsonString = JSON . stringify ( sourceTemplate ) ;
617+ expect ( ( ) => remoteConfig . createTemplateFromJSON ( jsonString ) )
618+ . to . throw ( 'Experiment exposure percent must be between 0 and 100 (experiment_enabled)' ) ;
619+ } ) ;
620+
621+ it ( 'should accept experiment exposure percent for boundary and middle values' , ( ) => {
622+ [ 0 , 52 , 100 ] . forEach ( ( validExposurePercent ) => {
623+ sourceTemplate = deepCopy ( REMOTE_CONFIG_RESPONSE ) ;
624+ ( sourceTemplate . parameters as any ) . experiment_enabled
625+ . conditionalValues . ios . experimentValue . exposurePercent = validExposurePercent ;
626+ const jsonString = JSON . stringify ( sourceTemplate ) ;
627+ expect ( ( ) => remoteConfig . createTemplateFromJSON ( jsonString ) ) . to . not . throw ( ) ;
628+ } ) ;
629+ } ) ;
630+
631+ it ( 'should throw if experiment exposure percent in a parameter group is out of range' , ( ) => {
632+ sourceTemplate = deepCopy ( REMOTE_CONFIG_RESPONSE ) ;
633+ ( sourceTemplate . parameterGroups as any ) . new_menu . parameters . pumpkin_spice_season
634+ . conditionalValues . android_en = {
635+ experimentValue : { experimentId : 'exp_1' , exposurePercent : 101 , variantValue : [ ] } ,
636+ } ;
637+ const jsonString = JSON . stringify ( sourceTemplate ) ;
638+ expect ( ( ) => remoteConfig . createTemplateFromJSON ( jsonString ) )
639+ . to . throw ( 'Experiment exposure percent must be between 0 and 100 (pumpkin_spice_season)' ) ;
640+ } ) ;
641+
642+ it ( 'should accept valid experiment exposure percent in a parameter group' , ( ) => {
643+ [ 0 , 50 , 100 ] . forEach ( ( validExposurePercent ) => {
644+ sourceTemplate = deepCopy ( REMOTE_CONFIG_RESPONSE ) ;
645+ ( sourceTemplate . parameterGroups as any ) . new_menu . parameters . pumpkin_spice_season
646+ . conditionalValues . android_en = {
647+ experimentValue : { experimentId : 'exp_1' , exposurePercent : validExposurePercent , variantValue : [ ] } ,
648+ } ;
649+ const jsonString = JSON . stringify ( sourceTemplate ) ;
650+ expect ( ( ) => remoteConfig . createTemplateFromJSON ( jsonString ) ) . to . not . throw ( ) ;
651+ } ) ;
652+ } ) ;
653+
610654 it ( 'should succeed when a valid json string is provided' , ( ) => {
611655 const jsonString = JSON . stringify ( REMOTE_CONFIG_RESPONSE ) ;
612656 const newTemplate = remoteConfig . createTemplateFromJSON ( jsonString ) ;
@@ -652,6 +696,7 @@ describe('RemoteConfig', () => {
652696 expect ( p4 . conditionalValues ) . to . not . be . undefined ;
653697 const experimentParam = p4 . conditionalValues ! [ 'ios' ] as ExperimentParameterValue ;
654698 expect ( experimentParam . experimentValue . experimentId ) . to . equal ( 'experiment_1' ) ;
699+ expect ( experimentParam . experimentValue . exposurePercent ) . to . equal ( 25 ) ;
655700 expect ( experimentParam . experimentValue . variantValue . length ) . to . equal ( 2 ) ;
656701 expect ( experimentParam . experimentValue . variantValue [ 0 ] ) . to . deep . equal ( { variantId : 'variant_A' , value : 'true' } ) ;
657702 expect ( experimentParam . experimentValue . variantValue [ 1 ] ) . to . deep . equal ( { variantId : 'variant_B' , noChange : true } ) ;
@@ -1668,6 +1713,19 @@ describe('RemoteConfig', () => {
16681713 . should . eventually . be . rejected . and . have . property (
16691714 'message' , 'Remote Config conditions must be an array' ) ;
16701715 } ) ;
1716+
1717+ it ( 'should reject when API response contains invalid experiment exposure percent' , ( ) => {
1718+ const response = deepCopy ( REMOTE_CONFIG_RESPONSE ) ;
1719+ ( response . parameters as any ) . experiment_enabled
1720+ . conditionalValues . ios . experimentValue . exposurePercent = 101 ;
1721+ const stub = sinon
1722+ . stub ( RemoteConfigApiClient . prototype , operationName )
1723+ . resolves ( response ) ;
1724+ stubs . push ( stub ) ;
1725+ return rcOperation ( )
1726+ . should . eventually . be . rejected . and . have . property (
1727+ 'message' , 'Experiment exposure percent must be between 0 and 100 (experiment_enabled)' ) ;
1728+ } ) ;
16711729 }
16721730
16731731 function runValidResponseTests ( rcOperation : ( ) => Promise < RemoteConfigTemplate > ,
0 commit comments