diff --git a/README.md b/README.md index 6ce7d6d0..a524f9c3 100644 --- a/README.md +++ b/README.md @@ -82,17 +82,17 @@ bool_value = client.fetch_boolean_value(flag_key: 'boolean_flag', default_value: # a details method is also available for more information about the flag evaluation # see `ResolutionDetails` for more info -bool_details = client.fetch_boolean_details(flag_key: 'boolean_flag', default_value: false) == +bool_details = client.fetch_boolean_details(flag_key: 'boolean_flag', default_value: false) # fetching string value feature flag -string_value = client.fetch_string_value(flag_key: 'string_flag', default_value: false) +string_value = client.fetch_string_value(flag_key: 'string_flag', default_value: 'default') # fetching number value feature flag float_value = client.fetch_number_value(flag_key: 'number_value', default_value: 1.0) integer_value = client.fetch_number_value(flag_key: 'number_value', default_value: 1) # get an object value -object = client.fetch_object_value(flag_key: 'object_value', default_value: JSON.dump({ name: 'object'})) +object = client.fetch_object_value(flag_key: 'object_value', default_value: { name: 'object'}) ``` ## 🌟 Features diff --git a/lib/open_feature/sdk/client.rb b/lib/open_feature/sdk/client.rb index f5fc98c3..7dee6928 100644 --- a/lib/open_feature/sdk/client.rb +++ b/lib/open_feature/sdk/client.rb @@ -44,6 +44,8 @@ def fetch_#{result_type}_#{suffix}(flag_key:, default_value:, evaluation_context private def fetch_details(type:, flag_key:, default_value:, evaluation_context: nil) + validate_default_value_type(type, default_value) + built_context = EvaluationContextBuilder.new.call(api_context: OpenFeature::SDK.evaluation_context, client_context: self.evaluation_context, invocation_context: evaluation_context) 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) EvaluationDetails.new(flag_key:, resolution_details:) end + + def validate_default_value_type(type, default_value) + expected_classes = TYPE_CLASS_MAP[type] + unless expected_classes.any? { |klass| default_value.is_a?(klass) } + expected_types = expected_classes.map(&:name).join(" or ") + actual_type = default_value.class.name + raise ArgumentError, "Default value for #{type} must be #{expected_types}, got #{actual_type}" + end + end end end end diff --git a/spec/open_feature/sdk/client_spec.rb b/spec/open_feature/sdk/client_spec.rb index b8c404fc..74c4722d 100644 --- a/spec/open_feature/sdk/client_spec.rb +++ b/spec/open_feature/sdk/client_spec.rb @@ -353,5 +353,83 @@ pending end end + + context "Default Value Type Validation" do + let(:flag_key) { "test-flag" } + + context "Valid default value types" do + it "accepts boolean values for fetch_boolean_value" do + expect { client.fetch_boolean_value(flag_key:, default_value: true) }.not_to raise_error + expect { client.fetch_boolean_value(flag_key:, default_value: false) }.not_to raise_error + end + + it "accepts string values for fetch_string_value" do + expect { client.fetch_string_value(flag_key:, default_value: "test") }.not_to raise_error + expect { client.fetch_string_value(flag_key:, default_value: "") }.not_to raise_error + end + + it "accepts numeric values for fetch_number_value" do + expect { client.fetch_number_value(flag_key:, default_value: 42) }.not_to raise_error + expect { client.fetch_number_value(flag_key:, default_value: 3.14) }.not_to raise_error + end + + it "accepts integer values for fetch_integer_value" do + expect { client.fetch_integer_value(flag_key:, default_value: 42) }.not_to raise_error + end + + it "accepts float values for fetch_float_value" do + expect { client.fetch_float_value(flag_key:, default_value: 3.14) }.not_to raise_error + end + + it "accepts array and hash values for fetch_object_value" do + expect { client.fetch_object_value(flag_key:, default_value: {}) }.not_to raise_error + expect { client.fetch_object_value(flag_key:, default_value: []) }.not_to raise_error + end + end + + context "Invalid default value types" do + it "raises ArgumentError for invalid boolean default values" do + expect { client.fetch_boolean_value(flag_key:, default_value: "not boolean") } + .to raise_error(ArgumentError, /Default value for boolean must be TrueClass or FalseClass, got String/) + expect { client.fetch_boolean_value(flag_key:, default_value: 1) } + .to raise_error(ArgumentError, /Default value for boolean must be TrueClass or FalseClass, got Integer/) + end + + it "raises ArgumentError for invalid string default values" do + expect { client.fetch_string_value(flag_key:, default_value: true) } + .to raise_error(ArgumentError, /Default value for string must be String, got TrueClass/) + expect { client.fetch_string_value(flag_key:, default_value: 42) } + .to raise_error(ArgumentError, /Default value for string must be String, got Integer/) + end + + it "raises ArgumentError for invalid number default values" do + expect { client.fetch_number_value(flag_key:, default_value: "42") } + .to raise_error(ArgumentError, /Default value for number must be Numeric, got String/) + expect { client.fetch_number_value(flag_key:, default_value: true) } + .to raise_error(ArgumentError, /Default value for number must be Numeric, got TrueClass/) + end + + it "raises ArgumentError for invalid integer default values" do + expect { client.fetch_integer_value(flag_key:, default_value: 3.14) } + .to raise_error(ArgumentError, /Default value for integer must be Integer, got Float/) + expect { client.fetch_integer_value(flag_key:, default_value: "42") } + .to raise_error(ArgumentError, /Default value for integer must be Integer, got String/) + end + + it "raises ArgumentError for invalid float default values" do + expect { client.fetch_float_value(flag_key:, default_value: 42) } + .to raise_error(ArgumentError, /Default value for float must be Float, got Integer/) + expect { client.fetch_float_value(flag_key:, default_value: "3.14") } + .to raise_error(ArgumentError, /Default value for float must be Float, got String/) + end + + it "raises ArgumentError for invalid object default values" do + expect { client.fetch_object_value(flag_key:, default_value: "not object") } + .to raise_error(ArgumentError, /Default value for object must be Array or Hash, got String/) + expect { client.fetch_object_value(flag_key:, default_value: 42) } + .to raise_error(ArgumentError, /Default value for object must be Array or Hash, got Integer/) + end + end + end end end