summary refs log tree commit
diff options
context:
space:
mode:
authorJeremy Evans <code@jeremyevans.net>2022-05-10 11:06:01 -0700
committerJeremy Evans <code@jeremyevans.net>2022-05-25 08:33:11 -0700
commit214023ea7745018125fcad43edc9ff8b6cfb209a (patch)
tree184d7181028714ba513661938590cfee13788002
parentf7e05d530ca306d2d730671765d88587c75b4741 (diff)
downloadrack-214023ea7745018125fcad43edc9ff8b6cfb209a.tar.gz
Add remaining tests for 100% branch coverage
Coverage after this commit:

3273 relevant lines, 3273 lines covered and 0 lines missed. ( 100.0% )
1083 total branches, 1083 branches covered and 0 branches missed. ( 100.0% )
-rw-r--r--test/multipart/content_type_and_unknown_charset6
-rw-r--r--test/spec_auth_digest.rb5
-rw-r--r--test/spec_chunked.rb16
-rw-r--r--test/spec_common_logger.rb4
-rw-r--r--test/spec_conditional_get.rb27
-rw-r--r--test/spec_directory.rb24
-rw-r--r--test/spec_etag.rb6
-rw-r--r--test/spec_files.rb14
-rwxr-xr-xtest/spec_headers.rb1
-rw-r--r--test/spec_mock.rb11
-rw-r--r--test/spec_multipart.rb24
-rw-r--r--test/spec_rewindable_input.rb19
-rw-r--r--test/spec_sendfile.rb13
-rw-r--r--test/spec_static.rb8
-rw-r--r--test/spec_tempfile_reaper.rb16
-rw-r--r--test/spec_urlmap.rb5
16 files changed, 191 insertions, 8 deletions
diff --git a/test/multipart/content_type_and_unknown_charset b/test/multipart/content_type_and_unknown_charset
new file mode 100644
index 00000000..cf9c14c7
--- /dev/null
+++ b/test/multipart/content_type_and_unknown_charset
@@ -0,0 +1,6 @@
+--AaB03x
+content-disposition: form-data; name="text"
+content-type: text/plain; charset=foo; bar=baz
+
+contents
+--AaB03x--
diff --git a/test/spec_auth_digest.rb b/test/spec_auth_digest.rb
index e428375c..95df9c1b 100644
--- a/test/spec_auth_digest.rb
+++ b/test/spec_auth_digest.rb
@@ -273,6 +273,7 @@ describe Rack::Auth::Digest::MD5 do
     req.respond_to?(:nonce).must_equal true
     req.respond_to?(:a).must_equal true
     req.a.must_equal 'b'
+    proc{req.missing}.must_raise NoMethodError
     lambda { req.a(2) }.must_raise ArgumentError
   end
 
@@ -280,4 +281,8 @@ describe Rack::Auth::Digest::MD5 do
     Rack::Auth::Digest::Nonce.new.fresh?.must_equal true
     Rack::Auth::Digest::Nonce.new.stale?.must_equal false
   end
+
+  it 'Params.new can be called without a block' do
+    Rack::Auth::Digest::Params.new.must_be_instance_of(Rack::Auth::Digest::Params)
+  end
 end
diff --git a/test/spec_chunked.rb b/test/spec_chunked.rb
index ec3e4fb2..933a0817 100644
--- a/test/spec_chunked.rb
+++ b/test/spec_chunked.rb
@@ -52,6 +52,22 @@ describe Rack::Chunked do
     response.body.must_equal "5\r\nHello\r\n1\r\n \r\n6\r\nWorld!\r\n0\r\n\r\n"
   end
 
+  it 'avoid empty chunks' do
+    app = lambda { |env| [200, { "content-type" => "text/plain" }, ['Hello', '', 'World!']] }
+    response = Rack::MockResponse.new(*chunked(app).call(@env))
+    response.headers.wont_include 'content-length'
+    response.headers['transfer-encoding'].must_equal 'chunked'
+    response.body.must_equal "5\r\nHello\r\n6\r\nWorld!\r\n0\r\n\r\n"
+  end
+
+  it 'handles unclosable bodies' do
+    app = lambda { |env| [200, { "content-type" => "text/plain" }, ['Hello', '', 'World!']] }
+    response = Rack::MockResponse.new(*Rack::Chunked.new(app).call(@env))
+    response.headers.wont_include 'content-length'
+    response.headers['transfer-encoding'].must_equal 'chunked'
+    response.body.must_equal "5\r\nHello\r\n6\r\nWorld!\r\n0\r\n\r\n"
+  end
+
   it 'chunks empty bodies properly' do
     app = lambda { |env| [200, { "content-type" => "text/plain" }, []] }
     response = Rack::MockResponse.new(*chunked(app).call(@env))
diff --git a/test/spec_common_logger.rb b/test/spec_common_logger.rb
index 09995dda..56495e6c 100644
--- a/test/spec_common_logger.rb
+++ b/test/spec_common_logger.rb
@@ -91,10 +91,10 @@ describe Rack::CommonLogger do
   it "log in common log format" do
     log = StringIO.new
     with_mock_time do
-      Rack::MockRequest.new(Rack::CommonLogger.new(app, log)).get("/")
+      Rack::MockRequest.new(Rack::CommonLogger.new(app, log)).get("/", 'QUERY_STRING' => 'foo=bar')
     end
 
-    md = /- - - \[([^\]]+)\] "(\w+) \/ HTTP\/1\.1" (\d{3}) \d+ ([\d\.]+)/.match(log.string)
+    md = /- - - \[([^\]]+)\] "(\w+) \/\?foo=bar HTTP\/1\.1" (\d{3}) \d+ ([\d\.]+)/.match(log.string)
     md.wont_equal nil
     time, method, status, duration = *md.captures
     time.must_equal Time.at(0).strftime("%d/%b/%Y:%H:%M:%S %z")
diff --git a/test/spec_conditional_get.rb b/test/spec_conditional_get.rb
index 36da7412..8811bb27 100644
--- a/test/spec_conditional_get.rb
+++ b/test/spec_conditional_get.rb
@@ -37,6 +37,22 @@ describe Rack::ConditionalGet do
     response.body.must_be :empty?
   end
 
+  it "closes bodies" do
+    body = Object.new
+    def body.each; yield 'TEST' end
+    closed = false
+    body.define_singleton_method(:close){closed = true}
+    app = conditional_get(lambda { |env|
+      [200, { 'last-modified' => (Time.now - 3600).httpdate }, body] })
+
+    response = Rack::MockRequest.new(app).
+      get("/", 'HTTP_IF_MODIFIED_SINCE' => Time.now.httpdate)
+
+    response.status.must_equal 304
+    response.body.must_be :empty?
+    closed.must_equal true
+  end
+
   it "set a 304 status and truncate body when if-none-match hits" do
     app = conditional_get(lambda { |env|
       [200, { 'etag' => '1234' }, ['TEST']] })
@@ -59,6 +75,17 @@ describe Rack::ConditionalGet do
     response.body.must_be :empty?
   end
 
+  it "not set a 304 status if last-modified is too short" do
+    app = conditional_get(lambda { |env|
+      [200, { 'last-modified' => '1234', 'content-type' => 'text/plain' }, ['TEST']] })
+
+    response = Rack::MockRequest.new(app).
+      get("/", 'HTTP_IF_MODIFIED_SINCE' => Time.now.httpdate)
+
+    response.status.must_equal 200
+    response.body.must_equal 'TEST'
+  end
+
   it "not set a 304 status if if-modified-since hits but etag does not" do
     timestamp = Time.now.httpdate
     app = conditional_get(lambda { |env|
diff --git a/test/spec_directory.rb b/test/spec_directory.rb
index d7e90333..4e5db0e8 100644
--- a/test/spec_directory.rb
+++ b/test/spec_directory.rb
@@ -40,12 +40,34 @@ describe Rack::Directory do
     end
   end
 
+  it "serve root directory index" do
+    res = Rack::MockRequest.new(Rack::Lint.new(app)).
+      get("/")
+
+    res.must_be :ok?
+    assert_includes(res.body, '<html><head>')
+    assert_includes(res.body, "href='cgi")
+  end
+
   it "serve directory indices" do
     res = Rack::MockRequest.new(Rack::Lint.new(app)).
       get("/cgi/")
 
     res.must_be :ok?
-    assert_match(res, /<html><head>/)
+    assert_includes(res.body, '<html><head>')
+    assert_includes(res.body, "rackup_stub.rb")
+  end
+
+  it "return 404 for pipes" do
+    begin
+      File.mkfifo('test/cgi/fifo')
+      res = Rack::MockRequest.new(Rack::Lint.new(app)).
+        get("/cgi/fifo")
+
+      res.status.must_equal 404
+    ensure
+      File.delete('test/cgi/fifo')
+    end
   end
 
   it "serve directory indices with bad symlinks" do
diff --git a/test/spec_etag.rb b/test/spec_etag.rb
index ecd9383f..35fab258 100644
--- a/test/spec_etag.rb
+++ b/test/spec_etag.rb
@@ -76,6 +76,12 @@ describe Rack::ETag do
     response[1]['etag'].must_be_nil
   end
 
+  it "set handle empty body parts" do
+    app = lambda { |env| [200, { 'content-type' => 'text/plain' }, ["Hello", "", ", World!"]] }
+    response = etag(app).call(request)
+    response[1]['etag'].must_equal "W/\"dffd6021bb2bd5b0af676290809ec3a5\""
+  end
+
   it "not set etag if last-modified is set" do
     app = lambda { |env| [200, { 'content-type' => 'text/plain', 'last-modified' => Time.now.httpdate }, ["Hello, World!"]] }
     response = etag(app).call(request)
diff --git a/test/spec_files.rb b/test/spec_files.rb
index f74e5195..b65a7cfd 100644
--- a/test/spec_files.rb
+++ b/test/spec_files.rb
@@ -192,6 +192,20 @@ describe Rack::Files do
     res.body.must_equal "IS FILE! ***"
   end
 
+  it "handle case where file is truncated during request" do
+    env = Rack::MockRequest.env_for("/cgi/test")
+    env["HTTP_RANGE"] = "bytes=0-3300"
+    files = Class.new(Rack::Files) do
+      def filesize(_); 10000 end
+    end.new(DOCROOT)
+
+    res = Rack::MockResponse.new(*files.call(env))
+
+    res.status.must_equal 206
+    res["content-length"].must_equal "209"
+    res["content-range"].must_equal "bytes 0-3300/10000"
+  end
+
   it "return correct multiple byte ranges in body" do
     env = Rack::MockRequest.env_for("/cgi/test")
     env["HTTP_RANGE"] = "bytes=22-33, 60-80"
diff --git a/test/spec_headers.rb b/test/spec_headers.rb
index cabf00ff..dcd6296b 100755
--- a/test/spec_headers.rb
+++ b/test/spec_headers.rb
@@ -468,6 +468,7 @@ class RackHeadersTest < Minitest::Spec
     def test_slice
       assert_equal(Rack::Headers['Ab'=>'1', 'cD'=>'2', '3'=>'4'], @fh.slice('aB', 'Cd', '3'))
       assert_equal(Rack::Headers['AB'=>'1', 'CD'=>'2'], @fh.slice('Ab', 'CD'))
+      assert_equal(Rack::Headers[], @fh.slice('ad'))
       assert_equal('1', @fh.slice('AB', 'cd')['Ab'])
     end
 
diff --git a/test/spec_mock.rb b/test/spec_mock.rb
index 48dfde06..4bc57b18 100644
--- a/test/spec_mock.rb
+++ b/test/spec_mock.rb
@@ -49,6 +49,13 @@ describe Rack::MockRequest do
     env.must_include "rack.version"
   end
 
+  it "should handle a non-GET request with both :input and :params" do
+    env = Rack::MockRequest.env_for("/", method: :post, input: nil, params: {})
+    env["PATH_INFO"].must_equal "/"
+    env.must_be_kind_of Hash
+    env['rack.input'].read.must_equal ''
+  end
+
   it "return an environment with a path" do
     env = Rack::MockRequest.env_for("http://www.example.com/parse?location[]=1&location[]=2&age_group[]=2")
     env["QUERY_STRING"].must_equal "location[]=1&location[]=2&age_group[]=2"
@@ -429,6 +436,10 @@ describe Rack::MockResponse do
     res.body.must_equal 'hi'
   end
 
+  it "ignores plain strings passed as errors" do
+    Rack::MockResponse.new(200, {}, [], 'e').errors.must_be_nil
+  end
+
   it "optionally make Rack errors fatal" do
     lambda {
       Rack::MockRequest.new(app).get("/?error=foo", fatal: true)
diff --git a/test/spec_multipart.rb b/test/spec_multipart.rb
index 8e4a0c4c..12065aa1 100644
--- a/test/spec_multipart.rb
+++ b/test/spec_multipart.rb
@@ -527,6 +527,15 @@ content-type: image/jpeg\r
     files[:tempfile].read.must_equal "contents"
   end
 
+  it "raises RuntimeError for invalid file path" do
+    proc{Rack::Multipart::UploadedFile.new('non-existant')}.must_raise RuntimeError
+  end
+
+  it "supports uploading files in binary mode" do
+    Rack::Multipart::UploadedFile.new(multipart_file("file1.txt")).wont_be :binmode?
+    Rack::Multipart::UploadedFile.new(multipart_file("file1.txt"), binary: true).must_be :binmode?
+  end
+
   it "builds multipart body" do
     files = Rack::Multipart::UploadedFile.new(multipart_file("file1.txt"))
     data  = Rack::Multipart.build_multipart("submit-name" => "Larry", "files" => files)
@@ -660,6 +669,21 @@ content-type: image/jpeg\r
     end
   end
 
+  it "treat a multipart limit of 0 as no limit" do
+    begin
+      previous_limit = Rack::Utils.multipart_part_limit
+      Rack::Utils.multipart_part_limit = 0
+
+      env = Rack::MockRequest.env_for '/', multipart_fixture(:three_files_three_fields)
+      params = Rack::Multipart.parse_multipart(env)
+      params['reply'].must_equal 'yes'
+      params['to'].must_equal 'people'
+      params['from'].must_equal 'others'
+    ensure
+      Rack::Utils.multipart_part_limit = previous_limit
+    end
+  end
+
   it "reach a multipart limit" do
     begin
       previous_limit = Rack::Utils.multipart_part_limit
diff --git a/test/spec_rewindable_input.rb b/test/spec_rewindable_input.rb
index e672bcbb..603afbda 100644
--- a/test/spec_rewindable_input.rb
+++ b/test/spec_rewindable_input.rb
@@ -13,10 +13,6 @@ module RewindableTest
     @rio = Rack::RewindableInput.new(@io)
   end
 
-  class << self # HACK to get this running w/ as few changes as possible
-    alias_method :should, :it
-  end
-
   it "be able to handle to read()" do
     @rio.read.must_equal "hello world"
   end
@@ -44,17 +40,23 @@ module RewindableTest
   end
 
   it "rewind to the beginning when #rewind is called" do
-    @rio.read(1)
+    @rio.rewind
+    @rio.read(1).must_equal 'h'
     @rio.rewind
     @rio.read.must_equal "hello world"
   end
 
   it "be able to handle gets" do
     @rio.gets.must_equal "hello world"
+    @rio.rewind
+    @rio.gets.must_equal "hello world"
   end
 
   it "be able to handle size" do
     @rio.size.must_equal "hello world".size
+    @rio.size.must_equal "hello world".size
+    @rio.rewind
+    @rio.gets.must_equal "hello world"
   end
 
   it "be able to handle each" do
@@ -63,6 +65,13 @@ module RewindableTest
       array << data
     end
     array.must_equal ["hello world"]
+
+    @rio.rewind
+    array = []
+    @rio.each do |data|
+      array << data
+    end
+    array.must_equal ["hello world"]
   end
 
   it "not buffer into a Tempfile if no data has been read yet" do
diff --git a/test/spec_sendfile.rb b/test/spec_sendfile.rb
index ee6f222a..c0ef5e5d 100644
--- a/test/spec_sendfile.rb
+++ b/test/spec_sendfile.rb
@@ -67,6 +67,19 @@ describe Rack::Sendfile do
     end
   end
 
+  it "closes body when x-sendfile used" do
+    body = sendfile_body
+    closed = false
+    body.define_singleton_method(:close){closed = true}
+    request({'HTTP_X_SENDFILE_TYPE' => 'x-sendfile'}, body) do |response|
+      response.must_be :ok?
+      response.body.must_be :empty?
+      response.headers['content-length'].must_equal '0'
+      response.headers['x-sendfile'].must_equal File.join(Dir.tmpdir,  "rack_sendfile")
+    end
+    closed.must_equal true
+  end
+
   it "sets x-lighttpd-send-file response header and discards body" do
     request 'HTTP_X_SENDFILE_TYPE' => 'x-lighttpd-send-file' do |response|
       response.must_be :ok?
diff --git a/test/spec_static.rb b/test/spec_static.rb
index e102ded6..9cc33a42 100644
--- a/test/spec_static.rb
+++ b/test/spec_static.rb
@@ -151,6 +151,14 @@ describe Rack::Static do
     res.headers['content-type'].must_be_nil
   end
 
+  it "return 304 if gzipped file isn't modified since last serve" do
+    path = File.join(DOCROOT, "/cgi/test")
+    res = @gzip_request.get("/cgi/test", 'HTTP_IF_MODIFIED_SINCE' => File.mtime(path+'.gz').httpdate, 'HTTP_ACCEPT_ENCODING' => 'deflate, gzip')
+
+    res.status.must_equal 304
+    res.body.must_be :empty?
+  end
+
   it "supports serving fixed cache-control (legacy option)" do
     opts = OPTIONS.merge(cache_control: 'public')
     request = Rack::MockRequest.new(static(DummyApp.new, opts))
diff --git a/test/spec_tempfile_reaper.rb b/test/spec_tempfile_reaper.rb
index ce5d2c76..7e5c6790 100644
--- a/test/spec_tempfile_reaper.rb
+++ b/test/spec_tempfile_reaper.rb
@@ -84,4 +84,20 @@ describe Rack::TempfileReaper do
     tempfile1.closed.must_equal true
     tempfile2.closed.must_equal true
   end
+
+  it 'handle missing rack.tempfiles on normal response' do
+    app = lambda do |env|
+      env.delete('rack.tempfiles')
+      [200, {}, ['Hello, World!']]
+    end
+    call(app)[2].close
+  end
+
+  it 'handle missing rack.tempfiles on error' do
+    app = lambda do |env|
+      env.delete('rack.tempfiles')
+      raise 'Foo'
+    end
+    proc{call(app)}.must_raise RuntimeError
+  end
 end
diff --git a/test/spec_urlmap.rb b/test/spec_urlmap.rb
index 855e4541..e11c4e6d 100644
--- a/test/spec_urlmap.rb
+++ b/test/spec_urlmap.rb
@@ -44,6 +44,11 @@ describe Rack::URLMap do
     res["x-scriptname"].must_equal "/foo/bar"
     res["x-pathinfo"].must_equal ""
 
+    res = Rack::MockRequest.new(map).get("/foo/bard")
+    res.must_be :ok?
+    res["x-scriptname"].must_equal "/foo"
+    res["x-pathinfo"].must_equal "/bard"
+
     res = Rack::MockRequest.new(map).get("/foo/bar/")
     res.must_be :ok?
     res["x-scriptname"].must_equal "/foo/bar"