aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/test/dispatch
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/test/dispatch')
-rw-r--r--actionpack/test/dispatch/callbacks_test.rb2
-rw-r--r--actionpack/test/dispatch/content_security_policy_test.rb368
-rw-r--r--actionpack/test/dispatch/cookies_test.rb423
-rw-r--r--actionpack/test/dispatch/debug_exceptions_test.rb4
-rw-r--r--actionpack/test/dispatch/debug_locks_test.rb38
-rw-r--r--actionpack/test/dispatch/exception_wrapper_test.rb2
-rw-r--r--actionpack/test/dispatch/executor_test.rb2
-rw-r--r--actionpack/test/dispatch/header_test.rb2
-rw-r--r--actionpack/test/dispatch/live_response_test.rb2
-rw-r--r--actionpack/test/dispatch/mapper_test.rb2
-rw-r--r--actionpack/test/dispatch/middleware_stack_test.rb2
-rw-r--r--actionpack/test/dispatch/mime_type_test.rb12
-rw-r--r--actionpack/test/dispatch/mount_test.rb2
-rw-r--r--actionpack/test/dispatch/prefix_generation_test.rb4
-rw-r--r--actionpack/test/dispatch/rack_cache_test.rb2
-rw-r--r--actionpack/test/dispatch/reloader_test.rb2
-rw-r--r--actionpack/test/dispatch/request/json_params_parsing_test.rb2
-rw-r--r--actionpack/test/dispatch/request/multipart_params_parsing_test.rb4
-rw-r--r--actionpack/test/dispatch/request/query_string_parsing_test.rb2
-rw-r--r--actionpack/test/dispatch/request/session_test.rb20
-rw-r--r--actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb6
-rw-r--r--actionpack/test/dispatch/request_id_test.rb2
-rw-r--r--actionpack/test/dispatch/request_test.rb203
-rw-r--r--actionpack/test/dispatch/response_test.rb25
-rw-r--r--actionpack/test/dispatch/routing/concerns_test.rb2
-rw-r--r--actionpack/test/dispatch/routing/custom_url_helpers_test.rb52
-rw-r--r--actionpack/test/dispatch/routing/inspector_test.rb2
-rw-r--r--actionpack/test/dispatch/routing/ipv6_redirect_test.rb2
-rw-r--r--actionpack/test/dispatch/routing/route_set_test.rb11
-rw-r--r--actionpack/test/dispatch/routing_assertions_test.rb85
-rw-r--r--actionpack/test/dispatch/routing_test.rb148
-rw-r--r--actionpack/test/dispatch/runner_test.rb2
-rw-r--r--actionpack/test/dispatch/session/abstract_store_test.rb2
-rw-r--r--actionpack/test/dispatch/session/cache_store_test.rb2
-rw-r--r--actionpack/test/dispatch/session/cookie_store_test.rb122
-rw-r--r--actionpack/test/dispatch/session/mem_cache_store_test.rb2
-rw-r--r--actionpack/test/dispatch/session/test_session_test.rb2
-rw-r--r--actionpack/test/dispatch/show_exceptions_test.rb2
-rw-r--r--actionpack/test/dispatch/ssl_test.rb2
-rw-r--r--actionpack/test/dispatch/static_test.rb6
-rw-r--r--actionpack/test/dispatch/system_testing/browser_test.rb10
-rw-r--r--actionpack/test/dispatch/system_testing/driver_test.rb44
-rw-r--r--actionpack/test/dispatch/system_testing/screenshot_helper_test.rb74
-rw-r--r--actionpack/test/dispatch/system_testing/server_test.rb23
-rw-r--r--actionpack/test/dispatch/system_testing/system_test_case_test.rb77
-rw-r--r--actionpack/test/dispatch/test_request_test.rb2
-rw-r--r--actionpack/test/dispatch/test_response_test.rb9
-rw-r--r--actionpack/test/dispatch/uploaded_file_test.rb8
-rw-r--r--actionpack/test/dispatch/url_generation_test.rb2
49 files changed, 1441 insertions, 385 deletions
diff --git a/actionpack/test/dispatch/callbacks_test.rb b/actionpack/test/dispatch/callbacks_test.rb
index 29a5dfc0ad..fc80191c02 100644
--- a/actionpack/test/dispatch/callbacks_test.rb
+++ b/actionpack/test/dispatch/callbacks_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
class DispatcherTest < ActiveSupport::TestCase
diff --git a/actionpack/test/dispatch/content_security_policy_test.rb b/actionpack/test/dispatch/content_security_policy_test.rb
new file mode 100644
index 0000000000..7c4a65a633
--- /dev/null
+++ b/actionpack/test/dispatch/content_security_policy_test.rb
@@ -0,0 +1,368 @@
+# frozen_string_literal: true
+
+require "abstract_unit"
+
+class ContentSecurityPolicyTest < ActiveSupport::TestCase
+ def setup
+ @policy = ActionDispatch::ContentSecurityPolicy.new
+ end
+
+ def test_build
+ assert_equal ";", @policy.build
+
+ @policy.script_src :self
+ assert_equal "script-src 'self';", @policy.build
+ end
+
+ def test_dup
+ @policy.img_src :self
+ @policy.block_all_mixed_content
+ @policy.upgrade_insecure_requests
+ @policy.sandbox
+ copied = @policy.dup
+ assert_equal copied.build, @policy.build
+ end
+
+ def test_mappings
+ @policy.script_src :data
+ assert_equal "script-src data:;", @policy.build
+
+ @policy.script_src :mediastream
+ assert_equal "script-src mediastream:;", @policy.build
+
+ @policy.script_src :blob
+ assert_equal "script-src blob:;", @policy.build
+
+ @policy.script_src :filesystem
+ assert_equal "script-src filesystem:;", @policy.build
+
+ @policy.script_src :self
+ assert_equal "script-src 'self';", @policy.build
+
+ @policy.script_src :unsafe_inline
+ assert_equal "script-src 'unsafe-inline';", @policy.build
+
+ @policy.script_src :unsafe_eval
+ assert_equal "script-src 'unsafe-eval';", @policy.build
+
+ @policy.script_src :none
+ assert_equal "script-src 'none';", @policy.build
+
+ @policy.script_src :strict_dynamic
+ assert_equal "script-src 'strict-dynamic';", @policy.build
+
+ @policy.script_src :none, :report_sample
+ assert_equal "script-src 'none' 'report-sample';", @policy.build
+ end
+
+ def test_fetch_directives
+ @policy.child_src :self
+ assert_match %r{child-src 'self'}, @policy.build
+
+ @policy.child_src false
+ assert_no_match %r{child-src}, @policy.build
+
+ @policy.connect_src :self
+ assert_match %r{connect-src 'self'}, @policy.build
+
+ @policy.connect_src false
+ assert_no_match %r{connect-src}, @policy.build
+
+ @policy.default_src :self
+ assert_match %r{default-src 'self'}, @policy.build
+
+ @policy.default_src false
+ assert_no_match %r{default-src}, @policy.build
+
+ @policy.font_src :self
+ assert_match %r{font-src 'self'}, @policy.build
+
+ @policy.font_src false
+ assert_no_match %r{font-src}, @policy.build
+
+ @policy.frame_src :self
+ assert_match %r{frame-src 'self'}, @policy.build
+
+ @policy.frame_src false
+ assert_no_match %r{frame-src}, @policy.build
+
+ @policy.img_src :self
+ assert_match %r{img-src 'self'}, @policy.build
+
+ @policy.img_src false
+ assert_no_match %r{img-src}, @policy.build
+
+ @policy.manifest_src :self
+ assert_match %r{manifest-src 'self'}, @policy.build
+
+ @policy.manifest_src false
+ assert_no_match %r{manifest-src}, @policy.build
+
+ @policy.media_src :self
+ assert_match %r{media-src 'self'}, @policy.build
+
+ @policy.media_src false
+ assert_no_match %r{media-src}, @policy.build
+
+ @policy.object_src :self
+ assert_match %r{object-src 'self'}, @policy.build
+
+ @policy.object_src false
+ assert_no_match %r{object-src}, @policy.build
+
+ @policy.script_src :self
+ assert_match %r{script-src 'self'}, @policy.build
+
+ @policy.script_src false
+ assert_no_match %r{script-src}, @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.worker_src :self
+ assert_match %r{worker-src 'self'}, @policy.build
+
+ @policy.worker_src false
+ assert_no_match %r{worker-src}, @policy.build
+ end
+
+ def test_document_directives
+ @policy.base_uri "https://example.com"
+ assert_match %r{base-uri https://example\.com;}, @policy.build
+
+ @policy.plugin_types "application/x-shockwave-flash"
+ assert_match %r{plugin-types application/x-shockwave-flash;}, @policy.build
+
+ @policy.sandbox
+ assert_match %r{sandbox;}, @policy.build
+
+ @policy.sandbox "allow-scripts", "allow-modals"
+ assert_match %r{sandbox allow-scripts allow-modals;}, @policy.build
+
+ @policy.sandbox false
+ assert_no_match %r{sandbox}, @policy.build
+ end
+
+ def test_navigation_directives
+ @policy.form_action :self
+ assert_match %r{form-action 'self';}, @policy.build
+
+ @policy.frame_ancestors :self
+ assert_match %r{frame-ancestors 'self';}, @policy.build
+ end
+
+ def test_reporting_directives
+ @policy.report_uri "/violations"
+ assert_match %r{report-uri /violations;}, @policy.build
+ end
+
+ def test_other_directives
+ @policy.block_all_mixed_content
+ assert_match %r{block-all-mixed-content;}, @policy.build
+
+ @policy.block_all_mixed_content false
+ assert_no_match %r{block-all-mixed-content}, @policy.build
+
+ @policy.require_sri_for :script, :style
+ assert_match %r{require-sri-for script style;}, @policy.build
+
+ @policy.require_sri_for "script", "style"
+ assert_match %r{require-sri-for script style;}, @policy.build
+
+ @policy.require_sri_for
+ assert_no_match %r{require-sri-for}, @policy.build
+
+ @policy.upgrade_insecure_requests
+ assert_match %r{upgrade-insecure-requests;}, @policy.build
+
+ @policy.upgrade_insecure_requests false
+ assert_no_match %r{upgrade-insecure-requests}, @policy.build
+ end
+
+ def test_multiple_sources
+ @policy.script_src :self, :https
+ assert_equal "script-src 'self' https:;", @policy.build
+ end
+
+ def test_multiple_directives
+ @policy.script_src :self, :https
+ @policy.style_src :self, :https
+ assert_equal "script-src 'self' https:; style-src 'self' https:;", @policy.build
+ end
+
+ def test_dynamic_directives
+ request = Struct.new(:host).new("www.example.com")
+ controller = Struct.new(:request).new(request)
+
+ @policy.script_src -> { request.host }
+ assert_equal "script-src www.example.com;", @policy.build(controller)
+ end
+
+ def test_mixed_static_and_dynamic_directives
+ @policy.script_src :self, -> { "foo.com" }, "bar.com"
+ assert_equal "script-src 'self' foo.com bar.com;", @policy.build(Object.new)
+ end
+
+ def test_invalid_directive_source
+ exception = assert_raises(ArgumentError) do
+ @policy.script_src [:self]
+ end
+
+ assert_equal "Invalid content security policy source: [:self]", exception.message
+ end
+
+ def test_missing_context_for_dynamic_source
+ @policy.script_src -> { request.host }
+
+ exception = assert_raises(RuntimeError) do
+ @policy.build
+ end
+
+ assert_match %r{\AMissing context for the dynamic content security policy source:}, exception.message
+ end
+
+ def test_raises_runtime_error_when_unexpected_source
+ @policy.plugin_types [:flash]
+
+ exception = assert_raises(RuntimeError) do
+ @policy.build
+ end
+
+ assert_match %r{\AUnexpected content security policy source:}, exception.message
+ end
+end
+
+class ContentSecurityPolicyIntegrationTest < ActionDispatch::IntegrationTest
+ class PolicyController < ActionController::Base
+ content_security_policy only: :inline do |p|
+ p.default_src "https://example.com"
+ end
+
+ content_security_policy only: :conditional, if: :condition? do |p|
+ p.default_src "https://true.example.com"
+ end
+
+ content_security_policy only: :conditional, unless: :condition? do |p|
+ p.default_src "https://false.example.com"
+ end
+
+ content_security_policy only: :report_only do |p|
+ p.report_uri "/violations"
+ end
+
+ content_security_policy_report_only only: :report_only
+
+ def index
+ head :ok
+ end
+
+ def inline
+ head :ok
+ end
+
+ def conditional
+ head :ok
+ end
+
+ def report_only
+ head :ok
+ end
+
+ private
+ def condition?
+ params[:condition] == "true"
+ end
+ end
+
+ ROUTES = ActionDispatch::Routing::RouteSet.new
+ ROUTES.draw do
+ scope module: "content_security_policy_integration_test" do
+ get "/", to: "policy#index"
+ get "/inline", to: "policy#inline"
+ get "/conditional", to: "policy#conditional"
+ get "/report-only", to: "policy#report_only"
+ end
+ end
+
+ POLICY = ActionDispatch::ContentSecurityPolicy.new do |p|
+ p.default_src :self
+ 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_report_only"] = false
+ 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_generates_content_security_policy_header
+ get "/"
+ assert_policy "default-src 'self';"
+ end
+
+ def test_generates_inline_content_security_policy
+ get "/inline"
+ assert_policy "default-src https://example.com;"
+ end
+
+ def test_generates_conditional_content_security_policy
+ get "/conditional", params: { condition: "true" }
+ assert_policy "default-src https://true.example.com;"
+
+ get "/conditional", params: { condition: "false" }
+ assert_policy "default-src https://false.example.com;"
+ end
+
+ def test_generates_report_only_content_security_policy
+ get "/report-only"
+ assert_policy "default-src 'self'; report-uri /violations;", report_only: true
+ end
+
+ private
+
+ def env_config
+ Rails.application.env_config
+ end
+
+ def content_security_policy
+ env_config["action_dispatch.content_security_policy"]
+ end
+
+ def content_security_policy=(policy)
+ env_config["action_dispatch.content_security_policy"] = policy
+ end
+
+ def assert_policy(expected, report_only: false)
+ assert_response :success
+
+ if report_only
+ expected_header = "Content-Security-Policy-Report-Only"
+ unexpected_header = "Content-Security-Policy"
+ else
+ expected_header = "Content-Security-Policy"
+ unexpected_header = "Content-Security-Policy-Report-Only"
+ end
+
+ assert_nil response.headers[unexpected_header]
+ assert_equal expected, response.headers[expected_header]
+ end
+end
diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb
index 664faa31bb..40cbad3b0d 100644
--- a/actionpack/test/dispatch/cookies_test.rb
+++ b/actionpack/test/dispatch/cookies_test.rb
@@ -1,7 +1,9 @@
+# frozen_string_literal: true
+
require "abstract_unit"
require "openssl"
require "active_support/key_generator"
-require "active_support/message_verifier"
+require "active_support/messages/rotation_configuration"
class CookieJarTest < ActiveSupport::TestCase
attr_reader :request
@@ -276,20 +278,34 @@ class CookiesTest < ActionController::TestCase
def encrypted_cookie
cookies.encrypted["foo"]
end
+
+ def cookie_expires_in_two_hours
+ cookies[:user_name] = { value: "assain", expires: 2.hours }
+ head :ok
+ end
end
tests TestController
- SALT = "b3c631c314c0bbca50c1b2843150fe33"
+ SECRET_KEY_BASE = "b3c631c314c0bbca50c1b2843150fe33"
+ SIGNED_COOKIE_SALT = "signed cookie"
+ ENCRYPTED_COOKIE_SALT = "encrypted cookie"
+ ENCRYPTED_SIGNED_COOKIE_SALT = "sigend encrypted cookie"
+ AUTHENTICATED_ENCRYPTED_COOKIE_SALT = "authenticated encrypted cookie"
def setup
super
- @request.env["action_dispatch.key_generator"] = ActiveSupport::KeyGenerator.new(SALT, iterations: 2)
+ @request.env["action_dispatch.key_generator"] = ActiveSupport::KeyGenerator.new(SECRET_KEY_BASE, iterations: 2)
+ @request.env["action_dispatch.cookies_rotations"] = ActiveSupport::Messages::RotationConfiguration.new
- @request.env["action_dispatch.signed_cookie_salt"] =
- @request.env["action_dispatch.encrypted_cookie_salt"] =
- @request.env["action_dispatch.encrypted_signed_cookie_salt"] = SALT
+ @request.env["action_dispatch.secret_key_base"] = SECRET_KEY_BASE
+ @request.env["action_dispatch.use_authenticated_cookie_encryption"] = true
+
+ @request.env["action_dispatch.signed_cookie_salt"] = SIGNED_COOKIE_SALT
+ @request.env["action_dispatch.encrypted_cookie_salt"] = ENCRYPTED_COOKIE_SALT
+ @request.env["action_dispatch.encrypted_signed_cookie_salt"] = ENCRYPTED_SIGNED_COOKIE_SALT
+ @request.env["action_dispatch.authenticated_encrypted_cookie_salt"] = AUTHENTICATED_ENCRYPTED_COOKIE_SALT
@request.host = "www.nextangle.com"
end
@@ -424,28 +440,72 @@ class CookiesTest < ActionController::TestCase
assert_equal 45, cookies.signed[:user_id]
key_generator = @request.env["action_dispatch.key_generator"]
- signed_cookie_salt = @request.env["action_dispatch.signed_cookie_salt"]
- secret = key_generator.generate_key(signed_cookie_salt)
+ secret = key_generator.generate_key(@request.env["action_dispatch.signed_cookie_salt"])
verifier = ActiveSupport::MessageVerifier.new(secret, serializer: Marshal, digest: "SHA1")
assert_equal verifier.generate(45), cookies[:user_id]
end
def test_signed_cookie_using_custom_digest
- @request.env["action_dispatch.cookies_digest"] = "SHA256"
+ @request.env["action_dispatch.signed_cookie_digest"] = "SHA256"
+
get :set_signed_cookie
cookies = @controller.send :cookies
assert_not_equal 45, cookies[:user_id]
assert_equal 45, cookies.signed[:user_id]
key_generator = @request.env["action_dispatch.key_generator"]
- signed_cookie_salt = @request.env["action_dispatch.signed_cookie_salt"]
- secret = key_generator.generate_key(signed_cookie_salt)
+ secret = key_generator.generate_key(@request.env["action_dispatch.signed_cookie_salt"])
verifier = ActiveSupport::MessageVerifier.new(secret, serializer: Marshal, digest: "SHA256")
assert_equal verifier.generate(45), cookies[:user_id]
end
+ def test_signed_cookie_rotating_secret_and_digest
+ secret = "b3c631c314c0bbca50c1b2843150fe33"
+
+ @request.env["action_dispatch.signed_cookie_digest"] = "SHA256"
+ @request.env["action_dispatch.cookies_rotations"].rotate :signed, secret, digest: "SHA1"
+
+ old_message = ActiveSupport::MessageVerifier.new(secret, 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(@request.env["action_dispatch.signed_cookie_salt"])
+ verifier = ActiveSupport::MessageVerifier.new(secret, digest: "SHA256", serializer: Marshal)
+ 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"])
+
+ verifier = ActiveSupport::MessageVerifier.new(secret, serializer: Marshal, digest: "SHA1")
+ message = verifier.generate(45)
+
+ @request.headers["Cookie"] = "user_id=#{Marshal.dump 45}--#{message.split("--").last}"
+ get :get_signed_cookie
+ assert_nil @controller.send(:cookies).signed[:user_id]
+ end
+
def test_signed_cookie_using_default_serializer
get :set_signed_cookie
cookies = @controller.send :cookies
@@ -488,8 +548,7 @@ class CookiesTest < ActionController::TestCase
@request.env["action_dispatch.cookies_serializer"] = :hybrid
key_generator = @request.env["action_dispatch.key_generator"]
- signed_cookie_salt = @request.env["action_dispatch.signed_cookie_salt"]
- secret = key_generator.generate_key(signed_cookie_salt)
+ secret = key_generator.generate_key(@request.env["action_dispatch.signed_cookie_salt"])
marshal_value = ActiveSupport::MessageVerifier.new(secret, serializer: Marshal).generate(45)
@request.headers["Cookie"] = "user_id=#{marshal_value}"
@@ -508,8 +567,8 @@ class CookiesTest < ActionController::TestCase
@request.env["action_dispatch.cookies_serializer"] = :hybrid
key_generator = @request.env["action_dispatch.key_generator"]
- signed_cookie_salt = @request.env["action_dispatch.signed_cookie_salt"]
- secret = key_generator.generate_key(signed_cookie_salt)
+ secret = key_generator.generate_key(@request.env["action_dispatch.signed_cookie_salt"])
+
json_value = ActiveSupport::MessageVerifier.new(secret, serializer: JSON).generate(45)
@request.headers["Cookie"] = "user_id=#{json_value}"
@@ -531,9 +590,7 @@ class CookiesTest < ActionController::TestCase
get :set_encrypted_cookie
cookies = @controller.send :cookies
assert_not_equal "bar", cookies[:foo]
- assert_raise TypeError do
- cookies.signed[:foo]
- end
+ assert_nil cookies.signed[:foo]
assert_equal "bar", cookies.encrypted[:foo]
end
@@ -542,9 +599,7 @@ class CookiesTest < ActionController::TestCase
get :set_encrypted_cookie
cookies = @controller.send :cookies
assert_not_equal "bar", cookies[:foo]
- assert_raises TypeError do
- cookies.signed[:foo]
- end
+ assert_nil cookies.signed[:foo]
assert_equal "bar", cookies.encrypted[:foo]
end
@@ -553,9 +608,7 @@ class CookiesTest < ActionController::TestCase
get :set_encrypted_cookie
cookies = @controller.send :cookies
assert_not_equal "bar", cookies[:foo]
- assert_raises ::JSON::ParserError do
- cookies.signed[:foo]
- end
+ assert_nil cookies.signed[:foo]
assert_equal "bar", cookies.encrypted[:foo]
end
@@ -564,9 +617,7 @@ class CookiesTest < ActionController::TestCase
get :set_wrapped_encrypted_cookie
cookies = @controller.send :cookies
assert_not_equal "wrapped: bar", cookies[:foo]
- assert_raises ::JSON::ParserError do
- cookies.signed[:foo]
- end
+ assert_nil cookies.signed[:foo]
assert_equal "wrapped: bar", cookies.encrypted[:foo]
end
@@ -577,38 +628,15 @@ class CookiesTest < ActionController::TestCase
assert_equal "bar was dumped and loaded", cookies.encrypted[:foo]
end
- def test_encrypted_cookie_using_custom_digest
- @request.env["action_dispatch.cookies_digest"] = "SHA256"
- get :set_encrypted_cookie
- cookies = @controller.send :cookies
- assert_not_equal "bar", cookies[:foo]
- assert_equal "bar", cookies.encrypted[:foo]
-
- sign_secret = @request.env["action_dispatch.key_generator"].generate_key(@request.env["action_dispatch.encrypted_signed_cookie_salt"])
-
- sha1_verifier = ActiveSupport::MessageVerifier.new(sign_secret, serializer: ActiveSupport::MessageEncryptor::NullSerializer, digest: "SHA1")
- sha256_verifier = ActiveSupport::MessageVerifier.new(sign_secret, serializer: ActiveSupport::MessageEncryptor::NullSerializer, digest: "SHA256")
-
- assert_raises(ActiveSupport::MessageVerifier::InvalidSignature) do
- sha1_verifier.verify(cookies[:foo])
- end
-
- assert_nothing_raised do
- sha256_verifier.verify(cookies[:foo])
- end
- end
-
def test_encrypted_cookie_using_hybrid_serializer_can_migrate_marshal_dumped_value_to_json
@request.env["action_dispatch.cookies_serializer"] = :hybrid
key_generator = @request.env["action_dispatch.key_generator"]
- encrypted_cookie_salt = @request.env["action_dispatch.encrypted_cookie_salt"]
- encrypted_signed_cookie_salt = @request.env["action_dispatch.encrypted_signed_cookie_salt"]
- secret = key_generator.generate_key(encrypted_cookie_salt)
- sign_secret = key_generator.generate_key(encrypted_signed_cookie_salt)
+ secret = key_generator.generate_key(@request.env["action_dispatch.authenticated_encrypted_cookie_salt"], 32)
- marshal_value = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len], sign_secret, serializer: Marshal).encrypt_and_sign("bar")
- @request.headers["Cookie"] = "foo=#{marshal_value}"
+ encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: "aes-256-gcm", serializer: Marshal)
+ marshal_value = encryptor.encrypt_and_sign("bar")
+ @request.headers["Cookie"] = "foo=#{::Rack::Utils.escape marshal_value}"
get :get_encrypted_cookie
@@ -616,40 +644,27 @@ class CookiesTest < ActionController::TestCase
assert_not_equal "bar", cookies[:foo]
assert_equal "bar", cookies.encrypted[:foo]
- encryptor = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len], sign_secret, serializer: JSON)
- assert_equal "bar", encryptor.decrypt_and_verify(@response.cookies["foo"])
+ json_encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: "aes-256-gcm", serializer: JSON)
+ assert_not_nil @response.cookies["foo"]
+ assert_equal "bar", json_encryptor.decrypt_and_verify(@response.cookies["foo"])
end
def test_encrypted_cookie_using_hybrid_serializer_can_read_from_json_dumped_value
@request.env["action_dispatch.cookies_serializer"] = :hybrid
key_generator = @request.env["action_dispatch.key_generator"]
- encrypted_cookie_salt = @request.env["action_dispatch.encrypted_cookie_salt"]
- encrypted_signed_cookie_salt = @request.env["action_dispatch.encrypted_signed_cookie_salt"]
- secret = key_generator.generate_key(encrypted_cookie_salt)
- sign_secret = key_generator.generate_key(encrypted_signed_cookie_salt)
- json_value = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len], sign_secret, serializer: JSON).encrypt_and_sign("bar")
- @request.headers["Cookie"] = "foo=#{json_value}"
-
- get :get_encrypted_cookie
-
- cookies = @controller.send :cookies
- assert_not_equal "bar", cookies[:foo]
- assert_equal "bar", cookies.encrypted[:foo]
-
- assert_nil @response.cookies["foo"]
- end
+ secret = key_generator.generate_key(@request.env["action_dispatch.authenticated_encrypted_cookie_salt"], 32)
- def test_compat_encrypted_cookie_using_64_byte_key
- # Cookie generated with 64 bytes secret
- message = ["566d4e75536d686e633246564e6b493062557079626c566d51574d30515430394c53315665564a694e4563786555744f57537454576b396a5a31566a626e52525054303d2d2d34663234333330623130623261306163363562316266323335396164666364613564643134623131"].pack("H*")
- @request.headers["Cookie"] = "foo=#{message}"
+ encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: "aes-256-gcm", serializer: JSON)
+ json_value = encryptor.encrypt_and_sign("bar")
+ @request.headers["Cookie"] = "foo=#{::Rack::Utils.escape json_value}"
get :get_encrypted_cookie
cookies = @controller.send :cookies
assert_not_equal "bar", cookies[:foo]
assert_equal "bar", cookies.encrypted[:foo]
+
assert_nil @response.cookies["foo"]
end
@@ -727,65 +742,8 @@ class CookiesTest < ActionController::TestCase
}
end
- def test_signed_uses_signed_cookie_jar_if_only_secret_token_is_set
- @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33"
- @request.env["action_dispatch.secret_key_base"] = nil
- get :set_signed_cookie
- assert_kind_of ActionDispatch::Cookies::SignedCookieJar, cookies.signed
- end
-
- def test_signed_uses_signed_cookie_jar_if_only_secret_key_base_is_set
- @request.env["action_dispatch.secret_token"] = nil
- @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff"
- get :set_signed_cookie
- assert_kind_of ActionDispatch::Cookies::SignedCookieJar, cookies.signed
- end
-
- def test_signed_uses_upgrade_legacy_signed_cookie_jar_if_both_secret_token_and_secret_key_base_are_set
- @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33"
- @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff"
- get :set_signed_cookie
- assert_kind_of ActionDispatch::Cookies::UpgradeLegacySignedCookieJar, cookies.signed
- end
-
- def test_signed_or_encrypted_uses_signed_cookie_jar_if_only_secret_token_is_set
- @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33"
- @request.env["action_dispatch.secret_key_base"] = nil
- get :get_encrypted_cookie
- assert_kind_of ActionDispatch::Cookies::SignedCookieJar, cookies.signed_or_encrypted
- end
-
- def test_signed_or_encrypted_uses_encrypted_cookie_jar_if_only_secret_key_base_is_set
- @request.env["action_dispatch.secret_token"] = nil
- @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff"
- get :get_encrypted_cookie
- assert_kind_of ActionDispatch::Cookies::EncryptedCookieJar, cookies.signed_or_encrypted
- end
-
- def test_signed_or_encrypted_uses_upgrade_legacy_encrypted_cookie_jar_if_both_secret_token_and_secret_key_base_are_set
- @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33"
- @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff"
- get :get_encrypted_cookie
- assert_kind_of ActionDispatch::Cookies::UpgradeLegacyEncryptedCookieJar, cookies.signed_or_encrypted
- end
-
- def test_encrypted_uses_encrypted_cookie_jar_if_only_secret_key_base_is_set
- @request.env["action_dispatch.secret_token"] = nil
- @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff"
- get :get_encrypted_cookie
- assert_kind_of ActionDispatch::Cookies::EncryptedCookieJar, cookies.encrypted
- end
-
- def test_encrypted_uses_upgrade_legacy_encrypted_cookie_jar_if_both_secret_token_and_secret_key_base_are_set
- @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33"
- @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff"
- get :get_encrypted_cookie
- assert_kind_of ActionDispatch::Cookies::UpgradeLegacyEncryptedCookieJar, cookies.encrypted
- 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"
- @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff"
legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33").generate(45)
@@ -802,9 +760,6 @@ class CookiesTest < ActionController::TestCase
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"
- @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff"
- @request.env["action_dispatch.encrypted_cookie_salt"] = "4433796b79d99a7735553e316522acee"
- @request.env["action_dispatch.encrypted_signed_cookie_salt"] = "00646eb40062e1b1deff205a27cd30f9"
legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33").generate("bar")
@@ -813,17 +768,14 @@ class CookiesTest < ActionController::TestCase
assert_equal "bar", @controller.send(:cookies).encrypted[:foo]
- key_generator = @request.env["action_dispatch.key_generator"]
- secret = key_generator.generate_key(@request.env["action_dispatch.encrypted_cookie_salt"])
- sign_secret = key_generator.generate_key(@request.env["action_dispatch.encrypted_signed_cookie_salt"])
- encryptor = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len], sign_secret)
+ 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"
- @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff"
legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33", serializer: JSON).generate(45)
@@ -841,9 +793,6 @@ class CookiesTest < ActionController::TestCase
def test_legacy_json_signed_cookie_is_read_and_transparently_encrypted_by_encrypted_json_cookie_jar_if_both_secret_token_and_secret_key_base_are_set
@request.env["action_dispatch.cookies_serializer"] = :json
@request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33"
- @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff"
- @request.env["action_dispatch.encrypted_cookie_salt"] = "4433796b79d99a7735553e316522acee"
- @request.env["action_dispatch.encrypted_signed_cookie_salt"] = "00646eb40062e1b1deff205a27cd30f9"
legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33", serializer: JSON).generate("bar")
@@ -852,17 +801,16 @@ class CookiesTest < ActionController::TestCase
assert_equal "bar", @controller.send(:cookies).encrypted[:foo]
- key_generator = @request.env["action_dispatch.key_generator"]
- secret = key_generator.generate_key(@request.env["action_dispatch.encrypted_cookie_salt"])
- sign_secret = key_generator.generate_key(@request.env["action_dispatch.encrypted_signed_cookie_salt"])
- encryptor = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len], sign_secret, serializer: JSON)
+ 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"
- @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff"
legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33", serializer: JSON).generate(45)
@@ -880,9 +828,6 @@ class CookiesTest < ActionController::TestCase
def test_legacy_json_signed_cookie_is_read_and_transparently_encrypted_by_encrypted_hybrid_cookie_jar_if_both_secret_token_and_secret_key_base_are_set
@request.env["action_dispatch.cookies_serializer"] = :hybrid
@request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33"
- @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff"
- @request.env["action_dispatch.encrypted_cookie_salt"] = "4433796b79d99a7735553e316522acee"
- @request.env["action_dispatch.encrypted_signed_cookie_salt"] = "00646eb40062e1b1deff205a27cd30f9"
legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33", serializer: JSON).generate("bar")
@@ -891,17 +836,15 @@ class CookiesTest < ActionController::TestCase
assert_equal "bar", @controller.send(:cookies).encrypted[:foo]
- key_generator = @request.env["action_dispatch.key_generator"]
- secret = key_generator.generate_key(@request.env["action_dispatch.encrypted_cookie_salt"])
- sign_secret = key_generator.generate_key(@request.env["action_dispatch.encrypted_signed_cookie_salt"])
- encryptor = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len], sign_secret, serializer: JSON)
+ 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"
- @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff"
legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33").generate(45)
@@ -918,10 +861,10 @@ class CookiesTest < ActionController::TestCase
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"
- @request.env["action_dispatch.encrypted_cookie_salt"] = "4433796b79d99a7735553e316522acee"
- @request.env["action_dispatch.encrypted_signed_cookie_salt"] = "00646eb40062e1b1deff205a27cd30f9"
legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33").generate("bar")
@@ -930,16 +873,14 @@ class CookiesTest < ActionController::TestCase
assert_equal "bar", @controller.send(:cookies).encrypted[:foo]
- key_generator = @request.env["action_dispatch.key_generator"]
- secret = key_generator.generate_key(@request.env["action_dispatch.encrypted_cookie_salt"])
- sign_secret = key_generator.generate_key(@request.env["action_dispatch.encrypted_signed_cookie_salt"])
- encryptor = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len], sign_secret, serializer: JSON)
+ 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.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff"
@request.headers["Cookie"] = "user_id=45"
get :get_signed_cookie
@@ -950,7 +891,6 @@ class CookiesTest < ActionController::TestCase
def test_legacy_signed_cookie_is_treated_as_nil_by_encrypted_cookie_jar_if_tampered
@request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33"
- @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff"
@request.headers["Cookie"] = "foo=baz"
get :get_encrypted_cookie
@@ -959,6 +899,134 @@ class CookiesTest < ActionController::TestCase
assert_nil @response.cookies["foo"]
end
+ def test_use_authenticated_cookie_encryption_uses_legacy_hmac_aes_cbc_encryption_when_not_enabled
+ @request.env["action_dispatch.use_authenticated_cookie_encryption"] = nil
+
+ key_generator = @request.env["action_dispatch.key_generator"]
+ encrypted_cookie_salt = @request.env["action_dispatch.encrypted_cookie_salt"]
+ encrypted_signed_cookie_salt = @request.env["action_dispatch.encrypted_signed_cookie_salt"]
+ secret = key_generator.generate_key(encrypted_cookie_salt, ActiveSupport::MessageEncryptor.key_len("aes-256-cbc"))
+ sign_secret = key_generator.generate_key(encrypted_signed_cookie_salt)
+ encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, cipher: "aes-256-cbc", digest: "SHA1", serializer: Marshal)
+
+ get :set_encrypted_cookie
+
+ cookies = @controller.send :cookies
+ assert_not_equal "bar", cookies[:foo]
+ assert_equal "bar", cookies.encrypted[:foo]
+ assert_equal "bar", encryptor.decrypt_and_verify(@response.cookies["foo"])
+ end
+
+ def test_rotating_signed_cookies_digest
+ @request.env["action_dispatch.signed_cookie_digest"] = "SHA256"
+ @request.env["action_dispatch.cookies_rotations"].rotate :signed, digest: "SHA1"
+
+ key_generator = @request.env["action_dispatch.key_generator"]
+
+ old_secret = key_generator.generate_key(@request.env["action_dispatch.signed_cookie_salt"])
+ old_value = ActiveSupport::MessageVerifier.new(old_secret).generate(45)
+
+ @request.headers["Cookie"] = "user_id=#{old_value}"
+ get :get_signed_cookie
+
+ assert_equal 45, @controller.send(:cookies).signed[:user_id]
+
+ secret = key_generator.generate_key(@request.env["action_dispatch.signed_cookie_salt"])
+ verifier = ActiveSupport::MessageVerifier.new(secret, digest: "SHA256")
+ assert_equal 45, verifier.verify(@response.cookies["user_id"])
+ end
+
+ def test_legacy_hmac_aes_cbc_encrypted_marshal_cookie_is_upgraded_to_authenticated_encrypted_cookie
+ key_generator = @request.env["action_dispatch.key_generator"]
+ encrypted_cookie_salt = @request.env["action_dispatch.encrypted_cookie_salt"]
+ encrypted_signed_cookie_salt = @request.env["action_dispatch.encrypted_signed_cookie_salt"]
+ secret = key_generator.generate_key(encrypted_cookie_salt, ActiveSupport::MessageEncryptor.key_len("aes-256-cbc"))
+ sign_secret = key_generator.generate_key(encrypted_signed_cookie_salt)
+ marshal_value = ActiveSupport::MessageEncryptor.new(secret, sign_secret, cipher: "aes-256-cbc", serializer: Marshal).encrypt_and_sign("bar")
+
+ @request.headers["Cookie"] = "foo=#{marshal_value}"
+
+ get :get_encrypted_cookie
+
+ cookies = @controller.send :cookies
+ assert_not_equal "bar", cookies[:foo]
+ assert_equal "bar", cookies.encrypted[:foo]
+
+ aead_salt = @request.env["action_dispatch.authenticated_encrypted_cookie_salt"]
+ aead_secret = key_generator.generate_key(aead_salt, ActiveSupport::MessageEncryptor.key_len("aes-256-gcm"))
+ aead_encryptor = ActiveSupport::MessageEncryptor.new(aead_secret, cipher: "aes-256-gcm", serializer: Marshal)
+
+ assert_equal "bar", aead_encryptor.decrypt_and_verify(@response.cookies["foo"])
+ end
+
+ def test_legacy_hmac_aes_cbc_encrypted_json_cookie_is_upgraded_to_authenticated_encrypted_cookie
+ @request.env["action_dispatch.cookies_serializer"] = :json
+
+ key_generator = @request.env["action_dispatch.key_generator"]
+ encrypted_cookie_salt = @request.env["action_dispatch.encrypted_cookie_salt"]
+ encrypted_signed_cookie_salt = @request.env["action_dispatch.encrypted_signed_cookie_salt"]
+ secret = key_generator.generate_key(encrypted_cookie_salt, ActiveSupport::MessageEncryptor.key_len("aes-256-cbc"))
+ sign_secret = key_generator.generate_key(encrypted_signed_cookie_salt)
+ marshal_value = ActiveSupport::MessageEncryptor.new(secret, sign_secret, cipher: "aes-256-cbc", serializer: JSON).encrypt_and_sign("bar")
+
+ @request.headers["Cookie"] = "foo=#{marshal_value}"
+
+ get :get_encrypted_cookie
+
+ cookies = @controller.send :cookies
+ assert_not_equal "bar", cookies[:foo]
+ assert_equal "bar", cookies.encrypted[:foo]
+
+ aead_salt = @request.env["action_dispatch.authenticated_encrypted_cookie_salt"]
+ aead_secret = key_generator.generate_key(aead_salt)[0, ActiveSupport::MessageEncryptor.key_len("aes-256-gcm")]
+ aead_encryptor = ActiveSupport::MessageEncryptor.new(aead_secret, cipher: "aes-256-gcm", serializer: JSON)
+
+ assert_equal "bar", aead_encryptor.decrypt_and_verify(@response.cookies["foo"])
+ end
+
+ def test_legacy_hmac_aes_cbc_encrypted_cookie_using_64_byte_key_is_upgraded_to_authenticated_encrypted_cookie
+ @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff"
+ @request.env["action_dispatch.encrypted_cookie_salt"] = "b3c631c314c0bbca50c1b2843150fe33"
+ @request.env["action_dispatch.encrypted_signed_cookie_salt"] = "b3c631c314c0bbca50c1b2843150fe33"
+
+ # Cookie generated with 64 bytes secret
+ message = ["566d4e75536d686e633246564e6b493062557079626c566d51574d30515430394c53315665564a694e4563786555744f57537454576b396a5a31566a626e52525054303d2d2d34663234333330623130623261306163363562316266323335396164666364613564643134623131"].pack("H*")
+ @request.headers["Cookie"] = "foo=#{message}"
+
+ get :get_encrypted_cookie
+
+ cookies = @controller.send :cookies
+ assert_not_equal "bar", cookies[:foo]
+ assert_equal "bar", cookies.encrypted[:foo]
+
+ salt = @request.env["action_dispatch.authenticated_encrypted_cookie_salt"]
+ secret = @request.env["action_dispatch.key_generator"].generate_key(salt, ActiveSupport::MessageEncryptor.key_len("aes-256-gcm"))
+ encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: "aes-256-gcm", serializer: Marshal)
+
+ assert_equal "bar", encryptor.decrypt_and_verify(@response.cookies["foo"])
+ end
+
+ def test_encrypted_cookie_rotating_secret
+ secret = "b3c631c314c0bbca50c1b2843150fe33"
+
+ @request.env["action_dispatch.encrypted_cookie_cipher"] = "aes-256-gcm"
+ @request.env["action_dispatch.cookies_rotations"].rotate :encrypted, secret
+
+ key_len = ActiveSupport::MessageEncryptor.key_len("aes-256-gcm")
+
+ old_message = ActiveSupport::MessageEncryptor.new(secret, cipher: "aes-256-gcm", serializer: Marshal).encrypt_and_sign(45)
+
+ @request.headers["Cookie"] = "foo=#{::Rack::Utils.escape old_message}"
+
+ get :get_encrypted_cookie
+ assert_equal 45, @controller.send(:cookies).encrypted[:foo]
+
+ key_generator = @request.env["action_dispatch.key_generator"]
+ secret = key_generator.generate_key(@request.env["action_dispatch.authenticated_encrypted_cookie_salt"], key_len)
+ encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: "aes-256-gcm", serializer: Marshal)
+ assert_equal 45, encryptor.decrypt_and_verify(@response.cookies["foo"])
+ end
+
def test_cookie_with_all_domain_option
get :set_cookie_with_domain
assert_response :success
@@ -1199,6 +1267,33 @@ class CookiesTest < ActionController::TestCase
assert_equal "bar", @controller.encrypted_cookie
end
+ def test_signed_cookie_with_expires_set_relatively
+ cookies.signed[:user_name] = { value: "assain", expires: 2.hours }
+
+ travel 1.hour
+ assert_equal "assain", cookies.signed[:user_name]
+
+ travel 2.hours
+ assert_nil cookies.signed[:user_name]
+ end
+
+ def test_encrypted_cookie_with_expires_set_relatively
+ cookies.encrypted[:user_name] = { value: "assain", expires: 2.hours }
+
+ travel 1.hour
+ assert_equal "assain", cookies.encrypted[:user_name]
+
+ travel 2.hours
+ assert_nil cookies.encrypted[:user_name]
+ end
+
+ def test_vanilla_cookie_with_expires_set_relatively
+ travel_to Time.utc(2017, 8, 15) do
+ get :cookie_expires_in_two_hours
+ assert_cookie_header "user_name=assain; path=/; expires=Tue, 15 Aug 2017 02:00:00 -0000"
+ end
+ end
+
private
def assert_cookie_header(expected)
header = @response.headers["Set-Cookie"]
diff --git a/actionpack/test/dispatch/debug_exceptions_test.rb b/actionpack/test/dispatch/debug_exceptions_test.rb
index ea477e8908..60acba0616 100644
--- a/actionpack/test/dispatch/debug_exceptions_test.rb
+++ b/actionpack/test/dispatch/debug_exceptions_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
class DebugExceptionsTest < ActionDispatch::IntegrationTest
@@ -344,7 +346,7 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest
})
assert_response 500
- assert_includes(body, CGI.escapeHTML(PP.pp(params, "", 200)))
+ assert_includes(body, CGI.escapeHTML(PP.pp(params, "".dup, 200)))
end
test "sets the HTTP charset parameter" do
diff --git a/actionpack/test/dispatch/debug_locks_test.rb b/actionpack/test/dispatch/debug_locks_test.rb
new file mode 100644
index 0000000000..d69614bd79
--- /dev/null
+++ b/actionpack/test/dispatch/debug_locks_test.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+require "abstract_unit"
+
+class DebugLocksTest < ActionDispatch::IntegrationTest
+ setup do
+ build_app
+ end
+
+ def test_render_threads_status
+ thread_ready = Concurrent::CountDownLatch.new
+ test_terminated = Concurrent::CountDownLatch.new
+
+ thread = Thread.new do
+ ActiveSupport::Dependencies.interlock.running do
+ thread_ready.count_down
+ test_terminated.wait
+ end
+ end
+
+ thread_ready.wait
+
+ get "/rails/locks"
+
+ test_terminated.count_down
+
+ assert_match(/Thread.*?Sharing/, @response.body)
+ ensure
+ thread.join
+ end
+
+ private
+ def build_app
+ @app = self.class.build_app do |middleware|
+ middleware.use ActionDispatch::DebugLocks
+ end
+ end
+end
diff --git a/actionpack/test/dispatch/exception_wrapper_test.rb b/actionpack/test/dispatch/exception_wrapper_test.rb
index 316661a116..f6e70382a8 100644
--- a/actionpack/test/dispatch/exception_wrapper_test.rb
+++ b/actionpack/test/dispatch/exception_wrapper_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
module ActionDispatch
diff --git a/actionpack/test/dispatch/executor_test.rb b/actionpack/test/dispatch/executor_test.rb
index 0b4e0849c3..8eb6450385 100644
--- a/actionpack/test/dispatch/executor_test.rb
+++ b/actionpack/test/dispatch/executor_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
class ExecutorTest < ActiveSupport::TestCase
diff --git a/actionpack/test/dispatch/header_test.rb b/actionpack/test/dispatch/header_test.rb
index 958450072e..3a265a056b 100644
--- a/actionpack/test/dispatch/header_test.rb
+++ b/actionpack/test/dispatch/header_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
class HeaderTest < ActiveSupport::TestCase
diff --git a/actionpack/test/dispatch/live_response_test.rb b/actionpack/test/dispatch/live_response_test.rb
index d10fc7d575..2901148a9e 100644
--- a/actionpack/test/dispatch/live_response_test.rb
+++ b/actionpack/test/dispatch/live_response_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
require "concurrent/atomic/count_down_latch"
diff --git a/actionpack/test/dispatch/mapper_test.rb b/actionpack/test/dispatch/mapper_test.rb
index 1596d23b1e..969a08efed 100644
--- a/actionpack/test/dispatch/mapper_test.rb
+++ b/actionpack/test/dispatch/mapper_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
module ActionDispatch
diff --git a/actionpack/test/dispatch/middleware_stack_test.rb b/actionpack/test/dispatch/middleware_stack_test.rb
index 481aa22b10..e9f7ad41dd 100644
--- a/actionpack/test/dispatch/middleware_stack_test.rb
+++ b/actionpack/test/dispatch/middleware_stack_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
class MiddlewareStackTest < ActiveSupport::TestCase
diff --git a/actionpack/test/dispatch/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb
index 2ca03c535a..6854783386 100644
--- a/actionpack/test/dispatch/mime_type_test.rb
+++ b/actionpack/test/dispatch/mime_type_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
class MimeTypeTest < ActiveSupport::TestCase
@@ -28,21 +30,21 @@ class MimeTypeTest < ActiveSupport::TestCase
test "parse text with trailing star at the beginning" do
accept = "text/*, text/html, application/json, multipart/form-data"
- expect = [Mime[:html], Mime[:text], Mime[:js], Mime[:css], Mime[:ics], Mime[:csv], Mime[:vcf], Mime[:xml], Mime[:yaml], Mime[:json], Mime[:multipart_form]]
+ expect = [Mime[:html], Mime[:text], Mime[:js], Mime[:css], Mime[:ics], Mime[:csv], Mime[:vcf], Mime[:vtt], Mime[:xml], Mime[:yaml], Mime[:json], Mime[:multipart_form]]
parsed = Mime::Type.parse(accept)
- assert_equal expect, parsed
+ assert_equal expect.map(&:to_s), parsed.map(&:to_s)
end
test "parse text with trailing star in the end" do
accept = "text/html, application/json, multipart/form-data, text/*"
- expect = [Mime[:html], Mime[:json], Mime[:multipart_form], Mime[:text], Mime[:js], Mime[:css], Mime[:ics], Mime[:csv], Mime[:vcf], Mime[:xml], Mime[:yaml]]
+ expect = [Mime[:html], Mime[:json], Mime[:multipart_form], Mime[:text], Mime[:js], Mime[:css], Mime[:ics], Mime[:csv], Mime[:vcf], Mime[:vtt], Mime[:xml], Mime[:yaml]]
parsed = Mime::Type.parse(accept)
- assert_equal expect, parsed
+ assert_equal expect.map(&:to_s), parsed.map(&:to_s)
end
test "parse text with trailing star" do
accept = "text/*"
- expect = [Mime[:html], Mime[:text], Mime[:js], Mime[:css], Mime[:ics], Mime[:csv], Mime[:vcf], Mime[:xml], Mime[:yaml], Mime[:json]]
+ expect = [Mime[:html], Mime[:text], Mime[:js], Mime[:css], Mime[:ics], Mime[:csv], Mime[:vcf], Mime[:vtt], Mime[:xml], Mime[:yaml], Mime[:json]]
parsed = Mime::Type.parse(accept)
assert_equal expect.map(&:to_s).sort!, parsed.map(&:to_s).sort!
end
diff --git a/actionpack/test/dispatch/mount_test.rb b/actionpack/test/dispatch/mount_test.rb
index a7d5ba2345..f6cf653980 100644
--- a/actionpack/test/dispatch/mount_test.rb
+++ b/actionpack/test/dispatch/mount_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
require "rails/engine"
diff --git a/actionpack/test/dispatch/prefix_generation_test.rb b/actionpack/test/dispatch/prefix_generation_test.rb
index 0e093d2188..85ea04356a 100644
--- a/actionpack/test/dispatch/prefix_generation_test.rb
+++ b/actionpack/test/dispatch/prefix_generation_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
require "rack/test"
require "rails/engine"
@@ -11,7 +13,7 @@ module TestGenerationPrefix
end
def self.model_name
- klass = "Post"
+ klass = "Post".dup
def klass.name; self end
ActiveModel::Name.new(klass)
diff --git a/actionpack/test/dispatch/rack_cache_test.rb b/actionpack/test/dispatch/rack_cache_test.rb
index d7bb90abbf..86b375a2a8 100644
--- a/actionpack/test/dispatch/rack_cache_test.rb
+++ b/actionpack/test/dispatch/rack_cache_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
require "action_dispatch/http/rack_cache"
diff --git a/actionpack/test/dispatch/reloader_test.rb b/actionpack/test/dispatch/reloader_test.rb
index 9eb78fe059..e529229fae 100644
--- a/actionpack/test/dispatch/reloader_test.rb
+++ b/actionpack/test/dispatch/reloader_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
class ReloaderTest < ActiveSupport::TestCase
diff --git a/actionpack/test/dispatch/request/json_params_parsing_test.rb b/actionpack/test/dispatch/request/json_params_parsing_test.rb
index 10234a4815..beab8e78b5 100644
--- a/actionpack/test/dispatch/request/json_params_parsing_test.rb
+++ b/actionpack/test/dispatch/request/json_params_parsing_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
class JsonParamsParsingTest < ActionDispatch::IntegrationTest
diff --git a/actionpack/test/dispatch/request/multipart_params_parsing_test.rb b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb
index 01c5ff1429..da8233c074 100644
--- a/actionpack/test/dispatch/request/multipart_params_parsing_test.rb
+++ b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
class MultipartParamsParsingTest < ActionDispatch::IntegrationTest
@@ -21,7 +23,7 @@ class MultipartParamsParsingTest < ActionDispatch::IntegrationTest
end
end
- FIXTURE_PATH = File.dirname(__FILE__) + "/../../fixtures/multipart"
+ FIXTURE_PATH = File.expand_path("../../fixtures/multipart", __dir__)
def teardown
TestController.last_request_parameters = nil
diff --git a/actionpack/test/dispatch/request/query_string_parsing_test.rb b/actionpack/test/dispatch/request/query_string_parsing_test.rb
index 2499c33cef..f9ae5ef4e8 100644
--- a/actionpack/test/dispatch/request/query_string_parsing_test.rb
+++ b/actionpack/test/dispatch/request/query_string_parsing_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
class QueryStringParsingTest < ActionDispatch::IntegrationTest
diff --git a/actionpack/test/dispatch/request/session_test.rb b/actionpack/test/dispatch/request/session_test.rb
index 311b80ea0a..7b6ce31f29 100644
--- a/actionpack/test/dispatch/request/session_test.rb
+++ b/actionpack/test/dispatch/request/session_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
require "action_dispatch/middleware/session/abstract_store"
@@ -54,6 +56,11 @@ module ActionDispatch
assert_equal %w[rails adequate], s.keys
end
+ def test_keys_with_deferred_loading
+ s = Session.create(store_with_data, req, {})
+ assert_equal %w[sample_key], s.keys
+ end
+
def test_values
s = Session.create(store, req, {})
s["rails"] = "ftw"
@@ -61,6 +68,11 @@ module ActionDispatch
assert_equal %w[ftw awesome], s.values
end
+ def test_values_with_deferred_loading
+ s = Session.create(store_with_data, req, {})
+ assert_equal %w[sample_value], s.values
+ end
+
def test_clear
s = Session.create(store, req, {})
s["rails"] = "ftw"
@@ -113,6 +125,14 @@ module ActionDispatch
def delete_session(env, id, options); 123; end
}.new
end
+
+ def store_with_data
+ Class.new {
+ def load_session(env); [1, { "sample_key" => "sample_value" }]; end
+ def session_exists?(env); true; end
+ def delete_session(env, id, options); 123; end
+ }.new
+ end
end
class SessionIntegrationTest < ActionDispatch::IntegrationTest
diff --git a/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb b/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb
index 1169bf0cdb..9e55a7242e 100644
--- a/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb
+++ b/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
class UrlEncodedParamsParsingTest < ActionDispatch::IntegrationTest
@@ -107,7 +109,7 @@ class UrlEncodedParamsParsingTest < ActionDispatch::IntegrationTest
query = [
"customers[boston][first][name]=David",
"something_else=blah",
- "logo=#{File.expand_path(__FILE__)}"
+ "logo=#{__FILE__}"
].join("&")
expected = {
"customers" => {
@@ -118,7 +120,7 @@ class UrlEncodedParamsParsingTest < ActionDispatch::IntegrationTest
}
},
"something_else" => "blah",
- "logo" => File.expand_path(__FILE__),
+ "logo" => __FILE__,
}
assert_parses expected, query
end
diff --git a/actionpack/test/dispatch/request_id_test.rb b/actionpack/test/dispatch/request_id_test.rb
index 4fcd45acf5..aa3175c986 100644
--- a/actionpack/test/dispatch/request_id_test.rb
+++ b/actionpack/test/dispatch/request_id_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
class RequestIdTest < ActiveSupport::TestCase
diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb
index 2f9228a62d..66736e7722 100644
--- a/actionpack/test/dispatch/request_test.rb
+++ b/actionpack/test/dispatch/request_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
class BaseRequestTest < ActiveSupport::TestCase
@@ -110,8 +112,8 @@ class RequestIP < BaseRequestTest
request.remote_ip
}
assert_match(/IP spoofing attack/, e.message)
- assert_match(/HTTP_X_FORWARDED_FOR="1.1.1.1"/, e.message)
- assert_match(/HTTP_CLIENT_IP="2.2.2.2"/, e.message)
+ assert_match(/HTTP_X_FORWARDED_FOR="1\.1\.1\.1"/, e.message)
+ assert_match(/HTTP_CLIENT_IP="2\.2\.2\.2"/, e.message)
end
test "remote ip with spoof detection disabled" do
@@ -761,7 +763,7 @@ class RequestMethod < BaseRequestTest
test "post uneffected by local inflections" do
existing_acronyms = ActiveSupport::Inflector.inflections.acronyms.dup
- existing_acronym_regex = ActiveSupport::Inflector.inflections.acronym_regex.dup
+ assert_deprecated { ActiveSupport::Inflector.inflections.acronym_regex.dup }
begin
ActiveSupport::Inflector.inflections do |inflect|
inflect.acronym "POS"
@@ -775,7 +777,7 @@ class RequestMethod < BaseRequestTest
# Reset original acronym set
ActiveSupport::Inflector.inflections do |inflect|
inflect.send(:instance_variable_set, "@acronyms", existing_acronyms)
- inflect.send(:instance_variable_set, "@acronym_regex", existing_acronym_regex)
+ inflect.send(:define_acronym_regex_patterns)
end
end
end
@@ -783,50 +785,44 @@ end
class RequestFormat < BaseRequestTest
test "xml format" do
- request = stub_request
- assert_called(request, :parameters, times: 2, returns: { format: :xml }) do
- assert_equal Mime[:xml], request.format
- end
+ request = stub_request "QUERY_STRING" => "format=xml"
+
+ assert_equal Mime[:xml], request.format
end
test "xhtml format" do
- request = stub_request
- assert_called(request, :parameters, times: 2, returns: { format: :xhtml }) do
- assert_equal Mime[:html], request.format
- end
+ request = stub_request "QUERY_STRING" => "format=xhtml"
+
+ assert_equal Mime[:html], request.format
end
test "txt format" do
- request = stub_request
- assert_called(request, :parameters, times: 2, returns: { format: :txt }) do
- assert_equal Mime[:text], request.format
- end
+ request = stub_request "QUERY_STRING" => "format=txt"
+
+ assert_equal Mime[:text], request.format
end
test "XMLHttpRequest" do
request = stub_request(
"HTTP_X_REQUESTED_WITH" => "XMLHttpRequest",
- "HTTP_ACCEPT" => [Mime[:js], Mime[:html], Mime[:xml], "text/xml", "*/*"].join(",")
+ "HTTP_ACCEPT" => [Mime[:js], Mime[:html], Mime[:xml], "text/xml", "*/*"].join(","),
+ "QUERY_STRING" => ""
)
- assert_called(request, :parameters, times: 1, returns: {}) do
- assert request.xhr?
- assert_equal Mime[:js], request.format
- end
+ assert request.xhr?
+ assert_equal Mime[:js], request.format
end
test "can override format with parameter negative" do
- request = stub_request
- assert_called(request, :parameters, times: 2, returns: { format: :txt }) do
- assert !request.format.xml?
- end
+ request = stub_request("QUERY_STRING" => "format=txt")
+
+ assert !request.format.xml?
end
test "can override format with parameter positive" do
- request = stub_request
- assert_called(request, :parameters, times: 2, returns: { format: :xml }) do
- assert request.format.xml?
- end
+ request = stub_request("QUERY_STRING" => "format=xml")
+
+ assert request.format.xml?
end
test "formats text/html with accept header" do
@@ -851,27 +847,24 @@ class RequestFormat < BaseRequestTest
end
test "formats format:text with accept header" do
- request = stub_request
- assert_called(request, :parameters, times: 2, returns: { format: :txt }) do
- assert_equal [Mime[:text]], request.formats
- end
+ request = stub_request("QUERY_STRING" => "format=txt")
+
+ assert_equal [Mime[:text]], request.formats
end
test "formats format:unknown with accept header" do
- request = stub_request
- assert_called(request, :parameters, times: 2, returns: { format: :unknown }) do
- assert_instance_of Mime::NullType, request.format
- end
+ request = stub_request("QUERY_STRING" => "format=unknown")
+
+ assert_instance_of Mime::NullType, request.format
end
test "format is not nil with unknown format" do
- request = stub_request
- assert_called(request, :parameters, times: 2, returns: { format: :hello }) do
- assert request.format.nil?
- assert_not request.format.html?
- assert_not request.format.xml?
- assert_not request.format.json?
- end
+ request = stub_request("QUERY_STRING" => "format=hello")
+
+ assert_nil request.format
+ assert_not request.format.html?
+ assert_not request.format.xml?
+ assert_not request.format.json?
end
test "format does not throw exceptions when malformed parameters" do
@@ -881,10 +874,10 @@ class RequestFormat < BaseRequestTest
end
test "formats with xhr request" do
- request = stub_request "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest"
- assert_called(request, :parameters, times: 1, returns: {}) do
- assert_equal [Mime[:js]], request.formats
- end
+ request = stub_request "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest",
+ "QUERY_STRING" => ""
+
+ assert_equal [Mime[:js]], request.formats
end
test "ignore_accept_header" do
@@ -892,62 +885,58 @@ class RequestFormat < BaseRequestTest
ActionDispatch::Request.ignore_accept_header = true
begin
- request = stub_request "HTTP_ACCEPT" => "application/xml"
- assert_called(request, :parameters, times: 1, returns: {}) do
- assert_equal [ Mime[:html] ], request.formats
- end
+ request = stub_request "HTTP_ACCEPT" => "application/xml",
+ "QUERY_STRING" => ""
- request = stub_request "HTTP_ACCEPT" => "koz-asked/something-crazy"
- assert_called(request, :parameters, times: 1, returns: {}) do
- assert_equal [ Mime[:html] ], request.formats
- end
+ assert_equal [ Mime[:html] ], request.formats
- request = stub_request "HTTP_ACCEPT" => "*/*;q=0.1"
- assert_called(request, :parameters, times: 1, returns: {}) do
- assert_equal [ Mime[:html] ], request.formats
- end
+ request = stub_request "HTTP_ACCEPT" => "koz-asked/something-crazy",
+ "QUERY_STRING" => ""
- request = stub_request "HTTP_ACCEPT" => "application/jxw"
- assert_called(request, :parameters, times: 1, returns: {}) do
- assert_equal [ Mime[:html] ], request.formats
- end
+ assert_equal [ Mime[:html] ], request.formats
+
+ request = stub_request "HTTP_ACCEPT" => "*/*;q=0.1",
+ "QUERY_STRING" => ""
+
+ assert_equal [ Mime[:html] ], request.formats
+
+ request = stub_request "HTTP_ACCEPT" => "application/jxw",
+ "QUERY_STRING" => ""
+
+ assert_equal [ Mime[:html] ], request.formats
request = stub_request "HTTP_ACCEPT" => "application/xml",
- "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest"
+ "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest",
+ "QUERY_STRING" => ""
- assert_called(request, :parameters, times: 1, returns: {}) do
- assert_equal [ Mime[:js] ], request.formats
- end
+ assert_equal [ Mime[:js] ], request.formats
request = stub_request "HTTP_ACCEPT" => "application/xml",
- "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest"
- assert_called(request, :parameters, times: 2, returns: { format: :json }) do
- assert_equal [ Mime[:json] ], request.formats
- end
+ "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest",
+ "QUERY_STRING" => "format=json"
+
+ assert_equal [ Mime[:json] ], request.formats
ensure
ActionDispatch::Request.ignore_accept_header = old_ignore_accept_header
end
end
test "format taken from the path extension" do
- request = stub_request "PATH_INFO" => "/foo.xml"
- assert_called(request, :parameters, times: 1, returns: {}) do
- assert_equal [Mime[:xml]], request.formats
- end
+ request = stub_request "PATH_INFO" => "/foo.xml", "QUERY_STRING" => ""
- request = stub_request "PATH_INFO" => "/foo.123"
- assert_called(request, :parameters, times: 1, returns: {}) do
- assert_equal [Mime[:html]], request.formats
- end
+ assert_equal [Mime[:xml]], request.formats
+
+ request = stub_request "PATH_INFO" => "/foo.123", "QUERY_STRING" => ""
+
+ assert_equal [Mime[:html]], request.formats
end
test "formats from accept headers have higher precedence than path extension" do
request = stub_request "HTTP_ACCEPT" => "application/json",
- "PATH_INFO" => "/foo.xml"
+ "PATH_INFO" => "/foo.xml",
+ "QUERY_STRING" => ""
- assert_called(request, :parameters, times: 1, returns: {}) do
- assert_equal [Mime[:json]], request.formats
- end
+ assert_equal [Mime[:json]], request.formats
end
end
@@ -995,15 +984,14 @@ end
class RequestParameters < BaseRequestTest
test "parameters" do
- request = stub_request
+ request = stub_request "CONTENT_TYPE" => "application/json",
+ "CONTENT_LENGTH" => 9,
+ "RAW_POST_DATA" => '{"foo":1}',
+ "QUERY_STRING" => "bar=2"
- assert_called(request, :request_parameters, times: 2, returns: { "foo" => 1 }) do
- assert_called(request, :query_parameters, times: 2, returns: { "bar" => 2 }) do
- assert_equal({ "foo" => 1, "bar" => 2 }, request.parameters)
- assert_equal({ "foo" => 1 }, request.request_parameters)
- assert_equal({ "bar" => 2 }, request.query_parameters)
- end
- end
+ assert_equal({ "foo" => 1, "bar" => "2" }, request.parameters)
+ assert_equal({ "foo" => 1 }, request.request_parameters)
+ assert_equal({ "bar" => "2" }, request.query_parameters)
end
test "parameters not accessible after rack parse error" do
@@ -1024,7 +1012,8 @@ class RequestParameters < BaseRequestTest
request.path_parameters = { foo: "\xBE" }
end
- assert_equal "Invalid path parameters: Non UTF-8 value: \xBE", err.message
+ assert_predicate err.message, :valid_encoding?
+ assert_equal "Invalid path parameters: Invalid encoding for parameter: �", err.message
end
test "parameters not accessible after rack parse error of invalid UTF8 character" do
@@ -1098,6 +1087,19 @@ class RequestParameterFilter < BaseRequestTest
end
end
+ test "parameter filter should maintain hash with indifferent access" do
+ test_hashes = [
+ [{ "foo" => "bar" }.with_indifferent_access, ["blah"]],
+ [{ "foo" => "bar" }.with_indifferent_access, []]
+ ]
+
+ test_hashes.each do |before_filter, filter_words|
+ parameter_filter = ActionDispatch::Http::ParameterFilter.new(filter_words)
+ assert_instance_of ActiveSupport::HashWithIndifferentAccess,
+ parameter_filter.filter(before_filter)
+ end
+ end
+
test "filtered_parameters returns params filtered" do
request = stub_request(
"action_dispatch.request.parameters" => {
@@ -1288,3 +1290,18 @@ class RequestFormData < BaseRequestTest
assert !request.form_data?
end
end
+
+class EarlyHintsRequestTest < BaseRequestTest
+ def setup
+ super
+ @env["rack.early_hints"] = lambda { |links| links }
+ @request = stub_request
+ end
+
+ test "when early hints is set in the env link headers are sent" do
+ early_hints = @request.send_early_hints("Link" => "</style.css>; rel=preload; as=style\n</script.js>; rel=preload")
+ expected_hints = { "Link" => "</style.css>; rel=preload; as=style\n</script.js>; rel=preload" }
+
+ assert_equal expected_hints, early_hints
+ end
+end
diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb
index 7433c5ce0c..0b727dad3d 100644
--- a/actionpack/test/dispatch/response_test.rb
+++ b/actionpack/test/dispatch/response_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
require "timeout"
require "rack/content_length"
@@ -294,13 +296,8 @@ class ResponseTest < ActiveSupport::TestCase
end
test "read content type with default charset utf-8" do
- original = ActionDispatch::Response.default_charset
- begin
- resp = ActionDispatch::Response.new(200, "Content-Type" => "text/xml")
- assert_equal("utf-8", resp.charset)
- ensure
- ActionDispatch::Response.default_charset = original
- end
+ resp = ActionDispatch::Response.new(200, "Content-Type" => "text/xml")
+ assert_equal("utf-8", resp.charset)
end
test "read content type with charset utf-16" do
@@ -314,13 +311,16 @@ class ResponseTest < ActiveSupport::TestCase
end
end
- test "read x_frame_options, x_content_type_options and x_xss_protection" do
+ test "read x_frame_options, x_content_type_options, x_xss_protection, x_download_options and x_permitted_cross_domain_policies, referrer_policy" do
original_default_headers = ActionDispatch::Response.default_headers
begin
ActionDispatch::Response.default_headers = {
"X-Frame-Options" => "DENY",
"X-Content-Type-Options" => "nosniff",
- "X-XSS-Protection" => "1;"
+ "X-XSS-Protection" => "1;",
+ "X-Download-Options" => "noopen",
+ "X-Permitted-Cross-Domain-Policies" => "none",
+ "Referrer-Policy" => "strict-origin-when-cross-origin"
}
resp = ActionDispatch::Response.create.tap { |response|
response.body = "Hello"
@@ -330,6 +330,9 @@ class ResponseTest < ActiveSupport::TestCase
assert_equal("DENY", resp.headers["X-Frame-Options"])
assert_equal("nosniff", resp.headers["X-Content-Type-Options"])
assert_equal("1;", resp.headers["X-XSS-Protection"])
+ assert_equal("noopen", resp.headers["X-Download-Options"])
+ assert_equal("none", resp.headers["X-Permitted-Cross-Domain-Policies"])
+ assert_equal("strict-origin-when-cross-origin", resp.headers["Referrer-Policy"])
ensure
ActionDispatch::Response.default_headers = original_default_headers
end
@@ -381,10 +384,10 @@ class ResponseTest < ActiveSupport::TestCase
app = lambda { |env| @response.to_a }
env = Rack::MockRequest.env_for("/")
- status, headers, body = app.call(env)
+ _status, headers, _body = app.call(env)
assert_nil headers["Content-Length"]
- status, headers, body = Rack::ContentLength.new(app).call(env)
+ _status, headers, _body = Rack::ContentLength.new(app).call(env)
assert_equal "5", headers["Content-Length"]
end
end
diff --git a/actionpack/test/dispatch/routing/concerns_test.rb b/actionpack/test/dispatch/routing/concerns_test.rb
index 2d71c37562..503a7ccd56 100644
--- a/actionpack/test/dispatch/routing/concerns_test.rb
+++ b/actionpack/test/dispatch/routing/concerns_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
class ReviewsController < ResourcesController; end
diff --git a/actionpack/test/dispatch/routing/custom_url_helpers_test.rb b/actionpack/test/dispatch/routing/custom_url_helpers_test.rb
index f85b989892..a1a1e79884 100644
--- a/actionpack/test/dispatch/routing/custom_url_helpers_test.rb
+++ b/actionpack/test/dispatch/routing/custom_url_helpers_test.rb
@@ -1,21 +1,28 @@
+# frozen_string_literal: true
+
require "abstract_unit"
class TestCustomUrlHelpers < ActionDispatch::IntegrationTest
class Linkable
attr_reader :id
+ def self.name
+ super.demodulize
+ end
+
def initialize(id)
@id = id
end
def linkable_type
- self.class.name.demodulize.underscore
+ self.class.name.underscore
end
end
class Category < Linkable; end
class Collection < Linkable; end
class Product < Linkable; end
+ class Manufacturer < Linkable; end
class Model
extend ActiveModel::Naming
@@ -79,7 +86,7 @@ class TestCustomUrlHelpers < ActionDispatch::IntegrationTest
get "/media/:id", to: "media#show", as: :media
get "/pages/:id", to: "pages#show", as: :page
- resources :categories, :collections, :products
+ resources :categories, :collections, :products, :manufacturers
namespace :admin do
get "/dashboard", to: "dashboard#index"
@@ -89,6 +96,7 @@ class TestCustomUrlHelpers < ActionDispatch::IntegrationTest
direct("string") { "http://www.rubyonrails.org" }
direct(:helper) { basket_url }
direct(:linkable) { |linkable| [:"#{linkable.linkable_type}", { id: linkable.id }] }
+ direct(:nested) { |linkable| route_for(:linkable, linkable) }
direct(:params) { |params| params }
direct(:symbol) { :basket }
direct(:hash) { { controller: "basket", action: "show" } }
@@ -102,6 +110,7 @@ class TestCustomUrlHelpers < ActionDispatch::IntegrationTest
resolve("Article") { |article| [:post, { id: article.id }] }
resolve("Basket") { |basket| [:basket] }
+ resolve("Manufacturer") { |manufacturer| route_for(:linkable, manufacturer) }
resolve("User", anchor: "details") { |user, options| [:profile, options] }
resolve("Video") { |video| [:media, { id: video.id }] }
resolve(%w[Page CategoryPage ProductPage]) { |page| [:page, { id: page.id }] }
@@ -119,6 +128,7 @@ class TestCustomUrlHelpers < ActionDispatch::IntegrationTest
@category = Category.new("1")
@collection = Collection.new("2")
@product = Product.new("3")
+ @manufacturer = Manufacturer.new("apple")
@basket = Basket.new
@user = User.new
@video = Video.new("4")
@@ -136,14 +146,14 @@ class TestCustomUrlHelpers < ActionDispatch::IntegrationTest
end
def test_direct_paths
- assert_equal "http://www.rubyonrails.org", website_path
- assert_equal "http://www.rubyonrails.org", Routes.url_helpers.website_path
+ assert_equal "/", website_path
+ assert_equal "/", Routes.url_helpers.website_path
- assert_equal "http://www.rubyonrails.org", string_path
- assert_equal "http://www.rubyonrails.org", Routes.url_helpers.string_path
+ assert_equal "/", string_path
+ assert_equal "/", Routes.url_helpers.string_path
- assert_equal "http://www.example.com/basket", helper_url
- assert_equal "http://www.example.com/basket", Routes.url_helpers.helper_url
+ assert_equal "/basket", helper_path
+ assert_equal "/basket", Routes.url_helpers.helper_path
assert_equal "/categories/1", linkable_path(@category)
assert_equal "/categories/1", Routes.url_helpers.linkable_path(@category)
@@ -152,10 +162,13 @@ class TestCustomUrlHelpers < ActionDispatch::IntegrationTest
assert_equal "/products/3", linkable_path(@product)
assert_equal "/products/3", Routes.url_helpers.linkable_path(@product)
+ assert_equal "/categories/1", nested_path(@category)
+ assert_equal "/categories/1", Routes.url_helpers.nested_path(@category)
+
assert_equal "/", params_path(@safe_params)
assert_equal "/", Routes.url_helpers.params_path(@safe_params)
- assert_raises(ArgumentError) { params_path(@unsafe_params) }
- assert_raises(ArgumentError) { Routes.url_helpers.params_path(@unsafe_params) }
+ assert_raises(ActionController::UnfilteredParameters) { params_path(@unsafe_params) }
+ assert_raises(ActionController::UnfilteredParameters) { Routes.url_helpers.params_path(@unsafe_params) }
assert_equal "/basket", symbol_path
assert_equal "/basket", Routes.url_helpers.symbol_path
@@ -192,10 +205,13 @@ class TestCustomUrlHelpers < ActionDispatch::IntegrationTest
assert_equal "http://www.example.com/products/3", linkable_url(@product)
assert_equal "http://www.example.com/products/3", Routes.url_helpers.linkable_url(@product)
+ assert_equal "http://www.example.com/categories/1", nested_url(@category)
+ assert_equal "http://www.example.com/categories/1", Routes.url_helpers.nested_url(@category)
+
assert_equal "http://www.example.com/", params_url(@safe_params)
assert_equal "http://www.example.com/", Routes.url_helpers.params_url(@safe_params)
- assert_raises(ArgumentError) { params_url(@unsafe_params) }
- assert_raises(ArgumentError) { Routes.url_helpers.params_url(@unsafe_params) }
+ assert_raises(ActionController::UnfilteredParameters) { params_url(@unsafe_params) }
+ assert_raises(ActionController::UnfilteredParameters) { Routes.url_helpers.params_url(@unsafe_params) }
assert_equal "http://www.example.com/basket", symbol_url
assert_equal "http://www.example.com/basket", Routes.url_helpers.symbol_url
@@ -244,6 +260,9 @@ class TestCustomUrlHelpers < ActionDispatch::IntegrationTest
assert_equal "/pages/8", polymorphic_path(@product_page)
assert_equal "/pages/8", Routes.url_helpers.polymorphic_path(@product_page)
assert_equal "/pages/8", ActionDispatch::Routing::PolymorphicRoutes::HelperMethodBuilder.path.handle_model_call(self, @product_page)
+
+ assert_equal "/manufacturers/apple", polymorphic_path(@manufacturer)
+ assert_equal "/manufacturers/apple", Routes.url_helpers.polymorphic_path(@manufacturer)
end
def test_resolve_urls
@@ -277,6 +296,9 @@ class TestCustomUrlHelpers < ActionDispatch::IntegrationTest
assert_equal "http://www.example.com/pages/8", polymorphic_url(@product_page)
assert_equal "http://www.example.com/pages/8", Routes.url_helpers.polymorphic_url(@product_page)
assert_equal "http://www.example.com/pages/8", ActionDispatch::Routing::PolymorphicRoutes::HelperMethodBuilder.url.handle_model_call(self, @product_page)
+
+ assert_equal "http://www.example.com/manufacturers/apple", polymorphic_url(@manufacturer)
+ assert_equal "http://www.example.com/manufacturers/apple", Routes.url_helpers.polymorphic_url(@manufacturer)
end
def test_defining_direct_inside_a_scope_raises_runtime_error
@@ -302,4 +324,10 @@ class TestCustomUrlHelpers < ActionDispatch::IntegrationTest
end
end
end
+
+ def test_defining_direct_url_registers_helper_method
+ assert_equal "http://www.example.com/basket", Routes.url_helpers.symbol_url
+ assert_equal true, Routes.named_routes.route_defined?(:symbol_url), "'symbol_url' named helper not found"
+ assert_equal true, Routes.named_routes.route_defined?(:symbol_path), "'symbol_path' named helper not found"
+ end
end
diff --git a/actionpack/test/dispatch/routing/inspector_test.rb b/actionpack/test/dispatch/routing/inspector_test.rb
index a4babf8554..438a918567 100644
--- a/actionpack/test/dispatch/routing/inspector_test.rb
+++ b/actionpack/test/dispatch/routing/inspector_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
require "rails/engine"
require "action_dispatch/routing/inspector"
diff --git a/actionpack/test/dispatch/routing/ipv6_redirect_test.rb b/actionpack/test/dispatch/routing/ipv6_redirect_test.rb
index 179aee9ba7..31559bffc7 100644
--- a/actionpack/test/dispatch/routing/ipv6_redirect_test.rb
+++ b/actionpack/test/dispatch/routing/ipv6_redirect_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
class IPv6IntegrationTest < ActionDispatch::IntegrationTest
diff --git a/actionpack/test/dispatch/routing/route_set_test.rb b/actionpack/test/dispatch/routing/route_set_test.rb
index ace35dda53..e61d47b160 100644
--- a/actionpack/test/dispatch/routing/route_set_test.rb
+++ b/actionpack/test/dispatch/routing/route_set_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
module ActionDispatch
@@ -138,6 +140,15 @@ module ActionDispatch
assert_equal "/a/users/1", url_helpers.user_path(1, foo: "a")
end
+ test "implicit path components consistently return the same result" do
+ draw do
+ resources :users, to: SimpleApp.new("foo#index")
+ end
+ assert_equal "/users/1.json", url_helpers.user_path(1, :json)
+ assert_equal "/users/1.json", url_helpers.user_path(1, format: :json)
+ assert_equal "/users/1.json", url_helpers.user_path(1, :json)
+ end
+
private
def draw(&block)
@set.draw(&block)
diff --git a/actionpack/test/dispatch/routing_assertions_test.rb b/actionpack/test/dispatch/routing_assertions_test.rb
index 917ce7e668..a5198f2f13 100644
--- a/actionpack/test/dispatch/routing_assertions_test.rb
+++ b/actionpack/test/dispatch/routing_assertions_test.rb
@@ -1,12 +1,40 @@
+# frozen_string_literal: true
+
require "abstract_unit"
+require "rails/engine"
require "controller/fake_controllers"
class SecureArticlesController < ArticlesController; end
class BlockArticlesController < ArticlesController; end
class QueryArticlesController < ArticlesController; end
+class SecureBooksController < BooksController; end
+class BlockBooksController < BooksController; end
+class QueryBooksController < BooksController; end
+
class RoutingAssertionsTest < ActionController::TestCase
def setup
+ engine = Class.new(Rails::Engine) do
+ def self.name
+ "blog_engine"
+ end
+ end
+ engine.routes.draw do
+ resources :books
+
+ scope "secure", constraints: { protocol: "https://" } do
+ resources :books, controller: "secure_books"
+ end
+
+ scope "block", constraints: lambda { |r| r.ssl? } do
+ resources :books, controller: "block_books"
+ end
+
+ scope "query", constraints: lambda { |r| r.params[:use_query] == "true" } do
+ resources :books, controller: "query_books"
+ end
+ end
+
@routes = ActionDispatch::Routing::RouteSet.new
@routes.draw do
resources :articles
@@ -22,6 +50,8 @@ class RoutingAssertionsTest < ActionController::TestCase
scope "query", constraints: lambda { |r| r.params[:use_query] == "true" } do
resources :articles, controller: "query_articles"
end
+
+ mount engine => "/shelf"
end
end
@@ -31,11 +61,11 @@ class RoutingAssertionsTest < ActionController::TestCase
end
def test_assert_generates_with_defaults
- assert_generates("/articles/1/edit", { controller: "articles", action: "edit" }, id: "1")
+ assert_generates("/articles/1/edit", { controller: "articles", action: "edit" }, { id: "1" })
end
def test_assert_generates_with_extras
- assert_generates("/articles", { controller: "articles", action: "index", page: "1" }, {}, page: "1")
+ assert_generates("/articles", { controller: "articles", action: "index", page: "1" }, {}, { page: "1" })
end
def test_assert_recognizes
@@ -48,8 +78,8 @@ class RoutingAssertionsTest < ActionController::TestCase
end
def test_assert_recognizes_with_method
- assert_recognizes({ controller: "articles", action: "create" }, path: "/articles", method: :post)
- assert_recognizes({ controller: "articles", action: "update", id: "1" }, path: "/articles/1", method: :put)
+ assert_recognizes({ controller: "articles", action: "create" }, { path: "/articles", method: :post })
+ assert_recognizes({ controller: "articles", action: "update", id: "1" }, { path: "/articles/1", method: :put })
end
def test_assert_recognizes_with_hash_constraint
@@ -81,6 +111,49 @@ class RoutingAssertionsTest < ActionController::TestCase
assert_match err.message, "This is a really bad msg"
end
+ def test_assert_recognizes_with_engine
+ assert_recognizes({ controller: "books", action: "index" }, "/shelf/books")
+ assert_recognizes({ controller: "books", action: "show", id: "1" }, "/shelf/books/1")
+ end
+
+ def test_assert_recognizes_with_engine_and_extras
+ assert_recognizes({ controller: "books", action: "index", page: "1" }, "/shelf/books", page: "1")
+ end
+
+ def test_assert_recognizes_with_engine_and_method
+ assert_recognizes({ controller: "books", action: "create" }, { path: "/shelf/books", method: :post })
+ assert_recognizes({ controller: "books", action: "update", id: "1" }, { path: "/shelf/books/1", method: :put })
+ end
+
+ def test_assert_recognizes_with_engine_and_hash_constraint
+ assert_raise(Assertion) do
+ assert_recognizes({ controller: "secure_books", action: "index" }, "http://test.host/shelf/secure/books")
+ end
+ assert_recognizes({ controller: "secure_books", action: "index", protocol: "https://" }, "https://test.host/shelf/secure/books")
+ end
+
+ def test_assert_recognizes_with_engine_and_block_constraint
+ assert_raise(Assertion) do
+ assert_recognizes({ controller: "block_books", action: "index" }, "http://test.host/shelf/block/books")
+ end
+ assert_recognizes({ controller: "block_books", action: "index" }, "https://test.host/shelf/block/books")
+ end
+
+ def test_assert_recognizes_with_engine_and_query_constraint
+ assert_raise(Assertion) do
+ assert_recognizes({ controller: "query_books", action: "index", use_query: "false" }, "/shelf/query/books", use_query: "false")
+ end
+ assert_recognizes({ controller: "query_books", action: "index", use_query: "true" }, "/shelf/query/books", use_query: "true")
+ end
+
+ def test_assert_recognizes_raises_message_with_engine
+ err = assert_raise(Assertion) do
+ assert_recognizes({ controller: "secure_books", action: "index" }, "http://test.host/shelf/secure/books", {}, "This is a really bad msg")
+ end
+
+ assert_match err.message, "This is a really bad msg"
+ end
+
def test_assert_routing
assert_routing("/articles", controller: "articles", action: "index")
end
@@ -94,11 +167,11 @@ class RoutingAssertionsTest < ActionController::TestCase
end
def test_assert_routing_with_defaults
- assert_routing("/articles/1/edit", { controller: "articles", action: "edit", id: "1" }, id: "1")
+ assert_routing("/articles/1/edit", { controller: "articles", action: "edit", id: "1" }, { id: "1" })
end
def test_assert_routing_with_extras
- assert_routing("/articles", { controller: "articles", action: "index", page: "1" }, {}, page: "1")
+ assert_routing("/articles", { controller: "articles", action: "index", page: "1" }, {}, { page: "1" })
end
def test_assert_routing_with_hash_constraint
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index d563df91df..8f4e7c96a9 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -1,6 +1,9 @@
+# frozen_string_literal: true
+
require "erb"
require "abstract_unit"
require "controller/fake_controllers"
+require "active_support/messages/rotation_configuration"
class TestRoutingMapper < ActionDispatch::IntegrationTest
SprocketsApp = lambda { |env|
@@ -3633,7 +3636,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
params = ActionController::Parameters.new(id: "1")
- assert_raises ArgumentError do
+ assert_raises ActionController::UnfilteredParameters do
root_path(params)
end
end
@@ -3706,6 +3709,24 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
assert_equal "/bar", bar_root_path
end
+ def test_nested_routes_under_format_resource
+ draw do
+ resources :formats do
+ resources :items
+ end
+ end
+
+ get "/formats/1/items.json"
+ assert_equal 200, @response.status
+ assert_equal "items#index", @response.body
+ assert_equal "/formats/1/items.json", format_items_path(1, :json)
+
+ get "/formats/1/items/2.json"
+ assert_equal 200, @response.status
+ assert_equal "items#show", @response.body
+ assert_equal "/formats/1/items/2.json", format_item_path(1, 2, :json)
+ end
+
private
def draw(&block)
@@ -4204,7 +4225,7 @@ class TestGlobRoutingMapper < ActionDispatch::IntegrationTest
end
end
- #include Routes.url_helpers
+ # include Routes.url_helpers
APP = build_app Routes
def app; APP end
@@ -4396,22 +4417,24 @@ end
class TestInvalidUrls < ActionDispatch::IntegrationTest
class FooController < ActionController::Base
+ def self.binary_params_for?(action)
+ action == "show"
+ end
+
def show
render plain: "foo#show"
end
end
- test "invalid UTF-8 encoding returns a 400 Bad Request" do
+ test "invalid UTF-8 encoding returns a bad request" do
with_routing do |set|
set.draw do
get "/bar/:id", to: redirect("/foo/show/%{id}")
- get "/foo/show(/:id)", to: "test_invalid_urls/foo#show"
ok = lambda { |env| [200, { "Content-Type" => "text/plain" }, []] }
get "/foobar/:id", to: ok
ActiveSupport::Deprecation.silence do
- get "/foo(/:action(/:id))", controller: "test_invalid_urls/foo"
get "/:controller(/:action(/:id))"
end
end
@@ -4422,9 +4445,6 @@ class TestInvalidUrls < ActionDispatch::IntegrationTest
get "/foo/%E2%EF%BF%BD%A6"
assert_response :bad_request
- get "/foo/show/%E2%EF%BF%BD%A6"
- assert_response :bad_request
-
get "/bar/%E2%EF%BF%BD%A6"
assert_response :bad_request
@@ -4432,6 +4452,17 @@ class TestInvalidUrls < ActionDispatch::IntegrationTest
assert_response :bad_request
end
end
+
+ test "params encoded with binary_params_for? are treated as ASCII 8bit" do
+ with_routing do |set|
+ set.draw do
+ get "/foo/show(/:id)", to: "test_invalid_urls/foo#show"
+ end
+
+ get "/foo/show/%E2%EF%BF%BD%A6"
+ assert_response :ok
+ end
+ end
end
class TestOptionalRootSegments < ActionDispatch::IntegrationTest
@@ -4917,6 +4948,7 @@ end
class FlashRedirectTest < ActionDispatch::IntegrationTest
SessionKey = "_myapp_session"
Generator = ActiveSupport::LegacyKeyGenerator.new("b3c631c314c0bbca50c1b2843150fe33")
+ Rotations = ActiveSupport::Messages::RotationConfiguration.new
class KeyGeneratorMiddleware
def initialize(app)
@@ -4925,6 +4957,8 @@ class FlashRedirectTest < ActionDispatch::IntegrationTest
def call(env)
env["action_dispatch.key_generator"] ||= Generator
+ env["action_dispatch.cookies_rotations"] ||= Rotations
+
@app.call(env)
end
end
@@ -4962,3 +4996,101 @@ class FlashRedirectTest < ActionDispatch::IntegrationTest
assert_equal "bar", response.body
end
end
+
+class TestRecognizePath < ActionDispatch::IntegrationTest
+ class PageConstraint
+ attr_reader :key, :pattern
+
+ def initialize(key, pattern)
+ @key = key
+ @pattern = pattern
+ end
+
+ def matches?(request)
+ request.path_parameters[key] =~ pattern
+ end
+ end
+
+ stub_controllers do |routes|
+ Routes = routes
+ routes.draw do
+ get "/hash/:foo", to: "pages#show", constraints: { foo: /foo/ }
+ get "/hash/:bar", to: "pages#show", constraints: { bar: /bar/ }
+
+ get "/proc/:foo", to: "pages#show", constraints: proc { |r| r.path_parameters[:foo] =~ /foo/ }
+ get "/proc/:bar", to: "pages#show", constraints: proc { |r| r.path_parameters[:bar] =~ /bar/ }
+
+ get "/class/:foo", to: "pages#show", constraints: PageConstraint.new(:foo, /foo/)
+ get "/class/:bar", to: "pages#show", constraints: PageConstraint.new(:bar, /bar/)
+ end
+ end
+
+ APP = build_app Routes
+ def app
+ APP
+ end
+
+ def test_hash_constraints_dont_leak_between_routes
+ expected_params = { controller: "pages", action: "show", bar: "bar" }
+ actual_params = recognize_path("/hash/bar")
+
+ assert_equal expected_params, actual_params
+ end
+
+ def test_proc_constraints_dont_leak_between_routes
+ expected_params = { controller: "pages", action: "show", bar: "bar" }
+ actual_params = recognize_path("/proc/bar")
+
+ assert_equal expected_params, actual_params
+ end
+
+ def test_class_constraints_dont_leak_between_routes
+ expected_params = { controller: "pages", action: "show", bar: "bar" }
+ actual_params = recognize_path("/class/bar")
+
+ assert_equal expected_params, actual_params
+ end
+
+ private
+
+ def recognize_path(*args)
+ Routes.recognize_path(*args)
+ end
+end
+
+class TestRelativeUrlRootGeneration < ActionDispatch::IntegrationTest
+ config = ActionDispatch::Routing::RouteSet::Config.new("/blog", false)
+
+ stub_controllers(config) do |routes|
+ Routes = routes
+
+ routes.draw do
+ get "/", to: "posts#index", as: :posts
+ get "/:id", to: "posts#show", as: :post
+ end
+ end
+
+ include Routes.url_helpers
+
+ APP = build_app Routes
+
+ def app
+ APP
+ end
+
+ def test_url_helpers
+ assert_equal "/blog/", posts_path({})
+ assert_equal "/blog/", Routes.url_helpers.posts_path({})
+
+ assert_equal "/blog/1", post_path(id: "1")
+ assert_equal "/blog/1", Routes.url_helpers.post_path(id: "1")
+ end
+
+ def test_optimized_url_helpers
+ assert_equal "/blog/", posts_path
+ assert_equal "/blog/", Routes.url_helpers.posts_path
+
+ assert_equal "/blog/1", post_path("1")
+ assert_equal "/blog/1", Routes.url_helpers.post_path("1")
+ end
+end
diff --git a/actionpack/test/dispatch/runner_test.rb b/actionpack/test/dispatch/runner_test.rb
index b76bf4a320..f16c7963af 100644
--- a/actionpack/test/dispatch/runner_test.rb
+++ b/actionpack/test/dispatch/runner_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
class RunnerTest < ActiveSupport::TestCase
diff --git a/actionpack/test/dispatch/session/abstract_store_test.rb b/actionpack/test/dispatch/session/abstract_store_test.rb
index fd4d359cf8..47616db15a 100644
--- a/actionpack/test/dispatch/session/abstract_store_test.rb
+++ b/actionpack/test/dispatch/session/abstract_store_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
require "action_dispatch/middleware/session/abstract_store"
diff --git a/actionpack/test/dispatch/session/cache_store_test.rb b/actionpack/test/dispatch/session/cache_store_test.rb
index 859059063f..06e67fac9f 100644
--- a/actionpack/test/dispatch/session/cache_store_test.rb
+++ b/actionpack/test/dispatch/session/cache_store_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
require "fixtures/session_autoload_test/session_autoload_test/foo"
diff --git a/actionpack/test/dispatch/session/cookie_store_test.rb b/actionpack/test/dispatch/session/cookie_store_test.rb
index 63dfc07c0d..e34426a471 100644
--- a/actionpack/test/dispatch/session/cookie_store_test.rb
+++ b/actionpack/test/dispatch/session/cookie_store_test.rb
@@ -1,14 +1,21 @@
+# frozen_string_literal: true
+
require "abstract_unit"
require "stringio"
require "active_support/key_generator"
+require "active_support/messages/rotation_configuration"
class CookieStoreTest < ActionDispatch::IntegrationTest
SessionKey = "_myapp_session"
SessionSecret = "b3c631c314c0bbca50c1b2843150fe33"
- Generator = ActiveSupport::LegacyKeyGenerator.new(SessionSecret)
+ SessionSalt = "authenticated encrypted cookie"
+
+ Generator = ActiveSupport::KeyGenerator.new(SessionSecret, iterations: 1000)
+ Rotations = ActiveSupport::Messages::RotationConfiguration.new
- Verifier = ActiveSupport::MessageVerifier.new(SessionSecret, digest: "SHA1")
- SignedBar = Verifier.generate(foo: "bar", session_id: SecureRandom.hex(16))
+ Encryptor = ActiveSupport::MessageEncryptor.new(
+ Generator.generate_key(SessionSalt, 32), cipher: "aes-256-gcm", serializer: Marshal
+ )
class TestController < ActionController::Base
def no_session_access
@@ -21,7 +28,7 @@ class CookieStoreTest < ActionDispatch::IntegrationTest
def set_session_value
session[:foo] = "bar"
- render plain: Rack::Utils.escape(Verifier.generate(session.to_hash))
+ render body: nil
end
def get_session_value
@@ -63,19 +70,35 @@ class CookieStoreTest < ActionDispatch::IntegrationTest
end
end
+ def parse_cookie_from_header
+ cookie_matches = headers["Set-Cookie"].match(/#{SessionKey}=([^;]+)/)
+ cookie_matches && cookie_matches[1]
+ end
+
+ def assert_session_cookie(cookie_string, contents)
+ assert_includes headers["Set-Cookie"], cookie_string
+
+ session_value = parse_cookie_from_header
+ session_data = Encryptor.decrypt_and_verify(Rack::Utils.unescape(session_value)) rescue nil
+
+ assert_not_nil session_data, "session failed to decrypt"
+ assert_equal session_data.slice(*contents.keys), contents
+ end
+
def test_setting_session_value
with_test_route_set do
get "/set_session_value"
+
assert_response :success
- assert_equal "_myapp_session=#{response.body}; path=/; HttpOnly",
- headers["Set-Cookie"]
+ assert_session_cookie "path=/; HttpOnly", "foo" => "bar"
end
end
def test_getting_session_value
with_test_route_set do
- cookies[SessionKey] = SignedBar
+ get "/set_session_value"
get "/get_session_value"
+
assert_response :success
assert_equal 'foo: "bar"', response.body
end
@@ -83,8 +106,9 @@ class CookieStoreTest < ActionDispatch::IntegrationTest
def test_getting_session_id
with_test_route_set do
- cookies[SessionKey] = SignedBar
+ get "/set_session_value"
get "/persistent_session_id"
+
assert_response :success
assert_equal 32, response.body.size
session_id = response.body
@@ -97,8 +121,12 @@ class CookieStoreTest < ActionDispatch::IntegrationTest
def test_disregards_tampered_sessions
with_test_route_set do
- cookies[SessionKey] = "BAh7BjoIZm9vIghiYXI%3D--123456780"
+ encryptor = ActiveSupport::MessageEncryptor.new("A" * 32, cipher: "aes-256-gcm", serializer: Marshal)
+
+ cookies[SessionKey] = encryptor.encrypt_and_sign("foo" => "bar", "session_id" => "abc")
+
get "/get_session_value"
+
assert_response :success
assert_equal "foo: nil", response.body
end
@@ -126,19 +154,19 @@ class CookieStoreTest < ActionDispatch::IntegrationTest
def test_does_set_secure_cookies_over_https
with_test_route_set(secure: true) do
get "/set_session_value", headers: { "HTTPS" => "on" }
+
assert_response :success
- assert_equal "_myapp_session=#{response.body}; path=/; secure; HttpOnly",
- headers["Set-Cookie"]
+ assert_session_cookie "path=/; secure; HttpOnly", "foo" => "bar"
end
end
# {:foo=>#<SessionAutoloadTest::Foo bar:"baz">, :session_id=>"ce8b0752a6ab7c7af3cdb8a80e6b9e46"}
- SignedSerializedCookie = "BAh7BzoIZm9vbzodU2Vzc2lvbkF1dG9sb2FkVGVzdDo6Rm9vBjoJQGJhciIIYmF6Og9zZXNzaW9uX2lkIiVjZThiMDc1MmE2YWI3YzdhZjNjZGI4YTgwZTZiOWU0Ng==--2bf3af1ae8bd4e52b9ac2099258ace0c380e601c"
+ EncryptedSerializedCookie = "9RZ2Fij0qLveUwM4s+CCjGqhpjyUC8jiBIf/AiBr9M3TB8xh2vQZtvSOMfN3uf6oYbbpIDHAcOFIEl69FcW1ozQYeSrCLonYCazoh34ZdYskIQfGwCiSYleVXG1OD9Z4jFqeVArw4Ewm0paOOPLbN1rc6A==--I359v/KWdZ1ok0ey--JFFhuPOY7WUo6tB/eP05Aw=="
def test_deserializes_unloaded_classes_on_get_id
with_test_route_set do
with_autoload_path "session_autoload_test" do
- cookies[SessionKey] = SignedSerializedCookie
+ cookies[SessionKey] = EncryptedSerializedCookie
get "/get_session_id"
assert_response :success
assert_equal "id: ce8b0752a6ab7c7af3cdb8a80e6b9e46", response.body, "should auto-load unloaded class"
@@ -149,7 +177,7 @@ class CookieStoreTest < ActionDispatch::IntegrationTest
def test_deserializes_unloaded_classes_on_get_value
with_test_route_set do
with_autoload_path "session_autoload_test" do
- cookies[SessionKey] = SignedSerializedCookie
+ cookies[SessionKey] = EncryptedSerializedCookie
get "/get_session_value"
assert_response :success
assert_equal 'foo: #<SessionAutoloadTest::Foo bar:"baz">', response.body, "should auto-load unloaded class"
@@ -188,8 +216,7 @@ class CookieStoreTest < ActionDispatch::IntegrationTest
get "/set_session_value"
assert_response :success
session_payload = response.body
- assert_equal "_myapp_session=#{response.body}; path=/; HttpOnly",
- headers["Set-Cookie"]
+ assert_session_cookie "path=/; HttpOnly", "foo" => "bar"
get "/call_reset_session"
assert_response :success
@@ -207,8 +234,7 @@ class CookieStoreTest < ActionDispatch::IntegrationTest
with_test_route_set do
get "/set_session_value"
assert_response :success
- assert_equal "_myapp_session=#{response.body}; path=/; HttpOnly",
- headers["Set-Cookie"]
+ assert_session_cookie "path=/; HttpOnly", "foo" => "bar"
get "/get_class_after_reset_session"
assert_response :success
@@ -230,8 +256,7 @@ class CookieStoreTest < ActionDispatch::IntegrationTest
with_test_route_set do
get "/set_session_value"
assert_response :success
- assert_equal "_myapp_session=#{response.body}; path=/; HttpOnly",
- headers["Set-Cookie"]
+ assert_session_cookie "path=/; HttpOnly", "foo" => "bar"
get "/call_session_clear"
assert_response :success
@@ -244,7 +269,7 @@ class CookieStoreTest < ActionDispatch::IntegrationTest
def test_persistent_session_id
with_test_route_set do
- cookies[SessionKey] = SignedBar
+ get "/set_session_value"
get "/persistent_session_id"
assert_response :success
assert_equal 32, response.body.size
@@ -259,8 +284,7 @@ class CookieStoreTest < ActionDispatch::IntegrationTest
def test_setting_session_id_to_nil_is_respected
with_test_route_set do
- cookies[SessionKey] = SignedBar
-
+ get "/set_session_value"
get "/get_session_id"
sid = response.body
assert_equal 36, sid.size
@@ -274,31 +298,53 @@ class CookieStoreTest < ActionDispatch::IntegrationTest
with_test_route_set(expire_after: 5.hours) do
# First request accesses the session
time = Time.local(2008, 4, 24)
- cookie_body = nil
Time.stub :now, time do
expected_expiry = (time + 5.hours).gmtime.strftime("%a, %d %b %Y %H:%M:%S -0000")
- cookies[SessionKey] = SignedBar
-
get "/set_session_value"
- assert_response :success
- cookie_body = response.body
- assert_equal "_myapp_session=#{cookie_body}; path=/; expires=#{expected_expiry}; HttpOnly",
- headers["Set-Cookie"]
+ assert_response :success
+ assert_session_cookie "path=/; expires=#{expected_expiry}; HttpOnly", "foo" => "bar"
end
# Second request does not access the session
- time = Time.local(2008, 4, 25)
+ time = time + 3.hours
Time.stub :now, time do
expected_expiry = (time + 5.hours).gmtime.strftime("%a, %d %b %Y %H:%M:%S -0000")
get "/no_session_access"
+
assert_response :success
+ assert_session_cookie "path=/; expires=#{expected_expiry}; HttpOnly", "foo" => "bar"
+ end
+ end
+ end
+
+ def test_session_store_with_expire_after_does_not_accept_expired_session
+ with_test_route_set(expire_after: 5.hours) do
+ # First request accesses the session
+ time = Time.local(2017, 11, 12)
+
+ Time.stub :now, time do
+ expected_expiry = (time + 5.hours).gmtime.strftime("%a, %d %b %Y %H:%M:%S -0000")
+
+ get "/set_session_value"
+ get "/get_session_value"
- assert_equal "_myapp_session=#{cookie_body}; path=/; expires=#{expected_expiry}; HttpOnly",
- headers["Set-Cookie"]
+ assert_response :success
+ assert_equal 'foo: "bar"', response.body
+ assert_session_cookie "path=/; expires=#{expected_expiry}; HttpOnly", "foo" => "bar"
+ end
+
+ # Second request is beyond the expiry time and the session is invalidated
+ time += 5.hours + 1.minute
+
+ Time.stub :now, time do
+ get "/get_session_value"
+
+ assert_response :success
+ assert_equal "foo: nil", response.body
end
end
end
@@ -338,7 +384,15 @@ class CookieStoreTest < ActionDispatch::IntegrationTest
def get(path, *args)
args[0] ||= {}
args[0][:headers] ||= {}
- args[0][:headers]["action_dispatch.key_generator"] ||= Generator
+ args[0][:headers].tap do |config|
+ config["action_dispatch.secret_key_base"] = SessionSecret
+ config["action_dispatch.authenticated_encrypted_cookie_salt"] = SessionSalt
+ config["action_dispatch.use_authenticated_cookie_encryption"] = true
+
+ config["action_dispatch.key_generator"] ||= Generator
+ config["action_dispatch.cookies_rotations"] ||= Rotations
+ end
+
super(path, *args)
end
diff --git a/actionpack/test/dispatch/session/mem_cache_store_test.rb b/actionpack/test/dispatch/session/mem_cache_store_test.rb
index 121e9ebef7..9b51ee1cad 100644
--- a/actionpack/test/dispatch/session/mem_cache_store_test.rb
+++ b/actionpack/test/dispatch/session/mem_cache_store_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
require "securerandom"
diff --git a/actionpack/test/dispatch/session/test_session_test.rb b/actionpack/test/dispatch/session/test_session_test.rb
index 0bf3a8b3ee..e90162a5fe 100644
--- a/actionpack/test/dispatch/session/test_session_test.rb
+++ b/actionpack/test/dispatch/session/test_session_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
require "stringio"
diff --git a/actionpack/test/dispatch/show_exceptions_test.rb b/actionpack/test/dispatch/show_exceptions_test.rb
index 3513534d72..b69071b44b 100644
--- a/actionpack/test/dispatch/show_exceptions_test.rb
+++ b/actionpack/test/dispatch/show_exceptions_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
class ShowExceptionsTest < ActionDispatch::IntegrationTest
diff --git a/actionpack/test/dispatch/ssl_test.rb b/actionpack/test/dispatch/ssl_test.rb
index 757e26973f..8ac9502af9 100644
--- a/actionpack/test/dispatch/ssl_test.rb
+++ b/actionpack/test/dispatch/ssl_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
class SSLTest < ActionDispatch::IntegrationTest
diff --git a/actionpack/test/dispatch/static_test.rb b/actionpack/test/dispatch/static_test.rb
index 3082d1072b..0bdff68692 100644
--- a/actionpack/test/dispatch/static_test.rb
+++ b/actionpack/test/dispatch/static_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
require "zlib"
@@ -29,7 +31,7 @@ module StaticTests
end
def test_handles_urls_with_ascii_8bit
- assert_equal "Hello, World!", get("/doorkeeper%E3E4".force_encoding("ASCII-8BIT")).body
+ assert_equal "Hello, World!", get("/doorkeeper%E3E4".dup.force_encoding("ASCII-8BIT")).body
end
def test_handles_urls_with_ascii_8bit_on_win_31j
@@ -37,7 +39,7 @@ module StaticTests
Encoding.default_internal = "Windows-31J"
Encoding.default_external = "Windows-31J"
end
- assert_equal "Hello, World!", get("/doorkeeper%E3E4".force_encoding("ASCII-8BIT")).body
+ assert_equal "Hello, World!", get("/doorkeeper%E3E4".dup.force_encoding("ASCII-8BIT")).body
end
def test_handles_urls_with_null_byte
diff --git a/actionpack/test/dispatch/system_testing/browser_test.rb b/actionpack/test/dispatch/system_testing/browser_test.rb
deleted file mode 100644
index b0ad309492..0000000000
--- a/actionpack/test/dispatch/system_testing/browser_test.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require "abstract_unit"
-require "action_dispatch/system_testing/browser"
-
-class BrowserTest < ActiveSupport::TestCase
- test "initializing the browser" do
- browser = ActionDispatch::SystemTesting::Browser.new(:chrome, [ 1400, 1400 ])
- assert_equal :chrome, browser.instance_variable_get(:@name)
- assert_equal [ 1400, 1400 ], browser.instance_variable_get(:@screen_size)
- end
-end
diff --git a/actionpack/test/dispatch/system_testing/driver_test.rb b/actionpack/test/dispatch/system_testing/driver_test.rb
index f0ebdb38db..fcdaf7fb4c 100644
--- a/actionpack/test/dispatch/system_testing/driver_test.rb
+++ b/actionpack/test/dispatch/system_testing/driver_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
require "action_dispatch/system_testing/driver"
@@ -6,4 +8,46 @@ class DriverTest < ActiveSupport::TestCase
driver = ActionDispatch::SystemTesting::Driver.new(:selenium)
assert_equal :selenium, driver.instance_variable_get(:@name)
end
+
+ test "initializing the driver with a browser" do
+ driver = ActionDispatch::SystemTesting::Driver.new(:selenium, using: :chrome, screen_size: [1400, 1400], options: { url: "http://example.com/wd/hub" })
+ assert_equal :selenium, driver.instance_variable_get(:@name)
+ assert_equal :chrome, driver.instance_variable_get(:@browser)
+ assert_equal [1400, 1400], driver.instance_variable_get(:@screen_size)
+ assert_equal ({ url: "http://example.com/wd/hub" }), driver.instance_variable_get(:@options)
+ end
+
+ test "initializing the driver with a headless chrome" do
+ 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)
+ assert_equal [1400, 1400], driver.instance_variable_get(:@screen_size)
+ assert_equal ({ url: "http://example.com/wd/hub" }), driver.instance_variable_get(:@options)
+ end
+
+ test "initializing the driver with a headless firefox" do
+ 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)
+ assert_equal [1400, 1400], driver.instance_variable_get(:@screen_size)
+ assert_equal ({ url: "http://example.com/wd/hub" }), driver.instance_variable_get(:@options)
+ end
+
+ test "initializing the driver with a poltergeist" do
+ driver = ActionDispatch::SystemTesting::Driver.new(:poltergeist, screen_size: [1400, 1400], options: { js_errors: false })
+ assert_equal :poltergeist, driver.instance_variable_get(:@name)
+ assert_equal [1400, 1400], driver.instance_variable_get(:@screen_size)
+ assert_equal ({ js_errors: false }), driver.instance_variable_get(:@options)
+ end
+
+ test "initializing the driver with a webkit" do
+ driver = ActionDispatch::SystemTesting::Driver.new(:webkit, screen_size: [1400, 1400], options: { skip_image_loading: true })
+ assert_equal :webkit, driver.instance_variable_get(:@name)
+ assert_equal [1400, 1400], driver.instance_variable_get(:@screen_size)
+ assert_equal ({ skip_image_loading: true }), driver.instance_variable_get(:@options)
+ end
+
+ test "registerable? returns false if driver is rack_test" do
+ assert_not ActionDispatch::SystemTesting::Driver.new(:rack_test).send(:registerable?)
+ 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 d6b501b3ac..264844fc7d 100644
--- a/actionpack/test/dispatch/system_testing/screenshot_helper_test.rb
+++ b/actionpack/test/dispatch/system_testing/screenshot_helper_test.rb
@@ -1,53 +1,81 @@
+# frozen_string_literal: true
+
require "abstract_unit"
require "action_dispatch/system_testing/test_helpers/screenshot_helper"
require "capybara/dsl"
class ScreenshotHelperTest < ActiveSupport::TestCase
test "image path is saved in tmp directory" do
- new_test = ActionDispatch::SystemTestCase.new("x")
+ new_test = DrivenBySeleniumWithChrome.new("x")
- assert_equal "tmp/screenshots/x.png", new_test.send(:image_path)
+ Rails.stub :root, Pathname.getwd do
+ assert_equal "tmp/screenshots/x.png", new_test.send(:image_path)
+ end
end
test "image path includes failures text if test did not pass" do
- new_test = ActionDispatch::SystemTestCase.new("x")
+ new_test = DrivenBySeleniumWithChrome.new("x")
- new_test.stub :passed?, false do
- assert_equal "tmp/screenshots/failures_x.png", new_test.send(:image_path)
+ Rails.stub :root, Pathname.getwd do
+ new_test.stub :passed?, false do
+ assert_equal "tmp/screenshots/failures_x.png", new_test.send(:image_path)
+ end
end
end
test "image path does not include failures text if test skipped" do
- new_test = ActionDispatch::SystemTestCase.new("x")
+ new_test = DrivenBySeleniumWithChrome.new("x")
- new_test.stub :passed?, false do
- new_test.stub :skipped?, true do
- assert_equal "tmp/screenshots/x.png", new_test.send(:image_path)
+ Rails.stub :root, Pathname.getwd do
+ new_test.stub :passed?, false do
+ new_test.stub :skipped?, true do
+ assert_equal "tmp/screenshots/x.png", new_test.send(:image_path)
+ end
end
end
end
- test "rack_test driver does not support screenshot" do
+ 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_driver = Capybara.current_driver
- Capybara.current_driver = :rack_test
+ original_output_type = ENV["RAILS_SYSTEM_TESTING_SCREENSHOT"]
+ ENV["RAILS_SYSTEM_TESTING_SCREENSHOT"] = "artifact"
+
+ new_test = DrivenBySeleniumWithChrome.new("x")
+
+ assert_equal "artifact", new_test.send(:output_type)
- new_test = ActionDispatch::SystemTestCase.new("x")
- assert_not new_test.send(:supports_screenshot?)
+ 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
+ end
ensure
- Capybara.current_driver = original_driver
+ ENV["RAILS_SYSTEM_TESTING_SCREENSHOT"] = original_output_type
end
end
- test "selenium driver supports screenshot" do
- begin
- original_driver = Capybara.current_driver
- Capybara.current_driver = :selenium
+ test "image path returns the relative path from current directory" do
+ new_test = DrivenBySeleniumWithChrome.new("x")
- new_test = ActionDispatch::SystemTestCase.new("x")
- assert new_test.send(:supports_screenshot?)
- ensure
- Capybara.current_driver = original_driver
+ Rails.stub :root, Pathname.getwd.join("..") do
+ assert_equal "../tmp/screenshots/x.png", new_test.send(:image_path)
end
end
end
+
+class RackTestScreenshotsTest < DrivenByRackTest
+ test "rack_test driver does not support screenshot" do
+ assert_not self.send(:supports_screenshot?)
+ end
+end
+
+class SeleniumScreenshotsTest < DrivenBySeleniumWithChrome
+ test "selenium driver supports screenshot" do
+ assert self.send(:supports_screenshot?)
+ end
+end
diff --git a/actionpack/test/dispatch/system_testing/server_test.rb b/actionpack/test/dispatch/system_testing/server_test.rb
index 10412d6367..95e411faf4 100644
--- a/actionpack/test/dispatch/system_testing/server_test.rb
+++ b/actionpack/test/dispatch/system_testing/server_test.rb
@@ -1,17 +1,32 @@
+# frozen_string_literal: true
+
require "abstract_unit"
require "capybara/dsl"
require "action_dispatch/system_testing/server"
class ServerTest < ActiveSupport::TestCase
setup do
+ @old_capybara_server = Capybara.server
+ end
+
+ test "port is always included" do
ActionDispatch::SystemTesting::Server.new.run
+ assert Capybara.always_include_port, "expected Capybara.always_include_port to be true"
end
- test "initializing the server port" do
- assert_includes Capybara.servers, :rails_puma
+ test "server is changed from `default` to `puma`" do
+ Capybara.server = :default
+ ActionDispatch::SystemTesting::Server.new.run
+ refute_equal Capybara.server, Capybara.servers[:default]
end
- test "port is always included" do
- assert Capybara.always_include_port, "expected Capybara.always_include_port to be true"
+ test "server is not changed to `puma` when is different than default" do
+ Capybara.server = :webrick
+ ActionDispatch::SystemTesting::Server.new.run
+ assert_equal Capybara.server, Capybara.servers[:webrick]
+ end
+
+ teardown do
+ Capybara.server = @old_capybara_server
end
end
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 ff01d6739a..b078a5abc5 100644
--- a/actionpack/test/dispatch/system_testing/system_test_case_test.rb
+++ b/actionpack/test/dispatch/system_testing/system_test_case_test.rb
@@ -1,12 +1,14 @@
+# frozen_string_literal: true
+
require "abstract_unit"
-class DrivenByCaseTestTest < ActiveSupport::TestCase
- test "selenium? returns false if driver is poltergeist" do
- assert_not ActionDispatch::SystemTestCase.selenium?(:poltergeist)
+class SetDriverToRackTestTest < DrivenByRackTest
+ test "uses rack_test" do
+ assert_equal :rack_test, Capybara.current_driver
end
end
-class DrivenByRackTestTest < ActionDispatch::SystemTestCase
+class OverrideSeleniumSubclassToRackTestTest < DrivenBySeleniumWithChrome
driven_by :rack_test
test "uses rack_test" do
@@ -14,10 +16,69 @@ class DrivenByRackTestTest < ActionDispatch::SystemTestCase
end
end
-class DrivenBySeleniumWithChromeTest < ActionDispatch::SystemTestCase
- driven_by :selenium, using: :chrome
-
+class SetDriverToSeleniumTest < DrivenBySeleniumWithChrome
test "uses selenium" do
- assert_equal :chrome, Capybara.current_driver
+ assert_equal :selenium, Capybara.current_driver
+ end
+end
+
+class SetDriverToSeleniumHeadlessChromeTest < DrivenBySeleniumWithHeadlessChrome
+ test "uses selenium headless chrome" do
+ assert_equal :selenium, Capybara.current_driver
+ end
+end
+
+class SetDriverToSeleniumHeadlessFirefoxTest < DrivenBySeleniumWithHeadlessFirefox
+ test "uses selenium headless firefox" do
+ assert_equal :selenium, Capybara.current_driver
+ end
+end
+
+class SetHostTest < DrivenByRackTest
+ test "sets default host" do
+ assert_equal "http://127.0.0.1", Capybara.app_host
+ end
+
+ test "overrides host" do
+ host! "http://example.com"
+
+ 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_request_test.rb b/actionpack/test/dispatch/test_request_test.rb
index 85a6df4975..e56537d80b 100644
--- a/actionpack/test/dispatch/test_request_test.rb
+++ b/actionpack/test/dispatch/test_request_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
class TestRequestTest < ActiveSupport::TestCase
diff --git a/actionpack/test/dispatch/test_response_test.rb b/actionpack/test/dispatch/test_response_test.rb
index 98eafb5119..f0b8f7785d 100644
--- a/actionpack/test/dispatch/test_response_test.rb
+++ b/actionpack/test/dispatch/test_response_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
class TestResponseTest < ActiveSupport::TestCase
@@ -25,4 +27,11 @@ 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 51680216e4..24c7135c7e 100644
--- a/actionpack/test/dispatch/uploaded_file_test.rb
+++ b/actionpack/test/dispatch/uploaded_file_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
module ActionDispatch
@@ -13,6 +15,12 @@ module ActionDispatch
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)
+ 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)
assert_equal "UTF-8", uf.original_filename.encoding.to_s
diff --git a/actionpack/test/dispatch/url_generation_test.rb b/actionpack/test/dispatch/url_generation_test.rb
index 5d81fd6834..aef9351de1 100644
--- a/actionpack/test/dispatch/url_generation_test.rb
+++ b/actionpack/test/dispatch/url_generation_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
module TestUrlGeneration