@@ -20,7 +20,7 @@ import OptimizelyUserContext from '../../optimizely_user_context';
20
20
import { bucket } from '../bucketer' ;
21
21
import { getTestProjectConfig , getTestProjectConfigWithFeatures } from '../../tests/test_data' ;
22
22
import { createProjectConfig , ProjectConfig } from '../../project_config/project_config' ;
23
- import { BucketerParams , Experiment } from '../../shared_types' ;
23
+ import { BucketerParams , Experiment , UserProfile } from '../../shared_types' ;
24
24
import { CONTROL_ATTRIBUTES , DECISION_SOURCES } from '../../utils/enums' ;
25
25
import { getDecisionTestDatafile } from '../../tests/decision_test_datafile' ;
26
26
@@ -688,6 +688,10 @@ describe('DecisionService', () => {
688
688
} ) ;
689
689
690
690
describe ( 'getVariationForFeature' , ( ) => {
691
+ beforeEach ( ( ) => {
692
+ mockBucket . mockReset ( ) ;
693
+ } ) ;
694
+
691
695
it ( 'should return variation from the first experiment for which a variation is available' , ( ) => {
692
696
const { decisionService } = getDecisionService ( ) ;
693
697
@@ -737,12 +741,95 @@ describe('DecisionService', () => {
737
741
config , config . experimentKeyMap [ 'exp_2' ] , user , false , expect . anything ( ) ) ;
738
742
} ) ;
739
743
740
- describe ( 'when no variation is found for any experiment and a target delivery audience condition is satisfied' , ( ) => {
744
+ it ( 'should save the variation found for an experiment in the user profile without' , ( ) => {
745
+ const { decisionService, userProfileService } = getDecisionService ( { userProfileService : true } ) ;
746
+
747
+ const resolveVariationSpy = vi . spyOn ( decisionService as any , 'resolveVariation' )
748
+ . mockImplementation ( (
749
+ config ,
750
+ experiment : any ,
751
+ user ,
752
+ shouldIgnoreUPS ,
753
+ userProfileTracker : any ,
754
+ ) => {
755
+ if ( experiment . key === 'exp_2' ) {
756
+ const variation = 'variation_2' ;
757
+
758
+ userProfileTracker . userProfile [ experiment . id ] = {
759
+ variation_id : '5002' ,
760
+ } ;
761
+ userProfileTracker . isProfileUpdated = true ;
762
+
763
+ return {
764
+ result : 'variation_2' ,
765
+ reasons : [ ] ,
766
+ } ;
767
+ }
768
+ return {
769
+ result : null ,
770
+ reasons : [ ] ,
771
+ }
772
+ } ) ;
773
+
774
+ const config = createProjectConfig ( getDecisionTestDatafile ( ) ) ;
775
+
776
+ const user = new OptimizelyUserContext ( {
777
+ optimizely : { } as any ,
778
+ userId : 'tester' ,
779
+ attributes : {
780
+ age : 40 ,
781
+ } ,
782
+ } ) ;
783
+
784
+ userProfileService ?. lookup . mockImplementation ( ( userId : string ) => {
785
+ if ( userId === 'tester' ) {
786
+ return {
787
+ user_id : 'tester' ,
788
+ experiment_bucket_map : {
789
+ '2001' : {
790
+ variation_id : '5001' ,
791
+ } ,
792
+ } ,
793
+ } ;
794
+ }
795
+ return null ;
796
+ } ) ;
797
+
798
+
799
+ const feature = config . featureKeyMap [ 'flag_1' ] ;
800
+ const variation = decisionService . getVariationForFeature ( config , feature , user ) ;
801
+
802
+ expect ( variation . result ) . toEqual ( {
803
+ experiment : config . experimentKeyMap [ 'exp_2' ] ,
804
+ variation : config . variationIdMap [ '5002' ] ,
805
+ decisionSource : DECISION_SOURCES . FEATURE_TEST ,
806
+ } ) ;
807
+
808
+ expect ( userProfileService ?. lookup ) . toHaveBeenCalledTimes ( 1 ) ;
809
+ expect ( userProfileService ?. lookup ) . toHaveBeenCalledWith ( 'tester' ) ;
810
+
811
+ expect ( userProfileService ?. save ) . toHaveBeenCalledTimes ( 1 ) ;
812
+ expect ( userProfileService ?. save ) . toHaveBeenCalledWith ( {
813
+ user_id : 'tester' ,
814
+ experiment_bucket_map : {
815
+ '2001' : {
816
+ variation_id : '5001' ,
817
+ } ,
818
+ '2002' : {
819
+ variation_id : '5002' ,
820
+ } ,
821
+ } ,
822
+ } ) ;
823
+ } ) ;
824
+
825
+ describe ( 'when no variation is found for any experiment and a targeted delivery \
826
+ audience condition is satisfied' , ( ) => {
741
827
beforeEach ( ( ) => {
742
828
mockBucket . mockReset ( ) ;
743
829
} ) ;
744
830
745
- it ( 'should return variation from the target delivery for which audience condition is satisfied if the user is bucketed into it' , ( ) => {
831
+ it ( 'should return variation from the target delivery for which audience condition \
832
+ is satisfied if the user is bucketed into it' , ( ) => {
746
833
const { decisionService } = getDecisionService ( ) ;
747
834
748
835
const resolveVariationSpy = vi . spyOn ( decisionService as any , 'resolveVariation' )
@@ -757,7 +844,7 @@ describe('DecisionService', () => {
757
844
optimizely : { } as any ,
758
845
userId : 'tester' ,
759
846
attributes : {
760
- age : 55 , // this should satisfy the audience condition for the targeted delivery with key delivery_2
847
+ age : 55 , // this should satisfy the audience condition for the targeted delivery with key delivery_2 and delivery_3
761
848
} ,
762
849
} ) ;
763
850
@@ -796,8 +883,8 @@ describe('DecisionService', () => {
796
883
verifyBucketCall ( 0 , config , config . experimentIdMap [ '3002' ] , user ) ;
797
884
} ) ;
798
885
799
- it ( 'should skip to everyone else targeting rule if the user is not bucketed into the targeted delivery for which \
800
- audience condition is satisfied' , ( ) => {
886
+ it ( 'should skip to everyone else targeting rule if the user is not bucketed \
887
+ into the targeted delivery for which audience condition is satisfied' , ( ) => {
801
888
const { decisionService } = getDecisionService ( ) ;
802
889
803
890
const resolveVariationSpy = vi . spyOn ( decisionService as any , 'resolveVariation' )
@@ -812,7 +899,7 @@ describe('DecisionService', () => {
812
899
optimizely : { } as any ,
813
900
userId : 'tester' ,
814
901
attributes : {
815
- age : 55 , // this should satisfy the audience condition for the targeted delivery with key delivery_2
902
+ age : 55 , // this should satisfy the audience condition for the targeted delivery with key delivery_2 and delivery_3
816
903
} ,
817
904
} ) ;
818
905
@@ -852,5 +939,106 @@ describe('DecisionService', () => {
852
939
verifyBucketCall ( 1 , config , config . experimentIdMap [ 'default-rollout-id' ] , user ) ;
853
940
} ) ;
854
941
} ) ;
942
+
943
+ it ( 'should return variation from the everyone else targeting rule if no variation \
944
+ is found for any experiment or targeted delivery' , ( ) => {
945
+ const { decisionService } = getDecisionService ( ) ;
946
+
947
+ const resolveVariationSpy = vi . spyOn ( decisionService as any , 'resolveVariation' )
948
+ . mockReturnValue ( {
949
+ result : null ,
950
+ reasons : [ ] ,
951
+ } ) ;
952
+
953
+ const config = createProjectConfig ( getDecisionTestDatafile ( ) ) ;
954
+
955
+ const user = new OptimizelyUserContext ( {
956
+ optimizely : { } as any ,
957
+ userId : 'tester' ,
958
+ attributes : {
959
+ age : 100 , // this should not satisfy any audience condition for any targeted delivery
960
+ } ,
961
+ } ) ;
962
+
963
+ mockBucket . mockImplementation ( ( param : BucketerParams ) => {
964
+ const ruleKey = param . experimentKey ;
965
+ console . log ( 'bucket called for ' + ruleKey ) ;
966
+ if ( ruleKey === 'default-rollout-key' ) {
967
+ return {
968
+ result : '5007' ,
969
+ reasons : [ ] ,
970
+ } ;
971
+ }
972
+ return {
973
+ result : null ,
974
+ reasons : [ ] ,
975
+ } ;
976
+ } ) ;
977
+
978
+ const feature = config . featureKeyMap [ 'flag_1' ] ;
979
+ const variation = decisionService . getVariationForFeature ( config , feature , user ) ;
980
+
981
+ expect ( variation . result ) . toEqual ( {
982
+ experiment : config . experimentIdMap [ 'default-rollout-id' ] ,
983
+ variation : config . variationIdMap [ '5007' ] ,
984
+ decisionSource : DECISION_SOURCES . ROLLOUT ,
985
+ } ) ;
986
+
987
+ expect ( resolveVariationSpy ) . toHaveBeenCalledTimes ( 3 ) ;
988
+ expect ( resolveVariationSpy ) . toHaveBeenNthCalledWith ( 1 ,
989
+ config , config . experimentKeyMap [ 'exp_1' ] , user , false , expect . anything ( ) ) ;
990
+ expect ( resolveVariationSpy ) . toHaveBeenNthCalledWith ( 2 ,
991
+ config , config . experimentKeyMap [ 'exp_2' ] , user , false , expect . anything ( ) ) ;
992
+ expect ( resolveVariationSpy ) . toHaveBeenNthCalledWith ( 3 ,
993
+ config , config . experimentKeyMap [ 'exp_3' ] , user , false , expect . anything ( ) ) ;
994
+
995
+ expect ( mockBucket ) . toHaveBeenCalledTimes ( 1 ) ;
996
+ verifyBucketCall ( 0 , config , config . experimentIdMap [ 'default-rollout-id' ] , user ) ;
997
+ } ) ;
998
+
999
+ it ( 'should return null if no variation is found for any experiment, targeted delivery, or everyone else targeting rule' , ( ) => {
1000
+ const { decisionService } = getDecisionService ( ) ;
1001
+
1002
+ const resolveVariationSpy = vi . spyOn ( decisionService as any , 'resolveVariation' )
1003
+ . mockReturnValue ( {
1004
+ result : null ,
1005
+ reasons : [ ] ,
1006
+ } ) ;
1007
+
1008
+ const config = createProjectConfig ( getDecisionTestDatafile ( ) ) ;
1009
+ const rolloutId = config . featureKeyMap [ 'flag_1' ] . rolloutId ;
1010
+ config . rolloutIdMap [ rolloutId ] . experiments = [ ] ; // remove the experiments from the rollout
1011
+
1012
+ const user = new OptimizelyUserContext ( {
1013
+ optimizely : { } as any ,
1014
+ userId : 'tester' ,
1015
+ attributes : {
1016
+ age : 10 ,
1017
+ } ,
1018
+ } ) ;
1019
+
1020
+ const feature = config . featureKeyMap [ 'flag_1' ] ;
1021
+ const variation = decisionService . getVariationForFeature ( config , feature , user ) ;
1022
+
1023
+ expect ( variation . result ) . toEqual ( {
1024
+ experiment : null ,
1025
+ variation : null ,
1026
+ decisionSource : DECISION_SOURCES . ROLLOUT ,
1027
+ } ) ;
1028
+
1029
+ expect ( resolveVariationSpy ) . toHaveBeenCalledTimes ( 3 ) ;
1030
+ expect ( resolveVariationSpy ) . toHaveBeenNthCalledWith ( 1 ,
1031
+ config , config . experimentKeyMap [ 'exp_1' ] , user , false , expect . anything ( ) ) ;
1032
+ expect ( resolveVariationSpy ) . toHaveBeenNthCalledWith ( 2 ,
1033
+ config , config . experimentKeyMap [ 'exp_2' ] , user , false , expect . anything ( ) ) ;
1034
+ expect ( resolveVariationSpy ) . toHaveBeenNthCalledWith ( 3 ,
1035
+ config , config . experimentKeyMap [ 'exp_3' ] , user , false , expect . anything ( ) ) ;
1036
+
1037
+ expect ( mockBucket ) . toHaveBeenCalledTimes ( 0 ) ;
1038
+ } ) ;
855
1039
} ) ;
1040
+
1041
+ // describe('getVariationsForFeatureList', () => {
1042
+
1043
+ // });
856
1044
} ) ;
0 commit comments