diff options
Diffstat (limited to 'actionpack/test')
73 files changed, 1868 insertions, 1612 deletions
diff --git a/actionpack/test/abstract/collector_test.rb b/actionpack/test/abstract/collector_test.rb index fc59bf19c4..edbb84d462 100644 --- a/actionpack/test/abstract/collector_test.rb +++ b/actionpack/test/abstract/collector_test.rb @@ -53,9 +53,9 @@ module AbstractController collector.html collector.text(:foo) collector.js(:bar) { :baz } - assert_equal [Mime::HTML, [], nil], collector.responses[0] - assert_equal [Mime::TEXT, [:foo], nil], collector.responses[1] - assert_equal [Mime::JS, [:bar]], collector.responses[2][0,2] + assert_equal [Mime[:html], [], nil], collector.responses[0] + assert_equal [Mime[:text], [:foo], nil], collector.responses[1] + assert_equal [Mime[:js], [:bar]], collector.responses[2][0,2] assert_equal :baz, collector.responses[2][2].call end end diff --git a/actionpack/test/abstract/translation_test.rb b/actionpack/test/abstract/translation_test.rb index 8289252dfc..1435928578 100644 --- a/actionpack/test/abstract/translation_test.rb +++ b/actionpack/test/abstract/translation_test.rb @@ -24,7 +24,6 @@ module AbstractController }, }, }) - @controller.stubs(action_name: :index) end def test_action_controller_base_responds_to_translate @@ -44,25 +43,34 @@ module AbstractController end def test_lazy_lookup - assert_equal 'bar', @controller.t('.foo') + @controller.stub :action_name, :index do + assert_equal 'bar', @controller.t('.foo') + end end def test_lazy_lookup_with_symbol - assert_equal 'bar', @controller.t(:'.foo') + @controller.stub :action_name, :index do + assert_equal 'bar', @controller.t(:'.foo') + end end def test_lazy_lookup_fallback - assert_equal 'no_action_tr', @controller.t(:'.no_action') + @controller.stub :action_name, :index do + assert_equal 'no_action_tr', @controller.t(:'.no_action') + end end def test_default_translation - assert_equal 'bar', @controller.t('one.two') + @controller.stub :action_name, :index do + assert_equal 'bar', @controller.t('one.two') + end end def test_localize time, expected = Time.gm(2000), 'Sat, 01 Jan 2000 00:00:00 +0000' - I18n.stubs(:localize).with(time).returns(expected) - assert_equal expected, @controller.l(time) + I18n.stub :localize, expected do + assert_equal expected, @controller.l(time) + end end end end diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index cc610b6d75..ef7aab72c6 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -19,7 +19,6 @@ begin rescue LoadError puts "'drb/unix' is not available" end -require 'tempfile' PROCESS_COUNT = (ENV['N'] || 4).to_i @@ -42,6 +41,8 @@ module Rails def env @_env ||= ActiveSupport::StringInquirer.new(ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "test") end + + def root; end; end end @@ -63,6 +64,10 @@ FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), 'fixtures') SharedTestRoutes = ActionDispatch::Routing::RouteSet.new +SharedTestRoutes.draw do + get ':controller(/:action)' +end + module ActionDispatch module SharedRoutes def before_setup @@ -70,35 +75,10 @@ module ActionDispatch super end end - - # Hold off drawing routes until all the possible controller classes - # have been loaded. - module DrawOnce - class << self - attr_accessor :drew - end - self.drew = false - - def before_setup - super - return if DrawOnce.drew - - SharedTestRoutes.draw do - get ':controller(/:action)' - end - - ActionDispatch::IntegrationTest.app.routes.draw do - get ':controller(/:action)' - end - - DrawOnce.drew = true - end - end end module ActiveSupport class TestCase - include ActionDispatch::DrawOnce if RUBY_ENGINE == "ruby" && PROCESS_COUNT > 0 parallelize_me! end @@ -119,44 +99,54 @@ class RoutedRackApp end class ActionDispatch::IntegrationTest < ActiveSupport::TestCase - include ActionDispatch::SharedRoutes - def self.build_app(routes = nil) RoutedRackApp.new(routes || ActionDispatch::Routing::RouteSet.new) do |middleware| - middleware.use "ActionDispatch::ShowExceptions", ActionDispatch::PublicExceptions.new("#{FIXTURE_LOAD_PATH}/public") - middleware.use "ActionDispatch::DebugExceptions" - middleware.use "ActionDispatch::Callbacks" - middleware.use "ActionDispatch::ParamsParser" - middleware.use "ActionDispatch::Cookies" - middleware.use "ActionDispatch::Flash" - middleware.use "Rack::Head" + middleware.use ActionDispatch::ShowExceptions, ActionDispatch::PublicExceptions.new("#{FIXTURE_LOAD_PATH}/public") + middleware.use ActionDispatch::DebugExceptions + middleware.use ActionDispatch::Callbacks + middleware.use ActionDispatch::Cookies + middleware.use ActionDispatch::Flash + middleware.use Rack::Head yield(middleware) if block_given? end end self.app = build_app - # Stub Rails dispatcher so it does not get controller references and - # simply return the controller#action as Rack::Body. - class StubDispatcher < ::ActionDispatch::Routing::RouteSet::Dispatcher - protected - def controller_reference(controller_param) - controller_param + app.routes.draw do + get ':controller(/:action)' + end + + class DeadEndRoutes < ActionDispatch::Routing::RouteSet + # Stub Rails dispatcher so it does not get controller references and + # simply return the controller#action as Rack::Body. + class NullController < ::ActionController::Metal + def initialize(controller_name) + @controller = controller_name + end + + def make_response!(request) + self.class.make_response! request + end + + def dispatch(action, req, res) + [200, {'Content-Type' => 'text/html'}, ["#{@controller}##{action}"]] + end end - def dispatch(controller, action, env) - [200, {'Content-Type' => 'text/html'}, ["#{controller}##{action}"]] + class NullControllerRequest < DelegateClass(ActionDispatch::Request) + def controller_class + NullController.new params[:controller] + end + end + + def make_request env + NullControllerRequest.new super end end - def self.stub_controllers - old_dispatcher = ActionDispatch::Routing::RouteSet::Dispatcher - ActionDispatch::Routing::RouteSet.module_eval { remove_const :Dispatcher } - ActionDispatch::Routing::RouteSet.module_eval { const_set :Dispatcher, StubDispatcher } - yield ActionDispatch::Routing::RouteSet.new - ensure - ActionDispatch::Routing::RouteSet.module_eval { remove_const :Dispatcher } - ActionDispatch::Routing::RouteSet.module_eval { const_set :Dispatcher, old_dispatcher } + def self.stub_controllers(config = ActionDispatch::Routing::RouteSet::DEFAULT_CONFIG) + yield DeadEndRoutes.new(config) end def with_routing(&block) @@ -346,39 +336,37 @@ module RoutingTestHelpers end class TestSet < ActionDispatch::Routing::RouteSet - attr_reader :strict - - def initialize(block, strict = false) - @block = block - @strict = strict - super() - end - - class Dispatcher < ActionDispatch::Routing::RouteSet::Dispatcher - def initialize(defaults, set, block) - super(defaults) + class Request < DelegateClass(ActionDispatch::Request) + def initialize(target, helpers, block, strict) + super(target) + @helpers = helpers @block = block - @set = set - end - - def controller(params, default_controller=true) - super(params, @set.strict) + @strict = strict end - def controller_reference(controller_param) + def controller_class + helpers = @helpers block = @block - set = @set - super if @set.strict - Class.new(ActionController::Base) { - include set.url_helpers + Class.new(@strict ? super : ActionController::Base) { + include helpers define_method(:process) { |name| block.call(self) } def to_a; [200, {}, []]; end } end end - def dispatcher defaults - TestSet::Dispatcher.new defaults, self, @block + attr_reader :strict + + def initialize(block, strict = false) + @block = block + @strict = strict + super() + end + + private + + def make_request(env) + Request.new super, url_helpers, @block, strict end end end @@ -392,13 +380,11 @@ class ThreadsController < ResourcesController; end class MessagesController < ResourcesController; end class CommentsController < ResourcesController; end class ReviewsController < ResourcesController; end -class LogosController < ResourcesController; end class AccountsController < ResourcesController; end class AdminController < ResourcesController; end class ProductsController < ResourcesController; end class ImagesController < ResourcesController; end -class PreferencesController < ResourcesController; end module Backoffice class ProductsController < ResourcesController; end @@ -419,7 +405,7 @@ def jruby_skip(message = '') skip message if defined?(JRUBY_VERSION) end -require 'mocha/setup' # FIXME: stop using mocha +require 'active_support/testing/method_call_assertions' class ForkingExecutor class Server @@ -491,3 +477,7 @@ if RUBY_ENGINE == "ruby" && PROCESS_COUNT > 0 # Use N processes (N defaults to 4) Minitest.parallel_executor = ForkingExecutor.new(PROCESS_COUNT) end + +class ActiveSupport::TestCase + include ActiveSupport::Testing::MethodCallAssertions +end diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb index beeafc2e53..899d92f815 100644 --- a/actionpack/test/controller/action_pack_assertions_test.rb +++ b/actionpack/test/controller/action_pack_assertions_test.rb @@ -65,7 +65,7 @@ class ActionPackAssertionsController < ActionController::Base end def render_text_with_custom_content_type - render body: "Hello!", content_type: Mime::RSS + render body: "Hello!", content_type: Mime[:rss] end def session_stuffing diff --git a/actionpack/test/controller/base_test.rb b/actionpack/test/controller/base_test.rb index d9374ce9c3..fb60dbd993 100644 --- a/actionpack/test/controller/base_test.rb +++ b/actionpack/test/controller/base_test.rb @@ -93,6 +93,8 @@ end class ControllerInstanceTests < ActiveSupport::TestCase def setup @empty = EmptyController.new + @empty.set_request!(ActionDispatch::Request.new({})) + @empty.set_response!(EmptyController.make_response!(@empty.request)) @contained = Submodule::ContainedEmptyController.new @empty_controllers = [@empty, @contained] end diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb index 5698159eba..bc0ffd3eaa 100644 --- a/actionpack/test/controller/caching_test.rb +++ b/actionpack/test/controller/caching_test.rb @@ -299,30 +299,42 @@ class CacheHelperOutputBufferTest < ActionController::TestCase def test_output_buffer output_buffer = ActionView::OutputBuffer.new controller = MockController.new - cache_helper = Object.new + cache_helper = Class.new do + def self.controller; end; + def self.output_buffer; end; + def self.output_buffer=; end; + end cache_helper.extend(ActionView::Helpers::CacheHelper) - cache_helper.expects(:controller).returns(controller).at_least(0) - cache_helper.expects(:output_buffer).returns(output_buffer).at_least(0) - # if the output_buffer is changed, the new one should be html_safe and of the same type - cache_helper.expects(:output_buffer=).with(responds_with(:html_safe?, true)).with(instance_of(output_buffer.class)).at_least(0) - assert_nothing_raised do - cache_helper.send :fragment_for, 'Test fragment name', 'Test fragment', &Proc.new{ nil } + cache_helper.stub :controller, controller do + cache_helper.stub :output_buffer, output_buffer do + assert_called_with cache_helper, :output_buffer=, [output_buffer.class.new(output_buffer)] do + assert_nothing_raised do + cache_helper.send :fragment_for, 'Test fragment name', 'Test fragment', &Proc.new{ nil } + end + end + end end end def test_safe_buffer output_buffer = ActiveSupport::SafeBuffer.new controller = MockController.new - cache_helper = Object.new + cache_helper = Class.new do + def self.controller; end; + def self.output_buffer; end; + def self.output_buffer=; end; + end cache_helper.extend(ActionView::Helpers::CacheHelper) - cache_helper.expects(:controller).returns(controller).at_least(0) - cache_helper.expects(:output_buffer).returns(output_buffer).at_least(0) - # if the output_buffer is changed, the new one should be html_safe and of the same type - cache_helper.expects(:output_buffer=).with(responds_with(:html_safe?, true)).with(instance_of(output_buffer.class)).at_least(0) - assert_nothing_raised do - cache_helper.send :fragment_for, 'Test fragment name', 'Test fragment', &Proc.new{ nil } + cache_helper.stub :controller, controller do + cache_helper.stub :output_buffer, output_buffer do + assert_called_with cache_helper, :output_buffer=, [output_buffer.class.new(output_buffer)] do + assert_nothing_raised do + cache_helper.send :fragment_for, 'Test fragment name', 'Test fragment', &Proc.new{ nil } + end + end + end end end end diff --git a/actionpack/test/controller/content_type_test.rb b/actionpack/test/controller/content_type_test.rb index c5bbc479c9..c02607b55e 100644 --- a/actionpack/test/controller/content_type_test.rb +++ b/actionpack/test/controller/content_type_test.rb @@ -3,7 +3,7 @@ require 'abstract_unit' class OldContentTypeController < ActionController::Base # :ported: def render_content_type_from_body - response.content_type = Mime::RSS + response.content_type = Mime[:rss] render body: "hello world!" end @@ -14,7 +14,7 @@ class OldContentTypeController < ActionController::Base # :ported: def render_content_type_from_render - render body: "hello world!", :content_type => Mime::RSS + render body: "hello world!", :content_type => Mime[:rss] end # :ported: @@ -36,7 +36,7 @@ class OldContentTypeController < ActionController::Base end def render_change_for_builder - response.content_type = Mime::HTML + response.content_type = Mime[:html] render :action => "render_default_for_builder" end @@ -45,7 +45,7 @@ class OldContentTypeController < ActionController::Base format.html { render body: "hello world!" } format.xml { render action: "render_default_content_types_for_respond_to" } format.js { render body: "hello world!" } - format.rss { render body: "hello world!", content_type: Mime::XML } + format.rss { render body: "hello world!", content_type: Mime[:xml] } end end end @@ -64,68 +64,68 @@ class ContentTypeTest < ActionController::TestCase def test_render_defaults get :render_defaults assert_equal "utf-8", @response.charset - assert_equal Mime::TEXT, @response.content_type + assert_equal Mime[:text], @response.content_type end def test_render_changed_charset_default with_default_charset "utf-16" do get :render_defaults assert_equal "utf-16", @response.charset - assert_equal Mime::TEXT, @response.content_type + assert_equal Mime[:text], @response.content_type end end # :ported: def test_content_type_from_body get :render_content_type_from_body - assert_equal Mime::RSS, @response.content_type + assert_equal Mime[:rss], @response.content_type assert_equal "utf-8", @response.charset end # :ported: def test_content_type_from_render get :render_content_type_from_render - assert_equal Mime::RSS, @response.content_type + assert_equal Mime[:rss], @response.content_type assert_equal "utf-8", @response.charset end # :ported: def test_charset_from_body get :render_charset_from_body - assert_equal Mime::TEXT, @response.content_type + assert_equal Mime[:text], @response.content_type assert_equal "utf-16", @response.charset end # :ported: def test_nil_charset_from_body get :render_nil_charset_from_body - assert_equal Mime::TEXT, @response.content_type + assert_equal Mime[:text], @response.content_type assert_equal "utf-8", @response.charset, @response.headers.inspect end def test_nil_default_for_erb with_default_charset nil do get :render_default_for_erb - assert_equal Mime::HTML, @response.content_type + assert_equal Mime[:html], @response.content_type assert_nil @response.charset, @response.headers.inspect end end def test_default_for_erb get :render_default_for_erb - assert_equal Mime::HTML, @response.content_type + assert_equal Mime[:html], @response.content_type assert_equal "utf-8", @response.charset end def test_default_for_builder get :render_default_for_builder - assert_equal Mime::XML, @response.content_type + assert_equal Mime[:xml], @response.content_type assert_equal "utf-8", @response.charset end def test_change_for_builder get :render_change_for_builder - assert_equal Mime::HTML, @response.content_type + assert_equal Mime[:html], @response.content_type assert_equal "utf-8", @response.charset end @@ -144,24 +144,24 @@ class AcceptBasedContentTypeTest < ActionController::TestCase tests OldContentTypeController def test_render_default_content_types_for_respond_to - @request.accept = Mime::HTML.to_s + @request.accept = Mime[:html].to_s get :render_default_content_types_for_respond_to - assert_equal Mime::HTML, @response.content_type + assert_equal Mime[:html], @response.content_type - @request.accept = Mime::JS.to_s + @request.accept = Mime[:js].to_s get :render_default_content_types_for_respond_to - assert_equal Mime::JS, @response.content_type + assert_equal Mime[:js], @response.content_type end def test_render_default_content_types_for_respond_to_with_template - @request.accept = Mime::XML.to_s + @request.accept = Mime[:xml].to_s get :render_default_content_types_for_respond_to - assert_equal Mime::XML, @response.content_type + assert_equal Mime[:xml], @response.content_type end def test_render_default_content_types_for_respond_to_with_overwrite - @request.accept = Mime::RSS.to_s + @request.accept = Mime[:rss].to_s get :render_default_content_types_for_respond_to - assert_equal Mime::XML, @response.content_type + assert_equal Mime[:xml], @response.content_type end end diff --git a/actionpack/test/controller/flash_test.rb b/actionpack/test/controller/flash_test.rb index 64543f0659..b063d769a4 100644 --- a/actionpack/test/controller/flash_test.rb +++ b/actionpack/test/controller/flash_test.rb @@ -329,7 +329,7 @@ class FlashIntegrationTest < ActionDispatch::IntegrationTest @app = self.class.build_app(set) do |middleware| middleware.use ActionDispatch::Session::CookieStore, :key => SessionKey middleware.use ActionDispatch::Flash - middleware.delete "ActionDispatch::ShowExceptions" + middleware.delete ActionDispatch::ShowExceptions end yield diff --git a/actionpack/test/controller/http_basic_authentication_test.rb b/actionpack/test/controller/http_basic_authentication_test.rb index ed3632007d..0a5e5402b9 100644 --- a/actionpack/test/controller/http_basic_authentication_test.rb +++ b/actionpack/test/controller/http_basic_authentication_test.rb @@ -100,6 +100,14 @@ class HttpBasicAuthenticationTest < ActionController::TestCase assert_no_match(/\n/, result) end + test "succesful authentication with uppercase authorization scheme" do + @request.env['HTTP_AUTHORIZATION'] = "BASIC #{::Base64.encode64("lifo:world")}" + get :index + + assert_response :success + assert_equal 'Hello Secret', @response.body, 'Authentication failed when authorization scheme BASIC' + end + test "authentication request without credential" do get :display diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb index 0b9be8d671..d0a1d1285f 100644 --- a/actionpack/test/controller/integration_test.rb +++ b/actionpack/test/controller/integration_test.rb @@ -26,289 +26,335 @@ class SessionTest < ActiveSupport::TestCase end def test_follow_redirect_raises_when_no_redirect - @session.stubs(:redirect?).returns(false) - assert_raise(RuntimeError) { @session.follow_redirect! } + @session.stub :redirect?, false do + assert_raise(RuntimeError) { @session.follow_redirect! } + end end def test_request_via_redirect_uses_given_method path = "/somepath"; args = {:id => '1'}; headers = {"X-Test-Header" => "testvalue"} - @session.expects(:process).with(:put, path, params: args, headers: headers) - @session.stubs(:redirect?).returns(false) - @session.request_via_redirect(:put, path, params: args, headers: headers) + assert_called_with @session, :process, [:put, path, params: args, headers: headers] do + @session.stub :redirect?, false do + @session.request_via_redirect(:put, path, params: args, headers: headers) + end + end end def test_deprecated_request_via_redirect_uses_given_method path = "/somepath"; args = { id: '1' }; headers = { "X-Test-Header" => "testvalue" } - @session.expects(:process).with(:put, path, params: args, headers: headers) - @session.stubs(:redirect?).returns(false) - assert_deprecated { @session.request_via_redirect(:put, path, args, headers) } + assert_called_with @session, :process, [:put, path, params: args, headers: headers] do + @session.stub :redirect?, false do + assert_deprecated { @session.request_via_redirect(:put, path, args, headers) } + end + end end def test_request_via_redirect_follows_redirects path = "/somepath"; args = {:id => '1'}; headers = {"X-Test-Header" => "testvalue"} - @session.stubs(:redirect?).returns(true, true, false) - @session.expects(:follow_redirect!).times(2) - @session.request_via_redirect(:get, path, params: args, headers: headers) + value_series = [true, true, false] + assert_called @session, :follow_redirect!, times: 2 do + @session.stub :redirect?, ->{ value_series.shift } do + @session.request_via_redirect(:get, path, params: args, headers: headers) + end + end end def test_request_via_redirect_returns_status path = "/somepath"; args = {:id => '1'}; headers = {"X-Test-Header" => "testvalue"} - @session.stubs(:redirect?).returns(false) - @session.stubs(:status).returns(200) - assert_equal 200, @session.request_via_redirect(:get, path, params: args, headers: headers) + @session.stub :redirect?, false do + @session.stub :status, 200 do + assert_equal 200, @session.request_via_redirect(:get, path, params: args, headers: headers) + end + end end def test_deprecated_get_via_redirect path = "/somepath"; args = { id: '1' }; headers = { "X-Test-Header" => "testvalue" } - @session.expects(:request_via_redirect).with(:get, path, args, headers) - assert_deprecated do - @session.get_via_redirect(path, args, headers) + assert_called_with @session, :request_via_redirect, [:get, path, args, headers] do + assert_deprecated do + @session.get_via_redirect(path, args, headers) + end end end def test_deprecated_post_via_redirect path = "/somepath"; args = { id: '1' }; headers = { "X-Test-Header" => "testvalue" } - @session.expects(:request_via_redirect).with(:post, path, args, headers) - assert_deprecated do - @session.post_via_redirect(path, args, headers) + assert_called_with @session, :request_via_redirect, [:post, path, args, headers] do + assert_deprecated do + @session.post_via_redirect(path, args, headers) + end end end def test_deprecated_patch_via_redirect path = "/somepath"; args = { id: '1' }; headers = { "X-Test-Header" => "testvalue" } - @session.expects(:request_via_redirect).with(:patch, path, args, headers) - assert_deprecated do - @session.patch_via_redirect(path, args, headers) + assert_called_with @session, :request_via_redirect, [:patch, path, args, headers] do + assert_deprecated do + @session.patch_via_redirect(path, args, headers) + end end end def test_deprecated_put_via_redirect path = "/somepath"; args = { id: '1' }; headers = { "X-Test-Header" => "testvalue" } - @session.expects(:request_via_redirect).with(:put, path, args, headers) - assert_deprecated do - @session.put_via_redirect(path, args, headers) + assert_called_with @session, :request_via_redirect, [:put, path, args, headers] do + assert_deprecated do + @session.put_via_redirect(path, args, headers) + end end end def test_deprecated_delete_via_redirect path = "/somepath"; args = { id: '1' }; headers = { "X-Test-Header" => "testvalue" } - @session.expects(:request_via_redirect).with(:delete, path, args, headers) - assert_deprecated do - @session.delete_via_redirect(path, args, headers) + assert_called_with @session, :request_via_redirect, [:delete, path, args, headers] do + assert_deprecated do + @session.delete_via_redirect(path, args, headers) + end end end def test_get path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:get, path, params: params, headers: headers) - @session.get(path, params: params, headers: headers) + + assert_called_with @session, :process, [:get, path, params: params, headers: headers] do + @session.get(path, params: params, headers: headers) + end end def test_get_with_env_and_headers path = "/index"; params = "blah"; headers = { location: 'blah' }; env = { 'HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest' } - @session.expects(:process).with(:get, path, params: params, headers: headers, env: env) - @session.get(path, params: params, headers: headers, env: env) + assert_called_with @session, :process, [:get, path, params: params, headers: headers, env: env] do + @session.get(path, params: params, headers: headers, env: env) + end end def test_deprecated_get path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:get, path, params: params, headers: headers) - assert_deprecated { - @session.get(path, params, headers) - } + + assert_called_with @session, :process, [:get, path, params: params, headers: headers] do + assert_deprecated { + @session.get(path, params, headers) + } + end end def test_post path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:post, path, params: params, headers: headers) - assert_deprecated { - @session.post(path, params, headers) - } + assert_called_with @session, :process, [:post, path, params: params, headers: headers] do + assert_deprecated { + @session.post(path, params, headers) + } + end end def test_deprecated_post path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:post, path, params: params, headers: headers) - @session.post(path, params: params, headers: headers) + assert_called_with @session, :process, [:post, path, params: params, headers: headers] do + @session.post(path, params: params, headers: headers) + end end def test_patch path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:patch, path, params: params, headers: headers) - @session.patch(path, params: params, headers: headers) + assert_called_with @session, :process, [:patch, path, params: params, headers: headers] do + @session.patch(path, params: params, headers: headers) + end end def test_deprecated_patch path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:patch, path, params: params, headers: headers) - assert_deprecated { - @session.patch(path, params, headers) - } + assert_called_with @session, :process, [:patch, path, params: params, headers: headers] do + assert_deprecated { + @session.patch(path, params, headers) + } + end end def test_put path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:put, path, params: params, headers: headers) - @session.put(path, params: params, headers: headers) + assert_called_with @session, :process, [:put, path, params: params, headers: headers] do + @session.put(path, params: params, headers: headers) + end end def test_deprecated_put path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:put, path, params: params, headers: headers) - assert_deprecated { - @session.put(path, params, headers) - } + assert_called_with @session, :process, [:put, path, params: params, headers: headers] do + assert_deprecated { + @session.put(path, params, headers) + } + end end def test_delete path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:delete, path, params: params, headers: headers) - assert_deprecated { - @session.delete(path,params,headers) - } + assert_called_with @session, :process, [:delete, path, params: params, headers: headers] do + assert_deprecated { + @session.delete(path,params,headers) + } + end end def test_deprecated_delete path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:delete, path, params: params, headers: headers) - @session.delete(path, params: params, headers: headers) + assert_called_with @session, :process, [:delete, path, params: params, headers: headers] do + @session.delete(path, params: params, headers: headers) + end end def test_head path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:head, path, params: params, headers: headers) - @session.head(path, params: params, headers: headers) + assert_called_with @session, :process, [:head, path, params: params, headers: headers] do + @session.head(path, params: params, headers: headers) + end end def deprecated_test_head path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:head, path, params: params, headers: headers) - assert_deprecated { - @session.head(path, params, headers) - } + assert_called_with @session, :process, [:head, path, params: params, headers: headers] do + assert_deprecated { + @session.head(path, params, headers) + } + end end def test_xml_http_request_get path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:get, path, params: params, headers: headers, xhr: true) - @session.get(path, params: params, headers: headers, xhr: true) + assert_called_with @session, :process, [:get, path, params: params, headers: headers, xhr: true] do + @session.get(path, params: params, headers: headers, xhr: true) + end end def test_deprecated_xml_http_request_get path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:get, path, params: params, headers: headers, xhr: true) - @session.get(path, params: params, headers: headers, xhr: true) + assert_called_with @session, :process, [:get, path, params: params, headers: headers, xhr: true] do + @session.get(path, params: params, headers: headers, xhr: true) + end end def test_deprecated_args_xml_http_request_get path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:get, path, params: params, headers: headers, xhr: true) - assert_deprecated(/xml_http_request/) { - @session.xml_http_request(:get, path, params, headers) - } + assert_called_with @session, :process, [:get, path, params: params, headers: headers, xhr: true] do + assert_deprecated(/xml_http_request/) { + @session.xml_http_request(:get, path, params, headers) + } + end end def test_xml_http_request_post path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:post, path, params: params, headers: headers, xhr: true) - @session.post(path, params: params, headers: headers, xhr: true) + assert_called_with @session, :process, [:post, path, params: params, headers: headers, xhr: true] do + @session.post(path, params: params, headers: headers, xhr: true) + end end def test_deprecated_xml_http_request_post path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:post, path, params: params, headers: headers, xhr: true) - @session.post(path, params: params, headers: headers, xhr: true) + assert_called_with @session, :process, [:post, path, params: params, headers: headers, xhr: true] do + @session.post(path, params: params, headers: headers, xhr: true) + end end def test_deprecated_args_xml_http_request_post path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:post, path, params: params, headers: headers, xhr: true) - assert_deprecated(/xml_http_request/) { @session.xml_http_request(:post,path,params,headers) } + assert_called_with @session, :process, [:post, path, params: params, headers: headers, xhr: true] do + assert_deprecated(/xml_http_request/) { @session.xml_http_request(:post,path,params,headers) } + end end def test_xml_http_request_patch path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:patch, path, params: params, headers: headers, xhr: true) - @session.patch(path, params: params, headers: headers, xhr: true) + assert_called_with @session, :process, [:patch, path, params: params, headers: headers, xhr: true] do + @session.patch(path, params: params, headers: headers, xhr: true) + end end def test_deprecated_xml_http_request_patch path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:patch, path, params: params, headers: headers, xhr: true) - @session.patch(path, params: params, headers: headers, xhr: true) + assert_called_with @session, :process, [:patch, path, params: params, headers: headers, xhr: true] do + @session.patch(path, params: params, headers: headers, xhr: true) + end end def test_deprecated_args_xml_http_request_patch path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:patch, path, params: params, headers: headers, xhr: true) - assert_deprecated(/xml_http_request/) { @session.xml_http_request(:patch,path,params,headers) } + assert_called_with @session, :process, [:patch, path, params: params, headers: headers, xhr: true] do + assert_deprecated(/xml_http_request/) { @session.xml_http_request(:patch,path,params,headers) } + end end def test_xml_http_request_put path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:put, path, params: params, headers: headers, xhr: true) - @session.put(path, params: params, headers: headers, xhr: true) + assert_called_with @session, :process, [:put, path, params: params, headers: headers, xhr: true] do + @session.put(path, params: params, headers: headers, xhr: true) + end end def test_deprecated_xml_http_request_put path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:put, path, params: params, headers: headers, xhr: true) - @session.put(path, params: params, headers: headers, xhr: true) + assert_called_with @session, :process, [:put, path, params: params, headers: headers, xhr: true] do + @session.put(path, params: params, headers: headers, xhr: true) + end end def test_deprecated_args_xml_http_request_put path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:put, path, params: params, headers: headers, xhr: true) - assert_deprecated(/xml_http_request/) { @session.xml_http_request(:put, path, params, headers) } + assert_called_with @session, :process, [:put, path, params: params, headers: headers, xhr: true] do + assert_deprecated(/xml_http_request/) { @session.xml_http_request(:put, path, params, headers) } + end end def test_xml_http_request_delete path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:delete, path, params: params, headers: headers, xhr: true) - @session.delete(path, params: params, headers: headers, xhr: true) + assert_called_with @session, :process, [:delete, path, params: params, headers: headers, xhr: true] do + @session.delete(path, params: params, headers: headers, xhr: true) + end end def test_deprecated_xml_http_request_delete path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:delete, path, params: params, headers: headers, xhr: true) - assert_deprecated { @session.xml_http_request(:delete, path, params: params, headers: headers) } + assert_called_with @session, :process, [:delete, path, params: params, headers: headers, xhr: true] do + assert_deprecated { @session.xml_http_request(:delete, path, params: params, headers: headers) } + end end def test_deprecated_args_xml_http_request_delete path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:delete, path, params: params, headers: headers, xhr: true) - assert_deprecated(/xml_http_request/) { @session.xml_http_request(:delete, path, params, headers) } + assert_called_with @session, :process, [:delete, path, params: params, headers: headers, xhr: true] do + assert_deprecated(/xml_http_request/) { @session.xml_http_request(:delete, path, params, headers) } + end end def test_xml_http_request_head path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:head, path, params: params, headers: headers, xhr: true) - @session.head(path, params: params, headers: headers, xhr: true) + assert_called_with @session, :process, [:head, path, params: params, headers: headers, xhr: true] do + @session.head(path, params: params, headers: headers, xhr: true) + end end def test_deprecated_xml_http_request_head path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:head, path, params: params, headers: headers, xhr: true) - assert_deprecated(/xml_http_request/) { @session.xml_http_request(:head, path, params: params, headers: headers) } + assert_called_with @session, :process, [:head, path, params: params, headers: headers, xhr: true] do + assert_deprecated(/xml_http_request/) { @session.xml_http_request(:head, path, params: params, headers: headers) } + end end def test_deprecated_args_xml_http_request_head path = "/index"; params = "blah"; headers = { location: 'blah' } - @session.expects(:process).with(:head, path, params: params, headers: headers, xhr: true) - assert_deprecated { @session.xml_http_request(:head, path, params, headers) } + assert_called_with @session, :process, [:head, path, params: params, headers: headers, xhr: true] do + assert_deprecated { @session.xml_http_request(:head, path, params, headers) } + end end end class IntegrationTestTest < ActiveSupport::TestCase def setup @test = ::ActionDispatch::IntegrationTest.new(:app) - @test.class.stubs(:fixture_table_names).returns([]) - @session = @test.open_session end def test_opens_new_session @@ -340,14 +386,8 @@ end # Tests that integration tests don't call Controller test methods for processing. # Integration tests have their own setup and teardown. class IntegrationTestUsesCorrectClass < ActionDispatch::IntegrationTest - def self.fixture_table_names - [] - end - def test_integration_methods_called reset! - @integration_session.stubs(:generic_url_rewriter) - @integration_session.stubs(:process) %w( get post head patch put delete ).each do |verb| assert_nothing_raised("'#{verb}' should use integration test methods") { __send__(verb, '/') } @@ -362,6 +402,8 @@ class IntegrationProcessTest < ActionDispatch::IntegrationTest format.html { render plain: "OK", status: 200 } format.js { render plain: "JS OK", status: 200 } format.xml { render :xml => "<root></root>", :status => 200 } + format.rss { render :xml => "<root></root>", :status => 200 } + format.atom { render :xml => "<root></root>", :status => 200 } end end @@ -418,19 +460,21 @@ class IntegrationProcessTest < ActionDispatch::IntegrationTest end end - def test_get_xml - with_test_route_set do - get "/get", params: {}, headers: {"HTTP_ACCEPT" => "application/xml"} - assert_equal 200, status - assert_equal "OK", status_message - assert_response 200 - assert_response :success - assert_response :ok - assert_equal({}, cookies.to_hash) - assert_equal "<root></root>", body - assert_equal "<root></root>", response.body - assert_instance_of Nokogiri::XML::Document, html_document - assert_equal 1, request_count + def test_get_xml_rss_atom + %w[ application/xml application/rss+xml application/atom+xml ].each do |mime_string| + with_test_route_set do + get "/get", headers: {"HTTP_ACCEPT" => mime_string} + assert_equal 200, status + assert_equal "OK", status_message + assert_response 200 + assert_response :success + assert_response :ok + assert_equal({}, cookies.to_hash) + assert_equal "<root></root>", body + assert_equal "<root></root>", response.body + assert_instance_of Nokogiri::XML::Document, html_document + assert_equal 1, request_count + end end end @@ -755,6 +799,25 @@ class MetalIntegrationTest < ActionDispatch::IntegrationTest assert_equal "http://test.com/", @request.env["HTTP_REFERER"] end + def test_ignores_common_ports_in_host + get "http://test.com" + assert_equal "test.com", @request.env["HTTP_HOST"] + + get "https://test.com" + assert_equal "test.com", @request.env["HTTP_HOST"] + end + + def test_keeps_uncommon_ports_in_host + get "http://test.com:123" + assert_equal "test.com:123", @request.env["HTTP_HOST"] + + get "http://test.com:443" + assert_equal "test.com:443", @request.env["HTTP_HOST"] + + get "https://test.com:80" + assert_equal "test.com:80", @request.env["HTTP_HOST"] + end + end class ApplicationIntegrationTest < ActionDispatch::IntegrationTest diff --git a/actionpack/test/controller/live_stream_test.rb b/actionpack/test/controller/live_stream_test.rb index 6ba361f2f7..843dafac06 100644 --- a/actionpack/test/controller/live_stream_test.rb +++ b/actionpack/test/controller/live_stream_test.rb @@ -1,5 +1,5 @@ require 'abstract_unit' -require 'concurrent/atomics' +require 'concurrent/atomic/count_down_latch' Thread.abort_on_exception = true module ActionController @@ -112,7 +112,7 @@ module ActionController class TestController < ActionController::Base include ActionController::Live - attr_accessor :latch, :tc + attr_accessor :latch, :tc, :error_latch def self.controller_path 'test' @@ -204,6 +204,12 @@ module ActionController end def overfill_buffer_and_die + logger = ActionController::Base.logger || Logger.new($stdout) + response.stream.on_error do + logger.warn 'Error while streaming' + error_latch.count_down + end + # Write until the buffer is full. It doesn't expose that # information directly, so we must hard-code its size: 10.times do @@ -256,20 +262,12 @@ module ActionController end def test_set_cookie - @controller = TestController.new get :set_cookie assert_equal({'hello' => 'world'}, @response.cookies) assert_equal "hello world", @response.body end - def test_set_response! - @controller.set_response!(@request) - assert_kind_of(Live::Response, @controller.response) - assert_equal @request, @controller.response.request - end - def test_write_to_stream - @controller = TestController.new get :basic_stream assert_equal "helloworld", @response.body assert_equal 'text/event-stream', @response.headers['Content-Type'] @@ -281,10 +279,9 @@ module ActionController @controller.latch = Concurrent::CountDownLatch.new parts = ['hello', 'world'] - @controller.request = @request - @controller.response = @response + get :blocking_stream - t = Thread.new(@response) { |resp| + t = Thread.new(response) { |resp| resp.await_commit resp.stream.each do |part| assert_equal parts.shift, part @@ -294,38 +291,28 @@ module ActionController end } - @controller.process :blocking_stream - assert t.join(3), 'timeout expired before the thread terminated' end def test_abort_with_full_buffer @controller.latch = Concurrent::CountDownLatch.new - - @request.parameters[:format] = 'plain' - @controller.request = @request - @controller.response = @response - - got_error = Concurrent::CountDownLatch.new - @response.stream.on_error do - ActionController::Base.logger.warn 'Error while streaming' - got_error.count_down - end - - t = Thread.new(@response) { |resp| - resp.await_commit - _, _, body = resp.to_a - body.each do |part| - @controller.latch.wait - body.close - break - end - } + @controller.error_latch = Concurrent::CountDownLatch.new capture_log_output do |output| - @controller.process :overfill_buffer_and_die + get :overfill_buffer_and_die, :format => 'plain' + + t = Thread.new(response) { |resp| + resp.await_commit + _, _, body = resp.to_a + body.each do + @controller.latch.wait + body.close + break + end + } + t.join - got_error.wait + @controller.error_latch.wait assert_match 'Error while streaming', output.rewind && output.read end end @@ -333,20 +320,18 @@ module ActionController def test_ignore_client_disconnect @controller.latch = Concurrent::CountDownLatch.new - @controller.request = @request - @controller.response = @response + capture_log_output do |output| + get :ignore_client_disconnect - t = Thread.new(@response) { |resp| - resp.await_commit - _, _, body = resp.to_a - body.each do |part| - body.close - break - end - } + t = Thread.new(response) { |resp| + resp.await_commit + _, _, body = resp.to_a + body.each do + body.close + break + end + } - capture_log_output do |output| - @controller.process :ignore_client_disconnect t.join Timeout.timeout(3) do @controller.latch.wait @@ -364,11 +349,8 @@ module ActionController end def test_live_stream_default_header - @controller.request = @request - @controller.response = @response - @controller.process :default_header - _, headers, _ = @response.prepare! - assert headers['Content-Type'] + get :default_header + assert response.headers['Content-Type'] end def test_render_text @@ -406,8 +388,14 @@ module ActionController end def test_exception_callback_when_committed + current_threads = Thread.list + capture_log_output do |output| get :exception_with_callback, format: 'text/event-stream' + + # Wait on the execution of all threads + (Thread.list - current_threads).each(&:join) + assert_equal %(data: "500 Internal Server Error"\n\n), response.body assert_match 'An exception occurred...', output.rewind && output.read assert_stream_closed @@ -437,13 +425,13 @@ module ActionController def test_stale_without_etag get :with_stale - assert_equal 200, @response.status.to_i + assert_equal 200, response.status.to_i end def test_stale_with_etag @request.if_none_match = Digest::MD5.hexdigest("123") get :with_stale - assert_equal 304, @response.status.to_i + assert_equal 304, response.status.to_i end end diff --git a/actionpack/test/controller/log_subscriber_test.rb b/actionpack/test/controller/log_subscriber_test.rb index 7835d2768a..6ae33be3c8 100644 --- a/actionpack/test/controller/log_subscriber_test.rb +++ b/actionpack/test/controller/log_subscriber_test.rb @@ -170,7 +170,7 @@ class ACLogSubscriberTest < ActionController::TestCase def test_process_action_with_view_runtime get :show wait - assert_match(/Completed 200 OK in [\d]ms/, logs[1]) + assert_match(/Completed 200 OK in \d+ms/, logs[1]) end def test_append_info_to_payload_is_called_even_with_exception diff --git a/actionpack/test/controller/mime/respond_to_test.rb b/actionpack/test/controller/mime/respond_to_test.rb index 64eb33f78f..c025c7fa00 100644 --- a/actionpack/test/controller/mime/respond_to_test.rb +++ b/actionpack/test/controller/mime/respond_to_test.rb @@ -81,7 +81,7 @@ class RespondToController < ActionController::Base def using_defaults_with_all respond_to do |type| type.html - type.all{ render body: "ALL" } + type.all { render body: "ALL" } 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 710c428dcc..e61f4d241b 100644 --- a/actionpack/test/controller/new_base/bare_metal_test.rb +++ b/actionpack/test/controller/new_base/bare_metal_test.rb @@ -2,8 +2,6 @@ require "abstract_unit" module BareMetalTest class BareController < ActionController::Metal - include ActionController::RackDelegation - def index self.response_body = "Hello world" end @@ -28,6 +26,8 @@ module BareMetalTest test "response_body value is wrapped in an array when the value is a String" do controller = BareController.new + controller.set_request!(ActionDispatch::Request.new({})) + controller.set_response!(BareController.make_response!(controller.request)) controller.index assert_equal ["Hello world"], controller.response_body end @@ -37,8 +37,6 @@ module BareMetalTest controller = BareController.new controller.set_request! ActionDispatch::Request.new(env) assert controller.request - assert controller.response - assert env['action_controller.instance'] end end @@ -123,34 +121,40 @@ module BareMetalTest end test "head :no_content (204) does not return any content" do - content = HeadController.action(:no_content).call(Rack::MockRequest.env_for("/")).third.first + content = body(HeadController.action(:no_content).call(Rack::MockRequest.env_for("/"))) assert_empty content end test "head :reset_content (205) does not return any content" do - content = HeadController.action(:reset_content).call(Rack::MockRequest.env_for("/")).third.first + content = body(HeadController.action(:reset_content).call(Rack::MockRequest.env_for("/"))) assert_empty content end test "head :not_modified (304) does not return any content" do - content = HeadController.action(:not_modified).call(Rack::MockRequest.env_for("/")).third.first + content = body(HeadController.action(:not_modified).call(Rack::MockRequest.env_for("/"))) assert_empty content end test "head :continue (100) does not return any content" do - content = HeadController.action(:continue).call(Rack::MockRequest.env_for("/")).third.first + content = body(HeadController.action(:continue).call(Rack::MockRequest.env_for("/"))) assert_empty content end test "head :switching_protocols (101) does not return any content" do - content = HeadController.action(:switching_protocols).call(Rack::MockRequest.env_for("/")).third.first + content = body(HeadController.action(:switching_protocols).call(Rack::MockRequest.env_for("/"))) assert_empty content end test "head :processing (102) does not return any content" do - content = HeadController.action(:processing).call(Rack::MockRequest.env_for("/")).third.first + content = body(HeadController.action(:processing).call(Rack::MockRequest.env_for("/"))) assert_empty content end + + def body(rack_response) + buf = [] + rack_response[2].each { |x| buf << x } + buf.join + end end class BareControllerTest < ActionController::TestCase diff --git a/actionpack/test/controller/new_base/content_type_test.rb b/actionpack/test/controller/new_base/content_type_test.rb index 0445a837ca..a9dcdde4b8 100644 --- a/actionpack/test/controller/new_base/content_type_test.rb +++ b/actionpack/test/controller/new_base/content_type_test.rb @@ -7,12 +7,12 @@ module ContentType end def set_on_response_obj - response.content_type = Mime::RSS + response.content_type = Mime[:rss] render body: "Hello world!" end def set_on_render - render body: "Hello world!", content_type: Mime::RSS + render body: "Hello world!", content_type: Mime[:rss] end end diff --git a/actionpack/test/controller/new_base/metal_test.rb b/actionpack/test/controller/new_base/metal_test.rb deleted file mode 100644 index 537b93387a..0000000000 --- a/actionpack/test/controller/new_base/metal_test.rb +++ /dev/null @@ -1,43 +0,0 @@ -require 'abstract_unit' - -module MetalTest - class MetalMiddleware < ActionController::Middleware - def call(env) - if env["PATH_INFO"] =~ /authed/ - app.call(env) - else - [401, headers, "Not authed!"] - end - end - end - - class Endpoint - def call(env) - [200, {}, "Hello World"] - end - end - - class TestMiddleware < ActiveSupport::TestCase - def setup - @app = Rack::Builder.new do - use MetalTest::MetalMiddleware - run MetalTest::Endpoint.new - end.to_app - end - - test "it can call the next app by using @app" do - env = Rack::MockRequest.env_for("/authed") - response = @app.call(env) - - assert_equal ["Hello World"], response[2] - end - - test "it can return a response using the normal AC::Metal techniques" do - env = Rack::MockRequest.env_for("/") - response = @app.call(env) - - assert_equal ["Not authed!"], response[2] - assert_equal 401, response[0] - end - end -end diff --git a/actionpack/test/controller/new_base/middleware_test.rb b/actionpack/test/controller/new_base/middleware_test.rb index a30e937bb3..85a1f351f0 100644 --- a/actionpack/test/controller/new_base/middleware_test.rb +++ b/actionpack/test/controller/new_base/middleware_test.rb @@ -75,7 +75,7 @@ module MiddlewareTest test "middleware that is 'use'd is called as part of the Rack application" do result = @app.call(env_for("/")) - assert_equal ["Hello World"], result[2] + assert_equal ["Hello World"], [].tap { |a| result[2].each { |x| a << x } } assert_equal "Success", result[1]["Middleware-Test"] end diff --git a/actionpack/test/controller/new_base/render_html_test.rb b/actionpack/test/controller/new_base/render_html_test.rb index fe11501eeb..e9ea57e329 100644 --- a/actionpack/test/controller/new_base/render_html_test.rb +++ b/actionpack/test/controller/new_base/render_html_test.rb @@ -179,7 +179,7 @@ module RenderHtml test "rendering from minimal controller returns response with text/html content type" do get "/render_html/minimal/index" - assert_content_type "text/html" + assert_content_type "text/html; charset=utf-8" end test "rendering from normal controller returns response with text/html content type" do diff --git a/actionpack/test/controller/new_base/render_plain_test.rb b/actionpack/test/controller/new_base/render_plain_test.rb index 0e36d36b50..0881442bd0 100644 --- a/actionpack/test/controller/new_base/render_plain_test.rb +++ b/actionpack/test/controller/new_base/render_plain_test.rb @@ -157,7 +157,7 @@ module RenderPlain test "rendering from minimal controller returns response with text/plain content type" do get "/render_plain/minimal/index" - assert_content_type "text/plain" + assert_content_type "text/plain; charset=utf-8" end test "rendering from normal controller returns response with text/plain content type" do diff --git a/actionpack/test/controller/new_base/render_text_test.rb b/actionpack/test/controller/new_base/render_text_test.rb index 435bb18dce..048458178c 100644 --- a/actionpack/test/controller/new_base/render_text_test.rb +++ b/actionpack/test/controller/new_base/render_text_test.rb @@ -1,5 +1,4 @@ require 'abstract_unit' -require 'active_support/deprecation' module RenderText class MinimalController < ActionController::Metal diff --git a/actionpack/test/controller/parameters/always_permitted_parameters_test.rb b/actionpack/test/controller/parameters/always_permitted_parameters_test.rb index 59be08db54..efaf8a96c3 100644 --- a/actionpack/test/controller/parameters/always_permitted_parameters_test.rb +++ b/actionpack/test/controller/parameters/always_permitted_parameters_test.rb @@ -1,6 +1,5 @@ require 'abstract_unit' require 'action_controller/metal/strong_parameters' -require 'minitest/mock' class AlwaysPermittedParametersTest < ActiveSupport::TestCase def setup diff --git a/actionpack/test/controller/parameters/mutators_test.rb b/actionpack/test/controller/parameters/mutators_test.rb index 6c57c4caeb..744d8664be 100644 --- a/actionpack/test/controller/parameters/mutators_test.rb +++ b/actionpack/test/controller/parameters/mutators_test.rb @@ -62,15 +62,11 @@ class ParametersMutatorsTest < ActiveSupport::TestCase end test "select! retains permitted status" do - jruby_skip "https://github.com/jruby/jruby/issues/3137" - @params.permit! assert @params.select! { |k| k != "person" }.permitted? end test "select! retains unpermitted status" do - jruby_skip "https://github.com/jruby/jruby/issues/3137" - assert_not @params.select! { |k| k != "person" }.permitted? end diff --git a/actionpack/test/controller/params_wrapper_test.rb b/actionpack/test/controller/params_wrapper_test.rb index 8bf016d060..7226beed26 100644 --- a/actionpack/test/controller/params_wrapper_test.rb +++ b/actionpack/test/controller/params_wrapper_test.rb @@ -28,8 +28,17 @@ class ParamsWrapperTest < ActionController::TestCase end end - class User; end - class Person; end + class User + def self.attribute_names + [] + end + end + + class Person + def self.attribute_names + [] + end + end tests UsersController @@ -155,33 +164,28 @@ class ParamsWrapperTest < ActionController::TestCase end def test_derived_wrapped_keys_from_matching_model - User.expects(:respond_to?).with(:attribute_names).returns(true) - User.expects(:attribute_names).twice.returns(["username"]) - - with_default_wrapper_options do - @request.env['CONTENT_TYPE'] = 'application/json' - post :parse, params: { 'username' => 'sikachu', 'title' => 'Developer' } - assert_parameters({ 'username' => 'sikachu', 'title' => 'Developer', 'user' => { 'username' => 'sikachu' }}) + assert_called(User, :attribute_names, times: 2, returns: ["username"]) do + with_default_wrapper_options do + @request.env['CONTENT_TYPE'] = 'application/json' + post :parse, params: { 'username' => 'sikachu', 'title' => 'Developer' } + assert_parameters({ 'username' => 'sikachu', 'title' => 'Developer', 'user' => { 'username' => 'sikachu' }}) + end end end def test_derived_wrapped_keys_from_specified_model with_default_wrapper_options do - Person.expects(:respond_to?).with(:attribute_names).returns(true) - Person.expects(:attribute_names).twice.returns(["username"]) + assert_called(Person, :attribute_names, times: 2, returns: ["username"]) do + UsersController.wrap_parameters Person - UsersController.wrap_parameters Person - - @request.env['CONTENT_TYPE'] = 'application/json' - post :parse, params: { 'username' => 'sikachu', 'title' => 'Developer' } - assert_parameters({ 'username' => 'sikachu', 'title' => 'Developer', 'person' => { 'username' => 'sikachu' }}) + @request.env['CONTENT_TYPE'] = 'application/json' + post :parse, params: { 'username' => 'sikachu', 'title' => 'Developer' } + assert_parameters({ 'username' => 'sikachu', 'title' => 'Developer', 'person' => { 'username' => 'sikachu' }}) + end end end def test_not_wrapping_abstract_model - User.expects(:respond_to?).with(:attribute_names).returns(true) - User.expects(:attribute_names).returns([]) - with_default_wrapper_options do @request.env['CONTENT_TYPE'] = 'application/json' post :parse, params: { 'username' => 'sikachu', 'title' => 'Developer' } diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb index 91b30ede6a..631ff7d02a 100644 --- a/actionpack/test/controller/redirect_test.rb +++ b/actionpack/test/controller/redirect_test.rb @@ -266,15 +266,17 @@ class RedirectTest < ActionController::TestCase end def test_redirect_to_nil - assert_raise(ActionController::ActionControllerError) do + error = assert_raise(ActionController::ActionControllerError) do get :redirect_to_nil end + assert_equal "Cannot redirect to nil!", error.message end def test_redirect_to_params - assert_raise(ActionController::ActionControllerError) do + error = assert_raise(ActionController::ActionControllerError) do get :redirect_to_params end + assert_equal "Cannot redirect to a parameter hash!", 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 index af50e11261..1f5215ac55 100644 --- a/actionpack/test/controller/render_other_test.rb +++ b/actionpack/test/controller/render_other_test.rb @@ -12,7 +12,7 @@ class RenderOtherTest < ActionController::TestCase def test_using_custom_render_option ActionController.add_renderer :simon do |says, options| - self.content_type = Mime::TEXT + self.content_type = Mime[:text] self.response_body = "Simon says: #{says}" end diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index 9acdc29aeb..256ebf6a07 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -1,6 +1,5 @@ require 'abstract_unit' require 'controller/fake_models' -require 'pathname' class TestControllerWithExtraEtags < ActionController::Base etag { nil } @@ -235,8 +234,6 @@ class MetalTestController < ActionController::Metal include AbstractController::Rendering include ActionView::Rendering include ActionController::Rendering - include ActionController::RackDelegation - def accessing_logger_in_template render :inline => "<%= logger.class %>" @@ -295,9 +292,10 @@ class ExpiresInRenderTest < ActionController::TestCase def test_date_header_when_expires_in time = Time.mktime(2011,10,30) - Time.stubs(:now).returns(time) - get :conditional_hello_with_expires_in - assert_equal Time.now.httpdate, @response.headers["Date"] + Time.stub :now, time do + get :conditional_hello_with_expires_in + assert_equal Time.now.httpdate, @response.headers["Date"] + end end end @@ -631,13 +629,13 @@ class HttpCacheForeverTest < ActionController::TestCase def test_cache_with_public get :cache_me_forever, params: {public: true} - assert_equal "max-age=#{100.years.to_i}, public", @response.headers["Cache-Control"] + assert_equal "max-age=#{100.years}, public", @response.headers["Cache-Control"] assert_not_nil @response.etag end def test_cache_with_private get :cache_me_forever - assert_equal "max-age=#{100.years.to_i}, private", @response.headers["Cache-Control"] + assert_equal "max-age=#{100.years}, private", @response.headers["Cache-Control"] assert_not_nil @response.etag assert_response :success end diff --git a/actionpack/test/controller/render_xml_test.rb b/actionpack/test/controller/render_xml_test.rb index 7a91577b17..f0fd7ddc5e 100644 --- a/actionpack/test/controller/render_xml_test.rb +++ b/actionpack/test/controller/render_xml_test.rb @@ -92,6 +92,6 @@ class RenderXmlTest < ActionController::TestCase def test_should_use_implicit_content_type get :implicit_content_type, format: 'atom' - assert_equal Mime::ATOM, @response.content_type + assert_equal Mime[:atom], @response.content_type end end diff --git a/actionpack/test/controller/renderer_test.rb b/actionpack/test/controller/renderer_test.rb index b55a25430b..16d24fa82a 100644 --- a/actionpack/test/controller/renderer_test.rb +++ b/actionpack/test/controller/renderer_test.rb @@ -1,6 +1,10 @@ require 'abstract_unit' class RendererTest < ActiveSupport::TestCase + test 'action controller base has a renderer' do + assert ActionController::Base.renderer + end + test 'creating with a controller' do controller = CommentsController renderer = ActionController::Renderer.for controller @@ -57,8 +61,7 @@ class RendererTest < ActiveSupport::TestCase end test 'rendering with defaults' do - renderer = ApplicationController.renderer - renderer.defaults[:https] = true + renderer = ApplicationController.renderer.new https: true content = renderer.render inline: '<%= request.ssl? %>' assert_equal 'true', content @@ -67,8 +70,8 @@ class RendererTest < ActiveSupport::TestCase test 'same defaults from the same controller' do renderer_defaults = ->(controller) { controller.renderer.defaults } - assert renderer_defaults[AccountsController].equal? renderer_defaults[AccountsController] - assert_not renderer_defaults[AccountsController].equal? renderer_defaults[CommentsController] + assert_equal renderer_defaults[AccountsController], renderer_defaults[AccountsController] + assert_equal renderer_defaults[AccountsController], renderer_defaults[CommentsController] end test 'rendering with different formats' do @@ -83,18 +86,6 @@ class RendererTest < ActiveSupport::TestCase test 'rendering with helpers' do assert_equal "<p>1\n<br />2</p>", render[inline: '<%= simple_format "1\n2" %>'] end - - test 'rendering from inherited renderer' do - inherited = Class.new ApplicationController.renderer do - defaults[:script_name] = 'script' - def render(options) - super options.merge(locals: { param: :value }) - end - end - - template = '<%= url_for controller: :foo, action: :bar, param: param %>' - assert_equal 'script/foo/bar?param=value', inherited.render(inline: template) - end private def render diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb index 868520a219..94ffbe3cd0 100644 --- a/actionpack/test/controller/request_forgery_protection_test.rb +++ b/actionpack/test/controller/request_forgery_protection_test.rb @@ -1,5 +1,4 @@ require 'abstract_unit' -require 'digest/sha1' require "active_support/log_subscriber/test_helper" # common controller actions @@ -132,10 +131,7 @@ end # common test methods module RequestForgeryProtectionTests def setup - @token = "cf50faa3fe97702ca1ae" - @controller.stubs(:form_authenticity_token).returns(@token) - @controller.stubs(:valid_authenticity_token?).with{ |_, t| t == @token }.returns(true) - @controller.stubs(:valid_authenticity_token?).with{ |_, t| t != @token }.returns(false) + @token = Base64.strict_encode64('railstestrailstestrailstestrails') @old_request_forgery_protection_token = ActionController::Base.request_forgery_protection_token ActionController::Base.request_forgery_protection_token = :custom_authenticity_token end @@ -145,17 +141,21 @@ module RequestForgeryProtectionTests end def test_should_render_form_with_token_tag - assert_not_blocked do - get :index + @controller.stub :form_authenticity_token, @token do + assert_not_blocked do + get :index + end + assert_select 'form>input[name=?][value=?]', 'custom_authenticity_token', @token end - assert_select 'form>input[name=?][value=?]', 'custom_authenticity_token', @token end def test_should_render_button_to_with_token_tag - assert_not_blocked do - get :show_button + @controller.stub :form_authenticity_token, @token do + assert_not_blocked do + get :show_button + end + assert_select 'form>input[name=?][value=?]', 'custom_authenticity_token', @token end - assert_select 'form>input[name=?][value=?]', 'custom_authenticity_token', @token end def test_should_render_form_without_token_tag_if_remote @@ -199,17 +199,21 @@ module RequestForgeryProtectionTests end def test_should_render_form_with_token_tag_if_remote_and_authenticity_token_requested - assert_not_blocked do - get :form_for_remote_with_token + @controller.stub :form_authenticity_token, @token do + assert_not_blocked do + get :form_for_remote_with_token + end + assert_select 'form>input[name=?][value=?]', 'custom_authenticity_token', @token end - assert_select 'form>input[name=?][value=?]', 'custom_authenticity_token', @token end def test_should_render_form_with_token_tag_with_authenticity_token_requested - assert_not_blocked do - get :form_for_with_token + @controller.stub :form_authenticity_token, @token do + assert_not_blocked do + get :form_for_with_token + end + assert_select 'form>input[name=?][value=?]', 'custom_authenticity_token', @token end - assert_select 'form>input[name=?][value=?]', 'custom_authenticity_token', @token end def test_should_allow_get @@ -249,37 +253,53 @@ module RequestForgeryProtectionTests end def test_should_allow_post_with_token - assert_not_blocked { post :index, params: { custom_authenticity_token: @token } } + session[:_csrf_token] = @token + @controller.stub :form_authenticity_token, @token do + assert_not_blocked { post :index, params: { custom_authenticity_token: @token } } + end end def test_should_allow_patch_with_token - assert_not_blocked { patch :index, params: { custom_authenticity_token: @token } } + session[:_csrf_token] = @token + @controller.stub :form_authenticity_token, @token do + assert_not_blocked { patch :index, params: { custom_authenticity_token: @token } } + end end def test_should_allow_put_with_token - assert_not_blocked { put :index, params: { custom_authenticity_token: @token } } + session[:_csrf_token] = @token + @controller.stub :form_authenticity_token, @token do + assert_not_blocked { put :index, params: { custom_authenticity_token: @token } } + end end def test_should_allow_delete_with_token - assert_not_blocked { delete :index, params: { custom_authenticity_token: @token } } + session[:_csrf_token] = @token + @controller.stub :form_authenticity_token, @token do + assert_not_blocked { delete :index, params: { custom_authenticity_token: @token } } + end end def test_should_allow_post_with_token_in_header + session[:_csrf_token] = @token @request.env['HTTP_X_CSRF_TOKEN'] = @token assert_not_blocked { post :index } end def test_should_allow_delete_with_token_in_header + session[:_csrf_token] = @token @request.env['HTTP_X_CSRF_TOKEN'] = @token assert_not_blocked { delete :index } end def test_should_allow_patch_with_token_in_header + session[:_csrf_token] = @token @request.env['HTTP_X_CSRF_TOKEN'] = @token assert_not_blocked { patch :index } end def test_should_allow_put_with_token_in_header + session[:_csrf_token] = @token @request.env['HTTP_X_CSRF_TOKEN'] = @token assert_not_blocked { put :index } end @@ -333,6 +353,7 @@ module RequestForgeryProtectionTests # Allow non-GET requests since GET is all a remote <script> tag can muster. def test_should_allow_non_get_js_without_xhr_header + session[:_csrf_token] = @token assert_cross_origin_not_blocked { post :same_origin_js, params: { custom_authenticity_token: @token } } assert_cross_origin_not_blocked { post :same_origin_js, params: { format: 'js', custom_authenticity_token: @token } } assert_cross_origin_not_blocked do @@ -358,7 +379,6 @@ module RequestForgeryProtectionTests end def test_should_not_raise_error_if_token_is_not_a_string - @controller.unstub(:valid_authenticity_token?) assert_blocked do patch :index, params: { custom_authenticity_token: { foo: 'bar' } } end @@ -402,11 +422,13 @@ class RequestForgeryProtectionControllerUsingResetSessionTest < ActionController end test 'should emit a csrf-param meta tag and a csrf-token meta tag' do - @controller.stubs(:form_authenticity_token).returns(@token + '<=?') - get :meta - assert_select 'meta[name=?][content=?]', 'csrf-param', 'custom_authenticity_token' - assert_select 'meta[name=?]', 'csrf-token' - assert_match(/cf50faa3fe97702ca1ae<=\?/, @response.body) + @controller.stub :form_authenticity_token, @token + '<=?' do + get :meta + assert_select 'meta[name=?][content=?]', 'csrf-param', 'custom_authenticity_token' + assert_select 'meta[name=?]', 'csrf-token' + regexp = "#{@token}<=\?" + assert_match(/#{regexp}/, @response.body) + end end end @@ -485,30 +507,36 @@ class FreeCookieControllerTest < ActionController::TestCase def setup @controller = FreeCookieController.new @token = "cf50faa3fe97702ca1ae" - - SecureRandom.stubs(:base64).returns(@token) super end def test_should_not_render_form_with_token_tag - get :index - assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token, false + SecureRandom.stub :base64, @token do + get :index + assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token, false + end end def test_should_not_render_button_to_with_token_tag - get :show_button - assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token, false + SecureRandom.stub :base64, @token do + get :show_button + assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token, false + end end def test_should_allow_all_methods_without_token - [:post, :patch, :put, :delete].each do |method| - assert_nothing_raised { send(method, :index)} + SecureRandom.stub :base64, @token do + [:post, :patch, :put, :delete].each do |method| + assert_nothing_raised { send(method, :index)} + end end end test 'should not emit a csrf-token meta tag' do - get :meta - assert @response.body.blank? + SecureRandom.stub :base64, @token do + get :meta + assert @response.body.blank? + end end end @@ -529,11 +557,11 @@ class CustomAuthenticityParamControllerTest < ActionController::TestCase def test_should_not_warn_if_form_authenticity_param_matches_form_authenticity_token ActionController::Base.logger = @logger - @controller.stubs(:valid_authenticity_token?).returns(:true) - begin - post :index, params: { custom_token_name: 'foobar' } - assert_equal 0, @logger.logged(:warn).size + @controller.stub :valid_authenticity_token?, :true do + post :index, params: { custom_token_name: 'foobar' } + assert_equal 0, @logger.logged(:warn).size + end ensure ActionController::Base.logger = @old_logger end diff --git a/actionpack/test/controller/required_params_test.rb b/actionpack/test/controller/required_params_test.rb index a901e56332..168f64ce41 100644 --- a/actionpack/test/controller/required_params_test.rb +++ b/actionpack/test/controller/required_params_test.rb @@ -48,4 +48,21 @@ class ParametersRequireTest < ActiveSupport::TestCase ActionController::Parameters.new(person: {}).require(:person) end end + + test "require array when all required params are present" do + safe_params = ActionController::Parameters.new(person: {first_name: 'Gaurish', title: 'Mjallo', city: 'Barcelona'}) + .require(:person) + .require([:first_name, :title]) + + assert_kind_of Array, safe_params + assert_equal ['Gaurish', 'Mjallo'], safe_params + end + + test "require array when a required param is missing" do + assert_raises(ActionController::ParameterMissing) do + ActionController::Parameters.new(person: {first_name: 'Gaurish', title: nil}) + .require(:person) + .require([:first_name, :title]) + end + end end diff --git a/actionpack/test/controller/rescue_test.rb b/actionpack/test/controller/rescue_test.rb index e767323773..f42bef883f 100644 --- a/actionpack/test/controller/rescue_test.rb +++ b/actionpack/test/controller/rescue_test.rb @@ -132,11 +132,19 @@ class RescueController < ActionController::Base end def io_error_in_view - raise ActionView::TemplateError.new(nil, IOError.new('this is io error')) + begin + raise IOError.new('this is io error') + rescue + raise ActionView::TemplateError.new(nil) + end end def zero_division_error_in_view - raise ActionView::TemplateError.new(nil, ZeroDivisionError.new('this is zero division error')) + begin + raise ZeroDivisionError.new('this is zero division error') + rescue + raise ActionView::TemplateError.new(nil) + end end protected @@ -246,12 +254,15 @@ class RescueControllerTest < ActionController::TestCase end def test_rescue_handler_with_argument - @controller.expects(:show_errors).once.with { |e| e.is_a?(Exception) } - get :record_invalid + assert_called_with @controller, :show_errors, [Exception] do + get :record_invalid + end end + def test_rescue_handler_with_argument_as_string - @controller.expects(:show_errors).once.with { |e| e.is_a?(Exception) } - get :record_invalid_raise_as_string + assert_called_with @controller, :show_errors, [Exception] do + get :record_invalid_raise_as_string + end end def test_proc_rescue_handler diff --git a/actionpack/test/controller/resources_test.rb b/actionpack/test/controller/resources_test.rb index 5a279639cc..4490abf7b2 100644 --- a/actionpack/test/controller/resources_test.rb +++ b/actionpack/test/controller/resources_test.rb @@ -149,7 +149,7 @@ class ResourcesTest < ActionController::TestCase end end - assert_restful_named_routes_for :messages do |options| + assert_restful_named_routes_for :messages do actions.each_key do |action| assert_named_route "/messages/#{action}", "#{action}_messages_path", :action => action end @@ -179,7 +179,7 @@ class ResourcesTest < ActionController::TestCase end end - assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do |options| + assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do actions.each_key do |action| assert_named_route "/threads/1/messages/#{action}", "#{action}_thread_messages_path", :action => action end @@ -206,7 +206,7 @@ class ResourcesTest < ActionController::TestCase end end - assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do |options| + assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do actions.each_key do |action| assert_named_route "/threads/1/messages/#{action}", "#{action}_thread_messages_path", :action => action end @@ -236,7 +236,7 @@ class ResourcesTest < ActionController::TestCase end end - assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do |options| + assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do actions.each_key do |action| assert_named_route "/threads/1/messages/#{action}.xml", "#{action}_thread_messages_path", :action => action, :format => 'xml' end @@ -253,7 +253,7 @@ class ResourcesTest < ActionController::TestCase assert_recognizes(options.merge(mark_options), :path => mark_path, :method => method) end - assert_restful_named_routes_for :messages do |options| + assert_restful_named_routes_for :messages do assert_named_route mark_path, :mark_message_path, mark_options end end @@ -278,7 +278,7 @@ class ResourcesTest < ActionController::TestCase assert_recognizes(options.merge(mark_options), :path => mark_path, :method => method) end - assert_restful_named_routes_for :messages, :path_names => {:new => 'nuevo'} do |options| + assert_restful_named_routes_for :messages, :path_names => {:new => 'nuevo'} do assert_named_route mark_path, :mark_message_path, mark_options end end @@ -304,7 +304,7 @@ class ResourcesTest < ActionController::TestCase assert_recognizes(options.merge(action_options), :path => action_path, :method => method) end - assert_restful_named_routes_for :messages do |options| + assert_restful_named_routes_for :messages do assert_named_route action_path, "#{action}_message_path".to_sym, action_options end end @@ -351,7 +351,7 @@ class ResourcesTest < ActionController::TestCase assert_recognizes(options.merge(preview_options), :path => preview_path, :method => :post) end - assert_restful_named_routes_for :messages do |options| + assert_restful_named_routes_for :messages do assert_named_route preview_path, :preview_new_message_path, preview_options end end @@ -373,7 +373,7 @@ class ResourcesTest < ActionController::TestCase assert_recognizes(options.merge(preview_options), :path => preview_path, :method => :post) end - assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do |options| + assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do assert_named_route preview_path, :preview_new_thread_message_path, preview_options end end @@ -395,7 +395,7 @@ class ResourcesTest < ActionController::TestCase assert_recognizes(options.merge(preview_options), :path => preview_path, :method => :post) end - assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do |options| + assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do assert_named_route preview_path, :preview_new_thread_message_path, preview_options end end @@ -505,8 +505,8 @@ class ResourcesTest < ActionController::TestCase routes = @routes.routes routes.each do |route| routes.each do |r| - next if route === r # skip the comparison instance - assert_not_equal [route.conditions, route.path.spec.to_s], [r.conditions, r.path.spec.to_s] + next if route == r # skip the comparison instance + assert_not_equal [route.conditions, route.path.spec.to_s, route.verb], [r.conditions, r.path.spec.to_s, r.verb] end end end @@ -519,9 +519,9 @@ class ResourcesTest < ActionController::TestCase end def test_should_create_multiple_singleton_resource_routes - with_singleton_resources :account, :logo do + with_singleton_resources :account, :product do assert_singleton_restful_for :account - assert_singleton_restful_for :logo + assert_singleton_restful_for :product end end @@ -553,7 +553,7 @@ class ResourcesTest < ActionController::TestCase assert_recognizes(options.merge(reset_options), :path => reset_path, :method => method) end - assert_singleton_named_routes_for :account do |options| + assert_singleton_named_routes_for :account do assert_named_route reset_path, :reset_account_path, reset_options end end @@ -577,7 +577,7 @@ class ResourcesTest < ActionController::TestCase assert_recognizes(options.merge(action_options), :path => action_path, :method => method) end - assert_singleton_named_routes_for :account do |options| + assert_singleton_named_routes_for :account do assert_named_route action_path, "#{action}_account_path".to_sym, action_options end end @@ -1070,8 +1070,8 @@ class ResourcesTest < ActionController::TestCase end def test_singleton_resource_name_is_not_singularized - with_singleton_resources(:preferences) do - assert_singleton_restful_for :preferences + with_singleton_resources(:products) do + assert_singleton_restful_for :products end end @@ -1128,14 +1128,14 @@ class ResourcesTest < ActionController::TestCase end def assert_restful_routes_for(controller_name, options = {}) - options[:options] ||= {} - options[:options][:controller] = options[:controller] || controller_name.to_s + route_options = (options[:options] ||= {}).dup + route_options[:controller] = options[:controller] || controller_name.to_s if options[:shallow] options[:shallow_options] ||= {} - options[:shallow_options][:controller] = options[:options][:controller] + options[:shallow_options][:controller] = route_options[:controller] else - options[:shallow_options] = options[:options] + options[:shallow_options] = route_options end new_action = @routes.resources_path_names[:new] || "new" @@ -1154,7 +1154,7 @@ class ResourcesTest < ActionController::TestCase edit_member_path = "#{member_path}/#{edit_action}" formatted_edit_member_path = "#{member_path}/#{edit_action}.xml" - with_options(options[:options]) do |controller| + with_options(route_options) do |controller| controller.assert_routing collection_path, :action => 'index' controller.assert_routing new_path, :action => 'new' controller.assert_routing "#{collection_path}.xml", :action => 'index', :format => 'xml' @@ -1168,23 +1168,23 @@ class ResourcesTest < ActionController::TestCase controller.assert_routing formatted_edit_member_path, :action => 'edit', :id => '1', :format => 'xml' end - assert_recognizes(options[:options].merge(:action => 'index'), :path => collection_path, :method => :get) - assert_recognizes(options[:options].merge(:action => 'new'), :path => new_path, :method => :get) - assert_recognizes(options[:options].merge(:action => 'create'), :path => collection_path, :method => :post) + assert_recognizes(route_options.merge(:action => 'index'), :path => collection_path, :method => :get) + assert_recognizes(route_options.merge(:action => 'new'), :path => new_path, :method => :get) + assert_recognizes(route_options.merge(:action => 'create'), :path => collection_path, :method => :post) assert_recognizes(options[:shallow_options].merge(:action => 'show', :id => '1'), :path => member_path, :method => :get) assert_recognizes(options[:shallow_options].merge(:action => 'edit', :id => '1'), :path => edit_member_path, :method => :get) assert_recognizes(options[:shallow_options].merge(:action => 'update', :id => '1'), :path => member_path, :method => :put) assert_recognizes(options[:shallow_options].merge(:action => 'destroy', :id => '1'), :path => member_path, :method => :delete) - assert_recognizes(options[:options].merge(:action => 'index', :format => 'xml'), :path => "#{collection_path}.xml", :method => :get) - assert_recognizes(options[:options].merge(:action => 'new', :format => 'xml'), :path => "#{new_path}.xml", :method => :get) - assert_recognizes(options[:options].merge(:action => 'create', :format => 'xml'), :path => "#{collection_path}.xml", :method => :post) + assert_recognizes(route_options.merge(:action => 'index', :format => 'xml'), :path => "#{collection_path}.xml", :method => :get) + assert_recognizes(route_options.merge(:action => 'new', :format => 'xml'), :path => "#{new_path}.xml", :method => :get) + assert_recognizes(route_options.merge(:action => 'create', :format => 'xml'), :path => "#{collection_path}.xml", :method => :post) assert_recognizes(options[:shallow_options].merge(:action => 'show', :id => '1', :format => 'xml'), :path => "#{member_path}.xml", :method => :get) assert_recognizes(options[:shallow_options].merge(:action => 'edit', :id => '1', :format => 'xml'), :path => formatted_edit_member_path, :method => :get) assert_recognizes(options[:shallow_options].merge(:action => 'update', :id => '1', :format => 'xml'), :path => "#{member_path}.xml", :method => :put) assert_recognizes(options[:shallow_options].merge(:action => 'destroy', :id => '1', :format => 'xml'), :path => "#{member_path}.xml", :method => :delete) - yield options[:options] if block_given? + yield route_options if block_given? end # test named routes like foo_path and foos_path map to the correct options. @@ -1195,20 +1195,20 @@ class ResourcesTest < ActionController::TestCase end singular_name ||= controller_name.to_s.singularize - options[:options] ||= {} - options[:options][:controller] = options[:controller] || controller_name.to_s + route_options = (options[:options] ||= {}).dup + route_options[:controller] = options[:controller] || controller_name.to_s if options[:shallow] options[:shallow_options] ||= {} - options[:shallow_options][:controller] = options[:options][:controller] + options[:shallow_options][:controller] = route_options[:controller] else - options[:shallow_options] = options[:options] + options[:shallow_options] = route_options end - @controller = "#{options[:options][:controller].camelize}Controller".constantize.new + @controller = "#{route_options[:controller].camelize}Controller".constantize.new @controller.singleton_class.include(@routes.url_helpers) - get :index, params: options[:options] - options[:options].delete :action + get :index, params: route_options + route_options.delete :action path = "#{options[:as] || controller_name}" shallow_path = "/#{options[:shallow] ? options[:namespace] : options[:path_prefix]}#{path}" @@ -1223,29 +1223,29 @@ class ResourcesTest < ActionController::TestCase edit_action = options[:path_names][:edit] || "edit" end - assert_named_route "#{full_path}", "#{name_prefix}#{controller_name}_path", options[:options] - assert_named_route "#{full_path}.xml", "#{name_prefix}#{controller_name}_path", options[:options].merge(:format => 'xml') + assert_named_route "#{full_path}", "#{name_prefix}#{controller_name}_path", route_options + assert_named_route "#{full_path}.xml", "#{name_prefix}#{controller_name}_path", route_options.merge(:format => 'xml') assert_named_route "#{shallow_path}/1", "#{shallow_prefix}#{singular_name}_path", options[:shallow_options].merge(:id => '1') assert_named_route "#{shallow_path}/1.xml", "#{shallow_prefix}#{singular_name}_path", options[:shallow_options].merge(:id => '1', :format => 'xml') - assert_named_route "#{full_path}/#{new_action}", "new_#{name_prefix}#{singular_name}_path", options[:options] - assert_named_route "#{full_path}/#{new_action}.xml", "new_#{name_prefix}#{singular_name}_path", options[:options].merge(:format => 'xml') + assert_named_route "#{full_path}/#{new_action}", "new_#{name_prefix}#{singular_name}_path", route_options + assert_named_route "#{full_path}/#{new_action}.xml", "new_#{name_prefix}#{singular_name}_path", route_options.merge(:format => 'xml') assert_named_route "#{shallow_path}/1/#{edit_action}", "edit_#{shallow_prefix}#{singular_name}_path", options[:shallow_options].merge(:id => '1') assert_named_route "#{shallow_path}/1/#{edit_action}.xml", "edit_#{shallow_prefix}#{singular_name}_path", options[:shallow_options].merge(:id => '1', :format => 'xml') - yield options[:options] if block_given? + yield route_options if block_given? end def assert_singleton_routes_for(singleton_name, options = {}) - options[:options] ||= {} - options[:options][:controller] = options[:controller] || singleton_name.to_s.pluralize + route_options = (options[:options] ||= {}).dup + route_options[:controller] = options[:controller] || singleton_name.to_s.pluralize full_path = "/#{options[:path_prefix]}#{options[:as] || singleton_name}" new_path = "#{full_path}/new" edit_path = "#{full_path}/edit" formatted_edit_path = "#{full_path}/edit.xml" - with_options options[:options] do |controller| + with_options route_options do |controller| controller.assert_routing full_path, :action => 'show' controller.assert_routing new_path, :action => 'new' controller.assert_routing edit_path, :action => 'edit' @@ -1254,40 +1254,41 @@ class ResourcesTest < ActionController::TestCase controller.assert_routing formatted_edit_path, :action => 'edit', :format => 'xml' end - assert_recognizes(options[:options].merge(:action => 'show'), :path => full_path, :method => :get) - assert_recognizes(options[:options].merge(:action => 'new'), :path => new_path, :method => :get) - assert_recognizes(options[:options].merge(:action => 'edit'), :path => edit_path, :method => :get) - assert_recognizes(options[:options].merge(:action => 'create'), :path => full_path, :method => :post) - assert_recognizes(options[:options].merge(:action => 'update'), :path => full_path, :method => :put) - assert_recognizes(options[:options].merge(:action => 'destroy'), :path => full_path, :method => :delete) + assert_recognizes(route_options.merge(:action => 'show'), :path => full_path, :method => :get) + assert_recognizes(route_options.merge(:action => 'new'), :path => new_path, :method => :get) + assert_recognizes(route_options.merge(:action => 'edit'), :path => edit_path, :method => :get) + assert_recognizes(route_options.merge(:action => 'create'), :path => full_path, :method => :post) + assert_recognizes(route_options.merge(:action => 'update'), :path => full_path, :method => :put) + assert_recognizes(route_options.merge(:action => 'destroy'), :path => full_path, :method => :delete) - assert_recognizes(options[:options].merge(:action => 'show', :format => 'xml'), :path => "#{full_path}.xml", :method => :get) - assert_recognizes(options[:options].merge(:action => 'new', :format => 'xml'), :path => "#{new_path}.xml", :method => :get) - assert_recognizes(options[:options].merge(:action => 'edit', :format => 'xml'), :path => formatted_edit_path, :method => :get) - assert_recognizes(options[:options].merge(:action => 'create', :format => 'xml'), :path => "#{full_path}.xml", :method => :post) - assert_recognizes(options[:options].merge(:action => 'update', :format => 'xml'), :path => "#{full_path}.xml", :method => :put) - assert_recognizes(options[:options].merge(:action => 'destroy', :format => 'xml'), :path => "#{full_path}.xml", :method => :delete) + assert_recognizes(route_options.merge(:action => 'show', :format => 'xml'), :path => "#{full_path}.xml", :method => :get) + assert_recognizes(route_options.merge(:action => 'new', :format => 'xml'), :path => "#{new_path}.xml", :method => :get) + assert_recognizes(route_options.merge(:action => 'edit', :format => 'xml'), :path => formatted_edit_path, :method => :get) + assert_recognizes(route_options.merge(:action => 'create', :format => 'xml'), :path => "#{full_path}.xml", :method => :post) + assert_recognizes(route_options.merge(:action => 'update', :format => 'xml'), :path => "#{full_path}.xml", :method => :put) + assert_recognizes(route_options.merge(:action => 'destroy', :format => 'xml'), :path => "#{full_path}.xml", :method => :delete) - yield options[:options] if block_given? + yield route_options if block_given? end def assert_singleton_named_routes_for(singleton_name, options = {}) - (options[:options] ||= {})[:controller] ||= singleton_name.to_s.pluralize - @controller = "#{options[:options][:controller].camelize}Controller".constantize.new + route_options = (options[:options] ||= {}).dup + controller_name = route_options[:controller] || options[:controller] || singleton_name.to_s.pluralize + @controller = "#{controller_name.camelize}Controller".constantize.new @controller.singleton_class.include(@routes.url_helpers) - get :show, params: options[:options] - options[:options].delete :action + get :show, params: route_options + route_options.delete :action full_path = "/#{options[:path_prefix]}#{options[:as] || singleton_name}" name_prefix = options[:name_prefix] - assert_named_route "#{full_path}", "#{name_prefix}#{singleton_name}_path", options[:options] - assert_named_route "#{full_path}.xml", "#{name_prefix}#{singleton_name}_path", options[:options].merge(:format => 'xml') + assert_named_route "#{full_path}", "#{name_prefix}#{singleton_name}_path", route_options + assert_named_route "#{full_path}.xml", "#{name_prefix}#{singleton_name}_path", route_options.merge(:format => 'xml') - assert_named_route "#{full_path}/new", "new_#{name_prefix}#{singleton_name}_path", options[:options] - assert_named_route "#{full_path}/new.xml", "new_#{name_prefix}#{singleton_name}_path", options[:options].merge(:format => 'xml') - assert_named_route "#{full_path}/edit", "edit_#{name_prefix}#{singleton_name}_path", options[:options] - assert_named_route "#{full_path}/edit.xml", "edit_#{name_prefix}#{singleton_name}_path", options[:options].merge(:format => 'xml') + assert_named_route "#{full_path}/new", "new_#{name_prefix}#{singleton_name}_path", route_options + assert_named_route "#{full_path}/new.xml", "new_#{name_prefix}#{singleton_name}_path", route_options.merge(:format => 'xml') + assert_named_route "#{full_path}/edit", "edit_#{name_prefix}#{singleton_name}_path", route_options + assert_named_route "#{full_path}/edit.xml", "edit_#{name_prefix}#{singleton_name}_path", route_options.merge(:format => 'xml') end def assert_named_route(expected, route, options) diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index 9bbfb74e72..a39fede5b9 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -289,12 +289,6 @@ class LegacyRouteSetTests < ActiveSupport::TestCase assert_equal({:id=>"1", :filters=>"foo", :format=>"js"}, params) end - def test_draw_with_block_arity_one_raises - assert_raise(RuntimeError) do - rs.draw { |map| map.match '/:controller(/:action(/:id))' } - end - end - def test_specific_controller_action_failure rs.draw do mount lambda {} => "/foo" @@ -327,17 +321,23 @@ class LegacyRouteSetTests < ActiveSupport::TestCase assert_equal '/stuff', controller.url_for({ :controller => '/stuff', :only_path => true }) end - def test_ignores_leading_slash - rs.clear! - rs.draw { get '/:controller(/:action(/:id))'} - test_default_setup - end - def test_route_with_colon_first rs.draw do - get '/:controller/:action/:id', :action => 'index', :id => nil - get ':url', :controller => 'tiny_url', :action => 'translate' + get '/:controller/:action/:id', action: 'index', id: nil + get ':url', controller: 'content', action: 'translate' end + + assert_equal({controller: 'content', action: 'translate', url: 'example'}, rs.recognize_path('/example')) + end + + def test_route_with_regexp_for_action + rs.draw { get '/:controller/:action', action: /auth[-|_].+/ } + + assert_equal({ action: 'auth_google', controller: 'content' }, rs.recognize_path('/content/auth_google')) + assert_equal({ action: 'auth-facebook', controller: 'content' }, rs.recognize_path('/content/auth-facebook')) + + assert_equal '/content/auth_google', url_for(rs, { controller: "content", action: "auth_google" }) + assert_equal '/content/auth-facebook', url_for(rs, { controller: "content", action: "auth-facebook" }) end def test_route_with_regexp_for_controller @@ -1746,40 +1746,10 @@ class RouteSetTest < ActiveSupport::TestCase include ActionDispatch::RoutingVerbs - class TestSet < ActionDispatch::Routing::RouteSet - def initialize(block) - @block = block - super() - end - - class Dispatcher < ActionDispatch::Routing::RouteSet::Dispatcher - def initialize(defaults, set, block) - super(defaults) - @block = block - @set = set - end - - def controller_reference(controller_param) - block = @block - set = @set - Class.new(ActionController::Base) { - include set.url_helpers - define_method(:process) { |name| block.call(self) } - def to_a; [200, {}, []]; end - } - end - end - - def dispatcher defaults - TestSet::Dispatcher.new defaults, self, @block - end - end - alias :routes :set def test_generate_with_optional_params_recalls_last_request - controller = nil - @set = TestSet.new ->(c) { controller = c } + @set = make_set false set.draw do get "blog/", :controller => "blog", :action => "index" diff --git a/actionpack/test/controller/send_file_test.rb b/actionpack/test/controller/send_file_test.rb index c0ddcf7f50..2820425c31 100644 --- a/actionpack/test/controller/send_file_test.rb +++ b/actionpack/test/controller/send_file_test.rb @@ -20,6 +20,47 @@ class SendFileController < ActionController::Base send_file(file_path, options) end + def test_send_file_headers_bang + options = { + :type => Mime[:png], + :disposition => 'disposition', + :filename => 'filename' + } + + send_data "foo", options + end + + def test_send_file_headers_with_disposition_as_a_symbol + options = { + :type => Mime[:png], + :disposition => :disposition, + :filename => 'filename' + } + + send_data "foo", options + end + + def test_send_file_headers_with_mime_lookup_with_symbol + options = { :type => :png } + + send_data "foo", options + end + + def test_send_file_headers_with_bad_symbol + options = { :type => :this_type_is_not_registered } + send_data "foo", options + end + + def test_send_file_headers_with_nil_content_type + options = { :type => nil } + send_data "foo", options + end + + def test_send_file_headers_guess_type_from_extension + options = { :filename => params[:filename] } + send_data "foo", options + end + def data send_data(file_data, options) end @@ -88,62 +129,39 @@ class SendFileTest < ActionController::TestCase # Test that send_file_headers! is setting the correct HTTP headers. def test_send_file_headers_bang - options = { - :type => Mime::PNG, - :disposition => 'disposition', - :filename => 'filename' - } - # Do it a few times: the resulting headers should be identical # no matter how many times you send with the same options. # Test resolving Ticket #458. - @controller.headers = {} - @controller.send(:send_file_headers!, options) - @controller.send(:send_file_headers!, options) - @controller.send(:send_file_headers!, options) + 5.times do + get :test_send_file_headers_bang - h = @controller.headers - assert_equal 'image/png', @controller.content_type - assert_equal 'disposition; filename="filename"', h['Content-Disposition'] - assert_equal 'binary', h['Content-Transfer-Encoding'] - - # test overriding Cache-Control: no-cache header to fix IE open/save dialog - @controller.send(:send_file_headers!, options) - @controller.response.prepare! - assert_equal 'private', h['Cache-Control'] + assert_equal 'image/png', response.content_type + assert_equal 'disposition; filename="filename"', response.get_header('Content-Disposition') + assert_equal 'binary', response.get_header('Content-Transfer-Encoding') + assert_equal 'private', response.get_header('Cache-Control') + end end def test_send_file_headers_with_disposition_as_a_symbol - options = { - :type => Mime::PNG, - :disposition => :disposition, - :filename => 'filename' - } + get :test_send_file_headers_with_disposition_as_a_symbol - @controller.headers = {} - @controller.send(:send_file_headers!, options) - assert_equal 'disposition; filename="filename"', @controller.headers['Content-Disposition'] + assert_equal 'disposition; filename="filename"', response.get_header('Content-Disposition') end def test_send_file_headers_with_mime_lookup_with_symbol - options = { - :type => :png - } - - @controller.headers = {} - @controller.send(:send_file_headers!, options) - - assert_equal 'image/png', @controller.content_type + get __method__ + assert_equal 'image/png', response.content_type end def test_send_file_headers_with_bad_symbol - options = { - :type => :this_type_is_not_registered - } + error = assert_raise(ArgumentError) { get __method__ } + assert_equal "Unknown MIME type this_type_is_not_registered", error.message + end - @controller.headers = {} - assert_raise(ArgumentError) { @controller.send(:send_file_headers!, options) } + def test_send_file_headers_with_nil_content_type + error = assert_raise(ArgumentError) { get __method__ } + assert_equal ":type option required", error.message end def test_send_file_headers_guess_type_from_extension @@ -158,10 +176,8 @@ class SendFileTest < ActionController::TestCase 'file.unk' => 'application/octet-stream', 'zip' => 'application/octet-stream' }.each do |filename,expected_type| - options = { :filename => filename } - @controller.headers = {} - @controller.send(:send_file_headers!, options) - assert_equal expected_type, @controller.content_type + get __method__, params: { filename: filename } + assert_equal expected_type, response.content_type end end @@ -179,7 +195,7 @@ class SendFileTest < ActionController::TestCase %w(file data).each do |method| define_method "test_send_#{method}_status" do @controller.options = { :stream => false, :status => 500 } - assert_nothing_raised { assert_not_nil process(method) } + assert_not_nil process(method) assert_equal 500, @response.status end diff --git a/actionpack/test/controller/test_case_test.rb b/actionpack/test/controller/test_case_test.rb index 1c5de983d8..e50373a0cc 100644 --- a/actionpack/test/controller/test_case_test.rb +++ b/actionpack/test/controller/test_case_test.rb @@ -4,6 +4,8 @@ require 'active_support/json/decoding' require 'rails/engine' class TestCaseTest < ActionController::TestCase + def self.fixture_path; end; + class TestController < ActionController::Base def no_op render plain: 'dummy' @@ -73,7 +75,7 @@ class TestCaseTest < ActionController::TestCase end def test_headers - render plain: request.headers.env.to_json + render plain: ::JSON.dump(request.headers.env) end def test_html_output @@ -137,7 +139,7 @@ XML def delete_cookie cookies.delete("foo") - head :ok + render plain: 'ok' end def test_without_body @@ -158,7 +160,7 @@ XML def setup super @controller = TestController.new - @request.env['PATH_INFO'] = nil + @request.delete_header 'PATH_INFO' @routes = ActionDispatch::Routing::RouteSet.new.tap do |r| r.draw do get ':controller(/:action(/:id))' @@ -625,6 +627,31 @@ XML assert_equal "application/json", parsed_env["CONTENT_TYPE"] end + def test_mutating_content_type_headers_for_plain_text_files_sets_the_header + @request.headers['Content-Type'] = 'text/plain' + post :render_body, params: { name: 'foo.txt' } + + assert_equal 'text/plain', @request.headers['Content-type'] + assert_equal 'foo.txt', @request.request_parameters[:name] + assert_equal 'render_body', @request.path_parameters[:action] + end + + def test_mutating_content_type_headers_for_html_files_sets_the_header + @request.headers['Content-Type'] = 'text/html' + post :render_body, params: { name: 'foo.html' } + + assert_equal 'text/html', @request.headers['Content-type'] + assert_equal 'foo.html', @request.request_parameters[:name] + assert_equal 'render_body', @request.path_parameters[:action] + end + + def test_mutating_content_type_headers_for_non_registered_mime_type_raises_an_error + assert_raises(RuntimeError) do + @request.headers['Content-Type'] = 'type/fake' + post :render_body, params: { name: 'foo.fake' } + end + end + def test_id_converted_to_string get :test_params, params: { id: 20, foo: Object.new @@ -849,10 +876,10 @@ XML end def test_fixture_path_is_accessed_from_self_instead_of_active_support_test_case - TestCaseTest.stubs(:fixture_path).returns(FILES_DIR) - - uploaded_file = fixture_file_upload('/mona_lisa.jpg', 'image/png') - assert_equal File.open("#{FILES_DIR}/mona_lisa.jpg", READ_PLAIN).read, uploaded_file.read + TestCaseTest.stub :fixture_path, FILES_DIR do + uploaded_file = fixture_file_upload('/mona_lisa.jpg', 'image/png') + assert_equal File.open("#{FILES_DIR}/mona_lisa.jpg", READ_PLAIN).read, uploaded_file.read + end end def test_test_uploaded_file_with_binary @@ -893,13 +920,13 @@ XML end def test_fixture_file_upload_relative_to_fixture_path - TestCaseTest.stubs(:fixture_path).returns(FILES_DIR) - uploaded_file = fixture_file_upload("mona_lisa.jpg", "image/jpg") - assert_equal File.open("#{FILES_DIR}/mona_lisa.jpg", READ_PLAIN).read, uploaded_file.read + TestCaseTest.stub :fixture_path, FILES_DIR do + uploaded_file = fixture_file_upload("mona_lisa.jpg", "image/jpg") + assert_equal File.open("#{FILES_DIR}/mona_lisa.jpg", READ_PLAIN).read, uploaded_file.read + end end def test_fixture_file_upload_ignores_nil_fixture_path - TestCaseTest.stubs(:fixture_path).returns(nil) uploaded_file = fixture_file_upload("#{FILES_DIR}/mona_lisa.jpg", "image/jpg") assert_equal File.open("#{FILES_DIR}/mona_lisa.jpg", READ_PLAIN).read, uploaded_file.read end @@ -947,6 +974,11 @@ class ResponseDefaultHeadersTest < ActionController::TestCase headers.delete params[:header] head :ok, 'C' => '3' end + + # Render a head response, but don't touch default headers + def leave_alone + head :ok + end end def before_setup @@ -972,9 +1004,13 @@ class ResponseDefaultHeadersTest < ActionController::TestCase end test "response contains default headers" do + get :leave_alone + # Response headers start out with the defaults - assert_equal @defaults, response.headers + assert_equal @defaults.merge('Content-Type' => 'text/html'), response.headers + end + test "response deletes a default header" do get :remove_header, params: { header: 'A' } assert_response :ok diff --git a/actionpack/test/controller/url_for_integration_test.rb b/actionpack/test/controller/url_for_integration_test.rb index 0e4c2b7c32..dfc2712e3e 100644 --- a/actionpack/test/controller/url_for_integration_test.rb +++ b/actionpack/test/controller/url_for_integration_test.rb @@ -158,6 +158,7 @@ module ActionPack ['/posts/ping',[ { :controller => 'posts', :action => 'ping' }]], ['/posts/show/1',[ { :controller => 'posts', :action => 'show', :id => '1' }]], + ['/posts/show/1',[ { :controller => 'posts', :action => 'show', :id => '1', :format => '' }]], ['/posts',[ { :controller => 'posts' }]], ['/posts',[ { :controller => 'posts', :action => 'index' }]], ['/posts/create',[ { :action => 'create' }, {:day=>nil, :month=>nil, :controller=>"posts", :action=>"show_date"}, '/blog']], diff --git a/actionpack/test/controller/url_for_test.rb b/actionpack/test/controller/url_for_test.rb index 31677f202d..78e883f134 100644 --- a/actionpack/test/controller/url_for_test.rb +++ b/actionpack/test/controller/url_for_test.rb @@ -451,6 +451,26 @@ module AbstractController end end + def test_url_for_with_array_is_unmodified + with_routing do |set| + set.draw do + namespace :admin do + resources :posts + end + end + + kls = Class.new { include set.url_helpers } + kls.default_url_options[:host] = 'www.basecamphq.com' + + original_components = [:new, :admin, :post, { param: 'value' }] + components = original_components.dup + + kls.new.url_for(components) + + assert_equal(original_components, components) + end + end + private def extract_params(url) url.split('?', 2).last.split('&').sort diff --git a/actionpack/test/controller/webservice_test.rb b/actionpack/test/controller/webservice_test.rb index b26f037c36..6d377c4691 100644 --- a/actionpack/test/controller/webservice_test.rb +++ b/actionpack/test/controller/webservice_test.rb @@ -65,7 +65,7 @@ class WebServiceTest < ActionDispatch::IntegrationTest def test_register_and_use_json_simple with_test_route_set do - with_params_parsers Mime::JSON => Proc.new { |data| ActiveSupport::JSON.decode(data)['request'].with_indifferent_access } do + with_params_parsers Mime[:json] => Proc.new { |data| ActiveSupport::JSON.decode(data)['request'].with_indifferent_access } do post "/", params: '{"request":{"summary":"content...","title":"JSON"}}', headers: { 'CONTENT_TYPE' => 'application/json' } @@ -97,24 +97,28 @@ class WebServiceTest < ActionDispatch::IntegrationTest end def test_parsing_json_doesnot_rescue_exception - with_test_route_set do - with_params_parsers Mime::JSON => Proc.new { |data| raise Interrupt } do - assert_raises(Interrupt) do - post "/", - params: '{"title":"JSON"}}', - headers: { 'CONTENT_TYPE' => 'application/json' } - end + req = Class.new(ActionDispatch::Request) do + def params_parsers + { Mime[:json] => Proc.new { |data| raise Interrupt } } end + + def content_length; get_header('rack.input').length; end + end.new({ 'rack.input' => StringIO.new('{"title":"JSON"}}'), 'CONTENT_TYPE' => 'application/json' }) + + assert_raises(Interrupt) do + req.request_parameters end end private def with_params_parsers(parsers = {}) old_session = @integration_session - @app = ActionDispatch::ParamsParser.new(app.routes, parsers) + original_parsers = ActionDispatch::Request.parameter_parsers + ActionDispatch::Request.parameter_parsers = original_parsers.merge parsers reset! yield ensure + ActionDispatch::Request.parameter_parsers = original_parsers @integration_session = old_session end diff --git a/actionpack/test/dispatch/callbacks_test.rb b/actionpack/test/dispatch/callbacks_test.rb index f767b07e75..5ba76d9ab9 100644 --- a/actionpack/test/dispatch/callbacks_test.rb +++ b/actionpack/test/dispatch/callbacks_test.rb @@ -28,7 +28,7 @@ class DispatcherTest < ActiveSupport::TestCase assert_equal 4, Foo.a assert_equal 4, Foo.b - dispatch do |env| + dispatch do raise "error" end rescue nil assert_equal 6, Foo.a diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb index aca28ae8d1..84c244c72a 100644 --- a/actionpack/test/dispatch/cookies_test.rb +++ b/actionpack/test/dispatch/cookies_test.rb @@ -3,6 +3,75 @@ require 'openssl' require 'active_support/key_generator' require 'active_support/message_verifier' +class CookieJarTest < ActiveSupport::TestCase + attr_reader :request + + def setup + @request = ActionDispatch::Request.new({}) + end + + def test_fetch + x = Object.new + assert_not request.cookie_jar.key?('zzzzzz') + assert_equal x, request.cookie_jar.fetch('zzzzzz', x) + assert_not request.cookie_jar.key?('zzzzzz') + end + + def test_fetch_exists + x = Object.new + request.cookie_jar['foo'] = 'bar' + assert_equal 'bar', request.cookie_jar.fetch('foo', x) + end + + def test_fetch_block + x = Object.new + assert_not request.cookie_jar.key?('zzzzzz') + assert_equal x, request.cookie_jar.fetch('zzzzzz') { x } + end + + def test_key_is_to_s + request.cookie_jar['foo'] = 'bar' + assert_equal 'bar', request.cookie_jar.fetch(:foo) + end + + def test_fetch_type_error + assert_raises(KeyError) do + request.cookie_jar.fetch(:omglolwut) + end + end + + def test_each + request.cookie_jar['foo'] = :bar + list = [] + request.cookie_jar.each do |k,v| + list << [k, v] + end + + assert_equal [['foo', :bar]], list + end + + def test_enumerable + request.cookie_jar['foo'] = :bar + actual = request.cookie_jar.map { |k,v| [k.to_s, v.to_s] } + assert_equal [['foo', 'bar']], actual + end + + def test_key_methods + assert !request.cookie_jar.key?(:foo) + assert !request.cookie_jar.has_key?("foo") + + request.cookie_jar[:foo] = :bar + assert request.cookie_jar.key?(:foo) + assert request.cookie_jar.has_key?("foo") + end + + def test_write_doesnt_set_a_nil_header + headers = {} + request.cookie_jar.write(headers) + assert !headers.include?('Set-Cookie') + end +end + class CookiesTest < ActionController::TestCase class CustomSerializer def self.load(value) @@ -14,16 +83,6 @@ class CookiesTest < ActionController::TestCase end end - class JSONWrapper - def initialize(obj) - @obj = obj - end - - def as_json(options = nil) - "wrapped: #{@obj.as_json(options)}" - end - end - class TestController < ActionController::Base def authenticate cookies["user_name"] = "david" @@ -88,11 +147,6 @@ class CookiesTest < ActionController::TestCase head :ok end - def set_wrapped_signed_cookie - cookies.signed[:user_id] = JSONWrapper.new(45) - head :ok - end - def get_signed_cookie cookies.signed[:user_id] head :ok @@ -103,6 +157,21 @@ class CookiesTest < ActionController::TestCase head :ok end + class JSONWrapper + def initialize(obj) + @obj = obj + end + + def as_json(options = nil) + "wrapped: #{@obj.as_json(options)}" + end + end + + def set_wrapped_signed_cookie + cookies.signed[:user_id] = JSONWrapper.new(45) + head :ok + end + def set_wrapped_encrypted_cookie cookies.encrypted[:foo] = JSONWrapper.new('bar') head :ok @@ -207,68 +276,18 @@ class CookiesTest < ActionController::TestCase tests TestController + SALT = 'b3c631c314c0bbca50c1b2843150fe33' + def setup super - @request.env["action_dispatch.key_generator"] = ActiveSupport::KeyGenerator.new("b3c631c314c0bbca50c1b2843150fe33", iterations: 2) - @request.env["action_dispatch.signed_cookie_salt"] = "b3c631c314c0bbca50c1b2843150fe33" - @request.env["action_dispatch.encrypted_cookie_salt"] = "b3c631c314c0bbca50c1b2843150fe33" - @request.env["action_dispatch.encrypted_signed_cookie_salt"] = "b3c631c314c0bbca50c1b2843150fe33" - @request.host = "www.nextangle.com" - end - - def test_fetch - x = Object.new - assert_not request.cookie_jar.key?('zzzzzz') - assert_equal x, request.cookie_jar.fetch('zzzzzz', x) - assert_not request.cookie_jar.key?('zzzzzz') - end - - def test_fetch_exists - x = Object.new - request.cookie_jar['foo'] = 'bar' - assert_equal 'bar', request.cookie_jar.fetch('foo', x) - end - - def test_fetch_block - x = Object.new - assert_not request.cookie_jar.key?('zzzzzz') - assert_equal x, request.cookie_jar.fetch('zzzzzz') { x } - end - - def test_key_is_to_s - request.cookie_jar['foo'] = 'bar' - assert_equal 'bar', request.cookie_jar.fetch(:foo) - end - - def test_fetch_type_error - assert_raises(KeyError) do - request.cookie_jar.fetch(:omglolwut) - end - end - def test_each - request.cookie_jar['foo'] = :bar - list = [] - request.cookie_jar.each do |k,v| - list << [k, v] - end + @request.env["action_dispatch.key_generator"] = ActiveSupport::KeyGenerator.new(SALT, iterations: 2) - assert_equal [['foo', :bar]], list - end + @request.env["action_dispatch.signed_cookie_salt"] = + @request.env["action_dispatch.encrypted_cookie_salt"] = + @request.env["action_dispatch.encrypted_signed_cookie_salt"] = SALT - def test_enumerable - request.cookie_jar['foo'] = :bar - actual = request.cookie_jar.map { |k,v| [k.to_s, v.to_s] } - assert_equal [['foo', 'bar']], actual - end - - def test_key_methods - assert !request.cookie_jar.key?(:foo) - assert !request.cookie_jar.has_key?("foo") - - request.cookie_jar[:foo] = :bar - assert request.cookie_jar.key?(:foo) - assert request.cookie_jar.has_key?("foo") + @request.host = "www.nextangle.com" end def test_setting_cookie @@ -321,10 +340,12 @@ class CookiesTest < ActionController::TestCase end def test_setting_cookie_with_secure_when_always_write_cookie_is_true - ActionDispatch::Cookies::CookieJar.any_instance.stubs(:always_write_cookie).returns(true) + old_cookie, @request.cookie_jar.always_write_cookie = @request.cookie_jar.always_write_cookie, true get :authenticate_with_secure assert_cookie_header "user_name=david; path=/; secure" assert_equal({"user_name" => "david"}, @response.cookies) + ensure + @request.cookie_jar.always_write_cookie = old_cookie end def test_not_setting_cookie_with_secure @@ -651,6 +672,15 @@ class CookiesTest < ActionController::TestCase end end + def test_cookie_jar_mutated_by_request_persists_on_future_requests + get :authenticate + cookie_jar = @request.cookie_jar + cookie_jar.signed[:user_id] = 123 + assert_equal ["user_name", "user_id"], @request.cookie_jar.instance_variable_get(:@cookies).keys + get :get_signed_cookie + assert_equal ["user_name", "user_id"], @request.cookie_jar.instance_variable_get(:@cookies).keys + end + def test_raises_argument_error_if_missing_secret assert_raise(ArgumentError, nil.inspect) { @request.env["action_dispatch.key_generator"] = ActiveSupport::LegacyKeyGenerator.new(nil) @@ -1072,11 +1102,11 @@ class CookiesTest < ActionController::TestCase assert_equal "david", cookies[:user_name] get :noop - assert_nil @response.headers["Set-Cookie"] + assert !@response.headers.include?("Set-Cookie") assert_equal "david", cookies[:user_name] get :noop - assert_nil @response.headers["Set-Cookie"] + assert !@response.headers.include?("Set-Cookie") assert_equal "david", cookies[:user_name] end diff --git a/actionpack/test/dispatch/debug_exceptions_test.rb b/actionpack/test/dispatch/debug_exceptions_test.rb index a867aee7ec..8960156d00 100644 --- a/actionpack/test/dispatch/debug_exceptions_test.rb +++ b/actionpack/test/dispatch/debug_exceptions_test.rb @@ -42,7 +42,11 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest when "/unprocessable_entity" raise ActionController::InvalidAuthenticityToken when "/not_found_original_exception" - raise ActionView::Template::Error.new('template', AbstractController::ActionNotFound.new) + begin + raise AbstractController::ActionNotFound.new + rescue + raise ActionView::Template::Error.new('template') + end when "/missing_template" raise ActionView::MissingTemplate.new(%w(foo), 'foo/index', %w(foo), false, 'mailer') when "/bad_request" @@ -56,12 +60,12 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest when "/syntax_error_into_view" begin eval 'broke_syntax =' - rescue Exception => e + rescue Exception template = ActionView::Template.new(File.read(__FILE__), __FILE__, ActionView::Template::Handlers::Raw.new, {}) - raise ActionView::Template::Error.new(template, e) + raise ActionView::Template::Error.new(template) end when "/framework_raises" method_that_raises @@ -71,14 +75,6 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest end end - def setup - app = ActiveSupport::OrderedOptions.new - app.config = ActiveSupport::OrderedOptions.new - app.config.assets = ActiveSupport::OrderedOptions.new - app.config.assets.prefix = '/sprockets' - Rails.stubs(:application).returns(app) - end - RoutesApp = Struct.new(:routes).new(SharedTestRoutes) ProductionApp = ActionDispatch::DebugExceptions.new(Boomer.new(false), RoutesApp) DevelopmentApp = ActionDispatch::DebugExceptions.new(Boomer.new(true), RoutesApp) @@ -280,9 +276,12 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest test 'uses backtrace cleaner from env' do @app = DevelopmentApp - cleaner = stub(:clean => ['passed backtrace cleaner']) - get "/", headers: { 'action_dispatch.show_exceptions' => true, 'action_dispatch.backtrace_cleaner' => cleaner } - assert_match(/passed backtrace cleaner/, body) + backtrace_cleaner = ActiveSupport::BacktraceCleaner.new + + backtrace_cleaner.stub :clean, ['passed backtrace cleaner'] do + get "/", headers: { 'action_dispatch.show_exceptions' => true, 'action_dispatch.backtrace_cleaner' => backtrace_cleaner } + assert_match(/passed backtrace cleaner/, body) + end end test 'logs exception backtrace when all lines silenced' do @@ -338,36 +337,37 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest test 'debug exceptions app shows user code that caused the error in source view' do @app = DevelopmentApp - Rails.stubs(:root).returns(Pathname.new('.')) - cleaner = ActiveSupport::BacktraceCleaner.new.tap do |bc| - bc.add_silencer { |line| line =~ /method_that_raises/ } - bc.add_silencer { |line| line !~ %r{test/dispatch/debug_exceptions_test.rb} } - end + Rails.stub :root, Pathname.new('.') do + cleaner = ActiveSupport::BacktraceCleaner.new.tap do |bc| + bc.add_silencer { |line| line =~ /method_that_raises/ } + bc.add_silencer { |line| line !~ %r{test/dispatch/debug_exceptions_test.rb} } + end - get '/framework_raises', headers: { 'action_dispatch.backtrace_cleaner' => cleaner } + get '/framework_raises', headers: { 'action_dispatch.backtrace_cleaner' => cleaner } - # Assert correct error - assert_response 500 - assert_select 'h2', /error in framework/ + # Assert correct error + assert_response 500 + assert_select 'h2', /error in framework/ - # assert source view line is the call to method_that_raises - assert_select 'div.source:not(.hidden)' do - assert_select 'pre .line.active', /method_that_raises/ - end + # assert source view line is the call to method_that_raises + assert_select 'div.source:not(.hidden)' do + assert_select 'pre .line.active', /method_that_raises/ + end - # assert first source view (hidden) that throws the error - assert_select 'div.source:first' do - assert_select 'pre .line.active', /raise StandardError\.new/ - end + # assert first source view (hidden) that throws the error + assert_select 'div.source:first' do + assert_select 'pre .line.active', /raise StandardError\.new/ + end - # assert application trace refers to line that calls method_that_raises is first - assert_select '#Application-Trace' do - assert_select 'pre code a:first', %r{test/dispatch/debug_exceptions_test\.rb:\d+:in `call} - end + # assert application trace refers to line that calls method_that_raises is first + assert_select '#Application-Trace' do + assert_select 'pre code a:first', %r{test/dispatch/debug_exceptions_test\.rb:\d+:in `call} + end - # assert framework trace that that threw the error is first - assert_select '#Framework-Trace' do - assert_select 'pre code a:first', /method_that_raises/ + # assert framework trace that threw the error is first + assert_select '#Framework-Trace' do + assert_select 'pre code a:first', /method_that_raises/ + end end end end diff --git a/actionpack/test/dispatch/exception_wrapper_test.rb b/actionpack/test/dispatch/exception_wrapper_test.rb index 7a29a7ff97..dfbb91c0ca 100644 --- a/actionpack/test/dispatch/exception_wrapper_test.rb +++ b/actionpack/test/dispatch/exception_wrapper_test.rb @@ -17,51 +17,49 @@ module ActionDispatch end setup do - Rails.stubs(:root).returns(Pathname.new('.')) - - cleaner = ActiveSupport::BacktraceCleaner.new - cleaner.add_silencer { |line| line !~ /^lib/ } - - @environment = { 'action_dispatch.backtrace_cleaner' => cleaner } + @cleaner = ActiveSupport::BacktraceCleaner.new + @cleaner.add_silencer { |line| line !~ /^lib/ } end test '#source_extracts fetches source fragments for every backtrace entry' do exception = TestError.new("lib/file.rb:42:in `index'") - wrapper = ExceptionWrapper.new({}, exception) - - wrapper.expects(:source_fragment).with('lib/file.rb', 42).returns('foo') + wrapper = ExceptionWrapper.new(nil, exception) - assert_equal [ code: 'foo', line_number: 42 ], wrapper.source_extracts + assert_called_with(wrapper, :source_fragment, ['lib/file.rb', 42], returns: 'foo') do + assert_equal [ code: 'foo', line_number: 42 ], wrapper.source_extracts + end end test '#source_extracts works with Windows paths' do exc = TestError.new("c:/path/to/rails/app/controller.rb:27:in 'index':") - wrapper = ExceptionWrapper.new({}, exc) - wrapper.expects(:source_fragment).with('c:/path/to/rails/app/controller.rb', 27).returns('nothing') + wrapper = ExceptionWrapper.new(nil, exc) - assert_equal [ code: 'nothing', line_number: 27 ], wrapper.source_extracts + assert_called_with(wrapper, :source_fragment, ['c:/path/to/rails/app/controller.rb', 27], returns: 'nothing') do + assert_equal [ code: 'nothing', line_number: 27 ], wrapper.source_extracts + end end test '#source_extracts works with non standard backtrace' do exc = TestError.new('invalid') - wrapper = ExceptionWrapper.new({}, exc) - wrapper.expects(:source_fragment).with('invalid', 0).returns('nothing') + wrapper = ExceptionWrapper.new(nil, exc) - assert_equal [ code: 'nothing', line_number: 0 ], wrapper.source_extracts + assert_called_with(wrapper, :source_fragment, ['invalid', 0], returns: 'nothing') do + assert_equal [ code: 'nothing', line_number: 0 ], wrapper.source_extracts + end end test '#application_trace returns traces only from the application' do exception = TestError.new(caller.prepend("lib/file.rb:42:in `index'")) - wrapper = ExceptionWrapper.new(@environment, exception) + wrapper = ExceptionWrapper.new(@cleaner, exception) assert_equal [ "lib/file.rb:42:in `index'" ], wrapper.application_trace end test '#application_trace cannot be nil' do - nil_backtrace_wrapper = ExceptionWrapper.new(@environment, BadlyDefinedError.new) - nil_cleaner_wrapper = ExceptionWrapper.new({}, BadlyDefinedError.new) + nil_backtrace_wrapper = ExceptionWrapper.new(@cleaner, BadlyDefinedError.new) + nil_cleaner_wrapper = ExceptionWrapper.new(nil, BadlyDefinedError.new) assert_equal [], nil_backtrace_wrapper.application_trace assert_equal [], nil_cleaner_wrapper.application_trace @@ -69,14 +67,14 @@ module ActionDispatch test '#framework_trace returns traces outside the application' do exception = TestError.new(caller.prepend("lib/file.rb:42:in `index'")) - wrapper = ExceptionWrapper.new(@environment, exception) + wrapper = ExceptionWrapper.new(@cleaner, exception) assert_equal caller, wrapper.framework_trace end test '#framework_trace cannot be nil' do - nil_backtrace_wrapper = ExceptionWrapper.new(@environment, BadlyDefinedError.new) - nil_cleaner_wrapper = ExceptionWrapper.new({}, BadlyDefinedError.new) + nil_backtrace_wrapper = ExceptionWrapper.new(@cleaner, BadlyDefinedError.new) + nil_cleaner_wrapper = ExceptionWrapper.new(nil, BadlyDefinedError.new) assert_equal [], nil_backtrace_wrapper.framework_trace assert_equal [], nil_cleaner_wrapper.framework_trace @@ -84,14 +82,14 @@ module ActionDispatch test '#full_trace returns application and framework traces' do exception = TestError.new(caller.prepend("lib/file.rb:42:in `index'")) - wrapper = ExceptionWrapper.new(@environment, exception) + wrapper = ExceptionWrapper.new(@cleaner, exception) assert_equal exception.backtrace, wrapper.full_trace end test '#full_trace cannot be nil' do - nil_backtrace_wrapper = ExceptionWrapper.new(@environment, BadlyDefinedError.new) - nil_cleaner_wrapper = ExceptionWrapper.new({}, BadlyDefinedError.new) + nil_backtrace_wrapper = ExceptionWrapper.new(@cleaner, BadlyDefinedError.new) + nil_cleaner_wrapper = ExceptionWrapper.new(nil, BadlyDefinedError.new) assert_equal [], nil_backtrace_wrapper.full_trace assert_equal [], nil_cleaner_wrapper.full_trace @@ -99,7 +97,7 @@ module ActionDispatch test '#traces returns every trace by category enumerated with an index' do exception = TestError.new("lib/file.rb:42:in `index'", "/gems/rack.rb:43:in `index'") - wrapper = ExceptionWrapper.new(@environment, exception) + wrapper = ExceptionWrapper.new(@cleaner, exception) assert_equal({ 'Application Trace' => [ id: 0, trace: "lib/file.rb:42:in `index'" ], diff --git a/actionpack/test/dispatch/header_test.rb b/actionpack/test/dispatch/header_test.rb index e2b38c23bc..7f1ef121b7 100644 --- a/actionpack/test/dispatch/header_test.rb +++ b/actionpack/test/dispatch/header_test.rb @@ -1,15 +1,19 @@ require "abstract_unit" class HeaderTest < ActiveSupport::TestCase + def make_headers(hash) + ActionDispatch::Http::Headers.new ActionDispatch::Request.new hash + end + setup do - @headers = ActionDispatch::Http::Headers.new( + @headers = make_headers( "CONTENT_TYPE" => "text/plain", "HTTP_REFERER" => "/some/page" ) end test "#new does not normalize the data" do - headers = ActionDispatch::Http::Headers.new( + headers = make_headers( "Content-Type" => "application/json", "HTTP_REFERER" => "/some/page", "Host" => "http://test.com") @@ -38,6 +42,24 @@ class HeaderTest < ActiveSupport::TestCase assert_equal "127.0.0.1", @headers["HTTP_HOST"] end + test "add to multivalued headers" do + # Sets header when not present + @headers.add 'Foo', '1' + assert_equal '1', @headers['Foo'] + + # Ignores nil values + @headers.add 'Foo', nil + assert_equal '1', @headers['Foo'] + + # Converts value to string + @headers.add 'Foo', 1 + assert_equal '1,1', @headers['Foo'] + + # Case-insensitive + @headers.add 'fOo', 2 + assert_equal '1,1,2', @headers['foO'] + end + test "headers can contain numbers" do @headers["Content-MD5"] = "Q2hlY2sgSW50ZWdyaXR5IQ==" @@ -108,7 +130,7 @@ class HeaderTest < ActiveSupport::TestCase end test "env variables with . are not modified" do - headers = ActionDispatch::Http::Headers.new + headers = make_headers({}) headers.merge! "rack.input" => "", "rack.request.cookie_hash" => "", "action_dispatch.logger" => "" @@ -119,7 +141,7 @@ class HeaderTest < ActiveSupport::TestCase end test "symbols are treated as strings" do - headers = ActionDispatch::Http::Headers.new + headers = make_headers({}) headers.merge!(:SERVER_NAME => "example.com", "HTTP_REFERER" => "/", :Host => "test.com") @@ -130,7 +152,7 @@ class HeaderTest < ActiveSupport::TestCase test "headers directly modifies the passed environment" do env = {"HTTP_REFERER" => "/"} - headers = ActionDispatch::Http::Headers.new(env) + headers = make_headers(env) headers['Referer'] = "http://example.com/" headers.merge! "CONTENT_TYPE" => "text/plain" assert_equal({"HTTP_REFERER"=>"http://example.com/", diff --git a/actionpack/test/dispatch/live_response_test.rb b/actionpack/test/dispatch/live_response_test.rb index 5cfa5f7b3b..160cdc1582 100644 --- a/actionpack/test/dispatch/live_response_test.rb +++ b/actionpack/test/dispatch/live_response_test.rb @@ -1,5 +1,5 @@ require 'abstract_unit' -require 'concurrent/atomics' +require 'concurrent/atomic/count_down_latch' module ActionController module Live @@ -65,7 +65,7 @@ module ActionController latch = Concurrent::CountDownLatch.new t = Thread.new { - @response.stream.each do |chunk| + @response.stream.each do latch.count_down end } @@ -83,6 +83,8 @@ module ActionController def test_headers_cannot_be_written_after_close @response.stream.close + # we can add data until it's actually written, which happens on `each` + @response.each { |x| } e = assert_raises(ActionDispatch::IllegalStateError) do @response.headers['Content-Length'] = "zomg" diff --git a/actionpack/test/dispatch/mapper_test.rb b/actionpack/test/dispatch/mapper_test.rb index 889f9a4736..e783df855e 100644 --- a/actionpack/test/dispatch/mapper_test.rb +++ b/actionpack/test/dispatch/mapper_test.rb @@ -4,13 +4,6 @@ module ActionDispatch module Routing class MapperTest < ActiveSupport::TestCase class FakeSet < ActionDispatch::Routing::RouteSet - attr_reader :routes - alias :set :routes - - def initialize - @routes = [] - end - def resources_path_names {} end @@ -19,16 +12,24 @@ module ActionDispatch ActionDispatch::Request end - def add_route(*args) - routes << args + def dispatcher_class + RouteSet::Dispatcher + end + + def defaults + routes.map(&:defaults) end def conditions - routes.map { |x| x[1] } + routes.map(&:constraints) end def requirements - routes.map { |x| x[2] } + routes.map(&:path).map(&:requirements) + end + + def asts + routes.map(&:path).map(&:spec) end end @@ -36,18 +37,76 @@ module ActionDispatch Mapper.new FakeSet.new end + def test_scope_raises_on_anchor + fakeset = FakeSet.new + mapper = Mapper.new fakeset + assert_raises(ArgumentError) do + mapper.scope(anchor: false) do + end + end + end + + def test_blows_up_without_via + fakeset = FakeSet.new + mapper = Mapper.new fakeset + assert_raises(ArgumentError) do + mapper.match '/', :to => 'posts#index', :as => :main + end + end + + def test_unscoped_formatted + fakeset = FakeSet.new + mapper = Mapper.new fakeset + mapper.get '/foo', :to => 'posts#index', :as => :main, :format => true + assert_equal({:controller=>"posts", :action=>"index"}, + fakeset.defaults.first) + assert_equal "/foo.:format", fakeset.asts.first.to_s + end + + def test_scoped_formatted + fakeset = FakeSet.new + mapper = Mapper.new fakeset + mapper.scope(format: true) do + mapper.get '/foo', :to => 'posts#index', :as => :main + end + assert_equal({:controller=>"posts", :action=>"index"}, + fakeset.defaults.first) + assert_equal "/foo.:format", fakeset.asts.first.to_s + end + + def test_random_keys + fakeset = FakeSet.new + mapper = Mapper.new fakeset + mapper.scope(omg: :awesome) do + mapper.get '/', :to => 'posts#index', :as => :main + end + assert_equal({:omg=>:awesome, :controller=>"posts", :action=>"index"}, + fakeset.defaults.first) + assert_equal("GET", fakeset.routes.first.verb) + end + def test_mapping_requirements - options = { :controller => 'foo', :action => 'bar', :via => :get } - m = Mapper::Mapping.build({}, FakeSet.new, '/store/:name(*rest)', nil, options) - _, _, requirements, _ = m.to_route - assert_equal(/.+?/, requirements[:rest]) + options = { } + scope = Mapper::Scope.new({}) + ast = Journey::Parser.parse '/store/:name(*rest)' + m = Mapper::Mapping.build(scope, FakeSet.new, ast, 'foo', 'bar', nil, [:get], nil, {}, true, options) + assert_equal(/.+?/, m.requirements[:rest]) + end + + def test_via_scope + fakeset = FakeSet.new + mapper = Mapper.new fakeset + mapper.scope(via: :put) do + mapper.match '/', :to => 'posts#index', :as => :main + end + assert_equal("PUT", fakeset.routes.first.verb) end def test_map_slash fakeset = FakeSet.new mapper = Mapper.new fakeset mapper.get '/', :to => 'posts#index', :as => :main - assert_equal '/', fakeset.conditions.first[:path_info] + assert_equal '/', fakeset.asts.first.to_s end def test_map_more_slashes @@ -56,14 +115,14 @@ module ActionDispatch # FIXME: is this a desired behavior? mapper.get '/one/two/', :to => 'posts#index', :as => :main - assert_equal '/one/two(.:format)', fakeset.conditions.first[:path_info] + assert_equal '/one/two(.:format)', fakeset.asts.first.to_s end def test_map_wildcard fakeset = FakeSet.new mapper = Mapper.new fakeset mapper.get '/*path', :to => 'pages#show' - assert_equal '/*path(.:format)', fakeset.conditions.first[:path_info] + assert_equal '/*path(.:format)', fakeset.asts.first.to_s assert_equal(/.+?/, fakeset.requirements.first[:path]) end @@ -71,7 +130,7 @@ module ActionDispatch fakeset = FakeSet.new mapper = Mapper.new fakeset mapper.get '/*path/foo/:bar', :to => 'pages#show' - assert_equal '/*path/foo/:bar(.:format)', fakeset.conditions.first[:path_info] + assert_equal '/*path/foo/:bar(.:format)', fakeset.asts.first.to_s assert_equal(/.+?/, fakeset.requirements.first[:path]) end @@ -79,7 +138,7 @@ module ActionDispatch fakeset = FakeSet.new mapper = Mapper.new fakeset mapper.get '/*foo/*bar', :to => 'pages#show' - assert_equal '/*foo/*bar(.:format)', fakeset.conditions.first[:path_info] + assert_equal '/*foo/*bar(.:format)', fakeset.asts.first.to_s assert_equal(/.+?/, fakeset.requirements.first[:foo]) assert_equal(/.+?/, fakeset.requirements.first[:bar]) end @@ -88,7 +147,7 @@ module ActionDispatch fakeset = FakeSet.new mapper = Mapper.new fakeset mapper.get '/*path', :to => 'pages#show', :format => false - assert_equal '/*path', fakeset.conditions.first[:path_info] + assert_equal '/*path', fakeset.asts.first.to_s assert_nil fakeset.requirements.first[:path] end @@ -96,7 +155,7 @@ module ActionDispatch fakeset = FakeSet.new mapper = Mapper.new fakeset mapper.get '/*path', :to => 'pages#show', :format => true - assert_equal '/*path.:format', fakeset.conditions.first[:path_info] + assert_equal '/*path.:format', fakeset.asts.first.to_s end def test_raising_helpful_error_on_invalid_arguments diff --git a/actionpack/test/dispatch/middleware_stack/middleware_test.rb b/actionpack/test/dispatch/middleware_stack/middleware_test.rb deleted file mode 100644 index 9607f026db..0000000000 --- a/actionpack/test/dispatch/middleware_stack/middleware_test.rb +++ /dev/null @@ -1,77 +0,0 @@ -require 'abstract_unit' -require 'action_dispatch/middleware/stack' - -module ActionDispatch - class MiddlewareStack - class MiddlewareTest < ActiveSupport::TestCase - class Omg; end - - { - 'concrete' => Omg, - 'anonymous' => Class.new - }.each do |name, klass| - - define_method("test_#{name}_klass") do - mw = Middleware.new klass - assert_equal klass, mw.klass - end - - define_method("test_#{name}_==") do - mw1 = Middleware.new klass - mw2 = Middleware.new klass - assert_equal mw1, mw2 - end - - end - - def test_string_class - mw = Middleware.new Omg.name - assert_equal Omg, mw.klass - end - - def test_double_equal_works_with_classes - k = Class.new - mw = Middleware.new k - assert_operator mw, :==, k - - result = mw != Class.new - assert result, 'middleware should not equal other anon class' - end - - def test_double_equal_works_with_strings - mw = Middleware.new Omg - assert_operator mw, :==, Omg.name - end - - def test_double_equal_normalizes_strings - mw = Middleware.new Omg - assert_operator mw, :==, "::#{Omg.name}" - end - - def test_middleware_loads_classnames_from_cache - mw = Class.new(Middleware) { - attr_accessor :classcache - }.new(Omg.name) - - fake_cache = { mw.name => Omg } - mw.classcache = fake_cache - - assert_equal Omg, mw.klass - - fake_cache[mw.name] = Middleware - assert_equal Middleware, mw.klass - end - - def test_middleware_always_returns_class - mw = Class.new(Middleware) { - attr_accessor :classcache - }.new(Omg) - - fake_cache = { mw.name => Middleware } - mw.classcache = fake_cache - - assert_equal Omg, mw.klass - end - end - end -end diff --git a/actionpack/test/dispatch/middleware_stack_test.rb b/actionpack/test/dispatch/middleware_stack_test.rb index 948a690979..33aa616474 100644 --- a/actionpack/test/dispatch/middleware_stack_test.rb +++ b/actionpack/test/dispatch/middleware_stack_test.rb @@ -4,6 +4,7 @@ class MiddlewareStackTest < ActiveSupport::TestCase class FooMiddleware; end class BarMiddleware; end class BazMiddleware; end + class HiyaMiddleware; end class BlockMiddleware attr_reader :block def initialize(&block) @@ -17,6 +18,20 @@ class MiddlewareStackTest < ActiveSupport::TestCase @stack.use BarMiddleware end + def test_delete_with_string_is_deprecated + assert_deprecated do + assert_difference "@stack.size", -1 do + @stack.delete FooMiddleware.name + end + end + end + + def test_delete_works + assert_difference "@stack.size", -1 do + @stack.delete FooMiddleware + end + end + test "use should push middleware as class onto the stack" do assert_difference "@stack.size" do @stack.use BazMiddleware @@ -25,17 +40,21 @@ class MiddlewareStackTest < ActiveSupport::TestCase end test "use should push middleware as a string onto the stack" do - assert_difference "@stack.size" do - @stack.use "MiddlewareStackTest::BazMiddleware" + assert_deprecated do + assert_difference "@stack.size" do + @stack.use "MiddlewareStackTest::BazMiddleware" + end + assert_equal BazMiddleware, @stack.last.klass end - assert_equal BazMiddleware, @stack.last.klass end test "use should push middleware as a symbol onto the stack" do - assert_difference "@stack.size" do - @stack.use :"MiddlewareStackTest::BazMiddleware" + assert_deprecated do + assert_difference "@stack.size" do + @stack.use :"MiddlewareStackTest::BazMiddleware" + end + assert_equal BazMiddleware, @stack.last.klass end - assert_equal BazMiddleware, @stack.last.klass end test "use should push middleware class with arguments onto the stack" do @@ -88,30 +107,28 @@ class MiddlewareStackTest < ActiveSupport::TestCase end test "unshift adds a new middleware at the beginning of the stack" do - @stack.unshift :"MiddlewareStackTest::BazMiddleware" - assert_equal BazMiddleware, @stack.first.klass + assert_deprecated do + @stack.unshift :"MiddlewareStackTest::BazMiddleware" + assert_equal BazMiddleware, @stack.first.klass + end end test "raise an error on invalid index" do assert_raise RuntimeError do - @stack.insert("HiyaMiddleware", BazMiddleware) + @stack.insert(HiyaMiddleware, BazMiddleware) end assert_raise RuntimeError do - @stack.insert_after("HiyaMiddleware", BazMiddleware) + @stack.insert_after(HiyaMiddleware, BazMiddleware) end end test "lazy evaluates middleware class" do - assert_difference "@stack.size" do - @stack.use "MiddlewareStackTest::BazMiddleware" + assert_deprecated do + assert_difference "@stack.size" do + @stack.use "MiddlewareStackTest::BazMiddleware" + end + assert_equal BazMiddleware, @stack.last.klass end - assert_equal BazMiddleware, @stack.last.klass - end - - test "lazy compares so unloaded constants are not loaded" do - @stack.use "UnknownMiddleware" - @stack.use :"MiddlewareStackTest::BazMiddleware" - assert @stack.include?("::MiddlewareStackTest::BazMiddleware") end end diff --git a/actionpack/test/dispatch/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb index 3017a9c2d6..149e37bf3d 100644 --- a/actionpack/test/dispatch/mime_type_test.rb +++ b/actionpack/test/dispatch/mime_type_test.rb @@ -1,7 +1,6 @@ require 'abstract_unit' class MimeTypeTest < ActiveSupport::TestCase - test "parse single" do Mime::LOOKUP.each_key do |mime_type| unless mime_type == 'image/*' @@ -11,97 +10,95 @@ class MimeTypeTest < ActiveSupport::TestCase end test "unregister" do + assert_nil Mime[:mobile] + begin - Mime::Type.register("text/x-mobile", :mobile) - assert defined?(Mime::MOBILE) - assert_equal Mime::MOBILE, Mime::LOOKUP['text/x-mobile'] - assert_equal Mime::MOBILE, Mime::EXTENSION_LOOKUP['mobile'] + mime = Mime::Type.register("text/x-mobile", :mobile) + assert_equal mime, Mime[:mobile] + assert_equal mime, Mime::Type.lookup('text/x-mobile') + assert_equal mime, Mime::Type.lookup_by_extension(:mobile) Mime::Type.unregister(:mobile) - assert !defined?(Mime::MOBILE), "Mime::MOBILE should not be defined" - assert !Mime::LOOKUP.has_key?('text/x-mobile'), "Mime::LOOKUP should not have key ['text/x-mobile]" - assert !Mime::EXTENSION_LOOKUP.has_key?('mobile'), "Mime::EXTENSION_LOOKUP should not have key ['mobile]" + assert_nil Mime[:mobile], "Mime[:mobile] should be nil after unregistering :mobile" + assert_nil Mime::Type.lookup_by_extension(:mobile), "Should be missing MIME extension lookup for :mobile" ensure - Mime.module_eval { remove_const :MOBILE if const_defined?(:MOBILE) } - Mime::LOOKUP.reject!{|key,_| key == 'text/x-mobile'} + Mime::Type.unregister :mobile end end 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[:xml], Mime[:yaml], Mime[:json], Mime[:multipart_form]] parsed = Mime::Type.parse(accept) assert_equal expect, parsed end test "parse text with trailing star in the end" do accept = "text/html, application/json, multipart/form-data, text/*" - expect = [Mime::HTML, Mime::JSON, Mime::MULTIPART_FORM, Mime::TEXT, Mime::JS, Mime::CSS, Mime::ICS, Mime::CSV, Mime::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[:xml], Mime[:yaml]] parsed = Mime::Type.parse(accept) assert_equal expect, parsed end test "parse text with trailing star" do accept = "text/*" - expect = [Mime::HTML, Mime::TEXT, Mime::JS, Mime::CSS, Mime::ICS, Mime::CSV, Mime::VCF, Mime::XML, Mime::YAML, Mime::JSON] + expect = [Mime[:html], Mime[:text], Mime[:js], Mime[:css], Mime[:ics], Mime[:csv], Mime[:vcf], Mime[:xml], Mime[:yaml], Mime[:json]] parsed = Mime::Type.parse(accept) assert_equal expect, parsed end test "parse application with trailing star" do accept = "application/*" - expect = [Mime::HTML, Mime::JS, Mime::XML, Mime::RSS, Mime::ATOM, Mime::YAML, Mime::URL_ENCODED_FORM, Mime::JSON, Mime::PDF, Mime::ZIP] + expect = [Mime[:html], Mime[:js], Mime[:xml], Mime[:rss], Mime[:atom], Mime[:yaml], Mime[:url_encoded_form], Mime[:json], Mime[:pdf], Mime[:zip]] parsed = Mime::Type.parse(accept) assert_equal expect, parsed end test "parse without q" do accept = "text/xml,application/xhtml+xml,text/yaml,application/xml,text/html,image/png,text/plain,application/pdf,*/*" - expect = [Mime::HTML, Mime::XML, Mime::YAML, Mime::PNG, Mime::TEXT, Mime::PDF, Mime::ALL] - assert_equal expect, Mime::Type.parse(accept) + expect = [Mime[:html], Mime[:xml], Mime[:yaml], Mime[:png], Mime[:text], Mime[:pdf], '*/*'] + assert_equal expect.map(&:to_s), Mime::Type.parse(accept).map(&:to_s) end test "parse with q" do accept = "text/xml,application/xhtml+xml,text/yaml; q=0.3,application/xml,text/html; q=0.8,image/png,text/plain; q=0.5,application/pdf,*/*; q=0.2" - expect = [Mime::HTML, Mime::XML, Mime::PNG, Mime::PDF, Mime::TEXT, Mime::YAML, Mime::ALL] - assert_equal expect, Mime::Type.parse(accept) + expect = [Mime[:html], Mime[:xml], Mime[:png], Mime[:pdf], Mime[:text], Mime[:yaml], '*/*'] + assert_equal expect.map(&:to_s), Mime::Type.parse(accept).map(&:to_s) end test "parse single media range with q" do accept = "text/html;q=0.9" - expect = [Mime::HTML] + expect = [Mime[:html]] assert_equal expect, Mime::Type.parse(accept) end test "parse arbitrary media type parameters" do accept = 'multipart/form-data; boundary="simple boundary"' - expect = [Mime::MULTIPART_FORM] + expect = [Mime[:multipart_form]] assert_equal expect, Mime::Type.parse(accept) end # Accept header send with user HTTP_USER_AGENT: Sunrise/0.42j (Windows XP) test "parse broken acceptlines" do accept = "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/*,,*/*;q=0.5" - expect = [Mime::HTML, Mime::XML, "image/*", Mime::TEXT, Mime::ALL] - assert_equal expect, Mime::Type.parse(accept).collect(&:to_s) + expect = [Mime[:html], Mime[:xml], "image/*", Mime[:text], '*/*'] + assert_equal expect.map(&:to_s), Mime::Type.parse(accept).map(&:to_s) end # Accept header send with user HTTP_USER_AGENT: Mozilla/4.0 # (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; InfoPath.1) test "parse other broken acceptlines" do accept = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, , pronto/1.00.00, sslvpn/1.00.00.00, */*" - expect = ['image/gif', 'image/x-xbitmap', 'image/jpeg','image/pjpeg', 'application/x-shockwave-flash', 'application/vnd.ms-excel', 'application/vnd.ms-powerpoint', 'application/msword', 'pronto/1.00.00', 'sslvpn/1.00.00.00', Mime::ALL] - assert_equal expect, Mime::Type.parse(accept).collect(&:to_s) + expect = ['image/gif', 'image/x-xbitmap', 'image/jpeg','image/pjpeg', 'application/x-shockwave-flash', 'application/vnd.ms-excel', 'application/vnd.ms-powerpoint', 'application/msword', 'pronto/1.00.00', 'sslvpn/1.00.00.00', '*/*'] + assert_equal expect.map(&:to_s), Mime::Type.parse(accept).map(&:to_s) end test "custom type" do begin - Mime::Type.register("image/foo", :foo) - assert_nothing_raised do - assert_equal Mime::FOO, Mime::SET.last - end + type = Mime::Type.register("image/foo", :foo) + assert_equal type, Mime[:foo] ensure - Mime::Type.unregister(:FOO) + Mime::Type.unregister(:foo) end end @@ -109,10 +106,10 @@ class MimeTypeTest < ActiveSupport::TestCase begin Mime::Type.register "text/foobar", :foobar, ["text/foo", "text/bar"] %w[text/foobar text/foo text/bar].each do |type| - assert_equal Mime::FOOBAR, type + assert_equal Mime[:foobar], type end ensure - Mime::Type.unregister(:FOOBAR) + Mime::Type.unregister(:foobar) end end @@ -123,10 +120,10 @@ class MimeTypeTest < ActiveSupport::TestCase registered_mimes << mime end - Mime::Type.register("text/foo", :foo) - assert_equal [Mime::FOO], registered_mimes + mime = Mime::Type.register("text/foo", :foo) + assert_equal [mime], registered_mimes ensure - Mime::Type.unregister(:FOO) + Mime::Type.unregister(:foo) end end @@ -134,70 +131,67 @@ class MimeTypeTest < ActiveSupport::TestCase begin Mime::Type.register "text/foobar", :foobar, [], [:foo, "bar"] %w[foobar foo bar].each do |extension| - assert_equal Mime::FOOBAR, Mime::EXTENSION_LOOKUP[extension] + assert_equal Mime[:foobar], Mime::EXTENSION_LOOKUP[extension] end ensure - Mime::Type.unregister(:FOOBAR) + Mime::Type.unregister(:foobar) end end test "register alias" do begin Mime::Type.register_alias "application/xhtml+xml", :foobar - assert_equal Mime::HTML, Mime::EXTENSION_LOOKUP['foobar'] + assert_equal Mime[:html], Mime::EXTENSION_LOOKUP['foobar'] ensure - Mime::Type.unregister(:FOOBAR) + Mime::Type.unregister(:foobar) end end test "type should be equal to symbol" do - assert_equal Mime::HTML, 'application/xhtml+xml' - assert_equal Mime::HTML, :html + assert_equal Mime[:html], 'application/xhtml+xml' + assert_equal Mime[:html], :html end test "type convenience methods" do - # Don't test Mime::ALL, since it Mime::ALL#html? == true - types = Mime::SET.symbols.uniq - [:all, :iphone] - - # Remove custom Mime::Type instances set in other tests, like Mime::GIF and Mime::IPHONE - types.delete_if { |type| !Mime.const_defined?(type.upcase) } - + types = Mime::SET.symbols.uniq - [:iphone] types.each do |type| - mime = Mime.const_get(type.upcase) + mime = Mime[type] assert mime.respond_to?("#{type}?"), "#{mime.inspect} does not respond to #{type}?" - assert mime.send("#{type}?"), "#{mime.inspect} is not #{type}?" + assert_equal type, mime.symbol, "#{mime.inspect} is not #{type}?" invalid_types = types - [type] - invalid_types.delete(:html) if Mime::Type.html_types.include?(type) - invalid_types.each { |other_type| assert !mime.send("#{other_type}?"), "#{mime.inspect} is #{other_type}?" } + invalid_types.delete(:html) + invalid_types.each { |other_type| + assert_not_equal mime.symbol, other_type, "#{mime.inspect} is #{other_type}?" + } end end - test "mime all is html" do - assert Mime::ALL.all?, "Mime::ALL is not all?" - assert Mime::ALL.html?, "Mime::ALL is not html?" + test "deprecated lookup" do + assert_deprecated do + Mime::HTML + end end - test "verifiable mime types" do - all_types = Mime::SET.symbols - all_types.uniq! - # Remove custom Mime::Type instances set in other tests, like Mime::GIF and Mime::IPHONE - all_types.delete_if { |type| !Mime.const_defined?(type.upcase) } + test "deprecated const_defined?" do + assert_deprecated do + Mime.const_defined? :HTML + end end test "references gives preference to symbols before strings" do - assert_equal :html, Mime::HTML.ref + assert_equal :html, Mime[:html].ref another = Mime::Type.lookup("foo/bar") assert_nil another.to_sym assert_equal "foo/bar", another.ref end test "regexp matcher" do - assert Mime::JS =~ "text/javascript" - assert Mime::JS =~ "application/javascript" - assert Mime::JS !~ "text/html" - assert !(Mime::JS !~ "text/javascript") - assert !(Mime::JS !~ "application/javascript") - assert Mime::HTML =~ 'application/xhtml+xml' + assert Mime[:js] =~ "text/javascript" + assert Mime[:js] =~ "application/javascript" + assert Mime[:js] !~ "text/html" + assert !(Mime[:js] !~ "text/javascript") + assert !(Mime[:js] !~ "application/javascript") + assert Mime[:html] =~ 'application/xhtml+xml' end end diff --git a/actionpack/test/dispatch/mount_test.rb b/actionpack/test/dispatch/mount_test.rb index 6a439be2b5..d027f09762 100644 --- a/actionpack/test/dispatch/mount_test.rb +++ b/actionpack/test/dispatch/mount_test.rb @@ -49,7 +49,7 @@ class TestRoutingMount < ActionDispatch::IntegrationTest def test_app_name_is_properly_generated_when_engine_is_mounted_in_resources assert Router.mounted_helpers.method_defined?(:user_fake_mounted_at_resource), "A mounted helper should be defined with a parent's prefix" - assert Router.named_routes.routes[:user_fake_mounted_at_resource], + assert Router.named_routes.key?(:user_fake_mounted_at_resource), "A named route should be defined with a parent's prefix" end diff --git a/actionpack/test/dispatch/request/json_params_parsing_test.rb b/actionpack/test/dispatch/request/json_params_parsing_test.rb index c2300a0142..a3992ad008 100644 --- a/actionpack/test/dispatch/request/json_params_parsing_test.rb +++ b/actionpack/test/dispatch/request/json_params_parsing_test.rb @@ -37,6 +37,13 @@ class JsonParamsParsingTest < ActionDispatch::IntegrationTest ) end + test "parses json params for application/vnd.api+json" do + assert_parses( + {"person" => {"name" => "David"}}, + "{\"person\": {\"name\": \"David\"}}", { 'CONTENT_TYPE' => 'application/vnd.api+json' } + ) + end + test "nils are stripped from collections" do assert_parses( {"person" => []}, @@ -69,8 +76,8 @@ class JsonParamsParsingTest < ActionDispatch::IntegrationTest $stderr = StringIO.new # suppress the log json = "[\"person]\": {\"name\": \"David\"}}" exception = assert_raise(ActionDispatch::ParamsParser::ParseError) { post "/parse", json, {'CONTENT_TYPE' => 'application/json', 'action_dispatch.show_exceptions' => false} } - assert_equal JSON::ParserError, exception.original_exception.class - assert_equal exception.original_exception.message, exception.message + assert_equal JSON::ParserError, exception.cause.class + assert_equal exception.cause.message, exception.message ensure $stderr = STDERR end @@ -136,6 +143,13 @@ class RootLessJSONParamsParsingTest < ActionDispatch::IntegrationTest ) end + test "parses json params for application/vnd.api+json" do + assert_parses( + {"user" => {"username" => "sikachu"}, "username" => "sikachu"}, + "{\"username\": \"sikachu\"}", { 'CONTENT_TYPE' => 'application/vnd.api+json' } + ) + end + test "parses json with non-object JSON content" do assert_parses( {"user" => {"_json" => "string content" }, "_json" => "string content" }, diff --git a/actionpack/test/dispatch/request/multipart_params_parsing_test.rb b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb index 939a771c65..b36fbd3c76 100644 --- a/actionpack/test/dispatch/request/multipart_params_parsing_test.rb +++ b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb @@ -63,6 +63,17 @@ class MultipartParamsParsingTest < ActionDispatch::IntegrationTest assert_equal 'contents', file.read end + test "parses utf8 filename with percent character" do + params = parse_multipart('utf8_filename') + assert_equal %w(file foo), params.keys.sort + assert_equal 'bar', params['foo'] + + file = params['file'] + assert_equal 'ファイル%名.txt', file.original_filename + assert_equal "text/plain", file.content_type + assert_equal 'contents', file.read + end + test "parses boundary problem file" do params = parse_multipart('boundary_problem_file') assert_equal %w(file foo), params.keys.sort diff --git a/actionpack/test/dispatch/request/session_test.rb b/actionpack/test/dispatch/request/session_test.rb index 10fb04e230..ae0e7e93ed 100644 --- a/actionpack/test/dispatch/request/session_test.rb +++ b/actionpack/test/dispatch/request/session_test.rb @@ -4,40 +4,42 @@ require 'action_dispatch/middleware/session/abstract_store' module ActionDispatch class Request class SessionTest < ActiveSupport::TestCase + attr_reader :req + + def setup + @req = ActionDispatch::Request.new({}) + end + def test_create_adds_itself_to_env - env = {} - s = Session.create(store, env, {}) - assert_equal s, env[Rack::Session::Abstract::ENV_SESSION_KEY] + s = Session.create(store, req, {}) + assert_equal s, req.env[Rack::RACK_SESSION] end def test_to_hash - env = {} - s = Session.create(store, env, {}) + s = Session.create(store, req, {}) s['foo'] = 'bar' assert_equal 'bar', s['foo'] assert_equal({'foo' => 'bar'}, s.to_hash) end def test_create_merges_old - env = {} - s = Session.create(store, env, {}) + s = Session.create(store, req, {}) s['foo'] = 'bar' - s1 = Session.create(store, env, {}) + s1 = Session.create(store, req, {}) assert_not_equal s, s1 assert_equal 'bar', s1['foo'] end def test_find - env = {} - assert_nil Session.find(env) + assert_nil Session.find(req) - s = Session.create(store, env, {}) - assert_equal s, Session.find(env) + s = Session.create(store, req, {}) + assert_equal s, Session.find(req) end def test_destroy - s = Session.create(store, {}, {}) + s = Session.create(store, req, {}) s['rails'] = 'ftw' s.destroy @@ -46,21 +48,21 @@ module ActionDispatch end def test_keys - s = Session.create(store, {}, {}) + s = Session.create(store, req, {}) s['rails'] = 'ftw' s['adequate'] = 'awesome' assert_equal %w[rails adequate], s.keys end def test_values - s = Session.create(store, {}, {}) + s = Session.create(store, req, {}) s['rails'] = 'ftw' s['adequate'] = 'awesome' assert_equal %w[ftw awesome], s.values end def test_clear - s = Session.create(store, {}, {}) + s = Session.create(store, req, {}) s['rails'] = 'ftw' s['adequate'] = 'awesome' @@ -69,7 +71,7 @@ module ActionDispatch end def test_update - s = Session.create(store, {}, {}) + s = Session.create(store, req, {}) s['rails'] = 'ftw' s.update(:rails => 'awesome') @@ -79,7 +81,7 @@ module ActionDispatch end def test_delete - s = Session.create(store, {}, {}) + s = Session.create(store, req, {}) s['rails'] = 'ftw' s.delete('rails') @@ -88,7 +90,7 @@ module ActionDispatch end def test_fetch - session = Session.create(store, {}, {}) + session = Session.create(store, req, {}) session['one'] = '1' assert_equal '1', session.fetch(:one) @@ -108,7 +110,7 @@ module ActionDispatch Class.new { def load_session(env); [1, {}]; end def session_exists?(env); true; end - def destroy_session(env, id, options); 123; end + def delete_session(env, id, options); 123; end }.new end end diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb index ff63c10e8d..22240699d9 100644 --- a/actionpack/test/dispatch/request_test.rb +++ b/actionpack/test/dispatch/request_test.rb @@ -749,84 +749,94 @@ end class RequestFormat < BaseRequestTest test "xml format" do request = stub_request - request.expects(:parameters).at_least_once.returns({ :format => 'xml' }) - assert_equal Mime::XML, request.format + assert_called(request, :parameters, times: 2, returns: {format: :xml}) do + assert_equal Mime[:xml], request.format + end end test "xhtml format" do request = stub_request - request.expects(:parameters).at_least_once.returns({ :format => 'xhtml' }) - assert_equal Mime::HTML, request.format + assert_called(request, :parameters, times: 2, returns: {format: :xhtml}) do + assert_equal Mime[:html], request.format + end end test "txt format" do request = stub_request - request.expects(:parameters).at_least_once.returns({ :format => 'txt' }) - assert_equal Mime::TEXT, request.format + assert_called(request, :parameters, times: 2, returns: {format: :txt}) do + assert_equal Mime[:text], request.format + end end test "XMLHttpRequest" do request = stub_request( 'HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest', - 'HTTP_ACCEPT' => [Mime::JS, Mime::HTML, Mime::XML, "text/xml", Mime::ALL].join(",") + 'HTTP_ACCEPT' => [Mime[:js], Mime[:html], Mime[:xml], "text/xml", "*/*"].join(",") ) - request.expects(:parameters).at_least_once.returns({}) - assert request.xhr? - assert_equal Mime::JS, request.format + + assert_called(request, :parameters, times: 1, returns: {}) do + assert request.xhr? + assert_equal Mime[:js], request.format + end end test "can override format with parameter negative" do request = stub_request - request.expects(:parameters).at_least_once.returns({ :format => :txt }) - assert !request.format.xml? + assert_called(request, :parameters, times: 2, returns: {format: :txt}) do + assert !request.format.xml? + end end test "can override format with parameter positive" do request = stub_request - request.expects(:parameters).at_least_once.returns({ :format => :xml }) - assert request.format.xml? + assert_called(request, :parameters, times: 2, returns: {format: :xml}) do + assert request.format.xml? + end end test "formats text/html with accept header" do request = stub_request 'HTTP_ACCEPT' => 'text/html' - assert_equal [Mime::HTML], request.formats + assert_equal [Mime[:html]], request.formats end test "formats blank with accept header" do request = stub_request 'HTTP_ACCEPT' => '' - assert_equal [Mime::HTML], request.formats + assert_equal [Mime[:html]], request.formats end test "formats XMLHttpRequest with accept header" do request = stub_request 'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest" - assert_equal [Mime::JS], request.formats + assert_equal [Mime[:js]], request.formats end test "formats application/xml with accept header" do request = stub_request('CONTENT_TYPE' => 'application/xml; charset=UTF-8', 'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest") - assert_equal [Mime::XML], request.formats + assert_equal [Mime[:xml]], request.formats end test "formats format:text with accept header" do request = stub_request - request.expects(:parameters).at_least_once.returns({ :format => :txt }) - assert_equal [Mime::TEXT], request.formats + assert_called(request, :parameters, times: 2, returns: {format: :txt}) do + assert_equal [Mime[:text]], request.formats + end end test "formats format:unknown with accept header" do request = stub_request - request.expects(:parameters).at_least_once.returns({ :format => :unknown }) - assert_instance_of Mime::NullType, request.format + assert_called(request, :parameters, times: 2, returns: {format: :unknown}) do + assert_instance_of Mime::NullType, request.format + end end test "format is not nil with unknown format" do request = stub_request - request.expects(:parameters).at_least_once.returns({ format: :hello }) - assert request.format.nil? - assert_not request.format.html? - assert_not request.format.xml? - assert_not request.format.json? + 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 end test "format does not throw exceptions when malformed parameters" do @@ -837,8 +847,9 @@ class RequestFormat < BaseRequestTest test "formats with xhr request" do request = stub_request 'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest" - request.expects(:parameters).at_least_once.returns({}) - assert_equal [Mime::JS], request.formats + assert_called(request, :parameters, times: 1, returns: {}) do + assert_equal [Mime[:js]], request.formats + end end test "ignore_accept_header" do @@ -847,30 +858,37 @@ class RequestFormat < BaseRequestTest begin request = stub_request 'HTTP_ACCEPT' => 'application/xml' - request.expects(:parameters).at_least_once.returns({}) - assert_equal [ Mime::HTML ], request.formats + assert_called(request, :parameters, times: 1, returns: {}) do + assert_equal [ Mime[:html] ], request.formats + end request = stub_request 'HTTP_ACCEPT' => 'koz-asked/something-crazy' - request.expects(:parameters).at_least_once.returns({}) - assert_equal [ Mime::HTML ], request.formats + assert_called(request, :parameters, times: 1, returns: {}) do + assert_equal [ Mime[:html] ], request.formats + end request = stub_request 'HTTP_ACCEPT' => '*/*;q=0.1' - request.expects(:parameters).at_least_once.returns({}) - assert_equal [ Mime::HTML ], request.formats + assert_called(request, :parameters, times: 1, returns: {}) do + assert_equal [ Mime[:html] ], request.formats + end request = stub_request 'HTTP_ACCEPT' => 'application/jxw' - request.expects(:parameters).at_least_once.returns({}) - assert_equal [ Mime::HTML ], request.formats + assert_called(request, :parameters, times: 1, returns: {}) do + assert_equal [ Mime[:html] ], request.formats + end request = stub_request 'HTTP_ACCEPT' => 'application/xml', 'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest" - request.expects(:parameters).at_least_once.returns({}) - assert_equal [ Mime::JS ], request.formats + + assert_called(request, :parameters, times: 1, returns: {}) do + assert_equal [ Mime[:js] ], request.formats + end request = stub_request 'HTTP_ACCEPT' => 'application/xml', 'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest" - request.expects(:parameters).at_least_once.returns({:format => :json}) - assert_equal [ Mime::JSON ], request.formats + assert_called(request, :parameters, times: 2, returns: {format: :json}) do + assert_equal [ Mime[:json] ], request.formats + end ensure ActionDispatch::Request.ignore_accept_header = old_ignore_accept_header end @@ -879,7 +897,7 @@ end class RequestMimeType < BaseRequestTest test "content type" do - assert_equal Mime::HTML, stub_request('CONTENT_TYPE' => 'text/html').content_mime_type + assert_equal Mime[:html], stub_request('CONTENT_TYPE' => 'text/html').content_mime_type end test "no content type" do @@ -887,11 +905,11 @@ class RequestMimeType < BaseRequestTest end test "content type is XML" do - assert_equal Mime::XML, stub_request('CONTENT_TYPE' => 'application/xml').content_mime_type + assert_equal Mime[:xml], stub_request('CONTENT_TYPE' => 'application/xml').content_mime_type end test "content type with charset" do - assert_equal Mime::XML, stub_request('CONTENT_TYPE' => 'application/xml; charset=UTF-8').content_mime_type + assert_equal Mime[:xml], stub_request('CONTENT_TYPE' => 'application/xml; charset=UTF-8').content_mime_type end test "user agent" do @@ -904,9 +922,9 @@ class RequestMimeType < BaseRequestTest 'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest" ) - assert_equal nil, request.negotiate_mime([Mime::XML, Mime::JSON]) - assert_equal Mime::HTML, request.negotiate_mime([Mime::XML, Mime::HTML]) - assert_equal Mime::HTML, request.negotiate_mime([Mime::XML, Mime::ALL]) + assert_equal nil, request.negotiate_mime([Mime[:xml], Mime[:json]]) + assert_equal Mime[:html], request.negotiate_mime([Mime[:xml], Mime[:html]]) + assert_equal Mime[:html], request.negotiate_mime([Mime[:xml], Mime::ALL]) end test "negotiate_mime with content_type" do @@ -915,19 +933,21 @@ class RequestMimeType < BaseRequestTest 'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest" ) - assert_equal Mime::XML, request.negotiate_mime([Mime::XML, Mime::CSV]) + assert_equal Mime[:xml], request.negotiate_mime([Mime[:xml], Mime[:csv]]) end end class RequestParameters < BaseRequestTest test "parameters" do request = stub_request - request.expects(:request_parameters).at_least_once.returns({ "foo" => 1 }) - request.expects(:query_parameters).at_least_once.returns({ "bar" => 2 }) - assert_equal({"foo" => 1, "bar" => 2}, request.parameters) - assert_equal({"foo" => 1}, request.request_parameters) - assert_equal({"bar" => 2}, request.query_parameters) + 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 end test "parameters not accessible after rack parse error" do @@ -941,15 +961,33 @@ class RequestParameters < BaseRequestTest end end + test "path parameters with invalid UTF8 encoding" do + request = stub_request( + "action_dispatch.request.path_parameters" => { foo: "\xBE" } + ) + + err = assert_raises(ActionController::BadRequest) do + request.check_path_parameters! + end + + assert_match "Invalid parameter encoding", err.message + assert_match "foo", err.message + assert_match "\\xBE", err.message + end + test "parameters not accessible after rack parse error of invalid UTF8 character" do request = stub_request("QUERY_STRING" => "foo%81E=1") + assert_raises(ActionController::BadRequest) { request.parameters } + end - 2.times do - assert_raises(ActionController::BadRequest) do - # rack will raise a Rack::Utils::InvalidParameterError when parsing this query string - request.parameters - end - end + test "parameters containing an invalid UTF8 character" do + request = stub_request("QUERY_STRING" => "foo=%81E") + assert_raises(ActionController::BadRequest) { request.parameters } + end + + test "parameters containing a deeply nested invalid UTF8 character" do + request = stub_request("QUERY_STRING" => "foo[bar]=%81E") + assert_raises(ActionController::BadRequest) { request.parameters } end test "parameters not accessible after rack parse error 1" do @@ -974,8 +1012,8 @@ class RequestParameters < BaseRequestTest request.parameters end - assert e.original_exception - assert_equal e.original_exception.backtrace, e.backtrace + assert_not_nil e.cause + assert_equal e.cause.backtrace, e.backtrace end end diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb index 5aa0215e8f..981d820ccf 100644 --- a/actionpack/test/dispatch/response_test.rb +++ b/actionpack/test/dispatch/response_test.rb @@ -4,7 +4,7 @@ require 'rack/content_length' class ResponseTest < ActiveSupport::TestCase def setup - @response = ActionDispatch::Response.new + @response = ActionDispatch::Response.create end def test_can_wait_until_commit @@ -42,6 +42,18 @@ class ResponseTest < ActiveSupport::TestCase assert_equal Encoding::UTF_8, response.body.encoding end + def test_response_charset_writer + @response.charset = 'utf-16' + assert_equal 'utf-16', @response.charset + @response.charset = nil + assert_equal 'utf-8', @response.charset + end + + def test_setting_content_type_header_impacts_content_type_method + @response.headers['Content-Type'] = "application/aaron" + assert_equal 'application/aaron', @response.content_type + end + test "simple output" do @response.body = "Hello, World!" @@ -60,6 +72,13 @@ class ResponseTest < ActiveSupport::TestCase assert_equal 200, ActionDispatch::Response.new('200 OK').status end + def test_only_set_charset_still_defaults_to_text_html + response = ActionDispatch::Response.new + response.charset = "utf-16" + _,headers,_ = response.to_a + assert_equal "text/html; charset=utf-16", headers['Content-Type'] + end + test "utf8 output" do @response.body = [1090, 1077, 1089, 1090].pack("U*") @@ -127,17 +146,23 @@ class ResponseTest < ActiveSupport::TestCase test "cookies" do @response.set_cookie("user_name", :value => "david", :path => "/") - status, headers, body = @response.to_a + _status, headers, _body = @response.to_a assert_equal "user_name=david; path=/", headers["Set-Cookie"] assert_equal({"user_name" => "david"}, @response.cookies) + end + test "multiple cookies" do + @response.set_cookie("user_name", :value => "david", :path => "/") @response.set_cookie("login", :value => "foo&bar", :path => "/", :expires => Time.utc(2005, 10, 10,5)) - status, headers, body = @response.to_a + _status, headers, _body = @response.to_a assert_equal "user_name=david; path=/\nlogin=foo%26bar; path=/; expires=Mon, 10 Oct 2005 05:00:00 -0000", headers["Set-Cookie"] assert_equal({"login" => "foo&bar", "user_name" => "david"}, @response.cookies) + end + test "delete cookies" do + @response.set_cookie("user_name", :value => "david", :path => "/") + @response.set_cookie("login", :value => "foo&bar", :path => "/", :expires => Time.utc(2005, 10, 10,5)) @response.delete_cookie("login") - status, headers, body = @response.to_a assert_equal({"user_name" => "david", "login" => nil}, @response.cookies) end @@ -159,20 +184,28 @@ class ResponseTest < ActiveSupport::TestCase test "read charset and content type" do resp = ActionDispatch::Response.new.tap { |response| response.charset = 'utf-16' - response.content_type = Mime::XML + response.content_type = Mime[:xml] response.body = 'Hello' } resp.to_a assert_equal('utf-16', resp.charset) - assert_equal(Mime::XML, resp.content_type) + assert_equal(Mime[:xml], resp.content_type) assert_equal('application/xml; charset=utf-16', resp.headers['Content-Type']) end - test "read content type without charset" do - jruby_skip "https://github.com/jruby/jruby/issues/3138" + 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 + end + test "read content type with charset utf-16" do original = ActionDispatch::Response.default_charset begin ActionDispatch::Response.default_charset = 'utf-16' @@ -191,7 +224,7 @@ class ResponseTest < ActiveSupport::TestCase 'X-Content-Type-Options' => 'nosniff', 'X-XSS-Protection' => '1;' } - resp = ActionDispatch::Response.new.tap { |response| + resp = ActionDispatch::Response.create.tap { |response| response.body = 'Hello' } resp.to_a @@ -210,7 +243,7 @@ class ResponseTest < ActiveSupport::TestCase ActionDispatch::Response.default_headers = { 'X-XX-XXXX' => 'Here is my phone number' } - resp = ActionDispatch::Response.new.tap { |response| + resp = ActionDispatch::Response.create.tap { |response| response.body = 'Hello' } resp.to_a @@ -257,6 +290,65 @@ class ResponseTest < ActiveSupport::TestCase end end +class ResponseHeadersTest < ActiveSupport::TestCase + def setup + @response = ActionDispatch::Response.create + @response.set_header 'Foo', '1' + end + + test 'has_header?' do + assert @response.has_header? 'Foo' + assert_not @response.has_header? 'foo' + assert_not @response.has_header? nil + end + + test 'get_header' do + assert_equal '1', @response.get_header('Foo') + assert_nil @response.get_header('foo') + assert_nil @response.get_header(nil) + end + + test 'set_header' do + assert_equal '2', @response.set_header('Foo', '2') + assert @response.has_header?('Foo') + assert_equal '2', @response.get_header('Foo') + + assert_nil @response.set_header('Foo', nil) + assert @response.has_header?('Foo') + assert_nil @response.get_header('Foo') + end + + test 'delete_header' do + assert_nil @response.delete_header(nil) + + assert_nil @response.delete_header('foo') + assert @response.has_header?('Foo') + + assert_equal '1', @response.delete_header('Foo') + assert_not @response.has_header?('Foo') + end + + test 'add_header' do + # Add a value to an existing header + assert_equal '1,2', @response.add_header('Foo', '2') + assert_equal '1,2', @response.get_header('Foo') + + # Add nil to an existing header + assert_equal '1,2', @response.add_header('Foo', nil) + assert_equal '1,2', @response.get_header('Foo') + + # Add nil to a nonexistent header + assert_nil @response.add_header('Bar', nil) + assert_not @response.has_header?('Bar') + assert_nil @response.get_header('Bar') + + # Add a value to a nonexistent header + assert_equal '1', @response.add_header('Bar', '1') + assert @response.has_header?('Bar') + assert_equal '1', @response.get_header('Bar') + end +end + class ResponseIntegrationTest < ActionDispatch::IntegrationTest test "response cache control from railsish app" do @app = lambda { |env| @@ -298,7 +390,7 @@ class ResponseIntegrationTest < ActionDispatch::IntegrationTest @app = lambda { |env| ActionDispatch::Response.new.tap { |resp| resp.charset = 'utf-16' - resp.content_type = Mime::XML + resp.content_type = Mime[:xml] resp.body = 'Hello' }.to_a } @@ -307,7 +399,7 @@ class ResponseIntegrationTest < ActionDispatch::IntegrationTest assert_response :success assert_equal('utf-16', @response.charset) - assert_equal(Mime::XML, @response.content_type) + assert_equal(Mime[:xml], @response.content_type) assert_equal('application/xml; charset=utf-16', @response.headers['Content-Type']) end @@ -323,7 +415,7 @@ class ResponseIntegrationTest < ActionDispatch::IntegrationTest assert_response :success assert_equal('utf-16', @response.charset) - assert_equal(Mime::XML, @response.content_type) + assert_equal(Mime[:xml], @response.content_type) assert_equal('application/xml; charset=utf-16', @response.headers['Content-Type']) end diff --git a/actionpack/test/dispatch/routing/concerns_test.rb b/actionpack/test/dispatch/routing/concerns_test.rb index 361ceca677..7ef513b0c8 100644 --- a/actionpack/test/dispatch/routing/concerns_test.rb +++ b/actionpack/test/dispatch/routing/concerns_test.rb @@ -109,8 +109,6 @@ class RoutingConcernsTest < ActionDispatch::IntegrationTest end def test_concerns_executes_block_in_context_of_current_mapper - jruby_skip "https://github.com/jruby/jruby/issues/3143" - mapper = ActionDispatch::Routing::Mapper.new(ActionDispatch::Routing::RouteSet.new) mapper.concern :test_concern do resources :things diff --git a/actionpack/test/dispatch/routing/inspector_test.rb b/actionpack/test/dispatch/routing/inspector_test.rb index 4047214843..a17d07c40b 100644 --- a/actionpack/test/dispatch/routing/inspector_test.rb +++ b/actionpack/test/dispatch/routing/inspector_test.rb @@ -12,12 +12,6 @@ module ActionDispatch class RoutesInspectorTest < ActiveSupport::TestCase def setup @set = ActionDispatch::Routing::RouteSet.new - app = ActiveSupport::OrderedOptions.new - app.config = ActiveSupport::OrderedOptions.new - app.config.assets = ActiveSupport::OrderedOptions.new - app.config.assets.prefix = '/sprockets' - Rails.stubs(:application).returns(app) - Rails.stubs(:env).returns("development") end def draw(options = {}, &block) @@ -83,6 +77,17 @@ module ActionDispatch ], output end + def test_articles_inspect_with_multiple_verbs + output = draw do + match 'articles/:id', to: 'articles#update', via: [:put, :patch] + end + + assert_equal [ + "Prefix Verb URI Pattern Controller#Action", + " PUT|PATCH /articles/:id(.:format) articles#update" + ], output + end + def test_inspect_shows_custom_assets output = draw do get '/custom/assets', :to => 'custom_assets#show' @@ -316,9 +321,6 @@ module ActionDispatch def test_inspect_routes_shows_resources_route_when_assets_disabled @set = ActionDispatch::Routing::RouteSet.new - app = ActiveSupport::OrderedOptions.new - - Rails.stubs(:application).returns(app) output = draw do get '/cart', to: 'cart#show' diff --git a/actionpack/test/dispatch/routing/ipv6_redirect_test.rb b/actionpack/test/dispatch/routing/ipv6_redirect_test.rb new file mode 100644 index 0000000000..f1b2e8cfc7 --- /dev/null +++ b/actionpack/test/dispatch/routing/ipv6_redirect_test.rb @@ -0,0 +1,45 @@ +require 'abstract_unit' + +class IPv6IntegrationTest < ActionDispatch::IntegrationTest + Routes = ActionDispatch::Routing::RouteSet.new + include Routes.url_helpers + + class ::BadRouteRequestController < ActionController::Base + include Routes.url_helpers + def index + render :text => foo_path + end + + def foo + redirect_to :action => :index + end + end + + Routes.draw do + get "/", :to => 'bad_route_request#index', :as => :index + get "/foo", :to => "bad_route_request#foo", :as => :foo + end + + def _routes + Routes + end + + APP = build_app Routes + def app + APP + end + + test "bad IPv6 redirection" do + # def test_simple_redirect + request_env = { + 'REMOTE_ADDR' => 'fd07:2fa:6cff:2112:225:90ff:fec7:22aa', + 'HTTP_HOST' => '[fd07:2fa:6cff:2112:225:90ff:fec7:22aa]:3000', + 'SERVER_NAME' => '[fd07:2fa:6cff:2112:225:90ff:fec7:22aa]', + 'SERVER_PORT' => 3000 } + + get '/foo', env: request_env + assert_response :redirect + assert_equal 'http://[fd07:2fa:6cff:2112:225:90ff:fec7:22aa]:3000/', redirect_to_url + end + +end diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index b18a9ab647..8972f3e74d 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -1,4 +1,3 @@ -# encoding: UTF-8 require 'erb' require 'abstract_unit' require 'controller/fake_controllers' @@ -168,12 +167,10 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end def test_session_singleton_resource_for_api_app - self.class.stub_controllers do |_| - config = ActionDispatch::Routing::RouteSet::Config.new - config.api_only = true - - routes = ActionDispatch::Routing::RouteSet.new(config) + config = ActionDispatch::Routing::RouteSet::Config.new + config.api_only = true + self.class.stub_controllers(config) do |routes| routes.draw do resource :session do get :create @@ -363,9 +360,12 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end def test_pagemarks + tc = self draw do scope "pagemark", :controller => "pagemarks", :as => :pagemark do - get "new", :path => "build" + tc.assert_deprecated do + get "new", :path => "build" + end post "create", :as => "" put "update" get "remove", :action => :destroy, :as => :remove @@ -550,11 +550,10 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end def test_projects_for_api_app - self.class.stub_controllers do |_| - config = ActionDispatch::Routing::RouteSet::Config.new - config.api_only = true + config = ActionDispatch::Routing::RouteSet::Config.new + config.api_only = true - routes = ActionDispatch::Routing::RouteSet.new(config) + self.class.stub_controllers(config) do |routes| routes.draw do resources :projects, controller: :project end @@ -3621,7 +3620,7 @@ private end class TestAltApp < ActionDispatch::IntegrationTest - class AltRequest + class AltRequest < ActionDispatch::Request attr_accessor :path_parameters, :path_info, :script_name attr_reader :env @@ -3630,6 +3629,7 @@ class TestAltApp < ActionDispatch::IntegrationTest @env = env @path_info = "/" @script_name = "" + super end def request_method @@ -4189,11 +4189,11 @@ class TestNamedRouteUrlHelpers < ActionDispatch::IntegrationTest include Routes.url_helpers test "url helpers do not ignore nil parameters when using non-optimized routes" do - Routes.stubs(:optimize_routes_generation?).returns(false) - - get "/categories/1" - assert_response :success - assert_raises(ActionController::UrlGenerationError) { product_path(nil) } + Routes.stub :optimize_routes_generation?, false do + get "/categories/1" + assert_response :success + assert_raises(ActionController::UrlGenerationError) { product_path(nil) } + end end end diff --git a/actionpack/test/dispatch/session/abstract_store_test.rb b/actionpack/test/dispatch/session/abstract_store_test.rb index fe1a7b4f86..d38d1bbce6 100644 --- a/actionpack/test/dispatch/session/abstract_store_test.rb +++ b/actionpack/test/dispatch/session/abstract_store_test.rb @@ -10,13 +10,13 @@ module ActionDispatch super end - def get_session(env, sid) + def find_session(env, sid) sid ||= 1 session = @sessions[sid] ||= {} [sid, session] end - def set_session(env, sid, session, options) + def write_session(env, sid, session, options) @sessions[sid] = session end end @@ -27,7 +27,7 @@ module ActionDispatch as.call(env) assert @env - assert Request::Session.find @env + assert Request::Session.find ActionDispatch::Request.new @env end def test_new_session_object_is_merged_with_old @@ -36,11 +36,11 @@ module ActionDispatch as.call(env) assert @env - session = Request::Session.find @env + session = Request::Session.find ActionDispatch::Request.new @env session['foo'] = 'bar' as.call(@env) - session1 = Request::Session.find @env + session1 = Request::Session.find ActionDispatch::Request.new @env assert_not_equal session, session1 assert_equal session.to_hash, session1.to_hash diff --git a/actionpack/test/dispatch/session/cache_store_test.rb b/actionpack/test/dispatch/session/cache_store_test.rb index e6a70358c8..dbb996973d 100644 --- a/actionpack/test/dispatch/session/cache_store_test.rb +++ b/actionpack/test/dispatch/session/cache_store_test.rb @@ -170,7 +170,7 @@ class CacheStoreTest < ActionDispatch::IntegrationTest @app = self.class.build_app(set) do |middleware| @cache = ActiveSupport::Cache::MemoryStore.new middleware.use ActionDispatch::Session::CacheStore, :key => '_session_id', :cache => @cache - middleware.delete "ActionDispatch::ShowExceptions" + middleware.delete ActionDispatch::ShowExceptions end yield diff --git a/actionpack/test/dispatch/session/cookie_store_test.rb b/actionpack/test/dispatch/session/cookie_store_test.rb index 715eb90566..f07e215e3a 100644 --- a/actionpack/test/dispatch/session/cookie_store_test.rb +++ b/actionpack/test/dispatch/session/cookie_store_test.rb @@ -274,28 +274,32 @@ class CookieStoreTest < ActionDispatch::IntegrationTest with_test_route_set(:expire_after => 5.hours) do # First request accesses the session time = Time.local(2008, 4, 24) - Time.stubs(:now).returns(time) - expected_expiry = (time + 5.hours).gmtime.strftime("%a, %d %b %Y %H:%M:%S -0000") + cookie_body = nil - cookies[SessionKey] = SignedBar + Time.stub :now, time do + expected_expiry = (time + 5.hours).gmtime.strftime("%a, %d %b %Y %H:%M:%S -0000") - get '/set_session_value' - assert_response :success + cookies[SessionKey] = SignedBar - cookie_body = response.body - assert_equal "_myapp_session=#{cookie_body}; path=/; expires=#{expected_expiry}; HttpOnly", - headers['Set-Cookie'] + 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'] + end # Second request does not access the session time = Time.local(2008, 4, 25) - Time.stubs(:now).returns(time) - expected_expiry = (time + 5.hours).gmtime.strftime("%a, %d %b %Y %H:%M:%S -0000") + 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 + get '/no_session_access' + assert_response :success - assert_equal "_myapp_session=#{cookie_body}; path=/; expires=#{expected_expiry}; HttpOnly", - headers['Set-Cookie'] + assert_equal "_myapp_session=#{cookie_body}; path=/; expires=#{expected_expiry}; HttpOnly", + headers['Set-Cookie'] + end end end @@ -348,7 +352,7 @@ class CookieStoreTest < ActionDispatch::IntegrationTest @app = self.class.build_app(set) do |middleware| middleware.use ActionDispatch::Session::CookieStore, options - middleware.delete "ActionDispatch::ShowExceptions" + middleware.delete ActionDispatch::ShowExceptions end yield diff --git a/actionpack/test/dispatch/session/mem_cache_store_test.rb b/actionpack/test/dispatch/session/mem_cache_store_test.rb index 6e9107ecbf..3fed9bad4f 100644 --- a/actionpack/test/dispatch/session/mem_cache_store_test.rb +++ b/actionpack/test/dispatch/session/mem_cache_store_test.rb @@ -192,7 +192,7 @@ class MemCacheStoreTest < ActionDispatch::IntegrationTest @app = self.class.build_app(set) do |middleware| middleware.use ActionDispatch::Session::MemCacheStore, :key => '_session_id', :namespace => "mem_cache_store_test:#{SecureRandom.hex(10)}" - middleware.delete "ActionDispatch::ShowExceptions" + middleware.delete ActionDispatch::ShowExceptions end yield diff --git a/actionpack/test/dispatch/session/test_session_test.rb b/actionpack/test/dispatch/session/test_session_test.rb index d30461a623..3e61d123e3 100644 --- a/actionpack/test/dispatch/session/test_session_test.rb +++ b/actionpack/test/dispatch/session/test_session_test.rb @@ -40,4 +40,24 @@ class ActionController::TestSessionTest < ActiveSupport::TestCase assert_equal %w(one two), session.keys assert_equal %w(1 2), session.values end + + def test_fetch_returns_default + session = ActionController::TestSession.new(one: '1') + assert_equal('2', session.fetch(:two, '2')) + end + + def test_fetch_on_symbol_returns_value + session = ActionController::TestSession.new(one: '1') + assert_equal('1', session.fetch(:one)) + end + + def test_fetch_on_string_returns_value + session = ActionController::TestSession.new(one: '1') + assert_equal('1', session.fetch('one')) + end + + def test_fetch_returns_block_value + session = ActionController::TestSession.new(one: '1') + assert_equal(2, session.fetch('2') { |key| key.to_i }) + end end diff --git a/actionpack/test/dispatch/show_exceptions_test.rb b/actionpack/test/dispatch/show_exceptions_test.rb index cbb12a2209..ffdf775836 100644 --- a/actionpack/test/dispatch/show_exceptions_test.rb +++ b/actionpack/test/dispatch/show_exceptions_test.rb @@ -9,13 +9,21 @@ class ShowExceptionsTest < ActionDispatch::IntegrationTest when "/not_found" raise AbstractController::ActionNotFound when "/bad_params" - raise ActionDispatch::ParamsParser::ParseError.new("", StandardError.new) + begin + raise StandardError.new + rescue + raise ActionDispatch::ParamsParser::ParseError + end when "/method_not_allowed" raise ActionController::MethodNotAllowed, 'PUT' when "/unknown_http_method" raise ActionController::UnknownHttpMethod when "/not_found_original_exception" - raise ActionView::Template::Error.new('template', AbstractController::ActionNotFound.new) + begin + raise AbstractController::ActionNotFound.new + rescue + raise ActionView::Template::Error.new('template') + end else raise "puke!" end @@ -93,13 +101,13 @@ class ShowExceptionsTest < ActionDispatch::IntegrationTest assert_kind_of AbstractController::ActionNotFound, env["action_dispatch.exception"] assert_equal "/404", env["PATH_INFO"] assert_equal "/not_found_original_exception", env["action_dispatch.original_path"] - [404, { "Content-Type" => "text/plain" }, ["YOU FAILED BRO"]] + [404, { "Content-Type" => "text/plain" }, ["YOU FAILED"]] end @app = ActionDispatch::ShowExceptions.new(Boomer.new, exceptions_app) get "/not_found_original_exception", headers: { 'action_dispatch.show_exceptions' => true } assert_response 404 - assert_equal "YOU FAILED BRO", body + assert_equal "YOU FAILED", body end test "returns an empty response if custom exceptions app returns X-Cascade pass" do diff --git a/actionpack/test/dispatch/ssl_test.rb b/actionpack/test/dispatch/ssl_test.rb index 017e9ba2dd..7a5b8393dc 100644 --- a/actionpack/test/dispatch/ssl_test.rb +++ b/actionpack/test/dispatch/ssl_test.rb @@ -1,230 +1,199 @@ require 'abstract_unit' class SSLTest < ActionDispatch::IntegrationTest - def default_app - lambda { |env| - headers = {'Content-Type' => "text/html"} - headers['Set-Cookie'] = "id=1; path=/\ntoken=abc; path=/; secure; HttpOnly" - [200, headers, ["OK"]] + HEADERS = Rack::Utils::HeaderHash.new 'Content-Type' => 'text/html' + + attr_accessor :app + + def build_app(headers: {}, ssl_options: {}) + headers = HEADERS.merge(headers) + ActionDispatch::SSL.new lambda { |env| [200, headers, []] }, ssl_options + end +end + +class RedirectSSLTest < SSLTest + def assert_not_redirected(url, headers: {}) + self.app = build_app + get url, headers: headers + assert_response :ok + end + + def assert_redirected(host: nil, port: nil, status: 301, body: [], + 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 }, + host: deprecated_host, port: deprecated_port } + + get from + assert_response status + assert_redirected_to to + assert_equal body.join, @response.body end - def app - @app ||= ActionDispatch::SSL.new(default_app) + test 'https is not redirected' do + assert_not_redirected 'https://example.org' end - attr_writer :app - def test_allows_https_url - get "https://example.org/path?key=value" - assert_response :success + test 'proxied https is not redirected' do + assert_not_redirected 'http://example.org', headers: { 'HTTP_X_FORWARDED_PROTO' => 'https' } end - def test_allows_https_proxy_header_url - get "http://example.org/", headers: { 'HTTP_X_FORWARDED_PROTO' => "https" } - assert_response :success + test 'http is redirected to https' do + assert_redirected end - def test_redirects_http_to_https - get "http://example.org/path?key=value" - assert_response :redirect - assert_equal "https://example.org/path?key=value", - response.headers['Location'] + test 'redirect with non-301 status' do + assert_redirected status: 307 end - def test_hsts_header_by_default - get "https://example.org/" - assert_equal "max-age=31536000", - response.headers['Strict-Transport-Security'] + test 'redirect with custom body' do + assert_redirected body: ['foo'] end - def test_no_hsts_with_insecure_connection - get "http://example.org/" - assert_not response.headers['Strict-Transport-Security'] + test 'redirect to specific host' do + assert_redirected host: 'ssl', to: 'https://ssl/b?c=d' end - def test_hsts_header - self.app = ActionDispatch::SSL.new(default_app, :hsts => true) - get "https://example.org/" - assert_equal "max-age=31536000", - response.headers['Strict-Transport-Security'] + test 'redirect to default port' do + assert_redirected port: 443 end - def test_disable_hsts_header - self.app = ActionDispatch::SSL.new(default_app, :hsts => false) - get "https://example.org/" - assert_not response.headers['Strict-Transport-Security'] + test 'redirect to non-default port' do + assert_redirected port: 8443, to: 'https://a:8443/b?c=d' end - def test_hsts_expires - self.app = ActionDispatch::SSL.new(default_app, :hsts => { :expires => 500 }) - get "https://example.org/" - assert_equal "max-age=500", - response.headers['Strict-Transport-Security'] + test 'redirect to different host and non-default port' do + assert_redirected host: 'ssl', port: 8443, to: 'https://ssl:8443/b?c=d' end - def test_hsts_expires_with_duration - self.app = ActionDispatch::SSL.new(default_app, :hsts => { :expires => 1.year }) - get "https://example.org/" - assert_equal "max-age=31557600", - response.headers['Strict-Transport-Security'] + test 'redirect to different host including port' do + assert_redirected host: 'ssl:443', to: 'https://ssl:443/b?c=d' end - def test_hsts_include_subdomains - self.app = ActionDispatch::SSL.new(default_app, :hsts => { :subdomains => true }) - get "https://example.org/" - assert_equal "max-age=31536000; includeSubDomains", - response.headers['Strict-Transport-Security'] + test ':host is deprecated, moved within redirect: { host: … }' do + assert_deprecated do + assert_redirected deprecated_host: 'foo', to: 'https://foo/b?c=d' + end end - def test_flag_cookies_as_secure - get "https://example.org/" - assert_equal ["id=1; path=/; secure", "token=abc; path=/; secure; HttpOnly" ], - response.headers['Set-Cookie'].split("\n") + test ':port is deprecated, moved within redirect: { port: … }' do + assert_deprecated do + assert_redirected deprecated_port: 1, to: 'https://a:1/b?c=d' + end end +end - def test_flag_cookies_as_secure_at_end_of_line - self.app = ActionDispatch::SSL.new(lambda { |env| - headers = { - 'Content-Type' => "text/html", - 'Set-Cookie' => "problem=def; path=/; HttpOnly; secure" - } - [200, headers, ["OK"]] - }) +class StrictTransportSecurityTest < SSLTest + EXPECTED = 'max-age=15552000' - get "https://example.org/" - assert_equal ["problem=def; path=/; HttpOnly; secure"], - response.headers['Set-Cookie'].split("\n") + def assert_hsts(expected, url: 'https://example.org', hsts: {}, headers: {}) + self.app = build_app ssl_options: { hsts: hsts }, headers: headers + get url + assert_equal expected, response.headers['Strict-Transport-Security'] end - def test_flag_cookies_as_secure_with_more_spaces_before - self.app = ActionDispatch::SSL.new(lambda { |env| - headers = { - 'Content-Type' => "text/html", - 'Set-Cookie' => "problem=def; path=/; HttpOnly; secure" - } - [200, headers, ["OK"]] - }) + test 'enabled by default' do + assert_hsts EXPECTED + end - get "https://example.org/" - assert_equal ["problem=def; path=/; HttpOnly; secure"], - response.headers['Set-Cookie'].split("\n") + test 'not sent with http:// responses' do + assert_hsts nil, url: 'http://example.org' end - def test_flag_cookies_as_secure_with_more_spaces_after - self.app = ActionDispatch::SSL.new(lambda { |env| - headers = { - 'Content-Type' => "text/html", - 'Set-Cookie' => "problem=def; path=/; secure; HttpOnly" - } - [200, headers, ["OK"]] - }) + test 'defers to app-provided header' do + assert_hsts 'app-provided', headers: { 'Strict-Transport-Security' => 'app-provided' } + end - get "https://example.org/" - assert_equal ["problem=def; path=/; secure; HttpOnly"], - response.headers['Set-Cookie'].split("\n") + test 'hsts: true enables default settings' do + assert_hsts EXPECTED, hsts: true end + test 'hsts: false sets max-age to zero, clearing browser HSTS settings' do + assert_hsts 'max-age=0', hsts: false + end - def test_flag_cookies_as_secure_with_has_not_spaces_before - self.app = ActionDispatch::SSL.new(lambda { |env| - headers = { - 'Content-Type' => "text/html", - 'Set-Cookie' => "problem=def; path=/;secure; HttpOnly" - } - [200, headers, ["OK"]] - }) + test ':expires sets max-age' do + assert_hsts 'max-age=500', hsts: { expires: 500 } + end - get "https://example.org/" - assert_equal ["problem=def; path=/;secure; HttpOnly"], - response.headers['Set-Cookie'].split("\n") + test ':expires supports AS::Duration arguments' do + assert_hsts 'max-age=31557600', hsts: { expires: 1.year } end - def test_flag_cookies_as_secure_with_has_not_spaces_after - self.app = ActionDispatch::SSL.new(lambda { |env| - headers = { - 'Content-Type' => "text/html", - 'Set-Cookie' => "problem=def; path=/; secure;HttpOnly" - } - [200, headers, ["OK"]] - }) + test 'include subdomains' do + assert_hsts "#{EXPECTED}; includeSubDomains", hsts: { subdomains: true } + end - get "https://example.org/" - assert_equal ["problem=def; path=/; secure;HttpOnly"], - response.headers['Set-Cookie'].split("\n") + test 'exclude subdomains' do + assert_hsts EXPECTED, hsts: { subdomains: false } end - def test_flag_cookies_as_secure_with_ignore_case - self.app = ActionDispatch::SSL.new(lambda { |env| - headers = { - 'Content-Type' => "text/html", - 'Set-Cookie' => "problem=def; path=/; Secure; HttpOnly" - } - [200, headers, ["OK"]] - }) + test 'opt in to browser preload lists' do + assert_hsts "#{EXPECTED}; preload", hsts: { preload: true } + end - get "https://example.org/" - assert_equal ["problem=def; path=/; Secure; HttpOnly"], - response.headers['Set-Cookie'].split("\n") + test 'opt out of browser preload lists' do + assert_hsts EXPECTED, hsts: { preload: false } end +end - def test_no_cookies - self.app = ActionDispatch::SSL.new(lambda { |env| - [200, {'Content-Type' => "text/html"}, ["OK"]] - }) - get "https://example.org/" - assert !response.headers['Set-Cookie'] +class SecureCookiesTest < SSLTest + DEFAULT = %(id=1; path=/\ntoken=abc; path=/; secure; HttpOnly) + + def get(**options) + self.app = build_app(**options) + super 'https://example.org' + end + + def assert_cookies(*expected) + assert_equal expected, response.headers['Set-Cookie'].split("\n") + end + + def test_flag_cookies_as_secure + get headers: { 'Set-Cookie' => DEFAULT } + assert_cookies 'id=1; path=/; secure', 'token=abc; path=/; secure; HttpOnly' end - def test_redirect_to_host - self.app = ActionDispatch::SSL.new(default_app, :host => "ssl.example.org") - get "http://example.org/path?key=value" - assert_equal "https://ssl.example.org/path?key=value", - response.headers['Location'] + def test_flag_cookies_as_secure_at_end_of_line + get headers: { 'Set-Cookie' => 'problem=def; path=/; HttpOnly; secure' } + assert_cookies 'problem=def; path=/; HttpOnly; secure' + end + + def test_flag_cookies_as_secure_with_more_spaces_before + get headers: { 'Set-Cookie' => 'problem=def; path=/; HttpOnly; secure' } + assert_cookies 'problem=def; path=/; HttpOnly; secure' end - def test_redirect_to_port - self.app = ActionDispatch::SSL.new(default_app, :port => 8443) - get "http://example.org/path?key=value" - assert_equal "https://example.org:8443/path?key=value", - response.headers['Location'] + def test_flag_cookies_as_secure_with_more_spaces_after + get headers: { 'Set-Cookie' => 'problem=def; path=/; secure; HttpOnly' } + assert_cookies 'problem=def; path=/; secure; HttpOnly' end - def test_redirect_to_host_and_port - self.app = ActionDispatch::SSL.new(default_app, :host => "ssl.example.org", :port => 8443) - get "http://example.org/path?key=value" - assert_equal "https://ssl.example.org:8443/path?key=value", - response.headers['Location'] + def test_flag_cookies_as_secure_with_has_not_spaces_before + get headers: { 'Set-Cookie' => 'problem=def; path=/;secure; HttpOnly' } + assert_cookies 'problem=def; path=/;secure; HttpOnly' end - def test_redirect_to_host_with_port - self.app = ActionDispatch::SSL.new(default_app, :host => "ssl.example.org:443") - get "http://example.org/path?key=value" - assert_equal "https://ssl.example.org:443/path?key=value", - response.headers['Location'] + def test_flag_cookies_as_secure_with_has_not_spaces_after + get headers: { 'Set-Cookie' => 'problem=def; path=/; secure;HttpOnly' } + assert_cookies 'problem=def; path=/; secure;HttpOnly' end - def test_redirect_to_secure_host_when_on_subdomain - self.app = ActionDispatch::SSL.new(default_app, :host => "ssl.example.org") - get "http://ssl.example.org/path?key=value" - assert_equal "https://ssl.example.org/path?key=value", - response.headers['Location'] + def test_flag_cookies_as_secure_with_ignore_case + get headers: { 'Set-Cookie' => 'problem=def; path=/; Secure; HttpOnly' } + assert_cookies 'problem=def; path=/; Secure; HttpOnly' end - def test_redirect_to_secure_subdomain_when_on_deep_subdomain - self.app = ActionDispatch::SSL.new(default_app, :host => "example.co.uk") - get "http://double.rainbow.what.does.it.mean.example.co.uk/path?key=value" - assert_equal "https://example.co.uk/path?key=value", - response.headers['Location'] + def test_no_cookies + get + assert_nil response.headers['Set-Cookie'] end def test_keeps_original_headers_behavior - headers = Rack::Utils::HeaderHash.new( - "Content-Type" => "text/html", - "Connection" => ["close"] - ) - self.app = ActionDispatch::SSL.new(lambda { |env| [200, headers, ["OK"]] }) - - get "https://example.org/" - assert_equal "close", response.headers["Connection"] + get headers: { 'Connection' => %w[close] } + assert_equal 'close', response.headers['Connection'] end end diff --git a/actionpack/test/dispatch/static_test.rb b/actionpack/test/dispatch/static_test.rb index 95971b3a0e..1da57ab50b 100644 --- a/actionpack/test/dispatch/static_test.rb +++ b/actionpack/test/dispatch/static_test.rb @@ -2,6 +2,10 @@ require 'abstract_unit' require 'zlib' module StaticTests + DummyApp = lambda { |env| + [200, {"Content-Type" => "text/plain"}, ["Hello, World!"]] + } + def setup silence_warnings do @default_internal_encoding = Encoding.default_internal @@ -37,7 +41,11 @@ module StaticTests end def test_sets_cache_control - response = get("/index.html") + app = assert_deprecated do + ActionDispatch::Static.new(DummyApp, @root, "public, max-age=60") + end + response = Rack::MockRequest.new(app).request("GET", "/index.html") + assert_html "/index.html", response assert_equal "public, max-age=60", response.headers["Cache-Control"] end @@ -67,8 +75,6 @@ module StaticTests end def test_served_static_file_with_non_english_filename - jruby_skip "Stop skipping if following bug gets fixed: " \ - "http://jira.codehaus.org/browse/JRUBY-7192" assert_html "means hello in Japanese\n", get("/foo/#{Rack::Utils.escape("こんにちは.html")}") end @@ -156,7 +162,7 @@ module StaticTests def test_does_not_modify_path_info file_name = "/gzip/application-a71b3024f80aea3181c09774ca17e712.js" - env = {'PATH_INFO' => file_name, 'HTTP_ACCEPT_ENCODING' => 'gzip'} + env = {'PATH_INFO' => file_name, 'HTTP_ACCEPT_ENCODING' => 'gzip', "REQUEST_METHOD" => 'POST'} @app.call(env) assert_equal file_name, env['PATH_INFO'] end @@ -180,6 +186,21 @@ module StaticTests assert_equal nil, response.headers['Vary'] end + def test_serves_files_with_headers + headers = { + "Access-Control-Allow-Origin" => 'http://rubyonrails.org', + "Cache-Control" => 'public, max-age=60', + "X-Custom-Header" => "I'm a teapot" + } + + app = ActionDispatch::Static.new(DummyApp, @root, headers: headers) + response = Rack::MockRequest.new(app).request("GET", "/foo/bar.html") + + assert_equal 'http://rubyonrails.org', response.headers["Access-Control-Allow-Origin"] + assert_equal 'public, max-age=60', response.headers["Cache-Control"] + assert_equal "I'm a teapot", response.headers["X-Custom-Header"] + end + # Windows doesn't allow \ / : * ? " < > | in filenames unless RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ def test_serves_static_file_with_colon @@ -230,14 +251,10 @@ module StaticTests end class StaticTest < ActiveSupport::TestCase - DummyApp = lambda { |env| - [200, {"Content-Type" => "text/plain"}, ["Hello, World!"]] - } - def setup super @root = "#{FIXTURE_LOAD_PATH}/public" - @app = ActionDispatch::Static.new(DummyApp, @root, "public, max-age=60") + @app = ActionDispatch::Static.new(DummyApp, @root, headers: {'Cache-Control' => "public, max-age=60"}) end def public_path @@ -263,7 +280,7 @@ class StaticTest < ActiveSupport::TestCase end def test_non_default_static_index - @app = ActionDispatch::Static.new(DummyApp, @root, "public, max-age=60", index: "other-index") + @app = ActionDispatch::Static.new(DummyApp, @root, index: "other-index") assert_html "/other-index.html", get("/other-index.html") assert_html "/other-index.html", get("/other-index") assert_html "/other-index.html", get("/") @@ -280,7 +297,7 @@ class StaticEncodingTest < StaticTest def setup super @root = "#{FIXTURE_LOAD_PATH}/公共" - @app = ActionDispatch::Static.new(DummyApp, @root, "public, max-age=60") + @app = ActionDispatch::Static.new(DummyApp, @root, headers: {'Cache-Control' => "public, max-age=60"}) end def public_path diff --git a/actionpack/test/dispatch/test_request_test.rb b/actionpack/test/dispatch/test_request_test.rb index ede1cec4e6..51c469a61a 100644 --- a/actionpack/test/dispatch/test_request_test.rb +++ b/actionpack/test/dispatch/test_request_test.rb @@ -53,10 +53,8 @@ class TestRequestTest < ActiveSupport::TestCase assert_cookies({"user_name" => "david"}, req.cookie_jar) end - test "does not complain when Rails.application is nil" do - Rails.stubs(:application).returns(nil) + test "does not complain when there is no application config" do req = ActionDispatch::TestRequest.create({}) - assert_equal false, req.env.empty? end diff --git a/actionpack/test/fixtures/multipart/utf8_filename b/actionpack/test/fixtures/multipart/utf8_filename new file mode 100644 index 0000000000..60738d53b0 --- /dev/null +++ b/actionpack/test/fixtures/multipart/utf8_filename @@ -0,0 +1,10 @@ +--AaB03x
+Content-Disposition: form-data; name="foo"
+
+bar
+--AaB03x
+Content-Disposition: form-data; name="file"; filename="ファイル%名.txt"
+Content-Type: text/plain
+
+contents
+--AaB03x--
diff --git a/actionpack/test/journey/nodes/symbol_test.rb b/actionpack/test/journey/nodes/symbol_test.rb index d411a5018a..adf85b860c 100644 --- a/actionpack/test/journey/nodes/symbol_test.rb +++ b/actionpack/test/journey/nodes/symbol_test.rb @@ -5,7 +5,7 @@ module ActionDispatch module Nodes class TestSymbol < ActiveSupport::TestCase def test_default_regexp? - sym = Symbol.new nil + sym = Symbol.new "foo" assert sym.default_regexp? sym.regexp = nil diff --git a/actionpack/test/journey/path/pattern_test.rb b/actionpack/test/journey/path/pattern_test.rb index 6939b426f6..72858f5eda 100644 --- a/actionpack/test/journey/path/pattern_test.rb +++ b/actionpack/test/journey/path/pattern_test.rb @@ -4,6 +4,8 @@ module ActionDispatch module Journey module Path class TestPattern < ActiveSupport::TestCase + SEPARATORS = ["/", ".", "?"].join + x = /.+/ { '/:controller(/:action)' => %r{\A/(#{x})(?:/([^/.?]+))?\Z}, @@ -19,12 +21,12 @@ module ActionDispatch '/:foo|*bar' => %r{\A/(?:([^/.?]+)|(.+))\Z}, }.each do |path, expected| define_method(:"test_to_regexp_#{path}") do - strexp = Router::Strexp.build( + path = Pattern.build( path, { :controller => /.+/ }, - ["/", ".", "?"] + SEPARATORS, + true ) - path = Pattern.new strexp assert_equal(expected, path.to_regexp) end end @@ -43,13 +45,12 @@ module ActionDispatch '/:foo|*bar' => %r{\A/(?:([^/.?]+)|(.+))}, }.each do |path, expected| define_method(:"test_to_non_anchored_regexp_#{path}") do - strexp = Router::Strexp.build( + path = Pattern.build( path, { :controller => /.+/ }, - ["/", ".", "?"], + SEPARATORS, false ) - path = Pattern.new strexp assert_equal(expected, path.to_regexp) end end @@ -67,27 +68,27 @@ module ActionDispatch '/:controller/*foo/bar' => %w{ controller foo }, }.each do |path, expected| define_method(:"test_names_#{path}") do - strexp = Router::Strexp.build( + path = Pattern.build( path, { :controller => /.+/ }, - ["/", ".", "?"] + SEPARATORS, + true ) - path = Pattern.new strexp assert_equal(expected, path.names) end end def test_to_regexp_with_extended_group - strexp = Router::Strexp.build( + path = Pattern.build( '/page/:name', { :name => / #ROFL (tender|love #MAO )/x }, - ["/", ".", "?"] + SEPARATORS, + true ) - path = Pattern.new strexp assert_match(path, '/page/tender') assert_match(path, '/page/love') assert_no_match(path, '/page/loving') @@ -105,23 +106,23 @@ module ActionDispatch end def test_to_regexp_match_non_optional - strexp = Router::Strexp.build( + path = Pattern.build( '/:name', { :name => /\d+/ }, - ["/", ".", "?"] + SEPARATORS, + true ) - path = Pattern.new strexp assert_match(path, '/123') assert_no_match(path, '/') end def test_to_regexp_with_group - strexp = Router::Strexp.build( + path = Pattern.build( '/page/:name', { :name => /(tender|love)/ }, - ["/", ".", "?"] + SEPARATORS, + true ) - path = Pattern.new strexp assert_match(path, '/page/tender') assert_match(path, '/page/love') assert_no_match(path, '/page/loving') @@ -129,15 +130,13 @@ module ActionDispatch def test_ast_sets_regular_expressions requirements = { :name => /(tender|love)/, :value => /./ } - strexp = Router::Strexp.build( + path = Pattern.build( '/page/:name/:value', requirements, - ["/", ".", "?"] + SEPARATORS, + true ) - assert_equal requirements, strexp.requirements - - path = Pattern.new strexp nodes = path.ast.grep(Nodes::Symbol) assert_equal 2, nodes.length nodes.each do |node| @@ -146,24 +145,24 @@ module ActionDispatch end def test_match_data_with_group - strexp = Router::Strexp.build( + path = Pattern.build( '/page/:name', { :name => /(tender|love)/ }, - ["/", ".", "?"] + SEPARATORS, + true ) - path = Pattern.new strexp match = path.match '/page/tender' assert_equal 'tender', match[1] assert_equal 2, match.length end def test_match_data_with_multi_group - strexp = Router::Strexp.build( + path = Pattern.build( '/page/:name/:id', { :name => /t(((ender|love)))()/ }, - ["/", ".", "?"] + SEPARATORS, + true ) - path = Pattern.new strexp match = path.match '/page/tender/10' assert_equal 'tender', match[1] assert_equal '10', match[2] @@ -173,30 +172,29 @@ module ActionDispatch def test_star_with_custom_re z = /\d+/ - strexp = Router::Strexp.build( + path = Pattern.build( '/page/*foo', { :foo => z }, - ["/", ".", "?"] + SEPARATORS, + true ) - path = Pattern.new strexp assert_equal(%r{\A/page/(#{z})\Z}, path.to_regexp) end def test_insensitive_regexp_with_group - strexp = Router::Strexp.build( + path = Pattern.build( '/page/:name/aaron', { :name => /(tender|love)/i }, - ["/", ".", "?"] + SEPARATORS, + true ) - path = Pattern.new strexp assert_match(path, '/page/TENDER/aaron') assert_match(path, '/page/loVE/aaron') assert_no_match(path, '/page/loVE/AAron') end def test_to_regexp_with_strexp - strexp = Router::Strexp.build('/:controller', { }, ["/", ".", "?"]) - path = Pattern.new strexp + path = Pattern.build('/:controller', { }, SEPARATORS, true) x = %r{\A/([^/.?]+)\Z} assert_equal(x.source, path.source) diff --git a/actionpack/test/journey/route_test.rb b/actionpack/test/journey/route_test.rb index eff96a0abc..22c3b8113d 100644 --- a/actionpack/test/journey/route_test.rb +++ b/actionpack/test/journey/route_test.rb @@ -7,7 +7,7 @@ module ActionDispatch app = Object.new path = Path::Pattern.from_string '/:controller(/:action(/:id(.:format)))' defaults = {} - route = Route.new("name", app, path, {}, [], defaults) + route = Route.build("name", app, path, {}, [], defaults) assert_equal app, route.app assert_equal path, route.path @@ -18,7 +18,7 @@ module ActionDispatch app = Object.new path = Path::Pattern.from_string '/:controller(/:action(/:id(.:format)))' defaults = {} - route = Route.new("name", app, path, {}, [], defaults) + route = Route.build("name", app, path, {}, [], defaults) route.ast.grep(Nodes::Terminal).each do |node| assert_equal route, node.memo @@ -26,30 +26,29 @@ module ActionDispatch end def test_path_requirements_override_defaults - strexp = Router::Strexp.build(':name', { name: /love/ }, ['/']) - path = Path::Pattern.new strexp + path = Path::Pattern.build(':name', { name: /love/ }, '/', true) defaults = { name: 'tender' } - route = Route.new('name', nil, path, nil, [], defaults) + route = Route.build('name', nil, path, {}, [], defaults) assert_equal(/love/, route.requirements[:name]) end def test_ip_address path = Path::Pattern.from_string '/messages/:id(.:format)' - route = Route.new("name", nil, path, {:ip => '192.168.1.1'}, [], + route = Route.build("name", nil, path, {:ip => '192.168.1.1'}, [], { :controller => 'foo', :action => 'bar' }) assert_equal '192.168.1.1', route.ip end def test_default_ip path = Path::Pattern.from_string '/messages/:id(.:format)' - route = Route.new("name", nil, path, {}, [], + route = Route.build("name", nil, path, {}, [], { :controller => 'foo', :action => 'bar' }) assert_equal(//, route.ip) end def test_format_with_star path = Path::Pattern.from_string '/:controller/*extra' - route = Route.new("name", nil, path, {}, [], + route = Route.build("name", nil, path, {}, [], { :controller => 'foo', :action => 'bar' }) assert_equal '/foo/himom', route.format({ :controller => 'foo', @@ -59,7 +58,7 @@ module ActionDispatch def test_connects_all_match path = Path::Pattern.from_string '/:controller(/:action(/:id(.:format)))' - route = Route.new("name", nil, path, {:action => 'bar'}, [], { :controller => 'foo' }) + route = Route.build("name", nil, path, {:action => 'bar'}, [], { :controller => 'foo' }) assert_equal '/foo/bar/10', route.format({ :controller => 'foo', @@ -70,21 +69,21 @@ module ActionDispatch def test_extras_are_not_included_if_optional path = Path::Pattern.from_string '/page/:id(/:action)' - route = Route.new("name", nil, path, { }, [], { :action => 'show' }) + route = Route.build("name", nil, path, { }, [], { :action => 'show' }) assert_equal '/page/10', route.format({ :id => 10 }) end def test_extras_are_not_included_if_optional_with_parameter path = Path::Pattern.from_string '(/sections/:section)/pages/:id' - route = Route.new("name", nil, path, { }, [], { :action => 'show' }) + route = Route.build("name", nil, path, { }, [], { :action => 'show' }) assert_equal '/pages/10', route.format({:id => 10}) end def test_extras_are_not_included_if_optional_parameter_is_nil path = Path::Pattern.from_string '(/sections/:section)/pages/:id' - route = Route.new("name", nil, path, { }, [], { :action => 'show' }) + route = Route.build("name", nil, path, { }, [], { :action => 'show' }) assert_equal '/pages/10', route.format({:id => 10, :section => nil}) end @@ -94,10 +93,10 @@ module ActionDispatch defaults = {:controller=>"pages", :action=>"show"} path = Path::Pattern.from_string "/page/:id(/:action)(.:format)" - specific = Route.new "name", nil, path, constraints, [:controller, :action], defaults + specific = Route.build "name", nil, path, constraints, [:controller, :action], defaults path = Path::Pattern.from_string "/:controller(/:action(/:id))(.:format)" - generic = Route.new "name", nil, path, constraints, [], {} + generic = Route.build "name", nil, path, constraints, [], {} knowledge = {:id=>20, :controller=>"pages", :action=>"show"} diff --git a/actionpack/test/journey/router_test.rb b/actionpack/test/journey/router_test.rb index 802fb93c69..15d51e5d6c 100644 --- a/actionpack/test/journey/router_test.rb +++ b/actionpack/test/journey/router_test.rb @@ -1,141 +1,45 @@ -# encoding: UTF-8 require 'abstract_unit' module ActionDispatch module Journey class TestRouter < ActiveSupport::TestCase - attr_reader :routes + attr_reader :routes, :mapper def setup @app = Routing::RouteSet::Dispatcher.new({}) - @routes = Routes.new - @router = Router.new(@routes) - @formatter = Formatter.new(@routes) - end - - class FakeRequestFeeler < Struct.new(:env, :called) - def new env - self.env = env - self - end - - def hello - self.called = true - 'world' - end - - def path_info; env['PATH_INFO']; end - def request_method; env['REQUEST_METHOD']; end - def ip; env['REMOTE_ADDR']; end + @route_set = ActionDispatch::Routing::RouteSet.new + @routes = @route_set.router.routes + @router = @route_set.router + @formatter = @route_set.formatter + @mapper = ActionDispatch::Routing::Mapper.new @route_set end def test_dashes - router = Router.new(routes) - - exp = Router::Strexp.build '/foo-bar-baz', {}, ['/.?'] - path = Path::Pattern.new exp - - routes.add_route nil, path, {}, [], {:id => nil}, {} + mapper.get '/foo-bar-baz', to: 'foo#bar' env = rails_env 'PATH_INFO' => '/foo-bar-baz' called = false - router.recognize(env) do |r, params| + @router.recognize(env) do |r, params| called = true end assert called end def test_unicode - router = Router.new(routes) + mapper.get '/ほげ', to: 'foo#bar' #match the escaped version of /ほげ - exp = Router::Strexp.build '/%E3%81%BB%E3%81%92', {}, ['/.?'] - path = Path::Pattern.new exp - - routes.add_route nil, path, {}, [], {:id => nil}, {} - env = rails_env 'PATH_INFO' => '/%E3%81%BB%E3%81%92' called = false - router.recognize(env) do |r, params| + @router.recognize(env) do |r, params| called = true end assert called end - def test_request_class_and_requirements_success - klass = FakeRequestFeeler.new nil - router = Router.new(routes) - - requirements = { :hello => /world/ } - - exp = Router::Strexp.build '/foo(/:id)', {}, ['/.?'] - path = Path::Pattern.new exp - - routes.add_route nil, path, requirements, [], {:id => nil}, {} - - env = rails_env({'PATH_INFO' => '/foo/10'}, klass) - router.recognize(env) do |r, params| - assert_equal({:id => '10'}, params) - end - - assert klass.called, 'hello should have been called' - assert_equal env.env, klass.env - end - - def test_request_class_and_requirements_fail - klass = FakeRequestFeeler.new nil - router = Router.new(routes) - - requirements = { :hello => /mom/ } - - exp = Router::Strexp.build '/foo(/:id)', {}, ['/.?'] - path = Path::Pattern.new exp - - router.routes.add_route nil, path, requirements, [], {:id => nil}, {} - - env = rails_env({'PATH_INFO' => '/foo/10'}, klass) - router.recognize(env) do |r, params| - flunk 'route should not be found' - end - - assert klass.called, 'hello should have been called' - assert_equal env.env, klass.env - end - - class CustomPathRequest < ActionDispatch::Request - def path_info - env['custom.path_info'] - end - - def path_info=(x) - env['custom.path_info'] = x - end - end - - def test_request_class_overrides_path_info - router = Router.new(routes) - - exp = Router::Strexp.build '/bar', {}, ['/.?'] - path = Path::Pattern.new exp - - routes.add_route nil, path, {}, [], {}, {} - - env = rails_env({'PATH_INFO' => '/foo', - 'custom.path_info' => '/bar'}, CustomPathRequest) - - recognized = false - router.recognize(env) do |r, params| - recognized = true - end - - assert recognized, "route should have been recognized" - end - def test_regexp_first_precedence - add_routes @router, [ - Router::Strexp.build("/whois/:domain", {:domain => /\w+\.[\w\.]+/}, ['/', '.', '?']), - Router::Strexp.build("/whois/:id(.:format)", {}, ['/', '.', '?']) - ] + mapper.get "/whois/:domain", :domain => /\w+\.[\w\.]+/, to: "foo#bar" + mapper.get "/whois/:id(.:format)", to: "foo#baz" env = rails_env 'PATH_INFO' => '/whois/example.com' @@ -147,25 +51,21 @@ module ActionDispatch r = list.first - assert_equal '/whois/:domain', r.path.spec.to_s + assert_equal '/whois/:domain(.:format)', r.path.spec.to_s end def test_required_parts_verified_are_anchored - add_routes @router, [ - Router::Strexp.build("/foo/:id", { :id => /\d/ }, ['/', '.', '?'], false) - ] + mapper.get "/foo/:id", :id => /\d/, anchor: false, to: "foo#bar" assert_raises(ActionController::UrlGenerationError) do - @formatter.generate(nil, { :id => '10' }, { }) + @formatter.generate(nil, { :controller => "foo", :action => "bar", :id => '10' }, { }) end end def test_required_parts_are_verified_when_building - add_routes @router, [ - Router::Strexp.build("/foo/:id", { :id => /\d+/ }, ['/', '.', '?'], false) - ] + mapper.get "/foo/:id", :id => /\d+/, anchor: false, to: "foo#bar" - path, _ = @formatter.generate(nil, { :id => '10' }, { }) + path, _ = @formatter.generate(nil, { :controller => "foo", :action => "bar", :id => '10' }, { }) assert_equal '/foo/10', path assert_raises(ActionController::UrlGenerationError) do @@ -174,25 +74,22 @@ module ActionDispatch end def test_only_required_parts_are_verified - add_routes @router, [ - Router::Strexp.build("/foo(/:id)", {:id => /\d/}, ['/', '.', '?'], false) - ] + mapper.get "/foo(/:id)", :id => /\d/, :to => "foo#bar" - path, _ = @formatter.generate(nil, { :id => '10' }, { }) + path, _ = @formatter.generate(nil, { :controller => "foo", :action => "bar", :id => '10' }, { }) assert_equal '/foo/10', path - path, _ = @formatter.generate(nil, { }, { }) + path, _ = @formatter.generate(nil, { :controller => "foo", :action => "bar" }, { }) assert_equal '/foo', path - path, _ = @formatter.generate(nil, { :id => 'aa' }, { }) + path, _ = @formatter.generate(nil, { :controller => "foo", :action => "bar", :id => 'aa' }, { }) assert_equal '/foo/aa', path end def test_knows_what_parts_are_missing_from_named_route route_name = "gorby_thunderhorse" - pattern = Router::Strexp.build("/foo/:id", { :id => /\d+/ }, ['/', '.', '?'], false) - path = Path::Pattern.new pattern - @router.routes.add_route nil, path, {}, [], {}, route_name + mapper = ActionDispatch::Routing::Mapper.new @route_set + mapper.get "/foo/:id", :as => route_name, :id => /\d+/, :to => "foo#bar" error = assert_raises(ActionController::UrlGenerationError) do @formatter.generate(route_name, { }, { }) @@ -212,7 +109,7 @@ module ActionDispatch end def test_X_Cascade - add_routes @router, [ "/messages(.:format)" ] + mapper.get "/messages(.:format)", to: "foo#bar" resp = @router.serve(rails_env({ 'REQUEST_METHOD' => 'GET', 'PATH_INFO' => '/lol' })) assert_equal ['Not Found'], resp.last assert_equal 'pass', resp[1]['X-Cascade'] @@ -233,24 +130,21 @@ module ActionDispatch end def test_defaults_merge_correctly - path = Path::Pattern.from_string '/foo(/:id)' - @router.routes.add_route nil, path, {}, [], {:id => nil}, {} + mapper.get '/foo(/:id)', to: "foo#bar", id: nil env = rails_env 'PATH_INFO' => '/foo/10' @router.recognize(env) do |r, params| - assert_equal({:id => '10'}, params) + assert_equal({:id => '10', :controller => "foo", :action => "bar"}, params) end env = rails_env 'PATH_INFO' => '/foo' @router.recognize(env) do |r, params| - assert_equal({:id => nil}, params) + assert_equal({:id => nil, :controller => "foo", :action => "bar"}, params) end end def test_recognize_with_unbound_regexp - add_routes @router, [ - Router::Strexp.build("/foo", { }, ['/', '.', '?'], false) - ] + mapper.get "/foo", anchor: false, to: "foo#bar" env = rails_env 'PATH_INFO' => '/foo/bar' @@ -261,9 +155,7 @@ module ActionDispatch end def test_bound_regexp_keeps_path_info - add_routes @router, [ - Router::Strexp.build("/foo", { }, ['/', '.', '?'], true) - ] + mapper.get "/foo", to: "foo#bar" env = rails_env 'PATH_INFO' => '/foo' @@ -276,12 +168,14 @@ module ActionDispatch end def test_path_not_found - add_routes @router, [ + [ "/messages(.:format)", "/messages/new(.:format)", "/messages/:id/edit(.:format)", "/messages/:id(.:format)" - ] + ].each do |path| + mapper.get path, to: "foo#bar" + end env = rails_env 'PATH_INFO' => '/messages/unknown/path' yielded = false @@ -292,32 +186,29 @@ module ActionDispatch end def test_required_part_in_recall - add_routes @router, [ "/messages/:a/:b" ] + mapper.get "/messages/:a/:b", to: "foo#bar" - path, _ = @formatter.generate(nil, { :a => 'a' }, { :b => 'b' }) + path, _ = @formatter.generate(nil, { :controller => "foo", :action => "bar", :a => 'a' }, { :b => 'b' }) assert_equal "/messages/a/b", path end def test_splat_in_recall - add_routes @router, [ "/*path" ] + mapper.get "/*path", to: "foo#bar" - path, _ = @formatter.generate(nil, { }, { :path => 'b' }) + path, _ = @formatter.generate(nil, { :controller => "foo", :action => "bar" }, { :path => 'b' }) assert_equal "/b", path end def test_recall_should_be_used_when_scoring - add_routes @router, [ - "/messages/:action(/:id(.:format))", - "/messages/:id(.:format)" - ] + mapper.get "/messages/:action(/:id(.:format))", to: 'foo#bar' + mapper.get "/messages/:id(.:format)", to: 'bar#baz' - path, _ = @formatter.generate(nil, { :id => 10 }, { :action => 'index' }) + path, _ = @formatter.generate(nil, { :controller => "foo", :id => 10 }, { :action => 'index' }) assert_equal "/messages/index/10", path end def test_nil_path_parts_are_ignored - path = Path::Pattern.from_string "/:controller(/:action(.:format))" - @router.routes.add_route @app, path, {}, [], {}, {} + mapper.get "/:controller(/:action(.:format))", to: "tasks#lol" params = { :controller => "tasks", :format => nil } extras = { :action => 'lol' } @@ -329,18 +220,14 @@ module ActionDispatch def test_generate_slash params = [ [:controller, "tasks"], [:action, "show"] ] - str = Router::Strexp.build("/", Hash[params], ['/', '.', '?'], true) - path = Path::Pattern.new str - - @router.routes.add_route @app, path, {}, [], {}, {} + mapper.get "/", Hash[params] path, _ = @formatter.generate(nil, Hash[params], {}) assert_equal '/', path end def test_generate_calls_param_proc - path = Path::Pattern.from_string '/:controller(/:action)' - @router.routes.add_route @app, path, {}, [], {}, {} + mapper.get '/:controller(/:action)', to: "foo#bar" parameterized = [] params = [ [:controller, "tasks"], @@ -356,8 +243,7 @@ module ActionDispatch end def test_generate_id - path = Path::Pattern.from_string '/:controller(/:action)' - @router.routes.add_route @app, path, {}, [], {}, {} + mapper.get '/:controller(/:action)', to: 'foo#bar' path, params = @formatter.generate( nil, {:id=>1, :controller=>"tasks", :action=>"show"}, {}) @@ -366,8 +252,7 @@ module ActionDispatch end def test_generate_escapes - path = Path::Pattern.from_string '/:controller(/:action)' - @router.routes.add_route @app, path, {}, [], {}, {} + mapper.get '/:controller(/:action)', to: "foo#bar" path, _ = @formatter.generate(nil, { :controller => "tasks", @@ -377,8 +262,7 @@ module ActionDispatch end def test_generate_escapes_with_namespaced_controller - path = Path::Pattern.from_string '/:controller(/:action)' - @router.routes.add_route @app, path, {}, [], {}, {} + mapper.get '/:controller(/:action)', to: "foo#bar" path, _ = @formatter.generate( nil, { :controller => "admin/tasks", @@ -388,8 +272,7 @@ module ActionDispatch end def test_generate_extra_params - path = Path::Pattern.from_string '/:controller(/:action)' - @router.routes.add_route @app, path, {}, [], {}, {} + mapper.get '/:controller(/:action)', to: "foo#bar" path, params = @formatter.generate( nil, { :id => 1, @@ -402,8 +285,7 @@ module ActionDispatch end def test_generate_missing_keys_no_matches_different_format_keys - path = Path::Pattern.from_string '/:controller/:action/:name' - @router.routes.add_route @app, path, {}, [], {}, {} + mapper.get '/:controller/:action/:name', to: "foo#bar" primarty_parameters = { :id => 1, :controller => "tasks", @@ -429,8 +311,7 @@ module ActionDispatch end def test_generate_uses_recall_if_needed - path = Path::Pattern.from_string '/:controller(/:action(/:id))' - @router.routes.add_route @app, path, {}, [], {}, {} + mapper.get '/:controller(/:action(/:id))', to: "foo#bar" path, params = @formatter.generate( nil, @@ -441,8 +322,7 @@ module ActionDispatch end def test_generate_with_name - path = Path::Pattern.from_string '/:controller(/:action)' - @router.routes.add_route @app, path, {}, [], {}, "tasks" + mapper.get '/:controller(/:action)', to: 'foo#bar', as: 'tasks' path, params = @formatter.generate( "tasks", @@ -458,16 +338,15 @@ module ActionDispatch '/content/show/10' => { :controller => 'content', :action => 'show', :id => "10" }, }.each do |request_path, expected| define_method("test_recognize_#{expected.keys.map(&:to_s).join('_')}") do - path = Path::Pattern.from_string "/:controller(/:action(/:id))" - app = Object.new - route = @router.routes.add_route(app, path, {}, [], {}, {}) + mapper.get "/:controller(/:action(/:id))", to: 'foo#bar' + route = @routes.first env = rails_env 'PATH_INFO' => request_path called = false @router.recognize(env) do |r, params| assert_equal route, r - assert_equal(expected, params) + assert_equal({ :action => "bar" }.merge(expected), params) called = true end @@ -480,16 +359,15 @@ module ActionDispatch :splat => ['/segment/a/b%20c+d', { :segment => 'segment', :splat => 'a/b c+d' }] }.each do |name, (request_path, expected)| define_method("test_recognize_#{name}") do - path = Path::Pattern.from_string '/:segment/*splat' - app = Object.new - route = @router.routes.add_route(app, path, {}, [], {}, {}) + mapper.get '/:segment/*splat', to: 'foo#bar' env = rails_env 'PATH_INFO' => request_path called = false + route = @routes.first @router.recognize(env) do |r, params| assert_equal route, r - assert_equal(expected, params) + assert_equal(expected.merge(:controller=>"foo", :action=>"bar"), params) called = true end @@ -498,14 +376,8 @@ module ActionDispatch end def test_namespaced_controller - strexp = Router::Strexp.build( - "/:controller(/:action(/:id))", - { :controller => /.+?/ }, - ["/", ".", "?"] - ) - path = Path::Pattern.new strexp - app = Object.new - route = @router.routes.add_route(app, path, {}, [], {}, {}) + mapper.get "/:controller(/:action(/:id))", { :controller => /.+?/ } + route = @routes.first env = rails_env 'PATH_INFO' => '/admin/users/show/10' called = false @@ -524,9 +396,8 @@ module ActionDispatch end def test_recognize_literal - path = Path::Pattern.from_string "/books(/:action(.:format))" - app = Object.new - route = @router.routes.add_route(app, path, {}, [], {:controller => 'books'}) + mapper.get "/books(/:action(.:format))", controller: "books" + route = @routes.first env = rails_env 'PATH_INFO' => '/books/list.rss' expected = { :controller => 'books', :action => 'list', :format => 'rss' } @@ -541,10 +412,7 @@ module ActionDispatch end def test_recognize_head_route - path = Path::Pattern.from_string "/books(/:action(.:format))" - app = Object.new - conditions = { request_method: 'HEAD' } - @router.routes.add_route(app, path, conditions, [], {}) + mapper.match "/books(/:action(.:format))", via: 'head', to: 'foo#bar' env = rails_env( 'PATH_INFO' => '/books/list.rss', @@ -560,12 +428,7 @@ module ActionDispatch end def test_recognize_head_request_as_get_route - path = Path::Pattern.from_string "/books(/:action(.:format))" - app = Object.new - conditions = { - :request_method => 'GET' - } - @router.routes.add_route(app, path, conditions, [], {}) + mapper.get "/books(/:action(.:format))", to: 'foo#bar' env = rails_env 'PATH_INFO' => '/books/list.rss', "REQUEST_METHOD" => "HEAD" @@ -578,11 +441,8 @@ module ActionDispatch assert called end - def test_recognize_cares_about_verbs - path = Path::Pattern.from_string "/books(/:action(.:format))" - app = Object.new - conditions = { request_method: 'GET' } - @router.routes.add_route(app, path, conditions, [], {}) + def test_recognize_cares_about_get_verbs + mapper.match "/books(/:action(.:format))", to: "foo#bar", via: :get env = rails_env 'PATH_INFO' => '/books/list.rss', "REQUEST_METHOD" => "POST" @@ -593,34 +453,50 @@ module ActionDispatch end assert_not called + end - conditions = conditions.dup - conditions[:request_method] = 'POST' + def test_recognize_cares_about_post_verbs + mapper.match "/books(/:action(.:format))", to: "foo#bar", via: :post - post = @router.routes.add_route(app, path, conditions, [], {}) + env = rails_env 'PATH_INFO' => '/books/list.rss', + "REQUEST_METHOD" => "POST" called = false @router.recognize(env) do |r, params| - assert_equal post, r called = true end assert called end - private + def test_multi_verb_recognition + mapper.match "/books(/:action(.:format))", to: "foo#bar", via: [:post, :get] - def add_routes router, paths - paths.each do |path| - if String === path - path = Path::Pattern.from_string path - else - path = Path::Pattern.new path + %w( POST GET ).each do |verb| + env = rails_env 'PATH_INFO' => '/books/list.rss', + "REQUEST_METHOD" => verb + + called = false + @router.recognize(env) do |r, params| + called = true end - router.routes.add_route @app, path, {}, [], {}, {} + + assert called + end + + env = rails_env 'PATH_INFO' => '/books/list.rss', + "REQUEST_METHOD" => 'PUT' + + called = false + @router.recognize(env) do |r, params| + called = true end + + assert_not called end + private + def rails_env env, klass = ActionDispatch::Request klass.new(rack_env(env)) end diff --git a/actionpack/test/journey/routes_test.rb b/actionpack/test/journey/routes_test.rb index b9dac8751c..f8293dfc5f 100644 --- a/actionpack/test/journey/routes_test.rb +++ b/actionpack/test/journey/routes_test.rb @@ -3,18 +3,19 @@ require 'abstract_unit' module ActionDispatch module Journey class TestRoutes < ActiveSupport::TestCase - setup do - @routes = Routes.new + attr_reader :routes, :mapper + + def setup + @route_set = ActionDispatch::Routing::RouteSet.new + @routes = @route_set.router.routes + @router = @route_set.router + @mapper = ActionDispatch::Routing::Mapper.new @route_set + super end def test_clear - routes = Routes.new - exp = Router::Strexp.build '/foo(/:id)', {}, ['/.?'] - path = Path::Pattern.new exp - requirements = { :hello => /world/ } - - routes.add_route nil, path, requirements, [], {:id => nil}, {} - assert_not routes.empty? + mapper.get "/foo(/:id)", to: "foo#bar", as: 'aaron' + assert_not_predicate routes, :empty? assert_equal 1, routes.length routes.clear @@ -23,52 +24,36 @@ module ActionDispatch end def test_ast - routes = Routes.new - path = Path::Pattern.from_string '/hello' - - routes.add_route nil, path, {}, [], {}, {} + mapper.get "/foo(/:id)", to: "foo#bar", as: 'aaron' ast = routes.ast - routes.add_route nil, path, {}, [], {}, {} + mapper.get "/foo(/:id)", to: "foo#bar", as: 'gorby' assert_not_equal ast, routes.ast end def test_simulator_changes - routes = Routes.new - path = Path::Pattern.from_string '/hello' - - routes.add_route nil, path, {}, [], {}, {} + mapper.get "/foo(/:id)", to: "foo#bar", as: 'aaron' sim = routes.simulator - routes.add_route nil, path, {}, [], {}, {} + mapper.get "/foo(/:id)", to: "foo#bar", as: 'gorby' assert_not_equal sim, routes.simulator end def test_partition_route - path = Path::Pattern.from_string '/hello' + mapper.get "/foo(/:id)", to: "foo#bar", as: 'aaron' - anchored_route = @routes.add_route nil, path, {}, [], {}, {} - assert_equal [anchored_route], @routes.anchored_routes - assert_equal [], @routes.custom_routes + assert_equal 1, @routes.anchored_routes.length + assert_predicate @routes.custom_routes, :empty? - strexp = Router::Strexp.build( - "/hello/:who", { who: /\d/ }, ['/', '.', '?'] - ) - path = Path::Pattern.new strexp + mapper.get "/hello/:who", to: "foo#bar", as: 'bar', who: /\d/ - custom_route = @routes.add_route nil, path, {}, [], {}, {} - assert_equal [custom_route], @routes.custom_routes - assert_equal [anchored_route], @routes.anchored_routes + assert_equal 1, @routes.custom_routes.length + assert_equal 1, @routes.anchored_routes.length end def test_first_name_wins - routes = Routes.new - - one = Path::Pattern.from_string '/hello' - two = Path::Pattern.from_string '/aaron' - - routes.add_route nil, one, {}, [], {}, 'aaron' - routes.add_route nil, two, {}, [], {}, 'aaron' - - assert_equal '/hello', routes.named_routes['aaron'].path.spec.to_s + mapper.get "/hello", to: "foo#bar", as: 'aaron' + assert_raise(ArgumentError) do + mapper.get "/aaron", to: "foo#bar", as: 'aaron' + end end end end |