Skip to content

Commit cd14c76

Browse files
Revise rollout bucketing to check audience in last rollout rule. (#65)
1 parent 2cb87f1 commit cd14c76

File tree

2 files changed

+48
-29
lines changed

2 files changed

+48
-29
lines changed

OptimizelySDK.Tests/DecisionServiceTest.cs

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
*
3-
* Copyright 2017, Optimizely and contributors
3+
* Copyright 2017-2018, Optimizely and contributors
44
*
55
* Licensed under the Apache License, Version 2.0 (the "License");
66
* you may not use this file except in compliance with the License.
@@ -591,10 +591,7 @@ public void TestGetVariationForFeatureRolloutWhenUserIsBucketedInTheTargetingRul
591591
var decisionService = new DecisionService(BucketerMock.Object, ErrorHandlerMock.Object, ProjectConfig, null, LoggerMock.Object);
592592

593593
var actualDecision = decisionService.GetVariationForFeatureRollout(featureFlag, "user_1", userAttributes);
594-
595594
Assert.IsTrue(TestData.CompareObjects(expectedDecision, actualDecision));
596-
597-
LoggerMock.Verify(l => l.Log(LogLevel.DEBUG, $"Attempting to bucket user \"user_1\" into rollout rule \"{experiment.Key}\"."));
598595
}
599596

600597
// Should return the variation the user is bucketed into when the user is bucketed into the "Everyone Else" rule
@@ -619,11 +616,7 @@ public void TestGetVariationForFeatureRolloutWhenUserIsNotBucketedInTheTargeting
619616
var decisionService = new DecisionService(BucketerMock.Object, ErrorHandlerMock.Object, ProjectConfig, null, LoggerMock.Object);
620617

621618
var actualDecision = decisionService.GetVariationForFeatureRollout(featureFlag, "user_1", userAttributes);
622-
623619
Assert.IsTrue(TestData.CompareObjects(expectedDecision, actualDecision));
624-
625-
LoggerMock.Verify(l => l.Log(LogLevel.DEBUG, $"Attempting to bucket user \"user_1\" into rollout rule \"{experiment.Key}\"."));
626-
LoggerMock.Verify(l => l.Log(LogLevel.DEBUG, "User \"user_1\" is excluded due to traffic allocation. Checking \"Eveyrone Else\" rule now."));
627620
}
628621

629622
// Should log and return null when the user is not bucketed into the targeting rule
@@ -634,9 +627,6 @@ public void TestGetVariationForFeatureRolloutWhenUserIsNeitherBucketedInTheTarge
634627
var featureFlag = ProjectConfig.GetFeatureFlagFromKey("boolean_single_variable_feature");
635628
var rolloutId = featureFlag.RolloutId;
636629
var rollout = ProjectConfig.GetRolloutFromId(rolloutId);
637-
var experiment = rollout.Experiments[0];
638-
var everyoneElseRule = rollout.Experiments[rollout.Experiments.Count - 1];
639-
640630
var userAttributes = new UserAttributes {
641631
{ "browser_type", "chrome" }
642632
};
@@ -646,10 +636,6 @@ public void TestGetVariationForFeatureRolloutWhenUserIsNeitherBucketedInTheTarge
646636

647637
var actualDecision = decisionService.GetVariationForFeatureRollout(featureFlag, "user_1", userAttributes);
648638
Assert.IsNull(actualDecision);
649-
650-
LoggerMock.Verify(l => l.Log(LogLevel.DEBUG, $"Attempting to bucket user \"user_1\" into rollout rule \"{experiment.Key}\"."));
651-
LoggerMock.Verify(l => l.Log(LogLevel.DEBUG, "User \"user_1\" is excluded due to traffic allocation. Checking \"Eveyrone Else\" rule now."));
652-
LoggerMock.Verify(l => l.Log(LogLevel.DEBUG, $"User \"user_1\" is excluded from \"Everyone Else\" rule for feature flag \"{featureFlag.Key}\"."));
653639
}
654640

655641
// Should return expected variation when the user is attempted to be bucketed into all targeting rules
@@ -735,6 +721,39 @@ public void TestGetVariationForFeatureRolloutAudienceAndTrafficeAllocationCheck(
735721
Assert.Null(actualDecision);
736722
}
737723

724+
[Test]
725+
public void TestGetVariationForFeatureRolloutCheckAudienceInEveryoneElseRule()
726+
{
727+
var featureFlag = ProjectConfig.GetFeatureFlagFromKey("boolean_single_variable_feature");
728+
var rolloutId = featureFlag.RolloutId;
729+
var rollout = ProjectConfig.GetRolloutFromId(rolloutId);
730+
var everyoneElseRule = rollout.Experiments[2];
731+
var variation = everyoneElseRule.Variations[0];
732+
var expectedDecision = new FeatureDecision(everyoneElseRule, variation, FeatureDecision.DECISION_SOURCE_ROLLOUT);
733+
734+
BucketerMock.Setup(bm => bm.Bucket(It.IsAny<ProjectConfig>(), everyoneElseRule, It.IsAny<string>(), WhitelistedUserId)).Returns(variation);
735+
BucketerMock.Setup(bm => bm.Bucket(It.IsAny<ProjectConfig>(), everyoneElseRule, It.IsAny<string>(), GenericUserId)).Returns<Variation>(null);
736+
var decisionService = new DecisionService(BucketerMock.Object, ErrorHandlerMock.Object, ProjectConfig, null, LoggerMock.Object);
737+
738+
// Returned variation id should be of everyone else rule as it passes audience Id checking.
739+
var actualDecision = decisionService.GetVariationForFeatureRollout(featureFlag, WhitelistedUserId, null);
740+
Assert.True(TestData.CompareObjects(expectedDecision, actualDecision));
741+
742+
// Returned variation id should be null.
743+
actualDecision = decisionService.GetVariationForFeatureRollout(featureFlag, GenericUserId, null);
744+
Assert.Null(actualDecision);
745+
746+
// Returned variation id should be null as it fails audience Id checking.
747+
everyoneElseRule.AudienceIds = new string[] { ProjectConfig.Audiences[0].Id };
748+
actualDecision = decisionService.GetVariationForFeatureRollout(featureFlag, GenericUserId, null);
749+
Assert.Null(actualDecision);
750+
751+
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);
752+
LoggerMock.Verify(l => l.Log(LogLevel.DEBUG, "User \"testUser1\" does not meet the conditions to be in rollout rule for audience \"iPhone users in San Francisco\"."), Times.Once);
753+
LoggerMock.Verify(l => l.Log(LogLevel.DEBUG, "User \"genericUserId\" does not meet the conditions to be in rollout rule for audience \"Chrome users\"."), Times.Exactly(2));
754+
LoggerMock.Verify(l => l.Log(LogLevel.DEBUG, "User \"genericUserId\" does not meet the conditions to be in rollout rule for audience \"iPhone users in San Francisco\"."), Times.Exactly(3));
755+
}
756+
738757
#endregion // GetVariationForFeatureRollout Tests
739758

740759
#region GetVariationForFeature Tests

OptimizelySDK/Bucketing/DecisionService.cs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2017, Optimizely
2+
* Copyright 2017-2018, Optimizely
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -289,35 +289,35 @@ public virtual FeatureDecision GetVariationForFeatureRollout(FeatureFlag feature
289289
for (int i=0; i < rolloutRulesLength - 1; i++)
290290
{
291291
var rolloutRule = rollout.Experiments[i];
292-
var audience = ProjectConfig.AudienceIdMap[rolloutRule.AudienceIds[0]];
293-
294292
if (ExperimentUtils.IsUserInExperiment(ProjectConfig, rolloutRule, filteredAttributes))
295293
{
296-
Logger.Log(LogLevel.DEBUG, $"Attempting to bucket user \"{userId}\" into rollout rule \"{rolloutRule.Key}\".");
297294
variation = Bucketer.Bucket(ProjectConfig, rolloutRule, bucketingId, userId);
298-
299295
if (variation == null || string.IsNullOrEmpty(variation.Id))
300-
{
301-
Logger.Log(LogLevel.DEBUG, $"User \"{userId}\" is excluded due to traffic allocation. Checking \"Eveyrone Else\" rule now.");
302296
break;
303-
}
304297

305298
return new FeatureDecision(rolloutRule, variation, FeatureDecision.DECISION_SOURCE_ROLLOUT);
306299
}
307300
else
308301
{
302+
var audience = ProjectConfig.GetAudience(rolloutRule.AudienceIds[0]);
309303
Logger.Log(LogLevel.DEBUG, $"User \"{userId}\" does not meet the conditions to be in rollout rule for audience \"{audience.Name}\".");
310304
}
311305
}
312306

313-
// Bucket the user into the last rule which is everyone else rule
307+
// Get the last rule which is everyone else rule.
314308
var everyoneElseRolloutRule = rollout.Experiments[rolloutRulesLength - 1];
315-
variation = Bucketer.Bucket(ProjectConfig, everyoneElseRolloutRule, bucketingId, userId);
316-
317-
if (variation != null && !string.IsNullOrEmpty(variation.Id))
318-
return new FeatureDecision(everyoneElseRolloutRule, variation, FeatureDecision.DECISION_SOURCE_ROLLOUT);
309+
if (ExperimentUtils.IsUserInExperiment(ProjectConfig, everyoneElseRolloutRule, filteredAttributes))
310+
{
311+
variation = Bucketer.Bucket(ProjectConfig, everyoneElseRolloutRule, bucketingId, userId);
312+
if (variation != null && !string.IsNullOrEmpty(variation.Id))
313+
return new FeatureDecision(everyoneElseRolloutRule, variation, FeatureDecision.DECISION_SOURCE_ROLLOUT);
314+
}
315+
else
316+
{
317+
var audience = ProjectConfig.GetAudience(everyoneElseRolloutRule.AudienceIds[0]);
318+
Logger.Log(LogLevel.DEBUG, $"User \"{userId}\" does not meet the conditions to be in rollout rule for audience \"{audience.Name}\".");
319+
}
319320

320-
Logger.Log(LogLevel.DEBUG, $"User \"{userId}\" is excluded from \"Everyone Else\" rule for feature flag \"{featureFlag.Key}\".");
321321
return null;
322322
}
323323

0 commit comments

Comments
 (0)