Skip to content

Commit b2f270f

Browse files
update: add tests for CMAB experiment variations in DecisionService
1 parent d8b0134 commit b2f270f

File tree

1 file changed

+129
-1
lines changed

1 file changed

+129
-1
lines changed

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

Lines changed: 129 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1560,6 +1560,134 @@ public void getVariationCmabExperimentServiceError() {
15601560
verify(mockCmabService, times(1)).getDecision(any(), any(), any(), any());
15611561
}
15621562

1563+
/**
1564+
* Verify that getVariation returns the variation from CMAB service
1565+
* when user is bucketed into CMAB traffic and service returns a valid decision.
1566+
*/
1567+
@Test
1568+
public void getVariationCmabExperimentServiceSuccess() {
1569+
// Create a CMAB experiment
1570+
Experiment cmabExperiment = createMockCmabExperiment();
1571+
Variation expectedVariation = cmabExperiment.getVariations().get(1); // Use second variation
1572+
1573+
// Create mock Cmab object
1574+
Cmab mockCmab = mock(Cmab.class);
1575+
when(mockCmab.getTrafficAllocation()).thenReturn(4000);
1576+
1577+
// Create experiment with CMAB config (no whitelisting, no forced variations)
1578+
Experiment experiment = new Experiment(
1579+
cmabExperiment.getId(),
1580+
cmabExperiment.getKey(),
1581+
cmabExperiment.getStatus(),
1582+
cmabExperiment.getLayerId(),
1583+
cmabExperiment.getAudienceIds(),
1584+
cmabExperiment.getAudienceConditions(),
1585+
cmabExperiment.getVariations(),
1586+
Collections.emptyMap(), // No whitelisting
1587+
cmabExperiment.getTrafficAllocation(),
1588+
mockCmab // This makes it a CMAB experiment
1589+
);
1590+
1591+
Bucketer mockBucketer = mock(Bucketer.class);
1592+
when(mockBucketer.bucketForCmab(any(Experiment.class), anyString(), any(ProjectConfig.class)))
1593+
.thenReturn(DecisionResponse.responseNoReasons("$"));
1594+
DecisionService decisionServiceWithMockCmabService = new DecisionService(
1595+
mockBucketer,
1596+
mockErrorHandler,
1597+
null,
1598+
mockCmabService
1599+
);
1600+
1601+
// Mock CmabService.getDecision to return a valid decision
1602+
CmabDecision mockCmabDecision = mock(CmabDecision.class);
1603+
when(mockCmabDecision.getVariationId()).thenReturn(expectedVariation.getId());
1604+
when(mockCmabService.getDecision(any(), any(), any(), any()))
1605+
.thenReturn(mockCmabDecision);
1606+
1607+
// Call getVariation
1608+
DecisionResponse<Variation> result = decisionServiceWithMockCmabService.getVariation(
1609+
experiment,
1610+
optimizely.createUserContext(genericUserId, Collections.emptyMap()),
1611+
v4ProjectConfig
1612+
);
1613+
1614+
// Verify that CMAB service decision is returned
1615+
assertEquals(expectedVariation, result.getResult());
1616+
1617+
// Verify that the result is not an error
1618+
assertFalse(result.isError());
1619+
1620+
// Assert that CmabService.getDecision was called exactly once
1621+
verify(mockCmabService, times(1)).getDecision(any(), any(), any(), any());
1622+
1623+
// Verify that the correct parameters were passed to CMAB service
1624+
verify(mockCmabService).getDecision(
1625+
eq(v4ProjectConfig),
1626+
any(OptimizelyUserContext.class),
1627+
eq(experiment.getId()),
1628+
any(List.class)
1629+
);
1630+
}
1631+
1632+
/**
1633+
* Verify that getVariation returns null when user is not bucketed into CMAB traffic
1634+
* by mocking the bucketer to return null for CMAB allocation.
1635+
*/
1636+
@Test
1637+
public void getVariationCmabExperimentUserNotInTrafficAllocation() {
1638+
// Create a CMAB experiment
1639+
Experiment cmabExperiment = createMockCmabExperiment();
1640+
1641+
// Create mock Cmab object
1642+
Cmab mockCmab = mock(Cmab.class);
1643+
when(mockCmab.getTrafficAllocation()).thenReturn(5000); // 50% traffic allocation
1644+
1645+
// Create experiment with CMAB config (no whitelisting, no forced variations)
1646+
Experiment experiment = new Experiment(
1647+
cmabExperiment.getId(),
1648+
cmabExperiment.getKey(),
1649+
cmabExperiment.getStatus(),
1650+
cmabExperiment.getLayerId(),
1651+
cmabExperiment.getAudienceIds(),
1652+
cmabExperiment.getAudienceConditions(),
1653+
cmabExperiment.getVariations(),
1654+
Collections.emptyMap(), // No whitelisting
1655+
cmabExperiment.getTrafficAllocation(),
1656+
mockCmab // This makes it a CMAB experiment
1657+
);
1658+
1659+
// Mock bucketer to return null for CMAB allocation (user not in CMAB traffic)
1660+
Bucketer mockBucketer = mock(Bucketer.class);
1661+
when(mockBucketer.bucketForCmab(any(Experiment.class), anyString(), any(ProjectConfig.class)))
1662+
.thenReturn(DecisionResponse.nullNoReasons());
1663+
1664+
DecisionService decisionServiceWithMockCmabService = new DecisionService(
1665+
mockBucketer,
1666+
mockErrorHandler,
1667+
null,
1668+
mockCmabService
1669+
);
1670+
1671+
// Call getVariation
1672+
DecisionResponse<Variation> result = decisionServiceWithMockCmabService.getVariation(
1673+
experiment,
1674+
optimizely.createUserContext(genericUserId, Collections.emptyMap()),
1675+
v4ProjectConfig
1676+
);
1677+
1678+
// Verify that no variation is returned (user not in CMAB traffic)
1679+
assertNull(result.getResult());
1680+
1681+
// Verify that the result is not an error
1682+
assertFalse(result.isError());
1683+
1684+
// Assert that CmabService.getDecision was never called (user not in CMAB traffic)
1685+
verify(mockCmabService, never()).getDecision(any(), any(), any(), any());
1686+
1687+
// Verify that bucketer was called for CMAB allocation
1688+
verify(mockBucketer, times(1)).bucketForCmab(any(Experiment.class), anyString(), any(ProjectConfig.class));
1689+
}
1690+
15631691
private Experiment createMockCmabExperiment() {
15641692
List<Variation> variations = Arrays.asList(
15651693
new Variation("111151", "variation_1"),
@@ -1568,7 +1696,7 @@ private Experiment createMockCmabExperiment() {
15681696

15691697
List<TrafficAllocation> trafficAllocations = Arrays.asList(
15701698
new TrafficAllocation("111151", 5000),
1571-
new TrafficAllocation("111152", 10000)
1699+
new TrafficAllocation("111152", 4000)
15721700
);
15731701

15741702
// Mock CMAB configuration

0 commit comments

Comments
 (0)