From 2968a19da5a319f3b635809b04a0bc7c04d8389f Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Mon, 4 Mar 2024 19:46:42 -0800 Subject: [PATCH 01/37] added undefined matcher flow --- .../cache/repositories/splits_repository.rb | 43 +++++++++- .../repositories/splits_repository_spec.rb | 82 +++++++++++++++---- spec/repository_helper.rb | 16 ++-- spec/telemetry/synchronizer_spec.rb | 6 +- 4 files changed, 118 insertions(+), 29 deletions(-) diff --git a/lib/splitclient-rb/cache/repositories/splits_repository.rb b/lib/splitclient-rb/cache/repositories/splits_repository.rb index 3f17a0c7..787c9061 100644 --- a/lib/splitclient-rb/cache/repositories/splits_repository.rb +++ b/lib/splitclient-rb/cache/repositories/splits_repository.rb @@ -5,7 +5,32 @@ module Cache module Repositories class SplitsRepository < Repository attr_reader :adapter - + DEFAULT_CONDITIONS_TEMPLATE = { + conditionType: "ROLLOUT", + matcherGroup: { + combiner: "AND", + matchers: [ + { + keySelector: nil, + matcherType: "ALL_KEYS", + negate: false, + userDefinedSegmentMatcherData: nil, + whitelistMatcherData: nil, + unaryNumericMatcherData: nil, + betweenMatcherData: nil, + dependencyMatcherData: nil, + booleanMatcherData: nil, + stringMatcherData: nil + }] + }, + partitions: [ + { + treatment: "control", + size: 100 + } + ], + label: "unsupported matcher type" + } def initialize(config, flag_sets_repository, flag_set_filter) super(config) @tt_cache = {} @@ -155,6 +180,10 @@ def add_feature_flag(split) remove_from_flag_sets(existing_split) end + if check_undefined_matcher(split) + @config.logger.warn("Feature Flag #{split[:name]} has undefined matcher, setting conditions to default template.") + split['conditions'] = [SplitsRepository::DEFAULT_CONDITIONS_TEMPLATE] + end if !split[:sets].nil? for flag_set in split[:sets] if !@flag_sets.flag_set_exist?(flag_set) @@ -170,6 +199,18 @@ def add_feature_flag(split) @adapter.set_string(namespace_key(".split.#{split[:name]}"), split.to_json) end + def check_undefined_matcher(split) + for condition in split[:conditions] + for matcher in condition[:matcherGroup][:matchers] + if !SplitIoClient::Condition.instance_methods(false).map(&:to_s).include?("matcher_#{matcher[:matcherType].downcase}") + @config.logger.error("Detected undefined matcher #{matcher[:matcherType].downcase} in feature flag #{split[:name]}") + return true + end + end + end + return false + end + def remove_feature_flag(split) decrease_tt_name_count(split[:trafficTypeName]) remove_from_flag_sets(split) diff --git a/spec/cache/repositories/splits_repository_spec.rb b/spec/cache/repositories/splits_repository_spec.rb index 351ed30d..6d8ae3f7 100644 --- a/spec/cache/repositories/splits_repository_spec.rb +++ b/spec/cache/repositories/splits_repository_spec.rb @@ -17,9 +17,9 @@ before do # in memory setup - repository.update([{name: 'foo', trafficTypeName: 'tt_name_1'}, - {name: 'bar', trafficTypeName: 'tt_name_2'}, - {name: 'baz', trafficTypeName: 'tt_name_1'}], [], -1) + repository.update([{name: 'foo', trafficTypeName: 'tt_name_1', conditions: []}, + {name: 'bar', trafficTypeName: 'tt_name_2', conditions: []}, + {name: 'baz', trafficTypeName: 'tt_name_1', conditions: []}], [], -1) # redis setup repository.instance_variable_get(:@adapter).set_string( @@ -31,13 +31,13 @@ end after do - repository.update([], [{name: 'foo', trafficTypeName: 'tt_name_1'}, - {name: 'bar', trafficTypeName: 'tt_name_2'}, - {name: 'bar', trafficTypeName: 'tt_name_2'}, - {name: 'qux', trafficTypeName: 'tt_name_3'}, - {name: 'quux', trafficTypeName: 'tt_name_4'}, - {name: 'corge', trafficTypeName: 'tt_name_5'}, - {name: 'corge', trafficTypeName: 'tt_name_6'}], -1) + repository.update([], [{name: 'foo', trafficTypeName: 'tt_name_1', conditions: []}, + {name: 'bar', trafficTypeName: 'tt_name_2', conditions: []}, + {name: 'bar', trafficTypeName: 'tt_name_2', conditions: []}, + {name: 'qux', trafficTypeName: 'tt_name_3', conditions: []}, + {name: 'quux', trafficTypeName: 'tt_name_4', conditions: []}, + {name: 'corge', trafficTypeName: 'tt_name_5', conditions: []}, + {name: 'corge', trafficTypeName: 'tt_name_6', conditions: []}], -1) end it 'returns splits names' do @@ -52,7 +52,7 @@ expect(repository.traffic_type_exists('tt_name_1')).to be true expect(repository.traffic_type_exists('tt_name_2')).to be true - split = { name: 'qux', trafficTypeName: 'tt_name_3' } + split = { name: 'qux', trafficTypeName: 'tt_name_3', conditions: [] } repository.update([split], [], -1) repository.update([], [split], -1) @@ -61,7 +61,7 @@ end it 'does not increment traffic type count when adding same split twice' do - split = { name: 'quux', trafficTypeName: 'tt_name_4' } + split = { name: 'quux', trafficTypeName: 'tt_name_4', conditions: [] } repository.update([split, split], [], -1) repository.update([], [split], -1) @@ -70,7 +70,7 @@ end it 'updates traffic type count accordingly when split changes traffic type' do - split = { name: 'corge', trafficTypeName: 'tt_name_5' } + split = { name: 'corge', trafficTypeName: 'tt_name_5', conditions: [] } repository.update([split], [], -1) repository.instance_variable_get(:@adapter).set_string( @@ -79,7 +79,7 @@ expect(repository.traffic_type_exists('tt_name_5')).to be true - split = { name: 'corge', trafficTypeName: 'tt_name_6' } + split = { name: 'corge', trafficTypeName: 'tt_name_6', conditions: [] } repository.update([split], [], -1) @@ -97,11 +97,59 @@ it 'returns splits data' do expect(repository.splits).to eq( - 'foo' => { name: 'foo', trafficTypeName: 'tt_name_1' }, - 'bar' => { name: 'bar', trafficTypeName: 'tt_name_2' }, - 'baz' => { name: 'baz', trafficTypeName: 'tt_name_1' } + 'foo' => { name: 'foo', trafficTypeName: 'tt_name_1', conditions: [] }, + 'bar' => { name: 'bar', trafficTypeName: 'tt_name_2', conditions: [] }, + 'baz' => { name: 'baz', trafficTypeName: 'tt_name_1', conditions: [] } ) end + + it 'remove undefined matcher with template condition' do + split = { name: 'corge', trafficTypeName: 'tt_name_5', conditions: [ + { + partitions: [ + {treatment: 'on', size: 50}, + {treatment: 'off', size: 50} + ], + contitionType: 'WHITELIST', + label: 'some_label', + matcherGroup: { + matchers: [ + { + matcherType: 'UNDEFINED', + whitelistMatcherData: { + whitelist: ['k1', 'k2', 'k3'] + }, + negate: false, + } + ], + combiner: 'AND' + } + }] + } + repository.update([split], [], -1) + expect(repository.get_split('corge')[:conditions]).to eq [SplitIoClient::Cache::Repositories::SplitsRepository::DEFAULT_CONDITIONS_TEMPLATE] + + # test with multiple conditions + split[:conditions] .append({ + partitions: [ + {treatment: 'on', size: 25}, + {treatment: 'off', size: 75} + ], + contitionType: 'WHITELIST', + label: 'some_other_label', + matcherGroup: { + matchers: [ + { + matcherType: 'ALL_KEYS', + negate: false, + } + ], + combiner: 'AND' + } + }) + repository.update([split], [], -1) + expect(repository.get_split('corge')[:conditions]).to eq [SplitIoClient::Cache::Repositories::SplitsRepository::DEFAULT_CONDITIONS_TEMPLATE] + end end describe 'with Memory Adapter' do diff --git a/spec/repository_helper.rb b/spec/repository_helper.rb index 994c9ca9..f4a72ab6 100644 --- a/spec/repository_helper.rb +++ b/spec/repository_helper.rb @@ -13,16 +13,16 @@ flag_sets_repository, flag_set_filter) - SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split1', :status => 'ACTIVE', :sets => []}], -1, config) + SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split1', :status => 'ACTIVE', conditions: [], :sets => []}], -1, config) expect(feature_flag_repository.get_split('split1').nil?).to eq(true) - SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split1', :status => 'ACTIVE', :sets => ['set_3']}], -1, config) + SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split1', :status => 'ACTIVE', conditions: [], :sets => ['set_3']}], -1, config) expect(feature_flag_repository.get_split('split1').nil?).to eq(true) - SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split1', :status => 'ACTIVE', :sets => ['set_1']}], -1, config) + SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split1', :status => 'ACTIVE', conditions: [], :sets => ['set_1']}], -1, config) expect(feature_flag_repository.get_split('split1').nil?).to eq(false) - SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split1', :status => 'ARCHIVED', :sets => ['set_1']}], -1, config) + SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split1', :status => 'ARCHIVED', conditions: [], :sets => ['set_1']}], -1, config) expect(feature_flag_repository.get_split('split1').nil?).to eq(true) end @@ -35,16 +35,16 @@ flag_sets_repository, flag_set_filter) - SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split1', :status => 'ACTIVE', :sets => []}], -1, config) + SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split1', :status => 'ACTIVE', conditions: [], :sets => []}], -1, config) expect(feature_flag_repository.get_split('split1').nil?).to eq(false) - SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split2', :status => 'ACTIVE', :sets => ['set_3']}], -1, config) + SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split2', :status => 'ACTIVE', conditions: [], :sets => ['set_3']}], -1, config) expect(feature_flag_repository.get_split('split2').nil?).to eq(false) - SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split3', :status => 'ACTIVE', :sets => ['set_1']}], -1, config) + SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split3', :status => 'ACTIVE', conditions: [], :sets => ['set_1']}], -1, config) expect(feature_flag_repository.get_split('split1').nil?).to eq(false) - SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split1', :status => 'ARCHIVED', :sets => ['set_1']}], -1, config) + SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split1', :status => 'ARCHIVED', conditions: [], :sets => ['set_1']}], -1, config) expect(feature_flag_repository.get_split('split1').nil?).to eq(true) end end diff --git a/spec/telemetry/synchronizer_spec.rb b/spec/telemetry/synchronizer_spec.rb index 18e67594..5448027c 100644 --- a/spec/telemetry/synchronizer_spec.rb +++ b/spec/telemetry/synchronizer_spec.rb @@ -65,9 +65,9 @@ end it 'with data' do - splits_repository.update([{name: 'foo', trafficTypeName: 'tt_name_1'}, - {name: 'bar', trafficTypeName: 'tt_name_2'}, - {name: 'baz', trafficTypeName: 'tt_name_1'}], [], -1) + splits_repository.update([{name: 'foo', trafficTypeName: 'tt_name_1', conditions: []}, + {name: 'bar', trafficTypeName: 'tt_name_2', conditions: []}, + {name: 'baz', trafficTypeName: 'tt_name_1', conditions: []}], [], -1) segments_repository.add_to_segment(name: 'foo-1', added: [1, 2, 3], removed: []) segments_repository.add_to_segment(name: 'foo-2', added: [1, 2, 3, 4], removed: []) From 5cbe5dc80b8919ce57092cedb037e807352f24c6 Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Mon, 4 Mar 2024 19:50:21 -0800 Subject: [PATCH 02/37] polish --- lib/splitclient-rb/cache/repositories/splits_repository.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/splitclient-rb/cache/repositories/splits_repository.rb b/lib/splitclient-rb/cache/repositories/splits_repository.rb index 787c9061..a023c75f 100644 --- a/lib/splitclient-rb/cache/repositories/splits_repository.rb +++ b/lib/splitclient-rb/cache/repositories/splits_repository.rb @@ -182,7 +182,7 @@ def add_feature_flag(split) if check_undefined_matcher(split) @config.logger.warn("Feature Flag #{split[:name]} has undefined matcher, setting conditions to default template.") - split['conditions'] = [SplitsRepository::DEFAULT_CONDITIONS_TEMPLATE] + split[:conditions] = [SplitsRepository::DEFAULT_CONDITIONS_TEMPLATE] end if !split[:sets].nil? for flag_set in split[:sets] From 75cc2223903a5d59582e9cec0f51fd36d6dd203b Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Tue, 26 Mar 2024 13:18:48 -0700 Subject: [PATCH 03/37] added semver class --- lib/splitclient-rb/engine/matchers/semver.rb | 150 +++++++++++++++++++ spec/engine/matchers/semver_spec.rb | 92 ++++++++++++ 2 files changed, 242 insertions(+) create mode 100644 lib/splitclient-rb/engine/matchers/semver.rb create mode 100644 spec/engine/matchers/semver_spec.rb diff --git a/lib/splitclient-rb/engine/matchers/semver.rb b/lib/splitclient-rb/engine/matchers/semver.rb new file mode 100644 index 00000000..43ad0496 --- /dev/null +++ b/lib/splitclient-rb/engine/matchers/semver.rb @@ -0,0 +1,150 @@ +# frozen_string_literal: true + +module SplitIoClient + class Semver + METADATA_DELIMITER = '+' + PRE_RELEASE_DELIMITER = '-' + VALUE_DELIMITER = '.' + + attr_reader :major, :minor, :patch, :pre_release, :is_stable, :old_version + + # + # Class Initializer + # + # @param version [String] raw version as read from splitChanges response. + def initialize(version) + @major = 0 + @minor = 0 + @patch = 0 + @pre_release = [] + @is_stable = false + @old_version = version + parse + end + + # + # Check if there is any metadata characters in self._old_version. + # + # @return [type] String semver without the metadata + # + def remove_metadata_if_exists + index = @old_version.index(METADATA_DELIMITER) + return @old_version if index.nil? + + @old_version[0, index] + end + + # Compare the current Semver object to a given Semver object, return: + # 0: if self == passed + # 1: if self > passed + # -1: if self < passed + # + # @param to_compare [trype] splitio.models.grammar.matchers.semver.Semver object + # + # @returns [Integer] based on comparison + def compare(to_compare) + return 0 if @old_version == to_compare.old_version + + # Compare major, minor, and patch versions numerically + return compare_attributes(to_compare) if compare_attributes(to_compare) != 0 + + # Compare pre-release versions lexically + compare_pre_release(to_compare) + end + + private + + def integer?(value) + value.to_i.to_s == value + end + + # + # Parse the string in self._old_version to update the other internal variables + # + def parse + without_metadata = remove_metadata_if_exists + + index = without_metadata.index(PRE_RELEASE_DELIMITER) + if index.nil? + @is_stable = true + else + pre_release_data = without_metadata[index + 1..-1] + without_metadata = without_metadata[0, index] + @pre_release = pre_release_data.split(VALUE_DELIMITER) + end + assign_major_minor_and_patch(without_metadata) + end + + # + # Set the major, minor and patch internal variables based on string passed. + # + # @param version [String] raw version containing major.minor.patch numbers. + def assign_major_minor_and_patch(version) + parts = version.split(VALUE_DELIMITER) + if parts.length != 3 || + !(integer?(parts[0]) && + integer?(parts[1]) && + integer?(parts[2])) + raise "Unable to convert to Semver, incorrect format: #{version}" + end + + @major = parts[0].to_i + @minor = parts[1].to_i + @patch = parts[2].to_i + end + + # + # Compare 2 variables and return int as follows: + # 0: if var1 == var2 + # 1: if var1 > var2 + # -1: if var1 < var2 + # + # @param var1 [type] String/Integer object that accept ==, < or > operators + # @param var2 [type] String/Integer object that accept ==, < or > operators + # + # @returns [Integer] based on comparison + def compare_vars(var1, var2) + return 0 if var1 == var2 + + return 1 if var1 > var2 + + -1 + end + + def compare_attributes(to_compare) + result = compare_vars(@major, to_compare.major) + return result if result != 0 + + result = compare_vars(@minor, to_compare.minor) + return result if result != 0 + + result = compare_vars(@patch, to_compare.patch) + return result if result != 0 + + return -1 if !@is_stable && to_compare.is_stable + + return 1 if @is_stable && !to_compare.is_stable + + 0 + end + + def compare_pre_release(to_compare) + min_length = get_pre_min_length(to_compare) + 0.upto(min_length - 1) do |i| + next if @pre_release[i] == to_compare.pre_release[i] + + if integer?(@pre_release[i]) && integer?(to_compare.pre_release[i]) + return compare_vars(@pre_release[i].to_i, to_compare.pre_release[i].to_i) + end + + return compare_vars(@pre_release[i], to_compare.pre_release[i]) + end + # Compare lengths of pre-release versions + compare_vars(@pre_release.length, to_compare.pre_release.length) + end + + def get_pre_min_length(to_compare) + [@pre_release.length, to_compare.pre_release.length].min + end + end +end diff --git a/spec/engine/matchers/semver_spec.rb b/spec/engine/matchers/semver_spec.rb new file mode 100644 index 00000000..858d7c8d --- /dev/null +++ b/spec/engine/matchers/semver_spec.rb @@ -0,0 +1,92 @@ +require 'spec_helper' + +describe SplitIoClient::Semver do + let(:valid_versions) { ["1.1.2", "1.1.1", "1.0.0", "1.0.0-rc.1", "1.0.0-beta.11", "1.0.0-beta.2", + "1.0.0-beta", "1.0.0-alpha.beta", "1.0.0-alpha.1", "1.0.0-alpha", "2.2.2-rc.2+metadata-lalala", "2.2.2-rc.1.2", + "1.2.3", "0.0.4", "1.1.2+meta", "1.1.2-prerelease+meta", "1.0.0-beta", "1.0.0-alpha", "1.0.0-alpha0.valid", + "1.0.0-alpha.0valid", "1.0.0-rc.1+build.1", "1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay", + "10.2.3-DEV-SNAPSHOT", "1.2.3-SNAPSHOT-123", "1.1.1-rc2", "1.0.0-0A.is.legal", "1.2.3----RC-SNAPSHOT.12.9.1--.12+788", + "1.2.3----R-S.12.9.1--.12+meta", "1.2.3----RC-SNAPSHOT.12.9.1--.12.88", "1.2.3----RC-SNAPSHOT.12.9.1--.12", + "9223372036854775807.9223372036854775807.9223372036854775807", "9223372036854775807.9223372036854775807.9223372036854775806", + "1.1.1-alpha.beta.rc.build.java.pr.support.10", "1.1.1-alpha.beta.rc.build.java.pr.support"] } + + context 'check versions' do + it 'accept valid versions' do + major = [1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2, 2, + 1, 0, 1, 1, 1, 1, 1, + 1, 1, 1, + 10, 1, 1, 1, 1, + 1, 1, 1, + 9223372036854775807, 9223372036854775807, + 1,1] + minor = [1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 2, + 2, 0, 1, 1, 0, 0, 0, + 0, 0, 0, + 2, 2, 1, 0, 2, + 2, 2, 2, + 9223372036854775807, 9223372036854775807, + 1, 1] + patch = [2, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 2, + 3, 4, 2, 2, 0, 0, 0, + 0, 0, 0, + 3, 3, 1, 0, 3, + 3, 3, 3, + 9223372036854775807, 9223372036854775806, + 1, 1] + pre_release = [[], [], [], ["rc","1"], ["beta","11"],["beta","2"], + ["beta"], ["alpha","beta"], ["alpha","1"], ["alpha"], ["rc","2"], ["rc","1","2"], + [], [], [], ["prerelease"], ["beta"], ["alpha"], ["alpha0","valid"], + ["alpha","0valid"], ["rc","1"], ["alpha-a","b-c-somethinglong"], + ["DEV-SNAPSHOT"], ["SNAPSHOT-123"], ["rc2"], ["0A","is","legal"], ["---RC-SNAPSHOT","12","9","1--","12"], + ["---R-S","12","9","1--","12"], ["---RC-SNAPSHOT","12","9","1--","12","88"], ["---RC-SNAPSHOT","12","9","1--","12"], + [], [], + ["alpha","beta","rc","build","java","pr","support","10"], ["alpha","beta","rc","build","java","pr","support"]] + + for i in (0..major.length-1) + semver = described_class.new(valid_versions[i]) + expect(verify_version(semver, major[i], minor[i], patch[i], pre_release[i], pre_release[i]==[])).to eq(true) + end + end + it 'reject invalid versions' do + invalid_versions = [ + "1", "1.2", "1.alpha.2", "+invalid", "-invalid", "-invalid+invalid", "+justmeta", + "-invalid.01", "alpha", "alpha.beta", "alpha.beta.1", "alpha.1", "alpha+beta", + "alpha_beta", "alpha.", "alpha..", "beta", "-alpha.", "1.2", "1.2.3.DEV", "-1.0.3-gamma+b7718", + "1.2-SNAPSHOT", "1.2.31.2.3----RC-SNAPSHOT.12.09.1--..12+788", "1.2-RC-SNAPSHOT"] + + for version in invalid_versions + expect{ described_class.new(version) }.to raise_error(RuntimeError) + end + end + end + + context 'compare versions' do + it 'higher, lower and equal' do + cnt = 0 + for i in (0..(valid_versions.length/2).to_i-1) + expect(described_class.new(valid_versions[cnt]).compare(described_class.new(valid_versions[cnt+1]))).to eq(1) + expect(described_class.new(valid_versions[cnt+1]).compare(described_class.new(valid_versions[cnt]))).to eq(-1) + expect(described_class.new(valid_versions[cnt]).compare(described_class.new(valid_versions[cnt]))).to eq(0) + expect(described_class.new(valid_versions[cnt+1]).compare(described_class.new(valid_versions[cnt+1]))).to eq(0) + cnt = cnt + 2 + end + expect(described_class.new("1.1.1").compare(described_class.new("1.1.1"))).to eq(0) + expect(described_class.new("1.1.1").compare(described_class.new("1.1.1+metadata"))).to eq(0) + expect(described_class.new("1.1.1").compare(described_class.new("1.1.1-rc.1"))).to eq(1) + expect(described_class.new("88.88.88").compare(described_class.new("88.88.88"))).to eq(0) + expect(described_class.new("1.2.3----RC-SNAPSHOT.12.9.1--.12").compare(described_class.new("1.2.3----RC-SNAPSHOT.12.9.1--.12"))).to eq(0) + expect(described_class.new("10.2.3-DEV-SNAPSHOT").compare(described_class.new("10.2.3-SNAPSHOT-123"))).to eq(-1) + end + end + + def verify_version(semver, major, minor, patch, pre_release="", is_stable=True) + if semver.major == major and semver.minor == minor and semver.patch == patch and + semver.pre_release == pre_release and semver.is_stable == is_stable + return true + end + return false + end +end From 21f408b55ee3d7beb4058d37ec84c06b3150bbf5 Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Tue, 26 Mar 2024 13:25:19 -0700 Subject: [PATCH 04/37] polish --- lib/splitclient-rb.rb | 1 + lib/splitclient-rb/engine/matchers/semver.rb | 21 ++++++++++++++++++++ spec/engine/matchers/semver_spec.rb | 4 ++-- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/lib/splitclient-rb.rb b/lib/splitclient-rb.rb index 642db7d5..2bc230dd 100644 --- a/lib/splitclient-rb.rb +++ b/lib/splitclient-rb.rb @@ -90,6 +90,7 @@ require 'splitclient-rb/engine/matchers/equal_to_boolean_matcher' require 'splitclient-rb/engine/matchers/equal_to_matcher' require 'splitclient-rb/engine/matchers/matches_string_matcher' +require 'splitclient-rb/engine/matchers/semver' require 'splitclient-rb/engine/evaluator/splitter' require 'splitclient-rb/engine/impressions/noop_unique_keys_tracker' require 'splitclient-rb/engine/impressions/unique_keys_tracker' diff --git a/lib/splitclient-rb/engine/matchers/semver.rb b/lib/splitclient-rb/engine/matchers/semver.rb index 43ad0496..ac9b7a58 100644 --- a/lib/splitclient-rb/engine/matchers/semver.rb +++ b/lib/splitclient-rb/engine/matchers/semver.rb @@ -111,6 +111,14 @@ def compare_vars(var1, var2) -1 end + # Compare the current Semver object's major, minor, patch and is_stable attributes to a given Semver object, return: + # 0: if self == passed + # 1: if self > passed + # -1: if self < passed + # + # @param to_compare [trype] splitio.models.grammar.matchers.semver.Semver object + # + # @returns [Integer] based on comparison def compare_attributes(to_compare) result = compare_vars(@major, to_compare.major) return result if result != 0 @@ -128,6 +136,14 @@ def compare_attributes(to_compare) 0 end + # Compare the current Semver object's pre_release attribute to a given Semver object, return: + # 0: if self == passed + # 1: if self > passed + # -1: if self < passed + # + # @param to_compare [trype] splitio.models.grammar.matchers.semver.Semver object + # + # @returns [Integer] based on comparison def compare_pre_release(to_compare) min_length = get_pre_min_length(to_compare) 0.upto(min_length - 1) do |i| @@ -143,6 +159,11 @@ def compare_pre_release(to_compare) compare_vars(@pre_release.length, to_compare.pre_release.length) end + # Get minimum of current Semver object's pre_release attributes length to a given Semver object + # + # @param to_compare [trype] splitio.models.grammar.matchers.semver.Semver object + # + # @returns [Integer] def get_pre_min_length(to_compare) [@pre_release.length, to_compare.pre_release.length].min end diff --git a/spec/engine/matchers/semver_spec.rb b/spec/engine/matchers/semver_spec.rb index 858d7c8d..a0f98429 100644 --- a/spec/engine/matchers/semver_spec.rb +++ b/spec/engine/matchers/semver_spec.rb @@ -83,8 +83,8 @@ end def verify_version(semver, major, minor, patch, pre_release="", is_stable=True) - if semver.major == major and semver.minor == minor and semver.patch == patch and - semver.pre_release == pre_release and semver.is_stable == is_stable + if semver.major == major && semver.minor == minor &&d semver.patch == patch && + semver.pre_release == pre_release && semver.is_stable == is_stable return true end return false From 6b83afc903ca6c8d5288e7ee638ddc59c66ec169 Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Thu, 28 Mar 2024 13:13:39 -0700 Subject: [PATCH 05/37] added equalto semver matcher --- lib/splitclient-rb.rb | 1 + .../matchers/equal_to_semver_matcher.rb | 31 +++++++++++++++++++ lib/splitclient-rb/engine/parser/condition.rb | 8 +++++ .../matches_equal_to_semver_matcher_spec.rb | 31 +++++++++++++++++++ 4 files changed, 71 insertions(+) create mode 100644 lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb create mode 100644 spec/engine/matchers/matches_equal_to_semver_matcher_spec.rb diff --git a/lib/splitclient-rb.rb b/lib/splitclient-rb.rb index 2bc230dd..2b05ab65 100644 --- a/lib/splitclient-rb.rb +++ b/lib/splitclient-rb.rb @@ -91,6 +91,7 @@ require 'splitclient-rb/engine/matchers/equal_to_matcher' require 'splitclient-rb/engine/matchers/matches_string_matcher' require 'splitclient-rb/engine/matchers/semver' +require 'splitclient-rb/engine/matchers/equal_to_semver_matcher' require 'splitclient-rb/engine/evaluator/splitter' require 'splitclient-rb/engine/impressions/noop_unique_keys_tracker' require 'splitclient-rb/engine/impressions/unique_keys_tracker' diff --git a/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb new file mode 100644 index 00000000..5d82b7e0 --- /dev/null +++ b/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module SplitIoClient + class EqualToSemverMatcher < Matcher + MATCHER_TYPE = 'EQUAL_TO_SEMVER' + + attr_reader :attribute + + def initialize(attribute, string_value, logger, validator) + super(logger) + @validator = validator + @attribute = attribute + @semver = SplitIoClient::Semver.new(string_value) + @logger = logger + end + + def match?(args) + @logger.log_if_debug('[EqualsToSemverMatcher] evaluating value and attributes.') + return false unless @validator.valid_matcher_arguments(args) + + value_to_match = args[:attributes][@attribute.to_sym] + if !value_to_match.is_a?(String) + @logger.error('stringMatcherData is required for EQUAL_TO_SEMVER matcher type') + return false + end + matches = (@semver.compare(SplitIoClient::Semver.new(value_to_match)) == 0) + @logger.log_if_debug("[EqualsToSemverMatcher] #{value_to_match} matches -> #{matches}") + matches + end + end +end diff --git a/lib/splitclient-rb/engine/parser/condition.rb b/lib/splitclient-rb/engine/parser/condition.rb index f80fbffb..b23dda8a 100644 --- a/lib/splitclient-rb/engine/parser/condition.rb +++ b/lib/splitclient-rb/engine/parser/condition.rb @@ -189,6 +189,14 @@ def matcher_matches_string(params) ) end + def matcher_equal_to_semver(params) + EqualToSemverMatcher.new( + params[:matcher][:keySelector][:attribute], + params[:matcher][:stringMatcherData], + @config.split_logger, @config.split_validator + ) + end + # # @return [object] the negate value for this condition def negate diff --git a/spec/engine/matchers/matches_equal_to_semver_matcher_spec.rb b/spec/engine/matchers/matches_equal_to_semver_matcher_spec.rb new file mode 100644 index 00000000..f8ecda72 --- /dev/null +++ b/spec/engine/matchers/matches_equal_to_semver_matcher_spec.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe SplitIoClient::EqualToSemverMatcher do + let(:raw) { { + 'negate': false, + 'matcherType': 'EQUAL_TO_SEMVER', + 'stringMatcherData': "2.1.8" + } } + let(:config) { SplitIoClient::SplitConfig.new } + + it 'initilized params' do + matcher = described_class.new("version", raw[:stringMatcherData], config.split_logger, config.split_validator) + expect(matcher.attribute).to eq("version") + semver = matcher.instance_variable_get(:@semver) + expect(semver.instance_variable_get(:@old_version)).to eq("2.1.8") + end + + it 'matches' do + matcher = described_class.new("version", raw[:stringMatcherData], config.split_logger, config.split_validator) + expect(matcher.match?(:attributes=>{"version": "2.1.8+rc"})).to eq(true) + expect(matcher.match?(:attributes=>{"version": "2.1.8"})).to eq(true) + end + + it 'does not match' do + matcher = described_class.new("version", raw[:stringMatcherData], config.split_logger, config.split_validator) + expect(matcher.match?(:attributes=>{"version": "2.1.5"})).to eq(false) + expect(matcher.match?(:attributes=>{"version": "2.1.5-rc1"})).to eq(false) + end +end From 93d73b06ae11816cc57af18e086fe1453bc1f3be Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Thu, 28 Mar 2024 13:17:52 -0700 Subject: [PATCH 06/37] polish --- lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb index 5d82b7e0..7907826b 100644 --- a/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb @@ -19,11 +19,11 @@ def match?(args) return false unless @validator.valid_matcher_arguments(args) value_to_match = args[:attributes][@attribute.to_sym] - if !value_to_match.is_a?(String) + unless value_to_match.is_a?(String) @logger.error('stringMatcherData is required for EQUAL_TO_SEMVER matcher type') return false end - matches = (@semver.compare(SplitIoClient::Semver.new(value_to_match)) == 0) + matches = @semver.compare(SplitIoClient::Semver.new(value_to_match)).zero? @logger.log_if_debug("[EqualsToSemverMatcher] #{value_to_match} matches -> #{matches}") matches end From 45fbd0b2d8d8d88f69bf6c791589c56747c70fa0 Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Thu, 28 Mar 2024 13:20:17 -0700 Subject: [PATCH 07/37] polish --- .../engine/matchers/equal_to_semver_matcher.rb | 2 +- .../matchers/matches_equal_to_semver_matcher_spec.rb | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb index 7907826b..01f62baf 100644 --- a/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb @@ -20,7 +20,7 @@ def match?(args) value_to_match = args[:attributes][@attribute.to_sym] unless value_to_match.is_a?(String) - @logger.error('stringMatcherData is required for EQUAL_TO_SEMVER matcher type') + @logger.log_if_debug('stringMatcherData is required for EQUAL_TO_SEMVER matcher type') return false end matches = @semver.compare(SplitIoClient::Semver.new(value_to_match)).zero? diff --git a/spec/engine/matchers/matches_equal_to_semver_matcher_spec.rb b/spec/engine/matchers/matches_equal_to_semver_matcher_spec.rb index f8ecda72..f1280866 100644 --- a/spec/engine/matchers/matches_equal_to_semver_matcher_spec.rb +++ b/spec/engine/matchers/matches_equal_to_semver_matcher_spec.rb @@ -28,4 +28,11 @@ expect(matcher.match?(:attributes=>{"version": "2.1.5"})).to eq(false) expect(matcher.match?(:attributes=>{"version": "2.1.5-rc1"})).to eq(false) end + + it 'invalid attribute' do + matcher = described_class.new("version", raw[:stringMatcherData], config.split_logger, config.split_validator) + expect(matcher.match?(:attributes=>{"version": 2.1})).to eq(false) + expect(matcher.match?(:attributes=>{"version": nil})).to eq(false) + end + end From 8aa6bb96c07df4893ac1d16b20d63b8e831aa891 Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Thu, 28 Mar 2024 13:45:59 -0700 Subject: [PATCH 08/37] added greater than or equal to semver matcher --- lib/splitclient-rb.rb | 1 + ...greater_than_or_equal_to_semver_matcher.rb | 31 ++++++++++++++ lib/splitclient-rb/engine/parser/condition.rb | 8 ++++ ...er_than_or_equal_to_semver_matcher_spec.rb | 40 +++++++++++++++++++ 4 files changed, 80 insertions(+) create mode 100644 lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb create mode 100644 spec/engine/matchers/matches_greater_than_or_equal_to_semver_matcher_spec.rb diff --git a/lib/splitclient-rb.rb b/lib/splitclient-rb.rb index 2b05ab65..10a12a56 100644 --- a/lib/splitclient-rb.rb +++ b/lib/splitclient-rb.rb @@ -92,6 +92,7 @@ require 'splitclient-rb/engine/matchers/matches_string_matcher' require 'splitclient-rb/engine/matchers/semver' require 'splitclient-rb/engine/matchers/equal_to_semver_matcher' +require 'splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher' require 'splitclient-rb/engine/evaluator/splitter' require 'splitclient-rb/engine/impressions/noop_unique_keys_tracker' require 'splitclient-rb/engine/impressions/unique_keys_tracker' diff --git a/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb new file mode 100644 index 00000000..818962eb --- /dev/null +++ b/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module SplitIoClient + class GreaterThanOrEqualToSemverMatcher < Matcher + MATCHER_TYPE = 'GREATER_THAN_OR_EQUAL_TO_SEMVER' + + attr_reader :attribute + + def initialize(attribute, string_value, logger, validator) + super(logger) + @validator = validator + @attribute = attribute + @semver = SplitIoClient::Semver.new(string_value) + @logger = logger + end + + def match?(args) + @logger.log_if_debug('[GreaterThanOrEqualsToSemverMatcher] evaluating value and attributes.') + return false unless @validator.valid_matcher_arguments(args) + + value_to_match = args[:attributes][@attribute.to_sym] + unless value_to_match.is_a?(String) + @logger.log_if_debug('stringMatcherData is required for GREATER_THAN_OR_EQUAL_TO_SEMVER matcher type') + return false + end + matches = [0, 1].include?(@semver.compare(SplitIoClient::Semver.new(value_to_match))) + @logger.log_if_debug("[GreaterThanOrEqualsToSemverMatcher] #{value_to_match} matches -> #{matches}") + matches + end + end +end diff --git a/lib/splitclient-rb/engine/parser/condition.rb b/lib/splitclient-rb/engine/parser/condition.rb index b23dda8a..1670ecde 100644 --- a/lib/splitclient-rb/engine/parser/condition.rb +++ b/lib/splitclient-rb/engine/parser/condition.rb @@ -197,6 +197,14 @@ def matcher_equal_to_semver(params) ) end + def matcher_greater_than_or_equal_to_semver(params) + GreaterThanOrEqualToSemverMatcher.new( + params[:matcher][:keySelector][:attribute], + params[:matcher][:stringMatcherData], + @config.split_logger, @config.split_validator + ) + end + # # @return [object] the negate value for this condition def negate diff --git a/spec/engine/matchers/matches_greater_than_or_equal_to_semver_matcher_spec.rb b/spec/engine/matchers/matches_greater_than_or_equal_to_semver_matcher_spec.rb new file mode 100644 index 00000000..7888cbe9 --- /dev/null +++ b/spec/engine/matchers/matches_greater_than_or_equal_to_semver_matcher_spec.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe SplitIoClient::GreaterThanOrEqualToSemverMatcher do + let(:raw) { { + 'negate': false, + 'matcherType': 'GREATER_THAN_OR_EQUAL_TO_SEMVER', + 'stringMatcherData': "2.1.8" + } } + let(:config) { SplitIoClient::SplitConfig.new } + + it 'initilized params' do + matcher = described_class.new("version", raw[:stringMatcherData], config.split_logger, config.split_validator) + expect(matcher.attribute).to eq("version") + semver = matcher.instance_variable_get(:@semver) + expect(semver.instance_variable_get(:@old_version)).to eq("2.1.8") + end + + it 'matches' do + matcher = described_class.new("version", raw[:stringMatcherData], config.split_logger, config.split_validator) + expect(matcher.match?(:attributes=>{"version": "2.1.8+rc"})).to eq(true) + expect(matcher.match?(:attributes=>{"version": "2.1.8"})).to eq(true) + expect(matcher.match?(:attributes=>{"version": "2.1.5"})).to eq(true) + expect(matcher.match?(:attributes=>{"version": "2.1.5-rc1"})).to eq(true) + end + + it 'does not match' do + matcher = described_class.new("version", raw[:stringMatcherData], config.split_logger, config.split_validator) + expect(matcher.match?(:attributes=>{"version": "2.1.11"})).to eq(false) + expect(matcher.match?(:attributes=>{"version": "2.2.0"})).to eq(false) + end + + it 'invalid attribute' do + matcher = described_class.new("version", raw[:stringMatcherData], config.split_logger, config.split_validator) + expect(matcher.match?(:attributes=>{"version": 2.1})).to eq(false) + expect(matcher.match?(:attributes=>{"version": nil})).to eq(false) + end + +end From 30232f8632c3d81ffe4db3b2bd93799aefe24517 Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Fri, 29 Mar 2024 11:01:04 -0700 Subject: [PATCH 09/37] added less than or equal semver matcher --- lib/splitclient-rb.rb | 1 + .../less_than_or_equal_to_semver_matcher.rb | 31 ++++++++++++++ lib/splitclient-rb/engine/parser/condition.rb | 8 ++++ ...ss_than_or_equal_to_semver_matcher_spec.rb | 40 +++++++++++++++++++ 4 files changed, 80 insertions(+) create mode 100644 lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb create mode 100644 spec/engine/matchers/matches_less_than_or_equal_to_semver_matcher_spec.rb diff --git a/lib/splitclient-rb.rb b/lib/splitclient-rb.rb index 10a12a56..b2fd8c99 100644 --- a/lib/splitclient-rb.rb +++ b/lib/splitclient-rb.rb @@ -93,6 +93,7 @@ require 'splitclient-rb/engine/matchers/semver' require 'splitclient-rb/engine/matchers/equal_to_semver_matcher' require 'splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher' +require 'splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher' require 'splitclient-rb/engine/evaluator/splitter' require 'splitclient-rb/engine/impressions/noop_unique_keys_tracker' require 'splitclient-rb/engine/impressions/unique_keys_tracker' diff --git a/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb new file mode 100644 index 00000000..eb95702f --- /dev/null +++ b/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module SplitIoClient + class LessThanOrEqualToSemverMatcher < Matcher + MATCHER_TYPE = 'LESS_THAN_OR_EQUAL_TO_SEMVER' + + attr_reader :attribute + + def initialize(attribute, string_value, logger, validator) + super(logger) + @validator = validator + @attribute = attribute + @semver = SplitIoClient::Semver.new(string_value) + @logger = logger + end + + def match?(args) + @logger.log_if_debug('[LessThanOrEqualsToSemverMatcher] evaluating value and attributes.') + return false unless @validator.valid_matcher_arguments(args) + + value_to_match = args[:attributes][@attribute.to_sym] + unless value_to_match.is_a?(String) + @logger.log_if_debug('stringMatcherData is required for LESS_THAN_OR_EQUAL_TO_SEMVER matcher type') + return false + end + matches = [0, -1].include?(@semver.compare(SplitIoClient::Semver.new(value_to_match))) + @logger.log_if_debug("[LessThanOrEqualsToSemverMatcher] #{value_to_match} matches -> #{matches}") + matches + end + end +end diff --git a/lib/splitclient-rb/engine/parser/condition.rb b/lib/splitclient-rb/engine/parser/condition.rb index 1670ecde..cd99225b 100644 --- a/lib/splitclient-rb/engine/parser/condition.rb +++ b/lib/splitclient-rb/engine/parser/condition.rb @@ -205,6 +205,14 @@ def matcher_greater_than_or_equal_to_semver(params) ) end + def matcher_less_than_or_equal_to_semver(params) + LessThanOrEqualToSemverMatcher.new( + params[:matcher][:keySelector][:attribute], + params[:matcher][:stringMatcherData], + @config.split_logger, @config.split_validator + ) + end + # # @return [object] the negate value for this condition def negate diff --git a/spec/engine/matchers/matches_less_than_or_equal_to_semver_matcher_spec.rb b/spec/engine/matchers/matches_less_than_or_equal_to_semver_matcher_spec.rb new file mode 100644 index 00000000..0a8bc6d3 --- /dev/null +++ b/spec/engine/matchers/matches_less_than_or_equal_to_semver_matcher_spec.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe SplitIoClient::LessThanOrEqualToSemverMatcher do + let(:raw) { { + 'negate': false, + 'matcherType': 'LESS_THAN_OR_EQUAL_TO_SEMVER', + 'stringMatcherData': "2.1.8" + } } + let(:config) { SplitIoClient::SplitConfig.new } + + it 'initilized params' do + matcher = described_class.new("version", raw[:stringMatcherData], config.split_logger, config.split_validator) + expect(matcher.attribute).to eq("version") + semver = matcher.instance_variable_get(:@semver) + expect(semver.instance_variable_get(:@old_version)).to eq("2.1.8") + end + + it 'matches' do + matcher = described_class.new("version", raw[:stringMatcherData], config.split_logger, config.split_validator) + expect(matcher.match?(:attributes=>{"version": "2.1.8+rc"})).to eq(true) + expect(matcher.match?(:attributes=>{"version": "2.1.8"})).to eq(true) + expect(matcher.match?(:attributes=>{"version": "2.1.11"})).to eq(true) + expect(matcher.match?(:attributes=>{"version": "2.2.0"})).to eq(true) + end + + it 'does not match' do + matcher = described_class.new("version", raw[:stringMatcherData], config.split_logger, config.split_validator) + expect(matcher.match?(:attributes=>{"version": "2.1.5"})).to eq(false) + expect(matcher.match?(:attributes=>{"version": "2.1.5-rc1"})).to eq(false) + end + + it 'invalid attribute' do + matcher = described_class.new("version", raw[:stringMatcherData], config.split_logger, config.split_validator) + expect(matcher.match?(:attributes=>{"version": 2.1})).to eq(false) + expect(matcher.match?(:attributes=>{"version": nil})).to eq(false) + end + +end From 2f95e4be0f09a3f07321152b4065673e2850e2a8 Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Fri, 29 Mar 2024 11:31:02 -0700 Subject: [PATCH 10/37] added semver between matcher --- lib/splitclient-rb.rb | 1 + .../engine/matchers/between_semver_matcher.rb | 33 +++++++++++++++ lib/splitclient-rb/engine/parser/condition.rb | 9 ++++ .../matches_between_semver_matcher_spec.rb | 41 +++++++++++++++++++ 4 files changed, 84 insertions(+) create mode 100644 lib/splitclient-rb/engine/matchers/between_semver_matcher.rb create mode 100644 spec/engine/matchers/matches_between_semver_matcher_spec.rb diff --git a/lib/splitclient-rb.rb b/lib/splitclient-rb.rb index b2fd8c99..d76f8218 100644 --- a/lib/splitclient-rb.rb +++ b/lib/splitclient-rb.rb @@ -94,6 +94,7 @@ require 'splitclient-rb/engine/matchers/equal_to_semver_matcher' require 'splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher' require 'splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher' +require 'splitclient-rb/engine/matchers/between_semver_matcher' require 'splitclient-rb/engine/evaluator/splitter' require 'splitclient-rb/engine/impressions/noop_unique_keys_tracker' require 'splitclient-rb/engine/impressions/unique_keys_tracker' diff --git a/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb new file mode 100644 index 00000000..282dcf5c --- /dev/null +++ b/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +module SplitIoClient + class BetweenSemverMatcher < Matcher + MATCHER_TYPE = 'BETWEEN_SEMVER' + + attr_reader :attribute + + def initialize(attribute, start_value, end_value, logger, validator) + super(logger) + @validator = validator + @attribute = attribute + @semver_start = SplitIoClient::Semver.new(start_value) + @semver_end = SplitIoClient::Semver.new(end_value) + @logger = logger + end + + def match?(args) + @logger.log_if_debug('[BetweenSemverMatcher] evaluating value and attributes.') + return false unless @validator.valid_matcher_arguments(args) + + value_to_match = args[:attributes][@attribute.to_sym] + unless value_to_match.is_a?(String) + @logger.log_if_debug('betweenStringMatcherData is required for BETWEEN_SEMVER matcher type') + return false + end + matches = ([0, -1].include?(@semver_start.compare(SplitIoClient::Semver.new(value_to_match))) && + [0, 1].include?(@semver_end.compare(SplitIoClient::Semver.new(value_to_match)))) + @logger.log_if_debug("[BetweenMatcher] #{value_to_match} matches -> #{matches}") + matches + end + end +end diff --git a/lib/splitclient-rb/engine/parser/condition.rb b/lib/splitclient-rb/engine/parser/condition.rb index cd99225b..cefec1da 100644 --- a/lib/splitclient-rb/engine/parser/condition.rb +++ b/lib/splitclient-rb/engine/parser/condition.rb @@ -213,6 +213,15 @@ def matcher_less_than_or_equal_to_semver(params) ) end + def matcher_between_semver(params) + BetweenSemverMatcher.new( + params[:matcher][:keySelector][:attribute], + params[:matcher][:betweenStringMatcherData][:start], + params[:matcher][:betweenStringMatcherData][:end], + @config.split_logger, @config.split_validator + ) + end + # # @return [object] the negate value for this condition def negate diff --git a/spec/engine/matchers/matches_between_semver_matcher_spec.rb b/spec/engine/matchers/matches_between_semver_matcher_spec.rb new file mode 100644 index 00000000..1fa7d0f8 --- /dev/null +++ b/spec/engine/matchers/matches_between_semver_matcher_spec.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe SplitIoClient::BetweenSemverMatcher do + let(:raw) { { + 'negate': false, + 'matcherType': 'BETWEEN_SEMVER', + 'betweenStringMatcherData': {"start": "2.1.8", "end": "2.1.11"} +} } + let(:config) { SplitIoClient::SplitConfig.new } + + it 'initilized params' do + matcher = described_class.new("version", raw[:betweenStringMatcherData][:start], raw[:betweenStringMatcherData][:end], config.split_logger, config.split_validator) + expect(matcher.attribute).to eq("version") + semver_start = matcher.instance_variable_get(:@semver_start) + expect(semver_start.instance_variable_get(:@old_version)).to eq("2.1.8") + semver_end = matcher.instance_variable_get(:@semver_end) + expect(semver_end.instance_variable_get(:@old_version)).to eq("2.1.11") + end + + it 'matches' do + matcher = described_class.new("version", raw[:betweenStringMatcherData][:start], raw[:betweenStringMatcherData][:end], config.split_logger, config.split_validator) + expect(matcher.match?(:attributes=>{"version": "2.1.8+rc"})).to eq(true) + expect(matcher.match?(:attributes=>{"version": "2.1.9"})).to eq(true) + expect(matcher.match?(:attributes=>{"version": "2.1.11-rc12"})).to eq(true) + end + + it 'does not match' do + matcher = described_class.new("version", raw[:betweenStringMatcherData][:start], raw[:betweenStringMatcherData][:end], config.split_logger, config.split_validator) + expect(matcher.match?(:attributes=>{"version": "2.1.5"})).to eq(false) + expect(matcher.match?(:attributes=>{"version": "2.1.12-rc1"})).to eq(false) + end + + it 'invalid attribute' do + matcher = described_class.new("version", raw[:betweenStringMatcherData][:start], raw[:betweenStringMatcherData][:end], config.split_logger, config.split_validator) + expect(matcher.match?(:attributes=>{"version": 2.1})).to eq(false) + expect(matcher.match?(:attributes=>{"version": nil})).to eq(false) + end + +end From 2749c653d8d4ecbfa7690cdc13a4bff2928d7450 Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Fri, 29 Mar 2024 13:26:23 -0700 Subject: [PATCH 11/37] added in list semver matcher --- lib/splitclient-rb.rb | 1 + .../engine/matchers/in_list_semver_matcher.rb | 31 ++++++++++++++ lib/splitclient-rb/engine/parser/condition.rb | 8 ++++ .../matches_in_list_semver_matcher_spec.rb | 40 +++++++++++++++++++ 4 files changed, 80 insertions(+) create mode 100644 lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb create mode 100644 spec/engine/matchers/matches_in_list_semver_matcher_spec.rb diff --git a/lib/splitclient-rb.rb b/lib/splitclient-rb.rb index d76f8218..a916dabb 100644 --- a/lib/splitclient-rb.rb +++ b/lib/splitclient-rb.rb @@ -95,6 +95,7 @@ require 'splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher' require 'splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher' require 'splitclient-rb/engine/matchers/between_semver_matcher' +require 'splitclient-rb/engine/matchers/in_list_semver_matcher' require 'splitclient-rb/engine/evaluator/splitter' require 'splitclient-rb/engine/impressions/noop_unique_keys_tracker' require 'splitclient-rb/engine/impressions/unique_keys_tracker' diff --git a/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb new file mode 100644 index 00000000..b1b5c1f4 --- /dev/null +++ b/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module SplitIoClient + class InListSemverMatcher < Matcher + MATCHER_TYPE = 'IN_LIST_SEMVER' + + attr_reader :attribute + + def initialize(attribute, list_value, logger, validator) + super(logger) + @validator = validator + @attribute = attribute + @semver_list = list_value.map { |item| SplitIoClient::Semver.new(item)} + @logger = logger + end + + def match?(args) + @logger.log_if_debug('[InListSemverMatcher] evaluating value and attributes.') + return false unless @validator.valid_matcher_arguments(args) + + value_to_match = args[:attributes][@attribute.to_sym] + unless value_to_match.is_a?(String) + @logger.log_if_debug('whitelistMatcherData is required for IN_LIST_SEMVER matcher type') + return false + end + matches = (@semver_list.map{ |item| item.compare(SplitIoClient::Semver.new(value_to_match)) }).any? { |item| item == 0 } + @logger.log_if_debug("[InListSemverMatcher] #{value_to_match} matches -> #{matches}") + matches + end + end +end diff --git a/lib/splitclient-rb/engine/parser/condition.rb b/lib/splitclient-rb/engine/parser/condition.rb index cefec1da..b5d7567b 100644 --- a/lib/splitclient-rb/engine/parser/condition.rb +++ b/lib/splitclient-rb/engine/parser/condition.rb @@ -222,6 +222,14 @@ def matcher_between_semver(params) ) end + def matcher_in_list_semver(params) + InListSemverMatcher.new( + params[:matcher][:keySelector][:attribute], + params[:matcher][:whitelistMatcherData][:whitelist], + @config.split_logger, @config.split_validator + ) + end + # # @return [object] the negate value for this condition def negate diff --git a/spec/engine/matchers/matches_in_list_semver_matcher_spec.rb b/spec/engine/matchers/matches_in_list_semver_matcher_spec.rb new file mode 100644 index 00000000..dad00078 --- /dev/null +++ b/spec/engine/matchers/matches_in_list_semver_matcher_spec.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe SplitIoClient::InListSemverMatcher do + let(:raw) { { + 'negate': false, + 'matcherType': 'INLIST_SEMVER', + 'whitelistMatcherData': {"whitelist": ["2.1.8", "2.1.11"]} +} } + let(:config) { SplitIoClient::SplitConfig.new } + + it 'initilized params' do + matcher = described_class.new("version", raw[:whitelistMatcherData][:whitelist], config.split_logger, config.split_validator) + expect(matcher.attribute).to eq("version") + semver_list = matcher.instance_variable_get(:@semver_list) + expect(semver_list[0].instance_variable_get(:@old_version)).to eq("2.1.8") + expect(semver_list[1].instance_variable_get(:@old_version)).to eq("2.1.11") + end + + it 'matches' do + matcher = described_class.new("version", raw[:whitelistMatcherData][:whitelist], config.split_logger, config.split_validator) + expect(matcher.match?(:attributes=>{"version": "2.1.8+rc"})).to eq(true) + expect(matcher.match?(:attributes=>{"version": "2.1.11"})).to eq(true) + end + + it 'does not match' do + matcher = described_class.new("version", raw[:whitelistMatcherData][:whitelist], config.split_logger, config.split_validator) + expect(matcher.match?(:attributes=>{"version": "2.1.7"})).to eq(false) + expect(matcher.match?(:attributes=>{"version": "2.1.11-rc12"})).to eq(false) + expect(matcher.match?(:attributes=>{"version": "2.1.8-rc1"})).to eq(false) + end + + it 'invalid attribute' do + matcher = described_class.new("version", raw[:whitelistMatcherData][:whitelist], config.split_logger, config.split_validator) + expect(matcher.match?(:attributes=>{"version": 2.1})).to eq(false) + expect(matcher.match?(:attributes=>{"version": nil})).to eq(false) + end + +end From 7fd6f34e63b2997b0f0f42f4faf13193ee996703 Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Fri, 29 Mar 2024 13:30:52 -0700 Subject: [PATCH 12/37] polish --- lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb index b1b5c1f4..7a424483 100644 --- a/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb @@ -10,7 +10,7 @@ def initialize(attribute, list_value, logger, validator) super(logger) @validator = validator @attribute = attribute - @semver_list = list_value.map { |item| SplitIoClient::Semver.new(item)} + @semver_list = list_value.map { |item| SplitIoClient::Semver.new(item) } @logger = logger end @@ -23,7 +23,7 @@ def match?(args) @logger.log_if_debug('whitelistMatcherData is required for IN_LIST_SEMVER matcher type') return false end - matches = (@semver_list.map{ |item| item.compare(SplitIoClient::Semver.new(value_to_match)) }).any? { |item| item == 0 } + matches = (@semver_list.map { |item| item.compare(SplitIoClient::Semver.new(value_to_match)) }).any?(&:zero?) @logger.log_if_debug("[InListSemverMatcher] #{value_to_match} matches -> #{matches}") matches end From e9677d93fcfd2fa38f726fde1bb8d2f87b66536a Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Mon, 1 Apr 2024 10:14:54 -0700 Subject: [PATCH 13/37] fixed matcher logic --- .../matchers/less_than_or_equal_to_semver_matcher.rb | 2 +- .../matches_less_than_or_equal_to_semver_matcher_spec.rb | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb index eb95702f..95c55f73 100644 --- a/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb @@ -23,7 +23,7 @@ def match?(args) @logger.log_if_debug('stringMatcherData is required for LESS_THAN_OR_EQUAL_TO_SEMVER matcher type') return false end - matches = [0, -1].include?(@semver.compare(SplitIoClient::Semver.new(value_to_match))) + matches = [0, -1].include?(SplitIoClient::Semver.new(value_to_match).compare(@semver)) @logger.log_if_debug("[LessThanOrEqualsToSemverMatcher] #{value_to_match} matches -> #{matches}") matches end diff --git a/spec/engine/matchers/matches_less_than_or_equal_to_semver_matcher_spec.rb b/spec/engine/matchers/matches_less_than_or_equal_to_semver_matcher_spec.rb index 0a8bc6d3..f25b76b3 100644 --- a/spec/engine/matchers/matches_less_than_or_equal_to_semver_matcher_spec.rb +++ b/spec/engine/matchers/matches_less_than_or_equal_to_semver_matcher_spec.rb @@ -21,14 +21,14 @@ matcher = described_class.new("version", raw[:stringMatcherData], config.split_logger, config.split_validator) expect(matcher.match?(:attributes=>{"version": "2.1.8+rc"})).to eq(true) expect(matcher.match?(:attributes=>{"version": "2.1.8"})).to eq(true) - expect(matcher.match?(:attributes=>{"version": "2.1.11"})).to eq(true) - expect(matcher.match?(:attributes=>{"version": "2.2.0"})).to eq(true) + expect(matcher.match?(:attributes=>{"version": "2.1.5"})).to eq(true) + expect(matcher.match?(:attributes=>{"version": "2.1.5-rc1"})).to eq(true) end it 'does not match' do matcher = described_class.new("version", raw[:stringMatcherData], config.split_logger, config.split_validator) - expect(matcher.match?(:attributes=>{"version": "2.1.5"})).to eq(false) - expect(matcher.match?(:attributes=>{"version": "2.1.5-rc1"})).to eq(false) + expect(matcher.match?(:attributes=>{"version": "2.1.11"})).to eq(false) + expect(matcher.match?(:attributes=>{"version": "2.2.0"})).to eq(false) end it 'invalid attribute' do From dc0caa64e16c9adcfc11ea63f7fda83be0e20af4 Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Mon, 1 Apr 2024 10:17:35 -0700 Subject: [PATCH 14/37] fixed matcher logic --- .../matchers/greater_than_or_equal_to_semver_matcher.rb | 2 +- ...atches_greater_than_or_equal_to_semver_matcher_spec.rb | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb index 818962eb..c67fe622 100644 --- a/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb @@ -23,7 +23,7 @@ def match?(args) @logger.log_if_debug('stringMatcherData is required for GREATER_THAN_OR_EQUAL_TO_SEMVER matcher type') return false end - matches = [0, 1].include?(@semver.compare(SplitIoClient::Semver.new(value_to_match))) + matches = [0, 1].include?(SplitIoClient::Semver.new(value_to_match).compare(@semver)) @logger.log_if_debug("[GreaterThanOrEqualsToSemverMatcher] #{value_to_match} matches -> #{matches}") matches end diff --git a/spec/engine/matchers/matches_greater_than_or_equal_to_semver_matcher_spec.rb b/spec/engine/matchers/matches_greater_than_or_equal_to_semver_matcher_spec.rb index 7888cbe9..69efa254 100644 --- a/spec/engine/matchers/matches_greater_than_or_equal_to_semver_matcher_spec.rb +++ b/spec/engine/matchers/matches_greater_than_or_equal_to_semver_matcher_spec.rb @@ -21,14 +21,14 @@ matcher = described_class.new("version", raw[:stringMatcherData], config.split_logger, config.split_validator) expect(matcher.match?(:attributes=>{"version": "2.1.8+rc"})).to eq(true) expect(matcher.match?(:attributes=>{"version": "2.1.8"})).to eq(true) - expect(matcher.match?(:attributes=>{"version": "2.1.5"})).to eq(true) - expect(matcher.match?(:attributes=>{"version": "2.1.5-rc1"})).to eq(true) + expect(matcher.match?(:attributes=>{"version": "2.1.11"})).to eq(true) + expect(matcher.match?(:attributes=>{"version": "2.2.0"})).to eq(true) end it 'does not match' do matcher = described_class.new("version", raw[:stringMatcherData], config.split_logger, config.split_validator) - expect(matcher.match?(:attributes=>{"version": "2.1.11"})).to eq(false) - expect(matcher.match?(:attributes=>{"version": "2.2.0"})).to eq(false) + expect(matcher.match?(:attributes=>{"version": "2.1.5"})).to eq(false) + expect(matcher.match?(:attributes=>{"version": "2.1.5-rc1"})).to eq(false) end it 'invalid attribute' do From 9f1b7d9ada9969b57c88a9cc248ccfe6bf37e988 Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Mon, 1 Apr 2024 10:34:41 -0700 Subject: [PATCH 15/37] semver matchers integrations spec --- .../semver_matchers_integration_spec.rb | 159 ++++++++++++++++++ .../semver_matchers/semver_between.json | 86 ++++++++++ .../semver_matchers/semver_equalto.json | 85 ++++++++++ .../semver_greater_or_equalto.json | 85 ++++++++++ .../splits/semver_matchers/semver_inlist.json | 86 ++++++++++ .../semver_less_or_equalto.json | 85 ++++++++++ 6 files changed, 586 insertions(+) create mode 100644 spec/engine/matchers/semver_matchers_integration_spec.rb create mode 100644 spec/test_data/splits/semver_matchers/semver_between.json create mode 100644 spec/test_data/splits/semver_matchers/semver_equalto.json create mode 100644 spec/test_data/splits/semver_matchers/semver_greater_or_equalto.json create mode 100644 spec/test_data/splits/semver_matchers/semver_inlist.json create mode 100644 spec/test_data/splits/semver_matchers/semver_less_or_equalto.json diff --git a/spec/engine/matchers/semver_matchers_integration_spec.rb b/spec/engine/matchers/semver_matchers_integration_spec.rb new file mode 100644 index 00000000..bedb07aa --- /dev/null +++ b/spec/engine/matchers/semver_matchers_integration_spec.rb @@ -0,0 +1,159 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Semver matchers integration' do + subject do + SplitIoClient::SplitFactory.new('test_api_key', { + logger: Logger.new(log), + streaming_enabled: false, + impressions_refresh_rate: 9999, + impressions_mode: :none, + features_refresh_rate: 9999, + telemetry_refresh_rate: 99999}).client + end + + let(:log) { StringIO.new } + + let(:semver_between_matcher_splits) do + File.read(File.expand_path(File.join(File.dirname(__FILE__), + '../../test_data/splits/semver_matchers/semver_between.json'))) + end + + let(:semver_equalto_matcher_splits) do + File.read(File.expand_path(File.join(File.dirname(__FILE__), + '../../test_data/splits/semver_matchers/semver_equalto.json'))) + end + + let(:semver_greater_or_equalto_matcher_splits) do + File.read(File.expand_path(File.join(File.dirname(__FILE__), + '../../test_data/splits/semver_matchers/semver_greater_or_equalto.json'))) + end + + let(:semver_less_or_equalto_matcher_splits) do + File.read(File.expand_path(File.join(File.dirname(__FILE__), + '../../test_data/splits/semver_matchers/semver_less_or_equalto.json'))) + end + + let(:semver_inlist_matcher_splits) do + File.read(File.expand_path(File.join(File.dirname(__FILE__), + '../../test_data/splits/semver_matchers/semver_inlist.json'))) + end + + let(:user) { 'fake_user_id_1' } + + before do + stub_request(:any, /https:\/\/telemetry.*/).to_return(status: 200, body: 'ok') + stub_request(:any, /https:\/\/events.*/).to_return(status: 200, body: "", headers: {}) + end + + context 'equal to matcher' do + before do + stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?since/) + .to_return(status: 200, body: semver_equalto_matcher_splits) + stub_request(:get, 'https://sdk.split.io/api/splitChanges?since=-1') + .to_return(status: 200, body: semver_equalto_matcher_splits) + sleep 1 + subject.block_until_ready + end + + it 'validates the treatment is ON for correct attribute value' do + expect(subject.get_treatment(user, 'semver_equalto', {:version => "1.22.9"})).to eq 'on' + end + + it 'validates the treatment is the default treatment for incorrect attributes hash and nil' do + expect(subject.get_treatment(user, 'semver_equalto')).to eq 'off' + expect(subject.get_treatment(user, 'semver_equalto', {:version => "1.22.10"})).to eq 'off' + subject.destroy() + end + end + + context 'greater than or equal to matcher' do + before do + stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?since/) + .to_return(status: 200, body: semver_greater_or_equalto_matcher_splits) + stub_request(:get, 'https://sdk.split.io/api/splitChanges?since=-1') + .to_return(status: 200, body: semver_greater_or_equalto_matcher_splits) + sleep 1 + subject.block_until_ready + end + + it 'validates the treatment is ON for correct attribute value' do + expect(subject.get_treatment(user, 'semver_greater_or_equalto', {:version => "1.22.9"})).to eq 'on' + expect(subject.get_treatment(user, 'semver_greater_or_equalto', {:version => "1.22.10"})).to eq 'on' + end + + it 'validates the treatment is the default treatment for incorrect attributes hash and nil' do + expect(subject.get_treatment(user, 'semver_greater_or_equalto')).to eq 'off' + expect(subject.get_treatment(user, 'semver_greater_or_equalto', {:version => "1.22.8"})).to eq 'off' + subject.destroy() + end + end + + context 'less than or equal to matcher' do + before do + stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?since/) + .to_return(status: 200, body: semver_less_or_equalto_matcher_splits) + stub_request(:get, 'https://sdk.split.io/api/splitChanges?since=-1') + .to_return(status: 200, body: semver_less_or_equalto_matcher_splits) + sleep 1 + subject.block_until_ready + end + + it 'validates the treatment is ON for correct attribute value' do + expect(subject.get_treatment(user, 'semver_less_or_equalto', {:version => "1.22.9"})).to eq 'on' + expect(subject.get_treatment(user, 'semver_less_or_equalto', {:version => "1.22.8"})).to eq 'on' + end + + it 'validates the treatment is the default treatment for incorrect attributes hash and nil' do + expect(subject.get_treatment(user, 'semver_less_or_equalto')).to eq 'off' + expect(subject.get_treatment(user, 'semver_less_or_equalto', {:version => "1.22.10"})).to eq 'off' + subject.destroy() + end + end + + context 'in list matcher' do + before do + stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?since/) + .to_return(status: 200, body: semver_inlist_matcher_splits) + stub_request(:get, 'https://sdk.split.io/api/splitChanges?since=-1') + .to_return(status: 200, body: semver_inlist_matcher_splits) + sleep 1 + subject.block_until_ready + end + + it 'validates the treatment is ON for correct attribute value' do + expect(subject.get_treatment(user, 'semver_inlist', {:version => "1.22.9"})).to eq 'on' + expect(subject.get_treatment(user, 'semver_inlist', {:version => "2.1.0"})).to eq 'on' + end + + it 'validates the treatment is the default treatment for incorrect attributes hash and nil' do + expect(subject.get_treatment(user, 'semver_inlist')).to eq 'off' + expect(subject.get_treatment(user, 'semver_inlist', {:version => "1.22.10"})).to eq 'off' + subject.destroy() + end + end + + context 'between matcher' do + before do + stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?since/) + .to_return(status: 200, body: semver_between_matcher_splits) + stub_request(:get, 'https://sdk.split.io/api/splitChanges?since=-1') + .to_return(status: 200, body: semver_between_matcher_splits) + sleep 1 + subject.block_until_ready + end + + it 'validates the treatment is ON for correct attribute value' do + expect(subject.get_treatment(user, 'semver_between', {:version => "1.22.9"})).to eq 'on' + expect(subject.get_treatment(user, 'semver_between', {:version => "2.0.10"})).to eq 'on' + end + + it 'validates the treatment is the default treatment for incorrect attributes hash and nil' do + expect(subject.get_treatment(user, 'semver_between')).to eq 'off' + expect(subject.get_treatment(user, 'semver_between', {:version => "1.22.9-rc1"})).to eq 'off' + expect(subject.get_treatment(user, 'semver_between', {:version => "2.1.1"})).to eq 'off' + subject.destroy() + end + end +end diff --git a/spec/test_data/splits/semver_matchers/semver_between.json b/spec/test_data/splits/semver_matchers/semver_between.json new file mode 100644 index 00000000..44edc2b6 --- /dev/null +++ b/spec/test_data/splits/semver_matchers/semver_between.json @@ -0,0 +1,86 @@ +{ + "splits": [ + { + "trafficTypeName": "user", + "name": "semver_between", + "trafficAllocation": 100, + "trafficAllocationSeed": 1068038034, + "seed": -1053389887, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1675259356568, + "algo": 2, + "configurations": {}, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": + [ + {"keySelector": {"trafficType": "user", "attribute": "version"}, + "matcherType": "BETWEEN_SEMVER", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "dependencyMatcherData": null, + "booleanMatcherData": null, + "stringMatcherData": null, + "betweenStringMatcherData": {"start": "1.22.9", "end": "2.1.0"}} + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + } + ], + "label": "between semver" + }, + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 + } + ], + "label": "default rule" + } + ] + } + ], + "since": -1, + "till": 1675259356568 +} diff --git a/spec/test_data/splits/semver_matchers/semver_equalto.json b/spec/test_data/splits/semver_matchers/semver_equalto.json new file mode 100644 index 00000000..c3daa9ea --- /dev/null +++ b/spec/test_data/splits/semver_matchers/semver_equalto.json @@ -0,0 +1,85 @@ +{ + "splits": [ + { + "trafficTypeName": "user", + "name": "semver_equalto", + "trafficAllocation": 100, + "trafficAllocationSeed": 1068038034, + "seed": -1053389887, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1675259356568, + "algo": 2, + "configurations": {}, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": + [ + {"keySelector": {"trafficType": "user", "attribute": "version"}, + "matcherType": "EQUAL_TO_SEMVER", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "dependencyMatcherData": null, + "booleanMatcherData": null, + "stringMatcherData": "1.22.9"} + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + } + ], + "label": "equal to semver" + }, + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 + } + ], + "label": "default rule" + } + ] + } + ], + "since": -1, + "till": 1675259356568 +} diff --git a/spec/test_data/splits/semver_matchers/semver_greater_or_equalto.json b/spec/test_data/splits/semver_matchers/semver_greater_or_equalto.json new file mode 100644 index 00000000..40f0f036 --- /dev/null +++ b/spec/test_data/splits/semver_matchers/semver_greater_or_equalto.json @@ -0,0 +1,85 @@ +{ + "splits": [ + { + "trafficTypeName": "user", + "name": "semver_greater_or_equalto", + "trafficAllocation": 100, + "trafficAllocationSeed": 1068038034, + "seed": -1053389887, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1675259356568, + "algo": 2, + "configurations": {}, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": + [ + {"keySelector": {"trafficType": "user", "attribute": "version"}, + "matcherType": "GREATER_THAN_OR_EQUAL_TO_SEMVER", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "dependencyMatcherData": null, + "booleanMatcherData": null, + "stringMatcherData": "1.22.9"} + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + } + ], + "label": "greater than or equal to semver" + }, + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 + } + ], + "label": "default rule" + } + ] + } + ], + "since": -1, + "till": 1675259356568 +} diff --git a/spec/test_data/splits/semver_matchers/semver_inlist.json b/spec/test_data/splits/semver_matchers/semver_inlist.json new file mode 100644 index 00000000..9f1e6246 --- /dev/null +++ b/spec/test_data/splits/semver_matchers/semver_inlist.json @@ -0,0 +1,86 @@ +{ + "splits": [ + { + "trafficTypeName": "user", + "name": "semver_inlist", + "trafficAllocation": 100, + "trafficAllocationSeed": 1068038034, + "seed": -1053389887, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1675259356568, + "algo": 2, + "configurations": {}, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": + [ + {"keySelector": {"trafficType": "user", "attribute": "version"}, + "matcherType": "IN_LIST_SEMVER", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": {"whitelist": ["1.22.9", "2.1.0"]}, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "dependencyMatcherData": null, + "booleanMatcherData": null, + "stringMatcherData": null, + "betweenStringMatcherData": null} + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + } + ], + "label": "between semver" + }, + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 + } + ], + "label": "default rule" + } + ] + } + ], + "since": -1, + "till": 1675259356568 +} diff --git a/spec/test_data/splits/semver_matchers/semver_less_or_equalto.json b/spec/test_data/splits/semver_matchers/semver_less_or_equalto.json new file mode 100644 index 00000000..9a46807f --- /dev/null +++ b/spec/test_data/splits/semver_matchers/semver_less_or_equalto.json @@ -0,0 +1,85 @@ +{ + "splits": [ + { + "trafficTypeName": "user", + "name": "semver_less_or_equalto", + "trafficAllocation": 100, + "trafficAllocationSeed": 1068038034, + "seed": -1053389887, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1675259356568, + "algo": 2, + "configurations": {}, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": + [ + {"keySelector": {"trafficType": "user", "attribute": "version"}, + "matcherType": "LESS_THAN_OR_EQUAL_TO_SEMVER", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "dependencyMatcherData": null, + "booleanMatcherData": null, + "stringMatcherData": "1.22.9"} + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + } + ], + "label": "less than or equal to semver" + }, + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 + } + ], + "label": "default rule" + } + ] + } + ], + "since": -1, + "till": 1675259356568 +} From 8a1d214778676961bdc0efde0a425dfe2d9bf0ca Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Mon, 8 Apr 2024 13:57:33 -0700 Subject: [PATCH 16/37] Used csv for spec and added builder for semver class --- lib/splitclient-rb/engine/matchers/semver.rb | 17 +++- spec/engine/matchers/semver_spec.rb | 99 +++++++------------- 2 files changed, 46 insertions(+), 70 deletions(-) diff --git a/lib/splitclient-rb/engine/matchers/semver.rb b/lib/splitclient-rb/engine/matchers/semver.rb index ac9b7a58..92d9a6a8 100644 --- a/lib/splitclient-rb/engine/matchers/semver.rb +++ b/lib/splitclient-rb/engine/matchers/semver.rb @@ -8,10 +8,6 @@ class Semver attr_reader :major, :minor, :patch, :pre_release, :is_stable, :old_version - # - # Class Initializer - # - # @param version [String] raw version as read from splitChanges response. def initialize(version) @major = 0 @minor = 0 @@ -22,6 +18,19 @@ def initialize(version) parse end + # + # Class builder + # + # @param version [String] raw version as read from splitChanges response. + # + # @return [type] Semver instance + def self.build(version, logger) + new(version) + rescue RuntimeError => e + logger.warn("Failed to parse Semver data: #{e}") + nil + end + # # Check if there is any metadata characters in self._old_version. # diff --git a/spec/engine/matchers/semver_spec.rb b/spec/engine/matchers/semver_spec.rb index a0f98429..7b90b2ec 100644 --- a/spec/engine/matchers/semver_spec.rb +++ b/spec/engine/matchers/semver_spec.rb @@ -1,89 +1,56 @@ require 'spec_helper' +require 'csv' describe SplitIoClient::Semver do - let(:valid_versions) { ["1.1.2", "1.1.1", "1.0.0", "1.0.0-rc.1", "1.0.0-beta.11", "1.0.0-beta.2", - "1.0.0-beta", "1.0.0-alpha.beta", "1.0.0-alpha.1", "1.0.0-alpha", "2.2.2-rc.2+metadata-lalala", "2.2.2-rc.1.2", - "1.2.3", "0.0.4", "1.1.2+meta", "1.1.2-prerelease+meta", "1.0.0-beta", "1.0.0-alpha", "1.0.0-alpha0.valid", - "1.0.0-alpha.0valid", "1.0.0-rc.1+build.1", "1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay", - "10.2.3-DEV-SNAPSHOT", "1.2.3-SNAPSHOT-123", "1.1.1-rc2", "1.0.0-0A.is.legal", "1.2.3----RC-SNAPSHOT.12.9.1--.12+788", - "1.2.3----R-S.12.9.1--.12+meta", "1.2.3----RC-SNAPSHOT.12.9.1--.12.88", "1.2.3----RC-SNAPSHOT.12.9.1--.12", - "9223372036854775807.9223372036854775807.9223372036854775807", "9223372036854775807.9223372036854775807.9223372036854775806", - "1.1.1-alpha.beta.rc.build.java.pr.support.10", "1.1.1-alpha.beta.rc.build.java.pr.support"] } + let(:valid_versions) do + CSV.parse(File.read(File.expand_path(File.join(File.dirname(__FILE__), + '../../test_data/splits/semver/valid-semantic-versions.csv')))) + end + let(:invalid_versions) do + CSV.parse(File.read(File.expand_path(File.join(File.dirname(__FILE__), + '../../test_data/splits/semver/invalid-semantic-versions.csv')))) + end + let(:equal_to_versions) do + CSV.parse(File.read(File.expand_path(File.join(File.dirname(__FILE__), + '../../test_data/splits/semver/equal-to-semver.csv')))) + end + + let(:logger) { Logger.new('/dev/null') } context 'check versions' do it 'accept valid versions' do - major = [1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 2, 2, - 1, 0, 1, 1, 1, 1, 1, - 1, 1, 1, - 10, 1, 1, 1, 1, - 1, 1, 1, - 9223372036854775807, 9223372036854775807, - 1,1] - minor = [1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 2, 2, - 2, 0, 1, 1, 0, 0, 0, - 0, 0, 0, - 2, 2, 1, 0, 2, - 2, 2, 2, - 9223372036854775807, 9223372036854775807, - 1, 1] - patch = [2, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 2, 2, - 3, 4, 2, 2, 0, 0, 0, - 0, 0, 0, - 3, 3, 1, 0, 3, - 3, 3, 3, - 9223372036854775807, 9223372036854775806, - 1, 1] - pre_release = [[], [], [], ["rc","1"], ["beta","11"],["beta","2"], - ["beta"], ["alpha","beta"], ["alpha","1"], ["alpha"], ["rc","2"], ["rc","1","2"], - [], [], [], ["prerelease"], ["beta"], ["alpha"], ["alpha0","valid"], - ["alpha","0valid"], ["rc","1"], ["alpha-a","b-c-somethinglong"], - ["DEV-SNAPSHOT"], ["SNAPSHOT-123"], ["rc2"], ["0A","is","legal"], ["---RC-SNAPSHOT","12","9","1--","12"], - ["---R-S","12","9","1--","12"], ["---RC-SNAPSHOT","12","9","1--","12","88"], ["---RC-SNAPSHOT","12","9","1--","12"], - [], [], - ["alpha","beta","rc","build","java","pr","support","10"], ["alpha","beta","rc","build","java","pr","support"]] - - for i in (0..major.length-1) - semver = described_class.new(valid_versions[i]) - expect(verify_version(semver, major[i], minor[i], patch[i], pre_release[i], pre_release[i]==[])).to eq(true) + for i in (0..valid_versions.length-1) + expect(described_class.build(valid_versions[i][0], logger)).should_not be_nil end end it 'reject invalid versions' do - invalid_versions = [ - "1", "1.2", "1.alpha.2", "+invalid", "-invalid", "-invalid+invalid", "+justmeta", - "-invalid.01", "alpha", "alpha.beta", "alpha.beta.1", "alpha.1", "alpha+beta", - "alpha_beta", "alpha.", "alpha..", "beta", "-alpha.", "1.2", "1.2.3.DEV", "-1.0.3-gamma+b7718", - "1.2-SNAPSHOT", "1.2.31.2.3----RC-SNAPSHOT.12.09.1--..12+788", "1.2-RC-SNAPSHOT"] - for version in invalid_versions - expect{ described_class.new(version) }.to raise_error(RuntimeError) + expect(described_class.build(version[0], logger)).to eq(nil) end end end context 'compare versions' do - it 'higher, lower and equal' do - cnt = 0 - for i in (0..(valid_versions.length/2).to_i-1) - expect(described_class.new(valid_versions[cnt]).compare(described_class.new(valid_versions[cnt+1]))).to eq(1) - expect(described_class.new(valid_versions[cnt+1]).compare(described_class.new(valid_versions[cnt]))).to eq(-1) - expect(described_class.new(valid_versions[cnt]).compare(described_class.new(valid_versions[cnt]))).to eq(0) - expect(described_class.new(valid_versions[cnt+1]).compare(described_class.new(valid_versions[cnt+1]))).to eq(0) - cnt = cnt + 2 + it 'equal and not equal' do + for i in (1..valid_versions.length-1) + expect(described_class.build(valid_versions[i][0], logger).compare(described_class.build(valid_versions[i][1], logger))).to eq(1) + expect(described_class.build(valid_versions[i][1], logger).compare(described_class.build(valid_versions[i][0], logger))).to eq(-1) + expect(described_class.build(valid_versions[i][0], logger).compare(described_class.build(valid_versions[i][0], logger))).to eq(0) + expect(described_class.build(valid_versions[i][1], logger).compare(described_class.build(valid_versions[i][1], logger))).to eq(0) end - expect(described_class.new("1.1.1").compare(described_class.new("1.1.1"))).to eq(0) - expect(described_class.new("1.1.1").compare(described_class.new("1.1.1+metadata"))).to eq(0) - expect(described_class.new("1.1.1").compare(described_class.new("1.1.1-rc.1"))).to eq(1) - expect(described_class.new("88.88.88").compare(described_class.new("88.88.88"))).to eq(0) - expect(described_class.new("1.2.3----RC-SNAPSHOT.12.9.1--.12").compare(described_class.new("1.2.3----RC-SNAPSHOT.12.9.1--.12"))).to eq(0) - expect(described_class.new("10.2.3-DEV-SNAPSHOT").compare(described_class.new("10.2.3-SNAPSHOT-123"))).to eq(-1) + for i in (1..equal_to_versions.length-1) + if valid_versions[i][2] + expect(described_class.build(valid_versions[i][0], logger).compare(described_class.build(valid_versions[i][1], logger))).to eq(0) + else + expect(described_class.build(valid_versions[i][0], logger).compare(described_class.build(valid_versions[i][1], logger))).not_to eq(0) + end + end + end end def verify_version(semver, major, minor, patch, pre_release="", is_stable=True) - if semver.major == major && semver.minor == minor &&d semver.patch == patch && + if semver.major == major && semver.minor == minor && semver.patch == patch && semver.pre_release == pre_release && semver.is_stable == is_stable return true end From f12ee2a583ea19d4f884c05e671f018291b5030c Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Mon, 8 Apr 2024 13:58:54 -0700 Subject: [PATCH 17/37] added csv files --- .../splits/semver/between-semver.csv | 18 +++++++++++++ .../splits/semver/equal-to-semver.csv | 7 +++++ .../semver/invalid-semantic-versions.csv | 26 +++++++++++++++++++ .../splits/semver/valid-semantic-versions.csv | 25 ++++++++++++++++++ 4 files changed, 76 insertions(+) create mode 100644 spec/test_data/splits/semver/between-semver.csv create mode 100644 spec/test_data/splits/semver/equal-to-semver.csv create mode 100644 spec/test_data/splits/semver/invalid-semantic-versions.csv create mode 100644 spec/test_data/splits/semver/valid-semantic-versions.csv diff --git a/spec/test_data/splits/semver/between-semver.csv b/spec/test_data/splits/semver/between-semver.csv new file mode 100644 index 00000000..4225e710 --- /dev/null +++ b/spec/test_data/splits/semver/between-semver.csv @@ -0,0 +1,18 @@ +# version1, version2, version3, expected +1.1.1,2.2.2,3.3.3,true +1.1.1-rc.1,1.1.1-rc.2,1.1.1-rc.3,true +1.0.0-alpha,1.0.0-alpha.1,1.0.0-alpha.beta,true +1.0.0-alpha.1,1.0.0-alpha.beta,1.0.0-beta,true +1.0.0-alpha.beta,1.0.0-beta,1.0.0-beta.2,true +1.0.0-beta,1.0.0-beta.2,1.0.0-beta.11,true +1.0.0-beta.2,1.0.0-beta.11,1.0.0-rc.1,true +1.0.0-beta.11,1.0.0-rc.1,1.0.0,true +1.1.2,1.1.3,1.1.4,true +1.2.1,1.3.1,1.4.1,true +2.0.0,3.0.0,4.0.0,true +2.2.2,2.2.3-rc1,2.2.3,true +2.2.2,2.3.2-rc100,2.3.3,true +1.0.0-rc.1+build.1,1.2.3-beta,1.2.3-rc.1+build.123,true +3.3.3,3.3.3-alpha,3.3.4,false +2.2.2-rc.1,2.2.2+metadata,2.2.2-rc.10,false +1.1.1-rc.1,1.1.1-rc.3,1.1.1-rc.2,false \ No newline at end of file diff --git a/spec/test_data/splits/semver/equal-to-semver.csv b/spec/test_data/splits/semver/equal-to-semver.csv new file mode 100644 index 00000000..4ac0b7c6 --- /dev/null +++ b/spec/test_data/splits/semver/equal-to-semver.csv @@ -0,0 +1,7 @@ +# version1, version2, equals +1.1.1,1.1.1,true +1.1.1,1.1.1+metadata,false +1.1.1,1.1.1-rc.1,false +88.88.88,88.88.88,true +1.2.3----RC-SNAPSHOT.12.9.1--.12,1.2.3----RC-SNAPSHOT.12.9.1--.12,true +10.2.3-DEV-SNAPSHOT,10.2.3-SNAPSHOT-123,false \ No newline at end of file diff --git a/spec/test_data/splits/semver/invalid-semantic-versions.csv b/spec/test_data/splits/semver/invalid-semantic-versions.csv new file mode 100644 index 00000000..b9eb3e50 --- /dev/null +++ b/spec/test_data/splits/semver/invalid-semantic-versions.csv @@ -0,0 +1,26 @@ +# invalid +1 +1.2 +1.alpha.2 ++invalid +-invalid +-invalid+invalid +-invalid.01 +alpha +alpha.beta +alpha.beta.1 +alpha.1 +alpha+beta +alpha_beta +alpha. +alpha.. +beta +-alpha. +1.2 +1.2.3.DEV +1.2-SNAPSHOT +1.2.31.2.3----RC-SNAPSHOT.12.09.1--..12+788 +1.2-RC-SNAPSHOT +-1.0.3-gamma+b7718 ++justmeta +#99999999999999999999999.999999999999999999.99999999999999999----RC-SNAPSHOT.12.09.1--------------------------------..12 \ No newline at end of file diff --git a/spec/test_data/splits/semver/valid-semantic-versions.csv b/spec/test_data/splits/semver/valid-semantic-versions.csv new file mode 100644 index 00000000..da85709c --- /dev/null +++ b/spec/test_data/splits/semver/valid-semantic-versions.csv @@ -0,0 +1,25 @@ +# higher, lower +1.1.2,1.1.1 +1.0.0,1.0.0-rc.1 +1.1.0-rc.1,1.0.0-beta.11 +1.0.0-beta.11,1.0.0-beta.2 +1.0.0-beta.2,1.0.0-beta +1.0.0-beta,1.0.0-alpha.beta +1.0.0-alpha.beta,1.0.0-alpha.1 +1.0.0-alpha.1,1.0.0-alpha +2.2.2-rc.2+metadata-lalala,2.2.2-rc.1.2 +1.2.3,0.0.4 +1.1.2+meta,1.1.2-prerelease+meta +1.0.0-beta,1.0.0-alpha +1.0.0-alpha0.valid,1.0.0-alpha.0valid +1.0.0-rc.1+build.1,1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay +10.2.3-DEV-SNAPSHOT,1.2.3-SNAPSHOT-123 +1.1.1-rc2,1.0.0-0A.is.legal +1.2.3----RC-SNAPSHOT.12.9.1--.12+788,1.2.3----R-S.12.9.1--.12+meta +1.2.3----RC-SNAPSHOT.12.9.1--.12.88,1.2.3----RC-SNAPSHOT.12.9.1--.12 +9223372036854775807.9223372036854775807.9223372036854775807,9223372036854775807.9223372036854775807.9223372036854775806 +1.1.1-alpha.beta.rc.build.java.pr.support.10,1.1.1-alpha.beta.rc.build.java.pr.support +1.1.2,1.1.1 +1.2.1,1.1.1 +2.1.1,1.1.1 +1.1.1-rc.1,1.1.1-rc.0 \ No newline at end of file From acad11353b99d70659650f4301c458e9e7ea91e2 Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Mon, 8 Apr 2024 19:00:53 -0700 Subject: [PATCH 18/37] updated using build for semver class --- .../engine/matchers/equal_to_semver_matcher.rb | 10 +++++----- lib/splitclient-rb/engine/matchers/semver.rb | 17 +++++++++++++---- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb index 01f62baf..30e76311 100644 --- a/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb @@ -10,20 +10,20 @@ def initialize(attribute, string_value, logger, validator) super(logger) @validator = validator @attribute = attribute - @semver = SplitIoClient::Semver.new(string_value) + @semver = SplitIoClient::Semver.build(string_value, logger) @logger = logger end def match?(args) @logger.log_if_debug('[EqualsToSemverMatcher] evaluating value and attributes.') - return false unless @validator.valid_matcher_arguments(args) + return false unless @validator.valid_matcher_arguments(args) && args[:attributes][@attribute.to_sym].is_a?(String) - value_to_match = args[:attributes][@attribute.to_sym] - unless value_to_match.is_a?(String) + value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) + unless !value_to_match.nil? && !@semver.nil? @logger.log_if_debug('stringMatcherData is required for EQUAL_TO_SEMVER matcher type') return false end - matches = @semver.compare(SplitIoClient::Semver.new(value_to_match)).zero? + matches = @semver.compare(value_to_match).zero? @logger.log_if_debug("[EqualsToSemverMatcher] #{value_to_match} matches -> #{matches}") matches end diff --git a/lib/splitclient-rb/engine/matchers/semver.rb b/lib/splitclient-rb/engine/matchers/semver.rb index ac9b7a58..92d9a6a8 100644 --- a/lib/splitclient-rb/engine/matchers/semver.rb +++ b/lib/splitclient-rb/engine/matchers/semver.rb @@ -8,10 +8,6 @@ class Semver attr_reader :major, :minor, :patch, :pre_release, :is_stable, :old_version - # - # Class Initializer - # - # @param version [String] raw version as read from splitChanges response. def initialize(version) @major = 0 @minor = 0 @@ -22,6 +18,19 @@ def initialize(version) parse end + # + # Class builder + # + # @param version [String] raw version as read from splitChanges response. + # + # @return [type] Semver instance + def self.build(version, logger) + new(version) + rescue RuntimeError => e + logger.warn("Failed to parse Semver data: #{e}") + nil + end + # # Check if there is any metadata characters in self._old_version. # From 1e4a23822cde2b9cd8adf3f6ee1cdc7a6f9b4ec8 Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Mon, 8 Apr 2024 19:09:35 -0700 Subject: [PATCH 19/37] used build for semver class --- .../less_than_or_equal_to_semver_matcher.rb | 10 +++++----- lib/splitclient-rb/engine/matchers/semver.rb | 17 +++++++++++++---- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb index 95c55f73..4acf405b 100644 --- a/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb @@ -10,20 +10,20 @@ def initialize(attribute, string_value, logger, validator) super(logger) @validator = validator @attribute = attribute - @semver = SplitIoClient::Semver.new(string_value) + @semver = SplitIoClient::Semver.build(string_value, logger) @logger = logger end def match?(args) @logger.log_if_debug('[LessThanOrEqualsToSemverMatcher] evaluating value and attributes.') - return false unless @validator.valid_matcher_arguments(args) + return false unless @validator.valid_matcher_arguments(args) && args[:attributes][@attribute.to_sym].is_a?(String) - value_to_match = args[:attributes][@attribute.to_sym] - unless value_to_match.is_a?(String) + value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) + unless !value_to_match.nil? && !@semver.nil? @logger.log_if_debug('stringMatcherData is required for LESS_THAN_OR_EQUAL_TO_SEMVER matcher type') return false end - matches = [0, -1].include?(SplitIoClient::Semver.new(value_to_match).compare(@semver)) + matches = [0, -1].include?(value_to_match.compare(@semver)) @logger.log_if_debug("[LessThanOrEqualsToSemverMatcher] #{value_to_match} matches -> #{matches}") matches end diff --git a/lib/splitclient-rb/engine/matchers/semver.rb b/lib/splitclient-rb/engine/matchers/semver.rb index ac9b7a58..92d9a6a8 100644 --- a/lib/splitclient-rb/engine/matchers/semver.rb +++ b/lib/splitclient-rb/engine/matchers/semver.rb @@ -8,10 +8,6 @@ class Semver attr_reader :major, :minor, :patch, :pre_release, :is_stable, :old_version - # - # Class Initializer - # - # @param version [String] raw version as read from splitChanges response. def initialize(version) @major = 0 @minor = 0 @@ -22,6 +18,19 @@ def initialize(version) parse end + # + # Class builder + # + # @param version [String] raw version as read from splitChanges response. + # + # @return [type] Semver instance + def self.build(version, logger) + new(version) + rescue RuntimeError => e + logger.warn("Failed to parse Semver data: #{e}") + nil + end + # # Check if there is any metadata characters in self._old_version. # From 8bf838b9c91242addf90b25c5a4319b592bc96a4 Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Mon, 8 Apr 2024 19:15:17 -0700 Subject: [PATCH 20/37] used build for semver class --- .../engine/matchers/between_semver_matcher.rb | 14 +++++++------- lib/splitclient-rb/engine/matchers/semver.rb | 17 +++++++++++++---- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb index 282dcf5c..a38a2801 100644 --- a/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb @@ -10,22 +10,22 @@ def initialize(attribute, start_value, end_value, logger, validator) super(logger) @validator = validator @attribute = attribute - @semver_start = SplitIoClient::Semver.new(start_value) - @semver_end = SplitIoClient::Semver.new(end_value) + @semver_start = SplitIoClient::Semver.build(start_value, logger) + @semver_end = SplitIoClient::Semver.build(end_value, logger) @logger = logger end def match?(args) @logger.log_if_debug('[BetweenSemverMatcher] evaluating value and attributes.') - return false unless @validator.valid_matcher_arguments(args) + return false unless @validator.valid_matcher_arguments(args) && args[:attributes][@attribute.to_sym].is_a?(String) - value_to_match = args[:attributes][@attribute.to_sym] - unless value_to_match.is_a?(String) + value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) + unless !value_to_match.nil? && !@semver_start.nil? && !@semver_end.nil? @logger.log_if_debug('betweenStringMatcherData is required for BETWEEN_SEMVER matcher type') return false end - matches = ([0, -1].include?(@semver_start.compare(SplitIoClient::Semver.new(value_to_match))) && - [0, 1].include?(@semver_end.compare(SplitIoClient::Semver.new(value_to_match)))) + matches = ([0, -1].include?(@semver_start.compare(value_to_match)) && + [0, 1].include?(@semver_end.compare(value_to_match))) @logger.log_if_debug("[BetweenMatcher] #{value_to_match} matches -> #{matches}") matches end diff --git a/lib/splitclient-rb/engine/matchers/semver.rb b/lib/splitclient-rb/engine/matchers/semver.rb index ac9b7a58..92d9a6a8 100644 --- a/lib/splitclient-rb/engine/matchers/semver.rb +++ b/lib/splitclient-rb/engine/matchers/semver.rb @@ -8,10 +8,6 @@ class Semver attr_reader :major, :minor, :patch, :pre_release, :is_stable, :old_version - # - # Class Initializer - # - # @param version [String] raw version as read from splitChanges response. def initialize(version) @major = 0 @minor = 0 @@ -22,6 +18,19 @@ def initialize(version) parse end + # + # Class builder + # + # @param version [String] raw version as read from splitChanges response. + # + # @return [type] Semver instance + def self.build(version, logger) + new(version) + rescue RuntimeError => e + logger.warn("Failed to parse Semver data: #{e}") + nil + end + # # Check if there is any metadata characters in self._old_version. # From cf60f11b11616eb1f9d6622d8e8e5b0e928ec7e7 Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Mon, 8 Apr 2024 19:25:03 -0700 Subject: [PATCH 21/37] used build for semver class --- .../engine/matchers/in_list_semver_matcher.rb | 10 +++++----- lib/splitclient-rb/engine/matchers/semver.rb | 17 +++++++++++++---- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb index 7a424483..9a3ae614 100644 --- a/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb @@ -10,20 +10,20 @@ def initialize(attribute, list_value, logger, validator) super(logger) @validator = validator @attribute = attribute - @semver_list = list_value.map { |item| SplitIoClient::Semver.new(item) } + @semver_list = list_value.map { |item| SplitIoClient::Semver.build(item, logger) } @logger = logger end def match?(args) @logger.log_if_debug('[InListSemverMatcher] evaluating value and attributes.') - return false unless @validator.valid_matcher_arguments(args) + return false unless @validator.valid_matcher_arguments(args) && args[:attributes][@attribute.to_sym].is_a?(String) - value_to_match = args[:attributes][@attribute.to_sym] - unless value_to_match.is_a?(String) + value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) + unless !value_to_match.nil? && @semver_list.all? { |n| !n.nil? } @logger.log_if_debug('whitelistMatcherData is required for IN_LIST_SEMVER matcher type') return false end - matches = (@semver_list.map { |item| item.compare(SplitIoClient::Semver.new(value_to_match)) }).any?(&:zero?) + matches = (@semver_list.map { |item| item.compare(value_to_match) }).any?(&:zero?) @logger.log_if_debug("[InListSemverMatcher] #{value_to_match} matches -> #{matches}") matches end diff --git a/lib/splitclient-rb/engine/matchers/semver.rb b/lib/splitclient-rb/engine/matchers/semver.rb index ac9b7a58..92d9a6a8 100644 --- a/lib/splitclient-rb/engine/matchers/semver.rb +++ b/lib/splitclient-rb/engine/matchers/semver.rb @@ -8,10 +8,6 @@ class Semver attr_reader :major, :minor, :patch, :pre_release, :is_stable, :old_version - # - # Class Initializer - # - # @param version [String] raw version as read from splitChanges response. def initialize(version) @major = 0 @minor = 0 @@ -22,6 +18,19 @@ def initialize(version) parse end + # + # Class builder + # + # @param version [String] raw version as read from splitChanges response. + # + # @return [type] Semver instance + def self.build(version, logger) + new(version) + rescue RuntimeError => e + logger.warn("Failed to parse Semver data: #{e}") + nil + end + # # Check if there is any metadata characters in self._old_version. # From 61b8c9e2372c83b4b8fbaf9536423ea4d1046e34 Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Mon, 8 Apr 2024 19:58:23 -0700 Subject: [PATCH 22/37] fixed condition --- .../greater_than_or_equal_to_semver_matcher.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb index c67fe622..d12fdb55 100644 --- a/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb @@ -10,20 +10,20 @@ def initialize(attribute, string_value, logger, validator) super(logger) @validator = validator @attribute = attribute - @semver = SplitIoClient::Semver.new(string_value) + @semver = SplitIoClient::Semver.build(string_value, logger) @logger = logger end def match?(args) @logger.log_if_debug('[GreaterThanOrEqualsToSemverMatcher] evaluating value and attributes.') - return false unless @validator.valid_matcher_arguments(args) + return false unless @validator.valid_matcher_arguments(args) && args[:attributes][@attribute.to_sym].is_a?(String) - value_to_match = args[:attributes][@attribute.to_sym] - unless value_to_match.is_a?(String) + value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) + unless !value_to_match.nil? && !@semver.nil? @logger.log_if_debug('stringMatcherData is required for GREATER_THAN_OR_EQUAL_TO_SEMVER matcher type') return false end - matches = [0, 1].include?(SplitIoClient::Semver.new(value_to_match).compare(@semver)) + matches = [0, 1].include?(value_to_match.compare(@semver)) @logger.log_if_debug("[GreaterThanOrEqualsToSemverMatcher] #{value_to_match} matches -> #{matches}") matches end From 980c272fd5e3f764e02e69bf6c95010e152c32b9 Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Wed, 10 Apr 2024 09:38:46 -0700 Subject: [PATCH 23/37] added version attribute and used it in compare --- lib/splitclient-rb/engine/matchers/semver.rb | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/splitclient-rb/engine/matchers/semver.rb b/lib/splitclient-rb/engine/matchers/semver.rb index 92d9a6a8..2dec6a71 100644 --- a/lib/splitclient-rb/engine/matchers/semver.rb +++ b/lib/splitclient-rb/engine/matchers/semver.rb @@ -6,7 +6,7 @@ class Semver PRE_RELEASE_DELIMITER = '-' VALUE_DELIMITER = '.' - attr_reader :major, :minor, :patch, :pre_release, :is_stable, :old_version + attr_reader :major, :minor, :patch, :pre_release, :is_stable, :old_version, :version def initialize(version) @major = 0 @@ -15,6 +15,8 @@ def initialize(version) @pre_release = [] @is_stable = false @old_version = version + @version = "" + @metadata = "" parse end @@ -39,7 +41,7 @@ def self.build(version, logger) def remove_metadata_if_exists index = @old_version.index(METADATA_DELIMITER) return @old_version if index.nil? - + @metadata = @old_version[index+1,@old_version.length] @old_version[0, index] end @@ -52,7 +54,7 @@ def remove_metadata_if_exists # # @returns [Integer] based on comparison def compare(to_compare) - return 0 if @old_version == to_compare.old_version + return 0 if @version == to_compare.version # Compare major, minor, and patch versions numerically return compare_attributes(to_compare) if compare_attributes(to_compare) != 0 @@ -100,6 +102,9 @@ def assign_major_minor_and_patch(version) @major = parts[0].to_i @minor = parts[1].to_i @patch = parts[2].to_i + @version = "#{@major}#{VALUE_DELIMITER}#{@minor}#{VALUE_DELIMITER}#{@patch}" + @version += "#{PRE_RELEASE_DELIMITER}#{@pre_release.join('.')}" if !@pre_release.empty? + @version += "#{METADATA_DELIMITER}#{@metadata}" if !@metadata.empty? end # From b9e8fd8282d6ced943f1ded164dfd25b8ab37f85 Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Wed, 10 Apr 2024 09:50:44 -0700 Subject: [PATCH 24/37] updated semver spec --- spec/engine/matchers/semver_spec.rb | 103 +++++++++------------------- 1 file changed, 31 insertions(+), 72 deletions(-) diff --git a/spec/engine/matchers/semver_spec.rb b/spec/engine/matchers/semver_spec.rb index a0f98429..fbb705d5 100644 --- a/spec/engine/matchers/semver_spec.rb +++ b/spec/engine/matchers/semver_spec.rb @@ -1,92 +1,51 @@ require 'spec_helper' +require 'csv' describe SplitIoClient::Semver do - let(:valid_versions) { ["1.1.2", "1.1.1", "1.0.0", "1.0.0-rc.1", "1.0.0-beta.11", "1.0.0-beta.2", - "1.0.0-beta", "1.0.0-alpha.beta", "1.0.0-alpha.1", "1.0.0-alpha", "2.2.2-rc.2+metadata-lalala", "2.2.2-rc.1.2", - "1.2.3", "0.0.4", "1.1.2+meta", "1.1.2-prerelease+meta", "1.0.0-beta", "1.0.0-alpha", "1.0.0-alpha0.valid", - "1.0.0-alpha.0valid", "1.0.0-rc.1+build.1", "1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay", - "10.2.3-DEV-SNAPSHOT", "1.2.3-SNAPSHOT-123", "1.1.1-rc2", "1.0.0-0A.is.legal", "1.2.3----RC-SNAPSHOT.12.9.1--.12+788", - "1.2.3----R-S.12.9.1--.12+meta", "1.2.3----RC-SNAPSHOT.12.9.1--.12.88", "1.2.3----RC-SNAPSHOT.12.9.1--.12", - "9223372036854775807.9223372036854775807.9223372036854775807", "9223372036854775807.9223372036854775807.9223372036854775806", - "1.1.1-alpha.beta.rc.build.java.pr.support.10", "1.1.1-alpha.beta.rc.build.java.pr.support"] } + let(:valid_versions) do + CSV.parse(File.read(File.expand_path(File.join(File.dirname(__FILE__), + '../../test_data/splits/semver/valid-semantic-versions.csv')))) + end + let(:invalid_versions) do + CSV.parse(File.read(File.expand_path(File.join(File.dirname(__FILE__), + '../../test_data/splits/semver/invalid-semantic-versions.csv')))) + end + let(:equal_to_versions) do + CSV.parse(File.read(File.expand_path(File.join(File.dirname(__FILE__), + '../../test_data/splits/semver/equal-to-semver.csv')))) + end + + let(:logger) { Logger.new('/dev/null') } context 'check versions' do it 'accept valid versions' do - major = [1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 2, 2, - 1, 0, 1, 1, 1, 1, 1, - 1, 1, 1, - 10, 1, 1, 1, 1, - 1, 1, 1, - 9223372036854775807, 9223372036854775807, - 1,1] - minor = [1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 2, 2, - 2, 0, 1, 1, 0, 0, 0, - 0, 0, 0, - 2, 2, 1, 0, 2, - 2, 2, 2, - 9223372036854775807, 9223372036854775807, - 1, 1] - patch = [2, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 2, 2, - 3, 4, 2, 2, 0, 0, 0, - 0, 0, 0, - 3, 3, 1, 0, 3, - 3, 3, 3, - 9223372036854775807, 9223372036854775806, - 1, 1] - pre_release = [[], [], [], ["rc","1"], ["beta","11"],["beta","2"], - ["beta"], ["alpha","beta"], ["alpha","1"], ["alpha"], ["rc","2"], ["rc","1","2"], - [], [], [], ["prerelease"], ["beta"], ["alpha"], ["alpha0","valid"], - ["alpha","0valid"], ["rc","1"], ["alpha-a","b-c-somethinglong"], - ["DEV-SNAPSHOT"], ["SNAPSHOT-123"], ["rc2"], ["0A","is","legal"], ["---RC-SNAPSHOT","12","9","1--","12"], - ["---R-S","12","9","1--","12"], ["---RC-SNAPSHOT","12","9","1--","12","88"], ["---RC-SNAPSHOT","12","9","1--","12"], - [], [], - ["alpha","beta","rc","build","java","pr","support","10"], ["alpha","beta","rc","build","java","pr","support"]] - - for i in (0..major.length-1) - semver = described_class.new(valid_versions[i]) - expect(verify_version(semver, major[i], minor[i], patch[i], pre_release[i], pre_release[i]==[])).to eq(true) + for i in (0..valid_versions.length-1) + expect(described_class.build(valid_versions[i][0], logger)).should_not be_nil end end it 'reject invalid versions' do - invalid_versions = [ - "1", "1.2", "1.alpha.2", "+invalid", "-invalid", "-invalid+invalid", "+justmeta", - "-invalid.01", "alpha", "alpha.beta", "alpha.beta.1", "alpha.1", "alpha+beta", - "alpha_beta", "alpha.", "alpha..", "beta", "-alpha.", "1.2", "1.2.3.DEV", "-1.0.3-gamma+b7718", - "1.2-SNAPSHOT", "1.2.31.2.3----RC-SNAPSHOT.12.09.1--..12+788", "1.2-RC-SNAPSHOT"] - for version in invalid_versions - expect{ described_class.new(version) }.to raise_error(RuntimeError) + expect(described_class.build(version[0], logger)).to eq(nil) end end end context 'compare versions' do - it 'higher, lower and equal' do - cnt = 0 - for i in (0..(valid_versions.length/2).to_i-1) - expect(described_class.new(valid_versions[cnt]).compare(described_class.new(valid_versions[cnt+1]))).to eq(1) - expect(described_class.new(valid_versions[cnt+1]).compare(described_class.new(valid_versions[cnt]))).to eq(-1) - expect(described_class.new(valid_versions[cnt]).compare(described_class.new(valid_versions[cnt]))).to eq(0) - expect(described_class.new(valid_versions[cnt+1]).compare(described_class.new(valid_versions[cnt+1]))).to eq(0) - cnt = cnt + 2 + it 'equal and not equal' do + for i in (1..valid_versions.length-1) + expect(described_class.build(valid_versions[i][0], logger).compare(described_class.build(valid_versions[i][1], logger))).to eq(1) + expect(described_class.build(valid_versions[i][1], logger).compare(described_class.build(valid_versions[i][0], logger))).to eq(-1) + expect(described_class.build(valid_versions[i][0], logger).compare(described_class.build(valid_versions[i][0], logger))).to eq(0) + expect(described_class.build(valid_versions[i][1], logger).compare(described_class.build(valid_versions[i][1], logger))).to eq(0) + end + for i in (1..equal_to_versions.length-1) + if valid_versions[i][2] + expect(described_class.build(valid_versions[i][0], logger).compare(described_class.build(valid_versions[i][1], logger))).to eq(0) + else + expect(described_class.build(valid_versions[i][0], logger).compare(described_class.build(valid_versions[i][1], logger))).not_to eq(0) + end end - expect(described_class.new("1.1.1").compare(described_class.new("1.1.1"))).to eq(0) - expect(described_class.new("1.1.1").compare(described_class.new("1.1.1+metadata"))).to eq(0) - expect(described_class.new("1.1.1").compare(described_class.new("1.1.1-rc.1"))).to eq(1) - expect(described_class.new("88.88.88").compare(described_class.new("88.88.88"))).to eq(0) - expect(described_class.new("1.2.3----RC-SNAPSHOT.12.9.1--.12").compare(described_class.new("1.2.3----RC-SNAPSHOT.12.9.1--.12"))).to eq(0) - expect(described_class.new("10.2.3-DEV-SNAPSHOT").compare(described_class.new("10.2.3-SNAPSHOT-123"))).to eq(-1) - end - end - def verify_version(semver, major, minor, patch, pre_release="", is_stable=True) - if semver.major == major && semver.minor == minor &&d semver.patch == patch && - semver.pre_release == pre_release && semver.is_stable == is_stable - return true end - return false end end From b1529d2e00057798b920c483871da8225118f3dd Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Wed, 10 Apr 2024 11:06:08 -0700 Subject: [PATCH 25/37] Fixed compare logic in semver, equalto and inlist matchers. --- .../matchers/equal_to_semver_matcher.rb | 2 +- .../engine/matchers/in_list_semver_matcher.rb | 2 +- lib/splitclient-rb/engine/matchers/semver.rb | 32 +++++++++---------- .../matches_between_semver_matcher_spec.rb | 4 +-- .../matches_equal_to_semver_matcher_spec.rb | 4 +-- ...er_than_or_equal_to_semver_matcher_spec.rb | 2 +- .../matches_in_list_semver_matcher_spec.rb | 7 ++-- ...ss_than_or_equal_to_semver_matcher_spec.rb | 2 +- 8 files changed, 28 insertions(+), 27 deletions(-) diff --git a/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb index 01f62baf..877b0f82 100644 --- a/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb @@ -23,7 +23,7 @@ def match?(args) @logger.log_if_debug('stringMatcherData is required for EQUAL_TO_SEMVER matcher type') return false end - matches = @semver.compare(SplitIoClient::Semver.new(value_to_match)).zero? + matches = (@semver.version == SplitIoClient::Semver.new(value_to_match).version) @logger.log_if_debug("[EqualsToSemverMatcher] #{value_to_match} matches -> #{matches}") matches end diff --git a/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb index 9a3ae614..57b13ddd 100644 --- a/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb @@ -23,7 +23,7 @@ def match?(args) @logger.log_if_debug('whitelistMatcherData is required for IN_LIST_SEMVER matcher type') return false end - matches = (@semver_list.map { |item| item.compare(value_to_match) }).any?(&:zero?) + matches = (@semver_list.map { |item| item.version == value_to_match.version }).any? { |item| item == true } @logger.log_if_debug("[InListSemverMatcher] #{value_to_match} matches -> #{matches}") matches end diff --git a/lib/splitclient-rb/engine/matchers/semver.rb b/lib/splitclient-rb/engine/matchers/semver.rb index 2dec6a71..6669b94b 100644 --- a/lib/splitclient-rb/engine/matchers/semver.rb +++ b/lib/splitclient-rb/engine/matchers/semver.rb @@ -6,7 +6,7 @@ class Semver PRE_RELEASE_DELIMITER = '-' VALUE_DELIMITER = '.' - attr_reader :major, :minor, :patch, :pre_release, :is_stable, :old_version, :version + attr_reader :major, :minor, :patch, :pre_release, :is_stable, :version def initialize(version) @major = 0 @@ -14,10 +14,9 @@ def initialize(version) @patch = 0 @pre_release = [] @is_stable = false - @old_version = version - @version = "" - @metadata = "" - parse + @version = '' + @metadata = '' + parse(version) end # @@ -34,15 +33,16 @@ def self.build(version, logger) end # - # Check if there is any metadata characters in self._old_version. + # Check if there is any metadata characters in version. # # @return [type] String semver without the metadata # - def remove_metadata_if_exists - index = @old_version.index(METADATA_DELIMITER) - return @old_version if index.nil? - @metadata = @old_version[index+1,@old_version.length] - @old_version[0, index] + def remove_metadata_if_exists(old_version) + index = old_version.index(METADATA_DELIMITER) + return old_version if index.nil? + + @metadata = old_version[index + 1, old_version.length] + old_version[0, index] end # Compare the current Semver object to a given Semver object, return: @@ -70,10 +70,10 @@ def integer?(value) end # - # Parse the string in self._old_version to update the other internal variables + # Parse the string in version to update the other internal variables # - def parse - without_metadata = remove_metadata_if_exists + def parse(old_version) + without_metadata = remove_metadata_if_exists(old_version) index = without_metadata.index(PRE_RELEASE_DELIMITER) if index.nil? @@ -103,8 +103,8 @@ def assign_major_minor_and_patch(version) @minor = parts[1].to_i @patch = parts[2].to_i @version = "#{@major}#{VALUE_DELIMITER}#{@minor}#{VALUE_DELIMITER}#{@patch}" - @version += "#{PRE_RELEASE_DELIMITER}#{@pre_release.join('.')}" if !@pre_release.empty? - @version += "#{METADATA_DELIMITER}#{@metadata}" if !@metadata.empty? + @version += "#{PRE_RELEASE_DELIMITER}#{@pre_release.join('.')}" unless @pre_release.empty? + @version += "#{METADATA_DELIMITER}#{@metadata}" unless @metadata.empty? end # diff --git a/spec/engine/matchers/matches_between_semver_matcher_spec.rb b/spec/engine/matchers/matches_between_semver_matcher_spec.rb index 1fa7d0f8..12474791 100644 --- a/spec/engine/matchers/matches_between_semver_matcher_spec.rb +++ b/spec/engine/matchers/matches_between_semver_matcher_spec.rb @@ -14,9 +14,9 @@ matcher = described_class.new("version", raw[:betweenStringMatcherData][:start], raw[:betweenStringMatcherData][:end], config.split_logger, config.split_validator) expect(matcher.attribute).to eq("version") semver_start = matcher.instance_variable_get(:@semver_start) - expect(semver_start.instance_variable_get(:@old_version)).to eq("2.1.8") + expect(semver_start.instance_variable_get(:@version)).to eq("2.1.8") semver_end = matcher.instance_variable_get(:@semver_end) - expect(semver_end.instance_variable_get(:@old_version)).to eq("2.1.11") + expect(semver_end.instance_variable_get(:@version)).to eq("2.1.11") end it 'matches' do diff --git a/spec/engine/matchers/matches_equal_to_semver_matcher_spec.rb b/spec/engine/matchers/matches_equal_to_semver_matcher_spec.rb index f1280866..f28d2521 100644 --- a/spec/engine/matchers/matches_equal_to_semver_matcher_spec.rb +++ b/spec/engine/matchers/matches_equal_to_semver_matcher_spec.rb @@ -14,17 +14,17 @@ matcher = described_class.new("version", raw[:stringMatcherData], config.split_logger, config.split_validator) expect(matcher.attribute).to eq("version") semver = matcher.instance_variable_get(:@semver) - expect(semver.instance_variable_get(:@old_version)).to eq("2.1.8") + expect(semver.instance_variable_get(:@version)).to eq("2.1.8") end it 'matches' do matcher = described_class.new("version", raw[:stringMatcherData], config.split_logger, config.split_validator) - expect(matcher.match?(:attributes=>{"version": "2.1.8+rc"})).to eq(true) expect(matcher.match?(:attributes=>{"version": "2.1.8"})).to eq(true) end it 'does not match' do matcher = described_class.new("version", raw[:stringMatcherData], config.split_logger, config.split_validator) + expect(matcher.match?(:attributes=>{"version": "2.1.8+rc"})).to eq(false) expect(matcher.match?(:attributes=>{"version": "2.1.5"})).to eq(false) expect(matcher.match?(:attributes=>{"version": "2.1.5-rc1"})).to eq(false) end diff --git a/spec/engine/matchers/matches_greater_than_or_equal_to_semver_matcher_spec.rb b/spec/engine/matchers/matches_greater_than_or_equal_to_semver_matcher_spec.rb index 7888cbe9..be31742c 100644 --- a/spec/engine/matchers/matches_greater_than_or_equal_to_semver_matcher_spec.rb +++ b/spec/engine/matchers/matches_greater_than_or_equal_to_semver_matcher_spec.rb @@ -14,7 +14,7 @@ matcher = described_class.new("version", raw[:stringMatcherData], config.split_logger, config.split_validator) expect(matcher.attribute).to eq("version") semver = matcher.instance_variable_get(:@semver) - expect(semver.instance_variable_get(:@old_version)).to eq("2.1.8") + expect(semver.instance_variable_get(:@version)).to eq("2.1.8") end it 'matches' do diff --git a/spec/engine/matchers/matches_in_list_semver_matcher_spec.rb b/spec/engine/matchers/matches_in_list_semver_matcher_spec.rb index dad00078..bffe3da1 100644 --- a/spec/engine/matchers/matches_in_list_semver_matcher_spec.rb +++ b/spec/engine/matchers/matches_in_list_semver_matcher_spec.rb @@ -14,18 +14,19 @@ matcher = described_class.new("version", raw[:whitelistMatcherData][:whitelist], config.split_logger, config.split_validator) expect(matcher.attribute).to eq("version") semver_list = matcher.instance_variable_get(:@semver_list) - expect(semver_list[0].instance_variable_get(:@old_version)).to eq("2.1.8") - expect(semver_list[1].instance_variable_get(:@old_version)).to eq("2.1.11") + expect(semver_list[0].instance_variable_get(:@version)).to eq("2.1.8") + expect(semver_list[1].instance_variable_get(:@version)).to eq("2.1.11") end it 'matches' do matcher = described_class.new("version", raw[:whitelistMatcherData][:whitelist], config.split_logger, config.split_validator) - expect(matcher.match?(:attributes=>{"version": "2.1.8+rc"})).to eq(true) + expect(matcher.match?(:attributes=>{"version": "2.1.8"})).to eq(true) expect(matcher.match?(:attributes=>{"version": "2.1.11"})).to eq(true) end it 'does not match' do matcher = described_class.new("version", raw[:whitelistMatcherData][:whitelist], config.split_logger, config.split_validator) + expect(matcher.match?(:attributes=>{"version": "2.1.8+rc"})).to eq(false) expect(matcher.match?(:attributes=>{"version": "2.1.7"})).to eq(false) expect(matcher.match?(:attributes=>{"version": "2.1.11-rc12"})).to eq(false) expect(matcher.match?(:attributes=>{"version": "2.1.8-rc1"})).to eq(false) diff --git a/spec/engine/matchers/matches_less_than_or_equal_to_semver_matcher_spec.rb b/spec/engine/matchers/matches_less_than_or_equal_to_semver_matcher_spec.rb index 0a8bc6d3..978ec18e 100644 --- a/spec/engine/matchers/matches_less_than_or_equal_to_semver_matcher_spec.rb +++ b/spec/engine/matchers/matches_less_than_or_equal_to_semver_matcher_spec.rb @@ -14,7 +14,7 @@ matcher = described_class.new("version", raw[:stringMatcherData], config.split_logger, config.split_validator) expect(matcher.attribute).to eq("version") semver = matcher.instance_variable_get(:@semver) - expect(semver.instance_variable_get(:@old_version)).to eq("2.1.8") + expect(semver.instance_variable_get(:@version)).to eq("2.1.8") end it 'matches' do From a8761d71faf76e3301897cada26e23958dc01220 Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Wed, 10 Apr 2024 19:10:53 -0700 Subject: [PATCH 26/37] Added nil check for all matchers and updated specs --- .../engine/matchers/between_semver_matcher.rb | 18 +++++++++--------- .../engine/matchers/equal_to_semver_matcher.rb | 14 +++++++------- .../greater_than_or_equal_to_semver_matcher.rb | 14 +++++++------- .../engine/matchers/in_list_semver_matcher.rb | 6 +++--- .../less_than_or_equal_to_semver_matcher.rb | 14 +++++++------- lib/splitclient-rb/engine/matchers/semver.rb | 4 ++-- .../repositories/splits_repository_spec.rb | 2 +- .../matches_between_semver_matcher_spec.rb | 8 ++++---- .../matches_equal_to_semver_matcher_spec.rb | 9 ++++----- ...ter_than_or_equal_to_semver_matcher_spec.rb | 8 ++++---- .../matches_in_list_semver_matcher_spec.rb | 8 ++++---- ...ess_than_or_equal_to_semver_matcher_spec.rb | 10 +++++----- 12 files changed, 57 insertions(+), 58 deletions(-) diff --git a/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb index 282dcf5c..8159272e 100644 --- a/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb @@ -10,23 +10,23 @@ def initialize(attribute, start_value, end_value, logger, validator) super(logger) @validator = validator @attribute = attribute - @semver_start = SplitIoClient::Semver.new(start_value) - @semver_end = SplitIoClient::Semver.new(end_value) + @semver_start = SplitIoClient::Semver.build(start_value, logger) + @semver_end = SplitIoClient::Semver.build(end_value, logger) @logger = logger end def match?(args) - @logger.log_if_debug('[BetweenSemverMatcher] evaluating value and attributes.') + @logger.debug('[BetweenSemverMatcher] evaluating value and attributes.') return false unless @validator.valid_matcher_arguments(args) - value_to_match = args[:attributes][@attribute.to_sym] - unless value_to_match.is_a?(String) - @logger.log_if_debug('betweenStringMatcherData is required for BETWEEN_SEMVER matcher type') + value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) + unless !value_to_match.nil? && !@semver_start.nil? && !@semver_end.nil? + @logger.error('betweenStringMatcherData is required for BETWEEN_SEMVER matcher type') return false end - matches = ([0, -1].include?(@semver_start.compare(SplitIoClient::Semver.new(value_to_match))) && - [0, 1].include?(@semver_end.compare(SplitIoClient::Semver.new(value_to_match)))) - @logger.log_if_debug("[BetweenMatcher] #{value_to_match} matches -> #{matches}") + matches = ([0, -1].include?(@semver_start.compare(value_to_match)) && + [0, 1].include?(@semver_end.compare(value_to_match))) + @logger.debug("[BetweenMatcher] #{value_to_match} matches -> #{matches}") matches end end diff --git a/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb index 877b0f82..0ffa2b2f 100644 --- a/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb @@ -10,21 +10,21 @@ def initialize(attribute, string_value, logger, validator) super(logger) @validator = validator @attribute = attribute - @semver = SplitIoClient::Semver.new(string_value) + @semver = SplitIoClient::Semver.build(string_value, logger) @logger = logger end def match?(args) - @logger.log_if_debug('[EqualsToSemverMatcher] evaluating value and attributes.') + @logger.debug('[EqualsToSemverMatcher] evaluating value and attributes.') return false unless @validator.valid_matcher_arguments(args) - value_to_match = args[:attributes][@attribute.to_sym] - unless value_to_match.is_a?(String) - @logger.log_if_debug('stringMatcherData is required for EQUAL_TO_SEMVER matcher type') + value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) + unless !value_to_match.nil? && !@semver.nil? + @logger.error('stringMatcherData is required for EQUAL_TO_SEMVER matcher type') return false end - matches = (@semver.version == SplitIoClient::Semver.new(value_to_match).version) - @logger.log_if_debug("[EqualsToSemverMatcher] #{value_to_match} matches -> #{matches}") + matches = (@semver.version == value_to_match.version) + @logger.debug("[EqualsToSemverMatcher] #{value_to_match} matches -> #{matches}") matches end end diff --git a/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb index 818962eb..cb9331c8 100644 --- a/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb @@ -10,21 +10,21 @@ def initialize(attribute, string_value, logger, validator) super(logger) @validator = validator @attribute = attribute - @semver = SplitIoClient::Semver.new(string_value) + @semver = SplitIoClient::Semver.build(string_value, logger) @logger = logger end def match?(args) - @logger.log_if_debug('[GreaterThanOrEqualsToSemverMatcher] evaluating value and attributes.') + @logger.debug('[GreaterThanOrEqualsToSemverMatcher] evaluating value and attributes.') return false unless @validator.valid_matcher_arguments(args) - value_to_match = args[:attributes][@attribute.to_sym] - unless value_to_match.is_a?(String) - @logger.log_if_debug('stringMatcherData is required for GREATER_THAN_OR_EQUAL_TO_SEMVER matcher type') + value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) + unless !value_to_match.nil? && !@semver.nil? + @logger.error('stringMatcherData is required for GREATER_THAN_OR_EQUAL_TO_SEMVER matcher type') return false end - matches = [0, 1].include?(@semver.compare(SplitIoClient::Semver.new(value_to_match))) - @logger.log_if_debug("[GreaterThanOrEqualsToSemverMatcher] #{value_to_match} matches -> #{matches}") + matches = [0, 1].include?(@semver.compare(value_to_match)) + @logger.debug("[GreaterThanOrEqualsToSemverMatcher] #{value_to_match} matches -> #{matches}") matches end end diff --git a/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb index 57b13ddd..298bc0b0 100644 --- a/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb @@ -15,16 +15,16 @@ def initialize(attribute, list_value, logger, validator) end def match?(args) - @logger.log_if_debug('[InListSemverMatcher] evaluating value and attributes.') + @logger.debug('[InListSemverMatcher] evaluating value and attributes.') return false unless @validator.valid_matcher_arguments(args) && args[:attributes][@attribute.to_sym].is_a?(String) value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) unless !value_to_match.nil? && @semver_list.all? { |n| !n.nil? } - @logger.log_if_debug('whitelistMatcherData is required for IN_LIST_SEMVER matcher type') + @logger.error('whitelistMatcherData is required for IN_LIST_SEMVER matcher type') return false end matches = (@semver_list.map { |item| item.version == value_to_match.version }).any? { |item| item == true } - @logger.log_if_debug("[InListSemverMatcher] #{value_to_match} matches -> #{matches}") + @logger.debug("[InListSemverMatcher] #{value_to_match} matches -> #{matches}") matches end end diff --git a/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb index eb95702f..ec10cb62 100644 --- a/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb @@ -10,21 +10,21 @@ def initialize(attribute, string_value, logger, validator) super(logger) @validator = validator @attribute = attribute - @semver = SplitIoClient::Semver.new(string_value) + @semver = SplitIoClient::Semver.build(string_value, logger) @logger = logger end def match?(args) - @logger.log_if_debug('[LessThanOrEqualsToSemverMatcher] evaluating value and attributes.') + @logger.debug('[LessThanOrEqualsToSemverMatcher] evaluating value and attributes.') return false unless @validator.valid_matcher_arguments(args) - value_to_match = args[:attributes][@attribute.to_sym] - unless value_to_match.is_a?(String) - @logger.log_if_debug('stringMatcherData is required for LESS_THAN_OR_EQUAL_TO_SEMVER matcher type') + value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) + unless !value_to_match.nil? && !@semver.nil? + @logger.error('stringMatcherData is required for LESS_THAN_OR_EQUAL_TO_SEMVER matcher type') return false end - matches = [0, -1].include?(@semver.compare(SplitIoClient::Semver.new(value_to_match))) - @logger.log_if_debug("[LessThanOrEqualsToSemverMatcher] #{value_to_match} matches -> #{matches}") + matches = [0, -1].include?(@semver.compare(value_to_match)) + @logger.debug("[LessThanOrEqualsToSemverMatcher] #{value_to_match} matches -> #{matches}") matches end end diff --git a/lib/splitclient-rb/engine/matchers/semver.rb b/lib/splitclient-rb/engine/matchers/semver.rb index 6669b94b..3de8d5f7 100644 --- a/lib/splitclient-rb/engine/matchers/semver.rb +++ b/lib/splitclient-rb/engine/matchers/semver.rb @@ -27,8 +27,8 @@ def initialize(version) # @return [type] Semver instance def self.build(version, logger) new(version) - rescue RuntimeError => e - logger.warn("Failed to parse Semver data: #{e}") + rescue StandardError, NoMethodError => e + logger.error("Failed to parse Semver data: #{e}") nil end diff --git a/spec/cache/repositories/splits_repository_spec.rb b/spec/cache/repositories/splits_repository_spec.rb index 6d8ae3f7..0b50df8a 100644 --- a/spec/cache/repositories/splits_repository_spec.rb +++ b/spec/cache/repositories/splits_repository_spec.rb @@ -126,7 +126,7 @@ } }] } - repository.update([split], [], -1) + repository.update([split.dup], [], -1) expect(repository.get_split('corge')[:conditions]).to eq [SplitIoClient::Cache::Repositories::SplitsRepository::DEFAULT_CONDITIONS_TEMPLATE] # test with multiple conditions diff --git a/spec/engine/matchers/matches_between_semver_matcher_spec.rb b/spec/engine/matchers/matches_between_semver_matcher_spec.rb index 12474791..b4431fe4 100644 --- a/spec/engine/matchers/matches_between_semver_matcher_spec.rb +++ b/spec/engine/matchers/matches_between_semver_matcher_spec.rb @@ -11,7 +11,7 @@ let(:config) { SplitIoClient::SplitConfig.new } it 'initilized params' do - matcher = described_class.new("version", raw[:betweenStringMatcherData][:start], raw[:betweenStringMatcherData][:end], config.split_logger, config.split_validator) + matcher = described_class.new("version", raw[:betweenStringMatcherData][:start], raw[:betweenStringMatcherData][:end], config.logger, config.split_validator) expect(matcher.attribute).to eq("version") semver_start = matcher.instance_variable_get(:@semver_start) expect(semver_start.instance_variable_get(:@version)).to eq("2.1.8") @@ -20,20 +20,20 @@ end it 'matches' do - matcher = described_class.new("version", raw[:betweenStringMatcherData][:start], raw[:betweenStringMatcherData][:end], config.split_logger, config.split_validator) + matcher = described_class.new("version", raw[:betweenStringMatcherData][:start], raw[:betweenStringMatcherData][:end], config.logger, config.split_validator) expect(matcher.match?(:attributes=>{"version": "2.1.8+rc"})).to eq(true) expect(matcher.match?(:attributes=>{"version": "2.1.9"})).to eq(true) expect(matcher.match?(:attributes=>{"version": "2.1.11-rc12"})).to eq(true) end it 'does not match' do - matcher = described_class.new("version", raw[:betweenStringMatcherData][:start], raw[:betweenStringMatcherData][:end], config.split_logger, config.split_validator) + matcher = described_class.new("version", raw[:betweenStringMatcherData][:start], raw[:betweenStringMatcherData][:end], config.logger, config.split_validator) expect(matcher.match?(:attributes=>{"version": "2.1.5"})).to eq(false) expect(matcher.match?(:attributes=>{"version": "2.1.12-rc1"})).to eq(false) end it 'invalid attribute' do - matcher = described_class.new("version", raw[:betweenStringMatcherData][:start], raw[:betweenStringMatcherData][:end], config.split_logger, config.split_validator) + matcher = described_class.new("version", raw[:betweenStringMatcherData][:start], raw[:betweenStringMatcherData][:end], config.logger, config.split_validator) expect(matcher.match?(:attributes=>{"version": 2.1})).to eq(false) expect(matcher.match?(:attributes=>{"version": nil})).to eq(false) end diff --git a/spec/engine/matchers/matches_equal_to_semver_matcher_spec.rb b/spec/engine/matchers/matches_equal_to_semver_matcher_spec.rb index f28d2521..6d382bb3 100644 --- a/spec/engine/matchers/matches_equal_to_semver_matcher_spec.rb +++ b/spec/engine/matchers/matches_equal_to_semver_matcher_spec.rb @@ -11,28 +11,27 @@ let(:config) { SplitIoClient::SplitConfig.new } it 'initilized params' do - matcher = described_class.new("version", raw[:stringMatcherData], config.split_logger, config.split_validator) + matcher = described_class.new("version", raw[:stringMatcherData], config.logger, config.split_validator) expect(matcher.attribute).to eq("version") semver = matcher.instance_variable_get(:@semver) expect(semver.instance_variable_get(:@version)).to eq("2.1.8") end it 'matches' do - matcher = described_class.new("version", raw[:stringMatcherData], config.split_logger, config.split_validator) + matcher = described_class.new("version", raw[:stringMatcherData], config.logger, config.split_validator) expect(matcher.match?(:attributes=>{"version": "2.1.8"})).to eq(true) end it 'does not match' do - matcher = described_class.new("version", raw[:stringMatcherData], config.split_logger, config.split_validator) + matcher = described_class.new("version", raw[:stringMatcherData], config.logger, config.split_validator) expect(matcher.match?(:attributes=>{"version": "2.1.8+rc"})).to eq(false) expect(matcher.match?(:attributes=>{"version": "2.1.5"})).to eq(false) expect(matcher.match?(:attributes=>{"version": "2.1.5-rc1"})).to eq(false) end it 'invalid attribute' do - matcher = described_class.new("version", raw[:stringMatcherData], config.split_logger, config.split_validator) + matcher = described_class.new("version", raw[:stringMatcherData], config.logger, config.split_validator) expect(matcher.match?(:attributes=>{"version": 2.1})).to eq(false) expect(matcher.match?(:attributes=>{"version": nil})).to eq(false) end - end diff --git a/spec/engine/matchers/matches_greater_than_or_equal_to_semver_matcher_spec.rb b/spec/engine/matchers/matches_greater_than_or_equal_to_semver_matcher_spec.rb index be31742c..db5d56cd 100644 --- a/spec/engine/matchers/matches_greater_than_or_equal_to_semver_matcher_spec.rb +++ b/spec/engine/matchers/matches_greater_than_or_equal_to_semver_matcher_spec.rb @@ -11,14 +11,14 @@ let(:config) { SplitIoClient::SplitConfig.new } it 'initilized params' do - matcher = described_class.new("version", raw[:stringMatcherData], config.split_logger, config.split_validator) + matcher = described_class.new("version", raw[:stringMatcherData], config.logger, config.split_validator) expect(matcher.attribute).to eq("version") semver = matcher.instance_variable_get(:@semver) expect(semver.instance_variable_get(:@version)).to eq("2.1.8") end it 'matches' do - matcher = described_class.new("version", raw[:stringMatcherData], config.split_logger, config.split_validator) + matcher = described_class.new("version", raw[:stringMatcherData], config.logger, config.split_validator) expect(matcher.match?(:attributes=>{"version": "2.1.8+rc"})).to eq(true) expect(matcher.match?(:attributes=>{"version": "2.1.8"})).to eq(true) expect(matcher.match?(:attributes=>{"version": "2.1.5"})).to eq(true) @@ -26,13 +26,13 @@ end it 'does not match' do - matcher = described_class.new("version", raw[:stringMatcherData], config.split_logger, config.split_validator) + matcher = described_class.new("version", raw[:stringMatcherData], config.logger, config.split_validator) expect(matcher.match?(:attributes=>{"version": "2.1.11"})).to eq(false) expect(matcher.match?(:attributes=>{"version": "2.2.0"})).to eq(false) end it 'invalid attribute' do - matcher = described_class.new("version", raw[:stringMatcherData], config.split_logger, config.split_validator) + matcher = described_class.new("version", raw[:stringMatcherData], config.logger, config.split_validator) expect(matcher.match?(:attributes=>{"version": 2.1})).to eq(false) expect(matcher.match?(:attributes=>{"version": nil})).to eq(false) end diff --git a/spec/engine/matchers/matches_in_list_semver_matcher_spec.rb b/spec/engine/matchers/matches_in_list_semver_matcher_spec.rb index bffe3da1..76461a65 100644 --- a/spec/engine/matchers/matches_in_list_semver_matcher_spec.rb +++ b/spec/engine/matchers/matches_in_list_semver_matcher_spec.rb @@ -11,7 +11,7 @@ let(:config) { SplitIoClient::SplitConfig.new } it 'initilized params' do - matcher = described_class.new("version", raw[:whitelistMatcherData][:whitelist], config.split_logger, config.split_validator) + matcher = described_class.new("version", raw[:whitelistMatcherData][:whitelist], config.logger, config.split_validator) expect(matcher.attribute).to eq("version") semver_list = matcher.instance_variable_get(:@semver_list) expect(semver_list[0].instance_variable_get(:@version)).to eq("2.1.8") @@ -19,13 +19,13 @@ end it 'matches' do - matcher = described_class.new("version", raw[:whitelistMatcherData][:whitelist], config.split_logger, config.split_validator) + matcher = described_class.new("version", raw[:whitelistMatcherData][:whitelist], config.logger, config.split_validator) expect(matcher.match?(:attributes=>{"version": "2.1.8"})).to eq(true) expect(matcher.match?(:attributes=>{"version": "2.1.11"})).to eq(true) end it 'does not match' do - matcher = described_class.new("version", raw[:whitelistMatcherData][:whitelist], config.split_logger, config.split_validator) + matcher = described_class.new("version", raw[:whitelistMatcherData][:whitelist], config.logger, config.split_validator) expect(matcher.match?(:attributes=>{"version": "2.1.8+rc"})).to eq(false) expect(matcher.match?(:attributes=>{"version": "2.1.7"})).to eq(false) expect(matcher.match?(:attributes=>{"version": "2.1.11-rc12"})).to eq(false) @@ -33,7 +33,7 @@ end it 'invalid attribute' do - matcher = described_class.new("version", raw[:whitelistMatcherData][:whitelist], config.split_logger, config.split_validator) + matcher = described_class.new("version", raw[:whitelistMatcherData][:whitelist], config.logger, config.split_validator) expect(matcher.match?(:attributes=>{"version": 2.1})).to eq(false) expect(matcher.match?(:attributes=>{"version": nil})).to eq(false) end diff --git a/spec/engine/matchers/matches_less_than_or_equal_to_semver_matcher_spec.rb b/spec/engine/matchers/matches_less_than_or_equal_to_semver_matcher_spec.rb index 978ec18e..8b20d8ab 100644 --- a/spec/engine/matchers/matches_less_than_or_equal_to_semver_matcher_spec.rb +++ b/spec/engine/matchers/matches_less_than_or_equal_to_semver_matcher_spec.rb @@ -8,17 +8,17 @@ 'matcherType': 'LESS_THAN_OR_EQUAL_TO_SEMVER', 'stringMatcherData': "2.1.8" } } - let(:config) { SplitIoClient::SplitConfig.new } + let(:config) { SplitIoClient::SplitConfig.new({:logger => Logger.new('/dev/null')}) } it 'initilized params' do - matcher = described_class.new("version", raw[:stringMatcherData], config.split_logger, config.split_validator) + matcher = described_class.new("version", raw[:stringMatcherData], config.logger, config.split_validator) expect(matcher.attribute).to eq("version") semver = matcher.instance_variable_get(:@semver) expect(semver.instance_variable_get(:@version)).to eq("2.1.8") end it 'matches' do - matcher = described_class.new("version", raw[:stringMatcherData], config.split_logger, config.split_validator) + matcher = described_class.new("version", raw[:stringMatcherData], config.logger, config.split_validator) expect(matcher.match?(:attributes=>{"version": "2.1.8+rc"})).to eq(true) expect(matcher.match?(:attributes=>{"version": "2.1.8"})).to eq(true) expect(matcher.match?(:attributes=>{"version": "2.1.11"})).to eq(true) @@ -26,13 +26,13 @@ end it 'does not match' do - matcher = described_class.new("version", raw[:stringMatcherData], config.split_logger, config.split_validator) + matcher = described_class.new("version", raw[:stringMatcherData], config.logger, config.split_validator) expect(matcher.match?(:attributes=>{"version": "2.1.5"})).to eq(false) expect(matcher.match?(:attributes=>{"version": "2.1.5-rc1"})).to eq(false) end it 'invalid attribute' do - matcher = described_class.new("version", raw[:stringMatcherData], config.split_logger, config.split_validator) + matcher = described_class.new("version", raw[:stringMatcherData], config.logger, config.split_validator) expect(matcher.match?(:attributes=>{"version": 2.1})).to eq(false) expect(matcher.match?(:attributes=>{"version": nil})).to eq(false) end From 5a084a8dd0b327c3b5ed6c3c3ddddb2fb04b07bd Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Wed, 10 Apr 2024 19:19:52 -0700 Subject: [PATCH 27/37] polish --- lib/splitclient-rb/engine/matchers/between_semver_matcher.rb | 2 +- .../engine/matchers/equal_to_semver_matcher.rb | 2 +- .../matchers/greater_than_or_equal_to_semver_matcher.rb | 2 +- .../engine/matchers/less_than_or_equal_to_semver_matcher.rb | 2 +- lib/splitclient-rb/engine/matchers/semver.rb | 5 ++++- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb index 8159272e..e175c541 100644 --- a/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb @@ -19,7 +19,7 @@ def match?(args) @logger.debug('[BetweenSemverMatcher] evaluating value and attributes.') return false unless @validator.valid_matcher_arguments(args) - value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) + value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) unless !value_to_match.nil? && !@semver_start.nil? && !@semver_end.nil? @logger.error('betweenStringMatcherData is required for BETWEEN_SEMVER matcher type') return false diff --git a/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb index 0ffa2b2f..c77f78a1 100644 --- a/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb @@ -18,7 +18,7 @@ def match?(args) @logger.debug('[EqualsToSemverMatcher] evaluating value and attributes.') return false unless @validator.valid_matcher_arguments(args) - value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) + value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) unless !value_to_match.nil? && !@semver.nil? @logger.error('stringMatcherData is required for EQUAL_TO_SEMVER matcher type') return false diff --git a/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb index cb9331c8..93114829 100644 --- a/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb @@ -18,7 +18,7 @@ def match?(args) @logger.debug('[GreaterThanOrEqualsToSemverMatcher] evaluating value and attributes.') return false unless @validator.valid_matcher_arguments(args) - value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) + value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) unless !value_to_match.nil? && !@semver.nil? @logger.error('stringMatcherData is required for GREATER_THAN_OR_EQUAL_TO_SEMVER matcher type') return false diff --git a/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb index ec10cb62..23532044 100644 --- a/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb @@ -18,7 +18,7 @@ def match?(args) @logger.debug('[LessThanOrEqualsToSemverMatcher] evaluating value and attributes.') return false unless @validator.valid_matcher_arguments(args) - value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) + value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) unless !value_to_match.nil? && !@semver.nil? @logger.error('stringMatcherData is required for LESS_THAN_OR_EQUAL_TO_SEMVER matcher type') return false diff --git a/lib/splitclient-rb/engine/matchers/semver.rb b/lib/splitclient-rb/engine/matchers/semver.rb index 3de8d5f7..bb09ec0f 100644 --- a/lib/splitclient-rb/engine/matchers/semver.rb +++ b/lib/splitclient-rb/engine/matchers/semver.rb @@ -27,7 +27,10 @@ def initialize(version) # @return [type] Semver instance def self.build(version, logger) new(version) - rescue StandardError, NoMethodError => e + rescue NoMethodError => e + logger.error("Failed to parse Semver data, incorrect data type: #{e}") + nil + rescue StandardError => e logger.error("Failed to parse Semver data: #{e}") nil end From 7ec5da7bfe46f6c4e02ef2c7baa9ea4fa4feccdb Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Wed, 10 Apr 2024 20:21:38 -0700 Subject: [PATCH 28/37] added version length check for compare --- lib/splitclient-rb/engine/matchers/semver.rb | 5 ++- spec/engine/matchers/semver_spec.rb | 33 +++++++++++++++----- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/lib/splitclient-rb/engine/matchers/semver.rb b/lib/splitclient-rb/engine/matchers/semver.rb index bb09ec0f..151375fc 100644 --- a/lib/splitclient-rb/engine/matchers/semver.rb +++ b/lib/splitclient-rb/engine/matchers/semver.rb @@ -63,7 +63,10 @@ def compare(to_compare) return compare_attributes(to_compare) if compare_attributes(to_compare) != 0 # Compare pre-release versions lexically - compare_pre_release(to_compare) + return compare_pre_release(to_compare) if compare_pre_release(to_compare) != 0 + + # compare length of version + compare_vars(@version.length, to_compare.version.length) end private diff --git a/spec/engine/matchers/semver_spec.rb b/spec/engine/matchers/semver_spec.rb index b65b3304..6b3d3bef 100644 --- a/spec/engine/matchers/semver_spec.rb +++ b/spec/engine/matchers/semver_spec.rb @@ -4,15 +4,20 @@ describe SplitIoClient::Semver do let(:valid_versions) do CSV.parse(File.read(File.expand_path(File.join(File.dirname(__FILE__), - '../../test_data/splits/semver/valid-semantic-versions.csv')))) + '../../test_data/splits/semver/valid-semantic-versions.csv'))), headers: true) end let(:invalid_versions) do CSV.parse(File.read(File.expand_path(File.join(File.dirname(__FILE__), - '../../test_data/splits/semver/invalid-semantic-versions.csv')))) + '../../test_data/splits/semver/invalid-semantic-versions.csv'))), headers: true) end let(:equal_to_versions) do CSV.parse(File.read(File.expand_path(File.join(File.dirname(__FILE__), - '../../test_data/splits/semver/equal-to-semver.csv')))) + '../../test_data/splits/semver/equal-to-semver.csv'))), headers: true) + end + + let(:between_versions) do + CSV.parse(File.read(File.expand_path(File.join(File.dirname(__FILE__), + '../../test_data/splits/semver/between-semver.csv'))), headers: true) end let(:logger) { Logger.new('/dev/null') } @@ -20,7 +25,7 @@ context 'check versions' do it 'accept valid versions' do for i in (0..valid_versions.length-1) - expect(described_class.build(valid_versions[i][0], logger)).should_not be_nil + expect(described_class.build(valid_versions[i][0], logger)).not_to be_nil end end it 'reject invalid versions' do @@ -39,12 +44,26 @@ expect(described_class.build(valid_versions[i][1], logger).compare(described_class.build(valid_versions[i][1], logger))).to eq(0) end for i in (1..equal_to_versions.length-1) - if valid_versions[i][2] - expect(described_class.build(valid_versions[i][0], logger).compare(described_class.build(valid_versions[i][1], logger))).to eq(0) + if equal_to_versions[i][2]=='true' + expect(described_class.build(equal_to_versions[i][0], logger).compare(described_class.build(equal_to_versions[i][1], logger))).to eq(0) else - expect(described_class.build(valid_versions[i][0], logger).compare(described_class.build(valid_versions[i][1], logger))).not_to eq(0) + expect(described_class.build(equal_to_versions[i][0], logger).compare(described_class.build(equal_to_versions[i][1], logger))).not_to eq(0) end end + for i in (1..between_versions.length-1) + sem1 = described_class.build(between_versions[i][0], logger) + sem2 = described_class.build(between_versions[i][2], logger) + to_check = described_class.build(between_versions[i][1], logger) + if between_versions[i][3]=='true' + expect(sem1.compare(to_check)).to eq(-1) + expect(sem2.compare(to_check)).to eq(1) + else + compare1 = sem1.compare(to_check) + compare2 = sem2.compare(to_check) + expect(compare1 == -1 && compare2 == 1).to eq(false) + end + end + end end end From 4ecad51cf83dfefb26e80005689930e18812f0c9 Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Thu, 11 Apr 2024 09:58:46 -0700 Subject: [PATCH 29/37] correcting specs and comparisons --- .../matchers/greater_than_or_equal_to_semver_matcher.rb | 2 +- .../engine/matchers/less_than_or_equal_to_semver_matcher.rb | 2 +- lib/splitclient-rb/engine/matchers/semver.rb | 5 +---- .../matches_greater_than_or_equal_to_semver_matcher_spec.rb | 4 ++-- .../matches_less_than_or_equal_to_semver_matcher_spec.rb | 4 ++-- spec/engine/matchers/semver_spec.rb | 4 ++-- 6 files changed, 9 insertions(+), 12 deletions(-) diff --git a/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb index 93114829..ffe46aef 100644 --- a/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb @@ -23,7 +23,7 @@ def match?(args) @logger.error('stringMatcherData is required for GREATER_THAN_OR_EQUAL_TO_SEMVER matcher type') return false end - matches = [0, 1].include?(@semver.compare(value_to_match)) + matches = [0, 1].include?(value_to_match.compare(@semver)) @logger.debug("[GreaterThanOrEqualsToSemverMatcher] #{value_to_match} matches -> #{matches}") matches end diff --git a/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb index 23532044..081a7317 100644 --- a/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb @@ -23,7 +23,7 @@ def match?(args) @logger.error('stringMatcherData is required for LESS_THAN_OR_EQUAL_TO_SEMVER matcher type') return false end - matches = [0, -1].include?(@semver.compare(value_to_match)) + matches = [0, -1].include?(value_to_match.compare(@semver)) @logger.debug("[LessThanOrEqualsToSemverMatcher] #{value_to_match} matches -> #{matches}") matches end diff --git a/lib/splitclient-rb/engine/matchers/semver.rb b/lib/splitclient-rb/engine/matchers/semver.rb index 151375fc..bb09ec0f 100644 --- a/lib/splitclient-rb/engine/matchers/semver.rb +++ b/lib/splitclient-rb/engine/matchers/semver.rb @@ -63,10 +63,7 @@ def compare(to_compare) return compare_attributes(to_compare) if compare_attributes(to_compare) != 0 # Compare pre-release versions lexically - return compare_pre_release(to_compare) if compare_pre_release(to_compare) != 0 - - # compare length of version - compare_vars(@version.length, to_compare.version.length) + compare_pre_release(to_compare) end private diff --git a/spec/engine/matchers/matches_greater_than_or_equal_to_semver_matcher_spec.rb b/spec/engine/matchers/matches_greater_than_or_equal_to_semver_matcher_spec.rb index 11facb21..91c81dec 100644 --- a/spec/engine/matchers/matches_greater_than_or_equal_to_semver_matcher_spec.rb +++ b/spec/engine/matchers/matches_greater_than_or_equal_to_semver_matcher_spec.rb @@ -27,8 +27,8 @@ it 'does not match' do matcher = described_class.new("version", raw[:stringMatcherData], config.logger, config.split_validator) - expect(matcher.match?(:attributes=>{"version": "2.1.11"})).to eq(false) - expect(matcher.match?(:attributes=>{"version": "2.2.0"})).to eq(false) + expect(matcher.match?(:attributes=>{"version": "2.1.7"})).to eq(false) + expect(matcher.match?(:attributes=>{"version": "2.0.22"})).to eq(false) end it 'invalid attribute' do diff --git a/spec/engine/matchers/matches_less_than_or_equal_to_semver_matcher_spec.rb b/spec/engine/matchers/matches_less_than_or_equal_to_semver_matcher_spec.rb index 64d37c53..a551e657 100644 --- a/spec/engine/matchers/matches_less_than_or_equal_to_semver_matcher_spec.rb +++ b/spec/engine/matchers/matches_less_than_or_equal_to_semver_matcher_spec.rb @@ -27,8 +27,8 @@ it 'does not match' do matcher = described_class.new("version", raw[:stringMatcherData], config.logger, config.split_validator) - expect(matcher.match?(:attributes=>{"version": "2.1.5"})).to eq(false) - expect(matcher.match?(:attributes=>{"version": "2.1.5-rc1"})).to eq(false) + expect(matcher.match?(:attributes=>{"version": "2.1.10"})).to eq(false) + expect(matcher.match?(:attributes=>{"version": "2.2.0-rc1"})).to eq(false) end it 'invalid attribute' do diff --git a/spec/engine/matchers/semver_spec.rb b/spec/engine/matchers/semver_spec.rb index 6b3d3bef..a10f64bf 100644 --- a/spec/engine/matchers/semver_spec.rb +++ b/spec/engine/matchers/semver_spec.rb @@ -45,9 +45,9 @@ end for i in (1..equal_to_versions.length-1) if equal_to_versions[i][2]=='true' - expect(described_class.build(equal_to_versions[i][0], logger).compare(described_class.build(equal_to_versions[i][1], logger))).to eq(0) + expect(described_class.build(equal_to_versions[i][0], logger).version == described_class.build(equal_to_versions[i][1], logger).version).to eq(true) else - expect(described_class.build(equal_to_versions[i][0], logger).compare(described_class.build(equal_to_versions[i][1], logger))).not_to eq(0) + expect(described_class.build(equal_to_versions[i][0], logger) == described_class.build(equal_to_versions[i][1], logger)).to eq(false) end end for i in (1..between_versions.length-1) From db9311aa39a290ed26c14586fc5adf2265a4a465 Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Wed, 1 May 2024 10:05:47 -0700 Subject: [PATCH 30/37] updated query api order, fixed spec and added error to split logger --- lib/splitclient-rb/engine/api/splits.rb | 2 +- .../engine/matchers/between_semver_matcher.rb | 4 +-- .../matchers/equal_to_semver_matcher.rb | 4 +-- ...greater_than_or_equal_to_semver_matcher.rb | 4 +-- .../engine/matchers/in_list_semver_matcher.rb | 4 +-- .../less_than_or_equal_to_semver_matcher.rb | 4 +-- lib/splitclient-rb/split_logger.rb | 4 +++ .../semver_matchers_integration_spec.rb | 28 +++++++++++-------- 8 files changed, 31 insertions(+), 23 deletions(-) diff --git a/lib/splitclient-rb/engine/api/splits.rb b/lib/splitclient-rb/engine/api/splits.rb index b4d24a5e..b4d17bda 100644 --- a/lib/splitclient-rb/engine/api/splits.rb +++ b/lib/splitclient-rb/engine/api/splits.rb @@ -16,8 +16,8 @@ def since(since, fetch_options = { cache_control_headers: false, till: nil, sets start = Time.now params = { s: SplitIoClient::Spec::FeatureFlags::SPEC_VERSION, since: since } - params[:till] = fetch_options[:till] unless fetch_options[:till].nil? params[:sets] = @flag_sets_filter.join(",") unless @flag_sets_filter.empty? + params[:till] = fetch_options[:till] unless fetch_options[:till].nil? @config.logger.debug("Fetching from splitChanges with #{params}: ") response = get_api("#{@config.base_uri}/splitChanges", @api_key, params, fetch_options[:cache_control_headers]) if response.status == 414 diff --git a/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb index e175c541..e5c2865e 100644 --- a/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb @@ -16,7 +16,7 @@ def initialize(attribute, start_value, end_value, logger, validator) end def match?(args) - @logger.debug('[BetweenSemverMatcher] evaluating value and attributes.') + @logger.log_if_debug('[BetweenSemverMatcher] evaluating value and attributes.') return false unless @validator.valid_matcher_arguments(args) value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) @@ -26,7 +26,7 @@ def match?(args) end matches = ([0, -1].include?(@semver_start.compare(value_to_match)) && [0, 1].include?(@semver_end.compare(value_to_match))) - @logger.debug("[BetweenMatcher] #{value_to_match} matches -> #{matches}") + @logger.log_if_debug("[BetweenMatcher] #{value_to_match} matches -> #{matches}") matches end end diff --git a/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb index c77f78a1..49151599 100644 --- a/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb @@ -15,7 +15,7 @@ def initialize(attribute, string_value, logger, validator) end def match?(args) - @logger.debug('[EqualsToSemverMatcher] evaluating value and attributes.') + @logger.log_if_debug('[EqualsToSemverMatcher] evaluating value and attributes.') return false unless @validator.valid_matcher_arguments(args) value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) @@ -24,7 +24,7 @@ def match?(args) return false end matches = (@semver.version == value_to_match.version) - @logger.debug("[EqualsToSemverMatcher] #{value_to_match} matches -> #{matches}") + @logger.log_if_debug("[EqualsToSemverMatcher] #{value_to_match} matches -> #{matches}") matches end end diff --git a/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb index ffe46aef..6eef18ff 100644 --- a/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb @@ -15,7 +15,7 @@ def initialize(attribute, string_value, logger, validator) end def match?(args) - @logger.debug('[GreaterThanOrEqualsToSemverMatcher] evaluating value and attributes.') + @logger.log_if_debug('[GreaterThanOrEqualsToSemverMatcher] evaluating value and attributes.') return false unless @validator.valid_matcher_arguments(args) value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) @@ -24,7 +24,7 @@ def match?(args) return false end matches = [0, 1].include?(value_to_match.compare(@semver)) - @logger.debug("[GreaterThanOrEqualsToSemverMatcher] #{value_to_match} matches -> #{matches}") + @logger.log_if_debug("[GreaterThanOrEqualsToSemverMatcher] #{value_to_match} matches -> #{matches}") matches end end diff --git a/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb index 298bc0b0..45db3052 100644 --- a/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb @@ -15,7 +15,7 @@ def initialize(attribute, list_value, logger, validator) end def match?(args) - @logger.debug('[InListSemverMatcher] evaluating value and attributes.') + @logger.log_if_debug('[InListSemverMatcher] evaluating value and attributes.') return false unless @validator.valid_matcher_arguments(args) && args[:attributes][@attribute.to_sym].is_a?(String) value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) @@ -24,7 +24,7 @@ def match?(args) return false end matches = (@semver_list.map { |item| item.version == value_to_match.version }).any? { |item| item == true } - @logger.debug("[InListSemverMatcher] #{value_to_match} matches -> #{matches}") + @logger.log_if_debug("[InListSemverMatcher] #{value_to_match} matches -> #{matches}") matches end end diff --git a/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb index 081a7317..2dd8ff36 100644 --- a/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb @@ -15,7 +15,7 @@ def initialize(attribute, string_value, logger, validator) end def match?(args) - @logger.debug('[LessThanOrEqualsToSemverMatcher] evaluating value and attributes.') + @logger.log_if_debug('[LessThanOrEqualsToSemverMatcher] evaluating value and attributes.') return false unless @validator.valid_matcher_arguments(args) value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) @@ -24,7 +24,7 @@ def match?(args) return false end matches = [0, -1].include?(value_to_match.compare(@semver)) - @logger.debug("[LessThanOrEqualsToSemverMatcher] #{value_to_match} matches -> #{matches}") + @logger.log_if_debug("[LessThanOrEqualsToSemverMatcher] #{value_to_match} matches -> #{matches}") matches end end diff --git a/lib/splitclient-rb/split_logger.rb b/lib/splitclient-rb/split_logger.rb index 5a82b0ce..bf7a899e 100644 --- a/lib/splitclient-rb/split_logger.rb +++ b/lib/splitclient-rb/split_logger.rb @@ -11,5 +11,9 @@ def log_if_debug(message) def log_if_transport(message) @config.logger.debug(message) if @config.transport_debug_enabled end + + def error(message) + @config.logger.error(message) + end end end diff --git a/spec/engine/matchers/semver_matchers_integration_spec.rb b/spec/engine/matchers/semver_matchers_integration_spec.rb index bedb07aa..9b5a7858 100644 --- a/spec/engine/matchers/semver_matchers_integration_spec.rb +++ b/spec/engine/matchers/semver_matchers_integration_spec.rb @@ -43,15 +43,19 @@ let(:user) { 'fake_user_id_1' } before do - stub_request(:any, /https:\/\/telemetry.*/).to_return(status: 200, body: 'ok') - stub_request(:any, /https:\/\/events.*/).to_return(status: 200, body: "", headers: {}) + stub_request(:any, /https:\/\/telemetry\.*/).to_return(status: 200, body: 'ok') + stub_request(:any, /https:\/\/events\.*/).to_return(status: 200, body: "", headers: {}) + stub_request(:any, /https:\/\/metrics\.*/).to_return(status: 200, body: "", headers: {}) + stub_request(:post, "https://telemetry.split.io/api/v1/metrics/config").to_return(status: 200, body: "", headers: {}) end context 'equal to matcher' do before do - stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?since/) + stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.1&since\.*/) .to_return(status: 200, body: semver_equalto_matcher_splits) - stub_request(:get, 'https://sdk.split.io/api/splitChanges?since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + .to_return(status: 200, body: semver_equalto_matcher_splits) + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.1&since=1675259356568") .to_return(status: 200, body: semver_equalto_matcher_splits) sleep 1 subject.block_until_ready @@ -70,9 +74,9 @@ context 'greater than or equal to matcher' do before do - stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?since/) + stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.1&since/) .to_return(status: 200, body: semver_greater_or_equalto_matcher_splits) - stub_request(:get, 'https://sdk.split.io/api/splitChanges?since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') .to_return(status: 200, body: semver_greater_or_equalto_matcher_splits) sleep 1 subject.block_until_ready @@ -92,9 +96,9 @@ context 'less than or equal to matcher' do before do - stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?since/) + stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.1&since/) .to_return(status: 200, body: semver_less_or_equalto_matcher_splits) - stub_request(:get, 'https://sdk.split.io/api/splitChanges?since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') .to_return(status: 200, body: semver_less_or_equalto_matcher_splits) sleep 1 subject.block_until_ready @@ -114,9 +118,9 @@ context 'in list matcher' do before do - stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?since/) + stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.1&since/) .to_return(status: 200, body: semver_inlist_matcher_splits) - stub_request(:get, 'https://sdk.split.io/api/splitChanges?since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') .to_return(status: 200, body: semver_inlist_matcher_splits) sleep 1 subject.block_until_ready @@ -136,9 +140,9 @@ context 'between matcher' do before do - stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?since/) + stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.1&since/) .to_return(status: 200, body: semver_between_matcher_splits) - stub_request(:get, 'https://sdk.split.io/api/splitChanges?since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') .to_return(status: 200, body: semver_between_matcher_splits) sleep 1 subject.block_until_ready From 418e5d799c21c2f674fcbb0831ba880310db0c77 Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Wed, 1 May 2024 10:14:33 -0700 Subject: [PATCH 31/37] added debug to split logger --- lib/splitclient-rb/split_logger.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/splitclient-rb/split_logger.rb b/lib/splitclient-rb/split_logger.rb index bf7a899e..fa901744 100644 --- a/lib/splitclient-rb/split_logger.rb +++ b/lib/splitclient-rb/split_logger.rb @@ -15,5 +15,9 @@ def log_if_transport(message) def error(message) @config.logger.error(message) end + + def debug(message) + @config.logger.debug(message) if @config.debug_enabled + end end end From b7ecdd1fd45beed3b65d0dfced33c3949534eb6c Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Wed, 1 May 2024 10:25:39 -0700 Subject: [PATCH 32/37] update debug logging in semver matchers --- lib/splitclient-rb/engine/matchers/between_semver_matcher.rb | 4 ++-- lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb | 4 ++-- .../matchers/greater_than_or_equal_to_semver_matcher.rb | 4 ++-- lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb | 4 ++-- .../engine/matchers/less_than_or_equal_to_semver_matcher.rb | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb index e5c2865e..e175c541 100644 --- a/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb @@ -16,7 +16,7 @@ def initialize(attribute, start_value, end_value, logger, validator) end def match?(args) - @logger.log_if_debug('[BetweenSemverMatcher] evaluating value and attributes.') + @logger.debug('[BetweenSemverMatcher] evaluating value and attributes.') return false unless @validator.valid_matcher_arguments(args) value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) @@ -26,7 +26,7 @@ def match?(args) end matches = ([0, -1].include?(@semver_start.compare(value_to_match)) && [0, 1].include?(@semver_end.compare(value_to_match))) - @logger.log_if_debug("[BetweenMatcher] #{value_to_match} matches -> #{matches}") + @logger.debug("[BetweenMatcher] #{value_to_match} matches -> #{matches}") matches end end diff --git a/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb index 49151599..c77f78a1 100644 --- a/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb @@ -15,7 +15,7 @@ def initialize(attribute, string_value, logger, validator) end def match?(args) - @logger.log_if_debug('[EqualsToSemverMatcher] evaluating value and attributes.') + @logger.debug('[EqualsToSemverMatcher] evaluating value and attributes.') return false unless @validator.valid_matcher_arguments(args) value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) @@ -24,7 +24,7 @@ def match?(args) return false end matches = (@semver.version == value_to_match.version) - @logger.log_if_debug("[EqualsToSemverMatcher] #{value_to_match} matches -> #{matches}") + @logger.debug("[EqualsToSemverMatcher] #{value_to_match} matches -> #{matches}") matches end end diff --git a/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb index 6eef18ff..ffe46aef 100644 --- a/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb @@ -15,7 +15,7 @@ def initialize(attribute, string_value, logger, validator) end def match?(args) - @logger.log_if_debug('[GreaterThanOrEqualsToSemverMatcher] evaluating value and attributes.') + @logger.debug('[GreaterThanOrEqualsToSemverMatcher] evaluating value and attributes.') return false unless @validator.valid_matcher_arguments(args) value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) @@ -24,7 +24,7 @@ def match?(args) return false end matches = [0, 1].include?(value_to_match.compare(@semver)) - @logger.log_if_debug("[GreaterThanOrEqualsToSemverMatcher] #{value_to_match} matches -> #{matches}") + @logger.debug("[GreaterThanOrEqualsToSemverMatcher] #{value_to_match} matches -> #{matches}") matches end end diff --git a/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb index 45db3052..298bc0b0 100644 --- a/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb @@ -15,7 +15,7 @@ def initialize(attribute, list_value, logger, validator) end def match?(args) - @logger.log_if_debug('[InListSemverMatcher] evaluating value and attributes.') + @logger.debug('[InListSemverMatcher] evaluating value and attributes.') return false unless @validator.valid_matcher_arguments(args) && args[:attributes][@attribute.to_sym].is_a?(String) value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) @@ -24,7 +24,7 @@ def match?(args) return false end matches = (@semver_list.map { |item| item.version == value_to_match.version }).any? { |item| item == true } - @logger.log_if_debug("[InListSemverMatcher] #{value_to_match} matches -> #{matches}") + @logger.debug("[InListSemverMatcher] #{value_to_match} matches -> #{matches}") matches end end diff --git a/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb index 2dd8ff36..081a7317 100644 --- a/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb @@ -15,7 +15,7 @@ def initialize(attribute, string_value, logger, validator) end def match?(args) - @logger.log_if_debug('[LessThanOrEqualsToSemverMatcher] evaluating value and attributes.') + @logger.debug('[LessThanOrEqualsToSemverMatcher] evaluating value and attributes.') return false unless @validator.valid_matcher_arguments(args) value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) @@ -24,7 +24,7 @@ def match?(args) return false end matches = [0, -1].include?(value_to_match.compare(@semver)) - @logger.log_if_debug("[LessThanOrEqualsToSemverMatcher] #{value_to_match} matches -> #{matches}") + @logger.debug("[LessThanOrEqualsToSemverMatcher] #{value_to_match} matches -> #{matches}") matches end end From 1bc71a58026b8ee336a37d5d216aa08532ecb2b4 Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Wed, 1 May 2024 10:59:58 -0700 Subject: [PATCH 33/37] fixed spec --- spec/engine/matchers/semver_matchers_integration_spec.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/spec/engine/matchers/semver_matchers_integration_spec.rb b/spec/engine/matchers/semver_matchers_integration_spec.rb index 9b5a7858..86909af5 100644 --- a/spec/engine/matchers/semver_matchers_integration_spec.rb +++ b/spec/engine/matchers/semver_matchers_integration_spec.rb @@ -68,6 +68,7 @@ it 'validates the treatment is the default treatment for incorrect attributes hash and nil' do expect(subject.get_treatment(user, 'semver_equalto')).to eq 'off' expect(subject.get_treatment(user, 'semver_equalto', {:version => "1.22.10"})).to eq 'off' + sleep 0.2 subject.destroy() end end @@ -90,6 +91,7 @@ it 'validates the treatment is the default treatment for incorrect attributes hash and nil' do expect(subject.get_treatment(user, 'semver_greater_or_equalto')).to eq 'off' expect(subject.get_treatment(user, 'semver_greater_or_equalto', {:version => "1.22.8"})).to eq 'off' + sleep 0.2 subject.destroy() end end @@ -112,6 +114,7 @@ it 'validates the treatment is the default treatment for incorrect attributes hash and nil' do expect(subject.get_treatment(user, 'semver_less_or_equalto')).to eq 'off' expect(subject.get_treatment(user, 'semver_less_or_equalto', {:version => "1.22.10"})).to eq 'off' + sleep 0.2 subject.destroy() end end @@ -134,6 +137,7 @@ it 'validates the treatment is the default treatment for incorrect attributes hash and nil' do expect(subject.get_treatment(user, 'semver_inlist')).to eq 'off' expect(subject.get_treatment(user, 'semver_inlist', {:version => "1.22.10"})).to eq 'off' + sleep 0.2 subject.destroy() end end @@ -157,6 +161,7 @@ expect(subject.get_treatment(user, 'semver_between')).to eq 'off' expect(subject.get_treatment(user, 'semver_between', {:version => "1.22.9-rc1"})).to eq 'off' expect(subject.get_treatment(user, 'semver_between', {:version => "2.1.1"})).to eq 'off' + sleep 0.2 subject.destroy() end end From abea9d2ce24e9f5c447c6dc011eb296628cf6791 Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Wed, 1 May 2024 13:58:45 -0700 Subject: [PATCH 34/37] polishing --- .../engine/matchers/between_semver_matcher.rb | 4 ++-- .../engine/matchers/equal_to_semver_matcher.rb | 9 +++------ .../greater_than_or_equal_to_semver_matcher.rb | 9 +++------ .../engine/matchers/in_list_semver_matcher.rb | 3 +-- .../less_than_or_equal_to_semver_matcher.rb | 9 +++------ lib/splitclient-rb/engine/matchers/matcher.rb | 18 ++++++++++++++++++ 6 files changed, 30 insertions(+), 22 deletions(-) diff --git a/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb index e175c541..d3c23380 100644 --- a/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb @@ -16,13 +16,13 @@ def initialize(attribute, start_value, end_value, logger, validator) end def match?(args) - @logger.debug('[BetweenSemverMatcher] evaluating value and attributes.') - return false unless @validator.valid_matcher_arguments(args) + return false if !verify_semver_arg?(args, "BetweenSemverMatcher") value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) unless !value_to_match.nil? && !@semver_start.nil? && !@semver_end.nil? @logger.error('betweenStringMatcherData is required for BETWEEN_SEMVER matcher type') return false + end matches = ([0, -1].include?(@semver_start.compare(value_to_match)) && [0, 1].include?(@semver_end.compare(value_to_match))) diff --git a/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb index c77f78a1..d200462c 100644 --- a/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb @@ -15,14 +15,11 @@ def initialize(attribute, string_value, logger, validator) end def match?(args) - @logger.debug('[EqualsToSemverMatcher] evaluating value and attributes.') - return false unless @validator.valid_matcher_arguments(args) + return false if !verify_semver_arg?(args, "EqualsToSemverMatcher") value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) - unless !value_to_match.nil? && !@semver.nil? - @logger.error('stringMatcherData is required for EQUAL_TO_SEMVER matcher type') - return false - end + return false if !check_semver_value_to_match(value_to_match, MATCHER_TYPE) + matches = (@semver.version == value_to_match.version) @logger.debug("[EqualsToSemverMatcher] #{value_to_match} matches -> #{matches}") matches diff --git a/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb index ffe46aef..7f6d7da3 100644 --- a/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb @@ -15,14 +15,11 @@ def initialize(attribute, string_value, logger, validator) end def match?(args) - @logger.debug('[GreaterThanOrEqualsToSemverMatcher] evaluating value and attributes.') - return false unless @validator.valid_matcher_arguments(args) + return false if !verify_semver_arg?(args, "GreaterThanOrEqualsToSemverMatcher") value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) - unless !value_to_match.nil? && !@semver.nil? - @logger.error('stringMatcherData is required for GREATER_THAN_OR_EQUAL_TO_SEMVER matcher type') - return false - end + return false if !check_semver_value_to_match(value_to_match, MATCHER_TYPE) + matches = [0, 1].include?(value_to_match.compare(@semver)) @logger.debug("[GreaterThanOrEqualsToSemverMatcher] #{value_to_match} matches -> #{matches}") matches diff --git a/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb index 298bc0b0..0681fe18 100644 --- a/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb @@ -15,8 +15,7 @@ def initialize(attribute, list_value, logger, validator) end def match?(args) - @logger.debug('[InListSemverMatcher] evaluating value and attributes.') - return false unless @validator.valid_matcher_arguments(args) && args[:attributes][@attribute.to_sym].is_a?(String) + return false if !verify_semver_arg?(args, "InListSemverMatcher") value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) unless !value_to_match.nil? && @semver_list.all? { |n| !n.nil? } diff --git a/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb index 081a7317..1141411f 100644 --- a/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb @@ -15,14 +15,11 @@ def initialize(attribute, string_value, logger, validator) end def match?(args) - @logger.debug('[LessThanOrEqualsToSemverMatcher] evaluating value and attributes.') - return false unless @validator.valid_matcher_arguments(args) + return false if !verify_semver_arg?(args, "LessThanOrEqualsToSemverMatcher") value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) - unless !value_to_match.nil? && !@semver.nil? - @logger.error('stringMatcherData is required for LESS_THAN_OR_EQUAL_TO_SEMVER matcher type') - return false - end + return false if !check_semver_value_to_match(value_to_match, MATCHER_TYPE) + matches = [0, -1].include?(value_to_match.compare(@semver)) @logger.debug("[LessThanOrEqualsToSemverMatcher] #{value_to_match} matches -> #{matches}") matches diff --git a/lib/splitclient-rb/engine/matchers/matcher.rb b/lib/splitclient-rb/engine/matchers/matcher.rb index 9f7a43d6..c46f7d69 100644 --- a/lib/splitclient-rb/engine/matchers/matcher.rb +++ b/lib/splitclient-rb/engine/matchers/matcher.rb @@ -30,5 +30,23 @@ def equals?(obj) def string_type? false end + + private + + def verify_semver_arg?(args, matcher_name) + @logger.debug("[#{matcher_name}] evaluating value and attributes.") + return false unless @validator.valid_matcher_arguments(args) + + true + end + + def check_semver_value_to_match(value_to_match, matcher_spec_name) + unless !value_to_match.nil? && !@semver.nil? + @logger.error("stringMatcherData is required for #{matcher_spec_name} matcher type") + return false + + end + true + end end end From 581d6e3d3f48a5ed7b9761c980c167897590a9c5 Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Wed, 1 May 2024 14:12:15 -0700 Subject: [PATCH 35/37] polish --- lib/splitclient-rb/engine/matchers/between_semver_matcher.rb | 2 +- lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb | 4 ++-- .../matchers/greater_than_or_equal_to_semver_matcher.rb | 4 ++-- lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb | 2 +- .../engine/matchers/less_than_or_equal_to_semver_matcher.rb | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb index d3c23380..315586ed 100644 --- a/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb @@ -16,7 +16,7 @@ def initialize(attribute, start_value, end_value, logger, validator) end def match?(args) - return false if !verify_semver_arg?(args, "BetweenSemverMatcher") + return false unless verify_semver_arg?(args, 'BetweenSemverMatcher') value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) unless !value_to_match.nil? && !@semver_start.nil? && !@semver_end.nil? diff --git a/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb index d200462c..476467b3 100644 --- a/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb @@ -15,10 +15,10 @@ def initialize(attribute, string_value, logger, validator) end def match?(args) - return false if !verify_semver_arg?(args, "EqualsToSemverMatcher") + return false unless verify_semver_arg?(args, 'EqualsToSemverMatcher') value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) - return false if !check_semver_value_to_match(value_to_match, MATCHER_TYPE) + return false unless check_semver_value_to_match(value_to_match, MATCHER_TYPE) matches = (@semver.version == value_to_match.version) @logger.debug("[EqualsToSemverMatcher] #{value_to_match} matches -> #{matches}") diff --git a/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb index 7f6d7da3..7f11f8df 100644 --- a/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb @@ -15,10 +15,10 @@ def initialize(attribute, string_value, logger, validator) end def match?(args) - return false if !verify_semver_arg?(args, "GreaterThanOrEqualsToSemverMatcher") + return false unless verify_semver_arg?(args, 'GreaterThanOrEqualsToSemverMatcher') value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) - return false if !check_semver_value_to_match(value_to_match, MATCHER_TYPE) + return false unless check_semver_value_to_match(value_to_match, MATCHER_TYPE) matches = [0, 1].include?(value_to_match.compare(@semver)) @logger.debug("[GreaterThanOrEqualsToSemverMatcher] #{value_to_match} matches -> #{matches}") diff --git a/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb index 0681fe18..9d481325 100644 --- a/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb @@ -15,7 +15,7 @@ def initialize(attribute, list_value, logger, validator) end def match?(args) - return false if !verify_semver_arg?(args, "InListSemverMatcher") + return false unless verify_semver_arg?(args, 'InListSemverMatcher') value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) unless !value_to_match.nil? && @semver_list.all? { |n| !n.nil? } diff --git a/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb index 1141411f..5ca527c5 100644 --- a/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb @@ -15,10 +15,10 @@ def initialize(attribute, string_value, logger, validator) end def match?(args) - return false if !verify_semver_arg?(args, "LessThanOrEqualsToSemverMatcher") + return false unless verify_semver_arg?(args, 'LessThanOrEqualsToSemverMatcher') value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) - return false if !check_semver_value_to_match(value_to_match, MATCHER_TYPE) + return false unless check_semver_value_to_match(value_to_match, MATCHER_TYPE) matches = [0, -1].include?(value_to_match.compare(@semver)) @logger.debug("[LessThanOrEqualsToSemverMatcher] #{value_to_match} matches -> #{matches}") From 50fe596cb57d16e3b4bda09854ed365c3a75c994 Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Thu, 2 May 2024 09:15:07 -0700 Subject: [PATCH 36/37] polish --- .../cache/repositories/splits_repository.rb | 2 +- .../engine/matchers/between_semver_matcher.rb | 2 +- .../engine/matchers/in_list_semver_matcher.rb | 9 +++++++-- lib/splitclient-rb/engine/matchers/semver.rb | 3 ++- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/splitclient-rb/cache/repositories/splits_repository.rb b/lib/splitclient-rb/cache/repositories/splits_repository.rb index 1bbbb953..e98e0d84 100644 --- a/lib/splitclient-rb/cache/repositories/splits_repository.rb +++ b/lib/splitclient-rb/cache/repositories/splits_repository.rb @@ -29,7 +29,7 @@ class SplitsRepository < Repository size: 100 } ], - label: "unsupported matcher type" + label: "targeting rule type unsupported by sdk" }] def initialize(config, flag_sets_repository, flag_set_filter) diff --git a/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb index 315586ed..71bd072b 100644 --- a/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb @@ -19,7 +19,7 @@ def match?(args) return false unless verify_semver_arg?(args, 'BetweenSemverMatcher') value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) - unless !value_to_match.nil? && !@semver_start.nil? && !@semver_end.nil? + if value_to_match.nil? || @semver_start.nil? || @semver_end.nil? @logger.error('betweenStringMatcherData is required for BETWEEN_SEMVER matcher type') return false diff --git a/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb index 9d481325..b1f2bb23 100644 --- a/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb @@ -10,7 +10,12 @@ def initialize(attribute, list_value, logger, validator) super(logger) @validator = validator @attribute = attribute - @semver_list = list_value.map { |item| SplitIoClient::Semver.build(item, logger) } + @semver_list = [] + + list_value.map do |item| + version = SplitIoClient::Semver.build(item, logger) + @semver_list << version unless version.nil? + end @logger = logger end @@ -18,7 +23,7 @@ def match?(args) return false unless verify_semver_arg?(args, 'InListSemverMatcher') value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) - unless !value_to_match.nil? && @semver_list.all? { |n| !n.nil? } + if value_to_match.nil? @logger.error('whitelistMatcherData is required for IN_LIST_SEMVER matcher type') return false end diff --git a/lib/splitclient-rb/engine/matchers/semver.rb b/lib/splitclient-rb/engine/matchers/semver.rb index bb09ec0f..b660b9be 100644 --- a/lib/splitclient-rb/engine/matchers/semver.rb +++ b/lib/splitclient-rb/engine/matchers/semver.rb @@ -60,7 +60,8 @@ def compare(to_compare) return 0 if @version == to_compare.version # Compare major, minor, and patch versions numerically - return compare_attributes(to_compare) if compare_attributes(to_compare) != 0 + result = compare_attributes(to_compare) + return result if result != 0 # Compare pre-release versions lexically compare_pre_release(to_compare) From 2a54d08d4bc9cf0f8c633702c2d7b4a6b13ac9f0 Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Thu, 2 May 2024 12:02:33 -0700 Subject: [PATCH 37/37] polish and test update --- .../engine/matchers/in_list_semver_matcher.rb | 3 ++- lib/splitclient-rb/engine/matchers/matcher.rb | 2 +- lib/splitclient-rb/engine/matchers/semver.rb | 16 ++++++++++++++-- spec/engine/matchers/semver_spec.rb | 7 +++++++ 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb index b1f2bb23..7b411ae7 100644 --- a/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb @@ -20,12 +20,13 @@ def initialize(attribute, list_value, logger, validator) end def match?(args) - return false unless verify_semver_arg?(args, 'InListSemverMatcher') + return false if @semver_list.empty? || !verify_semver_arg?(args, 'InListSemverMatcher') value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) if value_to_match.nil? @logger.error('whitelistMatcherData is required for IN_LIST_SEMVER matcher type') return false + end matches = (@semver_list.map { |item| item.version == value_to_match.version }).any? { |item| item == true } @logger.debug("[InListSemverMatcher] #{value_to_match} matches -> #{matches}") diff --git a/lib/splitclient-rb/engine/matchers/matcher.rb b/lib/splitclient-rb/engine/matchers/matcher.rb index c46f7d69..2a54bf00 100644 --- a/lib/splitclient-rb/engine/matchers/matcher.rb +++ b/lib/splitclient-rb/engine/matchers/matcher.rb @@ -41,7 +41,7 @@ def verify_semver_arg?(args, matcher_name) end def check_semver_value_to_match(value_to_match, matcher_spec_name) - unless !value_to_match.nil? && !@semver.nil? + if value_to_match.nil? || @semver.nil? @logger.error("stringMatcherData is required for #{matcher_spec_name} matcher type") return false diff --git a/lib/splitclient-rb/engine/matchers/semver.rb b/lib/splitclient-rb/engine/matchers/semver.rb index b660b9be..ddc7c4ac 100644 --- a/lib/splitclient-rb/engine/matchers/semver.rb +++ b/lib/splitclient-rb/engine/matchers/semver.rb @@ -70,7 +70,7 @@ def compare(to_compare) private def integer?(value) - value.to_i.to_s == value + !!value.match(/^(\d)+$/) end # @@ -107,10 +107,21 @@ def assign_major_minor_and_patch(version) @minor = parts[1].to_i @patch = parts[2].to_i @version = "#{@major}#{VALUE_DELIMITER}#{@minor}#{VALUE_DELIMITER}#{@patch}" - @version += "#{PRE_RELEASE_DELIMITER}#{@pre_release.join('.')}" unless @pre_release.empty? + @version += parse_pre_release @version += "#{METADATA_DELIMITER}#{@metadata}" unless @metadata.empty? end + def parse_pre_release + return '' if @pre_release.empty? + + pre_parsed = [] + @pre_release.each do |pre_digit| + pre_digit = pre_digit.to_i if integer?(pre_digit) + pre_parsed << pre_digit + end + "#{PRE_RELEASE_DELIMITER}#{pre_parsed.join('.')}" + end + # # Compare 2 variables and return int as follows: # 0: if var1 == var2 @@ -169,6 +180,7 @@ def compare_pre_release(to_compare) if integer?(@pre_release[i]) && integer?(to_compare.pre_release[i]) return compare_vars(@pre_release[i].to_i, to_compare.pre_release[i].to_i) + end return compare_vars(@pre_release[i], to_compare.pre_release[i]) diff --git a/spec/engine/matchers/semver_spec.rb b/spec/engine/matchers/semver_spec.rb index a10f64bf..2e1ca0f1 100644 --- a/spec/engine/matchers/semver_spec.rb +++ b/spec/engine/matchers/semver_spec.rb @@ -33,6 +33,13 @@ expect(described_class.build(version[0], logger)).to eq(nil) end end + + it 'verify leading-zero integers are converted' do + semver = described_class.build('1.01.2', logger) + expect(semver.version).to eq('1.1.2') + expect(described_class.build('1.01.2-rc.04', logger).version).to eq('1.1.2-rc.4') + end + end context 'compare versions' do