diff --git a/lib/splitclient-rb/clients/split_client.rb b/lib/splitclient-rb/clients/split_client.rb index 31a40742..b87c5899 100644 --- a/lib/splitclient-rb/clients/split_client.rb +++ b/lib/splitclient-rb/clients/split_client.rb @@ -18,7 +18,7 @@ class SplitClient # @param sdk_key [String] the SDK key for your split account # # @return [SplitIoClient] split.io client instance - def initialize(sdk_key, repositories, status_manager, config, impressions_manager, telemetry_evaluation_producer, evaluator, split_validator) + def initialize(sdk_key, repositories, status_manager, config, impressions_manager, telemetry_evaluation_producer, evaluator, split_validator, fallback_treatment_calculator) @api_key = sdk_key @splits_repository = repositories[:splits] @segments_repository = repositories[:segments] @@ -32,6 +32,7 @@ def initialize(sdk_key, repositories, status_manager, config, impressions_manage @telemetry_evaluation_producer = telemetry_evaluation_producer @split_validator = split_validator @evaluator = evaluator + @fallback_treatment_calculator = fallback_treatment_calculator end def get_treatment( @@ -277,7 +278,7 @@ def treatments(key, feature_flag_names, attributes = {}, evaluation_options = ni if !@config.split_validator.valid_get_treatments_parameters(calling_method, key, sanitized_feature_flag_names, matching_key, bucketing_key, attributes) to_return = Hash.new sanitized_feature_flag_names.each {|name| - to_return[name.to_sym] = control_treatment_with_config + to_return[name.to_sym] = check_fallback_treatment(name, '') } return to_return end @@ -286,9 +287,11 @@ def treatments(key, feature_flag_names, attributes = {}, evaluation_options = ni impressions = [] to_return = Hash.new sanitized_feature_flag_names.each {|name| - to_return[name.to_sym] = control_treatment_with_config + treatment_data = check_fallback_treatment(name, Engine::Models::Label::NOT_READY) + to_return[name.to_sym] = treatment_data + impressions << { :impression => @impressions_manager.build_impression(matching_key, bucketing_key, name.to_sym, - control_treatment_with_config.merge({ :label => Engine::Models::Label::NOT_READY }), false, { attributes: attributes, time: nil }, + get_treatment_without_config(treatment_data), false, { attributes: attributes, time: nil }, evaluation_options), :disabled => false } } @impressions_manager.track(impressions) @@ -308,7 +311,7 @@ def treatments(key, feature_flag_names, attributes = {}, evaluation_options = ni if feature_flag.nil? @config.logger.warn("#{calling_method}: you passed #{key} that " \ 'does not exist in this environment, please double check what feature flags exist in the Split user interface') - invalid_treatments[key] = control_treatment_with_config + invalid_treatments[key] = check_fallback_treatment(key, Engine::Models::Label::NOT_FOUND) next end treatments_labels_change_numbers, impressions = evaluate_treatment(feature_flag, key, bucketing_key, matching_key, attributes, calling_method, false, evaluation_options) @@ -344,7 +347,7 @@ def treatment(key, feature_flag_name, attributes = {}, split_data = nil, store_i attributes = parsed_attributes(attributes) - return parsed_treatment(control_treatment, multiple) unless valid_client && @config.split_validator.valid_get_treatment_parameters(calling_method, key, feature_flag_name, matching_key, bucketing_key, attributes) + return parsed_treatment(check_fallback_treatment(feature_flag_name, ""), multiple) unless valid_client && @config.split_validator.valid_get_treatment_parameters(calling_method, key, feature_flag_name, matching_key, bucketing_key, attributes) bucketing_key = bucketing_key ? bucketing_key.to_s : nil matching_key = matching_key.to_s @@ -373,7 +376,7 @@ def evaluate_treatment(feature_flag, feature_flag_name, bucketing_key, matching_ if feature_flag.nil? && ready? @config.logger.warn("#{calling_method}: you passed #{feature_flag_name} that " \ 'does not exist in this environment, please double check what feature flags exist in the Split user interface') - return parsed_treatment(control_treatment.merge({ :label => Engine::Models::Label::NOT_FOUND }), multiple), nil + return check_fallback_treatment(feature_flag_name, Engine::Models::Label::NOT_FOUND), nil end if !feature_flag.nil? && ready? @@ -383,7 +386,7 @@ def evaluate_treatment(feature_flag, feature_flag_name, bucketing_key, matching_ impressions_disabled = feature_flag[:impressionsDisabled] else @config.logger.error("#{calling_method}: the SDK is not ready, results may be incorrect for feature flag #{feature_flag_name}. Make sure to wait for SDK readiness before using this method.") - treatment_data = control_treatment.merge({ :label => Engine::Models::Label::NOT_READY }) + treatment_data = check_fallback_treatment(feature_flag_name, Engine::Models::Label::NOT_READY) impressions_disabled = false end @@ -396,22 +399,16 @@ def evaluate_treatment(feature_flag, feature_flag_name, bucketing_key, matching_ rescue StandardError => e @config.log_found_exception(__method__.to_s, e) record_exception(calling_method) - impression_decorator = { :impression => @impressions_manager.build_impression(matching_key, bucketing_key, feature_flag_name, control_treatment, false, { attributes: attributes, time: nil }, evaluation_options), :disabled => false } + treatment_data = check_fallback_treatment(feature_flag_name, Engine::Models::Label::EXCEPTION) + impression_decorator = { :impression => @impressions_manager.build_impression(matching_key, bucketing_key, feature_flag_name, get_treatment_without_config(treatment_data), false, { attributes: attributes, time: nil }, evaluation_options), :disabled => false } + impressions_decorator << impression_decorator unless impression_decorator.nil? - return parsed_treatment(control_treatment.merge({ :label => Engine::Models::Label::EXCEPTION }), multiple), impressions_decorator + return parsed_treatment(treatment_data, multiple), impressions_decorator end return parsed_treatment(treatment_data, multiple), impressions_decorator end - def control_treatment - { :treatment => Engine::Models::Treatment::CONTROL } - end - - def control_treatment_with_config - {:treatment => Engine::Models::Treatment::CONTROL, :config => nil} - end - def variable_size(value) value.is_a?(String) ? value.length : 0 end @@ -472,5 +469,31 @@ def record_exception(method) @telemetry_evaluation_producer.record_exception(Telemetry::Domain::Constants::TRACK) end end + + def check_fallback_treatment(feature_name, label) + fallback_treatment = @fallback_treatment_calculator.resolve(feature_name.to_sym, label) + + { + label: fallback_treatment.label, + treatment: fallback_treatment.treatment, + config: get_fallback_config(fallback_treatment) + } + end + + def get_treatment_without_config(treatment) + { + label: treatment[:label], + treatment: treatment[:treatment], + } + end + + def get_fallback_config(fallback_treatment) + if fallback_treatment.config != nil + return fallback_treatment.config + end + + return nil + end + end end diff --git a/lib/splitclient-rb/engine/models/fallback_treatments_configuration.rb b/lib/splitclient-rb/engine/models/fallback_treatments_configuration.rb index c4514fca..dbeb68ce 100644 --- a/lib/splitclient-rb/engine/models/fallback_treatments_configuration.rb +++ b/lib/splitclient-rb/engine/models/fallback_treatments_configuration.rb @@ -18,7 +18,7 @@ def build_global_fallback_treatment(global_fallback_treatment) end def build_by_flag_fallback_treatment(by_flag_fallback_treatment) - return nil unless by_flag_fallback_treatment.is_a? Hash + return nil unless by_flag_fallback_treatment.is_a?(Hash) processed_by_flag_fallback_treatment = Hash.new by_flag_fallback_treatment.each do |key, value| diff --git a/lib/splitclient-rb/split_factory.rb b/lib/splitclient-rb/split_factory.rb index 7c8a4306..5f3d0aac 100644 --- a/lib/splitclient-rb/split_factory.rb +++ b/lib/splitclient-rb/split_factory.rb @@ -58,8 +58,9 @@ def initialize(api_key, config_hash = {}) @evaluator = Engine::Parser::Evaluator.new(@segments_repository, @splits_repository, @rule_based_segment_repository, @config) start! - - @client = SplitClient.new(@api_key, repositories, @status_manager, @config, @impressions_manager, @evaluation_producer, @evaluator, @split_validator) + + fallback_treatment_calculator = SplitIoClient::Engine::FallbackTreatmentCalculator.new(SplitIoClient::Engine::Models::FallbackTreatmentsConfiguration.new) + @client = SplitClient.new(@api_key, repositories, @status_manager, @config, @impressions_manager, @evaluation_producer, @evaluator, @split_validator, fallback_treatment_calculator) @manager = SplitManager.new(@splits_repository, @status_manager, @config) end diff --git a/spec/splitclient/split_client_spec.rb b/spec/splitclient/split_client_spec.rb index 6c49fffd..c0596f23 100644 --- a/spec/splitclient/split_client_spec.rb +++ b/spec/splitclient/split_client_spec.rb @@ -16,7 +16,8 @@ let(:impression_manager) { SplitIoClient::Engine::Common::ImpressionManager.new(config, impressions_repository, SplitIoClient::Engine::Common::NoopImpressionCounter.new, runtime_producer, SplitIoClient::Observers::NoopImpressionObserver.new, SplitIoClient::Engine::Impressions::NoopUniqueKeysTracker.new) } let(:evaluation_producer) { SplitIoClient::Telemetry::EvaluationProducer.new(config) } let(:evaluator) { SplitIoClient::Engine::Parser::Evaluator.new(segments_repository, splits_repository, rule_based_segments_repository, config) } - let(:split_client) { SplitIoClient::SplitClient.new('sdk_key', {:splits => splits_repository, :segments => segments_repository, :impressions => impressions_repository, :events => events_repository, :rule_based_segments => rule_based_segments_repository}, nil, config, impression_manager, evaluation_producer, evaluator, SplitIoClient::Validators.new(config)) } + let(:fallback_treatment_calculator) { SplitIoClient::Engine::FallbackTreatmentCalculator.new(SplitIoClient::Engine::Models::FallbackTreatmentsConfiguration.new) } + let(:split_client) { SplitIoClient::SplitClient.new('sdk_key', {:splits => splits_repository, :segments => segments_repository, :impressions => impressions_repository, :events => events_repository, :rule_based_segments => rule_based_segments_repository}, nil, config, impression_manager, evaluation_producer, evaluator, SplitIoClient::Validators.new(config), fallback_treatment_calculator) } let(:splits) do File.read(File.join(SplitIoClient.root, 'spec/test_data/integrations/splits.json')) @@ -128,7 +129,8 @@ impression_manager = SplitIoClient::Engine::Common::ImpressionManager.new(config, impressions_repository, impressions_counter, runtime_producer, SplitIoClient::Observers::ImpressionObserver.new, unique_keys_tracker) evaluation_producer = SplitIoClient::Telemetry::EvaluationProducer.new(config) evaluator = SplitIoClient::Engine::Parser::Evaluator.new(segments_repository, splits_repository, rule_based_segments_repository, config) - split_client = SplitIoClient::SplitClient.new('sdk_key', {:splits => splits_repository, :segments => segments_repository, :impressions => impressions_repository, :events => events_repository}, nil, config, impression_manager, evaluation_producer, evaluator, SplitIoClient::Validators.new(config)) + fallback_treatment_calculator = SplitIoClient::Engine::FallbackTreatmentCalculator.new(SplitIoClient::Engine::Models::FallbackTreatmentsConfiguration.new) + split_client = SplitIoClient::SplitClient.new('sdk_key', {:splits => splits_repository, :segments => segments_repository, :impressions => impressions_repository, :events => events_repository}, nil, config, impression_manager, evaluation_producer, evaluator, SplitIoClient::Validators.new(config), fallback_treatment_calculator) splits = File.read(File.join(SplitIoClient.root, 'spec/test_data/splits/imp-toggle.json')) splits_repository.update([JSON.parse(splits,:symbolize_names => true)[:ff][:d][0]], [], -1) @@ -168,7 +170,8 @@ impression_manager = SplitIoClient::Engine::Common::ImpressionManager.new(config, impressions_repository, impressions_counter, runtime_producer, SplitIoClient::Observers::ImpressionObserver.new, unique_keys_tracker) evaluation_producer = SplitIoClient::Telemetry::EvaluationProducer.new(config) evaluator = SplitIoClient::Engine::Parser::Evaluator.new(segments_repository, splits_repository, rule_based_segments_repository, config) - split_client = SplitIoClient::SplitClient.new('sdk_key', {:splits => splits_repository, :segments => segments_repository, :impressions => impressions_repository, :events => events_repository}, nil, config, impression_manager, evaluation_producer, evaluator, SplitIoClient::Validators.new(config)) + fallback_treatment_calculator = SplitIoClient::Engine::FallbackTreatmentCalculator.new(SplitIoClient::Engine::Models::FallbackTreatmentsConfiguration.new) + split_client = SplitIoClient::SplitClient.new('sdk_key', {:splits => splits_repository, :segments => segments_repository, :impressions => impressions_repository, :events => events_repository}, nil, config, impression_manager, evaluation_producer, evaluator, SplitIoClient::Validators.new(config), fallback_treatment_calculator) splits = File.read(File.join(SplitIoClient.root, 'spec/test_data/splits/imp-toggle.json')) splits_repository.update([JSON.parse(splits,:symbolize_names => true)[:ff][:d][0]], [], -1) @@ -208,7 +211,8 @@ impression_manager = SplitIoClient::Engine::Common::ImpressionManager.new(config, impressions_repository, impressions_counter, runtime_producer, SplitIoClient::Observers::ImpressionObserver.new, unique_keys_tracker) evaluation_producer = SplitIoClient::Telemetry::EvaluationProducer.new(config) evaluator = SplitIoClient::Engine::Parser::Evaluator.new(segments_repository, splits_repository, rule_based_segments_repository, config) - split_client = SplitIoClient::SplitClient.new('sdk_key', {:splits => splits_repository, :segments => segments_repository, :impressions => impressions_repository, :events => events_repository}, nil, config, impression_manager, evaluation_producer, evaluator, SplitIoClient::Validators.new(config)) + fallback_treatment_calculator = SplitIoClient::Engine::FallbackTreatmentCalculator.new(SplitIoClient::Engine::Models::FallbackTreatmentsConfiguration.new) + split_client = SplitIoClient::SplitClient.new('sdk_key', {:splits => splits_repository, :segments => segments_repository, :impressions => impressions_repository, :events => events_repository}, nil, config, impression_manager, evaluation_producer, evaluator, SplitIoClient::Validators.new(config), fallback_treatment_calculator) splits = File.read(File.join(SplitIoClient.root, 'spec/test_data/splits/imp-toggle.json')) splits_repository.update([JSON.parse(splits,:symbolize_names => true)[:ff][:d][0]], [], -1) @@ -235,6 +239,166 @@ end end +context 'fallback treatments' do + it 'feature not found ' do + config = SplitIoClient::SplitConfig.new(cache_adapter: :memory, impressions_mode: :debug) + segments_repository = SplitIoClient::Cache::Repositories::SegmentsRepository.new(config) + flag_sets_repository = SplitIoClient::Cache::Repositories::MemoryFlagSetsRepository.new([]) + flag_set_filter = SplitIoClient::Cache::Filter::FlagSetsFilter.new([]) + splits_repository = SplitIoClient::Cache::Repositories::SplitsRepository.new(config, flag_sets_repository, flag_set_filter) + impressions_repository = SplitIoClient::Cache::Repositories::ImpressionsRepository.new(config) + rule_based_segments_repository = SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config) + runtime_producer = SplitIoClient::Telemetry::RuntimeProducer.new(config) + events_repository = SplitIoClient::Cache::Repositories::EventsRepository.new(config, 'sdk_key', runtime_producer) + impressions_counter = SplitIoClient::Engine::Common::ImpressionCounter.new + filter_adapter = SplitIoClient::Cache::Filter::FilterAdapter.new(config, SplitIoClient::Cache::Filter::BloomFilter.new(1_000)) + unique_keys_tracker = SplitIoClient::Engine::Impressions::UniqueKeysTracker.new(config, filter_adapter, nil, Concurrent::Hash.new) + impression_manager = SplitIoClient::Engine::Common::ImpressionManager.new(config, impressions_repository, impressions_counter, runtime_producer, SplitIoClient::Observers::ImpressionObserver.new, unique_keys_tracker) + evaluation_producer = SplitIoClient::Telemetry::EvaluationProducer.new(config) + evaluator = SplitIoClient::Engine::Parser::Evaluator.new(segments_repository, splits_repository, rule_based_segments_repository, config) + fallback_treatment_calculator = SplitIoClient::Engine::FallbackTreatmentCalculator.new(SplitIoClient::Engine::Models::FallbackTreatmentsConfiguration.new(SplitIoClient::Engine::Models::FallbackTreatment.new("on-global", '{"prop": "global"}'), {:feature => SplitIoClient::Engine::Models::FallbackTreatment.new("on-local", '{"prop": "local"}')})) + split_client = SplitIoClient::SplitClient.new('sdk_key', {:splits => splits_repository, :segments => segments_repository, :impressions => impressions_repository, :events => events_repository}, nil, config, impression_manager, evaluation_producer, evaluator, SplitIoClient::Validators.new(config), fallback_treatment_calculator) + + treatment = split_client.get_treatment_with_config('key2', 'feature') + expect(treatment[:treatment]).to eq('on-local') + expect(treatment[:config]).to eq('{"prop": "local"}') + treatment = split_client.get_treatment_with_config('key3', 'feature2') + expect(treatment[:treatment]).to eq('on-global') + expect(treatment[:config]).to eq('{"prop": "global"}') + imps = impressions_repository.batch + expect(imps.length()).to eq(0) + + treatment = split_client.get_treatment('key2', 'feature') + expect(treatment).to eq('on-local') + treatment = split_client.get_treatment('key3', 'feature2') + expect(treatment).to eq('on-global') + + treatment = split_client.get_treatments('key2', ['feature', 'feature2']) + expect(treatment[:feature]).to eq('on-local') + expect(treatment[:feature2]).to eq('on-global') + + treatment = split_client.get_treatments_with_config('key2', ['feature', 'feature2']) + expect(treatment[:feature][:treatment]).to eq('on-local') + expect(treatment[:feature][:config]).to eq('{"prop": "local"}') + expect(treatment[:feature2][:treatment]).to eq('on-global') + expect(treatment[:feature2][:config]).to eq('{"prop": "global"}') + end + + it 'exception' do + config = SplitIoClient::SplitConfig.new(cache_adapter: :memory, impressions_mode: :debug) + segments_repository = SplitIoClient::Cache::Repositories::SegmentsRepository.new(config) + flag_sets_repository = SplitIoClient::Cache::Repositories::MemoryFlagSetsRepository.new([]) + flag_set_filter = SplitIoClient::Cache::Filter::FlagSetsFilter.new([]) + splits_repository = SplitIoClient::Cache::Repositories::SplitsRepository.new(config, flag_sets_repository, flag_set_filter) + impressions_repository = SplitIoClient::Cache::Repositories::ImpressionsRepository.new(config) + rule_based_segments_repository = SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config) + runtime_producer = SplitIoClient::Telemetry::RuntimeProducer.new(config) + events_repository = SplitIoClient::Cache::Repositories::EventsRepository.new(config, 'sdk_key', runtime_producer) + impressions_counter = SplitIoClient::Engine::Common::ImpressionCounter.new + filter_adapter = SplitIoClient::Cache::Filter::FilterAdapter.new(config, SplitIoClient::Cache::Filter::BloomFilter.new(1_000)) + unique_keys_tracker = SplitIoClient::Engine::Impressions::UniqueKeysTracker.new(config, filter_adapter, nil, Concurrent::Hash.new) + impression_manager = SplitIoClient::Engine::Common::ImpressionManager.new(config, impressions_repository, impressions_counter, runtime_producer, SplitIoClient::Observers::ImpressionObserver.new, unique_keys_tracker) + evaluation_producer = SplitIoClient::Telemetry::EvaluationProducer.new(config) + evaluator = SplitIoClient::Engine::Parser::Evaluator.new(segments_repository, splits_repository, rule_based_segments_repository, config) + fallback_treatment_calculator = SplitIoClient::Engine::FallbackTreatmentCalculator.new(SplitIoClient::Engine::Models::FallbackTreatmentsConfiguration.new(SplitIoClient::Engine::Models::FallbackTreatment.new("on-global", '{"prop": "global"}'), {:feature => SplitIoClient::Engine::Models::FallbackTreatment.new("on-local", '{"prop": "local"}')})) + split_client = SplitIoClient::SplitClient.new('sdk_key', {:splits => splits_repository, :segments => segments_repository, :impressions => impressions_repository, :events => events_repository}, nil, config, impression_manager, evaluation_producer, evaluator, SplitIoClient::Validators.new(config), fallback_treatment_calculator) + + splits = File.read(File.join(SplitIoClient.root, 'spec/test_data/splits/imp-toggle.json')) + split = JSON.parse(splits,:symbolize_names => true)[:ff][:d][0] + split[:trafficAllocation] = nil + splits_repository.update([split], [], -1) + + treatment = split_client.get_treatment_with_config('key2', 'with_track_disabled') + expect(treatment[:treatment]).to eq('on-global') + expect(treatment[:config]).to eq('{"prop": "global"}') + imps = impressions_repository.batch + expect(imps.length()).to eq(1) + expect(imps[0][:i][:f]).to eq('with_track_disabled') + expect(imps[0][:i][:r]).to eq('fallback - exception') + + treatment = split_client.get_treatment('key3', 'with_track_disabled') + expect(treatment).to eq('on-global') + + treatment = split_client.get_treatments('key2', ['with_track_disabled']) + expect(treatment[:with_track_disabled]).to eq('on-global') + + treatment = split_client.get_treatments_with_config('key2', ['with_track_disabled']) + expect(treatment[:with_track_disabled][:treatment]).to eq('on-global') + expect(treatment[:with_track_disabled][:config]).to eq('{"prop": "global"}') + + treatment = split_client.get_treatments_by_flag_set('key2', 'set1') + expect(treatment[:with_track_disabled]).to eq('on-global') + + treatment = split_client.get_treatments_by_flag_sets('key2', ['set1']) + expect(treatment[:with_track_disabled]).to eq('on-global') + + treatment = split_client.get_treatments_with_config_by_flag_set('key2', 'set1') + expect(treatment[:with_track_disabled][:treatment]).to eq('on-global') + expect(treatment[:with_track_disabled][:config]).to eq('{"prop": "global"}') + + treatment = split_client.get_treatments_with_config_by_flag_sets('key2', ['set1']) + expect(treatment[:with_track_disabled][:treatment]).to eq('on-global') + expect(treatment[:with_track_disabled][:config]).to eq('{"prop": "global"}') + end + + it 'client not ready' do + config = SplitIoClient::SplitConfig.new(cache_adapter: :memory, impressions_mode: :debug) + segments_repository = SplitIoClient::Cache::Repositories::SegmentsRepository.new(config) + flag_sets_repository = SplitIoClient::Cache::Repositories::MemoryFlagSetsRepository.new([]) + flag_set_filter = SplitIoClient::Cache::Filter::FlagSetsFilter.new([]) + splits_repository = SplitIoClient::Cache::Repositories::SplitsRepository.new(config, flag_sets_repository, flag_set_filter) + impressions_repository = SplitIoClient::Cache::Repositories::ImpressionsRepository.new(config) + rule_based_segments_repository = SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config) + runtime_producer = SplitIoClient::Telemetry::RuntimeProducer.new(config) + events_repository = SplitIoClient::Cache::Repositories::EventsRepository.new(config, 'sdk_key', runtime_producer) + impressions_counter = SplitIoClient::Engine::Common::ImpressionCounter.new + filter_adapter = SplitIoClient::Cache::Filter::FilterAdapter.new(config, SplitIoClient::Cache::Filter::BloomFilter.new(1_000)) + unique_keys_tracker = SplitIoClient::Engine::Impressions::UniqueKeysTracker.new(config, filter_adapter, nil, Concurrent::Hash.new) + impression_manager = SplitIoClient::Engine::Common::ImpressionManager.new(config, impressions_repository, impressions_counter, runtime_producer, SplitIoClient::Observers::ImpressionObserver.new, unique_keys_tracker) + evaluation_producer = SplitIoClient::Telemetry::EvaluationProducer.new(config) + evaluator = SplitIoClient::Engine::Parser::Evaluator.new(segments_repository, splits_repository, rule_based_segments_repository, config) + fallback_treatment_calculator = SplitIoClient::Engine::FallbackTreatmentCalculator.new(SplitIoClient::Engine::Models::FallbackTreatmentsConfiguration.new(SplitIoClient::Engine::Models::FallbackTreatment.new("on-global", '{"prop": "global"}'), {:feature => SplitIoClient::Engine::Models::FallbackTreatment.new("on-local", '{"prop": "local"}')})) + + class MyStatusManager + def ready? + false + end + def wait_until_ready(time) + true + end + end + split_client = SplitIoClient::SplitClient.new('sdk_key', {:splits => splits_repository, :segments => segments_repository, :impressions => impressions_repository, :events => events_repository}, MyStatusManager.new, config, impression_manager, evaluation_producer, evaluator, SplitIoClient::Validators.new(config), fallback_treatment_calculator) + + treatment = split_client.get_treatment_with_config('key2', 'feature') + expect(treatment[:treatment]).to eq('on-local') + expect(treatment[:config]).to eq('{"prop": "local"}') + treatment = split_client.get_treatment_with_config('key3', 'feature2') + expect(treatment[:treatment]).to eq('on-global') + expect(treatment[:config]).to eq('{"prop": "global"}') + imps = impressions_repository.batch + expect(imps.length()).to eq(2) + expect(imps[0][:i][:f]).to eq('feature') + expect(imps[0][:i][:r]).to eq('fallback - not ready') + expect(imps[1][:i][:f]).to eq('feature2') + expect(imps[1][:i][:r]).to eq('fallback - not ready') + + treatment = split_client.get_treatment('key2', 'feature') + expect(treatment).to eq('on-local') + treatment = split_client.get_treatment('key3', 'feature2') + expect(treatment).to eq('on-global') + + treatment = split_client.get_treatments('key2', ['feature', 'feature2']) + expect(treatment[:feature]).to eq('on-local') + expect(treatment[:feature2]).to eq('on-global') + + treatment = split_client.get_treatments_with_config('key2', ['feature', 'feature2']) + expect(treatment[:feature][:treatment]).to eq('on-local') + expect(treatment[:feature][:config]).to eq('{"prop": "local"}') + expect(treatment[:feature2][:treatment]).to eq('on-global') + expect(treatment[:feature2][:config]).to eq('{"prop": "global"}') + end +end + def mock_segment_changes(segment_name, segment_json, since) stub_request(:get, "https://sdk.split.io/api/segmentChanges/#{segment_name}?since=#{since}") .to_return(status: 200, body: segment_json) diff --git a/spec/splitclient/split_config_spec.rb b/spec/splitclient/split_config_spec.rb index 4e05daff..9dcf14bf 100644 --- a/spec/splitclient/split_config_spec.rb +++ b/spec/splitclient/split_config_spec.rb @@ -192,15 +192,15 @@ expect(configs.fallback_treatments_configuration.global_fallback_treatment).to eq nil expect(configs.fallback_treatments_configuration.by_flag_fallback_treatment).to eq nil - configs = SplitIoClient::SplitConfig.new(fallback_treatments: SplitIoClient::Engine::Models::FallbackTreatmentsConfiguration.new('on-global', {'feature' => 'on_45-c'})) + configs = SplitIoClient::SplitConfig.new(fallback_treatments: SplitIoClient::Engine::Models::FallbackTreatmentsConfiguration.new('on-global', {:feature => 'on_45-c'})) expect(configs.fallback_treatments_configuration.global_fallback_treatment.is_a?(SplitIoClient::Engine::Models::FallbackTreatment)).to eq true expect(configs.fallback_treatments_configuration.global_fallback_treatment.treatment).to eq 'on-global' expect(configs.fallback_treatments_configuration.global_fallback_treatment.config).to eq nil - expect(configs.fallback_treatments_configuration.by_flag_fallback_treatment['feature'].is_a?(SplitIoClient::Engine::Models::FallbackTreatment)).to eq true - expect(configs.fallback_treatments_configuration.by_flag_fallback_treatment['feature'].treatment).to eq 'on_45-c' - expect(configs.fallback_treatments_configuration.by_flag_fallback_treatment['feature'].config).to eq nil + expect(configs.fallback_treatments_configuration.by_flag_fallback_treatment[:feature].is_a?(SplitIoClient::Engine::Models::FallbackTreatment)).to eq true + expect(configs.fallback_treatments_configuration.by_flag_fallback_treatment[:feature].treatment).to eq 'on_45-c' + expect(configs.fallback_treatments_configuration.by_flag_fallback_treatment[:feature].config).to eq nil - configs = SplitIoClient::SplitConfig.new(fallback_treatments: SplitIoClient::Engine::Models::FallbackTreatmentsConfiguration.new('on-gl/obal', {'feature' => "0" * 300})) + configs = SplitIoClient::SplitConfig.new(fallback_treatments: SplitIoClient::Engine::Models::FallbackTreatmentsConfiguration.new('on-gl/obal', {:feature => "0" * 300})) expect(configs.fallback_treatments_configuration.global_fallback_treatment).to eq nil expect(configs.fallback_treatments_configuration.by_flag_fallback_treatment).to eq Hash.new diff --git a/spec/test_data/splits/imp-toggle.json b/spec/test_data/splits/imp-toggle.json index b70d6684..9deba4ae 100644 --- a/spec/test_data/splits/imp-toggle.json +++ b/spec/test_data/splits/imp-toggle.json @@ -48,7 +48,8 @@ ], "label": "default rule" } - ] + ], + "sets": ["set1"] }, { "trafficTypeName": "user",