diff --git a/Gemfile b/Gemfile index dca545d4f42..84a0a3d30f1 100644 --- a/Gemfile +++ b/Gemfile @@ -32,7 +32,7 @@ gem 'pg' gem 'phonelib' gem 'pkcs11' gem 'premailer-rails' -gem 'proofer', github: '18F/identity-proofer-gem', tag: 'v2.6.1' +gem 'proofer', github: '18F/identity-proofer-gem', tag: 'v2.7.0' gem 'rack-attack' gem 'rack-cors', require: 'rack/cors' gem 'rack-headers_filter' @@ -112,6 +112,6 @@ group :test do end group :production do - gem 'aamva', git: 'git@github.com:18F/identity-aamva-api-client-gem', tag: 'v3.1.0' - gem 'lexisnexis', git: 'git@github.com:18F/identity-lexisnexis-api-client-gem', tag: 'v1.1.0' + gem 'aamva', git: 'git@github.com:18F/identity-aamva-api-client-gem', tag: 'v3.2.1' + gem 'lexisnexis', git: 'git@github.com:18F/identity-lexisnexis-api-client-gem', tag: 'v1.2.0' end diff --git a/Gemfile.lock b/Gemfile.lock index ddb048c453a..2139e4fcd18 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,20 +1,20 @@ GIT remote: git@github.com:18F/identity-aamva-api-client-gem - revision: f69b0295933809057292736ed173a5a5e11b668c - tag: v3.1.0 + revision: 03fc2f8c9bfb3218da2a7cb7c309b0650a5bebe5 + tag: v3.2.1 specs: - aamva (3.1.0) + aamva (3.2.1) dotenv hashie - httpi + typhoeus xmldsig GIT remote: git@github.com:18F/identity-lexisnexis-api-client-gem - revision: d17049ab1a03d50c0cc8a272d86cf2144192fab5 - tag: v1.1.0 + revision: 29f554ed2ea237c59a20fdbe4a675508b9c8539d + tag: v1.2.0 specs: - lexisnexis (1.1.0) + lexisnexis (1.2.0) dotenv typhoeus @@ -28,10 +28,10 @@ GIT GIT remote: https://github.com/18F/identity-proofer-gem.git - revision: 875246d603bbd9b29cbc82493513f948d4e8689b - tag: v2.6.1 + revision: f48ecd14ef602d574119022c0fc15350cbd7baa0 + tag: v2.7.0 specs: - proofer (2.6.1) + proofer (2.7.0) GIT remote: https://github.com/18F/redis-session-store.git @@ -226,7 +226,7 @@ GEM warden (~> 1.2.3) diff-lcs (1.3) docile (1.1.5) - dotenv (2.4.0) + dotenv (2.5.0) dotiw (4.0.1) actionpack (>= 4) i18n @@ -756,4 +756,4 @@ RUBY VERSION ruby 2.5.1p57 BUNDLED WITH - 1.16.4 + 1.16.5 diff --git a/app/services/idv/agent.rb b/app/services/idv/agent.rb index 1fe83925bf6..6f8b1fd4038 100644 --- a/app/services/idv/agent.rb +++ b/app/services/idv/agent.rb @@ -18,6 +18,7 @@ def proof(*stages) log_vendor(vendor, results, stage) proofer_result = vendor.proof(@applicant) results = merge_results(results, proofer_result) + results[:timed_out] = proofer_result.timed_out? break unless proofer_result.success? end @@ -35,6 +36,7 @@ def init_results }, exception: nil, success: false, + timed_out: false, } end diff --git a/app/services/idv/phone_step.rb b/app/services/idv/phone_step.rb index 9f2c8273ed3..13f01d7ae9e 100644 --- a/app/services/idv/phone_step.rb +++ b/app/services/idv/phone_step.rb @@ -18,6 +18,7 @@ def submit(step_params) def failure_reason return :fail if idv_session.step_attempts[:phone] >= Idv::Attempter.idv_max_attempts + return :timeout if idv_result[:timed_out] return :jobfail if idv_result[:exception].present? return :warning if idv_result[:success] != true end diff --git a/app/services/idv/profile_step.rb b/app/services/idv/profile_step.rb index f1e3b3c5c20..d8324c08fb6 100644 --- a/app/services/idv/profile_step.rb +++ b/app/services/idv/profile_step.rb @@ -18,6 +18,7 @@ def submit(step_params) def failure_reason return :fail if attempter.exceeded? + return :timeout if idv_result[:timed_out] return :jobfail if idv_result[:exception].present? return :warning if idv_result[:success] != true end diff --git a/lib/proofer_mocks/address_mock.rb b/lib/proofer_mocks/address_mock.rb index fafea412082..48c6e92cd17 100644 --- a/lib/proofer_mocks/address_mock.rb +++ b/lib/proofer_mocks/address_mock.rb @@ -9,6 +9,8 @@ class AddressMock < Proofer::Base result.add_error(:phone, 'The phone number could not be verified.') elsif plain_phone == '7035555999' raise 'Failed to contact proofing vendor' + elsif plain_phone == '7035555888' + raise Proofer::TimeoutError, 'address mock timeout' end result.context[:message] = 'some context for the mock address proofer' end diff --git a/lib/proofer_mocks/resolution_mock.rb b/lib/proofer_mocks/resolution_mock.rb index 60ad3bf31b0..ace8577c67d 100644 --- a/lib/proofer_mocks/resolution_mock.rb +++ b/lib/proofer_mocks/resolution_mock.rb @@ -12,7 +12,7 @@ class ResolutionMock < Proofer::Base result.add_error(:first_name, 'Unverified first name.') elsif first_name.match?(/Time/i) - sleep((Figaro.env.async_job_refresh_max_wait_seconds.to_i + 5).seconds) + raise Proofer::TimeoutError, 'resolution mock timeout' elsif applicant[:ssn].match?(/6666/) result.add_error(:ssn, 'Unverified SSN.') diff --git a/spec/controllers/idv/phone_controller_spec.rb b/spec/controllers/idv/phone_controller_spec.rb index 4fc6873ffc6..027ac201cd9 100644 --- a/spec/controllers/idv/phone_controller_spec.rb +++ b/spec/controllers/idv/phone_controller_spec.rb @@ -175,7 +175,7 @@ result = { success: true, errors: {}, - vendor: { messages: [], context: context, exception: nil }, + vendor: { messages: [], context: context, exception: nil, timed_out: false }, } expect(@analytics).to receive(:track_event).ordered.with( @@ -215,7 +215,7 @@ errors: { phone: ['The phone number could not be verified.'], }, - vendor: { messages: [], context: context, exception: nil }, + vendor: { messages: [], context: context, exception: nil, timed_out: false }, } expect(@analytics).to receive(:track_event).ordered.with( diff --git a/spec/controllers/idv/sessions_controller_spec.rb b/spec/controllers/idv/sessions_controller_spec.rb index 04a5345fc88..1affa55d209 100644 --- a/spec/controllers/idv/sessions_controller_spec.rb +++ b/spec/controllers/idv/sessions_controller_spec.rb @@ -132,7 +132,7 @@ errors: { first_name: ['Unverified first name.'], }, - vendor: { messages: [], context: context, exception: nil }, + vendor: { messages: [], context: context, exception: nil, timed_out: false }, } expect(@analytics).to receive(:track_event).ordered. @@ -154,7 +154,7 @@ success: true, idv_attempts_exceeded: false, errors: {}, - vendor: { messages: [], context: context, exception: nil }, + vendor: { messages: [], context: context, exception: nil, timed_out: false }, } expect(@analytics).to receive(:track_event).ordered. diff --git a/spec/services/idv/agent_spec.rb b/spec/services/idv/agent_spec.rb index 601caeeccc2..e4e15321ffe 100644 --- a/spec/services/idv/agent_spec.rb +++ b/spec/services/idv/agent_spec.rb @@ -70,6 +70,8 @@ proc { |_, r| r.add_message('reason 2') } when :failed proc { |_, r| r.add_message('bah humbug').add_error(:bad, 'stuff') } + when :timed_out + proc { |_, r| r.instance_variable_set(:@exception, Proofer::TimeoutError.new) } end Class.new(Proofer::Base) do required_attributes(:foo) @@ -86,7 +88,8 @@ errors: {}, messages: [resolution_message, state_id_message], success: true, - exception: nil + exception: nil, + timed_out: false ) end end @@ -99,7 +102,19 @@ errors: { bad: ['stuff'] }, messages: [failed_message], success: false, - exception: nil + exception: nil, + timed_out: false + ) + end + end + + context 'when the first stage times out' do + let(:stages) { %i[timed_out state_id] } + + it 'returns a result where timed out is true' do + expect(subject.to_h).to include( + success: false, + timed_out: true ) end end diff --git a/spec/services/idv/phone_step_spec.rb b/spec/services/idv/phone_step_spec.rb index be8ee29fac9..4d3e49e6e93 100644 --- a/spec/services/idv/phone_step_spec.rb +++ b/spec/services/idv/phone_step_spec.rb @@ -12,13 +12,14 @@ let(:good_phone) { '2255555000' } let(:bad_phone) { '7035555555' } let(:fail_phone) { '7035555999' } + let(:timeout_phone) { '7035555888' } subject { described_class.new(idv_session: idv_session) } describe '#submit' do it 'succeeds with good params' do context = { stages: [{ address: 'AddressMock' }] } - extra = { vendor: { messages: [], context: context, exception: nil } } + extra = { vendor: { messages: [], context: context, exception: nil, timed_out: false } } result = subject.submit(phone: good_phone) @@ -32,7 +33,7 @@ it 'fails with bad params' do context = { stages: [{ address: 'AddressMock' }] } - extra = { vendor: { messages: [], context: context, exception: nil } } + extra = { vendor: { messages: [], context: context, exception: nil, timed_out: false } } result = subject.submit(phone: bad_phone) @@ -90,6 +91,14 @@ end end + context 'when the vendor raises a timeout exception' do + it 'returns :timeout' do + subject.submit(phone: timeout_phone) + + expect(subject.failure_reason).to eq(:timeout) + end + end + context 'when the vendor raises an exception' do it 'returns :jobfail' do subject.submit(phone: fail_phone) diff --git a/spec/services/idv/profile_step_spec.rb b/spec/services/idv/profile_step_spec.rb index 844a0a8e0b1..ef0af5b7491 100644 --- a/spec/services/idv/profile_step_spec.rb +++ b/spec/services/idv/profile_step_spec.rb @@ -26,7 +26,7 @@ context = { stages: [{ resolution: 'ResolutionMock' }, { state_id: 'StateIdMock' }] } extra = { idv_attempts_exceeded: false, - vendor: { messages: [], context: context, exception: nil }, + vendor: { messages: [], context: context, exception: nil, timed_out: false }, } result = subject.submit(user_attrs) @@ -47,7 +47,7 @@ errors = { ssn: ['Unverified SSN.'] } extra = { idv_attempts_exceeded: false, - vendor: { messages: [], context: context, exception: nil }, + vendor: { messages: [], context: context, exception: nil, timed_out: false }, } result = subject.submit(user_attrs) @@ -85,6 +85,14 @@ end end + context 'when the vendor raises a timeout exception' do + it 'returns :timeout' do + subject.submit(user_attrs.merge(first_name: 'Time')) + + expect(subject.failure_reason).to eq(:timeout) + end + end + context 'when the vendor raises an exception' do it 'returns :jobfail' do subject.submit(user_attrs.merge(first_name: 'Fail')) diff --git a/spec/support/idv_examples/failed_idv_job.rb b/spec/support/idv_examples/failed_idv_job.rb index dece9fe87bb..6bf0074dbfd 100644 --- a/spec/support/idv_examples/failed_idv_job.rb +++ b/spec/support/idv_examples/failed_idv_job.rb @@ -13,10 +13,8 @@ context 'the proofer raises an error' do before do - stub_idv_proofers_to_raise_error_in_background - - fill_out_idv_form_ok if step == :profile - fill_out_phone_form_ok if step == :phone + fill_out_idv_form_error if step == :profile + fill_out_phone_form_error if step == :phone click_idv_continue end @@ -28,14 +26,37 @@ end end - def stub_idv_proofers_to_raise_error_in_background - proofer = instance_double(ResolutionMock) - allow(ResolutionMock).to receive(:new).and_return(proofer) - allow(AddressMock).to receive(:new).and_return(proofer) - allow(proofer).to receive(:class).and_return(ResolutionMock) + context 'the proofer times out' do + before do + fill_out_idv_form_timeout if step == :profile + fill_out_phone_form_timeout if step == :phone + click_idv_continue + end + + it 'renders a timeout failure screen' do + expect(page).to have_current_path(session_failure_path(:timeout)) if step == :profile + expect(page).to have_current_path(phone_failure_path(:timeout)) if step == :phone + expect(page).to have_content t("idv.failure.#{step_locale_key}.heading") + expect(page).to have_content t("idv.failure.#{step_locale_key}.timeout") + end + end + + def fill_out_idv_form_error + fill_out_idv_form_ok + fill_in 'profile_first_name', with: 'Fail' + end + + def fill_out_phone_form_error + fill_in :idv_phone_form_phone, with: '7035555999' + end + + def fill_out_idv_form_timeout + fill_out_idv_form_ok + fill_in 'profile_first_name', with: 'Time' + end - result = Proofer::Result.new(exception: RuntimeError.new('this is a test error')) - allow(proofer).to receive(:proof).and_return(result) + def fill_out_phone_form_timeout + fill_in :idv_phone_form_phone, with: '7035555888' end def session_failure_path(reason)