eeny-meeny 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
1
  require 'spec_helper'
2
- require 'eeny-meeny/variation'
2
+ require 'eeny-meeny/models/variation'
3
3
 
4
4
  describe EenyMeeny::Variation do
5
5
  describe 'when initialized' do
@@ -0,0 +1,39 @@
1
+ require 'eeny-meeny/routing/experiment_constraint'
2
+ require 'eeny-meeny/middleware'
3
+ require 'rack/test'
4
+
5
+ describe EenyMeeny::ExperimentConstraint, experiments: true do
6
+
7
+ let(:request) do
8
+ session = Rack::MockSession.new(EenyMeeny::Middleware.new(MockRackApp.new))
9
+ session.set_cookie('eeny_meeny_my_page_v1=IlI%2FGW9IZvayAGQbBOroxIrfr6Z116OJqdjFdrw6FOZXOrinmxQmsKw2a%2Fb8kJFP0Up%2BLr4FACovT9%2Bo0hRdcY0AJtcYqMXC96GDMSwa2HauZbjHw16Q3%2BboSnWjfaEOHmqlyxtPxQwxlr3rsT%2FYblPjqqQ%2FiPbaJUqou3LiMtpVg4V%2FJxJdhn0XJUgFMDaFWXVFYYA6VmJSFUGglhRlbg%3D%3D; path=/; expires=Tue, 11 Oct 2016 13:07:53 -0000; HttpOnly')
10
+ session
11
+ end
12
+
13
+ describe 'when initialized' do
14
+
15
+ context 'for an inactive experiment' do
16
+ subject do
17
+ described_class.new(:expired)
18
+ end
19
+
20
+ describe '#matches?' do
21
+ it 'returns false' do
22
+ expect(subject.matches?(request)).to be false
23
+ end
24
+ end
25
+ end
26
+
27
+ context 'for an active experiment' do
28
+ subject do
29
+ described_class.new(:my_page)
30
+ end
31
+
32
+ describe '#matches?' do
33
+ it 'returns true' do
34
+ expect(subject.matches?(request)).to be true
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,35 @@
1
+ require 'eeny-meeny/routing/smoke_test_constraint'
2
+ require 'eeny-meeny/middleware'
3
+ require 'rack/test'
4
+
5
+ describe EenyMeeny::SmokeTestConstraint do
6
+
7
+ let(:request) do
8
+ Rack::MockSession.new(EenyMeeny::Middleware.new(MockRackApp.new))
9
+ end
10
+
11
+ let(:request_with_cookie) do
12
+ request.set_cookie('smoke_test_shadow_v1=kqe%2Bt%2F72JZ9s7fOv0nQ8GszTEmmXj3tUsjqmqy31i4yZLku5okuya%2F3PYb8Oi%2BSi53hDP8egfeiCcbrlBN4s5ifQwToaZHNAs43V1EKb8ca%2BTRK0lpCWfR58%2BQjpWwZL; expires=Tue, 11 Oct 2016 13:30:31 -0000; HttpOnly')
13
+ request
14
+ end
15
+
16
+ describe 'when initialized' do
17
+
18
+ subject do
19
+ described_class.new(:shadow)
20
+ end
21
+
22
+ describe '#matches?' do
23
+ context 'for a request with a valid smoke test cookie' do
24
+ it 'returns true' do
25
+ expect(subject.matches?(request_with_cookie)).to be true
26
+ end
27
+ end
28
+ context 'for a request without a smoke test cookie' do
29
+ it 'returns false' do
30
+ expect(subject.matches?(request)).to be false
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -8,3 +8,15 @@
8
8
  :new:
9
9
  :name: New My Page
10
10
  :weight: 0.98
11
+ :expired:
12
+ :name: Expired
13
+ :version: 1
14
+ :start_at: '2016-08-11T11:55:40Z'
15
+ :end_at: '2016-08-11T11:55:40Z'
16
+ :variations:
17
+ :old:
18
+ :name: Old Expired Page
19
+ :weight: 0.02
20
+ :new:
21
+ :name: New Expired Page
22
+ :weight: 0.98
data/spec/spec_helper.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'simplecov'
2
2
  require 'simplecov-rcov'
3
3
  require 'codeclimate-test-reporter'
4
+ require 'active_support/time'
4
5
 
5
6
  SimpleCov.start do
6
7
  formatter SimpleCov::Formatter::MultiFormatter[
@@ -9,16 +10,32 @@ SimpleCov.start do
9
10
  CodeClimate::TestReporter::Formatter
10
11
  ]
11
12
  add_group('EenyMeeny', 'lib/eeny-meeny')
13
+ add_group('Rake Tasks', 'lib/tasks')
12
14
  add_group('Specs', 'spec')
13
15
  end
14
16
 
15
17
  require 'rspec'
16
18
  require 'yaml'
17
- require 'eeny-meeny'
18
19
  require 'mock_rack_app'
19
20
 
21
+ require 'eeny-meeny'
22
+
20
23
  RSpec.configure do |config|
21
24
  config.run_all_when_everything_filtered = true
22
25
  config.filter_run :focus
23
26
  config.order = "random"
27
+
28
+ config.before(:suite) do
29
+ Time.zone = 'UTC'
30
+ end
31
+
32
+ config.before(:each) do
33
+ EenyMeeny.reset! # reset configuration before every test.
34
+ end
35
+ config.before(:each, experiments: true) do
36
+ EenyMeeny.configure do |config|
37
+ config.experiments = YAML.load_file(File.join('spec','fixtures','experiments.yml'))
38
+ end
39
+ end
40
+
24
41
  end
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+ require 'rake'
3
+
4
+ describe 'cookie.rake', experiments: true do
5
+ before do
6
+ Rake.application.rake_require "tasks/cookie"
7
+ Rake::Task.define_task(:environment)
8
+ end
9
+
10
+ describe 'eeny_meeny:cookie:experiment' do
11
+ context 'executed with an experiment id' do
12
+ it 'generates a cookie' do
13
+ expect {
14
+ Rake::Task['eeny_meeny:cookie:experiment'].execute(Rake::TaskArguments.new([:experiment_id],['my_page']))
15
+ }.to_not raise_error
16
+ end
17
+ end
18
+
19
+ context 'executed without arguments' do
20
+ it 'results in an error' do
21
+ expect {
22
+ Rake::Task['eeny_meeny:cookie:experiment'].execute
23
+ }.to raise_error(RuntimeError, "Missing 'experiment_id' parameter")
24
+ end
25
+ end
26
+ end
27
+
28
+ describe 'eeny_meeny:cookie:experiment_variation' do
29
+ context 'executed with an experiment id' do
30
+ it 'results in an error' do
31
+ expect {
32
+ Rake::Task['eeny_meeny:cookie:experiment_variation'].execute(Rake::TaskArguments.new([:experiment_id],['my_page']))
33
+ }.to raise_error(RuntimeError, "Missing 'variation_id' parameter")
34
+ end
35
+
36
+ context 'and a variation_id' do
37
+ it 'generates a cookie' do
38
+ expect {
39
+ Rake::Task['eeny_meeny:cookie:experiment_variation'].execute(Rake::TaskArguments.new([:experiment_id, :variation_id],['my_page', 'new']))
40
+ }.to_not raise_error
41
+ end
42
+ end
43
+ end
44
+
45
+ context 'executed without arguments' do
46
+ it 'results in an error' do
47
+ expect {
48
+ Rake::Task['eeny_meeny:cookie:experiment_variation'].execute
49
+ }.to raise_error(RuntimeError, "Missing 'experiment_id' parameter")
50
+ end
51
+ end
52
+ end
53
+
54
+ describe 'eeny_meeny:cookie:smoke_test' do
55
+ context 'executed with an smoke test id' do
56
+ it 'generates a cookie' do
57
+ expect {
58
+ Rake::Task['eeny_meeny:cookie:smoke_test'].execute(Rake::TaskArguments.new([:smoke_test_id],['shadow']))
59
+ }.to_not raise_error
60
+ end
61
+ end
62
+
63
+ context 'executed without arguments' do
64
+ it 'results in an error' do
65
+ expect {
66
+ Rake::Task['eeny_meeny:cookie:smoke_test'].execute
67
+ }.to raise_error(RuntimeError, "Missing 'smoke_test_id' parameter")
68
+ end
69
+ end
70
+ end
71
+
72
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eeny-meeny
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Christian Orthmann
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-07-03 00:00:00.000000000 Z
11
+ date: 2016-09-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rack-test
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.6.3
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.6.3
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: rack
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -133,22 +147,27 @@ files:
133
147
  - Rakefile
134
148
  - eeny-meeny.gemspec
135
149
  - lib/eeny-meeny.rb
136
- - lib/eeny-meeny/encryptor.rb
137
- - lib/eeny-meeny/experiment.rb
138
150
  - lib/eeny-meeny/experiment_helper.rb
139
151
  - lib/eeny-meeny/middleware.rb
140
- - lib/eeny-meeny/middleware_helper.rb
152
+ - lib/eeny-meeny/models/cookie.rb
153
+ - lib/eeny-meeny/models/encryptor.rb
154
+ - lib/eeny-meeny/models/experiment.rb
155
+ - lib/eeny-meeny/models/variation.rb
141
156
  - lib/eeny-meeny/railtie.rb
142
- - lib/eeny-meeny/route_constraint.rb
143
- - lib/eeny-meeny/shared_methods.rb
144
- - lib/eeny-meeny/variation.rb
157
+ - lib/eeny-meeny/routing/experiment_constraint.rb
158
+ - lib/eeny-meeny/routing/smoke_test_constraint.rb
145
159
  - lib/eeny-meeny/version.rb
146
- - spec/eeny-meeny/experiment_spec.rb
160
+ - lib/tasks/cookie.rake
147
161
  - spec/eeny-meeny/middleware_spec.rb
148
- - spec/eeny-meeny/variation_spec.rb
162
+ - spec/eeny-meeny/models/cookie_spec.rb
163
+ - spec/eeny-meeny/models/experiment_spec.rb
164
+ - spec/eeny-meeny/models/variation_spec.rb
165
+ - spec/eeny-meeny/routing/experiment_constraint_spec.rb
166
+ - spec/eeny-meeny/routing/smoke_test_constraint_spec.rb
149
167
  - spec/fixtures/experiments.yml
150
168
  - spec/mock_rack_app.rb
151
169
  - spec/spec_helper.rb
170
+ - spec/tasks/cookie_task_spec.rb
152
171
  homepage: http://rubygems.org/gems/eeny-meeny
153
172
  licenses:
154
173
  - MIT
@@ -169,9 +188,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
169
188
  version: '0'
170
189
  requirements: []
171
190
  rubyforge_project:
172
- rubygems_version: 2.4.8
191
+ rubygems_version: 2.5.1
173
192
  signing_key:
174
193
  specification_version: 4
175
- summary: A simple split testing tool for Rails
194
+ summary: A simple split and smoke testing tool for Rails
176
195
  test_files: []
177
- has_rdoc:
@@ -1,25 +0,0 @@
1
- module EenyMeeny::MiddlewareHelper
2
- def has_experiment_cookie?(cookies, experiment)
3
- cookies.has_key?(experiment_cookie_name(experiment))
4
- end
5
-
6
- def generate_cookie_value(experiment, cookie_config)
7
- variation = experiment.pick_variation
8
- cookie = {
9
- expires: (experiment.end_at || 1.year.from_now),
10
- httponly: true,
11
- value: Marshal.dump({
12
- name: experiment.name,
13
- variation: variation,
14
- })
15
- }
16
- cookie[:same_site] = cookie_config[:same_site] unless cookie_config[:same_site].nil?
17
- cookie[:path] = cookie_config[:path] unless cookie_config[:path].nil?
18
- cookie
19
- end
20
-
21
- private
22
- def experiment_cookie_name(experiment)
23
- EenyMeeny::EENY_MEENY_COOKIE_PREFIX+experiment.id.to_s+'_v'+experiment.version.to_s
24
- end
25
- end
@@ -1,33 +0,0 @@
1
- require 'eeny-meeny/shared_methods'
2
-
3
- module EenyMeeny
4
- class RouteConstraint
5
- @@eeny_meeny_encryptor = nil
6
-
7
- def initialize(experiment_id, variation_id: nil)
8
- @experiment_id = experiment_id
9
- @variation_id = variation_id
10
- @version = experiment_version(experiment_id)
11
- end
12
-
13
- def matches?(request)
14
- !participates_in?(request).nil?
15
- end
16
-
17
- private
18
-
19
- def participates_in?(request)
20
- cookie = eeny_meeny_cookie(request)
21
- cookie[:variation] unless cookie.nil? || (!cookie.nil? && @variation_id.present? && @variation_id != cookie[:variation].id)
22
- end
23
-
24
- def eeny_meeny_cookie(request)
25
- cookie = request.cookie_jar[EenyMeeny::EENY_MEENY_COOKIE_PREFIX+@experiment_id.to_s+'_v'+@version.to_s]
26
- if cookie
27
- Marshal.load(decrypt(cookie)) rescue nil
28
- end
29
- end
30
-
31
- include EenyMeeny::SharedMethods
32
- end
33
- end
@@ -1,23 +0,0 @@
1
- module EenyMeeny::SharedMethods
2
-
3
- private
4
-
5
- def experiment_version(experiment_id)
6
- (Rails.application.config.eeny_meeny.experiments.
7
- try(:[], experiment_id.to_sym).try(:[], :version) || 1) rescue 1
8
- end
9
-
10
- def decrypt(cookie)
11
- begin
12
- if Rails.application.config.eeny_meeny.secure
13
- # Memoize encryptor to avoid creating new instances on every request.
14
- @@eeny_meeny_encryptor ||= EenyMeeny::Encryptor.new(Rails.application.config.eeny_meeny.secret)
15
- @@eeny_meeny_encryptor.decrypt(cookie)
16
- else
17
- cookie
18
- end
19
- rescue
20
- nil
21
- end
22
- end
23
- end
@@ -1,62 +0,0 @@
1
- require 'spec_helper'
2
- require 'eeny-meeny/experiment'
3
- require 'eeny-meeny/variation'
4
-
5
- describe EenyMeeny::Experiment do
6
- describe 'when initialized' do
7
-
8
- context 'with weighted variations' do
9
- subject do
10
- described_class.new(:experiment_1,
11
- name: 'Test 1',
12
- variations: {
13
- a: { name: 'A', weight: 0.5 },
14
- b: { name: 'B', weight: 0.3 }})
15
- end
16
-
17
- it 'sets the instance variables' do
18
- expect(subject.id).to eq(:experiment_1)
19
- expect(subject.name).to eq('Test 1')
20
- expect(subject.variations).to be_a Array
21
- expect(subject.variations.size).to eq(2)
22
- end
23
-
24
- it "has a 'total_weight' equal to the sum of the variation weights" do
25
- expect(subject.total_weight).to eq(0.8)
26
- end
27
-
28
- describe '#pick_variation' do
29
- it 'picks a variation' do
30
- expect(subject.pick_variation).to be_a EenyMeeny::Variation
31
- end
32
- end
33
- end
34
-
35
- context 'with non-weighted variations' do
36
- subject do
37
- described_class.new(:experiment_1,
38
- name: 'Test 1',
39
- variations: {
40
- a: { name: 'A' },
41
- b: { name: 'B' }})
42
- end
43
-
44
- it 'sets the instance variables' do
45
- expect(subject.id).to eq(:experiment_1)
46
- expect(subject.name).to eq('Test 1')
47
- expect(subject.variations).to be_a Array
48
- expect(subject.variations.size).to eq(2)
49
- end
50
-
51
- it "has a 'total_weight' equal to the number of the variation weights" do
52
- expect(subject.total_weight).to eq(2)
53
- end
54
-
55
- describe '#pick_variation' do
56
- it 'picks a variation' do
57
- expect(subject.pick_variation).to be_a EenyMeeny::Variation
58
- end
59
- end
60
- end
61
- end
62
- end