diff --git a/app/services/proofing/resolution/plugins/aamva_plugin.rb b/app/services/proofing/resolution/plugins/aamva_plugin.rb index e13a5179937..f516efb5386 100644 --- a/app/services/proofing/resolution/plugins/aamva_plugin.rb +++ b/app/services/proofing/resolution/plugins/aamva_plugin.rb @@ -15,13 +15,13 @@ class AamvaPlugin def call( applicant_pii:, current_sp:, - instant_verify_result:, + instant_verify_state_id_address_result:, ipp_enrollment_in_progress:, timer: ) should_proof = should_proof_state_id_with_aamva?( applicant_pii:, - instant_verify_result:, + instant_verify_state_id_address_result:, ipp_enrollment_in_progress:, ) @@ -87,34 +87,35 @@ def same_address_as_id?(applicant_pii) def should_proof_state_id_with_aamva?( applicant_pii:, - instant_verify_result:, + instant_verify_state_id_address_result:, ipp_enrollment_in_progress: ) return false unless aamva_supports_state_id_jurisdiction?(applicant_pii) # If the user is in in-person-proofing and they have changed their address then # they are not eligible for get-to-yes if !ipp_enrollment_in_progress || same_address_as_id?(applicant_pii) - user_can_pass_after_state_id_check?(instant_verify_result:) + user_can_pass_after_state_id_check?(instant_verify_state_id_address_result:) else - instant_verify_result.success? + instant_verify_state_id_address_result.success? end end def user_can_pass_after_state_id_check?( - instant_verify_result: + instant_verify_state_id_address_result: ) - return true if instant_verify_result.success? + return true if instant_verify_state_id_address_result.success? # For failed IV results, this method validates that the user is eligible to pass if the # failed attributes are covered by the same attributes in a successful AAMVA response # aka the Get-to-Yes w/ AAMVA feature. - if !instant_verify_result.failed_result_can_pass_with_additional_verification? + if !instant_verify_state_id_address_result. + failed_result_can_pass_with_additional_verification? return false end attributes_aamva_can_pass = [:address, :dob, :state_id_number] attributes_requiring_additional_verification = - instant_verify_result.attributes_requiring_additional_verification + instant_verify_state_id_address_result.attributes_requiring_additional_verification results_that_cannot_pass_aamva = attributes_requiring_additional_verification - attributes_aamva_can_pass diff --git a/app/services/proofing/resolution/plugins/instant_verify_state_id_address_plugin.rb b/app/services/proofing/resolution/plugins/instant_verify_state_id_address_plugin.rb new file mode 100644 index 00000000000..c3fe246cb06 --- /dev/null +++ b/app/services/proofing/resolution/plugins/instant_verify_state_id_address_plugin.rb @@ -0,0 +1,83 @@ +# frozen_string_literal: true + +module Proofing + module Resolution + module Plugins + class InstantVerifyStateIdAddressPlugin + SECONDARY_ID_ADDRESS_MAP = { + identity_doc_address1: :address1, + identity_doc_address2: :address2, + identity_doc_city: :city, + identity_doc_address_state: :state, + identity_doc_zipcode: :zipcode, + }.freeze + + def call( + applicant_pii:, + current_sp:, + instant_verify_residential_address_result:, + ipp_enrollment_in_progress:, + timer: + ) + if same_address_as_id?(applicant_pii) && ipp_enrollment_in_progress + return instant_verify_residential_address_result + end + + return resolution_cannot_pass unless instant_verify_residential_address_result.success? + + applicant_pii_with_state_id_address = + if ipp_enrollment_in_progress + with_state_id_address(applicant_pii) + else + applicant_pii + end + + timer.time('resolution') do + proofer.proof(applicant_pii_with_state_id_address) + end.tap do |result| + Db::SpCost::AddSpCost.call( + current_sp, + :lexis_nexis_resolution, + transaction_id: result.transaction_id, + ) + end + end + + def proofer + @proofer ||= + if IdentityConfig.store.proofer_mock_fallback + Proofing::Mock::ResolutionMockClient.new + else + Proofing::LexisNexis::InstantVerify::Proofer.new( + instant_verify_workflow: IdentityConfig.store.lexisnexis_instant_verify_workflow, + account_id: IdentityConfig.store.lexisnexis_account_id, + base_url: IdentityConfig.store.lexisnexis_base_url, + username: IdentityConfig.store.lexisnexis_username, + password: IdentityConfig.store.lexisnexis_password, + hmac_key_id: IdentityConfig.store.lexisnexis_hmac_key_id, + hmac_secret_key: IdentityConfig.store.lexisnexis_hmac_secret_key, + request_mode: IdentityConfig.store.lexisnexis_request_mode, + ) + end + end + + def resolution_cannot_pass + Proofing::Resolution::Result.new( + success: false, errors: {}, exception: nil, vendor_name: 'ResolutionCannotPass', + ) + end + + def same_address_as_id?(applicant_pii) + applicant_pii[:same_address_as_id].to_s == 'true' + end + + # Make a copy of pii with the user's state ID address overwriting the address keys + # Need to first remove the address keys to avoid key/value collision + def with_state_id_address(pii) + pii.except(*SECONDARY_ID_ADDRESS_MAP.values). + transform_keys(SECONDARY_ID_ADDRESS_MAP) + end + end + end + end +end diff --git a/app/services/proofing/resolution/progressive_proofer.rb b/app/services/proofing/resolution/progressive_proofer.rb index d787cf397f0..757a4f244e9 100644 --- a/app/services/proofing/resolution/progressive_proofer.rb +++ b/app/services/proofing/resolution/progressive_proofer.rb @@ -8,15 +8,17 @@ module Resolution # 2. The user has only provided one address for their residential and identity document # address or separate residential and identity document addresses class ProgressiveProofer - attr_reader :applicant_pii, :timer, :current_sp attr_reader :aamva_plugin, :instant_verify_residential_address_plugin, + :instant_verify_state_id_address_plugin, :threatmetrix_plugin def initialize @aamva_plugin = Plugins::AamvaPlugin.new @instant_verify_residential_address_plugin = Plugins::InstantVerifyResidentialAddressPlugin.new + @instant_verify_state_id_address_plugin = + Plugins::InstantVerifyStateIdAddressPlugin.new @threatmetrix_plugin = Plugins::ThreatMetrixPlugin.new end @@ -37,10 +39,7 @@ def proof( ipp_enrollment_in_progress:, current_sp: ) - @applicant_pii = applicant_pii.except(:best_effort_phone_number_for_socure) - @timer = timer - @ipp_enrollment_in_progress = ipp_enrollment_in_progress - @current_sp = current_sp + applicant_pii = applicant_pii.except(:best_effort_phone_number_for_socure) device_profiling_result = threatmetrix_plugin.call( applicant_pii:, @@ -51,19 +50,25 @@ def proof( user_email:, ) - @residential_instant_verify_result = instant_verify_residential_address_plugin.call( + instant_verify_residential_address_result = instant_verify_residential_address_plugin.call( applicant_pii:, current_sp:, ipp_enrollment_in_progress:, timer:, ) - @instant_verify_result = proof_id_address_with_lexis_nexis_if_needed + instant_verify_state_id_address_result = instant_verify_state_id_address_plugin.call( + applicant_pii:, + current_sp:, + instant_verify_residential_address_result:, + ipp_enrollment_in_progress:, + timer:, + ) state_id_result = aamva_plugin.call( applicant_pii:, current_sp:, - instant_verify_result:, + instant_verify_state_id_address_result:, ipp_enrollment_in_progress:, timer:, ) @@ -71,92 +76,14 @@ def proof( ResultAdjudicator.new( device_profiling_result: device_profiling_result, ipp_enrollment_in_progress: ipp_enrollment_in_progress, - resolution_result: instant_verify_result, + resolution_result: instant_verify_state_id_address_result, should_proof_state_id: aamva_plugin.aamva_supports_state_id_jurisdiction?(applicant_pii), state_id_result: state_id_result, - residential_resolution_result: residential_instant_verify_result, + residential_resolution_result: instant_verify_residential_address_result, same_address_as_id: applicant_pii[:same_address_as_id], applicant_pii: applicant_pii, ) end - - private - - attr_reader :device_profiling_result, - :residential_instant_verify_result, - :instant_verify_result - - def resolution_cannot_pass - Proofing::Resolution::Result.new( - success: false, errors: {}, exception: nil, vendor_name: 'ResolutionCannotPass', - ) - end - - def proof_id_address_with_lexis_nexis_if_needed - if same_address_as_id? && ipp_enrollment_in_progress? - return residential_instant_verify_result - end - return resolution_cannot_pass unless residential_instant_verify_result.success? - - timer.time('resolution') do - resolution_proofer.proof(applicant_pii_with_state_id_address) - end.tap do |result| - add_sp_cost(:lexis_nexis_resolution, result.transaction_id) - end - end - - def same_address_as_id? - applicant_pii[:same_address_as_id].to_s == 'true' - end - - def ipp_enrollment_in_progress? - @ipp_enrollment_in_progress - end - - def resolution_proofer - @resolution_proofer ||= - if IdentityConfig.store.proofer_mock_fallback - Proofing::Mock::ResolutionMockClient.new - else - Proofing::LexisNexis::InstantVerify::Proofer.new( - instant_verify_workflow: IdentityConfig.store.lexisnexis_instant_verify_workflow, - account_id: IdentityConfig.store.lexisnexis_account_id, - base_url: IdentityConfig.store.lexisnexis_base_url, - username: IdentityConfig.store.lexisnexis_username, - password: IdentityConfig.store.lexisnexis_password, - hmac_key_id: IdentityConfig.store.lexisnexis_hmac_key_id, - hmac_secret_key: IdentityConfig.store.lexisnexis_hmac_secret_key, - request_mode: IdentityConfig.store.lexisnexis_request_mode, - ) - end - end - - def applicant_pii_with_state_id_address - if ipp_enrollment_in_progress? - with_state_id_address(applicant_pii) - else - applicant_pii - end - end - - def add_sp_cost(token, transaction_id) - Db::SpCost::AddSpCost.call(current_sp, token, transaction_id: transaction_id) - end - - # Make a copy of pii with the user's state ID address overwriting the address keys - # Need to first remove the address keys to avoid key/value collision - def with_state_id_address(pii) - pii.except(*SECONDARY_ID_ADDRESS_MAP.values). - transform_keys(SECONDARY_ID_ADDRESS_MAP) - end - - SECONDARY_ID_ADDRESS_MAP = { - identity_doc_address1: :address1, - identity_doc_address2: :address2, - identity_doc_city: :city, - identity_doc_address_state: :state, - identity_doc_zipcode: :zipcode, - }.freeze end end end diff --git a/spec/services/proofing/resolution/plugins/aamva_plugin_spec.rb b/spec/services/proofing/resolution/plugins/aamva_plugin_spec.rb index f955716ee1f..cfd24e0c754 100644 --- a/spec/services/proofing/resolution/plugins/aamva_plugin_spec.rb +++ b/spec/services/proofing/resolution/plugins/aamva_plugin_spec.rb @@ -3,7 +3,7 @@ RSpec.describe Proofing::Resolution::Plugins::AamvaPlugin do let(:applicant_pii) { Idp::Constants::MOCK_IDV_APPLICANT_WITH_SSN } let(:current_sp) { build(:service_provider) } - let(:instant_verify_result) { nil } + let(:instant_verify_state_id_address_result) { nil } let(:ipp_enrollment_in_progress) { false } let(:proofer) { instance_double(Proofing::Aamva::Proofer, proof: proofer_result) } let(:proofer_result) do @@ -40,7 +40,7 @@ def sp_cost_count_with_transaction_id plugin.call( applicant_pii:, current_sp:, - instant_verify_result:, + instant_verify_state_id_address_result:, ipp_enrollment_in_progress:, timer: JobHelpers::Timer.new, ) @@ -60,7 +60,7 @@ def sp_cost_count_with_transaction_id end context 'InstantVerify succeeded' do - let(:instant_verify_result) do + let(:instant_verify_state_id_address_result) do Proofing::Resolution::Result.new( success: true, vendor_name: 'lexisnexis:instant_verify', @@ -96,7 +96,7 @@ def sp_cost_count_with_transaction_id context 'InstantVerify failed' do context 'and the failure can possibly be covered by AAMVA' do - let(:instant_verify_result) do + let(:instant_verify_state_id_address_result) do Proofing::Resolution::Result.new( success: false, vendor_name: 'lexisnexis:instant_verify', @@ -120,7 +120,7 @@ def sp_cost_count_with_transaction_id end context 'but the failure cannot be covered by AAMVA' do - let(:instant_verify_result) do + let(:instant_verify_state_id_address_result) do Proofing::Resolution::Result.new( success: false, vendor_name: 'lexisnexis:instant_verify', @@ -164,7 +164,7 @@ def sp_cost_count_with_transaction_id context 'residential address same as id address' do let(:applicant_pii) { Idp::Constants::MOCK_IDV_APPLICANT_SAME_ADDRESS_AS_ID } - let(:instant_verify_result) do + let(:instant_verify_state_id_address_result) do Proofing::Resolution::Result.new( success: true, vendor_name: 'lexisnexis:instant_verify', @@ -184,7 +184,7 @@ def sp_cost_count_with_transaction_id context 'InstantVerify failed' do context 'and the failure can possibly be covered by AAMVA' do - let(:instant_verify_result) do + let(:instant_verify_state_id_address_result) do Proofing::Resolution::Result.new( success: false, vendor_name: 'lexisnexis:instant_verify', @@ -204,7 +204,7 @@ def sp_cost_count_with_transaction_id end context 'but the failure cannot be covered by AAMVA' do - let(:instant_verify_result) do + let(:instant_verify_state_id_address_result) do Proofing::Resolution::Result.new( success: false, vendor_name: 'lexisnexis:instant_verify', @@ -236,7 +236,7 @@ def sp_cost_count_with_transaction_id context 'InstantVerify succeeded for residential address' do context 'and InstantVerify passed for id address' do - let(:instant_verify_result) do + let(:instant_verify_state_id_address_result) do Proofing::Resolution::Result.new( success: true, vendor_name: 'lexisnexis:instant_verify', @@ -255,7 +255,7 @@ def sp_cost_count_with_transaction_id context 'and InstantVerify failed for state id address' do context 'but the failure can possibly be covered by AAMVA' do - let(:instant_verify_result) do + let(:instant_verify_state_id_address_result) do Proofing::Resolution::Result.new( success: false, vendor_name: 'lexisnexis:instant_verify', @@ -275,7 +275,7 @@ def sp_cost_count_with_transaction_id end context 'and the failure cannot be covered by AAMVA' do - let(:instant_verify_result) do + let(:instant_verify_state_id_address_result) do Proofing::Resolution::Result.new( success: false, vendor_name: 'lexisnexis:instant_verify', diff --git a/spec/services/proofing/resolution/plugins/instant_verify_state_id_address_plugin_spec.rb b/spec/services/proofing/resolution/plugins/instant_verify_state_id_address_plugin_spec.rb new file mode 100644 index 00000000000..43c0edbfeb6 --- /dev/null +++ b/spec/services/proofing/resolution/plugins/instant_verify_state_id_address_plugin_spec.rb @@ -0,0 +1,289 @@ +require 'rails_helper' + +RSpec.describe Proofing::Resolution::Plugins::InstantVerifyStateIdAddressPlugin do + let(:applicant_pii) { Idp::Constants::MOCK_IDV_APPLICANT_SAME_ADDRESS_AS_ID } + + let(:current_sp) { build(:service_provider) } + + let(:instant_verify_residential_address_result) do + Proofing::Resolution::Result.new( + success: true, + errors: {}, + exception: nil, + vendor_name: 'lexisnexis:instant_verify', + ) + end + + let(:ipp_enrollment_in_progress) { true } + + let(:proofer_result) do + Proofing::Resolution::Result.new( + success: true, + errors: {}, + exception: nil, + vendor_name: 'lexisnexis:instant_verify', + ) + end + + subject(:plugin) do + described_class.new + end + + before do + allow(plugin.proofer).to receive(:proof).and_return(proofer_result) + end + + describe '#call' do + subject(:call) do + plugin.call( + applicant_pii:, + current_sp:, + ipp_enrollment_in_progress:, + instant_verify_residential_address_result:, + timer: JobHelpers::Timer.new, + ) + end + + context 'remote unsupervised proofing' do + let(:ipp_enrollment_in_progress) { false } + + let(:state_id_address) do + { + address1: applicant_pii[:address1], + address2: applicant_pii[:address2], + city: applicant_pii[:city], + state: applicant_pii[:state], + state_id_jurisdiction: applicant_pii[:state_id_jurisdiction], + zipcode: applicant_pii[:zipcode], + } + end + + it 'passes state id address to proofer' do + expect(plugin.proofer). + to receive(:proof). + with(hash_including(state_id_address)). + and_call_original + + call + end + + context 'when InstantVerify call succeeds' do + it 'returns the proofer result' do + expect(call).to eql(proofer_result) + end + + it 'records a LexisNexis SP cost' do + expect { call }. + to change { + SpCost.where( + cost_type: :lexis_nexis_resolution, + issuer: current_sp.issuer, + ).count + }.to(1) + end + end + + context 'when InstantVerify call fails' do + let(:proofer_result) do + Proofing::Resolution::Result.new( + success: false, + errors: {}, + exception: nil, + vendor_name: 'lexisnexis:instant_verify', + ) + end + + it 'returns the proofer result' do + expect(call).to eql(proofer_result) + end + + it 'records a LexisNexis SP cost' do + expect { call }. + to change { + SpCost.where( + cost_type: :lexis_nexis_resolution, + issuer: current_sp.issuer, + ).count + }.to(1) + end + end + + context 'when InstantVerify call results in exception' do + let(:proofer_result) do + Proofing::Resolution::Result.new( + success: false, + errors: {}, + exception: RuntimeError.new(':ohno:'), + vendor_name: 'lexisnexis:instant_verify', + ) + end + + it 'returns the proofer result' do + expect(call).to eql(proofer_result) + end + + it 'records a LexisNexis SP cost' do + expect { call }. + to change { + SpCost.where( + cost_type: :lexis_nexis_resolution, + issuer: current_sp.issuer, + ).count + }.to(1) + end + end + end + + context 'in person proofing' do + context 'residential address and id address are the same' do + it 'reuses residential address result' do + result = call + expect(plugin.proofer).not_to have_received(:proof) + expect(result).to eql(instant_verify_residential_address_result) + end + + it 'does not add a new LexisNexis SP cost (since residential address result was reused)' do + expect { call }. + not_to change { + SpCost.where( + cost_type: :lexis_nexis_resolution, + issuer: current_sp.issuer, + ).count + } + end + end + + context 'residential address and id address are diferent' do + let(:applicant_pii) { Idp::Constants::MOCK_IDV_APPLICANT_STATE_ID_ADDRESS } + let(:residential_address) do + { + address1: applicant_pii[:address1], + address2: applicant_pii[:address2], + city: applicant_pii[:city], + state: applicant_pii[:state], + state_id_jurisdiction: applicant_pii[:state_id_jurisdiction], + zipcode: applicant_pii[:zipcode], + } + end + let(:state_id_address) do + { + address1: applicant_pii[:identity_doc_address1], + address2: applicant_pii[:identity_doc_address2], + city: applicant_pii[:identity_doc_city], + state: applicant_pii[:identity_doc_address_state], + state_id_jurisdiction: applicant_pii[:state_id_jurisdiction], + zipcode: applicant_pii[:identity_doc_zipcode], + } + end + + context 'LexisNexis InstantVerify passes for residential address' do + it 'calls the InstantVerify Proofer with state id address' do + expect(plugin.proofer).to receive(:proof).with(hash_including(state_id_address)). + and_call_original + + call + end + + context 'when InstantVerify call succeeds' do + it 'returns the proofer result' do + expect(call).to eql(proofer_result) + end + + it 'records a LexisNexis SP cost' do + expect { call }. + to change { + SpCost.where( + cost_type: :lexis_nexis_resolution, + issuer: current_sp.issuer, + ).count + }.to(1) + end + end + + context 'when InstantVerify call fails' do + let(:proofer_result) do + Proofing::Resolution::Result.new( + success: false, + errors: {}, + exception: nil, + vendor_name: 'lexisnexis:instant_verify', + ) + end + + it 'returns the proofer result' do + expect(call).to eql(proofer_result) + end + + it 'records a LexisNexis SP cost' do + expect { call }. + to change { + SpCost.where( + cost_type: :lexis_nexis_resolution, + issuer: current_sp.issuer, + ).count + }.to(1) + end + end + + context 'when InstantVerify call results in exception' do + let(:proofer_result) do + Proofing::Resolution::Result.new( + success: false, + errors: {}, + exception: RuntimeError.new(':ohno:'), + vendor_name: 'lexisnexis:instant_verify', + ) + end + + it 'returns the proofer result' do + expect(call).to eql(proofer_result) + end + + it 'records a LexisNexis SP cost' do + expect { call }. + to change { + SpCost.where( + cost_type: :lexis_nexis_resolution, + issuer: current_sp.issuer, + ).count + }.to(1) + end + end + + context 'LexisNexis InstantVerify failed for residential address' do + let(:instant_verify_residential_address_result) do + Proofing::Resolution::Result.new( + success: false, + errors: {}, + exception: nil, + vendor_name: 'lexisnexis:instant_verify', + ) + end + + it 'does not make unnecessary calls' do + expect(plugin.proofer).not_to receive(:proof) + call + end + + it 'does not record an additional LexisNexis SP cost' do + expect { call }. + not_to change { + SpCost.where( + cost_type: :lexis_nexis_resolution, + issuer: current_sp.issuer, + ).count + } + end + + it 'returns a ResolutionCannotPass result' do + call.tap do |result| + expect(result.success?).to eql(false) + expect(result.vendor_name).to eql('ResolutionCannotPass') + end + end + end + end + end + end + end +end diff --git a/spec/services/proofing/resolution/progressive_proofer_spec.rb b/spec/services/proofing/resolution/progressive_proofer_spec.rb index 92ee058ee93..10723e687da 100644 --- a/spec/services/proofing/resolution/progressive_proofer_spec.rb +++ b/spec/services/proofing/resolution/progressive_proofer_spec.rb @@ -12,18 +12,35 @@ Proofing::Resolution::Plugins::InstantVerifyResidentialAddressPlugin.new end - let(:instant_verify_result) do + let(:instant_verify_residential_address_result) do Proofing::Resolution::Result.new( success: true, - attributes_requiring_additional_verification: [:address], - transaction_id: 'ln-123', + transaction_id: 'iv-residential', ) end - let(:instant_verify_proofer) do + let(:instant_verify_residential_address_proofer) do instance_double( Proofing::LexisNexis::InstantVerify::Proofer, - proof: instant_verify_result, + proof: instant_verify_residential_address_result, + ) + end + + let(:instant_verify_state_id_address_plugin) do + Proofing::Resolution::Plugins::InstantVerifyStateIdAddressPlugin.new + end + + let(:instant_verify_state_id_address_result) do + Proofing::Resolution::Result.new( + success: true, + transaction_id: 'iv-state-id', + ) + end + + let(:instant_verify_state_id_address_proofer) do + instance_double( + Proofing::LexisNexis::InstantVerify::Proofer, + proof: instant_verify_state_id_address_result, ) end @@ -56,54 +73,8 @@ ) end - let(:dcs_uuid) { SecureRandom.uuid } - subject(:progressive_proofer) { described_class.new } - let(:state_id_address) do - { - address1: applicant_pii[:identity_doc_address1], - address2: applicant_pii[:identity_doc_address2], - city: applicant_pii[:identity_doc_city], - state: applicant_pii[:identity_doc_address_state], - zipcode: applicant_pii[:identity_doc_zipcode], - } - end - - let(:residential_address) do - { - address1: applicant_pii[:address1], - address2: applicant_pii[:address2], - city: applicant_pii[:city], - state: applicant_pii[:state], - zipcode: applicant_pii[:zipcode], - } - end - - let(:transformed_pii) do - { - first_name: 'FAKEY', - last_name: 'MCFAKERSON', - dob: '1938-10-06', - address1: '123 Way St', - address2: '2nd Address Line', - city: 'Best City', - zipcode: '12345', - state_id_jurisdiction: 'VA', - address_state: 'VA', - state_id_number: '1111111111111', - same_address_as_id: 'true', - } - end - - let(:resolution_result) do - instance_double(Proofing::Resolution::Result, success?: true, errors: nil) - end - - def block_real_instant_verify_requests - allow(Proofing::LexisNexis::InstantVerify::VerificationRequest).to receive(:new) - end - before do allow(progressive_proofer).to receive(:threatmetrix_plugin).and_return(threatmetrix_plugin) allow(threatmetrix_plugin).to receive(:proofer).and_return(threatmetrix_proofer) @@ -114,11 +85,12 @@ def block_real_instant_verify_requests allow(progressive_proofer).to receive(:instant_verify_residential_address_plugin). and_return(instant_verify_residential_address_plugin) allow(instant_verify_residential_address_plugin).to receive(:proofer). - and_return(instant_verify_proofer) + and_return(instant_verify_residential_address_proofer) - allow(progressive_proofer).to receive(:resolution_proofer).and_return(instant_verify_proofer) - - block_real_instant_verify_requests + allow(progressive_proofer).to receive(:instant_verify_state_id_address_plugin). + and_return(instant_verify_state_id_address_plugin) + allow(instant_verify_state_id_address_plugin).to receive(:proofer). + and_return(instant_verify_state_id_address_proofer) end it 'assigns aamva_plugin' do @@ -133,6 +105,12 @@ def block_real_instant_verify_requests ) end + it 'assigns instant_verify_state_id_address_plugin' do + expect(described_class.new.instant_verify_state_id_address_plugin).to be_a( + Proofing::Resolution::Plugins::InstantVerifyStateIdAddressPlugin, + ) + end + it 'assigns threatmetrix_plugin' do expect(described_class.new.threatmetrix_plugin).to be_a( Proofing::Resolution::Plugins::ThreatMetrixPlugin, @@ -140,10 +118,6 @@ def block_real_instant_verify_requests end describe '#proof' do - before do - allow(IdentityConfig.store).to receive(:proofer_mock_fallback).and_return(false) - end - subject(:proof) do progressive_proofer.proof( applicant_pii:, @@ -161,7 +135,7 @@ def block_real_instant_verify_requests expect(aamva_plugin).to receive(:call).with( applicant_pii:, current_sp:, - instant_verify_result:, + instant_verify_state_id_address_result:, ipp_enrollment_in_progress: false, timer: an_instance_of(JobHelpers::Timer), ) @@ -178,6 +152,20 @@ def block_real_instant_verify_requests proof end + it 'calls InstantVerifyStateIdAddressPlugin' do + expect(instant_verify_state_id_address_plugin).to receive(:call).with( + applicant_pii:, + current_sp:, + instant_verify_residential_address_result: satisfy do |result| + expect(result.success?).to eql(true) + expect(result.vendor_name).to eql('ResidentialAddressNotRequired') + end, + ipp_enrollment_in_progress: false, + timer: an_instance_of(JobHelpers::Timer), + ).and_call_original + proof + end + it 'calls ThreatMetrixPlugin' do expect(threatmetrix_plugin).to receive(:call).with( applicant_pii:, @@ -194,7 +182,7 @@ def block_real_instant_verify_requests proof.tap do |result| expect(result).to be_an_instance_of(Proofing::Resolution::ResultAdjudicator) - expect(result.resolution_result).to eql(instant_verify_result) + expect(result.resolution_result).to eql(instant_verify_state_id_address_result) expect(result.state_id_result).to eql(aamva_result) expect(result.device_profiling_result).to eql(threatmetrix_result) expect(result.residential_resolution_result).to satisfy do |result| @@ -213,11 +201,15 @@ def block_real_instant_verify_requests context 'residential address is same as id' do let(:applicant_pii) { Idp::Constants::MOCK_IDV_APPLICANT_SAME_ADDRESS_AS_ID } + let(:instant_verify_state_id_address_result) do + instant_verify_residential_address_result + end + it 'calls AamvaPlugin' do expect(aamva_plugin).to receive(:call).with( applicant_pii:, current_sp:, - instant_verify_result: instant_verify_result, + instant_verify_state_id_address_result:, ipp_enrollment_in_progress: true, timer: an_instance_of(JobHelpers::Timer), ) @@ -235,6 +227,17 @@ def block_real_instant_verify_requests proof end + it 'calls InstantVerifyStateIdAddressPlugin' do + expect(instant_verify_state_id_address_plugin).to receive(:call).with( + applicant_pii:, + current_sp:, + instant_verify_residential_address_result:, + ipp_enrollment_in_progress: true, + timer: an_instance_of(JobHelpers::Timer), + ).and_call_original + proof + end + it 'calls ThreatMetrixPlugin' do expect(threatmetrix_plugin).to receive(:call).with( applicant_pii:, @@ -251,10 +254,12 @@ def block_real_instant_verify_requests proof.tap do |result| expect(result).to be_an_instance_of(Proofing::Resolution::ResultAdjudicator) - expect(result.resolution_result).to eql(instant_verify_result) + expect(result.resolution_result).to eql(instant_verify_state_id_address_result) expect(result.state_id_result).to eql(aamva_result) expect(result.device_profiling_result).to eql(threatmetrix_result) - expect(result.residential_resolution_result).to eql(instant_verify_result) + expect(result.residential_resolution_result).to( + eql(instant_verify_state_id_address_result), + ) expect(result.ipp_enrollment_in_progress).to eql(true) expect(proof.same_address_as_id).to eq(applicant_pii[:same_address_as_id]) end @@ -264,18 +269,6 @@ def block_real_instant_verify_requests context 'residential address is different than id' do let(:applicant_pii) { Idp::Constants::MOCK_IDV_APPLICANT_STATE_ID_ADDRESS } - let(:instant_verify_residential_result) do - Proofing::Resolution::Result.new( - success: true, - vendor_name: 'lexis_nexis_residential', - ) - end - - before do - allow(instant_verify_proofer).to receive(:proof). - and_return(instant_verify_residential_result, instant_verify_result) - end - it 'calls ThreatMetrixPlugin' do expect(threatmetrix_plugin).to receive(:call).with( applicant_pii:, @@ -298,11 +291,22 @@ def block_real_instant_verify_requests proof end + it 'calls InstantVerifyStateIdAddressPlugin' do + expect(instant_verify_state_id_address_plugin).to receive(:call).with( + applicant_pii:, + current_sp:, + instant_verify_residential_address_result:, + ipp_enrollment_in_progress: true, + timer: an_instance_of(JobHelpers::Timer), + ).and_call_original + proof + end + it 'calls AamvaPlugin' do expect(aamva_plugin).to receive(:call).with( applicant_pii:, current_sp:, - instant_verify_result:, + instant_verify_state_id_address_result:, ipp_enrollment_in_progress: true, timer: an_instance_of(JobHelpers::Timer), ).and_call_original @@ -312,10 +316,12 @@ def block_real_instant_verify_requests it 'returns a ResultAdjudicator' do proof.tap do |result| expect(result).to be_an_instance_of(Proofing::Resolution::ResultAdjudicator) - expect(result.resolution_result).to eql(instant_verify_result) + expect(result.resolution_result).to eql(instant_verify_state_id_address_result) expect(result.state_id_result).to eql(aamva_result) expect(result.device_profiling_result).to eql(threatmetrix_result) - expect(result.residential_resolution_result).to eql(instant_verify_residential_result) + expect(result.residential_resolution_result).to( + eql(instant_verify_residential_address_result), + ) expect(result.ipp_enrollment_in_progress).to eql(true) expect(result.same_address_as_id).to eql('false') end @@ -323,124 +329,30 @@ def block_real_instant_verify_requests end end - context 'ipp flow' do - let(:ipp_enrollment_in_progress) { true } - let(:applicant_pii) { Idp::Constants::MOCK_IDV_APPLICANT_SAME_ADDRESS_AS_ID } - - context 'residential address and id address are the same' do - it 'only makes one request to LexisNexis InstantVerify' do - proof - - expect(instant_verify_proofer).to have_received(:proof).exactly(:once) - expect(aamva_proofer).to have_received(:proof) - end - - it 'produces a result adjudicator with correct information' do - expect(proof.same_address_as_id).to eq('true') - expect(proof.ipp_enrollment_in_progress).to eq(true) - expect(proof.resolution_result).to eq(proof.residential_resolution_result) - expect(aamva_proofer).to have_received(:proof) - end - - it 'uses the transformed PII' do - allow(progressive_proofer).to receive(:with_state_id_address).and_return(transformed_pii) - - expect(proof.same_address_as_id).to eq('true') - expect(proof.ipp_enrollment_in_progress).to eq(true) - expect(proof.resolution_result).to eq(proof.residential_resolution_result) - expect(proof.resolution_result.success?).to eq(true) - end - - it 'records a single LexisNexis SP cost' do - proof - - lexis_nexis_sp_costs = SpCost.where( - cost_type: :lexis_nexis_resolution, - issuer: current_sp.issuer, - ) - - expect(lexis_nexis_sp_costs.count).to eq(1) - end - - context 'LexisNexis InstantVerify fails' do - let(:instant_verify_proofing_success) { false } - - before do - allow(instant_verify_result).to( - receive( - :failed_result_can_pass_with_additional_verification?, - ).and_return(true), - ) - end - - it 'includes the state ID in the InstantVerify call' do - expect(instant_verify_proofer).to receive(:proof). - with(hash_including(state_id_address)) - - proof - end - end + context 'when applicant_pii includes best_effort_phone_number_for_socure' do + let(:applicant_pii) do + Idp::Constants::MOCK_IDV_APPLICANT_WITH_SSN.merge( + best_effort_phone_number_for_socure: '3608675309', + ) end - context 'residential address and id address are different' do - let(:residential_address_proof) do - instance_double(Proofing::Resolution::Result, transaction_id: 'residential-123') - end - - let(:ipp_enrollment_in_progress) { true } - let(:applicant_pii) { Idp::Constants::MOCK_IDV_APPLICANT_STATE_ID_ADDRESS } - let(:residential_address) do - { - address1: applicant_pii[:address1], - address2: applicant_pii[:address2], - city: applicant_pii[:city], - state: applicant_pii[:state], - state_id_jurisdiction: applicant_pii[:state_id_jurisdiction], - zipcode: applicant_pii[:zipcode], - } - end - let(:state_id_address) do - { - address1: applicant_pii[:identity_doc_address1], - address2: applicant_pii[:identity_doc_address2], - city: applicant_pii[:identity_doc_city], - state: applicant_pii[:identity_doc_address_state], - state_id_jurisdiction: applicant_pii[:state_id_jurisdiction], - zipcode: applicant_pii[:identity_doc_zipcode], - } + it 'does not pass the phone number to plugins' do + expected_applicant_pii = applicant_pii.except(:best_effort_phone_number_for_socure) + + [ + aamva_plugin, + instant_verify_residential_address_plugin, + instant_verify_state_id_address_plugin, + threatmetrix_plugin, + ].each do |plugin| + expect(plugin).to receive(:call).with( + hash_including( + applicant_pii: expected_applicant_pii, + ), + ).and_call_original end - context 'LexisNexis InstantVerify passes for residential address' do - let(:instant_verify_result) { residential_address_proof } - - before do - allow(residential_address_proof).to receive(:success?).and_return(true) - end - - context 'LexisNexis InstantVerify passes for id address' do - it 'makes two requests to the InstantVerify Proofer' do - proof - - expect(instant_verify_proofer).to have_received(:proof). - with(hash_including(residential_address)). - ordered - expect(instant_verify_proofer).to have_received(:proof). - with(hash_including(state_id_address)). - ordered - end - - it 'records 2 LexisNexis SP cost' do - proof - - lexis_nexis_sp_costs = SpCost.where( - cost_type: :lexis_nexis_resolution, - issuer: current_sp.issuer, - ) - - expect(lexis_nexis_sp_costs.count).to eq(2) - end - end - end + proof end end end