Skip to content

Commit ac8a635

Browse files
Test cases added for ho
1 parent 841a5d2 commit ac8a635

File tree

2 files changed

+599
-0
lines changed

2 files changed

+599
-0
lines changed

android-sdk/src/androidTest/java/com/optimizely/ab/android/sdk/OptimizelyClientTest.java

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2291,6 +2291,173 @@ public void testDecide_withDefaultDecideOptions() throws IOException {
22912291
assertTrue(decision.getReasons().size() > 0);
22922292
}
22932293

2294+
// Holdout Tests
2295+
2296+
private OptimizelyClient createOptimizelyClientWithHoldouts(Context context) throws IOException {
2297+
String holdoutDatafile = loadRawResource(context, R.raw.holdouts_project_config);
2298+
OptimizelyManager optimizelyManager = OptimizelyManager.builder(testProjectId).build(context);
2299+
optimizelyManager.initialize(context, holdoutDatafile);
2300+
return optimizelyManager.getOptimizely();
2301+
}
2302+
2303+
@Test
2304+
public void testDecide_withHoldout() throws IOException {
2305+
assumeTrue(datafileVersion == Integer.parseInt(ProjectConfig.Version.V4.toString()));
2306+
2307+
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
2308+
OptimizelyClient optimizelyClient = createOptimizelyClientWithHoldouts(context);
2309+
2310+
String flagKey = "boolean_feature";
2311+
String userId = "user123";
2312+
String variationKey = "ho_off_key";
2313+
String ruleKey = "basic_holdout";
2314+
2315+
Map<String, Object> attributes = new HashMap<>();
2316+
attributes.put("$opt_bucketing_id", "ppid160000"); // deterministic bucketing into basic_holdout
2317+
attributes.put("nationality", "English"); // non-reserved attribute
2318+
2319+
OptimizelyUserContext userContext = optimizelyClient.createUserContext(userId, attributes);
2320+
OptimizelyDecision decision = userContext.decide(flagKey, Collections.singletonList(OptimizelyDecideOption.INCLUDE_REASONS));
2321+
2322+
// Validate holdout decision
2323+
assertEquals(flagKey, decision.getFlagKey());
2324+
assertEquals(variationKey, decision.getVariationKey());
2325+
assertEquals(ruleKey, decision.getRuleKey());
2326+
assertFalse(decision.getEnabled());
2327+
assertTrue(decision.getVariables().toMap().isEmpty());
2328+
assertTrue("Expected holdout reason", decision.getReasons().stream()
2329+
.anyMatch(reason -> reason.contains("holdout")));
2330+
}
2331+
2332+
@Test
2333+
public void testDecideForKeys_withHoldout() throws IOException {
2334+
assumeTrue(datafileVersion == Integer.parseInt(ProjectConfig.Version.V4.toString()));
2335+
2336+
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
2337+
OptimizelyClient optimizelyClient = createOptimizelyClientWithHoldouts(context);
2338+
2339+
String userId = "user123";
2340+
String variationKey = "ho_off_key";
2341+
String ruleKey = "basic_holdout";
2342+
2343+
Map<String, Object> attributes = new HashMap<>();
2344+
attributes.put("$opt_bucketing_id", "ppid160000"); // deterministic bucketing into basic_holdout
2345+
2346+
List<String> flagKeys = Arrays.asList(
2347+
"boolean_feature",
2348+
"double_single_variable_feature",
2349+
"integer_single_variable_feature"
2350+
);
2351+
2352+
OptimizelyUserContext userContext = optimizelyClient.createUserContext(userId, attributes);
2353+
Map<String, OptimizelyDecision> decisions = userContext.decideForKeys(flagKeys, Collections.singletonList(OptimizelyDecideOption.INCLUDE_REASONS));
2354+
2355+
assertEquals(3, decisions.size());
2356+
2357+
for (String flagKey : flagKeys) {
2358+
OptimizelyDecision decision = decisions.get(flagKey);
2359+
assertNotNull("Missing decision for flag " + flagKey, decision);
2360+
assertEquals(flagKey, decision.getFlagKey());
2361+
assertEquals(variationKey, decision.getVariationKey());
2362+
assertEquals(ruleKey, decision.getRuleKey());
2363+
assertFalse(decision.getEnabled());
2364+
assertTrue("Expected holdout reason for flag " + flagKey, decision.getReasons().stream()
2365+
.anyMatch(reason -> reason.contains("holdout")));
2366+
}
2367+
}
2368+
2369+
@Test
2370+
public void testDecideAll_withHoldout() throws IOException {
2371+
assumeTrue(datafileVersion == Integer.parseInt(ProjectConfig.Version.V4.toString()));
2372+
2373+
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
2374+
OptimizelyClient optimizelyClient = createOptimizelyClientWithHoldouts(context);
2375+
2376+
String userId = "user123";
2377+
String variationKey = "ho_off_key";
2378+
2379+
Map<String, Object> attributes = new HashMap<>();
2380+
// ppid120000 buckets user into holdout_included_flags (selective holdout)
2381+
attributes.put("$opt_bucketing_id", "ppid120000");
2382+
2383+
// Flags INCLUDED in holdout_included_flags (only these should be holdout decisions)
2384+
List<String> includedInHoldout = Arrays.asList(
2385+
"boolean_feature",
2386+
"double_single_variable_feature",
2387+
"integer_single_variable_feature"
2388+
);
2389+
2390+
OptimizelyUserContext userContext = optimizelyClient.createUserContext(userId, attributes);
2391+
Map<String, OptimizelyDecision> decisions = userContext.decideAll(Arrays.asList(
2392+
OptimizelyDecideOption.INCLUDE_REASONS,
2393+
OptimizelyDecideOption.DISABLE_DECISION_EVENT
2394+
));
2395+
2396+
assertTrue("Should have multiple decisions", decisions.size() > 0);
2397+
2398+
String expectedReason = "User (" + userId + ") is in variation (" + variationKey + ") of holdout (holdout_included_flags).";
2399+
2400+
int holdoutCount = 0;
2401+
for (Map.Entry<String, OptimizelyDecision> entry : decisions.entrySet()) {
2402+
String flagKey = entry.getKey();
2403+
OptimizelyDecision decision = entry.getValue();
2404+
assertNotNull("Missing decision for flag " + flagKey, decision);
2405+
2406+
if (includedInHoldout.contains(flagKey)) {
2407+
// Should be holdout decision
2408+
assertEquals(variationKey, decision.getVariationKey());
2409+
assertFalse(decision.getEnabled());
2410+
assertTrue("Expected holdout reason for flag " + flagKey, decision.getReasons().contains(expectedReason));
2411+
holdoutCount++;
2412+
} else {
2413+
// Should NOT be a holdout decision
2414+
assertFalse("Non-included flag should not have holdout reason: " + flagKey,
2415+
decision.getReasons().contains(expectedReason));
2416+
}
2417+
}
2418+
assertEquals("Expected exactly the included flags to be in holdout", includedInHoldout.size(), holdoutCount);
2419+
}
2420+
2421+
@Test
2422+
public void testDecisionNotificationHandler_withHoldout() throws IOException {
2423+
assumeTrue(datafileVersion == Integer.parseInt(ProjectConfig.Version.V4.toString()));
2424+
2425+
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
2426+
OptimizelyClient optimizelyClient = createOptimizelyClientWithHoldouts(context);
2427+
2428+
String flagKey = "boolean_feature";
2429+
String userId = "user123";
2430+
String variationKey = "ho_off_key";
2431+
String ruleKey = "basic_holdout";
2432+
2433+
Map<String, Object> attributes = new HashMap<>();
2434+
attributes.put("$opt_bucketing_id", "ppid160000"); // deterministic bucketing into basic_holdout
2435+
attributes.put("nationality", "English"); // non-reserved attribute
2436+
2437+
final boolean[] listenerCalled = {false};
2438+
optimizelyClient.addDecisionNotificationHandler(decisionNotification -> {
2439+
assertEquals("FLAG", decisionNotification.getType());
2440+
assertEquals(userId, decisionNotification.getUserId());
2441+
assertEquals(attributes, decisionNotification.getAttributes());
2442+
2443+
Map<String, ?> info = decisionNotification.getDecisionInfo();
2444+
assertEquals(flagKey, info.get("flagKey"));
2445+
assertEquals(variationKey, info.get("variationKey"));
2446+
assertEquals(false, info.get("enabled"));
2447+
assertEquals(ruleKey, info.get("ruleKey"));
2448+
assertTrue(((Map<?, ?>) info.get("variables")).isEmpty());
2449+
2450+
listenerCalled[0] = true;
2451+
});
2452+
2453+
OptimizelyUserContext userContext = optimizelyClient.createUserContext(userId, attributes);
2454+
OptimizelyDecision decision = userContext.decide(flagKey, Collections.singletonList(OptimizelyDecideOption.INCLUDE_REASONS));
2455+
2456+
assertTrue("Decision notification handler should have been called", listenerCalled[0]);
2457+
assertEquals(variationKey, decision.getVariationKey());
2458+
assertFalse(decision.getEnabled());
2459+
}
2460+
22942461
// Utils
22952462

22962463
private boolean compareJsonStrings(String str1, String str2) {

0 commit comments

Comments
 (0)