split 3.3.2 → 3.4.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.
@@ -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|