Skip to content

Commit dc56c76

Browse files
Copilotjosecolella
andauthored
feat: Add runtime type validation for default values in flag evaluation methods (#194)
Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: josecolella <[email protected]>
1 parent f983a3f commit dc56c76

File tree

3 files changed

+92
-3
lines changed

3 files changed

+92
-3
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,17 +82,17 @@ bool_value = client.fetch_boolean_value(flag_key: 'boolean_flag', default_value:
8282

8383
# a details method is also available for more information about the flag evaluation
8484
# see `ResolutionDetails` for more info
85-
bool_details = client.fetch_boolean_details(flag_key: 'boolean_flag', default_value: false) ==
85+
bool_details = client.fetch_boolean_details(flag_key: 'boolean_flag', default_value: false)
8686

8787
# fetching string value feature flag
88-
string_value = client.fetch_string_value(flag_key: 'string_flag', default_value: false)
88+
string_value = client.fetch_string_value(flag_key: 'string_flag', default_value: 'default')
8989

9090
# fetching number value feature flag
9191
float_value = client.fetch_number_value(flag_key: 'number_value', default_value: 1.0)
9292
integer_value = client.fetch_number_value(flag_key: 'number_value', default_value: 1)
9393

9494
# get an object value
95-
object = client.fetch_object_value(flag_key: 'object_value', default_value: JSON.dump({ name: 'object'}))
95+
object = client.fetch_object_value(flag_key: 'object_value', default_value: { name: 'object'})
9696
```
9797

9898
## 🌟 Features

lib/open_feature/sdk/client.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ def fetch_#{result_type}_#{suffix}(flag_key:, default_value:, evaluation_context
4444
private
4545

4646
def fetch_details(type:, flag_key:, default_value:, evaluation_context: nil)
47+
validate_default_value_type(type, default_value)
48+
4749
built_context = EvaluationContextBuilder.new.call(api_context: OpenFeature::SDK.evaluation_context, client_context: self.evaluation_context, invocation_context: evaluation_context)
4850

4951
resolution_details = @provider.send(:"fetch_#{type}_value", flag_key:, default_value:, evaluation_context: built_context)
@@ -56,6 +58,15 @@ def fetch_details(type:, flag_key:, default_value:, evaluation_context: nil)
5658

5759
EvaluationDetails.new(flag_key:, resolution_details:)
5860
end
61+
62+
def validate_default_value_type(type, default_value)
63+
expected_classes = TYPE_CLASS_MAP[type]
64+
unless expected_classes.any? { |klass| default_value.is_a?(klass) }
65+
expected_types = expected_classes.map(&:name).join(" or ")
66+
actual_type = default_value.class.name
67+
raise ArgumentError, "Default value for #{type} must be #{expected_types}, got #{actual_type}"
68+
end
69+
end
5970
end
6071
end
6172
end

spec/open_feature/sdk/client_spec.rb

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,5 +353,83 @@
353353
pending
354354
end
355355
end
356+
357+
context "Default Value Type Validation" do
358+
let(:flag_key) { "test-flag" }
359+
360+
context "Valid default value types" do
361+
it "accepts boolean values for fetch_boolean_value" do
362+
expect { client.fetch_boolean_value(flag_key:, default_value: true) }.not_to raise_error
363+
expect { client.fetch_boolean_value(flag_key:, default_value: false) }.not_to raise_error
364+
end
365+
366+
it "accepts string values for fetch_string_value" do
367+
expect { client.fetch_string_value(flag_key:, default_value: "test") }.not_to raise_error
368+
expect { client.fetch_string_value(flag_key:, default_value: "") }.not_to raise_error
369+
end
370+
371+
it "accepts numeric values for fetch_number_value" do
372+
expect { client.fetch_number_value(flag_key:, default_value: 42) }.not_to raise_error
373+
expect { client.fetch_number_value(flag_key:, default_value: 3.14) }.not_to raise_error
374+
end
375+
376+
it "accepts integer values for fetch_integer_value" do
377+
expect { client.fetch_integer_value(flag_key:, default_value: 42) }.not_to raise_error
378+
end
379+
380+
it "accepts float values for fetch_float_value" do
381+
expect { client.fetch_float_value(flag_key:, default_value: 3.14) }.not_to raise_error
382+
end
383+
384+
it "accepts array and hash values for fetch_object_value" do
385+
expect { client.fetch_object_value(flag_key:, default_value: {}) }.not_to raise_error
386+
expect { client.fetch_object_value(flag_key:, default_value: []) }.not_to raise_error
387+
end
388+
end
389+
390+
context "Invalid default value types" do
391+
it "raises ArgumentError for invalid boolean default values" do
392+
expect { client.fetch_boolean_value(flag_key:, default_value: "not boolean") }
393+
.to raise_error(ArgumentError, /Default value for boolean must be TrueClass or FalseClass, got String/)
394+
expect { client.fetch_boolean_value(flag_key:, default_value: 1) }
395+
.to raise_error(ArgumentError, /Default value for boolean must be TrueClass or FalseClass, got Integer/)
396+
end
397+
398+
it "raises ArgumentError for invalid string default values" do
399+
expect { client.fetch_string_value(flag_key:, default_value: true) }
400+
.to raise_error(ArgumentError, /Default value for string must be String, got TrueClass/)
401+
expect { client.fetch_string_value(flag_key:, default_value: 42) }
402+
.to raise_error(ArgumentError, /Default value for string must be String, got Integer/)
403+
end
404+
405+
it "raises ArgumentError for invalid number default values" do
406+
expect { client.fetch_number_value(flag_key:, default_value: "42") }
407+
.to raise_error(ArgumentError, /Default value for number must be Numeric, got String/)
408+
expect { client.fetch_number_value(flag_key:, default_value: true) }
409+
.to raise_error(ArgumentError, /Default value for number must be Numeric, got TrueClass/)
410+
end
411+
412+
it "raises ArgumentError for invalid integer default values" do
413+
expect { client.fetch_integer_value(flag_key:, default_value: 3.14) }
414+
.to raise_error(ArgumentError, /Default value for integer must be Integer, got Float/)
415+
expect { client.fetch_integer_value(flag_key:, default_value: "42") }
416+
.to raise_error(ArgumentError, /Default value for integer must be Integer, got String/)
417+
end
418+
419+
it "raises ArgumentError for invalid float default values" do
420+
expect { client.fetch_float_value(flag_key:, default_value: 42) }
421+
.to raise_error(ArgumentError, /Default value for float must be Float, got Integer/)
422+
expect { client.fetch_float_value(flag_key:, default_value: "3.14") }
423+
.to raise_error(ArgumentError, /Default value for float must be Float, got String/)
424+
end
425+
426+
it "raises ArgumentError for invalid object default values" do
427+
expect { client.fetch_object_value(flag_key:, default_value: "not object") }
428+
.to raise_error(ArgumentError, /Default value for object must be Array or Hash, got String/)
429+
expect { client.fetch_object_value(flag_key:, default_value: 42) }
430+
.to raise_error(ArgumentError, /Default value for object must be Array or Hash, got Integer/)
431+
end
432+
end
433+
end
356434
end
357435
end

0 commit comments

Comments
 (0)