diff --git a/modules/vaos/app/errors/eps/service_error.rb b/modules/vaos/app/errors/eps/service_error.rb deleted file mode 100644 index 3a1208abaae..00000000000 --- a/modules/vaos/app/errors/eps/service_error.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -module Eps - class ServiceError < StandardError - def initialize(msg = 'An error occurred in the Eps service') - super - end - end -end diff --git a/modules/vaos/app/services/eps/appointment_service.rb b/modules/vaos/app/services/eps/appointment_service.rb index aed9eb52e22..47b9e05fbac 100644 --- a/modules/vaos/app/services/eps/appointment_service.rb +++ b/modules/vaos/app/services/eps/appointment_service.rb @@ -15,8 +15,6 @@ def get_appointment(appointment_id:, retrieve_latest_details: false) response = perform(:get, "/#{config.base_path}/appointments/#{appointment_id}#{query_params}", {}, headers) OpenStruct.new(response.body) - rescue => e - raise Eps::ServiceError, "Error fetching appointment details: #{e.message}" end ## diff --git a/modules/vaos/app/sidekiq/eps/eps_appointment_worker.rb b/modules/vaos/app/sidekiq/eps/eps_appointment_worker.rb index 62405de5791..33905c5a3c8 100644 --- a/modules/vaos/app/sidekiq/eps/eps_appointment_worker.rb +++ b/modules/vaos/app/sidekiq/eps/eps_appointment_worker.rb @@ -34,7 +34,7 @@ def perform(appointment_id, user, retry_count = 0) else send_vanotify_message(user:, error: 'Could not complete booking') end - rescue Eps::AppointmentService::ServiceError + rescue Common::Exceptions::BackendServiceException send_vanotify_message(user:, error: 'Service error, please contact support') rescue => e send_vanotify_message(user:, error: e.message) diff --git a/modules/vaos/spec/requests/vaos/v2/appointments_spec.rb b/modules/vaos/spec/requests/vaos/v2/appointments_spec.rb index 2e0456ed9b4..e59fb802f99 100644 --- a/modules/vaos/spec/requests/vaos/v2/appointments_spec.rb +++ b/modules/vaos/spec/requests/vaos/v2/appointments_spec.rb @@ -1555,6 +1555,44 @@ def stub_clinics end end + context 'when the upstream service returns a 500 error' do + it 'returns a bad_gateway status and appropriate error message' do + VCR.use_cassette('vaos/eps/get_appointments/500_error') do + VCR.use_cassette('vaos/v2/appointments/get_appointments_200') do + VCR.use_cassette('vaos/eps/get_drive_times/200') do + VCR.use_cassette 'vaos/eps/get_provider_slots/200' do + VCR.use_cassette 'vaos/eps/get_provider_service/200' do + VCR.use_cassette 'vaos/eps/draft_appointment/200' do + VCR.use_cassette 'vaos/eps/token/token_200' do + post '/vaos/v2/appointments/draft', params: draft_params, headers: inflection_header + + expect(response).to have_http_status(:bad_gateway) + response_body = JSON.parse(response.body) + expect(response_body).to have_key('errors') + expect(response_body['errors']).to be_an(Array) + + error = response_body['errors'].first + expect(error).to include( + 'title' => 'Bad Gateway', + 'detail' => 'Received an an invalid response from the upstream server', + 'code' => 'VAOS_502', + 'status' => '502', + 'source' => { + 'vamfUrl' => 'https://api.wellhive.com/care-navigation/v1/appointments?patientId=care-nav-patient-casey', + 'vamfBody' => '{"isFault": true,"isTemporary": true,"name": "Internal Server Error"}', + 'vamfStatus' => 500 + } + ) + end + end + end + end + end + end + end + end + end + context 'when Redis connection fails' do it 'returns a bad_gateway status and appropriate error message' do # Mock the Redis client to raise a connection error diff --git a/modules/vaos/spec/requests/vaos/v2/eps_appointments_spec.rb b/modules/vaos/spec/requests/vaos/v2/eps_appointments_spec.rb index 121f019ce50..b404e3a836b 100644 --- a/modules/vaos/spec/requests/vaos/v2/eps_appointments_spec.rb +++ b/modules/vaos/spec/requests/vaos/v2/eps_appointments_spec.rb @@ -115,6 +115,30 @@ end end + context 'when a booked appointment corresponding to the referral is not found' do + it 'returns a 404 error' do + VCR.use_cassette('vaos/eps/token/token_200', match_requests_on: %i[method path query]) do + VCR.use_cassette('vaos/eps/get_appointment/404', match_requests_on: %i[method path query]) do + get '/vaos/v2/eps_appointments/qdm61cJ5', headers: inflection_header + + expect(response).to have_http_status(:not_found) + end + end + end + end + + context 'when the upstream service returns a 500 error' do + it 'returns a 502 error' do + VCR.use_cassette('vaos/eps/token/token_200', match_requests_on: %i[method path query]) do + VCR.use_cassette('vaos/eps/get_appointment/500', match_requests_on: %i[method path query]) do + get '/vaos/v2/eps_appointments/qdm61cJ5', headers: inflection_header + + expect(response).to have_http_status(:bad_gateway) + end + end + end + end + context 'draft appointment' do it 'returns 404' do VCR.use_cassette('vaos/eps/token/token_200', match_requests_on: %i[method path query]) do diff --git a/modules/vaos/spec/services/eps/appointment_service_spec.rb b/modules/vaos/spec/services/eps/appointment_service_spec.rb index 8668f357d5b..d0847867fa9 100644 --- a/modules/vaos/spec/services/eps/appointment_service_spec.rb +++ b/modules/vaos/spec/services/eps/appointment_service_spec.rb @@ -64,7 +64,8 @@ end it 'throws exception' do - expect { service.get_appointment(appointment_id:) }.to raise_error(Eps::ServiceError, /VA900/) + expect { service.get_appointment(appointment_id:) }.to raise_error(Common::Exceptions::BackendServiceException, + /VA900/) end end end @@ -143,8 +144,7 @@ it 'throws exception' do expect do service.create_draft_appointment(referral_id:) - end.to raise_error(Common::Exceptions::BackendServiceException, - /VA900/) + end.to raise_error(Common::Exceptions::BackendServiceException, /VA900/) end end end diff --git a/modules/vaos/spec/sidekiq/eps_appointment_worker_spec.rb b/modules/vaos/spec/sidekiq/eps_appointment_worker_spec.rb index 5b22634832d..13489650a65 100644 --- a/modules/vaos/spec/sidekiq/eps_appointment_worker_spec.rb +++ b/modules/vaos/spec/sidekiq/eps_appointment_worker_spec.rb @@ -45,10 +45,52 @@ end it 'sends failure message after max retries' do - # rubocop:disable RSpec/SubjectStub - expect(worker).to receive(:send_vanotify_message).with(user:, error: 'Could not complete booking') + expect(va_notify_service).to receive(:send_email).with( + email_address: user.va_profile_email, + template_id: Settings.vanotify.services.va_gov.template_id.va_appointment_failure, + parameters: { + 'error' => 'Could not complete booking' + } + ) + worker.perform(appointment_id, user, Eps::EpsAppointmentWorker::MAX_RETRIES) + end + end + + context 'when the appointment is not found' do + before do + allow(service).to receive(:get_appointment).with(appointment_id:).and_raise( + Common::Exceptions::BackendServiceException.new(nil, {}, 404, 'Appointment not found') + ) + end + + it 'sends failure message after max retries' do + expect(va_notify_service).to receive(:send_email).with( + email_address: user.va_profile_email, + template_id: Settings.vanotify.services.va_gov.template_id.va_appointment_failure, + parameters: { + 'error' => 'Service error, please contact support' + } + ) + worker.perform(appointment_id, user, Eps::EpsAppointmentWorker::MAX_RETRIES) + end + end + + context 'when the upstream service returns a 500 error' do + before do + allow(service).to receive(:get_appointment).with(appointment_id:).and_raise( + Common::Exceptions::BackendServiceException.new(nil, {}, 500, 'Internal server error') + ) + end + + it 'sends failure message after max retries' do + expect(va_notify_service).to receive(:send_email).with( + email_address: user.va_profile_email, + template_id: Settings.vanotify.services.va_gov.template_id.va_appointment_failure, + parameters: { + 'error' => 'Service error, please contact support' + } + ) worker.perform(appointment_id, user, Eps::EpsAppointmentWorker::MAX_RETRIES) - # rubocop:enable RSpec/SubjectStub end end @@ -61,7 +103,7 @@ 'error' => nil } ) - worker.__send__(:send_vanotify_message, user:) + worker.send(:send_vanotify_message, user:) end end end diff --git a/spec/support/vcr_cassettes/vaos/eps/get_appointment/404.yml b/spec/support/vcr_cassettes/vaos/eps/get_appointment/404.yml new file mode 100644 index 00000000000..5efe9ce0bee --- /dev/null +++ b/spec/support/vcr_cassettes/vaos/eps/get_appointment/404.yml @@ -0,0 +1,52 @@ +--- +http_interactions: +- request: + method: get + uri: https://api.wellhive.com/care-navigation/v1/appointments/qdm61cJ5?retrieveLatestDetails=true + body: + encoding: US-ASCII + string: '' + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - Vets.gov Agent + Authorization: Bearer + X-Request-Id: + - bf9cc857-bd57-44ea-ae08-adc9f012fa37 + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + response: + status: + code: 404 + message: Not Found + headers: + Date: + - Fri, 21 Mar 2025 21:26:35 GMT + Content-Type: + - application/json + Content-Length: + - '24' + Connection: + - keep-alive + Content-Security-Policy: + - frame-ancestors 'none' + Goa-Error: + - Unauthorized + Strict-Transport-Security: + - max-age=6307200; includeSubDomains; preload + Traceparent: + - 00-1dba6dccb4a50f0c512d5bd661ebc013-92322ba626fb5dfc-01 + X-Content-Type-Options: + - nosniff + X-Request-Id: + - bf9cc857-bd57-44ea-ae08-adc9f012fa37 + X-Wellhive-Trace-Id: + - 1dba6dccb4a50f0c512d5bd661ebc013 + body: + encoding: UTF-8 + string: '{"name": "Not Found"}' + recorded_at: Fri, 21 Mar 2025 21:26:35 GMT +recorded_with: VCR 6.3.1 diff --git a/spec/support/vcr_cassettes/vaos/eps/get_appointment/500.yml b/spec/support/vcr_cassettes/vaos/eps/get_appointment/500.yml new file mode 100644 index 00000000000..39adc9b501d --- /dev/null +++ b/spec/support/vcr_cassettes/vaos/eps/get_appointment/500.yml @@ -0,0 +1,56 @@ +--- +http_interactions: +- request: + method: get + uri: https://api.wellhive.com/care-navigation/v1/appointments/qdm61cJ5?retrieveLatestDetails=true + body: + encoding: US-ASCII + string: '' + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - Vets.gov Agent + Referer: + - https://review-instance.va.gov + X-Vamf-Jwt: + - stubbed-token + X-Request-Id: + - '' + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + response: + status: + code: 500 + message: Internal Server Error + headers: + Date: + - Wed, 26 May 2021 22:47:08 GMT + Content-Type: + - application/json + Content-Length: + - '260' + Server: + - openresty + X-Vamf-Version: + - 1.5.0 + B3: + - edb650c5fed99511-10c51619b9c07470-1 + Access-Control-Allow-Headers: + - x-vamf-jwt + X-Vamf-Build: + - d8af5ed + X-Vamf-Timestamp: + - '2021-05-24T22:00:44+0000' + Access-Control-Allow-Origin: + - "*" + Access-Control-Allow-Methods: + - GET,OPTIONS + Access-Control-Max-Age: + - '3600' + body: + encoding: UTF-8 + string: '{"isFault": true,"isTemporary": true,"name": "Internal Server Error"}' + recorded_at: Wed, 26 May 2021 22:47:08 GMT \ No newline at end of file diff --git a/spec/support/vcr_cassettes/vaos/eps/get_appointments/500_error.yml b/spec/support/vcr_cassettes/vaos/eps/get_appointments/500_error.yml new file mode 100644 index 00000000000..4dade0ded8d --- /dev/null +++ b/spec/support/vcr_cassettes/vaos/eps/get_appointments/500_error.yml @@ -0,0 +1,51 @@ +--- +http_interactions: +- request: + method: get + uri: "https://api.wellhive.com/care-navigation/v1/appointments?patientId=care-nav-patient-casey" + body: + encoding: US-ASCII + string: '' + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - Vets.gov Agent + Referer: + - https://review-instance.va.gov + X-Vamf-Jwt: + - stubbed_token + X-Request-Id: + - 5d202f6b-a460-4d4b-91ee-b89771def980 + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + response: + status: + code: 500 + message: Internal Server Error + headers: + Date: + - Mon, 24 Mar 2025 16:20:54 GMT + Content-Type: + - application/json + Connection: + - keep-alive + Access-Control-Allow-Origin: + - "*" + Access-Control-Allow-Headers: + - x-vamf-jwt + Access-Control-Allow-Methods: + - GET,OPTIONS + Access-Control-Max-Age: + - '3600' + X-Envoy-Upstream-Service-Time: + - '2' + Transfer-Encoding: + - chunked + body: + encoding: UTF-8 + string: '{"isFault": true,"isTemporary": true,"name": "Internal Server Error"}' + recorded_at: Thu, 02 Sep 2021 14:00:00 GMT +recorded_with: VCR 6.3.1