@@ -62,6 +62,7 @@ public class OptimizelyTest
6262 const string FEATUREVARIABLE_INTEGERTYPE = "integer" ;
6363 const string FEATUREVARIABLE_DOUBLETYPE = "double" ;
6464 const string FEATUREVARIABLE_STRINGTYPE = "string" ;
65+ const string FEATUREVARIABLE_JSONTYPE = "json" ;
6566
6667 #region Test Life Cycle
6768 [ SetUp ]
@@ -197,6 +198,7 @@ public void TestInvalidInstanceLogMessages()
197198 Assert . IsNull ( optimizely . GetFeatureVariableString ( string . Empty , string . Empty , string . Empty ) ) ;
198199 Assert . IsNull ( optimizely . GetFeatureVariableDouble ( string . Empty , string . Empty , string . Empty ) ) ;
199200 Assert . IsNull ( optimizely . GetFeatureVariableInteger ( string . Empty , string . Empty , string . Empty ) ) ;
201+ Assert . IsNull ( optimizely . GetFeatureVariableJSON ( string . Empty , string . Empty , string . Empty ) ) ;
200202
201203 LoggerMock . Verify ( log => log . Log ( LogLevel . ERROR , "Provided 'datafile' has invalid schema." ) , Times . Once ) ;
202204 LoggerMock . Verify ( log => log . Log ( LogLevel . ERROR , "Datafile has invalid format. Failing 'GetVariation'." ) , Times . Once ) ;
@@ -208,6 +210,7 @@ public void TestInvalidInstanceLogMessages()
208210 LoggerMock . Verify ( log => log . Log ( LogLevel . ERROR , "Datafile has invalid format. Failing 'GetFeatureVariableString'." ) , Times . Once ) ;
209211 LoggerMock . Verify ( log => log . Log ( LogLevel . ERROR , "Datafile has invalid format. Failing 'GetFeatureVariableDouble'." ) , Times . Once ) ;
210212 LoggerMock . Verify ( log => log . Log ( LogLevel . ERROR , "Datafile has invalid format. Failing 'GetFeatureVariableInteger'." ) , Times . Once ) ;
213+ LoggerMock . Verify ( log => log . Log ( LogLevel . ERROR , "Datafile has invalid format. Failing 'GetFeatureVariableJSON'." ) , Times . Once ) ;
211214
212215 }
213216 [ Test ]
@@ -1245,6 +1248,36 @@ public void TestGetFeatureVariableStringFRCulture()
12451248 Assert . AreEqual ( stringValueFR , "cta_1" ) ;
12461249 }
12471250
1251+ [ Test ]
1252+ public void TestGetFeatureVariableJSONFRCulture ( )
1253+ {
1254+ var fallbackConfigManager = new FallbackProjectConfigManager ( Config ) ;
1255+
1256+ var expectedDict = new Dictionary < string , object > ( )
1257+ {
1258+ { "int_var" , 1 } ,
1259+ { "boolean_key" , false }
1260+ } ;
1261+
1262+ SetCulture ( "en-US" ) ;
1263+ var optimizely = new Optimizely ( fallbackConfigManager ) ;
1264+
1265+ var optimizelyJsonValue = optimizely . GetFeatureVariableJSON ( "string_single_variable_feature" , "json_var" , "testUser1" ) ;
1266+
1267+ Assert . IsTrue ( TestData . CompareObjects ( optimizelyJsonValue . ToDictionary ( ) , expectedDict ) ) ;
1268+ Assert . AreEqual ( optimizelyJsonValue . GetValue < long > ( "int_var" ) , 1 ) ;
1269+ Assert . AreEqual ( optimizelyJsonValue . GetValue < bool > ( "boolean_key" ) , false ) ;
1270+ Assert . IsTrue ( TestData . CompareObjects ( optimizelyJsonValue . GetValue < object > ( "" ) , expectedDict ) ) ;
1271+
1272+ SetCulture ( "fr-FR" ) ;
1273+ var optimizelyJsonValueFR = optimizely . GetFeatureVariableJSON ( "string_single_variable_feature" , "json_var" , "testUser1" ) ;
1274+
1275+ Assert . IsTrue ( TestData . CompareObjects ( optimizelyJsonValue . ToDictionary ( ) , expectedDict ) ) ;
1276+ Assert . AreEqual ( optimizelyJsonValueFR . GetValue < long > ( "int_var" ) , 1 ) ;
1277+ Assert . AreEqual ( optimizelyJsonValueFR . GetValue < bool > ( "boolean_key" ) , false ) ;
1278+ Assert . IsTrue ( TestData . CompareObjects ( optimizelyJsonValue . GetValue < object > ( "" ) , expectedDict ) ) ;
1279+ }
1280+
12481281 [ Test ]
12491282 public void TestGetFeatureVariableDoubleReturnsCorrectValue ( )
12501283 {
@@ -1316,6 +1349,74 @@ public void TestGetFeatureVariableStringReturnsCorrectValue()
13161349 Assert . Null ( OptimizelyMock . Object . GetFeatureVariableString ( featureKey , variableKeyNull , TestUserId , null ) ) ;
13171350 }
13181351
1352+ [ Test ]
1353+ public void TestGetFeatureVariableJSONReturnsCorrectValue ( )
1354+ {
1355+ var featureKey = "featureKey" ;
1356+ var variableKeyString = "varJSONString1" ;
1357+ var variableKeyIntString = "varJSONString2" ;
1358+ var variableKeyDouble = "varJSONDouble" ;
1359+ var variableKeyNull = "varNull" ;
1360+ var featureVariableType = "json" ;
1361+
1362+ OptimizelyMock . Setup ( om => om . GetFeatureVariableValueForType < OptimizelyJson > ( It . IsAny < string > ( ) , variableKeyString , It . IsAny < string > ( ) ,
1363+ It . IsAny < UserAttributes > ( ) , featureVariableType ) ) . Returns ( new OptimizelyJson ( "{\" string\" : \" Test String\" }" , ErrorHandlerMock . Object , LoggerMock . Object ) ) ;
1364+ Assert . AreEqual ( "Test String" , OptimizelyMock . Object . GetFeatureVariableJSON ( featureKey , variableKeyString , TestUserId , null ) . GetValue < string > ( "string" ) ) ;
1365+
1366+ OptimizelyMock . Setup ( om => om . GetFeatureVariableValueForType < OptimizelyJson > ( It . IsAny < string > ( ) , variableKeyIntString , It . IsAny < string > ( ) ,
1367+ It . IsAny < UserAttributes > ( ) , featureVariableType ) ) . Returns ( new OptimizelyJson ( "{ \" integer\" : 123 }" , ErrorHandlerMock . Object , LoggerMock . Object ) ) ;
1368+ Assert . AreEqual ( 123 , OptimizelyMock . Object . GetFeatureVariableJSON ( featureKey , variableKeyIntString , TestUserId , null ) . GetValue < long > ( "integer" ) ) ;
1369+
1370+ OptimizelyMock . Setup ( om => om . GetFeatureVariableValueForType < OptimizelyJson > ( It . IsAny < string > ( ) , variableKeyDouble , It . IsAny < string > ( ) ,
1371+ It . IsAny < UserAttributes > ( ) , featureVariableType ) ) . Returns ( new OptimizelyJson ( "{ \" double\" : 123.28 }" , ErrorHandlerMock . Object , LoggerMock . Object ) ) ;
1372+ Assert . AreEqual ( 123.28 , OptimizelyMock . Object . GetFeatureVariableJSON ( featureKey , variableKeyDouble , TestUserId , null ) . GetValue < double > ( "double" ) ) ;
1373+
1374+ OptimizelyMock . Setup ( om => om . GetFeatureVariableValueForType < OptimizelyJson > ( It . IsAny < string > ( ) , variableKeyNull , It . IsAny < string > ( ) ,
1375+ It . IsAny < UserAttributes > ( ) , featureVariableType ) ) . Returns < OptimizelyJson > ( null ) ;
1376+ Assert . Null ( OptimizelyMock . Object . GetFeatureVariableJSON ( featureKey , variableKeyNull , TestUserId , null ) ) ;
1377+ }
1378+
1379+ [ Test ]
1380+ public void TestGetFeatureVariableJSONReturnsCorrectValueWhenInitializedUsingDictionary ( )
1381+ {
1382+ var featureKey = "featureKey" ;
1383+ var variableKeyString = "varJSONString1" ;
1384+ var variableKeyIntString = "varJSONString2" ;
1385+ var variableKeyDouble = "varJSONDouble" ;
1386+ var variableKeyBoolean = "varJSONBoolean" ;
1387+ var variableKeyNull = "varNull" ;
1388+ var featureVariableType = "json" ;
1389+
1390+ var expectedStringDict = new Dictionary < string , object > ( ) { { "string" , "Test String" } } ;
1391+ var expectedIntegerDict = new Dictionary < string , object > ( ) { { "integer" , 123 } } ;
1392+ var expectedDoubleDict = new Dictionary < string , object > ( ) { { "double" , 123.28 } } ;
1393+ var expectedBooleanDict = new Dictionary < string , object > ( ) { { "boolean" , true } } ;
1394+
1395+ OptimizelyMock . Setup ( om => om . GetFeatureVariableValueForType < OptimizelyJson > ( It . IsAny < string > ( ) , variableKeyString , It . IsAny < string > ( ) ,
1396+ It . IsAny < UserAttributes > ( ) , featureVariableType ) ) . Returns ( new OptimizelyJson ( expectedStringDict , ErrorHandlerMock . Object , LoggerMock . Object ) ) ;
1397+ Assert . IsTrue ( TestData . CompareObjects ( expectedStringDict , OptimizelyMock . Object . GetFeatureVariableJSON ( featureKey , variableKeyString , TestUserId , null ) . ToDictionary ( ) ) ) ;
1398+ Assert . AreEqual ( "Test String" , OptimizelyMock . Object . GetFeatureVariableJSON ( featureKey , variableKeyString , TestUserId , null ) . GetValue < string > ( "string" ) ) ;
1399+
1400+ OptimizelyMock . Setup ( om => om . GetFeatureVariableValueForType < OptimizelyJson > ( It . IsAny < string > ( ) , variableKeyIntString , It . IsAny < string > ( ) ,
1401+ It . IsAny < UserAttributes > ( ) , featureVariableType ) ) . Returns ( new OptimizelyJson ( expectedIntegerDict , ErrorHandlerMock . Object , LoggerMock . Object ) ) ;
1402+ Assert . IsTrue ( TestData . CompareObjects ( expectedIntegerDict , OptimizelyMock . Object . GetFeatureVariableJSON ( featureKey , variableKeyIntString , TestUserId , null ) . ToDictionary ( ) ) ) ;
1403+ Assert . AreEqual ( 123 , OptimizelyMock . Object . GetFeatureVariableJSON ( featureKey , variableKeyIntString , TestUserId , null ) . GetValue < long > ( "integer" ) ) ;
1404+
1405+ OptimizelyMock . Setup ( om => om . GetFeatureVariableValueForType < OptimizelyJson > ( It . IsAny < string > ( ) , variableKeyDouble , It . IsAny < string > ( ) ,
1406+ It . IsAny < UserAttributes > ( ) , featureVariableType ) ) . Returns ( new OptimizelyJson ( expectedDoubleDict , ErrorHandlerMock . Object , LoggerMock . Object ) ) ;
1407+ Assert . IsTrue ( TestData . CompareObjects ( expectedDoubleDict , OptimizelyMock . Object . GetFeatureVariableJSON ( featureKey , variableKeyDouble , TestUserId , null ) . ToDictionary ( ) ) ) ;
1408+ Assert . AreEqual ( 123.28 , OptimizelyMock . Object . GetFeatureVariableJSON ( featureKey , variableKeyDouble , TestUserId , null ) . GetValue < double > ( "double" ) ) ;
1409+
1410+ OptimizelyMock . Setup ( om => om . GetFeatureVariableValueForType < OptimizelyJson > ( It . IsAny < string > ( ) , variableKeyBoolean , It . IsAny < string > ( ) ,
1411+ It . IsAny < UserAttributes > ( ) , featureVariableType ) ) . Returns ( new OptimizelyJson ( expectedBooleanDict , ErrorHandlerMock . Object , LoggerMock . Object ) ) ;
1412+ Assert . IsTrue ( TestData . CompareObjects ( expectedBooleanDict , OptimizelyMock . Object . GetFeatureVariableJSON ( featureKey , variableKeyBoolean , TestUserId , null ) . ToDictionary ( ) ) ) ;
1413+ Assert . AreEqual ( true , OptimizelyMock . Object . GetFeatureVariableJSON ( featureKey , variableKeyBoolean , TestUserId , null ) . GetValue < bool > ( "boolean" ) ) ;
1414+
1415+ OptimizelyMock . Setup ( om => om . GetFeatureVariableValueForType < OptimizelyJson > ( It . IsAny < string > ( ) , variableKeyNull , It . IsAny < string > ( ) ,
1416+ It . IsAny < UserAttributes > ( ) , featureVariableType ) ) . Returns < OptimizelyJson > ( null ) ;
1417+ Assert . Null ( OptimizelyMock . Object . GetFeatureVariableJSON ( featureKey , variableKeyNull , TestUserId , null ) ) ;
1418+ }
1419+
13191420 #region Feature Toggle Tests
13201421
13211422 [ Test ]
@@ -1444,6 +1545,70 @@ public void TestGetFeatureVariableBooleanReturnsRightValueWhenUserBuckedIntoRoll
14441545 LoggerMock . Verify ( l => l . Log ( LogLevel . INFO , $@ "Returning variable value ""true"" for variation ""{ variation . Key } "" of feature flag ""{ featureKey } "".") ) ;
14451546 }
14461547
1548+ [ Test ]
1549+ public void TestGetFeatureVariableJSONReturnsRightValueWhenUserBucketIntoRolloutAndVariationIsToggleOn ( )
1550+ {
1551+ var featureKey = "string_single_variable_feature" ;
1552+ var featureFlag = Config . GetFeatureFlagFromKey ( featureKey ) ;
1553+ var variableKey = "json_var" ;
1554+ var expectedStringValue = "cta_4" ;
1555+ var expectedIntValue = 4 ;
1556+ var experiment = Config . GetRolloutFromId ( "166661" ) . Experiments [ 0 ] ;
1557+ var variation = Config . GetVariationFromKey ( experiment . Key , "177775" ) ;
1558+ var decision = new FeatureDecision ( experiment , variation , FeatureDecision . DECISION_SOURCE_ROLLOUT ) ;
1559+ var userAttributes = new UserAttributes
1560+ {
1561+ { "device_type" , "iPhone" } ,
1562+ { "company" , "Optimizely" } ,
1563+ { "location" , "San Francisco" }
1564+ } ;
1565+
1566+ DecisionServiceMock . Setup ( ds => ds . GetVariationForFeature ( featureFlag , TestUserId , Config , userAttributes ) ) . Returns ( decision ) ;
1567+
1568+ var optly = Helper . CreatePrivateOptimizely ( ) ;
1569+
1570+ optly . SetFieldOrProperty ( "ProjectConfigManager" , ConfigManager ) ;
1571+ optly . SetFieldOrProperty ( "DecisionService" , DecisionServiceMock . Object ) ;
1572+
1573+ var variableValue = ( OptimizelyJson ) optly . Invoke ( "GetFeatureVariableJSON" , featureKey , variableKey , TestUserId , userAttributes ) ;
1574+ Assert . AreEqual ( expectedIntValue , variableValue . GetValue < long > ( "int_var" ) ) ;
1575+ Assert . AreEqual ( expectedStringValue , variableValue . GetValue < string > ( "string_var" ) ) ;
1576+
1577+ LoggerMock . Verify ( l => l . Log ( LogLevel . INFO , $@ "Returning variable value ""{ variableValue } "" for variation ""{ variation . Key } "" of feature flag ""{ featureKey } "".") ) ;
1578+ }
1579+
1580+ [ Test ]
1581+ public void TestGetFeatureVariableJSONReturnsRightValueWhenUserBucketIntoRolloutAndVariationIsToggleOnTypeIsJson ( )
1582+ {
1583+ var featureKey = "string_single_variable_feature" ;
1584+ var featureFlag = Config . GetFeatureFlagFromKey ( featureKey ) ;
1585+ var variableKey = "true_json_var" ;
1586+ var expectedStringValue = "cta_5" ;
1587+ var expectedIntValue = 5 ;
1588+ var experiment = Config . GetRolloutFromId ( "166661" ) . Experiments [ 0 ] ;
1589+ var variation = Config . GetVariationFromKey ( experiment . Key , "177775" ) ;
1590+ var decision = new FeatureDecision ( experiment , variation , FeatureDecision . DECISION_SOURCE_ROLLOUT ) ;
1591+ var userAttributes = new UserAttributes
1592+ {
1593+ { "device_type" , "iPhone" } ,
1594+ { "company" , "Optimizely" } ,
1595+ { "location" , "San Francisco" }
1596+ } ;
1597+
1598+ DecisionServiceMock . Setup ( ds => ds . GetVariationForFeature ( featureFlag , TestUserId , Config , userAttributes ) ) . Returns ( decision ) ;
1599+
1600+ var optly = Helper . CreatePrivateOptimizely ( ) ;
1601+
1602+ optly . SetFieldOrProperty ( "ProjectConfigManager" , ConfigManager ) ;
1603+ optly . SetFieldOrProperty ( "DecisionService" , DecisionServiceMock . Object ) ;
1604+
1605+ var variableValue = ( OptimizelyJson ) optly . Invoke ( "GetFeatureVariableJSON" , featureKey , variableKey , TestUserId , userAttributes ) ;
1606+ Assert . AreEqual ( expectedIntValue , variableValue . GetValue < long > ( "int_var" ) ) ;
1607+ Assert . AreEqual ( expectedStringValue , variableValue . GetValue < string > ( "string_var" ) ) ;
1608+
1609+ LoggerMock . Verify ( l => l . Log ( LogLevel . INFO , $@ "Returning variable value ""{ variableValue } "" for variation ""{ variation . Key } "" of feature flag ""{ featureKey } "".") ) ;
1610+ }
1611+
14471612 [ Test ]
14481613 public void TestGetFeatureVariableStringReturnsRightValueWhenUserBuckedIntoRolloutAndVariationIsToggleOn ( )
14491614 {
@@ -1606,6 +1771,7 @@ public void TestGetFeatureVariableValueForTypeGivenInvalidVariableType()
16061771 Assert . IsNull ( Optimizely . GetFeatureVariableValueForType < bool ? > ( "boolean_single_variable_feature" , "boolean_variable" , TestUserId , null , variableTypeDouble ) ) ;
16071772 Assert . IsNull ( Optimizely . GetFeatureVariableValueForType < int ? > ( "integer_single_variable_feature" , "integer_variable" , TestUserId , null , variableTypeString ) ) ;
16081773 Assert . IsNull ( Optimizely . GetFeatureVariableValueForType < string > ( "string_single_variable_feature" , "string_variable" , TestUserId , null , variableTypeInt ) ) ;
1774+ Assert . IsNull ( Optimizely . GetFeatureVariableValueForType < OptimizelyJson > ( "string_single_variable_feature" , "json_var" , TestUserId , null , variableTypeInt ) ) ;
16091775
16101776 LoggerMock . Verify ( l => l . Log ( LogLevel . ERROR ,
16111777 $@ "Variable is of type ""double"", but you requested it as type ""{ variableTypeBool } "".") ) ;
@@ -1623,8 +1789,9 @@ public void TestUnsupportedVariableType()
16231789 var featureVariableStringRandomType = Optimizely . GetFeatureVariableString ( "" , "any_key" , TestUserId ) ;
16241790 Assert . IsNull ( featureVariableStringRandomType ) ;
16251791
1626- var featureVariableStringJsonType = Optimizely . GetFeatureVariableString ( "unsupported_variabletype" , "string_json_key" , TestUserId ) ;
1627- Assert . AreEqual ( featureVariableStringJsonType , "{\" myvalue\" : \" jsonValue\" }" ) ;
1792+ // This is to test that only json subtype is parsing and all other will subtype will be stringify
1793+ var featureVariableStringRegexSubType = Optimizely . GetFeatureVariableString ( "unsupported_variabletype" , "string_regex_key" , TestUserId ) ;
1794+ Assert . AreEqual ( featureVariableStringRegexSubType , "^\\ d+(\\ .\\ d+)?" ) ;
16281795 }
16291796
16301797 // Should return default value and log message when feature is not enabled for the user.
@@ -2632,6 +2799,55 @@ public void TestGetFeatureVariableDoubleSendsNotificationWhenUserBuckedIntoFeatu
26322799 NotificationCallbackMock . Verify ( nc => nc . TestDecisionCallback ( DecisionNotificationTypes . FEATURE_VARIABLE , TestUserId , new UserAttributes ( ) , It . Is < Dictionary < string , object > > ( info => TestData . CompareObjects ( info , decisionInfo ) ) ) , Times . Once ) ;
26332800 }
26342801
2802+ [ Test ]
2803+ public void TestGetFeatureVariableJsonSendsNotificationWhenUserBuckedIntoFeatureExperimentAndVariationIsToggleOn ( )
2804+ {
2805+ var featureKey = "string_single_variable_feature" ;
2806+ var featureFlag = Config . GetFeatureFlagFromKey ( featureKey ) ;
2807+ var variableKey = "json_var" ;
2808+ var expectedDict = new Dictionary < string , object > ( )
2809+ {
2810+ { "int_var" , 4 } ,
2811+ { "string_var" , "cta_4" }
2812+ } ;
2813+ var experiment = Config . GetRolloutFromId ( "166661" ) . Experiments [ 0 ] ;
2814+ var variation = Config . GetVariationFromKey ( experiment . Key , "177775" ) ;
2815+ var decision = new FeatureDecision ( experiment , variation , FeatureDecision . DECISION_SOURCE_ROLLOUT ) ;
2816+ var userAttributes = new UserAttributes
2817+ {
2818+ { "device_type" , "iPhone" } ,
2819+ { "company" , "Optimizely" } ,
2820+ { "location" , "San Francisco" }
2821+ } ;
2822+
2823+ DecisionServiceMock . Setup ( ds => ds . GetVariationForFeature ( featureFlag , TestUserId , Config , userAttributes ) ) . Returns ( decision ) ;
2824+ NotificationCallbackMock . Setup ( nc => nc . TestDecisionCallback ( It . IsAny < string > ( ) , It . IsAny < string > ( ) , It . IsAny < UserAttributes > ( ) ,
2825+ It . IsAny < Dictionary < string , object > > ( ) ) ) ;
2826+
2827+ var optly = Helper . CreatePrivateOptimizely ( ) ;
2828+
2829+ optly . SetFieldOrProperty ( "ProjectConfigManager" , ConfigManager ) ;
2830+ var optStronglyTyped = optly . GetObject ( ) as Optimizely ;
2831+
2832+ optly . SetFieldOrProperty ( "DecisionService" , DecisionServiceMock . Object ) ;
2833+ optStronglyTyped . NotificationCenter . AddNotification ( NotificationCenter . NotificationType . Decision , NotificationCallbackMock . Object . TestDecisionCallback ) ;
2834+
2835+ var variableValue = ( OptimizelyJson ) optly . Invoke ( "GetFeatureVariableJSON" , featureKey , variableKey , TestUserId , userAttributes ) ;
2836+ Assert . IsTrue ( TestData . CompareObjects ( expectedDict , variableValue . ToDictionary ( ) ) ) ;
2837+ var decisionInfo = new Dictionary < string , object >
2838+ {
2839+ { "featureKey" , featureKey } ,
2840+ { "featureEnabled" , true } ,
2841+ { "variableKey" , variableKey } ,
2842+ { "variableValue" , expectedDict } ,
2843+ { "variableType" , FEATUREVARIABLE_JSONTYPE } ,
2844+ { "source" , FeatureDecision . DECISION_SOURCE_ROLLOUT } ,
2845+ { "sourceInfo" , new Dictionary < string , string > ( ) } ,
2846+ } ;
2847+
2848+ NotificationCallbackMock . Verify ( nc => nc . TestDecisionCallback ( DecisionNotificationTypes . FEATURE_VARIABLE , TestUserId , userAttributes , It . Is < Dictionary < string , object > > ( info => TestData . CompareObjects ( info , decisionInfo ) ) ) , Times . Once ) ;
2849+ }
2850+
26352851 [ Test ]
26362852 public void TestGetFeatureVariableIntegerSendsNotificationWhenUserBuckedIntoFeatureExperimentAndVariationIsToggleOn ( )
26372853 {
0 commit comments