mock.rb 3.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
module ActionDispatch
  module Test
    class MockRequest < Rack::MockRequest
      MULTIPART_BOUNDARY = "----------XnJLe9ZIbbGUYtzPQJ16u1"

      class << self
        def env_for(path, opts)
          headers = opts.delete(:headers)

          method = (opts[:method] || opts["REQUEST_METHOD"]).to_s.upcase
          opts[:method] = opts["REQUEST_METHOD"] = method

          path = "/#{path}" unless path[0] == ?/
          uri = URI.parse(path)
          uri.host ||= "example.org"

          if URI::HTTPS === uri
            opts.update("SERVER_PORT" => "443", "HTTPS" => "on")
          end

          if method == "POST" && !opts.has_key?(:input)
            opts["CONTENT_TYPE"] = "application/x-www-form-urlencoded"

            multipart = (opts[:params] || {}).any? do |k, v|
              UploadedFile === v
            end

            if multipart
              opts[:input] = multipart_body(opts.delete(:params))
              opts["CONTENT_LENGTH"] ||= opts[:input].length.to_s
              opts["CONTENT_TYPE"] = "multipart/form-data; boundary=#{MULTIPART_BOUNDARY}"
            else
              params = opts.delete(:params)
              opts[:input] = case params
                  when Hash then requestify(params)
                  when nil  then ""
                  else params
                end
            end
          end

          params = opts[:params] || {}
          if params.is_a?(String)
            if method == "GET"
              uri.query = params
            else
              opts[:input] = params
            end
          else
            params.update(::Rack::Utils.parse_query(uri.query))
            uri.query = requestify(params)
          end

          env = ::Rack::MockRequest.env_for(uri.to_s, opts)

          (headers || {}).each do |key, value|
            key = key.to_s.upcase.gsub(/-/, "_")
            key = "HTTP_#{key}" unless env.has_key?(key) || key =~ /^HTTP_/
            env[key] = value
          end

          env
        end

        private
          def requestify(value, prefix = nil)
            case value
            when Array
              value.map do |v|
                requestify(v, "#{prefix}[]")
              end.join("&")
            when Hash
              value.map do |k, v|
                requestify(v, prefix ? "#{prefix}[#{::Rack::Utils.escape(k)}]" : ::Rack::Utils.escape(k))
              end.join("&")
            else
              "#{prefix}=#{::Rack::Utils.escape(value)}"
            end
          end

          def multipart_requestify(params, first=true)
            p = Hash.new

            params.each do |key, value|
              k = first ? key.to_s : "[#{key}]"

              if Hash === value
                multipart_requestify(value, false).each do |subkey, subvalue|
                  p[k + subkey] = subvalue
                end
              else
                p[k] = value
              end
            end

            return p
          end

          def multipart_body(params)
            multipart_requestify(params).map do |key, value|
              if value.respond_to?(:original_filename)
                ::File.open(value.path, "rb") do |f|
                  f.set_encoding(Encoding::BINARY) if f.respond_to?(:set_encoding)

                  <<-EOF
--#{MULTIPART_BOUNDARY}\r
Content-Disposition: form-data; name="#{key}"; filename="#{::Rack::Utils.escape(value.original_filename)}"\r
Content-Type: #{value.content_type}\r
Content-Length: #{::File.stat(value.path).size}\r
\r
#{f.read}\r
EOF
            end
          else
<<-EOF
--#{MULTIPART_BOUNDARY}\r
Content-Disposition: form-data; name="#{key}"\r
\r
#{value}\r
EOF
              end
            end.join("")+"--#{MULTIPART_BOUNDARY}--\r"
          end
      end
    end
  end
end