Skip to content

Commit 1f33478

Browse files
refactor: rename test from 'decide_holdoutApplied_basic' to 'decide_with_holdout' to accurately reflect the functionality being tested
1 parent 56ab9f1 commit 1f33478

File tree

1 file changed

+111
-1
lines changed

1 file changed

+111
-1
lines changed

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

Lines changed: 111 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2102,7 +2102,7 @@ OptimizelyDecision callDecideWithIncludeReasons(String flagKey) {
21022102
}
21032103

21042104
@Test
2105-
public void decide_holdoutApplied_basic() throws Exception {
2105+
public void decide_with_holdout() throws Exception {
21062106
Optimizely optWithHoldout = createOptimizelyWithHoldouts();
21072107
// pick a flag that is eligible for basic_holdout. Using boolean_feature from config.
21082108
String flagKey = "boolean_feature";
@@ -2139,4 +2139,114 @@ public void decide_holdoutApplied_basic() throws Exception {
21392139
.build();
21402140
eventHandler.expectImpression(experimentId, variationId, userId, Collections.emptyMap(), metadata);
21412141
}
2142+
2143+
@Test
2144+
public void decide_for_keys_with_holdout() throws Exception {
2145+
Optimizely optWithHoldout = createOptimizelyWithHoldouts();
2146+
String userId = "user123";
2147+
Map<String, Object> attrs = new HashMap<>();
2148+
attrs.put("$opt_bucketing_id", "ppid300002"); // deterministic bucketing used in prior holdout test
2149+
OptimizelyUserContext user = optWithHoldout.createUserContext(userId, attrs);
2150+
2151+
List<String> flagKeys = Arrays.asList(
2152+
"boolean_feature", // previously validated basic_holdout membership
2153+
"double_single_variable_feature", // also subject to global/basic holdout
2154+
"integer_single_variable_feature" // also subject to global/basic holdout
2155+
);
2156+
2157+
Map<String, OptimizelyDecision> decisions = user.decideForKeys(flagKeys, Collections.singletonList(OptimizelyDecideOption.INCLUDE_REASONS));
2158+
assertEquals(3, decisions.size());
2159+
2160+
String holdoutExperimentId = "10075323428"; // basic_holdout id
2161+
String variationId = "$opt_dummy_variation_id";
2162+
String variationKey = "ho_off_key";
2163+
String expectedReason = "User (" + userId + ") is in variation (" + variationKey + ") of holdout (basic_holdout).";
2164+
2165+
for (String flagKey : flagKeys) {
2166+
OptimizelyDecision d = decisions.get(flagKey);
2167+
assertNotNull(d);
2168+
assertEquals(flagKey, d.getFlagKey());
2169+
assertEquals(variationKey, d.getVariationKey());
2170+
assertFalse(d.getEnabled());
2171+
assertTrue("Expected holdout reason for flag " + flagKey, d.getReasons().contains(expectedReason));
2172+
DecisionMetadata metadata = new DecisionMetadata.Builder()
2173+
.setFlagKey(flagKey)
2174+
.setRuleKey("basic_holdout")
2175+
.setRuleType("holdout")
2176+
.setVariationKey(variationKey)
2177+
.setEnabled(false)
2178+
.build();
2179+
// attributes map expected empty (reserved $opt_ attribute filtered out)
2180+
eventHandler.expectImpression(holdoutExperimentId, variationId, userId, Collections.emptyMap(), metadata);
2181+
}
2182+
2183+
// At least one log message confirming holdout membership
2184+
logbackVerifier.expectMessage(Level.INFO, expectedReason);
2185+
}
2186+
2187+
@Test
2188+
public void decide_all_with_holdout() throws Exception {
2189+
2190+
Optimizely optWithHoldout = createOptimizelyWithHoldouts();
2191+
String userId = "user123";
2192+
Map<String, Object> attrs = new HashMap<>();
2193+
// ppid120000 buckets user into holdout_included_flags
2194+
attrs.put("$opt_bucketing_id", "ppid120000");
2195+
OptimizelyUserContext user = optWithHoldout.createUserContext(userId, attrs);
2196+
2197+
// All flag keys present in holdouts-project-config.json
2198+
List<String> allFlagKeys = Arrays.asList(
2199+
"boolean_feature",
2200+
"double_single_variable_feature",
2201+
"integer_single_variable_feature",
2202+
"boolean_single_variable_feature",
2203+
"string_single_variable_feature",
2204+
"multi_variate_feature",
2205+
"multi_variate_future_feature",
2206+
"mutex_group_feature"
2207+
);
2208+
2209+
// Flags INCLUDED in holdout_included_flags (only these should be holdout decisions)
2210+
List<String> includedInHoldout = Arrays.asList(
2211+
"boolean_feature",
2212+
"double_single_variable_feature",
2213+
"integer_single_variable_feature"
2214+
);
2215+
2216+
Map<String, OptimizelyDecision> decisions = user.decideAll(Arrays.asList(
2217+
OptimizelyDecideOption.INCLUDE_REASONS,
2218+
OptimizelyDecideOption.DISABLE_DECISION_EVENT
2219+
));
2220+
assertEquals(allFlagKeys.size(), decisions.size());
2221+
2222+
String holdoutExperimentId = "1007543323427"; // holdout_included_flags id
2223+
String variationId = "$opt_dummy_variation_id";
2224+
String variationKey = "ho_off_key";
2225+
String expectedReason = "User (" + userId + ") is in variation (" + variationKey + ") of holdout (holdout_included_flags).";
2226+
2227+
int holdoutCount = 0;
2228+
for (String flagKey : allFlagKeys) {
2229+
OptimizelyDecision d = decisions.get(flagKey);
2230+
assertNotNull("Missing decision for flag " + flagKey, d);
2231+
if (includedInHoldout.contains(flagKey)) {
2232+
// Should be holdout decision
2233+
assertEquals(variationKey, d.getVariationKey());
2234+
assertFalse(d.getEnabled());
2235+
assertTrue("Expected holdout reason for flag " + flagKey, d.getReasons().contains(expectedReason));
2236+
DecisionMetadata metadata = new DecisionMetadata.Builder()
2237+
.setFlagKey(flagKey)
2238+
.setRuleKey("holdout_included_flags")
2239+
.setRuleType("holdout")
2240+
.setVariationKey(variationKey)
2241+
.setEnabled(false)
2242+
.build();
2243+
holdoutCount++;
2244+
} else {
2245+
// Should NOT be a holdout decision
2246+
assertFalse("Non-included flag should not have holdout reason: " + flagKey, d.getReasons().contains(expectedReason));
2247+
}
2248+
}
2249+
assertEquals("Expected exactly the included flags to be in holdout", includedInHoldout.size(), holdoutCount);
2250+
logbackVerifier.expectMessage(Level.INFO, expectedReason);
2251+
}
21422252
}

0 commit comments

Comments
 (0)