split 4.0.1 → 4.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +8 -3
- data/.rubocop.yml +2 -5
- data/CHANGELOG.md +38 -0
- data/CONTRIBUTING.md +1 -1
- data/Gemfile +1 -1
- data/README.md +11 -3
- data/Rakefile +4 -5
- data/gemfiles/5.2.gemfile +1 -3
- data/gemfiles/6.0.gemfile +1 -3
- data/gemfiles/6.1.gemfile +1 -3
- data/gemfiles/7.0.gemfile +1 -3
- data/lib/split/algorithms/block_randomization.rb +5 -6
- data/lib/split/algorithms/whiplash.rb +16 -18
- data/lib/split/algorithms.rb +14 -0
- data/lib/split/alternative.rb +21 -22
- data/lib/split/cache.rb +0 -1
- data/lib/split/combined_experiments_helper.rb +4 -4
- data/lib/split/configuration.rb +83 -84
- data/lib/split/dashboard/helpers.rb +6 -7
- data/lib/split/dashboard/pagination_helpers.rb +53 -54
- data/lib/split/dashboard/public/style.css +5 -2
- data/lib/split/dashboard/views/_experiment.erb +2 -1
- data/lib/split/dashboard/views/index.erb +19 -4
- data/lib/split/dashboard.rb +29 -23
- data/lib/split/encapsulated_helper.rb +4 -6
- data/lib/split/experiment.rb +93 -88
- data/lib/split/experiment_catalog.rb +6 -5
- data/lib/split/extensions/string.rb +1 -1
- data/lib/split/goals_collection.rb +8 -10
- data/lib/split/helper.rb +20 -20
- data/lib/split/metric.rb +4 -5
- data/lib/split/persistence/cookie_adapter.rb +44 -47
- data/lib/split/persistence/dual_adapter.rb +7 -8
- data/lib/split/persistence/redis_adapter.rb +3 -4
- data/lib/split/persistence/session_adapter.rb +0 -2
- data/lib/split/persistence.rb +4 -4
- data/lib/split/redis_interface.rb +7 -1
- data/lib/split/trial.rb +23 -24
- data/lib/split/user.rb +12 -13
- data/lib/split/version.rb +1 -1
- data/lib/split/zscore.rb +1 -3
- data/lib/split.rb +26 -25
- data/spec/algorithms/block_randomization_spec.rb +6 -5
- data/spec/algorithms/weighted_sample_spec.rb +6 -5
- data/spec/algorithms/whiplash_spec.rb +4 -5
- data/spec/alternative_spec.rb +35 -36
- data/spec/cache_spec.rb +15 -19
- data/spec/combined_experiments_helper_spec.rb +18 -17
- data/spec/configuration_spec.rb +32 -38
- data/spec/dashboard/pagination_helpers_spec.rb +69 -67
- data/spec/dashboard/paginator_spec.rb +10 -9
- data/spec/dashboard_helpers_spec.rb +19 -18
- data/spec/dashboard_spec.rb +79 -35
- data/spec/encapsulated_helper_spec.rb +12 -14
- data/spec/experiment_catalog_spec.rb +14 -13
- data/spec/experiment_spec.rb +132 -123
- data/spec/goals_collection_spec.rb +17 -15
- data/spec/helper_spec.rb +415 -382
- data/spec/metric_spec.rb +14 -14
- data/spec/persistence/cookie_adapter_spec.rb +23 -8
- data/spec/persistence/dual_adapter_spec.rb +71 -71
- data/spec/persistence/redis_adapter_spec.rb +28 -29
- data/spec/persistence/session_adapter_spec.rb +2 -3
- data/spec/persistence_spec.rb +1 -2
- data/spec/redis_interface_spec.rb +26 -14
- data/spec/spec_helper.rb +16 -13
- data/spec/split_spec.rb +11 -11
- data/spec/support/cookies_mock.rb +1 -2
- data/spec/trial_spec.rb +61 -60
- data/spec/user_spec.rb +36 -36
- data/split.gemspec +21 -20
- metadata +25 -14
- data/.rubocop_todo.yml +0 -226
- data/Appraisals +0 -19
- data/gemfiles/5.0.gemfile +0 -9
- data/gemfiles/5.1.gemfile +0 -9
data/spec/alternative_spec.rb
CHANGED
@@ -1,19 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require 'spec_helper'
|
3
|
-
require 'split/alternative'
|
4
2
|
|
5
|
-
|
3
|
+
require "spec_helper"
|
4
|
+
require "split/alternative"
|
6
5
|
|
6
|
+
describe Split::Alternative do
|
7
7
|
let(:alternative) {
|
8
|
-
Split::Alternative.new(
|
8
|
+
Split::Alternative.new("Basket", "basket_text")
|
9
9
|
}
|
10
10
|
|
11
11
|
let(:alternative2) {
|
12
|
-
Split::Alternative.new(
|
12
|
+
Split::Alternative.new("Cart", "basket_text")
|
13
13
|
}
|
14
14
|
|
15
15
|
let!(:experiment) {
|
16
|
-
Split::ExperimentCatalog.find_or_create({"basket_text" => ["purchase", "refund"]}, "Basket", "Cart")
|
16
|
+
Split::ExperimentCatalog.find_or_create({ "basket_text" => ["purchase", "refund"] }, "Basket", "Cart")
|
17
17
|
}
|
18
18
|
|
19
19
|
let(:goal1) { "purchase" }
|
@@ -24,48 +24,48 @@ describe Split::Alternative do
|
|
24
24
|
end
|
25
25
|
|
26
26
|
it "should have and only return the name" do
|
27
|
-
expect(alternative.name).to eq(
|
27
|
+
expect(alternative.name).to eq("Basket")
|
28
28
|
end
|
29
29
|
|
30
|
-
describe
|
30
|
+
describe "weights" do
|
31
31
|
it "should set the weights" do
|
32
|
-
experiment = Split::Experiment.new(
|
32
|
+
experiment = Split::Experiment.new("basket_text", alternatives: [{ "Basket" => 0.6 }, { "Cart" => 0.4 }])
|
33
33
|
first = experiment.alternatives[0]
|
34
|
-
expect(first.name).to eq(
|
34
|
+
expect(first.name).to eq("Basket")
|
35
35
|
expect(first.weight).to eq(0.6)
|
36
36
|
|
37
37
|
second = experiment.alternatives[1]
|
38
|
-
expect(second.name).to eq(
|
38
|
+
expect(second.name).to eq("Cart")
|
39
39
|
expect(second.weight).to eq(0.4)
|
40
40
|
end
|
41
41
|
|
42
42
|
it "accepts probability on alternatives" do
|
43
43
|
Split.configuration.experiments = {
|
44
|
-
:
|
45
|
-
:
|
46
|
-
{ :
|
47
|
-
{ :
|
48
|
-
{ :
|
44
|
+
my_experiment: {
|
45
|
+
alternatives: [
|
46
|
+
{ name: "control_opt", percent: 67 },
|
47
|
+
{ name: "second_opt", percent: 10 },
|
48
|
+
{ name: "third_opt", percent: 23 },
|
49
49
|
]
|
50
50
|
}
|
51
51
|
}
|
52
52
|
experiment = Split::Experiment.new(:my_experiment)
|
53
53
|
first = experiment.alternatives[0]
|
54
|
-
expect(first.name).to eq(
|
54
|
+
expect(first.name).to eq("control_opt")
|
55
55
|
expect(first.weight).to eq(0.67)
|
56
56
|
|
57
57
|
second = experiment.alternatives[1]
|
58
|
-
expect(second.name).to eq(
|
58
|
+
expect(second.name).to eq("second_opt")
|
59
59
|
expect(second.weight).to eq(0.1)
|
60
60
|
end
|
61
61
|
|
62
62
|
it "accepts probability on some alternatives" do
|
63
63
|
Split.configuration.experiments = {
|
64
|
-
:
|
65
|
-
:
|
66
|
-
{ :
|
64
|
+
my_experiment: {
|
65
|
+
alternatives: [
|
66
|
+
{ name: "control_opt", percent: 34 },
|
67
67
|
"second_opt",
|
68
|
-
{ :
|
68
|
+
{ name: "third_opt", percent: 23 },
|
69
69
|
"fourth_opt",
|
70
70
|
],
|
71
71
|
}
|
@@ -87,11 +87,11 @@ describe Split::Alternative do
|
|
87
87
|
#
|
88
88
|
it "allows name param without probability" do
|
89
89
|
Split.configuration.experiments = {
|
90
|
-
:
|
91
|
-
:
|
92
|
-
{ :
|
90
|
+
my_experiment: {
|
91
|
+
alternatives: [
|
92
|
+
{ name: "control_opt" },
|
93
93
|
"second_opt",
|
94
|
-
{ :
|
94
|
+
{ name: "third_opt", percent: 64 },
|
95
95
|
],
|
96
96
|
}
|
97
97
|
}
|
@@ -126,7 +126,7 @@ describe Split::Alternative do
|
|
126
126
|
|
127
127
|
it "should save to redis" do
|
128
128
|
alternative.save
|
129
|
-
expect(Split.redis.exists?(
|
129
|
+
expect(Split.redis.exists?("basket_text:Basket")).to be true
|
130
130
|
end
|
131
131
|
|
132
132
|
it "should increment participation count" do
|
@@ -166,7 +166,7 @@ describe Split::Alternative do
|
|
166
166
|
expect(alternative2.control?).to be_falsey
|
167
167
|
end
|
168
168
|
|
169
|
-
describe
|
169
|
+
describe "unfinished_count" do
|
170
170
|
it "should be difference between participant and completed counts" do
|
171
171
|
alternative.increment_participation
|
172
172
|
expect(alternative.unfinished_count).to eq(alternative.participant_count)
|
@@ -182,7 +182,7 @@ describe Split::Alternative do
|
|
182
182
|
end
|
183
183
|
end
|
184
184
|
|
185
|
-
describe
|
185
|
+
describe "conversion rate" do
|
186
186
|
it "should be 0 if there are no conversions" do
|
187
187
|
expect(alternative.completed_count).to eq(0)
|
188
188
|
expect(alternative.conversion_rate).to eq(0)
|
@@ -225,8 +225,7 @@ describe Split::Alternative do
|
|
225
225
|
end
|
226
226
|
end
|
227
227
|
|
228
|
-
describe
|
229
|
-
|
228
|
+
describe "z score" do
|
230
229
|
it "should return an error string when the control has 0 people" do
|
231
230
|
expect(alternative2.z_score).to eq("Needs 30+ participants.")
|
232
231
|
expect(alternative2.z_score(goal1)).to eq("Needs 30+ participants.")
|
@@ -269,9 +268,9 @@ describe Split::Alternative do
|
|
269
268
|
|
270
269
|
it "should be N/A for the control" do
|
271
270
|
control = experiment.control
|
272
|
-
expect(control.z_score).to eq(
|
273
|
-
expect(control.z_score(goal1)).to eq(
|
274
|
-
expect(control.z_score(goal2)).to eq(
|
271
|
+
expect(control.z_score).to eq("N/A")
|
272
|
+
expect(control.z_score(goal1)).to eq("N/A")
|
273
|
+
expect(control.z_score(goal2)).to eq("N/A")
|
275
274
|
end
|
276
275
|
|
277
276
|
it "should not blow up for Conversion Rates > 1" do
|
@@ -289,8 +288,8 @@ describe Split::Alternative do
|
|
289
288
|
|
290
289
|
describe "extra_info" do
|
291
290
|
it "reads saved value of recorded_info in redis" do
|
292
|
-
saved_recorded_info = {"key_1" => 1, "key_2" => "2"}
|
293
|
-
Split.redis.hset "#{alternative.experiment_name}:#{alternative.name}",
|
291
|
+
saved_recorded_info = { "key_1" => 1, "key_2" => "2" }
|
292
|
+
Split.redis.hset "#{alternative.experiment_name}:#{alternative.name}", "recorded_info", saved_recorded_info.to_json
|
294
293
|
extra_info = alternative.extra_info
|
295
294
|
|
296
295
|
expect(extra_info).to eql(saved_recorded_info)
|
data/spec/cache_spec.rb
CHANGED
@@ -1,19 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require 'spec_helper'
|
3
2
|
|
4
|
-
|
3
|
+
require "spec_helper"
|
5
4
|
|
5
|
+
describe Split::Cache do
|
6
6
|
let(:namespace) { :test_namespace }
|
7
7
|
let(:key) { :test_key }
|
8
8
|
let(:now) { 1606189017 }
|
9
9
|
|
10
10
|
before { allow(Time).to receive(:now).and_return(now) }
|
11
11
|
|
12
|
-
describe
|
13
|
-
|
12
|
+
describe "clear" do
|
14
13
|
before { Split.configuration.cache = true }
|
15
14
|
|
16
|
-
it
|
15
|
+
it "clears the cache" do
|
17
16
|
expect(Time).to receive(:now).and_return(now).exactly(2).times
|
18
17
|
Split::Cache.fetch(namespace, key) { Time.now }
|
19
18
|
Split::Cache.clear
|
@@ -21,10 +20,10 @@ describe Split::Cache do
|
|
21
20
|
end
|
22
21
|
end
|
23
22
|
|
24
|
-
describe
|
23
|
+
describe "clear_key" do
|
25
24
|
before { Split.configuration.cache = true }
|
26
25
|
|
27
|
-
it
|
26
|
+
it "clears the cache" do
|
28
27
|
expect(Time).to receive(:now).and_return(now).exactly(3).times
|
29
28
|
Split::Cache.fetch(namespace, :key1) { Time.now }
|
30
29
|
Split::Cache.fetch(namespace, :key2) { Time.now }
|
@@ -35,40 +34,37 @@ describe Split::Cache do
|
|
35
34
|
end
|
36
35
|
end
|
37
36
|
|
38
|
-
describe
|
39
|
-
|
37
|
+
describe "fetch" do
|
40
38
|
subject { Split::Cache.fetch(namespace, key) { Time.now } }
|
41
39
|
|
42
|
-
context
|
43
|
-
|
40
|
+
context "when cache disabled" do
|
44
41
|
before { Split.configuration.cache = false }
|
45
42
|
|
46
|
-
it
|
43
|
+
it "returns the yield" do
|
47
44
|
expect(subject).to eql(now)
|
48
45
|
end
|
49
46
|
|
50
|
-
it
|
47
|
+
it "yields every time" do
|
51
48
|
expect(Time).to receive(:now).and_return(now).exactly(2).times
|
52
49
|
Split::Cache.fetch(namespace, key) { Time.now }
|
53
50
|
Split::Cache.fetch(namespace, key) { Time.now }
|
54
51
|
end
|
55
52
|
end
|
56
53
|
|
57
|
-
context
|
58
|
-
|
54
|
+
context "when cache enabled" do
|
59
55
|
before { Split.configuration.cache = true }
|
60
56
|
|
61
|
-
it
|
57
|
+
it "returns the yield" do
|
62
58
|
expect(subject).to eql(now)
|
63
59
|
end
|
64
60
|
|
65
|
-
it
|
61
|
+
it "yields once" do
|
66
62
|
expect(Time).to receive(:now).and_return(now).once
|
67
63
|
Split::Cache.fetch(namespace, key) { Time.now }
|
68
64
|
Split::Cache.fetch(namespace, key) { Time.now }
|
69
65
|
end
|
70
66
|
|
71
|
-
it
|
67
|
+
it "honors namespace" do
|
72
68
|
expect(Split::Cache.fetch(:a, key) { :a }).to eql(:a)
|
73
69
|
expect(Split::Cache.fetch(:b, key) { :b }).to eql(:b)
|
74
70
|
|
@@ -76,7 +72,7 @@ describe Split::Cache do
|
|
76
72
|
expect(Split::Cache.fetch(:b, key) { :b }).to eql(:b)
|
77
73
|
end
|
78
74
|
|
79
|
-
it
|
75
|
+
it "honors key" do
|
80
76
|
expect(Split::Cache.fetch(namespace, :a) { :a }).to eql(:a)
|
81
77
|
expect(Split::Cache.fetch(namespace, :b) { :b }).to eql(:b)
|
82
78
|
|
@@ -1,57 +1,58 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
require "split/combined_experiments_helper"
|
4
5
|
|
5
6
|
describe Split::CombinedExperimentsHelper do
|
6
7
|
include Split::CombinedExperimentsHelper
|
7
8
|
|
8
|
-
describe
|
9
|
+
describe "ab_combined_test" do
|
9
10
|
let!(:config_enabled) { true }
|
10
|
-
let!(:combined_experiments) { [:exp_1_click, :exp_1_scroll ]}
|
11
|
+
let!(:combined_experiments) { [:exp_1_click, :exp_1_scroll ] }
|
11
12
|
let!(:allow_multiple_experiments) { true }
|
12
13
|
|
13
14
|
before do
|
14
15
|
Split.configuration.experiments = {
|
15
|
-
:
|
16
|
-
:
|
17
|
-
:
|
18
|
-
:
|
16
|
+
combined_exp_1: {
|
17
|
+
alternatives: [ { "control"=> 0.5 }, { "test-alt"=> 0.5 } ],
|
18
|
+
metric: :my_metric,
|
19
|
+
combined_experiments: combined_experiments
|
19
20
|
}
|
20
21
|
}
|
21
22
|
Split.configuration.enabled = config_enabled
|
22
23
|
Split.configuration.allow_multiple_experiments = allow_multiple_experiments
|
23
24
|
end
|
24
25
|
|
25
|
-
context
|
26
|
+
context "without config enabled" do
|
26
27
|
let!(:config_enabled) { false }
|
27
28
|
|
28
29
|
it "raises an error" do
|
29
|
-
expect
|
30
|
+
expect { ab_combined_test :combined_exp_1 }.to raise_error(Split::InvalidExperimentsFormatError)
|
30
31
|
end
|
31
32
|
end
|
32
33
|
|
33
|
-
context
|
34
|
+
context "multiple experiments disabled" do
|
34
35
|
let!(:allow_multiple_experiments) { false }
|
35
36
|
|
36
37
|
it "raises an error if multiple experiments is disabled" do
|
37
|
-
expect
|
38
|
+
expect { ab_combined_test :combined_exp_1 }.to raise_error(Split::InvalidExperimentsFormatError)
|
38
39
|
end
|
39
40
|
end
|
40
41
|
|
41
|
-
context
|
42
|
+
context "without combined experiments" do
|
42
43
|
let!(:combined_experiments) { nil }
|
43
44
|
|
44
45
|
it "raises an error" do
|
45
|
-
expect
|
46
|
+
expect { ab_combined_test :combined_exp_1 }.to raise_error(Split::InvalidExperimentsFormatError)
|
46
47
|
end
|
47
48
|
end
|
48
49
|
|
49
50
|
it "uses same alternative for all sub experiments and returns the alternative" do
|
50
51
|
allow(self).to receive(:get_alternative) { "test-alt" }
|
51
|
-
expect(self).to receive(:ab_test).with(:exp_1_click, {"control"=>0.5}, {"test-alt"=>0.5}) { "test-alt" }
|
52
|
-
expect(self).to receive(:ab_test).with(:exp_1_scroll, [{"control" => 0, "test-alt" => 1}])
|
52
|
+
expect(self).to receive(:ab_test).with(:exp_1_click, { "control"=>0.5 }, { "test-alt"=>0.5 }) { "test-alt" }
|
53
|
+
expect(self).to receive(:ab_test).with(:exp_1_scroll, [{ "control" => 0, "test-alt" => 1 }])
|
53
54
|
|
54
|
-
expect(ab_combined_test(
|
55
|
+
expect(ab_combined_test("combined_exp_1")).to eq("test-alt")
|
55
56
|
end
|
56
57
|
end
|
57
58
|
end
|
data/spec/configuration_spec.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require 'spec_helper'
|
3
2
|
|
4
|
-
|
3
|
+
require "spec_helper"
|
5
4
|
|
5
|
+
describe Split::Configuration do
|
6
6
|
before(:each) { @config = Split::Configuration.new }
|
7
7
|
|
8
8
|
it "should provide a default value for ignore_ip_addresses" do
|
@@ -58,17 +58,15 @@ describe Split::Configuration do
|
|
58
58
|
end
|
59
59
|
|
60
60
|
it "should load a metric" do
|
61
|
-
@config.experiments = {:
|
62
|
-
{:alternatives=>["control_opt", "other_opt"], :metric=>:my_metric}}
|
61
|
+
@config.experiments = { my_experiment: { alternatives: ["control_opt", "other_opt"], metric: :my_metric } }
|
63
62
|
|
64
63
|
expect(@config.metrics).not_to be_nil
|
65
64
|
expect(@config.metrics.keys).to eq([:my_metric])
|
66
65
|
end
|
67
66
|
|
68
67
|
it "should allow loading of experiment using experment_for" do
|
69
|
-
@config.experiments = {:
|
70
|
-
|
71
|
-
expect(@config.experiment_for(:my_experiment)).to eq({:alternatives=>["control_opt", ["other_opt"]]})
|
68
|
+
@config.experiments = { my_experiment: { alternatives: ["control_opt", "other_opt"], metric: :my_metric } }
|
69
|
+
expect(@config.experiment_for(:my_experiment)).to eq({ alternatives: ["control_opt", ["other_opt"]] })
|
72
70
|
end
|
73
71
|
|
74
72
|
context "when experiments are defined via YAML" do
|
@@ -82,12 +80,12 @@ describe Split::Configuration do
|
|
82
80
|
- Alt One
|
83
81
|
- Alt Two
|
84
82
|
resettable: false
|
85
|
-
|
83
|
+
eos
|
86
84
|
@config.experiments = YAML.load(experiments_yaml)
|
87
85
|
end
|
88
86
|
|
89
|
-
it
|
90
|
-
expect(@config.normalized_experiments).to eq({:
|
87
|
+
it "should normalize experiments" do
|
88
|
+
expect(@config.normalized_experiments).to eq({ my_experiment: { resettable: false, alternatives: ["Control Opt", ["Alt One", "Alt Two"]] } })
|
91
89
|
end
|
92
90
|
end
|
93
91
|
|
@@ -110,14 +108,14 @@ describe Split::Configuration do
|
|
110
108
|
Alt Two:
|
111
109
|
text: 'Alternative Two'
|
112
110
|
resettable: false
|
113
|
-
|
111
|
+
eos
|
114
112
|
@config.experiments = YAML.load(experiments_yaml)
|
115
113
|
end
|
116
114
|
|
117
|
-
it
|
115
|
+
it "should have metadata on the experiment" do
|
118
116
|
meta = @config.normalized_experiments[:my_experiment][:metadata]
|
119
117
|
expect(meta).to_not be nil
|
120
|
-
expect(meta[
|
118
|
+
expect(meta["Control Opt"]["text"]).to eq("Control Option")
|
121
119
|
end
|
122
120
|
end
|
123
121
|
|
@@ -138,25 +136,23 @@ describe Split::Configuration do
|
|
138
136
|
alternatives:
|
139
137
|
- a
|
140
138
|
- b
|
141
|
-
|
139
|
+
eos
|
142
140
|
@config.experiments = YAML.load(experiments_yaml)
|
143
141
|
end
|
144
142
|
|
145
143
|
it "should normalize experiments" do
|
146
|
-
expect(@config.normalized_experiments).to eq({:
|
147
|
-
[{"Alt One"=>0.1}, {"Alt Two"=>0.23}]]}, :
|
144
|
+
expect(@config.normalized_experiments).to eq({ my_experiment: { resettable: false, alternatives: [{ "Control Opt"=>0.67 },
|
145
|
+
[{ "Alt One"=>0.1 }, { "Alt Two"=>0.23 }]] }, another_experiment: { alternatives: ["a", ["b"]] } })
|
148
146
|
end
|
149
147
|
|
150
148
|
it "should recognize metrics" do
|
151
149
|
expect(@config.metrics).not_to be_nil
|
152
150
|
expect(@config.metrics.keys).to eq([:my_metric])
|
153
151
|
end
|
154
|
-
|
155
152
|
end
|
156
153
|
end
|
157
154
|
|
158
155
|
context "as symbols" do
|
159
|
-
|
160
156
|
context "with valid YAML" do
|
161
157
|
before do
|
162
158
|
experiments_yaml = <<-eos
|
@@ -166,21 +162,20 @@ describe Split::Configuration do
|
|
166
162
|
- Alt One
|
167
163
|
- Alt Two
|
168
164
|
:resettable: false
|
169
|
-
|
165
|
+
eos
|
170
166
|
@config.experiments = YAML.load(experiments_yaml)
|
171
167
|
end
|
172
168
|
|
173
169
|
it "should normalize experiments" do
|
174
|
-
expect(@config.normalized_experiments).to eq({:
|
170
|
+
expect(@config.normalized_experiments).to eq({ my_experiment: { resettable: false, alternatives: ["Control Opt", ["Alt One", "Alt Two"]] } })
|
175
171
|
end
|
176
172
|
end
|
177
173
|
|
178
174
|
context "with invalid YAML" do
|
179
|
-
|
180
175
|
let(:yaml) { YAML.load(input) }
|
181
176
|
|
182
177
|
context "with an empty string" do
|
183
|
-
let(:input) {
|
178
|
+
let(:input) { "" }
|
184
179
|
|
185
180
|
it "should raise an error" do
|
186
181
|
expect { @config.experiments = yaml }.to raise_error(Split::InvalidExperimentsFormatError)
|
@@ -188,7 +183,7 @@ describe Split::Configuration do
|
|
188
183
|
end
|
189
184
|
|
190
185
|
context "with just the YAML header" do
|
191
|
-
let(:input) {
|
186
|
+
let(:input) { "---" }
|
192
187
|
|
193
188
|
it "should raise an error" do
|
194
189
|
expect { @config.experiments = yaml }.to raise_error(Split::InvalidExperimentsFormatError)
|
@@ -200,24 +195,24 @@ describe Split::Configuration do
|
|
200
195
|
|
201
196
|
it "should normalize experiments" do
|
202
197
|
@config.experiments = {
|
203
|
-
:
|
204
|
-
:
|
205
|
-
{ :
|
206
|
-
{ :
|
207
|
-
{ :
|
198
|
+
my_experiment: {
|
199
|
+
alternatives: [
|
200
|
+
{ name: "control_opt", percent: 67 },
|
201
|
+
{ name: "second_opt", percent: 10 },
|
202
|
+
{ name: "third_opt", percent: 23 },
|
208
203
|
],
|
209
204
|
}
|
210
205
|
}
|
211
206
|
|
212
|
-
expect(@config.normalized_experiments).to eq({:
|
207
|
+
expect(@config.normalized_experiments).to eq({ my_experiment: { alternatives: [{ "control_opt"=>0.67 }, [{ "second_opt"=>0.1 }, { "third_opt"=>0.23 }]] } })
|
213
208
|
end
|
214
209
|
|
215
210
|
context "redis configuration" do
|
216
211
|
it "should default to local redis server" do
|
217
|
-
old_redis_url = ENV[
|
218
|
-
ENV.delete(
|
212
|
+
old_redis_url = ENV["REDIS_URL"]
|
213
|
+
ENV.delete("REDIS_URL")
|
219
214
|
expect(Split::Configuration.new.redis).to eq("redis://localhost:6379")
|
220
|
-
ENV[
|
215
|
+
ENV["REDIS_URL"] = old_redis_url
|
221
216
|
end
|
222
217
|
|
223
218
|
it "should allow for redis url to be configured" do
|
@@ -227,10 +222,10 @@ describe Split::Configuration do
|
|
227
222
|
|
228
223
|
context "provided REDIS_URL environment variable" do
|
229
224
|
it "should use the ENV variable" do
|
230
|
-
old_redis_url = ENV[
|
231
|
-
ENV[
|
225
|
+
old_redis_url = ENV["REDIS_URL"]
|
226
|
+
ENV["REDIS_URL"] = "env_redis_url"
|
232
227
|
expect(Split::Configuration.new.redis).to eq("env_redis_url")
|
233
|
-
ENV[
|
228
|
+
ENV["REDIS_URL"] = old_redis_url
|
234
229
|
end
|
235
230
|
end
|
236
231
|
end
|
@@ -252,9 +247,8 @@ describe Split::Configuration do
|
|
252
247
|
end
|
253
248
|
|
254
249
|
it "should allow the persistence cookie domain to be configured" do
|
255
|
-
@config.persistence_cookie_domain =
|
256
|
-
expect(@config.persistence_cookie_domain).to eq(
|
250
|
+
@config.persistence_cookie_domain = ".acme.com"
|
251
|
+
expect(@config.persistence_cookie_domain).to eq(".acme.com")
|
257
252
|
end
|
258
253
|
end
|
259
|
-
|
260
254
|
end
|