@@ -1500,6 +1500,66 @@ public void getVariationCmabExperimentForcedPrecedesCmabService() {
15001500 verify (mockCmabService , never ()).getDecision (any (), any (), any (), any ());
15011501 }
15021502
1503+ /**
1504+ * Verify that getVariation handles CMAB service errors gracefully
1505+ * and falls back appropriately when CmabService throws an exception.
1506+ */
1507+ @ Test
1508+ public void getVariationCmabExperimentServiceError () {
1509+ // Create a CMAB experiment
1510+ Experiment cmabExperiment = createMockCmabExperiment ();
1511+
1512+ // Create mock Cmab object
1513+ Cmab mockCmab = mock (Cmab .class );
1514+ when (mockCmab .getTrafficAllocation ()).thenReturn (10000 );
1515+
1516+ // Create experiment with CMAB config (no whitelisting, no forced variations)
1517+ Experiment experiment = new Experiment (
1518+ cmabExperiment .getId (),
1519+ cmabExperiment .getKey (),
1520+ cmabExperiment .getStatus (),
1521+ cmabExperiment .getLayerId (),
1522+ cmabExperiment .getAudienceIds (),
1523+ cmabExperiment .getAudienceConditions (),
1524+ cmabExperiment .getVariations (),
1525+ Collections .emptyMap (), // No whitelisting
1526+ cmabExperiment .getTrafficAllocation (),
1527+ mockCmab // This makes it a CMAB experiment
1528+ );
1529+
1530+ Bucketer bucketer = new Bucketer ();
1531+ DecisionService decisionServiceWithMockCmabService = new DecisionService (
1532+ bucketer ,
1533+ mockErrorHandler ,
1534+ null ,
1535+ mockCmabService
1536+ );
1537+
1538+ // Mock CmabService.getDecision to throw an exception
1539+ RuntimeException cmabException = new RuntimeException ("CMAB service unavailable" );
1540+ when (mockCmabService .getDecision (any (), any (), any (), any ()))
1541+ .thenThrow (cmabException );
1542+
1543+ // Call getVariation
1544+ DecisionResponse <Variation > result = decisionServiceWithMockCmabService .getVariation (
1545+ experiment ,
1546+ optimizely .createUserContext (genericUserId , Collections .emptyMap ()),
1547+ v4ProjectConfig
1548+ );
1549+
1550+ // Verify that the method handles the error gracefully
1551+ // The result depends on whether the real bucketer allocates the user to CMAB traffic or not
1552+ // If user is not in CMAB traffic: result should be null
1553+ // If user is in CMAB traffic but CMAB service fails: result should be null
1554+ assertNull (result .getResult ());
1555+
1556+ // Verify that the error is not propagated (no exception thrown)
1557+ assertFalse (result .isError ());
1558+
1559+ // Assert that CmabService.getDecision was called exactly once
1560+ verify (mockCmabService , times (1 )).getDecision (any (), any (), any (), any ());
1561+ }
1562+
15031563 private Experiment createMockCmabExperiment () {
15041564 List <Variation > variations = Arrays .asList (
15051565 new Variation ("111151" , "variation_1" ),
0 commit comments