require 'abstract_unit' class RescueController < ActionController::Base class NotAuthorized < StandardError end class NotAuthorizedToRescueAsString < StandardError end class RecordInvalid < StandardError end class RecordInvalidToRescueAsString < StandardError end class NotAllowed < StandardError end class NotAllowedToRescueAsString < StandardError end class InvalidRequest < StandardError end class InvalidRequestToRescueAsString < StandardError end class BadGateway < StandardError end class BadGatewayToRescueAsString < StandardError end class ResourceUnavailable < StandardError end class ResourceUnavailableToRescueAsString < StandardError end # We use a fully-qualified name in some strings, and a relative constant # name in some other to test correct handling of both cases. rescue_from NotAuthorized, :with => :deny_access rescue_from 'RescueController::NotAuthorizedToRescueAsString', :with => :deny_access rescue_from RecordInvalid, :with => :show_errors rescue_from 'RescueController::RecordInvalidToRescueAsString', :with => :show_errors rescue_from NotAllowed, :with => proc { head :forbidden } rescue_from 'RescueController::NotAllowedToRescueAsString', :with => proc { head :forbidden } rescue_from InvalidRequest, :with => proc { |exception| render :text => exception.message } rescue_from 'InvalidRequestToRescueAsString', :with => proc { |exception| render :text => exception.message } rescue_from BadGateway do head :status => 502 end rescue_from 'BadGatewayToRescueAsString' do head :status => 502 end rescue_from ResourceUnavailable do |exception| render :text => exception.message end rescue_from 'ResourceUnavailableToRescueAsString' do |exception| render :text => exception.message end # This is a Dispatcher exception and should be in ApplicationController. rescue_from ActionController::RoutingError do render :text => 'no way' end before_filter(:only => :before_filter_raises) { raise 'umm nice' } def before_filter_raises end def raises render :text => 'already rendered' raise "don't panic!" end def method_not_allowed raise ActionController::MethodNotAllowed.new(:get, :head, :put) end def not_implemented raise ActionController::NotImplemented.new(:get, :put) end def not_authorized raise NotAuthorized end def not_authorized_raise_as_string raise NotAuthorizedToRescueAsString end def not_allowed raise NotAllowed end def not_allowed_raise_as_string raise NotAllowedToRescueAsString end def invalid_request raise InvalidRequest end def invalid_request_raise_as_string raise InvalidRequestToRescueAsString end def record_invalid raise RecordInvalid end def record_invalid_raise_as_string raise RecordInvalidToRescueAsString end def bad_gateway raise BadGateway end def bad_gateway_raise_as_string raise BadGatewayToRescueAsString end def resource_unavailable raise ResourceUnavailable end def resource_unavailable_raise_as_string raise ResourceUnavailableToRescueAsString end def missing_template end protected def deny_access head :forbidden end def show_errors(exception) head :unprocessable_entity end end class RescueControllerTest < ActionController::TestCase FIXTURE_PUBLIC = "#{File.dirname(__FILE__)}/../fixtures".freeze setup :set_all_requests_local setup :populate_exception_object def set_all_requests_local RescueController.consider_all_requests_local = true @request.remote_addr = '1.2.3.4' @request.host = 'example.com' end def populate_exception_object begin raise 'foo' rescue => @exception end end def test_rescue_exceptions_raised_by_filters with_rails_root FIXTURE_PUBLIC do with_all_requests_local false do get :before_filter_raises end end assert_response :internal_server_error end def test_rescue_action_locally_if_all_requests_local @controller.expects(:local_request?).never @controller.expects(:rescue_action_locally).with(@exception) @controller.expects(:rescue_action_in_public).never with_all_requests_local do @controller.send :rescue_action, @exception end end def test_rescue_action_locally_if_remote_addr_is_localhost @controller.expects(:local_request?).returns(true) @controller.expects(:rescue_action_locally).with(@exception) @controller.expects(:rescue_action_in_public).never with_all_requests_local false do @controller.send :rescue_action, @exception end end def test_rescue_action_in_public_otherwise @controller.expects(:local_request?).returns(false) @controller.expects(:rescue_action_locally).never @controller.expects(:rescue_action_in_public).with(@exception) with_all_requests_local false do @controller.send :rescue_action, @exception end end def test_rescue_action_in_public_with_localized_error_file # Change locale old_locale = I18n.locale I18n.locale = :da with_rails_root FIXTURE_PUBLIC do with_all_requests_local false do get :raises end end assert_response :internal_server_error body = File.read("#{FIXTURE_PUBLIC}/public/500.da.html") assert_equal body, @response.body ensure I18n.locale = old_locale end def test_rescue_action_in_public_with_error_file with_rails_root FIXTURE_PUBLIC do with_all_requests_local false do get :raises end end assert_response :internal_server_error body = File.read("#{FIXTURE_PUBLIC}/public/500.html") assert_equal body, @response.body end def test_rescue_action_in_public_without_error_file with_rails_root '/tmp' do with_all_requests_local false do get :raises end end assert_response :internal_server_error assert_equal ' ', @response.body end def test_rescue_unknown_action_in_public_with_error_file with_rails_root FIXTURE_PUBLIC do with_all_requests_local false do get :foobar_doesnt_exist end end assert_response :not_found body = File.read("#{FIXTURE_PUBLIC}/public/404.html") assert_equal body, @response.body end def test_rescue_unknown_action_in_public_without_error_file with_rails_root '/tmp' do with_all_requests_local false do get :foobar_doesnt_exist end end assert_response :not_found assert_equal ' ', @response.body end def test_rescue_missing_template_in_public with_rails_root FIXTURE_PUBLIC do with_all_requests_local true do get :missing_template end end assert_response :internal_server_error assert @response.body.include?('missing_template'), "Response should include the template name." end def test_rescue_action_locally get :raises assert_response :internal_server_error assert_template 'diagnostics.erb' assert @response.body.include?('RescueController#raises'), "Response should include controller and action." assert @response.body.include?("don't panic"), "Response should include exception message." end def test_local_request_when_remote_addr_is_localhost @controller.expects(:request).returns(@request).at_least_once with_remote_addr '127.0.0.1' do assert @controller.send(:local_request?) end end def test_local_request_when_remote_addr_isnt_locahost @controller.expects(:request).returns(@request) with_remote_addr '1.2.3.4' do assert !@controller.send(:local_request?) end end def test_rescue_responses responses = ActionController::Base.rescue_responses assert_equal ActionController::Rescue::DEFAULT_RESCUE_RESPONSE, responses.default assert_equal ActionController::Rescue::DEFAULT_RESCUE_RESPONSE, responses[Exception.new] assert_equal :not_found, responses[ActionController::RoutingError.name] assert_equal :not_found, responses[ActionController::UnknownAction.name] assert_equal :not_found, responses['ActiveRecord::RecordNotFound'] assert_equal :conflict, responses['ActiveRecord::StaleObjectError'] assert_equal :unprocessable_entity, responses['ActiveRecord::RecordInvalid'] assert_equal :unprocessable_entity, responses['ActiveRecord::RecordNotSaved'] assert_equal :method_not_allowed, responses['ActionController::MethodNotAllowed'] assert_equal :not_implemented, responses['ActionController::NotImplemented'] end def test_rescue_templates templates = ActionController::Base.rescue_templates assert_equal ActionController::Rescue::DEFAULT_RESCUE_TEMPLATE, templates.default assert_equal ActionController::Rescue::DEFAULT_RESCUE_TEMPLATE, templates[Exception.new] assert_equal 'missing_template', templates[ActionView::MissingTemplate.name] assert_equal 'routing_error', templates[ActionController::RoutingError.name] assert_equal 'unknown_action', templates[ActionController::UnknownAction.name] assert_equal 'template_error', templates[ActionView::TemplateError.name] end def test_not_implemented with_all_requests_local false do with_rails_public_path(".") do head :not_implemented end end assert_response :not_implemented assert_equal "GET, PUT", @response.headers['Allow'] end def test_method_not_allowed with_all_requests_local false do with_rails_public_path(".") do get :method_not_allowed end end assert_response :method_not_allowed assert_equal "GET, HEAD, PUT", @response.headers['Allow'] end def test_rescue_handler get :not_authorized assert_response :forbidden end def test_rescue_handler_string get :not_authorized_raise_as_string assert_response :forbidden end def test_rescue_handler_with_argument @controller.expects(:show_errors).once.with { |e| e.is_a?(Exception) } get :record_invalid 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 end def test_proc_rescue_handler get :not_allowed assert_response :forbidden end def test_proc_rescue_handler_as_string get :not_allowed_raise_as_string assert_response :forbidden end def test_proc_rescue_handle_with_argument get :invalid_request assert_equal "RescueController::InvalidRequest", @response.body end def test_proc_rescue_handle_with_argument_as_string get :invalid_request_raise_as_string assert_equal "RescueController::InvalidRequestToRescueAsString", @response.body end def test_block_rescue_handler get :bad_gateway assert_response 502 end def test_block_rescue_handler_as_string get :bad_gateway_raise_as_string assert_response 502 end def test_block_rescue_handler_with_argument get :resource_unavailable assert_equal "RescueController::ResourceUnavailable", @response.body end def test_block_rescue_handler_with_argument_as_string get :resource_unavailable_raise_as_string assert_equal "RescueController::ResourceUnavailableToRescueAsString", @response.body end def test_rescue_dispatcher_exceptions env = @request.env env["action_controller.rescue.request"] = @request env["action_controller.rescue.response"] = @response RescueController.call_with_exception(env, ActionController::RoutingError.new("Route not found")) assert_equal "no way", @response.body end def test_rescue_dispatcher_exceptions_without_request_set @request.env['REQUEST_URI'] = '/no_way' response = RescueController.call_with_exception(@request.env, ActionController::RoutingError.new("Route not found")) assert_kind_of ActionController::Response, response assert_equal "no way", response.body end protected def with_all_requests_local(local = true) old_local, ActionController::Base.consider_all_requests_local = ActionController::Base.consider_all_requests_local, local yield ensure ActionController::Base.consider_all_requests_local = old_local end def with_remote_addr(addr) old_remote_addr, @request.remote_addr = @request.remote_addr, addr yield ensure @request.remote_addr = old_remote_addr end def with_rails_public_path(rails_root) old_rails = Object.const_get(:Rails) rescue nil mod = Object.const_set(:Rails, Module.new) (class << mod; self; end).instance_eval do define_method(:public_path) { "#{rails_root}/public" } end yield ensure Object.module_eval { remove_const(:Rails) } if defined?(Rails) Object.const_set(:Rails, old_rails) if old_rails end def with_rails_root(path = nil,&block) old_rails_root = RAILS_ROOT if defined?(RAILS_ROOT) if path silence_warnings { Object.const_set(:RAILS_ROOT, path) } else Object.remove_const(:RAILS_ROOT) rescue nil end with_rails_public_path(path, &block) ensure if old_rails_root silence_warnings { Object.const_set(:RAILS_ROOT, old_rails_root) } else Object.remove_const(:RAILS_ROOT) rescue nil end end end class ExceptionInheritanceRescueController < ActionController::Base class ParentException < StandardError end class ChildException < ParentException end class GrandchildException < ChildException end rescue_from ChildException, :with => lambda { head :ok } rescue_from ParentException, :with => lambda { head :created } rescue_from GrandchildException, :with => lambda { head :no_content } def raise_parent_exception raise ParentException end def raise_child_exception raise ChildException end def raise_grandchild_exception raise GrandchildException end end class ExceptionInheritanceRescueControllerTest < ActionController::TestCase def test_bottom_first get :raise_grandchild_exception assert_response :no_content end def test_inheritance_works get :raise_child_exception assert_response :created end end class ControllerInheritanceRescueController < ExceptionInheritanceRescueController class FirstExceptionInChildController < StandardError end class SecondExceptionInChildController < StandardError end rescue_from FirstExceptionInChildController, 'SecondExceptionInChildController', :with => lambda { head :gone } def raise_first_exception_in_child_controller raise FirstExceptionInChildController end def raise_second_exception_in_child_controller raise SecondExceptionInChildController end end class ControllerInheritanceRescueControllerTest < ActionController::TestCase def test_first_exception_in_child_controller get :raise_first_exception_in_child_controller assert_response :gone end def test_second_exception_in_child_controller get :raise_second_exception_in_child_controller assert_response :gone end def test_exception_in_parent_controller get :raise_parent_exception assert_response :created end end