diff --git a/app/controllers/idv/capture_doc_status_controller.rb b/app/controllers/idv/capture_doc_status_controller.rb index 4e69fb81765..9dd7bc000ca 100644 --- a/app/controllers/idv/capture_doc_status_controller.rb +++ b/app/controllers/idv/capture_doc_status_controller.rb @@ -33,7 +33,7 @@ def status elsif session_result.blank? || pending_barcode_attention_confirmation? || redo_document_capture_pending? :accepted - elsif !session_result.success? + elsif !session_result.success?(selfie_required: decorated_sp_session.selfie_required?) :unauthorized else :ok diff --git a/app/controllers/idv/document_capture_controller.rb b/app/controllers/idv/document_capture_controller.rb index 207630bda73..1ebf750a44e 100644 --- a/app/controllers/idv/document_capture_controller.rb +++ b/app/controllers/idv/document_capture_controller.rb @@ -25,15 +25,17 @@ def update # Not used in standard flow, here for data consistency with hybrid flow. document_capture_session.confirm_ocr - result = handle_stored_result - analytics.idv_doc_auth_document_capture_submitted(**result.to_h.merge(analytics_arguments)) + form_response = handle_stored_result + analytics.idv_doc_auth_document_capture_submitted( + **form_response.to_h.merge(analytics_arguments), + ) Funnel::DocAuth::RegisterStep.new(current_user.id, sp_session[:issuer]). call('document_capture', :update, true) cancel_establishing_in_person_enrollments - if result.success? + if form_response.success? redirect_to idv_ssn_url else redirect_to idv_document_capture_url @@ -98,7 +100,8 @@ def analytics_arguments end def handle_stored_result - if stored_result&.success? && selfie_requirement_met? + if stored_result&.success?(selfie_required: decorated_sp_session.selfie_required?) && + selfie_requirement_met? save_proofing_components(current_user) extract_pii_from_doc(current_user, store_in_session: true) flash[:success] = t('doc_auth.headings.capture_complete') diff --git a/app/controllers/idv/hybrid_mobile/document_capture_controller.rb b/app/controllers/idv/hybrid_mobile/document_capture_controller.rb index 83892d2d038..72471772a41 100644 --- a/app/controllers/idv/hybrid_mobile/document_capture_controller.rb +++ b/app/controllers/idv/hybrid_mobile/document_capture_controller.rb @@ -20,15 +20,17 @@ def show def update document_capture_session.confirm_ocr - result = handle_stored_result + form_response = handle_stored_result - analytics.idv_doc_auth_document_capture_submitted(**result.to_h.merge(analytics_arguments)) + analytics.idv_doc_auth_document_capture_submitted( + **form_response.to_h.merge(analytics_arguments), + ) Funnel::DocAuth::RegisterStep.new(document_capture_user.id, sp_session[:issuer]). call('document_capture', :update, true) # rate limiting redirect is in ImageUploadResponsePresenter - if result.success? + if form_response.success? flash[:success] = t('doc_auth.headings.capture_complete') redirect_to idv_hybrid_mobile_capture_complete_url else @@ -63,7 +65,8 @@ def analytics_arguments end def handle_stored_result - if stored_result&.success? && selfie_requirement_met? + if stored_result&.success?(selfie_required: decorated_sp_session.selfie_required?) && + selfie_requirement_met? save_proofing_components(document_capture_user) extract_pii_from_doc(document_capture_user) successful_response @@ -74,7 +77,8 @@ def handle_stored_result end def confirm_document_capture_needed - return unless stored_result&.success? + return unless stored_result&. + success?(selfie_required: decorated_sp_session.selfie_required?) return if redo_document_capture_pending? redirect_to idv_hybrid_mobile_capture_complete_url diff --git a/app/controllers/idv/link_sent_controller.rb b/app/controllers/idv/link_sent_controller.rb index c99e007ac44..c59984a3f2b 100644 --- a/app/controllers/idv/link_sent_controller.rb +++ b/app/controllers/idv/link_sent_controller.rb @@ -80,7 +80,8 @@ def render_step_incomplete_error end def take_photo_with_phone_successful? - stored_result&.success? && selfie_requirement_met? + stored_result&.success?(selfie_required: decorated_sp_session.selfie_required?) && + selfie_requirement_met? end end end diff --git a/app/services/doc_auth/lexis_nexis/responses/true_id_response.rb b/app/services/doc_auth/lexis_nexis/responses/true_id_response.rb index b8c64a9f8d6..41bf6a71016 100644 --- a/app/services/doc_auth/lexis_nexis/responses/true_id_response.rb +++ b/app/services/doc_auth/lexis_nexis/responses/true_id_response.rb @@ -103,12 +103,13 @@ def billed? end # @return [:success, :fail, :not_processed] - # When selfie result is missing, return :not_processed + # When selfie result is missing or not requested: + # return :not_processed # Otherwise: # return :success if selfie check result == 'Pass' # return :fail def selfie_status - return :not_processed if selfie_result.nil? + return :not_processed if selfie_result.nil? || !@liveness_checking_enabled selfie_result == 'Pass' ? :success : :fail end diff --git a/app/services/doc_auth/mock/result_response.rb b/app/services/doc_auth/mock/result_response.rb index 4f1d388866a..480a9b8622b 100644 --- a/app/services/doc_auth/mock/result_response.rb +++ b/app/services/doc_auth/mock/result_response.rb @@ -86,7 +86,7 @@ def pii_from_doc end def success? - doc_auth_success? && (selfie_check_performed? ? selfie_passed? : true) + doc_auth_success? && (@selfie_required ? selfie_passed? : true) end def attention_with_barcode? diff --git a/app/services/doc_auth/selfie_concern.rb b/app/services/doc_auth/selfie_concern.rb index 89e562168fd..dc58f04ee16 100644 --- a/app/services/doc_auth/selfie_concern.rb +++ b/app/services/doc_auth/selfie_concern.rb @@ -29,7 +29,7 @@ def selfie_check_performed? SELFIE_PERFORMED_STATUSES.include?(selfie_status) end - private + private SELFIE_PERFORMED_STATUSES = %i[success fail] @@ -43,5 +43,5 @@ def selfie_check_performed? def get_portrait_error(portrait_match_results) portrait_match_results&.with_indifferent_access&.dig(:FaceErrorMessage) end -end + end end diff --git a/app/services/document_capture_session_result.rb b/app/services/document_capture_session_result.rb index 33ae3c94e94..1a8374366ef 100644 --- a/app/services/document_capture_session_result.rb +++ b/app/services/document_capture_session_result.rb @@ -26,9 +26,9 @@ def selfie_status self[:selfie_status].to_sym end - def success_status + def success_status(selfie_required:) # doc_auth_success : including document, attention_with_barcode and id type verification - !!doc_auth_success && selfie_status != :fail && !!pii + !!doc_auth_success && (selfie_required ? selfie_status == :success : true) && !!pii end alias_method :success?, :success_status diff --git a/spec/controllers/idv/image_uploads_controller_spec.rb b/spec/controllers/idv/image_uploads_controller_spec.rb index 157393aa063..ee436844033 100644 --- a/spec/controllers/idv/image_uploads_controller_spec.rb +++ b/spec/controllers/idv/image_uploads_controller_spec.rb @@ -8,6 +8,7 @@ let(:back_image) { DocAuthImageFixtures.document_back_image_multipart } let(:selfie_img) { nil } let(:state_id_number) { 'S59397998' } + let(:liveness_checking_required) { false } describe '#create' do subject(:action) do @@ -354,6 +355,7 @@ context 'selfie included' do let(:back_image) { DocAuthImageFixtures.portrait_match_success_yaml } let(:selfie_img) { DocAuthImageFixtures.selfie_image_multipart } + let(:liveness_checking_required) { true } before do allow(controller.decorated_sp_session).to receive(:selfie_required?).and_return(true) @@ -368,7 +370,7 @@ image_source: :unknown, user_uuid: an_instance_of(String), uuid_prefix: nil, - liveness_checking_required: true, + liveness_checking_required: liveness_checking_required, images_cropped: false, ).and_call_original @@ -376,7 +378,11 @@ expect(response.status).to eq(200) expect(json[:success]).to eq(true) - expect(document_capture_session.reload.load_result.success?).to eq(true) + expect( + document_capture_session.reload.load_result.success?( + selfie_required: liveness_checking_required, + ), + ).to eq(true) expect(document_capture_session.reload.load_result.selfie_check_performed?).to eq(true) end end @@ -390,7 +396,7 @@ image_source: :unknown, user_uuid: an_instance_of(String), uuid_prefix: nil, - liveness_checking_required: false, + liveness_checking_required: liveness_checking_required, images_cropped: false, ).and_call_original @@ -398,7 +404,10 @@ expect(response.status).to eq(200) expect(json[:success]).to eq(true) - expect(document_capture_session.reload.load_result.success?).to eq(true) + expect( + document_capture_session.reload.load_result. + success?(selfie_required: liveness_checking_required), + ).to eq(true) end it 'tracks events' do @@ -1271,12 +1280,17 @@ let(:back_image) { DocAuthImageFixtures.portrait_match_success_yaml } let(:selfie_img) { DocAuthImageFixtures.selfie_image_multipart } + let(:liveness_checking_required) { true } it 'returns a successful response' do action expect(response.status).to eq(200) expect(json[:success]).to eq(true) - expect(document_capture_session.reload.load_result.success?).to eq(true) + expect( + document_capture_session.reload.load_result.success?( + selfie_required: liveness_checking_required, + ), + ).to eq(true) expect(document_capture_session.reload.load_result.selfie_check_performed?).to eq(true) end @@ -1296,7 +1310,11 @@ action expect(response.status).to eq(200) expect(json[:success]).to eq(true) - expect(document_capture_session.reload.load_result.success?).to eq(true) + expect( + document_capture_session.reload.load_result.success?( + selfie_required: liveness_checking_required, + ), + ).to eq(true) end end end diff --git a/spec/features/idv/doc_auth/document_capture_spec.rb b/spec/features/idv/doc_auth/document_capture_spec.rb index 84e677d9c2f..c0b501e2348 100644 --- a/spec/features/idv/doc_auth/document_capture_spec.rb +++ b/spec/features/idv/doc_auth/document_capture_spec.rb @@ -192,11 +192,18 @@ expect_step_indicator_current_step(t('step_indicator.flows.idv.verify_id')) expect(page).not_to have_content(t('doc_auth.headings.document_capture_selfie')) - attach_and_submit_images + # doc auth is successful while liveness is not req'd + attach_images( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_credential_no_liveness.yml' + ), + ) + submit_images expect(page).to have_current_path(idv_ssn_url) expect_costing_for_document - expect(DocAuthLog.find_by(user_id: user.id).state).to eq('MT') + expect(DocAuthLog.find_by(user_id: user.id).state).to eq('NY') expect(page).to have_current_path(idv_ssn_url) fill_out_ssn_form_ok diff --git a/spec/models/document_capture_session_spec.rb b/spec/models/document_capture_session_spec.rb index 07b4ac0d08c..736e3a001ac 100644 --- a/spec/models/document_capture_session_spec.rb +++ b/spec/models/document_capture_session_spec.rb @@ -54,7 +54,7 @@ record.store_result_from_response(doc_auth_response) result = record.load_result - expect(result.success?).to eq(doc_auth_response.success?) + expect(result.success?(selfie_required: true)).to eq(doc_auth_response.success?) expect(result.pii).to eq(doc_auth_response.pii_from_doc.deep_symbolize_keys) end diff --git a/spec/services/doc_auth/lexis_nexis/responses/true_id_response_spec.rb b/spec/services/doc_auth/lexis_nexis/responses/true_id_response_spec.rb index 40479f87e60..1828ba098f5 100644 --- a/spec/services/doc_auth/lexis_nexis/responses/true_id_response_spec.rb +++ b/spec/services/doc_auth/lexis_nexis/responses/true_id_response_spec.rb @@ -9,6 +9,9 @@ let(:success_with_liveness_response) do instance_double(Faraday::Response, status: 200, body: LexisNexisFixtures.true_id_response_success_with_liveness) end + let(:doc_auth_success_with_face_match_fail) do + instance_double(Faraday::Response, status: 200, body: LexisNexisFixtures.true_id_response_with_face_match_fail) + end let(:failure_response_face_match_fail) do instance_double(Faraday::Response, status: 200, body: LexisNexisFixtures.true_id_response_with_face_match_fail) end @@ -50,7 +53,7 @@ let(:config) do DocAuth::LexisNexis::Config.new end - let(:liveness_enabled) { false } + let(:liveness_checking_enabled) { false } let(:workflow) { 'default_workflow' } let(:request_context) do { @@ -59,13 +62,53 @@ end context 'when the response is a success' do let(:response) do - described_class.new(success_response, config, liveness_enabled, request_context) + described_class.new(success_response, config, liveness_checking_enabled, request_context) end it 'is a successful result' do expect(response.successful_result?).to eq(true) expect(response.to_h[:vendor]).to eq('TrueID') end + + context 'when a portrait match is returned' do + let(:liveness_checking_enabled) { true } + context 'when selfie status is failed' do + let(:response) do + described_class.new( + doc_auth_success_with_face_match_fail, config, liveness_checking_enabled, + request_context + ) + end + it 'is a failed result' do + expect(response.selfie_status).to eq(:fail) + expect(response.success?).to eq(false) + expect(response.to_h[:vendor]).to eq('TrueID') + end + + context 'when a liveness check was not requested' do + let(:liveness_checking_enabled) { false } + it 'is a successful result' do + expect(response.selfie_status).to eq(:not_processed) + expect(response.success?).to eq(true) + expect(response.to_h[:vendor]).to eq('TrueID') + end + end + end + context 'when selfie status passes' do + let(:response) do + described_class.new( + success_with_liveness_response, config, liveness_checking_enabled, + request_context + ) + end + it 'is a successful result' do + expect(response.selfie_status).to eq(:success) + expect(response.success?).to eq(true) + expect(response.to_h[:vendor]).to eq('TrueID') + end + end + end + it 'has no error messages' do expect(response.error_messages).to be_empty end @@ -325,84 +368,92 @@ def get_decision_product(resp) expect(errors[:back]).to contain_exactly(DocAuth::Errors::FALLBACK_FIELD_LEVEL) expect(errors[:hints]).to eq(true) end + context 'when liveness enabled' do + let(:liveness_checking_enabled) { true } + it 'returns Failed for visible_pattern when it gets passed and failed value ' do + output = described_class.new( + failure_response_no_liveness, config, + liveness_checking_enabled + ).to_h + expect(output.to_h[:log_alert_results]). + to match(a_hash_including(visible_pattern: { no_side: 'Failed' })) + end - it 'returns Failed for visible_pattern when it gets passed and failed value ' do - output = described_class.new(failure_response_no_liveness, config).to_h - expect(output.to_h[:log_alert_results]). - to match(a_hash_including(visible_pattern: { no_side: 'Failed' })) - end - - it 'returns Failed for liveness failure' do - response = described_class.new(failure_response_with_liveness, config) - output = response.to_h - expect(output[:success]).to eq(false) - expect(response.doc_auth_success?).to eq(false) - expect(response.selfie_status).to eq(:fail) - end - - it 'produces expected hash output' do - output = described_class.new( - failure_response_with_all_failures, config, liveness_enabled, - request_context - ).to_h + it 'returns Failed for liveness failure' do + response = described_class.new( + failure_response_with_liveness, config, + liveness_checking_enabled + ) + output = response.to_h + expect(output[:success]).to eq(false) + expect(response.doc_auth_success?).to eq(false) + expect(response.selfie_status).to eq(:fail) + end - expect(output).to match( - success: false, - exception: nil, - errors: { - general: [DocAuth::Errors::GENERAL_ERROR], - front: [DocAuth::Errors::FALLBACK_FIELD_LEVEL], - back: [DocAuth::Errors::FALLBACK_FIELD_LEVEL], - hints: true, - }, - attention_with_barcode: false, - doc_type_supported: true, - conversation_id: a_kind_of(String), - request_id: a_kind_of(String), - reference: a_kind_of(String), - vendor: 'TrueID', - billed: true, - log_alert_results: a_hash_including('2d_barcode_content': { no_side: 'Failed' }), - transaction_status: 'failed', - transaction_reason_code: 'failed_true_id', - product_status: 'pass', - decision_product_status: 'fail', - doc_auth_result: 'Failed', - processed_alerts: a_hash_including(:passed, :failed), - address_line2_present: false, - alert_failure_count: a_kind_of(Numeric), - portrait_match_results: { - 'FaceMatchResult' => 'Fail', - 'FaceMatchScore' => '0', - 'FaceStatusCode' => '0', - 'FaceErrorMessage' => 'Liveness: PoorQuality', - }, - image_metrics: a_hash_including(:front, :back), - 'ClassificationMode' => 'Automatic', - 'DocAuthResult' => 'Failed', - 'DocClass' => 'DriversLicense', - 'DocClassCode' => 'DriversLicense', - 'DocClassName' => 'Drivers License', - 'DocumentName' => 'Connecticut (CT) Driver License', - 'DocIssuerCode' => 'CT', - 'DocIssuerType' => 'StateProvince', - 'DocIssuerName' => 'Connecticut', - 'DocIssue' => '2009', - 'DocIssueType' => 'Driver License', - 'DocIsGeneric' => 'false', - 'OrientationChanged' => 'false', - 'PresentationChanged' => 'false', - classification_info: { - Front: a_hash_including(:ClassName, :CountryCode, :IssuerType), - Back: a_hash_including(:ClassName, :CountryCode, :IssuerType), - }, - doc_auth_success: false, - selfie_status: :fail, - selfie_live: true, - selfie_quality_good: false, - liveness_enabled: false, - workflow: anything, - ) + it 'produces expected hash output' do + output = described_class.new( + failure_response_with_all_failures, config, liveness_checking_enabled, + request_context + ).to_h + + expect(output).to match( + success: false, + exception: nil, + errors: { + general: [DocAuth::Errors::GENERAL_ERROR], + front: [DocAuth::Errors::FALLBACK_FIELD_LEVEL], + back: [DocAuth::Errors::FALLBACK_FIELD_LEVEL], + hints: true, + }, + attention_with_barcode: false, + doc_type_supported: true, + conversation_id: a_kind_of(String), + request_id: a_kind_of(String), + reference: a_kind_of(String), + vendor: 'TrueID', + billed: true, + log_alert_results: a_hash_including('2d_barcode_content': { no_side: 'Failed' }), + transaction_status: 'failed', + transaction_reason_code: 'failed_true_id', + product_status: 'pass', + decision_product_status: 'fail', + doc_auth_result: 'Failed', + processed_alerts: a_hash_including(:passed, :failed), + address_line2_present: false, + alert_failure_count: a_kind_of(Numeric), + portrait_match_results: { + 'FaceMatchResult' => 'Fail', + 'FaceMatchScore' => '0', + 'FaceStatusCode' => '0', + 'FaceErrorMessage' => 'Liveness: PoorQuality', + }, + image_metrics: a_hash_including(:front, :back), + 'ClassificationMode' => 'Automatic', + 'DocAuthResult' => 'Failed', + 'DocClass' => 'DriversLicense', + 'DocClassCode' => 'DriversLicense', + 'DocClassName' => 'Drivers License', + 'DocumentName' => 'Connecticut (CT) Driver License', + 'DocIssuerCode' => 'CT', + 'DocIssuerType' => 'StateProvince', + 'DocIssuerName' => 'Connecticut', + 'DocIssue' => '2009', + 'DocIssueType' => 'Driver License', + 'DocIsGeneric' => 'false', + 'OrientationChanged' => 'false', + 'PresentationChanged' => 'false', + classification_info: { + Front: a_hash_including(:ClassName, :CountryCode, :IssuerType), + Back: a_hash_including(:ClassName, :CountryCode, :IssuerType), + }, + doc_auth_success: false, + selfie_status: :fail, + selfie_live: true, + selfie_quality_good: false, + liveness_enabled: true, + workflow: anything, + ) + end end it 'produces appropriate errors with document tampering' do output = described_class.new(failure_response_tampering, config).to_h @@ -440,7 +491,7 @@ def get_decision_product(resp) it 'produces reasonable output for a TrueID failure without details' do output = described_class.new( - failure_response_empty, config, liveness_enabled, + failure_response_empty, config, liveness_checking_enabled, request_context ).to_h @@ -457,7 +508,7 @@ def get_decision_product(resp) it 'produces reasonable output for a malformed TrueID response' do allow(NewRelic::Agent).to receive(:notice_error) output = described_class.new( - failure_response_malformed, config, liveness_enabled, + failure_response_malformed, config, liveness_checking_enabled, request_context ).to_h @@ -701,13 +752,16 @@ def get_decision_product(resp) end context 'when selfie check is enabled' do + let(:liveness_checking_enabled) { true } context 'whe missing selfie result in response' do let(:request_context) do { workflow: 'selfie_workflow', } end - let(:response) { described_class.new(success_response, config, true, request_context) } + let(:response) do + described_class.new(success_response, config, liveness_checking_enabled, request_context) + end it 'returns :not_processed when missing selfie in response' do expect(response.selfie_status).to eq(:not_processed) end @@ -718,13 +772,17 @@ def get_decision_product(resp) end end context 'when selfie passed' do - let(:response) { described_class.new(success_with_liveness_response, config, true) } + let(:response) do + described_class.new(success_with_liveness_response, config, liveness_checking_enabled) + end it 'returns :success' do expect(response.selfie_status).to eq(:success) end end context 'when selfie failed' do - let(:response) { described_class.new(failure_response_with_liveness, config, true) } + let(:response) do + described_class.new(failure_response_with_liveness, config, liveness_checking_enabled) + end it 'returns :fail' do expect(response.selfie_status).to eq(:fail) end @@ -733,7 +791,7 @@ def get_decision_product(resp) end describe '#successful_result?' do - context 'and selfie check is enabled' do + context 'and liveness check is enabled' do let(:liveness_checking_enabled) { true } it 'returns true with a passing selfie' do @@ -765,6 +823,19 @@ def get_decision_product(resp) expect(response.successful_result?).to eq(false) end end + + context 'when a portrait match result is not returned' do + let(:response) do + described_class.new( + success_response, config, liveness_checking_enabled + ) + end + + it 'returns false' do + expect(response.selfie_status).to eq(:not_processed) + expect(response.successful_result?).to eq(false) + end + end end context 'and selfie check is disabled' do diff --git a/spec/services/document_capture_session_result_spec.rb b/spec/services/document_capture_session_result_spec.rb index b32434f5ee7..76720fac1c2 100644 --- a/spec/services/document_capture_session_result_spec.rb +++ b/spec/services/document_capture_session_result_spec.rb @@ -4,27 +4,9 @@ let(:id) { SecureRandom.uuid } let(:success) { true } let(:pii) { { 'first_name' => 'Testy', 'last_name' => 'Testerson' } } + let(:selfie_required) { false } context 'EncryptedRedisStructStorage' do - it 'works with EncryptedRedisStructStorage' do - result = DocumentCaptureSessionResult.new( - id: id, - success: success, - doc_auth_success: success, - selfie_status: :success, - pii: pii, - attention_with_barcode: false, - ) - EncryptedRedisStructStorage.store(result) - loaded_result = EncryptedRedisStructStorage.load(id, type: DocumentCaptureSessionResult) - - expect(loaded_result.id).to eq(id) - expect(loaded_result.success?).to eq(success) - expect(loaded_result.pii).to eq(pii.deep_symbolize_keys) - expect(loaded_result.attention_with_barcode?).to eq(false) - expect(loaded_result.selfie_status).to eq(:success) - expect(loaded_result.doc_auth_success).to eq(true) - end it 'add fingerprint with EncryptedRedisStructStorage' do result = DocumentCaptureSessionResult.new( id: id, @@ -39,6 +21,29 @@ expect(result.failed_front_image?(nil)).to eq(false) expect(result.failed_back_image?(nil)).to eq(false) end + context 'with selfie' do + let(:selfie_required) { true } + it 'works with EncryptedRedisStructStorage' do + result = DocumentCaptureSessionResult.new( + id: id, + success: success, + doc_auth_success: success, + selfie_status: :success, + pii: pii, + attention_with_barcode: false, + ) + EncryptedRedisStructStorage.store(result) + loaded_result = EncryptedRedisStructStorage.load(id, type: DocumentCaptureSessionResult) + + expect(loaded_result.id).to eq(id) + expect(loaded_result.success?(selfie_required: selfie_required)).to eq(success) + expect(loaded_result.pii).to eq(pii.deep_symbolize_keys) + expect(loaded_result.attention_with_barcode?).to eq(false) + expect(loaded_result.selfie_status).to eq(:success) + expect(loaded_result.doc_auth_success).to eq(true) + end + end + describe '#selfie_status' do it 'returns a symbol' do result = DocumentCaptureSessionResult.new( @@ -62,7 +67,7 @@ selfie_status: :not_processed, doc_auth_success: true, ) - expect(result.success?).to eq(true) + expect(result.success?(selfie_required: selfie_required)).to eq(true) end it 'reports correctly from false when missing doc_auth_success and selfie_status' do result = DocumentCaptureSessionResult.new( @@ -71,44 +76,51 @@ pii: pii, attention_with_barcode: false, ) - expect(result.success?).to eq(false) + expect(result.success?(selfie_required: selfie_required)).to eq(false) end - it 'reports failure when selfie_status is :fail' do - result = DocumentCaptureSessionResult.new( - id: id, - success: false, - pii: pii, - attention_with_barcode: false, - selfie_status: :fail, - doc_auth_success: true, - ) - expect(result.success?).to eq(false) + context 'when pii is not present' do + it 'reports success status is failed' do + result = DocumentCaptureSessionResult.new( + id: id, + success: true, + pii: nil, + attention_with_barcode: false, + doc_auth_success: true, + selfie_status: :not_processed, + ) + expect(result.success?(selfie_required: selfie_required)).to eq(false) + end end - - it 'reports failure when doc_auth_success is false' do - result = DocumentCaptureSessionResult.new( - id: id, - success: false, - pii: pii, - attention_with_barcode: false, - selfie_status: :success, - doc_auth_success: false, - ) - expect(result.success?).to eq(false) + context 'when selfie status is failed' do + it 'reports success status is successful' do + result = DocumentCaptureSessionResult.new( + id: id, + success: false, + pii: pii, + attention_with_barcode: false, + doc_auth_success: true, + selfie_status: :fail, + ) + expect(result.success?(selfie_required: selfie_required)).to eq(true) + end end - - describe 'when success field, doc_auth_success, and selfie_status conflict' do - it 'reports correct result' do + context 'when selfie status is success' do + it 'reports success status is successful' do result = DocumentCaptureSessionResult.new( id: id, success: false, pii: pii, attention_with_barcode: false, - selfie_status: :not_processed, doc_auth_success: true, + selfie_status: :success, ) - expect(result.success?).to eq(true) + expect(result.success?(selfie_required: selfie_required)).to eq(true) + end + end + context 'when a liveness check is required' do + let(:selfie_required) { true } + it 'reports failure when selfie_status is :fail' do result = DocumentCaptureSessionResult.new( id: id, success: true, @@ -117,7 +129,43 @@ selfie_status: :fail, doc_auth_success: true, ) - expect(result.success?).to eq(false) + expect(result.success?(selfie_required: selfie_required)).to eq(false) + end + + it 'reports failure when selfie_status is not_processed' do + result = DocumentCaptureSessionResult.new( + id: id, + success: true, + pii: pii, + attention_with_barcode: false, + selfie_status: :not_processed, + doc_auth_success: true, + ) + expect(result.success?(selfie_required: selfie_required)).to eq(false) + end + + it 'reports failure when doc_auth_success is false' do + result = DocumentCaptureSessionResult.new( + id: id, + success: true, + pii: pii, + attention_with_barcode: false, + selfie_status: :success, + doc_auth_success: false, + ) + expect(result.success?(selfie_required: selfie_required)).to eq(false) + end + + it 'reports failure when selfie_status is not_processed' do + result = DocumentCaptureSessionResult.new( + id: id, + success: false, + pii: pii, + attention_with_barcode: false, + selfie_status: :success, + doc_auth_success: true, + ) + expect(result.success?(selfie_required: selfie_required)).to eq(true) end end end