# frozen_string_literal: true
require "json"
require "rack/test"
RSpec.describe "Web / Rendering errors", :app_integration do
include Rack::Test::Methods
let(:app) { Hanami.app }
before do
with_directory(@dir = make_tmp_directory) do
write "config/app.rb", <<~RUBY
require "hanami"
module TestApp
class App < Hanami::App
config.logger.stream = File.new("/dev/null", "w")
config.render_errors = true
config.render_detailed_errors = false
end
end
RUBY
write "config/routes.rb", <<~RUBY
module TestApp
class Routes < Hanami::Routes
get "index", to: "index"
get "error", to: "error"
end
end
RUBY
write "app/actions/index.rb", <<~RUBY
module TestApp
module Actions
class Index < Hanami::Action
def handle(*, response)
response.body = "Hello"
end
end
end
end
RUBY
write "app/actions/error.rb", <<~RUBY
module TestApp
module Actions
class Error < Hanami::Action
def handle(*)
raise "oops"
end
end
end
end
RUBY
before_prepare if respond_to?(:before_prepare)
require "hanami/prepare"
end
end
describe "HTML request" do
context "error pages present" do
def before_prepare
write "public/404.html", <<~HTML
Not found
HTML
write "public/500.html", <<~HTML
Error
HTML
end
it "responds with the HTML for a 404 from a not found error" do
get "/__not_found__"
expect(last_response.status).to eq 404
expect(last_response.body.strip).to eq "Not found
"
expect(last_response.get_header("Content-Type")).to eq "text/html; charset=utf-8"
expect(last_response.get_header("Content-Length")).to eq "19"
end
it "responds with the HTML for a 404 from a method not allowed error" do
post "/index"
expect(last_response.status).to eq 404
expect(last_response.body.strip).to eq "Not found
"
expect(last_response.get_header("Content-Type")).to eq "text/html; charset=utf-8"
expect(last_response.get_header("Content-Length")).to eq "19"
end
it "responds with the HTML for a 500" do
get "/error"
expect(last_response.status).to eq 500
expect(last_response.body.strip).to eq "Error
"
expect(last_response.get_header("Content-Type")).to eq "text/html; charset=utf-8"
expect(last_response.get_header("Content-Length")).to eq "15"
end
end
context "error pages missing" do
it "responds with default text for a 404 from a not found error" do
get "/__not_found__"
expect(last_response.status).to eq 404
expect(last_response.body.strip).to eq "Not Found"
expect(last_response.get_header("Content-Type")).to eq "text/html; charset=utf-8"
expect(last_response.get_header("Content-Length")).to eq "9"
end
it "responds with default text for a 404 from a metohd not allowed error" do
post "/index"
expect(last_response.status).to eq 404
expect(last_response.body.strip).to eq "Not Found"
expect(last_response.get_header("Content-Type")).to eq "text/html; charset=utf-8"
expect(last_response.get_header("Content-Length")).to eq "9"
end
it "responds with default text for a 500" do
get "/error"
expect(last_response.status).to eq 500
expect(last_response.body.strip).to eq "Internal Server Error"
expect(last_response.get_header("Content-Type")).to eq "text/html; charset=utf-8"
expect(last_response.get_header("Content-Length")).to eq "21"
end
end
end
describe "JSON request" do
it "renders a JSON response for a 404 from a not found error" do
get "/__not_found__", {}, "HTTP_ACCEPT" => "application/json"
expect(last_response.status).to eq 404
expect(last_response.body.strip).to eq %({"status":404,"error":"Not Found"})
expect(last_response.get_header("Content-Type")).to eq "application/json; charset=utf-8"
expect(last_response.get_header("Content-Length")).to eq "34"
end
it "renders a JSON response for a 404 from a metnod not allowed error" do
post "/index", {}, "HTTP_ACCEPT" => "application/json"
expect(last_response.status).to eq 404
expect(last_response.body.strip).to eq %({"status":404,"error":"Not Found"})
expect(last_response.get_header("Content-Type")).to eq "application/json; charset=utf-8"
expect(last_response.get_header("Content-Length")).to eq "34"
end
it "renders a JSON response for a 500" do
get "/error", {}, "HTTP_ACCEPT" => "application/json"
expect(last_response.status).to eq 500
expect(last_response.body.strip).to eq %({"status":500,"error":"Internal Server Error"})
expect(last_response.get_header("Content-Type")).to eq "application/json; charset=utf-8"
expect(last_response.get_header("Content-Length")).to eq "46"
end
end
describe "configuring error responses" do
def before_prepare
write "config/app.rb", <<~RUBY
require "hanami"
module TestApp
CustomNotFoundError = Class.new(StandardError)
class App < Hanami::App
config.logger.stream = File.new("/dev/null", "w")
config.render_errors = true
config.render_error_responses["TestApp::CustomNotFoundError"] = :not_found
config.render_detailed_errors = false
end
end
RUBY
write "app/actions/error.rb", <<~RUBY
module TestApp
module Actions
class Error < Hanami::Action
def handle(*)
raise CustomNotFoundError
end
end
end
end
RUBY
write "public/404.html", <<~HTML
Not found
HTML
end
it "uses the configured errors to determine the response" do
get "/error"
expect(last_response.status).to eq 404
expect(last_response.body.strip).to eq "Not found
"
end
end
describe "render_errors config disabled" do
def before_prepare
write "config/app.rb", <<~RUBY
require "hanami"
module TestApp
class App < Hanami::App
config.logger.stream = File.new("/dev/null", "w")
config.render_errors = false
config.render_detailed_errors = false
end
end
RUBY
# Include error pages here to prove they are _not_ used
write "public/404.html", <<~HTML
Not found
HTML
write "public/500.html", <<~HTML
Error
HTML
end
it "raises a Hanami::Router::NotFoundError for a 404" do
expect { get "/__not_found__" }.to raise_error(Hanami::Router::NotFoundError)
end
it "raises a Hanami::Router::NotAllowedError for a 405" do
expect { post "/index" }.to raise_error(Hanami::Router::NotAllowedError)
end
it "raises the original error for a 500" do
expect { get "/error" }.to raise_error(RuntimeError, "oops")
end
end
end