rack 1.1.6 → 1.6.9
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 +7 -0
- data/COPYING +1 -1
- data/HISTORY.md +375 -0
- data/KNOWN-ISSUES +23 -0
- data/README.rdoc +312 -0
- data/Rakefile +124 -0
- data/SPEC +125 -32
- data/contrib/rack.png +0 -0
- data/contrib/rack.svg +150 -0
- data/contrib/rack_logo.svg +1 -1
- data/contrib/rdoc.css +412 -0
- data/example/protectedlobster.rb +1 -1
- data/lib/rack/auth/abstract/handler.rb +4 -4
- data/lib/rack/auth/abstract/request.rb +7 -5
- data/lib/rack/auth/basic.rb +1 -1
- data/lib/rack/auth/digest/md5.rb +7 -3
- data/lib/rack/auth/digest/nonce.rb +1 -1
- data/lib/rack/auth/digest/params.rb +7 -9
- data/lib/rack/auth/digest/request.rb +10 -9
- data/lib/rack/backports/uri/common_18.rb +56 -0
- data/lib/rack/backports/uri/common_192.rb +52 -0
- data/lib/rack/backports/uri/common_193.rb +29 -0
- data/lib/rack/body_proxy.rb +39 -0
- data/lib/rack/builder.rb +106 -22
- data/lib/rack/cascade.rb +17 -6
- data/lib/rack/chunked.rb +44 -24
- data/lib/rack/commonlogger.rb +36 -13
- data/lib/rack/conditionalget.rb +49 -17
- data/lib/rack/config.rb +5 -0
- data/lib/rack/content_length.rb +14 -6
- data/lib/rack/content_type.rb +7 -1
- data/lib/rack/deflater.rb +73 -15
- data/lib/rack/directory.rb +18 -8
- data/lib/rack/etag.rb +59 -9
- data/lib/rack/file.rb +106 -44
- data/lib/rack/handler/cgi.rb +11 -11
- data/lib/rack/handler/fastcgi.rb +18 -6
- data/lib/rack/handler/lsws.rb +2 -4
- data/lib/rack/handler/mongrel.rb +22 -6
- data/lib/rack/handler/scgi.rb +16 -8
- data/lib/rack/handler/thin.rb +19 -4
- data/lib/rack/handler/webrick.rb +72 -19
- data/lib/rack/handler.rb +47 -14
- data/lib/rack/head.rb +10 -2
- data/lib/rack/lint.rb +260 -75
- data/lib/rack/lobster.rb +13 -8
- data/lib/rack/lock.rb +13 -3
- data/lib/rack/logger.rb +0 -2
- data/lib/rack/methodoverride.rb +27 -8
- data/lib/rack/mime.rb +625 -167
- data/lib/rack/mock.rb +78 -53
- data/lib/rack/multipart/generator.rb +93 -0
- data/lib/rack/multipart/parser.rb +253 -0
- data/lib/rack/multipart/uploaded_file.rb +34 -0
- data/lib/rack/multipart.rb +34 -0
- data/lib/rack/nulllogger.rb +21 -2
- data/lib/rack/recursive.rb +10 -5
- data/lib/rack/reloader.rb +3 -2
- data/lib/rack/request.rb +201 -74
- data/lib/rack/response.rb +41 -28
- data/lib/rack/rewindable_input.rb +15 -11
- data/lib/rack/runtime.rb +16 -3
- data/lib/rack/sendfile.rb +47 -29
- data/lib/rack/server.rb +223 -47
- data/lib/rack/session/abstract/id.rb +289 -30
- data/lib/rack/session/cookie.rb +133 -44
- data/lib/rack/session/memcache.rb +30 -56
- data/lib/rack/session/pool.rb +19 -43
- data/lib/rack/showexceptions.rb +53 -15
- data/lib/rack/showstatus.rb +14 -7
- data/lib/rack/static.rb +124 -12
- data/lib/rack/tempfile_reaper.rb +22 -0
- data/lib/rack/urlmap.rb +49 -15
- data/lib/rack/utils/okjson.rb +600 -0
- data/lib/rack/utils.rb +363 -361
- data/lib/rack.rb +17 -23
- data/rack.gemspec +11 -20
- data/test/builder/anything.rb +5 -0
- data/test/builder/comment.ru +4 -0
- data/test/builder/end.ru +5 -0
- data/test/builder/line.ru +1 -0
- data/test/builder/options.ru +2 -0
- data/test/cgi/assets/folder/test.js +1 -0
- data/test/cgi/assets/fonts/font.eot +1 -0
- data/test/cgi/assets/images/image.png +1 -0
- data/test/cgi/assets/index.html +1 -0
- data/test/cgi/assets/javascripts/app.js +1 -0
- data/test/cgi/assets/stylesheets/app.css +1 -0
- data/test/cgi/lighttpd.conf +26 -0
- data/test/cgi/rackup_stub.rb +6 -0
- data/test/cgi/sample_rackup.ru +5 -0
- data/test/cgi/test +9 -0
- data/test/cgi/test+directory/test+file +1 -0
- data/test/cgi/test.fcgi +8 -0
- data/test/cgi/test.ru +5 -0
- data/test/gemloader.rb +10 -0
- data/test/multipart/bad_robots +259 -0
- data/test/multipart/binary +0 -0
- data/test/multipart/content_type_and_no_filename +6 -0
- data/test/multipart/empty +10 -0
- data/test/multipart/fail_16384_nofile +814 -0
- data/test/multipart/file1.txt +1 -0
- data/test/multipart/filename_and_modification_param +7 -0
- data/test/multipart/filename_and_no_name +6 -0
- data/test/multipart/filename_with_escaped_quotes +6 -0
- data/test/multipart/filename_with_escaped_quotes_and_modification_param +7 -0
- data/test/multipart/filename_with_null_byte +7 -0
- data/test/multipart/filename_with_percent_escaped_quotes +6 -0
- data/test/multipart/filename_with_unescaped_percentages +6 -0
- data/test/multipart/filename_with_unescaped_percentages2 +6 -0
- data/test/multipart/filename_with_unescaped_percentages3 +6 -0
- data/test/multipart/filename_with_unescaped_quotes +6 -0
- data/test/multipart/ie +6 -0
- data/test/multipart/invalid_character +6 -0
- data/test/multipart/mixed_files +21 -0
- data/test/multipart/nested +10 -0
- data/test/multipart/none +9 -0
- data/test/multipart/semicolon +6 -0
- data/test/multipart/text +15 -0
- data/test/multipart/three_files_three_fields +31 -0
- data/test/multipart/webkit +32 -0
- data/test/rackup/config.ru +31 -0
- data/test/registering_handler/rack/handler/registering_myself.rb +8 -0
- data/test/{spec_rack_auth_basic.rb → spec_auth_basic.rb} +23 -15
- data/test/{spec_rack_auth_digest.rb → spec_auth_digest.rb} +56 -29
- data/test/spec_body_proxy.rb +85 -0
- data/test/spec_builder.rb +223 -0
- data/test/{spec_rack_cascade.rb → spec_cascade.rb} +28 -15
- data/test/{spec_rack_cgi.rb → spec_cgi.rb} +44 -31
- data/test/spec_chunked.rb +101 -0
- data/test/spec_commonlogger.rb +93 -0
- data/test/spec_conditionalget.rb +102 -0
- data/test/{spec_rack_config.rb → spec_config.rb} +6 -8
- data/test/spec_content_length.rb +85 -0
- data/test/spec_content_type.rb +45 -0
- data/test/spec_deflater.rb +339 -0
- data/test/{spec_rack_directory.rb → spec_directory.rb} +37 -10
- data/test/spec_etag.rb +107 -0
- data/test/{spec_rack_fastcgi.rb → spec_fastcgi.rb} +47 -29
- data/test/spec_file.rb +221 -0
- data/test/spec_handler.rb +72 -0
- data/test/spec_head.rb +45 -0
- data/test/{spec_rack_lint.rb → spec_lint.rb} +82 -60
- data/test/spec_lobster.rb +58 -0
- data/test/spec_lock.rb +164 -0
- data/test/spec_logger.rb +23 -0
- data/test/spec_methodoverride.rb +95 -0
- data/test/spec_mime.rb +51 -0
- data/test/{spec_rack_mock.rb → spec_mock.rb} +92 -38
- data/test/{spec_rack_mongrel.rb → spec_mongrel.rb} +46 -53
- data/test/spec_multipart.rb +600 -0
- data/test/spec_nulllogger.rb +20 -0
- data/test/spec_recursive.rb +72 -0
- data/test/spec_request.rb +1227 -0
- data/test/spec_response.rb +407 -0
- data/test/spec_rewindable_input.rb +118 -0
- data/test/spec_runtime.rb +49 -0
- data/test/spec_sendfile.rb +130 -0
- data/test/spec_server.rb +167 -0
- data/test/spec_session_abstract_id.rb +53 -0
- data/test/spec_session_cookie.rb +410 -0
- data/test/{spec_rack_session_memcache.rb → spec_session_memcache.rb} +119 -71
- data/test/{spec_rack_session_pool.rb → spec_session_pool.rb} +106 -69
- data/test/spec_showexceptions.rb +85 -0
- data/test/spec_showstatus.rb +103 -0
- data/test/spec_static.rb +145 -0
- data/test/spec_tempfile_reaper.rb +63 -0
- data/test/{spec_rack_thin.rb → spec_thin.rb} +35 -35
- data/test/{spec_rack_urlmap.rb → spec_urlmap.rb} +40 -19
- data/test/spec_utils.rb +647 -0
- data/test/spec_version.rb +17 -0
- data/test/spec_webrick.rb +184 -0
- data/test/static/another/index.html +1 -0
- data/test/static/index.html +1 -0
- data/test/testrequest.rb +78 -0
- data/test/unregistered_handler/rack/handler/unregistered.rb +7 -0
- data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +7 -0
- metadata +220 -239
- data/RDOX +0 -0
- data/README +0 -592
- data/lib/rack/adapter/camping.rb +0 -22
- data/test/spec_auth.rb +0 -57
- data/test/spec_rack_builder.rb +0 -84
- data/test/spec_rack_camping.rb +0 -55
- data/test/spec_rack_chunked.rb +0 -62
- data/test/spec_rack_commonlogger.rb +0 -61
- data/test/spec_rack_conditionalget.rb +0 -41
- data/test/spec_rack_content_length.rb +0 -43
- data/test/spec_rack_content_type.rb +0 -30
- data/test/spec_rack_deflater.rb +0 -127
- data/test/spec_rack_etag.rb +0 -17
- data/test/spec_rack_file.rb +0 -75
- data/test/spec_rack_handler.rb +0 -43
- data/test/spec_rack_head.rb +0 -30
- data/test/spec_rack_lobster.rb +0 -45
- data/test/spec_rack_lock.rb +0 -38
- data/test/spec_rack_logger.rb +0 -21
- data/test/spec_rack_methodoverride.rb +0 -60
- data/test/spec_rack_nulllogger.rb +0 -13
- data/test/spec_rack_recursive.rb +0 -77
- data/test/spec_rack_request.rb +0 -594
- data/test/spec_rack_response.rb +0 -221
- data/test/spec_rack_rewindable_input.rb +0 -118
- data/test/spec_rack_runtime.rb +0 -35
- data/test/spec_rack_sendfile.rb +0 -86
- data/test/spec_rack_session_cookie.rb +0 -92
- data/test/spec_rack_showexceptions.rb +0 -21
- data/test/spec_rack_showstatus.rb +0 -72
- data/test/spec_rack_static.rb +0 -37
- data/test/spec_rack_utils.rb +0 -557
- data/test/spec_rack_webrick.rb +0 -130
- data/test/spec_rackup.rb +0 -164
data/test/spec_rack_builder.rb
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
require 'test/spec'
|
|
2
|
-
|
|
3
|
-
require 'rack/builder'
|
|
4
|
-
require 'rack/mock'
|
|
5
|
-
require 'rack/showexceptions'
|
|
6
|
-
require 'rack/auth/basic'
|
|
7
|
-
|
|
8
|
-
context "Rack::Builder" do
|
|
9
|
-
specify "chains apps by default" do
|
|
10
|
-
app = Rack::Builder.new do
|
|
11
|
-
use Rack::ShowExceptions
|
|
12
|
-
run lambda { |env| raise "bzzzt" }
|
|
13
|
-
end.to_app
|
|
14
|
-
|
|
15
|
-
Rack::MockRequest.new(app).get("/").should.be.server_error
|
|
16
|
-
Rack::MockRequest.new(app).get("/").should.be.server_error
|
|
17
|
-
Rack::MockRequest.new(app).get("/").should.be.server_error
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
specify "has implicit #to_app" do
|
|
21
|
-
app = Rack::Builder.new do
|
|
22
|
-
use Rack::ShowExceptions
|
|
23
|
-
run lambda { |env| raise "bzzzt" }
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
Rack::MockRequest.new(app).get("/").should.be.server_error
|
|
27
|
-
Rack::MockRequest.new(app).get("/").should.be.server_error
|
|
28
|
-
Rack::MockRequest.new(app).get("/").should.be.server_error
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
specify "supports blocks on use" do
|
|
32
|
-
app = Rack::Builder.new do
|
|
33
|
-
use Rack::ShowExceptions
|
|
34
|
-
use Rack::Auth::Basic do |username, password|
|
|
35
|
-
'secret' == password
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
run lambda { |env| [200, {}, ['Hi Boss']] }
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
response = Rack::MockRequest.new(app).get("/")
|
|
42
|
-
response.should.be.client_error
|
|
43
|
-
response.status.should.equal 401
|
|
44
|
-
|
|
45
|
-
# with auth...
|
|
46
|
-
response = Rack::MockRequest.new(app).get("/",
|
|
47
|
-
'HTTP_AUTHORIZATION' => 'Basic ' + ["joe:secret"].pack("m*"))
|
|
48
|
-
response.status.should.equal 200
|
|
49
|
-
response.body.to_s.should.equal 'Hi Boss'
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
specify "has explicit #to_app" do
|
|
53
|
-
app = Rack::Builder.app do
|
|
54
|
-
use Rack::ShowExceptions
|
|
55
|
-
run lambda { |env| raise "bzzzt" }
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
Rack::MockRequest.new(app).get("/").should.be.server_error
|
|
59
|
-
Rack::MockRequest.new(app).get("/").should.be.server_error
|
|
60
|
-
Rack::MockRequest.new(app).get("/").should.be.server_error
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
specify "apps are initialized once" do
|
|
64
|
-
app = Rack::Builder.new do
|
|
65
|
-
class AppClass
|
|
66
|
-
def initialize
|
|
67
|
-
@called = 0
|
|
68
|
-
end
|
|
69
|
-
def call(env)
|
|
70
|
-
raise "bzzzt" if @called > 0
|
|
71
|
-
@called += 1
|
|
72
|
-
[200, {'Content-Type' => 'text/plain'}, ['OK']]
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
use Rack::ShowExceptions
|
|
77
|
-
run AppClass.new
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
Rack::MockRequest.new(app).get("/").status.should.equal 200
|
|
81
|
-
Rack::MockRequest.new(app).get("/").should.be.server_error
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
end
|
data/test/spec_rack_camping.rb
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
require 'test/spec'
|
|
2
|
-
require 'stringio'
|
|
3
|
-
require 'uri'
|
|
4
|
-
|
|
5
|
-
begin
|
|
6
|
-
require 'rack/mock'
|
|
7
|
-
|
|
8
|
-
$-w, w = nil, $-w # yuck
|
|
9
|
-
|
|
10
|
-
# campings dependencies also don't pull this in anymore:
|
|
11
|
-
class Object;def meta_def m,&b;(class<<self;self;end).send:define_method,m,&b end end
|
|
12
|
-
|
|
13
|
-
require 'camping'
|
|
14
|
-
require 'rack/adapter/camping'
|
|
15
|
-
|
|
16
|
-
Camping.goes :CampApp
|
|
17
|
-
module CampApp
|
|
18
|
-
module Controllers
|
|
19
|
-
class HW < R('/')
|
|
20
|
-
def get
|
|
21
|
-
@headers["X-Served-By"] = URI("http://rack.rubyforge.org")
|
|
22
|
-
"Camping works!"
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def post
|
|
26
|
-
"Data: #{input.foo}"
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
$-w = w
|
|
32
|
-
|
|
33
|
-
context "Rack::Adapter::Camping" do
|
|
34
|
-
specify "works with GET" do
|
|
35
|
-
res = Rack::MockRequest.new(Rack::Adapter::Camping.new(CampApp)).
|
|
36
|
-
get("/")
|
|
37
|
-
|
|
38
|
-
res.should.be.ok
|
|
39
|
-
res["Content-Type"].should.equal "text/html"
|
|
40
|
-
res["X-Served-By"].should.equal "http://rack.rubyforge.org"
|
|
41
|
-
|
|
42
|
-
res.body.should.equal "Camping works!"
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
specify "works with POST" do
|
|
46
|
-
res = Rack::MockRequest.new(Rack::Adapter::Camping.new(CampApp)).
|
|
47
|
-
post("/", :input => "foo=bar")
|
|
48
|
-
|
|
49
|
-
res.should.be.ok
|
|
50
|
-
res.body.should.equal "Data: bar"
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
rescue LoadError
|
|
54
|
-
$stderr.puts "Skipping Rack::Adapter::Camping tests (Camping is required). `gem install camping` and try again."
|
|
55
|
-
end
|
data/test/spec_rack_chunked.rb
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
require 'rack/mock'
|
|
2
|
-
require 'rack/chunked'
|
|
3
|
-
require 'rack/utils'
|
|
4
|
-
|
|
5
|
-
context "Rack::Chunked" do
|
|
6
|
-
|
|
7
|
-
before do
|
|
8
|
-
@env = Rack::MockRequest.
|
|
9
|
-
env_for('/', 'HTTP_VERSION' => '1.1', 'REQUEST_METHOD' => 'GET')
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
specify 'chunks responses with no Content-Length' do
|
|
13
|
-
app = lambda { |env| [200, {}, ['Hello', ' ', 'World!']] }
|
|
14
|
-
response = Rack::MockResponse.new(*Rack::Chunked.new(app).call(@env))
|
|
15
|
-
response.headers.should.not.include 'Content-Length'
|
|
16
|
-
response.headers['Transfer-Encoding'].should.equal 'chunked'
|
|
17
|
-
response.body.should.equal "5\r\nHello\r\n1\r\n \r\n6\r\nWorld!\r\n0\r\n\r\n"
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
specify 'chunks empty bodies properly' do
|
|
21
|
-
app = lambda { |env| [200, {}, []] }
|
|
22
|
-
response = Rack::MockResponse.new(*Rack::Chunked.new(app).call(@env))
|
|
23
|
-
response.headers.should.not.include 'Content-Length'
|
|
24
|
-
response.headers['Transfer-Encoding'].should.equal 'chunked'
|
|
25
|
-
response.body.should.equal "0\r\n\r\n"
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
specify 'does not modify response when Content-Length header present' do
|
|
29
|
-
app = lambda { |env| [200, {'Content-Length'=>'12'}, ['Hello', ' ', 'World!']] }
|
|
30
|
-
status, headers, body = Rack::Chunked.new(app).call(@env)
|
|
31
|
-
status.should.equal 200
|
|
32
|
-
headers.should.not.include 'Transfer-Encoding'
|
|
33
|
-
headers.should.include 'Content-Length'
|
|
34
|
-
body.join.should.equal 'Hello World!'
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
specify 'does not modify response when client is HTTP/1.0' do
|
|
38
|
-
app = lambda { |env| [200, {}, ['Hello', ' ', 'World!']] }
|
|
39
|
-
@env['HTTP_VERSION'] = 'HTTP/1.0'
|
|
40
|
-
status, headers, body = Rack::Chunked.new(app).call(@env)
|
|
41
|
-
status.should.equal 200
|
|
42
|
-
headers.should.not.include 'Transfer-Encoding'
|
|
43
|
-
body.join.should.equal 'Hello World!'
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
specify 'does not modify response when Transfer-Encoding header already present' do
|
|
47
|
-
app = lambda { |env| [200, {'Transfer-Encoding' => 'identity'}, ['Hello', ' ', 'World!']] }
|
|
48
|
-
status, headers, body = Rack::Chunked.new(app).call(@env)
|
|
49
|
-
status.should.equal 200
|
|
50
|
-
headers['Transfer-Encoding'].should.equal 'identity'
|
|
51
|
-
body.join.should.equal 'Hello World!'
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
[100, 204, 304].each do |status_code|
|
|
55
|
-
specify "does not modify response when status code is #{status_code}" do
|
|
56
|
-
app = lambda { |env| [status_code, {}, []] }
|
|
57
|
-
status, headers, body = Rack::Chunked.new(app).call(@env)
|
|
58
|
-
status.should.equal status_code
|
|
59
|
-
headers.should.not.include 'Transfer-Encoding'
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
end
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
require 'test/spec'
|
|
2
|
-
require 'stringio'
|
|
3
|
-
|
|
4
|
-
require 'rack/commonlogger'
|
|
5
|
-
require 'rack/lobster'
|
|
6
|
-
require 'rack/mock'
|
|
7
|
-
|
|
8
|
-
context "Rack::CommonLogger" do
|
|
9
|
-
app = lambda { |env|
|
|
10
|
-
[200,
|
|
11
|
-
{"Content-Type" => "text/html", "Content-Length" => length.to_s},
|
|
12
|
-
[obj]]}
|
|
13
|
-
app_without_length = lambda { |env|
|
|
14
|
-
[200,
|
|
15
|
-
{"Content-Type" => "text/html"},
|
|
16
|
-
[]]}
|
|
17
|
-
app_with_zero_length = lambda { |env|
|
|
18
|
-
[200,
|
|
19
|
-
{"Content-Type" => "text/html", "Content-Length" => "0"},
|
|
20
|
-
[]]}
|
|
21
|
-
|
|
22
|
-
specify "should log to rack.errors by default" do
|
|
23
|
-
res = Rack::MockRequest.new(Rack::CommonLogger.new(app)).get("/")
|
|
24
|
-
|
|
25
|
-
res.errors.should.not.be.empty
|
|
26
|
-
res.errors.should =~ /"GET \/ " 200 #{length} /
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
specify "should log to anything with +write+" do
|
|
30
|
-
log = StringIO.new
|
|
31
|
-
res = Rack::MockRequest.new(Rack::CommonLogger.new(app, log)).get("/")
|
|
32
|
-
|
|
33
|
-
log.string.should =~ /"GET \/ " 200 #{length} /
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
specify "should log - content length if header is missing" do
|
|
37
|
-
res = Rack::MockRequest.new(Rack::CommonLogger.new(app_without_length)).get("/")
|
|
38
|
-
|
|
39
|
-
res.errors.should.not.be.empty
|
|
40
|
-
res.errors.should =~ /"GET \/ " 200 - /
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
specify "should log - content length if header is zero" do
|
|
44
|
-
res = Rack::MockRequest.new(Rack::CommonLogger.new(app_with_zero_length)).get("/")
|
|
45
|
-
|
|
46
|
-
res.errors.should.not.be.empty
|
|
47
|
-
res.errors.should =~ /"GET \/ " 200 - /
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def length
|
|
51
|
-
self.class.length
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def self.length
|
|
55
|
-
123
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def self.obj
|
|
59
|
-
"hello world"
|
|
60
|
-
end
|
|
61
|
-
end
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
require 'test/spec'
|
|
2
|
-
require 'time'
|
|
3
|
-
|
|
4
|
-
require 'rack/mock'
|
|
5
|
-
require 'rack/conditionalget'
|
|
6
|
-
|
|
7
|
-
context "Rack::ConditionalGet" do
|
|
8
|
-
specify "should set a 304 status and truncate body when If-Modified-Since hits" do
|
|
9
|
-
timestamp = Time.now.httpdate
|
|
10
|
-
app = Rack::ConditionalGet.new(lambda { |env|
|
|
11
|
-
[200, {'Last-Modified'=>timestamp}, ['TEST']] })
|
|
12
|
-
|
|
13
|
-
response = Rack::MockRequest.new(app).
|
|
14
|
-
get("/", 'HTTP_IF_MODIFIED_SINCE' => timestamp)
|
|
15
|
-
|
|
16
|
-
response.status.should.equal 304
|
|
17
|
-
response.body.should.be.empty
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
specify "should set a 304 status and truncate body when If-None-Match hits" do
|
|
21
|
-
app = Rack::ConditionalGet.new(lambda { |env|
|
|
22
|
-
[200, {'Etag'=>'1234'}, ['TEST']] })
|
|
23
|
-
|
|
24
|
-
response = Rack::MockRequest.new(app).
|
|
25
|
-
get("/", 'HTTP_IF_NONE_MATCH' => '1234')
|
|
26
|
-
|
|
27
|
-
response.status.should.equal 304
|
|
28
|
-
response.body.should.be.empty
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
specify "should not affect non-GET/HEAD requests" do
|
|
32
|
-
app = Rack::ConditionalGet.new(lambda { |env|
|
|
33
|
-
[200, {'Etag'=>'1234'}, ['TEST']] })
|
|
34
|
-
|
|
35
|
-
response = Rack::MockRequest.new(app).
|
|
36
|
-
post("/", 'HTTP_IF_NONE_MATCH' => '1234')
|
|
37
|
-
|
|
38
|
-
response.status.should.equal 200
|
|
39
|
-
response.body.should.equal 'TEST'
|
|
40
|
-
end
|
|
41
|
-
end
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
require 'rack/mock'
|
|
2
|
-
require 'rack/content_length'
|
|
3
|
-
|
|
4
|
-
context "Rack::ContentLength" do
|
|
5
|
-
specify "sets Content-Length on String bodies if none is set" do
|
|
6
|
-
app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, "Hello, World!"] }
|
|
7
|
-
response = Rack::ContentLength.new(app).call({})
|
|
8
|
-
response[1]['Content-Length'].should.equal '13'
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
specify "sets Content-Length on Array bodies if none is set" do
|
|
12
|
-
app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] }
|
|
13
|
-
response = Rack::ContentLength.new(app).call({})
|
|
14
|
-
response[1]['Content-Length'].should.equal '13'
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
specify "does not set Content-Length on variable length bodies" do
|
|
18
|
-
body = lambda { "Hello World!" }
|
|
19
|
-
def body.each ; yield call ; end
|
|
20
|
-
|
|
21
|
-
app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, body] }
|
|
22
|
-
response = Rack::ContentLength.new(app).call({})
|
|
23
|
-
response[1]['Content-Length'].should.be.nil
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
specify "does not change Content-Length if it is already set" do
|
|
27
|
-
app = lambda { |env| [200, {'Content-Type' => 'text/plain', 'Content-Length' => '1'}, "Hello, World!"] }
|
|
28
|
-
response = Rack::ContentLength.new(app).call({})
|
|
29
|
-
response[1]['Content-Length'].should.equal '1'
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
specify "does not set Content-Length on 304 responses" do
|
|
33
|
-
app = lambda { |env| [304, {'Content-Type' => 'text/plain'}, []] }
|
|
34
|
-
response = Rack::ContentLength.new(app).call({})
|
|
35
|
-
response[1]['Content-Length'].should.equal nil
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
specify "does not set Content-Length when Transfer-Encoding is chunked" do
|
|
39
|
-
app = lambda { |env| [200, {'Transfer-Encoding' => 'chunked'}, []] }
|
|
40
|
-
response = Rack::ContentLength.new(app).call({})
|
|
41
|
-
response[1]['Content-Length'].should.equal nil
|
|
42
|
-
end
|
|
43
|
-
end
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
require 'rack/mock'
|
|
2
|
-
require 'rack/content_type'
|
|
3
|
-
|
|
4
|
-
context "Rack::ContentType" do
|
|
5
|
-
specify "sets Content-Type to default text/html if none is set" do
|
|
6
|
-
app = lambda { |env| [200, {}, "Hello, World!"] }
|
|
7
|
-
status, headers, body = Rack::ContentType.new(app).call({})
|
|
8
|
-
headers['Content-Type'].should.equal 'text/html'
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
specify "sets Content-Type to chosen default if none is set" do
|
|
12
|
-
app = lambda { |env| [200, {}, "Hello, World!"] }
|
|
13
|
-
status, headers, body =
|
|
14
|
-
Rack::ContentType.new(app, 'application/octet-stream').call({})
|
|
15
|
-
headers['Content-Type'].should.equal 'application/octet-stream'
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
specify "does not change Content-Type if it is already set" do
|
|
19
|
-
app = lambda { |env| [200, {'Content-Type' => 'foo/bar'}, "Hello, World!"] }
|
|
20
|
-
status, headers, body = Rack::ContentType.new(app).call({})
|
|
21
|
-
headers['Content-Type'].should.equal 'foo/bar'
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
specify "case insensitive detection of Content-Type" do
|
|
25
|
-
app = lambda { |env| [200, {'CONTENT-Type' => 'foo/bar'}, "Hello, World!"] }
|
|
26
|
-
status, headers, body = Rack::ContentType.new(app).call({})
|
|
27
|
-
headers.to_a.select { |k,v| k.downcase == "content-type" }.
|
|
28
|
-
should.equal [["CONTENT-Type","foo/bar"]]
|
|
29
|
-
end
|
|
30
|
-
end
|
data/test/spec_rack_deflater.rb
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
require 'test/spec'
|
|
2
|
-
|
|
3
|
-
require 'rack/mock'
|
|
4
|
-
require 'rack/deflater'
|
|
5
|
-
require 'stringio'
|
|
6
|
-
require 'time' # for Time#httpdate
|
|
7
|
-
|
|
8
|
-
context "Rack::Deflater" do
|
|
9
|
-
def build_response(status, body, accept_encoding, headers = {})
|
|
10
|
-
body = [body] if body.respond_to? :to_str
|
|
11
|
-
app = lambda { |env| [status, {}, body] }
|
|
12
|
-
request = Rack::MockRequest.env_for("", headers.merge("HTTP_ACCEPT_ENCODING" => accept_encoding))
|
|
13
|
-
response = Rack::Deflater.new(app).call(request)
|
|
14
|
-
|
|
15
|
-
return response
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
specify "should be able to deflate bodies that respond to each" do
|
|
19
|
-
body = Object.new
|
|
20
|
-
class << body; def each; yield("foo"); yield("bar"); end; end
|
|
21
|
-
|
|
22
|
-
response = build_response(200, body, "deflate")
|
|
23
|
-
|
|
24
|
-
response[0].should.equal(200)
|
|
25
|
-
response[1].should.equal({
|
|
26
|
-
"Content-Encoding" => "deflate",
|
|
27
|
-
"Vary" => "Accept-Encoding"
|
|
28
|
-
})
|
|
29
|
-
buf = ''
|
|
30
|
-
response[2].each { |part| buf << part }
|
|
31
|
-
buf.should.equal("K\313\317OJ,\002\000")
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
# TODO: This is really just a special case of the above...
|
|
35
|
-
specify "should be able to deflate String bodies" do
|
|
36
|
-
response = build_response(200, "Hello world!", "deflate")
|
|
37
|
-
|
|
38
|
-
response[0].should.equal(200)
|
|
39
|
-
response[1].should.equal({
|
|
40
|
-
"Content-Encoding" => "deflate",
|
|
41
|
-
"Vary" => "Accept-Encoding"
|
|
42
|
-
})
|
|
43
|
-
buf = ''
|
|
44
|
-
response[2].each { |part| buf << part }
|
|
45
|
-
buf.should.equal("\363H\315\311\311W(\317/\312IQ\004\000")
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
specify "should be able to gzip bodies that respond to each" do
|
|
49
|
-
body = Object.new
|
|
50
|
-
class << body; def each; yield("foo"); yield("bar"); end; end
|
|
51
|
-
|
|
52
|
-
response = build_response(200, body, "gzip")
|
|
53
|
-
|
|
54
|
-
response[0].should.equal(200)
|
|
55
|
-
response[1].should.equal({
|
|
56
|
-
"Content-Encoding" => "gzip",
|
|
57
|
-
"Vary" => "Accept-Encoding",
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
buf = ''
|
|
61
|
-
response[2].each { |part| buf << part }
|
|
62
|
-
io = StringIO.new(buf)
|
|
63
|
-
gz = Zlib::GzipReader.new(io)
|
|
64
|
-
gz.read.should.equal("foobar")
|
|
65
|
-
gz.close
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
specify "should be able to fallback to no deflation" do
|
|
69
|
-
response = build_response(200, "Hello world!", "superzip")
|
|
70
|
-
|
|
71
|
-
response[0].should.equal(200)
|
|
72
|
-
response[1].should.equal({ "Vary" => "Accept-Encoding" })
|
|
73
|
-
response[2].should.equal(["Hello world!"])
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
specify "should be able to skip when there is no response entity body" do
|
|
77
|
-
response = build_response(304, [], "gzip")
|
|
78
|
-
|
|
79
|
-
response[0].should.equal(304)
|
|
80
|
-
response[1].should.equal({})
|
|
81
|
-
response[2].should.equal([])
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
specify "should handle the lack of an acceptable encoding" do
|
|
85
|
-
response1 = build_response(200, "Hello world!", "identity;q=0", "PATH_INFO" => "/")
|
|
86
|
-
response1[0].should.equal(406)
|
|
87
|
-
response1[1].should.equal({"Content-Type" => "text/plain", "Content-Length" => "71"})
|
|
88
|
-
response1[2].should.equal(["An acceptable encoding for the requested resource / could not be found."])
|
|
89
|
-
|
|
90
|
-
response2 = build_response(200, "Hello world!", "identity;q=0", "SCRIPT_NAME" => "/foo", "PATH_INFO" => "/bar")
|
|
91
|
-
response2[0].should.equal(406)
|
|
92
|
-
response2[1].should.equal({"Content-Type" => "text/plain", "Content-Length" => "78"})
|
|
93
|
-
response2[2].should.equal(["An acceptable encoding for the requested resource /foo/bar could not be found."])
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
specify "should handle gzip response with Last-Modified header" do
|
|
97
|
-
last_modified = Time.now.httpdate
|
|
98
|
-
|
|
99
|
-
app = lambda { |env| [200, { "Last-Modified" => last_modified }, ["Hello World!"]] }
|
|
100
|
-
request = Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => "gzip")
|
|
101
|
-
response = Rack::Deflater.new(app).call(request)
|
|
102
|
-
|
|
103
|
-
response[0].should.equal(200)
|
|
104
|
-
response[1].should.equal({
|
|
105
|
-
"Content-Encoding" => "gzip",
|
|
106
|
-
"Vary" => "Accept-Encoding",
|
|
107
|
-
"Last-Modified" => last_modified
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
buf = ''
|
|
111
|
-
response[2].each { |part| buf << part }
|
|
112
|
-
io = StringIO.new(buf)
|
|
113
|
-
gz = Zlib::GzipReader.new(io)
|
|
114
|
-
gz.read.should.equal("Hello World!")
|
|
115
|
-
gz.close
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
specify "should do nothing when no-transform Cache-Control directive present" do
|
|
119
|
-
app = lambda { |env| [200, {'Cache-Control' => 'no-transform'}, ['Hello World!']] }
|
|
120
|
-
request = Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => "gzip")
|
|
121
|
-
response = Rack::Deflater.new(app).call(request)
|
|
122
|
-
|
|
123
|
-
response[0].should.equal(200)
|
|
124
|
-
response[1].should.not.include "Content-Encoding"
|
|
125
|
-
response[2].join.should.equal("Hello World!")
|
|
126
|
-
end
|
|
127
|
-
end
|
data/test/spec_rack_etag.rb
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
require 'test/spec'
|
|
2
|
-
require 'rack/mock'
|
|
3
|
-
require 'rack/etag'
|
|
4
|
-
|
|
5
|
-
context "Rack::ETag" do
|
|
6
|
-
specify "sets ETag if none is set" do
|
|
7
|
-
app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] }
|
|
8
|
-
response = Rack::ETag.new(app).call({})
|
|
9
|
-
response[1]['ETag'].should.equal "\"65a8e27d8879283831b664bd8b7f0ad4\""
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
specify "does not change ETag if it is already set" do
|
|
13
|
-
app = lambda { |env| [200, {'Content-Type' => 'text/plain', 'ETag' => '"abc"'}, ["Hello, World!"]] }
|
|
14
|
-
response = Rack::ETag.new(app).call({})
|
|
15
|
-
response[1]['ETag'].should.equal "\"abc\""
|
|
16
|
-
end
|
|
17
|
-
end
|
data/test/spec_rack_file.rb
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
require 'test/spec'
|
|
2
|
-
|
|
3
|
-
require 'rack/file'
|
|
4
|
-
require 'rack/lint'
|
|
5
|
-
|
|
6
|
-
require 'rack/mock'
|
|
7
|
-
|
|
8
|
-
context "Rack::File" do
|
|
9
|
-
DOCROOT = File.expand_path(File.dirname(__FILE__)) unless defined? DOCROOT
|
|
10
|
-
|
|
11
|
-
specify "serves files" do
|
|
12
|
-
res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
|
|
13
|
-
get("/cgi/test")
|
|
14
|
-
|
|
15
|
-
res.should.be.ok
|
|
16
|
-
res.should =~ /ruby/
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
specify "sets Last-Modified header" do
|
|
20
|
-
res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
|
|
21
|
-
get("/cgi/test")
|
|
22
|
-
|
|
23
|
-
path = File.join(DOCROOT, "/cgi/test")
|
|
24
|
-
|
|
25
|
-
res.should.be.ok
|
|
26
|
-
res["Last-Modified"].should.equal File.mtime(path).httpdate
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
specify "serves files with URL encoded filenames" do
|
|
30
|
-
res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
|
|
31
|
-
get("/cgi/%74%65%73%74") # "/cgi/test"
|
|
32
|
-
|
|
33
|
-
res.should.be.ok
|
|
34
|
-
res.should =~ /ruby/
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
specify "does not allow directory traversal" do
|
|
38
|
-
res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
|
|
39
|
-
get("/cgi/../test")
|
|
40
|
-
|
|
41
|
-
res.should.be.forbidden
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
specify "does not allow directory traversal with encoded periods" do
|
|
45
|
-
res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
|
|
46
|
-
get("/%2E%2E/README")
|
|
47
|
-
|
|
48
|
-
res.should.be.forbidden
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
specify "404s if it can't find the file" do
|
|
52
|
-
res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
|
|
53
|
-
get("/cgi/blubb")
|
|
54
|
-
|
|
55
|
-
res.should.be.not_found
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
specify "detects SystemCallErrors" do
|
|
59
|
-
res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
|
|
60
|
-
get("/cgi")
|
|
61
|
-
|
|
62
|
-
res.should.be.not_found
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
specify "returns bodies that respond to #to_path" do
|
|
66
|
-
env = Rack::MockRequest.env_for("/cgi/test")
|
|
67
|
-
status, headers, body = Rack::File.new(DOCROOT).call(env)
|
|
68
|
-
|
|
69
|
-
path = File.join(DOCROOT, "/cgi/test")
|
|
70
|
-
|
|
71
|
-
status.should.equal 200
|
|
72
|
-
body.should.respond_to :to_path
|
|
73
|
-
body.to_path.should.equal path
|
|
74
|
-
end
|
|
75
|
-
end
|
data/test/spec_rack_handler.rb
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
require 'test/spec'
|
|
2
|
-
|
|
3
|
-
require 'rack/handler'
|
|
4
|
-
|
|
5
|
-
class Rack::Handler::Lobster; end
|
|
6
|
-
class RockLobster; end
|
|
7
|
-
|
|
8
|
-
context "Rack::Handler" do
|
|
9
|
-
specify "has registered default handlers" do
|
|
10
|
-
Rack::Handler.get('cgi').should.equal Rack::Handler::CGI
|
|
11
|
-
Rack::Handler.get('fastcgi').should.equal Rack::Handler::FastCGI
|
|
12
|
-
Rack::Handler.get('mongrel').should.equal Rack::Handler::Mongrel
|
|
13
|
-
Rack::Handler.get('webrick').should.equal Rack::Handler::WEBrick
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
specify "handler that doesn't exist should raise a NameError" do
|
|
17
|
-
lambda {
|
|
18
|
-
Rack::Handler.get('boom')
|
|
19
|
-
}.should.raise(NameError)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
specify "should get unregistered, but already required, handler by name" do
|
|
23
|
-
Rack::Handler.get('Lobster').should.equal Rack::Handler::Lobster
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
specify "should register custom handler" do
|
|
27
|
-
Rack::Handler.register('rock_lobster', 'RockLobster')
|
|
28
|
-
Rack::Handler.get('rock_lobster').should.equal RockLobster
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
specify "should not need registration for properly coded handlers even if not already required" do
|
|
32
|
-
begin
|
|
33
|
-
$:.push "test/unregistered_handler"
|
|
34
|
-
Rack::Handler.get('Unregistered').should.equal Rack::Handler::Unregistered
|
|
35
|
-
lambda {
|
|
36
|
-
Rack::Handler.get('UnRegistered')
|
|
37
|
-
}.should.raise(NameError)
|
|
38
|
-
Rack::Handler.get('UnregisteredLongOne').should.equal Rack::Handler::UnregisteredLongOne
|
|
39
|
-
ensure
|
|
40
|
-
$:.delete "test/unregistered_handler"
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
end
|
data/test/spec_rack_head.rb
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
require 'rack/head'
|
|
2
|
-
require 'rack/mock'
|
|
3
|
-
|
|
4
|
-
context "Rack::Head" do
|
|
5
|
-
def test_response(headers = {})
|
|
6
|
-
app = lambda { |env| [200, {"Content-type" => "test/plain", "Content-length" => "3"}, ["foo"]] }
|
|
7
|
-
request = Rack::MockRequest.env_for("/", headers)
|
|
8
|
-
response = Rack::Head.new(app).call(request)
|
|
9
|
-
|
|
10
|
-
return response
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
specify "passes GET, POST, PUT, DELETE, OPTIONS, TRACE requests" do
|
|
14
|
-
%w[GET POST PUT DELETE OPTIONS TRACE].each do |type|
|
|
15
|
-
resp = test_response("REQUEST_METHOD" => type)
|
|
16
|
-
|
|
17
|
-
resp[0].should.equal(200)
|
|
18
|
-
resp[1].should.equal({"Content-type" => "test/plain", "Content-length" => "3"})
|
|
19
|
-
resp[2].should.equal(["foo"])
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
specify "removes body from HEAD requests" do
|
|
24
|
-
resp = test_response("REQUEST_METHOD" => "HEAD")
|
|
25
|
-
|
|
26
|
-
resp[0].should.equal(200)
|
|
27
|
-
resp[1].should.equal({"Content-type" => "test/plain", "Content-length" => "3"})
|
|
28
|
-
resp[2].should.equal([])
|
|
29
|
-
end
|
|
30
|
-
end
|