Skip to content

Commit 56ab9f1

Browse files
test: add holdout decision test to verify behavior
1 parent 2bd5e17 commit 56ab9f1

File tree

1 file changed

+56
-1
lines changed

1 file changed

+56
-1
lines changed

core-api/src/test/java/com/optimizely/ab/OptimizelyUserContextTest.java

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@
5757
import static junit.framework.TestCase.assertEquals;
5858
import static junit.framework.TestCase.assertTrue;
5959
import static org.junit.Assert.*;
60+
import static org.mockito.Matchers.any;
61+
import static org.mockito.Matchers.anyObject;
62+
import static org.mockito.Matchers.eq;
6063
import static org.mockito.Mockito.*;
6164

6265
public class OptimizelyUserContextTest {
@@ -76,6 +79,8 @@ public class OptimizelyUserContextTest {
7679
Map<String, FeatureFlag> featureKeyMapping;
7780
Map<String, Group> groupIdMapping;
7881

82+
private String holdoutDatafile;
83+
7984
@Before
8085
public void setUp() throws Exception {
8186
datafile = Resources.toString(Resources.getResource("config/decide-project-config.json"), Charsets.UTF_8);
@@ -85,6 +90,16 @@ public void setUp() throws Exception {
8590
.build();
8691
}
8792

93+
private Optimizely createOptimizelyWithHoldouts() throws Exception {
94+
if (holdoutDatafile == null) {
95+
holdoutDatafile = com.google.common.io.Resources.toString(
96+
com.google.common.io.Resources.getResource("config/holdouts-project-config.json"),
97+
com.google.common.base.Charsets.UTF_8
98+
);
99+
}
100+
return new Optimizely.Builder().withDatafile(holdoutDatafile).withEventProcessor(new ForwardingEventProcessor(eventHandler, null)).build();
101+
}
102+
88103
@Test
89104
public void optimizelyUserContext_withAttributes() {
90105
Map<String, Object> attributes = Collections.singletonMap(ATTRIBUTE_HOUSE_KEY, AUDIENCE_GRYFFINDOR_VALUE);
@@ -749,7 +764,7 @@ public void decisionNotification() {
749764
public void decideOptions_bypassUPS() throws Exception {
750765
String flagKey = "feature_2"; // embedding experiment: "exp_no_audience"
751766
String experimentId = "10420810910"; // "exp_no_audience"
752-
String variationId1 = "10418551353";
767+
String variationId = "10418551353";
753768
String variationId2 = "10418510624";
754769
String variationKey1 = "variation_with_traffic";
755770
String variationKey2 = "variation_no_traffic";
@@ -1786,6 +1801,8 @@ public void fetchQualifiedSegmentsAsync() throws InterruptedException {
17861801
.withODPManager(mockODPManager)
17871802
.build();
17881803

1804+
1805+
17891806
OptimizelyUserContext userContext = optimizely.createUserContext("test-user");
17901807

17911808
CountDownLatch countDownLatch = new CountDownLatch(1);
@@ -2084,4 +2101,42 @@ OptimizelyDecision callDecideWithIncludeReasons(String flagKey) {
20842101
return callDecideWithIncludeReasons(flagKey, Collections.emptyMap());
20852102
}
20862103

2104+
@Test
2105+
public void decide_holdoutApplied_basic() throws Exception {
2106+
Optimizely optWithHoldout = createOptimizelyWithHoldouts();
2107+
// pick a flag that is eligible for basic_holdout. Using boolean_feature from config.
2108+
String flagKey = "boolean_feature";
2109+
String userId = "user123";
2110+
Map<String, Object> attrs = new HashMap<>();
2111+
attrs.put("$opt_bucketing_id", "ppid160000");
2112+
OptimizelyUserContext user = optWithHoldout.createUserContext(userId, attrs);
2113+
2114+
// include reasons to surface holdout logs in decision reasons if implemented
2115+
OptimizelyDecision decision = user.decide(flagKey, Collections.singletonList(OptimizelyDecideOption.INCLUDE_REASONS));
2116+
2117+
assertNotNull(decision);
2118+
assertEquals(flagKey, decision.getFlagKey());
2119+
// holdout off variation => feature should be disabled
2120+
assertFalse(decision.getEnabled());
2121+
// Expect decision source to be holdout (either via metadata map or reasons text)
2122+
boolean hasHoldoutReason = false;
2123+
String expectedMString = "User (" + userId + ") is in variation (ho_off_key) of holdout (basic_holdout).";
2124+
if (decision.getReasons().contains(expectedMString)) {
2125+
hasHoldoutReason = true;
2126+
}
2127+
assertTrue("Expected holdout to influence decision (reason containing 'holdout')", hasHoldoutReason);
2128+
logbackVerifier.expectMessage(Level.INFO, expectedMString);
2129+
2130+
// Impression expectation: Holdout decisions still dispatch an impression with holdout context.
2131+
String variationId = "$opt_dummy_variation_id"; // from holdouts-project-config.json
2132+
String experimentId = "10075323428"; // holdout id for basic_holdout
2133+
DecisionMetadata metadata = new DecisionMetadata.Builder()
2134+
.setFlagKey(flagKey)
2135+
.setRuleKey("basic_holdout")
2136+
.setRuleType("holdout")
2137+
.setVariationKey("ho_off_key")
2138+
.setEnabled(false)
2139+
.build();
2140+
eventHandler.expectImpression(experimentId, variationId, userId, Collections.emptyMap(), metadata);
2141+
}
20872142
}

0 commit comments

Comments
 (0)