split 3.3.2 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Split
2
3
  class GoalsCollection
3
4
 
@@ -44,6 +44,7 @@ module Split
44
44
  end
45
45
 
46
46
  def finish_experiment(experiment, options = {:reset => true})
47
+ return false if active_experiments[experiment.name].nil?
47
48
  return true if experiment.has_winner?
48
49
  should_reset = experiment.resettable? && options[:reset]
49
50
  if ab_user[experiment.finished_key] && !should_reset
@@ -79,7 +80,7 @@ module Split
79
80
 
80
81
  def ab_record_extra_info(metric_descriptor, key, value = 1)
81
82
  return if exclude_visitor? || Split.configuration.disabled?
82
- metric_descriptor, goals = normalize_metric(metric_descriptor)
83
+ metric_descriptor, _ = normalize_metric(metric_descriptor)
83
84
  experiments = Metric.possible_experiments(metric_descriptor)
84
85
 
85
86
  if experiments.any?
@@ -1,12 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'forwardable'
4
-
5
3
  module Split
6
4
  module Persistence
7
5
  class DualAdapter
8
- extend Forwardable
9
- def_delegators :@adapter, :keys, :[], :[]=, :delete
6
+ def self.with_config(options={})
7
+ self.config.merge!(options)
8
+ self
9
+ end
10
+
11
+ def self.config
12
+ @config ||= {}
13
+ end
10
14
 
11
15
  def initialize(context)
12
16
  if logged_in = self.class.config[:logged_in]
@@ -22,22 +26,60 @@ module Split
22
26
  raise "Please configure :logged_out_adapter"
23
27
  end
24
28
 
25
- if logged_in.call(context)
26
- @adapter = logged_in_adapter.new(context)
29
+ @fallback_to_logged_out_adapter =
30
+ self.class.config[:fallback_to_logged_out_adapter] || false
31
+ @logged_in = logged_in.call(context)
32
+ @logged_in_adapter = logged_in_adapter.new(context)
33
+ @logged_out_adapter = logged_out_adapter.new(context)
34
+ @active_adapter = @logged_in ? @logged_in_adapter : @logged_out_adapter
35
+ end
36
+
37
+ def keys
38
+ if @fallback_to_logged_out_adapter
39
+ (@logged_in_adapter.keys + @logged_out_adapter.keys).uniq
27
40
  else
28
- @adapter = logged_out_adapter.new(context)
41
+ @active_adapter.keys
29
42
  end
30
43
  end
31
44
 
32
- def self.with_config(options={})
33
- self.config.merge!(options)
34
- self
45
+ def [](key)
46
+ if @fallback_to_logged_out_adapter
47
+ @logged_in && @logged_in_adapter[key] || @logged_out_adapter[key]
48
+ else
49
+ @active_adapter[key]
50
+ end
35
51
  end
36
52
 
37
- def self.config
38
- @config ||= {}
53
+ def []=(key, value)
54
+ if @fallback_to_logged_out_adapter
55
+ @logged_in_adapter[key] = value if @logged_in
56
+ old_value = @logged_out_adapter[key]
57
+ @logged_out_adapter[key] = value
58
+
59
+ decrement_participation(key, old_value) if decrement_participation?(old_value, value)
60
+ else
61
+ @active_adapter[key] = value
62
+ end
63
+ end
64
+
65
+ def delete(key)
66
+ if @fallback_to_logged_out_adapter
67
+ @logged_in_adapter.delete(key)
68
+ @logged_out_adapter.delete(key)
69
+ else
70
+ @active_adapter.delete(key)
71
+ end
39
72
  end
40
73
 
74
+ private
75
+
76
+ def decrement_participation?(old_value, value)
77
+ !old_value.nil? && !value.nil? && old_value != value
78
+ end
79
+
80
+ def decrement_participation(key, value)
81
+ Split.redis.hincrby("#{key}:#{value}", 'participant_count', -1)
82
+ end
41
83
  end
42
84
  end
43
85
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Split
2
3
  # Simplifies the interface to Redis.
3
4
  class RedisInterface
@@ -2,7 +2,7 @@
2
2
  module Split
3
3
  class Trial
4
4
  attr_accessor :experiment
5
- attr_accessor :metadata
5
+ attr_writer :metadata
6
6
 
7
7
  def initialize(attrs = {})
8
8
  self.experiment = attrs.delete(:experiment)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'forwardable'
2
3
 
3
4
  module Split
@@ -8,9 +9,11 @@ module Split
8
9
 
9
10
  def initialize(context, adapter=nil)
10
11
  @user = adapter || Split::Persistence.adapter.new(context)
12
+ @cleaned_up = false
11
13
  end
12
14
 
13
15
  def cleanup_old_experiments!
16
+ return if @cleaned_up
14
17
  keys_without_finished(user.keys).each do |key|
15
18
  experiment = ExperimentCatalog.find key_without_version(key)
16
19
  if experiment.nil? || experiment.has_winner? || experiment.start_time.nil?
@@ -18,6 +21,7 @@ module Split
18
21
  user.delete Experiment.finished_key(key)
19
22
  end
20
23
  end
24
+ @cleaned_up = true
21
25
  end
22
26
 
23
27
  def max_experiments_reached?(experiment_key)
@@ -38,7 +42,7 @@ module Split
38
42
 
39
43
  def active_experiments
40
44
  experiment_pairs = {}
41
- user.keys.each do |key|
45
+ keys_without_finished(user.keys).each do |key|
42
46
  Metric.possible_experiments(key_without_version(key)).each do |experiment|
43
47
  if !experiment.has_winner?
44
48
  experiment_pairs[key_without_version(key)] = user[key]
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  module Split
3
3
  MAJOR = 3
4
- MINOR = 3
5
- PATCH = 2
4
+ MINOR = 4
5
+ PATCH = 0
6
6
  VERSION = [MAJOR, MINOR, PATCH].join('.')
7
7
  end
@@ -10,7 +10,9 @@ describe Split::DashboardPaginationHelpers do
10
10
  context 'when params empty' do
11
11
  let(:params) { Hash[] }
12
12
 
13
- it 'returns 10' do
13
+ it 'returns the default (10)' do
14
+ default_per_page = Split.configuration.dashboard_pagination_default_per_page
15
+ expect(pagination_per).to eql default_per_page
14
16
  expect(pagination_per).to eql 10
15
17
  end
16
18
  end
@@ -27,11 +27,11 @@ describe Split::DashboardHelpers do
27
27
 
28
28
  describe '#round' do
29
29
  it 'can round number strings' do
30
- expect(round('3.1415')).to eq BigDecimal.new('3.14')
30
+ expect(round('3.1415')).to eq BigDecimal('3.14')
31
31
  end
32
32
 
33
33
  it 'can round number strings for precsion' do
34
- expect(round('3.1415', 1)).to eq BigDecimal.new('3.1')
34
+ expect(round('3.1415', 1)).to eq BigDecimal('3.1')
35
35
  end
36
36
 
37
37
  it 'can handle invalid number strings' do
@@ -29,6 +29,10 @@ describe Split::Dashboard do
29
29
  let(:red_link) { link("red") }
30
30
  let(:blue_link) { link("blue") }
31
31
 
32
+ before(:each) do
33
+ Split.configuration.beta_probability_simulations = 1
34
+ end
35
+
32
36
  it "should respond to /" do
33
37
  get '/'
34
38
  expect(last_response).to be_ok
@@ -74,17 +78,39 @@ describe Split::Dashboard do
74
78
  end
75
79
 
76
80
  describe "force alternative" do
77
- let!(:user) do
78
- Split::User.new(@app, { experiment.name => 'a' })
79
- end
81
+ context "initial version" do
82
+ let!(:user) do
83
+ Split::User.new(@app, { experiment.name => 'red' })
84
+ end
80
85
 
81
- before do
82
- allow(Split::User).to receive(:new).and_return(user)
86
+ before do
87
+ allow(Split::User).to receive(:new).and_return(user)
88
+ end
89
+
90
+ it "should set current user's alternative" do
91
+ blue_link.participant_count = 7
92
+ post "/force_alternative?experiment=#{experiment.name}", alternative: "blue"
93
+ expect(user[experiment.key]).to eq("blue")
94
+ expect(blue_link.participant_count).to eq(8)
95
+ end
83
96
  end
84
97
 
85
- it "should set current user's alternative" do
86
- post "/force_alternative?experiment=#{experiment.name}", alternative: "b"
87
- expect(user[experiment.name]).to eq("b")
98
+ context "incremented version" do
99
+ let!(:user) do
100
+ experiment.increment_version
101
+ Split::User.new(@app, { "#{experiment.name}:#{experiment.version}" => 'red' })
102
+ end
103
+
104
+ before do
105
+ allow(Split::User).to receive(:new).and_return(user)
106
+ end
107
+
108
+ it "should set current user's alternative" do
109
+ blue_link.participant_count = 7
110
+ post "/force_alternative?experiment=#{experiment.name}", alternative: "blue"
111
+ expect(user[experiment.key]).to eq("blue")
112
+ expect(blue_link.participant_count).to eq(8)
113
+ end
88
114
  end
89
115
  end
90
116
 
@@ -120,7 +146,7 @@ describe Split::Dashboard do
120
146
  it "removes winner" do
121
147
  post "/reopen?experiment=#{experiment.name}"
122
148
 
123
- expect(experiment).to_not have_winner
149
+ expect(Split::ExperimentCatalog.find(experiment.name)).to_not have_winner
124
150
  end
125
151
 
126
152
  it "keeps existing stats" do
@@ -167,19 +193,14 @@ describe Split::Dashboard do
167
193
  end
168
194
 
169
195
  it "should display the start date" do
170
- experiment_start_time = Time.parse('2011-07-07')
171
- expect(Time).to receive(:now).at_least(:once).and_return(experiment_start_time)
172
- experiment
196
+ experiment.start
173
197
 
174
198
  get '/'
175
199
 
176
- expect(last_response.body).to include('<small>2011-07-07</small>')
200
+ expect(last_response.body).to include("<small>#{experiment.start_time.strftime('%Y-%m-%d')}</small>")
177
201
  end
178
202
 
179
203
  it "should handle experiments without a start date" do
180
- experiment_start_time = Time.parse('2011-07-07')
181
- expect(Time).to receive(:now).at_least(:once).and_return(experiment_start_time)
182
-
183
204
  Split.redis.hdel(:experiment_start_times, experiment.name)
184
205
 
185
206
  get '/'
@@ -33,7 +33,7 @@ describe Split::EncapsulatedHelper do
33
33
  static <%= alt %>
34
34
  <% end %>
35
35
  ERB
36
- expect(template.result(binding)).to match /foo static \d/
36
+ expect(template.result(binding)).to match(/foo static \d/)
37
37
  end
38
38
 
39
39
  end
@@ -35,6 +35,12 @@ describe Split::Experiment do
35
35
  expect(experiment.resettable).to be_truthy
36
36
  end
37
37
 
38
+ it "should be resettable when loading from configuration" do
39
+ allow(Split.configuration).to receive(:experiment_for).with('some_experiment') { { alternatives: %w(a b) } }
40
+
41
+ expect(Split::Experiment.new('some_experiment')).to be_resettable
42
+ end
43
+
38
44
  it "should save to redis" do
39
45
  experiment.save
40
46
  expect(Split.redis.exists('basket_text')).to be true
@@ -86,7 +92,7 @@ describe Split::Experiment do
86
92
  experiment.save
87
93
  experiment.save
88
94
  expect(Split.redis.exists('basket_text')).to be true
89
- expect(Split.redis.lrange('basket_text', 0, -1)).to eq(['Basket', "Cart"])
95
+ expect(Split.redis.lrange('basket_text', 0, -1)).to eq(['{"Basket":1}', '{"Cart":1}'])
90
96
  end
91
97
 
92
98
  describe 'new record?' do
@@ -213,12 +219,41 @@ describe Split::Experiment do
213
219
  it "should have no winner initially" do
214
220
  expect(experiment.winner).to be_nil
215
221
  end
222
+ end
216
223
 
224
+ describe 'winner=' do
217
225
  it "should allow you to specify a winner" do
218
226
  experiment.save
219
227
  experiment.winner = 'red'
220
228
  expect(experiment.winner.name).to eq('red')
221
229
  end
230
+
231
+ context 'when has_winner state is memoized' do
232
+ before { expect(experiment).to_not have_winner }
233
+
234
+ it 'should keep has_winner state consistent' do
235
+ experiment.winner = 'red'
236
+ expect(experiment).to have_winner
237
+ end
238
+ end
239
+ end
240
+
241
+ describe 'reset_winner' do
242
+ before { experiment.winner = 'green' }
243
+
244
+ it 'should reset the winner' do
245
+ experiment.reset_winner
246
+ expect(experiment.winner).to be_nil
247
+ end
248
+
249
+ context 'when has_winner state is memoized' do
250
+ before { expect(experiment).to have_winner }
251
+
252
+ it 'should keep has_winner state consistent' do
253
+ experiment.reset_winner
254
+ expect(experiment).to_not have_winner
255
+ end
256
+ end
222
257
  end
223
258
 
224
259
  describe 'has_winner?' do
@@ -235,6 +270,12 @@ describe Split::Experiment do
235
270
  expect(experiment).to_not have_winner
236
271
  end
237
272
  end
273
+
274
+ it 'memoizes has_winner state' do
275
+ expect(experiment).to receive(:winner).once
276
+ expect(experiment).to_not have_winner
277
+ expect(experiment).to_not have_winner
278
+ end
238
279
  end
239
280
 
240
281
  describe 'reset' do
@@ -414,9 +455,7 @@ describe Split::Experiment do
414
455
  }
415
456
 
416
457
  context "saving experiment" do
417
- def same_but_different_goals
418
- Split::ExperimentCatalog.find_or_create({'link_color' => ["purchase", "refund"]}, 'blue', 'red', 'green')
419
- end
458
+ let(:same_but_different_goals) { Split::ExperimentCatalog.find_or_create({'link_color' => ["purchase", "refund"]}, 'blue', 'red', 'green') }
420
459
 
421
460
  before { experiment.save }
422
461
 
@@ -425,7 +464,7 @@ describe Split::Experiment do
425
464
  end
426
465
 
427
466
  it "should reset an experiment if it is loaded with different goals" do
428
- same_experiment = same_but_different_goals
467
+ same_but_different_goals
429
468
  expect(Split::ExperimentCatalog.find("link_color").goals).to eq(["purchase", "refund"])
430
469
  end
431
470
 
@@ -183,8 +183,7 @@ describe Split::Helper do
183
183
  ab_test('link_color', {'blue' => 0.01}, 'red' => 0.2)
184
184
  experiment = Split::ExperimentCatalog.find('link_color')
185
185
  expect(experiment.alternatives.map(&:name)).to eq(['blue', 'red'])
186
- # TODO: persist alternative weights
187
- # expect(experiment.alternatives.collect{|a| a.weight}).to eq([0.01, 0.2])
186
+ expect(experiment.alternatives.collect{|a| a.weight}).to match_array([0.01, 0.2])
188
187
  end
189
188
 
190
189
  it "should only let a user participate in one experiment at a time" do
@@ -297,98 +296,126 @@ describe Split::Helper do
297
296
  end
298
297
 
299
298
  describe 'ab_finished' do
300
- before(:each) do
301
- @experiment_name = 'link_color'
302
- @alternatives = ['blue', 'red']
303
- @experiment = Split::ExperimentCatalog.find_or_create(@experiment_name, *@alternatives)
304
- @alternative_name = ab_test(@experiment_name, *@alternatives)
305
- @previous_completion_count = Split::Alternative.new(@alternative_name, @experiment_name).completed_count
306
- end
299
+ context 'for an experiment that the user participates in' do
300
+ before(:each) do
301
+ @experiment_name = 'link_color'
302
+ @alternatives = ['blue', 'red']
303
+ @experiment = Split::ExperimentCatalog.find_or_create(@experiment_name, *@alternatives)
304
+ @alternative_name = ab_test(@experiment_name, *@alternatives)
305
+ @previous_completion_count = Split::Alternative.new(@alternative_name, @experiment_name).completed_count
306
+ end
307
307
 
308
- it 'should increment the counter for the completed alternative' do
309
- ab_finished(@experiment_name)
310
- new_completion_count = Split::Alternative.new(@alternative_name, @experiment_name).completed_count
311
- expect(new_completion_count).to eq(@previous_completion_count + 1)
312
- end
308
+ it 'should increment the counter for the completed alternative' do
309
+ ab_finished(@experiment_name)
310
+ new_completion_count = Split::Alternative.new(@alternative_name, @experiment_name).completed_count
311
+ expect(new_completion_count).to eq(@previous_completion_count + 1)
312
+ end
313
313
 
314
- it "should set experiment's finished key if reset is false" do
315
- ab_finished(@experiment_name, {:reset => false})
316
- expect(ab_user[@experiment.key]).to eq(@alternative_name)
317
- expect(ab_user[@experiment.finished_key]).to eq(true)
318
- end
314
+ it "should set experiment's finished key if reset is false" do
315
+ ab_finished(@experiment_name, {:reset => false})
316
+ expect(ab_user[@experiment.key]).to eq(@alternative_name)
317
+ expect(ab_user[@experiment.finished_key]).to eq(true)
318
+ end
319
319
 
320
- it 'should not increment the counter if reset is false and the experiment has been already finished' do
321
- 2.times { ab_finished(@experiment_name, {:reset => false}) }
322
- new_completion_count = Split::Alternative.new(@alternative_name, @experiment_name).completed_count
323
- expect(new_completion_count).to eq(@previous_completion_count + 1)
324
- end
320
+ it 'should not increment the counter if reset is false and the experiment has been already finished' do
321
+ 2.times { ab_finished(@experiment_name, {:reset => false}) }
322
+ new_completion_count = Split::Alternative.new(@alternative_name, @experiment_name).completed_count
323
+ expect(new_completion_count).to eq(@previous_completion_count + 1)
324
+ end
325
325
 
326
- it 'should not increment the counter for an experiment that the user is not participating in' do
327
- ab_test('button_size', 'small', 'big')
326
+ it 'should not increment the counter for an ended experiment' do
327
+ e = Split::ExperimentCatalog.find_or_create('button_size', 'small', 'big')
328
+ e.winner = 'small'
329
+ a = ab_test('button_size', 'small', 'big')
330
+ expect(a).to eq('small')
331
+ expect(lambda {
332
+ ab_finished('button_size')
333
+ }).not_to change { Split::Alternative.new(a, 'button_size').completed_count }
334
+ end
328
335
 
329
- # So, user should be participating in the link_color experiment and
330
- # receive the control for button_size. As the user is not participating in
331
- # the button size experiment, finishing it should not increase the
332
- # completion count for that alternative.
333
- expect(lambda {
334
- ab_finished('button_size')
335
- }).not_to change { Split::Alternative.new('small', 'button_size').completed_count }
336
- end
336
+ it "should clear out the user's participation from their session" do
337
+ expect(ab_user[@experiment.key]).to eq(@alternative_name)
338
+ ab_finished(@experiment_name)
339
+ expect(ab_user.keys).to be_empty
340
+ end
337
341
 
338
- it 'should not increment the counter for an ended experiment' do
339
- e = Split::ExperimentCatalog.find_or_create('button_size', 'small', 'big')
340
- e.winner = 'small'
341
- a = ab_test('button_size', 'small', 'big')
342
- expect(a).to eq('small')
343
- expect(lambda {
344
- ab_finished('button_size')
345
- }).not_to change { Split::Alternative.new(a, 'button_size').completed_count }
346
- end
342
+ it "should not clear out the users session if reset is false" do
343
+ expect(ab_user[@experiment.key]).to eq(@alternative_name)
344
+ ab_finished(@experiment_name, {:reset => false})
345
+ expect(ab_user[@experiment.key]).to eq(@alternative_name)
346
+ expect(ab_user[@experiment.finished_key]).to eq(true)
347
+ end
347
348
 
348
- it "should clear out the user's participation from their session" do
349
- expect(ab_user[@experiment.key]).to eq(@alternative_name)
350
- ab_finished(@experiment_name)
351
- expect(ab_user.keys).to be_empty
352
- end
349
+ it "should reset the users session when experiment is not versioned" do
350
+ expect(ab_user[@experiment.key]).to eq(@alternative_name)
351
+ ab_finished(@experiment_name)
352
+ expect(ab_user.keys).to be_empty
353
+ end
353
354
 
354
- it "should not clear out the users session if reset is false" do
355
- expect(ab_user[@experiment.key]).to eq(@alternative_name)
356
- ab_finished(@experiment_name, {:reset => false})
357
- expect(ab_user[@experiment.key]).to eq(@alternative_name)
358
- expect(ab_user[@experiment.finished_key]).to eq(true)
359
- end
355
+ it "should reset the users session when experiment is versioned" do
356
+ @experiment.increment_version
357
+ @alternative_name = ab_test(@experiment_name, *@alternatives)
360
358
 
361
- it "should reset the users session when experiment is not versioned" do
362
- expect(ab_user[@experiment.key]).to eq(@alternative_name)
363
- ab_finished(@experiment_name)
364
- expect(ab_user.keys).to be_empty
365
- end
359
+ expect(ab_user[@experiment.key]).to eq(@alternative_name)
360
+ ab_finished(@experiment_name)
361
+ expect(ab_user.keys).to be_empty
362
+ end
366
363
 
367
- it "should reset the users session when experiment is versioned" do
368
- @experiment.increment_version
369
- @alternative_name = ab_test(@experiment_name, *@alternatives)
364
+ context "when on_trial_complete is set" do
365
+ before { Split.configuration.on_trial_complete = :some_method }
366
+ it "should call the method" do
367
+ expect(self).to receive(:some_method)
368
+ ab_finished(@experiment_name)
369
+ end
370
370
 
371
- expect(ab_user[@experiment.key]).to eq(@alternative_name)
372
- ab_finished(@experiment_name)
373
- expect(ab_user.keys).to be_empty
371
+ it "should not call the method without alternative" do
372
+ ab_user[@experiment.key] = nil
373
+ expect(self).not_to receive(:some_method)
374
+ ab_finished(@experiment_name)
375
+ end
376
+ end
374
377
  end
375
378
 
376
- it "should do nothing where the experiment was not started by this user" do
377
- ab_user = nil
378
- expect(lambda { ab_finished('some_experiment_not_started_by_the_user') }).not_to raise_exception
379
+ context 'for an experiment that the user is excluded from' do
380
+ before do
381
+ alternative = ab_test('link_color', 'blue', 'red')
382
+ expect(Split::Alternative.new(alternative, 'link_color').participant_count).to eq(1)
383
+ alternative = ab_test('button_size', 'small', 'big')
384
+ expect(Split::Alternative.new(alternative, 'button_size').participant_count).to eq(0)
385
+ end
386
+
387
+ it 'should not increment the completed counter' do
388
+ # So, user should be participating in the link_color experiment and
389
+ # receive the control for button_size. As the user is not participating in
390
+ # the button size experiment, finishing it should not increase the
391
+ # completion count for that alternative.
392
+ expect(lambda {
393
+ ab_finished('button_size')
394
+ }).not_to change { Split::Alternative.new('small', 'button_size').completed_count }
395
+ end
379
396
  end
380
397
 
381
- context "when on_trial_complete is set" do
382
- before { Split.configuration.on_trial_complete = :some_method }
383
- it "should call the method" do
384
- expect(self).to receive(:some_method)
385
- ab_finished(@experiment_name)
398
+ context 'for an experiment that the user does not participate in' do
399
+ before do
400
+ Split::ExperimentCatalog.find_or_create(:not_started_experiment, 'control', 'alt')
401
+ end
402
+ it 'should not raise an exception' do
403
+ expect { ab_finished(:not_started_experiment) }.not_to raise_exception
386
404
  end
387
405
 
388
- it "should not call the method without alternative" do
389
- ab_user[@experiment.key] = nil
390
- expect(self).not_to receive(:some_method)
391
- ab_finished(@experiment_name)
406
+ it 'should not change the user state when reset is false' do
407
+ expect { ab_finished(:not_started_experiment, reset: false) }.not_to change { ab_user.keys}.from([])
408
+ end
409
+
410
+ it 'should not change the user state when reset is true' do
411
+ expect(self).not_to receive(:reset!)
412
+ ab_finished(:not_started_experiment)
413
+ end
414
+
415
+ it 'should not increment the completed counter' do
416
+ ab_finished(:not_started_experiment)
417
+ expect(Split::Alternative.new('control', :not_started_experiment).completed_count).to eq(0)
418
+ expect(Split::Alternative.new('alt', :not_started_experiment).completed_count).to eq(0)
392
419
  end
393
420
  end
394
421
  end
@@ -528,6 +555,17 @@ describe Split::Helper do
528
555
  expect(active_experiments.first[0]).to eq "link_color"
529
556
  end
530
557
 
558
+ it 'should show versioned tests properly' do
559
+ 10.times { experiment.reset }
560
+
561
+ alternative = ab_test(experiment.name, 'blue', 'red')
562
+ ab_finished(experiment.name, reset: false)
563
+
564
+ expect(experiment.version).to eq(10)
565
+ expect(active_experiments.count).to eq 1
566
+ expect(active_experiments).to eq({'link_color' => alternative })
567
+ end
568
+
531
569
  it 'should show multiple tests' do
532
570
  Split.configure do |config|
533
571
  config.allow_multiple_experiments = true
@@ -545,7 +583,7 @@ describe Split::Helper do
545
583
  end
546
584
  e = Split::ExperimentCatalog.find_or_create('def', '4', '5', '6')
547
585
  e.winner = '4'
548
- alternative = ab_test('def', '4', '5', '6')
586
+ ab_test('def', '4', '5', '6')
549
587
  another_alternative = ab_test('ghi', '7', '8', '9')
550
588
  expect(active_experiments.count).to eq 1
551
589
  expect(active_experiments.first[0]).to eq "ghi"
@@ -1021,8 +1059,8 @@ describe Split::Helper do
1021
1059
 
1022
1060
  it 'should handle multiple experiments correctly' do
1023
1061
  experiment2 = Split::ExperimentCatalog.find_or_create('link_color2', 'blue', 'red')
1024
- alternative_name = ab_test('link_color', 'blue', 'red')
1025
- alternative_name2 = ab_test('link_color2', 'blue', 'red')
1062
+ ab_test('link_color', 'blue', 'red')
1063
+ ab_test('link_color2', 'blue', 'red')
1026
1064
  ab_finished('link_color2')
1027
1065
 
1028
1066
  experiment2.alternatives.each do |alt|