diff options
author | Jeremy Evans <code@jeremyevans.net> | 2020-01-29 13:19:36 -0800 |
---|---|---|
committer | Samuel Williams <samuel.williams@oriontransfer.co.nz> | 2020-01-30 12:26:19 +1300 |
commit | 67c19ac90c5165ca596165dc926dc055d6ac5d9f (patch) | |
tree | 2cf0fe278306a07fab75daf5d5db956746a679c2 | |
parent | c982d1a6f63b785a2921dd91ba908e0b1a706591 (diff) | |
download | rack-67c19ac90c5165ca596165dc926dc055d6ac5d9f.tar.gz |
Add remaining covering tests
3378 / 3378 LOC (100.0%) covered
-rw-r--r-- | lib/rack/files.rb | 4 | ||||
-rw-r--r-- | lib/rack/lobster.rb | 2 | ||||
-rw-r--r-- | test/spec_auth_digest.rb | 14 | ||||
-rw-r--r-- | test/spec_builder.rb | 13 | ||||
-rw-r--r-- | test/spec_cascade.rb | 7 | ||||
-rw-r--r-- | test/spec_chunked.rb | 13 | ||||
-rw-r--r-- | test/spec_directory.rb | 26 | ||||
-rw-r--r-- | test/spec_files.rb | 5 | ||||
-rw-r--r-- | test/spec_multipart.rb | 29 | ||||
-rw-r--r-- | test/spec_response.rb | 10 | ||||
-rw-r--r-- | test/spec_rewindable_input.rb | 21 | ||||
-rw-r--r-- | test/spec_show_exceptions.rb | 21 | ||||
-rw-r--r-- | test/spec_show_status.rb | 18 | ||||
-rw-r--r-- | test/spec_static.rb | 3 | ||||
-rw-r--r-- | test/spec_urlmap.rb | 6 | ||||
-rw-r--r-- | test/spec_utils.rb | 6 |
16 files changed, 192 insertions, 6 deletions
diff --git a/lib/rack/files.rb b/lib/rack/files.rb index c50f2937..542db693 100644 --- a/lib/rack/files.rb +++ b/lib/rack/files.rb @@ -45,7 +45,11 @@ module Rack available = begin ::File.file?(path) && ::File.readable?(path) rescue SystemCallError + # Not sure in what conditions this exception can occur, but this + # is a safe way to handle such an error. + # :nocov: false + # :nocov: end if available diff --git a/lib/rack/lobster.rb b/lib/rack/lobster.rb index 67345cec..b86a625d 100644 --- a/lib/rack/lobster.rb +++ b/lib/rack/lobster.rb @@ -61,8 +61,10 @@ module Rack end if $0 == __FILE__ + # :nocov: require_relative '../rack' Rack::Server.start( app: Rack::ShowExceptions.new(Rack::Lint.new(Rack::Lobster.new)), Port: 9292 ) + # :nocov: end diff --git a/test/spec_auth_digest.rb b/test/spec_auth_digest.rb index f0fec5fc..6e32152f 100644 --- a/test/spec_auth_digest.rb +++ b/test/spec_auth_digest.rb @@ -256,4 +256,18 @@ describe Rack::Auth::Digest::MD5 do app = Rack::Auth::Digest::MD5.new(unprotected_app, realm) { true } realm.must_equal app.realm end + + it 'Request#respond_to? and method_missing work as expected' do + req = Rack::Auth::Digest::Request.new({ 'HTTP_AUTHORIZATION' => 'a=b' }) + req.respond_to?(:banana).must_equal false + req.respond_to?(:nonce).must_equal true + req.respond_to?(:a).must_equal true + req.a.must_equal 'b' + lambda { req.a(2) }.must_raise ArgumentError + end + + it 'Nonce#fresh? should be the opposite of stale?' do + Rack::Auth::Digest::Nonce.new.fresh?.must_equal true + Rack::Auth::Digest::Nonce.new.stale?.must_equal false + end end diff --git a/test/spec_builder.rb b/test/spec_builder.rb index 9fc492bd..424e3314 100644 --- a/test/spec_builder.rb +++ b/test/spec_builder.rb @@ -38,6 +38,19 @@ describe Rack::Builder do Rack::MockRequest.new(app).get("/sub").body.to_s.must_equal 'sub' end + it "supports use when mapping" do + app = builder_to_app do + map '/sub' do + use Rack::ContentLength + run lambda { |inner_env| [200, { "Content-Type" => "text/plain" }, ['sub']] } + end + use Rack::ContentLength + run lambda { |inner_env| [200, { "Content-Type" => "text/plain" }, ['root']] } + end + Rack::MockRequest.new(app).get("/").headers['Content-Length'].must_equal '4' + Rack::MockRequest.new(app).get("/sub").headers['Content-Length'].must_equal '3' + end + it "doesn't dupe env even when mapping" do app = builder_to_app do use NothingMiddleware, noop: :noop diff --git a/test/spec_cascade.rb b/test/spec_cascade.rb index eb14ece0..299aaad2 100644 --- a/test/spec_cascade.rb +++ b/test/spec_cascade.rb @@ -31,6 +31,13 @@ describe Rack::Cascade do Rack::MockRequest.new(cascade).get("/cgi/../bla").must_be :not_found? end + it "include? returns whether app is included" do + cascade = Rack::Cascade.new([app1, app2]) + cascade.include?(app1).must_equal true + cascade.include?(app2).must_equal true + cascade.include?(app3).must_equal false + end + it "return 404 if empty" do Rack::MockRequest.new(cascade([])).get('/').must_be :not_found? end diff --git a/test/spec_chunked.rb b/test/spec_chunked.rb index b43803db..ceb7bdfb 100644 --- a/test/spec_chunked.rb +++ b/test/spec_chunked.rb @@ -54,6 +54,19 @@ describe Rack::Chunked do response.body.must_equal "0\r\n\r\n" end + it 'closes body' do + obj = Object.new + closed = false + def obj.each; yield 's' end + obj.define_singleton_method(:close) { closed = true } + app = lambda { |env| [200, { "Content-Type" => "text/plain" }, obj] } + response = Rack::MockRequest.new(Rack::Chunked.new(app)).get('/', @env) + response.headers.wont_include 'Content-Length' + response.headers['Transfer-Encoding'].must_equal 'chunked' + response.body.must_equal "1\r\ns\r\n0\r\n\r\n" + closed.must_equal true + end + it 'chunks encoded bodies properly' do body = ["\uFFFEHello", " ", "World"].map {|t| t.encode("UTF-16LE") } app = lambda { |env| [200, { "Content-Type" => "text/plain" }, body] } diff --git a/test/spec_directory.rb b/test/spec_directory.rb index e61a2a7c..9b913c85 100644 --- a/test/spec_directory.rb +++ b/test/spec_directory.rb @@ -40,6 +40,32 @@ describe Rack::Directory do assert_match(res, /<html><head>/) end + it "serve directory indices with bad symlinks" do + begin + File.symlink('foo', 'test/cgi/foo') + res = Rack::MockRequest.new(Rack::Lint.new(app)). + get("/cgi/") + + res.must_be :ok? + assert_match(res, /<html><head>/) + ensure + File.delete('test/cgi/foo') + end + end + + it "return 404 for unreadable directories" do + begin + File.write('test/cgi/unreadable', '') + File.chmod(0, 'test/cgi/unreadable') + res = Rack::MockRequest.new(Rack::Lint.new(app)). + get("/cgi/unreadable") + + res.status.must_equal 404 + ensure + File.delete('test/cgi/unreadable') + end + end + it "pass to app if file found" do res = Rack::MockRequest.new(Rack::Lint.new(app)). get("/cgi/test") diff --git a/test/spec_files.rb b/test/spec_files.rb index 8ee3c2c9..106019fe 100644 --- a/test/spec_files.rb +++ b/test/spec_files.rb @@ -45,6 +45,11 @@ describe Rack::Files do assert_match(res, /ruby/) end + it "does not serve directories" do + res = Rack::MockRequest.new(files(DOCROOT)).get("/cgi/assets") + res.status.must_equal 404 + end + it "set Last-Modified header" do res = Rack::MockRequest.new(files(DOCROOT)).get("/cgi/test") diff --git a/test/spec_multipart.rb b/test/spec_multipart.rb index 8cd3664f..717e0dc8 100644 --- a/test/spec_multipart.rb +++ b/test/spec_multipart.rb @@ -32,6 +32,17 @@ describe Rack::Multipart do params["text/plain; charset=US-ASCII"].must_equal ["contents"] end + it "parse multipart content when content type present but disposition is not when using IO" do + read, write = IO.pipe + env = multipart_fixture(:content_type_and_no_disposition) + write.write(env[:input].read) + write.close + env[:input] = read + env = Rack::MockRequest.env_for("/", multipart_fixture(:content_type_and_no_disposition)) + params = Rack::Multipart.parse_multipart(env) + params["text/plain; charset=US-ASCII"].must_equal ["contents"] + end + it "parse multipart content when content type present but filename is not" do env = Rack::MockRequest.env_for("/", multipart_fixture(:content_type_and_no_filename)) params = Rack::Multipart.parse_multipart(env) @@ -521,7 +532,7 @@ Content-Type: image/jpeg\r params["files"][:tempfile].read.must_equal "contents" end - it "builds nested multipart body" do + it "builds nested multipart body using array" do files = Rack::Multipart::UploadedFile.new(multipart_file("file1.txt")) data = Rack::Multipart.build_multipart("people" => [{ "submit-name" => "Larry", "files" => files }]) @@ -537,6 +548,22 @@ Content-Type: image/jpeg\r params["people"][0]["files"][:tempfile].read.must_equal "contents" end + it "builds nested multipart body using hash" do + files = Rack::Multipart::UploadedFile.new(multipart_file("file1.txt")) + data = Rack::Multipart.build_multipart("people" => { "foo" => { "submit-name" => "Larry", "files" => files } }) + + options = { + "CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x", + "CONTENT_LENGTH" => data.length.to_s, + :input => StringIO.new(data) + } + env = Rack::MockRequest.env_for("/", options) + params = Rack::Multipart.parse_multipart(env) + params["people"]["foo"]["submit-name"].must_equal "Larry" + params["people"]["foo"]["files"][:filename].must_equal "file1.txt" + params["people"]["foo"]["files"][:tempfile].read.must_equal "contents" + end + it "builds multipart body from StringIO" do files = Rack::Multipart::UploadedFile.new(io: StringIO.new('foo'), filename: 'bar.txt') data = Rack::Multipart.build_multipart("submit-name" => "Larry", "files" => files) diff --git a/test/spec_response.rb b/test/spec_response.rb index 999f4828..c27112fb 100644 --- a/test/spec_response.rb +++ b/test/spec_response.rb @@ -512,12 +512,14 @@ describe Rack::Response do it "provide access to the HTTP headers" do res = Rack::Response.new - res["Content-Type"] = "text/yaml" + res["Content-Type"] = "text/yaml; charset=UTF-8" res.must_include "Content-Type" - res.headers["Content-Type"].must_equal "text/yaml" - res["Content-Type"].must_equal "text/yaml" - res.content_type.must_equal "text/yaml" + res.headers["Content-Type"].must_equal "text/yaml; charset=UTF-8" + res["Content-Type"].must_equal "text/yaml; charset=UTF-8" + res.content_type.must_equal "text/yaml; charset=UTF-8" + res.media_type.must_equal "text/yaml" + res.media_type_params.must_equal "charset" => "UTF-8" res.content_length.must_be_nil res.location.must_be_nil end diff --git a/test/spec_rewindable_input.rb b/test/spec_rewindable_input.rb index 64d56673..4efe7dc2 100644 --- a/test/spec_rewindable_input.rb +++ b/test/spec_rewindable_input.rb @@ -77,6 +77,27 @@ module RewindableTest tempfile.must_be :closed? end + it "handle partial writes to tempfile" do + def @rio.filesystem_has_posix_semantics? + def @rewindable_io.write(buffer) + super(buffer[0..1]) + end + super + end + @rio.read(1) + tempfile = @rio.instance_variable_get(:@rewindable_io) + @rio.close + tempfile.must_be :closed? + end + + it "close the underlying tempfile upon calling #close when not using posix semantics" do + def @rio.filesystem_has_posix_semantics?; false end + @rio.read(1) + tempfile = @rio.instance_variable_get(:@rewindable_io) + @rio.close + tempfile.must_be :closed? + end + it "be possible to call #close when no data has been buffered yet" do @rio.close.must_be_nil end diff --git a/test/spec_show_exceptions.rb b/test/spec_show_exceptions.rb index 82924391..441599b4 100644 --- a/test/spec_show_exceptions.rb +++ b/test/spec_show_exceptions.rb @@ -26,6 +26,27 @@ describe Rack::ShowExceptions do assert_match(res, /No POST data/) end + it "handles exceptions with backtrace lines for files that are not readable" do + res = nil + + req = Rack::MockRequest.new( + show_exceptions( + lambda{|env| raise RuntimeError, "foo", ["nonexistant.rb:2:in `a': adf (RuntimeError)", "bad-backtrace"] } + )) + + res = req.get("/", "HTTP_ACCEPT" => "text/html") + + res.must_be :server_error? + res.status.must_equal 500 + + assert_includes(res.body, 'RuntimeError') + assert_includes(res.body, 'ShowExceptions') + assert_includes(res.body, 'No GET data') + assert_includes(res.body, 'No POST data') + assert_includes(res.body, 'nonexistant.rb') + refute_includes(res.body, 'bad-backtrace') + end + it "handles invalid POST data exceptions" do res = nil diff --git a/test/spec_show_status.rb b/test/spec_show_status.rb index 2c6a2244..486076b8 100644 --- a/test/spec_show_status.rb +++ b/test/spec_show_status.rb @@ -40,6 +40,24 @@ describe Rack::ShowStatus do assert_match(res, /too meta/) end + it "let the app provide additional information with non-String details" do + req = Rack::MockRequest.new( + show_status( + lambda{|env| + env["rack.showstatus.detail"] = ['gone too meta.'] + [404, { "Content-Type" => "text/plain", "Content-Length" => "0" }, []] + })) + + res = req.get("/", lint: true) + res.must_be :not_found? + res.wont_be_empty + + res["Content-Type"].must_equal "text/html" + assert_includes(res.body, '404') + assert_includes(res.body, 'Not Found') + assert_includes(res.body, '["gone too meta."]') + end + it "escape error" do detail = "<script>alert('hi \"')</script>" req = Rack::MockRequest.new( diff --git a/test/spec_static.rb b/test/spec_static.rb index 1f3ece9c..2a94d68c 100644 --- a/test/spec_static.rb +++ b/test/spec_static.rb @@ -159,7 +159,8 @@ describe Rack::Static do [%w(png jpg), { 'Cache-Control' => 'public, max-age=300' }], ['/cgi/assets/folder/', { 'Cache-Control' => 'public, max-age=400' }], ['cgi/assets/javascripts', { 'Cache-Control' => 'public, max-age=500' }], - [/\.(css|erb)\z/, { 'Cache-Control' => 'public, max-age=600' }] + [/\.(css|erb)\z/, { 'Cache-Control' => 'public, max-age=600' }], + [false, { 'Cache-Control' => 'public, max-age=600' }] ] } it "supports header rule :all" do diff --git a/test/spec_urlmap.rb b/test/spec_urlmap.rb index b29b829b..29af5587 100644 --- a/test/spec_urlmap.rb +++ b/test/spec_urlmap.rb @@ -242,4 +242,10 @@ describe Rack::URLMap do res["X-PathInfo"].must_equal "/" res["X-ScriptName"].must_equal "" end + + it "not allow locations unless they start with /" do + lambda do + Rack::URLMap.new("a/" => lambda { |env| }) + end.must_raise ArgumentError + end end diff --git a/test/spec_utils.rb b/test/spec_utils.rb index 5064be43..890cf710 100644 --- a/test/spec_utils.rb +++ b/test/spec_utils.rb @@ -104,6 +104,12 @@ describe Rack::Utils do Rack::Utils.parse_query(",foo=bar;,", ";,").must_equal "foo" => "bar" end + it "parse query strings correctly using arrays" do + Rack::Utils.parse_query("a[]=1").must_equal "a[]" => "1" + Rack::Utils.parse_query("a[]=1&a[]=2").must_equal "a[]" => ["1", "2"] + Rack::Utils.parse_query("a[]=1&a[]=2&a[]=3").must_equal "a[]" => ["1", "2", "3"] + end + it "not create infinite loops with cycle structures" do ex = { "foo" => nil } ex["foo"] = ex |