diff --git a/lib/open_feature/sdk/client_metadata.rb b/lib/open_feature/sdk/client_metadata.rb index 1e2eb772..7e464f8a 100644 --- a/lib/open_feature/sdk/client_metadata.rb +++ b/lib/open_feature/sdk/client_metadata.rb @@ -4,6 +4,11 @@ module OpenFeature module SDK ClientMetadata = Struct.new(:domain, keyword_init: true) do alias_method :name, :domain + + def initialize(domain: nil) + super(domain: domain&.dup&.freeze) + freeze + end end end end diff --git a/lib/open_feature/sdk/provider/no_op_provider.rb b/lib/open_feature/sdk/provider/no_op_provider.rb index ab18959b..5cb851c4 100644 --- a/lib/open_feature/sdk/provider/no_op_provider.rb +++ b/lib/open_feature/sdk/provider/no_op_provider.rb @@ -23,7 +23,7 @@ module Provider # * fetch_object_value - Retrieve feature flag object value # class NoOpProvider - REASON_NO_OP = "No-op" + REASON_NO_OP = Reason::DEFAULT NAME = "No-op Provider" attr_reader :metadata diff --git a/lib/open_feature/sdk/provider_initialization_error.rb b/lib/open_feature/sdk/provider_initialization_error.rb index 88b210be..62a1efe1 100644 --- a/lib/open_feature/sdk/provider_initialization_error.rb +++ b/lib/open_feature/sdk/provider_initialization_error.rb @@ -21,8 +21,8 @@ class ProviderInitializationError < StandardError # @param message [String] the error message # @param provider [Object] the provider that failed to initialize # @param original_error [Exception] the original error that caused the failure - # @param error_code [String] the OpenFeature error code (defaults to PROVIDER_FATAL) - def initialize(message, provider: nil, original_error: nil, error_code: Provider::ErrorCode::PROVIDER_FATAL) + # @param error_code [String] the OpenFeature error code (defaults to GENERAL) + def initialize(message, provider: nil, original_error: nil, error_code: Provider::ErrorCode::GENERAL) super(message) @provider = provider @original_error = original_error diff --git a/lib/open_feature/sdk/provider_state_registry.rb b/lib/open_feature/sdk/provider_state_registry.rb index c94fa68e..486ee1b6 100644 --- a/lib/open_feature/sdk/provider_state_registry.rb +++ b/lib/open_feature/sdk/provider_state_registry.rb @@ -9,7 +9,7 @@ module SDK # Tracks provider states class ProviderStateRegistry def initialize - @states = {} + @states = {}.compare_by_identity @mutex = Mutex.new end @@ -17,7 +17,7 @@ def set_initial_state(provider, state = ProviderState::NOT_READY) return unless provider @mutex.synchronize do - @states[provider.object_id] = {state: state, details: {}} + @states[provider] = {state: state, details: {}} end end @@ -29,7 +29,7 @@ def update_state_from_event(provider, event_type, event_details = nil) # Only update state if the event should cause a state change if new_state @mutex.synchronize do - @states[provider.object_id] = {state: new_state, details: event_details || {}} + @states[provider] = {state: new_state, details: event_details || {}} end new_state else @@ -42,7 +42,7 @@ def get_state(provider) return ProviderState::NOT_READY unless provider @mutex.synchronize do - entry = @states[provider.object_id] + entry = @states[provider] entry ? entry[:state] : ProviderState::NOT_READY end end @@ -51,7 +51,7 @@ def get_details(provider) return {} unless provider @mutex.synchronize do - entry = @states[provider.object_id] + entry = @states[provider] entry ? entry[:details] : {} end end @@ -60,7 +60,7 @@ def remove_provider(provider) return unless provider @mutex.synchronize do - @states.delete(provider.object_id) + @states.delete(provider) end end @@ -68,7 +68,7 @@ def tracked?(provider) return false unless provider @mutex.synchronize do - @states.key?(provider.object_id) + @states.key?(provider) end end diff --git a/spec/open_feature/sdk/client_spec.rb b/spec/open_feature/sdk/client_spec.rb index a8e5da64..d8435438 100644 --- a/spec/open_feature/sdk/client_spec.rb +++ b/spec/open_feature/sdk/client_spec.rb @@ -28,6 +28,11 @@ expect(client).to respond_to(:metadata) expect(client.metadata.domain).to eq(domain) end + + it "client metadata is frozen (req 4.2.2.1 — hook context client_metadata must be immutable)" do + expect(client.metadata).to be_frozen + expect(client.metadata.domain).to be_frozen + end end context "Flag evaluation" do diff --git a/spec/open_feature/sdk/hooks/logging_hook_spec.rb b/spec/open_feature/sdk/hooks/logging_hook_spec.rb index e0aa60b2..9297e5f5 100644 --- a/spec/open_feature/sdk/hooks/logging_hook_spec.rb +++ b/spec/open_feature/sdk/hooks/logging_hook_spec.rb @@ -148,12 +148,15 @@ end it "uses error_code from exception when available" do - error_with_code = OpenFeature::SDK::ProviderInitializationError.new("init failed") + error_with_code = OpenFeature::SDK::ProviderInitializationError.new( + "init failed", + error_code: "CUSTOM_ERROR_CODE" + ) hook = described_class.new(logger: logger) expect(logger).to receive(:error) do |&block| message = block.call - expect(message).to include("error_code=PROVIDER_FATAL") + expect(message).to include("error_code=CUSTOM_ERROR_CODE") end hook.error(hook_context: hook_context, exception: error_with_code, hints: hints) diff --git a/spec/open_feature/sdk/provider_initialization_error_spec.rb b/spec/open_feature/sdk/provider_initialization_error_spec.rb index 5a3b5347..a17fa091 100644 --- a/spec/open_feature/sdk/provider_initialization_error_spec.rb +++ b/spec/open_feature/sdk/provider_initialization_error_spec.rb @@ -29,8 +29,8 @@ expect(error).to be_a(StandardError) end - it "sets the error code to PROVIDER_FATAL by default" do - expect(error.error_code).to eq(OpenFeature::SDK::Provider::ErrorCode::PROVIDER_FATAL) + it "sets the error code to GENERAL by default" do + expect(error.error_code).to eq(OpenFeature::SDK::Provider::ErrorCode::GENERAL) end end @@ -49,8 +49,8 @@ expect(error.original_error).to be_nil end - it "sets the error code to PROVIDER_FATAL by default" do - expect(error.error_code).to eq(OpenFeature::SDK::Provider::ErrorCode::PROVIDER_FATAL) + it "sets the error code to GENERAL by default" do + expect(error.error_code).to eq(OpenFeature::SDK::Provider::ErrorCode::GENERAL) end end diff --git a/spec/open_feature/sdk/provider_state_registry_spec.rb b/spec/open_feature/sdk/provider_state_registry_spec.rb index 8ab06d6f..c87e3d1f 100644 --- a/spec/open_feature/sdk/provider_state_registry_spec.rb +++ b/spec/open_feature/sdk/provider_state_registry_spec.rb @@ -7,8 +7,8 @@ RSpec.describe OpenFeature::SDK::ProviderStateRegistry do let(:registry) { described_class.new } - let(:provider) { double("Provider", object_id: 12_345) } - let(:provider2) { double("Provider2", object_id: 67_890) } + let(:provider) { double("Provider") } + let(:provider2) { double("Provider2") } describe "#set_initial_state" do it "sets NOT_READY as default state" do