diff options
Diffstat (limited to 'actionpack/test')
78 files changed, 1618 insertions, 932 deletions
diff --git a/actionpack/test/abstract/collector_test.rb b/actionpack/test/abstract/collector_test.rb index a4770b66e1..6db045fcd7 100644 --- a/actionpack/test/abstract/collector_test.rb +++ b/actionpack/test/abstract/collector_test.rb @@ -30,7 +30,7 @@ module AbstractController end test "register mime types on method missing" do - AbstractController::Collector.send(:remove_method, :js) + AbstractController::Collector.remove_method :js begin collector = MyCollector.new assert_not_respond_to collector, :js diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index 65dd28b3d7..1decfcee95 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -13,13 +13,6 @@ silence_warnings do Encoding.default_external = Encoding::UTF_8 end -require "drb" -begin - require "drb/unix" -rescue LoadError - puts "'drb/unix' is not available" -end - if ENV["TRAVIS"] PROCESS_COUNT = 0 else @@ -80,7 +73,7 @@ end module ActiveSupport class TestCase if RUBY_ENGINE == "ruby" && PROCESS_COUNT > 0 - parallelize_me! + parallelize(workers: PROCESS_COUNT) end end end @@ -103,6 +96,7 @@ class ActionDispatch::IntegrationTest < ActiveSupport::TestCase RoutedRackApp.new(routes || ActionDispatch::Routing::RouteSet.new) do |middleware| middleware.use ActionDispatch::ShowExceptions, ActionDispatch::PublicExceptions.new("#{FIXTURE_LOAD_PATH}/public") middleware.use ActionDispatch::DebugExceptions + middleware.use ActionDispatch::ActionableExceptions middleware.use ActionDispatch::Callbacks middleware.use ActionDispatch::Cookies middleware.use ActionDispatch::Flash @@ -341,7 +335,6 @@ module RoutingTestHelpers end private - def make_request(env) Request.new super, url_helpers, @block, strict end @@ -359,75 +352,6 @@ class ImagesController < ResourcesController; end require "active_support/testing/method_call_assertions" -class ForkingExecutor - class Server - include DRb::DRbUndumped - - def initialize - @queue = Queue.new - end - - def record(reporter, result) - reporter.record result - end - - def <<(o) - o[2] = DRbObject.new(o[2]) if o - @queue << o - end - def pop; @queue.pop; end - end - - def initialize(size) - @size = size - @queue = Server.new - @pool = nil - @url = DRb.start_service("drbunix:", @queue).uri - end - - def <<(work); @queue << work; end - - def shutdown - pool = @size.times.map { - fork { - DRb.stop_service - queue = DRbObject.new_with_uri @url - while job = queue.pop - klass = job[0] - method = job[1] - reporter = job[2] - result = Minitest.run_one_method klass, method - if result.error? - translate_exceptions result - end - queue.record reporter, result - end - } - } - @size.times { @queue << nil } - pool.each { |pid| Process.waitpid pid } - end - - private - def translate_exceptions(result) - result.failures.map! { |e| - begin - Marshal.dump e - e - rescue TypeError - ex = Exception.new e.message - ex.set_backtrace e.backtrace - Minitest::UnexpectedError.new ex - end - } - end -end - -if RUBY_ENGINE == "ruby" && PROCESS_COUNT > 0 - # Use N processes (N defaults to 4) - Minitest.parallel_executor = ForkingExecutor.new(PROCESS_COUNT) -end - class ActiveSupport::TestCase include ActiveSupport::Testing::MethodCallAssertions @@ -458,3 +382,5 @@ end class DrivenBySeleniumWithHeadlessFirefox < ActionDispatch::SystemTestCase driven_by :selenium, using: :headless_firefox end + +require_relative "../../tools/test_common" diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb index 763df3a776..51286155b9 100644 --- a/actionpack/test/controller/action_pack_assertions_test.rb +++ b/actionpack/test/controller/action_pack_assertions_test.rb @@ -276,16 +276,14 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase end def test_assert_redirect_failure_message_with_protocol_relative_url - begin - process :redirect_external_protocol_relative - assert_redirected_to "/foo" - rescue ActiveSupport::TestCase::Assertion => ex - assert_no_match( - /#{request.protocol}#{request.host}\/\/www.rubyonrails.org/, - ex.message, - "protocol relative url was incorrectly normalized" - ) - end + process :redirect_external_protocol_relative + assert_redirected_to "/foo" + rescue ActiveSupport::TestCase::Assertion => ex + assert_no_match( + /#{request.protocol}#{request.host}\/\/www.rubyonrails.org/, + ex.message, + "protocol relative URL was incorrectly normalized" + ) end def test_template_objects_exist diff --git a/actionpack/test/controller/api/conditional_get_test.rb b/actionpack/test/controller/api/conditional_get_test.rb index e366ce9532..f1cd9e46f9 100644 --- a/actionpack/test/controller/api/conditional_get_test.rb +++ b/actionpack/test/controller/api/conditional_get_test.rb @@ -18,7 +18,6 @@ class ConditionalGetApiController < ActionController::API end private - def handle_last_modified_and_etags fresh_when(last_modified: Time.now.utc.beginning_of_day, etag: [ :foo, 123 ]) end diff --git a/actionpack/test/controller/base_test.rb b/actionpack/test/controller/base_test.rb index 558e710df9..d8cea10153 100644 --- a/actionpack/test/controller/base_test.rb +++ b/actionpack/test/controller/base_test.rb @@ -193,7 +193,7 @@ class UrlOptionsTest < ActionController::TestCase action: "home", controller: "pages", only_path: true, - token: "secret" + params: { "token" => "secret" } } assert_equal "/home?token=secret", rs.url_for(options) diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb index 6fe036dd15..f09e812147 100644 --- a/actionpack/test/controller/caching_test.rb +++ b/actionpack/test/controller/caching_test.rb @@ -60,14 +60,6 @@ class FragmentCachingTest < ActionController::TestCase @m2v2 = ModelWithKeyAndVersion.new("model/2", "2") end - def test_fragment_cache_key - assert_deprecated do - assert_equal "views/what a key", @controller.fragment_cache_key("what a key") - assert_equal "views/test.host/fragment_caching_test/some_action", - @controller.fragment_cache_key(controller: "fragment_caching_test", action: "some_action") - end - end - def test_combined_fragment_cache_key assert_equal [ :views, "what a key" ], @controller.combined_fragment_cache_key("what a key") assert_equal [ :views, "test.host/fragment_caching_test/some_action" ], @@ -220,7 +212,7 @@ CACHED assert_equal expected_body, @response.body assert_equal "This bit's fragment cached", - @store.read("views/functional_caching/fragment_cached:#{template_digest("functional_caching/fragment_cached")}/fragment") + @store.read("views/functional_caching/fragment_cached:#{template_digest("functional_caching/fragment_cached", "html")}/fragment") end def test_fragment_caching_in_partials @@ -229,7 +221,7 @@ CACHED assert_match(/Old fragment caching in a partial/, @response.body) assert_match("Old fragment caching in a partial", - @store.read("views/functional_caching/_partial:#{template_digest("functional_caching/_partial")}/test.host/functional_caching/html_fragment_cached_with_partial")) + @store.read("views/functional_caching/_partial:#{template_digest("functional_caching/_partial", "html")}/test.host/functional_caching/html_fragment_cached_with_partial")) end def test_skipping_fragment_cache_digesting @@ -259,7 +251,7 @@ CACHED assert_match(/Some inline content/, @response.body) assert_match(/Some cached content/, @response.body) assert_match("Some cached content", - @store.read("views/functional_caching/inline_fragment_cached:#{template_digest("functional_caching/inline_fragment_cached")}/test.host/functional_caching/inline_fragment_cached")) + @store.read("views/functional_caching/inline_fragment_cached:#{template_digest("functional_caching/inline_fragment_cached", "html")}/test.host/functional_caching/inline_fragment_cached")) end def test_fragment_cache_instrumentation @@ -279,36 +271,39 @@ CACHED end def test_html_formatted_fragment_caching - get :formatted_fragment_cached, format: "html" + format = "html" + get :formatted_fragment_cached, format: format assert_response :success expected_body = "<body>\n<p>ERB</p>\n</body>\n" assert_equal expected_body, @response.body assert_equal "<p>ERB</p>", - @store.read("views/functional_caching/formatted_fragment_cached:#{template_digest("functional_caching/formatted_fragment_cached")}/fragment") + @store.read("views/functional_caching/formatted_fragment_cached:#{template_digest("functional_caching/formatted_fragment_cached", format)}/fragment") end def test_xml_formatted_fragment_caching - get :formatted_fragment_cached, format: "xml" + format = "xml" + get :formatted_fragment_cached, format: format assert_response :success expected_body = "<body>\n <p>Builder</p>\n</body>\n" assert_equal expected_body, @response.body assert_equal " <p>Builder</p>\n", - @store.read("views/functional_caching/formatted_fragment_cached:#{template_digest("functional_caching/formatted_fragment_cached")}/fragment") + @store.read("views/functional_caching/formatted_fragment_cached:#{template_digest("functional_caching/formatted_fragment_cached", format)}/fragment") end def test_fragment_caching_with_variant - get :formatted_fragment_cached_with_variant, format: "html", params: { v: :phone } + format = "html" + get :formatted_fragment_cached_with_variant, format: format, params: { v: :phone } assert_response :success expected_body = "<body>\n<p>PHONE</p>\n</body>\n" assert_equal expected_body, @response.body assert_equal "<p>PHONE</p>", - @store.read("views/functional_caching/formatted_fragment_cached_with_variant:#{template_digest("functional_caching/formatted_fragment_cached_with_variant")}/fragment") + @store.read("views/functional_caching/formatted_fragment_cached_with_variant:#{template_digest("functional_caching/formatted_fragment_cached_with_variant", format)}/fragment") end def test_fragment_caching_with_html_partials_in_xml @@ -317,8 +312,8 @@ CACHED end private - def template_digest(name) - ActionView::Digestor.digest(name: name, finder: @controller.lookup_context) + def template_digest(name, format) + ActionView::Digestor.digest(name: name, format: format, finder: @controller.lookup_context) end end diff --git a/actionpack/test/controller/content_type_test.rb b/actionpack/test/controller/content_type_test.rb index 636b025f2c..fcf767b706 100644 --- a/actionpack/test/controller/content_type_test.rb +++ b/actionpack/test/controller/content_type_test.rb @@ -66,73 +66,72 @@ class ContentTypeTest < ActionController::TestCase def test_render_defaults get :render_defaults assert_equal "utf-8", @response.charset - assert_equal Mime[:text], @response.content_type + assert_equal Mime[:text], @response.media_type end def test_render_changed_charset_default with_default_charset "utf-16" do get :render_defaults assert_equal "utf-16", @response.charset - assert_equal Mime[:text], @response.content_type + assert_equal Mime[:text], @response.media_type end end # :ported: def test_content_type_from_body get :render_content_type_from_body - assert_equal Mime[:rss], @response.content_type + assert_equal Mime[:rss], @response.media_type assert_equal "utf-8", @response.charset end # :ported: def test_content_type_from_render get :render_content_type_from_render - assert_equal Mime[:rss], @response.content_type + assert_equal Mime[:rss], @response.media_type assert_equal "utf-8", @response.charset end # :ported: def test_charset_from_body get :render_charset_from_body - assert_equal Mime[:text], @response.content_type + assert_equal Mime[:text], @response.media_type assert_equal "utf-16", @response.charset end # :ported: def test_nil_charset_from_body get :render_nil_charset_from_body - assert_equal Mime[:text], @response.content_type + assert_equal Mime[:text], @response.media_type assert_equal "utf-8", @response.charset, @response.headers.inspect end def test_nil_default_for_erb with_default_charset nil do get :render_default_for_erb - assert_equal Mime[:html], @response.content_type + assert_equal Mime[:html], @response.media_type assert_nil @response.charset, @response.headers.inspect end end def test_default_for_erb get :render_default_for_erb - assert_equal Mime[:html], @response.content_type + assert_equal Mime[:html], @response.media_type assert_equal "utf-8", @response.charset end def test_default_for_builder get :render_default_for_builder - assert_equal Mime[:xml], @response.content_type + assert_equal Mime[:xml], @response.media_type assert_equal "utf-8", @response.charset end def test_change_for_builder get :render_change_for_builder - assert_equal Mime[:html], @response.content_type + assert_equal Mime[:html], @response.media_type assert_equal "utf-8", @response.charset end private - def with_default_charset(charset) old_default_charset = ActionDispatch::Response.default_charset ActionDispatch::Response.default_charset = charset @@ -148,22 +147,22 @@ class AcceptBasedContentTypeTest < ActionController::TestCase def test_render_default_content_types_for_respond_to @request.accept = Mime[:html].to_s get :render_default_content_types_for_respond_to - assert_equal Mime[:html], @response.content_type + assert_equal Mime[:html], @response.media_type @request.accept = Mime[:js].to_s get :render_default_content_types_for_respond_to - assert_equal Mime[:js], @response.content_type + assert_equal Mime[:js], @response.media_type end def test_render_default_content_types_for_respond_to_with_template @request.accept = Mime[:xml].to_s get :render_default_content_types_for_respond_to - assert_equal Mime[:xml], @response.content_type + assert_equal Mime[:xml], @response.media_type end def test_render_default_content_types_for_respond_to_with_overwrite @request.accept = Mime[:rss].to_s get :render_default_content_types_for_respond_to - assert_equal Mime[:xml], @response.content_type + assert_equal Mime[:xml], @response.media_type end end diff --git a/actionpack/test/controller/filters_test.rb b/actionpack/test/controller/filters_test.rb index 425a6e25cc..40443a9397 100644 --- a/actionpack/test/controller/filters_test.rb +++ b/actionpack/test/controller/filters_test.rb @@ -310,7 +310,6 @@ class FilterTest < ActionController::TestCase after_action :conditional_in_parent_after, only: [:show, :another_action] private - def conditional_in_parent_before @ran_filter ||= [] @ran_filter << "conditional_in_parent_before" @@ -457,6 +456,7 @@ class FilterTest < ActionController::TestCase prepend_before_action :before_all prepend_after_action :after_all before_action :between_before_all_and_after_all + after_action :between_before_all_and_after_all def before_all @ran_filter ||= [] @@ -472,6 +472,7 @@ class FilterTest < ActionController::TestCase @ran_filter ||= [] @ran_filter << "between_before_all_and_after_all" end + def show render plain: "hello" end @@ -506,7 +507,6 @@ class FilterTest < ActionController::TestCase end private - def filter_one @filters ||= [] @filters << "filter_one" @@ -530,7 +530,6 @@ class FilterTest < ActionController::TestCase before_action :find_except, except: :edit private - def find_only @only = "Only" end @@ -765,7 +764,7 @@ class FilterTest < ActionController::TestCase def test_running_prepended_before_and_after_action test_process(PrependingBeforeAndAfterController) - assert_equal %w( before_all between_before_all_and_after_all after_all ), @controller.instance_variable_get(:@ran_filter) + assert_equal %w( before_all between_before_all_and_after_all between_before_all_and_after_all after_all ), @controller.instance_variable_get(:@ran_filter) end def test_skipping_and_limiting_controller @@ -886,7 +885,7 @@ class ControllerWithSymbolAsFilter < PostsController yield # Do stuff... - wtf += 1 + wtf + 1 end end @@ -998,16 +997,12 @@ class YieldingAroundFiltersTest < ActionController::TestCase def test_nested_actions controller = ControllerWithNestedFilters assert_nothing_raised do - begin - test_process(controller, "raises_both") - rescue Before, After - end + test_process(controller, "raises_both") + rescue Before, After end assert_raise Before do - begin - test_process(controller, "raises_both") - rescue After - end + test_process(controller, "raises_both") + rescue After end end diff --git a/actionpack/test/controller/flash_test.rb b/actionpack/test/controller/flash_test.rb index 409a4ec2e6..1f44c7a68e 100644 --- a/actionpack/test/controller/flash_test.rb +++ b/actionpack/test/controller/flash_test.rb @@ -242,8 +242,11 @@ end class FlashIntegrationTest < ActionDispatch::IntegrationTest SessionKey = "_myapp_session" - Generator = ActiveSupport::LegacyKeyGenerator.new("b3c631c314c0bbca50c1b2843150fe33") - Rotations = ActiveSupport::Messages::RotationConfiguration.new + Generator = ActiveSupport::CachingKeyGenerator.new( + ActiveSupport::KeyGenerator.new("b3c631c314c0bbca50c1b2843150fe33", iterations: 1000) + ) + Rotations = ActiveSupport::Messages::RotationConfiguration.new + SIGNED_COOKIE_SALT = "signed cookie" class TestController < ActionController::Base add_flash_types :bar @@ -358,13 +361,13 @@ class FlashIntegrationTest < ActionDispatch::IntegrationTest end private - # Overwrite get to send SessionSecret in env hash def get(path, *args) args[0] ||= {} args[0][:env] ||= {} args[0][:env]["action_dispatch.key_generator"] ||= Generator args[0][:env]["action_dispatch.cookies_rotations"] = Rotations + args[0][:env]["action_dispatch.signed_cookie_salt"] = SIGNED_COOKIE_SALT super(path, *args) end diff --git a/actionpack/test/controller/helper_test.rb b/actionpack/test/controller/helper_test.rb index de8072a994..93a2ba1071 100644 --- a/actionpack/test/controller/helper_test.rb +++ b/actionpack/test/controller/helper_test.rb @@ -150,8 +150,8 @@ class HelperTest < ActiveSupport::TestCase end def test_default_helpers_only - assert_equal [JustMeHelper], JustMeController._helpers.ancestors.reject(&:anonymous?) - assert_equal [MeTooHelper, JustMeHelper], MeTooController._helpers.ancestors.reject(&:anonymous?) + assert_equal %w[JustMeHelper], JustMeController._helpers.ancestors.reject(&:anonymous?).map(&:to_s) + assert_equal %w[MeTooController::HelperMethods MeTooHelper JustMeHelper], MeTooController._helpers.ancestors.reject(&:anonymous?).map(&:to_s) end def test_base_helper_methods_after_clear_helpers diff --git a/actionpack/test/controller/http_basic_authentication_test.rb b/actionpack/test/controller/http_basic_authentication_test.rb index 1544a627ee..73524d0443 100644 --- a/actionpack/test/controller/http_basic_authentication_test.rb +++ b/actionpack/test/controller/http_basic_authentication_test.rb @@ -32,7 +32,6 @@ class HttpBasicAuthenticationTest < ActionController::TestCase end private - def authenticate authenticate_or_request_with_http_basic do |username, password| username == "lifo" && password == "world" @@ -172,7 +171,6 @@ class HttpBasicAuthenticationTest < ActionController::TestCase end private - def encode_credentials(username, password) "Basic #{::Base64.encode64("#{username}:#{password}")}" end diff --git a/actionpack/test/controller/http_digest_authentication_test.rb b/actionpack/test/controller/http_digest_authentication_test.rb index b133afb343..a0f543f607 100644 --- a/actionpack/test/controller/http_digest_authentication_test.rb +++ b/actionpack/test/controller/http_digest_authentication_test.rb @@ -20,7 +20,6 @@ class HttpDigestAuthenticationTest < ActionController::TestCase end private - def authenticate authenticate_or_request_with_http_digest("SuperSecret") do |username| # Returns the password @@ -44,7 +43,10 @@ class HttpDigestAuthenticationTest < ActionController::TestCase setup do # Used as secret in generating nonce to prevent tampering of timestamp @secret = "4fb45da9e4ab4ddeb7580d6a35503d99" - @request.env["action_dispatch.key_generator"] = ActiveSupport::LegacyKeyGenerator.new(@secret) + @request.env["action_dispatch.key_generator"] = ActiveSupport::CachingKeyGenerator.new( + ActiveSupport::KeyGenerator.new(@secret) + ) + @request.env["action_dispatch.http_auth_salt"] = "http authentication" end teardown do @@ -251,7 +253,6 @@ class HttpDigestAuthenticationTest < ActionController::TestCase end private - def encode_credentials(options) options.reverse_merge!(nc: "00000001", cnonce: "0a4f113b", password_is_ha1: false) password = options.delete(:password) diff --git a/actionpack/test/controller/http_token_authentication_test.rb b/actionpack/test/controller/http_token_authentication_test.rb index 103123f98c..57b78154bc 100644 --- a/actionpack/test/controller/http_token_authentication_test.rb +++ b/actionpack/test/controller/http_token_authentication_test.rb @@ -21,7 +21,6 @@ class HttpTokenAuthenticationTest < ActionController::TestCase end private - def authenticate authenticate_or_request_with_http_token do |token, _| token == "lifo" @@ -190,7 +189,6 @@ class HttpTokenAuthenticationTest < ActionController::TestCase end private - def sample_request(token, options = { nonce: "def" }) authorization = options.inject([%{Token token="#{token}"}]) do |arr, (k, v)| arr << "#{k}=\"#{v}\"" diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb index 39ede1442a..cce229b30d 100644 --- a/actionpack/test/controller/integration_test.rb +++ b/actionpack/test/controller/integration_test.rb @@ -152,7 +152,7 @@ class IntegrationTestTest < ActiveSupport::TestCase assert_equal "pass", @test.foo ensure # leave other tests as unaffected as possible - mixin.__send__(:remove_method, :method_missing) + mixin.remove_method :method_missing end end end @@ -522,11 +522,11 @@ class IntegrationProcessTest < ActionDispatch::IntegrationTest with_test_route_set do get "/get", headers: { "Accept" => "application/json" }, xhr: true assert_equal "application/json", request.accept - assert_equal "application/json", response.content_type + assert_equal "application/json", response.media_type get "/get", headers: { "HTTP_ACCEPT" => "application/json" }, xhr: true assert_equal "application/json", request.accept - assert_equal "application/json", response.content_type + assert_equal "application/json", response.media_type end end @@ -808,17 +808,17 @@ class UrlOptionsIntegrationTest < ActionDispatch::IntegrationTest end end - test "session uses default url options from routes" do + test "session uses default URL options from routes" do assert_equal "http://foo.com/foo", foos_url end - test "current host overrides default url options from routes" do + test "current host overrides default URL options from routes" do get "/foo" assert_response :success assert_equal "http://www.example.com/foo", foos_url end - test "controller can override default url options from request" do + test "controller can override default URL options from request" do get "/bar" assert_response :success assert_equal "http://bar.com/foo", foos_url @@ -986,7 +986,7 @@ class IntegrationRequestEncodersTest < ActionDispatch::IntegrationTest def test_encoding_as_json post_to_foos as: :json do assert_response :success - assert_equal "application/json", request.content_type + assert_equal "application/json", request.media_type assert_equal "application/json", request.accepts.first.to_s assert_equal :json, request.format.ref assert_equal({ "foo" => "fighters" }, request.request_parameters) @@ -1025,7 +1025,7 @@ class IntegrationRequestEncodersTest < ActionDispatch::IntegrationTest post_to_foos as: :wibble do assert_response :success assert_equal "/foos_wibble", request.path - assert_equal "text/wibble", request.content_type + assert_equal "text/wibble", request.media_type assert_equal "text/wibble", request.accepts.first.to_s assert_equal :wibble, request.format.ref assert_equal Hash.new, request.request_parameters # Unregistered MIME Type can't be parsed. diff --git a/actionpack/test/controller/localized_templates_test.rb b/actionpack/test/controller/localized_templates_test.rb index d84a76fb46..5c5cef66d5 100644 --- a/actionpack/test/controller/localized_templates_test.rb +++ b/actionpack/test/controller/localized_templates_test.rb @@ -43,6 +43,6 @@ class LocalizedTemplatesTest < ActionController::TestCase I18n.locale = :it get :hello_world assert_equal "Ciao Mondo", @response.body - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type end end diff --git a/actionpack/test/controller/log_subscriber_test.rb b/actionpack/test/controller/log_subscriber_test.rb index 0562c16284..1a7e7f6cbb 100644 --- a/actionpack/test/controller/log_subscriber_test.rb +++ b/actionpack/test/controller/log_subscriber_test.rb @@ -98,6 +98,7 @@ class ACLogSubscriberTest < ActionController::TestCase @cache_path = Dir.mktmpdir(%w[tmp cache]) @controller.cache_store = :file_store, @cache_path + @controller.config.perform_caching = true ActionController::LogSubscriber.attach_to :action_controller end @@ -249,19 +250,15 @@ class ACLogSubscriberTest < ActionController::TestCase end def test_with_fragment_cache - @controller.config.perform_caching = true get :with_fragment_cache wait assert_equal 4, logs.size assert_match(/Read fragment views\/foo/, logs[1]) assert_match(/Write fragment views\/foo/, logs[2]) - ensure - @controller.config.perform_caching = true end def test_with_fragment_cache_when_log_disabled - @controller.config.perform_caching = true ActionController::Base.enable_fragment_cache_logging = false get :with_fragment_cache wait @@ -269,69 +266,52 @@ class ACLogSubscriberTest < ActionController::TestCase assert_equal 2, logs.size assert_equal "Processing by Another::LogSubscribersController#with_fragment_cache as HTML", logs[0] assert_match(/Completed 200 OK in \d+ms/, logs[1]) - ensure - @controller.config.perform_caching = true ActionController::Base.enable_fragment_cache_logging = true end def test_with_fragment_cache_if_with_true - @controller.config.perform_caching = true get :with_fragment_cache_if_with_true_condition wait assert_equal 4, logs.size assert_match(/Read fragment views\/foo/, logs[1]) assert_match(/Write fragment views\/foo/, logs[2]) - ensure - @controller.config.perform_caching = true end def test_with_fragment_cache_if_with_false - @controller.config.perform_caching = true get :with_fragment_cache_if_with_false_condition wait assert_equal 2, logs.size assert_no_match(/Read fragment views\/foo/, logs[1]) assert_no_match(/Write fragment views\/foo/, logs[2]) - ensure - @controller.config.perform_caching = true end def test_with_fragment_cache_unless_with_true - @controller.config.perform_caching = true get :with_fragment_cache_unless_with_true_condition wait assert_equal 2, logs.size assert_no_match(/Read fragment views\/foo/, logs[1]) assert_no_match(/Write fragment views\/foo/, logs[2]) - ensure - @controller.config.perform_caching = true end def test_with_fragment_cache_unless_with_false - @controller.config.perform_caching = true get :with_fragment_cache_unless_with_false_condition wait assert_equal 4, logs.size assert_match(/Read fragment views\/foo/, logs[1]) assert_match(/Write fragment views\/foo/, logs[2]) - ensure - @controller.config.perform_caching = true end def test_with_fragment_cache_and_percent_in_key - @controller.config.perform_caching = true get :with_fragment_cache_and_percent_in_key wait assert_equal 4, logs.size assert_match(/Read fragment views\/foo/, logs[1]) assert_match(/Write fragment views\/foo/, logs[2]) - ensure - @controller.config.perform_caching = true end def test_process_action_with_exception_includes_http_status_code diff --git a/actionpack/test/controller/metal/renderers_test.rb b/actionpack/test/controller/metal/renderers_test.rb index 5f0d125128..f6558f1354 100644 --- a/actionpack/test/controller/metal/renderers_test.rb +++ b/actionpack/test/controller/metal/renderers_test.rb @@ -38,13 +38,13 @@ class RenderersMetalTest < ActionController::TestCase get :one assert_response :success assert_equal({ a: "b" }.to_json, @response.body) - assert_equal "application/json", @response.content_type + assert_equal "application/json", @response.media_type end def test_render_xml get :two assert_response :success assert_equal(" ", @response.body) - assert_equal "text/plain", @response.content_type + assert_equal "text/plain", @response.media_type end end diff --git a/actionpack/test/controller/mime/accept_format_test.rb b/actionpack/test/controller/mime/accept_format_test.rb index eed671d593..fb038ae158 100644 --- a/actionpack/test/controller/mime/accept_format_test.rb +++ b/actionpack/test/controller/mime/accept_format_test.rb @@ -43,7 +43,6 @@ class PostController < AbstractPostController end private - def with_iphone request.format = "iphone" if request.env["HTTP_ACCEPT"] == "text/iphone" yield diff --git a/actionpack/test/controller/mime/respond_to_test.rb b/actionpack/test/controller/mime/respond_to_test.rb index 00e1d5f3b3..fc16c639fb 100644 --- a/actionpack/test/controller/mime/respond_to_test.rb +++ b/actionpack/test/controller/mime/respond_to_test.rb @@ -125,7 +125,7 @@ class RespondToController < ActionController::Base def custom_type_handling respond_to do |type| type.html { render body: "HTML" } - type.custom("application/crazy-xml") { render body: "Crazy XML" } + type.custom("application/fancy-xml") { render body: "Fancy XML" } type.all { render body: "Nothing" } end end @@ -158,6 +158,12 @@ class RespondToController < ActionController::Base end end + def handle_any_with_template + respond_to do |type| + type.any { render "test/hello_world" } + end + end + def all_types_with_layout respond_to do |type| type.html @@ -314,12 +320,14 @@ class RespondToControllerTest < ActionController::TestCase @request.host = "www.example.com" Mime::Type.register_alias("text/html", :iphone) Mime::Type.register("text/x-mobile", :mobile) + Mime::Type.register("application/fancy-xml", :fancy_xml) end def teardown super Mime::Type.unregister(:iphone) Mime::Type.unregister(:mobile) + Mime::Type.unregister(:fancy_xml) end def test_html @@ -415,12 +423,12 @@ class RespondToControllerTest < ActionController::TestCase def test_using_defaults @request.accept = "*/*" get :using_defaults - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "Hello world!", @response.body @request.accept = "application/xml" get :using_defaults - assert_equal "application/xml", @response.content_type + assert_equal "application/xml", @response.media_type assert_equal "<p>Hello world!</p>\n", @response.body end @@ -441,12 +449,12 @@ class RespondToControllerTest < ActionController::TestCase def test_using_defaults_with_type_list @request.accept = "*/*" get :using_defaults_with_type_list - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "Hello world!", @response.body @request.accept = "application/xml" get :using_defaults_with_type_list - assert_equal "application/xml", @response.content_type + assert_equal "application/xml", @response.media_type assert_equal "<p>Hello world!</p>\n", @response.body end @@ -460,7 +468,7 @@ class RespondToControllerTest < ActionController::TestCase def test_using_non_conflicting_nested_js_then_js @request.accept = "*/*" get :using_non_conflicting_nested_js_then_js - assert_equal "text/javascript", @response.content_type + assert_equal "text/javascript", @response.media_type assert_equal "JS", @response.body end @@ -489,14 +497,14 @@ class RespondToControllerTest < ActionController::TestCase end def test_custom_types - @request.accept = "application/crazy-xml" + @request.accept = "application/fancy-xml" get :custom_type_handling - assert_equal "application/crazy-xml", @response.content_type - assert_equal "Crazy XML", @response.body + assert_equal "application/fancy-xml", @response.media_type + assert_equal "Fancy XML", @response.body @request.accept = "text/html" get :custom_type_handling - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "HTML", @response.body end @@ -570,6 +578,13 @@ class RespondToControllerTest < ActionController::TestCase assert_equal "HTML", @response.body end + def test_handle_any_with_template + @request.accept = "*/*" + + get :handle_any_with_template + assert_equal "Hello world!", @response.body + end + def test_html_type_with_layout @request.accept = "text/html" get :all_types_with_layout @@ -580,7 +595,7 @@ class RespondToControllerTest < ActionController::TestCase @request.accept = "application/json" get :json_with_callback assert_equal "/**/alert(JS)", @response.body - assert_equal "text/javascript", @response.content_type + assert_equal "text/javascript", @response.media_type end def test_xhr @@ -590,13 +605,13 @@ class RespondToControllerTest < ActionController::TestCase def test_custom_constant get :custom_constant_handling, format: "mobile" - assert_equal "text/x-mobile", @response.content_type + assert_equal "text/x-mobile", @response.media_type assert_equal "Mobile", @response.body end def test_custom_constant_handling_without_block get :custom_constant_handling_without_block, format: "mobile" - assert_equal "text/x-mobile", @response.content_type + assert_equal "text/x-mobile", @response.media_type assert_equal "Mobile", @response.body end @@ -649,7 +664,7 @@ class RespondToControllerTest < ActionController::TestCase assert_equal '<html><div id="html">Hello future from Firefox!</div></html>', @response.body get :iphone_with_html_response_type, format: "iphone" - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal '<html><div id="iphone">Hello iPhone future from iPhone!</div></html>', @response.body end @@ -657,7 +672,7 @@ class RespondToControllerTest < ActionController::TestCase @request.accept = "text/iphone" get :iphone_with_html_response_type assert_equal '<html><div id="iphone">Hello iPhone future from iPhone!</div></html>', @response.body - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type end def test_invalid_format @@ -687,7 +702,7 @@ class RespondToControllerTest < ActionController::TestCase def test_variant_with_implicit_template_rendering get :variant_with_implicit_template_rendering, params: { v: :mobile } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "mobile", @response.body end @@ -741,137 +756,137 @@ class RespondToControllerTest < ActionController::TestCase def test_variant_with_format_and_custom_render get :variant_with_format_and_custom_render, params: { v: :phone } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "mobile", @response.body end def test_multiple_variants_for_format get :multiple_variants_for_format, params: { v: :tablet } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "tablet", @response.body end def test_no_variant_in_variant_setup get :variant_plus_none_for_format - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "none", @response.body end def test_variant_inline_syntax get :variant_inline_syntax - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "none", @response.body get :variant_inline_syntax, params: { v: :phone } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "phone", @response.body end def test_variant_inline_syntax_with_format get :variant_inline_syntax, format: :js - assert_equal "text/javascript", @response.content_type + assert_equal "text/javascript", @response.media_type assert_equal "js", @response.body end def test_variant_inline_syntax_without_block get :variant_inline_syntax_without_block, params: { v: :phone } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "phone", @response.body end def test_variant_any get :variant_any, params: { v: :phone } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "phone", @response.body get :variant_any, params: { v: :tablet } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "any", @response.body get :variant_any, params: { v: :phablet } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "any", @response.body end def test_variant_any_any get :variant_any_any - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "any", @response.body get :variant_any_any, params: { v: :phone } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "phone", @response.body get :variant_any_any, params: { v: :yolo } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "any", @response.body end def test_variant_inline_any get :variant_any, params: { v: :phone } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "phone", @response.body get :variant_inline_any, params: { v: :tablet } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "any", @response.body get :variant_inline_any, params: { v: :phablet } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "any", @response.body end def test_variant_inline_any_any get :variant_inline_any_any, params: { v: :phone } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "phone", @response.body get :variant_inline_any_any, params: { v: :yolo } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "any", @response.body end def test_variant_any_implicit_render get :variant_any_implicit_render, params: { v: :tablet } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "tablet", @response.body get :variant_any_implicit_render, params: { v: :phablet } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "phablet", @response.body end def test_variant_any_with_none get :variant_any_with_none - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "none or phone", @response.body get :variant_any_with_none, params: { v: :phone } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "none or phone", @response.body end def test_format_any_variant_any get :format_any_variant_any, format: :js, params: { v: :tablet } - assert_equal "text/javascript", @response.content_type + assert_equal "text/javascript", @response.media_type assert_equal "tablet", @response.body end def test_variant_negotiation_inline_syntax get :variant_inline_syntax_without_block, params: { v: [:tablet, :phone] } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "phone", @response.body end def test_variant_negotiation_block_syntax get :variant_plus_none_for_format, params: { v: [:tablet, :phone] } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "phone", @response.body end def test_variant_negotiation_without_block get :variant_inline_syntax_without_block, params: { v: [:tablet, :phone] } - assert_equal "text/html", @response.content_type + assert_equal "text/html", @response.media_type assert_equal "phone", @response.body end end diff --git a/actionpack/test/controller/new_base/content_negotiation_test.rb b/actionpack/test/controller/new_base/content_negotiation_test.rb index 7205e90176..548fa4300d 100644 --- a/actionpack/test/controller/new_base/content_negotiation_test.rb +++ b/actionpack/test/controller/new_base/content_negotiation_test.rb @@ -20,9 +20,19 @@ module ContentNegotiation assert_body "Hello world */*!" end - test "Not all mimes are converted to symbol" do + test "A js or */* Accept header will return HTML" do + get "/content_negotiation/basic/hello", headers: { "HTTP_ACCEPT" => "text/javascript, */*" } + assert_body "Hello world text/html!" + end + + test "A js or */* Accept header on xhr will return JavaScript" do + get "/content_negotiation/basic/hello", headers: { "HTTP_ACCEPT" => "text/javascript, */*" }, xhr: true + assert_body "Hello world text/javascript!" + end + + test "Unregistered mimes are ignored" do get "/content_negotiation/basic/all", headers: { "HTTP_ACCEPT" => "text/plain, mime/another" } - assert_body '[:text, "mime/another"]' + assert_body "[:text]" end end end diff --git a/actionpack/test/controller/new_base/render_context_test.rb b/actionpack/test/controller/new_base/render_context_test.rb deleted file mode 100644 index 5e570a1d79..0000000000 --- a/actionpack/test/controller/new_base/render_context_test.rb +++ /dev/null @@ -1,55 +0,0 @@ -# frozen_string_literal: true - -require "abstract_unit" - -# This is testing the decoupling of view renderer and view context -# by allowing the controller to be used as view context. This is -# similar to the way sinatra renders templates. -module RenderContext - class BasicController < ActionController::Base - self.view_paths = [ActionView::FixtureResolver.new( - "render_context/basic/hello_world.html.erb" => "<%= @value %> from <%= self.__controller_method__ %>", - "layouts/basic.html.erb" => "?<%= yield %>?" - )] - - # 1) Include ActionView::Context to bring the required dependencies - include ActionView::Context - - # 2) Call _prepare_context that will do the required initialization - before_action :_prepare_context - - def hello_world - @value = "Hello" - render action: "hello_world", layout: false - end - - def with_layout - @value = "Hello" - render action: "hello_world", layout: "basic" - end - - protected def __controller_method__ - "controller context!" - end - - private - # 3) Set view_context to self - def view_context - self - end - end - - class RenderContextTest < Rack::TestCase - test "rendering using the controller as context" do - get "/render_context/basic/hello_world" - assert_body "Hello from controller context!" - assert_status 200 - end - - test "rendering using the controller as context with layout" do - get "/render_context/basic/with_layout" - assert_body "?Hello from controller context!?" - assert_status 200 - end - end -end diff --git a/actionpack/test/controller/new_base/render_file_test.rb b/actionpack/test/controller/new_base/render_file_test.rb index de8af029e0..01d0223519 100644 --- a/actionpack/test/controller/new_base/render_file_test.rb +++ b/actionpack/test/controller/new_base/render_file_test.rb @@ -17,12 +17,12 @@ module RenderFile def relative_path @secret = "in the sauce" - render file: "../../fixtures/test/render_file_with_ivar" + render file: "../actionpack/test/fixtures/test/render_file_with_ivar" end def relative_path_with_dot @secret = "in the sauce" - render file: "../../fixtures/test/dot.directory/render_file_with_ivar" + render file: "../actionpack/test/fixtures/test/dot.directory/render_file_with_ivar" end def pathname @@ -40,32 +40,44 @@ module RenderFile testing RenderFile::BasicController test "rendering simple template" do - get :index + assert_deprecated do + get :index + end assert_response "Hello world!" end test "rendering template with ivar" do - get :with_instance_variables + assert_deprecated do + get :with_instance_variables + end assert_response "The secret is in the sauce\n" end test "rendering a relative path" do - get :relative_path + assert_deprecated do + get :relative_path + end assert_response "The secret is in the sauce\n" end test "rendering a relative path with dot" do - get :relative_path_with_dot + assert_deprecated do + get :relative_path_with_dot + end assert_response "The secret is in the sauce\n" end test "rendering a Pathname" do - get :pathname + assert_deprecated do + get :pathname + end assert_response "The secret is in the sauce\n" end test "rendering file with locals" do - get :with_locals + assert_deprecated do + get :with_locals + end assert_response "The secret is in the sauce\n" end end diff --git a/actionpack/test/controller/new_base/render_template_test.rb b/actionpack/test/controller/new_base/render_template_test.rb index 14dc958475..270f75eb9e 100644 --- a/actionpack/test/controller/new_base/render_template_test.rb +++ b/actionpack/test/controller/new_base/render_template_test.rb @@ -67,7 +67,6 @@ module RenderTemplate end private - def show_detailed_exceptions? request.local? end diff --git a/actionpack/test/controller/new_base/render_test.rb b/actionpack/test/controller/new_base/render_test.rb index eb29203f59..dfeb2e2b15 100644 --- a/actionpack/test/controller/new_base/render_test.rb +++ b/actionpack/test/controller/new_base/render_test.rb @@ -37,7 +37,6 @@ module Render end private - def secretz render plain: "FAIL WHALE!" end diff --git a/actionpack/test/controller/parameters/accessors_test.rb b/actionpack/test/controller/parameters/accessors_test.rb index 7789e654d5..3d1538ff64 100644 --- a/actionpack/test/controller/parameters/accessors_test.rb +++ b/actionpack/test/controller/parameters/accessors_test.rb @@ -203,6 +203,25 @@ class ParametersAccessorsTest < ActiveSupport::TestCase assert_not_predicate @params.transform_keys { |k| k }, :permitted? end + test "transform_keys without a block returns an enumerator" do + assert_kind_of Enumerator, @params.transform_keys + assert_kind_of ActionController::Parameters, @params.transform_keys.each { |k| k } + end + + test "transform_keys! without a block returns an enumerator" do + assert_kind_of Enumerator, @params.transform_keys! + assert_kind_of ActionController::Parameters, @params.transform_keys!.each { |k| k } + end + + test "deep_transform_keys retains permitted status" do + @params.permit! + assert_predicate @params.deep_transform_keys { |k| k }, :permitted? + end + + test "deep_transform_keys retains unpermitted status" do + assert_not_predicate @params.deep_transform_keys { |k| k }, :permitted? + end + test "transform_values retains permitted status" do @params.permit! assert_predicate @params.transform_values { |v| v }, :permitted? @@ -219,8 +238,9 @@ class ParametersAccessorsTest < ActiveSupport::TestCase end end - test "transform_values without block yieds an enumerator" do + test "transform_values without a block returns an enumerator" do assert_kind_of Enumerator, @params.transform_values + assert_kind_of ActionController::Parameters, @params.transform_values.each { |v| v } end test "transform_values! converts hashes to parameters" do @@ -229,8 +249,9 @@ class ParametersAccessorsTest < ActiveSupport::TestCase end end - test "transform_values! without block yields an enumerator" do + test "transform_values! without a block returns an enumerator" do assert_kind_of Enumerator, @params.transform_values! + assert_kind_of ActionController::Parameters, @params.transform_values!.each { |v| v } end test "value? returns true if the given value is present in the params" do @@ -263,12 +284,14 @@ class ParametersAccessorsTest < ActiveSupport::TestCase params1 = ActionController::Parameters.new(a: 1, b: 2) params2 = ActionController::Parameters.new(a: 1, b: 2) assert(params1 == params2) + assert(params1.hash == params2.hash) end test "is equal to Parameters instance with same permitted params" do params1 = ActionController::Parameters.new(a: 1, b: 2).permit(:a) params2 = ActionController::Parameters.new(a: 1, b: 2).permit(:a) assert(params1 == params2) + assert(params1.hash == params2.hash) end test "is equal to Parameters instance with same different source params, but same permitted params" do @@ -276,6 +299,8 @@ class ParametersAccessorsTest < ActiveSupport::TestCase params2 = ActionController::Parameters.new(a: 1, c: 3).permit(:a) assert(params1 == params2) assert(params2 == params1) + assert(params1.hash == params2.hash) + assert(params2.hash == params1.hash) end test "is not equal to an unpermitted Parameters instance with same params" do @@ -283,6 +308,8 @@ class ParametersAccessorsTest < ActiveSupport::TestCase params2 = ActionController::Parameters.new(a: 1) assert(params1 != params2) assert(params2 != params1) + assert(params1.hash != params2.hash) + assert(params2.hash != params1.hash) end test "is not equal to Parameters instance with different permitted params" do @@ -290,6 +317,8 @@ class ParametersAccessorsTest < ActiveSupport::TestCase params2 = ActionController::Parameters.new(a: 1, b: 2).permit(:a) assert(params1 != params2) assert(params2 != params1) + assert(params1.hash != params2.hash) + assert(params2.hash != params1.hash) end test "equality with simple types works" do diff --git a/actionpack/test/controller/parameters/log_on_unpermitted_params_test.rb b/actionpack/test/controller/parameters/log_on_unpermitted_params_test.rb index fc9229ca1d..4fffcf6b10 100644 --- a/actionpack/test/controller/parameters/log_on_unpermitted_params_test.rb +++ b/actionpack/test/controller/parameters/log_on_unpermitted_params_test.rb @@ -52,7 +52,6 @@ class LogOnUnpermittedParamsTest < ActiveSupport::TestCase end private - def assert_logged(message) old_logger = ActionController::Base.logger log = StringIO.new diff --git a/actionpack/test/controller/parameters/mutators_test.rb b/actionpack/test/controller/parameters/mutators_test.rb index 312b1e5b27..31ee7964f5 100644 --- a/actionpack/test/controller/parameters/mutators_test.rb +++ b/actionpack/test/controller/parameters/mutators_test.rb @@ -118,4 +118,13 @@ class ParametersMutatorsTest < ActiveSupport::TestCase test "transform_values! retains unpermitted status" do assert_not_predicate @params.transform_values! { |v| v }, :permitted? end + + test "deep_transform_keys! retains permitted status" do + @params.permit! + assert_predicate @params.deep_transform_keys! { |k| k }, :permitted? + end + + test "deep_transform_keys! retains unpermitted status" do + assert_not_predicate @params.deep_transform_keys! { |k| k }, :permitted? + end end diff --git a/actionpack/test/controller/parameters/nested_parameters_permit_test.rb b/actionpack/test/controller/parameters/nested_parameters_permit_test.rb index 1403e224c0..6243b5c51b 100644 --- a/actionpack/test/controller/parameters/nested_parameters_permit_test.rb +++ b/actionpack/test/controller/parameters/nested_parameters_permit_test.rb @@ -125,7 +125,7 @@ class NestedParametersPermitTest < ActiveSupport::TestCase assert_nil permitted[:book][:genre] end - test "fields_for-style nested params" do + test "nested params with numeric keys" do params = ActionController::Parameters.new( book: { authors_attributes: { @@ -150,7 +150,33 @@ class NestedParametersPermitTest < ActiveSupport::TestCase assert_filtered_out permitted[:book][:authors_attributes]["0"], :age_of_death end - test "fields_for-style nested params with negative numbers" do + test "nested params with non_numeric keys" do + params = ActionController::Parameters.new( + book: { + authors_attributes: { + '0': { name: "William Shakespeare", age_of_death: "52" }, + '1': { name: "Unattributed Assistant" }, + '2': "Not a hash", + 'new_record': { name: "Some name" } + } + }) + permitted = params.permit book: { authors_attributes: [ :name ] } + + assert_not_nil permitted[:book][:authors_attributes]["0"] + assert_not_nil permitted[:book][:authors_attributes]["1"] + + assert_nil permitted[:book][:authors_attributes]["2"] + assert_nil permitted[:book][:authors_attributes]["new_record"] + assert_equal "William Shakespeare", permitted[:book][:authors_attributes]["0"][:name] + assert_equal "Unattributed Assistant", permitted[:book][:authors_attributes]["1"][:name] + + assert_equal( + { "book" => { "authors_attributes" => { "0" => { "name" => "William Shakespeare" }, "1" => { "name" => "Unattributed Assistant" } } } }, + permitted.to_h + ) + end + + test "nested params with negative numeric keys" do params = ActionController::Parameters.new( book: { authors_attributes: { diff --git a/actionpack/test/controller/parameters/parameters_permit_test.rb b/actionpack/test/controller/parameters/parameters_permit_test.rb index d2fa0aa16e..fbfe24059b 100644 --- a/actionpack/test/controller/parameters/parameters_permit_test.rb +++ b/actionpack/test/controller/parameters/parameters_permit_test.rb @@ -365,17 +365,15 @@ class ParametersPermitTest < ActiveSupport::TestCase end test "permitted takes a default value when Parameters.permit_all_parameters is set" do - begin - ActionController::Parameters.permit_all_parameters = true - params = ActionController::Parameters.new(person: { - age: "32", name: { first: "David", last: "Heinemeier Hansson" } - }) - - assert_predicate params.slice(:person), :permitted? - assert_predicate params[:person][:name], :permitted? - ensure - ActionController::Parameters.permit_all_parameters = false - end + ActionController::Parameters.permit_all_parameters = true + params = ActionController::Parameters.new(person: { + age: "32", name: { first: "David", last: "Heinemeier Hansson" } + }) + + assert_predicate params.slice(:person), :permitted? + assert_predicate params[:person][:name], :permitted? + ensure + ActionController::Parameters.permit_all_parameters = false end test "permitting parameters as an array" do @@ -396,16 +394,14 @@ class ParametersPermitTest < ActiveSupport::TestCase end test "to_h returns converted hash when .permit_all_parameters is set" do - begin - ActionController::Parameters.permit_all_parameters = true - params = ActionController::Parameters.new(crab: "Senjougahara Hitagi") - - assert_instance_of ActiveSupport::HashWithIndifferentAccess, params.to_h - assert_not_kind_of ActionController::Parameters, params.to_h - assert_equal({ "crab" => "Senjougahara Hitagi" }, params.to_h) - ensure - ActionController::Parameters.permit_all_parameters = false - end + ActionController::Parameters.permit_all_parameters = true + params = ActionController::Parameters.new(crab: "Senjougahara Hitagi") + + assert_instance_of ActiveSupport::HashWithIndifferentAccess, params.to_h + assert_not_kind_of ActionController::Parameters, params.to_h + assert_equal({ "crab" => "Senjougahara Hitagi" }, params.to_h) + ensure + ActionController::Parameters.permit_all_parameters = false end test "to_hash raises UnfilteredParameters on unfiltered params" do @@ -429,17 +425,15 @@ class ParametersPermitTest < ActiveSupport::TestCase end test "to_hash returns converted hash when .permit_all_parameters is set" do - begin - ActionController::Parameters.permit_all_parameters = true - params = ActionController::Parameters.new(crab: "Senjougahara Hitagi") - - assert_instance_of Hash, params.to_hash - assert_not_kind_of ActionController::Parameters, params.to_hash - assert_equal({ "crab" => "Senjougahara Hitagi" }, params.to_hash) - assert_equal({ "crab" => "Senjougahara Hitagi" }, params) - ensure - ActionController::Parameters.permit_all_parameters = false - end + ActionController::Parameters.permit_all_parameters = true + params = ActionController::Parameters.new(crab: "Senjougahara Hitagi") + + assert_instance_of Hash, params.to_hash + assert_not_kind_of ActionController::Parameters, params.to_hash + assert_equal({ "crab" => "Senjougahara Hitagi" }, params.to_hash) + assert_equal({ "crab" => "Senjougahara Hitagi" }, params) + ensure + ActionController::Parameters.permit_all_parameters = false end test "to_unsafe_h returns unfiltered params" do diff --git a/actionpack/test/controller/params_parse_test.rb b/actionpack/test/controller/params_parse_test.rb index 440ab06fd7..091b567473 100644 --- a/actionpack/test/controller/params_parse_test.rb +++ b/actionpack/test/controller/params_parse_test.rb @@ -24,7 +24,6 @@ class ParamsParseTest < ActionController::TestCase end private - def capture_log_output output = StringIO.new request.set_header "action_dispatch.logger", ActiveSupport::Logger.new(output) diff --git a/actionpack/test/controller/params_wrapper_test.rb b/actionpack/test/controller/params_wrapper_test.rb index c4c74e8f2b..894a3824c0 100644 --- a/actionpack/test/controller/params_wrapper_test.rb +++ b/actionpack/test/controller/params_wrapper_test.rb @@ -411,7 +411,6 @@ class IrregularInflectionParamsWrapperTest < ActionController::TestCase end private - def with_dup original = ActiveSupport::Inflector::Inflections.instance_variable_get(:@__instance__)[:en] ActiveSupport::Inflector::Inflections.instance_variable_set(:@__instance__, en: original.dup) diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb index 998498e1b2..7f1c41787a 100644 --- a/actionpack/test/controller/redirect_test.rb +++ b/actionpack/test/controller/redirect_test.rb @@ -68,10 +68,18 @@ class RedirectController < ActionController::Base redirect_back(fallback_location: "/things/stuff", status: 307) end + def redirect_back_with_status_and_fallback_location_to_another_host + redirect_back(fallback_location: "http://www.rubyonrails.org/", status: 307) + end + def safe_redirect_back_with_status redirect_back(fallback_location: "/things/stuff", status: 307, allow_other_host: false) end + def safe_redirect_back_with_status_and_fallback_location_to_another_host + redirect_back(fallback_location: "http://www.rubyonrails.org/", status: 307, allow_other_host: false) + end + def host_redirect redirect_to action: "other_host", only_path: false, host: "other.test.host" end @@ -280,6 +288,13 @@ class RedirectTest < ActionController::TestCase assert_equal "http://test.host/things/stuff", redirect_to_url end + def test_redirect_back_with_no_referer_redirects_to_another_host + get :redirect_back_with_status_and_fallback_location_to_another_host + + assert_response 307 + assert_equal "http://www.rubyonrails.org/", redirect_to_url + end + def test_safe_redirect_back_from_other_host @request.env["HTTP_REFERER"] = "http://another.host/coming/from" get :safe_redirect_back_with_status @@ -297,6 +312,20 @@ class RedirectTest < ActionController::TestCase assert_equal referer, redirect_to_url end + def test_safe_redirect_back_with_no_referer + get :safe_redirect_back_with_status + + assert_response 307 + assert_equal "http://test.host/things/stuff", redirect_to_url + end + + def test_safe_redirect_back_with_no_referer_redirects_to_another_host + get :safe_redirect_back_with_status_and_fallback_location_to_another_host + + assert_response 307 + assert_equal "http://www.rubyonrails.org/", redirect_to_url + end + def test_redirect_to_record with_routing do |set| set.draw do diff --git a/actionpack/test/controller/render_js_test.rb b/actionpack/test/controller/render_js_test.rb index 1efc0b9de1..da8f6e8062 100644 --- a/actionpack/test/controller/render_js_test.rb +++ b/actionpack/test/controller/render_js_test.rb @@ -2,7 +2,6 @@ require "abstract_unit" require "controller/fake_models" -require "pathname" class RenderJSTest < ActionController::TestCase class TestController < ActionController::Base @@ -26,7 +25,7 @@ class RenderJSTest < ActionController::TestCase def test_render_vanilla_js get :render_vanilla_js_hello, xhr: true assert_equal "alert('hello')", @response.body - assert_equal "text/javascript", @response.content_type + assert_equal "text/javascript", @response.media_type end def test_should_render_js_partial diff --git a/actionpack/test/controller/render_json_test.rb b/actionpack/test/controller/render_json_test.rb index 82c1ba26cb..82c6aaafe5 100644 --- a/actionpack/test/controller/render_json_test.rb +++ b/actionpack/test/controller/render_json_test.rb @@ -3,7 +3,6 @@ require "abstract_unit" require "controller/fake_models" require "active_support/logger" -require "pathname" class RenderJsonTest < ActionController::TestCase class JsonRenderable @@ -80,7 +79,7 @@ class RenderJsonTest < ActionController::TestCase def test_render_json_nil get :render_json_nil assert_equal "null", @response.body - assert_equal "application/json", @response.content_type + assert_equal "application/json", @response.media_type end def test_render_json_render_to_string @@ -91,7 +90,7 @@ class RenderJsonTest < ActionController::TestCase def test_render_json get :render_json_hello_world assert_equal '{"hello":"world"}', @response.body - assert_equal "application/json", @response.content_type + assert_equal "application/json", @response.media_type end def test_render_json_with_status @@ -103,31 +102,31 @@ class RenderJsonTest < ActionController::TestCase def test_render_json_with_callback get :render_json_hello_world_with_callback, xhr: true assert_equal '/**/alert({"hello":"world"})', @response.body - assert_equal "text/javascript", @response.content_type + assert_equal "text/javascript", @response.media_type end def test_render_json_with_custom_content_type get :render_json_with_custom_content_type, xhr: true assert_equal '{"hello":"world"}', @response.body - assert_equal "text/javascript", @response.content_type + assert_equal "text/javascript", @response.media_type end def test_render_symbol_json get :render_symbol_json assert_equal '{"hello":"world"}', @response.body - assert_equal "application/json", @response.content_type + assert_equal "application/json", @response.media_type end def test_render_json_with_render_to_string get :render_json_with_render_to_string assert_equal '{"hello":"partial html"}', @response.body - assert_equal "application/json", @response.content_type + assert_equal "application/json", @response.media_type end def test_render_json_forwards_extra_options get :render_json_with_extra_options assert_equal '{"a":"b"}', @response.body - assert_equal "application/json", @response.content_type + assert_equal "application/json", @response.media_type end def test_render_json_calls_to_json_from_object diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index 306b245bd1..a2a6c69dd3 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -183,6 +183,11 @@ class TestController < ActionController::Base render action: "hello_world" end + def conditional_hello_without_expires_and_public_header + response.headers["Cache-Control"] = "public, no-cache" + render action: "hello_world" + end + def conditional_hello_with_bangs render action: "hello_world" end @@ -260,7 +265,6 @@ class TestController < ActionController::Base end private - def set_variable_for_layout @variable_for_layout = nil end @@ -318,11 +322,12 @@ class ExpiresInRenderTest < ActionController::TestCase end def test_dynamic_render_with_file - # This is extremely bad, but should be possible to do. assert File.exist?(File.expand_path("../../test/abstract_unit.rb", __dir__)) - response = get :dynamic_render_with_file, params: { id: '../\\../test/abstract_unit.rb' } - assert_equal File.read(File.expand_path("../../test/abstract_unit.rb", __dir__)), - response.body + assert_deprecated do + assert_raises ActionView::MissingTemplate do + get :dynamic_render_with_file, params: { id: '../\\../test/abstract_unit.rb' } + end + end end def test_dynamic_render_with_absolute_path @@ -346,9 +351,11 @@ class ExpiresInRenderTest < ActionController::TestCase def test_permitted_dynamic_render_file_hash assert File.exist?(File.expand_path("../../test/abstract_unit.rb", __dir__)) - response = get :dynamic_render_permit, params: { id: { file: '../\\../test/abstract_unit.rb' } } - assert_equal File.read(File.expand_path("../../test/abstract_unit.rb", __dir__)), - response.body + assert_deprecated do + assert_raises ActionView::MissingTemplate do + get :dynamic_render_permit, params: { id: { file: '../\\../test/abstract_unit.rb' } } + end + end end def test_dynamic_render_file_hash @@ -418,6 +425,11 @@ class ExpiresInRenderTest < ActionController::TestCase assert_equal "no-cache", @response.headers["Cache-Control"] end + def test_no_expires_now_with_public + get :conditional_hello_without_expires_and_public_header + assert_equal "public, no-cache", @response.headers["Cache-Control"] + end + def test_date_header_when_expires_in time = Time.mktime(2011, 10, 30) Time.stub :now, time do diff --git a/actionpack/test/controller/render_xml_test.rb b/actionpack/test/controller/render_xml_test.rb index a72d14e4bb..28d8e281ab 100644 --- a/actionpack/test/controller/render_xml_test.rb +++ b/actionpack/test/controller/render_xml_test.rb @@ -2,7 +2,6 @@ require "abstract_unit" require "controller/fake_models" -require "pathname" class RenderXmlTest < ActionController::TestCase class XmlRenderable @@ -92,11 +91,11 @@ class RenderXmlTest < ActionController::TestCase def test_should_render_xml_but_keep_custom_content_type get :render_xml_with_custom_content_type - assert_equal "application/atomsvc+xml", @response.content_type + assert_equal "application/atomsvc+xml", @response.media_type end def test_should_use_implicit_content_type get :implicit_content_type, format: "atom" - assert_equal Mime[:atom], @response.content_type + assert_equal Mime[:atom], @response.media_type end end diff --git a/actionpack/test/controller/renderer_test.rb b/actionpack/test/controller/renderer_test.rb index ae8330e029..ea79f4de85 100644 --- a/actionpack/test/controller/renderer_test.rb +++ b/actionpack/test/controller/renderer_test.rb @@ -40,7 +40,7 @@ class RendererTest < ActiveSupport::TestCase test "rendering with an instance renderer" do renderer = ApplicationController.renderer.new - content = renderer.render file: "test/hello_world" + content = assert_deprecated { renderer.render file: "test/hello_world" } assert_equal "Hello world!", content end @@ -115,14 +115,14 @@ class RendererTest < ActiveSupport::TestCase assert_equal "true", content end - test "return valid asset url with defaults" do + test "return valid asset URL with defaults" do renderer = ApplicationController.renderer content = renderer.render inline: "<%= asset_url 'asset.jpg' %>" assert_equal "http://example.org/asset.jpg", content end - test "return valid asset url when https is true" do + test "return valid asset URL when https is true" do renderer = ApplicationController.renderer.new https: true content = renderer.render inline: "<%= asset_url 'asset.jpg' %>" diff --git a/actionpack/test/controller/renderers_test.rb b/actionpack/test/controller/renderers_test.rb index d92de6f5d5..96cce664a4 100644 --- a/actionpack/test/controller/renderers_test.rb +++ b/actionpack/test/controller/renderers_test.rb @@ -73,7 +73,7 @@ class RenderersTest < ActionController::TestCase assert_raise ActionView::MissingTemplate do get :respond_to_mime, format: "csv" end - assert_equal Mime[:csv], @response.content_type + assert_equal Mime[:csv], @response.media_type assert_equal "", @response.body end @@ -83,7 +83,7 @@ class RenderersTest < ActionController::TestCase end @request.accept = "text/csv" get :respond_to_mime, format: "csv" - assert_equal Mime[:csv], @response.content_type + assert_equal Mime[:csv], @response.media_type assert_equal "c,s,v", @response.body ensure ActionController::Renderers.remove :csv diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb index ea94a3e048..01250880f5 100644 --- a/actionpack/test/controller/request_forgery_protection_test.rb +++ b/actionpack/test/controller/request_forgery_protection_test.rb @@ -112,7 +112,6 @@ class PrependProtectForgeryBaseController < ActionController::Base end private - def add_called_callback(name) @called_callbacks ||= [] @called_callbacks << name diff --git a/actionpack/test/controller/rescue_test.rb b/actionpack/test/controller/rescue_test.rb index 3c39373e55..538bc15fc9 100644 --- a/actionpack/test/controller/rescue_test.rb +++ b/actionpack/test/controller/rescue_test.rb @@ -62,14 +62,6 @@ class RescueController < ActionController::Base render plain: exception.message end - rescue_from ActionView::TemplateError do - render plain: "action_view templater error" - end - - rescue_from IOError do - render plain: "io error" - end - rescue_from ActionDispatch::Http::Parameters::ParseError do render plain: "parse error", status: :bad_request end @@ -79,19 +71,6 @@ class RescueController < ActionController::Base def before_action_raises end - def raises - render plain: "already rendered" - raise "don't panic!" - end - - def method_not_allowed - raise ActionController::MethodNotAllowed.new(:get, :head, :put) - end - - def not_implemented - raise ActionController::NotImplemented.new(:get, :put) - end - def not_authorized raise NotAuthorized end @@ -325,7 +304,6 @@ class RescueControllerTest < ActionController::TestCase end private - def capture_log_output output = StringIO.new request.set_header "action_dispatch.logger", ActiveSupport::Logger.new(output) @@ -351,10 +329,6 @@ class RescueTest < ActionDispatch::IntegrationTest raise RecordInvalid end - def b00m - raise "b00m" - end - private def show_errors(exception) render plain: exception.message @@ -376,13 +350,11 @@ class RescueTest < ActionDispatch::IntegrationTest end private - def with_test_routing with_routing do |set| set.draw do get "foo", to: ::RescueTest::TestController.action(:foo) get "invalid", to: ::RescueTest::TestController.action(:invalid) - get "b00m", to: ::RescueTest::TestController.action(:b00m) end yield end diff --git a/actionpack/test/controller/resources_test.rb b/actionpack/test/controller/resources_test.rb index d2146f12a5..339025ec52 100644 --- a/actionpack/test/controller/resources_test.rb +++ b/actionpack/test/controller/resources_test.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require "abstract_unit" -require "active_support/core_ext/object/try" require "active_support/core_ext/object/with_options" require "active_support/core_ext/array/extract_options" @@ -36,7 +35,6 @@ class ResourcesTest < ActionController::TestCase collection: collection_methods, member: member_methods, path_names: path_names do - assert_restful_routes_for :messages, collection: collection_methods, member: member_methods, @@ -58,7 +56,6 @@ class ResourcesTest < ActionController::TestCase collection: collection_methods, member: member_methods, path_names: path_names do |options| - collection_methods.each_key do |action| assert_named_route "/messages/#{path_names[action] || action}", "#{action}_messages_path", action: action end @@ -1251,7 +1248,7 @@ class ResourcesTest < ActionController::TestCase shallow_path = "/#{options[:shallow] ? options[:namespace] : options[:path_prefix]}#{path}" full_path = "/#{options[:path_prefix]}#{path}" name_prefix = options[:name_prefix] - shallow_prefix = options[:shallow] ? options[:namespace].try(:gsub, /\//, "_") : options[:name_prefix] + shallow_prefix = options[:shallow] ? options[:namespace]&.gsub(/\//, "_") : options[:name_prefix] new_action = "new" edit_action = "edit" diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index 30f2a23b33..b378bb80b8 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -355,10 +355,10 @@ class LegacyRouteSetTests < ActiveSupport::TestCase rs.draw { ActiveSupport::Deprecation.silence { get "/:controller/:action", action: /auth[-|_].+/ } } assert_equal({ action: "auth_google", controller: "content" }, rs.recognize_path("/content/auth_google")) - assert_equal({ action: "auth-facebook", controller: "content" }, rs.recognize_path("/content/auth-facebook")) + assert_equal({ action: "auth-twitter", controller: "content" }, rs.recognize_path("/content/auth-twitter")) assert_equal "/content/auth_google", url_for(rs, controller: "content", action: "auth_google") - assert_equal "/content/auth-facebook", url_for(rs, controller: "content", action: "auth-facebook") + assert_equal "/content/auth-twitter", url_for(rs, controller: "content", action: "auth-twitter") end def test_route_with_regexp_for_controller diff --git a/actionpack/test/controller/show_exceptions_test.rb b/actionpack/test/controller/show_exceptions_test.rb index 2094aa1aed..8e1068fecf 100644 --- a/actionpack/test/controller/show_exceptions_test.rb +++ b/actionpack/test/controller/show_exceptions_test.rb @@ -51,7 +51,6 @@ module ShowExceptions class ShowExceptionsOverriddenController < ShowExceptionsController private - def show_detailed_exceptions? params["detailed"] == "1" end @@ -76,7 +75,7 @@ module ShowExceptions @app = ShowExceptionsOverriddenController.action(:boom) get "/", headers: { "HTTP_ACCEPT" => "application/json" } assert_response :internal_server_error - assert_equal "application/json", response.content_type.to_s + assert_equal "application/json", response.media_type assert_equal({ status: 500, error: "Internal Server Error" }.to_json, response.body) end @@ -84,7 +83,7 @@ module ShowExceptions @app = ShowExceptionsOverriddenController.action(:boom) get "/", headers: { "HTTP_ACCEPT" => "application/xml" } assert_response :internal_server_error - assert_equal "application/xml", response.content_type.to_s + assert_equal "application/xml", response.media_type assert_equal({ status: 500, error: "Internal Server Error" }.to_xml, response.body) end @@ -92,22 +91,23 @@ module ShowExceptions @app = ShowExceptionsOverriddenController.action(:boom) get "/", headers: { "HTTP_ACCEPT" => "text/csv" } assert_response :internal_server_error - assert_equal "text/html", response.content_type.to_s + assert_equal "text/html", response.media_type end end class ShowFailsafeExceptionsTest < ActionDispatch::IntegrationTest def test_render_failsafe_exception @app = ShowExceptionsOverriddenController.action(:boom) - @exceptions_app = @app.instance_variable_get(:@exceptions_app) - @app.instance_variable_set(:@exceptions_app, nil) + middleware = @app + @exceptions_app = middleware.instance_variable_get(:@exceptions_app) + middleware.instance_variable_set(:@exceptions_app, nil) $stderr = StringIO.new get "/", headers: { "HTTP_ACCEPT" => "text/json" } assert_response :internal_server_error - assert_equal "text/plain", response.content_type.to_s + assert_equal "text/plain", response.media_type ensure - @app.instance_variable_set(:@exceptions_app, @exceptions_app) + middleware.instance_variable_set(:@exceptions_app, @exceptions_app) $stderr = STDERR end end diff --git a/actionpack/test/controller/test_case_test.rb b/actionpack/test/controller/test_case_test.rb index 6fc70d6248..635a91507d 100644 --- a/actionpack/test/controller/test_case_test.rb +++ b/actionpack/test/controller/test_case_test.rb @@ -156,12 +156,15 @@ XML render html: '<body class="foo"></body>'.html_safe end + def render_json + render json: request.raw_post + end + def boom raise "boom!" end private - def generate_url(opts) url_for(opts.merge(action: "test_uri")) end @@ -474,6 +477,18 @@ XML ) end + def test_nil_params + get :test_params, params: nil + parsed_params = JSON.parse(@response.body) + assert_equal( + { + "action" => "test_params", + "controller" => "test_case_test/test" + }, + parsed_params + ) + end + def test_query_param_named_action get :test_query_parameters, params: { action: "foobar" } parsed_params = JSON.parse(@response.body) @@ -936,7 +951,7 @@ XML get :create assert_response :created - # Redirect url doesn't care that it wasn't a :redirect response. + # Redirect URL doesn't care that it wasn't a :redirect response. assert_equal "/resource", @response.redirect_url assert_equal @response.redirect_url, redirect_to_url @@ -965,6 +980,16 @@ XML assert_equal "q=test2", @response.body end + + def test_parsed_body_without_as_option + post :render_json, body: { foo: "heyo" } + assert_equal({ "foo" => "heyo" }, response.parsed_body) + end + + def test_parsed_body_with_as_option + post :render_json, body: { foo: "heyo" }.to_json, as: :json + assert_equal({ "foo" => "heyo" }, response.parsed_body) + end end class ResponseDefaultHeadersTest < ActionController::TestCase diff --git a/actionpack/test/controller/url_for_test.rb b/actionpack/test/controller/url_for_test.rb index e381abee36..9222250b9c 100644 --- a/actionpack/test/controller/url_for_test.rb +++ b/actionpack/test/controller/url_for_test.rb @@ -354,6 +354,14 @@ module AbstractController assert_equal({ p2: "Y2" }.to_query, params[1]) end + def test_params_option + url = W.new.url_for(only_path: true, controller: "c", action: "a", params: { domain: "foo", id: "1" }) + params = extract_params(url) + assert_equal("/c/a?domain=foo&id=1", url) + assert_equal({ domain: "foo" }.to_query, params[0]) + assert_equal({ id: "1" }.to_query, params[1]) + end + def test_hash_parameter url = W.new.url_for(only_path: true, controller: "c", action: "a", query: { name: "Bob", category: "prof" }) params = extract_params(url) diff --git a/actionpack/test/controller/webservice_test.rb b/actionpack/test/controller/webservice_test.rb index 4a10637b54..23a46df5cd 100644 --- a/actionpack/test/controller/webservice_test.rb +++ b/actionpack/test/controller/webservice_test.rb @@ -14,7 +14,7 @@ class WebServiceTest < ActionDispatch::IntegrationTest end def dump_params_keys(hash = params) - hash.keys.sort.inject("") do |s, k| + hash.keys.sort.each_with_object(+"") do |k, s| value = hash[k] if value.is_a?(Hash) || value.is_a?(ActionController::Parameters) @@ -23,8 +23,8 @@ class WebServiceTest < ActionDispatch::IntegrationTest value = "" end - s += ", " unless s.empty? - s += "#{k}#{value}" + s << ", " unless s.empty? + s << "#{k}#{value}" end end end diff --git a/actionpack/test/dispatch/actionable_exceptions_test.rb b/actionpack/test/dispatch/actionable_exceptions_test.rb new file mode 100644 index 0000000000..9215a91e9c --- /dev/null +++ b/actionpack/test/dispatch/actionable_exceptions_test.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +require "abstract_unit" + +class ActionableExceptionsTest < ActionDispatch::IntegrationTest + Actions = [] + + class ActionError < StandardError + include ActiveSupport::ActionableError + + action "Successful action" do + Actions << "Action!" + end + + action "Failed action" do + raise "Inaction!" + end + end + + Noop = -> env { [200, {}, [""]] } + + setup do + @app = ActionDispatch::ActionableExceptions.new(Noop) + + Actions.clear + end + + test "dispatches an actionable error" do + post ActionDispatch::ActionableExceptions.endpoint, params: { + error: ActionError.name, + action: "Successful action", + location: "/", + } + + assert_equal ["Action!"], Actions + + assert_equal 302, response.status + assert_equal "/", response.headers["Location"] + end + + test "cannot dispatch errors if not allowed" do + post ActionDispatch::ActionableExceptions.endpoint, params: { + error: ActionError.name, + action: "Successful action", + location: "/", + }, headers: { "action_dispatch.show_exceptions" => false } + + assert_empty Actions + end + + test "dispatched action can fail" do + assert_raise RuntimeError do + post ActionDispatch::ActionableExceptions.endpoint, params: { + error: ActionError.name, + action: "Failed action", + location: "/", + } + end + end + + test "cannot dispatch non-actionable errors" do + assert_raise ActiveSupport::ActionableError::NonActionable do + post ActionDispatch::ActionableExceptions.endpoint, params: { + error: RuntimeError.name, + action: "Inexistent action", + location: "/", + } + end + end + + test "cannot dispatch Inexistent errors" do + assert_raise ActiveSupport::ActionableError::NonActionable do + post ActionDispatch::ActionableExceptions.endpoint, params: { + error: "", + action: "Inexistent action", + location: "/", + } + end + end +end diff --git a/actionpack/test/dispatch/callbacks_test.rb b/actionpack/test/dispatch/callbacks_test.rb index fc80191c02..aa8640c506 100644 --- a/actionpack/test/dispatch/callbacks_test.rb +++ b/actionpack/test/dispatch/callbacks_test.rb @@ -38,7 +38,6 @@ class DispatcherTest < ActiveSupport::TestCase end private - def dispatch(&block) ActionDispatch::Callbacks.new(block || DummyApp.new).call( "rack.input" => StringIO.new("") diff --git a/actionpack/test/dispatch/content_security_policy_test.rb b/actionpack/test/dispatch/content_security_policy_test.rb index c8c885f35c..3d60dc1661 100644 --- a/actionpack/test/dispatch/content_security_policy_test.rb +++ b/actionpack/test/dispatch/content_security_policy_test.rb @@ -128,12 +128,36 @@ class ContentSecurityPolicyTest < ActiveSupport::TestCase @policy.script_src false assert_no_match %r{script-src}, @policy.build + @policy.script_src_attr :self + assert_match %r{script-src-attr 'self'}, @policy.build + + @policy.script_src_attr false + assert_no_match %r{script-src-attr}, @policy.build + + @policy.script_src_elem :self + assert_match %r{script-src-elem 'self'}, @policy.build + + @policy.script_src_elem false + assert_no_match %r{script-src-elem}, @policy.build + @policy.style_src :self assert_match %r{style-src 'self'}, @policy.build @policy.style_src false assert_no_match %r{style-src}, @policy.build + @policy.style_src_attr :self + assert_match %r{style-src-attr 'self'}, @policy.build + + @policy.style_src_attr false + assert_no_match %r{style-src-attr}, @policy.build + + @policy.style_src_elem :self + assert_match %r{style-src-elem 'self'}, @policy.build + + @policy.style_src_elem false + assert_no_match %r{style-src-elem}, @policy.build + @policy.worker_src :self assert_match %r{worker-src 'self'}, @policy.build @@ -307,7 +331,6 @@ class DefaultContentSecurityPolicyIntegrationTest < ActionDispatch::IntegrationT end private - def assert_policy(expected, report_only: false) if report_only expected_header = "Content-Security-Policy-Report-Only" @@ -470,7 +493,6 @@ class ContentSecurityPolicyIntegrationTest < ActionDispatch::IntegrationTest end private - def assert_policy(expected, report_only: false) assert_response :success @@ -544,3 +566,57 @@ class DisabledContentSecurityPolicyIntegrationTest < ActionDispatch::Integration assert_equal "default-src https://example.com", response.headers["Content-Security-Policy"] end end + +class NonceDirectiveContentSecurityPolicyIntegrationTest < ActionDispatch::IntegrationTest + class PolicyController < ActionController::Base + def index + head :ok + end + end + + ROUTES = ActionDispatch::Routing::RouteSet.new + ROUTES.draw do + scope module: "nonce_directive_content_security_policy_integration_test" do + get "/", to: "policy#index" + end + end + + POLICY = ActionDispatch::ContentSecurityPolicy.new do |p| + p.default_src -> { :self } + p.script_src -> { :https } + p.style_src -> { :https } + end + + class PolicyConfigMiddleware + def initialize(app) + @app = app + end + + def call(env) + env["action_dispatch.content_security_policy"] = POLICY + env["action_dispatch.content_security_policy_nonce_generator"] = proc { "iyhD0Yc0W+c=" } + env["action_dispatch.content_security_policy_report_only"] = false + env["action_dispatch.content_security_policy_nonce_directives"] = %w(script-src) + env["action_dispatch.show_exceptions"] = false + + @app.call(env) + end + end + + APP = build_app(ROUTES) do |middleware| + middleware.use PolicyConfigMiddleware + middleware.use ActionDispatch::ContentSecurityPolicy::Middleware + end + + def app + APP + end + + def test_generate_nonce_only_specified_in_nonce_directives + get "/" + + assert_response :success + assert_match "script-src https: 'nonce-iyhD0Yc0W+c='", response.headers["Content-Security-Policy"] + assert_no_match "style-src https: 'nonce-iyhD0Yc0W+c='", response.headers["Content-Security-Policy"] + end +end diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb index 6637c2cae9..d129fa717d 100644 --- a/actionpack/test/dispatch/cookies_test.rb +++ b/actionpack/test/dispatch/cookies_test.rb @@ -123,6 +123,11 @@ class CookiesTest < ActionController::TestCase head :ok end + def set_cookie_if_not_present + cookies["user_name"] = "alice" unless cookies["user_name"].present? + head :ok + end + def logout cookies.delete("user_name") head :ok @@ -312,7 +317,7 @@ class CookiesTest < ActionController::TestCase end def rails_5_2_stable_encrypted_cookie_with_authenticated_encryption_flag_off - cookies[:favorite] = "Wmg4amgvcVVvWGcwK3c4WjJEbTdRQUgrWXhBdDliUTR0cVNidXpmVTMrc2RjcitwUzVsWWEwZGtuVGtFUjJwNi0tcVhVMTFMOTQ1d0hIVE1FK0pJc05SQT09--8b2a55c375049a50f7a959b9d42b31ef0b2bb594" + cookies[:favorite] = "rTG4zs5UufEFAr+ppKwh+MDMymKyAUMOSaWyYa3uUVmD8sMQqyiyQBxgYeAncDHVZIlo4y+kDVSzp66u1/7BNYpnmFe8ES/YT2m8ckNA23jBDmnRZ9CTNfMIRXjFtfxO9YxEOzzhn0ZiA0/zFtr5wkluXtxplOz959Q7MgLOyvTze2h9p8A=--QHOS3rAEGq/HCxXs--xQNra8dk24Idc2qBtpMLpg==" head :ok end @@ -336,7 +341,7 @@ class CookiesTest < ActionController::TestCase SECRET_KEY_BASE = "b3c631c314c0bbca50c1b2843150fe33" SIGNED_COOKIE_SALT = "signed cookie" ENCRYPTED_COOKIE_SALT = "encrypted cookie" - ENCRYPTED_SIGNED_COOKIE_SALT = "sigend encrypted cookie" + ENCRYPTED_SIGNED_COOKIE_SALT = "signed encrypted cookie" AUTHENTICATED_ENCRYPTED_COOKIE_SALT = "authenticated encrypted cookie" def setup @@ -525,21 +530,6 @@ class CookiesTest < ActionController::TestCase assert_equal 45, verifier.verify(@response.cookies["user_id"]) end - def test_signed_cookie_with_legacy_secret_scheme - @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" - - old_message = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33", digest: "SHA1", serializer: Marshal).generate(45) - - @request.headers["Cookie"] = "user_id=#{old_message}" - 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("signed cookie") - verifier = ActiveSupport::MessageVerifier.new(secret, digest: "SHA1", serializer: Marshal) - assert_equal 45, verifier.verify(@response.cookies["user_id"]) - end - def test_tampered_with_signed_cookie key_generator = @request.env["action_dispatch.key_generator"] secret = key_generator.generate_key(@request.env["action_dispatch.signed_cookie_salt"]) @@ -759,175 +749,7 @@ class CookiesTest < ActionController::TestCase assert_equal ["user_name", "user_id"], @request.cookie_jar.instance_variable_get(:@cookies).keys end - def test_raises_argument_error_if_missing_secret - assert_raise(ArgumentError, nil.inspect) { - @request.env["action_dispatch.key_generator"] = ActiveSupport::LegacyKeyGenerator.new(nil) - get :set_signed_cookie - } - - assert_raise(ArgumentError, "".inspect) { - @request.env["action_dispatch.key_generator"] = ActiveSupport::LegacyKeyGenerator.new("") - get :set_signed_cookie - } - end - - def test_raises_argument_error_if_secret_is_probably_insecure - assert_raise(ArgumentError, "password".inspect) { - @request.env["action_dispatch.key_generator"] = ActiveSupport::LegacyKeyGenerator.new("password") - get :set_signed_cookie - } - - assert_raise(ArgumentError, "secret".inspect) { - @request.env["action_dispatch.key_generator"] = ActiveSupport::LegacyKeyGenerator.new("secret") - get :set_signed_cookie - } - - assert_raise(ArgumentError, "12345678901234567890123456789".inspect) { - @request.env["action_dispatch.key_generator"] = ActiveSupport::LegacyKeyGenerator.new("12345678901234567890123456789") - get :set_signed_cookie - } - end - - def test_legacy_signed_cookie_is_read_and_transparently_upgraded_by_signed_cookie_jar_if_both_secret_token_and_secret_key_base_are_set - @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" - - 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) - assert_equal 45, verifier.verify(@response.cookies["user_id"]) - end - - def test_legacy_signed_cookie_is_read_and_transparently_encrypted_by_encrypted_cookie_jar_if_both_secret_token_and_secret_key_base_are_set - @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" - - 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] - - secret = @request.env["action_dispatch.key_generator"].generate_key(@request.env["action_dispatch.authenticated_encrypted_cookie_salt"], 32) - encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: "aes-256-gcm", serializer: Marshal) - 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" - - 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" - - 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] - - cipher = "aes-256-gcm" - salt = @request.env["action_dispatch.authenticated_encrypted_cookie_salt"] - secret = @request.env["action_dispatch.key_generator"].generate_key(salt)[0, ActiveSupport::MessageEncryptor.key_len(cipher)] - encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: cipher, 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" - - 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" - - 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] - - salt = @request.env["action_dispatch.authenticated_encrypted_cookie_salt"] - secret = @request.env["action_dispatch.key_generator"].generate_key(salt)[0, ActiveSupport::MessageEncryptor.key_len("aes-256-gcm")] - encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: "aes-256-gcm", 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" - - 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.use_authenticated_cookie_encryption"] = true - @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" - @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" - - 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] - - salt = @request.env["action_dispatch.authenticated_encrypted_cookie_salt"] - secret = @request.env["action_dispatch.key_generator"].generate_key(salt)[0, ActiveSupport::MessageEncryptor.key_len("aes-256-gcm")] - encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: "aes-256-gcm", 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.headers["Cookie"] = "user_id=45" get :get_signed_cookie @@ -936,8 +758,6 @@ class CookiesTest < ActionController::TestCase end def test_legacy_signed_cookie_is_treated_as_nil_by_encrypted_cookie_jar_if_tampered - @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" - @request.headers["Cookie"] = "foo=baz" get :get_encrypted_cookie @@ -1313,6 +1133,14 @@ class CookiesTest < ActionController::TestCase assert_equal "bar", @controller.encrypted_cookie end + def test_cookie_override + get :set_cookie_if_not_present + assert_equal "alice", cookies["user_name"] + cookies["user_name"] = "bob" + get :set_cookie_if_not_present + assert_equal "bob", cookies["user_name"] + end + def test_signed_cookie_with_expires_set_relatively request.env["action_dispatch.use_cookies_with_metadata"] = true @@ -1378,11 +1206,7 @@ class CookiesTest < ActionController::TestCase get :encrypted_discount_and_user_id_cookie travel 2.hours - assert_equal 50, cookies.encrypted[:user_id] - - cookies[:discount_percentage] = cookies[:user_id] - assert_not_equal 10, cookies.encrypted[:discount_percentage] - assert_equal 50, cookies.encrypted[:discount_percentage] + assert_nil cookies.signed[:user_id] end def test_switch_off_metadata_for_signed_cookies_if_config_is_false @@ -1391,11 +1215,8 @@ class CookiesTest < ActionController::TestCase get :signed_discount_and_user_id_cookie travel 2.hours - assert_equal 50, cookies.signed[:user_id] - cookies[:discount_percentage] = cookies[:user_id] - assert_not_equal 10, cookies.signed[:discount_percentage] - assert_equal 50, cookies.signed[:discount_percentage] + assert_nil cookies.signed[:user_id] end def test_read_rails_5_2_stable_encrypted_cookies_if_config_is_false diff --git a/actionpack/test/dispatch/debug_exceptions_test.rb b/actionpack/test/dispatch/debug_exceptions_test.rb index 37399cfd07..fa629bc761 100644 --- a/actionpack/test/dispatch/debug_exceptions_test.rb +++ b/actionpack/test/dispatch/debug_exceptions_test.rb @@ -5,10 +5,22 @@ require "abstract_unit" class DebugExceptionsTest < ActionDispatch::IntegrationTest InterceptedErrorInstance = StandardError.new + class CustomActionableError < StandardError + include ActiveSupport::ActionableError + + action "Action 1" do + nil + end + + action "Action 2" do + nil + end + end + class Boomer attr_accessor :closed - def initialize(detailed = false) + def initialize(detailed = false) @detailed = detailed @closed = false end @@ -27,67 +39,73 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest end def raise_nested_exceptions + raise "First error" + rescue begin - raise "First error" + raise "Second error" rescue - begin - raise "Second error" - rescue - raise "Third error" - end + raise "Third error" end end def call(env) env["action_dispatch.show_detailed_exceptions"] = @detailed req = ActionDispatch::Request.new(env) + template = ActionView::Template.new(File.read(__FILE__), __FILE__, ActionView::Template::Handlers::Raw.new, format: :html, locals: []) + case req.path - when %r{/pass} + when "/pass" [404, { "X-Cascade" => "pass" }, self] - when %r{/not_found} + when "/not_found" raise AbstractController::ActionNotFound - when %r{/runtime_error} + when "/runtime_error" raise RuntimeError - when %r{/method_not_allowed} + when "/method_not_allowed" raise ActionController::MethodNotAllowed - when %r{/intercepted_error} + when "/intercepted_error" raise InterceptedErrorInstance - when %r{/unknown_http_method} + when "/unknown_http_method" raise ActionController::UnknownHttpMethod - when %r{/not_implemented} + when "/not_implemented" raise ActionController::NotImplemented - when %r{/unprocessable_entity} + when "/unprocessable_entity" raise ActionController::InvalidAuthenticityToken - when %r{/not_found_original_exception} + when "/invalid_mimetype" + raise Mime::Type::InvalidMimeType + when "/not_found_original_exception" begin raise AbstractController::ActionNotFound.new rescue - raise ActionView::Template::Error.new("template") + raise ActionView::Template::Error.new(template) + end + when "/cause_mapped_to_rescue_responses" + begin + raise ActionController::ParameterMissing, :missing_param_key + rescue + raise NameError.new("uninitialized constant Userr") end - when %r{/missing_template} + when "/missing_template" raise ActionView::MissingTemplate.new(%w(foo), "foo/index", %w(foo), false, "mailer") - when %r{/bad_request} + when "/bad_request" raise ActionController::BadRequest - when %r{/missing_keys} + when "/missing_keys" raise ActionController::UrlGenerationError, "No route matches" - when %r{/parameter_missing} + when "/parameter_missing" raise ActionController::ParameterMissing, :missing_param_key - when %r{/original_syntax_error} + when "/original_syntax_error" eval "broke_syntax =" # `eval` need for raise native SyntaxError at runtime - when %r{/syntax_error_into_view} + when "/syntax_error_into_view" begin eval "broke_syntax =" rescue Exception - template = ActionView::Template.new(File.read(__FILE__), - __FILE__, - ActionView::Template::Handlers::Raw.new, - {}) raise ActionView::Template::Error.new(template) end - when %r{/framework_raises} + when "/framework_raises" method_that_raises - when %r{/nested_exceptions} + when "/nested_exceptions" raise_nested_exceptions + when %r{/actionable_error} + raise CustomActionableError else raise "puke!" end @@ -176,6 +194,10 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest get "/parameter_missing", headers: { "action_dispatch.show_exceptions" => true } assert_response 400 assert_match(/ActionController::ParameterMissing/, body) + + get "/invalid_mimetype", headers: { "Accept" => "text/html,*", "action_dispatch.show_exceptions" => true } + assert_response 406 + assert_match(/Mime::Type::InvalidMimeType/, body) end test "rescue with text error for xhr request" do @@ -186,7 +208,7 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest assert_response 500 assert_no_match(/<header>/, body) assert_no_match(/<body>/, body) - assert_equal "text/plain", response.content_type + assert_equal "text/plain", response.media_type assert_match(/RuntimeError\npuke/, body) Rails.stub :root, Pathname.new(".") do @@ -200,31 +222,31 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest get "/not_found", headers: xhr_request_env assert_response 404 assert_no_match(/<body>/, body) - assert_equal "text/plain", response.content_type + assert_equal "text/plain", response.media_type assert_match(/#{AbstractController::ActionNotFound.name}/, body) get "/method_not_allowed", headers: xhr_request_env assert_response 405 assert_no_match(/<body>/, body) - assert_equal "text/plain", response.content_type + assert_equal "text/plain", response.media_type assert_match(/ActionController::MethodNotAllowed/, body) get "/unknown_http_method", headers: xhr_request_env assert_response 405 assert_no_match(/<body>/, body) - assert_equal "text/plain", response.content_type + assert_equal "text/plain", response.media_type assert_match(/ActionController::UnknownHttpMethod/, body) get "/bad_request", headers: xhr_request_env assert_response 400 assert_no_match(/<body>/, body) - assert_equal "text/plain", response.content_type + assert_equal "text/plain", response.media_type assert_match(/ActionController::BadRequest/, body) get "/parameter_missing", headers: xhr_request_env assert_response 400 assert_no_match(/<body>/, body) - assert_equal "text/plain", response.content_type + assert_equal "text/plain", response.media_type assert_match(/ActionController::ParameterMissing/, body) end @@ -235,37 +257,37 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest assert_response 500 assert_no_match(/<header>/, body) assert_no_match(/<body>/, body) - assert_equal "application/json", response.content_type + assert_equal "application/json", response.media_type assert_match(/RuntimeError: puke/, body) get "/not_found", headers: { "action_dispatch.show_exceptions" => true }, as: :json assert_response 404 assert_no_match(/<body>/, body) - assert_equal "application/json", response.content_type + assert_equal "application/json", response.media_type assert_match(/#{AbstractController::ActionNotFound.name}/, body) get "/method_not_allowed", headers: { "action_dispatch.show_exceptions" => true }, as: :json assert_response 405 assert_no_match(/<body>/, body) - assert_equal "application/json", response.content_type + assert_equal "application/json", response.media_type assert_match(/ActionController::MethodNotAllowed/, body) get "/unknown_http_method", headers: { "action_dispatch.show_exceptions" => true }, as: :json assert_response 405 assert_no_match(/<body>/, body) - assert_equal "application/json", response.content_type + assert_equal "application/json", response.media_type assert_match(/ActionController::UnknownHttpMethod/, body) get "/bad_request", headers: { "action_dispatch.show_exceptions" => true }, as: :json assert_response 400 assert_no_match(/<body>/, body) - assert_equal "application/json", response.content_type + assert_equal "application/json", response.media_type assert_match(/ActionController::BadRequest/, body) get "/parameter_missing", headers: { "action_dispatch.show_exceptions" => true }, as: :json assert_response 400 assert_no_match(/<body>/, body) - assert_equal "application/json", response.content_type + assert_equal "application/json", response.media_type assert_match(/ActionController::ParameterMissing/, body) end @@ -276,7 +298,7 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest assert_response 500 assert_match(/<header>/, body) assert_match(/<body>/, body) - assert_equal "text/html", response.content_type + assert_equal "text/html", response.media_type assert_match(/puke/, body) end @@ -285,27 +307,25 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest get "/index.xml", headers: { "action_dispatch.show_exceptions" => true } assert_response 500 - assert_equal "application/xml", response.content_type + assert_equal "application/xml", response.media_type assert_match(/RuntimeError: puke/, body) end test "rescue with JSON format as fallback if API request format is not supported" do - begin - Mime::Type.register "text/wibble", :wibble + Mime::Type.register "text/wibble", :wibble - ActionDispatch::IntegrationTest.register_encoder(:wibble, - param_encoder: -> params { params }) + ActionDispatch::IntegrationTest.register_encoder(:wibble, + param_encoder: -> params { params }) - @app = ActionDispatch::DebugExceptions.new(Boomer.new(true), RoutesApp, :api) + @app = ActionDispatch::DebugExceptions.new(Boomer.new(true), RoutesApp, :api) - get "/index", headers: { "action_dispatch.show_exceptions" => true }, as: :wibble - assert_response 500 - assert_equal "application/json", response.content_type - assert_match(/RuntimeError: puke/, body) + get "/index", headers: { "action_dispatch.show_exceptions" => true }, as: :wibble + assert_response 500 + assert_equal "application/json", response.media_type + assert_match(/RuntimeError: puke/, body) - ensure - Mime::Type.unregister :wibble - end + ensure + Mime::Type.unregister :wibble end test "does not show filtered parameters" do @@ -317,15 +337,25 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest assert_match(""foo"=>"[FILTERED]"", body) end - test "show registered original exception for wrapped exceptions" do + test "show registered original exception if the last exception is TemplateError" do @app = DevelopmentApp get "/not_found_original_exception", headers: { "action_dispatch.show_exceptions" => true } assert_response 404 - assert_match(/AbstractController::ActionNotFound/, body) + assert_match %r{AbstractController::ActionNotFound}, body + assert_match %r{Showing <i>.*test/dispatch/debug_exceptions_test.rb</i>}, body end - test "named urls missing keys raise 500 level error" do + test "show the last exception and cause even when the cause is mapped to resque_responses" do + @app = DevelopmentApp + + get "/cause_mapped_to_rescue_responses", headers: { "action_dispatch.show_exceptions" => true } + assert_response 500 + assert_match %r{ActionController::ParameterMissing}, body + assert_match %r{NameError}, body + end + + test "named URLs missing keys raise 500 level error" do @app = DevelopmentApp get "/missing_keys", headers: { "action_dispatch.show_exceptions" => true } @@ -436,6 +466,8 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest end test "logs exception backtrace when all lines silenced" do + @app = DevelopmentApp + output = StringIO.new backtrace_cleaner = ActiveSupport::BacktraceCleaner.new backtrace_cleaner.add_silencer { true } @@ -448,6 +480,27 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest assert_operator((output.rewind && output.read).lines.count, :>, 10) end + test "doesn't log the framework backtrace when error type is a routing error" do + @app = ProductionApp + + output = StringIO.new + backtrace_cleaner = ActiveSupport::BacktraceCleaner.new + backtrace_cleaner.add_silencer { true } + + env = { "action_dispatch.show_exceptions" => true, + "action_dispatch.logger" => Logger.new(output), + "action_dispatch.backtrace_cleaner" => backtrace_cleaner } + + assert_raises ActionController::RoutingError do + get "/pass", headers: env + end + + log = output.rewind && output.read + + assert_includes log, "ActionController::RoutingError (No route matches [GET] \"/pass\")" + assert_equal 3, log.lines.count + end + test "display backtrace when error type is SyntaxError" do @app = DevelopmentApp @@ -484,6 +537,7 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest assert_select "#Application-Trace-0" do assert_select "code", /syntax error, unexpected/ end + assert_match %r{Showing <i>.*test/dispatch/debug_exceptions_test.rb</i>}, body end test "debug exceptions app shows user code that caused the error in source view" do @@ -572,4 +626,40 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest end end end + + test "shows a buttons for every action in an actionable error" do + @app = DevelopmentApp + Rails.stub :root, Pathname.new(".") do + cleaner = ActiveSupport::BacktraceCleaner.new.tap do |bc| + bc.add_silencer { |line| line !~ %r{test/dispatch/debug_exceptions_test.rb} } + end + + get "/actionable_error", headers: { "action_dispatch.backtrace_cleaner" => cleaner } + + # Assert correct error + assert_response 500 + + assert_select 'input[value="Action 1"]' + assert_select 'input[value="Action 2"]' + end + end + + test "debug exceptions app shows diagnostics when malformed query parameters are provided" do + @app = DevelopmentApp + + get "/bad_request?x[y]=1&x[y][][w]=2" + + assert_response 400 + assert_match "ActionController::BadRequest", body + end + + test "debug exceptions app shows diagnostics when malformed query parameters are provided by XHR" do + @app = DevelopmentApp + xhr_request_env = { "action_dispatch.show_exceptions" => true, "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest" } + + get "/bad_request?x[y]=1&x[y][][w]=2", headers: xhr_request_env + + assert_response 400 + assert_match "ActionController::BadRequest", body + end end diff --git a/actionpack/test/dispatch/feature_policy_test.rb b/actionpack/test/dispatch/feature_policy_test.rb new file mode 100644 index 0000000000..ebcc8a8b6d --- /dev/null +++ b/actionpack/test/dispatch/feature_policy_test.rb @@ -0,0 +1,142 @@ +# frozen_string_literal: true + +require "abstract_unit" + +class FeaturePolicyTest < ActiveSupport::TestCase + def setup + @policy = ActionDispatch::FeaturePolicy.new + end + + def test_mappings + @policy.midi :self + assert_equal "midi 'self'", @policy.build + + @policy.midi :none + assert_equal "midi 'none'", @policy.build + end + + def test_multiple_sources_for_a_single_directive + @policy.geolocation :self, "https://example.com" + assert_equal "geolocation 'self' https://example.com", @policy.build + end + + def test_single_directive_for_multiple_directives + @policy.geolocation :self + @policy.usb :none + assert_equal "geolocation 'self'; usb 'none'", @policy.build + end + + def test_multiple_directives_for_multiple_directives + @policy.geolocation :self, "https://example.com" + @policy.usb :none, "https://example.com" + assert_equal "geolocation 'self' https://example.com; usb 'none' https://example.com", @policy.build + end + + def test_invalid_directive_source + exception = assert_raises(ArgumentError) do + @policy.vr [:non_existent] + end + + assert_equal "Invalid HTTP feature policy source: [:non_existent]", exception.message + end +end + +class FeaturePolicyIntegrationTest < ActionDispatch::IntegrationTest + class PolicyController < ActionController::Base + feature_policy only: :index do |f| + f.gyroscope :none + end + + feature_policy only: :sample_controller do |f| + f.gyroscope nil + f.usb :self + end + + feature_policy only: :multiple_directives do |f| + f.gyroscope nil + f.usb :self + f.autoplay "https://example.com" + f.payment "https://secure.example.com" + end + + def index + head :ok + end + + def sample_controller + head :ok + end + + def multiple_directives + head :ok + end + end + + ROUTES = ActionDispatch::Routing::RouteSet.new + ROUTES.draw do + scope module: "feature_policy_integration_test" do + get "/", to: "policy#index" + get "/sample_controller", to: "policy#sample_controller" + get "/multiple_directives", to: "policy#multiple_directives" + end + end + + POLICY = ActionDispatch::FeaturePolicy.new do |p| + p.gyroscope :self + end + + class PolicyConfigMiddleware + def initialize(app) + @app = app + end + + def call(env) + env["action_dispatch.feature_policy"] = POLICY + env["action_dispatch.show_exceptions"] = false + + @app.call(env) + end + end + + APP = build_app(ROUTES) do |middleware| + middleware.use PolicyConfigMiddleware + middleware.use ActionDispatch::FeaturePolicy::Middleware + end + + def app + APP + end + + def test_generates_feature_policy_header + get "/" + assert_policy "gyroscope 'none'" + end + + def test_generates_per_controller_feature_policy_header + get "/sample_controller" + assert_policy "usb 'self'" + end + + def test_generates_multiple_directives_feature_policy_header + get "/multiple_directives" + assert_policy "usb 'self'; autoplay https://example.com; payment https://secure.example.com" + end + + private + def env_config + Rails.application.env_config + end + + def feature_policy + env_config["action_dispatch.feature_policy"] + end + + def feature_policy=(policy) + env_config["action_dispatch.feature_policy"] = policy + end + + def assert_policy(expected) + assert_response :success + assert_equal expected, response.headers["Feature-Policy"] + end +end diff --git a/actionpack/test/dispatch/host_authorization_test.rb b/actionpack/test/dispatch/host_authorization_test.rb new file mode 100644 index 0000000000..5263dd2597 --- /dev/null +++ b/actionpack/test/dispatch/host_authorization_test.rb @@ -0,0 +1,161 @@ +# frozen_string_literal: true + +require "abstract_unit" +require "ipaddr" + +class HostAuthorizationTest < ActionDispatch::IntegrationTest + App = -> env { [200, {}, %w(Success)] } + + test "blocks requests to unallowed host" do + @app = ActionDispatch::HostAuthorization.new(App, %w(only.com)) + + get "/" + + assert_response :forbidden + assert_match "Blocked host: www.example.com", response.body + end + + test "allows all requests if hosts is empty" do + @app = ActionDispatch::HostAuthorization.new(App, nil) + + get "/" + + assert_response :ok + assert_equal "Success", body + end + + test "hosts can be a single element array" do + @app = ActionDispatch::HostAuthorization.new(App, %w(www.example.com)) + + get "/" + + assert_response :ok + assert_equal "Success", body + end + + test "hosts can be a string" do + @app = ActionDispatch::HostAuthorization.new(App, "www.example.com") + + get "/" + + assert_response :ok + assert_equal "Success", body + end + + test "passes requests to allowed hosts with domain name notation" do + @app = ActionDispatch::HostAuthorization.new(App, ".example.com") + + get "/" + + assert_response :ok + assert_equal "Success", body + end + + test "does not allow domain name notation in the HOST header itself" do + @app = ActionDispatch::HostAuthorization.new(App, ".example.com") + + get "/", env: { + "HOST" => ".example.com", + } + + assert_response :forbidden + assert_match "Blocked host: .example.com", response.body + end + + test "checks for requests with #=== to support wider range of host checks" do + @app = ActionDispatch::HostAuthorization.new(App, [-> input { input == "www.example.com" }]) + + get "/" + + assert_response :ok + assert_equal "Success", body + end + + test "mark the host when authorized" do + @app = ActionDispatch::HostAuthorization.new(App, ".example.com") + + get "/" + + assert_equal "www.example.com", request.get_header("action_dispatch.authorized_host") + end + + test "sanitizes regular expressions to prevent accidental matches" do + @app = ActionDispatch::HostAuthorization.new(App, [/w.example.co/]) + + get "/" + + assert_response :forbidden + assert_match "Blocked host: www.example.com", response.body + end + + test "blocks requests to unallowed host supporting custom responses" do + @app = ActionDispatch::HostAuthorization.new(App, ["w.example.co"], -> env do + [401, {}, %w(Custom)] + end) + + get "/" + + assert_response :unauthorized + assert_equal "Custom", body + end + + test "blocks requests with spoofed X-FORWARDED-HOST" do + @app = ActionDispatch::HostAuthorization.new(App, [IPAddr.new("127.0.0.1")]) + + get "/", env: { + "HTTP_X_FORWARDED_HOST" => "127.0.0.1", + "HOST" => "www.example.com", + } + + assert_response :forbidden + assert_match "Blocked host: 127.0.0.1", response.body + end + + test "does not consider IP addresses in X-FORWARDED-HOST spoofed when disabled" do + @app = ActionDispatch::HostAuthorization.new(App, nil) + + get "/", env: { + "HTTP_X_FORWARDED_HOST" => "127.0.0.1", + "HOST" => "www.example.com", + } + + assert_response :ok + assert_equal "Success", body + end + + test "detects localhost domain spoofing" do + @app = ActionDispatch::HostAuthorization.new(App, "localhost") + + get "/", env: { + "HTTP_X_FORWARDED_HOST" => "localhost", + "HOST" => "www.example.com", + } + + assert_response :forbidden + assert_match "Blocked host: localhost", response.body + end + + test "forwarded hosts should be permitted" do + @app = ActionDispatch::HostAuthorization.new(App, "domain.com") + + get "/", env: { + "HTTP_X_FORWARDED_HOST" => "sub.domain.com", + "HOST" => "domain.com", + } + + assert_response :forbidden + assert_match "Blocked host: sub.domain.com", response.body + end + + test "forwarded hosts are allowed when permitted" do + @app = ActionDispatch::HostAuthorization.new(App, ".domain.com") + + get "/", env: { + "HTTP_X_FORWARDED_HOST" => "sub.domain.com", + "HOST" => "domain.com", + } + + assert_response :ok + assert_equal "Success", body + end +end diff --git a/actionpack/test/dispatch/live_response_test.rb b/actionpack/test/dispatch/live_response_test.rb index a9a56f205f..f2459112b2 100644 --- a/actionpack/test/dispatch/live_response_test.rb +++ b/actionpack/test/dispatch/live_response_test.rb @@ -51,18 +51,24 @@ module ActionController assert_equal ["omg"], @response.body_parts end - def test_cache_control_is_set + def test_cache_control_is_set_by_default @response.stream.write "omg" assert_equal "no-cache", @response.headers["Cache-Control"] end + def test_cache_control_is_set_manually + @response.set_header("Cache-Control", "public") + @response.stream.write "omg" + assert_equal "public", @response.headers["Cache-Control"] + end + def test_content_length_is_removed @response.headers["Content-Length"] = "1234" @response.stream.write "omg" assert_nil @response.headers["Content-Length"] end - def test_headers_cannot_be_written_after_webserver_reads + def test_headers_cannot_be_written_after_web_server_reads @response.stream.write "omg" latch = Concurrent::CountDownLatch.new diff --git a/actionpack/test/dispatch/middleware_stack_test.rb b/actionpack/test/dispatch/middleware_stack_test.rb index 5f43e5a3c5..c534e60c74 100644 --- a/actionpack/test/dispatch/middleware_stack_test.rb +++ b/actionpack/test/dispatch/middleware_stack_test.rb @@ -3,13 +3,24 @@ require "abstract_unit" class MiddlewareStackTest < ActiveSupport::TestCase - class FooMiddleware; end - class BarMiddleware; end - class BazMiddleware; end - class HiyaMiddleware; end - class BlockMiddleware + class Base + def initialize(app) + @app = app + end + + def call(env) + @app.call(env) + end + end + + class FooMiddleware < Base; end + class BarMiddleware < Base; end + class BazMiddleware < Base; end + class HiyaMiddleware < Base; end + class BlockMiddleware < Base attr_reader :block - def initialize(&block) + def initialize(app, &block) + super(app) @block = block end end @@ -109,6 +120,24 @@ class MiddlewareStackTest < ActiveSupport::TestCase assert_equal @stack.last, @stack.last end + test "instruments the execution of middlewares" do + events = [] + + subscriber = proc do |*args| + events << ActiveSupport::Notifications::Event.new(*args) + end + + ActiveSupport::Notifications.subscribed(subscriber, "process_middleware.action_dispatch") do + app = @stack.build(proc { |env| [200, {}, []] }) + + env = {} + app.call(env) + end + + assert_equal 2, events.count + assert_equal ["MiddlewareStackTest::BarMiddleware", "MiddlewareStackTest::FooMiddleware"], events.map { |e| e.payload[:middleware] } + end + test "includes a middleware" do assert_equal true, @stack.include?(ActionDispatch::MiddlewareStack::Middleware.new(BarMiddleware, nil, nil)) end diff --git a/actionpack/test/dispatch/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb index fa264417e1..50f6c06fee 100644 --- a/actionpack/test/dispatch/mime_type_test.rb +++ b/actionpack/test/dispatch/mime_type_test.rb @@ -96,57 +96,47 @@ class MimeTypeTest < ActiveSupport::TestCase end test "custom type" do - begin - type = Mime::Type.register("image/foo", :foo) - assert_equal type, Mime[:foo] - ensure - Mime::Type.unregister(:foo) - end + type = Mime::Type.register("image/foo", :foo) + assert_equal type, Mime[:foo] + ensure + Mime::Type.unregister(:foo) end test "custom type with type aliases" do - begin - Mime::Type.register "text/foobar", :foobar, ["text/foo", "text/bar"] - %w[text/foobar text/foo text/bar].each do |type| - assert_equal Mime[:foobar], type - end - ensure - Mime::Type.unregister(:foobar) + Mime::Type.register "text/foobar", :foobar, ["text/foo", "text/bar"] + %w[text/foobar text/foo text/bar].each do |type| + assert_equal Mime[:foobar], type end + ensure + Mime::Type.unregister(:foobar) end test "register callbacks" do - begin - registered_mimes = [] - Mime::Type.register_callback do |mime| - registered_mimes << mime - end - - mime = Mime::Type.register("text/foo", :foo) - assert_equal [mime], registered_mimes - ensure - Mime::Type.unregister(:foo) + registered_mimes = [] + Mime::Type.register_callback do |mime| + registered_mimes << mime end + + mime = Mime::Type.register("text/foo", :foo) + assert_equal [mime], registered_mimes + ensure + Mime::Type.unregister(:foo) end test "custom type with extension aliases" do - begin - Mime::Type.register "text/foobar", :foobar, [], [:foo, "bar"] - %w[foobar foo bar].each do |extension| - assert_equal Mime[:foobar], Mime::EXTENSION_LOOKUP[extension] - end - ensure - Mime::Type.unregister(:foobar) + Mime::Type.register "text/foobar", :foobar, [], [:foo, "bar"] + %w[foobar foo bar].each do |extension| + assert_equal Mime[:foobar], Mime::EXTENSION_LOOKUP[extension] end + ensure + Mime::Type.unregister(:foobar) end test "register alias" do - begin - Mime::Type.register_alias "application/xhtml+xml", :foobar - assert_equal Mime[:html], Mime::EXTENSION_LOOKUP["foobar"] - ensure - Mime::Type.unregister(:foobar) - end + Mime::Type.register_alias "application/xhtml+xml", :foobar + assert_equal Mime[:html], Mime::EXTENSION_LOOKUP["foobar"] + ensure + Mime::Type.unregister(:foobar) end test "type should be equal to symbol" do @@ -184,4 +174,51 @@ class MimeTypeTest < ActiveSupport::TestCase assert_not (Mime[:js] !~ "application/javascript") assert Mime[:html] =~ "application/xhtml+xml" end + + test "can be initialized with wildcards" do + assert_equal "*/*", Mime::Type.new("*/*").to_s + assert_equal "text/*", Mime::Type.new("text/*").to_s + assert_equal "video/*", Mime::Type.new("video/*").to_s + end + + test "can be initialized with parameters" do + assert_equal "text/html; parameter", Mime::Type.new("text/html; parameter").to_s + assert_equal "text/html; parameter=abc", Mime::Type.new("text/html; parameter=abc").to_s + assert_equal 'text/html; parameter="abc"', Mime::Type.new('text/html; parameter="abc"').to_s + assert_equal 'text/html; parameter=abc; parameter2="xyz"', Mime::Type.new('text/html; parameter=abc; parameter2="xyz"').to_s + end + + test "invalid mime types raise error" do + assert_raises Mime::Type::InvalidMimeType do + Mime::Type.new("too/many/slash") + end + + assert_raises Mime::Type::InvalidMimeType do + Mime::Type.new("missingslash") + end + + assert_raises Mime::Type::InvalidMimeType do + Mime::Type.new("improper/semicolon;") + end + + assert_raises Mime::Type::InvalidMimeType do + Mime::Type.new('improper/semicolon; parameter=abc; parameter2="xyz";') + end + + assert_raises Mime::Type::InvalidMimeType do + Mime::Type.new("text/html, text/plain") + end + + assert_raises Mime::Type::InvalidMimeType do + Mime::Type.new("*/html") + end + + assert_raises Mime::Type::InvalidMimeType do + Mime::Type.new("") + end + + assert_raises Mime::Type::InvalidMimeType do + Mime::Type.new(nil) + end + end end diff --git a/actionpack/test/dispatch/mount_test.rb b/actionpack/test/dispatch/mount_test.rb index f6cf653980..758cee9930 100644 --- a/actionpack/test/dispatch/mount_test.rb +++ b/actionpack/test/dispatch/mount_test.rb @@ -27,6 +27,7 @@ class TestRoutingMount < ActionDispatch::IntegrationTest } mount SprocketsApp, at: "/sprockets" + mount SprocketsApp, at: "/star*" mount SprocketsApp => "/shorthand" mount SinatraLikeApp, at: "/fakeengine", as: :fake @@ -58,6 +59,14 @@ class TestRoutingMount < ActionDispatch::IntegrationTest def test_mounting_at_root_path get "/omg" assert_equal " -- /omg", response.body + + get "/~omg" + assert_equal " -- /~omg", response.body + end + + def test_mounting_at_path_with_non_word_character + get "/star*/omg" + assert_equal "/star* -- /omg", response.body end def test_mounting_sets_script_name @@ -80,6 +89,12 @@ class TestRoutingMount < ActionDispatch::IntegrationTest assert_equal "/shorthand -- /omg", response.body end + def test_mounting_does_not_match_similar_paths + get "/shorthandomg" + assert_not_equal "/shorthand -- /omg", response.body + assert_equal " -- /shorthandomg", response.body + end + def test_mounting_works_with_via get "/getfake" assert_equal "OK", response.body diff --git a/actionpack/test/dispatch/prefix_generation_test.rb b/actionpack/test/dispatch/prefix_generation_test.rb index 7a7a201b11..63c147cb1b 100644 --- a/actionpack/test/dispatch/prefix_generation_test.rb +++ b/actionpack/test/dispatch/prefix_generation_test.rb @@ -151,17 +151,17 @@ module TestGenerationPrefix include BlogEngine.routes.mounted_helpers # Inside Engine - test "[ENGINE] generating engine's url use SCRIPT_NAME from request" do + test "[ENGINE] generating engine's URL use SCRIPT_NAME from request" do get "/pure-awesomeness/blog/posts/1" assert_equal "/pure-awesomeness/blog/posts/1", response.body end - test "[ENGINE] generating application's url never uses SCRIPT_NAME from request" do + test "[ENGINE] generating application's URL never uses SCRIPT_NAME from request" do get "/pure-awesomeness/blog/url_to_application" assert_equal "/generate", response.body end - test "[ENGINE] generating engine's url with polymorphic path" do + test "[ENGINE] generating engine's URL with polymorphic path" do get "/pure-awesomeness/blog/polymorphic_path_for_engine" assert_equal "/pure-awesomeness/blog/posts/1", response.body end @@ -243,7 +243,7 @@ module TestGenerationPrefix assert_equal "/something/awesome/blog/posts/1", response.body end - test "[APP] generating engine's url with polymorphic path" do + test "[APP] generating engine's URL with polymorphic path" do get "/polymorphic_path_for_engine" assert_equal "/awesome/blog/posts/1", response.body end @@ -253,7 +253,7 @@ module TestGenerationPrefix assert_equal "/posts/1", response.body end - test "[APP] generating engine's url with url_for(@post)" do + test "[APP] generating engine's URL with url_for(@post)" do get "/polymorphic_with_url_for" assert_equal "http://www.example.com/awesome/blog/posts/1", response.body end diff --git a/actionpack/test/dispatch/request/json_params_parsing_test.rb b/actionpack/test/dispatch/request/json_params_parsing_test.rb index beab8e78b5..2a48a12497 100644 --- a/actionpack/test/dispatch/request/json_params_parsing_test.rb +++ b/actionpack/test/dispatch/request/json_params_parsing_test.rb @@ -74,17 +74,15 @@ class JsonParamsParsingTest < ActionDispatch::IntegrationTest test "occurring a parse error if parsing unsuccessful" do with_test_routing do - begin - $stderr = StringIO.new # suppress the log - json = "[\"person]\": {\"name\": \"David\"}}" - exception = assert_raise(ActionDispatch::Http::Parameters::ParseError) do - post "/parse", params: json, headers: { "CONTENT_TYPE" => "application/json", "action_dispatch.show_exceptions" => false } - end - assert_equal JSON::ParserError, exception.cause.class - assert_equal exception.cause.message, exception.message - ensure - $stderr = STDERR + $stderr = StringIO.new # suppress the log + json = "[\"person]\": {\"name\": \"David\"}}" + exception = assert_raise(ActionDispatch::Http::Parameters::ParseError) do + post "/parse", params: json, headers: { "CONTENT_TYPE" => "application/json", "action_dispatch.show_exceptions" => false } end + assert_equal JSON::ParserError, exception.cause.class + assert_equal exception.cause.message, exception.message + ensure + $stderr = STDERR end end @@ -157,31 +155,27 @@ class RootLessJSONParamsParsingTest < ActionDispatch::IntegrationTest end test "parses json params after custom json mime type registered" do - begin - Mime::Type.unregister :json - Mime::Type.register "application/json", :json, %w(application/vnd.rails+json) - assert_parses( - { "user" => { "username" => "meinac" }, "username" => "meinac" }, - "{\"username\": \"meinac\"}", "CONTENT_TYPE" => "application/json" - ) - ensure - Mime::Type.unregister :json - Mime::Type.register "application/json", :json, %w( text/x-json application/jsonrequest ) - end + Mime::Type.unregister :json + Mime::Type.register "application/json", :json, %w(application/vnd.rails+json) + assert_parses( + { "user" => { "username" => "meinac" }, "username" => "meinac" }, + "{\"username\": \"meinac\"}", "CONTENT_TYPE" => "application/json" + ) + ensure + Mime::Type.unregister :json + Mime::Type.register "application/json", :json, %w( text/x-json application/jsonrequest ) end test "parses json params after custom json mime type registered with synonym" do - begin - Mime::Type.unregister :json - Mime::Type.register "application/json", :json, %w(application/vnd.rails+json) - assert_parses( - { "user" => { "username" => "meinac" }, "username" => "meinac" }, - "{\"username\": \"meinac\"}", "CONTENT_TYPE" => "application/vnd.rails+json" - ) - ensure - Mime::Type.unregister :json - Mime::Type.register "application/json", :json, %w( text/x-json application/jsonrequest ) - end + Mime::Type.unregister :json + Mime::Type.register "application/json", :json, %w(application/vnd.rails+json) + assert_parses( + { "user" => { "username" => "meinac" }, "username" => "meinac" }, + "{\"username\": \"meinac\"}", "CONTENT_TYPE" => "application/vnd.rails+json" + ) + ensure + Mime::Type.unregister :json + Mime::Type.register "application/json", :json, %w( text/x-json application/jsonrequest ) end private diff --git a/actionpack/test/dispatch/request_id_test.rb b/actionpack/test/dispatch/request_id_test.rb index 9df4712dab..036180c297 100644 --- a/actionpack/test/dispatch/request_id_test.rb +++ b/actionpack/test/dispatch/request_id_test.rb @@ -29,7 +29,6 @@ class RequestIdTest < ActiveSupport::TestCase end private - def stub_request(env = {}) ActionDispatch::RequestId.new(lambda { |environment| [ 200, environment, [] ] }).call(env) ActionDispatch::Request.new(env) @@ -58,7 +57,6 @@ class RequestIdResponseTest < ActionDispatch::IntegrationTest end private - def with_test_route_set with_routing do |set| set.draw do diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb index 9d1246b3a4..0ec8dd25e0 100644 --- a/actionpack/test/dispatch/request_test.rb +++ b/actionpack/test/dispatch/request_test.rb @@ -411,7 +411,7 @@ class RequestPath < BaseRequestTest assert_equal "/foo?bar", path end - test "original_url returns url built using ORIGINAL_FULLPATH" do + 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") @@ -681,7 +681,6 @@ end class RequestMethod < BaseRequestTest test "method returns environment's request method when it has not been overridden by middleware".squish do - ActionDispatch::Request::HTTP_METHODS.each do |method| request = stub_request("REQUEST_METHOD" => method) @@ -763,7 +762,6 @@ class RequestMethod < BaseRequestTest test "post uneffected by local inflections" do existing_acronyms = ActiveSupport::Inflector.inflections.acronyms.dup - assert_deprecated { ActiveSupport::Inflector.inflections.acronym_regex.dup } begin ActiveSupport::Inflector.inflections do |inflect| inflect.acronym "POS" diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb index 0f37d074af..ed64d89902 100644 --- a/actionpack/test/dispatch/response_test.rb +++ b/actionpack/test/dispatch/response_test.rb @@ -42,7 +42,7 @@ class ResponseTest < ActiveSupport::TestCase def test_each_isnt_called_if_str_body_is_written # Controller writes and reads response body each_counter = 0 - @response.body = Object.new.tap { |o| o.singleton_class.send(:define_method, :each) { |&block| each_counter += 1; block.call "foo" } } + @response.body = Object.new.tap { |o| o.singleton_class.define_method(:each) { |&block| each_counter += 1; block.call "foo" } } @response["X-Foo"] = @response.body assert_equal 1, each_counter, "#each was not called once" @@ -290,8 +290,8 @@ class ResponseTest < ActiveSupport::TestCase resp.to_a assert_equal("utf-16", resp.charset) - assert_equal(Mime[:xml], resp.content_type) - + assert_equal(Mime[:xml], resp.media_type) + assert_equal("application/xml; charset=utf-16", resp.content_type) assert_equal("application/xml; charset=utf-16", resp.headers["Content-Type"]) end @@ -503,8 +503,8 @@ class ResponseIntegrationTest < ActionDispatch::IntegrationTest assert_response :success assert_equal("utf-16", @response.charset) - assert_equal(Mime[:xml], @response.content_type) - + assert_equal(Mime[:xml], @response.media_type) + assert_equal("application/xml; charset=utf-16", @response.content_type) assert_equal("application/xml; charset=utf-16", @response.headers["Content-Type"]) end @@ -519,8 +519,8 @@ class ResponseIntegrationTest < ActionDispatch::IntegrationTest assert_response :success assert_equal("utf-16", @response.charset) - assert_equal(Mime[:xml], @response.content_type) - + assert_equal(Mime[:xml], @response.media_type) + assert_equal("application/xml; charset=utf-16", @response.content_type) assert_equal("application/xml; charset=utf-16", @response.headers["Content-Type"]) end @@ -539,4 +539,87 @@ class ResponseIntegrationTest < ActionDispatch::IntegrationTest assert_equal('"202cb962ac59075b964b07152d234b70"', @response.headers["ETag"]) assert_equal('"202cb962ac59075b964b07152d234b70"', @response.etag) end + + test "response Content-Type with optional parameters" do + @app = lambda { |env| + [ + 200, + { "Content-Type" => "text/csv; charset=utf-16; header=present" }, + ["Hello"] + ] + } + + get "/" + assert_response :success + + assert_equal("text/csv; charset=utf-16; header=present", @response.headers["Content-Type"]) + assert_equal("text/csv; charset=utf-16; header=present", @response.content_type) + assert_equal("text/csv", @response.media_type) + assert_equal("utf-16", @response.charset) + end + + test "response Content-Type with optional parameters that set before charset" do + @app = lambda { |env| + [ + 200, + { "Content-Type" => "text/csv; header=present; charset=utf-16" }, + ["Hello"] + ] + } + + get "/" + assert_response :success + + assert_equal("text/csv; header=present; charset=utf-16", @response.headers["Content-Type"]) + assert_equal("text/csv; header=present; charset=utf-16", @response.content_type) + assert_equal("text/csv", @response.media_type) + assert_equal("utf-16", @response.charset) + end + + test "response Content-Type with quoted-string" do + @app = lambda { |env| + [ + 200, + { "Content-Type" => 'text/csv; header=present; charset="utf-16"' }, + ["Hello"] + ] + } + + get "/" + assert_response :success + + assert_equal('text/csv; header=present; charset="utf-16"', @response.headers["Content-Type"]) + assert_equal('text/csv; header=present; charset="utf-16"', @response.content_type) + assert_equal("text/csv", @response.media_type) + assert_equal("utf-16", @response.charset) + end + + test "`content type` returns header that excludes `charset` when specified `return_only_media_type_on_content_type`" do + original = ActionDispatch::Response.return_only_media_type_on_content_type + ActionDispatch::Response.return_only_media_type_on_content_type = true + + @app = lambda { |env| + if env["PATH_INFO"] == "/with_parameters" + [200, { "Content-Type" => "text/csv; header=present; charset=utf-16" }, [""]] + else + [200, { "Content-Type" => "text/csv; charset=utf-16" }, [""]] + end + } + + get "/" + assert_response :success + + assert_deprecated do + assert_equal("text/csv", @response.content_type) + end + + get "/with_parameters" + assert_response :success + + assert_deprecated do + assert_equal("text/csv; header=present", @response.content_type) + end + ensure + ActionDispatch::Response.return_only_media_type_on_content_type = original + end end diff --git a/actionpack/test/dispatch/routing/non_dispatch_routed_app_test.rb b/actionpack/test/dispatch/routing/non_dispatch_routed_app_test.rb new file mode 100644 index 0000000000..676a8c38d4 --- /dev/null +++ b/actionpack/test/dispatch/routing/non_dispatch_routed_app_test.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require "abstract_unit" + +module ActionDispatch + module Routing + class NonDispatchRoutedAppTest < ActionDispatch::IntegrationTest + # For example, Grape::API + class SimpleApp + def self.call(env) + [ 200, { "Content-Type" => "text/plain" }, [] ] + end + + def self.routes + [] + end + end + + setup { @app = SimpleApp } + + test "does not except" do + get "/foo" + assert_response :success + end + end + end +end diff --git a/actionpack/test/dispatch/routing/route_set_test.rb b/actionpack/test/dispatch/routing/route_set_test.rb index e61d47b160..e6a2c35798 100644 --- a/actionpack/test/dispatch/routing/route_set_test.rb +++ b/actionpack/test/dispatch/routing/route_set_test.rb @@ -29,7 +29,7 @@ module ActionDispatch assert_not empty? end - test "url helpers are added when route is added" do + test "URL helpers are added when route is added" do draw do get "foo", to: SimpleApp.new("foo#index") end @@ -48,7 +48,7 @@ module ActionDispatch assert_equal "/bar", url_helpers.bar_path end - test "url helpers are updated when route is updated" do + test "URL helpers are updated when route is updated" do draw do get "bar", to: SimpleApp.new("bar#index"), as: :bar end @@ -62,7 +62,7 @@ module ActionDispatch assert_equal "/baz", url_helpers.bar_path end - test "url helpers are removed when route is removed" do + test "URL helpers are removed when route is removed" do draw do get "foo", to: SimpleApp.new("foo#index") get "bar", to: SimpleApp.new("bar#index") diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index affc2d8497..b67b1dd347 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -2200,6 +2200,37 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest assert_equal "cards#destroy", @response.body end + def test_shallow_false_inside_nested_shallow_resource + draw do + resources :blogs, shallow: true do + resources :posts do + resources :comments, shallow: false + resources :tags + end + end + end + + get "/posts/1/comments" + assert_equal "comments#index", @response.body + assert_equal "/posts/1/comments", post_comments_path("1") + + get "/posts/1/comments/new" + assert_equal "comments#new", @response.body + assert_equal "/posts/1/comments/new", new_post_comment_path("1") + + get "/posts/1/comments/2" + assert_equal "comments#show", @response.body + assert_equal "/posts/1/comments/2", post_comment_path("1", "2") + + get "/posts/1/comments/2/edit" + assert_equal "comments#edit", @response.body + assert_equal "/posts/1/comments/2/edit", edit_post_comment_path("1", "2") + + get "/tags/3" + assert_equal "tags#show", @response.body + assert_equal "/tags/3", tag_path("3") + end + def test_shallow_deeply_nested_resources draw do resources :blogs do @@ -3338,13 +3369,23 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest assert_equal "0c0c0b68-d24b-11e1-a861-001ff3fffe6f", @request.params[:download] end - def test_action_from_path_is_not_frozen + def test_colon_containing_custom_param + ex = assert_raises(ArgumentError) { + draw do + resources :profiles, param: "username/:is_admin" + end + } + + assert_match(/:param option can't contain colon/, ex.message) + end + + def test_action_from_path_is_frozen draw do get "search" => "search" end get "/search" - assert_not_predicate @request.params[:action], :frozen? + assert_predicate @request.params[:action], :frozen? end def test_multiple_positional_args_with_the_same_name @@ -3698,15 +3739,25 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end - def test_multiple_roots + def test_multiple_roots_raises_error + ex = assert_raises(ArgumentError) { + draw do + root "pages#index", constraints: { host: "www.example.com" } + root "admin/pages#index", constraints: { host: "admin.example.com" } + end + } + assert_match(/Invalid route name, already in use: 'root'/, ex.message) + end + + def test_multiple_named_roots draw do namespace :foo do root "pages#index", constraints: { host: "www.example.com" } - root "admin/pages#index", constraints: { host: "admin.example.com" } + root "admin/pages#index", constraints: { host: "admin.example.com" }, as: :admin_root end root "pages#index", constraints: { host: "www.example.com" } - root "admin/pages#index", constraints: { host: "admin.example.com" } + root "admin/pages#index", constraints: { host: "admin.example.com" }, as: :admin_root end get "http://www.example.com/foo" @@ -3759,7 +3810,6 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end private - def draw(&block) self.class.stub_controllers do |routes| routes.default_url_options = { host: "www.example.com" } @@ -4372,7 +4422,7 @@ class TestNamedRouteUrlHelpers < ActionDispatch::IntegrationTest include Routes.url_helpers - test "url helpers do not ignore nil parameters when using non-optimized routes" do + test "URL helpers do not ignore nil parameters when using non-optimized routes" do Routes.stub :optimize_routes_generation?, false do get "/categories/1" assert_response :success @@ -4744,7 +4794,7 @@ class TestUrlGenerationErrors < ActionDispatch::IntegrationTest include Routes.url_helpers - test "url helpers raise a 'missing keys' error for a nil param with optimized helpers" do + test "URL helpers raise a 'missing keys' error for a nil param with optimized helpers" do url, missing = { action: "show", controller: "products", id: nil }, [:id] message = "No route matches #{url.inspect}, missing required keys: #{missing.inspect}" @@ -4752,7 +4802,7 @@ class TestUrlGenerationErrors < ActionDispatch::IntegrationTest assert_equal message, error.message end - test "url helpers raise a 'constraint failure' error for a nil param with non-optimized helpers" do + test "URL helpers raise a 'constraint failure' error for a nil param with non-optimized helpers" do url, missing = { action: "show", controller: "products", id: nil }, [:id] message = "No route matches #{url.inspect}, possible unmatched constraints: #{missing.inspect}" @@ -4760,15 +4810,15 @@ class TestUrlGenerationErrors < ActionDispatch::IntegrationTest assert_equal message, error.message end - test "url helpers raise message with mixed parameters when generation fails" do + test "URL helpers raise message with mixed parameters when generation fails" do url, missing = { action: "show", controller: "products", id: nil, "id" => "url-tested" }, [:id] message = "No route matches #{url.inspect}, possible unmatched constraints: #{missing.inspect}" - # Optimized url helper + # Optimized URL helper error = assert_raises(ActionController::UrlGenerationError) { product_path(nil, "id" => "url-tested") } assert_equal message, error.message - # Non-optimized url helper + # Non-optimized URL helper error = assert_raises(ActionController::UrlGenerationError, message) { product_path(id: nil, "id" => "url-tested") } assert_equal message, error.message end @@ -4902,12 +4952,52 @@ class TestPartialDynamicPathSegments < ActionDispatch::IntegrationTest end private - def assert_params(params) assert_equal(params, request.path_parameters) end end +class TestOptionalScopesWithOrWithoutParams < ActionDispatch::IntegrationTest + Routes = ActionDispatch::Routing::RouteSet.new.tap do |app| + app.draw do + scope module: "test_optional_scopes_with_or_without_params" do + scope "(:locale)", locale: /en|es/ do + get "home", to: "home#index" + get "with_param/:foo", to: "home#with_param", as: "with_param" + get "without_param", to: "home#without_param" + end + end + end + end + + class HomeController < ActionController::Base + include Routes.url_helpers + + def index + render inline: "<%= with_param_path(foo: 'bar') %> | <%= without_param_path %>" + end + + def with_param; end + def without_param; end + end + + APP = build_app Routes + + def app + APP + end + + def test_stays_unscoped_with_or_without_params + get "/home" + assert_equal "/with_param/bar | /without_param", response.body + end + + def test_preserves_scope_with_or_without_params + get "/es/home" + assert_equal "/es/with_param/bar | /es/without_param", response.body + end +end + class TestPathParameters < ActionDispatch::IntegrationTest Routes = ActionDispatch::Routing::RouteSet.new.tap do |app| app.draw do @@ -4981,8 +5071,12 @@ end class FlashRedirectTest < ActionDispatch::IntegrationTest SessionKey = "_myapp_session" - Generator = ActiveSupport::LegacyKeyGenerator.new("b3c631c314c0bbca50c1b2843150fe33") - Rotations = ActiveSupport::Messages::RotationConfiguration.new + Generator = ActiveSupport::CachingKeyGenerator.new( + ActiveSupport::KeyGenerator.new("b3c631c314c0bbca50c1b2843150fe33", iterations: 1000) + ) + Rotations = ActiveSupport::Messages::RotationConfiguration.new + SIGNED_COOKIE_SALT = "signed cookie" + ENCRYPTED_SIGNED_COOKIE_SALT = "signed encrypted cookie" class KeyGeneratorMiddleware def initialize(app) @@ -4992,6 +5086,8 @@ class FlashRedirectTest < ActionDispatch::IntegrationTest def call(env) env["action_dispatch.key_generator"] ||= Generator env["action_dispatch.cookies_rotations"] ||= Rotations + env["action_dispatch.signed_cookie_salt"] = SIGNED_COOKIE_SALT + env["action_dispatch.encrypted_signed_cookie_salt"] = ENCRYPTED_SIGNED_COOKIE_SALT @app.call(env) end @@ -5086,7 +5182,6 @@ class TestRecognizePath < ActionDispatch::IntegrationTest end private - def recognize_path(*args) Routes.recognize_path(*args) end diff --git a/actionpack/test/dispatch/session/cookie_store_test.rb b/actionpack/test/dispatch/session/cookie_store_test.rb index e34426a471..b6f83f4062 100644 --- a/actionpack/test/dispatch/session/cookie_store_test.rb +++ b/actionpack/test/dispatch/session/cookie_store_test.rb @@ -379,7 +379,6 @@ class CookieStoreTest < ActionDispatch::IntegrationTest end private - # Overwrite get to send SessionSecret in env hash def get(path, *args) args[0] ||= {} diff --git a/actionpack/test/dispatch/session/mem_cache_store_test.rb b/actionpack/test/dispatch/session/mem_cache_store_test.rb index 9b51ee1cad..ac685a7dca 100644 --- a/actionpack/test/dispatch/session/mem_cache_store_test.rb +++ b/actionpack/test/dispatch/session/mem_cache_store_test.rb @@ -38,8 +38,9 @@ class MemCacheStoreTest < ActionDispatch::IntegrationTest begin require "dalli" - ss = Dalli::Client.new("localhost:11211").stats - raise Dalli::DalliError unless ss["localhost:11211"] + servers = ENV["MEMCACHE_SERVERS"] || "localhost:11211" + ss = Dalli::Client.new(servers).stats + raise Dalli::DalliError unless ss[servers] def test_setting_and_getting_session_value with_test_route_set do @@ -195,7 +196,9 @@ class MemCacheStoreTest < ActionDispatch::IntegrationTest end @app = self.class.build_app(set) do |middleware| - middleware.use ActionDispatch::Session::MemCacheStore, key: "_session_id", namespace: "mem_cache_store_test:#{SecureRandom.hex(10)}" + middleware.use ActionDispatch::Session::MemCacheStore, + key: "_session_id", namespace: "mem_cache_store_test:#{SecureRandom.hex(10)}", + memcache_server: ENV["MEMCACHE_SERVERS"] || "localhost:11211" middleware.delete ActionDispatch::ShowExceptions end diff --git a/actionpack/test/dispatch/show_exceptions_test.rb b/actionpack/test/dispatch/show_exceptions_test.rb index b69071b44b..6fafa4e426 100644 --- a/actionpack/test/dispatch/show_exceptions_test.rb +++ b/actionpack/test/dispatch/show_exceptions_test.rb @@ -9,6 +9,8 @@ class ShowExceptionsTest < ActionDispatch::IntegrationTest case req.path when "/not_found" raise AbstractController::ActionNotFound + when "/invalid_mimetype" + raise Mime::Type::InvalidMimeType when "/bad_params", "/bad_params.json" begin raise StandardError.new @@ -36,32 +38,36 @@ class ShowExceptionsTest < ActionDispatch::IntegrationTest test "skip exceptions app if not showing exceptions" do @app = ProductionApp assert_raise RuntimeError do - get "/", headers: { "action_dispatch.show_exceptions" => false } + get "/", env: { "action_dispatch.show_exceptions" => false } end end test "rescue with error page" do @app = ProductionApp - get "/", headers: { "action_dispatch.show_exceptions" => true } + get "/", env: { "action_dispatch.show_exceptions" => true } assert_response 500 assert_equal "500 error fixture\n", body - get "/bad_params", headers: { "action_dispatch.show_exceptions" => true } + get "/bad_params", env: { "action_dispatch.show_exceptions" => true } assert_response 400 assert_equal "400 error fixture\n", body - get "/not_found", headers: { "action_dispatch.show_exceptions" => true } + get "/not_found", env: { "action_dispatch.show_exceptions" => true } assert_response 404 assert_equal "404 error fixture\n", body - get "/method_not_allowed", headers: { "action_dispatch.show_exceptions" => true } + get "/method_not_allowed", env: { "action_dispatch.show_exceptions" => true } assert_response 405 assert_equal "", body - get "/unknown_http_method", headers: { "action_dispatch.show_exceptions" => true } + get "/unknown_http_method", env: { "action_dispatch.show_exceptions" => true } assert_response 405 assert_equal "", body + + get "/invalid_mimetype", headers: { "Accept" => "text/html,*", "action_dispatch.show_exceptions" => true } + assert_response 406 + assert_equal "", body end test "localize rescue error page" do @@ -70,11 +76,11 @@ class ShowExceptionsTest < ActionDispatch::IntegrationTest begin @app = ProductionApp - get "/", headers: { "action_dispatch.show_exceptions" => true } + get "/", env: { "action_dispatch.show_exceptions" => true } assert_response 500 assert_equal "500 localized error fixture\n", body - get "/not_found", headers: { "action_dispatch.show_exceptions" => true } + get "/not_found", env: { "action_dispatch.show_exceptions" => true } assert_response 404 assert_equal "404 error fixture\n", body ensure @@ -85,14 +91,14 @@ class ShowExceptionsTest < ActionDispatch::IntegrationTest test "sets the HTTP charset parameter" do @app = ProductionApp - get "/", headers: { "action_dispatch.show_exceptions" => true } + get "/", env: { "action_dispatch.show_exceptions" => true } assert_equal "text/html; charset=utf-8", response.headers["Content-Type"] end test "show registered original exception for wrapped exceptions" do @app = ProductionApp - get "/not_found_original_exception", headers: { "action_dispatch.show_exceptions" => true } + get "/not_found_original_exception", env: { "action_dispatch.show_exceptions" => true } assert_response 404 assert_match(/404 error/, body) end @@ -106,7 +112,7 @@ class ShowExceptionsTest < ActionDispatch::IntegrationTest end @app = ActionDispatch::ShowExceptions.new(Boomer.new, exceptions_app) - get "/not_found_original_exception", headers: { "action_dispatch.show_exceptions" => true } + get "/not_found_original_exception", env: { "action_dispatch.show_exceptions" => true } assert_response 404 assert_equal "YOU FAILED", body end @@ -117,7 +123,7 @@ class ShowExceptionsTest < ActionDispatch::IntegrationTest end @app = ActionDispatch::ShowExceptions.new(Boomer.new, exceptions_app) - get "/method_not_allowed", headers: { "action_dispatch.show_exceptions" => true } + get "/method_not_allowed", env: { "action_dispatch.show_exceptions" => true } assert_response 405 assert_equal "", body end @@ -125,12 +131,12 @@ class ShowExceptionsTest < ActionDispatch::IntegrationTest test "bad params exception is returned in the correct format" do @app = ProductionApp - get "/bad_params", headers: { "action_dispatch.show_exceptions" => true } + get "/bad_params", env: { "action_dispatch.show_exceptions" => true } assert_equal "text/html; charset=utf-8", response.headers["Content-Type"] assert_response 400 assert_match(/400 error/, body) - get "/bad_params.json", headers: { "action_dispatch.show_exceptions" => true } + get "/bad_params.json", env: { "action_dispatch.show_exceptions" => true } assert_equal "application/json; charset=utf-8", response.headers["Content-Type"] assert_response 400 assert_equal("{\"status\":400,\"error\":\"Bad Request\"}", body) diff --git a/actionpack/test/dispatch/static_test.rb b/actionpack/test/dispatch/static_test.rb index d44aa00122..1f93d594a6 100644 --- a/actionpack/test/dispatch/static_test.rb +++ b/actionpack/test/dispatch/static_test.rb @@ -232,7 +232,6 @@ module StaticTests end private - def assert_gzip(file_name, response) expected = File.read("#{FIXTURE_LOAD_PATH}/#{public_path}" + file_name) actual = ActiveSupport::Gzip.decompress(response.body) diff --git a/actionpack/test/dispatch/system_testing/driver_test.rb b/actionpack/test/dispatch/system_testing/driver_test.rb index a824ee0c84..d3b16d0328 100644 --- a/actionpack/test/dispatch/system_testing/driver_test.rb +++ b/actionpack/test/dispatch/system_testing/driver_test.rb @@ -2,6 +2,7 @@ require "abstract_unit" require "action_dispatch/system_testing/driver" +require "selenium/webdriver" class DriverTest < ActiveSupport::TestCase test "initializing the driver" do @@ -22,6 +23,7 @@ class DriverTest < ActiveSupport::TestCase driver = ActionDispatch::SystemTesting::Driver.new(:selenium, using: :headless_chrome, screen_size: [1400, 1400], options: { url: "http://example.com/wd/hub" }) assert_equal :selenium, driver.instance_variable_get(:@name) assert_equal :headless_chrome, driver.instance_variable_get(:@browser).name + assert_instance_of Selenium::WebDriver::Chrome::Options, driver.instance_variable_get(:@browser).options assert_equal [1400, 1400], driver.instance_variable_get(:@screen_size) assert_equal ({ url: "http://example.com/wd/hub" }), driver.instance_variable_get(:@options) end @@ -30,6 +32,7 @@ class DriverTest < ActiveSupport::TestCase driver = ActionDispatch::SystemTesting::Driver.new(:selenium, using: :headless_firefox, screen_size: [1400, 1400], options: { url: "http://example.com/wd/hub" }) assert_equal :selenium, driver.instance_variable_get(:@name) assert_equal :headless_firefox, driver.instance_variable_get(:@browser).name + assert_instance_of Selenium::WebDriver::Firefox::Options, driver.instance_variable_get(:@browser).options assert_equal [1400, 1400], driver.instance_variable_get(:@screen_size) assert_equal ({ url: "http://example.com/wd/hub" }), driver.instance_variable_get(:@options) end @@ -51,4 +54,83 @@ class DriverTest < ActiveSupport::TestCase test "registerable? returns false if driver is rack_test" do assert_not ActionDispatch::SystemTesting::Driver.new(:rack_test).send(:registerable?) end + + test "define extra capabilities using chrome" do + driver_option = nil + driver = ActionDispatch::SystemTesting::Driver.new(:selenium, screen_size: [1400, 1400], using: :chrome) do |option| + option.add_argument("start-maximized") + option.add_emulation(device_name: "iphone 6") + option.add_preference(:detach, true) + + driver_option = option + end + driver.use + + expected = { "goog:chromeOptions" => { args: ["start-maximized"], mobileEmulation: { deviceName: "iphone 6" }, prefs: { detach: true } } } + assert_equal expected, driver_option.as_json + end + + test "define extra capabilities using headless_chrome" do + driver_option = nil + driver = ActionDispatch::SystemTesting::Driver.new(:selenium, screen_size: [1400, 1400], using: :headless_chrome) do |option| + option.add_argument("start-maximized") + option.add_emulation(device_name: "iphone 6") + option.add_preference(:detach, true) + + driver_option = option + end + driver.use + + expected = { "goog:chromeOptions" => { args: ["start-maximized"], mobileEmulation: { deviceName: "iphone 6" }, prefs: { detach: true } } } + assert_equal expected, driver_option.as_json + end + + test "define extra capabilities using firefox" do + driver_option = nil + driver = ActionDispatch::SystemTesting::Driver.new(:selenium, screen_size: [1400, 1400], using: :firefox) do |option| + option.add_preference("browser.startup.homepage", "http://www.seleniumhq.com/") + option.add_argument("--host=127.0.0.1") + + driver_option = option + end + driver.use + + expected = { "moz:firefoxOptions" => { args: ["--host=127.0.0.1"], prefs: { "browser.startup.homepage" => "http://www.seleniumhq.com/" } } } + assert_equal expected, driver_option.as_json + end + + test "define extra capabilities using headless_firefox" do + driver_option = nil + driver = ActionDispatch::SystemTesting::Driver.new(:selenium, screen_size: [1400, 1400], using: :headless_firefox) do |option| + option.add_preference("browser.startup.homepage", "http://www.seleniumhq.com/") + option.add_argument("--host=127.0.0.1") + + driver_option = option + end + driver.use + + expected = { "moz:firefoxOptions" => { args: ["--host=127.0.0.1"], prefs: { "browser.startup.homepage" => "http://www.seleniumhq.com/" } } } + assert_equal expected, driver_option.as_json + end + + test "does not define extra capabilities" do + driver = ActionDispatch::SystemTesting::Driver.new(:selenium, screen_size: [1400, 1400], using: :firefox) + + assert_nothing_raised do + driver.use + end + end + + test "preloads browser's driver_path" do + called = false + + original_driver_path = ::Selenium::WebDriver::Chrome::Service.driver_path + ::Selenium::WebDriver::Chrome::Service.driver_path = -> { called = true } + + ActionDispatch::SystemTesting::Driver.new(:selenium, screen_size: [1400, 1400], using: :chrome) + + assert called + ensure + ::Selenium::WebDriver::Chrome::Service.driver_path = original_driver_path + end end diff --git a/actionpack/test/dispatch/system_testing/screenshot_helper_test.rb b/actionpack/test/dispatch/system_testing/screenshot_helper_test.rb index de79c05657..b0b36f9d74 100644 --- a/actionpack/test/dispatch/system_testing/screenshot_helper_test.rb +++ b/actionpack/test/dispatch/system_testing/screenshot_helper_test.rb @@ -3,6 +3,7 @@ require "abstract_unit" require "action_dispatch/system_testing/test_helpers/screenshot_helper" require "capybara/dsl" +require "selenium/webdriver" class ScreenshotHelperTest < ActiveSupport::TestCase test "image path is saved in tmp directory" do @@ -35,28 +36,34 @@ class ScreenshotHelperTest < ActiveSupport::TestCase end end + test "image name truncates names over 225 characters" do + new_test = DrivenBySeleniumWithChrome.new("x" * 400) + + Rails.stub :root, Pathname.getwd do + assert_equal Rails.root.join("tmp/screenshots/#{"x" * 225}.png").to_s, new_test.send(:image_path) + end + end + test "defaults to simple output for the screenshot" do new_test = DrivenBySeleniumWithChrome.new("x") assert_equal "simple", new_test.send(:output_type) end test "display_image return artifact format when specify RAILS_SYSTEM_TESTING_SCREENSHOT environment" do - begin - original_output_type = ENV["RAILS_SYSTEM_TESTING_SCREENSHOT"] - ENV["RAILS_SYSTEM_TESTING_SCREENSHOT"] = "artifact" + original_output_type = ENV["RAILS_SYSTEM_TESTING_SCREENSHOT"] + ENV["RAILS_SYSTEM_TESTING_SCREENSHOT"] = "artifact" - new_test = DrivenBySeleniumWithChrome.new("x") + new_test = DrivenBySeleniumWithChrome.new("x") - assert_equal "artifact", new_test.send(:output_type) + assert_equal "artifact", new_test.send(:output_type) - Rails.stub :root, Pathname.getwd do - new_test.stub :passed?, false do - assert_match %r|url=artifact://.+?tmp/screenshots/failures_x\.png|, new_test.send(:display_image) - end + Rails.stub :root, Pathname.getwd do + new_test.stub :passed?, false do + assert_match %r|url=artifact://.+?tmp/screenshots/failures_x\.png|, new_test.send(:display_image) end - ensure - ENV["RAILS_SYSTEM_TESTING_SCREENSHOT"] = original_output_type end + ensure + ENV["RAILS_SYSTEM_TESTING_SCREENSHOT"] = original_output_type end test "image path returns the absolute path from root" do diff --git a/actionpack/test/dispatch/system_testing/system_test_case_test.rb b/actionpack/test/dispatch/system_testing/system_test_case_test.rb index b078a5abc5..3319db1665 100644 --- a/actionpack/test/dispatch/system_testing/system_test_case_test.rb +++ b/actionpack/test/dispatch/system_testing/system_test_case_test.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require "abstract_unit" +require "selenium/webdriver" class SetDriverToRackTestTest < DrivenByRackTest test "uses rack_test" do @@ -45,40 +46,3 @@ class SetHostTest < DrivenByRackTest assert_equal "http://example.com", Capybara.app_host end end - -class UndefMethodsTest < DrivenBySeleniumWithChrome - test "get" do - exception = assert_raise NoMethodError do - get "http://example.com" - end - assert_equal "System tests cannot make direct requests via #get; use #visit and #click_on instead. See http://www.rubydoc.info/github/teamcapybara/capybara/master#The_DSL for more information.", exception.message - end - - test "post" do - exception = assert_raise NoMethodError do - post "http://example.com" - end - assert_equal "System tests cannot make direct requests via #post; use #visit and #click_on instead. See http://www.rubydoc.info/github/teamcapybara/capybara/master#The_DSL for more information.", exception.message - end - - test "put" do - exception = assert_raise NoMethodError do - put "http://example.com" - end - assert_equal "System tests cannot make direct requests via #put; use #visit and #click_on instead. See http://www.rubydoc.info/github/teamcapybara/capybara/master#The_DSL for more information.", exception.message - end - - test "patch" do - exception = assert_raise NoMethodError do - patch "http://example.com" - end - assert_equal "System tests cannot make direct requests via #patch; use #visit and #click_on instead. See http://www.rubydoc.info/github/teamcapybara/capybara/master#The_DSL for more information.", exception.message - end - - test "delete" do - exception = assert_raise NoMethodError do - delete "http://example.com" - end - assert_equal "System tests cannot make direct requests via #delete; use #visit and #click_on instead. See http://www.rubydoc.info/github/teamcapybara/capybara/master#The_DSL for more information.", exception.message - end -end diff --git a/actionpack/test/dispatch/test_response_test.rb b/actionpack/test/dispatch/test_response_test.rb index f0b8f7785d..2629a61057 100644 --- a/actionpack/test/dispatch/test_response_test.rb +++ b/actionpack/test/dispatch/test_response_test.rb @@ -27,11 +27,4 @@ class TestResponseTest < ActiveSupport::TestCase response = ActionDispatch::TestResponse.create(200, { "Content-Type" => "application/json" }, '{ "foo": "fighters" }') assert_equal({ "foo" => "fighters" }, response.parsed_body) end - - test "response status aliases deprecated" do - response = ActionDispatch::TestResponse.create - assert_deprecated { response.success? } - assert_deprecated { response.missing? } - assert_deprecated { response.error? } - end end diff --git a/actionpack/test/dispatch/uploaded_file_test.rb b/actionpack/test/dispatch/uploaded_file_test.rb index 21169fcb5c..03e5274541 100644 --- a/actionpack/test/dispatch/uploaded_file_test.rb +++ b/actionpack/test/dispatch/uploaded_file_test.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true require "abstract_unit" +require "tempfile" +require "stringio" module ActionDispatch class UploadedFileTest < ActiveSupport::TestCase @@ -11,109 +13,118 @@ module ActionDispatch end def test_original_filename - uf = Http::UploadedFile.new(filename: "foo", tempfile: Object.new) + uf = Http::UploadedFile.new(filename: "foo", tempfile: Tempfile.new) assert_equal "foo", uf.original_filename end def test_filename_is_different_object file_str = "foo" - uf = Http::UploadedFile.new(filename: file_str, tempfile: Object.new) + uf = Http::UploadedFile.new(filename: file_str, tempfile: Tempfile.new) assert_not_equal file_str.object_id, uf.original_filename.object_id end def test_filename_should_be_in_utf_8 - uf = Http::UploadedFile.new(filename: "foo", tempfile: Object.new) + uf = Http::UploadedFile.new(filename: "foo", tempfile: Tempfile.new) assert_equal "UTF-8", uf.original_filename.encoding.to_s end def test_filename_should_always_be_in_utf_8 uf = Http::UploadedFile.new(filename: "foo".encode(Encoding::SHIFT_JIS), - tempfile: Object.new) + tempfile: Tempfile.new) assert_equal "UTF-8", uf.original_filename.encoding.to_s end def test_content_type - uf = Http::UploadedFile.new(type: "foo", tempfile: Object.new) + uf = Http::UploadedFile.new(type: "foo", tempfile: Tempfile.new) assert_equal "foo", uf.content_type end def test_headers - uf = Http::UploadedFile.new(head: "foo", tempfile: Object.new) + uf = Http::UploadedFile.new(head: "foo", tempfile: Tempfile.new) assert_equal "foo", uf.headers end def test_tempfile - uf = Http::UploadedFile.new(tempfile: "foo") - assert_equal "foo", uf.tempfile + tf = Tempfile.new + uf = Http::UploadedFile.new(tempfile: tf) + assert_equal tf, uf.tempfile end - def test_to_io_returns_the_tempfile - tf = Object.new + def test_to_io_returns_file + tf = Tempfile.new uf = Http::UploadedFile.new(tempfile: tf) - assert_equal tf, uf.to_io + assert_equal tf.to_io, uf.to_io end def test_delegates_path_to_tempfile - tf = Class.new { def path; "thunderhorse" end } - uf = Http::UploadedFile.new(tempfile: tf.new) - assert_equal "thunderhorse", uf.path + tf = Tempfile.new + uf = Http::UploadedFile.new(tempfile: tf) + assert_equal tf.path, uf.path end def test_delegates_open_to_tempfile - tf = Class.new { def open; "thunderhorse" end } - uf = Http::UploadedFile.new(tempfile: tf.new) - assert_equal "thunderhorse", uf.open + tf = Tempfile.new + tf.close + uf = Http::UploadedFile.new(tempfile: tf) + assert_equal tf, uf.open + assert_not tf.closed? end def test_delegates_close_to_tempfile - tf = Class.new { def close(unlink_now = false); "thunderhorse" end } - uf = Http::UploadedFile.new(tempfile: tf.new) - assert_equal "thunderhorse", uf.close + tf = Tempfile.new + uf = Http::UploadedFile.new(tempfile: tf) + uf.close + assert tf.closed? end def test_close_accepts_parameter - tf = Class.new { def close(unlink_now = false); "thunderhorse: #{unlink_now}" end } - uf = Http::UploadedFile.new(tempfile: tf.new) - assert_equal "thunderhorse: true", uf.close(true) + tf = Tempfile.new + uf = Http::UploadedFile.new(tempfile: tf) + uf.close(true) + assert tf.closed? + assert_nil tf.path end def test_delegates_read_to_tempfile - tf = Class.new { def read(length = nil, buffer = nil); "thunderhorse" end } - uf = Http::UploadedFile.new(tempfile: tf.new) + tf = Tempfile.new + tf << "thunderhorse" + tf.rewind + uf = Http::UploadedFile.new(tempfile: tf) assert_equal "thunderhorse", uf.read end def test_delegates_read_to_tempfile_with_params - tf = Class.new { def read(length = nil, buffer = nil); [length, buffer] end } - uf = Http::UploadedFile.new(tempfile: tf.new) - assert_equal %w{ thunder horse }, uf.read(*%w{ thunder horse }) - end - - def test_delegate_respects_respond_to? - tf = Class.new { def read; yield end; private :read } - uf = Http::UploadedFile.new(tempfile: tf.new) - assert_raises(NoMethodError) do - uf.read - end + tf = Tempfile.new + tf << "thunderhorse" + tf.rewind + uf = Http::UploadedFile.new(tempfile: tf) + assert_equal "thunder", uf.read(7) + assert_equal "horse", uf.read(5, String.new) end def test_delegate_eof_to_tempfile - tf = Class.new { def eof?; true end; } - uf = Http::UploadedFile.new(tempfile: tf.new) - assert_predicate uf, :eof? + tf = Tempfile.new + tf << "thunderhorse" + uf = Http::UploadedFile.new(tempfile: tf) + assert_equal true, uf.eof? + tf.rewind + assert_equal false, uf.eof? end def test_delegate_to_path_to_tempfile - tf = Class.new { def to_path; "/any/file/path" end; } - uf = Http::UploadedFile.new(tempfile: tf.new) - assert_equal "/any/file/path", uf.to_path + tf = Tempfile.new + uf = Http::UploadedFile.new(tempfile: tf) + assert_equal tf.to_path, uf.to_path end - def test_respond_to? - tf = Class.new { def read; yield end } - uf = Http::UploadedFile.new(tempfile: tf.new) - assert_respond_to uf, :headers - assert_respond_to uf, :read + def test_io_copy_stream + tf = Tempfile.new + tf << "thunderhorse" + tf.rewind + uf = Http::UploadedFile.new(tempfile: tf) + result = StringIO.new + IO.copy_stream(uf, result) + assert_equal "thunderhorse", result.string end end end diff --git a/actionpack/test/journey/path/pattern_test.rb b/actionpack/test/journey/path/pattern_test.rb index 3e7aea57f1..77c19369b0 100644 --- a/actionpack/test/journey/path/pattern_test.rb +++ b/actionpack/test/journey/path/pattern_test.rb @@ -34,17 +34,17 @@ module ActionDispatch end { - "/:controller(/:action)" => %r{\A/(#{x})(?:/([^/.?]+))?}, - "/:controller/foo" => %r{\A/(#{x})/foo}, - "/:controller/:action" => %r{\A/(#{x})/([^/.?]+)}, - "/:controller" => %r{\A/(#{x})}, - "/:controller(/:action(/:id))" => %r{\A/(#{x})(?:/([^/.?]+)(?:/([^/.?]+))?)?}, - "/:controller/:action.xml" => %r{\A/(#{x})/([^/.?]+)\.xml}, - "/:controller.:format" => %r{\A/(#{x})\.([^/.?]+)}, - "/:controller(.:format)" => %r{\A/(#{x})(?:\.([^/.?]+))?}, - "/:controller/*foo" => %r{\A/(#{x})/(.+)}, - "/:controller/*foo/bar" => %r{\A/(#{x})/(.+)/bar}, - "/:foo|*bar" => %r{\A/(?:([^/.?]+)|(.+))}, + "/:controller(/:action)" => %r{\A/(#{x})(?:/([^/.?]+))?(?:\b|\Z|/)}, + "/:controller/foo" => %r{\A/(#{x})/foo(?:\b|\Z|/)}, + "/:controller/:action" => %r{\A/(#{x})/([^/.?]+)(?:\b|\Z|/)}, + "/:controller" => %r{\A/(#{x})(?:\b|\Z|/)}, + "/:controller(/:action(/:id))" => %r{\A/(#{x})(?:/([^/.?]+)(?:/([^/.?]+))?)?(?:\b|\Z|/)}, + "/:controller/:action.xml" => %r{\A/(#{x})/([^/.?]+)\.xml(?:\b|\Z|/)}, + "/:controller.:format" => %r{\A/(#{x})\.([^/.?]+)(?:\b|\Z|/)}, + "/:controller(.:format)" => %r{\A/(#{x})(?:\.([^/.?]+))?(?:\b|\Z|/)}, + "/:controller/*foo" => %r{\A/(#{x})/(.+)(?:\b|\Z|/)}, + "/:controller/*foo/bar" => %r{\A/(#{x})/(.+)/bar(?:\b|\Z|/)}, + "/:foo|*bar" => %r{\A/(?:([^/.?]+)|(.+))(?:\b|\Z|/)}, }.each do |path, expected| define_method(:"test_to_non_anchored_regexp_#{Regexp.escape(path)}") do path = Pattern.build( @@ -280,6 +280,15 @@ module ActionDispatch assert_equal "list", match[1] assert_equal "rss", match[2] end + + def test_named_captures + path = Path::Pattern.from_string "/books(/:action(.:format))" + + uri = "/books/list.rss" + match = path =~ uri + named_captures = { "action" => "list", "format" => "rss" } + assert_equal named_captures, match.named_captures + end end end end diff --git a/actionpack/test/journey/route/definition/scanner_test.rb b/actionpack/test/journey/route/definition/scanner_test.rb index 092177d315..e55ed92cc8 100644 --- a/actionpack/test/journey/route/definition/scanner_test.rb +++ b/actionpack/test/journey/route/definition/scanner_test.rb @@ -66,7 +66,6 @@ module ActionDispatch end private - def assert_tokens(expected_tokens, scanner, pattern) actual_tokens = [] while token = scanner.next_token diff --git a/actionpack/test/journey/route_test.rb b/actionpack/test/journey/route_test.rb index a8bf4a11e2..8828201e4f 100644 --- a/actionpack/test/journey/route_test.rb +++ b/actionpack/test/journey/route_test.rb @@ -9,7 +9,7 @@ module ActionDispatch app = Object.new path = Path::Pattern.from_string "/:controller(/:action(/:id(.:format)))" defaults = {} - route = Route.build("name", app, path, {}, [], defaults) + route = Route.new(name: "name", app: app, path: path, defaults: defaults) assert_equal app, route.app assert_equal path, route.path @@ -17,10 +17,9 @@ module ActionDispatch end def test_route_adds_itself_as_memo - app = Object.new - path = Path::Pattern.from_string "/:controller(/:action(/:id(.:format)))" - defaults = {} - route = Route.build("name", app, path, {}, [], defaults) + app = Object.new + path = Path::Pattern.from_string "/:controller(/:action(/:id(.:format)))" + route = Route.new(name: "name", app: app, path: path) route.ast.grep(Nodes::Terminal).each do |node| assert_equal route, node.memo @@ -28,30 +27,30 @@ module ActionDispatch end def test_path_requirements_override_defaults - path = Path::Pattern.build(":name", { name: /love/ }, "/", true) - defaults = { name: "tender" } - route = Route.build("name", nil, path, {}, [], defaults) + path = Path::Pattern.build(":name", { name: /love/ }, "/", true) + defaults = { name: "tender" } + route = Route.new(name: "name", path: path, defaults: defaults) assert_equal(/love/, route.requirements[:name]) end def test_ip_address path = Path::Pattern.from_string "/messages/:id(.:format)" - route = Route.build("name", nil, path, { ip: "192.168.1.1" }, [], - controller: "foo", action: "bar") + route = Route.new(name: "name", path: path, constraints: { ip: "192.168.1.1" }, + defaults: { controller: "foo", action: "bar" }) assert_equal "192.168.1.1", route.ip end def test_default_ip path = Path::Pattern.from_string "/messages/:id(.:format)" - route = Route.build("name", nil, path, {}, [], - controller: "foo", action: "bar") + route = Route.new(name: "name", path: path, + defaults: { controller: "foo", action: "bar" }) assert_equal(//, route.ip) end def test_format_with_star path = Path::Pattern.from_string "/:controller/*extra" - route = Route.build("name", nil, path, {}, [], - controller: "foo", action: "bar") + route = Route.new(name: "name", path: path, + defaults: { controller: "foo", action: "bar" }) assert_equal "/foo/himom", route.format( controller: "foo", extra: "himom") @@ -59,7 +58,8 @@ module ActionDispatch def test_connects_all_match path = Path::Pattern.from_string "/:controller(/:action(/:id(.:format)))" - route = Route.build("name", nil, path, { action: "bar" }, [], controller: "foo") + route = Route.new(name: "name", path: path, constraints: { action: "bar" }, + defaults: { controller: "foo" }) assert_equal "/foo/bar/10", route.format( controller: "foo", @@ -69,34 +69,33 @@ module ActionDispatch def test_extras_are_not_included_if_optional path = Path::Pattern.from_string "/page/:id(/:action)" - route = Route.build("name", nil, path, {}, [], action: "show") + route = Route.new(name: "name", path: path, defaults: { action: "show" }) assert_equal "/page/10", route.format(id: 10) end def test_extras_are_not_included_if_optional_with_parameter path = Path::Pattern.from_string "(/sections/:section)/pages/:id" - route = Route.build("name", nil, path, {}, [], action: "show") + route = Route.new(name: "name", path: path, defaults: { action: "show" }) assert_equal "/pages/10", route.format(id: 10) end def test_extras_are_not_included_if_optional_parameter_is_nil path = Path::Pattern.from_string "(/sections/:section)/pages/:id" - route = Route.build("name", nil, path, {}, [], action: "show") + route = Route.new(name: "name", path: path, defaults: { action: "show" }) assert_equal "/pages/10", route.format(id: 10, section: nil) end def test_score - constraints = {} defaults = { controller: "pages", action: "show" } path = Path::Pattern.from_string "/page/:id(/:action)(.:format)" - specific = Route.build "name", nil, path, constraints, [:controller, :action], defaults + specific = Route.new name: "name", path: path, required_defaults: [:controller, :action], defaults: defaults path = Path::Pattern.from_string "/:controller(/:action(/:id))(.:format)" - generic = Route.build "name", nil, path, constraints, [], {} + generic = Route.new name: "name", path: path knowledge = { "id" => true, "controller" => true, "action" => true } diff --git a/actionpack/test/journey/router_test.rb b/actionpack/test/journey/router_test.rb index 1f4e14aef6..fe0e3a975b 100644 --- a/actionpack/test/journey/router_test.rb +++ b/actionpack/test/journey/router_test.rb @@ -284,7 +284,7 @@ module ActionDispatch def test_generate_missing_keys_no_matches_different_format_keys get "/:controller/:action/:name", to: "foo#bar" - primarty_parameters = { + primary_parameters = { id: 1, controller: "tasks", action: "show", @@ -297,9 +297,9 @@ module ActionDispatch missing_parameters = { missing_key => "task_1" } - request_parameters = primarty_parameters.merge(redirection_parameters).merge(missing_parameters) + request_parameters = primary_parameters.merge(redirection_parameters).merge(missing_parameters) - message = "No route matches #{Hash[request_parameters.sort_by { |k, v|k.to_s }].inspect}, missing required keys: #{[missing_key.to_sym].inspect}" + message = "No route matches #{Hash[request_parameters.sort_by { |k, _|k.to_s }].inspect}, missing required keys: #{[missing_key.to_sym].inspect}" error = assert_raises(ActionController::UrlGenerationError) do @formatter.generate( @@ -503,7 +503,6 @@ module ActionDispatch end private - def get(*args) ActiveSupport::Deprecation.silence do mapper.get(*args) |