1 require File.dirname(__FILE__) + '/../abstract_unit'
5 class RescueController < ActionController::Base
6 class NotAuthorized < StandardError
8 class NotAuthorizedToRescueAsString < StandardError
11 class RecordInvalid < StandardError
13 class RecordInvalidToRescueAsString < StandardError
16 class NotAllowed < StandardError
18 class NotAllowedToRescueAsString < StandardError
21 class InvalidRequest < StandardError
23 class InvalidRequestToRescueAsString < StandardError
26 class BadGateway < StandardError
28 class BadGatewayToRescueAsString < StandardError
31 class ResourceUnavailable < StandardError
33 class ResourceUnavailableToRescueAsString < StandardError
36 # We use a fully-qualified name in some strings, and a relative constant
37 # name in some other to test correct handling of both cases.
39 rescue_from NotAuthorized, :with => :deny_access
40 rescue_from 'RescueController::NotAuthorizedToRescueAsString', :with => :deny_access
42 rescue_from RecordInvalid, :with => :show_errors
43 rescue_from 'RescueController::RecordInvalidToRescueAsString', :with => :show_errors
45 rescue_from NotAllowed, :with => proc { head :forbidden }
46 rescue_from 'RescueController::NotAllowedToRescueAsString', :with => proc { head :forbidden }
48 rescue_from InvalidRequest, :with => proc { |exception| render :text => exception.message }
49 rescue_from 'InvalidRequestToRescueAsString', :with => proc { |exception| render :text => exception.message }
51 rescue_from BadGateway do
54 rescue_from 'BadGatewayToRescueAsString' do
58 rescue_from ResourceUnavailable do |exception|
59 render :text => exception.message
61 rescue_from 'ResourceUnavailableToRescueAsString' do |exception|
62 render :text => exception.message
66 render :text => 'already rendered'
70 def method_not_allowed
71 raise ActionController::MethodNotAllowed.new(:get, :head, :put)
75 raise ActionController::NotImplemented.new(:get, :put)
81 def not_authorized_raise_as_string
82 raise NotAuthorizedToRescueAsString
88 def not_allowed_raise_as_string
89 raise NotAllowedToRescueAsString
95 def invalid_request_raise_as_string
96 raise InvalidRequestToRescueAsString
102 def record_invalid_raise_as_string
103 raise RecordInvalidToRescueAsString
109 def bad_gateway_raise_as_string
110 raise BadGatewayToRescueAsString
113 def resource_unavailable
114 raise ResourceUnavailable
116 def resource_unavailable_raise_as_string
117 raise ResourceUnavailableToRescueAsString
128 def show_errors(exception)
129 head :unprocessable_entity
133 class RescueTest < Test::Unit::TestCase
134 FIXTURE_PUBLIC = "#{File.dirname(__FILE__)}/../fixtures".freeze
137 @controller = RescueController.new
138 @request = ActionController::TestRequest.new
139 @response = ActionController::TestResponse.new
141 RescueController.consider_all_requests_local = true
142 @request.remote_addr = '1.2.3.4'
143 @request.host = 'example.com'
151 def test_rescue_action_locally_if_all_requests_local
152 @controller.expects(:local_request?).never
153 @controller.expects(:rescue_action_locally).with(@exception)
154 @controller.expects(:rescue_action_in_public).never
156 with_all_requests_local do
157 @controller.send :rescue_action, @exception
161 def test_rescue_action_locally_if_remote_addr_is_localhost
162 @controller.expects(:local_request?).returns(true)
163 @controller.expects(:rescue_action_locally).with(@exception)
164 @controller.expects(:rescue_action_in_public).never
166 with_all_requests_local false do
167 @controller.send :rescue_action, @exception
171 def test_rescue_action_in_public_otherwise
172 @controller.expects(:local_request?).returns(false)
173 @controller.expects(:rescue_action_locally).never
174 @controller.expects(:rescue_action_in_public).with(@exception)
176 with_all_requests_local false do
177 @controller.send :rescue_action, @exception
181 def test_rescue_action_in_public_with_error_file
182 with_rails_root FIXTURE_PUBLIC do
183 with_all_requests_local false do
188 assert_response :internal_server_error
189 body = File.read("#{FIXTURE_PUBLIC}/public/500.html")
190 assert_equal body, @response.body
193 def test_rescue_action_in_public_without_error_file
194 with_rails_root '/tmp' do
195 with_all_requests_local false do
200 assert_response :internal_server_error
201 assert_equal ' ', @response.body
204 def test_rescue_unknown_action_in_public_with_error_file
205 with_rails_root FIXTURE_PUBLIC do
206 with_all_requests_local false do
207 get :foobar_doesnt_exist
211 assert_response :not_found
212 body = File.read("#{FIXTURE_PUBLIC}/public/404.html")
213 assert_equal body, @response.body
216 def test_rescue_unknown_action_in_public_without_error_file
217 with_rails_root '/tmp' do
218 with_all_requests_local false do
219 get :foobar_doesnt_exist
223 assert_response :not_found
224 assert_equal ' ', @response.body
227 def test_rescue_missing_template_in_public
228 with_rails_root FIXTURE_PUBLIC do
229 with_all_requests_local true do
230 get :missing_template
234 assert_response :internal_server_error
235 assert @response.body.include?('missing_template'), "Response should include the template name."
238 def test_rescue_action_locally
240 assert_response :internal_server_error
241 assert_template 'diagnostics.erb'
242 assert @response.body.include?('RescueController#raises'), "Response should include controller and action."
243 assert @response.body.include?("don't panic"), "Response should include exception message."
246 def test_local_request_when_remote_addr_is_localhost
247 @controller.expects(:request).returns(@request).at_least_once
248 with_remote_addr '127.0.0.1' do
249 assert @controller.send(:local_request?)
253 def test_local_request_when_remote_addr_isnt_locahost
254 @controller.expects(:request).returns(@request)
255 with_remote_addr '1.2.3.4' do
256 assert !@controller.send(:local_request?)
260 def test_rescue_responses
261 responses = ActionController::Base.rescue_responses
263 assert_equal ActionController::Rescue::DEFAULT_RESCUE_RESPONSE, responses.default
264 assert_equal ActionController::Rescue::DEFAULT_RESCUE_RESPONSE, responses[Exception.new]
266 assert_equal :not_found, responses[ActionController::RoutingError.name]
267 assert_equal :not_found, responses[ActionController::UnknownAction.name]
268 assert_equal :not_found, responses['ActiveRecord::RecordNotFound']
269 assert_equal :conflict, responses['ActiveRecord::StaleObjectError']
270 assert_equal :unprocessable_entity, responses['ActiveRecord::RecordInvalid']
271 assert_equal :unprocessable_entity, responses['ActiveRecord::RecordNotSaved']
272 assert_equal :method_not_allowed, responses['ActionController::MethodNotAllowed']
273 assert_equal :not_implemented, responses['ActionController::NotImplemented']
276 def test_rescue_templates
277 templates = ActionController::Base.rescue_templates
279 assert_equal ActionController::Rescue::DEFAULT_RESCUE_TEMPLATE, templates.default
280 assert_equal ActionController::Rescue::DEFAULT_RESCUE_TEMPLATE, templates[Exception.new]
282 assert_equal 'missing_template', templates[ActionController::MissingTemplate.name]
283 assert_equal 'routing_error', templates[ActionController::RoutingError.name]
284 assert_equal 'unknown_action', templates[ActionController::UnknownAction.name]
285 assert_equal 'template_error', templates[ActionView::TemplateError.name]
288 def test_clean_backtrace
289 with_rails_root nil do
290 # No action if RAILS_ROOT isn't set.
291 cleaned = @controller.send(:clean_backtrace, @exception)
292 assert_equal @exception.backtrace, cleaned
295 with_rails_root Dir.pwd do
296 # RAILS_ROOT is removed from backtrace.
297 cleaned = @controller.send(:clean_backtrace, @exception)
298 expected = @exception.backtrace.map { |line| line.sub(RAILS_ROOT, '') }
299 assert_equal expected, cleaned
301 # No action if backtrace is nil.
302 assert_nil @controller.send(:clean_backtrace, Exception.new)
306 def test_not_implemented
307 with_all_requests_local false do
308 head :not_implemented
310 assert_response :not_implemented
311 assert_equal "GET, PUT", @response.headers['Allow']
314 def test_method_not_allowed
315 with_all_requests_local false do
316 get :method_not_allowed
318 assert_response :method_not_allowed
319 assert_equal "GET, HEAD, PUT", @response.headers['Allow']
322 def test_rescue_handler
324 assert_response :forbidden
326 def test_rescue_handler_string
327 get :not_authorized_raise_as_string
328 assert_response :forbidden
331 def test_rescue_handler_with_argument
332 @controller.expects(:show_errors).once.with { |e| e.is_a?(Exception) }
335 def test_rescue_handler_with_argument_as_string
336 @controller.expects(:show_errors).once.with { |e| e.is_a?(Exception) }
337 get :record_invalid_raise_as_string
340 def test_proc_rescue_handler
342 assert_response :forbidden
344 def test_proc_rescue_handler_as_string
345 get :not_allowed_raise_as_string
346 assert_response :forbidden
349 def test_proc_rescue_handle_with_argument
351 assert_equal "RescueController::InvalidRequest", @response.body
353 def test_proc_rescue_handle_with_argument_as_string
354 get :invalid_request_raise_as_string
355 assert_equal "RescueController::InvalidRequestToRescueAsString", @response.body
358 def test_block_rescue_handler
362 def test_block_rescue_handler_as_string
363 get :bad_gateway_raise_as_string
367 def test_block_rescue_handler_with_argument
368 get :resource_unavailable
369 assert_equal "RescueController::ResourceUnavailable", @response.body
372 def test_block_rescue_handler_with_argument_as_string
373 get :resource_unavailable_raise_as_string
374 assert_equal "RescueController::ResourceUnavailableToRescueAsString", @response.body
379 def with_all_requests_local(local = true)
380 old_local, ActionController::Base.consider_all_requests_local =
381 ActionController::Base.consider_all_requests_local, local
384 ActionController::Base.consider_all_requests_local = old_local
387 def with_remote_addr(addr)
388 old_remote_addr, @request.remote_addr = @request.remote_addr, addr
391 @request.remote_addr = old_remote_addr
394 def with_rails_root(path = nil)
395 old_rails_root = RAILS_ROOT if defined?(RAILS_ROOT)
397 silence_warnings { Object.const_set(:RAILS_ROOT, path) }
399 Object.remove_const(:RAILS_ROOT) rescue nil
406 silence_warnings { Object.const_set(:RAILS_ROOT, old_rails_root) }
408 Object.remove_const(:RAILS_ROOT) rescue nil
413 class ExceptionInheritanceRescueController < ActionController::Base
415 class ParentException < StandardError
418 class ChildException < ParentException
421 class GrandchildException < ChildException
424 rescue_from ChildException, :with => lambda { head :ok }
425 rescue_from ParentException, :with => lambda { head :created }
426 rescue_from GrandchildException, :with => lambda { head :no_content }
428 def raise_parent_exception
429 raise ParentException
432 def raise_child_exception
436 def raise_grandchild_exception
437 raise GrandchildException
441 class ExceptionInheritanceRescueTest < Test::Unit::TestCase
444 @controller = ExceptionInheritanceRescueController.new
445 @request = ActionController::TestRequest.new
446 @response = ActionController::TestResponse.new
449 def test_bottom_first
450 get :raise_grandchild_exception
451 assert_response :no_content
454 def test_inheritance_works
455 get :raise_child_exception
456 assert_response :created
460 class ControllerInheritanceRescueController < ExceptionInheritanceRescueController
461 class FirstExceptionInChildController < StandardError
464 class SecondExceptionInChildController < StandardError
467 rescue_from FirstExceptionInChildController, 'SecondExceptionInChildController', :with => lambda { head :gone }
469 def raise_first_exception_in_child_controller
470 raise FirstExceptionInChildController
473 def raise_second_exception_in_child_controller
474 raise SecondExceptionInChildController
478 class ControllerInheritanceRescueControllerTest < Test::Unit::TestCase
481 @controller = ControllerInheritanceRescueController.new
482 @request = ActionController::TestRequest.new
483 @response = ActionController::TestResponse.new
486 def test_first_exception_in_child_controller
487 get :raise_first_exception_in_child_controller
488 assert_response :gone
491 def test_second_exception_in_child_controller
492 get :raise_second_exception_in_child_controller
493 assert_response :gone
496 def test_exception_in_parent_controller
497 get :raise_parent_exception
498 assert_response :created