13
13
* See the License for the specific language governing permissions and
14
14
* limitations under the License.
15
15
*/
16
- import { describe , it , expect , beforeEach , afterEach , vi , assert } from 'vitest' ;
16
+ import { describe , it , expect , beforeEach , afterEach , vi , assert , Mock } from 'vitest' ;
17
17
import { forEach , cloneDeep } from 'lodash' ;
18
18
import { sprintf } from '../utils/fns' ;
19
19
import fns from '../utils/fns' ;
@@ -33,6 +33,7 @@ import {
33
33
import exp from 'constants' ;
34
34
import { VariableType } from '../shared_types' ;
35
35
import { OptimizelyError } from '../error/optimizly_error' ;
36
+ import { J } from 'vitest/dist/chunks/environment.0M5R1SX_.js' ;
36
37
37
38
const createLogger = ( ...args : any ) => ( {
38
39
debug : ( ) => { } ,
@@ -162,17 +163,17 @@ describe('createProjectConfig - feature management', () => {
162
163
expect ( configObj . rolloutIdMap ) . toEqual ( testDatafile . datafileWithFeaturesExpectedData . rolloutIdMap ) ;
163
164
} ) ;
164
165
165
- it ( 'creates a variationVariableUsageMap from rollouts and experiments with features in the datafile' , ( ) => {
166
+ it ( 'should create a variationVariableUsageMap from rollouts and experiments with features in the datafile' , ( ) => {
166
167
expect ( configObj . variationVariableUsageMap ) . toEqual (
167
168
testDatafile . datafileWithFeaturesExpectedData . variationVariableUsageMap
168
169
) ;
169
170
} ) ;
170
171
171
- it ( 'creates a featureKeyMap from features in the datafile' , ( ) => {
172
+ it ( 'should create a featureKeyMap from features in the datafile' , ( ) => {
172
173
expect ( configObj . featureKeyMap ) . toEqual ( testDatafile . datafileWithFeaturesExpectedData . featureKeyMap ) ;
173
174
} ) ;
174
175
175
- it ( 'adds variations from rollout experiements to the variationKeyMap' , ( ) => {
176
+ it ( 'should add variations from rollout experiements to the variationKeyMap' , ( ) => {
176
177
expect ( configObj . variationIdMap [ '594032' ] ) . toEqual ( {
177
178
variables : [
178
179
{ value : 'true' , id : '4919852825313280' } ,
@@ -232,7 +233,7 @@ describe('createProjectConfig - flag variations', () => {
232
233
configObj = projectConfig . createProjectConfig ( testDatafile . getTestDecideProjectConfig ( ) ) ;
233
234
} ) ;
234
235
235
- it ( 'it should populate flagVariationsMap correctly' , function ( ) {
236
+ it ( 'should populate flagVariationsMap correctly' , function ( ) {
236
237
const allVariationsForFlag = configObj . flagVariationsMap ;
237
238
const feature1Variations = allVariationsForFlag . feature_1 ;
238
239
const feature2Variations = allVariationsForFlag . feature_2 ;
@@ -595,7 +596,7 @@ describe('getVariableValueForVariation', () => {
595
596
vi . restoreAllMocks ( ) ;
596
597
} ) ;
597
598
598
- it ( 'returns a value for a valid variation and variable' , ( ) => {
599
+ it ( 'should return a value for a valid variation and variable' , ( ) => {
599
600
const variation = configObj . variationIdMap [ '594096' ] ;
600
601
let variable = configObj . featureKeyMap . test_feature_for_experiment . variableKeyMap . num_buttons ;
601
602
let result = projectConfig . getVariableValueForVariation ( configObj , variable , variation , featureManagementLogger ) ;
@@ -617,7 +618,7 @@ describe('getVariableValueForVariation', () => {
617
618
expect ( result ) . toBe ( '20.25' ) ;
618
619
} ) ;
619
620
620
- it ( 'returns null for a null variation' , ( ) => {
621
+ it ( 'should return null for a null variation' , ( ) => {
621
622
const variation = null ;
622
623
const variable = configObj . featureKeyMap . test_feature_for_experiment . variableKeyMap . num_buttons ;
623
624
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -627,7 +628,7 @@ describe('getVariableValueForVariation', () => {
627
628
expect ( result ) . toBe ( null ) ;
628
629
} ) ;
629
630
630
- it ( 'returns null for a null variable' , ( ) => {
631
+ it ( 'should return null for a null variable' , ( ) => {
631
632
const variation = configObj . variationIdMap [ '594096' ] ;
632
633
const variable = null ;
633
634
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -637,7 +638,7 @@ describe('getVariableValueForVariation', () => {
637
638
expect ( result ) . toBe ( null ) ;
638
639
} ) ;
639
640
640
- it ( 'returns null for a null variation and null variable' , ( ) => {
641
+ it ( 'should return null for a null variation and null variable' , ( ) => {
641
642
const variation = null ;
642
643
const variable = null ;
643
644
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -647,7 +648,7 @@ describe('getVariableValueForVariation', () => {
647
648
expect ( result ) . toBe ( null ) ;
648
649
} ) ;
649
650
650
- it ( 'returns null for a variation whose id is not in the datafile' , ( ) => {
651
+ it ( 'should return null for a variation whose id is not in the datafile' , ( ) => {
651
652
const variation = {
652
653
key : 'some_variation' ,
653
654
id : '999999999999' ,
@@ -661,7 +662,7 @@ describe('getVariableValueForVariation', () => {
661
662
expect ( result ) . toBe ( null ) ;
662
663
} ) ;
663
664
664
- it ( 'returns null if the variation does not have a value for this variable' , ( ) => {
665
+ it ( 'should return null if the variation does not have a value for this variable' , ( ) => {
665
666
const variation = configObj . variationIdMap [ '595008' ] ; // This variation has no variable values associated with it
666
667
const variable = configObj . featureKeyMap . test_feature_for_experiment . variableKeyMap . num_buttons ;
667
668
const result = projectConfig . getVariableValueForVariation ( configObj , variable , variation , featureManagementLogger ) ;
@@ -687,7 +688,7 @@ describe('getTypeCastValue', () => {
687
688
vi . restoreAllMocks ( ) ;
688
689
} ) ;
689
690
690
- it ( 'can cast a boolean' , ( ) => {
691
+ it ( 'should cast a boolean' , ( ) => {
691
692
let result = projectConfig . getTypeCastValue (
692
693
'true' ,
693
694
FEATURE_VARIABLE_TYPES . BOOLEAN as VariableType ,
@@ -705,7 +706,7 @@ describe('getTypeCastValue', () => {
705
706
expect ( result ) . toBe ( false ) ;
706
707
} ) ;
707
708
708
- it ( 'can cast an integer' , ( ) => {
709
+ it ( 'should cast an integer' , ( ) => {
709
710
let result = projectConfig . getTypeCastValue (
710
711
'50' ,
711
712
FEATURE_VARIABLE_TYPES . INTEGER as VariableType ,
@@ -731,7 +732,7 @@ describe('getTypeCastValue', () => {
731
732
expect ( result ) . toBe ( 0 ) ;
732
733
} ) ;
733
734
734
- it ( 'can cast a double' , ( ) => {
735
+ it ( 'should cast a double' , ( ) => {
735
736
let result = projectConfig . getTypeCastValue (
736
737
'89.99' ,
737
738
FEATURE_VARIABLE_TYPES . DOUBLE as VariableType ,
@@ -765,7 +766,7 @@ describe('getTypeCastValue', () => {
765
766
expect ( result ) . toBe ( 10 ) ;
766
767
} ) ;
767
768
768
- it ( 'can return a string unmodified' , ( ) => {
769
+ it ( 'should return a string unmodified' , ( ) => {
769
770
const result = projectConfig . getTypeCastValue (
770
771
'message' ,
771
772
FEATURE_VARIABLE_TYPES . STRING as VariableType ,
@@ -775,7 +776,7 @@ describe('getTypeCastValue', () => {
775
776
expect ( result ) . toBe ( 'message' ) ;
776
777
} ) ;
777
778
778
- it ( 'returns null and logs an error for an invalid boolean' , ( ) => {
779
+ it ( 'should return null and logs an error for an invalid boolean' , ( ) => {
779
780
const result = projectConfig . getTypeCastValue (
780
781
'notabool' ,
781
782
FEATURE_VARIABLE_TYPES . BOOLEAN as VariableType ,
@@ -786,7 +787,7 @@ describe('getTypeCastValue', () => {
786
787
expect ( featureManagementLogger . error ) . toHaveBeenCalledWith ( UNABLE_TO_CAST_VALUE , 'notabool' , 'boolean' ) ;
787
788
} ) ;
788
789
789
- it ( 'returns null and logs an error for an invalid integer' , ( ) => {
790
+ it ( 'should return null and logs an error for an invalid integer' , ( ) => {
790
791
const result = projectConfig . getTypeCastValue (
791
792
'notanint' ,
792
793
FEATURE_VARIABLE_TYPES . INTEGER as VariableType ,
@@ -797,7 +798,7 @@ describe('getTypeCastValue', () => {
797
798
expect ( featureManagementLogger . error ) . toHaveBeenCalledWith ( UNABLE_TO_CAST_VALUE , 'notanint' , 'integer' ) ;
798
799
} ) ;
799
800
800
- it ( 'returns null and logs an error for an invalid double' , ( ) => {
801
+ it ( 'should return null and logs an error for an invalid double' , ( ) => {
801
802
const result = projectConfig . getTypeCastValue (
802
803
'notadouble' ,
803
804
FEATURE_VARIABLE_TYPES . DOUBLE as VariableType ,
@@ -878,21 +879,21 @@ describe('getExperimentAudienceConditions', () => {
878
879
} ) ;
879
880
880
881
describe ( 'isFeatureExperiment' , ( ) => {
881
- it ( 'returns true for a feature test' , ( ) => {
882
+ it ( 'should return true for a feature test' , ( ) => {
882
883
const config = projectConfig . createProjectConfig ( testDatafile . getTestProjectConfigWithFeatures ( ) ) ;
883
884
const result = projectConfig . isFeatureExperiment ( config , '594098' ) ; // id of 'testing_my_feature'
884
885
885
886
expect ( result ) . toBe ( true ) ;
886
887
} ) ;
887
888
888
- it ( 'returns false for an A/B test' , ( ) => {
889
+ it ( 'should return false for an A/B test' , ( ) => {
889
890
const config = projectConfig . createProjectConfig ( testDatafile . getTestProjectConfig ( ) ) ;
890
891
const result = projectConfig . isFeatureExperiment ( config , '111127' ) ; // id of 'testExperiment'
891
892
892
893
expect ( result ) . toBe ( false ) ;
893
894
} ) ;
894
895
895
- it ( 'returns true for a feature test in a mutex group' , ( ) => {
896
+ it ( 'should return true for a feature test in a mutex group' , ( ) => {
896
897
const config = projectConfig . createProjectConfig ( testDatafile . getMutexFeatureTestsConfig ( ) ) ;
897
898
let result = projectConfig . isFeatureExperiment ( config , '17128410791' ) ; // id of 'f_test1'
898
899
@@ -905,7 +906,7 @@ describe('isFeatureExperiment', () => {
905
906
} ) ;
906
907
907
908
describe ( 'getAudienceSegments' , ( ) => {
908
- it ( 'returns all qualified segments from an audience' , ( ) => {
909
+ it ( 'should return all qualified segments from an audience' , ( ) => {
909
910
const dummyQualifiedAudienceJson = {
910
911
id : '13389142234' ,
911
912
conditions : [
@@ -984,7 +985,7 @@ describe('integrations: with segments', () => {
984
985
} ) ;
985
986
} ) ;
986
987
987
- describe ( 'withoutSegments ' , ( ) => {
988
+ describe ( 'integrations: without segments ' , ( ) => {
988
989
let config : ProjectConfig ;
989
990
beforeEach ( ( ) => {
990
991
config = projectConfig . createProjectConfig ( testDatafile . getOdpIntegratedConfigWithoutSegments ( ) ) ;
@@ -1006,3 +1007,128 @@ describe('withoutSegments', () => {
1006
1007
expect ( config . odpIntegrationConfig . odpConfig . segmentsToCheck ) . toEqual ( [ ] ) ;
1007
1008
} ) ;
1008
1009
} ) ;
1010
+
1011
+ describe ( 'without valid integration key' , ( ) => {
1012
+ it ( 'should throw an error when parsing the project config due to integrations not containing a key' , ( ) => {
1013
+ const odpIntegratedConfigWithoutKey = testDatafile . getOdpIntegratedConfigWithoutKey ( ) ;
1014
+
1015
+ expect ( ( ) => projectConfig . createProjectConfig ( odpIntegratedConfigWithoutKey ) ) . toThrowError ( OptimizelyError ) ;
1016
+ } ) ;
1017
+ } ) ;
1018
+
1019
+ describe ( 'without integrations' , ( ) => {
1020
+ let config : ProjectConfig ;
1021
+
1022
+ beforeEach ( ( ) => {
1023
+ const odpIntegratedConfigWithSegments = testDatafile . getOdpIntegratedConfigWithSegments ( ) ;
1024
+ const noIntegrationsConfigWithSegments = { ...odpIntegratedConfigWithSegments , integrations : [ ] } ;
1025
+ config = projectConfig . createProjectConfig ( noIntegrationsConfigWithSegments ) ;
1026
+ } ) ;
1027
+
1028
+ it ( 'should convert integrations from the datafile into the project config' , ( ) => {
1029
+ expect ( config . integrations . length ) . toBe ( 0 ) ;
1030
+ } ) ;
1031
+
1032
+ it ( 'should populate odpIntegrationConfig' , ( ) => {
1033
+ expect ( config . odpIntegrationConfig . integrated ) . toBe ( false ) ;
1034
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
1035
+ // @ts -ignore
1036
+ expect ( config . odpIntegrationConfig . odpConfig ) . toBeUndefined ( ) ;
1037
+ } ) ;
1038
+ } ) ;
1039
+
1040
+ describe ( 'tryCreatingProjectConfig' , ( ) => {
1041
+ let mockJsonSchemaValidator : Mock ;
1042
+ beforeEach ( ( ) => {
1043
+ mockJsonSchemaValidator = vi . fn ( ) . mockReturnValue ( true ) ;
1044
+ vi . spyOn ( configValidator , 'validateDatafile' ) . mockReturnValue ( true ) ;
1045
+ vi . spyOn ( logger , 'error' ) ;
1046
+ } ) ;
1047
+
1048
+ afterEach ( ( ) => {
1049
+ vi . restoreAllMocks ( ) ;
1050
+ } ) ;
1051
+
1052
+ it ( 'returns a project config object created by createProjectConfig when all validation is applied and there are no errors' , ( ) => {
1053
+ const configDatafile = {
1054
+ foo : 'bar' ,
1055
+ experiments : [ { key : 'a' } , { key : 'b' } ] ,
1056
+ } ;
1057
+
1058
+ vi . spyOn ( configValidator , 'validateDatafile' ) . mockReturnValueOnce ( configDatafile ) ;
1059
+
1060
+ const configObj = {
1061
+ foo : 'bar' ,
1062
+ experimentKeyMap : {
1063
+ a : { key : 'a' , variationKeyMap : { } } ,
1064
+ b : { key : 'b' , variationKeyMap : { } } ,
1065
+ } ,
1066
+ } ;
1067
+
1068
+ // stubJsonSchemaValidator.returns(true);
1069
+ mockJsonSchemaValidator . mockReturnValueOnce ( true ) ;
1070
+
1071
+ const result = projectConfig . tryCreatingProjectConfig ( {
1072
+ datafile : configDatafile ,
1073
+ jsonSchemaValidator : mockJsonSchemaValidator ,
1074
+ logger : logger ,
1075
+ } ) ;
1076
+
1077
+ expect ( result ) . toMatchObject ( configObj ) ;
1078
+ } ) ;
1079
+
1080
+ it ( 'throws an error when validateDatafile throws' , function ( ) {
1081
+ vi . spyOn ( configValidator , 'validateDatafile' ) . mockImplementationOnce ( ( ) => {
1082
+ throw new Error ( ) ;
1083
+ } ) ;
1084
+ mockJsonSchemaValidator . mockReturnValueOnce ( true ) ;
1085
+
1086
+ expect ( ( ) =>
1087
+ projectConfig . tryCreatingProjectConfig ( {
1088
+ datafile : { foo : 'bar' } ,
1089
+ jsonSchemaValidator : mockJsonSchemaValidator ,
1090
+ logger : logger ,
1091
+ } )
1092
+ ) . toThrowError ( ) ;
1093
+ } ) ;
1094
+
1095
+ it ( 'throws an error when jsonSchemaValidator.validate throws' , function ( ) {
1096
+ vi . spyOn ( configValidator , 'validateDatafile' ) . mockReturnValueOnce ( true ) ;
1097
+ mockJsonSchemaValidator . mockImplementationOnce ( ( ) => {
1098
+ throw new Error ( ) ;
1099
+ } )
1100
+
1101
+ expect ( ( ) =>
1102
+ projectConfig . tryCreatingProjectConfig ( {
1103
+ datafile : { foo : 'bar' } ,
1104
+ jsonSchemaValidator : mockJsonSchemaValidator ,
1105
+ logger : logger ,
1106
+ } )
1107
+ ) . toThrowError ( ) ;
1108
+ } ) ;
1109
+
1110
+ it ( 'skips json validation when jsonSchemaValidator is not provided' , function ( ) {
1111
+ const configDatafile = {
1112
+ foo : 'bar' ,
1113
+ experiments : [ { key : 'a' } , { key : 'b' } ] ,
1114
+ } ;
1115
+
1116
+ vi . spyOn ( configValidator , 'validateDatafile' ) . mockReturnValueOnce ( configDatafile ) ;
1117
+
1118
+ const configObj = {
1119
+ foo : 'bar' ,
1120
+ experimentKeyMap : {
1121
+ a : { key : 'a' , variationKeyMap : { } } ,
1122
+ b : { key : 'b' , variationKeyMap : { } } ,
1123
+ } ,
1124
+ } ;
1125
+
1126
+ const result = projectConfig . tryCreatingProjectConfig ( {
1127
+ datafile : configDatafile ,
1128
+ logger : logger ,
1129
+ } ) ;
1130
+
1131
+ expect ( result ) . toMatchObject ( configObj ) ;
1132
+ expect ( logger . error ) . not . toHaveBeenCalled ( ) ;
1133
+ } ) ;
1134
+ } ) ;
0 commit comments