@@ -1074,6 +1074,84 @@ def test_get_variation_cmab_experiment_with_whitelisted_variation(self):
10741074 mock_bucket .assert_not_called ()
10751075 mock_cmab_decision .assert_not_called ()
10761076
1077+ def test_get_variation_cmab_experiment_excludes_user_profile_service (self ):
1078+ """Test that CMAB experiments do not use user profile service for sticky bucketing."""
1079+
1080+ # Create a user context
1081+ user = optimizely_user_context .OptimizelyUserContext (
1082+ optimizely_client = None ,
1083+ logger = None ,
1084+ user_id = "test_user" ,
1085+ user_attributes = {}
1086+ )
1087+
1088+ # Create a user profile service and tracker
1089+ user_profile_service = user_profile .UserProfileService ()
1090+ user_profile_tracker = user_profile .UserProfileTracker (user .user_id , user_profile_service )
1091+
1092+ # Create a CMAB experiment
1093+ cmab_experiment = entities .Experiment (
1094+ '111150' ,
1095+ 'cmab_experiment' ,
1096+ 'Running' ,
1097+ '111150' ,
1098+ [], # No audience IDs
1099+ {},
1100+ [
1101+ entities .Variation ('111151' , 'variation_1' ),
1102+ entities .Variation ('111152' , 'variation_2' )
1103+ ],
1104+ [
1105+ {'entityId' : '111151' , 'endOfRange' : 5000 },
1106+ {'entityId' : '111152' , 'endOfRange' : 10000 }
1107+ ],
1108+ cmab = {'trafficAllocation' : 5000 }
1109+ )
1110+
1111+ with mock .patch ('optimizely.helpers.experiment.is_experiment_running' , return_value = True ), \
1112+ mock .patch ('optimizely.helpers.audience.does_user_meet_audience_conditions' , return_value = [True , []]), \
1113+ mock .patch .object (self .decision_service .bucketer , 'bucket_to_entity_id' ,
1114+ return_value = ['$' , []]) as mock_bucket , \
1115+ mock .patch .object (self .decision_service , 'cmab_service' ) as mock_cmab_service , \
1116+ mock .patch .object (self .project_config , 'get_variation_from_id' ,
1117+ return_value = entities .Variation ('111151' , 'variation_1' )), \
1118+ mock .patch .object (self .decision_service , 'get_stored_variation' ) as mock_get_stored_variation , \
1119+ mock .patch .object (user_profile_tracker , 'update_user_profile' ) as mock_update_profile , \
1120+ mock .patch .object (self .decision_service , 'logger' ) as mock_logger :
1121+
1122+ # Configure CMAB service to return a decision
1123+ mock_cmab_service .get_decision .return_value = (
1124+ {
1125+ 'variation_id' : '111151' ,
1126+ 'cmab_uuid' : 'test-cmab-uuid'
1127+ },
1128+ []
1129+ )
1130+
1131+ # Call get_variation with the CMAB experiment and user profile tracker
1132+ variation_result = self .decision_service .get_variation (
1133+ self .project_config ,
1134+ cmab_experiment ,
1135+ user ,
1136+ user_profile_tracker
1137+ )
1138+ variation = variation_result ['variation' ]
1139+ cmab_uuid = variation_result ['cmab_uuid' ]
1140+ reasons = variation_result ['reasons' ]
1141+
1142+ # Verify we get a variation from CMAB decision
1143+ self .assertEqual ('variation_1' , variation .key )
1144+ self .assertEqual ('test-cmab-uuid' , cmab_uuid )
1145+
1146+ # Verify that get_stored_variation was NOT called (UPS lookup skipped)
1147+ mock_get_stored_variation .assert_not_called ()
1148+
1149+ # Verify that update_user_profile was NOT called (UPS save skipped)
1150+ mock_update_profile .assert_not_called ()
1151+
1152+ # Verify decision reasons include the UPS exclusion message
1153+ self .assertIn ('Skipping user profile service for CMAB experiment "cmab_experiment".' , reasons )
1154+
10771155
10781156class FeatureFlagDecisionTests (base .BaseTest ):
10791157 def setUp (self ):
0 commit comments