Skip to content

Commit 6f4c1fd

Browse files
committed
Add test coverage for InternalEvaluator using fixture files
- Remove redundant manual test cases now covered by fixture-based tests - Add parametrized tests that iterate through all test case files in spec/fixtures/ufc/test_cases/ - Cover all edge cases including boolean logic, numeric comparisons, regex matching, null handling, disabled flags, type validation, and complex targeting rules - Use aggregate_failures for detailed error reporting on test failures - Maintain essential unit tests for initialization, type mapping, and error handling
1 parent b229404 commit 6f4c1fd

File tree

1 file changed

+82
-166
lines changed

1 file changed

+82
-166
lines changed

spec/datadog/open_feature/binding/internal_evaluator_spec.rb

Lines changed: 82 additions & 166 deletions
Original file line numberDiff line numberDiff line change
@@ -57,38 +57,6 @@
5757
end
5858
end
5959

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
9260

9361
context 'with type validation' do
9462
it 'returns TYPE_MISMATCH when types do not match with default value' do
@@ -122,139 +90,6 @@
12290
end
12391
end
12492

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
25893

25994
context 'type mapping' do
26095
let(:type_checker) { evaluator }
@@ -287,7 +122,6 @@
287122
end
288123

289124
it 'accesses flag properties correctly' do
290-
result = evaluator.get_assignment('empty_flag', {}, :string, 'default')
291125
config = evaluator.instance_variable_get(:@parsed_config)
292126
flag = config.get_flag('empty_flag')
293127

@@ -335,4 +169,86 @@
335169
expect(type_result.flag_metadata).to be_nil
336170
end
337171
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
338254
end

0 commit comments

Comments
 (0)