require File.dirname(__FILE__) + '/helper'

class FooError < RuntimeError
end

class FooNotFound < Sinatra::NotFound
end

class MappedErrorTest < Test::Unit::TestCase
  def test_default
    assert true
  end

  describe 'Exception Mappings' do
    it 'invokes handlers registered with ::error when raised' do
      mock_app {
        set :raise_errors, false
        error(FooError) { 'Foo!' }
        get '/' do
          raise FooError
        end
      }
      get '/'
      assert_equal 500, status
      assert_equal 'Foo!', body
    end

    it 'uses the Exception handler if no matching handler found' do
      mock_app {
        set :raise_errors, false
        error(Exception) { 'Exception!' }
        get '/' do
          raise FooError
        end
      }

      get '/'
      assert_equal 500, status
      assert_equal 'Exception!', body
    end

    it "sets env['sinatra.error'] to the rescued exception" do
      mock_app {
        set :raise_errors, false
        error(FooError) {
          assert env.include?('sinatra.error')
          assert env['sinatra.error'].kind_of?(FooError)
          'looks good'
        }
        get '/' do
          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 {
        set :raise_errors, true
        get '/' do
          raise FooError
        end
      }
      assert_raise(FooError) { get '/' }
    end

    it "calls error handlers before raising errors even when raise_errors is set" do
      mock_app {
        set :raise_errors, true
        error(FooError) { "she's there." }
        get '/' do
          raise FooError
        end
      }
      assert_nothing_raised { get '/' }
      assert_equal 500, status
    end

    it "never raises Sinatra::NotFound beyond the application" do
      mock_app {
        set :raise_errors, true
        get '/' do
          raise Sinatra::NotFound
        end
      }
      assert_nothing_raised { get '/' }
      assert_equal 404, status
    end

    it "cascades for subclasses of Sinatra::NotFound" do
      mock_app {
        set :raise_errors, true
        error(FooNotFound) { "foo! not found." }
        get '/' do
          raise FooNotFound
        end
      }
      assert_nothing_raised { 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 do
          "Lost, are we?"
        end
      }

      get '/test'
      assert_equal 404, status
      assert_equal "Lost, are we?", body
    end

    it 'inherits error mappings from base class' do
      base = Class.new(Sinatra::Base)
      base.error(FooError) { 'base class' }

      mock_app(base) {
        set :raise_errors, false
        get '/' do
          raise FooError
        end
      }

      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) {
        set :raise_errors, false
        error(FooError) { 'subclass' }
        get '/' do
          raise FooError
        end
      }

      get '/'
      assert_equal 'subclass', body
    end
  end

  describe 'Custom Error Pages' do
    it 'allows numeric status code mappings to be registered with ::error' do
      mock_app {
        set :raise_errors, false
        error(500) { 'Foo!' }
        get '/' do
          [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 {
        set :raise_errors, false
        error(500..550) { "Error: #{response.status}" }
        get '/' do
          [507, {}, 'A very special error']
        end
      }
      get '/'
      assert_equal 507, status
      assert_equal 'Error: 507', body
    end

    class FooError < RuntimeError
    end

    it 'runs after exception mappings and overwrites body' do
      mock_app {
        set :raise_errors, false
        error FooError do
          response.status = 502
          'from exception mapping'
        end
        error(500) { 'from 500 handler' }
        error(502) { 'from custom error page' }

        get '/' do
          raise FooError
        end
      }
      get '/'
      assert_equal 502, status
      assert_equal 'from custom error page', body
    end
  end
end
