corntrace-rack-contrib 1.0.2
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.
- data/AUTHORS +26 -0
- data/COPYING +18 -0
- data/README.rdoc +87 -0
- data/Rakefile +89 -0
- data/lib/rack/contrib/accept_format.rb +46 -0
- data/lib/rack/contrib/access.rb +85 -0
- data/lib/rack/contrib/backstage.rb +20 -0
- data/lib/rack/contrib/bounce_favicon.rb +16 -0
- data/lib/rack/contrib/callbacks.rb +37 -0
- data/lib/rack/contrib/config.rb +16 -0
- data/lib/rack/contrib/cookies.rb +50 -0
- data/lib/rack/contrib/csshttprequest.rb +39 -0
- data/lib/rack/contrib/deflect.rb +137 -0
- data/lib/rack/contrib/evil.rb +12 -0
- data/lib/rack/contrib/expectation_cascade.rb +32 -0
- data/lib/rack/contrib/garbagecollector.rb +14 -0
- data/lib/rack/contrib/host_meta.rb +47 -0
- data/lib/rack/contrib/jsonp.rb +78 -0
- data/lib/rack/contrib/lighttpd_script_name_fix.rb +16 -0
- data/lib/rack/contrib/locale.rb +31 -0
- data/lib/rack/contrib/mailexceptions.rb +120 -0
- data/lib/rack/contrib/nested_params.rb +143 -0
- data/lib/rack/contrib/not_found.rb +18 -0
- data/lib/rack/contrib/post_body_content_type_parser.rb +40 -0
- data/lib/rack/contrib/proctitle.rb +30 -0
- data/lib/rack/contrib/profiler.rb +108 -0
- data/lib/rack/contrib/relative_redirect.rb +44 -0
- data/lib/rack/contrib/response_cache.rb +59 -0
- data/lib/rack/contrib/response_headers.rb +24 -0
- data/lib/rack/contrib/route_exceptions.rb +49 -0
- data/lib/rack/contrib/runtime.rb +31 -0
- data/lib/rack/contrib/sendfile.rb +142 -0
- data/lib/rack/contrib/signals.rb +63 -0
- data/lib/rack/contrib/simple_endpoint.rb +81 -0
- data/lib/rack/contrib/static_cache.rb +93 -0
- data/lib/rack/contrib/time_zone.rb +25 -0
- data/lib/rack/contrib.rb +39 -0
- data/rack-contrib.gemspec +104 -0
- data/test/404.html +1 -0
- data/test/Maintenance.html +1 -0
- data/test/documents/test +1 -0
- data/test/mail_settings.rb +12 -0
- data/test/spec_rack_accept_format.rb +72 -0
- data/test/spec_rack_access.rb +154 -0
- data/test/spec_rack_backstage.rb +26 -0
- data/test/spec_rack_callbacks.rb +65 -0
- data/test/spec_rack_config.rb +22 -0
- data/test/spec_rack_contrib.rb +8 -0
- data/test/spec_rack_cookies.rb +56 -0
- data/test/spec_rack_csshttprequest.rb +66 -0
- data/test/spec_rack_deflect.rb +107 -0
- data/test/spec_rack_evil.rb +19 -0
- data/test/spec_rack_expectation_cascade.rb +72 -0
- data/test/spec_rack_garbagecollector.rb +13 -0
- data/test/spec_rack_host_meta.rb +50 -0
- data/test/spec_rack_jsonp.rb +83 -0
- data/test/spec_rack_lighttpd_script_name_fix.rb +16 -0
- data/test/spec_rack_mailexceptions.rb +97 -0
- data/test/spec_rack_nested_params.rb +46 -0
- data/test/spec_rack_not_found.rb +17 -0
- data/test/spec_rack_post_body_content_type_parser.rb +32 -0
- data/test/spec_rack_proctitle.rb +26 -0
- data/test/spec_rack_profiler.rb +37 -0
- data/test/spec_rack_relative_redirect.rb +78 -0
- data/test/spec_rack_response_cache.rb +137 -0
- data/test/spec_rack_response_headers.rb +35 -0
- data/test/spec_rack_runtime.rb +35 -0
- data/test/spec_rack_sendfile.rb +86 -0
- data/test/spec_rack_simple_endpoint.rb +95 -0
- data/test/spec_rack_static_cache.rb +91 -0
- data/test/statics/test +1 -0
- metadata +234 -0
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'test/spec'
|
2
|
+
require 'rack/mock'
|
3
|
+
require 'rack/contrib/deflect'
|
4
|
+
|
5
|
+
context "Rack::Deflect" do
|
6
|
+
|
7
|
+
setup do
|
8
|
+
@app = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, ['cookies']] }
|
9
|
+
@mock_addr_1 = '111.111.111.111'
|
10
|
+
@mock_addr_2 = '222.222.222.222'
|
11
|
+
@mock_addr_3 = '333.333.333.333'
|
12
|
+
end
|
13
|
+
|
14
|
+
def mock_env remote_addr, path = '/'
|
15
|
+
Rack::MockRequest.env_for path, { 'REMOTE_ADDR' => remote_addr }
|
16
|
+
end
|
17
|
+
|
18
|
+
def mock_deflect options = {}
|
19
|
+
Rack::Deflect.new @app, options
|
20
|
+
end
|
21
|
+
|
22
|
+
specify "should allow regular requests to follow through" do
|
23
|
+
app = mock_deflect
|
24
|
+
status, headers, body = app.call mock_env(@mock_addr_1)
|
25
|
+
status.should.equal 200
|
26
|
+
body.should.equal ['cookies']
|
27
|
+
end
|
28
|
+
|
29
|
+
specify "should deflect requests exceeding the request threshold" do
|
30
|
+
log = StringIO.new
|
31
|
+
app = mock_deflect :request_threshold => 5, :interval => 10, :block_duration => 10, :log => log
|
32
|
+
env = mock_env @mock_addr_1
|
33
|
+
|
34
|
+
# First 5 should be fine
|
35
|
+
5.times do
|
36
|
+
status, headers, body = app.call env
|
37
|
+
status.should.equal 200
|
38
|
+
body.should.equal ['cookies']
|
39
|
+
end
|
40
|
+
|
41
|
+
# Remaining requests should fail for 10 seconds
|
42
|
+
10.times do
|
43
|
+
status, headers, body = app.call env
|
44
|
+
status.should.equal 403
|
45
|
+
body.should.equal []
|
46
|
+
end
|
47
|
+
|
48
|
+
# Log should reflect that we have blocked an address
|
49
|
+
log.string.should.match(/^deflect\(\d+\/\d+\/\d+\): blocked 111.111.111.111\n/)
|
50
|
+
end
|
51
|
+
|
52
|
+
specify "should expire blocking" do
|
53
|
+
log = StringIO.new
|
54
|
+
app = mock_deflect :request_threshold => 5, :interval => 2, :block_duration => 2, :log => log
|
55
|
+
env = mock_env @mock_addr_1
|
56
|
+
|
57
|
+
# First 5 should be fine
|
58
|
+
5.times do
|
59
|
+
status, headers, body = app.call env
|
60
|
+
status.should.equal 200
|
61
|
+
body.should.equal ['cookies']
|
62
|
+
end
|
63
|
+
|
64
|
+
# Exceeds request threshold
|
65
|
+
status, headers, body = app.call env
|
66
|
+
status.should.equal 403
|
67
|
+
body.should.equal []
|
68
|
+
|
69
|
+
# Allow block to expire
|
70
|
+
sleep 3
|
71
|
+
|
72
|
+
# Another 5 is fine now
|
73
|
+
5.times do
|
74
|
+
status, headers, body = app.call env
|
75
|
+
status.should.equal 200
|
76
|
+
body.should.equal ['cookies']
|
77
|
+
end
|
78
|
+
|
79
|
+
# Log should reflect block and release
|
80
|
+
log.string.should.match(/deflect.*: blocked 111\.111\.111\.111\ndeflect.*: released 111\.111\.111\.111\n/)
|
81
|
+
end
|
82
|
+
|
83
|
+
specify "should allow whitelisting of remote addresses" do
|
84
|
+
app = mock_deflect :whitelist => [@mock_addr_1], :request_threshold => 5, :interval => 2
|
85
|
+
env = mock_env @mock_addr_1
|
86
|
+
|
87
|
+
# Whitelisted addresses are always fine
|
88
|
+
10.times do
|
89
|
+
status, headers, body = app.call env
|
90
|
+
status.should.equal 200
|
91
|
+
body.should.equal ['cookies']
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
specify "should allow blacklisting of remote addresses" do
|
96
|
+
app = mock_deflect :blacklist => [@mock_addr_2]
|
97
|
+
|
98
|
+
status, headers, body = app.call mock_env(@mock_addr_1)
|
99
|
+
status.should.equal 200
|
100
|
+
body.should.equal ['cookies']
|
101
|
+
|
102
|
+
status, headers, body = app.call mock_env(@mock_addr_2)
|
103
|
+
status.should.equal 403
|
104
|
+
body.should.equal []
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'test/spec'
|
2
|
+
require 'rack/mock'
|
3
|
+
require 'rack/contrib/evil'
|
4
|
+
require 'erb'
|
5
|
+
|
6
|
+
context "Rack::Evil" do
|
7
|
+
app = lambda do |env|
|
8
|
+
template = ERB.new("<%= throw :response, [404, {'Content-Type' => 'text/html'}, 'Never know where it comes from'] %>")
|
9
|
+
[200, {'Content-Type' => 'text/plain'}, template.result(binding)]
|
10
|
+
end
|
11
|
+
|
12
|
+
specify "should enable the app to return the response from anywhere" do
|
13
|
+
status, headers, body = Rack::Evil.new(app).call({})
|
14
|
+
|
15
|
+
status.should.equal 404
|
16
|
+
headers['Content-Type'].should.equal 'text/html'
|
17
|
+
body.should.equal 'Never know where it comes from'
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'test/spec'
|
2
|
+
require 'rack/mock'
|
3
|
+
require 'rack/contrib/expectation_cascade'
|
4
|
+
|
5
|
+
context "Rack::ExpectationCascade" do
|
6
|
+
specify "with no apps returns a 404 if no expectation header was set" do
|
7
|
+
app = Rack::ExpectationCascade.new
|
8
|
+
env = {}
|
9
|
+
response = app.call(env)
|
10
|
+
response[0].should.equal 404
|
11
|
+
env.should.equal({})
|
12
|
+
end
|
13
|
+
|
14
|
+
specify "with no apps returns a 417 if expectation header was set" do
|
15
|
+
app = Rack::ExpectationCascade.new
|
16
|
+
env = {"Expect" => "100-continue"}
|
17
|
+
response = app.call(env)
|
18
|
+
response[0].should.equal 417
|
19
|
+
env.should.equal({"Expect" => "100-continue"})
|
20
|
+
end
|
21
|
+
|
22
|
+
specify "returns first successful response" do
|
23
|
+
app = Rack::ExpectationCascade.new do |cascade|
|
24
|
+
cascade << lambda { |env| [417, {"Content-Type" => "text/plain"}, []] }
|
25
|
+
cascade << lambda { |env| [200, {"Content-Type" => "text/plain"}, ["OK"]] }
|
26
|
+
end
|
27
|
+
response = app.call({})
|
28
|
+
response[0].should.equal 200
|
29
|
+
response[2][0].should.equal "OK"
|
30
|
+
end
|
31
|
+
|
32
|
+
specify "expectation is set if it has not been already" do
|
33
|
+
app = Rack::ExpectationCascade.new do |cascade|
|
34
|
+
cascade << lambda { |env| [200, {"Content-Type" => "text/plain"}, ["Expect: #{env["Expect"]}"]] }
|
35
|
+
end
|
36
|
+
response = app.call({})
|
37
|
+
response[0].should.equal 200
|
38
|
+
response[2][0].should.equal "Expect: 100-continue"
|
39
|
+
end
|
40
|
+
|
41
|
+
specify "returns a 404 if no apps where matched and no expectation header was set" do
|
42
|
+
app = Rack::ExpectationCascade.new do |cascade|
|
43
|
+
cascade << lambda { |env| [417, {"Content-Type" => "text/plain"}, []] }
|
44
|
+
end
|
45
|
+
response = app.call({})
|
46
|
+
response[0].should.equal 404
|
47
|
+
response[2][0].should.equal nil
|
48
|
+
end
|
49
|
+
|
50
|
+
specify "returns a 417 if no apps where matched and a expectation header was set" do
|
51
|
+
app = Rack::ExpectationCascade.new do |cascade|
|
52
|
+
cascade << lambda { |env| [417, {"Content-Type" => "text/plain"}, []] }
|
53
|
+
end
|
54
|
+
response = app.call({"Expect" => "100-continue"})
|
55
|
+
response[0].should.equal 417
|
56
|
+
response[2][0].should.equal nil
|
57
|
+
end
|
58
|
+
|
59
|
+
specify "nests expectation cascades" do
|
60
|
+
app = Rack::ExpectationCascade.new do |c1|
|
61
|
+
c1 << Rack::ExpectationCascade.new do |c2|
|
62
|
+
c2 << lambda { |env| [417, {"Content-Type" => "text/plain"}, []] }
|
63
|
+
end
|
64
|
+
c1 << Rack::ExpectationCascade.new do |c2|
|
65
|
+
c2 << lambda { |env| [200, {"Content-Type" => "text/plain"}, ["OK"]] }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
response = app.call({})
|
69
|
+
response[0].should.equal 200
|
70
|
+
response[2][0].should.equal "OK"
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'test/spec'
|
2
|
+
require 'rack/mock'
|
3
|
+
require 'rack/contrib/garbagecollector'
|
4
|
+
|
5
|
+
context 'Rack::GarbageCollector' do
|
6
|
+
|
7
|
+
specify 'starts the garbage collector after each request' do
|
8
|
+
app = lambda { |env|
|
9
|
+
[200, {'Content-Type'=>'text/plain'}, ['Hello World']] }
|
10
|
+
Rack::GarbageCollector.new(app).call({})
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'test/spec'
|
2
|
+
require 'rack/mock'
|
3
|
+
require 'rack/contrib/host_meta'
|
4
|
+
require 'rack/contrib/not_found'
|
5
|
+
|
6
|
+
context "Rack::HostMeta" do
|
7
|
+
|
8
|
+
setup do
|
9
|
+
app = Rack::Builder.new do
|
10
|
+
use Rack::Lint
|
11
|
+
use Rack::ContentLength
|
12
|
+
use Rack::HostMeta do
|
13
|
+
link :uri => '/robots.txt', :rel => 'robots'
|
14
|
+
link :uri => '/w3c/p3p.xml', :rel => 'privacy', :type => 'application/p3p.xml'
|
15
|
+
link :pattern => '{uri};json_schema', :rel => 'describedby', :type => 'application/x-schema+json'
|
16
|
+
end
|
17
|
+
run Rack::NotFound.new('test/404.html')
|
18
|
+
end
|
19
|
+
@response = Rack::MockRequest.new(app).get('/host-meta')
|
20
|
+
end
|
21
|
+
|
22
|
+
specify "should respond to /host-meta" do
|
23
|
+
@response.status.should.equal 200
|
24
|
+
end
|
25
|
+
|
26
|
+
specify "should respond with the correct media type" do
|
27
|
+
@response['Content-Type'].should.equal 'application/host-meta'
|
28
|
+
end
|
29
|
+
|
30
|
+
specify "should include a Link entry for each Link item in the config block" do
|
31
|
+
@response.body.should.match(/Link:\s*<\/robots.txt>;.*\n/)
|
32
|
+
@response.body.should.match(/Link:\s*<\/w3c\/p3p.xml>;.*/)
|
33
|
+
end
|
34
|
+
|
35
|
+
specify "should include a Link-Pattern entry for each Link-Pattern item in the config" do
|
36
|
+
@response.body.should.match(/Link-Pattern:\s*<\{uri\};json_schema>;.*/)
|
37
|
+
end
|
38
|
+
|
39
|
+
specify "should include a rel attribute for each Link or Link-Pattern entry where specified" do
|
40
|
+
@response.body.should.match(/rel="robots"/)
|
41
|
+
@response.body.should.match(/rel="privacy"/)
|
42
|
+
@response.body.should.match(/rel="describedby"/)
|
43
|
+
end
|
44
|
+
|
45
|
+
specify "should include a type attribute for each Link or Link-Pattern entry where specified" do
|
46
|
+
@response.body.should.match(/Link:\s*<\/w3c\/p3p.xml>;.*type.*application\/p3p.xml/)
|
47
|
+
@response.body.should.match(/Link-Pattern:\s*<\{uri\};json_schema>;.*type.*application\/x-schema\+json/)
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'test/spec'
|
2
|
+
require 'rack/mock'
|
3
|
+
# require File.expand_path(File.dirname(__FILE__)+"/../lib/rack/contrib/jsonp")
|
4
|
+
require 'rack/contrib/jsonp'
|
5
|
+
|
6
|
+
context "Rack::JSONP" do
|
7
|
+
|
8
|
+
context "when a callback parameter is provided" do
|
9
|
+
specify "should wrap the response body in the Javascript callback if JSON" do
|
10
|
+
test_body = '{"bar":"foo"}'
|
11
|
+
callback = 'foo'
|
12
|
+
app = lambda { |env| [200, {'Content-Type' => 'application/json'}, [test_body]] }
|
13
|
+
request = Rack::MockRequest.env_for("/", :params => "foo=bar&callback=#{callback}")
|
14
|
+
body = Rack::JSONP.new(app).call(request).last
|
15
|
+
body.should.equal ["#{callback}(#{test_body})"]
|
16
|
+
end
|
17
|
+
|
18
|
+
specify "should not wrap the response body in a callback if body is not JSON" do
|
19
|
+
test_body = '{"bar":"foo"}'
|
20
|
+
callback = 'foo'
|
21
|
+
app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, [test_body]] }
|
22
|
+
request = Rack::MockRequest.env_for("/", :params => "foo=bar&callback=#{callback}")
|
23
|
+
body = Rack::JSONP.new(app).call(request).last
|
24
|
+
body.should.equal ['{"bar":"foo"}']
|
25
|
+
end
|
26
|
+
|
27
|
+
specify "should update content length if it was set" do
|
28
|
+
test_body = '{"bar":"foo"}'
|
29
|
+
callback = 'foo'
|
30
|
+
app = lambda { |env| [200, {'Content-Type' => 'application/json', 'Content-Length' => test_body.length}, [test_body]] }
|
31
|
+
request = Rack::MockRequest.env_for("/", :params => "foo=bar&callback=#{callback}")
|
32
|
+
|
33
|
+
headers = Rack::JSONP.new(app).call(request)[1]
|
34
|
+
expected_length = test_body.length + callback.length + "()".length
|
35
|
+
headers['Content-Length'].should.equal (expected_length.to_s)
|
36
|
+
end
|
37
|
+
|
38
|
+
specify "should not touch content length if not set" do
|
39
|
+
test_body = '{"bar":"foo"}'
|
40
|
+
callback = 'foo'
|
41
|
+
app = lambda { |env| [200, {'Content-Type' => 'application/json'}, [test_body]] }
|
42
|
+
request = Rack::MockRequest.env_for("/", :params => "foo=bar&callback=#{callback}")
|
43
|
+
headers = Rack::JSONP.new(app).call(request)[1]
|
44
|
+
headers['Content-Length'].should.equal nil
|
45
|
+
end
|
46
|
+
|
47
|
+
specify "should modify the content type to application/javascript" do
|
48
|
+
test_body = '{"bar":"foo"}'
|
49
|
+
callback = 'foo'
|
50
|
+
app = lambda { |env| [200, {'Content-Type' => 'application/json'}, [test_body]] }
|
51
|
+
request = Rack::MockRequest.env_for("/", :params => "foo=bar&callback=#{callback}")
|
52
|
+
headers = Rack::JSONP.new(app).call(request)[1]
|
53
|
+
headers['Content-Type'].should.equal ('application/javascript')
|
54
|
+
end
|
55
|
+
|
56
|
+
specify "should write status code to response and set status to 200 if status is 40x or 50x" do
|
57
|
+
callback = 'foo'
|
58
|
+
app = lambda { |env| [404, {'Content-Type' => 'application/json'}, [""]] }
|
59
|
+
request = Rack::MockRequest.env_for("/", :params => "foo=bar&callback=#{callback}")
|
60
|
+
status, headers, body = Rack::JSONP.new(app).call(request)
|
61
|
+
status.should.equal 200
|
62
|
+
body.should.equal ["#{callback}({\"errorCode\":404})"]
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
specify "should not change anything if no callback param is provided" do
|
68
|
+
test_body = ['{"bar":"foo"}']
|
69
|
+
app = lambda { |env| [200, {'Content-Type' => 'application/json'}, test_body] }
|
70
|
+
request = Rack::MockRequest.env_for("/", :params => "foo=bar")
|
71
|
+
body = Rack::JSONP.new(app).call(request).last
|
72
|
+
body.should.equal test_body
|
73
|
+
end
|
74
|
+
|
75
|
+
specify "should not change anything if it's not a json response" do
|
76
|
+
test_body = '<html><body>404 Not Found</body></html>'
|
77
|
+
app = lambda { |env| [404, {'Content-Type' => 'text/html'}, [test_body]] }
|
78
|
+
request = Rack::MockRequest.env_for("/", :params => "callback=foo", 'HTTP_ACCEPT' => 'application/json')
|
79
|
+
body = Rack::JSONP.new(app).call(request).last
|
80
|
+
body.should.equal [test_body]
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'test/spec'
|
2
|
+
require 'rack/mock'
|
3
|
+
require 'rack/contrib/lighttpd_script_name_fix'
|
4
|
+
|
5
|
+
context "Rack::LighttpdScriptNameFix" do
|
6
|
+
specify "corrects SCRIPT_NAME and PATH_INFO set by lighttpd " do
|
7
|
+
env = {
|
8
|
+
"PATH_INFO" => "/foo/bar/baz",
|
9
|
+
"SCRIPT_NAME" => "/hello"
|
10
|
+
}
|
11
|
+
app = lambda { |_| [200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] }
|
12
|
+
response = Rack::LighttpdScriptNameFix.new(app).call(env)
|
13
|
+
env['SCRIPT_NAME'].should.be.empty
|
14
|
+
env['PATH_INFO'].should.equal '/hello/foo/bar/baz'
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'test/spec'
|
2
|
+
require 'rack/mock'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'tmail'
|
6
|
+
require 'rack/contrib/mailexceptions'
|
7
|
+
|
8
|
+
require File.dirname(__FILE__) + '/mail_settings.rb'
|
9
|
+
|
10
|
+
class TestError < RuntimeError
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_exception
|
14
|
+
raise TestError, 'Suffering Succotash!'
|
15
|
+
rescue => boom
|
16
|
+
return boom
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'Rack::MailExceptions' do
|
20
|
+
|
21
|
+
setup do
|
22
|
+
@app = lambda { |env| raise TestError, 'Why, I say' }
|
23
|
+
@env = Rack::MockRequest.env_for("/foo",
|
24
|
+
'FOO' => 'BAR',
|
25
|
+
:method => 'GET',
|
26
|
+
:input => 'THE BODY'
|
27
|
+
)
|
28
|
+
@smtp_settings = {
|
29
|
+
:server => 'example.com',
|
30
|
+
:domain => 'example.com',
|
31
|
+
:port => 500,
|
32
|
+
:authentication => :login,
|
33
|
+
:user_name => 'joe',
|
34
|
+
:password => 'secret'
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
specify 'yields a configuration object to the block when created' do
|
39
|
+
called = false
|
40
|
+
mailer =
|
41
|
+
Rack::MailExceptions.new(@app) do |mail|
|
42
|
+
called = true
|
43
|
+
mail.to 'foo@example.org'
|
44
|
+
mail.from 'bar@example.org'
|
45
|
+
mail.subject '[ERROR] %s'
|
46
|
+
mail.smtp @smtp_settings
|
47
|
+
end
|
48
|
+
called.should.be == true
|
49
|
+
end
|
50
|
+
|
51
|
+
specify 'generates a TMail object with configured settings' do
|
52
|
+
mailer =
|
53
|
+
Rack::MailExceptions.new(@app) do |mail|
|
54
|
+
mail.to 'foo@example.org'
|
55
|
+
mail.from 'bar@example.org'
|
56
|
+
mail.subject '[ERROR] %s'
|
57
|
+
mail.smtp @smtp_settings
|
58
|
+
end
|
59
|
+
|
60
|
+
tmail = mailer.send(:generate_mail, test_exception, @env)
|
61
|
+
tmail.to.should.equal ['foo@example.org']
|
62
|
+
tmail.from.should.equal ['bar@example.org']
|
63
|
+
tmail.subject.should.equal '[ERROR] Suffering Succotash!'
|
64
|
+
tmail.body.should.not.be.nil
|
65
|
+
tmail.body.should.be =~ /FOO:\s+"BAR"/
|
66
|
+
tmail.body.should.be =~ /^\s*THE BODY\s*$/
|
67
|
+
end
|
68
|
+
|
69
|
+
specify 'catches exceptions raised from app, sends mail, and re-raises' do
|
70
|
+
mailer =
|
71
|
+
Rack::MailExceptions.new(@app) do |mail|
|
72
|
+
mail.to 'foo@example.org'
|
73
|
+
mail.from 'bar@example.org'
|
74
|
+
mail.subject '[ERROR] %s'
|
75
|
+
mail.smtp @smtp_settings
|
76
|
+
end
|
77
|
+
lambda { mailer.call(@env) }.should.raise(TestError)
|
78
|
+
@env['mail.sent'].should.be == true
|
79
|
+
end
|
80
|
+
|
81
|
+
if TEST_SMTP && ! TEST_SMTP.empty?
|
82
|
+
specify 'sends mail' do
|
83
|
+
mailer =
|
84
|
+
Rack::MailExceptions.new(@app) do |mail|
|
85
|
+
mail.config.merge! TEST_SMTP
|
86
|
+
end
|
87
|
+
lambda { mailer.call(@env) }.should.raise(TestError)
|
88
|
+
@env['mail.sent'].should.be == true
|
89
|
+
end
|
90
|
+
else
|
91
|
+
STDERR.puts 'WARN: Skipping SMTP tests (edit test/mail_settings.rb to enable)'
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
rescue LoadError => boom
|
96
|
+
STDERR.puts "WARN: Skipping Rack::MailExceptions tests (tmail not installed)"
|
97
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'test/spec'
|
2
|
+
require 'rack/mock'
|
3
|
+
require 'rack/contrib/nested_params'
|
4
|
+
require 'rack/methodoverride'
|
5
|
+
|
6
|
+
context Rack::NestedParams do
|
7
|
+
|
8
|
+
App = lambda { |env| [200, {'Content-Type' => 'text/plain'}, Rack::Request.new(env)] }
|
9
|
+
|
10
|
+
def env_for_post_with_headers(path, headers, body)
|
11
|
+
Rack::MockRequest.env_for(path, {:method => "POST", :input => body}.merge(headers))
|
12
|
+
end
|
13
|
+
|
14
|
+
def form_post(params, content_type = 'application/x-www-form-urlencoded')
|
15
|
+
params = Rack::Utils.build_query(params) if Hash === params
|
16
|
+
env_for_post_with_headers('/', {'CONTENT_TYPE' => content_type}, params)
|
17
|
+
end
|
18
|
+
|
19
|
+
def middleware
|
20
|
+
Rack::NestedParams.new(App)
|
21
|
+
end
|
22
|
+
|
23
|
+
specify "should handle requests with POST body Content-Type of application/x-www-form-urlencoded" do
|
24
|
+
req = middleware.call(form_post({'foo[bar][baz]' => 'nested'})).last
|
25
|
+
req.POST.should.equal({"foo" => { "bar" => { "baz" => "nested" }}})
|
26
|
+
end
|
27
|
+
|
28
|
+
specify "should not parse requests with other Content-Type" do
|
29
|
+
req = middleware.call(form_post({'foo[bar][baz]' => 'nested'}, 'text/plain')).last
|
30
|
+
req.POST.should.equal({})
|
31
|
+
end
|
32
|
+
|
33
|
+
specify "should work even after another middleware already parsed the request" do
|
34
|
+
app = Rack::MethodOverride.new(middleware)
|
35
|
+
req = app.call(form_post({'_method' => 'put', 'foo[bar]' => 'nested'})).last
|
36
|
+
req.POST.should.equal({'_method' => 'put', "foo" => { "bar" => "nested" }})
|
37
|
+
req.put?.should.equal true
|
38
|
+
end
|
39
|
+
|
40
|
+
specify "should make first boolean have precedence even after request already parsed" do
|
41
|
+
app = Rack::MethodOverride.new(middleware)
|
42
|
+
req = app.call(form_post("foo=1&foo=0")).last
|
43
|
+
req.POST.should.equal({"foo" => '1'})
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'test/spec'
|
2
|
+
require 'rack/mock'
|
3
|
+
require 'rack/contrib/not_found'
|
4
|
+
|
5
|
+
context "Rack::NotFound" do
|
6
|
+
|
7
|
+
specify "should render the file at the given path for all requests" do
|
8
|
+
app = Rack::Builder.new do
|
9
|
+
use Rack::Lint
|
10
|
+
run Rack::NotFound.new('test/404.html')
|
11
|
+
end
|
12
|
+
response = Rack::MockRequest.new(app).get('/')
|
13
|
+
response.body.should.equal('Not Found')
|
14
|
+
response.status.should.equal(404)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'test/spec'
|
2
|
+
require 'rack/mock'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rack/contrib/post_body_content_type_parser'
|
6
|
+
|
7
|
+
context "Rack::PostBodyContentTypeParser" do
|
8
|
+
|
9
|
+
specify "should handle requests with POST body Content-Type of application/json" do
|
10
|
+
app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, Rack::Request.new(env).POST] }
|
11
|
+
env = env_for_post_with_headers('/', {'Content_Type'.upcase => 'application/json'}, {:body => "asdf", :status => "12"}.to_json)
|
12
|
+
body = Rack::PostBodyContentTypeParser.new(app).call(env).last
|
13
|
+
body['body'].should.equal "asdf"
|
14
|
+
body['status'].should.equal "12"
|
15
|
+
end
|
16
|
+
|
17
|
+
specify "should change nothing when the POST body content type isn't application/json" do
|
18
|
+
app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, Rack::Request.new(env).POST] }
|
19
|
+
body = Rack::PostBodyContentTypeParser.new(app).call(Rack::MockRequest.env_for("/", {:method => 'POST', :params => "body=asdf&status=12"})).last
|
20
|
+
body['body'].should.equal "asdf"
|
21
|
+
body['status'].should.equal "12"
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
def env_for_post_with_headers(path, headers, body)
|
27
|
+
Rack::MockRequest.env_for(path, {:method => "POST", :input => body}.merge(headers))
|
28
|
+
end
|
29
|
+
rescue LoadError => e
|
30
|
+
# Missing dependency JSON, skipping tests.
|
31
|
+
STDERR.puts "WARN: Skipping Rack::PostBodyContentTypeParser tests (json not installed)"
|
32
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'test/spec'
|
2
|
+
require 'rack/mock'
|
3
|
+
require 'rack/contrib/proctitle'
|
4
|
+
|
5
|
+
context "Rack::ProcTitle" do
|
6
|
+
F = ::File
|
7
|
+
|
8
|
+
progname = File.basename($0)
|
9
|
+
appname = F.expand_path(__FILE__).split('/')[-3]
|
10
|
+
|
11
|
+
def simple_app(body=['Hello World!'])
|
12
|
+
lambda { |env| [200, {'Content-Type' => 'text/plain'}, body] }
|
13
|
+
end
|
14
|
+
|
15
|
+
specify "should set the process title when created" do
|
16
|
+
Rack::ProcTitle.new(simple_app)
|
17
|
+
$0.should.equal "#{progname} [#{appname}] init ..."
|
18
|
+
end
|
19
|
+
|
20
|
+
specify "should set the process title on each request" do
|
21
|
+
app = Rack::ProcTitle.new(simple_app)
|
22
|
+
req = Rack::MockRequest.new(app)
|
23
|
+
10.times { req.get('/hello') }
|
24
|
+
$0.should.equal "#{progname} [#{appname}/80] (10) GET /hello"
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'test/spec'
|
2
|
+
require 'rack/mock'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rack/contrib/profiler'
|
6
|
+
|
7
|
+
context 'Rack::Profiler' do
|
8
|
+
app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, 'Oh hai der'] }
|
9
|
+
request = Rack::MockRequest.env_for("/", :params => "profile=process_time")
|
10
|
+
|
11
|
+
specify 'printer defaults to RubyProf::CallTreePrinter' do
|
12
|
+
profiler = Rack::Profiler.new(nil)
|
13
|
+
profiler.instance_variable_get('@printer').should.equal RubyProf::CallTreePrinter
|
14
|
+
profiler.instance_variable_get('@times').should.equal 1
|
15
|
+
end
|
16
|
+
|
17
|
+
specify 'CallTreePrinter has correct headers' do
|
18
|
+
headers = Rack::Profiler.new(app).call(request)[1]
|
19
|
+
headers.should.equal "Content-Disposition"=>"attachment; filename=\"/.process_time.tree\"", "Content-Type"=>"application/octet-stream"
|
20
|
+
end
|
21
|
+
|
22
|
+
specify 'FlatPrinter and GraphPrinter has Content-Type text/plain' do
|
23
|
+
%w(flat graph).each do |printer|
|
24
|
+
headers = Rack::Profiler.new(app, :printer => printer.to_sym).call(request)[1]
|
25
|
+
headers.should.equal "Content-Type"=>"text/plain"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
specify 'GraphHtmlPrinter has Content-Type text/html' do
|
30
|
+
headers = Rack::Profiler.new(app, :printer => :graph_html).call(request)[1]
|
31
|
+
headers.should.equal "Content-Type"=>"text/html"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
rescue LoadError => boom
|
36
|
+
$stderr.puts "WARN: Skipping Rack::Profiler tests (ruby-prof not installed)"
|
37
|
+
end
|