require 'abstract_unit' class UrlEncodedParamsParsingTest < ActionController::IntegrationTest class TestController < ActionController::Base class << self attr_accessor :last_request_parameters, :last_request_type end def parse self.class.last_request_parameters = request.request_parameters head :ok end end def teardown TestController.last_request_parameters = nil end test "parses unbalanced query string with array" do assert_parses( {'location' => ["1", "2"], 'age_group' => ["2"]}, "location[]=1&location[]=2&age_group[]=2" ) end test "parses nested hash" do query = [ "note[viewers][viewer][][type]=User", "note[viewers][viewer][][id]=1", "note[viewers][viewer][][type]=Group", "note[viewers][viewer][][id]=2" ].join("&") expected = { "note" => { "viewers"=>{"viewer"=>[{ "id"=>"1", "type"=>"User"}, {"type"=>"Group", "id"=>"2"} ]} } } assert_parses(expected, query) end test "parses more complex nesting" do query = [ "customers[boston][first][name]=David", "customers[boston][first][url]=http://David", "customers[boston][second][name]=Allan", "customers[boston][second][url]=http://Allan", "something_else=blah", "something_nil=", "something_empty=", "products[first]=Apple Computer", "products[second]=Pc", "=Save" ].join("&") expected = { "customers" => { "boston" => { "first" => { "name" => "David", "url" => "http://David" }, "second" => { "name" => "Allan", "url" => "http://Allan" } } }, "something_else" => "blah", "something_empty" => "", "something_nil" => "", "products" => { "first" => "Apple Computer", "second" => "Pc" } } assert_parses expected, query end test "parses params with array" do query = "selected[]=1&selected[]=2&selected[]=3" expected = { "selected" => [ "1", "2", "3" ] } assert_parses expected, query end test "parses params with nil key" do query = "=&test2=value1" expected = { "test2" => "value1" } assert_parses expected, query end test "parses params with array prefix and hashes" do query = "a[][b][c]=d" expected = {"a" => [{"b" => {"c" => "d"}}]} assert_parses expected, query end test "parses params with complex nesting" do query = "a[][b][c][][d][]=e" expected = {"a" => [{"b" => {"c" => [{"d" => ["e"]}]}}]} assert_parses expected, query end test "parses params with file path" do query = [ "customers[boston][first][name]=David", "something_else=blah", "logo=#{File.expand_path(__FILE__)}" ].join("&") expected = { "customers" => { "boston" => { "first" => { "name" => "David" } } }, "something_else" => "blah", "logo" => File.expand_path(__FILE__), } assert_parses expected, query end test "parses params with Safari 2 trailing null character" do query = "selected[]=1&selected[]=2&selected[]=3\0" expected = { "selected" => [ "1", "2", "3" ] } assert_parses expected, query end test "passes through rack middleware and parses params" do with_muck_middleware do assert_parses({ "a" => { "b" => "c" } }, "a[b]=c") end end # The lint wrapper is used in integration tests # instead of a normal StringIO class InputWrapper = Rack::Lint::InputWrapper test "passes through rack middleware and parses params with unwindable input" do InputWrapper.any_instance.stubs(:rewind).raises(Errno::ESPIPE) with_muck_middleware do assert_parses({ "a" => { "b" => "c" } }, "a[b]=c") end end private class MuckMiddleware def initialize(app) @app = app end def call(env) req = Rack::Request.new(env) req.params # Parse params @app.call(env) end end def with_muck_middleware original_middleware = ActionController::Dispatcher.middleware middleware = original_middleware.dup middleware.insert_after ActionController::RewindableInput, MuckMiddleware ActionController::Dispatcher.middleware = middleware yield ActionController::Dispatcher.middleware = original_middleware end def with_test_routing with_routing do |set| set.draw do |map| map.connect ':action', :controller => "url_encoded_params_parsing_test/test" end yield end end def assert_parses(expected, actual) with_test_routing do post "/parse", actual assert_response :ok assert_equal(expected, TestController.last_request_parameters) end end end