|
57 | 57 | end |
58 | 58 | end |
59 | 59 |
|
60 | | - context 'with flag lookup' do |
61 | | - it 'returns success for existing enabled flag' do |
62 | | - result = evaluator.get_assignment('numeric_flag', {}, :float, 0.0) |
63 | | - |
64 | | - expect(result.error_code).to eq(:Ok) |
65 | | - expect(result.value).not_to be_nil |
66 | | - expect(['STATIC', 'SPLIT', 'TARGETING_MATCH', 'DEFAULT']).to include(result.reason) |
67 | | - expect(result.variant).not_to be_nil |
68 | | - expect(result.flag_metadata).not_to be_nil |
69 | | - expect(result.flag_metadata.variation_type).to eq('number') |
70 | | - end |
71 | | - |
72 | | - it 'returns FLAG_UNRECOGNIZED_OR_DISABLED for missing flag with default value' do |
73 | | - result = evaluator.get_assignment('nonexistent_flag', {}, :string, 'fallback') |
74 | | - |
75 | | - expect(result.error_code).to eq(:FlagNotFound) |
76 | | - expect(result.error_message).to eq('flag is missing in configuration, it is either unrecognized or disabled') |
77 | | - expect(result.value).to eq('fallback') |
78 | | - expect(result.variant).to be_nil |
79 | | - expect(result.flag_metadata).to be_nil |
80 | | - end |
81 | | - |
82 | | - it 'returns FLAG_DISABLED for disabled flag with default value' do |
83 | | - result = evaluator.get_assignment('disabled_flag', {}, :integer, 42) |
84 | | - |
85 | | - expect(result.error_code).to eq(:Ok) # Special case - expected condition |
86 | | - expect(result.error_message).to eq('flag is disabled') |
87 | | - expect(result.value).to eq(42) |
88 | | - expect(result.variant).to be_nil |
89 | | - expect(result.flag_metadata).to be_nil |
90 | | - end |
91 | | - end |
92 | 60 |
|
93 | 61 | context 'with type validation' do |
94 | 62 | it 'returns TYPE_MISMATCH when types do not match with default value' do |
|
122 | 90 | end |
123 | 91 | end |
124 | 92 |
|
125 | | - context 'with different flag types' do |
126 | | - it 'handles STRING flags correctly' do |
127 | | - # empty_flag has no allocations, so let's test a flag with variations but expect DEFAULT behavior |
128 | | - result = evaluator.get_assignment('empty_flag', {}, :string, 'default') |
129 | | - |
130 | | - expect(result.error_code).to eq(:Ok) |
131 | | - expect(result.reason).to eq('DEFAULT') |
132 | | - expect(result.value).to eq('default') |
133 | | - expect(result.variant).to be_nil |
134 | | - expect(result.flag_metadata).to be_nil |
135 | | - end |
136 | | - |
137 | | - it 'handles INTEGER flags correctly' do |
138 | | - # Find an integer flag in our test data |
139 | | - result = evaluator.get_assignment('disabled_flag', {}, :integer, 0) |
140 | | - |
141 | | - # This should be disabled, but let's test with an enabled integer flag if available |
142 | | - # For now, test the type validation logic |
143 | | - expect(result.error_code).to eq(:Ok) # Expected since disabled_flag is disabled (special case) |
144 | | - expect(result.value).to eq(0) # Should return default value |
145 | | - expect(result.variant).to be_nil |
146 | | - expect(result.flag_metadata).to be_nil |
147 | | - end |
148 | | - |
149 | | - it 'handles NUMERIC flags correctly' do |
150 | | - result = evaluator.get_assignment('numeric_flag', {}, :float, 0.0) |
151 | | - |
152 | | - expect(result.error_code).to eq(:Ok) |
153 | | - expect(result.flag_metadata.variation_type).to eq('number') |
154 | | - expect(result.value).to be_a(Numeric) |
155 | | - end |
156 | | - |
157 | | - it 'handles JSON flags correctly' do |
158 | | - result = evaluator.get_assignment('no_allocations_flag', {}, :object, {}) |
159 | | - |
160 | | - # This flag likely has no allocations, so should return DEFAULT_ALLOCATION_NULL |
161 | | - if result.error_code == :Ok && result.variant.nil? # DEFAULT_ALLOCATION_NULL case |
162 | | - expect(result.value).to eq({}) # Default value |
163 | | - expect(result.flag_metadata).to be_nil |
164 | | - else |
165 | | - expect(result.error_code).to eq(:Ok) |
166 | | - expect(result.flag_metadata.variation_type).to eq('object') |
167 | | - expect(result.value).to be_a(Hash) |
168 | | - end |
169 | | - end |
170 | | - end |
171 | | - |
172 | | - context 'with flag variations and allocations' do |
173 | | - it 'uses actual variation values from allocations when available' do |
174 | | - result = evaluator.get_assignment('numeric_flag', {}, :float, 0.0) |
175 | | - |
176 | | - expect(result.error_code).to eq(:Ok) |
177 | | - expect(result.value).to be_a(Numeric) |
178 | | - expect(result.variant).not_to eq('default') # Should use actual variation key from split |
179 | | - expect(['STATIC', 'SPLIT', 'TARGETING_MATCH']).to include(result.reason) # Should use allocation-based reason |
180 | | - expect(result.flag_metadata).not_to be_nil |
181 | | - end |
182 | | - |
183 | | - it 'returns DEFAULT_ALLOCATION_NULL for flags without allocations' do |
184 | | - result = evaluator.get_assignment('no_allocations_flag', {}, :object, { "fallback" => true }) |
185 | | - |
186 | | - # Flag with no allocations should return DEFAULT_ALLOCATION_NULL error with default value |
187 | | - expect(result.error_code).to eq(:Ok) # Special case - expected condition |
188 | | - expect(result.value).to eq({ "fallback" => true }) |
189 | | - expect(result.variant).to be_nil |
190 | | - expect(result.flag_metadata).to be_nil |
191 | | - end |
192 | | - |
193 | | - it 'uses real allocation metadata' do |
194 | | - result = evaluator.get_assignment('numeric_flag', {}, :float, 0.0) |
195 | | - |
196 | | - expect(result.error_code).to eq(:Ok) |
197 | | - expect(result.flag_metadata.allocation_key).not_to eq('mock_allocation') |
198 | | - expect([true, false]).to include(result.flag_metadata.do_log) |
199 | | - end |
200 | | - |
201 | | - it 'handles flags with allocations that have splits' do |
202 | | - result = evaluator.get_assignment('empty_flag', {}, :string, 'default') |
203 | | - |
204 | | - if result.variant.nil? # DEFAULT_ALLOCATION_NULL case |
205 | | - # Flag has no valid allocations |
206 | | - expect(result.error_code).to eq(:Ok) |
207 | | - expect(result.value).to eq('default') |
208 | | - expect(result.flag_metadata).to be_nil |
209 | | - else |
210 | | - # Flag has valid allocation and split |
211 | | - expect(result.error_code).to eq(:Ok) |
212 | | - expect(result.value).not_to be_nil |
213 | | - expect(result.variant).not_to be_empty |
214 | | - expect(['STATIC', 'SPLIT', 'TARGETING_MATCH', 'DEFAULT']).to include(result.reason) |
215 | | - expect(result.flag_metadata).not_to be_nil |
216 | | - end |
217 | | - end |
218 | | - |
219 | | - it 'uses real variation values not generated defaults' do |
220 | | - # Test with flags that have actual variations |
221 | | - result = evaluator.get_assignment('numeric_flag', {}, :float, 0.0) |
222 | | - |
223 | | - if result.error_code == :Ok && result.variant |
224 | | - expect(result.value).to be_a(Numeric) |
225 | | - expect(result.value).not_to eq(0.0) # Should be real variation value, not default |
226 | | - end |
227 | | - end |
228 | | - |
229 | | - it 'handles allocation matching correctly' do |
230 | | - # Test allocation matching logic works for different flags |
231 | | - result = evaluator.get_assignment('numeric_flag', {}, :float, 0.0) |
232 | | - |
233 | | - if result.error_code == :Ok && result.variant |
234 | | - expect(result.flag_metadata.allocation_key).not_to be_empty |
235 | | - expect([true, false]).to include(result.flag_metadata.do_log) |
236 | | - end |
237 | | - end |
238 | | - |
239 | | - it 'returns DEFAULT_ALLOCATION_NULL for flags without valid allocations' do |
240 | | - # Test that doLog handling works correctly for error cases |
241 | | - result = evaluator.get_assignment('no_allocations_flag', {}, :object, { "default" => "value" }) |
242 | | - |
243 | | - expect(result.error_code).to eq(:Ok) # Special case - expected condition |
244 | | - expect(result.value).to eq({ "default" => "value" }) |
245 | | - expect(result.variant).to be_nil |
246 | | - expect(result.flag_metadata).to be_nil |
247 | | - end |
248 | | - |
249 | | - it 'handles different flags with proper allocation evaluation' do |
250 | | - # Ensure allocation matching works for different flags |
251 | | - flag1 = evaluator.get_assignment('numeric_flag', {}, :float, 1.0) |
252 | | - flag2 = evaluator.get_assignment('empty_flag', {}, :string, 'empty_default') |
253 | | - |
254 | | - # Results should be different (either success with different values, or errors with different defaults) |
255 | | - expect([flag1.value, flag1.error_code]).not_to eq([flag2.value, flag2.error_code]) |
256 | | - end |
257 | | - end |
258 | 93 |
|
259 | 94 | context 'type mapping' do |
260 | 95 | let(:type_checker) { evaluator } |
|
287 | 122 | end |
288 | 123 |
|
289 | 124 | it 'accesses flag properties correctly' do |
290 | | - result = evaluator.get_assignment('empty_flag', {}, :string, 'default') |
291 | 125 | config = evaluator.instance_variable_get(:@parsed_config) |
292 | 126 | flag = config.get_flag('empty_flag') |
293 | 127 |
|
|
335 | 169 | expect(type_result.flag_metadata).to be_nil |
336 | 170 | end |
337 | 171 | end |
| 172 | + |
| 173 | + describe 'test case coverage' do |
| 174 | + let(:evaluator) { described_class.new(valid_ufc_json) } |
| 175 | + |
| 176 | + # Load all test case files at evaluation time |
| 177 | + Dir.glob(File.join(__dir__, '../../../fixtures/ufc/test_cases/*.json')).each do |test_file| |
| 178 | + describe "Test cases from #{File.basename(test_file)}" do |
| 179 | + let(:test_cases) { JSON.parse(File.read(test_file)) } |
| 180 | + |
| 181 | + it 'executes all test cases in the file' do |
| 182 | + test_cases.each_with_index do |test_case, index| |
| 183 | + # Extract test case data |
| 184 | + flag_key = test_case['flag'] |
| 185 | + variation_type = test_case['variationType'] |
| 186 | + default_value = test_case['defaultValue'] |
| 187 | + targeting_key = test_case['targetingKey'] |
| 188 | + attributes = test_case['attributes'] || {} |
| 189 | + expected_result = test_case['result'] |
| 190 | + |
| 191 | + # Convert variation type to expected_type symbol |
| 192 | + expected_type = case variation_type |
| 193 | + when 'STRING' then :string |
| 194 | + when 'INTEGER' then :integer |
| 195 | + when 'NUMERIC' then :float |
| 196 | + when 'BOOLEAN' then :boolean |
| 197 | + when 'JSON' then :object |
| 198 | + else nil |
| 199 | + end |
| 200 | + |
| 201 | + # Build evaluation context |
| 202 | + evaluation_context = attributes.dup |
| 203 | + evaluation_context['targetingKey'] = targeting_key if targeting_key |
| 204 | + |
| 205 | + # Execute test case |
| 206 | + result = evaluator.get_assignment(flag_key, evaluation_context, expected_type, default_value) |
| 207 | + |
| 208 | + # Wrap expectations in aggregate_failures for better error reporting |
| 209 | + aggregate_failures "Test case ##{index + 1}: #{targeting_key} with #{attributes.keys.join(', ')}" do |
| 210 | + # Check value |
| 211 | + expect(result.value).to eq(expected_result['value']), |
| 212 | + "Expected value #{expected_result['value'].inspect}, got #{result.value.inspect}" |
| 213 | + |
| 214 | + # Check variant (if expected) |
| 215 | + if expected_result.key?('variant') |
| 216 | + expect(result.variant).to eq(expected_result['variant']), |
| 217 | + "Expected variant #{expected_result['variant'].inspect}, got #{result.variant.inspect}" |
| 218 | + else |
| 219 | + expect(result.variant).to be_nil, |
| 220 | + "Expected no variant, got #{result.variant.inspect}" |
| 221 | + end |
| 222 | + |
| 223 | + # Check flag metadata (if expected) |
| 224 | + if expected_result.key?('flagMetadata') |
| 225 | + expected_metadata = expected_result['flagMetadata'] |
| 226 | + expect(result.flag_metadata).not_to be_nil, |
| 227 | + "Expected flag metadata, got nil" |
| 228 | + expect(result.flag_metadata.allocation_key).to eq(expected_metadata['allocationKey']), |
| 229 | + "Expected allocation key #{expected_metadata['allocationKey'].inspect}, got #{result.flag_metadata&.allocation_key.inspect}" |
| 230 | + expect(result.flag_metadata.variation_type).to eq(expected_metadata['variationType']), |
| 231 | + "Expected variation type #{expected_metadata['variationType'].inspect}, got #{result.flag_metadata&.variation_type.inspect}" |
| 232 | + expect(result.flag_metadata.do_log).to eq(expected_metadata['doLog']), |
| 233 | + "Expected do_log #{expected_metadata['doLog'].inspect}, got #{result.flag_metadata&.do_log.inspect}" |
| 234 | + else |
| 235 | + expect(result.flag_metadata).to be_nil, |
| 236 | + "Expected no flag metadata, got #{result.flag_metadata.inspect}" |
| 237 | + end |
| 238 | + |
| 239 | + # Check error code - should be :Ok for successful evaluations, or specific error for failures |
| 240 | + if expected_result.key?('variant') || expected_result.key?('flagMetadata') |
| 241 | + expect(result.error_code).to eq(:Ok), |
| 242 | + "Expected :Ok error code for successful evaluation, got #{result.error_code.inspect}" |
| 243 | + else |
| 244 | + # For cases that return default value only, check if it's an expected condition |
| 245 | + expect([nil, :Ok]).to include(result.error_code), |
| 246 | + "Expected nil or :Ok error code for default value case, got #{result.error_code.inspect}" |
| 247 | + end |
| 248 | + end |
| 249 | + end |
| 250 | + end |
| 251 | + end |
| 252 | + end |
| 253 | + end |
338 | 254 | end |
0 commit comments