aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/test/controller
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/test/controller')
-rw-r--r--actionpack/test/controller/api/renderers_test.rb22
-rw-r--r--actionpack/test/controller/caching_test.rb34
-rw-r--r--actionpack/test/controller/force_ssl_test.rb9
-rw-r--r--actionpack/test/controller/http_basic_authentication_test.rb21
-rw-r--r--actionpack/test/controller/integration_test.rb68
-rw-r--r--actionpack/test/controller/live_stream_test.rb11
-rw-r--r--actionpack/test/controller/metal/renderers_test.rb42
-rw-r--r--actionpack/test/controller/mime/respond_to_test.rb93
-rw-r--r--actionpack/test/controller/new_base/bare_metal_test.rb16
-rw-r--r--actionpack/test/controller/parameters/accessors_test.rb72
-rw-r--r--actionpack/test/controller/parameters/always_permitted_parameters_test.rb6
-rw-r--r--actionpack/test/controller/parameters/parameters_permit_test.rb39
-rw-r--r--actionpack/test/controller/redirect_test.rb2
-rw-r--r--actionpack/test/controller/render_other_test.rb24
-rw-r--r--actionpack/test/controller/render_test.rb89
-rw-r--r--actionpack/test/controller/renderers_test.rb90
-rw-r--r--actionpack/test/controller/request_forgery_protection_test.rb198
-rw-r--r--actionpack/test/controller/required_params_test.rb13
-rw-r--r--actionpack/test/controller/test_case_test.rb10
-rw-r--r--actionpack/test/controller/webservice_test.rb2
20 files changed, 780 insertions, 81 deletions
diff --git a/actionpack/test/controller/api/renderers_test.rb b/actionpack/test/controller/api/renderers_test.rb
index 9405538833..911a8144b2 100644
--- a/actionpack/test/controller/api/renderers_test.rb
+++ b/actionpack/test/controller/api/renderers_test.rb
@@ -19,6 +19,14 @@ class RenderersApiController < ActionController::API
def two
render xml: Model.new
end
+
+ def plain
+ render plain: 'Hi from plain', status: 500
+ end
+
+ def text
+ render text: 'Hi from text', status: 500
+ end
end
class RenderersApiTest < ActionController::TestCase
@@ -35,4 +43,18 @@ class RenderersApiTest < ActionController::TestCase
assert_response :success
assert_equal({ a: 'b' }.to_xml, @response.body)
end
+
+ def test_render_plain
+ get :plain
+ assert_response :internal_server_error
+ assert_equal('Hi from plain', @response.body)
+ end
+
+ def test_render_text
+ assert_deprecated do
+ get :text
+ end
+ assert_response :internal_server_error
+ assert_equal('Hi from text', @response.body)
+ end
end
diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb
index d19b3810c2..754ac144cc 100644
--- a/actionpack/test/controller/caching_test.rb
+++ b/actionpack/test/controller/caching_test.rb
@@ -172,6 +172,9 @@ class FunctionalCachingController < CachingController
def fragment_cached_without_digest
end
+
+ def fragment_cached_with_options
+ end
end
class FunctionalFragmentCachingTest < ActionController::TestCase
@@ -215,6 +218,15 @@ CACHED
assert_equal "<p>ERB</p>", @store.read("views/nodigest")
end
+ def test_fragment_caching_with_options
+ get :fragment_cached_with_options
+ assert_response :success
+ expected_body = "<body>\n<p>ERB</p>\n</body>\n"
+
+ assert_equal expected_body, @response.body
+ assert_equal "<p>ERB</p>", @store.read("views/with_options")
+ end
+
def test_render_inline_before_fragment_caching
get :inline_fragment_cached
assert_response :success
@@ -369,14 +381,14 @@ class CollectionCacheController < ActionController::Base
render 'index'
end
- def index_explicit_render
+ def index_explicit_render_in_controller
@customers = [Customer.new('david', 1)]
- render partial: 'customers/customer', collection: @customers
+ render partial: 'customers/customer', collection: @customers, cached: true
end
def index_with_comment
@customers = [Customer.new('david', 1)]
- render partial: 'customers/commented_customer', collection: @customers, as: :customer
+ render partial: 'customers/commented_customer', collection: @customers, as: :customer, cached: true
end
end
@@ -387,12 +399,13 @@ class AutomaticCollectionCacheTest < ActionController::TestCase
@controller.perform_caching = true
@controller.partial_rendered_times = 0
@controller.cache_store = ActiveSupport::Cache::MemoryStore.new
- ActionView::PartialRenderer.collection_cache = @controller.cache_store
+ ActionView::PartialRenderer.collection_cache = ActiveSupport::Cache::MemoryStore.new
end
def test_collection_fetches_cached_views
get :index
assert_equal 1, @controller.partial_rendered_times
+ assert_customer_cached 'david/1', 'david, 1'
get :index
assert_equal 1, @controller.partial_rendered_times
@@ -400,13 +413,16 @@ class AutomaticCollectionCacheTest < ActionController::TestCase
def test_preserves_order_when_reading_from_cache_plus_rendering
get :index, params: { id: 2 }
- get :index_ordered
+ assert_equal 1, @controller.partial_rendered_times
+ assert_select ':root', 'david, 2'
+ get :index_ordered
+ assert_equal 3, @controller.partial_rendered_times
assert_select ':root', "david, 1\n david, 2\n david, 3"
end
def test_explicit_render_call_with_options
- get :index_explicit_render
+ get :index_explicit_render_in_controller
assert_select ':root', "david, 1"
end
@@ -418,6 +434,12 @@ class AutomaticCollectionCacheTest < ActionController::TestCase
get :index_with_comment
assert_equal 1, @controller.partial_rendered_times
end
+
+ private
+ def assert_customer_cached(key, content)
+ assert_match content,
+ ActionView::PartialRenderer.collection_cache.read("views/#{key}/7c228ab609f0baf0b1f2367469210937")
+ end
end
class FragmentCacheKeyTestController < CachingController
diff --git a/actionpack/test/controller/force_ssl_test.rb b/actionpack/test/controller/force_ssl_test.rb
index 22f1cc7c22..03a9c9ae78 100644
--- a/actionpack/test/controller/force_ssl_test.rb
+++ b/actionpack/test/controller/force_ssl_test.rb
@@ -322,3 +322,12 @@ class RedirectToSSLTest < ActionController::TestCase
assert_equal 'ihaz', response.body
end
end
+
+class ForceSSLControllerLevelTest < ActionController::TestCase
+ def test_no_redirect_websocket_ssl_request
+ request.env['rack.url_scheme'] = 'wss'
+ request.env['Upgrade'] = 'websocket'
+ get :cheeseburger
+ assert_response 200
+ end
+end
diff --git a/actionpack/test/controller/http_basic_authentication_test.rb b/actionpack/test/controller/http_basic_authentication_test.rb
index 0a5e5402b9..adcf259317 100644
--- a/actionpack/test/controller/http_basic_authentication_test.rb
+++ b/actionpack/test/controller/http_basic_authentication_test.rb
@@ -5,6 +5,7 @@ class HttpBasicAuthenticationTest < ActionController::TestCase
before_action :authenticate, only: :index
before_action :authenticate_with_request, only: :display
before_action :authenticate_long_credentials, only: :show
+ before_action :auth_with_special_chars, only: :special_creds
http_basic_authenticate_with :name => "David", :password => "Goliath", :only => :search
@@ -20,6 +21,10 @@ class HttpBasicAuthenticationTest < ActionController::TestCase
render plain: 'Only for loooooong credentials'
end
+ def special_creds
+ render plain: 'Only for special credentials'
+ end
+
def search
render plain: 'All inline'
end
@@ -40,6 +45,12 @@ class HttpBasicAuthenticationTest < ActionController::TestCase
end
end
+ def auth_with_special_chars
+ authenticate_or_request_with_http_basic do |username, password|
+ username == 'login!@#$%^&*()_+{}[];"\',./<>?`~ \n\r\t' && password == 'pwd:!@#$%^&*()_+{}[];"\',./<>?`~ \n\r\t'
+ end
+ end
+
def authenticate_long_credentials
authenticate_or_request_with_http_basic do |username, password|
username == '1234567890123456789012345678901234567890' && password == '1234567890123456789012345678901234567890'
@@ -100,7 +111,7 @@ class HttpBasicAuthenticationTest < ActionController::TestCase
assert_no_match(/\n/, result)
end
- test "succesful authentication with uppercase authorization scheme" do
+ test "successful authentication with uppercase authorization scheme" do
@request.env['HTTP_AUTHORIZATION'] = "BASIC #{::Base64.encode64("lifo:world")}"
get :index
@@ -133,6 +144,14 @@ class HttpBasicAuthenticationTest < ActionController::TestCase
assert_equal 'Definitely Maybe', @response.body
end
+ test "authentication request with valid credential special chars" do
+ @request.env['HTTP_AUTHORIZATION'] = encode_credentials('login!@#$%^&*()_+{}[];"\',./<>?`~ \n\r\t', 'pwd:!@#$%^&*()_+{}[];"\',./<>?`~ \n\r\t')
+ get :special_creds
+
+ assert_response :success
+ assert_equal 'Only for special credentials', @response.body
+ end
+
test "authenticate with class method" do
@request.env['HTTP_AUTHORIZATION'] = encode_credentials('David', 'Goliath')
get :search
diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb
index d0a1d1285f..6277407ff7 100644
--- a/actionpack/test/controller/integration_test.rb
+++ b/actionpack/test/controller/integration_test.rb
@@ -390,7 +390,7 @@ class IntegrationTestUsesCorrectClass < ActionDispatch::IntegrationTest
reset!
%w( get post head patch put delete ).each do |verb|
- assert_nothing_raised("'#{verb}' should use integration test methods") { __send__(verb, '/') }
+ assert_nothing_raised { __send__(verb, '/') }
end
end
end
@@ -1126,3 +1126,69 @@ class IntegrationRequestsWithSessionSetup < ActionDispatch::IntegrationTest
assert_equal({"user_name"=>"david"}, cookies.to_hash)
end
end
+
+class IntegrationRequestEncodersTest < ActionDispatch::IntegrationTest
+ class FooController < ActionController::Base
+ def foos_json
+ render json: params.permit(:foo)
+ end
+
+ def foos_wibble
+ render plain: 'ok'
+ end
+ end
+
+ def test_encoding_as_json
+ post_to_foos as: :json do
+ assert_response :success
+ assert_match 'foos_json.json', request.path
+ assert_equal 'application/json', request.content_type
+ assert_equal({ 'foo' => 'fighters' }, request.request_parameters)
+ assert_equal({ 'foo' => 'fighters' }, response.parsed_body)
+ end
+ end
+
+ def test_encoding_as_without_mime_registration
+ assert_raise ArgumentError do
+ ActionDispatch::IntegrationTest.register_encoder :wibble
+ end
+ end
+
+ def test_registering_custom_encoder
+ Mime::Type.register 'text/wibble', :wibble
+
+ ActionDispatch::IntegrationTest.register_encoder(:wibble,
+ param_encoder: -> params { params })
+
+ post_to_foos as: :wibble do
+ assert_response :success
+ assert_match 'foos_wibble.wibble', request.path
+ assert_equal 'text/wibble', request.content_type
+ assert_equal Hash.new, request.request_parameters # Unregistered MIME Type can't be parsed.
+ assert_equal 'ok', response.parsed_body
+ end
+ ensure
+ Mime::Type.unregister :wibble
+ end
+
+ def test_parsed_body_without_as_option
+ with_routing do |routes|
+ routes.draw { get ':action' => FooController }
+
+ get '/foos_json.json', params: { foo: 'heyo' }
+
+ assert_equal({ 'foo' => 'heyo' }, response.parsed_body)
+ end
+ end
+
+ private
+ def post_to_foos(as:)
+ with_routing do |routes|
+ routes.draw { post ':action' => FooController }
+
+ post "/foos_#{as}", params: { foo: 'fighters' }, as: as
+
+ yield
+ end
+ end
+end
diff --git a/actionpack/test/controller/live_stream_test.rb b/actionpack/test/controller/live_stream_test.rb
index aab2d9545d..0c3884cd38 100644
--- a/actionpack/test/controller/live_stream_test.rb
+++ b/actionpack/test/controller/live_stream_test.rb
@@ -152,7 +152,6 @@ module ActionController
def thread_locals
tc.assert_equal 'aaron', Thread.current[:setting]
- tc.assert_not_equal Thread.current.object_id, Thread.current[:originating_thread]
response.headers['Content-Type'] = 'text/event-stream'
%w{ hello world }.each do |word|
@@ -261,6 +260,14 @@ module ActionController
end
end
+ def setup
+ super
+
+ def @controller.new_controller_thread
+ Thread.new { yield }
+ end
+ end
+
def test_set_cookie
get :set_cookie
assert_equal({'hello' => 'world'}, @response.cookies)
@@ -429,7 +436,7 @@ module ActionController
end
def test_stale_with_etag
- @request.if_none_match = Digest::MD5.hexdigest("123")
+ @request.if_none_match = %(W/"#{Digest::MD5.hexdigest('123')}")
get :with_stale
assert_equal 304, response.status.to_i
end
diff --git a/actionpack/test/controller/metal/renderers_test.rb b/actionpack/test/controller/metal/renderers_test.rb
new file mode 100644
index 0000000000..007866a559
--- /dev/null
+++ b/actionpack/test/controller/metal/renderers_test.rb
@@ -0,0 +1,42 @@
+require 'abstract_unit'
+require 'active_support/core_ext/hash/conversions'
+
+class MetalRenderingJsonController < MetalRenderingController
+ class Model
+ def to_json(options = {})
+ { a: 'b' }.to_json(options)
+ end
+
+ def to_xml(options = {})
+ { a: 'b' }.to_xml(options)
+ end
+ end
+
+ use_renderers :json
+
+ def one
+ render json: Model.new
+ end
+
+ def two
+ render xml: Model.new
+ end
+end
+
+class RenderersMetalTest < ActionController::TestCase
+ tests MetalRenderingJsonController
+
+ def test_render_json
+ get :one
+ assert_response :success
+ assert_equal({ a: 'b' }.to_json, @response.body)
+ assert_equal 'application/json', @response.content_type
+ end
+
+ def test_render_xml
+ get :two
+ assert_response :success
+ assert_equal(" ", @response.body)
+ assert_equal 'text/plain', @response.content_type
+ end
+end
diff --git a/actionpack/test/controller/mime/respond_to_test.rb b/actionpack/test/controller/mime/respond_to_test.rb
index 76e2d3ff43..d0c7b2e06a 100644
--- a/actionpack/test/controller/mime/respond_to_test.rb
+++ b/actionpack/test/controller/mime/respond_to_test.rb
@@ -160,7 +160,14 @@ class RespondToController < ActionController::Base
end
end
- def variant_with_implicit_rendering
+ def variant_with_implicit_template_rendering
+ # This has exactly one variant template defined in the file system (+mobile.html.erb),
+ # which raises the regular MissingTemplate error for other variants.
+ end
+
+ def variant_without_implicit_template_rendering
+ # This differs from the above in that it does not have any templates defined in the file
+ # system, which triggers the ImplicitRender (204 No Content) behavior.
end
def variant_with_format_and_custom_render
@@ -272,6 +279,8 @@ class RespondToController < ActionController::Base
end
class RespondToControllerTest < ActionController::TestCase
+ NO_CONTENT_WARNING = "No template found for RespondToController#variant_without_implicit_template_rendering, rendering head :no_content"
+
def setup
super
@request.host = "www.example.com"
@@ -616,30 +625,69 @@ class RespondToControllerTest < ActionController::TestCase
end
def test_invalid_variant
+ assert_raises(ActionController::UnknownFormat) do
+ get :variant_with_implicit_template_rendering, params: { v: :invalid }
+ end
+ end
+
+ def test_variant_not_set_regular_unknown_format
+ assert_raises(ActionController::UnknownFormat) do
+ get :variant_with_implicit_template_rendering
+ end
+ end
+
+ def test_variant_with_implicit_template_rendering
+ get :variant_with_implicit_template_rendering, params: { v: :mobile }
+ assert_equal "text/html", @response.content_type
+ assert_equal "mobile", @response.body
+ end
+
+ def test_variant_without_implicit_rendering_from_browser
+ assert_raises(ActionController::UnknownFormat) do
+ get :variant_without_implicit_template_rendering, params: { v: :does_not_matter }
+ end
+ end
+
+ def test_variant_variant_not_set_and_without_implicit_rendering_from_browser
+ assert_raises(ActionController::UnknownFormat) do
+ get :variant_without_implicit_template_rendering
+ end
+ end
+
+ def test_variant_without_implicit_rendering_from_xhr
logger = ActiveSupport::LogSubscriber::TestHelper::MockLogger.new
old_logger, ActionController::Base.logger = ActionController::Base.logger, logger
- get :variant_with_implicit_rendering, params: { v: :invalid }
+ get :variant_without_implicit_template_rendering, xhr: true, params: { v: :does_not_matter }
assert_response :no_content
- assert_equal 1, logger.logged(:info).select{ |s| s =~ /No template found/ }.size, "Implicit head :no_content not logged"
+
+ assert_equal 1, logger.logged(:info).select{ |s| s == NO_CONTENT_WARNING }.size, "Implicit head :no_content not logged"
ensure
ActionController::Base.logger = old_logger
end
- def test_variant_not_set_regular_template_missing
- get :variant_with_implicit_rendering
+ def test_variant_without_implicit_rendering_from_api
+ logger = ActiveSupport::LogSubscriber::TestHelper::MockLogger.new
+ old_logger, ActionController::Base.logger = ActionController::Base.logger, logger
+
+ get :variant_without_implicit_template_rendering, format: 'json', params: { v: :does_not_matter }
assert_response :no_content
+
+ assert_equal 1, logger.logged(:info).select{ |s| s == NO_CONTENT_WARNING }.size, "Implicit head :no_content not logged"
+ ensure
+ ActionController::Base.logger = old_logger
end
- def test_variant_with_implicit_rendering
- get :variant_with_implicit_rendering, params: { v: :implicit }
+ def test_variant_variant_not_set_and_without_implicit_rendering_from_xhr
+ logger = ActiveSupport::LogSubscriber::TestHelper::MockLogger.new
+ old_logger, ActionController::Base.logger = ActionController::Base.logger, logger
+
+ get :variant_without_implicit_template_rendering, xhr: true
assert_response :no_content
- end
- def test_variant_with_implicit_template_rendering
- get :variant_with_implicit_rendering, params: { v: :mobile }
- assert_equal "text/html", @response.content_type
- assert_equal "mobile", @response.body
+ assert_equal 1, logger.logged(:info).select { |s| s == NO_CONTENT_WARNING }.size, "Implicit head :no_content not logged"
+ ensure
+ ActionController::Base.logger = old_logger
end
def test_variant_with_format_and_custom_render
@@ -778,24 +826,3 @@ class RespondToControllerTest < ActionController::TestCase
assert_equal "phone", @response.body
end
end
-
-class RespondToWithBlockOnDefaultRenderController < ActionController::Base
- def show
- default_render do
- render body: 'default_render yielded'
- end
- end
-end
-
-class RespondToWithBlockOnDefaultRenderControllerTest < ActionController::TestCase
- def setup
- super
- @request.host = "www.example.com"
- end
-
- def test_default_render_uses_block_when_no_template_exists
- get :show
- assert_equal "default_render yielded", @response.body
- assert_equal "text/plain", @response.content_type
- end
-end
diff --git a/actionpack/test/controller/new_base/bare_metal_test.rb b/actionpack/test/controller/new_base/bare_metal_test.rb
index c226fa57ee..ee3c498b1c 100644
--- a/actionpack/test/controller/new_base/bare_metal_test.rb
+++ b/actionpack/test/controller/new_base/bare_metal_test.rb
@@ -40,6 +40,22 @@ module BareMetalTest
end
end
+ class BareEmptyController < ActionController::Metal
+ def index
+ self.response_body = nil
+ end
+ end
+
+ class BareEmptyTest < ActiveSupport::TestCase
+ test "response body is nil" do
+ controller = BareEmptyController.new
+ controller.set_request!(ActionDispatch::Request.empty)
+ controller.set_response!(BareController.make_response!(controller.request))
+ controller.index
+ assert_equal nil, controller.response_body
+ end
+ end
+
class HeadController < ActionController::Metal
include ActionController::Head
diff --git a/actionpack/test/controller/parameters/accessors_test.rb b/actionpack/test/controller/parameters/accessors_test.rb
index 97875c3cbb..cea265f9ab 100644
--- a/actionpack/test/controller/parameters/accessors_test.rb
+++ b/actionpack/test/controller/parameters/accessors_test.rb
@@ -4,6 +4,8 @@ require 'active_support/core_ext/hash/transform_values'
class ParametersAccessorsTest < ActiveSupport::TestCase
setup do
+ ActionController::Parameters.permit_all_parameters = false
+
@params = ActionController::Parameters.new(
person: {
age: '32',
@@ -27,6 +29,12 @@ class ParametersAccessorsTest < ActiveSupport::TestCase
assert_not @params[:person][:name].permitted?
end
+ test "as_json returns the JSON representation of the parameters hash" do
+ assert_not @params.as_json.key? "parameters"
+ assert_not @params.as_json.key? "permitted"
+ assert @params.as_json.key? "person"
+ end
+
test "each carries permitted status" do
@params.permit!
@params.each { |key, value| assert(value.permitted?) if key == "person" }
@@ -122,4 +130,68 @@ class ParametersAccessorsTest < ActiveSupport::TestCase
assert_not @params.values_at(:person).first.permitted?
assert_not @params[:person].values_at(:name).first.permitted?
end
+
+ test "equality with a hash is deprecated" do
+ hash1 = { foo: :bar }
+ params1 = ActionController::Parameters.new(hash1)
+ assert_deprecated("will be removed in Rails 5.1") do
+ assert(params1 == hash1)
+ end
+ end
+
+ test "is equal to Parameters instance with same params" do
+ params1 = ActionController::Parameters.new(a: 1, b: 2)
+ params2 = ActionController::Parameters.new(a: 1, b: 2)
+ assert(params1 == params2)
+ end
+
+ test "is equal to Parameters instance with same permitted params" do
+ params1 = ActionController::Parameters.new(a: 1, b: 2).permit(:a)
+ params2 = ActionController::Parameters.new(a: 1, b: 2).permit(:a)
+ assert(params1 == params2)
+ end
+
+ test "is equal to Parameters instance with same different source params, but same permitted params" do
+ params1 = ActionController::Parameters.new(a: 1, b: 2).permit(:a)
+ params2 = ActionController::Parameters.new(a: 1, c: 3).permit(:a)
+ assert(params1 == params2)
+ assert(params2 == params1)
+ end
+
+ test 'is not equal to an unpermitted Parameters instance with same params' do
+ params1 = ActionController::Parameters.new(a: 1).permit(:a)
+ params2 = ActionController::Parameters.new(a: 1)
+ assert(params1 != params2)
+ assert(params2 != params1)
+ end
+
+ test "is not equal to Parameters instance with different permitted params" do
+ params1 = ActionController::Parameters.new(a: 1, b: 2).permit(:a, :b)
+ params2 = ActionController::Parameters.new(a: 1, b: 2).permit(:a)
+ assert(params1 != params2)
+ assert(params2 != params1)
+ end
+
+ test "equality with simple types works" do
+ assert(@params != 'Hello')
+ assert(@params != 42)
+ assert(@params != false)
+ end
+
+ test "inspect shows both class name, parameters and permitted flag" do
+ assert_equal(
+ '<ActionController::Parameters {"person"=>{"age"=>"32", '\
+ '"name"=>{"first"=>"David", "last"=>"Heinemeier Hansson"}, ' \
+ '"addresses"=>[{"city"=>"Chicago", "state"=>"Illinois"}]}} permitted: false>',
+ @params.inspect
+ )
+ end
+
+ test "inspect prints updated permitted flag in the output" do
+ assert_match(/permitted: false/, @params.inspect)
+
+ @params.permit!
+
+ assert_match(/permitted: true/, @params.inspect)
+ end
end
diff --git a/actionpack/test/controller/parameters/always_permitted_parameters_test.rb b/actionpack/test/controller/parameters/always_permitted_parameters_test.rb
index efaf8a96c3..c5bfb10b53 100644
--- a/actionpack/test/controller/parameters/always_permitted_parameters_test.rb
+++ b/actionpack/test/controller/parameters/always_permitted_parameters_test.rb
@@ -12,12 +12,6 @@ class AlwaysPermittedParametersTest < ActiveSupport::TestCase
ActionController::Parameters.always_permitted_parameters = %w( controller action )
end
- test "shows deprecations warning on NEVER_UNPERMITTED_PARAMS" do
- assert_deprecated do
- ActionController::Parameters::NEVER_UNPERMITTED_PARAMS
- end
- end
-
test "returns super on missing constant other than NEVER_UNPERMITTED_PARAMS" do
ActionController::Parameters.superclass.stub :const_missing, "super" do
assert_equal "super", ActionController::Parameters::NON_EXISTING_CONSTANT
diff --git a/actionpack/test/controller/parameters/parameters_permit_test.rb b/actionpack/test/controller/parameters/parameters_permit_test.rb
index f23aa599c1..96048e2868 100644
--- a/actionpack/test/controller/parameters/parameters_permit_test.rb
+++ b/actionpack/test/controller/parameters/parameters_permit_test.rb
@@ -27,6 +27,27 @@ class ParametersPermitTest < ActiveSupport::TestCase
end
end
+ def walk_permitted params
+ params.each do |k,v|
+ case v
+ when ActionController::Parameters
+ walk_permitted v
+ when Array
+ v.each { |x| walk_permitted v }
+ end
+ end
+ end
+
+ test 'iteration should not impact permit' do
+ hash = {"foo"=>{"bar"=>{"0"=>{"baz"=>"hello", "zot"=>"1"}}}}
+ params = ActionController::Parameters.new(hash)
+
+ walk_permitted params
+
+ sanitized = params[:foo].permit(bar: [:baz])
+ assert_equal({"0"=>{"baz"=>"hello"}}, sanitized[:bar].to_unsafe_h)
+ end
+
test 'if nothing is permitted, the hash becomes empty' do
params = ActionController::Parameters.new(id: '1234')
permitted = params.permit
@@ -294,8 +315,16 @@ class ParametersPermitTest < ActiveSupport::TestCase
end
test "to_unsafe_h returns unfiltered params" do
- assert @params.to_h.is_a? ActiveSupport::HashWithIndifferentAccess
- assert_not @params.to_h.is_a? ActionController::Parameters
+ assert @params.to_unsafe_h.is_a? ActiveSupport::HashWithIndifferentAccess
+ assert_not @params.to_unsafe_h.is_a? ActionController::Parameters
+ end
+
+ test "to_unsafe_h returns unfiltered params even after accessing few keys" do
+ params = ActionController::Parameters.new("f"=>{"language_facet"=>["Tibetan"]})
+ expected = {"f"=>{"language_facet"=>["Tibetan"]}}
+
+ assert params['f'].is_a? ActionController::Parameters
+ assert_equal expected, params.to_unsafe_h
end
test "to_h only deep dups Ruby collections" do
@@ -325,4 +354,10 @@ class ParametersPermitTest < ActiveSupport::TestCase
assert_equal({ 'companies' => [ company, :acme ] }, params.to_unsafe_h)
assert_not company.dupped
end
+
+ test "include? returns true when the key is present" do
+ assert @params.include? :person
+ assert @params.include? 'person'
+ assert_not @params.include? :gorilla
+ end
end
diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb
index 0b184eace9..3ea03be74a 100644
--- a/actionpack/test/controller/redirect_test.rb
+++ b/actionpack/test/controller/redirect_test.rb
@@ -310,7 +310,7 @@ class RedirectTest < ActionController::TestCase
error = assert_raise(ArgumentError) do
get :redirect_to_params
end
- assert_equal "Generating an URL from non sanitized request parameters is insecure!", error.message
+ assert_equal "Generating a URL from non sanitized request parameters is insecure!", error.message
end
def test_redirect_to_with_block
diff --git a/actionpack/test/controller/render_other_test.rb b/actionpack/test/controller/render_other_test.rb
deleted file mode 100644
index 1f5215ac55..0000000000
--- a/actionpack/test/controller/render_other_test.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-require 'abstract_unit'
-
-
-class RenderOtherTest < ActionController::TestCase
- class TestController < ActionController::Base
- def render_simon_says
- render :simon => "foo"
- end
- end
-
- tests TestController
-
- def test_using_custom_render_option
- ActionController.add_renderer :simon do |says, options|
- self.content_type = Mime[:text]
- self.response_body = "Simon says: #{says}"
- end
-
- get :render_simon_says
- assert_equal "Simon says: foo", @response.body
- ensure
- ActionController.remove_renderer :simon
- end
-end
diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb
index 256ebf6a07..83d7405e4d 100644
--- a/actionpack/test/controller/render_test.rb
+++ b/actionpack/test/controller/render_test.rb
@@ -26,6 +26,9 @@ end
class ImplicitRenderTestController < ActionController::Base
def empty_action
end
+
+ def empty_action_with_template
+ end
end
class TestController < ActionController::Base
@@ -62,6 +65,20 @@ class TestController < ActionController::Base
end
end
+ def dynamic_render
+ render params[:id] # => String, AC::Params
+ end
+
+ def dynamic_render_permit
+ render params[:id].permit(:file)
+ end
+
+ def dynamic_render_with_file
+ # This is extremely bad, but should be possible to do.
+ file = params[:id] # => String, AC::Params
+ render file: file
+ end
+
class Collection
def initialize(records)
@records = records
@@ -243,6 +260,52 @@ end
class ExpiresInRenderTest < ActionController::TestCase
tests TestController
+ def setup
+ super
+ ActionController::Base.view_paths.paths.each(&:clear_cache)
+ end
+
+ def test_dynamic_render_with_file
+ # This is extremely bad, but should be possible to do.
+ assert File.exist?(File.join(File.dirname(__FILE__), '../../test/abstract_unit.rb'))
+ response = get :dynamic_render_with_file, params: { id: '../\\../test/abstract_unit.rb' }
+ assert_equal File.read(File.join(File.dirname(__FILE__), '../../test/abstract_unit.rb')),
+ response.body
+ end
+
+ def test_dynamic_render_with_absolute_path
+ file = Tempfile.new('name')
+ file.write "secrets!"
+ file.flush
+ assert_raises ActionView::MissingTemplate do
+ get :dynamic_render, params: { id: file.path }
+ end
+ ensure
+ file.close
+ file.unlink
+ end
+
+ def test_dynamic_render
+ assert File.exist?(File.join(File.dirname(__FILE__), '../../test/abstract_unit.rb'))
+ assert_raises ActionView::MissingTemplate do
+ get :dynamic_render, params: { id: '../\\../test/abstract_unit.rb' }
+ end
+ end
+
+ def test_permitted_dynamic_render_file_hash
+ skip "FIXME: this test passes on 4-2-stable but not master. Why?"
+ assert File.exist?(File.join(File.dirname(__FILE__), '../../test/abstract_unit.rb'))
+ response = get :dynamic_render_permit, params: { id: { file: '../\\../test/abstract_unit.rb' } }
+ assert_equal File.read(File.join(File.dirname(__FILE__), '../../test/abstract_unit.rb')),
+ response.body
+ end
+
+ def test_dynamic_render_file_hash
+ assert_raises ArgumentError do
+ get :dynamic_render, params: { id: { file: '../\\../test/abstract_unit.rb' } }
+ end
+ end
+
def test_expires_in_header
get :conditional_hello_with_expires_in
assert_equal "max-age=60, private", @response.headers["Cache-Control"]
@@ -449,7 +512,7 @@ class EtagRenderTest < ActionController::TestCase
begin
File.write path, 'foo'
- ActionView::Digestor.cache.clear
+ ActionView::LookupContext::DetailsKey.clear
request.if_none_match = etag
get :with_template
@@ -461,7 +524,7 @@ class EtagRenderTest < ActionController::TestCase
end
def etag(record)
- Digest::MD5.hexdigest(ActiveSupport::Cache.expand_cache_key(record)).inspect
+ %(W/"#{Digest::MD5.hexdigest(ActiveSupport::Cache.expand_cache_key(record))}")
end
end
@@ -477,10 +540,28 @@ end
class ImplicitRenderTest < ActionController::TestCase
tests ImplicitRenderTestController
- def test_implicit_no_content_response
- get :empty_action
+ def test_implicit_no_content_response_as_browser
+ assert_raises(ActionController::UnknownFormat) do
+ get :empty_action
+ end
+ end
+
+ def test_implicit_no_content_response_as_xhr
+ get :empty_action, xhr: true
assert_response :no_content
end
+
+ def test_implicit_success_response_with_right_format
+ get :empty_action_with_template
+ assert_equal "<h1>Empty action rendered this implicitly.</h1>\n", @response.body
+ assert_response :success
+ end
+
+ def test_implicit_unknown_format_response
+ assert_raises(ActionController::UnknownFormat) do
+ get :empty_action_with_template, format: 'json'
+ end
+ end
end
class HeadRenderTest < ActionController::TestCase
diff --git a/actionpack/test/controller/renderers_test.rb b/actionpack/test/controller/renderers_test.rb
new file mode 100644
index 0000000000..e6c2e4636e
--- /dev/null
+++ b/actionpack/test/controller/renderers_test.rb
@@ -0,0 +1,90 @@
+require 'abstract_unit'
+require 'controller/fake_models'
+require 'active_support/logger'
+
+class RenderersTest < ActionController::TestCase
+ class XmlRenderable
+ def to_xml(options)
+ options[:root] ||= "i-am-xml"
+ "<#{options[:root]}/>"
+ end
+ end
+ class JsonRenderable
+ def as_json(options={})
+ hash = { :a => :b, :c => :d, :e => :f }
+ hash.except!(*options[:except]) if options[:except]
+ hash
+ end
+
+ def to_json(options = {})
+ super :except => [:c, :e]
+ end
+ end
+ class CsvRenderable
+ def to_csv
+ "c,s,v"
+ end
+ end
+ class TestController < ActionController::Base
+
+ def render_simon_says
+ render :simon => "foo"
+ end
+
+ def respond_to_mime
+ respond_to do |type|
+ type.json do
+ render json: JsonRenderable.new
+ end
+ type.js { render json: 'JS', callback: 'alert' }
+ type.csv { render csv: CsvRenderable.new }
+ type.xml { render xml: XmlRenderable.new }
+ type.html { render body: "HTML" }
+ type.rss { render body: "RSS" }
+ type.all { render body: "Nothing" }
+ type.any(:js, :xml) { render body: "Either JS or XML" }
+ end
+ end
+ end
+
+ tests TestController
+
+ def setup
+ # enable a logger so that (e.g.) the benchmarking stuff runs, so we can get
+ # a more accurate simulation of what happens in "real life".
+ super
+ @controller.logger = ActiveSupport::Logger.new(nil)
+ end
+
+ def test_using_custom_render_option
+ ActionController.add_renderer :simon do |says, options|
+ self.content_type = Mime[:text]
+ self.response_body = "Simon says: #{says}"
+ end
+
+ get :render_simon_says
+ assert_equal "Simon says: foo", @response.body
+ ensure
+ ActionController.remove_renderer :simon
+ end
+
+ def test_raises_missing_template_no_renderer
+ assert_raise ActionView::MissingTemplate do
+ get :respond_to_mime, format: 'csv'
+ end
+ assert_equal Mime[:csv], @response.content_type
+ assert_equal "", @response.body
+ end
+
+ def test_adding_csv_rendering_via_renderers_add
+ ActionController::Renderers.add :csv do |value, options|
+ send_data value.to_csv, type: Mime[:csv]
+ end
+ @request.accept = "text/csv"
+ get :respond_to_mime, format: 'csv'
+ assert_equal Mime[:csv], @response.content_type
+ assert_equal "c,s,v", @response.body
+ ensure
+ ActionController::Renderers.remove :csv
+ end
+end
diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb
index 87a8ed3dc9..f7dcbc1984 100644
--- a/actionpack/test/controller/request_forgery_protection_test.rb
+++ b/actionpack/test/controller/request_forgery_protection_test.rb
@@ -128,6 +128,27 @@ class CustomAuthenticityParamController < RequestForgeryProtectionControllerUsin
end
end
+class PerFormTokensController < ActionController::Base
+ protect_from_forgery :with => :exception
+ self.per_form_csrf_tokens = true
+
+ def index
+ render inline: "<%= form_tag (params[:form_path] || '/per_form_tokens/post_one'), method: params[:form_method] %>"
+ end
+
+ def button_to
+ render inline: "<%= button_to 'Button', (params[:form_path] || '/per_form_tokens/post_one'), method: params[:form_method] %>"
+ end
+
+ def post_one
+ render plain: ''
+ end
+
+ def post_two
+ render plain: ''
+ end
+end
+
# common test methods
module RequestForgeryProtectionTests
def setup
@@ -623,3 +644,180 @@ class CustomAuthenticityParamControllerTest < ActionController::TestCase
end
end
end
+
+class PerFormTokensControllerTest < ActionController::TestCase
+ def test_per_form_token_is_same_size_as_global_token
+ get :index
+ expected = ActionController::RequestForgeryProtection::AUTHENTICITY_TOKEN_LENGTH
+ actual = @controller.send(:per_form_csrf_token, session, '/path', 'post').size
+ assert_equal expected, actual
+ end
+
+ def test_accepts_token_for_correct_path_and_method
+ get :index
+
+ form_token = assert_presence_and_fetch_form_csrf_token
+
+ assert_matches_session_token_on_server form_token
+
+ # This is required because PATH_INFO isn't reset between requests.
+ @request.env['PATH_INFO'] = '/per_form_tokens/post_one'
+ assert_nothing_raised do
+ post :post_one, params: {custom_authenticity_token: form_token}
+ end
+ assert_response :success
+ end
+
+ def test_rejects_token_for_incorrect_path
+ get :index
+
+ form_token = assert_presence_and_fetch_form_csrf_token
+
+ assert_matches_session_token_on_server form_token
+
+ # This is required because PATH_INFO isn't reset between requests.
+ @request.env['PATH_INFO'] = '/per_form_tokens/post_two'
+ assert_raises(ActionController::InvalidAuthenticityToken) do
+ post :post_two, params: {custom_authenticity_token: form_token}
+ end
+ end
+
+ def test_rejects_token_for_incorrect_method
+ get :index
+
+ form_token = assert_presence_and_fetch_form_csrf_token
+
+ assert_matches_session_token_on_server form_token
+
+ # This is required because PATH_INFO isn't reset between requests.
+ @request.env['PATH_INFO'] = '/per_form_tokens/post_one'
+ assert_raises(ActionController::InvalidAuthenticityToken) do
+ patch :post_one, params: {custom_authenticity_token: form_token}
+ end
+ end
+
+ def test_rejects_token_for_incorrect_method_button_to
+ get :button_to, params: { form_method: 'delete' }
+
+ form_token = assert_presence_and_fetch_form_csrf_token
+
+ assert_matches_session_token_on_server form_token, 'delete'
+
+ # This is required because PATH_INFO isn't reset between requests.
+ @request.env['PATH_INFO'] = '/per_form_tokens/post_one'
+ assert_raises(ActionController::InvalidAuthenticityToken) do
+ patch :post_one, params: { custom_authenticity_token: form_token }
+ end
+ end
+
+ test "Accepts proper token for implicit post method on button_to tag" do
+ get :button_to
+
+ form_token = assert_presence_and_fetch_form_csrf_token
+
+ assert_matches_session_token_on_server form_token, 'post'
+
+ # This is required because PATH_INFO isn't reset between requests.
+ @request.env['PATH_INFO'] = '/per_form_tokens/post_one'
+ assert_nothing_raised do
+ post :post_one, params: { custom_authenticity_token: form_token }
+ end
+ end
+
+ %w{delete post patch}.each do |verb|
+ test "Accepts proper token for #{verb} method on button_to tag" do
+ get :button_to, params: { form_method: verb }
+
+ form_token = assert_presence_and_fetch_form_csrf_token
+
+ assert_matches_session_token_on_server form_token, verb
+
+ # This is required because PATH_INFO isn't reset between requests.
+ @request.env['PATH_INFO'] = '/per_form_tokens/post_one'
+ assert_nothing_raised do
+ send verb, :post_one, params: { custom_authenticity_token: form_token }
+ end
+ end
+ end
+
+ def test_accepts_global_csrf_token
+ get :index
+
+ token = @controller.send(:form_authenticity_token)
+
+ # This is required because PATH_INFO isn't reset between requests.
+ @request.env['PATH_INFO'] = '/per_form_tokens/post_one'
+ assert_nothing_raised do
+ post :post_one, params: {custom_authenticity_token: token}
+ end
+ assert_response :success
+ end
+
+ def test_ignores_params
+ get :index, params: {form_path: '/per_form_tokens/post_one?foo=bar'}
+
+ form_token = assert_presence_and_fetch_form_csrf_token
+
+ assert_matches_session_token_on_server form_token
+
+ # This is required because PATH_INFO isn't reset between requests.
+ @request.env['PATH_INFO'] = '/per_form_tokens/post_one?foo=baz'
+ assert_nothing_raised do
+ post :post_one, params: {custom_authenticity_token: form_token, baz: 'foo'}
+ end
+ assert_response :success
+ end
+
+ def test_ignores_trailing_slash_during_generation
+ get :index, params: {form_path: '/per_form_tokens/post_one/'}
+
+ form_token = assert_presence_and_fetch_form_csrf_token
+
+ # This is required because PATH_INFO isn't reset between requests.
+ @request.env['PATH_INFO'] = '/per_form_tokens/post_one'
+ assert_nothing_raised do
+ post :post_one, params: {custom_authenticity_token: form_token}
+ end
+ assert_response :success
+ end
+
+ def test_ignores_trailing_slash_during_validation
+ get :index
+
+ form_token = assert_presence_and_fetch_form_csrf_token
+
+ # This is required because PATH_INFO isn't reset between requests.
+ @request.env['PATH_INFO'] = '/per_form_tokens/post_one/'
+ assert_nothing_raised do
+ post :post_one, params: {custom_authenticity_token: form_token}
+ end
+ assert_response :success
+ end
+
+ def test_method_is_case_insensitive
+ get :index, params: {form_method: "POST"}
+
+ form_token = assert_presence_and_fetch_form_csrf_token
+ # This is required because PATH_INFO isn't reset between requests.
+ @request.env['PATH_INFO'] = '/per_form_tokens/post_one/'
+ assert_nothing_raised do
+ post :post_one, params: {custom_authenticity_token: form_token}
+ end
+ assert_response :success
+ end
+
+ private
+ def assert_presence_and_fetch_form_csrf_token
+ assert_select 'input[name="custom_authenticity_token"]' do |input|
+ form_csrf_token = input.first['value']
+ assert_not_nil form_csrf_token
+ return form_csrf_token
+ end
+ end
+
+ def assert_matches_session_token_on_server(form_token, method = 'post')
+ actual = @controller.send(:unmask_token, Base64.strict_decode64(form_token))
+ expected = @controller.send(:per_form_csrf_token, session, '/per_form_tokens/post_one', method)
+ assert_equal expected, actual
+ end
+end
diff --git a/actionpack/test/controller/required_params_test.rb b/actionpack/test/controller/required_params_test.rb
index 168f64ce41..b6efcd6f9a 100644
--- a/actionpack/test/controller/required_params_test.rb
+++ b/actionpack/test/controller/required_params_test.rb
@@ -65,4 +65,17 @@ class ParametersRequireTest < ActiveSupport::TestCase
.require([:first_name, :title])
end
end
+
+ test "value params" do
+ params = ActionController::Parameters.new(foo: "bar", dog: "cinco")
+ assert_equal ["bar", "cinco"], params.values
+ assert params.has_value?("cinco")
+ assert params.value?("cinco")
+ end
+
+ test "Deprecated methods are deprecated" do
+ assert_deprecated do
+ ActionController::Parameters.new(foo: "bar").merge!({bar: "foo"})
+ end
+ end
end
diff --git a/actionpack/test/controller/test_case_test.rb b/actionpack/test/controller/test_case_test.rb
index b9caddcdb7..0c1393548e 100644
--- a/actionpack/test/controller/test_case_test.rb
+++ b/actionpack/test/controller/test_case_test.rb
@@ -137,6 +137,10 @@ XML
head :created, location: 'created resource'
end
+ def render_cookie
+ render plain: cookies["foo"]
+ end
+
def delete_cookie
cookies.delete("foo")
render plain: 'ok'
@@ -829,6 +833,12 @@ XML
assert_equal 'bar', cookies['foo']
end
+ def test_cookies_should_be_escaped_properly
+ cookies['foo'] = '+'
+ get :render_cookie
+ assert_equal '+', @response.body
+ end
+
def test_should_detect_if_cookie_is_deleted
cookies['foo'] = 'bar'
get :delete_cookie
diff --git a/actionpack/test/controller/webservice_test.rb b/actionpack/test/controller/webservice_test.rb
index 6d377c4691..daf17558aa 100644
--- a/actionpack/test/controller/webservice_test.rb
+++ b/actionpack/test/controller/webservice_test.rb
@@ -99,7 +99,7 @@ class WebServiceTest < ActionDispatch::IntegrationTest
def test_parsing_json_doesnot_rescue_exception
req = Class.new(ActionDispatch::Request) do
def params_parsers
- { Mime[:json] => Proc.new { |data| raise Interrupt } }
+ { json: Proc.new { |data| raise Interrupt } }
end
def content_length; get_header('rack.input').length; end