split 0.4.6 → 0.5.0
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.
- data/.travis.yml +11 -3
- data/CHANGELOG.mdown +22 -1
- data/CONTRIBUTING.md +10 -0
- data/LICENSE +1 -1
- data/README.mdown +235 -60
- data/lib/split.rb +8 -9
- data/lib/split/algorithms.rb +3 -0
- data/lib/split/algorithms/weighted_sample.rb +17 -0
- data/lib/split/algorithms/whiplash.rb +35 -0
- data/lib/split/alternative.rb +12 -4
- data/lib/split/configuration.rb +91 -1
- data/lib/split/dashboard/helpers.rb +3 -3
- data/lib/split/dashboard/views/_experiment.erb +1 -1
- data/lib/split/exceptions.rb +4 -0
- data/lib/split/experiment.rb +112 -24
- data/lib/split/extensions.rb +3 -0
- data/lib/split/extensions/array.rb +4 -0
- data/lib/split/extensions/string.rb +15 -0
- data/lib/split/helper.rb +87 -55
- data/lib/split/metric.rb +68 -0
- data/lib/split/persistence.rb +28 -0
- data/lib/split/persistence/cookie_adapter.rb +44 -0
- data/lib/split/persistence/session_adapter.rb +28 -0
- data/lib/split/trial.rb +43 -0
- data/lib/split/version.rb +3 -3
- data/spec/algorithms/weighted_sample_spec.rb +18 -0
- data/spec/algorithms/whiplash_spec.rb +23 -0
- data/spec/alternative_spec.rb +81 -9
- data/spec/configuration_spec.rb +61 -9
- data/spec/dashboard_helpers_spec.rb +2 -5
- data/spec/dashboard_spec.rb +0 -2
- data/spec/experiment_spec.rb +144 -74
- data/spec/helper_spec.rb +234 -29
- data/spec/metric_spec.rb +30 -0
- data/spec/persistence/cookie_adapter_spec.rb +31 -0
- data/spec/persistence/session_adapter_spec.rb +31 -0
- data/spec/persistence_spec.rb +33 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/support/cookies_mock.rb +19 -0
- data/spec/trial_spec.rb +59 -0
- data/split.gemspec +7 -3
- metadata +58 -29
- data/Guardfile +0 -5
data/spec/helper_spec.rb
CHANGED
@@ -5,13 +5,20 @@ require 'spec_helper'
|
|
5
5
|
describe Split::Helper do
|
6
6
|
include Split::Helper
|
7
7
|
|
8
|
-
before(:each) do
|
9
|
-
Split.redis.flushall
|
10
|
-
@session = {}
|
11
|
-
params = nil
|
12
|
-
end
|
13
|
-
|
14
8
|
describe "ab_test" do
|
9
|
+
|
10
|
+
it "should not raise an error when passed strings for alternatives" do
|
11
|
+
lambda { ab_test('xyz', '1', '2', '3') }.should_not raise_error
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should raise the appropriate error when passed integers for alternatives" do
|
15
|
+
lambda { ab_test('xyz', 1, 2, 3) }.should raise_error(ArgumentError)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should raise the appropriate error when passed symbols for alternatives" do
|
19
|
+
lambda { ab_test('xyz', :a, :b, :c) }.should raise_error(ArgumentError)
|
20
|
+
end
|
21
|
+
|
15
22
|
it "should assign a random alternative to a new user when there are an equal number of alternatives assigned" do
|
16
23
|
ab_test('link_color', 'blue', 'red')
|
17
24
|
['red', 'blue'].should include(ab_user['link_color'])
|
@@ -74,12 +81,14 @@ describe Split::Helper do
|
|
74
81
|
ab_test('link_color', {'blue' => 0.01}, 'red' => 0.2)
|
75
82
|
experiment = Split::Experiment.find('link_color')
|
76
83
|
experiment.alternative_names.should eql(['blue', 'red'])
|
84
|
+
# TODO: persist alternative weights
|
85
|
+
# experiment.alternatives.collect{|a| a.weight}.should == [0.01, 0.2]
|
77
86
|
end
|
78
87
|
|
79
88
|
it "should only let a user participate in one experiment at a time" do
|
80
|
-
ab_test('link_color', 'blue', 'red')
|
89
|
+
link_color = ab_test('link_color', 'blue', 'red')
|
81
90
|
ab_test('button_size', 'small', 'big')
|
82
|
-
ab_user
|
91
|
+
ab_user.should eql({'link_color' => link_color})
|
83
92
|
big = Split::Alternative.new('big', 'button_size')
|
84
93
|
big.participant_count.should eql(0)
|
85
94
|
small = Split::Alternative.new('small', 'button_size')
|
@@ -92,10 +101,19 @@ describe Split::Helper do
|
|
92
101
|
end
|
93
102
|
link_color = ab_test('link_color', 'blue', 'red')
|
94
103
|
button_size = ab_test('button_size', 'small', 'big')
|
95
|
-
ab_user
|
104
|
+
ab_user.should eql({'link_color' => link_color, 'button_size' => button_size})
|
96
105
|
button_size_alt = Split::Alternative.new(button_size, 'button_size')
|
97
106
|
button_size_alt.participant_count.should eql(1)
|
98
107
|
end
|
108
|
+
|
109
|
+
it "should not over-write a finished key when an experiment is on a later version" do
|
110
|
+
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
111
|
+
experiment.increment_version
|
112
|
+
ab_user = { experiment.key => 'blue', experiment.finished_key => true }
|
113
|
+
finshed_session = ab_user.dup
|
114
|
+
ab_test('link_color', 'blue', 'red')
|
115
|
+
ab_user.should eql(finshed_session)
|
116
|
+
end
|
99
117
|
end
|
100
118
|
|
101
119
|
describe 'finished' do
|
@@ -115,7 +133,7 @@ describe Split::Helper do
|
|
115
133
|
|
116
134
|
it "should set experiment's finished key if reset is false" do
|
117
135
|
finished(@experiment_name, :reset => false)
|
118
|
-
|
136
|
+
ab_user.should eql(@experiment.key => @alternative_name, @experiment.finished_key => true)
|
119
137
|
end
|
120
138
|
|
121
139
|
it 'should not increment the counter if reset is false and the experiment has been already finished' do
|
@@ -125,36 +143,121 @@ describe Split::Helper do
|
|
125
143
|
end
|
126
144
|
|
127
145
|
it "should clear out the user's participation from their session" do
|
128
|
-
|
146
|
+
ab_user.should eql(@experiment.key => @alternative_name)
|
129
147
|
finished(@experiment_name)
|
130
|
-
|
148
|
+
ab_user.should == {}
|
131
149
|
end
|
132
150
|
|
133
151
|
it "should not clear out the users session if reset is false" do
|
134
|
-
|
152
|
+
ab_user.should eql(@experiment.key => @alternative_name)
|
135
153
|
finished(@experiment_name, :reset => false)
|
136
|
-
|
154
|
+
ab_user.should eql(@experiment.key => @alternative_name, @experiment.finished_key => true)
|
137
155
|
end
|
138
156
|
|
139
157
|
it "should reset the users session when experiment is not versioned" do
|
140
|
-
|
158
|
+
ab_user.should eql(@experiment.key => @alternative_name)
|
141
159
|
finished(@experiment_name)
|
142
|
-
|
160
|
+
ab_user.should eql({})
|
143
161
|
end
|
144
162
|
|
145
163
|
it "should reset the users session when experiment is versioned" do
|
146
164
|
@experiment.increment_version
|
147
165
|
@alternative_name = ab_test(@experiment_name, *@alternatives)
|
148
166
|
|
149
|
-
|
167
|
+
ab_user.should eql(@experiment.key => @alternative_name)
|
150
168
|
finished(@experiment_name)
|
151
|
-
|
169
|
+
ab_user.should eql({})
|
152
170
|
end
|
153
171
|
|
154
172
|
it "should do nothing where the experiment was not started by this user" do
|
155
|
-
|
173
|
+
ab_user = nil
|
156
174
|
lambda { finished('some_experiment_not_started_by_the_user') }.should_not raise_exception
|
157
175
|
end
|
176
|
+
|
177
|
+
it 'should not be doing other tests when it has completed one that has :reset => false' do
|
178
|
+
ab_user[@experiment.key] = @alternative_name
|
179
|
+
ab_user[@experiment.finished_key] = true
|
180
|
+
doing_other_tests?(@experiment.key).should be false
|
181
|
+
end
|
182
|
+
|
183
|
+
it "passes reset option from config" do
|
184
|
+
Split.configuration.experiments = {
|
185
|
+
@experiment_name => {
|
186
|
+
:alternatives => @alternatives,
|
187
|
+
:resettable => false,
|
188
|
+
}
|
189
|
+
}
|
190
|
+
finished @experiment_name
|
191
|
+
ab_user.should eql(@experiment.key => @alternative_name, @experiment.finished_key => true)
|
192
|
+
end
|
193
|
+
|
194
|
+
context "with metric name" do
|
195
|
+
before { Split.configuration.experiments = {} }
|
196
|
+
before { Split::Alternative.stub(:new).and_call_original }
|
197
|
+
|
198
|
+
def should_finish_experiment(experiment_name, should_finish=true)
|
199
|
+
alts = Split.configuration.experiments[experiment_name][:alternatives]
|
200
|
+
experiment = Split::Experiment.find_or_create(experiment_name, *alts)
|
201
|
+
alt_name = ab_user[experiment.key] = alts.first
|
202
|
+
alt = mock('alternative')
|
203
|
+
alt.stub(:name).and_return(alt_name)
|
204
|
+
Split::Alternative.stub(:new).with(alt_name, experiment_name).and_return(alt)
|
205
|
+
if should_finish
|
206
|
+
alt.should_receive(:increment_completion)
|
207
|
+
else
|
208
|
+
alt.should_not_receive(:increment_completion)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
it "completes the test" do
|
213
|
+
Split.configuration.experiments[:my_experiment] = {
|
214
|
+
:alternatives => [ "control_opt", "other_opt" ],
|
215
|
+
:metric => :my_metric
|
216
|
+
}
|
217
|
+
should_finish_experiment :my_experiment
|
218
|
+
finished :my_metric
|
219
|
+
end
|
220
|
+
|
221
|
+
it "completes all relevant tests" do
|
222
|
+
Split.configuration.experiments = {
|
223
|
+
:exp_1 => {
|
224
|
+
:alternatives => [ "1-1", "1-2" ],
|
225
|
+
:metric => :my_metric
|
226
|
+
},
|
227
|
+
:exp_2 => {
|
228
|
+
:alternatives => [ "2-1", "2-2" ],
|
229
|
+
:metric => :another_metric
|
230
|
+
},
|
231
|
+
:exp_3 => {
|
232
|
+
:alternatives => [ "3-1", "3-2" ],
|
233
|
+
:metric => :my_metric
|
234
|
+
},
|
235
|
+
}
|
236
|
+
should_finish_experiment :exp_1
|
237
|
+
should_finish_experiment :exp_2, false
|
238
|
+
should_finish_experiment :exp_3
|
239
|
+
finished :my_metric
|
240
|
+
end
|
241
|
+
|
242
|
+
it "passes reset option" do
|
243
|
+
Split.configuration.experiments[@experiment_name] = {
|
244
|
+
:alternatives => @alternatives,
|
245
|
+
:metric => :my_metric,
|
246
|
+
:resettable => false,
|
247
|
+
}
|
248
|
+
finished :my_metric
|
249
|
+
ab_user.should eql(@experiment.key => @alternative_name, @experiment.finished_key => true)
|
250
|
+
end
|
251
|
+
|
252
|
+
it "passes through options" do
|
253
|
+
Split.configuration.experiments[@experiment_name] = {
|
254
|
+
:alternatives => @alternatives,
|
255
|
+
:metric => :my_metric,
|
256
|
+
}
|
257
|
+
finished :my_metric, :reset => false
|
258
|
+
ab_user.should eql(@experiment.key => @alternative_name, @experiment.finished_key => true)
|
259
|
+
end
|
260
|
+
end
|
158
261
|
end
|
159
262
|
|
160
263
|
describe 'conversions' do
|
@@ -198,6 +301,7 @@ describe Split::Helper do
|
|
198
301
|
(new_red_count + new_blue_count).should eql(previous_red_count + previous_blue_count)
|
199
302
|
end
|
200
303
|
end
|
304
|
+
|
201
305
|
describe 'finished' do
|
202
306
|
it "should not increment the completed count" do
|
203
307
|
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
@@ -213,6 +317,7 @@ describe Split::Helper do
|
|
213
317
|
end
|
214
318
|
end
|
215
319
|
end
|
320
|
+
|
216
321
|
describe 'when ip address is ignored' do
|
217
322
|
before(:each) do
|
218
323
|
@request = OpenStruct.new(:ip => '81.19.48.130')
|
@@ -242,6 +347,7 @@ describe Split::Helper do
|
|
242
347
|
(new_red_count + new_blue_count).should eql(previous_red_count + previous_blue_count)
|
243
348
|
end
|
244
349
|
end
|
350
|
+
|
245
351
|
describe 'finished' do
|
246
352
|
it "should not increment the completed count" do
|
247
353
|
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
@@ -263,7 +369,7 @@ describe Split::Helper do
|
|
263
369
|
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
264
370
|
alternative_name = ab_test('link_color', 'blue', 'red')
|
265
371
|
experiment.version.should eql(0)
|
266
|
-
|
372
|
+
ab_user.should eql({'link_color' => alternative_name})
|
267
373
|
end
|
268
374
|
|
269
375
|
it "should save the version of the experiment to the session" do
|
@@ -271,7 +377,7 @@ describe Split::Helper do
|
|
271
377
|
experiment.reset
|
272
378
|
experiment.version.should eql(1)
|
273
379
|
alternative_name = ab_test('link_color', 'blue', 'red')
|
274
|
-
|
380
|
+
ab_user.should eql({'link_color:1' => alternative_name})
|
275
381
|
end
|
276
382
|
|
277
383
|
it "should load the experiment even if the version is not 0" do
|
@@ -279,7 +385,7 @@ describe Split::Helper do
|
|
279
385
|
experiment.reset
|
280
386
|
experiment.version.should eql(1)
|
281
387
|
alternative_name = ab_test('link_color', 'blue', 'red')
|
282
|
-
|
388
|
+
ab_user.should eql({'link_color:1' => alternative_name})
|
283
389
|
return_alternative_name = ab_test('link_color', 'blue', 'red')
|
284
390
|
return_alternative_name.should eql(alternative_name)
|
285
391
|
end
|
@@ -287,7 +393,7 @@ describe Split::Helper do
|
|
287
393
|
it "should reset the session of a user on an older version of the experiment" do
|
288
394
|
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
289
395
|
alternative_name = ab_test('link_color', 'blue', 'red')
|
290
|
-
|
396
|
+
ab_user.should eql({'link_color' => alternative_name})
|
291
397
|
alternative = Split::Alternative.new(alternative_name, 'link_color')
|
292
398
|
alternative.participant_count.should eql(1)
|
293
399
|
|
@@ -297,7 +403,7 @@ describe Split::Helper do
|
|
297
403
|
alternative.participant_count.should eql(0)
|
298
404
|
|
299
405
|
new_alternative_name = ab_test('link_color', 'blue', 'red')
|
300
|
-
|
406
|
+
ab_user['link_color:1'].should eql(new_alternative_name)
|
301
407
|
new_alternative = Split::Alternative.new(new_alternative_name, 'link_color')
|
302
408
|
new_alternative.participant_count.should eql(1)
|
303
409
|
end
|
@@ -305,7 +411,7 @@ describe Split::Helper do
|
|
305
411
|
it "should cleanup old versions of experiments from the session" do
|
306
412
|
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
307
413
|
alternative_name = ab_test('link_color', 'blue', 'red')
|
308
|
-
|
414
|
+
ab_user.should eql({'link_color' => alternative_name})
|
309
415
|
alternative = Split::Alternative.new(alternative_name, 'link_color')
|
310
416
|
alternative.participant_count.should eql(1)
|
311
417
|
|
@@ -315,13 +421,13 @@ describe Split::Helper do
|
|
315
421
|
alternative.participant_count.should eql(0)
|
316
422
|
|
317
423
|
new_alternative_name = ab_test('link_color', 'blue', 'red')
|
318
|
-
|
424
|
+
ab_user.should eql({'link_color:1' => new_alternative_name})
|
319
425
|
end
|
320
426
|
|
321
427
|
it "should only count completion of users on the current version" do
|
322
428
|
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
323
429
|
alternative_name = ab_test('link_color', 'blue', 'red')
|
324
|
-
|
430
|
+
ab_user.should eql({'link_color' => alternative_name})
|
325
431
|
alternative = Split::Alternative.new(alternative_name, 'link_color')
|
326
432
|
|
327
433
|
experiment.reset
|
@@ -466,10 +572,109 @@ describe Split::Helper do
|
|
466
572
|
finished('link_color')
|
467
573
|
end
|
468
574
|
end
|
469
|
-
|
470
|
-
|
471
575
|
end
|
576
|
+
end
|
472
577
|
|
578
|
+
context "with preloaded config" do
|
579
|
+
before { Split.configuration.experiments = {}}
|
580
|
+
|
581
|
+
it "pulls options from config file" do
|
582
|
+
Split.configuration.experiments[:my_experiment] = {
|
583
|
+
:alternatives => [ "control_opt", "other_opt" ],
|
584
|
+
}
|
585
|
+
ab_test :my_experiment
|
586
|
+
Split::Experiment.find(:my_experiment).alternative_names.should == [ "control_opt", "other_opt" ]
|
587
|
+
end
|
588
|
+
|
589
|
+
it "can be called multiple times" do
|
590
|
+
Split.configuration.experiments[:my_experiment] = {
|
591
|
+
:alternatives => [ "control_opt", "other_opt" ],
|
592
|
+
}
|
593
|
+
5.times { ab_test :my_experiment }
|
594
|
+
experiment = Split::Experiment.find(:my_experiment)
|
595
|
+
experiment.alternative_names.should == [ "control_opt", "other_opt" ]
|
596
|
+
experiment.participant_count.should == 1
|
597
|
+
end
|
598
|
+
|
599
|
+
it "accepts multiple alternatives" do
|
600
|
+
Split.configuration.experiments[:my_experiment] = {
|
601
|
+
:alternatives => [ "control_opt", "second_opt", "third_opt" ],
|
602
|
+
}
|
603
|
+
ab_test :my_experiment
|
604
|
+
experiment = Split::Experiment.find(:my_experiment)
|
605
|
+
experiment.alternative_names.should == [ "control_opt", "second_opt", "third_opt" ]
|
606
|
+
end
|
607
|
+
|
608
|
+
it "accepts probability on alternatives" do
|
609
|
+
Split.configuration.experiments[:my_experiment] = {
|
610
|
+
:alternatives => [
|
611
|
+
{ :name => "control_opt", :percent => 67 },
|
612
|
+
{ :name => "second_opt", :percent => 10 },
|
613
|
+
{ :name => "third_opt", :percent => 23 },
|
614
|
+
],
|
615
|
+
}
|
616
|
+
ab_test :my_experiment
|
617
|
+
experiment = Split::Experiment.find(:my_experiment)
|
618
|
+
experiment.alternatives.collect{|a| [a.name, a.weight]}.should == [['control_opt', 0.67], ['second_opt', 0.1], ['third_opt', 0.23]]
|
619
|
+
|
620
|
+
end
|
621
|
+
|
622
|
+
it "accepts probability on some alternatives" do
|
623
|
+
Split.configuration.experiments[:my_experiment] = {
|
624
|
+
:alternatives => [
|
625
|
+
{ :name => "control_opt", :percent => 34 },
|
626
|
+
"second_opt",
|
627
|
+
{ :name => "third_opt", :percent => 23 },
|
628
|
+
"fourth_opt",
|
629
|
+
],
|
630
|
+
}
|
631
|
+
ab_test :my_experiment
|
632
|
+
experiment = Split::Experiment.find(:my_experiment)
|
633
|
+
names_and_weights = experiment.alternatives.collect{|a| [a.name, a.weight]}
|
634
|
+
names_and_weights.should == [['control_opt', 0.34], ['second_opt', 0.215], ['third_opt', 0.23], ['fourth_opt', 0.215]]
|
635
|
+
names_and_weights.inject(0){|sum, nw| sum + nw[1]}.should == 1.0
|
636
|
+
end
|
637
|
+
|
638
|
+
it "allows name param without probability" do
|
639
|
+
Split.configuration.experiments[:my_experiment] = {
|
640
|
+
:alternatives => [
|
641
|
+
{ :name => "control_opt" },
|
642
|
+
"second_opt",
|
643
|
+
{ :name => "third_opt", :percent => 64 },
|
644
|
+
],
|
645
|
+
}
|
646
|
+
ab_test :my_experiment
|
647
|
+
experiment = Split::Experiment.find(:my_experiment)
|
648
|
+
names_and_weights = experiment.alternatives.collect{|a| [a.name, a.weight]}
|
649
|
+
names_and_weights.should == [['control_opt', 0.18], ['second_opt', 0.18], ['third_opt', 0.64]]
|
650
|
+
names_and_weights.inject(0){|sum, nw| sum + nw[1]}.should == 1.0
|
651
|
+
end
|
652
|
+
|
653
|
+
it "fails gracefully if config is missing experiment" do
|
654
|
+
Split.configuration.experiments = { :other_experiment => { :foo => "Bar" } }
|
655
|
+
lambda { ab_test :my_experiment }.should raise_error(/not found/i)
|
656
|
+
end
|
657
|
+
|
658
|
+
it "fails gracefully if config is missing" do
|
659
|
+
Split.configuration.experiments = nil
|
660
|
+
lambda { ab_test :my_experiment }.should raise_error(/not found/i)
|
661
|
+
end
|
662
|
+
|
663
|
+
it "fails gracefully if config is missing alternatives" do
|
664
|
+
Split.configuration.experiments[:my_experiment] = { :foo => "Bar" }
|
665
|
+
lambda { ab_test :my_experiment }.should raise_error(/alternatives/i)
|
666
|
+
end
|
473
667
|
end
|
474
668
|
|
669
|
+
it 'should handle multiple experiments correctly' do
|
670
|
+
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
671
|
+
experiment2 = Split::Experiment.find_or_create('link_color2', 'blue', 'red')
|
672
|
+
alternative_name = ab_test('link_color', 'blue', 'red')
|
673
|
+
alternative_name2 = ab_test('link_color2', 'blue', 'red')
|
674
|
+
finished('link_color2')
|
675
|
+
|
676
|
+
experiment2.alternatives.each do |alt|
|
677
|
+
alt.unfinished_count.should eq(0)
|
678
|
+
end
|
679
|
+
end
|
475
680
|
end
|
data/spec/metric_spec.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'split/metric'
|
3
|
+
|
4
|
+
describe Split::Metric do
|
5
|
+
describe 'possible experiments' do
|
6
|
+
it "should load the experiment if there is one, but no metric" do
|
7
|
+
experiment = Split::Experiment.find_or_create('color', 'red', 'blue')
|
8
|
+
Split::Metric.possible_experiments('color').should == [experiment]
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should load the experiments in a metric" do
|
12
|
+
experiment1 = Split::Experiment.find_or_create('color', 'red', 'blue')
|
13
|
+
experiment2 = Split::Experiment.find_or_create('size', 'big', 'small')
|
14
|
+
|
15
|
+
metric = Split::Metric.new(:name => 'purchase', :experiments => [experiment1, experiment2])
|
16
|
+
metric.save
|
17
|
+
Split::Metric.possible_experiments('purchase').should include(experiment1, experiment2)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should load both the metric experiments and an experiment with the same name" do
|
21
|
+
experiment1 = Split::Experiment.find_or_create('purchase', 'red', 'blue')
|
22
|
+
experiment2 = Split::Experiment.find_or_create('size', 'big', 'small')
|
23
|
+
|
24
|
+
metric = Split::Metric.new(:name => 'purchase', :experiments => [experiment2])
|
25
|
+
metric.save
|
26
|
+
Split::Metric.possible_experiments('purchase').should include(experiment1, experiment2)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Split::Persistence::CookieAdapter do
|
4
|
+
|
5
|
+
let(:context) { mock(:cookies => CookiesMock.new) }
|
6
|
+
subject { Split::Persistence::CookieAdapter.new(context) }
|
7
|
+
|
8
|
+
describe "#[] and #[]=" do
|
9
|
+
it "should set and return the value for given key" do
|
10
|
+
subject["my_key"] = "my_value"
|
11
|
+
subject["my_key"].should eq("my_value")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#delete" do
|
16
|
+
it "should delete the given key" do
|
17
|
+
subject["my_key"] = "my_value"
|
18
|
+
subject.delete("my_key")
|
19
|
+
subject["my_key"].should be_nil
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "#keys" do
|
24
|
+
it "should return an array of the session's stored keys" do
|
25
|
+
subject["my_key"] = "my_value"
|
26
|
+
subject["my_second_key"] = "my_second_value"
|
27
|
+
subject.keys.should =~ ["my_key", "my_second_key"]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|