Skip to content

Commit 648fcd4

Browse files
add test cases
1 parent 7490378 commit 648fcd4

File tree

4 files changed

+831
-40
lines changed

4 files changed

+831
-40
lines changed

lib/optimizely/decision_service.rb

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -167,15 +167,14 @@ def get_variation_for_feature(project_config, feature_flag, user_context, decide
167167
# user_context - Optimizely user context instance
168168
#
169169
# Returns DecisionResult struct.
170-
# holdouts = project_config.get_holdouts_for_flag(feature_flag['id'])
171-
172-
# if holdouts && !holdouts.empty?
173-
# # Has holdouts - use get_decision_for_flag which checks holdouts first
174-
# get_decision_for_flag(feature_flag, user_context, project_config, decide_options)
175-
# else
176-
# get_variations_for_feature_list(project_config, [feature_flag], user_context, decide_options).first
177-
# end
178-
get_variations_for_feature_list(project_config, [feature_flag], user_context, decide_options).first
170+
holdouts = project_config.get_holdouts_for_flag(feature_flag['id'])
171+
172+
if holdouts && !holdouts.empty?
173+
# Has holdouts - use get_decision_for_flag which checks holdouts first
174+
get_decision_for_flag(feature_flag, user_context, project_config, decide_options)
175+
else
176+
get_variations_for_feature_list(project_config, [feature_flag], user_context, decide_options).first
177+
end
179178
end
180179

181180
def get_decision_for_flag(feature_flag, user_context, project_config, decide_options = [], user_profile_tracker = nil, decide_reasons = nil)
@@ -313,12 +312,9 @@ def get_variations_for_feature_list(project_config, feature_flags, user_context,
313312

314313
decisions = []
315314
feature_flags.each do |feature_flag|
316-
# check if the feature is being experiment on and whether the user is bucketed into the experiment
317-
decision_result = get_decision_for_flag(project_config, feature_flag, user_context, decide_options, user_profile_tracker)
318-
# # Only process rollout if no experiment decision was found and no error
319-
# if decision_result.decision.nil? && !decision_result.error
320-
# decision_result.reasons.push(*decision_result.reasons)
321-
# end
315+
# Always use get_decision_for_flag which checks holdouts, experiments, and rollouts in order
316+
# This matches Swift SDK's getDecisionForFlag behavior (DefaultDecisionService.swift:381-419)
317+
decision_result = get_decision_for_flag(feature_flag, user_context, project_config, decide_options, user_profile_tracker)
322318
decisions << decision_result
323319
end
324320
user_profile_tracker&.save_user_profile

spec/config/datafile_project_config_spec.rb

Lines changed: 53 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1249,41 +1249,60 @@
12491249
expect(holdouts).to eq([])
12501250
end
12511251

1252-
it 'should return only the most specific holdout for the flag' do
1253-
holdouts = config_with_holdouts.get_holdouts_for_flag('multi_variate_feature')
1254-
expect(holdouts.length).to eq(1)
1255-
1256-
# Should only return the explicit holdout (specific_holdout), not the global ones
1252+
it 'should return all applicable holdouts for the flag' do
1253+
# get_holdouts_for_flag expects flag ID, not key
1254+
feature_flag = config_with_holdouts.feature_flag_key_map['multi_variate_feature']
1255+
holdouts = config_with_holdouts.get_holdouts_for_flag(feature_flag['id'])
1256+
# Should return global holdouts (not excluded) + included holdouts
1257+
# global_holdout (not excluded), holdout_empty_1 (global), specific_holdout (included)
1258+
expect(holdouts.length).to eq(3)
1259+
1260+
# Should include the explicit holdout (specific_holdout)
12571261
specific_holdout = holdouts.find { |h| h['key'] == 'specific_holdout' }
12581262
expect(specific_holdout).not_to be_nil
12591263
expect(specific_holdout['id']).to eq('holdout_2')
12601264

1261-
# Global holdout should NOT be included when explicit holdout exists
1265+
# Global holdouts should also be included (Swift SDK alignment)
12621266
global_holdout = holdouts.find { |h| h['key'] == 'global_holdout' }
1263-
expect(global_holdout).to be_nil
1267+
expect(global_holdout).not_to be_nil
12641268
end
12651269

12661270
it 'should not return global holdouts that exclude the flag' do
1267-
holdouts = config_with_holdouts.get_holdouts_for_flag('boolean_single_variable_feature')
1271+
feature_flag = config_with_holdouts.feature_flag_key_map['boolean_single_variable_feature']
1272+
holdouts = config_with_holdouts.get_holdouts_for_flag(feature_flag['id'])
1273+
# Should only return holdout_empty_1 (global, not excluded)
1274+
# holdout_1 excludes this flag, so it shouldn't be included
12681275
expect(holdouts.length).to eq(1)
12691276

12701277
global_holdout = holdouts.find { |h| h['key'] == 'global_holdout' }
12711278
expect(global_holdout).to be_nil
1279+
1280+
empty_holdout = holdouts.find { |h| h['key'] == 'holdout_empty_1' }
1281+
expect(empty_holdout).not_to be_nil
12721282
end
12731283

1274-
it 'should cache results for subsequent calls' do
1275-
holdouts1 = config_with_holdouts.get_holdouts_for_flag('multi_variate_feature')
1276-
holdouts2 = config_with_holdouts.get_holdouts_for_flag('multi_variate_feature')
1284+
it 'should return consistent results for subsequent calls' do
1285+
feature_flag = config_with_holdouts.feature_flag_key_map['multi_variate_feature']
1286+
holdouts1 = config_with_holdouts.get_holdouts_for_flag(feature_flag['id'])
1287+
holdouts2 = config_with_holdouts.get_holdouts_for_flag(feature_flag['id'])
1288+
# Results are cached in @flag_holdouts_map
12771289
expect(holdouts1).to equal(holdouts2)
1278-
expect(holdouts1.length).to eq(1)
1290+
expect(holdouts1.length).to eq(3)
12791291
end
12801292

1281-
it 'should return only one global holdout for flags not specifically targeted' do
1282-
holdouts = config_with_holdouts.get_holdouts_for_flag('string_single_variable_feature')
1293+
it 'should return all global holdouts for flags not specifically targeted' do
1294+
feature_flag = config_with_holdouts.feature_flag_key_map['string_single_variable_feature']
1295+
holdouts = config_with_holdouts.get_holdouts_for_flag(feature_flag['id'])
12831296

1284-
# Should only include one global holdout (not excluded and no specific targeting)
1285-
expect(holdouts.length).to eq(1)
1286-
expect(holdouts.first['key']).to eq('global_holdout')
1297+
# Should include all global holdouts (not excluded and no specific targeting)
1298+
# global_holdout + holdout_empty_1
1299+
expect(holdouts.length).to eq(2)
1300+
1301+
global_holdout = holdouts.find { |h| h['key'] == 'global_holdout' }
1302+
expect(global_holdout).not_to be_nil
1303+
1304+
empty_holdout = holdouts.find { |h| h['key'] == 'holdout_empty_1' }
1305+
expect(empty_holdout).not_to be_nil
12871306
end
12881307
end
12891308

@@ -1395,7 +1414,11 @@
13951414

13961415
it 'should properly categorize holdouts during initialization' do
13971416
expect(config_with_complex_holdouts.holdout_id_map.keys).to contain_exactly('global_holdout', 'specific_holdout')
1398-
expect(config_with_complex_holdouts.global_holdouts.keys).to contain_exactly('global_holdout')
1417+
1418+
# global_holdouts is now an Array (Swift alignment)
1419+
expect(config_with_complex_holdouts.global_holdouts).to be_an(Array)
1420+
expect(config_with_complex_holdouts.global_holdouts.length).to eq(1)
1421+
expect(config_with_complex_holdouts.global_holdouts.first['id']).to eq('global_holdout')
13991422

14001423
# Use the correct feature flag IDs
14011424
boolean_feature_id = '155554'
@@ -1417,7 +1440,10 @@
14171440

14181441
it 'should only process running holdouts during initialization' do
14191442
expect(config_with_complex_holdouts.holdout_id_map['inactive_holdout']).to be_nil
1420-
expect(config_with_complex_holdouts.global_holdouts['inactive_holdout']).to be_nil
1443+
1444+
# global_holdouts is now an Array - check it doesn't contain inactive holdout
1445+
inactive_in_global = config_with_complex_holdouts.global_holdouts.any? { |h| h['id'] == 'inactive_holdout' }
1446+
expect(inactive_in_global).to be false
14211447

14221448
boolean_feature_id = '155554'
14231449
included_for_boolean = config_with_complex_holdouts.included_holdouts[boolean_feature_id]
@@ -1520,8 +1546,9 @@
15201546
end
15211547

15221548
it 'should properly cache holdout lookups' do
1523-
holdouts_1 = config_with_holdouts.get_holdouts_for_flag('boolean_feature')
1524-
holdouts_2 = config_with_holdouts.get_holdouts_for_flag('boolean_feature')
1549+
feature_flag = config_with_holdouts.feature_flag_key_map['boolean_feature']
1550+
holdouts_1 = config_with_holdouts.get_holdouts_for_flag(feature_flag['id'])
1551+
holdouts_2 = config_with_holdouts.get_holdouts_for_flag(feature_flag['id'])
15251552

15261553
expect(holdouts_1).to equal(holdouts_2)
15271554
end
@@ -1595,7 +1622,8 @@
15951622

15961623
it 'should handle mixed holdout configurations' do
15971624
# Verify the config has properly categorized holdouts
1598-
expect(config_with_holdouts.global_holdouts).to be_a(Hash)
1625+
# global_holdouts is an Array (Swift alignment), others are Hashes
1626+
expect(config_with_holdouts.global_holdouts).to be_an(Array)
15991627
expect(config_with_holdouts.included_holdouts).to be_a(Hash)
16001628
expect(config_with_holdouts.excluded_holdouts).to be_a(Hash)
16011629
end
@@ -1774,8 +1802,9 @@
17741802
config_json = JSON.dump(config_body_with_nil)
17751803
config = Optimizely::DatafileProjectConfig.new(config_json, logger, error_handler)
17761804

1777-
# Should treat as global holdout
1778-
expect(config.global_holdouts['holdout_nil']).not_to be_nil
1805+
# Should treat as global holdout (global_holdouts is an Array)
1806+
holdout_found = config.global_holdouts.any? { |h| h['id'] == 'holdout_nil' }
1807+
expect(holdout_found).to be true
17791808
end
17801809

17811810
it 'should only include running holdouts in maps' do

0 commit comments

Comments
 (0)