aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_dispatch/test/mock.rb
blob: 8dc048af11be09563c30fc81f7bc319d553b1bbf (plain) (blame)
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
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].respond_to?(:any?) && opts[:params].any? { |k, v| UploadedFile === v }
            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