diff --git a/Gemfile.lock b/Gemfile.lock index 5010fd59..de61634c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -30,35 +30,35 @@ GEM specs: aasm (5.5.0) concurrent-ruby (~> 1.0) - actioncable (7.1.3) - actionpack (= 7.1.3) - activesupport (= 7.1.3) + actioncable (7.1.3.2) + actionpack (= 7.1.3.2) + activesupport (= 7.1.3.2) nio4r (~> 2.0) websocket-driver (>= 0.6.1) zeitwerk (~> 2.6) - actionmailbox (7.1.3) - actionpack (= 7.1.3) - activejob (= 7.1.3) - activerecord (= 7.1.3) - activestorage (= 7.1.3) - activesupport (= 7.1.3) + actionmailbox (7.1.3.2) + actionpack (= 7.1.3.2) + activejob (= 7.1.3.2) + activerecord (= 7.1.3.2) + activestorage (= 7.1.3.2) + activesupport (= 7.1.3.2) mail (>= 2.7.1) net-imap net-pop net-smtp - actionmailer (7.1.3) - actionpack (= 7.1.3) - actionview (= 7.1.3) - activejob (= 7.1.3) - activesupport (= 7.1.3) + actionmailer (7.1.3.2) + actionpack (= 7.1.3.2) + actionview (= 7.1.3.2) + activejob (= 7.1.3.2) + activesupport (= 7.1.3.2) mail (~> 2.5, >= 2.5.4) net-imap net-pop net-smtp rails-dom-testing (~> 2.2) - actionpack (7.1.3) - actionview (= 7.1.3) - activesupport (= 7.1.3) + actionpack (7.1.3.2) + actionview (= 7.1.3.2) + activesupport (= 7.1.3.2) nokogiri (>= 1.8.5) racc rack (>= 2.2.4) @@ -66,27 +66,27 @@ GEM rack-test (>= 0.6.3) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - actiontext (7.1.3) - actionpack (= 7.1.3) - activerecord (= 7.1.3) - activestorage (= 7.1.3) - activesupport (= 7.1.3) + actiontext (7.1.3.2) + actionpack (= 7.1.3.2) + activerecord (= 7.1.3.2) + activestorage (= 7.1.3.2) + activesupport (= 7.1.3.2) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.1.3) - activesupport (= 7.1.3) + actionview (7.1.3.2) + activesupport (= 7.1.3.2) builder (~> 3.1) erubi (~> 1.11) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - activejob (7.1.3) - activesupport (= 7.1.3) + activejob (7.1.3.2) + activesupport (= 7.1.3.2) globalid (>= 0.3.6) - activemodel (7.1.3) - activesupport (= 7.1.3) - activerecord (7.1.3) - activemodel (= 7.1.3) - activesupport (= 7.1.3) + activemodel (7.1.3.2) + activesupport (= 7.1.3.2) + activerecord (7.1.3.2) + activemodel (= 7.1.3.2) + activesupport (= 7.1.3.2) timeout (>= 0.4.0) activerecord-session_store (2.1.0) actionpack (>= 6.1) @@ -95,13 +95,13 @@ GEM multi_json (~> 1.11, >= 1.11.2) rack (>= 2.0.8, < 4) railties (>= 6.1) - activestorage (7.1.3) - actionpack (= 7.1.3) - activejob (= 7.1.3) - activerecord (= 7.1.3) - activesupport (= 7.1.3) + activestorage (7.1.3.2) + actionpack (= 7.1.3.2) + activejob (= 7.1.3.2) + activerecord (= 7.1.3.2) + activesupport (= 7.1.3.2) marcel (~> 1.0) - activesupport (7.1.3) + activesupport (7.1.3.2) base64 bigdecimal concurrent-ruby (~> 1.0, >= 1.0.2) @@ -204,8 +204,7 @@ GEM dotenv-rails (2.8.1) dotenv (= 2.8.1) railties (>= 3.2) - drb (2.2.0) - ruby2_keywords + drb (2.2.1) erubi (1.12.0) execjs (2.8.1) factory_bot (6.4.5) @@ -249,11 +248,12 @@ GEM http-accept (1.7.0) http-cookie (1.0.5) domain_name (~> 0.5) - i18n (1.14.1) + i18n (1.14.3) concurrent-ruby (~> 1.0) + racc (~> 1.7) io-console (0.7.2) io-event (1.2.2) - irb (1.11.1) + irb (1.11.2) rdoc reline (>= 0.4.2) jquery-rails (4.6.0) @@ -271,7 +271,7 @@ GEM net-imap net-pop net-smtp - marcel (1.0.2) + marcel (1.0.4) matrix (0.4.2) mime-types (3.5.2) mime-types-data (~> 3.2015) @@ -324,7 +324,7 @@ GEM stringio public_suffix (5.0.4) racc (1.7.3) - rack (2.2.8) + rack (2.2.8.1) rack-session (1.0.2) rack (< 3) rack-test (2.1.0) @@ -332,20 +332,20 @@ GEM rackup (1.0.0) rack (< 3) webrick - rails (7.1.3) - actioncable (= 7.1.3) - actionmailbox (= 7.1.3) - actionmailer (= 7.1.3) - actionpack (= 7.1.3) - actiontext (= 7.1.3) - actionview (= 7.1.3) - activejob (= 7.1.3) - activemodel (= 7.1.3) - activerecord (= 7.1.3) - activestorage (= 7.1.3) - activesupport (= 7.1.3) + rails (7.1.3.2) + actioncable (= 7.1.3.2) + actionmailbox (= 7.1.3.2) + actionmailer (= 7.1.3.2) + actionpack (= 7.1.3.2) + actiontext (= 7.1.3.2) + actionview (= 7.1.3.2) + activejob (= 7.1.3.2) + activemodel (= 7.1.3.2) + activerecord (= 7.1.3.2) + activestorage (= 7.1.3.2) + activesupport (= 7.1.3.2) bundler (>= 1.15.0) - railties (= 7.1.3) + railties (= 7.1.3.2) rails-controller-testing (1.0.5) actionpack (>= 5.0.1.rc1) actionview (>= 5.0.1.rc1) @@ -357,9 +357,9 @@ GEM rails-html-sanitizer (1.6.0) loofah (~> 2.21) nokogiri (~> 1.14) - railties (7.1.3) - actionpack (= 7.1.3) - activesupport (= 7.1.3) + railties (7.1.3.2) + actionpack (= 7.1.3.2) + activesupport (= 7.1.3.2) irb rackup (>= 1.0.0) rake (>= 12.2) @@ -374,7 +374,7 @@ GEM rdoc (6.6.2) psych (>= 4.0.0) regexp_parser (2.9.0) - reline (0.4.2) + reline (0.4.3) io-console (~> 0.5) rest-client (2.1.0) http-accept (>= 1.7.0, < 2.0) @@ -469,7 +469,7 @@ GEM stringio (3.1.0) sucker_punch (3.2.0) concurrent-ruby (~> 1.0) - thor (1.3.0) + thor (1.3.1) tilt (2.2.0) timeout (0.4.1) timers (4.3.5) @@ -492,7 +492,7 @@ GEM websocket-extensions (0.1.5) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.6.12) + zeitwerk (2.6.13) PLATFORMS ruby diff --git a/app/controllers/flood_risk_engine/application_controller.rb b/app/controllers/flood_risk_engine/application_controller.rb index cf25f15b..7dc0ef48 100644 --- a/app/controllers/flood_risk_engine/application_controller.rb +++ b/app/controllers/flood_risk_engine/application_controller.rb @@ -6,6 +6,12 @@ class ApplicationController < ActionController::Base layout ->(_) { FloodRiskEngine.config.layout } + rescue_from StandardError do |e| + Airbrake.notify e + Rails.logger.error "Unhandled exception: #{e}\n#{e.backtrace}" + redirect_to error_path("500") + end + protected # http://jacopretorius.net/2014/01/force-page-to-reload-on-browser-back-in-rails.html diff --git a/app/controllers/flood_risk_engine/errors_controller.rb b/app/controllers/flood_risk_engine/errors_controller.rb index 664d8efe..e2a3ea33 100644 --- a/app/controllers/flood_risk_engine/errors_controller.rb +++ b/app/controllers/flood_risk_engine/errors_controller.rb @@ -5,15 +5,27 @@ module FloodRiskEngine class ErrorsController < ApplicationController + before_action :set_html_response_format + def show render( template: file_for(template), - locals: { message: exception.try(:message), enrollment: } + locals: { message: exception.try(:message), enrollment: }, + status: response_status ) end protected + # Changes the request format to HTML to always display the error pages + def set_html_response_format + request.format = :html + end + + def response_status + template.to_i.positive? ? template : :internal_server_error + end + def error_code @error_code ||= params[:id] end @@ -30,7 +42,11 @@ def template_path(name) end def template - @template ||= template_exists(error_code) ? error_code : "generic" + @template ||= if template_exists(error_code) + error_code + else + (error_code.to_i.positive? ? "generic" : "404") + end end def file_for(name) diff --git a/config/locales/flood_risk_engine/errors.en.yml b/config/locales/flood_risk_engine/errors.en.yml index 834190ab..1e02129c 100644 --- a/config/locales/flood_risk_engine/errors.en.yml +++ b/config/locales/flood_risk_engine/errors.en.yml @@ -17,12 +17,12 @@ en: heading: Page not found if_you_typed: If you typed the web address, check it is correct. if_you_pasted: If you pasted the web address, check you copied the entire address. - call_our_helpline: If the web address is correct or you selected a link or button, call our helpline on 03708 506 506, + call_our_helpline: If the web address is correct or you selected a link or button, call our helpline on 03708 506 506. error_404: heading: Page not found if_you_typed: If you typed the web address, check it is correct. if_you_pasted: If you pasted the web address, check you copied the entire address. - call_our_helpline: If the web address is correct or you selected a link or button, call our helpline on 03708 506 506, + call_our_helpline: If the web address is correct or you selected a link or button, call our helpline on 03708 506 506. error_422: heading: There was a problem sorry: Sorry, the system wasn't able to complete that step. diff --git a/config/routes.rb b/config/routes.rb index 1856a37a..3f80c90e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -242,7 +242,7 @@ mount DefraRubyEmail::Engine => "/email" - # See http://patrickperey.com/railscast-053-handling-exceptions/ + # See http://railscasts.com/episodes/53-handling-exceptions-revised get "(errors)/:id", to: "errors#show", as: "error" end # rubocop:enable Metrics/BlockLength diff --git a/spec/controllers/flood_risk_engine/errors_controller_spec.rb b/spec/controllers/flood_risk_engine/errors_controller_spec.rb deleted file mode 100644 index a6fdf420..00000000 --- a/spec/controllers/flood_risk_engine/errors_controller_spec.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -require "rails_helper" - -module FloodRiskEngine - RSpec.describe ErrorsController do - routes { Engine.routes } - render_views - describe "#show" do - it "renders error templates when known" do - get :show, params: { id: "401" } - expect(response).to have_http_status(:success) - expect(response).to render_template(:error_401) - end - - it "renders generic when not known" do - get :show, params: { id: "unknown" } - expect(response).to have_http_status(:success) - expect(response).to render_template(:error_generic) - end - end - end -end diff --git a/spec/requests/flood_risk_engine/errors_spec.rb b/spec/requests/flood_risk_engine/errors_spec.rb new file mode 100644 index 00000000..2f62722f --- /dev/null +++ b/spec/requests/flood_risk_engine/errors_spec.rb @@ -0,0 +1,84 @@ +# frozen_string_literal: true + +require "rails_helper" + +module FloodRiskEngine + RSpec.describe "Errors" do + + before do + env_config = Rails.application.env_config + allow(env_config).to receive(:[]).with("action_dispatch.show_exceptions").and_return(true) + allow(env_config).to receive(:[]).with("action_dispatch.show_detailed_exceptions").and_return(false) + end + + shared_examples "not found" do |path| + before { get path } + + it "responds with HTTP 404" do + expect(response).to have_http_status(:not_found) + end + + it "renders a HTML error page" do + expect(response.headers["Content-Type"]).to include("text/html") + end + + it "includes expected error text" do + expect(response.body).to match(/If you typed the web address/) + end + end + + describe "explicit error paths" do + context "when a matching error template exists" do + %w[401 403 404 422].each do |code| + it "responds with a status of #{code} and renders the error_#{code} template" do + get error_path(code) + + expect(response.code).to eq(code) + expect(response).to render_template("error_#{code}") + end + end + end + + context "when no matching error template exists" do + it "renders the generic error template" do + get error_path("601") + + expect(response).to have_http_status(:internal_server_error) + expect(response).to render_template(:error_generic) + end + end + end + + describe "internal redirects to error pages" do + context "when the requested route does not exist" do + context "when the requested route format is not specified" do + it_behaves_like "not found", "/this-page-does-not-exist" + end + + context "when the requested route format is html" do + it_behaves_like "not found", "/this-page-does-not-exist.html" + end + + context "when the requested route format is not html" do + it_behaves_like "not found", "/this-page-does-not-exist.xml" + end + end + end + + context "when an unhandled internal system error occurs (500)" do + + let(:start_forms_controller) { FloodRiskEngine::StartFormsController.new } + + before do + allow(FloodRiskEngine::StartFormsController).to receive(:new).and_return(start_forms_controller) + allow(start_forms_controller).to receive(:create).and_raise(StandardError) + end + + it do + post start_forms_path + + expect(response).to redirect_to error_path("500") + end + end + end +end