Skip to content

Commit

Permalink
Always render debug page if needed
Browse files Browse the repository at this point in the history
  • Loading branch information
paulcsmith committed Sep 16, 2019
1 parent ad3d99d commit 3c6495e
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 43 deletions.
17 changes: 17 additions & 0 deletions spec/lucky/error_action_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
require "../spec_helper"

include ContextHelper

private class Special
end

private class FakeErrorAction < Lucky::ErrorAction
default_format :html

def render(error : Exception) : Lucky::Response
plain_text "This is not a debug page", status: 500
end
end

describe Lucky::ErrorAction do
end
108 changes: 76 additions & 32 deletions spec/lucky/error_handling_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,60 +2,104 @@ require "../spec_helper"

include ContextHelper

private class FakeError < Exception
private class CustomError < Exception
end

private class UnhandledError < Exception
end

private class InvalidParam < Lucky::Exceptions::InvalidParam
end

private class FakeErrorAction < Lucky::ErrorAction
def render(error : FakeError) : Lucky::Response
default_format :html

def render(error : CustomError) : Lucky::Response
head status: 404
end

def render(error : Exception) : Lucky::Response
plain_text "Oops", status: 500
plain_text "This is not a debug page", status: 500
end
end

describe Lucky::ErrorHandler do
it "does nothing if no errors are raised" do
error_handler = Lucky::ErrorHandler.new(action: FakeErrorAction)
error_handler.next = ->(_ctx : HTTP::Server::Context) {}
describe "Error handling" do
describe "ErrorAction" do
describe "show_debug_output setting is true" do
it "renders debug output if request accepts HTML" do
handle_error(format: :html, show_debug_output: true) do |context, output|
context.response.headers["Content-Type"].should eq("text/html")
output.should contain("code-explorer")
context.response.status_code.should eq(500)
end
end

it "does not render debug output if request is not HTML" do
handle_error(format: :json, show_debug_output: true) do |context, output|
context.response.headers["Content-Type"].should eq("text/plain")
output.should_not contain("code-explorer")
output.should contain("This is not a debug page")
context.response.status_code.should eq(500)
end
end

error_handler.call(build_context)
it "renders debug page with the error's status" do
handle_error(format: :html, show_debug_output: true, error: CustomError.new) do |context, output|
context.response.headers["Content-Type"].should eq("text/html")
output.should contain("code-explorer")
context.response.status_code.should eq(404)
end
end
end

describe "show_debug_output setting is false" do
it "does not render debug output" do
handle_error(format: :json, show_debug_output: false) do |context, output|
context.response.headers["Content-Type"].should eq("text/plain")
output.should contain("This is not a debug page")
context.response.status_code.should eq(500)
end
end
end
end

it "handles the error with an overloaded 'render' method if defined" do
error_handler = Lucky::ErrorHandler.new(action: FakeErrorAction)
error_handler.next = ->(_ctx : HTTP::Server::Context) { raise FakeError.new }
describe "ErrorHandler" do
it "does nothing if no errors are raised" do
error_handler = Lucky::ErrorHandler.new(action: FakeErrorAction)
error_handler.next = ->(_ctx : HTTP::Server::Context) {}

context = error_handler.call(build_context).as(HTTP::Server::Context)
error_handler.call(build_context)
end

it "handles the error with an overloaded 'render' method if defined" do
handle_error(error: CustomError.new) do |context, _output|
context.response.headers["Content-Type"].should eq("")
context.response.status_code.should eq(404)
end
end

context.response.headers["Content-Type"].should eq("")
context.response.status_code.should eq(404)
it "falls back to generic error handling if there are no custom error handlers" do
handle_error(error: UnhandledError.new) do |context, output|
output.should contain("This is not a debug page")
context.response.headers["Content-Type"].should eq("text/plain")
context.response.status_code.should eq(500)
end
end
end
end

it "falls back to generic error handling if there are no custom error handlers" do
private def handle_error(
format : Symbol = :html,
show_debug_output : Bool = false,
error : Exception = UnhandledError.new
)
Lucky::ErrorHandler.temp_config(show_debug_output: show_debug_output) do
error_handler = Lucky::ErrorHandler.new(action: FakeErrorAction)
error_handler.next = ->(_ctx : HTTP::Server::Context) { raise UnhandledError.new }

context = error_handler.call(build_context).as(HTTP::Server::Context)
error_handler.next = ->(_ctx : HTTP::Server::Context) { raise error }
io = IO::Memory.new
context = build_context_with_io(io)
context._clients_desired_format = format

context.response.headers["Content-Type"].should eq("text/plain")
context.response.status_code.should eq(500)
end
context = error_handler.call(context).as(HTTP::Server::Context)

describe ".render_exception_page" do
it "returns a exception page as a response with a 500 status" do
context = build_context
error = Exception.new
response = Lucky::ErrorHandler.render_exception_page(context, error)
response.should be_a(Lucky::Response)
response.status.should eq(500)
end
context.response.close
yield context, io.to_s
end
end
22 changes: 21 additions & 1 deletion src/lucky/error_action.cr
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ abstract class Lucky::ErrorAction
include Lucky::ActionDelegates
include Lucky::Renderable
include Lucky::Redirectable
include Lucky::RequestTypeHelpers
include Lucky::Exposable

macro inherited
include Lucky::RequestTypeHelpers
end

getter context

def initialize(@context : HTTP::Server::Context)
Expand All @@ -19,8 +22,15 @@ abstract class Lucky::ErrorAction
abstract def render(error : Exception) : Lucky::Response

def perform_action(error : Exception)
# Always get the rendered error because it also includes the HTTP status.
# We need the HTTP status to use in the debug page.
response = render(error)
ensure_response_is_returned(response)

if html? && Lucky::ErrorHandler.settings.show_debug_output
response = render_exception_page(error, response.status)
end

response.print
end

Expand All @@ -44,4 +54,14 @@ abstract class Lucky::ErrorAction
ERROR
%}
end

def render_exception_page(error : Exception, status : Int) : Lucky::Response
context.response.reset
Lucky::TextResponse.new(
context: context,
status: status,
content_type: "text/html",
body: Lucky::ExceptionPage.for_runtime_exception(context, error).to_s
)
end
end
10 changes: 0 additions & 10 deletions src/lucky/error_handler.cr
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,4 @@ class Lucky::ErrorHandler
action.new(context).perform_action(error)
context
end

def self.render_exception_page(context, error)
context.response.reset
Lucky::TextResponse.new(
context: context,
status: 500,
content_type: "text/html",
body: Lucky::ExceptionPage.for_runtime_exception(context, error).to_s
)
end
end
1 change: 1 addition & 0 deletions src/lucky/response.cr
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
abstract class Lucky::Response
abstract def print
abstract def status : Int
end

0 comments on commit 3c6495e

Please sign in to comment.