aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/test
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/test')
-rw-r--r--actionpack/test/abstract_unit.rb12
-rw-r--r--actionpack/test/assertions/response_assertions_test.rb19
-rw-r--r--actionpack/test/controller/api/renderers_test.rb22
-rw-r--r--actionpack/test/controller/http_basic_authentication_test.rb21
-rw-r--r--actionpack/test/controller/live_stream_test.rb2
-rw-r--r--actionpack/test/controller/metal/renderers_test.rb42
-rw-r--r--actionpack/test/controller/parameters/accessors_test.rb12
-rw-r--r--actionpack/test/controller/parameters/parameters_permit_test.rb18
-rw-r--r--actionpack/test/controller/render_other_test.rb24
-rw-r--r--actionpack/test/controller/render_test.rb62
-rw-r--r--actionpack/test/controller/renderers_test.rb90
-rw-r--r--actionpack/test/controller/request_forgery_protection_test.rb172
-rw-r--r--actionpack/test/controller/required_params_test.rb13
-rw-r--r--actionpack/test/dispatch/request_test.rb21
-rw-r--r--actionpack/test/dispatch/response_test.rb35
-rw-r--r--actionpack/test/dispatch/routing/inspector_test.rb38
-rw-r--r--actionpack/test/dispatch/routing_test.rb84
-rw-r--r--actionpack/test/dispatch/ssl_test.rb45
-rw-r--r--actionpack/test/dispatch/static_test.rb4
-rw-r--r--actionpack/test/fixtures/functional_caching/fragment_cached_with_options.html.erb2
20 files changed, 682 insertions, 56 deletions
diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb
index ef7aab72c6..1ef10ff956 100644
--- a/actionpack/test/abstract_unit.rb
+++ b/actionpack/test/abstract_unit.rb
@@ -20,7 +20,11 @@ rescue LoadError
puts "'drb/unix' is not available"
end
-PROCESS_COUNT = (ENV['N'] || 4).to_i
+if ENV['TRAVIS']
+ PROCESS_COUNT = 0
+else
+ PROCESS_COUNT = (ENV['N'] || 4).to_i
+end
require 'active_support/testing/autorun'
require 'abstract_controller'
@@ -371,6 +375,12 @@ module RoutingTestHelpers
end
end
+class MetalRenderingController < ActionController::Metal
+ include AbstractController::Rendering
+ include ActionController::Rendering
+ include ActionController::Renderers
+end
+
class ResourcesController < ActionController::Base
def index() head :ok end
alias_method :show, :index
diff --git a/actionpack/test/assertions/response_assertions_test.rb b/actionpack/test/assertions/response_assertions_test.rb
index 841fa6aaad..579ce0ed29 100644
--- a/actionpack/test/assertions/response_assertions_test.rb
+++ b/actionpack/test/assertions/response_assertions_test.rb
@@ -74,7 +74,18 @@ module ActionDispatch
@response.status = 404
error = assert_raises(Minitest::Assertion) { assert_response :success }
- expected = "Expected response to be a <success>, but was a <404>"
+ expected = "Expected response to be a <2XX: success>,"\
+ " but was a <404: Not Found>"
+ assert_match expected, error.message
+ end
+
+ def test_error_message_shows_404_when_asserted_for_200
+ @response = ActionDispatch::Response.new
+ @response.status = 404
+
+ error = assert_raises(Minitest::Assertion) { assert_response 200 }
+ expected = "Expected response to be a <200: OK>,"\
+ " but was a <404: Not Found>"
assert_match expected, error.message
end
@@ -84,7 +95,8 @@ module ActionDispatch
@response.location = 'http://test.host/posts/redirect/1'
error = assert_raises(Minitest::Assertion) { assert_response :success }
- expected = "Expected response to be a <success>, but was a <302>" \
+ expected = "Expected response to be a <2XX: success>,"\
+ " but was a <302: Found>" \
" redirect to <http://test.host/posts/redirect/1>"
assert_match expected, error.message
end
@@ -95,7 +107,8 @@ module ActionDispatch
@response.location = 'http://test.host/posts/redirect/2'
error = assert_raises(Minitest::Assertion) { assert_response 301 }
- expected = "Expected response to be a <301>, but was a <302>" \
+ expected = "Expected response to be a <301: Moved Permanently>,"\
+ " but was a <302: Found>" \
" redirect to <http://test.host/posts/redirect/2>"
assert_match expected, error.message
end
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/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/live_stream_test.rb b/actionpack/test/controller/live_stream_test.rb
index aab2d9545d..2ef9734269 100644
--- a/actionpack/test/controller/live_stream_test.rb
+++ b/actionpack/test/controller/live_stream_test.rb
@@ -429,7 +429,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/parameters/accessors_test.rb b/actionpack/test/controller/parameters/accessors_test.rb
index 97875c3cbb..bd43ff7697 100644
--- a/actionpack/test/controller/parameters/accessors_test.rb
+++ b/actionpack/test/controller/parameters/accessors_test.rb
@@ -27,6 +27,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 +128,10 @@ 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 another hash works" do
+ hash1 = { foo: :bar }
+ params1 = ActionController::Parameters.new(hash1)
+ assert(params1 == hash1)
+ end
end
diff --git a/actionpack/test/controller/parameters/parameters_permit_test.rb b/actionpack/test/controller/parameters/parameters_permit_test.rb
index f23aa599c1..3299f2d9d0 100644
--- a/actionpack/test/controller/parameters/parameters_permit_test.rb
+++ b/actionpack/test/controller/parameters/parameters_permit_test.rb
@@ -294,8 +294,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 +333,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/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..9430ab8db8 100644
--- a/actionpack/test/controller/render_test.rb
+++ b/actionpack/test/controller/render_test.rb
@@ -62,6 +62,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 +257,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
+ response = 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"]
@@ -461,7 +521,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
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..1984ad8825 100644
--- a/actionpack/test/controller/request_forgery_protection_test.rb
+++ b/actionpack/test/controller/request_forgery_protection_test.rb
@@ -128,6 +128,23 @@ 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] || :post) %>"
+ end
+
+ def post_one
+ render plain: ''
+ end
+
+ def post_two
+ render plain: ''
+ end
+end
+
# common test methods
module RequestForgeryProtectionTests
def setup
@@ -623,3 +640,158 @@ 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 = nil
+ assert_select 'input[name=custom_authenticity_token]' do |elts|
+ form_token = elts.first['value']
+ assert_not_nil form_token
+ end
+
+ actual = @controller.send(:unmask_token, Base64.strict_decode64(form_token))
+ expected = @controller.send(:per_form_csrf_token, session, '/per_form_tokens/post_one', 'post')
+ assert_equal expected, actual
+
+ # 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 = nil
+ assert_select 'input[name=custom_authenticity_token]' do |elts|
+ form_token = elts.first['value']
+ assert_not_nil form_token
+ end
+
+ actual = @controller.send(:unmask_token, Base64.strict_decode64(form_token))
+ expected = @controller.send(:per_form_csrf_token, session, '/per_form_tokens/post_one', 'post')
+ assert_equal expected, actual
+
+ # 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 = nil
+ assert_select 'input[name=custom_authenticity_token]' do |elts|
+ form_token = elts.first['value']
+ assert_not_nil form_token
+ end
+
+ actual = @controller.send(:unmask_token, Base64.strict_decode64(form_token))
+ expected = @controller.send(:per_form_csrf_token, session, '/per_form_tokens/post_one', 'post')
+ assert_equal expected, actual
+
+ # 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_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 = nil
+ assert_select 'input[name=custom_authenticity_token]' do |elts|
+ form_token = elts.first['value']
+ assert_not_nil form_token
+ end
+
+ actual = @controller.send(:unmask_token, Base64.strict_decode64(form_token))
+ expected = @controller.send(:per_form_csrf_token, session, '/per_form_tokens/post_one', 'post')
+ assert_equal expected, actual
+
+ # 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 = nil
+ assert_select 'input[name=custom_authenticity_token]' do |elts|
+ form_token = elts.first['value']
+ assert_not_nil form_token
+ end
+
+ # 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 = nil
+ assert_select 'input[name=custom_authenticity_token]' do |elts|
+ form_token = elts.first['value']
+ assert_not_nil form_token
+ end
+
+ # 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 = nil
+ assert_select 'input[name=custom_authenticity_token]' do |elts|
+ form_token = elts.first['value']
+ assert_not_nil form_token
+ end
+
+ # 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
+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/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb
index 7dd9d05e62..0edad72fd9 100644
--- a/actionpack/test/dispatch/request_test.rb
+++ b/actionpack/test/dispatch/request_test.rb
@@ -897,6 +897,27 @@ class RequestFormat < BaseRequestTest
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.123'
+ assert_called(request, :parameters, times: 1, returns: {}) do
+ assert_equal [Mime[:html]], request.formats
+ end
+ end
+
+ test "formats from accept headers have higher precedence than path extension" do
+ request = stub_request 'HTTP_ACCEPT' => 'application/json',
+ 'PATH_INFO' => '/foo.xml'
+
+ assert_called(request, :parameters, times: 1, returns: {}) do
+ assert_equal [Mime[:json]], request.formats
+ end
+ end
end
class RequestMimeType < BaseRequestTest
diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb
index c37679bc5f..8b3849cb7a 100644
--- a/actionpack/test/dispatch/response_test.rb
+++ b/actionpack/test/dispatch/response_test.rb
@@ -37,6 +37,27 @@ class ResponseTest < ActiveSupport::TestCase
assert_equal "closed stream", e.message
end
+ def test_read_body_during_action
+ @response.body = "Hello, World!"
+
+ # even though there's no explicitly set content-type,
+ assert_equal nil, @response.content_type
+
+ # after the action reads back @response.body,
+ assert_equal "Hello, World!", @response.body
+
+ # the response can be built.
+ status, headers, body = @response.to_a
+ assert_equal 200, status
+ assert_equal({
+ "Content-Type" => "text/html; charset=utf-8"
+ }, headers)
+
+ parts = []
+ body.each { |part| parts << part }
+ assert_equal ["Hello, World!"], parts
+ end
+
def test_response_body_encoding
body = ["hello".encode(Encoding::UTF_8)]
response = ActionDispatch::Response.new 200, {}, body
@@ -176,11 +197,11 @@ class ResponseTest < ActiveSupport::TestCase
}
resp.to_a
- assert_equal('"202cb962ac59075b964b07152d234b70"', resp.etag)
+ assert_equal('W/"202cb962ac59075b964b07152d234b70"', resp.etag)
assert_equal({:public => true}, resp.cache_control)
assert_equal('public', resp.headers['Cache-Control'])
- assert_equal('"202cb962ac59075b964b07152d234b70"', resp.headers['ETag'])
+ assert_equal('W/"202cb962ac59075b964b07152d234b70"', resp.headers['ETag'])
end
test "read charset and content type" do
@@ -367,16 +388,16 @@ class ResponseIntegrationTest < ActionDispatch::IntegrationTest
assert_response :success
assert_equal('public', @response.headers['Cache-Control'])
- assert_equal('"202cb962ac59075b964b07152d234b70"', @response.headers['ETag'])
+ assert_equal('W/"202cb962ac59075b964b07152d234b70"', @response.headers['ETag'])
- assert_equal('"202cb962ac59075b964b07152d234b70"', @response.etag)
+ assert_equal('W/"202cb962ac59075b964b07152d234b70"', @response.etag)
assert_equal({:public => true}, @response.cache_control)
end
test "response cache control from rackish app" do
@app = lambda { |env|
[200,
- {'ETag' => '"202cb962ac59075b964b07152d234b70"',
+ {'ETag' => 'W/"202cb962ac59075b964b07152d234b70"',
'Cache-Control' => 'public'}, ['Hello']]
}
@@ -384,9 +405,9 @@ class ResponseIntegrationTest < ActionDispatch::IntegrationTest
assert_response :success
assert_equal('public', @response.headers['Cache-Control'])
- assert_equal('"202cb962ac59075b964b07152d234b70"', @response.headers['ETag'])
+ assert_equal('W/"202cb962ac59075b964b07152d234b70"', @response.headers['ETag'])
- assert_equal('"202cb962ac59075b964b07152d234b70"', @response.etag)
+ assert_equal('W/"202cb962ac59075b964b07152d234b70"', @response.etag)
assert_equal({:public => true}, @response.cache_control)
end
diff --git a/actionpack/test/dispatch/routing/inspector_test.rb b/actionpack/test/dispatch/routing/inspector_test.rb
index a17d07c40b..7382c267c7 100644
--- a/actionpack/test/dispatch/routing/inspector_test.rb
+++ b/actionpack/test/dispatch/routing/inspector_test.rb
@@ -7,6 +7,9 @@ class MountedRackApp
end
end
+class Rails::DummyController
+end
+
module ActionDispatch
module Routing
class RoutesInspectorTest < ActiveSupport::TestCase
@@ -331,6 +334,41 @@ module ActionDispatch
" cart GET /cart(.:format) cart#show"
], output
end
+
+ def test_routes_with_undefined_filter
+ output = draw(:filter => 'Rails::MissingController') do
+ get 'photos/:id' => 'photos#show', :id => /[A-Z]\d{5}/
+ end
+
+ assert_equal [
+ "The controller Rails::MissingController does not exist!",
+ "For more information about routes, see the Rails guide: http://guides.rubyonrails.org/routing.html."
+ ], output
+ end
+
+ def test_no_routes_matched_filter
+ output = draw(:filter => 'rails/dummy') do
+ get 'photos/:id' => 'photos#show', :id => /[A-Z]\d{5}/
+ end
+
+ assert_equal [
+ "No routes were found for this controller",
+ "For more information about routes, see the Rails guide: http://guides.rubyonrails.org/routing.html."
+ ], output
+ end
+
+ def test_no_routes_were_defined
+ output = draw(:filter => 'Rails::DummyController') { }
+
+ assert_equal [
+ "You don't have any routes defined!",
+ "",
+ "Please add some routes in config/routes.rb.",
+ "",
+ "For more information about routes, see the Rails guide: http://guides.rubyonrails.org/routing.html."
+ ], output
+ end
+
end
end
end
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index 82222a141c..5ead9357ae 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -3578,6 +3578,27 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
assert_equal 'HEAD', @response.body
end
+ def test_passing_action_parameters_to_url_helpers_raises_error_if_parameters_are_not_permitted
+ draw do
+ root :to => 'projects#index'
+ end
+ params = ActionController::Parameters.new(id: '1')
+
+ assert_raises ArgumentError do
+ root_path(params)
+ end
+ end
+
+ def test_passing_action_parameters_to_url_helpers_is_allowed_if_parameters_are_permitted
+ draw do
+ root :to => 'projects#index'
+ end
+ params = ActionController::Parameters.new(id: '1')
+ params.permit!
+
+ assert_equal '/?id=1', root_path(params)
+ end
+
private
def draw(&block)
@@ -4633,3 +4654,66 @@ class TestErrorsInController < ActionDispatch::IntegrationTest
assert_equal 404, response.status
end
end
+
+class TestPartialDynamicPathSegments < ActionDispatch::IntegrationTest
+ Routes = ActionDispatch::Routing::RouteSet.new
+ Routes.draw do
+ ok = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, []] }
+
+ get '/songs/song-:song', to: ok
+ get '/songs/:song-song', to: ok
+ get '/:artist/song-:song', to: ok
+ get '/:artist/:song-song', to: ok
+
+ get '/optional/songs(/song-:song)', to: ok
+ get '/optional/songs(/:song-song)', to: ok
+ get '/optional/:artist(/song-:song)', to: ok
+ get '/optional/:artist(/:song-song)', to: ok
+ end
+
+ APP = build_app Routes
+
+ def app
+ APP
+ end
+
+ def test_paths_with_partial_dynamic_segments_are_recognised
+ get '/david-bowie/changes-song'
+ assert_equal 200, response.status
+ assert_params artist: 'david-bowie', song: 'changes'
+
+ get '/david-bowie/song-changes'
+ assert_equal 200, response.status
+ assert_params artist: 'david-bowie', song: 'changes'
+
+ get '/songs/song-changes'
+ assert_equal 200, response.status
+ assert_params song: 'changes'
+
+ get '/songs/changes-song'
+ assert_equal 200, response.status
+ assert_params song: 'changes'
+
+ get '/optional/songs/song-changes'
+ assert_equal 200, response.status
+ assert_params song: 'changes'
+
+ get '/optional/songs/changes-song'
+ assert_equal 200, response.status
+ assert_params song: 'changes'
+
+ get '/optional/david-bowie/changes-song'
+ assert_equal 200, response.status
+ assert_params artist: 'david-bowie', song: 'changes'
+
+ get '/optional/david-bowie/song-changes'
+ assert_equal 200, response.status
+ assert_params artist: 'david-bowie', song: 'changes'
+ end
+
+ private
+
+ def assert_params(params)
+ assert_equal(params, request.path_parameters)
+ end
+end
diff --git a/actionpack/test/dispatch/ssl_test.rb b/actionpack/test/dispatch/ssl_test.rb
index 7a5b8393dc..c66a0e6a7a 100644
--- a/actionpack/test/dispatch/ssl_test.rb
+++ b/actionpack/test/dispatch/ssl_test.rb
@@ -12,25 +12,31 @@ class SSLTest < ActionDispatch::IntegrationTest
end
class RedirectSSLTest < SSLTest
- def assert_not_redirected(url, headers: {})
- self.app = build_app
+
+ def assert_not_redirected(url, headers: {}, redirect: {}, deprecated_host: nil,
+ deprecated_port: nil)
+
+ self.app = build_app ssl_options: { redirect: redirect,
+ host: deprecated_host, port: deprecated_port
+ }
+
get url, headers: headers
assert_response :ok
end
- def assert_redirected(host: nil, port: nil, status: 301, body: [],
- deprecated_host: nil, deprecated_port: nil,
+ def assert_redirected(redirect: {}, deprecated_host: nil, deprecated_port: nil,
from: 'http://a/b?c=d', to: from.sub('http', 'https'))
- self.app = build_app ssl_options: {
- redirect: { host: host, port: port, status: status, body: body },
+ redirect = { status: 301, body: [] }.merge(redirect)
+
+ self.app = build_app ssl_options: { redirect: redirect,
host: deprecated_host, port: deprecated_port
}
get from
- assert_response status
+ assert_response redirect[:status] || 301
assert_redirected_to to
- assert_equal body.join, @response.body
+ assert_equal redirect[:body].join, @response.body
end
test 'https is not redirected' do
@@ -46,31 +52,31 @@ class RedirectSSLTest < SSLTest
end
test 'redirect with non-301 status' do
- assert_redirected status: 307
+ assert_redirected redirect: { status: 307 }
end
test 'redirect with custom body' do
- assert_redirected body: ['foo']
+ assert_redirected redirect: { body: ['foo'] }
end
test 'redirect to specific host' do
- assert_redirected host: 'ssl', to: 'https://ssl/b?c=d'
+ assert_redirected redirect: { host: 'ssl' }, to: 'https://ssl/b?c=d'
end
test 'redirect to default port' do
- assert_redirected port: 443
+ assert_redirected redirect: { port: 443 }
end
test 'redirect to non-default port' do
- assert_redirected port: 8443, to: 'https://a:8443/b?c=d'
+ assert_redirected redirect: { port: 8443 }, to: 'https://a:8443/b?c=d'
end
test 'redirect to different host and non-default port' do
- assert_redirected host: 'ssl', port: 8443, to: 'https://ssl:8443/b?c=d'
+ assert_redirected redirect: { host: 'ssl', port: 8443 }, to: 'https://ssl:8443/b?c=d'
end
test 'redirect to different host including port' do
- assert_redirected host: 'ssl:443', to: 'https://ssl:443/b?c=d'
+ assert_redirected redirect: { host: 'ssl:443' }, to: 'https://ssl:443/b?c=d'
end
test ':host is deprecated, moved within redirect: { host: … }' do
@@ -84,6 +90,10 @@ class RedirectSSLTest < SSLTest
assert_redirected deprecated_port: 1, to: 'https://a:1/b?c=d'
end
end
+
+ test 'no redirect with redirect set to false' do
+ assert_not_redirected 'http://example.org', redirect: false
+ end
end
class StrictTransportSecurityTest < SSLTest
@@ -187,6 +197,11 @@ class SecureCookiesTest < SSLTest
assert_cookies 'problem=def; path=/; Secure; HttpOnly'
end
+ def test_cookies_as_not_secure_with_secure_cookies_disabled
+ get headers: { 'Set-Cookie' => DEFAULT }, ssl_options: { secure_cookies: false }
+ assert_cookies(*DEFAULT.split("\n"))
+ end
+
def test_no_cookies
get
assert_nil response.headers['Set-Cookie']
diff --git a/actionpack/test/dispatch/static_test.rb b/actionpack/test/dispatch/static_test.rb
index 1da57ab50b..ea8b5e904e 100644
--- a/actionpack/test/dispatch/static_test.rb
+++ b/actionpack/test/dispatch/static_test.rb
@@ -40,6 +40,10 @@ module StaticTests
assert_equal "Hello, World!", get("/doorkeeper%E3E4".force_encoding('ASCII-8BIT')).body
end
+ def test_handles_urls_with_null_byte
+ assert_equal "Hello, World!", get("/doorkeeper%00").body
+ end
+
def test_sets_cache_control
app = assert_deprecated do
ActionDispatch::Static.new(DummyApp, @root, "public, max-age=60")
diff --git a/actionpack/test/fixtures/functional_caching/fragment_cached_with_options.html.erb b/actionpack/test/fixtures/functional_caching/fragment_cached_with_options.html.erb
index 6865df9b7e..01453323ef 100644
--- a/actionpack/test/fixtures/functional_caching/fragment_cached_with_options.html.erb
+++ b/actionpack/test/fixtures/functional_caching/fragment_cached_with_options.html.erb
@@ -1,3 +1,3 @@
<body>
-<%= cache 'with_options', :skip_digest => true, :expires_in => 1.minute do %><p>ERB</p><% end %>
+<%= cache 'with_options', skip_digest: true, expires_in: 1.minute do %><p>ERB</p><% end %>
</body>