require 'rails_helper' RSpec.describe 'Error handling', type: :request do shared_examples 'JSON response format' do let(:as) { :json } it { expect(response).to have_http_status(http_status) } it { expect(json_response).to be_a_kind_of(Hash) } it do # There is a special case where we mask technical errors and return # a random error code that can be easily found in the logs by an # administrator. However, this makes it hard to check for the exact error # message. Therefore we only check for the substring in this particular case if message == 'Please contact your administrator' expect(json_response['error']).to include(message) else expect(json_response['error']).to eq(message) end end end shared_examples 'HTML response format' do let(:as) { :html } it { expect(response).to have_http_status(http_status) } it { expect(response.body).to include('#{title}") } it { expect(response.body).to include("

#{headline}

") } it { expect(response.body).to include(message) } end context 'error with confidential message is raised' do let(:admin_user) { create(:admin_user, groups: Group.all) } let!(:ticket) { create(:ticket) } let(:invalid_group_id) { 99_999 } let(:message) { 'Please contact your administrator' } let(:http_status) { :unprocessable_entity } before do # fake production ENV to enable error hiding env = double( production?: true, test?: false, development?: false ) allow(::Rails).to receive(:env).and_return(env) authenticated_as(admin_user) put "/api/v1/tickets/#{ticket.id}?all=true", params: { group_id: invalid_group_id }, as: as end context 'requesting JSON' do include_examples 'JSON response format' end context 'requesting HTML' do let(:title) { '422: Unprocessable Entity' } let(:headline) { '422: The change you wanted was rejected.' } include_examples 'HTML response format' end end context 'URL route does not exist' do before do get '/not_existing_url', as: as end let(:message) { 'No route matches [GET] /not_existing_url' } let(:http_status) { :not_found } context 'requesting JSON' do include_examples 'JSON response format' end context 'requesting HTML' do let(:title) { '404: Not Found' } let(:headline) { '404: Requested resource was not found' } include_examples 'HTML response format' end end context 'request is not authenticated' do before do get '/api/v1/organizations', as: as end let(:message) { 'authentication failed' } let(:http_status) { :unauthorized } context 'requesting JSON' do include_examples 'JSON response format' end context 'requesting HTML' do let(:title) { '401: Unauthorized' } let(:headline) { '401: Unauthorized' } include_examples 'HTML response format' end end context 'exception is raised' do before do get '/tests/raised_exception', params: { exception: exception.name, message: message }, as: as end shared_examples 'handles exception' do |exception, http_status, title, headline| context "#{exception} is raised" do let(:exception) { exception } let(:http_status) { http_status } let(:message) { 'some error message' } context 'requesting JSON' do include_examples 'JSON response format' end context 'requesting HTML' do let(:title) { title } let(:headline) { headline } include_examples 'HTML response format' end end end include_examples 'handles exception', ActiveRecord::RecordNotFound, :not_found, '404: Not Found', '404: Requested resource was not found' include_examples 'handles exception', Exceptions::NotAuthorized, :unauthorized, '401: Unauthorized', '401: Unauthorized' include_examples 'handles exception', Exceptions::UnprocessableEntity, :unprocessable_entity, '422: Unprocessable Entity', '422: The change you wanted was rejected.' include_examples 'handles exception', ArgumentError, :unprocessable_entity, '422: Unprocessable Entity', '422: The change you wanted was rejected.' include_examples 'handles exception', StandardError, :internal_server_error, '500: Something went wrong', "500: We're sorry, but something went wrong." end end