From f758ecb77d91792a2b9a6a57802fdad2163125f9 Mon Sep 17 00:00:00 2001 From: Zach Margolis Date: Mon, 3 Jul 2017 13:08:59 -0400 Subject: [PATCH] Extract a synchronous proofing job **Why**: One last small refactor step before making the job async --- .reek | 1 + .rubocop.yml | 3 + app/controllers/concerns/idv_session.rb | 4 ++ app/controllers/verify/finance_controller.rb | 11 +++- app/controllers/verify/phone_controller.rb | 11 +++- app/controllers/verify/sessions_controller.rb | 11 +++- app/jobs/vendor_validator_job.rb | 37 +++++++++++ app/services/idv/financials_step.rb | 4 -- app/services/idv/phone_step.rb | 4 -- app/services/idv/profile_step.rb | 4 -- app/services/idv/session.rb | 1 + app/services/idv/step.rb | 35 +--------- app/services/idv/vendor_result.rb | 3 +- app/services/submit_idv_job.rb | 32 +++++++++ .../vendor_validator_result_storage.rb | 24 +++++++ config/sidekiq.yml | 1 + spec/jobs/vendor_validator_job_spec.rb | 44 +++++++++++++ spec/services/idv/financials_step_spec.rb | 20 ++++-- spec/services/idv/phone_step_spec.rb | 22 +++++-- spec/services/idv/profile_step_spec.rb | 65 +++++++++++++------ spec/services/submit_idv_job_spec.rb | 53 +++++++++++++++ .../vendor_validator_result_storage_spec.rb | 51 +++++++++++++++ 22 files changed, 363 insertions(+), 78 deletions(-) create mode 100644 app/jobs/vendor_validator_job.rb create mode 100644 app/services/submit_idv_job.rb create mode 100644 app/services/vendor_validator_result_storage.rb create mode 100644 spec/jobs/vendor_validator_job_spec.rb create mode 100644 spec/services/submit_idv_job_spec.rb create mode 100644 spec/services/vendor_validator_result_storage_spec.rb diff --git a/.reek b/.reek index 3b4a3620b61..4eee4bbb042 100644 --- a/.reek +++ b/.reek @@ -42,6 +42,7 @@ NilCheck: LongParameterList: exclude: - IdentityLinker#optional_attributes + - VendorValidatorJob#perform RepeatedConditional: exclude: - Users::ResetPasswordsController diff --git a/.rubocop.yml b/.rubocop.yml index b2afe411f82..a8a5847908d 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -93,6 +93,9 @@ Metrics/ModuleLength: - spec/**/* - 'app/controllers/concerns/two_factor_authenticatable.rb' +Metrics/ParameterLists: + CountKeywordArgs: false + # This is a Rails 5 feature, so it should be disabled until we upgrade Rails/HttpPositionalArguments: Description: 'Use keyword arguments instead of positional arguments in http method calls.' diff --git a/app/controllers/concerns/idv_session.rb b/app/controllers/concerns/idv_session.rb index 819eff862ec..eee58296772 100644 --- a/app/controllers/concerns/idv_session.rb +++ b/app/controllers/concerns/idv_session.rb @@ -40,4 +40,8 @@ def idv_vendor def idv_attempter @_idv_attempter ||= Idv::Attempter.new(current_user) end + + def vendor_validator_result + VendorValidatorResultStorage.new.load(idv_session.async_result_id) + end end diff --git a/app/controllers/verify/finance_controller.rb b/app/controllers/verify/finance_controller.rb index 7150a79cf1d..258a96be46a 100644 --- a/app/controllers/verify/finance_controller.rb +++ b/app/controllers/verify/finance_controller.rb @@ -6,6 +6,7 @@ class FinanceController < ApplicationController before_action :confirm_step_needed before_action :confirm_step_allowed before_action :submit_idv_form, only: [:create] + before_action :submit_idv_job, only: [:create] def new @view_model = view_model @@ -37,6 +38,14 @@ def submit_idv_form render_form end + def submit_idv_job + SubmitIdvJob.new( + vendor_validator_class: Idv::FinancialsValidator, + idv_session: idv_session, + vendor_params: vendor_params + ).call + end + def step_name :financials end @@ -66,7 +75,7 @@ def step @_step ||= Idv::FinancialsStep.new( idv_form_params: idv_form.idv_params, idv_session: idv_session, - vendor_params: vendor_params + vendor_validator_result: vendor_validator_result ) end diff --git a/app/controllers/verify/phone_controller.rb b/app/controllers/verify/phone_controller.rb index 2a0a0999cbd..1f498df83e5 100644 --- a/app/controllers/verify/phone_controller.rb +++ b/app/controllers/verify/phone_controller.rb @@ -6,6 +6,7 @@ class PhoneController < ApplicationController before_action :confirm_step_needed before_action :confirm_step_allowed before_action :submit_idv_form, only: [:create] + before_action :submit_idv_job, only: [:create] def new @view_model = view_model @@ -37,6 +38,14 @@ def submit_idv_form render :new end + def submit_idv_job + SubmitIdvJob.new( + vendor_validator_class: Idv::PhoneValidator, + idv_session: idv_session, + vendor_params: idv_form.phone + ).call + end + def step_name :phone end @@ -45,7 +54,7 @@ def step @_step ||= Idv::PhoneStep.new( idv_session: idv_session, idv_form_params: idv_form.idv_params, - vendor_params: idv_form.phone + vendor_validator_result: vendor_validator_result ) end diff --git a/app/controllers/verify/sessions_controller.rb b/app/controllers/verify/sessions_controller.rb index 3be7548c016..191507de2dc 100644 --- a/app/controllers/verify/sessions_controller.rb +++ b/app/controllers/verify/sessions_controller.rb @@ -9,6 +9,7 @@ class SessionsController < ApplicationController before_action :confirm_step_needed, except: [:destroy] before_action :initialize_idv_session, only: [:create] before_action :submit_idv_form, only: [:create] + before_action :submit_idv_job, only: [:create] delegate :attempts_exceeded?, to: :step, prefix: true @@ -44,6 +45,14 @@ def submit_idv_form process_failure unless result.success? end + def submit_idv_job + SubmitIdvJob.new( + vendor_validator_class: Idv::ProfileValidator, + idv_session: idv_session, + vendor_params: idv_session.vendor_params + ).call + end + def step_name :sessions end @@ -56,7 +65,7 @@ def step @_step ||= Idv::ProfileStep.new( idv_form_params: profile_params, idv_session: idv_session, - vendor_params: idv_session.vendor_params + vendor_validator_result: vendor_validator_result ) end diff --git a/app/jobs/vendor_validator_job.rb b/app/jobs/vendor_validator_job.rb new file mode 100644 index 00000000000..b78784082f8 --- /dev/null +++ b/app/jobs/vendor_validator_job.rb @@ -0,0 +1,37 @@ +class VendorValidatorJob < ActiveJob::Base + queue_as :idv + + def perform(result_id:, vendor_validator_class:, vendor:, vendor_params:, applicant_json:, + vendor_session_id:) + vendor_validator = vendor_validator_class.constantize.new( + applicant: Proofer::Applicant.new(JSON.parse(applicant_json, symbolize_names: true)), + vendor: vendor, + vendor_params: indifferent_access(vendor_params), + vendor_session_id: vendor_session_id + ) + + VendorValidatorResultStorage.new.store( + result_id: result_id, + result: extract_result(vendor_validator.result) + ) + end + + private + + def extract_result(result) + vendor_resp = result.vendor_resp + + Idv::VendorResult.new( + success: result.success?, + errors: result.errors, + reasons: vendor_resp.reasons, + normalized_applicant: vendor_resp.try(:normalized_applicant), + session_id: result.try(:session_id) + ) + end + + def indifferent_access(params) + return params if params.is_a?(String) + params.with_indifferent_access + end +end diff --git a/app/services/idv/financials_step.rb b/app/services/idv/financials_step.rb index e3deec1b95b..0979372b0b4 100644 --- a/app/services/idv/financials_step.rb +++ b/app/services/idv/financials_step.rb @@ -20,9 +20,5 @@ def submit def complete? vendor_validation_passed? end - - def vendor_validator_class - Idv::FinancialsValidator - end end end diff --git a/app/services/idv/phone_step.rb b/app/services/idv/phone_step.rb index 4345e56b8ed..49be5cfdd04 100644 --- a/app/services/idv/phone_step.rb +++ b/app/services/idv/phone_step.rb @@ -16,10 +16,6 @@ def complete? vendor_validation_passed? end - def vendor_validator_class - Idv::PhoneValidator - end - def update_idv_session idv_session.phone_confirmation = true idv_session.address_verification_mechanism = :phone diff --git a/app/services/idv/profile_step.rb b/app/services/idv/profile_step.rb index cccbc0be87c..371e6d2a71a 100644 --- a/app/services/idv/profile_step.rb +++ b/app/services/idv/profile_step.rb @@ -29,10 +29,6 @@ def increment_attempts_count attempter.increment end - def vendor_validator_class - Idv::ProfileValidator - end - def update_idv_session idv_session.profile_confirmation = true idv_session.vendor_session_id = vendor_validator_result.session_id diff --git a/app/services/idv/session.rb b/app/services/idv/session.rb index a0baa6894a9..2467d7a7d03 100644 --- a/app/services/idv/session.rb +++ b/app/services/idv/session.rb @@ -1,6 +1,7 @@ module Idv class Session VALID_SESSION_ATTRIBUTES = %i[ + async_result_id address_verification_mechanism applicant financials_confirmation diff --git a/app/services/idv/step.rb b/app/services/idv/step.rb index ad5c3d11b42..355fde1cf3c 100644 --- a/app/services/idv/step.rb +++ b/app/services/idv/step.rb @@ -1,10 +1,10 @@ # abstract base class for Idv Steps module Idv class Step - def initialize(idv_session:, idv_form_params:, vendor_params:) + def initialize(idv_session:, idv_form_params:, vendor_validator_result:) @idv_form_params = idv_form_params @idv_session = idv_session - @vendor_params = vendor_params + @vendor_validator_result = vendor_validator_result end def vendor_validation_passed? @@ -14,23 +14,7 @@ def vendor_validation_passed? private attr_accessor :idv_session - attr_reader :idv_form_params, :vendor_params - - def vendor_validator_result - @_vendor_validator_result ||= extract_vendor_result(vendor_validator.result) - end - - def extract_vendor_result(result) - vendor_resp = result.vendor_resp - - Idv::VendorResult.new( - success: result.success?, - errors: result.errors, - reasons: vendor_resp.reasons, - normalized_applicant: vendor_resp.try(:normalized_applicant), - session_id: result.try(:session_id) - ) - end + attr_reader :idv_form_params, :vendor_validator_result def errors @_errors ||= begin @@ -39,18 +23,5 @@ def errors end end end - - def idv_vendor - @_idv_vendor ||= Idv::Vendor.new - end - - def vendor_validator - @_vendor_validator ||= vendor_validator_class.new( - applicant: idv_session.applicant, - vendor: (idv_session.vendor || idv_vendor.pick), - vendor_params: vendor_params, - vendor_session_id: idv_session.vendor_session_id - ) - end end end diff --git a/app/services/idv/vendor_result.rb b/app/services/idv/vendor_result.rb index 87db473181d..7f01ee62b90 100644 --- a/app/services/idv/vendor_result.rb +++ b/app/services/idv/vendor_result.rb @@ -11,7 +11,8 @@ def self.new_from_json(json) new(**parsed) end - def initialize(success:, errors:, reasons:, session_id:, normalized_applicant:) + def initialize(success: nil, errors: nil, reasons: nil, session_id: nil, + normalized_applicant: nil) @success = success @errors = errors @reasons = reasons diff --git a/app/services/submit_idv_job.rb b/app/services/submit_idv_job.rb new file mode 100644 index 00000000000..a74dfba5df0 --- /dev/null +++ b/app/services/submit_idv_job.rb @@ -0,0 +1,32 @@ +class SubmitIdvJob + def initialize(vendor_validator_class:, idv_session:, vendor_params:) + @vendor_validator_class = vendor_validator_class + @idv_session = idv_session + @vendor_params = vendor_params + end + + def call + idv_session.async_result_id = result_id + + VendorValidatorJob.perform_now( + result_id: result_id, + vendor_validator_class: vendor_validator_class.to_s, + vendor: vendor, + vendor_params: vendor_params, + vendor_session_id: idv_session.vendor_session_id, + applicant_json: idv_session.applicant.to_json + ) + end + + private + + attr_reader :vendor_validator_class, :idv_session, :vendor_params + + def result_id + @_result_id ||= SecureRandom.uuid + end + + def vendor + idv_session.vendor || Idv::Vendor.new.pick + end +end diff --git a/app/services/vendor_validator_result_storage.rb b/app/services/vendor_validator_result_storage.rb new file mode 100644 index 00000000000..098ba2422ff --- /dev/null +++ b/app/services/vendor_validator_result_storage.rb @@ -0,0 +1,24 @@ +class VendorValidatorResultStorage + TTL = Figaro.env.session_timeout_in_minutes.to_i.minutes.seconds.to_i + + def store(result_id:, result:) + Sidekiq.redis do |redis| + redis.setex(redis_key(result_id), TTL, result.to_json) + end + end + + def load(result_id) + result_json = Sidekiq.redis do |redis| + redis.get(redis_key(result_id)) + end + + return unless result_json + + Idv::VendorResult.new_from_json(result_json) + end + + # @api private + def redis_key(result_id) + "vendor-validator-result-#{result_id}" + end +end diff --git a/config/sidekiq.yml b/config/sidekiq.yml index 8a0466b9b10..f72f4153bcd 100644 --- a/config/sidekiq.yml +++ b/config/sidekiq.yml @@ -3,4 +3,5 @@ - voice - mailers - analytics + - idv :logfile: 'log/sidekiq.log' diff --git a/spec/jobs/vendor_validator_job_spec.rb b/spec/jobs/vendor_validator_job_spec.rb new file mode 100644 index 00000000000..d10bd5f4533 --- /dev/null +++ b/spec/jobs/vendor_validator_job_spec.rb @@ -0,0 +1,44 @@ +require 'rails_helper' + +RSpec.describe VendorValidatorJob do + let(:result_id) { SecureRandom.uuid } + let(:vendor_validator_class) { 'Idv::PhoneValidator' } + let(:vendor) { :mock } + let(:vendor_params) { '+1 (888) 123-4567' } + let(:applicant) { Proofer::Applicant.new(first_name: 'Test') } + let(:applicant_json) { applicant.to_json } + let(:vendor_session_id) { SecureRandom.uuid } + + subject(:job) { VendorValidatorJob.new } + + describe '#perform' do + subject(:perform) do + job.perform( + result_id: result_id, + vendor_validator_class: vendor_validator_class, + vendor: vendor, + vendor_params: vendor_params, + applicant_json: applicant_json, + vendor_session_id: vendor_session_id + ) + end + + it 'calls out to a vendor and serializes the result' do + expect(Idv::PhoneValidator).to receive(:new). + with( + applicant: kind_of(Proofer::Applicant), + vendor: vendor, + vendor_params: vendor_params, + vendor_session_id: vendor_session_id + ).and_call_original + + before_result = VendorValidatorResultStorage.new.load(result_id) + expect(before_result).to be_nil + + perform + + after_result = VendorValidatorResultStorage.new.load(result_id) + expect(after_result).to be_a(Idv::VendorResult) + end + end +end diff --git a/spec/services/idv/financials_step_spec.rb b/spec/services/idv/financials_step_spec.rb index b20fbd952fc..1445ab394f7 100644 --- a/spec/services/idv/financials_step_spec.rb +++ b/spec/services/idv/financials_step_spec.rb @@ -9,17 +9,22 @@ end let(:idv_form_params) { idv_session.params } - def build_step(vendor_params) + def build_step(vendor_validator_result) described_class.new( idv_form_params: idv_form_params, idv_session: idv_session, - vendor_params: vendor_params + vendor_validator_result: vendor_validator_result ) end describe '#submit' do it 'returns FormResponse with success: true for mock-happy CCN' do - step = build_step(ccn: '12345678') + step = build_step( + Idv::VendorResult.new( + success: true, + errors: {} + ) + ) result = step.submit expect(result).to be_kind_of(FormResponse) @@ -31,10 +36,15 @@ def build_step(vendor_params) end it 'returns FormResponse with success: false for mock-sad CCN' do - step = build_step(ccn: '00000000') - errors = { ccn: ['The ccn could not be verified.'] } + step = build_step( + Idv::VendorResult.new( + success: false, + errors: errors + ) + ) + result = step.submit expect(result).to be_kind_of(FormResponse) expect(result.success?).to eq(false) diff --git a/spec/services/idv/phone_step_spec.rb b/spec/services/idv/phone_step_spec.rb index 7dacb2ace6c..4de8223d203 100644 --- a/spec/services/idv/phone_step_spec.rb +++ b/spec/services/idv/phone_step_spec.rb @@ -12,17 +12,23 @@ end let(:idv_phone_form) { Idv::PhoneForm.new(idv_session.params, user) } - def build_step(phone) + def build_step(phone, vendor_validator_result) described_class.new( idv_session: idv_session, idv_form_params: { phone: phone }, - vendor_params: phone + vendor_validator_result: vendor_validator_result ) end describe '#submit' do it 'returns true for mock-happy phone' do - step = build_step('555-555-0000') + step = build_step( + '555-555-0000', + Idv::VendorResult.new( + success: true, + errors: {} + ) + ) result = step.submit @@ -34,10 +40,16 @@ def build_step(phone) end it 'returns false for mock-sad phone' do - step = build_step('555-555-5555') - errors = { phone: ['The phone number could not be verified.'] } + step = build_step( + '555-555-5555', + Idv::VendorResult.new( + success: false, + errors: errors + ) + ) + result = step.submit expect(result).to be_kind_of(FormResponse) diff --git a/spec/services/idv/profile_step_spec.rb b/spec/services/idv/profile_step_spec.rb index 3906d020b1d..cc7655d0599 100644 --- a/spec/services/idv/profile_step_spec.rb +++ b/spec/services/idv/profile_step_spec.rb @@ -18,26 +18,35 @@ } end - def build_step(params) + def build_step(params, vendor_validator_result) idv_session.params.merge!(params) idv_session.applicant = idv_session.vendor_params described_class.new( idv_form_params: params, - vendor_params: idv_session.vendor_params, + vendor_validator_result: vendor_validator_result, idv_session: idv_session ) end describe '#submit' do it 'succeeds with good params' do - step = build_step(user_attrs) - + reasons = ['Everything looks good'] extra = { idv_attempts_exceeded: false, - vendor: { reasons: ['Everything looks good'] }, + vendor: { reasons: reasons }, } + step = build_step( + user_attrs, + Idv::VendorResult.new( + success: true, + errors: {}, + reasons: reasons, + normalized_applicant: Proofer::Applicant.new(first_name: 'Some') + ) + ) + result = step.submit expect(result).to be_kind_of(FormResponse) @@ -48,14 +57,18 @@ def build_step(params) end it 'fails with invalid SSN' do - step = build_step(user_attrs.merge(ssn: '666-66-6666')) - + reasons = ['The SSN was suspicious'] errors = { ssn: ['Unverified SSN.'] } extra = { idv_attempts_exceeded: false, - vendor: { reasons: ['The SSN was suspicious'] }, + vendor: { reasons: reasons }, } + step = build_step( + user_attrs.merge(ssn: '666-66-6666'), + Idv::VendorResult.new(success: false, errors: errors, reasons: reasons) + ) + result = step.submit expect(result).to be_kind_of(FormResponse) @@ -66,14 +79,18 @@ def build_step(params) end it 'fails with invalid first name' do - step = build_step(user_attrs.merge(first_name: 'Bad')) - errors = { first_name: ['Unverified first name.'] } + reasons = ['The name was suspicious'] extra = { idv_attempts_exceeded: false, - vendor: { reasons: ['The name was suspicious'] }, + vendor: { reasons: reasons }, } + step = build_step( + user_attrs.merge(first_name: 'Bad'), + Idv::VendorResult.new(success: false, errors: errors, reasons: reasons) + ) + result = step.submit expect(result).to be_kind_of(FormResponse) @@ -84,14 +101,18 @@ def build_step(params) end it 'fails with invalid ZIP code on current address' do - step = build_step(user_attrs.merge(zipcode: '00000')) - + reasons = ['The ZIP code was suspicious'] errors = { zipcode: ['Unverified ZIP code.'] } extra = { idv_attempts_exceeded: false, - vendor: { reasons: ['The ZIP code was suspicious'] }, + vendor: { reasons: reasons }, } + step = build_step( + user_attrs.merge(zipcode: '00000'), + Idv::VendorResult.new(success: false, errors: errors, reasons: reasons) + ) + result = step.submit expect(result).to be_kind_of(FormResponse) @@ -102,14 +123,18 @@ def build_step(params) end it 'fails with invalid ZIP code on previous address' do - step = build_step(user_attrs.merge(prev_zipcode: '00000')) - + reasons = ['The ZIP code was suspicious'] errors = { zipcode: ['Unverified ZIP code.'] } extra = { idv_attempts_exceeded: false, - vendor: { reasons: ['The ZIP code was suspicious'] }, + vendor: { reasons: reasons }, } + step = build_step( + user_attrs.merge(prev_zipcode: '00000'), + Idv::VendorResult.new(success: false, errors: errors, reasons: reasons) + ) + result = step.submit expect(result).to be_kind_of(FormResponse) @@ -120,12 +145,12 @@ def build_step(params) end it 'increments attempts count' do - step = build_step(user_attrs) + step = build_step(user_attrs, Idv::VendorResult.new(errors: {})) expect { step.submit }.to change(user, :idv_attempts).by(1) end it 'initializes the idv_session' do - step = build_step(user_attrs) + step = build_step(user_attrs, Idv::VendorResult.new(errors: {})) step.submit expect(idv_session.params).to eq user_attrs @@ -139,7 +164,7 @@ def build_step(params) allow(Idv::Attempter).to receive(:new).with(user).and_return(attempter) allow(attempter).to receive(:exceeded?) - step = build_step(user_attrs) + step = build_step(user_attrs, Idv::VendorResult.new(errors: {})) expect(step.attempts_exceeded?).to eq attempter.exceeded? end end diff --git a/spec/services/submit_idv_job_spec.rb b/spec/services/submit_idv_job_spec.rb new file mode 100644 index 00000000000..ba802f21269 --- /dev/null +++ b/spec/services/submit_idv_job_spec.rb @@ -0,0 +1,53 @@ +require 'rails_helper' + +RSpec.describe SubmitIdvJob do + subject(:service) do + SubmitIdvJob.new( + vendor_validator_class: vendor_validator_class, + idv_session: idv_session, + vendor_params: vendor_params + ) + end + + let(:idv_session) do + Idv::Session.new( + current_user: user, + issuer: nil, + user_session: { + idv: { + applicant: applicant, + vendor_session_id: vendor_session_id, + vendor: :mock, + }, + } + ) + end + + let(:user) { build(:user) } + let(:applicant) { Proofer::Applicant.new(first_name: 'Greatest') } + let(:vendor_session_id) { '12345' } + let(:result_id) { 'abcdef' } + let(:vendor_params) { '+1 (888) 123-4567' } + let(:vendor_validator_class) { 'Idv::PhoneValidator' } + + describe '#call' do + subject(:call) { service.call } + + it 'generates a UUID and enqueues a job, and saves the UUID in the session' do + expect(SecureRandom).to receive(:uuid).and_return(result_id).once + + expect(VendorValidatorJob).to receive(:perform_now). + with( + result_id: result_id, + vendor_validator_class: vendor_validator_class, + vendor: :mock, + vendor_params: vendor_params, + vendor_session_id: vendor_session_id, + applicant_json: applicant.to_json + ) + + expect { call }. + to change { idv_session.async_result_id }.from(nil).to(result_id) + end + end +end diff --git a/spec/services/vendor_validator_result_storage_spec.rb b/spec/services/vendor_validator_result_storage_spec.rb new file mode 100644 index 00000000000..6cd36a9270f --- /dev/null +++ b/spec/services/vendor_validator_result_storage_spec.rb @@ -0,0 +1,51 @@ +require 'rails_helper' + +RSpec.describe VendorValidatorResultStorage do + subject(:service) { VendorValidatorResultStorage.new } + + let(:session_id) { SecureRandom.uuid } + let(:result_id) { SecureRandom.uuid } + let(:original_result) do + Idv::VendorResult.new( + success: false, + session_id: session_id, + normalized_applicant: Proofer::Applicant.new(first_name: 'First') + ) + end + + describe '#store_result' do + it 'stores the result in redis with a TTL' do + key = service.redis_key(result_id) + + before_redis = Sidekiq.redis { |redis| redis.get(key) } + expect(before_redis).to be_nil + + service.store(result_id: result_id, result: original_result) + + Sidekiq.redis do |redis| + expect(redis.get(key)).to be_present + expect(redis.ttl(key)).to be_within(1).of(VendorValidatorResultStorage::TTL) + end + end + end + + describe '#vendor_validator_result' do + before { service.store(result_id: result_id, result: original_result) } + + it 'retrieves a stored result' do + result = service.load(result_id) + + expect(result.success?).to eq(original_result.success?) + expect(result.errors).to eq(original_result.errors) + expect(result.reasons).to eq(original_result.reasons) + expect(result.normalized_applicant.as_json). + to eq(original_result.normalized_applicant.as_json) + end + + it 'is nil with a bad result id' do + result = service.load(SecureRandom.uuid) + + expect(result).to be_nil + end + end +end