eeny-meeny 1.0.0 → 2.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +21 -0
- data/README.md +175 -9
- data/eeny-meeny.gemspec +3 -2
- data/lib/eeny-meeny.rb +28 -1
- data/lib/eeny-meeny/experiment_helper.rb +12 -10
- data/lib/eeny-meeny/middleware.rb +14 -21
- data/lib/eeny-meeny/models/cookie.rb +104 -0
- data/lib/eeny-meeny/{encryptor.rb → models/encryptor.rb} +0 -0
- data/lib/eeny-meeny/{experiment.rb → models/experiment.rb} +19 -2
- data/lib/eeny-meeny/{variation.rb → models/variation.rb} +0 -0
- data/lib/eeny-meeny/railtie.rb +15 -14
- data/lib/eeny-meeny/routing/experiment_constraint.rb +19 -0
- data/lib/eeny-meeny/routing/smoke_test_constraint.rb +15 -0
- data/lib/eeny-meeny/version.rb +1 -1
- data/lib/tasks/cookie.rake +48 -0
- data/spec/eeny-meeny/middleware_spec.rb +15 -18
- data/spec/eeny-meeny/models/cookie_spec.rb +137 -0
- data/spec/eeny-meeny/models/experiment_spec.rb +181 -0
- data/spec/eeny-meeny/{variation_spec.rb → models/variation_spec.rb} +1 -1
- data/spec/eeny-meeny/routing/experiment_constraint_spec.rb +39 -0
- data/spec/eeny-meeny/routing/smoke_test_constraint_spec.rb +35 -0
- data/spec/fixtures/experiments.yml +12 -0
- data/spec/spec_helper.rb +18 -1
- data/spec/tasks/cookie_task_spec.rb +72 -0
- metadata +31 -13
- data/lib/eeny-meeny/middleware_helper.rb +0 -25
- data/lib/eeny-meeny/route_constraint.rb +0 -33
- data/lib/eeny-meeny/shared_methods.rb +0 -23
- data/spec/eeny-meeny/experiment_spec.rb +0 -62
File without changes
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'eeny-meeny/variation'
|
1
|
+
require 'eeny-meeny/models/variation'
|
2
2
|
require 'active_support/time'
|
3
3
|
require 'active_support/core_ext/enumerable'
|
4
4
|
|
@@ -6,6 +6,17 @@ module EenyMeeny
|
|
6
6
|
class Experiment
|
7
7
|
attr_reader :id, :name, :version, :variations, :total_weight, :end_at, :start_at
|
8
8
|
|
9
|
+
def self.find_all
|
10
|
+
EenyMeeny.config.experiments.map do |id, experiment|
|
11
|
+
new(id, **experiment)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.find_by_id(experiment_id)
|
16
|
+
experiment = EenyMeeny.config.experiments[experiment_id.to_sym]
|
17
|
+
new(experiment_id, **experiment) if experiment
|
18
|
+
end
|
19
|
+
|
9
20
|
def initialize(id, name: '', version: 1, variations: {}, start_at: nil, end_at: nil)
|
10
21
|
@id = id
|
11
22
|
@name = name
|
@@ -14,11 +25,17 @@ module EenyMeeny
|
|
14
25
|
Variation.new(variation_id, **variation)
|
15
26
|
end
|
16
27
|
@total_weight = (@variations.empty? ? 1.0 : @variations.sum { |variation| variation.weight.to_f })
|
17
|
-
|
18
28
|
@start_at = Time.zone.parse(start_at) if start_at
|
19
29
|
@end_at = Time.zone.parse(end_at) if end_at
|
20
30
|
end
|
21
31
|
|
32
|
+
def active?(now = Time.zone.now)
|
33
|
+
return true if @start_at.nil? && @end_at.nil?
|
34
|
+
return true if @end_at.nil? && (@start_at && (now > @start_at)) # specified start - open-ended
|
35
|
+
return true if @start_at.nil? && (@end_at && (now < @end_at)) # unspecified start - specified end
|
36
|
+
!!((@start_at && (now > @start_at)) && (@end_at && (now < @end_at))) # specified start and end
|
37
|
+
end
|
38
|
+
|
22
39
|
def pick_variation
|
23
40
|
Hash[
|
24
41
|
@variations.map do |variation|
|
File without changes
|
data/lib/eeny-meeny/railtie.rb
CHANGED
@@ -1,27 +1,28 @@
|
|
1
|
+
require 'eeny-meeny'
|
1
2
|
require 'eeny-meeny/experiment_helper'
|
2
3
|
require 'eeny-meeny/middleware'
|
3
4
|
|
4
5
|
module EenyMeeny
|
5
6
|
class Railtie < Rails::Railtie
|
6
7
|
config.eeny_meeny = ActiveSupport::OrderedOptions.new
|
7
|
-
# default config values. these can be changed in the rails environment configuration.
|
8
|
-
config.eeny_meeny.experiments = []
|
9
|
-
config.eeny_meeny.secure = true
|
10
|
-
config.eeny_meeny.secret = '9fc8b966eca7d03d55df40c01c10b8e02bf1f9d12d27b8968d53eb53e8c239902d00bf6afae5e726ce1111159eeb2f8f0e77233405db1d82dd71397f651a0a4f'
|
11
|
-
config.eeny_meeny.cookies = ActiveSupport::OrderedOptions.new
|
12
|
-
config.eeny_meeny.cookies.path = '/'
|
13
|
-
config.eeny_meeny.cookies.same_site = :strict
|
14
8
|
|
15
|
-
initializer 'eeny_meeny.
|
9
|
+
initializer 'eeny_meeny.configure' do |app|
|
10
|
+
# Configrue EenyMeeny (defaults set in eeny_meeny.rb)
|
11
|
+
EenyMeeny.configure do |config|
|
12
|
+
config.cookies = app.config.eeny_meeny[:cookies] if app.config.eeny_meeny.has_key?(:cookies)
|
13
|
+
config.experiments = app.config.eeny_meeny[:experiments] if app.config.eeny_meeny.has_key?(:experiments)
|
14
|
+
config.secret = app.config.eeny_meeny[:secret] if app.config.eeny_meeny.has_key?(:secret)
|
15
|
+
config.secure = app.config.eeny_meeny[:secure] if app.config.eeny_meeny.has_key?(:secure)
|
16
|
+
end
|
17
|
+
# Include Helpers in ActionController and ActionView
|
16
18
|
ActionController::Base.send :include, EenyMeeny::ExperimentHelper
|
17
19
|
ActionView::Base.send :include, EenyMeeny::ExperimentHelper
|
20
|
+
# Insert Middleware
|
21
|
+
app.middleware.insert_before 'ActionDispatch::Cookies', EenyMeeny::Middleware
|
22
|
+
end
|
18
23
|
|
19
|
-
|
20
|
-
|
21
|
-
config.eeny_meeny.secure,
|
22
|
-
config.eeny_meeny.secret,
|
23
|
-
config.eeny_meeny.cookies.path,
|
24
|
-
config.eeny_meeny.cookies.same_site
|
24
|
+
rake_tasks do
|
25
|
+
load 'tasks/cookie.rake'
|
25
26
|
end
|
26
27
|
end
|
27
28
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'eeny-meeny/models/cookie'
|
2
|
+
require 'eeny-meeny/models/experiment'
|
3
|
+
|
4
|
+
module EenyMeeny
|
5
|
+
class ExperimentConstraint
|
6
|
+
|
7
|
+
def initialize(experiment_id, variation_id: nil)
|
8
|
+
@experiment = EenyMeeny::Experiment.find_by_id(experiment_id)
|
9
|
+
@variation_id = variation_id
|
10
|
+
end
|
11
|
+
|
12
|
+
def matches?(request)
|
13
|
+
return false unless @experiment.active?
|
14
|
+
cookie = EenyMeeny::Cookie.read(request.cookie_jar[EenyMeeny::Cookie.cookie_name(@experiment)])
|
15
|
+
return false if cookie.nil? # Not participating in experiment
|
16
|
+
(@variation_id.nil? || @variation_id == cookie[:variation].id)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'eeny-meeny/models/cookie'
|
2
|
+
|
3
|
+
module EenyMeeny
|
4
|
+
class SmokeTestConstraint
|
5
|
+
|
6
|
+
def initialize(smoke_test_id, version: 1)
|
7
|
+
@smoke_test_cookie_name = EenyMeeny::Cookie.smoke_test_name(smoke_test_id, version: version)
|
8
|
+
end
|
9
|
+
|
10
|
+
def matches?(request)
|
11
|
+
cookie = EenyMeeny::Cookie.read(request.cookie_jar[@smoke_test_cookie_name])
|
12
|
+
!cookie.nil?
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/eeny-meeny/version.rb
CHANGED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'eeny-meeny/models/cookie'
|
3
|
+
require 'eeny-meeny/models/experiment'
|
4
|
+
|
5
|
+
def write_cookie(experiment_id, variation_id: nil)
|
6
|
+
experiment = EenyMeeny::Experiment.find_by_id(experiment_id)
|
7
|
+
raise "Experiment with id '#{experiment_id}' not found!" if experiment.nil?
|
8
|
+
if variation_id.nil?
|
9
|
+
EenyMeeny::Cookie.create_for_experiment(experiment, EenyMeeny.config.cookies)
|
10
|
+
else
|
11
|
+
EenyMeeny::Cookie.create_for_experiment_variation(experiment, variation_id, EenyMeeny.config.cookies)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
namespace :eeny_meeny do
|
16
|
+
|
17
|
+
namespace :cookie do
|
18
|
+
desc 'Create a valid EenyMeeny experiment cookie'
|
19
|
+
task :experiment, [:experiment_id] => :environment do |t, args|
|
20
|
+
raise "Missing 'experiment_id' parameter" if (args['experiment_id'].nil? || args['experiment_id'].empty?)
|
21
|
+
experiment_id = args['experiment_id'].to_sym
|
22
|
+
cookie = write_cookie(experiment_id)
|
23
|
+
puts cookie
|
24
|
+
end
|
25
|
+
|
26
|
+
desc 'Create a valid EenyMeeny experiment cookie for a specific variation'
|
27
|
+
task :experiment_variation, [:experiment_id, :variation_id] => :environment do |t, args|
|
28
|
+
raise "Missing 'experiment_id' parameter" if (args['experiment_id'].nil? || args['experiment_id'].empty?)
|
29
|
+
raise "Missing 'variation_id' parameter" if (args['variation_id'].nil? || args['variation_id'].empty?)
|
30
|
+
experiment_id = args['experiment_id'].to_sym
|
31
|
+
variation_id = args['variation_id'].to_sym
|
32
|
+
cookie = write_cookie(experiment_id, variation_id: variation_id)
|
33
|
+
puts cookie
|
34
|
+
end
|
35
|
+
|
36
|
+
desc 'Create a valid EenyMeeny smoke test cookie'
|
37
|
+
task :smoke_test, [:smoke_test_id, :version] => :environment do |t, args|
|
38
|
+
raise "Missing 'smoke_test_id' parameter" if (args['smoke_test_id'].nil? || args['smoke_test_id'].empty?)
|
39
|
+
smoke_test_id = args['smoke_test_id']
|
40
|
+
version = args['version'] || 1
|
41
|
+
cookie = EenyMeeny::Cookie.create_for_smoke_test(smoke_test_id, version: version)
|
42
|
+
puts cookie
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
|
48
|
+
end
|
@@ -1,35 +1,32 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'eeny-meeny/encryptor'
|
2
|
+
require 'eeny-meeny/models/encryptor'
|
3
3
|
require 'eeny-meeny/middleware'
|
4
4
|
|
5
5
|
def initialize_app(secure: true, secret: 'test', path: '/', same_site: :strict)
|
6
|
-
|
7
|
-
|
6
|
+
EenyMeeny.reset!
|
7
|
+
EenyMeeny.configure do |config|
|
8
|
+
config.cookies = { path: path, same_site: same_site }
|
9
|
+
config.experiments = YAML.load_file(File.join('spec','fixtures','experiments.yml'))
|
10
|
+
config.secret = secret
|
11
|
+
config.secure = secure
|
12
|
+
end
|
13
|
+
described_class.new(app)
|
8
14
|
end
|
9
15
|
|
10
16
|
describe EenyMeeny::Middleware do
|
11
17
|
|
12
18
|
let(:app) { MockRackApp.new }
|
13
|
-
before(:example) do
|
14
|
-
allow(Time).to receive_message_chain(:zone, :now) { Time.now }
|
15
|
-
end
|
16
19
|
|
17
20
|
describe 'when initialized' do
|
18
21
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
expect(instance.instance_variable_get(:@encryptor)).to be_a EenyMeeny::Encryptor
|
24
|
-
end
|
22
|
+
subject { initialize_app }
|
23
|
+
|
24
|
+
it 'sets the experiments' do
|
25
|
+
expect(subject.instance_variable_get(:@experiments)).to be
|
25
26
|
end
|
26
27
|
|
27
|
-
|
28
|
-
|
29
|
-
instance = initialize_app(secure: false)
|
30
|
-
expect(instance.instance_variable_get(:@secure)).to be false
|
31
|
-
expect(instance.instance_variable_get(:@encryptor)).to be nil
|
32
|
-
end
|
28
|
+
it 'sets the cookie config' do
|
29
|
+
expect(subject.instance_variable_get(:@cookie_config)).to be
|
33
30
|
end
|
34
31
|
end
|
35
32
|
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'eeny-meeny/models/cookie'
|
3
|
+
require 'eeny-meeny/models/experiment'
|
4
|
+
|
5
|
+
describe EenyMeeny::Cookie do
|
6
|
+
describe 'when initialized' do
|
7
|
+
subject do
|
8
|
+
described_class.new(name: 'test', value: '12345')
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'sets the instance variables correctly' do
|
12
|
+
expect(subject.name).to eq('test')
|
13
|
+
expect(subject.value).to eq('12345')
|
14
|
+
expect(subject.expires).to be
|
15
|
+
expect(subject.httponly).to be
|
16
|
+
expect(subject.same_site).to be_nil
|
17
|
+
expect(subject.path).to be_nil
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#to_h' do
|
21
|
+
it 'returns cookie options' do
|
22
|
+
options = subject.to_h
|
23
|
+
expect(options).to be_a Hash
|
24
|
+
expect(options.keys.sort).to eq([:value, :httponly, :expires].sort)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '.create_for_smoke_test' do
|
30
|
+
context 'given a smoke test id' do
|
31
|
+
it 'creates a cookie' do
|
32
|
+
instance = described_class.create_for_smoke_test(:shadow)
|
33
|
+
expect(instance).to be_a EenyMeeny::Cookie
|
34
|
+
expect(instance.name).to eq(described_class.smoke_test_name(:shadow))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '.create_for_experiment', experiments: true do
|
40
|
+
context 'given an experiment' do
|
41
|
+
it 'creates a cookie' do
|
42
|
+
experiment = EenyMeeny::Experiment.find_by_id(:my_page)
|
43
|
+
instance = described_class.create_for_experiment(experiment)
|
44
|
+
expect(instance).to be_a EenyMeeny::Cookie
|
45
|
+
expect(instance.name).to eq(described_class.cookie_name(experiment))
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'and given cookie options' do
|
49
|
+
it 'creates a cookie with the given options' do
|
50
|
+
experiment = EenyMeeny::Experiment.find_by_id(:my_page)
|
51
|
+
instance = described_class.create_for_experiment(experiment, same_site: :fun_stuff)
|
52
|
+
expect(instance).to be_a EenyMeeny::Cookie
|
53
|
+
expect(instance.name).to eq(described_class.cookie_name(experiment))
|
54
|
+
expect(instance.same_site).to eq(:fun_stuff)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe '.create_for_experiment_variation', experiments: true do
|
61
|
+
context 'given an experiment and an variation id' do
|
62
|
+
it 'creates a cookie for that variation' do
|
63
|
+
experiment = EenyMeeny::Experiment.find_by_id(:my_page)
|
64
|
+
instance = described_class.create_for_experiment_variation(experiment, :new)
|
65
|
+
expect(instance).to be_a EenyMeeny::Cookie
|
66
|
+
expect(instance.name).to eq(described_class.cookie_name(experiment))
|
67
|
+
expect(described_class.read(instance.value)[:variation].id).to eq(:new)
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'and given cookie options' do
|
71
|
+
it 'creates a cookie for that variation with the given options' do
|
72
|
+
experiment = EenyMeeny::Experiment.find_by_id(:my_page)
|
73
|
+
instance = described_class.create_for_experiment_variation(experiment, :new, same_site: :fun_stuff)
|
74
|
+
expect(instance).to be_a EenyMeeny::Cookie
|
75
|
+
expect(instance.name).to eq(described_class.cookie_name(experiment))
|
76
|
+
expect(instance.same_site).to eq(:fun_stuff)
|
77
|
+
expect(described_class.read(instance.value)[:variation].id).to eq(:new)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe '.smoke_test_name' do
|
84
|
+
context 'given a smoke_test_id' do
|
85
|
+
it 'returns the smoke test cookie name' do
|
86
|
+
expect(described_class.smoke_test_name(:something)).to eq('smoke_test_something_v1')
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe '.cookie_name', experiments: true do
|
92
|
+
context 'given an experiment' do
|
93
|
+
it 'returns the experiment cookie name' do
|
94
|
+
experiment = EenyMeeny::Experiment.find_by_id(:my_page)
|
95
|
+
expect(described_class.cookie_name(experiment)).to eq('eeny_meeny_my_page_v1')
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe '.read', experiments: true do
|
101
|
+
context 'given an empty cookie string' do
|
102
|
+
it 'returns nil' do
|
103
|
+
expect(described_class.read(nil)).to be_nil
|
104
|
+
expect(described_class.read('')).to be_nil
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
context 'when EenyMeeny.config.secure = true' do
|
109
|
+
context 'and given a valid cookie string' do
|
110
|
+
it 'decrypts the string and returns the cookie hash' do
|
111
|
+
valid_cookie_string = 'DhtZMLAtVpWiruuq6BdQ+YEeDTK4G1p0HQLeyKFZd2+fD8YyT004p56S03yXsE/kzCASnD9O1sk1tsIHDZ8W2gq+5zQD3fu2aqqLm461FOfy0En4/LqHCP0J0VYol3Py0BlhepjSGDuJrRU7TdKZWsG2/dCiMVLjMf0Pt00NZWooUvQfRz9SCzaFL0mywoVrY1ErKKQCNEPmLREaxavCng=='
|
112
|
+
expect(described_class.read(valid_cookie_string)).to be_a Hash
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
context 'and given an invalid cookie string' do
|
117
|
+
it 'returns nil' do
|
118
|
+
expect(described_class.read('qwedasdafagasdaasdasd')).to be_nil
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
context 'when EenyMeeny.config.secure = false' do
|
124
|
+
context 'and given a valid cookie string' do
|
125
|
+
it 'returns the cookie hash' do
|
126
|
+
EenyMeeny.configure do |config|
|
127
|
+
config.secure = false
|
128
|
+
config.experiments = YAML.load_file(File.join('spec','fixtures','experiments.yml'))
|
129
|
+
end
|
130
|
+
experiment = EenyMeeny::Experiment.find_by_id(:my_page)
|
131
|
+
valid_cookie_string = described_class.create_for_experiment(experiment).value
|
132
|
+
expect(described_class.read(valid_cookie_string)).to be_a Hash
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,181 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'eeny-meeny/models/experiment'
|
3
|
+
require 'eeny-meeny/models/variation'
|
4
|
+
|
5
|
+
def experiment_with_time(time = {})
|
6
|
+
experiment_options = {
|
7
|
+
name: 'Test 1',
|
8
|
+
variations: {
|
9
|
+
a: { name: 'A' },
|
10
|
+
b: { name: 'B' }}
|
11
|
+
}.merge(time)
|
12
|
+
described_class.new(:experiment_1,
|
13
|
+
**experiment_options)
|
14
|
+
end
|
15
|
+
|
16
|
+
describe EenyMeeny::Experiment do
|
17
|
+
describe 'when initialized' do
|
18
|
+
|
19
|
+
context 'with weighted variations' do
|
20
|
+
subject do
|
21
|
+
described_class.new(:experiment_1,
|
22
|
+
name: 'Test 1',
|
23
|
+
variations: {
|
24
|
+
a: { name: 'A', weight: 0.5 },
|
25
|
+
b: { name: 'B', weight: 0.3 }})
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'sets the instance variables' do
|
29
|
+
expect(subject.id).to eq(:experiment_1)
|
30
|
+
expect(subject.name).to eq('Test 1')
|
31
|
+
expect(subject.variations).to be_a Array
|
32
|
+
expect(subject.variations.size).to eq(2)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "has a 'total_weight' equal to the sum of the variation weights" do
|
36
|
+
expect(subject.total_weight).to eq(0.8)
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '#pick_variation' do
|
40
|
+
it 'picks a variation' do
|
41
|
+
expect(subject.pick_variation).to be_a EenyMeeny::Variation
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'with non-weighted variations' do
|
47
|
+
subject do
|
48
|
+
described_class.new(:experiment_1,
|
49
|
+
name: 'Test 1',
|
50
|
+
variations: {
|
51
|
+
a: { name: 'A' },
|
52
|
+
b: { name: 'B' }})
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'sets the instance variables' do
|
56
|
+
expect(subject.id).to eq(:experiment_1)
|
57
|
+
expect(subject.name).to eq('Test 1')
|
58
|
+
expect(subject.variations).to be_a Array
|
59
|
+
expect(subject.variations.size).to eq(2)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "has a 'total_weight' equal to the number of the variation weights" do
|
63
|
+
expect(subject.total_weight).to eq(2)
|
64
|
+
end
|
65
|
+
|
66
|
+
describe '#pick_variation' do
|
67
|
+
it 'picks a variation' do
|
68
|
+
expect(subject.pick_variation).to be_a EenyMeeny::Variation
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe '#active?' do
|
73
|
+
context 'when the experiment neither have a start_at or end_at time' do
|
74
|
+
it 'returns true' do
|
75
|
+
expect(subject.active?).to be true
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context 'when the experiment only have an end_at time' do
|
80
|
+
context 'and the current time < end_at' do
|
81
|
+
it 'returns true' do
|
82
|
+
instance = experiment_with_time(end_at: (Time.zone.now+3600).iso8601)
|
83
|
+
expect(instance.active?).to be true
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context 'and the current time > end_at' do
|
88
|
+
it 'returns false' do
|
89
|
+
instance = experiment_with_time(end_at: (Time.zone.now-3600).iso8601)
|
90
|
+
expect(instance.active?).to be false
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
context 'when the experiment only have a start_at time' do
|
96
|
+
context 'and the current time < start_at' do
|
97
|
+
it 'returns false' do
|
98
|
+
instance = experiment_with_time(start_at: (Time.zone.now+3600).iso8601)
|
99
|
+
expect(instance.active?).to be false
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context 'and the current time > start_at' do
|
104
|
+
it 'returns true' do
|
105
|
+
instance = experiment_with_time(start_at: (Time.zone.now-3600).iso8601)
|
106
|
+
expect(instance.active?).to be true
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context 'when the experiment both have a start_at and end_at time' do
|
112
|
+
context 'and current_time < start_at' do
|
113
|
+
it 'returns false' do
|
114
|
+
instance = experiment_with_time(start_at: (Time.zone.now+3600).iso8601,
|
115
|
+
end_at: (Time.zone.now+7200).iso8601)
|
116
|
+
expect(instance.active?).to be false
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context 'and current_time > start_at and current time < end_at' do
|
121
|
+
it 'returns true' do
|
122
|
+
instance = experiment_with_time(start_at: (Time.zone.now-3600).iso8601,
|
123
|
+
end_at: (Time.zone.now+7200).iso8601)
|
124
|
+
expect(instance.active?).to be true
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
context 'and current time > start_at and current time > end_at' do
|
129
|
+
it 'returns false' do
|
130
|
+
instance = experiment_with_time(start_at: (Time.zone.now-7200).iso8601,
|
131
|
+
end_at: (Time.zone.now-3600).iso8601)
|
132
|
+
expect(instance.active?).to be false
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
describe '.find_all' do
|
141
|
+
context 'when the EenyMeeny is configured with experiments', experiments: true do
|
142
|
+
it 'returns those experiments' do
|
143
|
+
instances = described_class.find_all
|
144
|
+
expect(instances).to be_a Array
|
145
|
+
expect(instances.size).to eq(2)
|
146
|
+
instances.each do |instance|
|
147
|
+
expect(instance).to be_a EenyMeeny::Experiment
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
context 'when EenyMeeny is not configured with experiments' do
|
153
|
+
it 'returns an empty array' do
|
154
|
+
expect(described_class.find_all).to eq([])
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
describe '.find_by_id' do
|
160
|
+
context 'when EenyMeeny is configured with experiments', experiments: true do
|
161
|
+
context 'and the given id exists' do
|
162
|
+
it 'returns the experiment' do
|
163
|
+
expect(described_class.find_by_id(:my_page)).to be_a EenyMeeny::Experiment
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
context 'and the given id does not exist' do
|
168
|
+
it 'returns nil' do
|
169
|
+
expect(described_class.find_by_id(:experiment_missing)).to be_nil
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
context 'when EenyMeeny is not configured with experiments' do
|
175
|
+
it 'returns nil' do
|
176
|
+
expect(described_class.find_by_id(:experiment_missing)).to be_nil
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|