Skip to content

Commit f76b944

Browse files
authored
Merge pull request #521 from splitio/semver-integration-spec
semver matchers integrations spec
2 parents 89826a3 + 9f1b7d9 commit f76b944

File tree

10 files changed

+666
-0
lines changed

10 files changed

+666
-0
lines changed

lib/splitclient-rb.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@
9595
require 'splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher'
9696
require 'splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher'
9797
require 'splitclient-rb/engine/matchers/between_semver_matcher'
98+
require 'splitclient-rb/engine/matchers/in_list_semver_matcher'
9899
require 'splitclient-rb/engine/evaluator/splitter'
99100
require 'splitclient-rb/engine/impressions/noop_unique_keys_tracker'
100101
require 'splitclient-rb/engine/impressions/unique_keys_tracker'
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# frozen_string_literal: true
2+
3+
module SplitIoClient
4+
class InListSemverMatcher < Matcher
5+
MATCHER_TYPE = 'IN_LIST_SEMVER'
6+
7+
attr_reader :attribute
8+
9+
def initialize(attribute, list_value, logger, validator)
10+
super(logger)
11+
@validator = validator
12+
@attribute = attribute
13+
@semver_list = list_value.map { |item| SplitIoClient::Semver.new(item) }
14+
@logger = logger
15+
end
16+
17+
def match?(args)
18+
@logger.log_if_debug('[InListSemverMatcher] evaluating value and attributes.')
19+
return false unless @validator.valid_matcher_arguments(args)
20+
21+
value_to_match = args[:attributes][@attribute.to_sym]
22+
unless value_to_match.is_a?(String)
23+
@logger.log_if_debug('whitelistMatcherData is required for IN_LIST_SEMVER matcher type')
24+
return false
25+
end
26+
matches = (@semver_list.map { |item| item.compare(SplitIoClient::Semver.new(value_to_match)) }).any?(&:zero?)
27+
@logger.log_if_debug("[InListSemverMatcher] #{value_to_match} matches -> #{matches}")
28+
matches
29+
end
30+
end
31+
end

lib/splitclient-rb/engine/parser/condition.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,14 @@ def matcher_between_semver(params)
222222
)
223223
end
224224

225+
def matcher_in_list_semver(params)
226+
InListSemverMatcher.new(
227+
params[:matcher][:keySelector][:attribute],
228+
params[:matcher][:whitelistMatcherData][:whitelist],
229+
@config.split_logger, @config.split_validator
230+
)
231+
end
232+
225233
#
226234
# @return [object] the negate value for this condition
227235
def negate
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper'
4+
5+
describe SplitIoClient::InListSemverMatcher do
6+
let(:raw) { {
7+
'negate': false,
8+
'matcherType': 'INLIST_SEMVER',
9+
'whitelistMatcherData': {"whitelist": ["2.1.8", "2.1.11"]}
10+
} }
11+
let(:config) { SplitIoClient::SplitConfig.new }
12+
13+
it 'initilized params' do
14+
matcher = described_class.new("version", raw[:whitelistMatcherData][:whitelist], config.split_logger, config.split_validator)
15+
expect(matcher.attribute).to eq("version")
16+
semver_list = matcher.instance_variable_get(:@semver_list)
17+
expect(semver_list[0].instance_variable_get(:@old_version)).to eq("2.1.8")
18+
expect(semver_list[1].instance_variable_get(:@old_version)).to eq("2.1.11")
19+
end
20+
21+
it 'matches' do
22+
matcher = described_class.new("version", raw[:whitelistMatcherData][:whitelist], config.split_logger, config.split_validator)
23+
expect(matcher.match?(:attributes=>{"version": "2.1.8+rc"})).to eq(true)
24+
expect(matcher.match?(:attributes=>{"version": "2.1.11"})).to eq(true)
25+
end
26+
27+
it 'does not match' do
28+
matcher = described_class.new("version", raw[:whitelistMatcherData][:whitelist], config.split_logger, config.split_validator)
29+
expect(matcher.match?(:attributes=>{"version": "2.1.7"})).to eq(false)
30+
expect(matcher.match?(:attributes=>{"version": "2.1.11-rc12"})).to eq(false)
31+
expect(matcher.match?(:attributes=>{"version": "2.1.8-rc1"})).to eq(false)
32+
end
33+
34+
it 'invalid attribute' do
35+
matcher = described_class.new("version", raw[:whitelistMatcherData][:whitelist], config.split_logger, config.split_validator)
36+
expect(matcher.match?(:attributes=>{"version": 2.1})).to eq(false)
37+
expect(matcher.match?(:attributes=>{"version": nil})).to eq(false)
38+
end
39+
40+
end
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper'
4+
5+
describe 'Semver matchers integration' do
6+
subject do
7+
SplitIoClient::SplitFactory.new('test_api_key', {
8+
logger: Logger.new(log),
9+
streaming_enabled: false,
10+
impressions_refresh_rate: 9999,
11+
impressions_mode: :none,
12+
features_refresh_rate: 9999,
13+
telemetry_refresh_rate: 99999}).client
14+
end
15+
16+
let(:log) { StringIO.new }
17+
18+
let(:semver_between_matcher_splits) do
19+
File.read(File.expand_path(File.join(File.dirname(__FILE__),
20+
'../../test_data/splits/semver_matchers/semver_between.json')))
21+
end
22+
23+
let(:semver_equalto_matcher_splits) do
24+
File.read(File.expand_path(File.join(File.dirname(__FILE__),
25+
'../../test_data/splits/semver_matchers/semver_equalto.json')))
26+
end
27+
28+
let(:semver_greater_or_equalto_matcher_splits) do
29+
File.read(File.expand_path(File.join(File.dirname(__FILE__),
30+
'../../test_data/splits/semver_matchers/semver_greater_or_equalto.json')))
31+
end
32+
33+
let(:semver_less_or_equalto_matcher_splits) do
34+
File.read(File.expand_path(File.join(File.dirname(__FILE__),
35+
'../../test_data/splits/semver_matchers/semver_less_or_equalto.json')))
36+
end
37+
38+
let(:semver_inlist_matcher_splits) do
39+
File.read(File.expand_path(File.join(File.dirname(__FILE__),
40+
'../../test_data/splits/semver_matchers/semver_inlist.json')))
41+
end
42+
43+
let(:user) { 'fake_user_id_1' }
44+
45+
before do
46+
stub_request(:any, /https:\/\/telemetry.*/).to_return(status: 200, body: 'ok')
47+
stub_request(:any, /https:\/\/events.*/).to_return(status: 200, body: "", headers: {})
48+
end
49+
50+
context 'equal to matcher' do
51+
before do
52+
stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?since/)
53+
.to_return(status: 200, body: semver_equalto_matcher_splits)
54+
stub_request(:get, 'https://sdk.split.io/api/splitChanges?since=-1')
55+
.to_return(status: 200, body: semver_equalto_matcher_splits)
56+
sleep 1
57+
subject.block_until_ready
58+
end
59+
60+
it 'validates the treatment is ON for correct attribute value' do
61+
expect(subject.get_treatment(user, 'semver_equalto', {:version => "1.22.9"})).to eq 'on'
62+
end
63+
64+
it 'validates the treatment is the default treatment for incorrect attributes hash and nil' do
65+
expect(subject.get_treatment(user, 'semver_equalto')).to eq 'off'
66+
expect(subject.get_treatment(user, 'semver_equalto', {:version => "1.22.10"})).to eq 'off'
67+
subject.destroy()
68+
end
69+
end
70+
71+
context 'greater than or equal to matcher' do
72+
before do
73+
stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?since/)
74+
.to_return(status: 200, body: semver_greater_or_equalto_matcher_splits)
75+
stub_request(:get, 'https://sdk.split.io/api/splitChanges?since=-1')
76+
.to_return(status: 200, body: semver_greater_or_equalto_matcher_splits)
77+
sleep 1
78+
subject.block_until_ready
79+
end
80+
81+
it 'validates the treatment is ON for correct attribute value' do
82+
expect(subject.get_treatment(user, 'semver_greater_or_equalto', {:version => "1.22.9"})).to eq 'on'
83+
expect(subject.get_treatment(user, 'semver_greater_or_equalto', {:version => "1.22.10"})).to eq 'on'
84+
end
85+
86+
it 'validates the treatment is the default treatment for incorrect attributes hash and nil' do
87+
expect(subject.get_treatment(user, 'semver_greater_or_equalto')).to eq 'off'
88+
expect(subject.get_treatment(user, 'semver_greater_or_equalto', {:version => "1.22.8"})).to eq 'off'
89+
subject.destroy()
90+
end
91+
end
92+
93+
context 'less than or equal to matcher' do
94+
before do
95+
stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?since/)
96+
.to_return(status: 200, body: semver_less_or_equalto_matcher_splits)
97+
stub_request(:get, 'https://sdk.split.io/api/splitChanges?since=-1')
98+
.to_return(status: 200, body: semver_less_or_equalto_matcher_splits)
99+
sleep 1
100+
subject.block_until_ready
101+
end
102+
103+
it 'validates the treatment is ON for correct attribute value' do
104+
expect(subject.get_treatment(user, 'semver_less_or_equalto', {:version => "1.22.9"})).to eq 'on'
105+
expect(subject.get_treatment(user, 'semver_less_or_equalto', {:version => "1.22.8"})).to eq 'on'
106+
end
107+
108+
it 'validates the treatment is the default treatment for incorrect attributes hash and nil' do
109+
expect(subject.get_treatment(user, 'semver_less_or_equalto')).to eq 'off'
110+
expect(subject.get_treatment(user, 'semver_less_or_equalto', {:version => "1.22.10"})).to eq 'off'
111+
subject.destroy()
112+
end
113+
end
114+
115+
context 'in list matcher' do
116+
before do
117+
stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?since/)
118+
.to_return(status: 200, body: semver_inlist_matcher_splits)
119+
stub_request(:get, 'https://sdk.split.io/api/splitChanges?since=-1')
120+
.to_return(status: 200, body: semver_inlist_matcher_splits)
121+
sleep 1
122+
subject.block_until_ready
123+
end
124+
125+
it 'validates the treatment is ON for correct attribute value' do
126+
expect(subject.get_treatment(user, 'semver_inlist', {:version => "1.22.9"})).to eq 'on'
127+
expect(subject.get_treatment(user, 'semver_inlist', {:version => "2.1.0"})).to eq 'on'
128+
end
129+
130+
it 'validates the treatment is the default treatment for incorrect attributes hash and nil' do
131+
expect(subject.get_treatment(user, 'semver_inlist')).to eq 'off'
132+
expect(subject.get_treatment(user, 'semver_inlist', {:version => "1.22.10"})).to eq 'off'
133+
subject.destroy()
134+
end
135+
end
136+
137+
context 'between matcher' do
138+
before do
139+
stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?since/)
140+
.to_return(status: 200, body: semver_between_matcher_splits)
141+
stub_request(:get, 'https://sdk.split.io/api/splitChanges?since=-1')
142+
.to_return(status: 200, body: semver_between_matcher_splits)
143+
sleep 1
144+
subject.block_until_ready
145+
end
146+
147+
it 'validates the treatment is ON for correct attribute value' do
148+
expect(subject.get_treatment(user, 'semver_between', {:version => "1.22.9"})).to eq 'on'
149+
expect(subject.get_treatment(user, 'semver_between', {:version => "2.0.10"})).to eq 'on'
150+
end
151+
152+
it 'validates the treatment is the default treatment for incorrect attributes hash and nil' do
153+
expect(subject.get_treatment(user, 'semver_between')).to eq 'off'
154+
expect(subject.get_treatment(user, 'semver_between', {:version => "1.22.9-rc1"})).to eq 'off'
155+
expect(subject.get_treatment(user, 'semver_between', {:version => "2.1.1"})).to eq 'off'
156+
subject.destroy()
157+
end
158+
end
159+
end
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
{
2+
"splits": [
3+
{
4+
"trafficTypeName": "user",
5+
"name": "semver_between",
6+
"trafficAllocation": 100,
7+
"trafficAllocationSeed": 1068038034,
8+
"seed": -1053389887,
9+
"status": "ACTIVE",
10+
"killed": false,
11+
"defaultTreatment": "off",
12+
"changeNumber": 1675259356568,
13+
"algo": 2,
14+
"configurations": {},
15+
"conditions": [
16+
{
17+
"conditionType": "ROLLOUT",
18+
"matcherGroup": {
19+
"combiner": "AND",
20+
"matchers":
21+
[
22+
{"keySelector": {"trafficType": "user", "attribute": "version"},
23+
"matcherType": "BETWEEN_SEMVER",
24+
"negate": false,
25+
"userDefinedSegmentMatcherData": null,
26+
"whitelistMatcherData": null,
27+
"unaryNumericMatcherData": null,
28+
"betweenMatcherData": null,
29+
"dependencyMatcherData": null,
30+
"booleanMatcherData": null,
31+
"stringMatcherData": null,
32+
"betweenStringMatcherData": {"start": "1.22.9", "end": "2.1.0"}}
33+
]
34+
},
35+
"partitions": [
36+
{
37+
"treatment": "on",
38+
"size": 100
39+
},
40+
{
41+
"treatment": "off",
42+
"size": 0
43+
}
44+
],
45+
"label": "between semver"
46+
},
47+
{
48+
"conditionType": "ROLLOUT",
49+
"matcherGroup": {
50+
"combiner": "AND",
51+
"matchers": [
52+
{
53+
"keySelector": {
54+
"trafficType": "user",
55+
"attribute": null
56+
},
57+
"matcherType": "ALL_KEYS",
58+
"negate": false,
59+
"userDefinedSegmentMatcherData": null,
60+
"whitelistMatcherData": null,
61+
"unaryNumericMatcherData": null,
62+
"betweenMatcherData": null,
63+
"booleanMatcherData": null,
64+
"dependencyMatcherData": null,
65+
"stringMatcherData": null
66+
}
67+
]
68+
},
69+
"partitions": [
70+
{
71+
"treatment": "on",
72+
"size": 0
73+
},
74+
{
75+
"treatment": "off",
76+
"size": 100
77+
}
78+
],
79+
"label": "default rule"
80+
}
81+
]
82+
}
83+
],
84+
"since": -1,
85+
"till": 1675259356568
86+
}

0 commit comments

Comments
 (0)