diff options
Diffstat (limited to 'actionpack/test/dispatch/request/query_string_parsing_test.rb')
-rw-r--r-- | actionpack/test/dispatch/request/query_string_parsing_test.rb | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/actionpack/test/dispatch/request/query_string_parsing_test.rb b/actionpack/test/dispatch/request/query_string_parsing_test.rb new file mode 100644 index 0000000000..f9ae5ef4e8 --- /dev/null +++ b/actionpack/test/dispatch/request/query_string_parsing_test.rb @@ -0,0 +1,176 @@ +# frozen_string_literal: true + +require "abstract_unit" + +class QueryStringParsingTest < ActionDispatch::IntegrationTest + class TestController < ActionController::Base + class << self + attr_accessor :last_query_parameters + end + + def parse + self.class.last_query_parameters = request.query_parameters + head :ok + end + end + class EarlyParse + def initialize(app) + @app = app + end + + def call(env) + # Trigger a Rack parse so that env caches the query params + Rack::Request.new(env).params + @app.call(env) + end + end + + def teardown + TestController.last_query_parameters = nil + end + + test "query string" do + assert_parses( + { "action" => "create_customer", "full_name" => "David Heinemeier Hansson", "customerId" => "1" }, + "action=create_customer&full_name=David%20Heinemeier%20Hansson&customerId=1" + ) + end + + test "deep query string" do + assert_parses( + { "x" => { "y" => { "z" => "10" } } }, + "x[y][z]=10" + ) + end + + test "deep query string with array" do + assert_parses({ "x" => { "y" => { "z" => ["10"] } } }, "x[y][z][]=10") + assert_parses({ "x" => { "y" => { "z" => ["10", "5"] } } }, "x[y][z][]=10&x[y][z][]=5") + end + + test "deep query string with array of hash" do + assert_parses({ "x" => { "y" => [{ "z" => "10" }] } }, "x[y][][z]=10") + assert_parses({ "x" => { "y" => [{ "z" => "10", "w" => "10" }] } }, "x[y][][z]=10&x[y][][w]=10") + assert_parses({ "x" => { "y" => [{ "z" => "10", "v" => { "w" => "10" } }] } }, "x[y][][z]=10&x[y][][v][w]=10") + end + + test "deep query string with array of hashes with one pair" do + assert_parses({ "x" => { "y" => [{ "z" => "10" }, { "z" => "20" }] } }, "x[y][][z]=10&x[y][][z]=20") + end + + test "deep query string with array of hashes with multiple pairs" do + assert_parses( + { "x" => { "y" => [{ "z" => "10", "w" => "a" }, { "z" => "20", "w" => "b" }] } }, + "x[y][][z]=10&x[y][][w]=a&x[y][][z]=20&x[y][][w]=b" + ) + end + + test "query string with nil" do + assert_parses( + { "action" => "create_customer", "full_name" => "" }, + "action=create_customer&full_name=" + ) + end + + test "query string with array" do + assert_parses( + { "action" => "create_customer", "selected" => ["1", "2", "3"] }, + "action=create_customer&selected[]=1&selected[]=2&selected[]=3" + ) + end + + test "query string with amps" do + assert_parses( + { "action" => "create_customer", "name" => "Don't & Does" }, + "action=create_customer&name=Don%27t+%26+Does" + ) + end + + test "query string with many equal" do + assert_parses( + { "action" => "create_customer", "full_name" => "abc=def=ghi" }, + "action=create_customer&full_name=abc=def=ghi" + ) + end + + test "query string without equal" do + assert_parses({ "action" => nil }, "action") + assert_parses({ "action" => { "foo" => nil } }, "action[foo]") + assert_parses({ "action" => { "foo" => { "bar" => nil } } }, "action[foo][bar]") + assert_parses({ "action" => { "foo" => { "bar" => [] } } }, "action[foo][bar][]") + assert_parses({ "action" => { "foo" => [] } }, "action[foo][]") + assert_parses({ "action" => { "foo" => [{ "bar" => nil }] } }, "action[foo][][bar]") + end + + def test_array_parses_without_nil + assert_parses({ "action" => ["1"] }, "action[]=1&action[]") + end + + test "perform_deep_munge" do + old_perform_deep_munge = ActionDispatch::Request::Utils.perform_deep_munge + ActionDispatch::Request::Utils.perform_deep_munge = false + begin + assert_parses({ "action" => nil }, "action") + assert_parses({ "action" => { "foo" => nil } }, "action[foo]") + assert_parses({ "action" => { "foo" => { "bar" => nil } } }, "action[foo][bar]") + assert_parses({ "action" => { "foo" => { "bar" => [nil] } } }, "action[foo][bar][]") + assert_parses({ "action" => { "foo" => [nil] } }, "action[foo][]") + assert_parses({ "action" => { "foo" => [{ "bar" => nil }] } }, "action[foo][][bar]") + assert_parses({ "action" => ["1", nil] }, "action[]=1&action[]") + ensure + ActionDispatch::Request::Utils.perform_deep_munge = old_perform_deep_munge + end + end + + test "query string with empty key" do + assert_parses( + { "action" => "create_customer", "full_name" => "David Heinemeier Hansson" }, + "action=create_customer&full_name=David%20Heinemeier%20Hansson&=Save" + ) + end + + test "query string with many ampersands" do + assert_parses( + { "action" => "create_customer", "full_name" => "David Heinemeier Hansson" }, + "&action=create_customer&&&full_name=David%20Heinemeier%20Hansson" + ) + end + + test "unbalanced query string with array" do + assert_parses( + { "location" => ["1", "2"], "age_group" => ["2"] }, + "location[]=1&location[]=2&age_group[]=2" + ) + end + + test "ambiguous query string returns a bad request" do + with_routing do |set| + set.draw do + ActiveSupport::Deprecation.silence do + get ":action", to: ::QueryStringParsingTest::TestController + end + end + + get "/parse", headers: { "QUERY_STRING" => "foo[]=bar&foo[4]=bar" } + assert_response :bad_request + end + end + + private + def assert_parses(expected, actual) + with_routing do |set| + set.draw do + ActiveSupport::Deprecation.silence do + get ":action", to: ::QueryStringParsingTest::TestController + end + end + @app = self.class.build_app(set) do |middleware| + middleware.use(EarlyParse) + end + + get "/parse", params: actual + assert_response :ok + assert_equal(expected, ::QueryStringParsingTest::TestController.last_query_parameters) + end + end +end |