summary refs log tree commit
diff options
context:
space:
mode:
authorSamuel Williams <samuel.williams@oriontransfer.co.nz>2022-08-03 14:28:08 +1200
committerGitHub <noreply@github.com>2022-08-03 14:28:08 +1200
commit75fff851b46a2ada897eaa9dca74812592c39aa3 (patch)
tree8ffe6fcaf558f470233e1b5538e9bebece6979b6
parent3012643ea6a89fefe8cc0c68d4992531c367c906 (diff)
downloadrack-75fff851b46a2ada897eaa9dca74812592c39aa3.tar.gz
Move Rack::MockRequest/Response into dedicated files. (#1935)
* Move Rack::MockRequest/Response into dedicated files.

At some point I think we want to improve the implementation of `Rack::Mock`
in a separate gem. So let's be consistent with naming these files to avoid
clobbering namespace in the future.
-rw-r--r--lib/rack.rb4
-rw-r--r--lib/rack/mock.rb285
-rw-r--r--lib/rack/mock_request.rb166
-rw-r--r--lib/rack/mock_response.rb124
-rw-r--r--test/spec_auth_basic.rb2
-rw-r--r--test/spec_auth_digest.rb2
-rw-r--r--test/spec_builder.rb2
-rw-r--r--test/spec_cascade.rb2
-rw-r--r--test/spec_chunked.rb2
-rw-r--r--test/spec_common_logger.rb2
-rw-r--r--test/spec_conditional_get.rb2
-rw-r--r--test/spec_config.rb2
-rw-r--r--test/spec_content_length.rb2
-rw-r--r--test/spec_content_type.rb2
-rw-r--r--test/spec_deflater.rb2
-rw-r--r--test/spec_directory.rb2
-rw-r--r--test/spec_etag.rb2
-rw-r--r--test/spec_files.rb2
-rw-r--r--test/spec_head.rb2
-rwxr-xr-xtest/spec_lint.rb2
-rw-r--r--test/spec_lobster.rb2
-rw-r--r--test/spec_lock.rb6
-rw-r--r--test/spec_logger.rb2
-rw-r--r--test/spec_method_override.rb2
-rw-r--r--test/spec_mock_request.rb (renamed from test/spec_mock.rb)246
-rw-r--r--test/spec_mock_response.rb276
-rw-r--r--test/spec_multipart.rb2
-rw-r--r--test/spec_null_logger.rb2
-rw-r--r--test/spec_recursive.rb2
-rw-r--r--test/spec_request.rb2
-rw-r--r--test/spec_runtime.rb2
-rw-r--r--test/spec_sendfile.rb2
-rw-r--r--test/spec_server.rb2
-rw-r--r--test/spec_show_exceptions.rb2
-rw-r--r--test/spec_show_status.rb2
-rw-r--r--test/spec_static.rb2
-rw-r--r--test/spec_tempfile_reaper.rb2
-rw-r--r--test/spec_urlmap.rb2
-rw-r--r--test/spec_utils.rb2
39 files changed, 605 insertions, 566 deletions
diff --git a/lib/rack.rb b/lib/rack.rb
index 93646fc0..5b87ea1b 100644
--- a/lib/rack.rb
+++ b/lib/rack.rb
@@ -55,8 +55,8 @@ module Rack
   autoload :Utils, "rack/utils"
   autoload :Multipart, "rack/multipart"
 
-  autoload :MockRequest, "rack/mock"
-  autoload :MockResponse, "rack/mock"
+  autoload :MockRequest, "rack/mock_request"
+  autoload :MockResponse, "rack/mock_response"
 
   autoload :Request, "rack/request"
   autoload :Response, "rack/response"
diff --git a/lib/rack/mock.rb b/lib/rack/mock.rb
index c048e302..5e5c457c 100644
--- a/lib/rack/mock.rb
+++ b/lib/rack/mock.rb
@@ -1,286 +1,3 @@
 # frozen_string_literal: true
 
-require 'uri'
-require 'stringio'
-require 'cgi/cookie'
-require 'time'
-
-require_relative 'response'
-require_relative 'version'
-require_relative 'constants'
-require_relative 'headers'
-
-module Rack
-  # Rack::MockRequest helps testing your Rack application without
-  # actually using HTTP.
-  #
-  # After performing a request on a URL with get/post/put/patch/delete, it
-  # returns a MockResponse with useful helper methods for effective
-  # testing.
-  #
-  # You can pass a hash with additional configuration to the
-  # get/post/put/patch/delete.
-  # <tt>:input</tt>:: A String or IO-like to be used as rack.input.
-  # <tt>:fatal</tt>:: Raise a FatalWarning if the app writes to rack.errors.
-  # <tt>:lint</tt>:: If true, wrap the application in a Rack::Lint.
-
-  class MockRequest
-    class FatalWarning < RuntimeError
-    end
-
-    class FatalWarner
-      def puts(warning)
-        raise FatalWarning, warning
-      end
-
-      def write(warning)
-        raise FatalWarning, warning
-      end
-
-      def flush
-      end
-
-      def string
-        ""
-      end
-    end
-
-    DEFAULT_ENV = {
-      RACK_INPUT        => StringIO.new,
-      RACK_ERRORS       => StringIO.new,
-    }.freeze
-
-    def initialize(app)
-      @app = app
-    end
-
-    # Make a GET request and return a MockResponse. See #request.
-    def get(uri, opts = {})     request(GET, uri, opts)     end
-    # Make a POST request and return a MockResponse. See #request.
-    def post(uri, opts = {})    request(POST, uri, opts)    end
-    # Make a PUT request and return a MockResponse. See #request.
-    def put(uri, opts = {})     request(PUT, uri, opts)     end
-    # Make a PATCH request and return a MockResponse. See #request.
-    def patch(uri, opts = {})   request(PATCH, uri, opts)   end
-    # Make a DELETE request and return a MockResponse. See #request.
-    def delete(uri, opts = {})  request(DELETE, uri, opts)  end
-    # Make a HEAD request and return a MockResponse. See #request.
-    def head(uri, opts = {})    request(HEAD, uri, opts)    end
-    # Make an OPTIONS request and return a MockResponse. See #request.
-    def options(uri, opts = {}) request(OPTIONS, uri, opts) end
-
-    # Make a request using the given request method for the given
-    # uri to the rack application and return a MockResponse.
-    # Options given are passed to MockRequest.env_for.
-    def request(method = GET, uri = "", opts = {})
-      env = self.class.env_for(uri, opts.merge(method: method))
-
-      if opts[:lint]
-        app = Rack::Lint.new(@app)
-      else
-        app = @app
-      end
-
-      errors = env[RACK_ERRORS]
-      status, headers, body = app.call(env)
-      MockResponse.new(status, headers, body, errors)
-    ensure
-      body.close if body.respond_to?(:close)
-    end
-
-    # For historical reasons, we're pinning to RFC 2396.
-    # URI::Parser = URI::RFC2396_Parser
-    def self.parse_uri_rfc2396(uri)
-      @parser ||= URI::Parser.new
-      @parser.parse(uri)
-    end
-
-    # Return the Rack environment used for a request to +uri+.
-    # All options that are strings are added to the returned environment.
-    # Options:
-    # :fatal :: Whether to raise an exception if request outputs to rack.errors
-    # :input :: The rack.input to set
-    # :http_version :: The SERVER_PROTOCOL to set
-    # :method :: The HTTP request method to use
-    # :params :: The params to use
-    # :script_name :: The SCRIPT_NAME to set
-    def self.env_for(uri = "", opts = {})
-      uri = parse_uri_rfc2396(uri)
-      uri.path = "/#{uri.path}" unless uri.path[0] == ?/
-
-      env = DEFAULT_ENV.dup
-
-      env[REQUEST_METHOD]  = (opts[:method] ? opts[:method].to_s.upcase : GET).b
-      env[SERVER_NAME]     = (uri.host || "example.org").b
-      env[SERVER_PORT]     = (uri.port ? uri.port.to_s : "80").b
-      env[SERVER_PROTOCOL] = opts[:http_version] || 'HTTP/1.1'
-      env[QUERY_STRING]    = (uri.query.to_s).b
-      env[PATH_INFO]       = (uri.path).b
-      env[RACK_URL_SCHEME] = (uri.scheme || "http").b
-      env[HTTPS]           = (env[RACK_URL_SCHEME] == "https" ? "on" : "off").b
-
-      env[SCRIPT_NAME] = opts[:script_name] || ""
-
-      if opts[:fatal]
-        env[RACK_ERRORS] = FatalWarner.new
-      else
-        env[RACK_ERRORS] = StringIO.new
-      end
-
-      if params = opts[:params]
-        if env[REQUEST_METHOD] == GET
-          params = Utils.parse_nested_query(params) if params.is_a?(String)
-          params.update(Utils.parse_nested_query(env[QUERY_STRING]))
-          env[QUERY_STRING] = Utils.build_nested_query(params)
-        elsif !opts.has_key?(:input)
-          opts["CONTENT_TYPE"] = "application/x-www-form-urlencoded"
-          if params.is_a?(Hash)
-            if data = Rack::Multipart.build_multipart(params)
-              opts[:input] = data
-              opts["CONTENT_LENGTH"] ||= data.length.to_s
-              opts["CONTENT_TYPE"] = "multipart/form-data; boundary=#{Rack::Multipart::MULTIPART_BOUNDARY}"
-            else
-              opts[:input] = Utils.build_nested_query(params)
-            end
-          else
-            opts[:input] = params
-          end
-        end
-      end
-
-      opts[:input] ||= String.new
-      if String === opts[:input]
-        rack_input = StringIO.new(opts[:input])
-      else
-        rack_input = opts[:input]
-      end
-
-      rack_input.set_encoding(Encoding::BINARY)
-      env[RACK_INPUT] = rack_input
-
-      env["CONTENT_LENGTH"] ||= env[RACK_INPUT].size.to_s if env[RACK_INPUT].respond_to?(:size)
-
-      opts.each { |field, value|
-        env[field] = value  if String === field
-      }
-
-      env
-    end
-  end
-
-  # Rack::MockResponse provides useful helpers for testing your apps.
-  # Usually, you don't create the MockResponse on your own, but use
-  # MockRequest.
-
-  class MockResponse < Rack::Response
-    class << self
-      alias [] new
-    end
-
-    # Headers
-    attr_reader :original_headers, :cookies
-
-    # Errors
-    attr_accessor :errors
-
-    def initialize(status, headers, body, errors = nil)
-      @original_headers = headers
-
-      if errors
-        @errors = errors.string if errors.respond_to?(:string)
-      else
-        @errors = ""
-      end
-
-      super(body, status, headers)
-
-      @cookies = parse_cookies_from_header
-      buffered_body!
-    end
-
-    def =~(other)
-      body =~ other
-    end
-
-    def match(other)
-      body.match other
-    end
-
-    def body
-      # FIXME: apparently users of MockResponse expect the return value of
-      # MockResponse#body to be a string.  However, the real response object
-      # returns the body as a list.
-      #
-      # See spec_showstatus.rb:
-      #
-      #   should "not replace existing messages" do
-      #     ...
-      #     res.body.should == "foo!"
-      #   end
-      buffer = String.new
-
-      super.each do |chunk|
-        buffer << chunk
-      end
-
-      return buffer
-    end
-
-    def empty?
-      [201, 204, 304].include? status
-    end
-
-    def cookie(name)
-      cookies.fetch(name, nil)
-    end
-
-    private
-
-    def parse_cookies_from_header
-      cookies = Hash.new
-      if headers.has_key? 'set-cookie'
-        set_cookie_header = headers.fetch('set-cookie')
-        Array(set_cookie_header).each do |header_value|
-          header_value.split("\n").each do |cookie|
-            cookie_name, cookie_filling = cookie.split('=', 2)
-            cookie_attributes = identify_cookie_attributes cookie_filling
-            parsed_cookie = CGI::Cookie.new(
-              'name' => cookie_name.strip,
-              'value' => cookie_attributes.fetch('value'),
-              'path' => cookie_attributes.fetch('path', nil),
-              'domain' => cookie_attributes.fetch('domain', nil),
-              'expires' => cookie_attributes.fetch('expires', nil),
-              'secure' => cookie_attributes.fetch('secure', false)
-            )
-            cookies.store(cookie_name, parsed_cookie)
-          end
-        end
-      end
-      cookies
-    end
-
-    def identify_cookie_attributes(cookie_filling)
-      cookie_bits = cookie_filling.split(';')
-      cookie_attributes = Hash.new
-      cookie_attributes.store('value', cookie_bits[0].strip)
-      cookie_bits.drop(1).each do |bit|
-        if bit.include? '='
-          cookie_attribute, attribute_value = bit.split('=', 2)
-          cookie_attributes.store(cookie_attribute.strip.downcase, attribute_value.strip)
-        end
-        if bit.include? 'secure'
-          cookie_attributes.store('secure', true)
-        end
-      end
-
-      if cookie_attributes.key? 'max-age'
-        cookie_attributes.store('expires', Time.now + cookie_attributes['max-age'].to_i)
-      elsif cookie_attributes.key? 'expires'
-        cookie_attributes.store('expires', Time.httpdate(cookie_attributes['expires']))
-      end
-
-      cookie_attributes
-    end
-
-  end
-end
+require_relative 'mock_request'
diff --git a/lib/rack/mock_request.rb b/lib/rack/mock_request.rb
new file mode 100644
index 00000000..b6d7ef4f
--- /dev/null
+++ b/lib/rack/mock_request.rb
@@ -0,0 +1,166 @@
+# frozen_string_literal: true
+
+require 'uri'
+require 'stringio'
+
+require_relative 'constants'
+require_relative 'mock_response'
+
+module Rack
+  # Rack::MockRequest helps testing your Rack application without
+  # actually using HTTP.
+  #
+  # After performing a request on a URL with get/post/put/patch/delete, it
+  # returns a MockResponse with useful helper methods for effective
+  # testing.
+  #
+  # You can pass a hash with additional configuration to the
+  # get/post/put/patch/delete.
+  # <tt>:input</tt>:: A String or IO-like to be used as rack.input.
+  # <tt>:fatal</tt>:: Raise a FatalWarning if the app writes to rack.errors.
+  # <tt>:lint</tt>:: If true, wrap the application in a Rack::Lint.
+
+  class MockRequest
+    class FatalWarning < RuntimeError
+    end
+
+    class FatalWarner
+      def puts(warning)
+        raise FatalWarning, warning
+      end
+
+      def write(warning)
+        raise FatalWarning, warning
+      end
+
+      def flush
+      end
+
+      def string
+        ""
+      end
+    end
+
+    DEFAULT_ENV = {
+      RACK_INPUT        => StringIO.new,
+      RACK_ERRORS       => StringIO.new,
+    }.freeze
+
+    def initialize(app)
+      @app = app
+    end
+
+    # Make a GET request and return a MockResponse. See #request.
+    def get(uri, opts = {})     request(GET, uri, opts)     end
+    # Make a POST request and return a MockResponse. See #request.
+    def post(uri, opts = {})    request(POST, uri, opts)    end
+    # Make a PUT request and return a MockResponse. See #request.
+    def put(uri, opts = {})     request(PUT, uri, opts)     end
+    # Make a PATCH request and return a MockResponse. See #request.
+    def patch(uri, opts = {})   request(PATCH, uri, opts)   end
+    # Make a DELETE request and return a MockResponse. See #request.
+    def delete(uri, opts = {})  request(DELETE, uri, opts)  end
+    # Make a HEAD request and return a MockResponse. See #request.
+    def head(uri, opts = {})    request(HEAD, uri, opts)    end
+    # Make an OPTIONS request and return a MockResponse. See #request.
+    def options(uri, opts = {}) request(OPTIONS, uri, opts) end
+
+    # Make a request using the given request method for the given
+    # uri to the rack application and return a MockResponse.
+    # Options given are passed to MockRequest.env_for.
+    def request(method = GET, uri = "", opts = {})
+      env = self.class.env_for(uri, opts.merge(method: method))
+
+      if opts[:lint]
+        app = Rack::Lint.new(@app)
+      else
+        app = @app
+      end
+
+      errors = env[RACK_ERRORS]
+      status, headers, body = app.call(env)
+      MockResponse.new(status, headers, body, errors)
+    ensure
+      body.close if body.respond_to?(:close)
+    end
+
+    # For historical reasons, we're pinning to RFC 2396.
+    # URI::Parser = URI::RFC2396_Parser
+    def self.parse_uri_rfc2396(uri)
+      @parser ||= URI::Parser.new
+      @parser.parse(uri)
+    end
+
+    # Return the Rack environment used for a request to +uri+.
+    # All options that are strings are added to the returned environment.
+    # Options:
+    # :fatal :: Whether to raise an exception if request outputs to rack.errors
+    # :input :: The rack.input to set
+    # :http_version :: The SERVER_PROTOCOL to set
+    # :method :: The HTTP request method to use
+    # :params :: The params to use
+    # :script_name :: The SCRIPT_NAME to set
+    def self.env_for(uri = "", opts = {})
+      uri = parse_uri_rfc2396(uri)
+      uri.path = "/#{uri.path}" unless uri.path[0] == ?/
+
+      env = DEFAULT_ENV.dup
+
+      env[REQUEST_METHOD]  = (opts[:method] ? opts[:method].to_s.upcase : GET).b
+      env[SERVER_NAME]     = (uri.host || "example.org").b
+      env[SERVER_PORT]     = (uri.port ? uri.port.to_s : "80").b
+      env[SERVER_PROTOCOL] = opts[:http_version] || 'HTTP/1.1'
+      env[QUERY_STRING]    = (uri.query.to_s).b
+      env[PATH_INFO]       = (uri.path).b
+      env[RACK_URL_SCHEME] = (uri.scheme || "http").b
+      env[HTTPS]           = (env[RACK_URL_SCHEME] == "https" ? "on" : "off").b
+
+      env[SCRIPT_NAME] = opts[:script_name] || ""
+
+      if opts[:fatal]
+        env[RACK_ERRORS] = FatalWarner.new
+      else
+        env[RACK_ERRORS] = StringIO.new
+      end
+
+      if params = opts[:params]
+        if env[REQUEST_METHOD] == GET
+          params = Utils.parse_nested_query(params) if params.is_a?(String)
+          params.update(Utils.parse_nested_query(env[QUERY_STRING]))
+          env[QUERY_STRING] = Utils.build_nested_query(params)
+        elsif !opts.has_key?(:input)
+          opts["CONTENT_TYPE"] = "application/x-www-form-urlencoded"
+          if params.is_a?(Hash)
+            if data = Rack::Multipart.build_multipart(params)
+              opts[:input] = data
+              opts["CONTENT_LENGTH"] ||= data.length.to_s
+              opts["CONTENT_TYPE"] = "multipart/form-data; boundary=#{Rack::Multipart::MULTIPART_BOUNDARY}"
+            else
+              opts[:input] = Utils.build_nested_query(params)
+            end
+          else
+            opts[:input] = params
+          end
+        end
+      end
+
+      opts[:input] ||= String.new
+      if String === opts[:input]
+        rack_input = StringIO.new(opts[:input])
+      else
+        rack_input = opts[:input]
+      end
+
+      rack_input.set_encoding(Encoding::BINARY)
+      env[RACK_INPUT] = rack_input
+
+      env["CONTENT_LENGTH"] ||= env[RACK_INPUT].size.to_s if env[RACK_INPUT].respond_to?(:size)
+
+      opts.each { |field, value|
+        env[field] = value  if String === field
+      }
+
+      env
+    end
+  end
+end
diff --git a/lib/rack/mock_response.rb b/lib/rack/mock_response.rb
new file mode 100644
index 00000000..3142f68a
--- /dev/null
+++ b/lib/rack/mock_response.rb
@@ -0,0 +1,124 @@
+# frozen_string_literal: true
+
+require 'cgi/cookie'
+require 'time'
+
+require_relative 'response'
+
+module Rack
+  # Rack::MockResponse provides useful helpers for testing your apps.
+  # Usually, you don't create the MockResponse on your own, but use
+  # MockRequest.
+
+  class MockResponse < Rack::Response
+    class << self
+      alias [] new
+    end
+
+    # Headers
+    attr_reader :original_headers, :cookies
+
+    # Errors
+    attr_accessor :errors
+
+    def initialize(status, headers, body, errors = nil)
+      @original_headers = headers
+
+      if errors
+        @errors = errors.string if errors.respond_to?(:string)
+      else
+        @errors = ""
+      end
+
+      super(body, status, headers)
+
+      @cookies = parse_cookies_from_header
+      buffered_body!
+    end
+
+    def =~(other)
+      body =~ other
+    end
+
+    def match(other)
+      body.match other
+    end
+
+    def body
+      # FIXME: apparently users of MockResponse expect the return value of
+      # MockResponse#body to be a string.  However, the real response object
+      # returns the body as a list.
+      #
+      # See spec_showstatus.rb:
+      #
+      #   should "not replace existing messages" do
+      #     ...
+      #     res.body.should == "foo!"
+      #   end
+      buffer = String.new
+
+      super.each do |chunk|
+        buffer << chunk
+      end
+
+      return buffer
+    end
+
+    def empty?
+      [201, 204, 304].include? status
+    end
+
+    def cookie(name)
+      cookies.fetch(name, nil)
+    end
+
+    private
+
+    def parse_cookies_from_header
+      cookies = Hash.new
+      if headers.has_key? 'set-cookie'
+        set_cookie_header = headers.fetch('set-cookie')
+        Array(set_cookie_header).each do |header_value|
+          header_value.split("\n").each do |cookie|
+            cookie_name, cookie_filling = cookie.split('=', 2)
+            cookie_attributes = identify_cookie_attributes cookie_filling
+            parsed_cookie = CGI::Cookie.new(
+              'name' => cookie_name.strip,
+              'value' => cookie_attributes.fetch('value'),
+              'path' => cookie_attributes.fetch('path', nil),
+              'domain' => cookie_attributes.fetch('domain', nil),
+              'expires' => cookie_attributes.fetch('expires', nil),
+              'secure' => cookie_attributes.fetch('secure', false)
+            )
+            cookies.store(cookie_name, parsed_cookie)
+          end
+        end
+      end
+      cookies
+    end
+
+    def identify_cookie_attributes(cookie_filling)
+      cookie_bits = cookie_filling.split(';')
+      cookie_attributes = Hash.new
+      cookie_attributes.store('value', cookie_bits[0].strip)
+      cookie_bits.drop(1).each do |bit|
+        if bit.include? '='
+          cookie_attribute, attribute_value = bit.split('=', 2)
+          cookie_attributes.store(cookie_attribute.strip.downcase, attribute_value.strip)
+        end
+        if bit.include? 'secure'
+          cookie_attributes.store('secure', true)
+        end
+      end
+
+      if cookie_attributes.key? 'max-age'
+        cookie_attributes.store('expires', Time.now + cookie_attributes['max-age'].to_i)
+      elsif cookie_attributes.key? 'expires'
+        cookie_attributes.store('expires', Time.httpdate(cookie_attributes['expires']))
+      end
+
+      cookie_attributes
+    end
+
+  end
+end
diff --git a/test/spec_auth_basic.rb b/test/spec_auth_basic.rb
index f1b09c88..ee700049 100644
--- a/test/spec_auth_basic.rb
+++ b/test/spec_auth_basic.rb
@@ -4,7 +4,7 @@ require_relative 'helper'
 
 separate_testing do
   require_relative '../lib/rack/auth/basic'
-  require_relative '../lib/rack/mock'
+  require_relative '../lib/rack/mock_request'
   require_relative '../lib/rack/lint'
 end
 
diff --git a/test/spec_auth_digest.rb b/test/spec_auth_digest.rb
index 95df9c1b..3a8981c5 100644
--- a/test/spec_auth_digest.rb
+++ b/test/spec_auth_digest.rb
@@ -7,7 +7,7 @@ separate_testing do
   require_relative '../lib/rack/auth/digest/nonce'
   require_relative '../lib/rack/auth/digest/params'
   require_relative '../lib/rack/lint'
-  require_relative '../lib/rack/mock'
+  require_relative '../lib/rack/mock_request'
   require_relative '../lib/rack/urlmap'
   require_relative '../lib/rack/method_override'
 end
diff --git a/test/spec_builder.rb b/test/spec_builder.rb
index 3b02bcc1..52bdf76c 100644
--- a/test/spec_builder.rb
+++ b/test/spec_builder.rb
@@ -5,7 +5,7 @@ require_relative 'helper'
 separate_testing do
   require_relative '../lib/rack/builder'
   require_relative '../lib/rack/lint'
-  require_relative '../lib/rack/mock'
+  require_relative '../lib/rack/mock_request'
   require_relative '../lib/rack/content_length'
   require_relative '../lib/rack/show_exceptions'
   require_relative '../lib/rack/auth/basic'
diff --git a/test/spec_cascade.rb b/test/spec_cascade.rb
index adafdb13..50dd2805 100644
--- a/test/spec_cascade.rb
+++ b/test/spec_cascade.rb
@@ -5,7 +5,7 @@ require_relative 'helper'
 separate_testing do
   require_relative '../lib/rack/cascade'
   require_relative '../lib/rack/lint'
-  require_relative '../lib/rack/mock'
+  require_relative '../lib/rack/mock_request'
   require_relative '../lib/rack/urlmap'
   require_relative '../lib/rack/files'
 end
diff --git a/test/spec_chunked.rb b/test/spec_chunked.rb
index 933a0817..4ba4eefb 100644
--- a/test/spec_chunked.rb
+++ b/test/spec_chunked.rb
@@ -5,7 +5,7 @@ require_relative 'helper'
 separate_testing do
   require_relative '../lib/rack/chunked'
   require_relative '../lib/rack/lint'
-  require_relative '../lib/rack/mock'
+  require_relative '../lib/rack/mock_request'
 end
 
 describe Rack::Chunked do
diff --git a/test/spec_common_logger.rb b/test/spec_common_logger.rb
index be020073..595debd4 100644
--- a/test/spec_common_logger.rb
+++ b/test/spec_common_logger.rb
@@ -6,7 +6,7 @@ require 'logger'
 separate_testing do
   require_relative '../lib/rack/common_logger'
   require_relative '../lib/rack/lint'
-  require_relative '../lib/rack/mock'
+  require_relative '../lib/rack/mock_request'
 end
 
 describe Rack::CommonLogger do
diff --git a/test/spec_conditional_get.rb b/test/spec_conditional_get.rb
index 8811bb27..d028b3de 100644
--- a/test/spec_conditional_get.rb
+++ b/test/spec_conditional_get.rb
@@ -6,7 +6,7 @@ require 'time'
 separate_testing do
   require_relative '../lib/rack/conditional_get'
   require_relative '../lib/rack/lint'
-  require_relative '../lib/rack/mock'
+  require_relative '../lib/rack/mock_request'
 end
 
 describe Rack::ConditionalGet do
diff --git a/test/spec_config.rb b/test/spec_config.rb
index 8ef28965..ce5f7777 100644
--- a/test/spec_config.rb
+++ b/test/spec_config.rb
@@ -6,7 +6,7 @@ separate_testing do
   require_relative '../lib/rack/config'
   require_relative '../lib/rack/lint'
   require_relative '../lib/rack/builder'
-  require_relative '../lib/rack/mock'
+  require_relative '../lib/rack/mock_request'
 end
 
 describe Rack::Config do
diff --git a/test/spec_content_length.rb b/test/spec_content_length.rb
index 8f7bba9c..cffe8cb4 100644
--- a/test/spec_content_length.rb
+++ b/test/spec_content_length.rb
@@ -5,7 +5,7 @@ require_relative 'helper'
 separate_testing do
   require_relative '../lib/rack/content_length'
   require_relative '../lib/rack/lint'
-  require_relative '../lib/rack/mock'
+  require_relative '../lib/rack/mock_request'
 end
 
 describe Rack::ContentLength do
diff --git a/test/spec_content_type.rb b/test/spec_content_type.rb
index 4808376a..fff68708 100644
--- a/test/spec_content_type.rb
+++ b/test/spec_content_type.rb
@@ -5,7 +5,7 @@ require_relative 'helper'
 separate_testing do
   require_relative '../lib/rack/content_type'
   require_relative '../lib/rack/lint'
-  require_relative '../lib/rack/mock'
+  require_relative '../lib/rack/mock_request'
 end
 
 describe Rack::ContentType do
diff --git a/test/spec_deflater.rb b/test/spec_deflater.rb
index 8d95cfaa..87c3c4f0 100644
--- a/test/spec_deflater.rb
+++ b/test/spec_deflater.rb
@@ -7,7 +7,7 @@ require 'zlib'
 separate_testing do
   require_relative '../lib/rack/deflater'
   require_relative '../lib/rack/lint'
-  require_relative '../lib/rack/mock'
+  require_relative '../lib/rack/mock_request'
 end
 
 describe Rack::Deflater do
diff --git a/test/spec_directory.rb b/test/spec_directory.rb
index 4e5db0e8..fcd1f16c 100644
--- a/test/spec_directory.rb
+++ b/test/spec_directory.rb
@@ -7,7 +7,7 @@ require 'fileutils'
 separate_testing do
   require_relative '../lib/rack/directory'
   require_relative '../lib/rack/lint'
-  require_relative '../lib/rack/mock'
+  require_relative '../lib/rack/mock_request'
   require_relative '../lib/rack/utils'
   require_relative '../lib/rack/builder'
 end
diff --git a/test/spec_etag.rb b/test/spec_etag.rb
index 35fab258..e1670dfa 100644
--- a/test/spec_etag.rb
+++ b/test/spec_etag.rb
@@ -6,7 +6,7 @@ require 'time'
 separate_testing do
   require_relative '../lib/rack/etag'
   require_relative '../lib/rack/lint'
-  require_relative '../lib/rack/mock'
+  require_relative '../lib/rack/mock_request'
 end
 
 describe Rack::ETag do
diff --git a/test/spec_files.rb b/test/spec_files.rb
index b65a7cfd..5b5a09d8 100644
--- a/test/spec_files.rb
+++ b/test/spec_files.rb
@@ -5,7 +5,7 @@ require_relative 'helper'
 separate_testing do
   require_relative '../lib/rack/files'
   require_relative '../lib/rack/lint'
-  require_relative '../lib/rack/mock'
+  require_relative '../lib/rack/mock_request'
 end
 
 describe Rack::Files do
diff --git a/test/spec_head.rb b/test/spec_head.rb
index d09eb8f9..4ab90c2e 100644
--- a/test/spec_head.rb
+++ b/test/spec_head.rb
@@ -5,7 +5,7 @@ require_relative 'helper'
 separate_testing do
   require_relative '../lib/rack/head'
   require_relative '../lib/rack/lint'
-  require_relative '../lib/rack/mock'
+  require_relative '../lib/rack/mock_request'
 end
 
 describe Rack::Head do
diff --git a/test/spec_lint.rb b/test/spec_lint.rb
index 6a735b0d..6ad2ca78 100755
--- a/test/spec_lint.rb
+++ b/test/spec_lint.rb
@@ -5,7 +5,7 @@ require 'tempfile'
 
 separate_testing do
   require_relative '../lib/rack/lint'
-  require_relative '../lib/rack/mock'
+  require_relative '../lib/rack/mock_request'
 end
 
 describe Rack::Lint do
diff --git a/test/spec_lobster.rb b/test/spec_lobster.rb
index 71584d4b..0db9cd8c 100644
--- a/test/spec_lobster.rb
+++ b/test/spec_lobster.rb
@@ -5,7 +5,7 @@ require_relative 'helper'
 require_relative '../lib/rack/lobster'
 separate_testing do
   require_relative '../lib/rack/lint'
-  require_relative '../lib/rack/mock'
+  require_relative '../lib/rack/mock_request'
 end
 
 module LobsterHelpers
diff --git a/test/spec_lock.rb b/test/spec_lock.rb
index 3980fc98..dfc07449 100644
--- a/test/spec_lock.rb
+++ b/test/spec_lock.rb
@@ -4,7 +4,7 @@ require_relative 'helper'
 
 separate_testing do
   require_relative '../lib/rack/lock'
-  require_relative '../lib/rack/mock'
+  require_relative '../lib/rack/mock_request'
   require_relative '../lib/rack/lint'
 end
 
@@ -84,11 +84,11 @@ describe Rack::Lock do
 
   it 'call super on close' do
     env      = Rack::MockRequest.env_for("/")
-    response = Class.new {
+    response = Class.new do
       attr_accessor :close_called
       def initialize; @close_called = false; end
       def close; @close_called = true; end
-    }.new
+    end.new
 
     app = lock_app(lambda { |inner_env| [200, { "content-type" => "text/plain" }, response] })
     app.call(env)
diff --git a/test/spec_logger.rb b/test/spec_logger.rb
index 26d05def..ab20d363 100644
--- a/test/spec_logger.rb
+++ b/test/spec_logger.rb
@@ -5,7 +5,7 @@ require_relative 'helper'
 separate_testing do
   require_relative '../lib/rack/logger'
   require_relative '../lib/rack/lint'
-  require_relative '../lib/rack/mock'
+  require_relative '../lib/rack/mock_request'
 end
 
 describe Rack::Logger do
diff --git a/test/spec_method_override.rb b/test/spec_method_override.rb
index b36fc32d..d0538a70 100644
--- a/test/spec_method_override.rb
+++ b/test/spec_method_override.rb
@@ -5,7 +5,7 @@ require_relative 'helper'
 separate_testing do
   require_relative '../lib/rack/method_override'
   require_relative '../lib/rack/lint'
-  require_relative '../lib/rack/mock'
+  require_relative '../lib/rack/mock_request'
 end
 
 describe Rack::MethodOverride do
diff --git a/test/spec_mock.rb b/test/spec_mock_request.rb
index 1eb4fc43..889c8a9f 100644
--- a/test/spec_mock.rb
+++ b/test/spec_mock_request.rb
@@ -5,12 +5,9 @@ require 'yaml'
 require_relative 'psych_fix'
 
 separate_testing do
-  require_relative '../lib/rack/mock'
+  require_relative '../lib/rack/mock_request'
   require_relative '../lib/rack/lint'
   require_relative '../lib/rack/request'
-  require_relative '../lib/rack/response'
-  require_relative '../lib/rack/multipart'
-  require_relative '../lib/rack/constants'
   require_relative '../lib/rack/body_proxy'
 end
 
@@ -274,244 +271,3 @@ describe Rack::MockRequest do
     end
   end
 end
-
-describe Rack::MockResponse do
-  it 'has standard constructor' do
-    headers = { "header" => "value" }
-    body = ["body"]
-
-    response = Rack::MockResponse[200, headers, body]
-
-    response.status.must_equal 200
-    response.headers.must_equal headers
-    response.body.must_equal body.join
-  end
-
-  it "provide access to the HTTP status" do
-    res = Rack::MockRequest.new(app).get("")
-    res.must_be :successful?
-    res.must_be :ok?
-
-    res = Rack::MockRequest.new(app).get("/?status=404")
-    res.wont_be :successful?
-    res.must_be :client_error?
-    res.must_be :not_found?
-
-    res = Rack::MockRequest.new(app).get("/?status=501")
-    res.wont_be :successful?
-    res.must_be :server_error?
-
-    res = Rack::MockRequest.new(app).get("/?status=307")
-    res.must_be :redirect?
-
-    res = Rack::MockRequest.new(app).get("/?status=201", lint: true)
-    res.must_be :empty?
-  end
-
-  it "provide access to the HTTP headers" do
-    res = Rack::MockRequest.new(app).get("")
-    res.must_include "content-type"
-    res.headers["content-type"].must_equal "text/yaml"
-    res.original_headers["content-type"].must_equal "text/yaml"
-    res["content-type"].must_equal "text/yaml"
-    res.content_type.must_equal "text/yaml"
-    res.content_length.wont_equal 0
-    res.location.must_be_nil
-  end
-
-  it "provide access to session cookies" do
-    res = Rack::MockRequest.new(app).get("")
-    session_cookie = res.cookie("session_test")
-    session_cookie.value[0].must_equal "session_test"
-    session_cookie.domain.must_equal ".test.com"
-    session_cookie.path.must_equal "/"
-    session_cookie.secure.must_equal false
-    session_cookie.expires.must_be_nil
-  end
-
-  it "provides access to persistent cookies set with max-age" do
-    res = Rack::MockRequest.new(app).get("")
-    persistent_cookie = res.cookie("persistent_test")
-    persistent_cookie.value[0].must_equal "persistent_test"
-    persistent_cookie.domain.must_be_nil
-    persistent_cookie.path.must_equal "/"
-    persistent_cookie.secure.must_equal false
-    persistent_cookie.expires.wont_be_nil
-    persistent_cookie.expires.must_be :<, (Time.now + 15552000)
-  end
-
-  it "provides access to persistent cookies set with expires" do
-    res = Rack::MockRequest.new(app).get("")
-    persistent_cookie = res.cookie("persistent_with_expires_test")
-    persistent_cookie.value[0].must_equal "persistent_with_expires_test"
-    persistent_cookie.domain.must_be_nil
-    persistent_cookie.path.must_equal "/"
-    persistent_cookie.secure.must_equal false
-    persistent_cookie.expires.wont_be_nil
-    persistent_cookie.expires.must_equal Time.httpdate("Thu, 31 Oct 2021 07:28:00 GMT")
-  end
-
-  it "parses cookies giving max-age precedence over expires" do
-    res = Rack::MockRequest.new(app).get("")
-    persistent_cookie = res.cookie("expires_and_max-age_test")
-    persistent_cookie.value[0].must_equal "expires_and_max-age_test"
-    persistent_cookie.expires.wont_be_nil
-    persistent_cookie.expires.must_be :<, (Time.now + 15552000)
-  end
-
-  it "provide access to secure cookies" do
-    res = Rack::MockRequest.new(app).get("")
-    secure_cookie = res.cookie("secure_test")
-    secure_cookie.value[0].must_equal "secure_test"
-    secure_cookie.domain.must_equal ".test.com"
-    secure_cookie.path.must_equal "/"
-    secure_cookie.secure.must_equal true
-    secure_cookie.expires.must_be_nil
-  end
-
-  it "parses cookie headers with equals sign at the end" do
-    res = Rack::MockRequest.new(->(env) { [200, { "Set-Cookie" => "__cf_bm=_somebase64encodedstringwithequalsatthened=; array=awesome" }, [""]] }).get("")
-    cookie = res.cookie("__cf_bm")
-    cookie.value[0].must_equal "_somebase64encodedstringwithequalsatthened="
-  end
-
-  it "return nil if a non existent cookie is requested" do
-    res = Rack::MockRequest.new(app).get("")
-    res.cookie("i_dont_exist").must_be_nil
-  end
-
-  deprecated "parses cookie headers provided as an array" do
-    res = Rack::MockRequest.new(->(env) { [200, [["set-cookie", "array=awesome"]], [""]] }).get("")
-    array_cookie = res.cookie("array")
-    array_cookie.value[0].must_equal "awesome"
-  end
-
-  deprecated "parses multiple set-cookie headers provided as an array" do
-    cookie_headers = [["set-cookie", "array=awesome\nmultiple=times"]]
-    res = Rack::MockRequest.new(->(env) { [200, cookie_headers, [""]] }).get("")
-    array_cookie = res.cookie("array")
-    array_cookie.value[0].must_equal "awesome"
-    second_cookie = res.cookie("multiple")
-    second_cookie.value[0].must_equal "times"
-  end
-
-  it "parses multiple set-cookie headers provided as hash with array value" do
-    cookie_headers = { "set-cookie" => ["array=awesome", "multiple=times"]}
-    res = Rack::MockRequest.new(->(env) { [200, cookie_headers, [""]] }).get("")
-    array_cookie = res.cookie("array")
-    array_cookie.value[0].must_equal "awesome"
-    second_cookie = res.cookie("multiple")
-    second_cookie.value[0].must_equal "times"
-  end
-
-  it "provide access to the HTTP body" do
-    res = Rack::MockRequest.new(app).get("")
-    res.body.must_match(/rack/)
-    assert_match(res, /rack/)
-
-    res.match('rack')[0].must_equal 'rack'
-    res.match('banana').must_be_nil
-  end
-
-  it "provide access to the Rack errors" do
-    res = Rack::MockRequest.new(app).get("/?error=foo", lint: true)
-    res.must_be :ok?
-    res.errors.wont_be :empty?
-    res.errors.must_include "foo"
-  end
-
-  deprecated "handle enumerable headers that are not a hash" do
-    # this is exactly what rack-test does
-    res = Rack::MockResponse.new(200, [], [])
-    res.cookies.must_equal({})
-  end
-
-  it "allow calling body.close afterwards" do
-    # this is exactly what rack-test does
-    body = StringIO.new("hi")
-    res = Rack::MockResponse.new(200, {}, body)
-    body.close if body.respond_to?(:close)
-    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)
-    }.must_raise Rack::MockRequest::FatalWarning
-
-    lambda {
-      Rack::MockRequest.new(lambda { |env| env['rack.errors'].write(env['rack.errors'].string) }).get("/", fatal: true)
-    }.must_raise(Rack::MockRequest::FatalWarning).message.must_equal ''
-  end
-end
-
-describe Rack::MockResponse, 'headers' do
-  before do
-    @res = Rack::MockRequest.new(app).get('')
-    @res.set_header 'FOO', '1'
-  end
-
-  it 'has_header?' do
-    lambda { @res.has_header? nil }.must_raise ArgumentError
-
-    @res.has_header?('FOO').must_equal true
-    @res.has_header?('Foo').must_equal true
-  end
-
-  it 'get_header' do
-    lambda { @res.get_header nil }.must_raise ArgumentError
-
-    @res.get_header('FOO').must_equal '1'
-    @res.get_header('Foo').must_equal '1'
-  end
-
-  it 'set_header' do
-    lambda { @res.set_header nil, '1' }.must_raise ArgumentError
-
-    @res.set_header('FOO', '2').must_equal '2'
-    @res.get_header('FOO').must_equal '2'
-
-    @res.set_header('Foo', '3').must_equal '3'
-    @res.get_header('Foo').must_equal '3'
-    @res.get_header('FOO').must_equal '3'
-
-    @res.set_header('FOO', nil).must_be_nil
-    @res.get_header('FOO').must_be_nil
-    @res.has_header?('FOO').must_equal true
-  end
-
-  it 'add_header' do
-    lambda { @res.add_header nil, '1' }.must_raise ArgumentError
-
-    # Sets header on first addition
-    @res.add_header('FOO', '1').must_equal ['1', '1']
-    @res.get_header('FOO').must_equal ['1', '1']
-
-    # Ignores nil additions
-    @res.add_header('FOO', nil).must_equal ['1', '1']
-    @res.get_header('FOO').must_equal ['1', '1']
-
-    # Converts additions to strings
-    @res.add_header('FOO', 2).must_equal ['1', '1', '2']
-    @res.get_header('FOO').must_equal ['1', '1', '2']
-
-    # Respects underlying case-sensitivity
-    @res.add_header('Foo', 'yep').must_equal ['1', '1', '2', 'yep']
-    @res.get_header('Foo').must_equal ['1', '1', '2', 'yep']
-    @res.get_header('FOO').must_equal ['1', '1', '2', 'yep']
-  end
-
-  it 'delete_header' do
-    lambda { @res.delete_header nil }.must_raise ArgumentError
-
-    @res.delete_header('FOO').must_equal '1'
-    @res.has_header?('FOO').must_equal false
-
-    @res.has_header?('Foo').must_equal false
-    @res.delete_header('Foo').must_be_nil
-  end
-end
diff --git a/test/spec_mock_response.rb b/test/spec_mock_response.rb
new file mode 100644
index 00000000..2813ecd6
--- /dev/null
+++ b/test/spec_mock_response.rb
@@ -0,0 +1,276 @@
+# frozen_string_literal: true
+
+require_relative 'helper'
+require 'yaml'
+require_relative 'psych_fix'
+
+separate_testing do
+  require_relative '../lib/rack/mock_request'
+  require_relative '../lib/rack/mock_response'
+  require_relative '../lib/rack/lint'
+  require_relative '../lib/rack/request'
+end
+
+app = Rack::Lint.new(lambda { |env|
+  req = Rack::Request.new(env)
+
+  env["mock.postdata"] = env["rack.input"].read
+  if req.GET["error"]
+    env["rack.errors"].puts req.GET["error"]
+    env["rack.errors"].flush
+  end
+
+  body = req.head? ? "" : env.to_yaml
+  response = Rack::Response.new(
+    body,
+    req.GET["status"] || 200,
+    "content-type" => "text/yaml"
+  )
+  response.set_cookie("session_test", { value: "session_test", domain: ".test.com", path: "/" })
+  response.set_cookie("secure_test", { value: "secure_test", domain: ".test.com",  path: "/", secure: true })
+  response.set_cookie("persistent_test", { value: "persistent_test", max_age: 15552000, path: "/" })
+  response.set_cookie("persistent_with_expires_test", { value: "persistent_with_expires_test", expires: Time.httpdate("Thu, 31 Oct 2021 07:28:00 GMT"), path: "/" })
+  response.set_cookie("expires_and_max-age_test", { value: "expires_and_max-age_test", expires: Time.now + 15552000 * 2, max_age: 15552000, path: "/" })
+  response.finish
+})
+
+describe Rack::MockResponse do
+  it 'has standard constructor' do
+    headers = { "header" => "value" }
+    body = ["body"]
+
+    response = Rack::MockResponse[200, headers, body]
+
+    response.status.must_equal 200
+    response.headers.must_equal headers
+    response.body.must_equal body.join
+  end
+
+  it "provide access to the HTTP status" do
+    res = Rack::MockRequest.new(app).get("")
+    res.must_be :successful?
+    res.must_be :ok?
+
+    res = Rack::MockRequest.new(app).get("/?status=404")
+    res.wont_be :successful?
+    res.must_be :client_error?
+    res.must_be :not_found?
+
+    res = Rack::MockRequest.new(app).get("/?status=501")
+    res.wont_be :successful?
+    res.must_be :server_error?
+
+    res = Rack::MockRequest.new(app).get("/?status=307")
+    res.must_be :redirect?
+
+    res = Rack::MockRequest.new(app).get("/?status=201", lint: true)
+    res.must_be :empty?
+  end
+
+  it "provide access to the HTTP headers" do
+    res = Rack::MockRequest.new(app).get("")
+    res.must_include "content-type"
+    res.headers["content-type"].must_equal "text/yaml"
+    res.original_headers["content-type"].must_equal "text/yaml"
+    res["content-type"].must_equal "text/yaml"
+    res.content_type.must_equal "text/yaml"
+    res.content_length.wont_equal 0
+    res.location.must_be_nil
+  end
+
+  it "provide access to session cookies" do
+    res = Rack::MockRequest.new(app).get("")
+    session_cookie = res.cookie("session_test")
+    session_cookie.value[0].must_equal "session_test"
+    session_cookie.domain.must_equal ".test.com"
+    session_cookie.path.must_equal "/"
+    session_cookie.secure.must_equal false
+    session_cookie.expires.must_be_nil
+  end
+
+  it "provides access to persistent cookies set with max-age" do
+    res = Rack::MockRequest.new(app).get("")
+    persistent_cookie = res.cookie("persistent_test")
+    persistent_cookie.value[0].must_equal "persistent_test"
+    persistent_cookie.domain.must_be_nil
+    persistent_cookie.path.must_equal "/"
+    persistent_cookie.secure.must_equal false
+    persistent_cookie.expires.wont_be_nil
+    persistent_cookie.expires.must_be :<, (Time.now + 15552000)
+  end
+
+  it "provides access to persistent cookies set with expires" do
+    res = Rack::MockRequest.new(app).get("")
+    persistent_cookie = res.cookie("persistent_with_expires_test")
+    persistent_cookie.value[0].must_equal "persistent_with_expires_test"
+    persistent_cookie.domain.must_be_nil
+    persistent_cookie.path.must_equal "/"
+    persistent_cookie.secure.must_equal false
+    persistent_cookie.expires.wont_be_nil
+    persistent_cookie.expires.must_equal Time.httpdate("Thu, 31 Oct 2021 07:28:00 GMT")
+  end
+
+  it "parses cookies giving max-age precedence over expires" do
+    res = Rack::MockRequest.new(app).get("")
+    persistent_cookie = res.cookie("expires_and_max-age_test")
+    persistent_cookie.value[0].must_equal "expires_and_max-age_test"
+    persistent_cookie.expires.wont_be_nil
+    persistent_cookie.expires.must_be :<, (Time.now + 15552000)
+  end
+
+  it "provide access to secure cookies" do
+    res = Rack::MockRequest.new(app).get("")
+    secure_cookie = res.cookie("secure_test")
+    secure_cookie.value[0].must_equal "secure_test"
+    secure_cookie.domain.must_equal ".test.com"
+    secure_cookie.path.must_equal "/"
+    secure_cookie.secure.must_equal true
+    secure_cookie.expires.must_be_nil
+  end
+
+  it "parses cookie headers with equals sign at the end" do
+    res = Rack::MockRequest.new(->(env) { [200, { "Set-Cookie" => "__cf_bm=_somebase64encodedstringwithequalsatthened=; array=awesome" }, [""]] }).get("")
+    cookie = res.cookie("__cf_bm")
+    cookie.value[0].must_equal "_somebase64encodedstringwithequalsatthened="
+  end
+
+  it "return nil if a non existent cookie is requested" do
+    res = Rack::MockRequest.new(app).get("")
+    res.cookie("i_dont_exist").must_be_nil
+  end
+
+  deprecated "parses cookie headers provided as an array" do
+    res = Rack::MockRequest.new(->(env) { [200, [["set-cookie", "array=awesome"]], [""]] }).get("")
+    array_cookie = res.cookie("array")
+    array_cookie.value[0].must_equal "awesome"
+  end
+
+  deprecated "parses multiple set-cookie headers provided as an array" do
+    cookie_headers = [["set-cookie", "array=awesome\nmultiple=times"]]
+    res = Rack::MockRequest.new(->(env) { [200, cookie_headers, [""]] }).get("")
+    array_cookie = res.cookie("array")
+    array_cookie.value[0].must_equal "awesome"
+    second_cookie = res.cookie("multiple")
+    second_cookie.value[0].must_equal "times"
+  end
+
+  it "parses multiple set-cookie headers provided as hash with array value" do
+    cookie_headers = { "set-cookie" => ["array=awesome", "multiple=times"]}
+    res = Rack::MockRequest.new(->(env) { [200, cookie_headers, [""]] }).get("")
+    array_cookie = res.cookie("array")
+    array_cookie.value[0].must_equal "awesome"
+    second_cookie = res.cookie("multiple")
+    second_cookie.value[0].must_equal "times"
+  end
+
+  it "provide access to the HTTP body" do
+    res = Rack::MockRequest.new(app).get("")
+    res.body.must_match(/rack/)
+    assert_match(res, /rack/)
+
+    res.match('rack')[0].must_equal 'rack'
+    res.match('banana').must_be_nil
+  end
+
+  it "provide access to the Rack errors" do
+    res = Rack::MockRequest.new(app).get("/?error=foo", lint: true)
+    res.must_be :ok?
+    res.errors.wont_be :empty?
+    res.errors.must_include "foo"
+  end
+
+  deprecated "handle enumerable headers that are not a hash" do
+    # this is exactly what rack-test does
+    res = Rack::MockResponse.new(200, [], [])
+    res.cookies.must_equal({})
+  end
+
+  it "allow calling body.close afterwards" do
+    # this is exactly what rack-test does
+    body = StringIO.new("hi")
+    res = Rack::MockResponse.new(200, {}, body)
+    body.close if body.respond_to?(:close)
+    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)
+    }.must_raise Rack::MockRequest::FatalWarning
+
+    lambda {
+      Rack::MockRequest.new(lambda { |env| env['rack.errors'].write(env['rack.errors'].string) }).get("/", fatal: true)
+    }.must_raise(Rack::MockRequest::FatalWarning).message.must_equal ''
+  end
+end
+
+describe Rack::MockResponse, 'headers' do
+  before do
+    @res = Rack::MockRequest.new(app).get('')
+    @res.set_header 'FOO', '1'
+  end
+
+  it 'has_header?' do
+    lambda { @res.has_header? nil }.must_raise ArgumentError
+
+    @res.has_header?('FOO').must_equal true
+    @res.has_header?('Foo').must_equal true
+  end
+
+  it 'get_header' do
+    lambda { @res.get_header nil }.must_raise ArgumentError
+
+    @res.get_header('FOO').must_equal '1'
+    @res.get_header('Foo').must_equal '1'
+  end
+
+  it 'set_header' do
+    lambda { @res.set_header nil, '1' }.must_raise ArgumentError
+
+    @res.set_header('FOO', '2').must_equal '2'
+    @res.get_header('FOO').must_equal '2'
+
+    @res.set_header('Foo', '3').must_equal '3'
+    @res.get_header('Foo').must_equal '3'
+    @res.get_header('FOO').must_equal '3'
+
+    @res.set_header('FOO', nil).must_be_nil
+    @res.get_header('FOO').must_be_nil
+    @res.has_header?('FOO').must_equal true
+  end
+
+  it 'add_header' do
+    lambda { @res.add_header nil, '1' }.must_raise ArgumentError
+
+    # Sets header on first addition
+    @res.add_header('FOO', '1').must_equal ['1', '1']
+    @res.get_header('FOO').must_equal ['1', '1']
+
+    # Ignores nil additions
+    @res.add_header('FOO', nil).must_equal ['1', '1']
+    @res.get_header('FOO').must_equal ['1', '1']
+
+    # Converts additions to strings
+    @res.add_header('FOO', 2).must_equal ['1', '1', '2']
+    @res.get_header('FOO').must_equal ['1', '1', '2']
+
+    # Respects underlying case-sensitivity
+    @res.add_header('Foo', 'yep').must_equal ['1', '1', '2', 'yep']
+    @res.get_header('Foo').must_equal ['1', '1', '2', 'yep']
+    @res.get_header('FOO').must_equal ['1', '1', '2', 'yep']
+  end
+
+  it 'delete_header' do
+    lambda { @res.delete_header nil }.must_raise ArgumentError
+
+    @res.delete_header('FOO').must_equal '1'
+    @res.has_header?('FOO').must_equal false
+
+    @res.has_header?('Foo').must_equal false
+    @res.delete_header('Foo').must_be_nil
+  end
+end
diff --git a/test/spec_multipart.rb b/test/spec_multipart.rb
index a1e0a7e4..43bb90b2 100644
--- a/test/spec_multipart.rb
+++ b/test/spec_multipart.rb
@@ -6,7 +6,7 @@ require 'timeout'
 separate_testing do
   require_relative '../lib/rack/multipart'
   require_relative '../lib/rack/lint'
-  require_relative '../lib/rack/mock'
+  require_relative '../lib/rack/mock_request'
   require_relative '../lib/rack/query_parser'
   require_relative '../lib/rack/utils'
   require_relative '../lib/rack/request'
diff --git a/test/spec_null_logger.rb b/test/spec_null_logger.rb
index 698b07eb..65ddb279 100644
--- a/test/spec_null_logger.rb
+++ b/test/spec_null_logger.rb
@@ -5,7 +5,7 @@ require_relative 'helper'
 separate_testing do
   require_relative '../lib/rack/null_logger'
   require_relative '../lib/rack/lint'
-  require_relative '../lib/rack/mock'
+  require_relative '../lib/rack/mock_request'
 end
 
 describe Rack::NullLogger do
diff --git a/test/spec_recursive.rb b/test/spec_recursive.rb
index 23e6487b..650626c0 100644
--- a/test/spec_recursive.rb
+++ b/test/spec_recursive.rb
@@ -5,7 +5,7 @@ require_relative 'helper'
 separate_testing do
   require_relative '../lib/rack/recursive'
   require_relative '../lib/rack/lint'
-  require_relative '../lib/rack/mock'
+  require_relative '../lib/rack/mock_request'
   require_relative '../lib/rack/urlmap'
 end
 
diff --git a/test/spec_request.rb b/test/spec_request.rb
index 58797462..750639de 100644
--- a/test/spec_request.rb
+++ b/test/spec_request.rb
@@ -7,7 +7,7 @@ require 'securerandom'
 
 separate_testing do
   require_relative '../lib/rack/request'
-  require_relative '../lib/rack/mock'
+  require_relative '../lib/rack/mock_request'
   require_relative '../lib/rack/lint'
 end
 
diff --git a/test/spec_runtime.rb b/test/spec_runtime.rb
index 0e22d2b5..5f23dce7 100644
--- a/test/spec_runtime.rb
+++ b/test/spec_runtime.rb
@@ -5,7 +5,7 @@ require_relative 'helper'
 separate_testing do
   require_relative '../lib/rack/runtime'
   require_relative '../lib/rack/lint'
-  require_relative '../lib/rack/mock'
+  require_relative '../lib/rack/mock_request'
 end
 
 describe Rack::Runtime do
diff --git a/test/spec_sendfile.rb b/test/spec_sendfile.rb
index c0ef5e5d..5205b671 100644
--- a/test/spec_sendfile.rb
+++ b/test/spec_sendfile.rb
@@ -7,7 +7,7 @@ require 'tmpdir'
 separate_testing do
   require_relative '../lib/rack/sendfile'
   require_relative '../lib/rack/lint'
-  require_relative '../lib/rack/mock'
+  require_relative '../lib/rack/mock_request'
 end
 
 describe Rack::Sendfile do
diff --git a/test/spec_server.rb b/test/spec_server.rb
index d9adcd36..7e4850b4 100644
--- a/test/spec_server.rb
+++ b/test/spec_server.rb
@@ -19,7 +19,7 @@ end
 separate_testing do
   require_relative '../lib/rack/server'
   require_relative '../lib/rack/lint'
-  require_relative '../lib/rack/mock'
+  require_relative '../lib/rack/mock_request'
   require_relative '../lib/rack/show_exceptions'
   require_relative '../lib/rack/tempfile_reaper'
   require_relative '../lib/rack/handler'
diff --git a/test/spec_show_exceptions.rb b/test/spec_show_exceptions.rb
index f6fc68de..debc5c30 100644
--- a/test/spec_show_exceptions.rb
+++ b/test/spec_show_exceptions.rb
@@ -5,7 +5,7 @@ require_relative 'helper'
 separate_testing do
   require_relative '../lib/rack/show_exceptions'
   require_relative '../lib/rack/lint'
-  require_relative '../lib/rack/mock'
+  require_relative '../lib/rack/mock_request'
 end
 
 describe Rack::ShowExceptions do
diff --git a/test/spec_show_status.rb b/test/spec_show_status.rb
index 6a5edb6e..14c3da45 100644
--- a/test/spec_show_status.rb
+++ b/test/spec_show_status.rb
@@ -5,7 +5,7 @@ require_relative 'helper'
 separate_testing do
   require_relative '../lib/rack/show_status'
   require_relative '../lib/rack/lint'
-  require_relative '../lib/rack/mock'
+  require_relative '../lib/rack/mock_request'
 end
 
 describe Rack::ShowStatus do
diff --git a/test/spec_static.rb b/test/spec_static.rb
index 9cc33a42..7496c032 100644
--- a/test/spec_static.rb
+++ b/test/spec_static.rb
@@ -6,7 +6,7 @@ require 'zlib'
 separate_testing do
   require_relative '../lib/rack/static'
   require_relative '../lib/rack/lint'
-  require_relative '../lib/rack/mock'
+  require_relative '../lib/rack/mock_request'
 end
 
 class DummyApp
diff --git a/test/spec_tempfile_reaper.rb b/test/spec_tempfile_reaper.rb
index 7e5c6790..7153297f 100644
--- a/test/spec_tempfile_reaper.rb
+++ b/test/spec_tempfile_reaper.rb
@@ -5,7 +5,7 @@ require_relative 'helper'
 separate_testing do
   require_relative '../lib/rack/tempfile_reaper'
   require_relative '../lib/rack/lint'
-  require_relative '../lib/rack/mock'
+  require_relative '../lib/rack/mock_request'
 end
 
 describe Rack::TempfileReaper do
diff --git a/test/spec_urlmap.rb b/test/spec_urlmap.rb
index e11c4e6d..c3ecffe2 100644
--- a/test/spec_urlmap.rb
+++ b/test/spec_urlmap.rb
@@ -5,7 +5,7 @@ require_relative 'helper'
 separate_testing do
   require_relative '../lib/rack/urlmap'
   require_relative '../lib/rack/lint'
-  require_relative '../lib/rack/mock'
+  require_relative '../lib/rack/mock_request'
 end
 
 describe Rack::URLMap do
diff --git a/test/spec_utils.rb b/test/spec_utils.rb
index 6b75e2ef..52d29502 100644
--- a/test/spec_utils.rb
+++ b/test/spec_utils.rb
@@ -6,7 +6,7 @@ require 'timeout'
 separate_testing do
   require_relative '../lib/rack/utils'
   require_relative '../lib/rack/lint'
-  require_relative '../lib/rack/mock'
+  require_relative '../lib/rack/mock_request'
   require_relative '../lib/rack/request'
   require_relative '../lib/rack/content_length'
 end