diff options
Diffstat (limited to 'actionpack/test/dispatch')
20 files changed, 1592 insertions, 452 deletions
diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb index 91ac13e7c6..0f145666d1 100644 --- a/actionpack/test/dispatch/cookies_test.rb +++ b/actionpack/test/dispatch/cookies_test.rb @@ -11,6 +11,16 @@ require 'active_support/key_generator' require 'active_support/message_verifier' class CookiesTest < ActionController::TestCase + class CustomSerializer + def self.load(value) + value.to_s + " and loaded" + end + + def self.dump(value) + value.to_s + " was dumped" + end + end + class TestController < ActionController::Base def authenticate cookies["user_name"] = "david" @@ -359,9 +369,72 @@ class CookiesTest < ActionController::TestCase assert_equal 'Jamie', @controller.send(:cookies).permanent[:user_name] end - def test_signed_cookie + def test_signed_cookie_using_default_serializer get :set_signed_cookie - assert_equal 45, @controller.send(:cookies).signed[:user_id] + cookies = @controller.send :cookies + assert_not_equal 45, cookies[:user_id] + assert_equal 45, cookies.signed[:user_id] + end + + def test_signed_cookie_using_marshal_serializer + @request.env["action_dispatch.cookies_serializer"] = :marshal + get :set_signed_cookie + cookies = @controller.send :cookies + assert_not_equal 45, cookies[:user_id] + assert_equal 45, cookies.signed[:user_id] + end + + def test_signed_cookie_using_json_serializer + @request.env["action_dispatch.cookies_serializer"] = :json + get :set_signed_cookie + cookies = @controller.send :cookies + assert_not_equal 45, cookies[:user_id] + assert_equal 45, cookies.signed[:user_id] + end + + def test_signed_cookie_using_custom_serializer + @request.env["action_dispatch.cookies_serializer"] = CustomSerializer + get :set_signed_cookie + assert_not_equal 45, cookies[:user_id] + assert_equal '45 was dumped and loaded', cookies.signed[:user_id] + end + + def test_signed_cookie_using_hybrid_serializer_can_migrate_marshal_dumped_value_to_json + @request.env["action_dispatch.cookies_serializer"] = :hybrid + + key_generator = @request.env["action_dispatch.key_generator"] + signed_cookie_salt = @request.env["action_dispatch.signed_cookie_salt"] + secret = key_generator.generate_key(signed_cookie_salt) + + marshal_value = ActiveSupport::MessageVerifier.new(secret, serializer: Marshal).generate(45) + @request.headers["Cookie"] = "user_id=#{marshal_value}" + + get :get_signed_cookie + + cookies = @controller.send :cookies + assert_not_equal 45, cookies[:user_id] + assert_equal 45, cookies.signed[:user_id] + + verifier = ActiveSupport::MessageVerifier.new(secret, serializer: JSON) + assert_equal 45, verifier.verify(@response.cookies['user_id']) + end + + def test_signed_cookie_using_hybrid_serializer_can_read_from_json_dumped_value + @request.env["action_dispatch.cookies_serializer"] = :hybrid + + key_generator = @request.env["action_dispatch.key_generator"] + signed_cookie_salt = @request.env["action_dispatch.signed_cookie_salt"] + secret = key_generator.generate_key(signed_cookie_salt) + json_value = ActiveSupport::MessageVerifier.new(secret, serializer: JSON).generate(45) + @request.headers["Cookie"] = "user_id=#{json_value}" + + get :get_signed_cookie + + cookies = @controller.send :cookies + assert_not_equal 45, cookies[:user_id] + assert_equal 45, cookies.signed[:user_id] + + assert_nil @response.cookies["user_id"] end def test_accessing_nonexistant_signed_cookie_should_not_raise_an_invalid_signature @@ -369,7 +442,18 @@ class CookiesTest < ActionController::TestCase assert_nil @controller.send(:cookies).signed[:non_existant_attribute] end - def test_encrypted_cookie + def test_encrypted_cookie_using_default_serializer + get :set_encrypted_cookie + cookies = @controller.send :cookies + assert_not_equal 'bar', cookies[:foo] + assert_raise TypeError do + cookies.signed[:foo] + end + assert_equal 'bar', cookies.encrypted[:foo] + end + + def test_encrypted_cookie_using_marshal_serializer + @request.env["action_dispatch.cookies_serializer"] = :marshal get :set_encrypted_cookie cookies = @controller.send :cookies assert_not_equal 'bar', cookies[:foo] @@ -379,6 +463,66 @@ class CookiesTest < ActionController::TestCase assert_equal 'bar', cookies.encrypted[:foo] end + def test_encrypted_cookie_using_json_serializer + @request.env["action_dispatch.cookies_serializer"] = :json + get :set_encrypted_cookie + cookies = @controller.send :cookies + assert_not_equal 'bar', cookies[:foo] + assert_raises ::JSON::ParserError do + cookies.signed[:foo] + end + assert_equal 'bar', cookies.encrypted[:foo] + end + + def test_encrypted_cookie_using_custom_serializer + @request.env["action_dispatch.cookies_serializer"] = CustomSerializer + get :set_encrypted_cookie + assert_not_equal 'bar', cookies.encrypted[:foo] + assert_equal 'bar was dumped and loaded', cookies.encrypted[:foo] + end + + def test_encrypted_cookie_using_hybrid_serializer_can_migrate_marshal_dumped_value_to_json + @request.env["action_dispatch.cookies_serializer"] = :hybrid + + key_generator = @request.env["action_dispatch.key_generator"] + encrypted_cookie_salt = @request.env["action_dispatch.encrypted_cookie_salt"] + encrypted_signed_cookie_salt = @request.env["action_dispatch.encrypted_signed_cookie_salt"] + secret = key_generator.generate_key(encrypted_cookie_salt) + sign_secret = key_generator.generate_key(encrypted_signed_cookie_salt) + + marshal_value = ActiveSupport::MessageEncryptor.new(secret, sign_secret, serializer: Marshal).encrypt_and_sign("bar") + @request.headers["Cookie"] = "foo=#{marshal_value}" + + get :get_encrypted_cookie + + cookies = @controller.send :cookies + assert_not_equal "bar", cookies[:foo] + assert_equal "bar", cookies.encrypted[:foo] + + encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, serializer: JSON) + assert_equal "bar", encryptor.decrypt_and_verify(@response.cookies["foo"]) + end + + def test_encrypted_cookie_using_hybrid_serializer_can_read_from_json_dumped_value + @request.env["action_dispatch.cookies_serializer"] = :hybrid + + key_generator = @request.env["action_dispatch.key_generator"] + encrypted_cookie_salt = @request.env["action_dispatch.encrypted_cookie_salt"] + encrypted_signed_cookie_salt = @request.env["action_dispatch.encrypted_signed_cookie_salt"] + secret = key_generator.generate_key(encrypted_cookie_salt) + sign_secret = key_generator.generate_key(encrypted_signed_cookie_salt) + json_value = ActiveSupport::MessageEncryptor.new(secret, sign_secret, serializer: JSON).encrypt_and_sign("bar") + @request.headers["Cookie"] = "foo=#{json_value}" + + get :get_encrypted_cookie + + cookies = @controller.send :cookies + assert_not_equal "bar", cookies[:foo] + assert_equal "bar", cookies.encrypted[:foo] + + assert_nil @response.cookies["foo"] + end + def test_accessing_nonexistant_encrypted_cookie_should_not_raise_invalid_message get :set_encrypted_cookie assert_nil @controller.send(:cookies).encrypted[:non_existant_attribute] @@ -537,6 +681,123 @@ class CookiesTest < ActionController::TestCase assert_equal 'bar', encryptor.decrypt_and_verify(@response.cookies["foo"]) end + def test_legacy_json_signed_cookie_is_read_and_transparently_upgraded_by_signed_json_cookie_jar_if_both_secret_token_and_secret_key_base_are_set + @request.env["action_dispatch.cookies_serializer"] = :json + @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" + @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" + + legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33", serializer: JSON).generate(45) + + @request.headers["Cookie"] = "user_id=#{legacy_value}" + get :get_signed_cookie + + assert_equal 45, @controller.send(:cookies).signed[:user_id] + + key_generator = @request.env["action_dispatch.key_generator"] + secret = key_generator.generate_key(@request.env["action_dispatch.signed_cookie_salt"]) + verifier = ActiveSupport::MessageVerifier.new(secret, serializer: JSON) + assert_equal 45, verifier.verify(@response.cookies["user_id"]) + end + + def test_legacy_json_signed_cookie_is_read_and_transparently_encrypted_by_encrypted_json_cookie_jar_if_both_secret_token_and_secret_key_base_are_set + @request.env["action_dispatch.cookies_serializer"] = :json + @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" + @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" + @request.env["action_dispatch.encrypted_cookie_salt"] = "4433796b79d99a7735553e316522acee" + @request.env["action_dispatch.encrypted_signed_cookie_salt"] = "00646eb40062e1b1deff205a27cd30f9" + + legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33", serializer: JSON).generate('bar') + + @request.headers["Cookie"] = "foo=#{legacy_value}" + get :get_encrypted_cookie + + assert_equal 'bar', @controller.send(:cookies).encrypted[:foo] + + key_generator = @request.env["action_dispatch.key_generator"] + secret = key_generator.generate_key(@request.env["action_dispatch.encrypted_cookie_salt"]) + sign_secret = key_generator.generate_key(@request.env["action_dispatch.encrypted_signed_cookie_salt"]) + encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, serializer: JSON) + assert_equal 'bar', encryptor.decrypt_and_verify(@response.cookies["foo"]) + end + + def test_legacy_json_signed_cookie_is_read_and_transparently_upgraded_by_signed_json_hybrid_jar_if_both_secret_token_and_secret_key_base_are_set + @request.env["action_dispatch.cookies_serializer"] = :hybrid + @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" + @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" + + legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33", serializer: JSON).generate(45) + + @request.headers["Cookie"] = "user_id=#{legacy_value}" + get :get_signed_cookie + + assert_equal 45, @controller.send(:cookies).signed[:user_id] + + key_generator = @request.env["action_dispatch.key_generator"] + secret = key_generator.generate_key(@request.env["action_dispatch.signed_cookie_salt"]) + verifier = ActiveSupport::MessageVerifier.new(secret, serializer: JSON) + assert_equal 45, verifier.verify(@response.cookies["user_id"]) + end + + def test_legacy_json_signed_cookie_is_read_and_transparently_encrypted_by_encrypted_hybrid_cookie_jar_if_both_secret_token_and_secret_key_base_are_set + @request.env["action_dispatch.cookies_serializer"] = :hybrid + @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" + @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" + @request.env["action_dispatch.encrypted_cookie_salt"] = "4433796b79d99a7735553e316522acee" + @request.env["action_dispatch.encrypted_signed_cookie_salt"] = "00646eb40062e1b1deff205a27cd30f9" + + legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33", serializer: JSON).generate('bar') + + @request.headers["Cookie"] = "foo=#{legacy_value}" + get :get_encrypted_cookie + + assert_equal 'bar', @controller.send(:cookies).encrypted[:foo] + + key_generator = @request.env["action_dispatch.key_generator"] + secret = key_generator.generate_key(@request.env["action_dispatch.encrypted_cookie_salt"]) + sign_secret = key_generator.generate_key(@request.env["action_dispatch.encrypted_signed_cookie_salt"]) + encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, serializer: JSON) + assert_equal 'bar', encryptor.decrypt_and_verify(@response.cookies["foo"]) + end + + def test_legacy_marshal_signed_cookie_is_read_and_transparently_upgraded_by_signed_json_hybrid_jar_if_both_secret_token_and_secret_key_base_are_set + @request.env["action_dispatch.cookies_serializer"] = :hybrid + @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" + @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" + + legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33").generate(45) + + @request.headers["Cookie"] = "user_id=#{legacy_value}" + get :get_signed_cookie + + assert_equal 45, @controller.send(:cookies).signed[:user_id] + + key_generator = @request.env["action_dispatch.key_generator"] + secret = key_generator.generate_key(@request.env["action_dispatch.signed_cookie_salt"]) + verifier = ActiveSupport::MessageVerifier.new(secret, serializer: JSON) + assert_equal 45, verifier.verify(@response.cookies["user_id"]) + end + + def test_legacy_marshal_signed_cookie_is_read_and_transparently_encrypted_by_encrypted_hybrid_cookie_jar_if_both_secret_token_and_secret_key_base_are_set + @request.env["action_dispatch.cookies_serializer"] = :hybrid + @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" + @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" + @request.env["action_dispatch.encrypted_cookie_salt"] = "4433796b79d99a7735553e316522acee" + @request.env["action_dispatch.encrypted_signed_cookie_salt"] = "00646eb40062e1b1deff205a27cd30f9" + + legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33").generate('bar') + + @request.headers["Cookie"] = "foo=#{legacy_value}" + get :get_encrypted_cookie + + assert_equal 'bar', @controller.send(:cookies).encrypted[:foo] + + key_generator = @request.env["action_dispatch.key_generator"] + secret = key_generator.generate_key(@request.env["action_dispatch.encrypted_cookie_salt"]) + sign_secret = key_generator.generate_key(@request.env["action_dispatch.encrypted_signed_cookie_salt"]) + encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, serializer: JSON) + assert_equal 'bar', encryptor.decrypt_and_verify(@response.cookies["foo"]) + end + def test_legacy_signed_cookie_is_treated_as_nil_by_signed_cookie_jar_if_tampered @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" @@ -694,8 +955,6 @@ class CookiesTest < ActionController::TestCase assert_equal "dhh", cookies['user_name'] end - - def test_setting_request_cookies_is_indifferent_access cookies.clear cookies[:user_name] = "andrew" diff --git a/actionpack/test/dispatch/debug_exceptions_test.rb b/actionpack/test/dispatch/debug_exceptions_test.rb index 3045a07ad6..8660deb634 100644 --- a/actionpack/test/dispatch/debug_exceptions_test.rb +++ b/actionpack/test/dispatch/debug_exceptions_test.rb @@ -43,6 +43,19 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest raise ActionController::UrlGenerationError, "No route matches" when "/parameter_missing" raise ActionController::ParameterMissing, :missing_param_key + when "/original_syntax_error" + eval 'broke_syntax =' # `eval` need for raise native SyntaxError at runtime + when "/syntax_error_into_view" + begin + eval 'broke_syntax =' + rescue Exception => e + template = ActionView::Template.new(File.read(__FILE__), + __FILE__, + ActionView::Template::Handlers::Raw.new, + {}) + raise ActionView::Template::Error.new(template, e) + end + else raise "puke!" end @@ -134,9 +147,10 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest get "/", {}, xhr_request_env assert_response 500 + assert_no_match(/<header>/, body) assert_no_match(/<body>/, body) assert_equal response.content_type, "text/plain" - assert_match(/puke/, body) + assert_match(/RuntimeError\npuke/, body) get "/not_found", {}, xhr_request_env assert_response 404 @@ -242,4 +256,26 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest get "/", {}, env assert_operator((output.rewind && output.read).lines.count, :>, 10) end + + test 'display backtrace when error type is SyntaxError' do + @app = DevelopmentApp + + get '/original_syntax_error', {}, {'action_dispatch.backtrace_cleaner' => ActiveSupport::BacktraceCleaner.new} + + assert_response 500 + assert_select '#Application-Trace' do + assert_select 'pre code', /\(eval\):1: syntax error, unexpected/ + end + end + + test 'display backtrace when error type is SyntaxError wrapped by ActionView::Template::Error' do + @app = DevelopmentApp + + get '/syntax_error_into_view', {}, {'action_dispatch.backtrace_cleaner' => ActiveSupport::BacktraceCleaner.new} + + assert_response 500 + assert_select '#Application-Trace' do + assert_select 'pre code', /\(eval\):1: syntax error, unexpected/ + end + end end diff --git a/actionpack/test/dispatch/header_test.rb b/actionpack/test/dispatch/header_test.rb index 9e37b96951..e2b38c23bc 100644 --- a/actionpack/test/dispatch/header_test.rb +++ b/actionpack/test/dispatch/header_test.rb @@ -55,6 +55,8 @@ class HeaderTest < ActiveSupport::TestCase test "key?" do assert @headers.key?("CONTENT_TYPE") assert @headers.include?("CONTENT_TYPE") + assert @headers.key?("Content-Type") + assert @headers.include?("Content-Type") end test "fetch with block" do diff --git a/actionpack/test/dispatch/live_response_test.rb b/actionpack/test/dispatch/live_response_test.rb index e0cfb73acf..512f3a8a7a 100644 --- a/actionpack/test/dispatch/live_response_test.rb +++ b/actionpack/test/dispatch/live_response_test.rb @@ -6,6 +6,7 @@ module ActionController class ResponseTest < ActiveSupport::TestCase def setup @response = Live::Response.new + @response.request = ActionDispatch::Request.new({}) #yolo end def test_header_merge @@ -34,6 +35,7 @@ module ActionController @response.stream.close } + @response.await_commit @response.each do |part| assert_equal 'foo', part latch.release @@ -58,21 +60,30 @@ module ActionController assert_nil @response.headers['Content-Length'] end - def test_headers_cannot_be_written_after_write + def test_headers_cannot_be_written_after_webserver_reads @response.stream.write 'omg' + latch = ActiveSupport::Concurrency::Latch.new + t = Thread.new { + @response.stream.each do |chunk| + latch.release + end + } + + latch.await assert @response.headers.frozen? e = assert_raises(ActionDispatch::IllegalStateError) do @response.headers['Content-Length'] = "zomg" end assert_equal 'header already sent', e.message + @response.stream.close + t.join end def test_headers_cannot_be_written_after_close @response.stream.close - assert @response.headers.frozen? e = assert_raises(ActionDispatch::IllegalStateError) do @response.headers['Content-Length'] = "zomg" end diff --git a/actionpack/test/dispatch/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb index 8a19129695..981cf2426e 100644 --- a/actionpack/test/dispatch/mime_type_test.rb +++ b/actionpack/test/dispatch/mime_type_test.rb @@ -31,21 +31,21 @@ class MimeTypeTest < ActiveSupport::TestCase test "parse text with trailing star at the beginning" do accept = "text/*, text/html, application/json, multipart/form-data" - expect = [Mime::HTML, Mime::TEXT, Mime::JS, Mime::CSS, Mime::ICS, Mime::CSV, Mime::XML, Mime::YAML, Mime::JSON, Mime::MULTIPART_FORM] + expect = [Mime::HTML, Mime::TEXT, Mime::JS, Mime::CSS, Mime::ICS, Mime::CSV, Mime::VCF, Mime::XML, Mime::YAML, Mime::JSON, Mime::MULTIPART_FORM] parsed = Mime::Type.parse(accept) assert_equal expect, parsed end test "parse text with trailing star in the end" do accept = "text/html, application/json, multipart/form-data, text/*" - expect = [Mime::HTML, Mime::JSON, Mime::MULTIPART_FORM, Mime::TEXT, Mime::JS, Mime::CSS, Mime::ICS, Mime::CSV, Mime::XML, Mime::YAML] + expect = [Mime::HTML, Mime::JSON, Mime::MULTIPART_FORM, Mime::TEXT, Mime::JS, Mime::CSS, Mime::ICS, Mime::CSV, Mime::VCF, Mime::XML, Mime::YAML] parsed = Mime::Type.parse(accept) assert_equal expect, parsed end test "parse text with trailing star" do accept = "text/*" - expect = [Mime::HTML, Mime::TEXT, Mime::JS, Mime::CSS, Mime::ICS, Mime::CSV, Mime::XML, Mime::YAML, Mime::JSON] + expect = [Mime::HTML, Mime::TEXT, Mime::JS, Mime::CSS, Mime::ICS, Mime::CSV, Mime::VCF, Mime::XML, Mime::YAML, Mime::JSON] parsed = Mime::Type.parse(accept) assert_equal expect, parsed end diff --git a/actionpack/test/dispatch/mount_test.rb b/actionpack/test/dispatch/mount_test.rb index 30e95a0b75..cdf00d84fb 100644 --- a/actionpack/test/dispatch/mount_test.rb +++ b/actionpack/test/dispatch/mount_test.rb @@ -5,7 +5,7 @@ class TestRoutingMount < ActionDispatch::IntegrationTest class FakeEngine def self.routes - Object.new + @routes ||= ActionDispatch::Routing::RouteSet.new end def self.call(env) @@ -27,15 +27,21 @@ class TestRoutingMount < ActionDispatch::IntegrationTest scope "/its_a" do mount SprocketsApp, :at => "/sprocket" end + + resources :users do + mount FakeEngine, :at => "/fakeengine", :as => :fake_mounted_at_resource + end end def app Router end - def test_trailing_slash_is_not_removed_from_path_info - get "/sprockets/omg/" - assert_equal "/sprockets -- /omg/", response.body + def test_app_name_is_properly_generated_when_engine_is_mounted_in_resources + assert Router.mounted_helpers.method_defined?(:user_fake_mounted_at_resource), + "A mounted helper should be defined with a parent's prefix" + assert Router.named_routes.routes[:user_fake_mounted_at_resource], + "A named route should be defined with a parent's prefix" end def test_mounting_sets_script_name diff --git a/actionpack/test/dispatch/prefix_generation_test.rb b/actionpack/test/dispatch/prefix_generation_test.rb index e519fff51e..08501d19c0 100644 --- a/actionpack/test/dispatch/prefix_generation_test.rb +++ b/actionpack/test/dispatch/prefix_generation_test.rb @@ -32,12 +32,18 @@ module TestGenerationPrefix get "/conflicting_url", :to => "inside_engine_generating#conflicting" get "/foo", :to => "never#invoked", :as => :named_helper_that_should_be_invoked_only_in_respond_to_test - get "/relative_path_redirect", :to => redirect("foo") + get "/relative_path_root", :to => redirect("") + get "/relative_path_redirect", :to => redirect("foo") + get "/relative_option_root", :to => redirect(:path => "") get "/relative_option_redirect", :to => redirect(:path => "foo") + get "/relative_custom_root", :to => redirect { |params, request| "" } get "/relative_custom_redirect", :to => redirect { |params, request| "foo" } - get "/absolute_path_redirect", :to => redirect("/foo") + get "/absolute_path_root", :to => redirect("/") + get "/absolute_path_redirect", :to => redirect("/foo") + get "/absolute_option_root", :to => redirect(:path => "/") get "/absolute_option_redirect", :to => redirect(:path => "/foo") + get "/absolute_custom_root", :to => redirect { |params, request| "/" } get "/absolute_custom_redirect", :to => redirect { |params, request| "/foo" } end @@ -190,46 +196,64 @@ module TestGenerationPrefix assert_equal "engine", last_response.body end + test "[ENGINE] relative path root uses SCRIPT_NAME from request" do + get "/awesome/blog/relative_path_root" + verify_redirect "http://example.org/awesome/blog" + end + test "[ENGINE] relative path redirect uses SCRIPT_NAME from request" do get "/awesome/blog/relative_path_redirect" - assert_equal 301, last_response.status - assert_equal "http://example.org/awesome/blog/foo", last_response.headers["Location"] - assert_equal %(<html><body>You are being <a href="http://example.org/awesome/blog/foo">redirected</a>.</body></html>), last_response.body + verify_redirect "http://example.org/awesome/blog/foo" + end + + test "[ENGINE] relative option root uses SCRIPT_NAME from request" do + get "/awesome/blog/relative_option_root" + verify_redirect "http://example.org/awesome/blog" end test "[ENGINE] relative option redirect uses SCRIPT_NAME from request" do get "/awesome/blog/relative_option_redirect" - assert_equal 301, last_response.status - assert_equal "http://example.org/awesome/blog/foo", last_response.headers["Location"] - assert_equal %(<html><body>You are being <a href="http://example.org/awesome/blog/foo">redirected</a>.</body></html>), last_response.body + verify_redirect "http://example.org/awesome/blog/foo" + end + + test "[ENGINE] relative custom root uses SCRIPT_NAME from request" do + get "/awesome/blog/relative_custom_root" + verify_redirect "http://example.org/awesome/blog" end test "[ENGINE] relative custom redirect uses SCRIPT_NAME from request" do get "/awesome/blog/relative_custom_redirect" - assert_equal 301, last_response.status - assert_equal "http://example.org/awesome/blog/foo", last_response.headers["Location"] - assert_equal %(<html><body>You are being <a href="http://example.org/awesome/blog/foo">redirected</a>.</body></html>), last_response.body + verify_redirect "http://example.org/awesome/blog/foo" + end + + test "[ENGINE] absolute path root doesn't use SCRIPT_NAME from request" do + get "/awesome/blog/absolute_path_root" + verify_redirect "http://example.org/" end test "[ENGINE] absolute path redirect doesn't use SCRIPT_NAME from request" do get "/awesome/blog/absolute_path_redirect" - assert_equal 301, last_response.status - assert_equal "http://example.org/foo", last_response.headers["Location"] - assert_equal %(<html><body>You are being <a href="http://example.org/foo">redirected</a>.</body></html>), last_response.body + verify_redirect "http://example.org/foo" + end + + test "[ENGINE] absolute option root doesn't use SCRIPT_NAME from request" do + get "/awesome/blog/absolute_option_root" + verify_redirect "http://example.org/" end test "[ENGINE] absolute option redirect doesn't use SCRIPT_NAME from request" do get "/awesome/blog/absolute_option_redirect" - assert_equal 301, last_response.status - assert_equal "http://example.org/foo", last_response.headers["Location"] - assert_equal %(<html><body>You are being <a href="http://example.org/foo">redirected</a>.</body></html>), last_response.body + verify_redirect "http://example.org/foo" + end + + test "[ENGINE] absolute custom root doesn't use SCRIPT_NAME from request" do + get "/awesome/blog/absolute_custom_root" + verify_redirect "http://example.org/" end test "[ENGINE] absolute custom redirect doesn't use SCRIPT_NAME from request" do get "/awesome/blog/absolute_custom_redirect" - assert_equal 301, last_response.status - assert_equal "http://example.org/foo", last_response.headers["Location"] - assert_equal %(<html><body>You are being <a href="http://example.org/foo">redirected</a>.</body></html>), last_response.body + verify_redirect "http://example.org/foo" end # Inside Application @@ -320,6 +344,17 @@ module TestGenerationPrefix path = engine_object.polymorphic_url(Post.new, :host => "www.example.com") assert_equal "http://www.example.com/awesome/blog/posts/1", path end + + private + def verify_redirect(url, status = 301) + assert_equal status, last_response.status + assert_equal url, last_response.headers["Location"] + assert_equal expected_redirect_body(url), last_response.body + end + + def expected_redirect_body(url) + %(<html><body>You are being <a href="#{url}">redirected</a>.</body></html>) + end end class EngineMountedAtRoot < ActionDispatch::IntegrationTest @@ -332,12 +367,18 @@ module TestGenerationPrefix routes.draw do get "/posts/:id", :to => "posts#show", :as => :post - get "/relative_path_redirect", :to => redirect("foo") + get "/relative_path_root", :to => redirect("") + get "/relative_path_redirect", :to => redirect("foo") + get "/relative_option_root", :to => redirect(:path => "") get "/relative_option_redirect", :to => redirect(:path => "foo") + get "/relative_custom_root", :to => redirect { |params, request| "" } get "/relative_custom_redirect", :to => redirect { |params, request| "foo" } - get "/absolute_path_redirect", :to => redirect("/foo") + get "/absolute_path_root", :to => redirect("/") + get "/absolute_path_redirect", :to => redirect("/foo") + get "/absolute_option_root", :to => redirect(:path => "/") get "/absolute_option_redirect", :to => redirect(:path => "/foo") + get "/absolute_custom_root", :to => redirect { |params, request| "/" } get "/absolute_custom_redirect", :to => redirect { |params, request| "/foo" } end @@ -390,46 +431,75 @@ module TestGenerationPrefix assert_equal "/posts/1", last_response.body end + test "[ENGINE] relative path root uses SCRIPT_NAME from request" do + get "/relative_path_root" + verify_redirect "http://example.org/" + end + test "[ENGINE] relative path redirect uses SCRIPT_NAME from request" do get "/relative_path_redirect" - assert_equal 301, last_response.status - assert_equal "http://example.org/foo", last_response.headers["Location"] - assert_equal %(<html><body>You are being <a href="http://example.org/foo">redirected</a>.</body></html>), last_response.body + verify_redirect "http://example.org/foo" + end + + test "[ENGINE] relative option root uses SCRIPT_NAME from request" do + get "/relative_option_root" + verify_redirect "http://example.org/" end test "[ENGINE] relative option redirect uses SCRIPT_NAME from request" do get "/relative_option_redirect" - assert_equal 301, last_response.status - assert_equal "http://example.org/foo", last_response.headers["Location"] - assert_equal %(<html><body>You are being <a href="http://example.org/foo">redirected</a>.</body></html>), last_response.body + verify_redirect "http://example.org/foo" + end + + test "[ENGINE] relative custom root uses SCRIPT_NAME from request" do + get "/relative_custom_root" + verify_redirect "http://example.org/" end test "[ENGINE] relative custom redirect uses SCRIPT_NAME from request" do get "/relative_custom_redirect" - assert_equal 301, last_response.status - assert_equal "http://example.org/foo", last_response.headers["Location"] - assert_equal %(<html><body>You are being <a href="http://example.org/foo">redirected</a>.</body></html>), last_response.body + verify_redirect "http://example.org/foo" + end + + test "[ENGINE] absolute path root doesn't use SCRIPT_NAME from request" do + get "/absolute_path_root" + verify_redirect "http://example.org/" end test "[ENGINE] absolute path redirect doesn't use SCRIPT_NAME from request" do get "/absolute_path_redirect" - assert_equal 301, last_response.status - assert_equal "http://example.org/foo", last_response.headers["Location"] - assert_equal %(<html><body>You are being <a href="http://example.org/foo">redirected</a>.</body></html>), last_response.body + verify_redirect "http://example.org/foo" + end + + test "[ENGINE] absolute option root doesn't use SCRIPT_NAME from request" do + get "/absolute_option_root" + verify_redirect "http://example.org/" end test "[ENGINE] absolute option redirect doesn't use SCRIPT_NAME from request" do get "/absolute_option_redirect" - assert_equal 301, last_response.status - assert_equal "http://example.org/foo", last_response.headers["Location"] - assert_equal %(<html><body>You are being <a href="http://example.org/foo">redirected</a>.</body></html>), last_response.body + verify_redirect "http://example.org/foo" + end + + test "[ENGINE] absolute custom root doesn't use SCRIPT_NAME from request" do + get "/absolute_custom_root" + verify_redirect "http://example.org/" end test "[ENGINE] absolute custom redirect doesn't use SCRIPT_NAME from request" do get "/absolute_custom_redirect" - assert_equal 301, last_response.status - assert_equal "http://example.org/foo", last_response.headers["Location"] - assert_equal %(<html><body>You are being <a href="http://example.org/foo">redirected</a>.</body></html>), last_response.body - end + verify_redirect "http://example.org/foo" + end + + private + def verify_redirect(url, status = 301) + assert_equal status, last_response.status + assert_equal url, last_response.headers["Location"] + assert_equal expected_redirect_body(url), last_response.body + end + + def expected_redirect_body(url) + %(<html><body>You are being <a href="#{url}">redirected</a>.</body></html>) + end end end diff --git a/actionpack/test/dispatch/rack_test.rb b/actionpack/test/dispatch/rack_test.rb deleted file mode 100644 index 42067854ee..0000000000 --- a/actionpack/test/dispatch/rack_test.rb +++ /dev/null @@ -1,191 +0,0 @@ -require 'abstract_unit' - -# TODO: Merge these tests into RequestTest - -class BaseRackTest < ActiveSupport::TestCase - def setup - @env = { - "HTTP_MAX_FORWARDS" => "10", - "SERVER_NAME" => "glu.ttono.us", - "FCGI_ROLE" => "RESPONDER", - "AUTH_TYPE" => "Basic", - "HTTP_X_FORWARDED_HOST" => "glu.ttono.us", - "HTTP_ACCEPT_CHARSET" => "UTF-8", - "HTTP_ACCEPT_ENCODING" => "gzip, deflate", - "HTTP_CACHE_CONTROL" => "no-cache, max-age=0", - "HTTP_PRAGMA" => "no-cache", - "HTTP_USER_AGENT" => "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en)", - "PATH_INFO" => "/homepage/", - "HTTP_ACCEPT_LANGUAGE" => "en", - "HTTP_NEGOTIATE" => "trans", - "HTTP_HOST" => "glu.ttono.us:8007", - "HTTP_REFERER" => "http://www.google.com/search?q=glu.ttono.us", - "HTTP_FROM" => "googlebot", - "SERVER_PROTOCOL" => "HTTP/1.1", - "REDIRECT_URI" => "/dispatch.fcgi", - "SCRIPT_NAME" => "/dispatch.fcgi", - "SERVER_ADDR" => "207.7.108.53", - "REMOTE_ADDR" => "207.7.108.53", - "REMOTE_HOST" => "google.com", - "REMOTE_IDENT" => "kevin", - "REMOTE_USER" => "kevin", - "SERVER_SOFTWARE" => "lighttpd/1.4.5", - "HTTP_COOKIE" => "_session_id=c84ace84796670c052c6ceb2451fb0f2; is_admin=yes", - "HTTP_X_FORWARDED_SERVER" => "glu.ttono.us", - "REQUEST_URI" => "/admin", - "DOCUMENT_ROOT" => "/home/kevinc/sites/typo/public", - "PATH_TRANSLATED" => "/home/kevinc/sites/typo/public/homepage/", - "SERVER_PORT" => "8007", - "QUERY_STRING" => "", - "REMOTE_PORT" => "63137", - "GATEWAY_INTERFACE" => "CGI/1.1", - "HTTP_X_FORWARDED_FOR" => "65.88.180.234", - "HTTP_ACCEPT" => "*/*", - "SCRIPT_FILENAME" => "/home/kevinc/sites/typo/public/dispatch.fcgi", - "REDIRECT_STATUS" => "200", - "REQUEST_METHOD" => "GET" - } - @request = ActionDispatch::Request.new(@env) - # some Nokia phone browsers omit the space after the semicolon separator. - # some developers have grown accustomed to using comma in cookie values. - @alt_cookie_fmt_request = ActionDispatch::Request.new(@env.merge({"HTTP_COOKIE"=>"_session_id=c84ace847,96670c052c6ceb2451fb0f2;is_admin=yes"})) - end - - private - def set_content_data(data) - @request.env['REQUEST_METHOD'] = 'POST' - @request.env['CONTENT_LENGTH'] = data.length - @request.env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded; charset=utf-8' - @request.env['rack.input'] = StringIO.new(data) - end -end - -class RackRequestTest < BaseRackTest - test "proxy request" do - assert_equal 'glu.ttono.us', @request.host_with_port - end - - test "http host" do - @env.delete "HTTP_X_FORWARDED_HOST" - @env['HTTP_HOST'] = "rubyonrails.org:8080" - assert_equal "rubyonrails.org", @request.host - assert_equal "rubyonrails.org:8080", @request.host_with_port - - @env['HTTP_X_FORWARDED_HOST'] = "www.firsthost.org, www.secondhost.org" - assert_equal "www.secondhost.org", @request.host - end - - test "http host with default port overrides server port" do - @env.delete "HTTP_X_FORWARDED_HOST" - @env['HTTP_HOST'] = "rubyonrails.org" - assert_equal "rubyonrails.org", @request.host_with_port - end - - test "host with port defaults to server name if no host headers" do - @env.delete "HTTP_X_FORWARDED_HOST" - @env.delete "HTTP_HOST" - assert_equal "glu.ttono.us:8007", @request.host_with_port - end - - test "host with port falls back to server addr if necessary" do - @env.delete "HTTP_X_FORWARDED_HOST" - @env.delete "HTTP_HOST" - @env.delete "SERVER_NAME" - assert_equal "207.7.108.53", @request.host - assert_equal 8007, @request.port - assert_equal "207.7.108.53:8007", @request.host_with_port - end - - test "host with port if http standard port is specified" do - @env['HTTP_X_FORWARDED_HOST'] = "glu.ttono.us:80" - assert_equal "glu.ttono.us", @request.host_with_port - end - - test "host with port if https standard port is specified" do - @env['HTTP_X_FORWARDED_PROTO'] = "https" - @env['HTTP_X_FORWARDED_HOST'] = "glu.ttono.us:443" - assert_equal "glu.ttono.us", @request.host_with_port - end - - test "host if ipv6 reference" do - @env.delete "HTTP_X_FORWARDED_HOST" - @env['HTTP_HOST'] = "[2001:1234:5678:9abc:def0::dead:beef]" - assert_equal "[2001:1234:5678:9abc:def0::dead:beef]", @request.host - end - - test "host if ipv6 reference with port" do - @env.delete "HTTP_X_FORWARDED_HOST" - @env['HTTP_HOST'] = "[2001:1234:5678:9abc:def0::dead:beef]:8008" - assert_equal "[2001:1234:5678:9abc:def0::dead:beef]", @request.host - end - - test "cgi environment variables" do - assert_equal "Basic", @request.auth_type - assert_equal 0, @request.content_length - assert_equal nil, @request.content_mime_type - assert_equal "CGI/1.1", @request.gateway_interface - assert_equal "*/*", @request.accept - assert_equal "UTF-8", @request.accept_charset - assert_equal "gzip, deflate", @request.accept_encoding - assert_equal "en", @request.accept_language - assert_equal "no-cache, max-age=0", @request.cache_control - assert_equal "googlebot", @request.from - assert_equal "glu.ttono.us", @request.host - assert_equal "trans", @request.negotiate - assert_equal "no-cache", @request.pragma - assert_equal "http://www.google.com/search?q=glu.ttono.us", @request.referer - assert_equal "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en)", @request.user_agent - assert_equal "/homepage/", @request.path_info - assert_equal "/home/kevinc/sites/typo/public/homepage/", @request.path_translated - assert_equal "", @request.query_string - assert_equal "207.7.108.53", @request.remote_addr - assert_equal "google.com", @request.remote_host - assert_equal "kevin", @request.remote_ident - assert_equal "kevin", @request.remote_user - assert_equal "GET", @request.request_method - assert_equal "/dispatch.fcgi", @request.script_name - assert_equal "glu.ttono.us", @request.server_name - assert_equal 8007, @request.server_port - assert_equal "HTTP/1.1", @request.server_protocol - assert_equal "lighttpd", @request.server_software - end - - test "cookie syntax resilience" do - cookies = @request.cookies - assert_equal "c84ace84796670c052c6ceb2451fb0f2", cookies["_session_id"], cookies.inspect - assert_equal "yes", cookies["is_admin"], cookies.inspect - - alt_cookies = @alt_cookie_fmt_request.cookies - #assert_equal "c84ace847,96670c052c6ceb2451fb0f2", alt_cookies["_session_id"], alt_cookies.inspect - assert_equal "yes", alt_cookies["is_admin"], alt_cookies.inspect - end -end - -class RackRequestParamsParsingTest < BaseRackTest - test "doesnt break when content type has charset" do - set_content_data 'flamenco=love' - - assert_equal({"flamenco"=> "love"}, @request.request_parameters) - end - - test "doesnt interpret request uri as query string when missing" do - @request.env['REQUEST_URI'] = 'foo' - assert_equal({}, @request.query_parameters) - end -end - -class RackRequestNeedsRewoundTest < BaseRackTest - test "body should be rewound" do - data = 'foo' - @env['rack.input'] = StringIO.new(data) - @env['CONTENT_LENGTH'] = data.length - @env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded; charset=utf-8' - - # Read the request body by parsing params. - request = ActionDispatch::Request.new(@env) - request.request_parameters - - # Should have rewound the body. - assert_equal 0, request.body.pos - end -end diff --git a/actionpack/test/dispatch/request/json_params_parsing_test.rb b/actionpack/test/dispatch/request/json_params_parsing_test.rb index dba9ab688f..c609075e6b 100644 --- a/actionpack/test/dispatch/request/json_params_parsing_test.rb +++ b/actionpack/test/dispatch/request/json_params_parsing_test.rb @@ -23,6 +23,13 @@ class JsonParamsParsingTest < ActionDispatch::IntegrationTest ) end + test "parses boolean and number json params for application json" do + assert_parses( + {"item" => {"enabled" => false, "count" => 10}}, + "{\"item\": {\"enabled\": false, \"count\": 10}}", { 'CONTENT_TYPE' => 'application/json' } + ) + end + test "parses json params for application jsonrequest" do assert_parses( {"person" => {"name" => "David"}}, diff --git a/actionpack/test/dispatch/request/query_string_parsing_test.rb b/actionpack/test/dispatch/request/query_string_parsing_test.rb index f072a9f717..d82493140f 100644 --- a/actionpack/test/dispatch/request/query_string_parsing_test.rb +++ b/actionpack/test/dispatch/request/query_string_parsing_test.rb @@ -11,6 +11,17 @@ class QueryStringParsingTest < ActionDispatch::IntegrationTest 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 @@ -93,6 +104,21 @@ class QueryStringParsingTest < ActionDispatch::IntegrationTest assert_parses({"action" => ['1']}, "action[]=1&action[]") end + test "perform_deep_munge" do + 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 = true + end + end + test "query string with empty key" do assert_parses( { "action" => "create_customer", "full_name" => "David Heinemeier Hansson" }, @@ -131,6 +157,10 @@ class QueryStringParsingTest < ActionDispatch::IntegrationTest set.draw do get ':action', :to => ::QueryStringParsingTest::TestController end + @app = self.class.build_app(set) do |middleware| + middleware.use(EarlyParse) + end + get "/parse", actual assert_response :ok diff --git a/actionpack/test/dispatch/request/session_test.rb b/actionpack/test/dispatch/request/session_test.rb index a244d1364c..10fb04e230 100644 --- a/actionpack/test/dispatch/request/session_test.rb +++ b/actionpack/test/dispatch/request/session_test.rb @@ -36,29 +36,55 @@ module ActionDispatch assert_equal s, Session.find(env) end + def test_destroy + s = Session.create(store, {}, {}) + s['rails'] = 'ftw' + + s.destroy + + assert_empty s + end + def test_keys - env = {} - s = Session.create(store, env, {}) + s = Session.create(store, {}, {}) s['rails'] = 'ftw' s['adequate'] = 'awesome' assert_equal %w[rails adequate], s.keys end def test_values - env = {} - s = Session.create(store, env, {}) + s = Session.create(store, {}, {}) s['rails'] = 'ftw' s['adequate'] = 'awesome' assert_equal %w[ftw awesome], s.values end def test_clear - env = {} - s = Session.create(store, env, {}) + s = Session.create(store, {}, {}) s['rails'] = 'ftw' s['adequate'] = 'awesome' + s.clear - assert_equal([], s.values) + assert_empty(s.values) + end + + def test_update + s = Session.create(store, {}, {}) + s['rails'] = 'ftw' + + s.update(:rails => 'awesome') + + assert_equal(['rails'], s.keys) + assert_equal('awesome', s['rails']) + end + + def test_delete + s = Session.create(store, {}, {}) + s['rails'] = 'ftw' + + s.delete('rails') + + assert_empty(s.keys) end def test_fetch @@ -68,13 +94,12 @@ module ActionDispatch assert_equal '1', session.fetch(:one) assert_equal '2', session.fetch(:two, '2') - assert_equal '2', session.fetch(:two) + assert_nil session.fetch(:two, nil) assert_equal 'three', session.fetch(:three) {|el| el.to_s } - assert_equal 'three', session.fetch(:three) assert_raise KeyError do - session.fetch(:four) + session.fetch(:three) end end @@ -83,6 +108,7 @@ module ActionDispatch Class.new { def load_session(env); [1, {}]; end def session_exists?(env); true; end + def destroy_session(env, id, options); 123; end }.new end end diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb index 9c7789bcfb..b48e8ab974 100644 --- a/actionpack/test/dispatch/request_test.rb +++ b/actionpack/test/dispatch/request_test.rb @@ -1,12 +1,34 @@ require 'abstract_unit' -class RequestTest < ActiveSupport::TestCase +class BaseRequestTest < ActiveSupport::TestCase + def setup + @env = { + :ip_spoofing_check => true, + :tld_length => 1, + "rack.input" => "foo" + } + end def url_for(options = {}) options = { host: 'www.example.com' }.merge!(options) ActionDispatch::Http::URL.url_for(options) end + protected + def stub_request(env = {}) + ip_spoofing_check = env.key?(:ip_spoofing_check) ? env.delete(:ip_spoofing_check) : true + @trusted_proxies ||= nil + ip_app = ActionDispatch::RemoteIp.new(Proc.new { }, ip_spoofing_check, @trusted_proxies) + tld_length = env.key?(:tld_length) ? env.delete(:tld_length) : 1 + ip_app.call(env) + ActionDispatch::Http::URL.tld_length = tld_length + + env = @env.merge(env) + ActionDispatch::Request.new(env) + end +end + +class RequestUrlFor < BaseRequestTest test "url_for class method" do e = assert_raise(ArgumentError) { url_for(:host => nil) } assert_match(/Please provide the :host parameter/, e.message) @@ -31,7 +53,9 @@ class RequestTest < ActiveSupport::TestCase assert_equal 'http://www.example.com?params=', url_for(:params => '') assert_equal 'http://www.example.com?params=1', url_for(:params => 1) end +end +class RequestIP < BaseRequestTest test "remote ip" do request = stub_request 'REMOTE_ADDR' => '1.2.3.4' assert_equal '1.2.3.4', request.remote_ip @@ -128,9 +152,12 @@ class RequestTest < ActiveSupport::TestCase request = stub_request 'HTTP_X_FORWARDED_FOR' => 'unknown,::1' assert_equal nil, request.remote_ip - request = stub_request 'HTTP_X_FORWARDED_FOR' => '2001:0db8:85a3:0000:0000:8a2e:0370:7334, fe80:0000:0000:0000:0202:b3ff:fe1e:8329, ::1, fc00::' + request = stub_request 'HTTP_X_FORWARDED_FOR' => '2001:0db8:85a3:0000:0000:8a2e:0370:7334, fe80:0000:0000:0000:0202:b3ff:fe1e:8329, ::1, fc00::, fc01::, fdff' assert_equal 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329', request.remote_ip + request = stub_request 'HTTP_X_FORWARDED_FOR' => 'FE00::, FDFF::' + assert_equal 'FE00::', request.remote_ip + request = stub_request 'HTTP_X_FORWARDED_FOR' => 'not_ip_address' assert_equal nil, request.remote_ip end @@ -220,10 +247,12 @@ class RequestTest < ActiveSupport::TestCase end test "remote ip middleware not present still returns an IP" do - request = ActionDispatch::Request.new({'REMOTE_ADDR' => '127.0.0.1'}) + request = stub_request('REMOTE_ADDR' => '127.0.0.1') assert_equal '127.0.0.1', request.remote_ip end +end +class RequestDomain < BaseRequestTest test "domains" do request = stub_request 'HTTP_HOST' => 'www.rubyonrails.org' assert_equal "rubyonrails.org", request.domain @@ -281,7 +310,9 @@ class RequestTest < ActiveSupport::TestCase assert_equal [], request.subdomains assert_equal "", request.subdomain end +end +class RequestPort < BaseRequestTest test "standard_port" do request = stub_request assert_equal 80, request.standard_port @@ -323,7 +354,9 @@ class RequestTest < ActiveSupport::TestCase request = stub_request 'HTTP_HOST' => 'www.example.org:8080' assert_equal ':8080', request.port_string end +end +class RequestPath < BaseRequestTest test "full path" do request = stub_request 'SCRIPT_NAME' => '', 'PATH_INFO' => '/path/of/some/uri', 'QUERY_STRING' => 'mapped=1' assert_equal "/path/of/some/uri?mapped=1", request.fullpath @@ -354,6 +387,32 @@ class RequestTest < ActiveSupport::TestCase assert_equal "/of/some/uri", request.path_info end + test "original_fullpath returns ORIGINAL_FULLPATH" do + request = stub_request('ORIGINAL_FULLPATH' => "/foo?bar") + + path = request.original_fullpath + assert_equal "/foo?bar", path + end + + test "original_url returns url built using ORIGINAL_FULLPATH" do + request = stub_request('ORIGINAL_FULLPATH' => "/foo?bar", + 'HTTP_HOST' => "example.org", + 'rack.url_scheme' => "http") + + url = request.original_url + assert_equal "http://example.org/foo?bar", url + end + + test "original_fullpath returns fullpath if ORIGINAL_FULLPATH is not present" do + request = stub_request('PATH_INFO' => "/foo", + 'QUERY_STRING' => "bar") + + path = request.original_fullpath + assert_equal "/foo?bar", path + end +end + +class RequestHost < BaseRequestTest test "host with default port" do request = stub_request 'HTTP_HOST' => 'rubyonrails.org:80' assert_equal "rubyonrails.org", request.host_with_port @@ -364,15 +423,174 @@ class RequestTest < ActiveSupport::TestCase assert_equal "rubyonrails.org:81", request.host_with_port end - test "server software" do - request = stub_request - assert_equal nil, request.server_software + test "proxy request" do + request = stub_request 'HTTP_HOST' => 'glu.ttono.us:80' + assert_equal "glu.ttono.us", request.host_with_port + end + + test "http host" do + request = stub_request 'HTTP_HOST' => "rubyonrails.org:8080" + assert_equal "rubyonrails.org", request.host + assert_equal "rubyonrails.org:8080", request.host_with_port + + request = stub_request 'HTTP_X_FORWARDED_HOST' => "www.firsthost.org, www.secondhost.org" + assert_equal "www.secondhost.org", request.host + end + + test "http host with default port overrides server port" do + request = stub_request 'HTTP_HOST' => "rubyonrails.org" + assert_equal "rubyonrails.org", request.host_with_port + end + + test "host with port if http standard port is specified" do + request = stub_request 'HTTP_X_FORWARDED_HOST' => "glu.ttono.us:80" + assert_equal "glu.ttono.us", request.host_with_port + end + + test "host with port if https standard port is specified" do + request = stub_request( + 'HTTP_X_FORWARDED_PROTO' => "https", + 'HTTP_X_FORWARDED_HOST' => "glu.ttono.us:443" + ) + assert_equal "glu.ttono.us", request.host_with_port + end + + test "host if ipv6 reference" do + request = stub_request 'HTTP_HOST' => "[2001:1234:5678:9abc:def0::dead:beef]" + assert_equal "[2001:1234:5678:9abc:def0::dead:beef]", request.host + end + + test "host if ipv6 reference with port" do + request = stub_request 'HTTP_HOST' => "[2001:1234:5678:9abc:def0::dead:beef]:8008" + assert_equal "[2001:1234:5678:9abc:def0::dead:beef]", request.host + end +end + +class RequestCGI < BaseRequestTest + test "CGI environment variables" do + request = stub_request( + "AUTH_TYPE" => "Basic", + "GATEWAY_INTERFACE" => "CGI/1.1", + "HTTP_ACCEPT" => "*/*", + "HTTP_ACCEPT_CHARSET" => "UTF-8", + "HTTP_ACCEPT_ENCODING" => "gzip, deflate", + "HTTP_ACCEPT_LANGUAGE" => "en", + "HTTP_CACHE_CONTROL" => "no-cache, max-age=0", + "HTTP_FROM" => "googlebot", + "HTTP_HOST" => "glu.ttono.us:8007", + "HTTP_NEGOTIATE" => "trans", + "HTTP_PRAGMA" => "no-cache", + "HTTP_REFERER" => "http://www.google.com/search?q=glu.ttono.us", + "HTTP_USER_AGENT" => "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en)", + "PATH_INFO" => "/homepage/", + "PATH_TRANSLATED" => "/home/kevinc/sites/typo/public/homepage/", + "QUERY_STRING" => "", + "REMOTE_ADDR" => "207.7.108.53", + "REMOTE_HOST" => "google.com", + "REMOTE_IDENT" => "kevin", + "REMOTE_USER" => "kevin", + "REQUEST_METHOD" => "GET", + "SCRIPT_NAME" => "/dispatch.fcgi", + "SERVER_NAME" => "glu.ttono.us", + "SERVER_PORT" => "8007", + "SERVER_PROTOCOL" => "HTTP/1.1", + "SERVER_SOFTWARE" => "lighttpd/1.4.5", + ) + + assert_equal "Basic", request.auth_type + assert_equal 0, request.content_length + assert_equal nil, request.content_mime_type + assert_equal "CGI/1.1", request.gateway_interface + assert_equal "*/*", request.accept + assert_equal "UTF-8", request.accept_charset + assert_equal "gzip, deflate", request.accept_encoding + assert_equal "en", request.accept_language + assert_equal "no-cache, max-age=0", request.cache_control + assert_equal "googlebot", request.from + assert_equal "glu.ttono.us", request.host + assert_equal "trans", request.negotiate + assert_equal "no-cache", request.pragma + assert_equal "http://www.google.com/search?q=glu.ttono.us", request.referer + assert_equal "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en)", request.user_agent + assert_equal "/homepage/", request.path_info + assert_equal "/home/kevinc/sites/typo/public/homepage/", request.path_translated + assert_equal "", request.query_string + assert_equal "207.7.108.53", request.remote_addr + assert_equal "google.com", request.remote_host + assert_equal "kevin", request.remote_ident + assert_equal "kevin", request.remote_user + assert_equal "GET", request.request_method + assert_equal "/dispatch.fcgi", request.script_name + assert_equal "glu.ttono.us", request.server_name + assert_equal 8007, request.server_port + assert_equal "HTTP/1.1", request.server_protocol + assert_equal "lighttpd", request.server_software + end +end + +class RequestCookie < BaseRequestTest + test "cookie syntax resilience" do + request = stub_request("HTTP_COOKIE" => "_session_id=c84ace84796670c052c6ceb2451fb0f2; is_admin=yes") + assert_equal "c84ace84796670c052c6ceb2451fb0f2", request.cookies["_session_id"], request.cookies.inspect + assert_equal "yes", request.cookies["is_admin"], request.cookies.inspect + + # some Nokia phone browsers omit the space after the semicolon separator. + # some developers have grown accustomed to using comma in cookie values. + request = stub_request("HTTP_COOKIE"=>"_session_id=c84ace847,96670c052c6ceb2451fb0f2;is_admin=yes") + assert_equal "c84ace847", request.cookies["_session_id"], request.cookies.inspect + assert_equal "yes", request.cookies["is_admin"], request.cookies.inspect + end +end - request = stub_request 'SERVER_SOFTWARE' => 'Apache3.422' - assert_equal 'apache', request.server_software +class RequestParamsParsing < BaseRequestTest + test "doesnt break when content type has charset" do + request = stub_request( + 'REQUEST_METHOD' => 'POST', + 'CONTENT_LENGTH' => "flamenco=love".length, + 'CONTENT_TYPE' => 'application/x-www-form-urlencoded; charset=utf-8', + 'rack.input' => StringIO.new("flamenco=love") + ) - request = stub_request 'SERVER_SOFTWARE' => 'lighttpd(1.1.4)' - assert_equal 'lighttpd', request.server_software + assert_equal({"flamenco"=> "love"}, request.request_parameters) + end + + test "doesnt interpret request uri as query string when missing" do + request = stub_request('REQUEST_URI' => 'foo') + assert_equal({}, request.query_parameters) + end +end + +class RequestRewind < BaseRequestTest + test "body should be rewound" do + data = 'rewind' + env = { + 'rack.input' => StringIO.new(data), + 'CONTENT_LENGTH' => data.length, + 'CONTENT_TYPE' => 'application/x-www-form-urlencoded; charset=utf-8' + } + + # Read the request body by parsing params. + request = stub_request(env) + request.request_parameters + + # Should have rewound the body. + assert_equal 0, request.body.pos + end + + test "raw_post rewinds rack.input if RAW_POST_DATA is nil" do + request = stub_request( + 'rack.input' => StringIO.new("raw"), + 'CONTENT_LENGTH' => 3 + ) + assert_equal "raw", request.raw_post + assert_equal "raw", request.env['rack.input'].read + end +end + +class RequestProtocol < BaseRequestTest + test "server software" do + assert_equal 'lighttpd', stub_request('SERVER_SOFTWARE' => 'lighttpd/1.4.5').server_software + assert_equal 'apache', stub_request('SERVER_SOFTWARE' => 'Apache3.422').server_software end test "xml http request" do @@ -391,19 +609,12 @@ class RequestTest < ActiveSupport::TestCase end test "reports ssl" do - request = stub_request - assert !request.ssl? - - request = stub_request 'HTTPS' => 'on' - assert request.ssl? + assert !stub_request.ssl? + assert stub_request('HTTPS' => 'on').ssl? end test "reports ssl when proxied via lighttpd" do - request = stub_request - assert !request.ssl? - - request = stub_request 'HTTP_X_FORWARDED_PROTO' => 'https' - assert request.ssl? + assert stub_request('HTTP_X_FORWARDED_PROTO' => 'https').ssl? end test "scheme returns https when proxied" do @@ -411,63 +622,72 @@ class RequestTest < ActiveSupport::TestCase assert !request.ssl? assert_equal 'http', request.scheme - request = stub_request 'rack.url_scheme' => 'http', 'HTTP_X_FORWARDED_PROTO' => 'https' + request = stub_request( + 'rack.url_scheme' => 'http', + 'HTTP_X_FORWARDED_PROTO' => 'https' + ) assert request.ssl? assert_equal 'https', request.scheme end +end - test "String request methods" do - [:get, :post, :patch, :put, :delete].each do |method| - request = stub_request 'REQUEST_METHOD' => method.to_s.upcase - assert_equal method.to_s.upcase, request.method - end - end +class RequestMethod < BaseRequestTest + test "request methods" do + [:post, :get, :patch, :put, :delete].each do |method| + request = stub_request('REQUEST_METHOD' => method.to_s.upcase) - test "Symbol forms of request methods via method_symbol" do - [:get, :post, :patch, :put, :delete].each do |method| - request = stub_request 'REQUEST_METHOD' => method.to_s.upcase + assert_equal method.to_s.upcase, request.method assert_equal method, request.method_symbol end end test "invalid http method raises exception" do assert_raise(ActionController::UnknownHttpMethod) do - request = stub_request 'REQUEST_METHOD' => 'RANDOM_METHOD' - request.request_method + stub_request('REQUEST_METHOD' => 'RANDOM_METHOD').request_method end end test "allow method hacking on post" do %w(GET OPTIONS PATCH PUT POST DELETE).each do |method| - request = stub_request "REQUEST_METHOD" => method.to_s.upcase + request = stub_request 'REQUEST_METHOD' => method.to_s.upcase + assert_equal(method == "HEAD" ? "GET" : method, request.method) end end test "invalid method hacking on post raises exception" do assert_raise(ActionController::UnknownHttpMethod) do - request = stub_request "REQUEST_METHOD" => "_RANDOM_METHOD" - request.request_method + stub_request('REQUEST_METHOD' => '_RANDOM_METHOD').request_method end end test "restrict method hacking" do [:get, :patch, :put, :delete].each do |method| - request = stub_request 'REQUEST_METHOD' => method.to_s.upcase, - 'action_dispatch.request.request_parameters' => { :_method => 'put' } + request = stub_request( + 'action_dispatch.request.request_parameters' => { :_method => 'put' }, + 'REQUEST_METHOD' => method.to_s.upcase + ) + assert_equal method.to_s.upcase, request.method end end test "post masquerading as patch" do - request = stub_request 'REQUEST_METHOD' => 'PATCH', "rack.methodoverride.original_method" => "POST" + request = stub_request( + 'REQUEST_METHOD' => 'PATCH', + "rack.methodoverride.original_method" => "POST" + ) + assert_equal "POST", request.method assert_equal "PATCH", request.request_method assert request.patch? end test "post masquerading as put" do - request = stub_request 'REQUEST_METHOD' => 'PUT', "rack.methodoverride.original_method" => "POST" + request = stub_request( + 'REQUEST_METHOD' => 'PUT', + "rack.methodoverride.original_method" => "POST" + ) assert_equal "POST", request.method assert_equal "PUT", request.request_method assert request.put? @@ -493,7 +713,9 @@ class RequestTest < ActiveSupport::TestCase end end end +end +class RequestFormat < BaseRequestTest test "xml format" do request = stub_request request.expects(:parameters).at_least_once.returns({ :format => 'xml' }) @@ -513,109 +735,55 @@ class RequestTest < ActiveSupport::TestCase end test "XMLHttpRequest" do - request = stub_request 'HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest', - 'HTTP_ACCEPT' => - [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(",") + request = stub_request( + 'HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest', + 'HTTP_ACCEPT' => [Mime::JS, Mime::HTML, Mime::XML, "text/xml", Mime::ALL].join(",") + ) request.expects(:parameters).at_least_once.returns({}) assert request.xhr? assert_equal Mime::JS, request.format end - test "content type" do - request = stub_request 'CONTENT_TYPE' => 'text/html' - assert_equal Mime::HTML, request.content_mime_type - end - - test "can override format with parameter" do + test "can override format with parameter negative" do request = stub_request request.expects(:parameters).at_least_once.returns({ :format => :txt }) assert !request.format.xml? + end + test "can override format with parameter positive" do request = stub_request request.expects(:parameters).at_least_once.returns({ :format => :xml }) assert request.format.xml? end - test "no content type" do - request = stub_request - assert_equal nil, request.content_mime_type - end - - test "content type is XML" do - request = stub_request 'CONTENT_TYPE' => 'application/xml' - assert_equal Mime::XML, request.content_mime_type - end - - test "content type with charset" do - request = stub_request 'CONTENT_TYPE' => 'application/xml; charset=UTF-8' - assert_equal Mime::XML, request.content_mime_type - end - - test "user agent" do - request = stub_request 'HTTP_USER_AGENT' => 'TestAgent' - assert_equal 'TestAgent', request.user_agent - end - - test "parameters" do - request = stub_request - request.stubs(:request_parameters).returns({ "foo" => 1 }) - request.stubs(:query_parameters).returns({ "bar" => 2 }) - - assert_equal({"foo" => 1, "bar" => 2}, request.parameters) - assert_equal({"foo" => 1}, request.request_parameters) - assert_equal({"bar" => 2}, request.query_parameters) - end - - test "parameters still accessible after rack parse error" do - mock_rack_env = { "QUERY_STRING" => "x[y]=1&x[y][][w]=2", "rack.input" => "foo" } - request = nil - request = stub_request(mock_rack_env) - - assert_raises(ActionController::BadRequest) do - # rack will raise a TypeError when parsing this query string - request.parameters - end - - assert_equal({}, request.parameters) - end - - test "we have access to the original exception" do - mock_rack_env = { "QUERY_STRING" => "x[y]=1&x[y][][w]=2", "rack.input" => "foo" } - request = nil - request = stub_request(mock_rack_env) - - e = assert_raises(ActionController::BadRequest) do - # rack will raise a TypeError when parsing this query string - request.parameters - end - - assert e.original_exception - assert_equal e.original_exception.backtrace, e.backtrace - end - - test "formats with accept header" do + test "formats text/html with accept header" do request = stub_request 'HTTP_ACCEPT' => 'text/html' - request.expects(:parameters).at_least_once.returns({}) assert_equal [Mime::HTML], request.formats + end + test "formats blank with accept header" do request = stub_request 'HTTP_ACCEPT' => '' - request.expects(:parameters).at_least_once.returns({}) assert_equal [Mime::HTML], request.formats + end - request = stub_request 'HTTP_ACCEPT' => '', - 'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest" - request.expects(:parameters).at_least_once.returns({}) + test "formats XMLHttpRequest with accept header" do + request = stub_request 'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest" assert_equal [Mime::JS], request.formats - - request = stub_request 'CONTENT_TYPE' => 'application/xml; charset=UTF-8', - 'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest" - request.expects(:parameters).at_least_once.returns({}) + end + + test "formats application/xml with accept header" do + request = stub_request('CONTENT_TYPE' => 'application/xml; charset=UTF-8', + 'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest") assert_equal [Mime::XML], request.formats + end + test "formats format:text with accept header" do request = stub_request request.expects(:parameters).at_least_once.returns({ :format => :txt }) assert_equal [Mime::TEXT], request.formats + end + test "formats format:unknown with accept header" do request = stub_request request.expects(:parameters).at_least_once.returns({ :format => :unknown }) assert_instance_of Mime::NullType, request.format @@ -624,10 +792,10 @@ class RequestTest < ActiveSupport::TestCase test "format is not nil with unknown format" do request = stub_request request.expects(:parameters).at_least_once.returns({ format: :hello }) - assert_equal request.format.nil?, true - assert_equal request.format.html?, false - assert_equal request.format.xml?, false - assert_equal request.format.json?, false + assert request.format.nil? + assert_not request.format.html? + assert_not request.format.xml? + assert_not request.format.json? end test "formats with xhr request" do @@ -669,30 +837,87 @@ class RequestTest < ActiveSupport::TestCase ActionDispatch::Request.ignore_accept_header = false end end +end - test "negotiate_mime" do - request = stub_request 'HTTP_ACCEPT' => 'text/html', - 'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest" +class RequestMimeType < BaseRequestTest + test "content type" do + assert_equal Mime::HTML, stub_request('CONTENT_TYPE' => 'text/html').content_mime_type + end - request.expects(:parameters).at_least_once.returns({}) + test "no content type" do + assert_equal nil, stub_request.content_mime_type + end + + test "content type is XML" do + assert_equal Mime::XML, stub_request('CONTENT_TYPE' => 'application/xml').content_mime_type + end + + test "content type with charset" do + assert_equal Mime::XML, stub_request('CONTENT_TYPE' => 'application/xml; charset=UTF-8').content_mime_type + end + + test "user agent" do + assert_equal 'TestAgent', stub_request('HTTP_USER_AGENT' => 'TestAgent').user_agent + end + + test "negotiate_mime" do + request = stub_request( + 'HTTP_ACCEPT' => 'text/html', + 'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest" + ) assert_equal nil, request.negotiate_mime([Mime::XML, Mime::JSON]) assert_equal Mime::HTML, request.negotiate_mime([Mime::XML, Mime::HTML]) assert_equal Mime::HTML, request.negotiate_mime([Mime::XML, Mime::ALL]) + end + + test "negotiate_mime with content_type" do + request = stub_request( + 'CONTENT_TYPE' => 'application/xml; charset=UTF-8', + 'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest" + ) - request = stub_request 'CONTENT_TYPE' => 'application/xml; charset=UTF-8', - 'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest" - request.expects(:parameters).at_least_once.returns({}) assert_equal Mime::XML, request.negotiate_mime([Mime::XML, Mime::CSV]) end +end - test "raw_post rewinds rack.input if RAW_POST_DATA is nil" do - request = stub_request('rack.input' => StringIO.new("foo"), - 'CONTENT_LENGTH' => 3) - assert_equal "foo", request.raw_post - assert_equal "foo", request.env['rack.input'].read +class RequestParameters < BaseRequestTest + test "parameters" do + request = stub_request + request.expects(:request_parameters).at_least_once.returns({ "foo" => 1 }) + request.expects(:query_parameters).at_least_once.returns({ "bar" => 2 }) + + assert_equal({"foo" => 1, "bar" => 2}, request.parameters) + assert_equal({"foo" => 1}, request.request_parameters) + assert_equal({"bar" => 2}, request.query_parameters) + end + + test "parameters still accessible after rack parse error" do + request = stub_request("QUERY_STRING" => "x[y]=1&x[y][][w]=2") + + assert_raises(ActionController::BadRequest) do + # rack will raise a TypeError when parsing this query string + request.parameters + end + + assert_equal({}, request.parameters) end + test "we have access to the original exception" do + request = stub_request("QUERY_STRING" => "x[y]=1&x[y][][w]=2") + + e = assert_raises(ActionController::BadRequest) do + # rack will raise a TypeError when parsing this query string + request.parameters + end + + assert e.original_exception + assert_equal e.original_exception.backtrace, e.backtrace + end +end + + +class RequestParameterFilter < BaseRequestTest test "process parameter filter" do test_hashes = [ [{'foo'=>'bar'},{'foo'=>'bar'},%w'food'], @@ -721,9 +946,14 @@ class RequestTest < ActiveSupport::TestCase end test "filtered_parameters returns params filtered" do - request = stub_request('action_dispatch.request.parameters' => - { 'lifo' => 'Pratik', 'amount' => '420', 'step' => '1' }, - 'action_dispatch.parameter_filter' => [:lifo, :amount]) + request = stub_request( + 'action_dispatch.request.parameters' => { + 'lifo' => 'Pratik', + 'amount' => '420', + 'step' => '1' + }, + 'action_dispatch.parameter_filter' => [:lifo, :amount] + ) params = request.filtered_parameters assert_equal "[FILTERED]", params["lifo"] @@ -732,10 +962,14 @@ class RequestTest < ActiveSupport::TestCase end test "filtered_env filters env as a whole" do - request = stub_request('action_dispatch.request.parameters' => - { 'amount' => '420', 'step' => '1' }, "RAW_POST_DATA" => "yada yada", - 'action_dispatch.parameter_filter' => [:lifo, :amount]) - + request = stub_request( + 'action_dispatch.request.parameters' => { + 'amount' => '420', + 'step' => '1' + }, + "RAW_POST_DATA" => "yada yada", + 'action_dispatch.parameter_filter' => [:lifo, :amount] + ) request = stub_request(request.filtered_env) assert_equal "[FILTERED]", request.raw_post @@ -745,9 +979,11 @@ class RequestTest < ActiveSupport::TestCase test "filtered_path returns path with filtered query string" do %w(; &).each do |sep| - request = stub_request('QUERY_STRING' => %w(username=sikachu secret=bd4f21f api_key=b1bc3b3cd352f68d79d7).join(sep), + request = stub_request( + 'QUERY_STRING' => %w(username=sikachu secret=bd4f21f api_key=b1bc3b3cd352f68d79d7).join(sep), 'PATH_INFO' => '/authenticate', - 'action_dispatch.parameter_filter' => [:secret, :api_key]) + 'action_dispatch.parameter_filter' => [:secret, :api_key] + ) path = request.filtered_path assert_equal %w(/authenticate?username=sikachu secret=[FILTERED] api_key=[FILTERED]).join(sep), path @@ -755,56 +991,40 @@ class RequestTest < ActiveSupport::TestCase end test "filtered_path should not unescape a genuine '[FILTERED]' value" do - request = stub_request('QUERY_STRING' => "secret=bd4f21f&genuine=%5BFILTERED%5D", + request = stub_request( + 'QUERY_STRING' => "secret=bd4f21f&genuine=%5BFILTERED%5D", 'PATH_INFO' => '/authenticate', - 'action_dispatch.parameter_filter' => [:secret]) + 'action_dispatch.parameter_filter' => [:secret] + ) path = request.filtered_path - assert_equal "/authenticate?secret=[FILTERED]&genuine=%5BFILTERED%5D", path + assert_equal request.script_name + "/authenticate?secret=[FILTERED]&genuine=%5BFILTERED%5D", path end test "filtered_path should preserve duplication of keys in query string" do - request = stub_request('QUERY_STRING' => "username=sikachu&secret=bd4f21f&username=fxn", + request = stub_request( + 'QUERY_STRING' => "username=sikachu&secret=bd4f21f&username=fxn", 'PATH_INFO' => '/authenticate', - 'action_dispatch.parameter_filter' => [:secret]) + 'action_dispatch.parameter_filter' => [:secret] + ) path = request.filtered_path - assert_equal "/authenticate?username=sikachu&secret=[FILTERED]&username=fxn", path + assert_equal request.script_name + "/authenticate?username=sikachu&secret=[FILTERED]&username=fxn", path end test "filtered_path should ignore searchparts" do - request = stub_request('QUERY_STRING' => "secret", + request = stub_request( + 'QUERY_STRING' => "secret", 'PATH_INFO' => '/authenticate', - 'action_dispatch.parameter_filter' => [:secret]) + 'action_dispatch.parameter_filter' => [:secret] + ) path = request.filtered_path - assert_equal "/authenticate?secret", path - end - - test "original_fullpath returns ORIGINAL_FULLPATH" do - request = stub_request('ORIGINAL_FULLPATH' => "/foo?bar") - - path = request.original_fullpath - assert_equal "/foo?bar", path - end - - test "original_url returns url built using ORIGINAL_FULLPATH" do - request = stub_request('ORIGINAL_FULLPATH' => "/foo?bar", - 'HTTP_HOST' => "example.org", - 'rack.url_scheme' => "http") - - url = request.original_url - assert_equal "http://example.org/foo?bar", url - end - - test "original_fullpath returns fullpath if ORIGINAL_FULLPATH is not present" do - request = stub_request('PATH_INFO' => "/foo", - 'QUERY_STRING' => "bar") - - path = request.original_fullpath - assert_equal "/foo?bar", path + assert_equal request.script_name + "/authenticate?secret", path end +end +class RequestEtag < BaseRequestTest test "if_none_match_etags none" do request = stub_request @@ -843,16 +1063,31 @@ class RequestTest < ActiveSupport::TestCase assert request.etag_matches?(etag), etag end end +end + +class RequestVarient < BaseRequestTest + test "setting variant" do + request = stub_request + + request.variant = :mobile + assert_equal [:mobile], request.variant -protected + request.variant = [:phone, :tablet] + assert_equal [:phone, :tablet], request.variant - def stub_request(env = {}) - ip_spoofing_check = env.key?(:ip_spoofing_check) ? env.delete(:ip_spoofing_check) : true - @trusted_proxies ||= nil - ip_app = ActionDispatch::RemoteIp.new(Proc.new { }, ip_spoofing_check, @trusted_proxies) - tld_length = env.key?(:tld_length) ? env.delete(:tld_length) : 1 - ip_app.call(env) - ActionDispatch::Http::URL.tld_length = tld_length - ActionDispatch::Request.new(env) + assert_raise ArgumentError do + request.variant = [:phone, "tablet"] + end + + assert_raise ArgumentError do + request.variant = "yolo" + end + end + + test "setting variant with non symbol value" do + request = stub_request + assert_raise ArgumentError do + request.variant = "mobile" + end end end diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb index 4501ea095c..959a3bc5cd 100644 --- a/actionpack/test/dispatch/response_test.rb +++ b/actionpack/test/dispatch/response_test.rb @@ -217,6 +217,24 @@ class ResponseTest < ActiveSupport::TestCase assert_not @response.respond_to?(:method_missing) assert @response.respond_to?(:method_missing, true) end + + test "can be destructured into status, headers and an enumerable body" do + response = ActionDispatch::Response.new(404, { 'Content-Type' => 'text/plain' }, ['Not Found']) + status, headers, body = response + + assert_equal 404, status + assert_equal({ 'Content-Type' => 'text/plain' }, headers) + assert_equal ['Not Found'], body.each.to_a + end + + test "[response].flatten does not recurse infinitely" do + Timeout.timeout(1) do # use a timeout to prevent it stalling indefinitely + status, headers, body = [@response].flatten + assert_equal @response.status, status + assert_equal @response.headers, headers + assert_equal @response.body, body.each.to_a.join + end + end end class ResponseIntegrationTest < ActionDispatch::IntegrationTest diff --git a/actionpack/test/dispatch/routing/inspector_test.rb b/actionpack/test/dispatch/routing/inspector_test.rb index c8038bbd7c..ff33dd5652 100644 --- a/actionpack/test/dispatch/routing/inspector_test.rb +++ b/actionpack/test/dispatch/routing/inspector_test.rb @@ -54,6 +54,27 @@ module ActionDispatch ], output end + def test_displaying_routes_for_engines_without_routes + engine = Class.new(Rails::Engine) do + def self.inspect + "Blog::Engine" + end + end + engine.routes.draw do + end + + output = draw do + mount engine => "/blog", as: "blog" + end + + assert_equal [ + "Prefix Verb URI Pattern Controller#Action", + " blog /blog Blog::Engine", + "", + "Routes for Blog::Engine:" + ], output + end + def test_cart_inspect output = draw do get '/cart', :to => 'cart#show' @@ -160,6 +181,29 @@ module ActionDispatch ], output end + def test_rake_routes_shows_routes_with_dashes + output = draw do + get 'about-us' => 'pages#about_us' + get 'our-work/latest' + + resources :photos, only: [:show] do + get 'user-favorites', on: :collection + get 'preview-photo', on: :member + get 'summary-text' + end + end + + assert_equal [ + " Prefix Verb URI Pattern Controller#Action", + " about_us GET /about-us(.:format) pages#about_us", + " our_work_latest GET /our-work/latest(.:format) our_work#latest", + "user_favorites_photos GET /photos/user-favorites(.:format) photos#user_favorites", + " preview_photo_photo GET /photos/:id/preview-photo(.:format) photos#preview_photo", + " photo_summary_text GET /photos/:photo_id/summary-text(.:format) photos#summary_text", + " photo GET /photos/:id(.:format) photos#show" + ], output + end + class RackApp def self.call(env) end @@ -203,6 +247,18 @@ module ActionDispatch assert_no_match(/\/sprockets/, output.first) end + def test_rake_routes_shows_route_defined_in_under_assets_prefix + output = draw do + scope '/sprockets' do + get '/foo' => 'foo#bar' + end + end + assert_equal [ + "Prefix Verb URI Pattern Controller#Action", + " foo GET /sprockets/foo(.:format) foo#bar" + ], output + end + def test_redirect output = draw do get "/foo" => redirect("/foo/bar"), :constraints => { :subdomain => "admin" } diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index aac808afda..0a13dcfad4 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -1031,6 +1031,136 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest assert_equal 'users/home#index', @response.body end + def test_namespaced_shallow_routes_with_module_option + draw do + namespace :foo, module: 'bar' do + resources :posts, only: [:index, :show] do + resources :comments, only: [:index, :show], shallow: true + end + end + end + + get '/foo/posts' + assert_equal '/foo/posts', foo_posts_path + assert_equal 'bar/posts#index', @response.body + + get '/foo/posts/1' + assert_equal '/foo/posts/1', foo_post_path('1') + assert_equal 'bar/posts#show', @response.body + + get '/foo/posts/1/comments' + assert_equal '/foo/posts/1/comments', foo_post_comments_path('1') + assert_equal 'bar/comments#index', @response.body + + get '/foo/comments/2' + assert_equal '/foo/comments/2', foo_comment_path('2') + assert_equal 'bar/comments#show', @response.body + end + + def test_namespaced_shallow_routes_with_path_option + draw do + namespace :foo, path: 'bar' do + resources :posts, only: [:index, :show] do + resources :comments, only: [:index, :show], shallow: true + end + end + end + + get '/bar/posts' + assert_equal '/bar/posts', foo_posts_path + assert_equal 'foo/posts#index', @response.body + + get '/bar/posts/1' + assert_equal '/bar/posts/1', foo_post_path('1') + assert_equal 'foo/posts#show', @response.body + + get '/bar/posts/1/comments' + assert_equal '/bar/posts/1/comments', foo_post_comments_path('1') + assert_equal 'foo/comments#index', @response.body + + get '/bar/comments/2' + assert_equal '/bar/comments/2', foo_comment_path('2') + assert_equal 'foo/comments#show', @response.body + end + + def test_namespaced_shallow_routes_with_as_option + draw do + namespace :foo, as: 'bar' do + resources :posts, only: [:index, :show] do + resources :comments, only: [:index, :show], shallow: true + end + end + end + + get '/foo/posts' + assert_equal '/foo/posts', bar_posts_path + assert_equal 'foo/posts#index', @response.body + + get '/foo/posts/1' + assert_equal '/foo/posts/1', bar_post_path('1') + assert_equal 'foo/posts#show', @response.body + + get '/foo/posts/1/comments' + assert_equal '/foo/posts/1/comments', bar_post_comments_path('1') + assert_equal 'foo/comments#index', @response.body + + get '/foo/comments/2' + assert_equal '/foo/comments/2', bar_comment_path('2') + assert_equal 'foo/comments#show', @response.body + end + + def test_namespaced_shallow_routes_with_shallow_path_option + draw do + namespace :foo, shallow_path: 'bar' do + resources :posts, only: [:index, :show] do + resources :comments, only: [:index, :show], shallow: true + end + end + end + + get '/foo/posts' + assert_equal '/foo/posts', foo_posts_path + assert_equal 'foo/posts#index', @response.body + + get '/foo/posts/1' + assert_equal '/foo/posts/1', foo_post_path('1') + assert_equal 'foo/posts#show', @response.body + + get '/foo/posts/1/comments' + assert_equal '/foo/posts/1/comments', foo_post_comments_path('1') + assert_equal 'foo/comments#index', @response.body + + get '/bar/comments/2' + assert_equal '/bar/comments/2', foo_comment_path('2') + assert_equal 'foo/comments#show', @response.body + end + + def test_namespaced_shallow_routes_with_shallow_prefix_option + draw do + namespace :foo, shallow_prefix: 'bar' do + resources :posts, only: [:index, :show] do + resources :comments, only: [:index, :show], shallow: true + end + end + end + + get '/foo/posts' + assert_equal '/foo/posts', foo_posts_path + assert_equal 'foo/posts#index', @response.body + + get '/foo/posts/1' + assert_equal '/foo/posts/1', foo_post_path('1') + assert_equal 'foo/posts#show', @response.body + + get '/foo/posts/1/comments' + assert_equal '/foo/posts/1/comments', foo_post_comments_path('1') + assert_equal 'foo/comments#index', @response.body + + get '/foo/comments/2' + assert_equal '/foo/comments/2', bar_comment_path('2') + assert_equal 'foo/comments#show', @response.body + end + def test_namespace_containing_numbers draw do namespace :v2 do @@ -1828,6 +1958,60 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest assert_equal '/comments/3/preview', preview_comment_path(:id => '3') end + def test_shallow_nested_resources_inside_resource + draw do + resource :membership, shallow: true do + resources :cards + end + end + + get '/membership/cards' + assert_equal 'cards#index', @response.body + assert_equal '/membership/cards', membership_cards_path + + get '/membership/cards/new' + assert_equal 'cards#new', @response.body + assert_equal '/membership/cards/new', new_membership_card_path + + post '/membership/cards' + assert_equal 'cards#create', @response.body + + get '/cards/1' + assert_equal 'cards#show', @response.body + assert_equal '/cards/1', card_path('1') + + get '/cards/1/edit' + assert_equal 'cards#edit', @response.body + assert_equal '/cards/1/edit', edit_card_path('1') + + put '/cards/1' + assert_equal 'cards#update', @response.body + + patch '/cards/1' + assert_equal 'cards#update', @response.body + + delete '/cards/1' + assert_equal 'cards#destroy', @response.body + end + + def test_shallow_deeply_nested_resources + draw do + resources :blogs do + resources :posts do + resources :comments, shallow: true + end + end + end + + get '/comments/1' + assert_equal 'comments#show', @response.body + + assert_equal '/comments/1', comment_path('1') + assert_equal '/blogs/new', new_blog_path + assert_equal '/blogs/1/posts/new', new_blog_post_path(:blog_id => 1) + assert_equal '/blogs/1/posts/2/comments/new', new_blog_post_comment_path(:blog_id => 1, :post_id => 2) + end + def test_shallow_nested_resources_within_scope draw do scope '/hello' do @@ -1889,6 +2073,65 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest assert_equal 'notes#destroy', @response.body end + def test_shallow_option_nested_resources_within_scope + draw do + scope '/hello' do + resources :notes, :shallow => true do + resources :trackbacks + end + end + end + + get '/hello/notes/1/trackbacks' + assert_equal 'trackbacks#index', @response.body + assert_equal '/hello/notes/1/trackbacks', note_trackbacks_path(:note_id => 1) + + get '/hello/notes/1/edit' + assert_equal 'notes#edit', @response.body + assert_equal '/hello/notes/1/edit', edit_note_path(:id => '1') + + get '/hello/notes/1/trackbacks/new' + assert_equal 'trackbacks#new', @response.body + assert_equal '/hello/notes/1/trackbacks/new', new_note_trackback_path(:note_id => 1) + + get '/hello/trackbacks/1' + assert_equal 'trackbacks#show', @response.body + assert_equal '/hello/trackbacks/1', trackback_path(:id => '1') + + get '/hello/trackbacks/1/edit' + assert_equal 'trackbacks#edit', @response.body + assert_equal '/hello/trackbacks/1/edit', edit_trackback_path(:id => '1') + + put '/hello/trackbacks/1' + assert_equal 'trackbacks#update', @response.body + + post '/hello/notes/1/trackbacks' + assert_equal 'trackbacks#create', @response.body + + delete '/hello/trackbacks/1' + assert_equal 'trackbacks#destroy', @response.body + + get '/hello/notes' + assert_equal 'notes#index', @response.body + + post '/hello/notes' + assert_equal 'notes#create', @response.body + + get '/hello/notes/new' + assert_equal 'notes#new', @response.body + assert_equal '/hello/notes/new', new_note_path + + get '/hello/notes/1' + assert_equal 'notes#show', @response.body + assert_equal '/hello/notes/1', note_path(:id => 1) + + put '/hello/notes/1' + assert_equal 'notes#update', @response.body + + delete '/hello/notes/1' + assert_equal 'notes#destroy', @response.body + end + def test_custom_resource_routes_are_scoped draw do resources :customers do @@ -2864,6 +3107,224 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest assert !@request.params[:action].frozen? end + def test_multiple_positional_args_with_the_same_name + draw do + get '/downloads/:id/:id.tar' => 'downloads#show', as: :download, format: false + end + + expected_params = { + controller: 'downloads', + action: 'show', + id: '1' + } + + get '/downloads/1/1.tar' + assert_equal 'downloads#show', @response.body + assert_equal expected_params, @request.symbolized_path_parameters + assert_equal '/downloads/1/1.tar', download_path('1') + assert_equal '/downloads/1/1.tar', download_path('1', '1') + end + + def test_absolute_controller_namespace + draw do + namespace :foo do + get '/', to: '/bar#index', as: 'root' + end + end + + get '/foo' + assert_equal 'bar#index', @response.body + assert_equal '/foo', foo_root_path + end + + def test_trailing_slash + draw do + resources :streams + end + + get '/streams' + assert @response.ok?, 'route without trailing slash should work' + + get '/streams/' + assert @response.ok?, 'route with trailing slash should work' + + get '/streams?foobar' + assert @response.ok?, 'route without trailing slash and with QUERY_STRING should work' + + get '/streams/?foobar' + assert @response.ok?, 'route with trailing slash and with QUERY_STRING should work' + end + + def test_route_with_dashes_in_path + draw do + get '/contact-us', to: 'pages#contact_us' + end + + get '/contact-us' + assert_equal 'pages#contact_us', @response.body + assert_equal '/contact-us', contact_us_path + end + + def test_shorthand_route_with_dashes_in_path + draw do + get '/about-us/index' + end + + get '/about-us/index' + assert_equal 'about_us#index', @response.body + assert_equal '/about-us/index', about_us_index_path + end + + def test_resource_routes_with_dashes_in_path + draw do + resources :photos, only: [:show] do + get 'user-favorites', on: :collection + get 'preview-photo', on: :member + get 'summary-text' + end + end + + get '/photos/user-favorites' + assert_equal 'photos#user_favorites', @response.body + assert_equal '/photos/user-favorites', user_favorites_photos_path + + get '/photos/1/preview-photo' + assert_equal 'photos#preview_photo', @response.body + assert_equal '/photos/1/preview-photo', preview_photo_photo_path('1') + + get '/photos/1/summary-text' + assert_equal 'photos#summary_text', @response.body + assert_equal '/photos/1/summary-text', photo_summary_text_path('1') + + get '/photos/1' + assert_equal 'photos#show', @response.body + assert_equal '/photos/1', photo_path('1') + end + + def test_shallow_path_inside_namespace_is_not_added_twice + draw do + namespace :admin do + shallow do + resources :posts do + resources :comments + end + end + end + end + + get '/admin/posts/1/comments' + assert_equal 'admin/comments#index', @response.body + assert_equal '/admin/posts/1/comments', admin_post_comments_path('1') + end + + def test_shallow_path_and_prefix_are_not_added_to_non_shallow_routes + draw do + scope shallow_path: 'projects', shallow_prefix: 'project' do + resources :projects do + resources :files, controller: 'project_files', shallow: true + end + end + end + + get '/projects' + assert_equal 'projects#index', @response.body + assert_equal '/projects', projects_path + + get '/projects/new' + assert_equal 'projects#new', @response.body + assert_equal '/projects/new', new_project_path + + post '/projects' + assert_equal 'projects#create', @response.body + + get '/projects/1' + assert_equal 'projects#show', @response.body + assert_equal '/projects/1', project_path('1') + + get '/projects/1/edit' + assert_equal 'projects#edit', @response.body + assert_equal '/projects/1/edit', edit_project_path('1') + + patch '/projects/1' + assert_equal 'projects#update', @response.body + + delete '/projects/1' + assert_equal 'projects#destroy', @response.body + + get '/projects/1/files' + assert_equal 'project_files#index', @response.body + assert_equal '/projects/1/files', project_files_path('1') + + get '/projects/1/files/new' + assert_equal 'project_files#new', @response.body + assert_equal '/projects/1/files/new', new_project_file_path('1') + + post '/projects/1/files' + assert_equal 'project_files#create', @response.body + + get '/projects/files/2' + assert_equal 'project_files#show', @response.body + assert_equal '/projects/files/2', project_file_path('2') + + get '/projects/files/2/edit' + assert_equal 'project_files#edit', @response.body + assert_equal '/projects/files/2/edit', edit_project_file_path('2') + + patch '/projects/files/2' + assert_equal 'project_files#update', @response.body + + delete '/projects/files/2' + assert_equal 'project_files#destroy', @response.body + end + + def test_scope_path_is_copied_to_shallow_path + draw do + scope path: 'foo' do + resources :posts do + resources :comments, shallow: true + end + end + end + + assert_equal '/foo/comments/1', comment_path('1') + end + + def test_scope_as_is_copied_to_shallow_prefix + draw do + scope as: 'foo' do + resources :posts do + resources :comments, shallow: true + end + end + end + + assert_equal '/comments/1', foo_comment_path('1') + end + + def test_scope_shallow_prefix_is_not_overwritten_by_as + draw do + scope as: 'foo', shallow_prefix: 'bar' do + resources :posts do + resources :comments, shallow: true + end + end + end + + assert_equal '/comments/1', bar_comment_path('1') + end + + def test_scope_shallow_path_is_not_overwritten_by_path + draw do + scope path: 'foo', shallow_path: 'bar' do + resources :posts do + resources :comments, shallow: true + end + end + end + + assert_equal '/bar/comments/1', comment_path('1') + end + private def draw(&block) @@ -3135,8 +3596,8 @@ class TestUriPathEscaping < ActionDispatch::IntegrationTest include Routes.url_helpers def app; Routes end - test 'escapes generated path segment' do - assert_equal '/a%20b/c+d', segment_path(:segment => 'a b/c+d') + test 'escapes slash in generated path segment' do + assert_equal '/a%20b%2Fc+d', segment_path(:segment => 'a b/c+d') end test 'unescapes recognized path segment' do @@ -3144,7 +3605,7 @@ class TestUriPathEscaping < ActionDispatch::IntegrationTest assert_equal 'a b/c+d', @response.body end - test 'escapes generated path splat' do + test 'does not escape slash in generated path splat' do assert_equal '/a%20b/c+d', splat_path(:splat => 'a b/c+d') end @@ -3327,6 +3788,10 @@ class TestOptimizedNamedRoutes < ActionDispatch::IntegrationTest ok = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, []] } get '/foo' => ok, as: :foo get '/post(/:action(/:id))' => ok, as: :posts + get '/:foo/:foo_type/bars/:id' => ok, as: :bar + get '/projects/:id.:format' => ok, as: :project + get '/pages/:id' => ok, as: :page + get '/wiki/*page' => ok, as: :wiki end end @@ -3349,6 +3814,36 @@ class TestOptimizedNamedRoutes < ActionDispatch::IntegrationTest assert_equal '/post', Routes.url_helpers.posts_path assert_equal '/post', posts_path end + + test 'segments with same prefix are replaced correctly' do + assert_equal '/foo/baz/bars/1', Routes.url_helpers.bar_path('foo', 'baz', '1') + assert_equal '/foo/baz/bars/1', bar_path('foo', 'baz', '1') + end + + test 'segments separated with a period are replaced correctly' do + assert_equal '/projects/1.json', Routes.url_helpers.project_path(1, :json) + assert_equal '/projects/1.json', project_path(1, :json) + end + + test 'segments with question marks are escaped' do + assert_equal '/pages/foo%3Fbar', Routes.url_helpers.page_path('foo?bar') + assert_equal '/pages/foo%3Fbar', page_path('foo?bar') + end + + test 'segments with slashes are escaped' do + assert_equal '/pages/foo%2Fbar', Routes.url_helpers.page_path('foo/bar') + assert_equal '/pages/foo%2Fbar', page_path('foo/bar') + end + + test 'glob segments with question marks are escaped' do + assert_equal '/wiki/foo%3Fbar', Routes.url_helpers.wiki_path('foo?bar') + assert_equal '/wiki/foo%3Fbar', wiki_path('foo?bar') + end + + test 'glob segments with slashes are not escaped' do + assert_equal '/wiki/foo/bar', Routes.url_helpers.wiki_path('foo/bar') + assert_equal '/wiki/foo/bar', wiki_path('foo/bar') + end end class TestNamedRouteUrlHelpers < ActionDispatch::IntegrationTest @@ -3616,6 +4111,19 @@ class TestFormatConstraints < ActionDispatch::IntegrationTest end end +class TestCallableConstraintValidation < ActionDispatch::IntegrationTest + def test_constraint_with_object_not_callable + assert_raises(ArgumentError) do + ActionDispatch::Routing::RouteSet.new.tap do |app| + app.draw do + ok = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, []] } + get '/test', to: ok, constraints: Object.new + end + end + end + end +end + class TestRouteDefaults < ActionDispatch::IntegrationTest stub_controllers do |routes| Routes = routes @@ -3696,3 +4204,28 @@ class TestRedirectRouteGeneration < ActionDispatch::IntegrationTest end end end + +class TestUrlGenerationErrors < ActionDispatch::IntegrationTest + Routes = ActionDispatch::Routing::RouteSet.new.tap do |app| + app.draw do + get "/products/:id" => 'products#show', :as => :product + end + end + + def app; Routes end + + include Routes.url_helpers + + test "url helpers raise a helpful error message whem generation fails" do + url, missing = { action: 'show', controller: 'products', id: nil }, [:id] + message = "No route matches #{url.inspect} missing required keys: #{missing.inspect}" + + # Optimized url helper + error = assert_raises(ActionController::UrlGenerationError){ product_path(nil) } + assert_equal message, error.message + + # Non-optimized url helper + error = assert_raises(ActionController::UrlGenerationError, message){ product_path(id: nil) } + assert_equal message, error.message + end +end diff --git a/actionpack/test/dispatch/session/mem_cache_store_test.rb b/actionpack/test/dispatch/session/mem_cache_store_test.rb index e53ce4195b..92544230b2 100644 --- a/actionpack/test/dispatch/session/mem_cache_store_test.rb +++ b/actionpack/test/dispatch/session/mem_cache_store_test.rb @@ -1,4 +1,5 @@ require 'abstract_unit' +require 'securerandom' # You need to start a memcached server inorder to run these tests class MemCacheStoreTest < ActionDispatch::IntegrationTest @@ -172,7 +173,7 @@ class MemCacheStoreTest < ActionDispatch::IntegrationTest end @app = self.class.build_app(set) do |middleware| - middleware.use ActionDispatch::Session::MemCacheStore, :key => '_session_id' + middleware.use ActionDispatch::Session::MemCacheStore, :key => '_session_id', :namespace => "mem_cache_store_test:#{SecureRandom.hex(10)}" middleware.delete "ActionDispatch::ShowExceptions" end diff --git a/actionpack/test/dispatch/ssl_test.rb b/actionpack/test/dispatch/ssl_test.rb index 94969f795a..c3598c5e8e 100644 --- a/actionpack/test/dispatch/ssl_test.rb +++ b/actionpack/test/dispatch/ssl_test.rb @@ -196,6 +196,13 @@ class SSLTest < ActionDispatch::IntegrationTest response.headers['Location'] end + def test_redirect_to_host_with_port + self.app = ActionDispatch::SSL.new(default_app, :host => "ssl.example.org:443") + get "http://example.org/path?key=value" + assert_equal "https://ssl.example.org:443/path?key=value", + response.headers['Location'] + end + def test_redirect_to_secure_host_when_on_subdomain self.app = ActionDispatch::SSL.new(default_app, :host => "ssl.example.org") get "http://ssl.example.org/path?key=value" diff --git a/actionpack/test/dispatch/static_test.rb b/actionpack/test/dispatch/static_test.rb index acccbcb2e6..afdda70748 100644 --- a/actionpack/test/dispatch/static_test.rb +++ b/actionpack/test/dispatch/static_test.rb @@ -37,10 +37,8 @@ module StaticTests end def test_served_static_file_with_non_english_filename - if RUBY_ENGINE == 'jruby ' - skip "Stop skipping if following bug gets fixed: " \ + jruby_skip "Stop skipping if following bug gets fixed: " \ "http://jira.codehaus.org/browse/JRUBY-7192" - end assert_html "means hello in Japanese\n", get("/foo/#{Rack::Utils.escape("こんにちは.html")}") end @@ -137,11 +135,16 @@ module StaticTests end def with_static_file(file) - path = "#{FIXTURE_LOAD_PATH}/public" + file - File.open(path, "wb+") { |f| f.write(file) } + path = "#{FIXTURE_LOAD_PATH}/#{public_path}" + file + begin + File.open(path, "wb+") { |f| f.write(file) } + rescue Errno::EPROTO + skip "Couldn't create a file #{path}" + end + yield file ensure - File.delete(path) + File.delete(path) if File.exist? path end end @@ -149,11 +152,24 @@ class StaticTest < ActiveSupport::TestCase DummyApp = lambda { |env| [200, {"Content-Type" => "text/plain"}, ["Hello, World!"]] } - App = ActionDispatch::Static.new(DummyApp, "#{FIXTURE_LOAD_PATH}/public", "public, max-age=60") def setup - @app = App + @app = ActionDispatch::Static.new(DummyApp, "#{FIXTURE_LOAD_PATH}/public", "public, max-age=60") + end + + def public_path + "public" end include StaticTests end + +class StaticEncodingTest < StaticTest + def setup + @app = ActionDispatch::Static.new(DummyApp, "#{FIXTURE_LOAD_PATH}/公共", "public, max-age=60") + end + + def public_path + "公共" + end +end diff --git a/actionpack/test/dispatch/uploaded_file_test.rb b/actionpack/test/dispatch/uploaded_file_test.rb index 72f3d1db0d..9f6381f118 100644 --- a/actionpack/test/dispatch/uploaded_file_test.rb +++ b/actionpack/test/dispatch/uploaded_file_test.rb @@ -33,6 +33,12 @@ module ActionDispatch assert_equal 'foo', uf.tempfile end + def test_to_io_returns_the_tempfile + tf = Object.new + uf = Http::UploadedFile.new(:tempfile => tf) + assert_equal tf, uf.to_io + end + def test_delegates_path_to_tempfile tf = Class.new { def path; 'thunderhorse' end } uf = Http::UploadedFile.new(:tempfile => tf.new) diff --git a/actionpack/test/dispatch/url_generation_test.rb b/actionpack/test/dispatch/url_generation_test.rb index f919592d24..910ff8a80f 100644 --- a/actionpack/test/dispatch/url_generation_test.rb +++ b/actionpack/test/dispatch/url_generation_test.rb @@ -64,18 +64,30 @@ module TestUrlGeneration test "port is extracted from the host" do assert_equal "http://www.example.com:8080/foo", foo_url(host: "www.example.com:8080", protocol: "http://") + assert_equal "//www.example.com:8080/foo", foo_url(host: "www.example.com:8080", protocol: "//") + assert_equal "//www.example.com:80/foo", foo_url(host: "www.example.com:80", protocol: "//") end - test "port option overides the host" do + test "port option is used" do + assert_equal "http://www.example.com:8080/foo", foo_url(host: "www.example.com", protocol: "http://", port: 8080) + assert_equal "//www.example.com:8080/foo", foo_url(host: "www.example.com", protocol: "//", port: 8080) + assert_equal "//www.example.com:80/foo", foo_url(host: "www.example.com", protocol: "//", port: 80) + end + + test "port option overrides the host" do assert_equal "http://www.example.com:8080/foo", foo_url(host: "www.example.com:8443", protocol: "http://", port: 8080) + assert_equal "//www.example.com:8080/foo", foo_url(host: "www.example.com:8443", protocol: "//", port: 8080) + assert_equal "//www.example.com:80/foo", foo_url(host: "www.example.com:443", protocol: "//", port: 80) end test "port option disables the host when set to nil" do assert_equal "http://www.example.com/foo", foo_url(host: "www.example.com:8443", protocol: "http://", port: nil) + assert_equal "//www.example.com/foo", foo_url(host: "www.example.com:8443", protocol: "//", port: nil) end test "port option disables the host when set to false" do assert_equal "http://www.example.com/foo", foo_url(host: "www.example.com:8443", protocol: "http://", port: false) + assert_equal "//www.example.com/foo", foo_url(host: "www.example.com:8443", protocol: "//", port: false) end test "keep subdomain when key is true" do |