From dcd56af3afe0fa1f26ad70a1c7cae094da6e0ae6 Mon Sep 17 00:00:00 2001 From: Richard Davis Date: Tue, 26 Mar 2024 12:53:57 -0400 Subject: [PATCH] [Bugfix] Personal health care contacts: Selects only valid contact types (#16062) * Selects only valid contact types --- .../profile/v3/health_benefit_bio_response.rb | 44 ++++++++----- .../v3/health_benefit_bio_response_spec.rb | 62 +++++++++++++++++++ .../lib/va_profile/profile/v3/service_spec.rb | 56 +++++++++++++---- .../profile/v3/health_benefit_bio_500.yml | 61 ++++++++++++++++++ 4 files changed, 196 insertions(+), 27 deletions(-) create mode 100644 spec/lib/va_profile/profile/v3/health_benefit_bio_response_spec.rb create mode 100644 spec/support/vcr_cassettes/va_profile/profile/v3/health_benefit_bio_500.yml diff --git a/lib/va_profile/profile/v3/health_benefit_bio_response.rb b/lib/va_profile/profile/v3/health_benefit_bio_response.rb index ffea96f8731..1dde3fb9f89 100644 --- a/lib/va_profile/profile/v3/health_benefit_bio_response.rb +++ b/lib/va_profile/profile/v3/health_benefit_bio_response.rb @@ -4,23 +4,39 @@ require 'va_profile/models/associated_person' require 'va_profile/models/message' -module VAProfile::Profile::V3 - class HealthBenefitBioResponse < VAProfile::Response - attr_reader :body +module VAProfile + module Profile + module V3 + class HealthBenefitBioResponse < VAProfile::Response + attr_reader :body - attribute :contacts, Array[VAProfile::Models::AssociatedPerson] - attribute :messages, Array[VAProfile::Models::Message] + attribute :contacts, Array[VAProfile::Models::AssociatedPerson] + attribute :messages, Array[VAProfile::Models::Message] - def initialize(response) - @body = response.body - contacts = body.dig('profile', 'health_benefit', 'associated_persons') - &.sort_by { |p| VAProfile::Models::AssociatedPerson::CONTACT_TYPES.index(p['contact_type']) } - messages = body['messages'] - super(response.status, { contacts:, messages: }) - end + def initialize(response) + @body = response.body + contacts = body.dig('profile', 'health_benefit', 'associated_persons') + &.select { |p| valid_contact_types.include?(p['contact_type']) } + &.sort_by { |p| valid_contact_types.index(p['contact_type']) } + messages = body['messages'] + super(response.status, { contacts:, messages: }) + end + + def metadata + { status:, messages: } + end + + private - def metadata - { status:, messages: } + def valid_contact_types + [ + VAProfile::Models::AssociatedPerson::EMERGENCY_CONTACT, + VAProfile::Models::AssociatedPerson::OTHER_EMERGENCY_CONTACT, + VAProfile::Models::AssociatedPerson::PRIMARY_NEXT_OF_KIN, + VAProfile::Models::AssociatedPerson::OTHER_NEXT_OF_KIN + ] + end + end end end end diff --git a/spec/lib/va_profile/profile/v3/health_benefit_bio_response_spec.rb b/spec/lib/va_profile/profile/v3/health_benefit_bio_response_spec.rb new file mode 100644 index 00000000000..70f51f3ed61 --- /dev/null +++ b/spec/lib/va_profile/profile/v3/health_benefit_bio_response_spec.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +require 'rails_helper' +require 'va_profile/profile/v3/health_benefit_bio_response' + +describe VAProfile::Profile::V3::HealthBenefitBioResponse do + subject { described_class.new(response) } + + let(:response) do + double('Faraday::Response', + status: 200, + body: { + 'profile' => { + 'health_benefit' => { + 'associated_persons' => [{ + 'contact_type' => contact_type + }] + } + } + }) + end + + describe 'Emergency contact' do + let(:contact_type) { 'Emergency Contact' } + + it 'includes contact' do + expect(subject.contacts).not_to be_empty + end + end + + describe 'Other emergency contact' do + let(:contact_type) { 'Other emergency contact' } + + it 'includes contact' do + expect(subject.contacts).not_to be_empty + end + end + + describe 'Primary Next of Kin' do + let(:contact_type) { 'Primary Next of Kin' } + + it 'includes contact' do + expect(subject.contacts).not_to be_empty + end + end + + describe 'Other Next of Kin' do + let(:contact_type) { 'Other Next of Kin' } + + it 'includes contact' do + expect(subject.contacts).not_to be_empty + end + end + + describe 'Invalid contact type' do + let(:contact_type) { 'Invalid type' } + + it 'does not include contact' do + expect(subject.contacts).to be_empty + end + end +end diff --git a/spec/lib/va_profile/profile/v3/service_spec.rb b/spec/lib/va_profile/profile/v3/service_spec.rb index ddcae4f7b05..da5ec7b9b89 100644 --- a/spec/lib/va_profile/profile/v3/service_spec.rb +++ b/spec/lib/va_profile/profile/v3/service_spec.rb @@ -55,30 +55,60 @@ describe '#get_health_benefit_bio' do let(:user) { build(:user, :loa3, idme_uuid:) } + around do |ex| + VCR.use_cassette(cassette) { ex.run } + end + context '200 response' do let(:idme_uuid) { 'dd681e7d6dea41ad8b80f8d39284ef29' } + let(:cassette) { 'va_profile/profile/v3/health_benefit_bio_200' } it 'returns the contacts (aka associated_persons) for a user, sorted' do - VCR.use_cassette('va_profile/profile/v3/health_benefit_bio_200') do - response = subject.get_health_benefit_bio - expect(response.status).to eq(200) - expect(response.contacts.size).to eq(4) - types = response.contacts.map(&:contact_type) - expect(types).to match_array(VAProfile::Models::AssociatedPerson::CONTACT_TYPES) - end + response = subject.get_health_benefit_bio + expect(response.status).to eq(200) + expect(response.contacts.size).to eq(4) + types = response.contacts.map(&:contact_type) + valid_contact_types = [ + VAProfile::Models::AssociatedPerson::EMERGENCY_CONTACT, + VAProfile::Models::AssociatedPerson::OTHER_EMERGENCY_CONTACT, + VAProfile::Models::AssociatedPerson::PRIMARY_NEXT_OF_KIN, + VAProfile::Models::AssociatedPerson::OTHER_NEXT_OF_KIN + ] + expect(types).to match_array(valid_contact_types) end end context '404 response' do let(:idme_uuid) { '88f572d4-91af-46ef-a393-cba6c351e252' } + let(:cassette) { 'va_profile/profile/v3/health_benefit_bio_404' } + + it 'includes messages received from the api' do + response = subject.get_health_benefit_bio + expect(response.status).to eq(404) + expect(response.contacts.size).to eq(0) + expect(response.messages.size).to eq(1) + end + end + + context '500 response' do + let(:idme_uuid) { '88f572d4-91af-46ef-a393-cba6c351e252' } + let(:cassette) { 'va_profile/profile/v3/health_benefit_bio_500' } it 'includes messages recieved from the api' do - VCR.use_cassette('va_profile/profile/v3/health_benefit_bio_404') do - response = subject.get_health_benefit_bio - expect(response.status).to eq(404) - expect(response.contacts.size).to eq(0) - expect(response.messages.size).to eq(1) - end + response = subject.get_health_benefit_bio + expect(response.status).to eq(500) + expect(response.contacts.size).to eq(0) + expect(response.messages.size).to eq(1) + end + end + + context 'api timeout' do + let(:idme_uuid) { '88f572d4-91af-46ef-a393-cba6c351e252' } + let(:cassette) { 'va_profile/profile/v3/health_benefit_bio_500' } + + it 'raises an error' do + allow_any_instance_of(Faraday::Connection).to receive(:post).and_raise(Faraday::TimeoutError) + expect { subject.get_health_benefit_bio }.to raise_error(Common::Exceptions::GatewayTimeout) end end end diff --git a/spec/support/vcr_cassettes/va_profile/profile/v3/health_benefit_bio_500.yml b/spec/support/vcr_cassettes/va_profile/profile/v3/health_benefit_bio_500.yml new file mode 100644 index 00000000000..66944f355f2 --- /dev/null +++ b/spec/support/vcr_cassettes/va_profile/profile/v3/health_benefit_bio_500.yml @@ -0,0 +1,61 @@ +--- +http_interactions: +- request: + method: post + uri: https://int.vet360.va.gov/profile-service/profile/v3/2.16.840.1.113883.4.349/88f572d4-91af-46ef-a393-cba6c351e252%5EPN%5E200VIDM%5EUSDVA + body: + encoding: UTF-8 + string: '{"bios":[{"bioPath":"healthBenefit"}]}' + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - Vets.gov Agent + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + response: + status: + code: 500 + message: Internal Server Error + headers: + X-Oneagent-Js-Injection: + - 'true' + Server-Timing: + - dtRpid;desc="-552585048", dtSInfo;desc="0" + - dtRpid;desc="667499283", dtSInfo;desc="0" + Vaprofiletxauditid: + - ab45e9d3-6491-4062-8384-40d23135d252 + X-Content-Type-Options: + - nosniff + X-Xss-Protection: + - '0' + Cache-Control: + - no-cache, no-store, max-age=0, must-revalidate + Pragma: + - no-cache + Expires: + - '0' + X-Frame-Options: + - DENY + Content-Security-Policy: + - 'default-src ''self'' ''unsafe-eval'' ''unsafe-inline'' data: filesystem: + about: blob: ws: wss:' + Date: + - Mon, 05 Feb 2024 22:13:44 GMT + Referrer-Policy: + - no-referrer + Content-Type: + - application/json + Content-Length: + - '184' + Strict-Transport-Security: + - max-age=16000000; includeSubDomains; preload; + body: + encoding: UTF-8 + string: '{"messages":[{"code":"MVI203","key":"MviResponseError","text":"MVI returned + acknowledgement error code AE with error detail: More Than One Active Correlation + Exists","severity":"FATAL"}]}' + recorded_at: Mon, 05 Feb 2024 22:13:44 GMT +recorded_with: VCR 6.2.0