vanity 1.8.4 → 1.9.0.beta
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +3 -2
- data/CHANGELOG +12 -0
- data/Gemfile +6 -3
- data/Gemfile.lock +12 -10
- data/README.rdoc +45 -16
- data/Rakefile +14 -9
- data/doc/_layouts/page.html +4 -6
- data/doc/ab_testing.textile +1 -1
- data/doc/configuring.textile +2 -4
- data/doc/email.textile +1 -3
- data/doc/index.textile +3 -63
- data/doc/rails.textile +34 -8
- data/gemfiles/rails3.gemfile +12 -3
- data/gemfiles/rails3.gemfile.lock +37 -11
- data/gemfiles/rails31.gemfile +12 -3
- data/gemfiles/rails31.gemfile.lock +37 -11
- data/gemfiles/rails32.gemfile +12 -3
- data/gemfiles/rails32.gemfile.lock +37 -11
- data/gemfiles/rails4.gemfile +12 -3
- data/gemfiles/rails4.gemfile.lock +37 -11
- data/lib/vanity/adapters/abstract_adapter.rb +4 -0
- data/lib/vanity/adapters/active_record_adapter.rb +18 -10
- data/lib/vanity/adapters/mock_adapter.rb +8 -4
- data/lib/vanity/adapters/mongodb_adapter.rb +11 -7
- data/lib/vanity/adapters/redis_adapter.rb +88 -37
- data/lib/vanity/commands/report.rb +9 -9
- data/lib/vanity/experiment/ab_test.rb +120 -101
- data/lib/vanity/experiment/alternative.rb +21 -21
- data/lib/vanity/experiment/base.rb +5 -5
- data/lib/vanity/experiment/bayesian_bandit_score.rb +51 -51
- data/lib/vanity/experiment/definition.rb +10 -10
- data/lib/vanity/frameworks/rails.rb +39 -36
- data/lib/vanity/helpers.rb +6 -4
- data/lib/vanity/metric/active_record.rb +1 -1
- data/lib/vanity/metric/base.rb +23 -24
- data/lib/vanity/metric/google_analytics.rb +5 -5
- data/lib/vanity/playground.rb +118 -24
- data/lib/vanity/templates/_report.erb +20 -6
- data/lib/vanity/templates/vanity.css +2 -0
- data/lib/vanity/version.rb +1 -1
- data/test/adapters/redis_adapter_test.rb +106 -1
- data/test/dummy/config/database.yml +21 -4
- data/test/dummy/config/routes.rb +1 -1
- data/test/experiment/ab_test.rb +93 -13
- data/test/metric/active_record_test.rb +9 -4
- data/test/passenger_test.rb +43 -42
- data/test/playground_test.rb +50 -1
- data/test/rails_dashboard_test.rb +38 -1
- data/test/rails_helper_test.rb +5 -0
- data/test/rails_test.rb +66 -15
- data/test/test_helper.rb +24 -2
- data/vanity.gemspec +0 -2
- metadata +45 -57
@@ -24,3 +24,5 @@
|
|
24
24
|
.vanity form#milestones label { margin-right: .5em }
|
25
25
|
.vanity form#milestones input { vertical-align: bottom }
|
26
26
|
.vanity .metric .marking.label { position: absolute; bottom: 2em; color: #c66; font-size: 80% }
|
27
|
+
|
28
|
+
.vanity .alert { padding: 8px 35px 8px 14px; margin-bottom: 20px; text-shadow: 0px 1px 0px rgba(255, 255, 255, 0.5); background-color: rgb(252, 248, 227); border: 1px solid rgb(251, 238, 213); border-radius: 4px 4px 4px 4px; }
|
data/lib/vanity/version.rb
CHANGED
@@ -1,6 +1,12 @@
|
|
1
1
|
require 'test/test_helper'
|
2
2
|
|
3
3
|
class RedisAdapterTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
require "vanity/adapters/redis_adapter"
|
6
|
+
require "redis"
|
7
|
+
require "redis/namespace"
|
8
|
+
end
|
9
|
+
|
4
10
|
def test_warn_on_disconnect_error
|
5
11
|
if defined?(Redis)
|
6
12
|
assert_nothing_raised do
|
@@ -8,10 +14,109 @@ class RedisAdapterTest < Test::Unit::TestCase
|
|
8
14
|
mocked_redis = stub("Redis")
|
9
15
|
mocked_redis.expects(:client).raises(RuntimeError)
|
10
16
|
redis_adapter = Vanity::Adapters::RedisAdapter.new({})
|
11
|
-
redis_adapter.expects(:warn).with("Error while disconnecting from redis: RuntimeError")
|
12
17
|
redis_adapter.stubs(:redis).returns(mocked_redis)
|
18
|
+
redis_adapter.expects(:warn).with("Error while disconnecting from redis: RuntimeError")
|
13
19
|
redis_adapter.disconnect!
|
14
20
|
end
|
15
21
|
end
|
16
22
|
end
|
23
|
+
|
24
|
+
def stub_redis
|
25
|
+
Vanity.playground.failover_on_datastore_error!
|
26
|
+
mocked_redis = stub("Redis")
|
27
|
+
redis_adapter = Vanity::Adapters::RedisAdapter.new(:redis => mocked_redis)
|
28
|
+
|
29
|
+
[redis_adapter, mocked_redis]
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_graceful_failure_metric_track
|
33
|
+
redis_adapter, mocked_redis = stub_redis
|
34
|
+
mocked_redis.stubs(:incrby).raises(RuntimeError)
|
35
|
+
|
36
|
+
assert_nothing_raised do
|
37
|
+
redis_adapter.metric_track("metric", Time.now.to_s, "3ff62e2fb51f0b22646a342a2d357aec", [1])
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_graceful_failure_set_experiment_created_at
|
42
|
+
redis_adapter, mocked_redis = stub_redis
|
43
|
+
mocked_redis.stubs(:setnx).raises(RuntimeError)
|
44
|
+
|
45
|
+
assert_nothing_raised do
|
46
|
+
redis_adapter.set_experiment_created_at("price_options", Time.now)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_graceful_failure_is_experiment_completed?
|
51
|
+
redis_adapter, mocked_redis = stub_redis
|
52
|
+
mocked_redis.stubs(:exists).raises(RuntimeError)
|
53
|
+
|
54
|
+
assert_nothing_raised do
|
55
|
+
redis_adapter.is_experiment_completed?("price_options")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_graceful_failure_ab_show
|
60
|
+
redis_adapter, mocked_redis = stub_redis
|
61
|
+
mocked_redis.stubs(:[]=).raises(RuntimeError)
|
62
|
+
|
63
|
+
assert_nothing_raised do
|
64
|
+
redis_adapter.ab_show("price_options", "3ff62e2fb51f0b22646a342a2d357aec", 0)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_graceful_failure_ab_showing
|
69
|
+
redis_adapter, mocked_redis = stub_redis
|
70
|
+
mocked_redis.stubs(:[]).raises(RuntimeError)
|
71
|
+
|
72
|
+
assert_nothing_raised do
|
73
|
+
redis_adapter.ab_showing("price_options", "3ff62e2fb51f0b22646a342a2d357aec")
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_graceful_failure_ab_not_showing
|
78
|
+
redis_adapter, mocked_redis = stub_redis
|
79
|
+
mocked_redis.stubs(:del).raises(RuntimeError)
|
80
|
+
|
81
|
+
assert_nothing_raised do
|
82
|
+
redis_adapter.ab_not_showing("price_options", "3ff62e2fb51f0b22646a342a2d357aec")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_graceful_failure_ab_add_participant
|
87
|
+
redis_adapter, mocked_redis = stub_redis
|
88
|
+
mocked_redis.stubs(:sadd).raises(RuntimeError)
|
89
|
+
|
90
|
+
assert_nothing_raised do
|
91
|
+
redis_adapter.ab_add_participant("price_options", "3ff62e2fb51f0b22646a342a2d357aec", 0)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_graceful_failure_ab_seen
|
96
|
+
redis_adapter, mocked_redis = stub_redis
|
97
|
+
mocked_redis.stubs(:sismember).raises(RuntimeError)
|
98
|
+
|
99
|
+
assert_nothing_raised do
|
100
|
+
redis_adapter.ab_seen("price_options", "3ff62e2fb51f0b22646a342a2d357aec", 0)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_graceful_failure_ab_assigned
|
105
|
+
redis_adapter, mocked_redis = stub_redis
|
106
|
+
mocked_redis.stubs(:sismember).raises(RuntimeError)
|
107
|
+
|
108
|
+
assert_nothing_raised do
|
109
|
+
redis_adapter.ab_assigned("price_options", "3ff62e2fb51f0b22646a342a2d357aec")
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_graceful_failure_ab_add_conversion
|
114
|
+
redis_adapter, mocked_redis = stub_redis
|
115
|
+
mocked_redis.stubs(:sismember).raises(RuntimeError)
|
116
|
+
|
117
|
+
assert_nothing_raised do
|
118
|
+
redis_adapter.ab_add_conversion("price_options", 0, "3ff62e2fb51f0b22646a342a2d357aec")
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
17
122
|
end
|
@@ -1,5 +1,22 @@
|
|
1
|
+
# Based on https://github.com/plataformatec/devise/blob/master/test/rails_app/config/database.yml
|
2
|
+
|
3
|
+
# SQLite version 3.x
|
4
|
+
# gem install sqlite3
|
5
|
+
#
|
6
|
+
# Ensure the SQLite 3 gem is defined in your Gemfile
|
7
|
+
# gem 'sqlite3'
|
8
|
+
|
9
|
+
# Set databases in all environments since we may test loading Rails in non-
|
10
|
+
# test environments
|
11
|
+
|
12
|
+
development:
|
13
|
+
adapter: sqlite3
|
14
|
+
database: ":memory:"
|
15
|
+
|
1
16
|
test:
|
2
|
-
adapter:
|
3
|
-
database:
|
4
|
-
|
5
|
-
|
17
|
+
adapter: sqlite3
|
18
|
+
database: ":memory:"
|
19
|
+
|
20
|
+
production:
|
21
|
+
adapter: sqlite3
|
22
|
+
database: ":memory:"
|
data/test/dummy/config/routes.rb
CHANGED
@@ -54,5 +54,5 @@ Dummy::Application.routes.draw do
|
|
54
54
|
|
55
55
|
# This is a legacy wild controller route that's not recommended for RESTful applications.
|
56
56
|
# Note: This route will make all actions in every controller accessible via GET requests.
|
57
|
-
match ':controller(/:action(/:id(.:format)))', :via => [:get]
|
57
|
+
match ':controller(/:action(/:id(.:format)))', :via => [:get, :post]
|
58
58
|
end
|
data/test/experiment/ab_test.rb
CHANGED
@@ -133,7 +133,7 @@ class AbTestTest < ActionController::TestCase
|
|
133
133
|
|
134
134
|
# -- use_js! --
|
135
135
|
|
136
|
-
def
|
136
|
+
def test_choose_does_not_record_participant_when_using_js
|
137
137
|
Vanity.playground.use_js!
|
138
138
|
ids = (0...10).to_a
|
139
139
|
new_ab_test :foobar do
|
@@ -160,6 +160,44 @@ class AbTestTest < ActionController::TestCase
|
|
160
160
|
assert_equal 1, on_assignment_called_times
|
161
161
|
end
|
162
162
|
|
163
|
+
def test_calls_on_assignment_when_given_valid_request
|
164
|
+
on_assignment_called_times = 0
|
165
|
+
new_ab_test :foobar do
|
166
|
+
alternatives "foo", "bar"
|
167
|
+
identify { "6e98ec" }
|
168
|
+
metrics :coolness
|
169
|
+
on_assignment { on_assignment_called_times = on_assignment_called_times+1 }
|
170
|
+
end
|
171
|
+
experiment(:foobar).choose(dummy_request)
|
172
|
+
assert_equal 1, on_assignment_called_times
|
173
|
+
end
|
174
|
+
|
175
|
+
def test_does_not_call_on_assignment_when_given_invalid_request
|
176
|
+
on_assignment_called_times = 0
|
177
|
+
new_ab_test :foobar do
|
178
|
+
alternatives "foo", "bar"
|
179
|
+
identify { "6e98ec" }
|
180
|
+
metrics :coolness
|
181
|
+
on_assignment { on_assignment_called_times = on_assignment_called_times+1 }
|
182
|
+
end
|
183
|
+
request = dummy_request
|
184
|
+
request.user_agent = "Googlebot/2.1 ( http://www.google.com/bot.html)"
|
185
|
+
experiment(:foobar).choose(request)
|
186
|
+
assert_equal 0, on_assignment_called_times
|
187
|
+
end
|
188
|
+
|
189
|
+
def test_calls_on_assignment_on_new_assignment_via_chooses
|
190
|
+
on_assignment_called_times = 0
|
191
|
+
new_ab_test :foobar do
|
192
|
+
alternatives "foo", "bar"
|
193
|
+
identify { "6e98ec" }
|
194
|
+
metrics :coolness
|
195
|
+
on_assignment { on_assignment_called_times = on_assignment_called_times+1 }
|
196
|
+
end
|
197
|
+
2.times { experiment(:foobar).chooses("foo") }
|
198
|
+
assert_equal 1, on_assignment_called_times
|
199
|
+
end
|
200
|
+
|
163
201
|
def test_returns_the_same_alternative_consistently_when_on_assignment_is_set
|
164
202
|
new_ab_test :foobar do
|
165
203
|
alternatives "foo", "bar"
|
@@ -344,6 +382,27 @@ class AbTestTest < ActionController::TestCase
|
|
344
382
|
assert_equal 100, alts.map(&:converted).sum
|
345
383
|
end
|
346
384
|
|
385
|
+
def test_choose_records_participants_given_a_valid_request
|
386
|
+
new_ab_test :foobar do
|
387
|
+
alternatives "foo", "bar"
|
388
|
+
identify { "me" }
|
389
|
+
metrics :coolness
|
390
|
+
end
|
391
|
+
experiment(:foobar).choose(dummy_request)
|
392
|
+
assert_equal 1, experiment(:foobar).alternatives.map(&:participants).sum
|
393
|
+
end
|
394
|
+
|
395
|
+
def test_choose_ignores_participants_given_an_invalid_request
|
396
|
+
new_ab_test :foobar do
|
397
|
+
alternatives "foo", "bar"
|
398
|
+
identify { "me" }
|
399
|
+
metrics :coolness
|
400
|
+
end
|
401
|
+
request = dummy_request
|
402
|
+
request.user_agent = "Googlebot/2.1 ( http://www.google.com/bot.html)"
|
403
|
+
experiment(:foobar).choose(request)
|
404
|
+
assert_equal 0, experiment(:foobar).alternatives.map(&:participants).sum
|
405
|
+
end
|
347
406
|
|
348
407
|
def test_destroy_experiment
|
349
408
|
new_ab_test :simple do
|
@@ -465,6 +524,7 @@ class AbTestTest < ActionController::TestCase
|
|
465
524
|
|
466
525
|
|
467
526
|
# -- Scoring --
|
527
|
+
|
468
528
|
def test_calculate_score
|
469
529
|
new_ab_test :abcd do
|
470
530
|
alternatives :a, :b, :c, :d
|
@@ -488,10 +548,10 @@ class AbTestTest < ActionController::TestCase
|
|
488
548
|
metrics :coolness
|
489
549
|
end
|
490
550
|
# participating, conversions, rate, z-score
|
491
|
-
# Control: 182
|
492
|
-
# Treatment A: 180
|
493
|
-
# treatment B: 189
|
494
|
-
# treatment C: 188
|
551
|
+
# Control: 182 35 19.23% N/A
|
552
|
+
# Treatment A: 180 45 25.00% 1.33
|
553
|
+
# treatment B: 189 28 14.81% -1.13
|
554
|
+
# treatment C: 188 61 32.45% 2.94
|
495
555
|
fake :abcd, :a=>[182, 35], :b=>[180, 45], :c=>[189,28], :d=>[188, 61]
|
496
556
|
|
497
557
|
z_scores = experiment(:abcd).score.alts.map { |alt| "%.2f" % alt.z_score }
|
@@ -514,10 +574,10 @@ class AbTestTest < ActionController::TestCase
|
|
514
574
|
metrics :coolness
|
515
575
|
end
|
516
576
|
# participating, conversions, rate, z-score
|
517
|
-
# Control: 182
|
518
|
-
# Treatment A: 180
|
519
|
-
# treatment B: 189
|
520
|
-
# treatment C: 188
|
577
|
+
# Control: 182 35 19.23% N/A
|
578
|
+
# Treatment A: 180 45 25.00% 1.33
|
579
|
+
# treatment B: 189 28 14.81% -1.13
|
580
|
+
# treatment C: 188 61 32.45% 2.94
|
521
581
|
fake :abcd, :a=>[182, 35], :b=>[180, 45], :c=>[189,28], :d=>[188, 61]
|
522
582
|
|
523
583
|
score_result = experiment(:abcd).bayes_bandit_score
|
@@ -593,10 +653,10 @@ class AbTestTest < ActionController::TestCase
|
|
593
653
|
metrics :coolness
|
594
654
|
end
|
595
655
|
# participating, conversions, rate, z-score
|
596
|
-
# Control: 182
|
597
|
-
# Treatment A: 180
|
598
|
-
# treatment B: 189
|
599
|
-
# treatment C: 188
|
656
|
+
# Control: 182 35 19.23% N/A
|
657
|
+
# Treatment A: 180 45 25.00% 1.33
|
658
|
+
# treatment B: 189 28 14.81% -1.13
|
659
|
+
# treatment C: 188 61 32.45% 2.94
|
600
660
|
fake :abcd, :a=>[182, 35], :b=>[180, 45], :c=>[189,28], :d=>[188, 61]
|
601
661
|
|
602
662
|
assert_equal <<-TEXT, experiment(:abcd).conclusion.join("\n") << "\n"
|
@@ -919,6 +979,26 @@ This experiment did not run long enough to find a clear winner.
|
|
919
979
|
assert_equal experiment(:simple).alternatives[2].participants, 1
|
920
980
|
end
|
921
981
|
|
982
|
+
def test_chooses_records_participants_given_a_valid_request
|
983
|
+
new_ab_test :simple do
|
984
|
+
alternatives :a, :b, :c
|
985
|
+
metrics :coolness
|
986
|
+
end
|
987
|
+
experiment(:simple).chooses(:a, dummy_request)
|
988
|
+
assert_equal 1, experiment(:simple).alternatives[0].participants
|
989
|
+
end
|
990
|
+
|
991
|
+
def test_chooses_ignores_participants_given_an_invalid_request
|
992
|
+
new_ab_test :simple do
|
993
|
+
alternatives :a, :b, :c
|
994
|
+
metrics :coolness
|
995
|
+
end
|
996
|
+
request = dummy_request
|
997
|
+
request.user_agent = "Googlebot/2.1 ( http://www.google.com/bot.html)"
|
998
|
+
experiment(:simple).chooses(:a, request)
|
999
|
+
assert_equal 0, experiment(:simple).alternatives[0].participants
|
1000
|
+
end
|
1001
|
+
|
922
1002
|
def test_no_collection_and_chooses
|
923
1003
|
not_collecting!
|
924
1004
|
new_ab_test :simple do
|
@@ -1,10 +1,12 @@
|
|
1
1
|
require "test/test_helper"
|
2
2
|
|
3
3
|
class Sky < ActiveRecord::Base
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
if connected?
|
5
|
+
connection.drop_table :skies if table_exists?
|
6
|
+
connection.create_table :skies do |t|
|
7
|
+
t.integer :height
|
8
|
+
t.timestamps
|
9
|
+
end
|
8
10
|
end
|
9
11
|
|
10
12
|
if defined?(Rails::Railtie)
|
@@ -14,6 +16,7 @@ class Sky < ActiveRecord::Base
|
|
14
16
|
end
|
15
17
|
end
|
16
18
|
|
19
|
+
if ActiveRecord::Base.connected?
|
17
20
|
|
18
21
|
context "ActiveRecord Metric" do
|
19
22
|
|
@@ -305,3 +308,5 @@ context "ActiveRecord Metric" do
|
|
305
308
|
end
|
306
309
|
end
|
307
310
|
end
|
311
|
+
|
312
|
+
end
|
data/test/passenger_test.rb
CHANGED
@@ -1,51 +1,52 @@
|
|
1
1
|
require "test/test_helper"
|
2
2
|
|
3
|
-
#
|
4
|
-
|
5
|
-
require "phusion_passenger/spawn_manager"
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
3
|
+
# Not supported for rails3
|
4
|
+
if !defined?(Rails::Railtie) && ActiveRecord::Base.connected?
|
5
|
+
require "phusion_passenger/spawn_manager"
|
6
|
+
|
7
|
+
class PassengerTest < Test::Unit::TestCase
|
8
|
+
def setup
|
9
|
+
super
|
10
|
+
ActiveRecord::Base.connection.disconnect! # Otherwise AR metric tests fail
|
11
|
+
@original = Vanity.playground.connection
|
12
|
+
File.unlink "test/myapp/config/vanity.yml" rescue nil
|
13
|
+
File.open("test/myapp/config/vanity.yml", "w") do |io|
|
14
|
+
io.write YAML.dump({ "production"=>DATABASE })
|
15
|
+
end
|
16
|
+
@server = PhusionPassenger::SpawnManager.new
|
17
|
+
@server.start
|
18
|
+
Thread.pass until @server.started?
|
19
|
+
app_root = File.expand_path("myapp", File.dirname(__FILE__))
|
20
|
+
@app = @server.spawn_application "app_root"=>app_root, "spawn_method"=>"smart"
|
14
21
|
end
|
15
|
-
@server = PhusionPassenger::SpawnManager.new
|
16
|
-
@server.start
|
17
|
-
Thread.pass until @server.started?
|
18
|
-
app_root = File.expand_path("myapp", File.dirname(__FILE__))
|
19
|
-
@app = @server.spawn_application "app_root"=>app_root, "spawn_method"=>"smart"
|
20
|
-
end
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
def test_reconnect
|
24
|
+
# When using AR adapter, we're not responsible to reconnect, and we're going
|
25
|
+
# to get the same "connect" (AR connection handler) either way.
|
26
|
+
# return if defined?(Vanity::Adapters::ActiveRecordAdapter) && Vanity::Adapters::ActiveRecordAdapter === Vanity.playground.connection
|
26
27
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
28
|
+
sleep 0.1
|
29
|
+
case @app.listen_socket_type
|
30
|
+
when "tcp" ; socket = TCPSocket.new(*@app.listen_socket_name.split(":"))
|
31
|
+
when "unix"; socket = UNIXSocket.new(@app.listen_socket_name)
|
32
|
+
else fail
|
33
|
+
end
|
34
|
+
channel = PhusionPassenger::MessageChannel.new(socket)
|
35
|
+
request = {"REQUEST_PATH"=>"/", "REQUEST_METHOD"=>"GET", "QUERY_STRING"=>" "}
|
36
|
+
channel.write_scalar request.to_a.join("\0")
|
37
|
+
response = socket.read.split("\r\n\r\n").last
|
38
|
+
socket.close
|
39
|
+
conn, obj_id = response.split("\n")
|
40
|
+
assert_equal @original.to_s, conn
|
41
|
+
assert_not_equal @original.object_id.to_s, obj_id
|
32
42
|
end
|
33
|
-
channel = PhusionPassenger::MessageChannel.new(socket)
|
34
|
-
request = {"REQUEST_PATH"=>"/", "REQUEST_METHOD"=>"GET", "QUERY_STRING"=>" "}
|
35
|
-
channel.write_scalar request.to_a.join("\0")
|
36
|
-
response = socket.read.split("\r\n\r\n").last
|
37
|
-
socket.close
|
38
|
-
conn, obj_id = response.split("\n")
|
39
|
-
assert_equal @original.to_s, conn
|
40
|
-
assert_not_equal @original.object_id.to_s, obj_id
|
41
|
-
end
|
42
43
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
44
|
+
def teardown
|
45
|
+
super
|
46
|
+
@server.cleanup
|
47
|
+
@server.stop
|
48
|
+
Process.kill('SIGKILL', @app.pid.to_i) # Just in case...KIDS, GET OUT OF THE POOL!
|
49
|
+
File.unlink "test/myapp/config/vanity.yml"
|
50
|
+
end
|
49
51
|
end
|
50
52
|
end
|
51
|
-
end
|