diff --git a/OptimizelySDK.Net40/OptimizelySDK.Net40.csproj b/OptimizelySDK.Net40/OptimizelySDK.Net40.csproj
index 7ffcc618..c4eaf327 100644
--- a/OptimizelySDK.Net40/OptimizelySDK.Net40.csproj
+++ b/OptimizelySDK.Net40/OptimizelySDK.Net40.csproj
@@ -234,6 +234,9 @@
Config\FallbackProjectConfigManager.cs
+
+ OptimizelyFactory.cs
+
diff --git a/OptimizelySDK.NetStandard16/OptimizelySDK.NetStandard16.csproj b/OptimizelySDK.NetStandard16/OptimizelySDK.NetStandard16.csproj
index e1648e1d..28d326e4 100644
--- a/OptimizelySDK.NetStandard16/OptimizelySDK.NetStandard16.csproj
+++ b/OptimizelySDK.NetStandard16/OptimizelySDK.NetStandard16.csproj
@@ -76,6 +76,9 @@
AtomicProjectConfigManager.cs
+
+ OptimizelyFactory.cs
+
diff --git a/OptimizelySDK.Tests/DatafileManagement Tests/AtomicProjectConfigManagerTest.cs b/OptimizelySDK.Tests/ConfigTest/FallbackProjectConfigManagerTest.cs
similarity index 88%
rename from OptimizelySDK.Tests/DatafileManagement Tests/AtomicProjectConfigManagerTest.cs
rename to OptimizelySDK.Tests/ConfigTest/FallbackProjectConfigManagerTest.cs
index 036bdab2..5610ad09 100644
--- a/OptimizelySDK.Tests/DatafileManagement Tests/AtomicProjectConfigManagerTest.cs
+++ b/OptimizelySDK.Tests/ConfigTest/FallbackProjectConfigManagerTest.cs
@@ -22,13 +22,14 @@ namespace OptimizelySDK.Tests.DatafileManagement_Tests
[TestFixture]
public class AtomicProjectConfigManagerTest
{
- private FallbackProjectConfigManager ConfigManager = new FallbackProjectConfigManager();
+ private FallbackProjectConfigManager ConfigManager;
[Test]
public void TestStaticProjectConfigManagerReturnsCorrectProjectConfig()
{
var expectedConfig = DatafileProjectConfig.Create(TestData.TypedAudienceDatafile, null, null);
- ConfigManager.SetConfig(expectedConfig);
+ ConfigManager = new FallbackProjectConfigManager(expectedConfig);
+
Assert.True(TestData.CompareObjects(expectedConfig, ConfigManager.GetConfig()));
}
}
diff --git a/OptimizelySDK.Tests/DatafileManagement Tests/HttpProjectConfigManagerTest.cs b/OptimizelySDK.Tests/ConfigTest/HttpProjectConfigManagerTest.cs
similarity index 100%
rename from OptimizelySDK.Tests/DatafileManagement Tests/HttpProjectConfigManagerTest.cs
rename to OptimizelySDK.Tests/ConfigTest/HttpProjectConfigManagerTest.cs
diff --git a/OptimizelySDK.Tests/DatafileManagement Tests/PollingProjectConfigManagerTest.cs b/OptimizelySDK.Tests/ConfigTest/PollingProjectConfigManagerTest.cs
similarity index 100%
rename from OptimizelySDK.Tests/DatafileManagement Tests/PollingProjectConfigManagerTest.cs
rename to OptimizelySDK.Tests/ConfigTest/PollingProjectConfigManagerTest.cs
diff --git a/OptimizelySDK.Tests/DatafileManagement Tests/TestPollingProjectConfigManager.cs b/OptimizelySDK.Tests/ConfigTest/TestPollingProjectConfigManager.cs
similarity index 100%
rename from OptimizelySDK.Tests/DatafileManagement Tests/TestPollingProjectConfigManager.cs
rename to OptimizelySDK.Tests/ConfigTest/TestPollingProjectConfigManager.cs
diff --git a/OptimizelySDK.Tests/DecisionServiceTest.cs b/OptimizelySDK.Tests/DecisionServiceTest.cs
index 66fa958f..d92dd495 100644
--- a/OptimizelySDK.Tests/DecisionServiceTest.cs
+++ b/OptimizelySDK.Tests/DecisionServiceTest.cs
@@ -37,7 +37,7 @@ public class DecisionServiceTest
private Mock UserProfileServiceMock;
private Mock BucketerMock;
private Mock DecisionServiceMock;
-
+
private ProjectConfig ProjectConfig;
private Experiment WhitelistedExperiment;
private Variation WhitelistedVariation;
@@ -46,36 +46,40 @@ public class DecisionServiceTest
private Variation VariationWithKeyControl;
private Variation VariationWithKeyVariation;
+ private ProjectConfig Config;
+
[SetUp]
public void SetUp()
{
- LoggerMock = new Mock();
- ErrorHandlerMock = new Mock();
- UserProfileServiceMock = new Mock();
- BucketerMock = new Mock(LoggerMock.Object);
-
- ProjectConfig = DatafileProjectConfig.Create(TestData.Datafile, LoggerMock.Object, ErrorHandlerMock.Object);
- WhitelistedExperiment = ProjectConfig.ExperimentIdMap["224"];
- WhitelistedVariation = WhitelistedExperiment.VariationKeyToVariationMap["vtag5"];
-
- DecisionService = new DecisionService(new Bucketer(LoggerMock.Object), ErrorHandlerMock.Object, ProjectConfig, null, LoggerMock.Object);
- DecisionServiceMock = new Mock(BucketerMock.Object, ErrorHandlerMock.Object, ProjectConfig, null, LoggerMock.Object) { CallBase = true };
-
- VariationWithKeyControl = ProjectConfig.GetVariationFromKey("test_experiment", "control");
- VariationWithKeyVariation = ProjectConfig.GetVariationFromKey("test_experiment", "variation");
+ LoggerMock = new Mock();
+ ErrorHandlerMock = new Mock();
+ UserProfileServiceMock = new Mock();
+ BucketerMock = new Mock(LoggerMock.Object);
+
+ ProjectConfig = DatafileProjectConfig.Create(TestData.Datafile, LoggerMock.Object, ErrorHandlerMock.Object);
+ WhitelistedExperiment = ProjectConfig.ExperimentIdMap["224"];
+ WhitelistedVariation = WhitelistedExperiment.VariationKeyToVariationMap["vtag5"];
+
+ DecisionService = new DecisionService(new Bucketer(LoggerMock.Object), ErrorHandlerMock.Object, null, LoggerMock.Object);
+ DecisionServiceMock = new Mock(BucketerMock.Object, ErrorHandlerMock.Object, null, LoggerMock.Object) { CallBase = true };
+
+ VariationWithKeyControl = ProjectConfig.GetVariationFromKey("test_experiment", "control");
+ VariationWithKeyVariation = ProjectConfig.GetVariationFromKey("test_experiment", "variation");
+
+ Config = DatafileProjectConfig.Create(TestData.Datafile, LoggerMock.Object, ErrorHandlerMock.Object);
}
[Test]
public void TestGetVariationForcedVariationPrecedesAudienceEval()
{
- DecisionService decisionService = new DecisionService(BucketerMock.Object, ErrorHandlerMock.Object, ProjectConfig, null, LoggerMock.Object);
+ DecisionService decisionService = new DecisionService(BucketerMock.Object, ErrorHandlerMock.Object, null, LoggerMock.Object);
Experiment experiment = ProjectConfig.Experiments[8];
Variation expectedVariation = experiment.Variations[0];
// user excluded without audiences and whitelisting
- Assert.IsNull(decisionService.GetVariation(experiment, GenericUserId, new UserAttributes()));
+ Assert.IsNull(decisionService.GetVariation(experiment, GenericUserId, ProjectConfig, new UserAttributes()));
- var actualVariation = decisionService.GetVariation(experiment, WhitelistedUserId, new UserAttributes());
+ var actualVariation = decisionService.GetVariation(experiment, WhitelistedUserId, ProjectConfig, new UserAttributes());
LoggerMock.Verify(l => l.Log(LogLevel.INFO, string.Format("User \"{0}\" is forced in variation \"vtag5\".", WhitelistedUserId)), Times.Once);
// no attributes provided for a experiment that has an audience
@@ -97,15 +101,15 @@ public void TestGetVariationEvaluatesUserProfileBeforeAudienceTargeting()
UserProfileServiceMock.Setup(up => up.Lookup(WhitelistedUserId)).Returns(userProfile.ToMap());
- DecisionService decisionService = new DecisionService(BucketerMock.Object, ErrorHandlerMock.Object, ProjectConfig, UserProfileServiceMock.Object, LoggerMock.Object);
+ DecisionService decisionService = new DecisionService(BucketerMock.Object, ErrorHandlerMock.Object, UserProfileServiceMock.Object, LoggerMock.Object);
- decisionService.GetVariation(experiment, GenericUserId, new UserAttributes());
+ decisionService.GetVariation(experiment, GenericUserId, ProjectConfig, new UserAttributes());
LoggerMock.Verify(l => l.Log(LogLevel.INFO, string.Format("User \"{0}\" does not meet conditions to be in experiment \"{1}\".",
GenericUserId, experiment.Key)), Times.Once);
// ensure that a user with a saved user profile, sees the same variation regardless of audience evaluation
- decisionService.GetVariation(experiment, UserProfileId, new UserAttributes());
+ decisionService.GetVariation(experiment, UserProfileId, ProjectConfig, new UserAttributes());
BucketerMock.Verify(_ => _.Bucket(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Never);
}
@@ -113,7 +117,7 @@ public void TestGetVariationEvaluatesUserProfileBeforeAudienceTargeting()
[Test]
public void TestGetForcedVariationReturnsForcedVariation()
{
- DecisionService decisionService = new DecisionService(BucketerMock.Object, ErrorHandlerMock.Object, ProjectConfig, null, LoggerMock.Object);
+ DecisionService decisionService = new DecisionService(BucketerMock.Object, ErrorHandlerMock.Object, null, LoggerMock.Object);
Assert.IsTrue(TestData.CompareObjects(WhitelistedVariation, decisionService.GetWhitelistedVariation(WhitelistedExperiment, WhitelistedUserId)));
LoggerMock.Verify(l => l.Log(LogLevel.INFO, string.Format("User \"{0}\" is forced in variation \"{1}\".",
@@ -128,7 +132,7 @@ public void TestGetForcedVariationWithInvalidVariation()
string userId = "testUser1";
string invalidVariationKey = "invalidVarKey";
- DecisionService decisionService = new DecisionService(BucketerMock.Object, ErrorHandlerMock.Object, ProjectConfig, null, LoggerMock.Object);
+ DecisionService decisionService = new DecisionService(BucketerMock.Object, ErrorHandlerMock.Object, null, LoggerMock.Object);
var variations = new Variation[]
{
@@ -144,8 +148,7 @@ public void TestGetForcedVariationWithInvalidVariation()
{userId, invalidVariationKey }
};
- var experiment = new Experiment
- {
+ var experiment = new Experiment {
Id = "1234",
Key = "exp_key",
Status = "Running",
@@ -168,7 +171,7 @@ public void TestGetForcedVariationWithInvalidVariation()
public void TestGetForcedVariationReturnsNullWhenUserIsNotWhitelisted()
{
Bucketer bucketer = new Bucketer(LoggerMock.Object);
- DecisionService decisionService = new DecisionService(bucketer, ErrorHandlerMock.Object, ProjectConfig, null, LoggerMock.Object);
+ DecisionService decisionService = new DecisionService(bucketer, ErrorHandlerMock.Object, null, LoggerMock.Object);
Assert.IsNull(decisionService.GetWhitelistedVariation(WhitelistedExperiment, GenericUserId));
}
@@ -188,9 +191,9 @@ public void TestBucketReturnsVariationStoredInUserProfile()
UserProfileServiceMock.Setup(_ => _.Lookup(UserProfileId)).Returns(userProfile.ToMap());
- DecisionService decisionService = new DecisionService(BucketerMock.Object, ErrorHandlerMock.Object, ProjectConfig, UserProfileServiceMock.Object, LoggerMock.Object);
+ DecisionService decisionService = new DecisionService(BucketerMock.Object, ErrorHandlerMock.Object, UserProfileServiceMock.Object, LoggerMock.Object);
- Assert.IsTrue(TestData.CompareObjects(variation, decisionService.GetVariation(experiment, UserProfileId, new UserAttributes())));
+ Assert.IsTrue(TestData.CompareObjects(variation, decisionService.GetVariation(experiment, UserProfileId, ProjectConfig, new UserAttributes())));
LoggerMock.Verify(l => l.Log(LogLevel.INFO, string.Format("Returning previously activated variation \"{0}\" of experiment \"{1}\" for user \"{2}\" from user profile.",
variation.Key, experiment.Key, UserProfileId)));
@@ -211,9 +214,9 @@ public void TestGetStoredVariationLogsWhenLookupReturnsNull()
UserProfileServiceMock.Setup(_ => _.Lookup(UserProfileId)).Returns(userProfile.ToMap());
DecisionService decisionService = new DecisionService(bucketer,
- ErrorHandlerMock.Object, ProjectConfig, userProfileService, LoggerMock.Object);
+ ErrorHandlerMock.Object, userProfileService, LoggerMock.Object);
- Assert.IsNull(decisionService.GetStoredVariation(experiment, userProfile));
+ Assert.IsNull(decisionService.GetStoredVariation(experiment, userProfile, ProjectConfig));
LoggerMock.Verify(l => l.Log(LogLevel.INFO, string.Format("No previously activated variation of experiment \"{0}\" for user \"{1}\" found in user profile."
, experiment.Key, UserProfileId)), Times.Once);
@@ -236,9 +239,9 @@ public void TestGetStoredVariationReturnsNullWhenVariationIsNoLongerInConfig()
UserProfileServiceMock.Setup(up => up.Lookup(UserProfileId)).Returns(storedUserProfile.ToMap());
- DecisionService decisionService = new DecisionService(bucketer, ErrorHandlerMock.Object, ProjectConfig,
+ DecisionService decisionService = new DecisionService(bucketer, ErrorHandlerMock.Object,
UserProfileServiceMock.Object, LoggerMock.Object);
- Assert.IsNull(decisionService.GetStoredVariation(experiment, storedUserProfile));
+ Assert.IsNull(decisionService.GetStoredVariation(experiment, storedUserProfile, ProjectConfig));
LoggerMock.Verify(l => l.Log(LogLevel.INFO, string.Format("User \"{0}\" was previously bucketed into variation with ID \"{1}\" for experiment \"{2}\", but no matching variation was found for that user. We will re-bucket the user."
, UserProfileId, storedVariationId, experiment.Id)), Times.Once);
}
@@ -264,9 +267,9 @@ public void TestGetVariationSavesBucketedVariationIntoUserProfile()
var mockBucketer = new Mock(LoggerMock.Object);
mockBucketer.Setup(m => m.Bucket(ProjectConfig, experiment, UserProfileId, UserProfileId)).Returns(variation);
- DecisionService decisionService = new DecisionService(mockBucketer.Object, ErrorHandlerMock.Object, ProjectConfig, UserProfileServiceMock.Object, LoggerMock.Object);
+ DecisionService decisionService = new DecisionService(mockBucketer.Object, ErrorHandlerMock.Object, UserProfileServiceMock.Object, LoggerMock.Object);
- Assert.IsTrue(TestData.CompareObjects(variation, decisionService.GetVariation(experiment, UserProfileId, new UserAttributes())));
+ Assert.IsTrue(TestData.CompareObjects(variation, decisionService.GetVariation(experiment, UserProfileId, ProjectConfig, new UserAttributes())));
LoggerMock.Verify(l => l.Log(LogLevel.INFO, string.Format("Saved variation \"{0}\" of experiment \"{1}\" for user \"{2}\".", variation.Id,
experiment.Id, UserProfileId)), Times.Once);
@@ -291,7 +294,7 @@ public void TestBucketLogsCorrectlyWhenUserProfileFailsToSave()
UserProfile saveUserProfile = new UserProfile(UserProfileId, new Dictionary());
DecisionService decisionService = new DecisionService(bucketer,
- ErrorHandlerMock.Object, ProjectConfig, UserProfileServiceMock.Object, LoggerMock.Object);
+ ErrorHandlerMock.Object, UserProfileServiceMock.Object, LoggerMock.Object);
decisionService.SaveVariation(experiment, variation, saveUserProfile);
@@ -320,10 +323,10 @@ public void TestGetVariationSavesANewUserProfile()
UserProfileServiceMock.Setup(up => up.Lookup(UserProfileId)).Returns(userProfile);
- DecisionService decisionService = new DecisionService(mockBucketer.Object, ErrorHandlerMock.Object, ProjectConfig,
+ DecisionService decisionService = new DecisionService(mockBucketer.Object, ErrorHandlerMock.Object,
UserProfileServiceMock.Object, LoggerMock.Object);
- Assert.IsTrue(TestData.CompareObjects(variation, decisionService.GetVariation(experiment, UserProfileId, new UserAttributes())));
+ Assert.IsTrue(TestData.CompareObjects(variation, decisionService.GetVariation(experiment, UserProfileId, ProjectConfig, new UserAttributes())));
UserProfileServiceMock.Verify(_ => _.Save(It.IsAny>()), Times.Once);
}
@@ -452,7 +455,7 @@ public void TestGetVariationWithBucketingId()
});
UserProfileServiceMock.Setup(up => up.Lookup(userId)).Returns(storedUserProfile.ToMap());
- DecisionService decisionService = new DecisionService(bucketerMock.Object, ErrorHandlerMock.Object, ProjectConfig, UserProfileServiceMock.Object, LoggerMock.Object);
+ DecisionService decisionService = new DecisionService(bucketerMock.Object, ErrorHandlerMock.Object, UserProfileServiceMock.Object, LoggerMock.Object);
actualVariation = optlyObject.GetVariation(experimentKey, userId, userAttributesWithBucketingId);
Assert.IsTrue(TestData.CompareObjects(VariationWithKeyControl, actualVariation), string.Format("Variation \"{0}\" does not match expected user profile variation \"{1}\".", actualVariation?.Key, variationKeyControl));
@@ -465,7 +468,7 @@ public void TestGetVariationWithBucketingId()
public void TestGetVariationForFeatureExperimentGivenNullExperimentIds()
{
var featureFlag = ProjectConfig.GetFeatureFlagFromKey("empty_feature");
- var decision = DecisionService.GetVariationForFeatureExperiment(featureFlag, GenericUserId, new UserAttributes() { });
+ var decision = DecisionService.GetVariationForFeatureExperiment(featureFlag, GenericUserId, new UserAttributes() { }, ProjectConfig);
Assert.IsNull(decision);
@@ -477,15 +480,14 @@ public void TestGetVariationForFeatureExperimentGivenNullExperimentIds()
public void TestGetVariationForFeatureExperimentGivenExperimentNotInDataFile()
{
var booleanFeature = ProjectConfig.GetFeatureFlagFromKey("boolean_feature");
- var featureFlag = new FeatureFlag
- {
+ var featureFlag = new FeatureFlag {
Id = booleanFeature.Id,
Key = booleanFeature.Key,
RolloutId = booleanFeature.RolloutId,
ExperimentIds = new List { "29039203" }
};
- var decision = DecisionService.GetVariationForFeatureExperiment(featureFlag, GenericUserId, new UserAttributes() { });
+ var decision = DecisionService.GetVariationForFeatureExperiment(featureFlag, GenericUserId, new UserAttributes() { }, ProjectConfig);
Assert.IsNull(decision);
LoggerMock.Verify(l => l.Log(LogLevel.ERROR, "Experiment ID \"29039203\" is not in datafile."));
@@ -497,10 +499,10 @@ public void TestGetVariationForFeatureExperimentGivenNonMutexGroupAndUserNotBuck
{
var multiVariateExp = ProjectConfig.GetExperimentFromKey("test_experiment_multivariate");
- DecisionServiceMock.Setup(ds => ds.GetVariation(multiVariateExp, "user1", null)).Returns(null);
+ DecisionServiceMock.Setup(ds => ds.GetVariation(multiVariateExp, "user1", ProjectConfig, null)).Returns(null);
var featureFlag = ProjectConfig.GetFeatureFlagFromKey("multi_variate_feature");
- var decision = DecisionServiceMock.Object.GetVariationForFeatureExperiment(featureFlag, "user1", new UserAttributes());
+ var decision = DecisionServiceMock.Object.GetVariationForFeatureExperiment(featureFlag, "user1", new UserAttributes(), ProjectConfig);
Assert.IsNull(decision);
LoggerMock.Verify(l => l.Log(LogLevel.INFO, "The user \"user1\" is not bucketed into any of the experiments on the feature \"multi_variate_feature\"."));
@@ -515,11 +517,11 @@ public void TestGetVariationForFeatureExperimentGivenNonMutexGroupAndUserIsBucke
var expectedDecision = new FeatureDecision(experiment, variation, FeatureDecision.DECISION_SOURCE_FEATURE_TEST);
var userAttributes = new UserAttributes();
- DecisionServiceMock.Setup(ds => ds.GetVariation(ProjectConfig.GetExperimentFromKey("test_experiment_multivariate"),
- "user1", userAttributes)).Returns(variation);
+ DecisionServiceMock.Setup(ds => ds.GetVariation(ProjectConfig.GetExperimentFromKey("test_experiment_multivariate"),
+ "user1", ProjectConfig, userAttributes)).Returns(variation);
var featureFlag = ProjectConfig.GetFeatureFlagFromKey("multi_variate_feature");
- var decision = DecisionServiceMock.Object.GetVariationForFeatureExperiment(featureFlag, "user1", userAttributes);
+ var decision = DecisionServiceMock.Object.GetVariationForFeatureExperiment(featureFlag, "user1", userAttributes, ProjectConfig);
Assert.IsTrue(TestData.CompareObjects(expectedDecision, decision));
@@ -535,11 +537,11 @@ public void TestGetVariationForFeatureExperimentGivenMutexGroupAndUserIsBucketed
var userAttributes = new UserAttributes();
var expectedDecision = new FeatureDecision(mutexExperiment, variation, FeatureDecision.DECISION_SOURCE_FEATURE_TEST);
- DecisionServiceMock.Setup(ds => ds.GetVariation(ProjectConfig.GetExperimentFromKey("group_experiment_1"), "user1",
+ DecisionServiceMock.Setup(ds => ds.GetVariation(ProjectConfig.GetExperimentFromKey("group_experiment_1"), "user1", ProjectConfig,
userAttributes)).Returns(variation);
var featureFlag = ProjectConfig.GetFeatureFlagFromKey("boolean_feature");
- var actualDecision = DecisionServiceMock.Object.GetVariationForFeatureExperiment(featureFlag, "user1", userAttributes);
+ var actualDecision = DecisionServiceMock.Object.GetVariationForFeatureExperiment(featureFlag, "user1", userAttributes, ProjectConfig);
Assert.IsTrue(TestData.CompareObjects(expectedDecision, actualDecision));
@@ -551,11 +553,11 @@ public void TestGetVariationForFeatureExperimentGivenMutexGroupAndUserIsBucketed
public void TestGetVariationForFeatureExperimentGivenMutexGroupAndUserNotBucketed()
{
var mutexExperiment = ProjectConfig.GetExperimentFromKey("group_experiment_1");
- DecisionServiceMock.Setup(ds => ds.GetVariation(It.IsAny(), It.IsAny(), It.IsAny())).
+ DecisionServiceMock.Setup(ds => ds.GetVariation(It.IsAny(), It.IsAny(), ProjectConfig, It.IsAny())).
Returns(null);
var featureFlag = ProjectConfig.GetFeatureFlagFromKey("boolean_feature");
- var actualDecision = DecisionServiceMock.Object.GetVariationForFeatureExperiment(featureFlag, "user1", new UserAttributes());
+ var actualDecision = DecisionServiceMock.Object.GetVariationForFeatureExperiment(featureFlag, "user1", new UserAttributes(), ProjectConfig);
Assert.IsNull(actualDecision);
@@ -577,16 +579,16 @@ public void TestGetVariationForFeatureRolloutWhenRolloutIsNotInDataFile()
Key = featureFlag.Key,
ExperimentIds = new List(featureFlag.ExperimentIds),
Variables = featureFlag.Variables
- };
+ };
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeatureExperiment(It.IsAny(), It.IsAny(), It.IsAny())).Returns(null);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeatureExperiment(It.IsAny(), It.IsAny(), It.IsAny(), ProjectConfig)).Returns(null);
- var actualDecision = DecisionServiceMock.Object.GetVariationForFeatureRollout(featureFlag, "user1", new UserAttributes());
+ var actualDecision = DecisionServiceMock.Object.GetVariationForFeatureRollout(featureFlag, "user1", new UserAttributes(), ProjectConfig);
Assert.IsNull(actualDecision);
LoggerMock.Verify(l => l.Log(LogLevel.INFO, "The feature flag \"boolean_feature\" is not used in a rollout."));
}
-
+
// Should return the variation the user is bucketed into when the user is bucketed into the targeting rule
[Test]
public void TestGetVariationForFeatureRolloutWhenUserIsBucketedInTheTargetingRule()
@@ -602,11 +604,11 @@ public void TestGetVariationForFeatureRolloutWhenUserIsBucketedInTheTargetingRul
{ "browser_type", "chrome" }
};
- BucketerMock.Setup(bm => bm.Bucket(It.IsAny(), It.IsAny(), It.IsAny(),
+ BucketerMock.Setup(bm => bm.Bucket(It.IsAny(), It.IsAny(), It.IsAny(),
It.IsAny())).Returns(variation);
- var decisionService = new DecisionService(BucketerMock.Object, ErrorHandlerMock.Object, ProjectConfig, null, LoggerMock.Object);
+ var decisionService = new DecisionService(BucketerMock.Object, ErrorHandlerMock.Object, null, LoggerMock.Object);
- var actualDecision = decisionService.GetVariationForFeatureRollout(featureFlag, "user_1", userAttributes);
+ var actualDecision = decisionService.GetVariationForFeatureRollout(featureFlag, "user_1", userAttributes, ProjectConfig);
Assert.IsTrue(TestData.CompareObjects(expectedDecision, actualDecision));
}
@@ -629,9 +631,9 @@ public void TestGetVariationForFeatureRolloutWhenUserIsNotBucketedInTheTargeting
BucketerMock.Setup(bm => bm.Bucket(It.IsAny(), experiment, It.IsAny(), It.IsAny())).Returns(null);
BucketerMock.Setup(bm => bm.Bucket(It.IsAny(), everyoneElseRule, It.IsAny(), It.IsAny())).Returns(variation);
- var decisionService = new DecisionService(BucketerMock.Object, ErrorHandlerMock.Object, ProjectConfig, null, LoggerMock.Object);
+ var decisionService = new DecisionService(BucketerMock.Object, ErrorHandlerMock.Object, null, LoggerMock.Object);
- var actualDecision = decisionService.GetVariationForFeatureRollout(featureFlag, "user_1", userAttributes);
+ var actualDecision = decisionService.GetVariationForFeatureRollout(featureFlag, "user_1", userAttributes, ProjectConfig);
Assert.IsTrue(TestData.CompareObjects(expectedDecision, actualDecision));
}
@@ -648,12 +650,12 @@ public void TestGetVariationForFeatureRolloutWhenUserIsNeitherBucketedInTheTarge
};
BucketerMock.Setup(bm => bm.Bucket(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Returns(null);
- var decisionService = new DecisionService(BucketerMock.Object, ErrorHandlerMock.Object, ProjectConfig, null, LoggerMock.Object);
+ var decisionService = new DecisionService(BucketerMock.Object, ErrorHandlerMock.Object, null, LoggerMock.Object);
- var actualDecision = decisionService.GetVariationForFeatureRollout(featureFlag, "user_1", userAttributes);
+ var actualDecision = decisionService.GetVariationForFeatureRollout(featureFlag, "user_1", userAttributes, ProjectConfig);
Assert.IsNull(actualDecision);
}
-
+
// Should return expected variation when the user is attempted to be bucketed into all targeting rules
// including Everyone Else rule
[Test]
@@ -669,17 +671,17 @@ public void TestGetVariationForFeatureRolloutWhenUserDoesNotQualifyForAnyTargeti
var expectedDecision = new FeatureDecision(everyoneElseRule, variation, FeatureDecision.DECISION_SOURCE_ROLLOUT);
BucketerMock.Setup(bm => bm.Bucket(It.IsAny(), everyoneElseRule, It.IsAny(), It.IsAny())).Returns(variation);
- var decisionService = new DecisionService(BucketerMock.Object, ErrorHandlerMock.Object, ProjectConfig, null, LoggerMock.Object);
+ var decisionService = new DecisionService(BucketerMock.Object, ErrorHandlerMock.Object, null, LoggerMock.Object);
// Provide null attributes so that user does not qualify for audience.
- var actualDecision = decisionService.GetVariationForFeatureRollout(featureFlag, "user_1", null);
+ var actualDecision = decisionService.GetVariationForFeatureRollout(featureFlag, "user_1", null, ProjectConfig);
Assert.IsTrue(TestData.CompareObjects(expectedDecision, actualDecision));
LoggerMock.Verify(l => l.Log(LogLevel.DEBUG, $"User \"user_1\" does not meet the conditions to be in rollout rule for audience \"{ProjectConfig.AudienceIdMap[experiment0.AudienceIds[0]].Name}\"."));
LoggerMock.Verify(l => l.Log(LogLevel.DEBUG, $"User \"user_1\" does not meet the conditions to be in rollout rule for audience \"{ProjectConfig.AudienceIdMap[experiment1.AudienceIds[0]].Name}\"."));
}
-
+
[Test]
public void TestGetVariationForFeatureRolloutAudienceAndTrafficeAllocationCheck()
{
@@ -695,14 +697,14 @@ public void TestGetVariationForFeatureRolloutAudienceAndTrafficeAllocationCheck(
var mockBucketer = new Mock(LoggerMock.Object) { CallBase = true };
mockBucketer.Setup(bm => bm.GenerateBucketValue(It.IsAny())).Returns(980);
- var decisionService = new DecisionService(mockBucketer.Object, ErrorHandlerMock.Object, ProjectConfig, null, LoggerMock.Object);
+ var decisionService = new DecisionService(mockBucketer.Object, ErrorHandlerMock.Object, null, LoggerMock.Object);
// Calling with audience iPhone users in San Francisco.
var actualDecision = decisionService.GetVariationForFeatureRollout(featureFlag, GenericUserId, new UserAttributes
{
{ "device_type", "iPhone" },
{ "location", "San Francisco" }
- });
+ }, ProjectConfig);
// Returned variation id should be '177773' because of audience 'iPhone users in San Francisco'.
var expectedDecision = new FeatureDecision(expWithAudienceiPhoneUsers, varWithAudienceiPhoneUsers, FeatureDecision.DECISION_SOURCE_ROLLOUT);
@@ -712,7 +714,7 @@ public void TestGetVariationForFeatureRolloutAudienceAndTrafficeAllocationCheck(
actualDecision = decisionService.GetVariationForFeatureRollout(featureFlag, GenericUserId, new UserAttributes
{
{ "browser_type", "chrome" }
- });
+ }, ProjectConfig);
// Returned variation id should be '177771' because of audience 'Chrome users'.
expectedDecision = new FeatureDecision(expWithAudienceChromeUsers, varWithAudienceChromeUsers, FeatureDecision.DECISION_SOURCE_ROLLOUT);
@@ -720,7 +722,7 @@ public void TestGetVariationForFeatureRolloutAudienceAndTrafficeAllocationCheck(
// Calling with no audience.
mockBucketer.Setup(bm => bm.GenerateBucketValue(It.IsAny())).Returns(8000);
- actualDecision = decisionService.GetVariationForFeatureRollout(featureFlag, GenericUserId, new UserAttributes());
+ actualDecision = decisionService.GetVariationForFeatureRollout(featureFlag, GenericUserId, new UserAttributes(), ProjectConfig);
// Returned variation id should be of everyone else rule because of no audience.
expectedDecision = new FeatureDecision(expWithNoAudience, varWithNoAudience, FeatureDecision.DECISION_SOURCE_ROLLOUT);
@@ -731,7 +733,7 @@ public void TestGetVariationForFeatureRolloutAudienceAndTrafficeAllocationCheck(
actualDecision = decisionService.GetVariationForFeatureRollout(featureFlag, GenericUserId, new UserAttributes
{
{ "browser_type", "chrome" }
- });
+ }, ProjectConfig);
// Returned decision entity should be null because bucket value exceeds traffice allocation of everyone else rule.
Assert.Null(actualDecision);
@@ -749,19 +751,19 @@ public void TestGetVariationForFeatureRolloutCheckAudienceInEveryoneElseRule()
BucketerMock.Setup(bm => bm.Bucket(It.IsAny(), everyoneElseRule, It.IsAny(), WhitelistedUserId)).Returns(variation);
BucketerMock.Setup(bm => bm.Bucket(It.IsAny(), everyoneElseRule, It.IsAny(), GenericUserId)).Returns(null);
- var decisionService = new DecisionService(BucketerMock.Object, ErrorHandlerMock.Object, ProjectConfig, null, LoggerMock.Object);
+ var decisionService = new DecisionService(BucketerMock.Object, ErrorHandlerMock.Object, null, LoggerMock.Object);
// Returned variation id should be of everyone else rule as it passes audience Id checking.
- var actualDecision = decisionService.GetVariationForFeatureRollout(featureFlag, WhitelistedUserId, null);
+ var actualDecision = decisionService.GetVariationForFeatureRollout(featureFlag, WhitelistedUserId, null, ProjectConfig);
Assert.True(TestData.CompareObjects(expectedDecision, actualDecision));
// Returned variation id should be null.
- actualDecision = decisionService.GetVariationForFeatureRollout(featureFlag, GenericUserId, null);
+ actualDecision = decisionService.GetVariationForFeatureRollout(featureFlag, GenericUserId, null, ProjectConfig);
Assert.Null(actualDecision);
// Returned variation id should be null as it fails audience Id checking.
everyoneElseRule.AudienceIds = new string[] { ProjectConfig.Audiences[0].Id };
- actualDecision = decisionService.GetVariationForFeatureRollout(featureFlag, GenericUserId, null);
+ actualDecision = decisionService.GetVariationForFeatureRollout(featureFlag, GenericUserId, null, ProjectConfig);
Assert.Null(actualDecision);
LoggerMock.Verify(l => l.Log(LogLevel.DEBUG, "User \"testUser1\" does not meet the conditions to be in rollout rule for audience \"Chrome users\"."), Times.Once);
@@ -784,10 +786,10 @@ public void TestGetVariationForFeatureWhenTheUserIsBucketedIntoFeatureExperiment
var variation = expectedExperiment.Variations[0];
var expectedDecision = new FeatureDecision(expectedExperiment, variation, FeatureDecision.DECISION_SOURCE_FEATURE_TEST);
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeatureExperiment(It.IsAny(), It.IsAny(),
- It.IsAny())).Returns(expectedDecision);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeatureExperiment(It.IsAny(), It.IsAny(),
+ It.IsAny(), ProjectConfig)).Returns(expectedDecision);
- var actualDecision = DecisionServiceMock.Object.GetVariationForFeature(featureFlag, "user1", new UserAttributes());
+ var actualDecision = DecisionServiceMock.Object.GetVariationForFeature(featureFlag, "user1", ProjectConfig, new UserAttributes());
Assert.IsTrue(TestData.CompareObjects(expectedDecision, actualDecision));
}
@@ -803,12 +805,12 @@ public void TestGetVariationForFeatureWhenTheUserIsNotBucketedIntoFeatureExperim
var variation = expectedExperiment.Variations[0];
var expectedDecision = new FeatureDecision(expectedExperiment, variation, FeatureDecision.DECISION_SOURCE_ROLLOUT);
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeatureExperiment(It.IsAny(), It.IsAny(),
- It.IsAny())).Returns(null);
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeatureRollout(It.IsAny(), It.IsAny(),
- It.IsAny())).Returns(expectedDecision);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeatureExperiment(It.IsAny(), It.IsAny(),
+ It.IsAny(), ProjectConfig)).Returns(null);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeatureRollout(It.IsAny(), It.IsAny(),
+ It.IsAny(), ProjectConfig)).Returns(expectedDecision);
- var actualDecision = DecisionServiceMock.Object.GetVariationForFeature(featureFlag, "user1", new UserAttributes());
+ var actualDecision = DecisionServiceMock.Object.GetVariationForFeature(featureFlag, "user1", ProjectConfig, new UserAttributes());
Assert.IsTrue(TestData.CompareObjects(expectedDecision, actualDecision));
@@ -822,10 +824,10 @@ public void TestGetVariationForFeatureWhenTheUserIsNeitherBucketedIntoFeatureExp
var featureFlag = ProjectConfig.GetFeatureFlagFromKey("string_single_variable_feature");
var expectedDecision = new FeatureDecision(null, null, FeatureDecision.DECISION_SOURCE_ROLLOUT);
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeatureExperiment(It.IsAny(), It.IsAny(), It.IsAny())).Returns(null);
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeatureRollout(It.IsAny(), It.IsAny(), It.IsAny())).Returns(null);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeatureExperiment(It.IsAny(), It.IsAny(), It.IsAny(), ProjectConfig)).Returns(null);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeatureRollout(It.IsAny(), It.IsAny(), It.IsAny(), ProjectConfig)).Returns(null);
- var actualDecision = DecisionServiceMock.Object.GetVariationForFeature(featureFlag, "user1", new UserAttributes());
+ var actualDecision = DecisionServiceMock.Object.GetVariationForFeature(featureFlag, "user1", ProjectConfig, new UserAttributes());
Assert.IsTrue(TestData.CompareObjects(expectedDecision, actualDecision));
LoggerMock.Verify(l => l.Log(LogLevel.INFO, "The user \"user1\" is not bucketed into a rollout for feature flag \"string_single_variable_feature\"."));
@@ -844,8 +846,8 @@ public void TestGetVariationForFeatureWhenTheUserIsBuckedtedInBothExperimentAndR
{ "browser_type", "chrome" }
};
- DecisionServiceMock.Setup(ds => ds.GetVariation(experiment, "user1", userAttributes)).Returns(variation);
- var actualDecision = DecisionServiceMock.Object.GetVariationForFeatureExperiment(featureFlag, "user1", userAttributes);
+ DecisionServiceMock.Setup(ds => ds.GetVariation(experiment, "user1", ProjectConfig, userAttributes)).Returns(variation);
+ var actualDecision = DecisionServiceMock.Object.GetVariationForFeatureExperiment(featureFlag, "user1", userAttributes, ProjectConfig);
// The user is bucketed into feature experiment's variation.
Assert.IsTrue(TestData.CompareObjects(expectedDecision, actualDecision));
@@ -856,13 +858,13 @@ public void TestGetVariationForFeatureWhenTheUserIsBuckedtedInBothExperimentAndR
var expectedRolloutDecision = new FeatureDecision(rolloutExperiment, rolloutVariation, FeatureDecision.DECISION_SOURCE_ROLLOUT);
BucketerMock.Setup(bm => bm.Bucket(ProjectConfig, rolloutExperiment, It.IsAny(), It.IsAny())).Returns(rolloutVariation);
- var actualRolloutDecision = DecisionServiceMock.Object.GetVariationForFeatureRollout(featureFlag, "user1", userAttributes);
+ var actualRolloutDecision = DecisionServiceMock.Object.GetVariationForFeatureRollout(featureFlag, "user1", userAttributes, ProjectConfig);
// The user is bucketed into feature rollout's variation.
Assert.IsTrue(TestData.CompareObjects(expectedRolloutDecision, actualRolloutDecision));
- actualDecision = DecisionServiceMock.Object.GetVariationForFeature(featureFlag, "user1", userAttributes);
+ actualDecision = DecisionServiceMock.Object.GetVariationForFeature(featureFlag, "user1", ProjectConfig, userAttributes);
// The user is bucketed into feature experiment's variation and not the rollout's variation.
Assert.IsTrue(TestData.CompareObjects(expectedDecision, actualDecision));
diff --git a/OptimizelySDK.Tests/EntityTests/FeatureVariableTest.cs b/OptimizelySDK.Tests/EntityTests/FeatureVariableTest.cs
new file mode 100644
index 00000000..86965648
--- /dev/null
+++ b/OptimizelySDK.Tests/EntityTests/FeatureVariableTest.cs
@@ -0,0 +1,19 @@
+using System;
+using NUnit.Framework;
+using OptimizelySDK.Entity;
+
+namespace OptimizelySDK.Tests.EntityTests
+{
+ [TestFixture]
+ public class FeatureVariableTest
+ {
+ [Test]
+ public void TestFeatureVariableTypeName()
+ {
+ Assert.AreEqual(FeatureVariable.GetFeatureVariableTypeName(FeatureVariable.VariableType.BOOLEAN), "GetFeatureVariableBoolean");
+ Assert.AreEqual(FeatureVariable.GetFeatureVariableTypeName(FeatureVariable.VariableType.DOUBLE), "GetFeatureVariableDouble");
+ Assert.AreEqual(FeatureVariable.GetFeatureVariableTypeName(FeatureVariable.VariableType.INTEGER), "GetFeatureVariableInteger");
+ Assert.AreEqual(FeatureVariable.GetFeatureVariableTypeName(FeatureVariable.VariableType.STRING), "GetFeatureVariableString");
+ }
+ }
+}
diff --git a/OptimizelySDK.Tests/OptimizelySDK.Tests.csproj b/OptimizelySDK.Tests/OptimizelySDK.Tests.csproj
index 25e09f67..553ff22b 100644
--- a/OptimizelySDK.Tests/OptimizelySDK.Tests.csproj
+++ b/OptimizelySDK.Tests/OptimizelySDK.Tests.csproj
@@ -72,9 +72,9 @@
-
-
-
+
+
+
@@ -95,7 +95,8 @@
-
+
+
diff --git a/OptimizelySDK.Tests/OptimizelyTest.cs b/OptimizelySDK.Tests/OptimizelyTest.cs
index 0cb88ba4..38714a19 100644
--- a/OptimizelySDK.Tests/OptimizelyTest.cs
+++ b/OptimizelySDK.Tests/OptimizelyTest.cs
@@ -37,6 +37,7 @@ namespace OptimizelySDK.Tests
public class OptimizelyTest
{
private Mock LoggerMock;
+ private ProjectConfigManager ConfigManager;
private ProjectConfig Config;
private Mock EventBuilderMock;
private Mock ErrorHandlerMock;
@@ -76,11 +77,12 @@ public void Initialize()
EventBuilderMock.Setup(b => b.CreateConversionEvent(It.IsAny(), It.IsAny(),
It.IsAny(), It.IsAny(), It.IsAny()));
- Config = DatafileProjectConfig.Create(
+ var config = DatafileProjectConfig.Create(
content: TestData.Datafile,
logger: LoggerMock.Object,
errorHandler: new NoOpErrorHandler());
-
+ ConfigManager = new FallbackProjectConfigManager(config);
+ Config = ConfigManager.GetConfig();
EventDispatcherMock = new Mock();
Optimizely = new Optimizely(TestData.Datafile, EventDispatcherMock.Object, LoggerMock.Object, ErrorHandlerMock.Object);
OptimizelyWithTypedAudiences = new Optimizely(TestData.TypedAudienceDatafile, EventDispatcherMock.Object, LoggerMock.Object, ErrorHandlerMock.Object);
@@ -100,7 +102,7 @@ public void Initialize()
};
DecisionServiceMock = new Mock(new Bucketer(LoggerMock.Object), ErrorHandlerMock.Object,
- Config, null, LoggerMock.Object);
+ null, LoggerMock.Object);
NotificationCenter = new NotificationCenter(LoggerMock.Object);
NotificationCallbackMock = new Mock();
@@ -180,6 +182,34 @@ public PrivateObject CreatePrivateOptimizely()
#region Test Validate
[Test]
+ public void TestInvalidInstanceLogMessages()
+ {
+ string datafile = "{\"name\":\"optimizely\"}";
+ var optimizely = new Optimizely(datafile, null, LoggerMock.Object);
+
+ Assert.IsNull(optimizely.GetVariation(string.Empty, string.Empty));
+ Assert.IsNull(optimizely.Activate(string.Empty, string.Empty));
+ optimizely.Track(string.Empty, string.Empty);
+ Assert.IsFalse(optimizely.IsFeatureEnabled(string.Empty, string.Empty));
+ Assert.AreEqual(optimizely.GetEnabledFeatures(string.Empty).Count, 0);
+ Assert.IsNull(optimizely.GetFeatureVariableBoolean(string.Empty, string.Empty, string.Empty));
+ Assert.IsNull(optimizely.GetFeatureVariableString(string.Empty, string.Empty, string.Empty));
+ Assert.IsNull(optimizely.GetFeatureVariableDouble(string.Empty, string.Empty, string.Empty));
+ Assert.IsNull(optimizely.GetFeatureVariableInteger(string.Empty, string.Empty, string.Empty));
+
+ LoggerMock.Verify(log => log.Log(LogLevel.ERROR, "Provided 'datafile' has invalid schema."), Times.Once);
+ LoggerMock.Verify(log => log.Log(LogLevel.ERROR, "Datafile has invalid format. Failing 'GetVariation'."), Times.Once);
+ LoggerMock.Verify(log => log.Log(LogLevel.ERROR, "Datafile has invalid format. Failing 'Activate'."), Times.Once);
+ LoggerMock.Verify(log => log.Log(LogLevel.ERROR, "Datafile has invalid format. Failing 'Track'."), Times.Once);
+ LoggerMock.Verify(log => log.Log(LogLevel.ERROR, "Datafile has invalid format. Failing 'IsFeatureEnabled'."), Times.Once);
+ LoggerMock.Verify(log => log.Log(LogLevel.ERROR, "Datafile has invalid format. Failing 'GetEnabledFeatures'."), Times.Once);
+ LoggerMock.Verify(log => log.Log(LogLevel.ERROR, "Datafile has invalid format. Failing 'GetFeatureVariableBoolean'."), Times.Once);
+ LoggerMock.Verify(log => log.Log(LogLevel.ERROR, "Datafile has invalid format. Failing 'GetFeatureVariableString'."), Times.Once);
+ LoggerMock.Verify(log => log.Log(LogLevel.ERROR, "Datafile has invalid format. Failing 'GetFeatureVariableDouble'."), Times.Once);
+ LoggerMock.Verify(log => log.Log(LogLevel.ERROR, "Datafile has invalid format. Failing 'GetFeatureVariableInteger'."), Times.Once);
+
+ }
+ [Test]
public void TestValidateInputsInvalidFileJsonValidationNotSkipped()
{
string datafile = "{\"name\":\"optimizely\"}";
@@ -223,9 +253,12 @@ public void TestErrorHandlingWithUnsupportedConfigVersion()
public void TestValidatePreconditionsExperimentNotRunning()
{
var optly = Helper.CreatePrivateOptimizely();
+
+ optly.SetFieldOrProperty("ProjectConfigManager", ConfigManager);
+
bool result = (bool)optly.Invoke("ValidatePreconditions",
Config.GetExperimentFromKey("paused_experiment"),
- TestUserId, new UserAttributes { });
+ TestUserId, ConfigManager.GetConfig(), new UserAttributes { });
Assert.IsFalse(result);
}
@@ -236,6 +269,7 @@ public void TestValidatePreconditionsExperimentRunning()
bool result = (bool)optly.Invoke("ValidatePreconditions",
Config.GetExperimentFromKey("test_experiment"),
TestUserId,
+ ConfigManager.GetConfig(),
new UserAttributes
{
{ "device_type", "iPhone" },
@@ -251,7 +285,7 @@ public void TestValidatePreconditionsUserInForcedVariationNotInExperiment()
var optly = Helper.CreatePrivateOptimizely();
bool result = (bool)optly.Invoke("ValidatePreconditions",
Config.GetExperimentFromKey("test_experiment"),
- "user1", new UserAttributes { });
+ "user1", ConfigManager.GetConfig(), new UserAttributes { });
Assert.IsTrue(result);
}
@@ -261,7 +295,7 @@ public void TestValidatePreconditionsUserInForcedVariationInExperiment()
var optly = Helper.CreatePrivateOptimizely();
bool result = (bool)optly.Invoke("ValidatePreconditions",
Config.GetExperimentFromKey("test_experiment"),
- "user1", new UserAttributes { });
+ "user1", ConfigManager.GetConfig(), new UserAttributes { });
Assert.IsTrue(result);
}
@@ -271,7 +305,7 @@ public void TestValidatePreconditionsUserNotInForcedVariationNotInExperiment()
var optly = Helper.CreatePrivateOptimizely();
bool result = (bool)optly.Invoke("ValidatePreconditions",
Config.GetExperimentFromKey("test_experiment"),
- TestUserId, new UserAttributes { });
+ TestUserId, ConfigManager.GetConfig(), new UserAttributes { });
Assert.IsFalse(result);
}
@@ -497,7 +531,7 @@ public void TestGetVariationInvalidOptimizelyObject()
var optly = new Optimizely("Random datafile", null, LoggerMock.Object);
var variationkey = optly.Activate("some_experiment", "some_user");
LoggerMock.Verify(l => l.Log(It.IsAny(), It.IsAny()), Times.Exactly(2));
- LoggerMock.Verify(l => l.Log(LogLevel.ERROR, "Datafile has invalid format. Failing 'activate'."), Times.Once);
+ LoggerMock.Verify(l => l.Log(LogLevel.ERROR, "Datafile has invalid format. Failing 'Activate'."), Times.Once);
//Assert.IsNull(variationkey);
}
@@ -556,7 +590,7 @@ public void TestTrackInvalidOptimizelyObject()
var optly = new Optimizely("Random datafile", null, LoggerMock.Object);
optly.Track("some_event", "some_user");
LoggerMock.Verify(l => l.Log(It.IsAny(), It.IsAny()), Times.Exactly(2));
- LoggerMock.Verify(l => l.Log(LogLevel.ERROR, "Datafile has invalid format. Failing 'track'."), Times.Once);
+ LoggerMock.Verify(l => l.Log(LogLevel.ERROR, "Datafile has invalid format. Failing 'Track'."), Times.Once);
}
#endregion
@@ -1100,7 +1134,7 @@ public void TestActivateNoAudienceNoAttributesAfterSetForcedVariation()
var optly = Helper.CreatePrivateOptimizely();
optly.SetFieldOrProperty("EventBuilder", EventBuilderMock.Object);
- optly.SetFieldOrProperty("Config", Config);
+ optly.SetFieldOrProperty("ProjectConfigManager", ConfigManager);
// Set forced variation
Assert.True((bool)optly.Invoke("SetForcedVariation", experimentKey, userId, variationKey), "Set variation for paused experiment should have failed.");
@@ -1272,7 +1306,7 @@ public void TestGetFeatureVariableIntegerReturnsCorrectValue()
Assert.Null(OptimizelyMock.Object.GetFeatureVariableInteger(featureKey, variableNonInt, TestUserId, null));
OptimizelyMock.Setup(om => om.GetFeatureVariableValueForType(It.IsAny(), variableKeyNull, It.IsAny(),
- It.IsAny(), featureVariableType)).Returns(null);
+ It.IsAny(), featureVariableType)).Returns(null);
Assert.Null(OptimizelyMock.Object.GetFeatureVariableInteger(featureKey, variableKeyNull, TestUserId, null));
}
@@ -1311,11 +1345,12 @@ public void TestGetFeatureVariableDoubleReturnsRightValueWhenUserBuckedIntoFeatu
var variation = Config.GetVariationFromKey("test_experiment_double_feature", "control");
var decision = new FeatureDecision(experiment, variation, FeatureDecision.DECISION_SOURCE_FEATURE_TEST);
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns(decision);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, Config, null)).Returns(decision);
var optly = Helper.CreatePrivateOptimizely();
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
-
+ optly.SetFieldOrProperty("ProjectConfigManager", ConfigManager);
+
var variableValue = (double)optly.Invoke("GetFeatureVariableDouble", featureKey, variableKey, TestUserId, null);
Assert.AreEqual(expectedValue, variableValue);
@@ -1339,11 +1374,12 @@ public void TestGetFeatureVariableIntegerReturnsRightValueWhenUserBuckedIntoFeat
{ "location", "San Francisco" }
};
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, userAttributes)).Returns(decision);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, Config, userAttributes)).Returns(decision);
var optly = Helper.CreatePrivateOptimizely();
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
-
+ optly.SetFieldOrProperty("ProjectConfigManager", ConfigManager);
+
var variableValue = (int)optly.Invoke("GetFeatureVariableInteger", featureKey, variableKey, TestUserId, userAttributes);
Assert.AreEqual(expectedValue, variableValue);
@@ -1361,11 +1397,12 @@ public void TestGetFeatureVariableDoubleReturnsDefaultValueWhenUserBuckedIntoFea
var variation = Config.GetVariationFromKey("test_experiment_double_feature", "variation");
var decision = new FeatureDecision(experiment, variation, FeatureDecision.DECISION_SOURCE_FEATURE_TEST);
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns(decision);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, Config, null)).Returns(decision);
var optly = Helper.CreatePrivateOptimizely();
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
-
+ optly.SetFieldOrProperty("ProjectConfigManager", ConfigManager);
+
var variableValue = (double)optly.Invoke("GetFeatureVariableDouble", featureKey, variableKey, TestUserId, null);
Assert.AreEqual(expectedValue, variableValue);
@@ -1389,10 +1426,11 @@ public void TestGetFeatureVariableIntegerReturnsDefaultValueWhenUserBuckedIntoFe
{ "location", "San Francisco" }
};
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, userAttributes)).Returns(decision);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, Config, userAttributes)).Returns(decision);
var optly = Helper.CreatePrivateOptimizely();
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
+ optly.SetFieldOrProperty("ProjectConfigManager", ConfigManager);
var variableValue = (int)optly.Invoke("GetFeatureVariableInteger", featureKey, variableKey, TestUserId, userAttributes);
Assert.AreEqual(expectedValue, variableValue);
@@ -1411,11 +1449,11 @@ public void TestGetFeatureVariableBooleanReturnsRightValueWhenUserBuckedIntoRoll
var variation = Config.GetVariationFromKey(experiment.Key, "177771");
var decision = new FeatureDecision(experiment, variation, FeatureDecision.DECISION_SOURCE_ROLLOUT);
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns(decision);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, Config, null)).Returns(decision);
var optly = Helper.CreatePrivateOptimizely();
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
-
+ optly.SetFieldOrProperty("ProjectConfigManager", ConfigManager);
var variableValue = (bool)optly.Invoke("GetFeatureVariableBoolean", featureKey, variableKey, TestUserId, null);
Assert.AreEqual(expectedValue, variableValue);
@@ -1439,9 +1477,11 @@ public void TestGetFeatureVariableStringReturnsRightValueWhenUserBuckedIntoRollo
{ "location", "San Francisco" }
};
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, userAttributes)).Returns(decision);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, Config, userAttributes)).Returns(decision);
var optly = Helper.CreatePrivateOptimizely();
+
+ optly.SetFieldOrProperty("ProjectConfigManager", ConfigManager);
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
var variableValue = (string)optly.Invoke("GetFeatureVariableString", featureKey, variableKey, TestUserId, userAttributes);
@@ -1461,11 +1501,11 @@ public void TestGetFeatureVariableBooleanReturnsDefaultValueWhenUserBuckedIntoRo
var variation = Config.GetVariationFromKey(experiment.Key, "177782");
var decision = new FeatureDecision(experiment, variation, FeatureDecision.DECISION_SOURCE_ROLLOUT);
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns(decision);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, Config, null)).Returns(decision);
var optly = Helper.CreatePrivateOptimizely();
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
-
+ optly.SetFieldOrProperty("ProjectConfigManager", ConfigManager);
var variableValue = (bool)optly.Invoke("GetFeatureVariableBoolean", featureKey, variableKey, TestUserId, null);
Assert.AreEqual(expectedValue, variableValue);
@@ -1489,9 +1529,10 @@ public void TestGetFeatureVariableStringReturnsDefaultValueWhenUserBuckedIntoRol
{ "location", "San Francisco" }
};
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, userAttributes)).Returns(decision);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, Config, userAttributes)).Returns(decision);
var optly = Helper.CreatePrivateOptimizely();
+ optly.SetFieldOrProperty("ProjectConfigManager", ConfigManager);
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
var variableValue = (string)optly.Invoke("GetFeatureVariableString", featureKey, variableKey, TestUserId, userAttributes);
@@ -1509,10 +1550,11 @@ public void TestGetFeatureVariableDoubleReturnsDefaultValueWhenUserNotBuckedInto
var expectedValue = 14.99;
var decision = new FeatureDecision(null, null, FeatureDecision.DECISION_SOURCE_ROLLOUT);
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns(decision);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, Config, null)).Returns(decision);
var optly = Helper.CreatePrivateOptimizely();
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
+ optly.SetFieldOrProperty("ProjectConfigManager", ConfigManager);
var variableValue = (double)optly.Invoke("GetFeatureVariableDouble", featureKey, variableKey, TestUserId, null);
Assert.AreEqual(expectedValue, variableValue);
@@ -1605,9 +1647,11 @@ public void TestGetFeatureVariableValueForTypeGivenFeatureFlagIsNotEnabledForUse
var decision = new FeatureDecision(experiment, variation, FeatureDecision.DECISION_SOURCE_FEATURE_TEST);
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns(decision);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, Config, null)).Returns(decision);
var optly = Helper.CreatePrivateOptimizely();
+
+ optly.SetFieldOrProperty("ProjectConfigManager", ConfigManager);
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
var variableValue = (double?)optly.InvokeGeneric("GetFeatureVariableValueForType", new Type[] { typeof(double?) }, featureKey, variableKey, TestUserId, null, variableType);
@@ -1632,9 +1676,11 @@ public void TestGetFeatureVariableValueForTypeGivenFeatureFlagIsEnabledForUserAn
var expectedValue = 14.99;
// Mock GetVariationForFeature method to return variation of different feature.
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns(expectedDecision);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, Config, null)).Returns(expectedDecision);
var optly = Helper.CreatePrivateOptimizely();
+
+ optly.SetFieldOrProperty("ProjectConfigManager", ConfigManager);
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
var variableValue = (double?)optly.InvokeGeneric("GetFeatureVariableValueForType", new Type[] { typeof(double?) }, featureKey, variableKey, TestUserId, null, variableType);
@@ -1658,11 +1704,13 @@ public void TestGetFeatureVariableValueForTypeGivenFeatureFlagIsEnabledForUserAn
var variation = Config.GetVariationFromKey("test_experiment_double_feature", "control");
var decision = new FeatureDecision(experiment, variation, FeatureDecision.DECISION_SOURCE_FEATURE_TEST);
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns(decision);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, Config, null)).Returns(decision);
var optly = Helper.CreatePrivateOptimizely();
+
+ optly.SetFieldOrProperty("ProjectConfigManager", ConfigManager);
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
-
+
var variableValue = (double?)optly.InvokeGeneric("GetFeatureVariableValueForType", new Type[] { typeof(double?) }, featureKey, variableKey, TestUserId, null, variableType);
Assert.AreEqual(expectedValue, variableValue);
@@ -1683,9 +1731,11 @@ public void TestGetFeatureVariableValueForTypeWithRolloutRule()
var decision = new FeatureDecision(experiment, variation, FeatureDecision.DECISION_SOURCE_ROLLOUT);
var expectedVariableValue = false;
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns(decision);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, Config, null)).Returns(decision);
var optly = Helper.CreatePrivateOptimizely();
+
+ optly.SetFieldOrProperty("ProjectConfigManager", ConfigManager);
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
// Calling GetFeatureVariableBoolean to get GetFeatureVariableValueForType returned value casted in bool.
@@ -1731,10 +1781,11 @@ public void TestIsFeatureEnabledGivenFeatureFlagNotFound()
public void TestIsFeatureEnabledGivenFeatureFlagContainsInvalidExperiment()
{
var tempConfig = DatafileProjectConfig.Create(TestData.Datafile, LoggerMock.Object, new NoOpErrorHandler());
+ var tempConfigManager = new FallbackProjectConfigManager(tempConfig);
var featureFlag = tempConfig.GetFeatureFlagFromKey("multi_variate_feature");
var optly = Helper.CreatePrivateOptimizely();
- optly.SetFieldOrProperty("Config", tempConfig);
+ optly.SetFieldOrProperty("ProjectConfigManager", tempConfigManager);
// Set such an experiment to the list of experiment ids, that does not belong to the feature.
featureFlag.ExperimentIds = new List { "4209211" };
@@ -1755,10 +1806,11 @@ public void TestIsFeatureEnabledGivenFeatureFlagIsNotEnabledForUser()
var variation = Config.GetVariationFromKey("test_experiment_double_feature", "variation");
var decision = new FeatureDecision(experiment, variation, FeatureDecision.DECISION_SOURCE_FEATURE_TEST);
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns(decision);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, Config, null)).Returns(decision);
var optly = Helper.CreatePrivateOptimizely();
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
+ optly.SetFieldOrProperty("ProjectConfigManager", ConfigManager);
bool result = (bool)optly.Invoke("IsFeatureEnabled", featureKey, TestUserId, null);
Assert.False(result);
@@ -1779,10 +1831,11 @@ public void TestIsFeatureEnabledGivenFeatureFlagIsEnabledAndUserIsNotBeingExperi
var featureFlag = Config.GetFeatureFlagFromKey(featureKey);
var decision = new FeatureDecision(experiment, variation, FeatureDecision.DECISION_SOURCE_ROLLOUT);
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns(decision);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, Config, null)).Returns(decision);
var optly = Helper.CreatePrivateOptimizely();
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
+ optly.SetFieldOrProperty("ProjectConfigManager", ConfigManager);
bool result = (bool)optly.Invoke("IsFeatureEnabled", featureKey, TestUserId, null);
Assert.True(result);
@@ -1806,10 +1859,11 @@ public void TestIsFeatureEnabledGivenFeatureFlagIsEnabledAndUserIsBeingExperimen
var featureFlag = Config.GetFeatureFlagFromKey(featureKey);
var decision = new FeatureDecision(experiment, variation, FeatureDecision.DECISION_SOURCE_FEATURE_TEST);
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns(decision);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, Config, null)).Returns(decision);
var optly = Helper.CreatePrivateOptimizely();
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
+ optly.SetFieldOrProperty("ProjectConfigManager", ConfigManager);
bool result = (bool)optly.Invoke("IsFeatureEnabled", featureKey, TestUserId, null);
Assert.True(result);
@@ -1833,10 +1887,11 @@ public void TestIsFeatureEnabledGivenFeatureFlagIsNotEnabledAndUserIsBeingExperi
var featureFlag = Config.GetFeatureFlagFromKey(featureKey);
var decision = new FeatureDecision(experiment, variation, FeatureDecision.DECISION_SOURCE_FEATURE_TEST);
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns(decision);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, Config, null)).Returns(decision);
var optly = Helper.CreatePrivateOptimizely();
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
+ optly.SetFieldOrProperty("ProjectConfigManager", ConfigManager);
bool result = (bool)optly.Invoke("IsFeatureEnabled", featureKey, TestUserId, null);
Assert.False(result);
@@ -1884,8 +1939,8 @@ public void TestIsFeatureEnabledWithFeatureEnabledPropertyGivenFeatureExperiment
var decisionTrue = new FeatureDecision(experiment, featureEnabledTrue, FeatureDecision.DECISION_SOURCE_FEATURE_TEST);
var decisionFalse = new FeatureDecision(experiment, featureEnabledFalse, FeatureDecision.DECISION_SOURCE_FEATURE_TEST);
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns(decisionTrue);
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, userId, null)).Returns(decisionFalse);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, Config, null)).Returns(decisionTrue);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, userId, Config, null)).Returns(decisionFalse);
var optly = Helper.CreatePrivateOptimizely();
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
@@ -1907,7 +1962,7 @@ public void TestIsFeatureEnabledWithFeatureEnabledPropertyGivenRolloutRule()
// Verify that IsFeatureEnabled returns true when user is bucketed into the rollout rule's variation.
Assert.True(Optimizely.IsFeatureEnabled("boolean_single_variable_feature", TestUserId));
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns(null);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, Config, null)).Returns(null);
var optly = Helper.CreatePrivateOptimizely();
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
@@ -1960,8 +2015,8 @@ public void TestActivateListener(UserAttributes userAttributes)
It.IsAny(), It.IsAny(), It.IsAny()));
EventBuilderMock.Setup(ebm => ebm.CreateImpressionEvent(It.IsAny(), It.IsAny(),
It.IsAny(), It.IsAny(), It.IsAny())).Returns(logEvent);
- DecisionServiceMock.Setup(ds => ds.GetVariation(experiment, TestUserId, userAttributes)).Returns(variation);
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, userAttributes)).Returns(decision);
+ DecisionServiceMock.Setup(ds => ds.GetVariation(experiment, TestUserId, It.IsAny(), userAttributes)).Returns(variation);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, It.IsAny(), userAttributes)).Returns(decision);
var optly = Helper.CreatePrivateOptimizely();
var optStronglyTyped = optly.GetObject() as Optimizely;
@@ -2041,7 +2096,7 @@ public void TestTrackListener(UserAttributes userAttributes, EventTags eventTags
EventBuilderMock.Setup(ebm => ebm.CreateConversionEvent(It.IsAny(), It.IsAny(),
It.IsAny(), It.IsAny(),
It.IsAny())).Returns(logEvent);
- DecisionServiceMock.Setup(ds => ds.GetVariation(experiment, TestUserId, userAttributes)).Returns(variation);
+ DecisionServiceMock.Setup(ds => ds.GetVariation(experiment, TestUserId, Config, userAttributes)).Returns(variation);
// Adding notification listeners.
var notificationType = NotificationCenter.NotificationType.Track;
@@ -2078,9 +2133,10 @@ public void TestActivateSendsDecisionNotificationWithActualVariationKey()
// Mocking objects.
NotificationCallbackMock.Setup(nc => nc.TestDecisionCallback(It.IsAny(), It.IsAny(),
It.IsAny(), It.IsAny>()));
- DecisionServiceMock.Setup(ds => ds.GetVariation(experiment, TestUserId, userAttributes)).Returns(variation);
+ DecisionServiceMock.Setup(ds => ds.GetVariation(experiment, TestUserId, Config, userAttributes)).Returns(variation);
var optly = Helper.CreatePrivateOptimizely();
+ optly.SetFieldOrProperty("ProjectConfigManager", ConfigManager);
var optStronglyTyped = optly.GetObject() as Optimizely;
optStronglyTyped.NotificationCenter.AddNotification(NotificationCenter.NotificationType.Decision, NotificationCallbackMock.Object.TestDecisionCallback);
@@ -2113,9 +2169,10 @@ public void TestActivateSendsDecisionNotificationWithVariationKeyAndTypeFeatureT
// Mocking objects.
NotificationCallbackMock.Setup(nc => nc.TestDecisionCallback(It.IsAny(), It.IsAny(),
It.IsAny(), It.IsAny>()));
- DecisionServiceMock.Setup(ds => ds.GetVariation(experiment, TestUserId, userAttributes)).Returns(variation);
+ DecisionServiceMock.Setup(ds => ds.GetVariation(experiment, TestUserId, Config, userAttributes)).Returns(variation);
var optly = Helper.CreatePrivateOptimizely();
+ optly.SetFieldOrProperty("ProjectConfigManager", ConfigManager);
var optStronglyTyped = optly.GetObject() as Optimizely;
optStronglyTyped.NotificationCenter.AddNotification(NotificationCenter.NotificationType.Decision, NotificationCallbackMock.Object.TestDecisionCallback);
@@ -2139,7 +2196,7 @@ public void TestActivateSendsDecisionNotificationWithNullVariationKey()
NotificationCallbackMock.Setup(nc => nc.TestDecisionCallback(It.IsAny(), It.IsAny(),
It.IsAny(), It.IsAny>()));
- DecisionServiceMock.Setup(ds => ds.GetVariation(experiment, TestUserId, null)).Returns(null);
+ DecisionServiceMock.Setup(ds => ds.GetVariation(experiment, TestUserId, Config, null)).Returns(null);
var optly = Helper.CreatePrivateOptimizely();
var optStronglyTyped = optly.GetObject() as Optimizely;
@@ -2174,9 +2231,11 @@ public void TestGetVariationSendsDecisionNotificationWithActualVariationKey()
// Mocking objects.
NotificationCallbackMock.Setup(nc => nc.TestDecisionCallback(It.IsAny(), It.IsAny(),
It.IsAny(), It.IsAny>()));
- DecisionServiceMock.Setup(ds => ds.GetVariation(experiment, TestUserId, userAttributes)).Returns(variation);
+ DecisionServiceMock.Setup(ds => ds.GetVariation(experiment, TestUserId, Config, userAttributes)).Returns(variation);
var optly = Helper.CreatePrivateOptimizely();
+
+ optly.SetFieldOrProperty("ProjectConfigManager", ConfigManager);
var optStronglyTyped = optly.GetObject() as Optimizely;
optStronglyTyped.NotificationCenter.AddNotification(NotificationCenter.NotificationType.Decision, NotificationCallbackMock.Object.TestDecisionCallback);
@@ -2209,13 +2268,14 @@ public void TestGetVariationSendsDecisionNotificationWithVariationKeyAndTypeFeat
// Mocking objects.
NotificationCallbackMock.Setup(nc => nc.TestDecisionCallback(It.IsAny(), It.IsAny(),
It.IsAny(), It.IsAny>()));
- DecisionServiceMock.Setup(ds => ds.GetVariation(experiment, TestUserId, userAttributes)).Returns(variation);
+ DecisionServiceMock.Setup(ds => ds.GetVariation(experiment, TestUserId, Config, userAttributes)).Returns(variation);
var optly = Helper.CreatePrivateOptimizely();
var optStronglyTyped = optly.GetObject() as Optimizely;
optStronglyTyped.NotificationCenter.AddNotification(NotificationCenter.NotificationType.Decision, NotificationCallbackMock.Object.TestDecisionCallback);
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
+ optly.SetFieldOrProperty("ProjectConfigManager", ConfigManager);
optly.Invoke("GetVariation", experimentKey, TestUserId, userAttributes);
var decisionInfo = new Dictionary
@@ -2235,7 +2295,7 @@ public void TestGetVariationSendsDecisionNotificationWithNullVariationKey()
NotificationCallbackMock.Setup(nc => nc.TestDecisionCallback(It.IsAny(), It.IsAny(),
It.IsAny(), It.IsAny>()));
- DecisionServiceMock.Setup(ds => ds.GetVariation(experiment, TestUserId, null)).Returns(null);
+ DecisionServiceMock.Setup(ds => ds.GetVariation(experiment, TestUserId, Config, null)).Returns(null);
var optly = Helper.CreatePrivateOptimizely();
var optStronglyTyped = optly.GetObject() as Optimizely;
@@ -2260,10 +2320,10 @@ public void TestIsFeatureEnabledSendsDecisionNotificationWithFeatureEnabledTrueF
var featureFlag = Config.GetFeatureFlagFromKey(featureKey);
var decision = new FeatureDecision(experiment, variation, FeatureDecision.DECISION_SOURCE_FEATURE_TEST);
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns(decision);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, Config, null)).Returns(decision);
NotificationCallbackMock.Setup(nc => nc.TestDecisionCallback(It.IsAny(), It.IsAny(),
It.IsAny(), It.IsAny>()));
- DecisionServiceMock.Setup(ds => ds.GetVariation(experiment, TestUserId, null)).Returns(variation);
+ DecisionServiceMock.Setup(ds => ds.GetVariation(experiment, TestUserId, ConfigManager.GetConfig(), null)).Returns(variation);
var optly = Helper.CreatePrivateOptimizely();
var optStronglyTyped = optly.GetObject() as Optimizely;
@@ -2295,16 +2355,17 @@ public void TestIsFeatureEnabledSendsDecisionNotificationWithFeatureEnabledFalse
var featureFlag = Config.GetFeatureFlagFromKey(featureKey);
var decision = new FeatureDecision(experiment, variation, FeatureDecision.DECISION_SOURCE_FEATURE_TEST);
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns(decision);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, Config, null)).Returns(decision);
NotificationCallbackMock.Setup(nc => nc.TestDecisionCallback(It.IsAny(), It.IsAny(),
It.IsAny(), It.IsAny>()));
- DecisionServiceMock.Setup(ds => ds.GetVariation(experiment, TestUserId, null)).Returns(variation);
+ DecisionServiceMock.Setup(ds => ds.GetVariation(experiment, TestUserId, Config, null)).Returns(variation);
var optly = Helper.CreatePrivateOptimizely();
var optStronglyTyped = optly.GetObject() as Optimizely;
optStronglyTyped.NotificationCenter.AddNotification(NotificationCenter.NotificationType.Decision, NotificationCallbackMock.Object.TestDecisionCallback);
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
+ optly.SetFieldOrProperty("ProjectConfigManager", ConfigManager);
bool result = (bool)optly.Invoke("IsFeatureEnabled", featureKey, TestUserId, null);
Assert.False(result);
@@ -2339,11 +2400,12 @@ public void TestIsFeatureEnabledSendsDecisionNotificationWithFeatureEnabledTrueF
var featureFlag = Config.GetFeatureFlagFromKey(featureKey);
var decision = new FeatureDecision(experiment, variation, FeatureDecision.DECISION_SOURCE_ROLLOUT);
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, userAttributes)).Returns(decision);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, Config, userAttributes)).Returns(decision);
NotificationCallbackMock.Setup(nc => nc.TestDecisionCallback(It.IsAny(), It.IsAny(),
It.IsAny(), It.IsAny>()));
var optly = Helper.CreatePrivateOptimizely();
+ optly.SetFieldOrProperty("ProjectConfigManager", ConfigManager);
var optStronglyTyped = optly.GetObject() as Optimizely;
optStronglyTyped.NotificationCenter.AddNotification(NotificationCenter.NotificationType.Decision, NotificationCallbackMock.Object.TestDecisionCallback);
@@ -2377,7 +2439,7 @@ public void TestIsFeatureEnabledSendsDecisionNotificationWithFeatureEnabledFalse
var featureFlag = Config.GetFeatureFlagFromKey(featureKey);
var decision = new FeatureDecision(experiment, variation, FeatureDecision.DECISION_SOURCE_ROLLOUT);
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, userAttributes)).Returns(decision);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, Config, userAttributes)).Returns(decision);
NotificationCallbackMock.Setup(nc => nc.TestDecisionCallback(It.IsAny(), It.IsAny(),
It.IsAny(), It.IsAny>()));
@@ -2386,6 +2448,7 @@ public void TestIsFeatureEnabledSendsDecisionNotificationWithFeatureEnabledFalse
optStronglyTyped.NotificationCenter.AddNotification(NotificationCenter.NotificationType.Decision, NotificationCallbackMock.Object.TestDecisionCallback);
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
+ optly.SetFieldOrProperty("ProjectConfigManager", ConfigManager);
bool result = (bool)optly.Invoke("IsFeatureEnabled", featureKey, TestUserId, userAttributes);
Assert.False(result);
@@ -2408,7 +2471,7 @@ public void TestIsFeatureEnabledSendsDecisionNotificationWithFeatureEnabledFalse
var featureFlag = Config.GetFeatureFlagFromKey(featureKey);
var decision = new FeatureDecision(null, null, FeatureDecision.DECISION_SOURCE_ROLLOUT);
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns(decision);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, Config, null)).Returns(decision);
NotificationCallbackMock.Setup(nc => nc.TestDecisionCallback(It.IsAny(), It.IsAny(),
It.IsAny(), It.IsAny>()));
@@ -2417,6 +2480,7 @@ public void TestIsFeatureEnabledSendsDecisionNotificationWithFeatureEnabledFalse
optStronglyTyped.NotificationCenter.AddNotification(NotificationCenter.NotificationType.Decision, NotificationCallbackMock.Object.TestDecisionCallback);
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
+ optly.SetFieldOrProperty("ProjectConfigManager", ConfigManager);
bool result = (bool)optly.Invoke("IsFeatureEnabled", featureKey, TestUserId, null);
Assert.False(result);
@@ -2547,7 +2611,7 @@ public void TestGetFeatureVariableDoubleSendsNotificationWhenUserBuckedIntoFeatu
var variation = Config.GetVariationFromKey("test_experiment_double_feature", "control");
var decision = new FeatureDecision(experiment, variation, FeatureDecision.DECISION_SOURCE_FEATURE_TEST);
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns(decision);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, Config, null)).Returns(decision);
NotificationCallbackMock.Setup(nc => nc.TestDecisionCallback(It.IsAny(), It.IsAny(), It.IsAny(),
It.IsAny>()));
@@ -2555,6 +2619,8 @@ public void TestGetFeatureVariableDoubleSendsNotificationWhenUserBuckedIntoFeatu
var optStronglyTyped = optly.GetObject() as Optimizely;
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
+ optly.SetFieldOrProperty("ProjectConfigManager", ConfigManager);
+
optStronglyTyped.NotificationCenter.AddNotification(NotificationCenter.NotificationType.Decision, NotificationCallbackMock.Object.TestDecisionCallback);
var variableValue = (double)optly.Invoke("GetFeatureVariableDouble", featureKey, variableKey, TestUserId, null);
@@ -2595,11 +2661,13 @@ public void TestGetFeatureVariableIntegerSendsNotificationWhenUserBuckedIntoFeat
{ "location", "San Francisco" }
};
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, userAttributes)).Returns(decision);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, Config, userAttributes)).Returns(decision);
NotificationCallbackMock.Setup(nc => nc.TestDecisionCallback(It.IsAny(), It.IsAny(), It.IsAny(),
It.IsAny>()));
var optly = Helper.CreatePrivateOptimizely();
+ optly.SetFieldOrProperty("ProjectConfigManager", ConfigManager);
+
var optStronglyTyped = optly.GetObject() as Optimizely;
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
@@ -2637,7 +2705,7 @@ public void TestGetFeatureVariableDoubleSendsNotificationWhenUserBuckedIntoFeatu
var variation = Config.GetVariationFromKey("test_experiment_double_feature", "variation");
var decision = new FeatureDecision(experiment, variation, FeatureDecision.DECISION_SOURCE_FEATURE_TEST);
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns(decision);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, Config, null)).Returns(decision);
NotificationCallbackMock.Setup(nc => nc.TestDecisionCallback(It.IsAny(), It.IsAny(), It.IsAny(),
It.IsAny>()));
@@ -2645,6 +2713,7 @@ public void TestGetFeatureVariableDoubleSendsNotificationWhenUserBuckedIntoFeatu
var optStronglyTyped = optly.GetObject() as Optimizely;
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
+ optly.SetFieldOrProperty("ProjectConfigManager", ConfigManager);
optStronglyTyped.NotificationCenter.AddNotification(NotificationCenter.NotificationType.Decision, NotificationCallbackMock.Object.TestDecisionCallback);
var variableValue = (double)optly.Invoke("GetFeatureVariableDouble", featureKey, variableKey, TestUserId, null);
@@ -2685,11 +2754,12 @@ public void TestGetFeatureVariableIntegerSendsNotificationWhenUserBuckedIntoFeat
{ "location", "San Francisco" }
};
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, userAttributes)).Returns(decision);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, Config, userAttributes)).Returns(decision);
NotificationCallbackMock.Setup(nc => nc.TestDecisionCallback(It.IsAny(), It.IsAny(), It.IsAny(),
It.IsAny>()));
var optly = Helper.CreatePrivateOptimizely();
+ optly.SetFieldOrProperty("ProjectConfigManager", ConfigManager);
var optStronglyTyped = optly.GetObject() as Optimizely;
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
@@ -2727,11 +2797,12 @@ public void TestGetFeatureVariableBooleanSendsNotificationWhenUserBuckedIntoRoll
var variation = Config.GetVariationFromKey(experiment.Key, "177771");
var decision = new FeatureDecision(experiment, variation, FeatureDecision.DECISION_SOURCE_ROLLOUT);
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns(decision);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, Config, null)).Returns(decision);
NotificationCallbackMock.Setup(nc => nc.TestDecisionCallback(It.IsAny(), It.IsAny(), It.IsAny(),
It.IsAny>()));
var optly = Helper.CreatePrivateOptimizely();
+ optly.SetFieldOrProperty("ProjectConfigManager", ConfigManager);
var optStronglyTyped = optly.GetObject() as Optimizely;
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
@@ -2770,11 +2841,13 @@ public void TestGetFeatureVariableStringSendsNotificationWhenUserBuckedIntoRollo
{ "location", "San Francisco" }
};
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, userAttributes)).Returns(decision);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, Config, userAttributes)).Returns(decision);
NotificationCallbackMock.Setup(nc => nc.TestDecisionCallback(It.IsAny(), It.IsAny(), It.IsAny(),
It.IsAny>()));
var optly = Helper.CreatePrivateOptimizely();
+
+ optly.SetFieldOrProperty("ProjectConfigManager", ConfigManager);
var optStronglyTyped = optly.GetObject() as Optimizely;
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
@@ -2807,7 +2880,7 @@ public void TestGetFeatureVariableBooleanSendsNotificationWhenUserBuckedIntoRoll
var variation = Config.GetVariationFromKey(experiment.Key, "177782");
var decision = new FeatureDecision(experiment, variation, FeatureDecision.DECISION_SOURCE_ROLLOUT);
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns(decision);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, Config, null)).Returns(decision);
NotificationCallbackMock.Setup(nc => nc.TestDecisionCallback(It.IsAny(), It.IsAny(), It.IsAny(),
It.IsAny>()));
@@ -2815,6 +2888,7 @@ public void TestGetFeatureVariableBooleanSendsNotificationWhenUserBuckedIntoRoll
var optStronglyTyped = optly.GetObject() as Optimizely;
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
+ optly.SetFieldOrProperty("ProjectConfigManager", ConfigManager);
optStronglyTyped.NotificationCenter.AddNotification(NotificationCenter.NotificationType.Decision, NotificationCallbackMock.Object.TestDecisionCallback);
var variableValue = (bool)optly.Invoke("GetFeatureVariableBoolean", featureKey, variableKey, TestUserId, null);
@@ -2850,11 +2924,12 @@ public void TestGetFeatureVariableStringSendsNotificationWhenUserBuckedIntoRollo
{ "location", "San Francisco" }
};
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, userAttributes)).Returns(decision);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, Config, userAttributes)).Returns(decision);
NotificationCallbackMock.Setup(nc => nc.TestDecisionCallback(It.IsAny(), It.IsAny(), It.IsAny(),
It.IsAny>()));
var optly = Helper.CreatePrivateOptimizely();
+ optly.SetFieldOrProperty("ProjectConfigManager", ConfigManager);
var optStronglyTyped = optly.GetObject() as Optimizely;
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
@@ -2885,7 +2960,7 @@ public void TestGetFeatureVariableDoubleSendsNotificationWhenUserNotBuckedIntoBo
var expectedValue = 14.99;
var decision = new FeatureDecision(null, null, FeatureDecision.DECISION_SOURCE_ROLLOUT);
- DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns(decision);
+ DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, Config, null)).Returns(decision);
NotificationCallbackMock.Setup(nc => nc.TestDecisionCallback(It.IsAny(), It.IsAny(), It.IsAny(),
It.IsAny>()));
@@ -2893,6 +2968,7 @@ public void TestGetFeatureVariableDoubleSendsNotificationWhenUserNotBuckedIntoBo
var optStronglyTyped = optly.GetObject() as Optimizely;
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
+ optly.SetFieldOrProperty("ProjectConfigManager", ConfigManager);
optStronglyTyped.NotificationCenter.AddNotification(NotificationCenter.NotificationType.Decision, NotificationCallbackMock.Object.TestDecisionCallback);
var variableValue = (double)optly.Invoke("GetFeatureVariableDouble", featureKey, variableKey, TestUserId, null);
@@ -2921,6 +2997,7 @@ public void TestGetFeatureVariableDoubleSendsNotificationWhenUserNotBuckedIntoBo
public void TestGetEnabledFeaturesWithInvalidDatafile()
{
var optly = new Optimizely("Random datafile", null, LoggerMock.Object);
+
Assert.IsEmpty(optly.GetEnabledFeatures("some_user", null));
LoggerMock.Verify(l => l.Log(LogLevel.ERROR, "Datafile has invalid format. Failing 'GetEnabledFeatures'."), Times.Once);
diff --git a/OptimizelySDK.Tests/ProjectConfigTest.cs b/OptimizelySDK.Tests/ProjectConfigTest.cs
index 2ee770c2..47f47d2b 100644
--- a/OptimizelySDK.Tests/ProjectConfigTest.cs
+++ b/OptimizelySDK.Tests/ProjectConfigTest.cs
@@ -669,41 +669,41 @@ public void TestSetGetForcedVariation()
var optlyObject = new Optimizely(TestData.Datafile, new ValidEventDispatcher(), LoggerMock.Object);
optlyObject.Activate("test_experiment", "test_user", userAttributes);
- // invalid experiment key should return a null variation
+ // invalid experiment key should return a null variation
Assert.False(Config.SetForcedVariation(invalidExperimentKey, userId, expectedVariationKey));
Assert.Null(Config.GetForcedVariation(invalidExperimentKey, userId));
- // setting a null variation should return a null variation
+ // setting a null variation should return a null variation
Assert.True(Config.SetForcedVariation(experimentKey, userId, null));
Assert.Null(Config.GetForcedVariation(experimentKey, userId));
- // setting an invalid variation should return a null variation
+ // setting an invalid variation should return a null variation
Assert.False(Config.SetForcedVariation(experimentKey, userId, invalidVariationKey));
Assert.Null(Config.GetForcedVariation(experimentKey, userId));
- // confirm the forced variation is returned after a set
+ // confirm the forced variation is returned after a set
Assert.True(Config.SetForcedVariation(experimentKey, userId, expectedVariationKey));
var actualForcedVariation = Config.GetForcedVariation(experimentKey, userId);
Assert.AreEqual(expectedVariationKey, actualForcedVariation.Key);
- // check multiple sets
+ // check multiple sets
Assert.True(Config.SetForcedVariation(experimentKey2, userId, expectedVariationKey2));
var actualForcedVariation2 = Config.GetForcedVariation(experimentKey2, userId);
Assert.AreEqual(expectedVariationKey2, actualForcedVariation2.Key);
- // make sure the second set does not overwrite the first set
+ // make sure the second set does not overwrite the first set
actualForcedVariation = Config.GetForcedVariation(experimentKey, userId);
Assert.AreEqual(expectedVariationKey, actualForcedVariation.Key);
- // make sure unsetting the second experiment-to-variation mapping does not unset the
- // first experiment-to-variation mapping
+ // make sure unsetting the second experiment-to-variation mapping does not unset the
+ // first experiment-to-variation mapping
Assert.True(Config.SetForcedVariation(experimentKey2, userId, null));
actualForcedVariation = Config.GetForcedVariation(experimentKey, userId);
Assert.AreEqual(expectedVariationKey, actualForcedVariation.Key);
- // an invalid user ID should return a null variation
+ // an invalid user ID should return a null variation
Assert.Null(Config.GetForcedVariation(experimentKey, invalidUserId));
}
- // test that all the logs in setForcedVariation are getting called
+ // test that all the logs in setForcedVariation are getting called
[Test]
public void TestSetForcedVariationLogs()
{
@@ -714,7 +714,7 @@ public void TestSetForcedVariationLogs()
var variationKey = "control";
var variationId = "7722370027";
var invalidVariationKey = "invalid_variation";
-
+
Config.SetForcedVariation(invalidExperimentKey, userId, variationKey);
Config.SetForcedVariation(experimentKey, userId, null);
Config.SetForcedVariation(experimentKey, userId, invalidVariationKey);
@@ -727,7 +727,7 @@ public void TestSetForcedVariationLogs()
LoggerMock.Verify(l => l.Log(LogLevel.DEBUG, string.Format(@"Set variation ""{0}"" for experiment ""{1}"" and user ""{2}"" in the forced variation map.", variationId, experimentId, userId)));
}
- // test that all the logs in getForcedVariation are getting called
+ // test that all the logs in getForcedVariation are getting called
[Test]
public void TestGetForcedVariationLogs()
{
@@ -753,30 +753,30 @@ public void TestGetForcedVariationLogs()
LoggerMock.Verify(l => l.Log(LogLevel.DEBUG, string.Format(@"No experiment ""{0}"" mapped to user ""{1}"" in the forced variation map.", pausedExperimentKey, userId)));
LoggerMock.Verify(l => l.Log(LogLevel.DEBUG, string.Format(@"Variation ""{0}"" is mapped to experiment ""{1}"" and user ""{2}"" in the forced variation map", variationKey, experimentKey, userId)));
}
-
+
[Test]
public void TestSetForcedVariationMultipleSets()
{
Assert.True(Config.SetForcedVariation("test_experiment", "test_user_1", "variation"));
Assert.AreEqual(Config.GetForcedVariation("test_experiment", "test_user_1").Key, "variation");
-
- // same user, same experiment, different variation
+
+ // same user, same experiment, different variation
Assert.True(Config.SetForcedVariation("test_experiment", "test_user_1", "control"));
Assert.AreEqual(Config.GetForcedVariation("test_experiment", "test_user_1").Key, "control");
-
- // same user, different experiment
+
+ // same user, different experiment
Assert.True(Config.SetForcedVariation("group_experiment_1", "test_user_1", "group_exp_1_var_1"));
Assert.AreEqual(Config.GetForcedVariation("group_experiment_1", "test_user_1").Key, "group_exp_1_var_1");
- // different user
+ // different user
Assert.True(Config.SetForcedVariation("test_experiment", "test_user_2", "variation"));
Assert.AreEqual(Config.GetForcedVariation("test_experiment", "test_user_2").Key, "variation");
-
- // different user, different experiment
+
+ // different user, different experiment
Assert.True(Config.SetForcedVariation("group_experiment_1", "test_user_2", "group_exp_1_var_1"));
Assert.AreEqual(Config.GetForcedVariation("group_experiment_1", "test_user_2").Key, "group_exp_1_var_1");
- // make sure the first user forced variations are still valid
+ // make sure the first user forced variations are still valid
Assert.AreEqual(Config.GetForcedVariation("test_experiment", "test_user_1").Key, "control");
Assert.AreEqual(Config.GetForcedVariation("group_experiment_1", "test_user_1").Key, "group_exp_1_var_1");
}
diff --git a/OptimizelySDK/Bucketing/DecisionService.cs b/OptimizelySDK/Bucketing/DecisionService.cs
index e9369a26..1fd75b15 100644
--- a/OptimizelySDK/Bucketing/DecisionService.cs
+++ b/OptimizelySDK/Bucketing/DecisionService.cs
@@ -37,7 +37,6 @@ public class DecisionService
{
private Bucketer Bucketer;
private IErrorHandler ErrorHandler;
- private ProjectConfig ProjectConfig;
private UserProfileService UserProfileService;
private ILogger Logger;
@@ -46,14 +45,12 @@ public class DecisionService
///
/// Base bucketer to allocate new users to an experiment.
/// The error handler of the Optimizely client.
- /// Optimizely Project Config representing the datafile.
/// param >
/// < param name= "logger" > UserProfileService implementation for storing user info.
- public DecisionService(Bucketer bucketer, IErrorHandler errorHandler, ProjectConfig projectConfig, UserProfileService userProfileService, ILogger logger)
+ public DecisionService(Bucketer bucketer, IErrorHandler errorHandler, UserProfileService userProfileService, ILogger logger)
{
Bucketer = bucketer;
ErrorHandler = errorHandler;
- ProjectConfig = projectConfig;
UserProfileService = userProfileService;
Logger = logger;
}
@@ -65,12 +62,12 @@ public DecisionService(Bucketer bucketer, IErrorHandler errorHandler, ProjectCon
/// The userId of the user.
/// The user's attributes. This should be filtered to just attributes in the Datafile.
/// The Variation the user is allocated into.
- public virtual Variation GetVariation(Experiment experiment, string userId, UserAttributes filteredAttributes)
+ public virtual Variation GetVariation(Experiment experiment, string userId, ProjectConfig config, UserAttributes filteredAttributes)
{
if (!ExperimentUtils.IsExperimentActive(experiment, Logger)) return null;
// check if a forced variation is set
- var forcedVariation = ProjectConfig.GetForcedVariation(experiment.Key, userId);
+ var forcedVariation = config.GetForcedVariation(experiment.Key, userId);
if (forcedVariation != null)
return forcedVariation;
@@ -86,7 +83,7 @@ public virtual Variation GetVariation(Experiment experiment, string userId, User
if (userProfileMap != null && UserProfileUtil.IsValidUserProfileMap(userProfileMap))
{
userProfile = UserProfileUtil.ConvertMapToUserProfile(userProfileMap);
- variation = GetStoredVariation(experiment, userProfile);
+ variation = GetStoredVariation(experiment, userProfile, config);
if (variation != null) return variation;
}
else if (userProfileMap == null)
@@ -105,12 +102,12 @@ public virtual Variation GetVariation(Experiment experiment, string userId, User
}
}
- if (ExperimentUtils.IsUserInExperiment(ProjectConfig, experiment, filteredAttributes, Logger))
+ if (ExperimentUtils.IsUserInExperiment(config, experiment, filteredAttributes, Logger))
{
// Get Bucketing ID from user attributes.
string bucketingId = GetBucketingId(userId, filteredAttributes);
- variation = Bucketer.Bucket(ProjectConfig, experiment, bucketingId, userId);
+ variation = Bucketer.Bucket(config, experiment, bucketingId, userId);
if (variation != null && variation.Key != null)
{
@@ -165,7 +162,7 @@ public Variation GetWhitelistedVariation(Experiment experiment, string userId)
/// which the user was bucketed
/// User profile of the user
/// The user was previously bucketed into.
- public Variation GetStoredVariation(Experiment experiment, UserProfile userProfile)
+ public Variation GetStoredVariation(Experiment experiment, UserProfile userProfile, ProjectConfig config)
{
// ---------- Check User Profile for Sticky Bucketing ----------
// If a user profile instance is present then check it for a saved variation
@@ -185,8 +182,8 @@ public Variation GetStoredVariation(Experiment experiment, UserProfile userProfi
{
string variationId = decision.VariationId;
- Variation savedVariation = ProjectConfig.ExperimentIdMap[experimentId].VariationIdToVariationMap.ContainsKey(variationId)
- ? ProjectConfig.ExperimentIdMap[experimentId].VariationIdToVariationMap[variationId]
+ Variation savedVariation = config.ExperimentIdMap[experimentId].VariationIdToVariationMap.ContainsKey(variationId)
+ ? config.ExperimentIdMap[experimentId].VariationIdToVariationMap[variationId]
: null;
if (savedVariation == null)
@@ -255,7 +252,7 @@ public void SaveVariation(Experiment experiment, Variation variation, UserProfil
/// The user's attributes. This should be filtered to just attributes in the Datafile.
/// null if the user is not bucketed into the rollout or if the feature flag was not attached to a rollout.
/// otherwise the FeatureDecision entity
- public virtual FeatureDecision GetVariationForFeatureRollout(FeatureFlag featureFlag, string userId, UserAttributes filteredAttributes)
+ public virtual FeatureDecision GetVariationForFeatureRollout(FeatureFlag featureFlag, string userId, UserAttributes filteredAttributes, ProjectConfig config)
{
if (featureFlag == null)
{
@@ -269,7 +266,7 @@ public virtual FeatureDecision GetVariationForFeatureRollout(FeatureFlag feature
return null;
}
- Rollout rollout = ProjectConfig.GetRolloutFromId(featureFlag.RolloutId);
+ Rollout rollout = config.GetRolloutFromId(featureFlag.RolloutId);
if (string.IsNullOrEmpty(rollout.Id))
{
@@ -287,9 +284,9 @@ public virtual FeatureDecision GetVariationForFeatureRollout(FeatureFlag feature
for (int i=0; i < rolloutRulesLength - 1; i++)
{
var rolloutRule = rollout.Experiments[i];
- if (ExperimentUtils.IsUserInExperiment(ProjectConfig, rolloutRule, filteredAttributes, Logger))
+ if (ExperimentUtils.IsUserInExperiment(config, rolloutRule, filteredAttributes, Logger))
{
- variation = Bucketer.Bucket(ProjectConfig, rolloutRule, bucketingId, userId);
+ variation = Bucketer.Bucket(config, rolloutRule, bucketingId, userId);
if (variation == null || string.IsNullOrEmpty(variation.Id))
break;
@@ -297,22 +294,22 @@ public virtual FeatureDecision GetVariationForFeatureRollout(FeatureFlag feature
}
else
{
- var audience = ProjectConfig.GetAudience(rolloutRule.AudienceIds[0]);
+ var audience = config.GetAudience(rolloutRule.AudienceIds[0]);
Logger.Log(LogLevel.DEBUG, $"User \"{userId}\" does not meet the conditions to be in rollout rule for audience \"{audience.Name}\".");
}
}
// Get the last rule which is everyone else rule.
var everyoneElseRolloutRule = rollout.Experiments[rolloutRulesLength - 1];
- if (ExperimentUtils.IsUserInExperiment(ProjectConfig, everyoneElseRolloutRule, filteredAttributes, Logger))
+ if (ExperimentUtils.IsUserInExperiment(config, everyoneElseRolloutRule, filteredAttributes, Logger))
{
- variation = Bucketer.Bucket(ProjectConfig, everyoneElseRolloutRule, bucketingId, userId);
+ variation = Bucketer.Bucket(config, everyoneElseRolloutRule, bucketingId, userId);
if (variation != null && !string.IsNullOrEmpty(variation.Id))
return new FeatureDecision(everyoneElseRolloutRule, variation, FeatureDecision.DECISION_SOURCE_ROLLOUT);
}
else
{
- var audience = ProjectConfig.GetAudience(everyoneElseRolloutRule.AudienceIds[0]);
+ var audience = config.GetAudience(everyoneElseRolloutRule.AudienceIds[0]);
Logger.Log(LogLevel.DEBUG, $"User \"{userId}\" does not meet the conditions to be in rollout rule for audience \"{audience.Name}\".");
}
@@ -327,7 +324,7 @@ public virtual FeatureDecision GetVariationForFeatureRollout(FeatureFlag feature
/// The user's attributes. This should be filtered to just attributes in the Datafile.
/// null if the user is not bucketed into the rollout or if the feature flag was not attached to a rollout.
/// Otherwise the FeatureDecision entity
- public virtual FeatureDecision GetVariationForFeatureExperiment(FeatureFlag featureFlag, string userId, UserAttributes filteredAttributes)
+ public virtual FeatureDecision GetVariationForFeatureExperiment(FeatureFlag featureFlag, string userId, UserAttributes filteredAttributes, ProjectConfig config)
{
if (featureFlag == null)
{
@@ -343,12 +340,12 @@ public virtual FeatureDecision GetVariationForFeatureExperiment(FeatureFlag feat
foreach (var experimentId in featureFlag.ExperimentIds)
{
- var experiment = ProjectConfig.GetExperimentFromId(experimentId);
+ var experiment = config.GetExperimentFromId(experimentId);
if (string.IsNullOrEmpty(experiment.Key))
continue;
- var variation = GetVariation(experiment, userId, filteredAttributes);
+ var variation = GetVariation(experiment, userId, config, filteredAttributes);
if (variation != null && !string.IsNullOrEmpty(variation.Id))
{
@@ -369,16 +366,16 @@ public virtual FeatureDecision GetVariationForFeatureExperiment(FeatureFlag feat
/// The user's attributes. This should be filtered to just attributes in the Datafile.
/// null if the user is not bucketed into any variation or the FeatureDecision entity if the user is
/// successfully bucketed.
- public virtual FeatureDecision GetVariationForFeature(FeatureFlag featureFlag, string userId, UserAttributes filteredAttributes)
+ public virtual FeatureDecision GetVariationForFeature(FeatureFlag featureFlag, string userId, ProjectConfig config, UserAttributes filteredAttributes)
{
// Check if the feature flag has an experiment and the user is bucketed into that experiment.
- var decision = GetVariationForFeatureExperiment(featureFlag, userId, filteredAttributes);
+ var decision = GetVariationForFeatureExperiment(featureFlag, userId, filteredAttributes, config);
if (decision != null)
return decision;
// Check if the feature flag has rollout and the the user is bucketed into one of its rules.
- decision = GetVariationForFeatureRollout(featureFlag, userId, filteredAttributes);
+ decision = GetVariationForFeatureRollout(featureFlag, userId, filteredAttributes, config);
if (decision != null)
{
diff --git a/OptimizelySDK/Config/DatafileProjectConfig.cs b/OptimizelySDK/Config/DatafileProjectConfig.cs
index 82c2878d..bd9b6e87 100644
--- a/OptimizelySDK/Config/DatafileProjectConfig.cs
+++ b/OptimizelySDK/Config/DatafileProjectConfig.cs
@@ -24,8 +24,14 @@
namespace OptimizelySDK.Config
{
+ ///
+ /// Implementation of ProjectConfig interface backed by a JSON data file.
+ ///
public class DatafileProjectConfig : ProjectConfig
{
+ ///
+ /// Datafile versions.
+ ///
public enum OPTLYSDKVersion
{
V2 = 2,
@@ -33,6 +39,9 @@ public enum OPTLYSDKVersion
V4 = 4
}
+ ///
+ /// Prefix used for reserved attributes.
+ ///
public const string RESERVED_ATTRIBUTE_PREFIX = "$opt_";
///
@@ -68,7 +77,9 @@ public enum OPTLYSDKVersion
///
public bool? BotFiltering { get; set; }
-
+ ///
+ /// Supported datafile versions list.
+ ///
private static List SupportedVersions = new List {
OPTLYSDKVersion.V2,
OPTLYSDKVersion.V3,
@@ -128,12 +139,12 @@ private Dictionary> _VariationIdMap
private Dictionary _AudienceIdMap;
public Dictionary AudienceIdMap { get { return _AudienceIdMap; } }
- ///
- /// Associative array of user IDs to an associative array
- /// of experiments to variations.This contains all the forced variations
- /// set by the user by calling setForcedVariation (it is not the same as the
- /// whitelisting forcedVariations data structure in the Experiments class).
- ///
+ ///
+ /// Associative array of user IDs to an associative array
+ /// of experiments to variations.This contains all the forced variations
+ /// set by the user by calling setForcedVariation (it is not the same as the
+ /// whitelisting forcedVariations data structure in the Experiments class).
+ ///
private Dictionary> _ForcedVariationMap;
public Dictionary> ForcedVariationMap { get { return _ForcedVariationMap; } }
@@ -307,6 +318,13 @@ private void Initialize()
}
}
+ ///
+ /// Parse datafile string to create ProjectConfig instance.
+ ///
+ /// datafile
+ /// Logger instance
+ /// ErrorHandler instance
+ /// ProjectConfig instance created from datafile string
public static ProjectConfig Create(string content, ILogger logger, IErrorHandler errorHandler)
{
DatafileProjectConfig config = GetConfig(content);
@@ -473,102 +491,102 @@ public Variation GetVariationFromId(string experimentKey, string variationId)
return new Variation();
}
- ///
- /// Gets the forced variation for the given user and experiment.
- ///
- /// The experiment key
- /// The user ID
- /// Variation entity which the given user and experiment should be forced into.
- public Variation GetForcedVariation(string experimentKey, string userId)
- {
- if (_ForcedVariationMap.ContainsKey(userId) == false)
- {
- Logger.Log(LogLevel.DEBUG, string.Format(@"User ""{0}"" is not in the forced variation map.", userId));
- return null;
- }
-
- Dictionary experimentToVariationMap = _ForcedVariationMap[userId];
-
- string experimentId = GetExperimentFromKey(experimentKey).Id;
-
- // this case is logged in getExperimentFromKey
- if (string.IsNullOrEmpty(experimentId))
- return null;
-
- if (experimentToVariationMap.ContainsKey(experimentId) == false)
- {
- Logger.Log(LogLevel.DEBUG, string.Format(@"No experiment ""{0}"" mapped to user ""{1}"" in the forced variation map.", experimentKey, userId));
- return null;
- }
-
- string variationId = experimentToVariationMap[experimentId];
-
- if (string.IsNullOrEmpty(variationId))
- {
- Logger.Log(LogLevel.DEBUG, string.Format(@"No variation mapped to experiment ""{0}"" in the forced variation map.", experimentKey));
- return null;
- }
-
- string variationKey = GetVariationFromId(experimentKey, variationId).Key;
-
- // this case is logged in getVariationFromKey
- if (string.IsNullOrEmpty(variationKey))
- return null;
-
- Logger.Log(LogLevel.DEBUG, string.Format(@"Variation ""{0}"" is mapped to experiment ""{1}"" and user ""{2}"" in the forced variation map", variationKey, experimentKey, userId));
-
- Variation variation = GetVariationFromKey(experimentKey, variationKey);
-
- return variation;
- }
-
- ///
- /// Sets an associative array of user IDs to an associative array of experiments to forced variations.
- ///
- /// The experiment key
- /// The user ID
- /// The variation key
- /// A boolean value that indicates if the set completed successfully.
- public bool SetForcedVariation(string experimentKey, string userId, string variationKey)
- {
- // Empty variation key is considered as invalid.
- if (variationKey != null && variationKey.Length == 0)
- {
- Logger.Log(LogLevel.DEBUG, "Variation key is invalid.");
- return false;
- }
-
- var experimentId = GetExperimentFromKey(experimentKey).Id;
-
- // this case is logged in getExperimentFromKey
- if (string.IsNullOrEmpty(experimentId))
- return false;
-
- // clear the forced variation if the variation key is null
- if (variationKey == null)
- {
- if (_ForcedVariationMap.ContainsKey(userId) && _ForcedVariationMap[userId].ContainsKey(experimentId))
- _ForcedVariationMap[userId].Remove(experimentId);
-
- Logger.Log(LogLevel.DEBUG, string.Format(@"Variation mapped to experiment ""{0}"" has been removed for user ""{1}"".", experimentKey, userId));
- return true;
- }
-
- string variationId = GetVariationFromKey(experimentKey, variationKey).Id;
-
- // this case is logged in getVariationFromKey
- if (string.IsNullOrEmpty(variationId))
- return false;
-
- // Add User if not exist.
- if (_ForcedVariationMap.ContainsKey(userId) == false)
- _ForcedVariationMap[userId] = new Dictionary();
-
- // Add/Replace Experiment to Variation ID map.
- _ForcedVariationMap[userId][experimentId] = variationId;
-
- Logger.Log(LogLevel.DEBUG, string.Format(@"Set variation ""{0}"" for experiment ""{1}"" and user ""{2}"" in the forced variation map.", variationId, experimentId, userId));
- return true;
+ ///
+ /// Gets the forced variation for the given user and experiment.
+ ///
+ /// The experiment key
+ /// The user ID
+ /// Variation entity which the given user and experiment should be forced into.
+ public Variation GetForcedVariation(string experimentKey, string userId)
+ {
+ if (_ForcedVariationMap.ContainsKey(userId) == false)
+ {
+ Logger.Log(LogLevel.DEBUG, string.Format(@"User ""{0}"" is not in the forced variation map.", userId));
+ return null;
+ }
+
+ Dictionary experimentToVariationMap = _ForcedVariationMap[userId];
+
+ string experimentId = GetExperimentFromKey(experimentKey).Id;
+
+ // this case is logged in getExperimentFromKey
+ if (string.IsNullOrEmpty(experimentId))
+ return null;
+
+ if (experimentToVariationMap.ContainsKey(experimentId) == false)
+ {
+ Logger.Log(LogLevel.DEBUG, string.Format(@"No experiment ""{0}"" mapped to user ""{1}"" in the forced variation map.", experimentKey, userId));
+ return null;
+ }
+
+ string variationId = experimentToVariationMap[experimentId];
+
+ if (string.IsNullOrEmpty(variationId))
+ {
+ Logger.Log(LogLevel.DEBUG, string.Format(@"No variation mapped to experiment ""{0}"" in the forced variation map.", experimentKey));
+ return null;
+ }
+
+ string variationKey = GetVariationFromId(experimentKey, variationId).Key;
+
+ // this case is logged in getVariationFromKey
+ if (string.IsNullOrEmpty(variationKey))
+ return null;
+
+ Logger.Log(LogLevel.DEBUG, string.Format(@"Variation ""{0}"" is mapped to experiment ""{1}"" and user ""{2}"" in the forced variation map", variationKey, experimentKey, userId));
+
+ Variation variation = GetVariationFromKey(experimentKey, variationKey);
+
+ return variation;
+ }
+
+ ///
+ /// Sets an associative array of user IDs to an associative array of experiments to forced variations.
+ ///
+ /// The experiment key
+ /// The user ID
+ /// The variation key
+ /// A boolean value that indicates if the set completed successfully.
+ public bool SetForcedVariation(string experimentKey, string userId, string variationKey)
+ {
+ // Empty variation key is considered as invalid.
+ if (variationKey != null && variationKey.Length == 0)
+ {
+ Logger.Log(LogLevel.DEBUG, "Variation key is invalid.");
+ return false;
+ }
+
+ var experimentId = GetExperimentFromKey(experimentKey).Id;
+
+ // this case is logged in getExperimentFromKey
+ if (string.IsNullOrEmpty(experimentId))
+ return false;
+
+ // clear the forced variation if the variation key is null
+ if (variationKey == null)
+ {
+ if (_ForcedVariationMap.ContainsKey(userId) && _ForcedVariationMap[userId].ContainsKey(experimentId))
+ _ForcedVariationMap[userId].Remove(experimentId);
+
+ Logger.Log(LogLevel.DEBUG, string.Format(@"Variation mapped to experiment ""{0}"" has been removed for user ""{1}"".", experimentKey, userId));
+ return true;
+ }
+
+ string variationId = GetVariationFromKey(experimentKey, variationKey).Id;
+
+ // this case is logged in getVariationFromKey
+ if (string.IsNullOrEmpty(variationId))
+ return false;
+
+ // Add User if not exist.
+ if (_ForcedVariationMap.ContainsKey(userId) == false)
+ _ForcedVariationMap[userId] = new Dictionary();
+
+ // Add/Replace Experiment to Variation ID map.
+ _ForcedVariationMap[userId][experimentId] = variationId;
+
+ Logger.Log(LogLevel.DEBUG, string.Format(@"Set variation ""{0}"" for experiment ""{1}"" and user ""{2}"" in the forced variation map.", variationId, experimentId, userId));
+ return true;
}
///
diff --git a/OptimizelySDK/Config/FallbackProjectConfigManager.cs b/OptimizelySDK/Config/FallbackProjectConfigManager.cs
index 5b08d23f..6f0d7b71 100644
--- a/OptimizelySDK/Config/FallbackProjectConfigManager.cs
+++ b/OptimizelySDK/Config/FallbackProjectConfigManager.cs
@@ -16,20 +16,31 @@
namespace OptimizelySDK.Config
{
- /// TODO: Cleanup done in other PR. sohail/optimizelydfm
+ ///
+ /// Implementation of ProjectConfigManager interface that simply
+ /// returns the stored ProjectConfig instance which is immmutable.
+ ///
public class FallbackProjectConfigManager : ProjectConfigManager
{
private ProjectConfig ProjectConfig;
- public ProjectConfig GetConfig()
+ ///
+ /// Initializes a new instance of the FallbackProjectConfigManager class
+ /// with the given ProjectConfig instance.
+ ///
+ /// Config.
+ public FallbackProjectConfigManager(ProjectConfig config)
{
- return ProjectConfig;
+ ProjectConfig = config;
}
- public bool SetConfig(ProjectConfig projectConfig)
+ ///
+ /// Returns the stored ProjectConfig instance.
+ ///
+ /// ProjectConfig instance
+ public ProjectConfig GetConfig()
{
- ProjectConfig = projectConfig;
- return true;
+ return ProjectConfig;
}
}
}
diff --git a/OptimizelySDK/Config/PollingProjectConfigManager.cs b/OptimizelySDK/Config/PollingProjectConfigManager.cs
index 24c621b1..f61334c5 100644
--- a/OptimizelySDK/Config/PollingProjectConfigManager.cs
+++ b/OptimizelySDK/Config/PollingProjectConfigManager.cs
@@ -23,6 +23,12 @@
namespace OptimizelySDK.Config
{
+ ///
+ /// Abstract class that implements ProjectConfigManager interface and provides
+ /// basic scheduling and caching.
+ /// Instances of this class, must implement the method
+ /// which is responsible for fetching a given ProjectConfig.
+ ///
public abstract class PollingProjectConfigManager : ProjectConfigManager
{
private TimeSpan PollingInterval;
@@ -57,8 +63,15 @@ public PollingProjectConfigManager(TimeSpan period, TimeSpan blockingTimeout, IL
}
+ ///
+ /// Abstract method for fetching ProjectConfig instance.
+ ///
+ /// ProjectConfig instance
protected abstract ProjectConfig Poll();
+ ///
+ /// Starts datafile scheduler.
+ ///
public void Start()
{
if (IsStarted)
@@ -72,6 +85,9 @@ public void Start()
IsStarted = true;
}
+ ///
+ /// Stops datafile scheduler.
+ ///
public void Stop()
{
// don't call now and onwards.
@@ -81,6 +97,11 @@ public void Stop()
Logger.Log(LogLevel.WARN, $"Stopping Config scheduler.");
}
+ ///
+ /// Retrieve ProjectConfig instance and waits untill the instance
+ /// gets available or blocking timeout expires.
+ ///
+ /// ProjectConfig
public ProjectConfig GetConfig()
{
if (IsStarted)
@@ -108,6 +129,11 @@ public ProjectConfig GetConfig()
return projectConfig ?? CurrentProjectConfig;
}
+ ///
+ /// Sets the latest available ProjectConfig valid instance.
+ ///
+ /// ProjectConfig
+ /// true if the ProjectConfig saved successfully, false otherwise
public bool SetConfig(ProjectConfig projectConfig)
{
// trigger now, due because of delayed latency response
@@ -134,7 +160,11 @@ public bool SetConfig(ProjectConfig projectConfig)
return true;
}
-
+
+ ///
+ /// Scheduler method that periodically runs on provided
+ /// polling interval.
+ ///
public virtual void Run()
{
if (Monitor.TryEnter(mutex)){
diff --git a/OptimizelySDK/Config/ProjectConfigManager.cs b/OptimizelySDK/Config/ProjectConfigManager.cs
index 59fc25a2..c6f94e7b 100644
--- a/OptimizelySDK/Config/ProjectConfigManager.cs
+++ b/OptimizelySDK/Config/ProjectConfigManager.cs
@@ -16,8 +16,16 @@
namespace OptimizelySDK.Config
{
+ ///
+ /// Interface for fetching ProjectConfig instance.
+ ///
public interface ProjectConfigManager
{
+
+ ///
+ /// Implementations of this method should block until a datafile is available.
+ ///
+ /// ProjectConfig instance
ProjectConfig GetConfig();
}
}
diff --git a/OptimizelySDK/Entity/FeatureVariable.cs b/OptimizelySDK/Entity/FeatureVariable.cs
index 0a71e78e..1d2d10fa 100644
--- a/OptimizelySDK/Entity/FeatureVariable.cs
+++ b/OptimizelySDK/Entity/FeatureVariable.cs
@@ -1,5 +1,5 @@
/*
- * Copyright 2017, Optimizely
+ * Copyright 2017, 2019, Optimizely
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,6 +35,26 @@ public enum VariableType
public string DefaultValue { get; set; }
public VariableType Type { get; set; }
public VariableStatus Status { get; set; }
-
+
+ ///
+ /// Returns the feature variable api name based on VariableType.
+ ///
+ /// The feature variable type name.
+ /// Variable type.
+ public static string GetFeatureVariableTypeName(VariableType variableType)
+ {
+ switch (variableType) {
+ case FeatureVariable.VariableType.BOOLEAN:
+ return "GetFeatureVariableBoolean";
+ case FeatureVariable.VariableType.DOUBLE:
+ return "GetFeatureVariableDouble";
+ case FeatureVariable.VariableType.INTEGER:
+ return "GetFeatureVariableInteger";
+ case FeatureVariable.VariableType.STRING:
+ return "GetFeatureVariableString";
+ default:
+ return null;
+ }
+ }
}
}
diff --git a/OptimizelySDK/Optimizely.cs b/OptimizelySDK/Optimizely.cs
index 232b5cc8..e1f44c0a 100644
--- a/OptimizelySDK/Optimizely.cs
+++ b/OptimizelySDK/Optimizely.cs
@@ -36,8 +36,6 @@ public class Optimizely : IOptimizely
private EventBuilder EventBuilder;
private IEventDispatcher EventDispatcher;
-
- private ProjectConfig Config;
private ILogger Logger;
@@ -49,7 +47,17 @@ public class Optimizely : IOptimizely
public NotificationCenter NotificationCenter;
- public bool IsValid { get; private set; }
+ public ProjectConfigManager ProjectConfigManager;
+
+ ///
+ /// It returns true if the ProjectConfig is valid otherwise false.
+ /// Also, it may block execution if GetConfig() blocks execution to get ProjectConfig.
+ ///
+ public bool IsValid {
+ get {
+ return ProjectConfigManager?.GetConfig() != null;
+ }
+ }
public static String SDK_VERSION {
get {
@@ -94,30 +102,17 @@ public Optimizely(string datafile,
UserProfileService userProfileService = null,
bool skipJsonValidation = false)
{
- IsValid = false; // invalid until proven valid
- Logger = logger ?? new NoOpLogger();
- EventDispatcher = eventDispatcher ?? new DefaultEventDispatcher(Logger);
- ErrorHandler = errorHandler ?? new NoOpErrorHandler();
- Bucketer = new Bucketer(Logger);
- EventBuilder = new EventBuilder(Bucketer, Logger);
- UserProfileService = userProfileService;
- NotificationCenter = new NotificationCenter(Logger);
-
try {
+ InitializeComponents(eventDispatcher, logger, errorHandler, userProfileService);
- if (!ValidateInputs(datafile, skipJsonValidation))
- {
+ if (ValidateInputs(datafile, skipJsonValidation)) {
+ var config = DatafileProjectConfig.Create(datafile, Logger, ErrorHandler);
+ ProjectConfigManager = new FallbackProjectConfigManager(config);
+ } else {
Logger.Log(LogLevel.ERROR, "Provided 'datafile' has invalid schema.");
- return;
}
- Config = DatafileProjectConfig.Create(datafile, Logger, ErrorHandler);
- IsValid = true;
-
- DecisionService = new DecisionService(Bucketer, ErrorHandler, Config, userProfileService, Logger);
- }
- catch (Exception ex)
- {
+ } catch (Exception ex) {
string error = String.Empty;
if (ex.GetType() == typeof(ConfigParseException))
error = ex.Message;
@@ -129,6 +124,38 @@ public Optimizely(string datafile,
}
}
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Config manager.
+ /// Event dispatcher.
+ /// Logger.
+ /// Error handler.
+ /// User profile service.
+ public Optimizely(ProjectConfigManager configManager,
+ IEventDispatcher eventDispatcher = null,
+ ILogger logger = null,
+ IErrorHandler errorHandler = null,
+ UserProfileService userProfileService = null)
+ {
+ ProjectConfigManager = configManager;
+ InitializeComponents(eventDispatcher, logger, errorHandler, userProfileService);
+ }
+
+ private void InitializeComponents(IEventDispatcher eventDispatcher = null,
+ ILogger logger = null,
+ IErrorHandler errorHandler = null,
+ UserProfileService userProfileService = null)
+ {
+ Logger = logger ?? new NoOpLogger();
+ EventDispatcher = eventDispatcher ?? new DefaultEventDispatcher(Logger);
+ ErrorHandler = errorHandler ?? new NoOpErrorHandler();
+ Bucketer = new Bucketer(Logger);
+ EventBuilder = new EventBuilder(Bucketer, Logger);
+ UserProfileService = userProfileService;
+ NotificationCenter = new NotificationCenter(Logger);
+ DecisionService = new DecisionService(Bucketer, ErrorHandler, userProfileService, Logger);
+ }
///
/// Helper function to validate all required conditions before performing activate or track.
@@ -136,7 +163,7 @@ public Optimizely(string datafile,
/// Experiment Object representing experiment
/// string ID for user
/// associative array of Attributes for the user
- private bool ValidatePreconditions(Experiment experiment, string userId, UserAttributes userAttributes = null)
+ private bool ValidatePreconditions(Experiment experiment, string userId, ProjectConfig config, UserAttributes userAttributes = null)
{
if (!experiment.IsExperimentRunning)
{
@@ -149,7 +176,7 @@ private bool ValidatePreconditions(Experiment experiment, string userId, UserAtt
return true;
}
- if (!ExperimentUtils.IsUserInExperiment(Config, experiment, userAttributes, Logger))
+ if (!ExperimentUtils.IsUserInExperiment(config, experiment, userAttributes, Logger))
{
Logger.Log(LogLevel.INFO, string.Format("User \"{0}\" does not meet conditions to be in experiment \"{1}\".", userId, experiment.Key));
return false;
@@ -168,9 +195,10 @@ private bool ValidatePreconditions(Experiment experiment, string userId, UserAtt
/// null|Variation Representing variation
public Variation Activate(string experimentKey, string userId, UserAttributes userAttributes = null)
{
- if (!IsValid)
+ var config = ProjectConfigManager?.GetConfig();
+ if (config == null)
{
- Logger.Log(LogLevel.ERROR, "Datafile has invalid format. Failing 'activate'.");
+ Logger.Log(LogLevel.ERROR, "Datafile has invalid format. Failing 'Activate'.");
return null;
}
@@ -183,7 +211,7 @@ public Variation Activate(string experimentKey, string userId, UserAttributes us
if (!ValidateStringInputs(inputValues))
return null;
- var experiment = Config.GetExperimentFromKey(experimentKey);
+ var experiment = config.GetExperimentFromKey(experimentKey);
if (experiment.Key == null)
{
@@ -191,7 +219,7 @@ public Variation Activate(string experimentKey, string userId, UserAttributes us
return null;
}
- var variation = GetVariation(experimentKey, userId, userAttributes);
+ var variation = GetVariation(experimentKey, userId, config, userAttributes);
if (variation == null || variation.Key == null)
{
@@ -199,7 +227,7 @@ public Variation Activate(string experimentKey, string userId, UserAttributes us
return null;
}
- SendImpressionEvent(experiment, variation, userId, userAttributes);
+ SendImpressionEvent(experiment, variation, userId, userAttributes, config);
return variation;
}
@@ -225,9 +253,10 @@ private bool ValidateInputs(string datafile, bool skipJsonValidation)
/// eventTags array Hash representing metadata associated with the event.
public void Track(string eventKey, string userId, UserAttributes userAttributes = null, EventTags eventTags = null)
{
- if (!IsValid)
+ var config = ProjectConfigManager?.GetConfig();
+ if (config == null)
{
- Logger.Log(LogLevel.ERROR, "Datafile has invalid format. Failing 'track'.");
+ Logger.Log(LogLevel.ERROR, "Datafile has invalid format. Failing 'Track'.");
return;
}
@@ -240,7 +269,7 @@ public void Track(string eventKey, string userId, UserAttributes userAttributes
if (!ValidateStringInputs(inputValues))
return;
- var eevent = Config.GetEvent(eventKey);
+ var eevent = config.GetEvent(eventKey);
if (eevent.Key == null)
{
@@ -254,7 +283,7 @@ public void Track(string eventKey, string userId, UserAttributes userAttributes
eventTags = eventTags.FilterNullValues(Logger);
}
- var conversionEvent = EventBuilder.CreateConversionEvent(Config, eventKey,
+ var conversionEvent = EventBuilder.CreateConversionEvent(config, eventKey,
userId, userAttributes, eventTags);
Logger.Log(LogLevel.INFO, string.Format("Tracking event {0} for user {1}.", eventKey, userId));
Logger.Log(LogLevel.DEBUG, string.Format("Dispatching conversion event to URL {0} with params {1}.",
@@ -283,8 +312,21 @@ public void Track(string eventKey, string userId, UserAttributes userAttributes
/// null|Variation Representing variation
public Variation GetVariation(string experimentKey, string userId, UserAttributes userAttributes = null)
{
- if (!IsValid)
- {
+ var config = ProjectConfigManager?.GetConfig();
+ return GetVariation(experimentKey, userId, config, userAttributes);
+ }
+
+ ///
+ /// Get variation where user will be bucketed from the given ProjectConfig.
+ ///
+ /// experimentKey string Key identifying the experiment
+ /// ID for the user
+ /// ProjectConfig to be used for variation
+ /// Attributes for the users
+ /// null|Variation Representing variation
+ private Variation GetVariation(string experimentKey, string userId, ProjectConfig config, UserAttributes userAttributes = null)
+ {
+ if (config == null) {
Logger.Log(LogLevel.ERROR, "Datafile has invalid format. Failing 'GetVariation'.");
return null;
}
@@ -296,13 +338,13 @@ public Variation GetVariation(string experimentKey, string userId, UserAttribute
};
if (!ValidateStringInputs(inputValues))
- return null;
+ return null;
- Experiment experiment = Config.GetExperimentFromKey(experimentKey);
+ Experiment experiment = config.GetExperimentFromKey(experimentKey);
if (experiment.Key == null)
return null;
- var variation = DecisionService.GetVariation(experiment, userId, userAttributes);
+ var variation = DecisionService.GetVariation(experiment, userId, config, userAttributes);
var decisionInfo = new Dictionary
{
{ "experimentKey", experimentKey },
@@ -310,7 +352,7 @@ public Variation GetVariation(string experimentKey, string userId, UserAttribute
};
userAttributes = userAttributes ?? new UserAttributes();
- var decisionNotificationType = Config.IsFeatureExperiment(experiment.Id) ? DecisionNotificationTypes.FEATURE_TEST : DecisionNotificationTypes.AB_TEST;
+ var decisionNotificationType = config.IsFeatureExperiment(experiment.Id) ? DecisionNotificationTypes.FEATURE_TEST : DecisionNotificationTypes.AB_TEST;
NotificationCenter.SendNotifications(NotificationCenter.NotificationType.Decision, decisionNotificationType, userId,
userAttributes, decisionInfo);
return variation;
@@ -326,13 +368,19 @@ public Variation GetVariation(string experimentKey, string userId, UserAttribute
/// A boolean value that indicates if the set completed successfully.
public bool SetForcedVariation(string experimentKey, string userId, string variationKey)
{
+ var config = ProjectConfigManager?.GetConfig();
+ if (config == null)
+ {
+ return false;
+ }
+
var inputValues = new Dictionary
{
{ USER_ID, userId },
{ EXPERIMENT_KEY, experimentKey }
};
- return ValidateStringInputs(inputValues) && Config.SetForcedVariation(experimentKey, userId, variationKey);
+ return ValidateStringInputs(inputValues) && config.SetForcedVariation(experimentKey, userId, variationKey);
}
///
@@ -343,6 +391,11 @@ public bool SetForcedVariation(string experimentKey, string userId, string varia
/// null|string The variation key.
public Variation GetForcedVariation(string experimentKey, string userId)
{
+ var config = ProjectConfigManager?.GetConfig();
+ if (config == null) {
+ return null;
+ }
+
var inputValues = new Dictionary
{
{ USER_ID, userId },
@@ -352,7 +405,7 @@ public Variation GetForcedVariation(string experimentKey, string userId)
if (!ValidateStringInputs(inputValues))
return null;
- return Config.GetForcedVariation(experimentKey, userId);
+ return config.GetForcedVariation(experimentKey, userId);
}
#region FeatureFlag APIs
@@ -367,6 +420,14 @@ public Variation GetForcedVariation(string experimentKey, string userId)
/// True if feature is enabled, false or null otherwise
public virtual bool IsFeatureEnabled(string featureKey, string userId, UserAttributes userAttributes = null)
{
+ var config = ProjectConfigManager?.GetConfig();
+ if (config == null) {
+
+ Logger.Log(LogLevel.ERROR, "Datafile has invalid format. Failing 'IsFeatureEnabled'.");
+
+ return false;
+ }
+
var inputValues = new Dictionary
{
{ USER_ID, userId },
@@ -376,16 +437,16 @@ public virtual bool IsFeatureEnabled(string featureKey, string userId, UserAttri
if (!ValidateStringInputs(inputValues))
return false;
- var featureFlag = Config.GetFeatureFlagFromKey(featureKey);
+ var featureFlag = config.GetFeatureFlagFromKey(featureKey);
if (string.IsNullOrEmpty(featureFlag.Key))
return false;
- if (!Validator.IsFeatureFlagValid(Config, featureFlag))
+ if (!Validator.IsFeatureFlagValid(config, featureFlag))
return false;
bool featureEnabled = false;
var sourceInfo = new Dictionary();
- var decision = DecisionService.GetVariationForFeature(featureFlag, userId, userAttributes);
+ var decision = DecisionService.GetVariationForFeature(featureFlag, userId, config, userAttributes);
if (decision.Variation != null)
{
@@ -396,7 +457,7 @@ public virtual bool IsFeatureEnabled(string featureKey, string userId, UserAttri
{
sourceInfo["experimentKey"] = decision.Experiment.Key;
sourceInfo["variationKey"] = variation.Key;
- SendImpressionEvent(decision.Experiment, variation, userId, userAttributes);
+ SendImpressionEvent(decision.Experiment, variation, userId, userAttributes, config);
}
else
{
@@ -432,8 +493,16 @@ public virtual bool IsFeatureEnabled(string featureKey, string userId, UserAttri
///