diff --git a/lib/rack/test/utils.rb b/lib/rack/test/utils.rb index d1f74568..09ff2e25 100644 --- a/lib/rack/test/utils.rb +++ b/lib/rack/test/utils.rb @@ -74,16 +74,10 @@ def normalize_multipart_params(params, first=false) case value when Array - value.map do |v| - if v.is_a?(Hash) - nested_params = {} - normalize_multipart_params(v).each do |subkey, subvalue| - nested_params[subkey] = subvalue - end - (flattened_params["#{k}[]"] ||= []) << nested_params - else - flattened_params["#{k}[]"] = value - end + next if value.empty? + + flattened_params["#{k}[]"] = value.map do |v| + v.is_a?(Hash) ? normalize_multipart_params(v) : v end when Hash normalize_multipart_params(value).each do |subkey, subvalue| @@ -106,10 +100,14 @@ def build_parts(buffer, parameters) # Append each multipart parameter value to the buffer. def _build_parts(buffer, parameters) parameters.map do |name, value| - if name =~ /\[\]\Z/ && value.is_a?(Array) && value.all? { |v| v.is_a?(Hash) } - value.each do |hash| + if name =~ /\[\]\Z/ && value.is_a?(Array) && value.any? { |v| v.is_a?(Hash) } + value.each do |item| new_value = {} - hash.each { |k, v| new_value[name + k] = v } + if item.is_a?(Hash) + item.each { |k, v| new_value[name + k] = v } + else + new_value[name] = item + end _build_parts(buffer, new_value) end else diff --git a/spec/rack/test/utils_spec.rb b/spec/rack/test/utils_spec.rb index 090276d3..c11c52dc 100644 --- a/spec/rack/test/utils_spec.rb +++ b/spec/rack/test/utils_spec.rb @@ -148,6 +148,21 @@ def respond_to?(m, *a) params['files'][1][:tempfile].read.must_equal "baz\n" end + it 'builds mixed hash and primitive arrays without mutating them' do + values = [{'id' => '1'}, 'two', {'id' => '3'}].freeze + data = Rack::Test::Utils.build_multipart('items' => values) + + options = { + 'CONTENT_TYPE' => "multipart/form-data; boundary=#{Rack::Test::MULTIPART_BOUNDARY}", + 'CONTENT_LENGTH' => data.length.to_s, + :input => StringIO.new(data) + } + params = Rack::Multipart.parse_multipart(Rack::MockRequest.env_for('/', options)) + + params['items'].must_equal [{'id' => '1'}, 'two', {'id' => '3'}] + values.must_equal [{'id' => '1'}, 'two', {'id' => '3'}] + end + it 'builds multipart bodies from mixed array of a file and a primitive' do files = [Rack::Test::UploadedFile.new(multipart_file('foo.txt')), 'baz'] data = Rack::Test::Utils.build_multipart('files' => files)