Skip to content

Commit 3566826

Browse files
msohailhussainmikeproeng37
authored andcommitted
Feature toggleability (#61)
1 parent c0a936a commit 3566826

File tree

5 files changed

+102
-30
lines changed

5 files changed

+102
-30
lines changed

OptimizelySDK.Tests/OptimizelyTest.cs

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1562,6 +1562,49 @@ public void TestIsFeatureEnabledGivenVariationNotFoundInFeatureExperimentButInRo
15621562
LoggerMock.Verify(l => l.Log(LogLevel.INFO, "Feature flag \"boolean_single_variable_feature\" is enabled for user \"testUserId\"."), Times.Once);
15631563
}
15641564

1565+
public void TestIsFeatureEnabledWithFeatureEnabledPropertyGivenFeatureExperiment()
1566+
{
1567+
var userId = "testUserId2";
1568+
var featureKey = "double_single_variable_feature";
1569+
var experiment = Config.GetExperimentFromKey("test_experiment_double_feature");
1570+
var featureEnabledTrue = Config.GetVariationFromKey("test_experiment_double_feature", "control");
1571+
var featureEnabledFalse = Config.GetVariationFromKey("test_experiment_double_feature", "variation");
1572+
var featureFlag = Config.GetFeatureFlagFromKey(featureKey);
1573+
var decisionTrue = new FeatureDecision(experiment, featureEnabledTrue, FeatureDecision.DECISION_SOURCE_EXPERIMENT);
1574+
var decisionFalse = new FeatureDecision(experiment, featureEnabledFalse, FeatureDecision.DECISION_SOURCE_EXPERIMENT);
1575+
1576+
DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns(decisionTrue);
1577+
DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, userId, null)).Returns(decisionFalse);
1578+
1579+
var optly = Helper.CreatePrivateOptimizely();
1580+
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
1581+
1582+
// Verify that IsFeatureEnabled returns true when feature experiment variation's 'featureEnabled' property is true.
1583+
bool result = (bool)optly.Invoke("IsFeatureEnabled", featureKey, TestUserId, null);
1584+
Assert.True(result);
1585+
1586+
// Verify that IsFeatureEnabled returns false when feature experiment variation's 'featureEnabled' property is false.
1587+
result = (bool)optly.Invoke("IsFeatureEnabled", featureKey, userId, null);
1588+
Assert.False(result);
1589+
}
1590+
1591+
public void TestIsFeatureEnabledWithFeatureEnabledPropertyGivenRolloutRule()
1592+
{
1593+
var featureKey = "boolean_single_variable_feature";
1594+
var featureFlag = Config.GetFeatureFlagFromKey(featureKey);
1595+
1596+
// Verify that IsFeatureEnabled returns true when user is bucketed into the rollout rule's variation.
1597+
Assert.True(Optimizely.IsFeatureEnabled("boolean_single_variable_feature", TestUserId));
1598+
1599+
DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns<FeatureDecision>(null);
1600+
var optly = Helper.CreatePrivateOptimizely();
1601+
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
1602+
1603+
// Verify that IsFeatureEnabled returns false when user does not get bucketed into the rollout rule's variation.
1604+
bool result = (bool)optly.Invoke("IsFeatureEnabled", featureKey, TestUserId, null);
1605+
Assert.False(result);
1606+
}
1607+
15651608
#endregion // Test IsFeatureEnabled method
15661609

15671610
#region Test NotificationCenter
@@ -1587,8 +1630,8 @@ public void TestActivateListenerWithAttributes()
15871630

15881631
public void TestActivateListener(UserAttributes userAttributes)
15891632
{
1590-
var experimentKey = "test_experiment";
1591-
var variationKey = "control";
1633+
var experimentKey = "group_experiment_1";
1634+
var variationKey = "group_exp_1_var_1";
15921635
var featureKey = "boolean_feature";
15931636
var experiment = Config.GetExperimentFromKey(experimentKey);
15941637
var variation = Config.GetVariationFromKey(experimentKey, variationKey);
@@ -1612,7 +1655,6 @@ public void TestActivateListener(UserAttributes userAttributes)
16121655
var optly = Helper.CreatePrivateOptimizely();
16131656
var optStronglyTyped = optly.GetObject() as Optimizely;
16141657

1615-
16161658
// Adding notification listeners.
16171659
var notificationType = NotificationCenter.NotificationType.Activate;
16181660
optStronglyTyped. NotificationCenter.AddNotification(notificationType, NotificationCallbackMock.Object.TestActivateCallback);

OptimizelySDK.Tests/ProjectConfigTest.cs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -339,16 +339,16 @@ public void TestInit()
339339

340340
Assert.IsTrue(TestData.CompareObjects(expectedVariationIdMap, Config.VariationIdMap));
341341

342-
// Check Variation returns correct variable usage
342+
// Check Variation returns correct variable usage
343343
var featureVariableUsageInstance = new List<FeatureVariableUsage>
344344
{
345345
new FeatureVariableUsage{Id="155560", Value="F"},
346346
new FeatureVariableUsage{Id="155561", Value="red"},
347347
};
348348

349-
var expectedVariationUsage = new Variation { Id = "122231", Key = "Fred", FeatureVariableUsageInstances = featureVariableUsageInstance};
349+
var expectedVariationUsage = new Variation { Id = "122231", Key = "Fred", FeatureVariableUsageInstances = featureVariableUsageInstance, FeatureEnabled = true };
350350
var actualVariationUsage = Config.GetVariationFromKey("test_experiment_multivariate", "Fred");
351-
351+
352352
Assert.IsTrue(TestData.CompareObjects(expectedVariationUsage, actualVariationUsage));
353353

354354
// Check Feature Key map.
@@ -802,5 +802,13 @@ public void TestSetForcedVariationMultipleSets()
802802
Assert.AreEqual(Config.GetForcedVariation("test_experiment", "test_user_1").Key, "control");
803803
Assert.AreEqual(Config.GetForcedVariation("group_experiment_1", "test_user_1").Key, "group_exp_1_var_1");
804804
}
805+
806+
[Test]
807+
public void TestVariationFeatureEnabledProperty()
808+
{
809+
// Verify that featureEnabled property of variation is false if not defined.
810+
var variation = Config.GetVariationFromKey("test_experiment", "control");
811+
Assert.IsFalse(variation.IsFeatureEnabled);
812+
}
805813
}
806814
}

OptimizelySDK.Tests/TestData.json

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@
8585
"id": "155561",
8686
"value": "red"
8787
}
88-
]
88+
],
89+
"featureEnabled": true
8990
}, {
9091
"id": "122232",
9192
"key": "Feorge",
@@ -97,7 +98,8 @@
9798
"id": "155561",
9899
"value": "eorge"
99100
}
100-
]
101+
],
102+
"featureEnabled": false
101103
}, {
102104
"id": "122233",
103105
"key": "Gred",
@@ -121,7 +123,8 @@
121123
"id": "155561",
122124
"value": "eorge"
123125
}
124-
]
126+
],
127+
"featureEnabled": true
125128
}]
126129
}, {
127130
"key": "test_experiment_with_feature_rollout",
@@ -143,14 +146,16 @@
143146
"variables": [{
144147
"id": "155558",
145148
"value": "cta_1"
146-
}]
149+
}],
150+
"featureEnabled": true
147151
}, {
148152
"id": "122237",
149153
"key": "variation",
150154
"variables": [{
151155
"id": "155558",
152156
"value": "cta_2"
153-
}]
157+
}],
158+
"featureEnabled": true
154159
}]
155160
}, {
156161
"key": "test_experiment_double_feature",
@@ -172,14 +177,16 @@
172177
"variables": [{
173178
"id": "155551",
174179
"value": "42.42"
175-
}]
180+
}],
181+
"featureEnabled": true
176182
}, {
177183
"id": "122240",
178184
"key": "variation",
179185
"variables": [{
180186
"id": "155551",
181187
"value": "13.37"
182-
}]
188+
}],
189+
"featureEnabled": false
183190
}]
184191
}, {
185192
"key": "test_experiment_integer_feature",
@@ -201,14 +208,16 @@
201208
"variables": [{
202209
"id": "155553",
203210
"value": "42"
204-
}]
211+
}],
212+
"featureEnabled": false
205213
}, {
206214
"id": "122243",
207215
"key": "variation",
208216
"variables": [{
209217
"id": "155553",
210218
"value": "13"
211-
}]
219+
}],
220+
"featureEnabled": true
212221
}]
213222
}, {
214223
"id": "223",
@@ -362,15 +371,17 @@
362371
"variables": [{
363372
"id": "155563",
364373
"value": "groupie_1_v1"
365-
}]
374+
}],
375+
"featureEnabled": true
366376
},
367377
{
368378
"id": "7722360022",
369379
"key": "group_exp_1_var_2",
370380
"variables": [{
371381
"id": "155563",
372382
"value": "groupie_1_v2"
373-
}]
383+
}],
384+
"featureEnabled": true
374385
}
375386
],
376387
"forcedVariations": {
@@ -398,15 +409,17 @@
398409
"variables": [{
399410
"id": "155563",
400411
"value": "groupie_2_v1"
401-
}]
412+
}],
413+
"featureEnabled": false
402414
},
403415
{
404416
"id": "7725250007",
405417
"key": "group_exp_2_var_2",
406418
"variables": [{
407419
"id": "155563",
408420
"value": "groupie_2_v2"
409-
}]
421+
}],
422+
"featureEnabled": false
410423
}
411424
],
412425
"forcedVariations": {},
@@ -521,7 +534,8 @@
521534
"variables": [{
522535
"id": "155556",
523536
"value": "true"
524-
}]
537+
}],
538+
"featureEnabled": true
525539
}],
526540
"trafficAllocation": [{
527541
"entityId": "177771",
@@ -539,7 +553,8 @@
539553
"variables": [{
540554
"id": "155556",
541555
"value": "false"
542-
}]
556+
}],
557+
"featureEnabled": true
543558
}],
544559
"trafficAllocation": [{
545560
"entityId": "177773",
@@ -557,7 +572,8 @@
557572
"variables": [{
558573
"id": "155556",
559574
"value": "false"
560-
}]
575+
}],
576+
"featureEnabled": true
561577
}],
562578
"trafficAllocation": [{
563579
"entityId": "177778",
@@ -575,7 +591,8 @@
575591
"variations": [{
576592
"id": "177775",
577593
"key": "177775",
578-
"variables": []
594+
"variables": [],
595+
"featureEnabled": true
579596
}],
580597
"trafficAllocation": [{
581598
"entityId": "177775",
@@ -590,7 +607,8 @@
590607
"variations": [{
591608
"id": "177780",
592609
"key": "177780",
593-
"variables": []
610+
"variables": [],
611+
"featureEnabled": true
594612
}],
595613
"trafficAllocation": [{
596614
"entityId": "177780",

OptimizelySDK/Entity/Variation.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,14 @@ public FeatureVariableUsage GetFeatureVariableUsageFromId(string variableId)
4949

5050
return null;
5151
}
52+
53+
public bool? FeatureEnabled { get; set; }
54+
public bool IsFeatureEnabled
55+
{
56+
get
57+
{
58+
return FeatureEnabled ?? false;
59+
}
60+
}
5261
}
5362
}

OptimizelySDK/Optimizely.cs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -355,21 +355,16 @@ public virtual bool IsFeatureEnabled(string featureKey, string userId, UserAttri
355355
return false;
356356

357357
var decision = DecisionService.GetVariationForFeature(featureFlag, userId, userAttributes);
358-
if (decision == null)
358+
if (decision == null || !decision.Variation.IsFeatureEnabled)
359359
{
360360
Logger.Log(LogLevel.INFO, $@"Feature flag ""{featureKey}"" is not enabled for user ""{userId}"".");
361361
return false;
362362
}
363363

364364
if (decision.Source == FeatureDecision.DECISION_SOURCE_EXPERIMENT)
365-
{
366-
367365
SendImpressionEvent(decision.Experiment, decision.Variation, userId, userAttributes);
368-
}
369366
else
370-
{
371367
Logger.Log(LogLevel.INFO, $@"The user ""{userId}"" is not being experimented on feature ""{featureKey}"".");
372-
}
373368

374369
Logger.Log(LogLevel.INFO, $@"Feature flag ""{featureKey}"" is enabled for user ""{userId}"".");
375370
return true;

0 commit comments

Comments
 (0)