sinatra/test/mapped_error_test.rb

286 lines
7.1 KiB
Ruby
Raw Normal View History

require_relative 'test_helper'
2008-02-24 01:00:04 +00:00
class FooError < RuntimeError
end
class FooNotFound < Sinatra::NotFound
end
class FooSpecialError < Sinatra::Error
def http_status; 501 end
2011-06-10 11:21:37 +00:00
end
class FooStatusOutOfRangeError < Sinatra::Error
def code; 4000 end
end
class FooWithCode < Sinatra::Error
def code; 419 end
end
class FirstError < RuntimeError; end
class SecondError < RuntimeError; end
class MappedErrorTest < Minitest::Test
def test_default
assert true
end
describe 'Exception Mappings' do
it 'invokes handlers registered with ::error when raised' do
mock_app do
set :raise_errors, false
error(FooError) { 'Foo!' }
get('/') { raise FooError }
end
get '/'
assert_equal 500, status
assert_equal 'Foo!', body
end
it 'passes the exception object to the error handler' do
mock_app do
set :raise_errors, false
error(FooError) { |e| assert_equal(FooError, e.class) }
get('/') { raise FooError }
end
get('/')
end
it 'uses the Exception handler if no matching handler found' do
mock_app do
set :raise_errors, false
error(Exception) { 'Exception!' }
get('/') { raise FooError }
end
get '/'
assert_equal 500, status
assert_equal 'Exception!', body
end
it 'walks down inheritance chain for errors' do
mock_app do
set :raise_errors, false
error(RuntimeError) { 'Exception!' }
get('/') { raise FooError }
end
get '/'
assert_equal 500, status
assert_equal 'Exception!', body
end
it 'favors subclass handler over superclass handler if available' do
mock_app do
set :raise_errors, false
error(Exception) { 'Exception!' }
error(FooError) { 'FooError!' }
error(RuntimeError) { 'Exception!' }
get('/') { raise FooError }
end
get '/'
assert_equal 500, status
assert_equal 'FooError!', body
end
it "sets env['sinatra.error'] to the rescued exception" do
mock_app do
set :raise_errors, false
error(FooError) do
assert env.include?('sinatra.error')
assert env['sinatra.error'].kind_of?(FooError)
'looks good'
end
get('/') { raise FooError }
end
get '/'
assert_equal 'looks good', body
end
it "raises errors from the app when raise_errors set and no handler defined" do
mock_app do
set :raise_errors, true
get('/') { raise FooError }
end
assert_raises(FooError) { get '/' }
end
it "calls error handlers before raising errors even when raise_errors is set" do
mock_app do
set :raise_errors, true
error(FooError) { "she's there." }
get('/') { raise FooError }
end
get '/'
assert_equal 500, status
end
it "never raises Sinatra::NotFound beyond the application" do
mock_app(Sinatra::Application) do
get('/') { raise Sinatra::NotFound }
end
get '/'
assert_equal 404, status
end
it "cascades for subclasses of Sinatra::NotFound" do
mock_app do
set :raise_errors, true
error(FooNotFound) { "foo! not found." }
get('/') { raise FooNotFound }
end
get '/'
assert_equal 404, status
assert_equal 'foo! not found.', body
end
it 'has a not_found method for backwards compatibility' do
mock_app { not_found { "Lost, are we?" } }
get '/test'
assert_equal 404, status
assert_equal "Lost, are we?", body
end
2009-03-26 16:02:40 +00:00
it 'inherits error mappings from base class' do
base = Class.new(Sinatra::Base)
base.error(FooError) { 'base class' }
mock_app(base) do
2009-03-26 16:02:40 +00:00
set :raise_errors, false
get('/') { raise FooError }
end
2009-03-26 16:02:40 +00:00
get '/'
assert_equal 'base class', body
end
it 'overrides error mappings in base class' do
base = Class.new(Sinatra::Base)
base.error(FooError) { 'base class' }
mock_app(base) do
2009-03-26 16:02:40 +00:00
set :raise_errors, false
error(FooError) { 'subclass' }
get('/') { raise FooError }
end
2009-03-26 16:02:40 +00:00
get '/'
assert_equal 'subclass', body
end
2011-06-10 11:21:37 +00:00
it 'honors Exception#http_status if present' do
2011-06-10 11:21:37 +00:00
mock_app do
set :raise_errors, false
error(501) { 'Foo!' }
get('/') { raise FooSpecialError }
end
get '/'
assert_equal 501, status
assert_equal 'Foo!', body
end
it 'does not use Exception#code by default' do
mock_app do
set :raise_errors, false
get('/') { raise FooWithCode }
end
get '/'
assert_equal 500, status
end
it 'uses Exception#code if use_code is enabled' do
mock_app do
set :raise_errors, false
set :use_code, true
get('/') { raise FooWithCode }
end
get '/'
assert_equal 419, status
end
it 'does not rely on Exception#code for invalid codes' do
mock_app do
set :raise_errors, false
set :use_code, true
get('/') { raise FooStatusOutOfRangeError }
end
get '/'
assert_equal 500, status
end
2012-07-18 19:07:24 +00:00
it "allows a stack of exception_handlers" do
mock_app do
set :raise_errors, false
error(FirstError) { 'First!' }
error(SecondError) { 'Second!' }
get('/'){ raise SecondError }
end
get '/'
assert_equal 500, status
assert_equal 'Second!', body
end
2012-07-18 19:07:24 +00:00
it "allows an exception handler to pass control to the next exception handler" do
mock_app do
set :raise_errors, false
error(500, FirstError) { 'First!' }
error(500, SecondError) { pass }
get('/') { raise 500 }
end
get '/'
assert_equal 500, status
assert_equal 'First!', body
end
it "allows an exception handler to handle the exception" do
mock_app do
set :raise_errors, false
error(500, FirstError) { 'First!' }
error(500, SecondError) { 'Second!' }
get('/') { raise 500 }
end
get '/'
assert_equal 500, status
assert_equal 'Second!', body
end
end
describe 'Custom Error Pages' do
it 'allows numeric status code mappings to be registered with ::error' do
mock_app do
set :raise_errors, false
error(500) { 'Foo!' }
get('/') { [500, {}, 'Internal Foo Error'] }
end
get '/'
assert_equal 500, status
assert_equal 'Foo!', body
end
it 'allows ranges of status code mappings to be registered with :error' do
mock_app do
set :raise_errors, false
error(500..550) { "Error: #{response.status}" }
get('/') { [507, {}, 'A very special error'] }
end
get '/'
assert_equal 507, status
assert_equal 'Error: 507', body
end
it 'allows passing more than one range' do
mock_app do
set :raise_errors, false
error(409..411, 503..509) { "Error: #{response.status}" }
get('/') { [507, {}, 'A very special error'] }
end
get '/'
assert_equal 507, status
assert_equal 'Error: 507', body
end
I knew I shoulda taken that left turn at Hoboken This is a fairly large reworking of Sinatra's innards. Although most of the internal implementation has been modified, it provides the same basic feature set and is meant to be compatible with Sinatra 0.3.2. * The Event and EventContext classes have been removed. Sinatra applications are now defined within the class context of a Sinatra::Base subclass; each request is processed within a new instance. * Sinatra::Base can be used as a base class for multiple Rack applications within a single process and can be used as Rack middleware. * The routing and result type processing implementation has been simplified and enhanced a bit. There's a new route conditions system for things like :agent/:host matching and a request level #pass method has been added to allow an event handler to exit immediately, passing control to the next matching route. * Regular expressions may now be used in route patterns. Captures are available as an array from "params[:captures]". * The #body helper method now takes a block. The block is not evaluated until an attempt is made to read the body. * Options are now dynamically generated class attributes on the Sinatra::Base subclass (instead of OpenStruct); options are inherited by subclasses and may be overridden up the inheritance hierarchy. The Base.set manages all option related stuff. * The application file (app_file) detection heuristics are bit more sane now. This fixes some bugs with reloading and public/views directory detection. All thin / passenger issues of these type should be better now. * Error mappings are now split into to distinct layers: exception mappings and custom error pages. Exception mappings are registered with 'error(Exception)' and are run only when the app raises an exception. Custom error pages are registered with error(status_code) and are run any time the response has the status code specified. It's also possible to register an error page for a range of status codes: 'error(500..599)'. * The spec and unit testing extensions have been modified to take advantage of the ability to have multiple Sinatra applications. The Sinatra::Test module must be included within the TestCase in order to take advantage of these methods (unless the 'sinatra/compat' library has been required). * Rebuilt specs from scratch for better coverage and organization. Sinatra 3.2 unit tests have been retained under ./compat to ensure a baseline level of compatibility with previous versions; use the 'rake compat' task to run these. A large number of existing Sinatra idioms have been deprecated but continue to be supported through the 'sinatra/compat' library. * The "set_option" and "set_options" methods have been deprecated due to redundancy; use "set". * The "env" option (Sinatra::Base.env) has been renamed to "environment" and deprecated because it's too easy to confuse with the request-level Rack environment Hash (Sinatra::Base#env). * The request level "stop" method has been renamed "halt" and deprecated. This is for consistency with `throw :halt`. * The request level "entity_tag" method has been renamed "etag" and deprecated. Both versions were previously supported. * The request level "headers" method has been deprecated. Use response['Header-Name'] to access and modify response headers. * Sinatra.application is deprecated. Use Sinatra::Application instead. * Setting Sinatra.application = nil to reset an application is deprecated. You shouldn't have to reset objects anymore. * The Sinatra.default_options Hash is deprecated. Modifying this object now results in "set(key, value)" invocations on the Sinatra::Base subclass. * The "body.to_result" convention has been deprecated. * The ServerError exception has been deprecated. Any Exception is now considered a ServerError.
2008-12-13 21:06:02 +00:00
end
2008-02-24 01:00:04 +00:00
end