Skip to content

Commit c07028a

Browse files
committed
[AI-FSSDK] [FSSDK-12262] Exclude CMAB from UserProfileService
1 parent 4829b04 commit c07028a

2 files changed

Lines changed: 80 additions & 2 deletions

File tree

lib/optimizely/decision_service.rb

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,12 @@ def get_variation(project_config, experiment_id, user_context, user_profile_trac
100100

101101
should_ignore_user_profile_service = decide_options.include? Optimizely::Decide::OptimizelyDecideOption::IGNORE_USER_PROFILE_SERVICE
102102
# Check for saved bucketing decisions if decide_options do not include ignoreUserProfileService
103-
unless should_ignore_user_profile_service && user_profile_tracker
103+
# Skip UPS for CMAB experiments as they require dynamic bucketing
104+
if experiment.key?('cmab')
105+
message = "User Profile Service excluded for CMAB experiment '#{experiment_key}'."
106+
@logger.log(Logger::INFO, message)
107+
decide_reasons.push(message)
108+
elsif !should_ignore_user_profile_service && user_profile_tracker
104109
saved_variation_id, reasons_received = get_saved_variation_id(project_config, experiment_id, user_profile_tracker.user_profile)
105110
decide_reasons.push(*reasons_received)
106111
if saved_variation_id
@@ -155,7 +160,8 @@ def get_variation(project_config, experiment_id, user_context, user_profile_trac
155160
decide_reasons.push(message) if message
156161

157162
# Persist bucketing decision
158-
user_profile_tracker.update_user_profile(experiment_id, variation_id) unless should_ignore_user_profile_service && user_profile_tracker
163+
# Skip UPS update for CMAB experiments
164+
user_profile_tracker.update_user_profile(experiment_id, variation_id) unless should_ignore_user_profile_service || experiment.key?('cmab') || !user_profile_tracker
159165
VariationResult.new(cmab_uuid, false, decide_reasons, variation_id)
160166
end
161167

spec/decision_service_spec.rb

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1166,5 +1166,77 @@
11661166
expect(spy_cmab_service).not_to have_received(:get_decision)
11671167
end
11681168
end
1169+
1170+
describe 'when User Profile Service should be excluded' do
1171+
it 'should skip UPS lookup and update for CMAB experiments' do
1172+
# Create a CMAB experiment configuration
1173+
cmab_experiment = {
1174+
'id' => '111150',
1175+
'key' => 'cmab_experiment',
1176+
'status' => 'Running',
1177+
'layerId' => '111150',
1178+
'audienceIds' => [],
1179+
'forcedVariations' => {},
1180+
'variations' => [
1181+
{'id' => '111151', 'key' => 'variation_1'},
1182+
{'id' => '111152', 'key' => 'variation_2'}
1183+
],
1184+
'trafficAllocation' => [
1185+
{'entityId' => '111151', 'endOfRange' => 5000},
1186+
{'entityId' => '111152', 'endOfRange' => 10_000}
1187+
],
1188+
'cmab' => {'trafficAllocation' => 5000}
1189+
}
1190+
user_context = project_instance.create_user_context('test_user', {})
1191+
1192+
# Create a user profile tracker
1193+
user_profile_tracker = Optimizely::UserProfileTracker.new('test_user', spy_user_profile_service, spy_logger)
1194+
1195+
# Mock experiment lookup to return our CMAB experiment
1196+
allow(config).to receive(:get_experiment_from_id).with('111150').and_return(cmab_experiment)
1197+
allow(config).to receive(:experiment_running?).with(cmab_experiment).and_return(true)
1198+
1199+
# Mock audience evaluation to pass
1200+
allow(Optimizely::Audience).to receive(:user_meets_audience_conditions?).and_return([true, []])
1201+
1202+
# Mock bucketer to return a valid entity ID (user is in traffic allocation)
1203+
allow(decision_service.bucketer).to receive(:bucket_to_entity_id)
1204+
.with(config, cmab_experiment, 'test_user', 'test_user')
1205+
.and_return(['$', []])
1206+
1207+
# Mock CMAB service to return a decision
1208+
allow(spy_cmab_service).to receive(:get_decision)
1209+
.with(config, user_context, '111150', [])
1210+
.and_return(Optimizely::CmabDecision.new(variation_id: '111151', cmab_uuid: 'test-cmab-uuid-123'))
1211+
1212+
# Mock variation lookup
1213+
allow(config).to receive(:get_variation_from_id_by_experiment_id)
1214+
.with('111150', '111151')
1215+
.and_return({'id' => '111151', 'key' => 'variation_1'})
1216+
1217+
# Spy on get_saved_variation_id to ensure it's not called
1218+
allow(decision_service).to receive(:get_saved_variation_id).and_call_original
1219+
1220+
# Spy on user_profile_tracker methods
1221+
allow(user_profile_tracker).to receive(:update_user_profile).and_call_original
1222+
1223+
variation_result = decision_service.get_variation(config, '111150', user_context, user_profile_tracker)
1224+
1225+
expect(variation_result.variation_id).to eq('111151')
1226+
expect(variation_result.cmab_uuid).to eq('test-cmab-uuid-123')
1227+
expect(variation_result.error).to eq(false)
1228+
1229+
# Verify that UPS exclusion message is in reasons
1230+
expect(variation_result.reasons).to include(
1231+
"User Profile Service excluded for CMAB experiment 'cmab_experiment'."
1232+
)
1233+
1234+
# Verify get_saved_variation_id was NOT called (UPS lookup excluded)
1235+
expect(decision_service).not_to have_received(:get_saved_variation_id)
1236+
1237+
# Verify update_user_profile was NOT called (UPS update excluded)
1238+
expect(user_profile_tracker).not_to have_received(:update_user_profile)
1239+
end
1240+
end
11691241
end
11701242
end

0 commit comments

Comments
 (0)