aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_dispatch/test/mock.rb
blob: 4042f9fbc8e6fb0b6d9492448189318dc8f92f7b (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
module ActionDispatch
  module Test
    class MockRequest < Rack::MockRequest
      MULTIPART_BOUNDARY = "----------XnJLe9ZIbbGUYtzPQJ16u1"

      class << self
        def env_for(path, opts)
          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

          ::Rack::MockRequest.env_for(uri.to_s, opts)
        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