Skip to content

Commit 5e0808f

Browse files
update: add tests to verify precedence of whitelisted and forced variations over CMAB service decisions in DecisionService
1 parent ecf9199 commit 5e0808f

File tree

1 file changed

+134
-0
lines changed

1 file changed

+134
-0
lines changed

core-api/src/test/java/com/optimizely/ab/bucketing/DecisionServiceTest.java

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@
5656
import com.optimizely.ab.OptimizelyForcedDecision;
5757
import com.optimizely.ab.OptimizelyUserContext;
5858
import com.optimizely.ab.cmab.service.CmabService;
59+
import com.optimizely.ab.cmab.service.CmabDecision;
60+
import com.optimizely.ab.config.Cmab;
5961
import com.optimizely.ab.config.DatafileProjectConfigTestUtils;
6062
import static com.optimizely.ab.config.DatafileProjectConfigTestUtils.noAudienceProjectConfigV3;
6163
import static com.optimizely.ab.config.DatafileProjectConfigTestUtils.validProjectConfigV3;
@@ -1393,4 +1395,136 @@ public void userMeetsHoldoutAudienceConditions() {
13931395

13941396
logbackVerifier.expectMessage(Level.INFO, "User (user123) is in variation (ho_off_key) of holdout (typed_audience_holdout).");
13951397
}
1398+
1399+
/**
1400+
* Verify that whitelisted variations take precedence over CMAB service decisions
1401+
* in CMAB experiments.
1402+
*/
1403+
@Test
1404+
public void getVariationCmabExperimentWhitelistedPrecedesCmabService() {
1405+
// Create a CMAB experiment with whitelisting
1406+
Experiment cmabExperiment = createMockCmabExperiment();
1407+
Variation whitelistedVariation = cmabExperiment.getVariations().get(0);
1408+
1409+
// Setup whitelisting for the test user
1410+
Map<String, String> userIdToVariationKeyMap = new HashMap<>();
1411+
userIdToVariationKeyMap.put(whitelistedUserId, whitelistedVariation.getKey());
1412+
1413+
// Create mock Cmab object
1414+
Cmab mockCmab = mock(Cmab.class);
1415+
1416+
// Create experiment with whitelisting and CMAB config
1417+
Experiment experimentWithWhitelisting = new Experiment(
1418+
cmabExperiment.getId(),
1419+
cmabExperiment.getKey(),
1420+
cmabExperiment.getStatus(),
1421+
cmabExperiment.getLayerId(),
1422+
cmabExperiment.getAudienceIds(),
1423+
cmabExperiment.getAudienceConditions(),
1424+
cmabExperiment.getVariations(),
1425+
userIdToVariationKeyMap,
1426+
cmabExperiment.getTrafficAllocation(),
1427+
mockCmab // This makes it a CMAB experiment
1428+
);
1429+
1430+
// Mock CmabService.getDecision to return a different variation (should be ignored)
1431+
// Note: We don't need to mock anything since the user is whitelisted
1432+
1433+
// Call getVariation
1434+
DecisionResponse<Variation> result = decisionService.getVariation(
1435+
experimentWithWhitelisting,
1436+
optimizely.createUserContext(whitelistedUserId, Collections.emptyMap()),
1437+
v4ProjectConfig
1438+
);
1439+
1440+
// Verify whitelisted variation is returned
1441+
assertEquals(whitelistedVariation, result.getResult());
1442+
1443+
// Verify CmabService was never called since user is whitelisted
1444+
verify(mockCmabService, never()).getDecision(any(), any(), any(), any());
1445+
1446+
// Verify appropriate logging
1447+
logbackVerifier.expectMessage(Level.INFO,
1448+
"User \"" + whitelistedUserId + "\" is forced in variation \"" +
1449+
whitelistedVariation.getKey() + "\".");
1450+
}
1451+
1452+
/**
1453+
* Verify that forced variations take precedence over CMAB service decisions
1454+
* in CMAB experiments.
1455+
*/
1456+
@Test
1457+
public void getVariationCmabExperimentForcedPrecedesCmabService() {
1458+
// Create a CMAB experiment
1459+
Experiment cmabExperiment = createMockCmabExperiment();
1460+
Variation forcedVariation = cmabExperiment.getVariations().get(0);
1461+
Variation cmabServiceVariation = cmabExperiment.getVariations().get(1);
1462+
1463+
// Create mock Cmab object
1464+
Cmab mockCmab = mock(Cmab.class);
1465+
1466+
// Create experiment with CMAB config (no whitelisting)
1467+
Experiment experiment = new Experiment(
1468+
cmabExperiment.getId(),
1469+
cmabExperiment.getKey(),
1470+
cmabExperiment.getStatus(),
1471+
cmabExperiment.getLayerId(),
1472+
cmabExperiment.getAudienceIds(),
1473+
cmabExperiment.getAudienceConditions(),
1474+
cmabExperiment.getVariations(),
1475+
Collections.emptyMap(), // No whitelisting
1476+
cmabExperiment.getTrafficAllocation(),
1477+
mockCmab // This makes it a CMAB experiment
1478+
);
1479+
1480+
// Set forced variation for the user
1481+
decisionService.setForcedVariation(experiment, genericUserId, forcedVariation.getKey());
1482+
1483+
// Mock CmabService.getDecision to return a different variation (should be ignored)
1484+
CmabDecision mockCmabDecision = mock(CmabDecision.class);
1485+
when(mockCmabDecision.getVariationId()).thenReturn(cmabServiceVariation.getId());
1486+
when(mockCmabService.getDecision(any(), any(), any(), any()))
1487+
.thenReturn(mockCmabDecision);
1488+
1489+
// Call getVariation
1490+
DecisionResponse<Variation> result = decisionService.getVariation(
1491+
experiment,
1492+
optimizely.createUserContext(genericUserId, Collections.emptyMap()),
1493+
v4ProjectConfig
1494+
);
1495+
1496+
// Verify forced variation is returned (not CMAB service result)
1497+
assertEquals(forcedVariation, result.getResult());
1498+
1499+
// Verify CmabService was never called since user has forced variation
1500+
verify(mockCmabService, never()).getDecision(any(), any(), any(), any());
1501+
}
1502+
1503+
private Experiment createMockCmabExperiment() {
1504+
List<Variation> variations = Arrays.asList(
1505+
new Variation("111151", "variation_1"),
1506+
new Variation("111152", "variation_2")
1507+
);
1508+
1509+
List<TrafficAllocation> trafficAllocations = Arrays.asList(
1510+
new TrafficAllocation("111151", 5000),
1511+
new TrafficAllocation("111152", 10000)
1512+
);
1513+
1514+
// Mock CMAB configuration
1515+
Cmab mockCmab = mock(Cmab.class);
1516+
1517+
return new Experiment(
1518+
"111150",
1519+
"cmab_experiment",
1520+
"Running",
1521+
"111150",
1522+
Collections.emptyList(), // No audience IDs
1523+
null, // No audience conditions
1524+
variations,
1525+
Collections.emptyMap(), // No whitelisting initially
1526+
trafficAllocations,
1527+
mockCmab // This makes it a CMAB experiment
1528+
);
1529+
}
13961530
}

0 commit comments

Comments
 (0)