<%= button_to @presenter.button,
diff --git a/app/views/idv/review/new.html.erb b/app/views/idv/review/new.html.erb
index 192a21354dc..53a36869575 100644
--- a/app/views/idv/review/new.html.erb
+++ b/app/views/idv/review/new.html.erb
@@ -3,7 +3,7 @@
<% content_for(:pre_flash_content) do %>
<%= render StepIndicatorComponent.new(
steps: step_indicator_steps,
- current_step: :secure_account,
+ current_step: step_indicator_step,
locale_scope: 'idv',
class: 'margin-x-neg-2 margin-top-neg-4 tablet:margin-x-neg-6 tablet:margin-top-neg-4',
) %>
@@ -12,14 +12,9 @@
<%= render PageHeadingComponent.new.with_content(t('idv.titles.session.review', app_name: APP_NAME)) %>
- <%= t('idv.messages.sessions.review_message', app_name: APP_NAME) %>
+ <%= t('idv.messages.review.message', app_name: APP_NAME) %>
-<%= new_tab_link_to(
- t('idv.messages.sessions.read_more_encrypt', app_name: APP_NAME),
- MarketingSite.security_url,
- ) %>
-
<%= simple_form_for(
current_user,
url: idv_review_path,
@@ -35,14 +30,9 @@
},
},
) %>
-
+
<%= link_to(t('idv.forgot_password.link_text'), idv_forgot_password_url, class: 'margin-left-1') %>
- <%= render AccordionComponent.new do |c| %>
- <% c.with_header { t('idv.messages.review.intro') } %>
- <%= render 'shared/pii_review', pii: @applicant,
- phone: PhoneFormatter.format(@applicant[:phone]) %>
- <% end %>
<%= f.submit t('forms.buttons.continue'), class: 'margin-top-5' %>
<% end %>
diff --git a/app/views/shared/_pii_review.html.erb b/app/views/shared/_pii_review.html.erb
deleted file mode 100644
index 37c5c82f5a5..00000000000
--- a/app/views/shared/_pii_review.html.erb
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
- <%= t('idv.review.full_name') %>
-
-
-
- <%= pii[:first_name] %> <%= pii[:last_name] %>
-
-
-
- <%= t('idv.review.mailing_address') %>
-
-
-
- <%= render 'shared/address', address: pii %>
-
-
-
- <%= t('idv.review.dob') %>
-
-
-
- <%= DateParser.parse_legacy(pii[:dob]).to_formatted_s(:long) %>
-
-
-
- <%= t('idv.review.ssn') %>
-
-
-
- <%= pii[:ssn] %>
-
-
- <% if phone %>
-
- <%= t('idv.messages.phone.phone_of_record') %>
-
-
- <%= PhoneFormatter.format(phone) %>
-
- <% end %>
-
diff --git a/app/views/users/phone_setup/spam_protection.html.erb b/app/views/users/phone_setup/spam_protection.html.erb
index 61d823d06f7..93147fbf77a 100644
--- a/app/views/users/phone_setup/spam_protection.html.erb
+++ b/app/views/users/phone_setup/spam_protection.html.erb
@@ -58,3 +58,9 @@
new_tab: true,
).with_content(t('two_factor_authentication.phone_verification.troubleshooting.learn_more')) %>
<% end %>
+
+<% unless local_assigns[:two_factor_options_path].present? %>
+ <%= render PageFooterComponent.new do %>
+ <%= link_to t('links.cancel'), account_path %>
+ <% end %>
+<% end %>
\ No newline at end of file
diff --git a/app/views/users/verify_password/new.html.erb b/app/views/users/verify_password/new.html.erb
index 4062087225e..a05a0abd77f 100644
--- a/app/views/users/verify_password/new.html.erb
+++ b/app/views/users/verify_password/new.html.erb
@@ -23,10 +23,3 @@
) %>
<%= f.submit t('forms.buttons.continue'), class: 'margin-top-5' %>
<% end %>
-
-
- <%= render AccordionComponent.new do |c| %>
- <% c.with_header { t('idv.messages.review.intro') } %>
- <%= render 'shared/pii_review', pii: @decrypted_pii, phone: @decrypted_pii[:phone] %>
- <% end %>
-
diff --git a/bin/setup b/bin/setup
index e8431e847b2..4a92358cb1d 100755
--- a/bin/setup
+++ b/bin/setup
@@ -62,7 +62,7 @@ Dir.chdir APP_ROOT do
puts "\n== Starting services =="
run "brew services start redis" if brew_installed
- run "brew services start postgresql@13" if brew_installed
+ run "brew services start postgresql@14" if brew_installed
puts "\n== Preparing database =="
run 'make clobber_db'
diff --git a/config/application.yml.default b/config/application.yml.default
index a63c02d1c93..e53c475b156 100644
--- a/config/application.yml.default
+++ b/config/application.yml.default
@@ -355,7 +355,9 @@ usps_ipp_sponsor_id: ''
usps_ipp_username: ''
usps_mock_fallback: true
usps_ipp_transliteration_enabled: false
-get_usps_proofing_results_job_cron: '0/10 * * * *'
+get_usps_ready_proofing_results_job_cron: '0/10 * * * *'
+get_usps_waiting_proofing_results_job_cron: '0/30 * * * *'
+get_usps_proofing_results_job_cron: '0/30 * * * *'
get_usps_proofing_results_job_reprocess_delay_minutes: 5
get_usps_proofing_results_job_request_delay_milliseconds: 1000
voice_otp_pause_time: '0.5s'
diff --git a/config/initializers/job_configurations.rb b/config/initializers/job_configurations.rb
index 675bb6c44ec..b6380ab6fab 100644
--- a/config/initializers/job_configurations.rb
+++ b/config/initializers/job_configurations.rb
@@ -116,6 +116,18 @@
cron: IdentityConfig.store.get_usps_proofing_results_job_cron,
args: -> { [Time.zone.now] },
},
+ # Queue usps proofing job to GoodJob for ready enrollments
+ get_usps_ready_proofing_results_job: {
+ class: 'GetUspsReadyProofingResultsJob',
+ cron: IdentityConfig.store.get_usps_ready_proofing_results_job_cron,
+ args: -> { [Time.zone.now] },
+ },
+ # Queue usps proofing job to GoodJob for waiting enrollments
+ get_usps_waiting_proofing_results_job: {
+ class: 'GetUspsWaitingProofingResultsJob',
+ cron: IdentityConfig.store.get_usps_waiting_proofing_results_job_cron,
+ args: -> { [Time.zone.now] },
+ },
# Queue daily in-person proofing reminder email job
email_reminder_job: {
class: 'InPerson::EmailReminderJob',
diff --git a/config/locales/idv/en.yml b/config/locales/idv/en.yml
index 0ee13be812f..23976eacca7 100644
--- a/config/locales/idv/en.yml
+++ b/config/locales/idv/en.yml
@@ -200,6 +200,10 @@ en:
info_alert: You’ll need to wait until your letter is delivered to finish
verifying your identity.
resend: Send me another letter
+ resend_code_warning: If you request a new letter now, the one-time code from
+ your current letter will remain valid for a limited time. You may
+ still use the one-time code from either letter.
+ resend_timeframe: Letters typically take 3 to 7 business days to arrive.
timeframe_html: Letters are sent the next business day via USPS First Class Mail
and
typically take 3 to 7 business days to arrive.
otp_delivery_method_description: If you entered a landline above, please select “Phone call” below.
@@ -209,13 +213,15 @@ en:
alert_html: '
Enter a phone number that is:'
description: We’ll check this number with records and send you a one-time code.
This is to help verify your identity.
- phone_of_record: Phone of record
rules:
- Based in the United States (including U.S. territories)
- Your primary number (the one you use the most often)
return_to_profile: '‹ Return to your %{app_name} profile'
review:
- intro: Your verified information
+ gpo_pending: We’ll send your letter once you re-enter your password.
+ message: '%{app_name} will encrypt your information with your password. This
+ means that your information is secure and only you will be able to
+ access or change it.'
phone_verified: We verified your phone number
select_verification_with_sp: To protect you from identity fraud, we will contact
you to confirm that this %{sp_name} account is legitimate.
@@ -224,25 +230,19 @@ en:
sessions:
no_pii: TEST SITE - Do not use real personal information (demo purposes only) -
TEST SITE
- read_more_encrypt: Read more about how %{app_name} protects your personal information
review_message: When you re-enter your password, %{app_name} will protect the
information you’ve given us, so that only you can access it.
verifying: Verifying…
- review:
- dob: Date of birth
- full_name: Full name
- mailing_address: Mailing address
- ssn: Social Security number (SSN)
titles:
activated: Your identity has already been verified
come_back_later: Your letter is on the way
mail:
- resend: Want another letter?
+ resend: Send another letter?
verify: Want a letter?
otp_delivery_method: How should we send a code?
review: Review and submit
session:
- review: Re-enter your %{app_name} password to protect your data
+ review: Re-enter your %{app_name} password
unavailable: 'We are working to resolve an error'
troubleshooting:
headings:
diff --git a/config/locales/idv/es.yml b/config/locales/idv/es.yml
index 636f3519321..8de261b44d7 100644
--- a/config/locales/idv/es.yml
+++ b/config/locales/idv/es.yml
@@ -212,6 +212,11 @@ es:
info_alert: Tendrá que esperar a que su carta sea entregada para terminar de
verificar su identidad.
resend: Envíeme otra carta
+ resend_code_warning: Si solicitas una nueva carta ahora, el código de una sola
+ vez de tu carta actual seguirá siendo válido durante un tiempo
+ limitado. Puedes seguir utilizando el código de una sola vez de
+ cualquiera de las dos cartas.
+ resend_timeframe: Las cartas suelen tardar entre 3 y 7 días hábiles en llegar.
timeframe_html: Las cartas se envían al día siguiente por First Class Mail de
USPS y
suelen tardar entre 3 y 7 días hábiles en
llegar.
@@ -223,13 +228,15 @@ es:
alert_html: '
Introduzca un número de teléfono que sea:'
description: Comprobaremos este número con los registros y le enviaremos un
código único. Esto es para ayudar a verificar su identidad.
- phone_of_record: Teléfono del registro
rules:
- Con base en Estados Unidos (incluidos los territorios de EE.UU.)
- Su número principal (el que utiliza con más frecuencia)
return_to_profile: '‹ Volver a tu perfil de %{app_name}'
review:
- intro: Su información verificada
+ gpo_pending: Enviaremos tu carta una vez que hayas ingresado tu contraseña.
+ message: '%{app_name} encriptará tu información con tu contraseña. Esto
+ significa que tu información estará segura y solo tú podrás
+ consultarla o modificarla.'
phone_verified: Verificamos su número de teléfono
select_verification_with_sp: Para protegerlo de robo de identidad, no puede
utilizar su cuenta en %{sp_name} hasta que la active ingresando un
@@ -239,25 +246,19 @@ es:
sessions:
no_pii: SITIO DE PRUEBA - No utilice información personal real (sólo para
propósitos de demostración) - SITIO DE PRUEBA
- read_more_encrypt: Lea más sobre cómo %{app_name} protege su información personal
review_message: Cuando vuelva a ingresar su contraseña, %{app_name} cifrará sus
datos para asegurarse de que nadie más pueda acceder a ellos.
verifying: Verificando…
- review:
- dob: Fecha de nacimiento
- full_name: Nombre completo
- mailing_address: Dirección postal
- ssn: Número de Seguro Social (SSN, sigla en inglés)
titles:
activated: Ya se verificó tu identidad.
come_back_later: Su carta está en camino
mail:
- resend: '¿Desea otra carta?'
+ resend: '¿Enviar otra carta?'
verify: '¿Desea una carta?'
otp_delivery_method: '¿Cómo debemos enviar un código?'
review: Revise y envíe
session:
- review: Vuelve a ingresar tu contraseña de %{app_name} para encriptar tus datos
+ review: 'Vuelve a ingresar tu contraseña de %{app_name}'
unavailable: Estamos trabajando para resolver un error
troubleshooting:
headings:
diff --git a/config/locales/idv/fr.yml b/config/locales/idv/fr.yml
index d32f10c8eb0..32ee3c3b972 100644
--- a/config/locales/idv/fr.yml
+++ b/config/locales/idv/fr.yml
@@ -225,6 +225,12 @@ fr:
info_alert: Vous devrez attendre la livraison de votre lettre pour compléter la
vérification de votre identité.
resend: Envoyez-moi une autre lettre
+ resend_code_warning: Si vous demandez une nouvelle lettre maintenant, le code à
+ usage unique de votre lettre actuelle restera valable pendant une
+ période limitée. Vous pouvez toujours utiliser le code à usage unique
+ de l’une ou l’autre lettre.
+ resend_timeframe: Les lettres mettent généralement entre trois et sept jours
+ ouvrables pour arriver.
timeframe_html: Les lettres sont envoyées les jours ouvrables par courriel de
première classe de USPS et
prennent généralement entre trois à
sept jours ouvrables pour être reçues.
@@ -237,13 +243,16 @@ fr:
alert_html: '
Entrez un numéro de téléphone qui est :'
description: Nous vérifierons ce numéro dans nos archives et vous enverrons un
code à usage unique. Ceci est pour aider à vérifier votre identité.
- phone_of_record: numéro de téléphone enregistré
rules:
- Basé aux Etats-Unis (y compris les territoires américains)
- Votre numéro principal (celui que vous utilisez le plus souvent)
return_to_profile: '‹ Revenir à votre profil %{app_name}'
review:
- intro: Vos informations vérifiées
+ gpo_pending: Nous vous enverrons votre lettre une fois que vous aurez
+ réintroduit votre mot de passe.
+ message: '%{app_name} crypte vos informations avec votre mot de passe. Cela
+ signifie que vos informations sont sécurisées et que vous seul pourrez
+ y accéder ou les modifier.'
phone_verified: Nous avons vérifié votre numéro de téléphone
select_verification_with_sp: Afin de vous protéger des fraudes d’identité, vous
ne pouvez pas utiliser votre compte au %{sp_name} tant que vous ne
@@ -254,26 +263,19 @@ fr:
sessions:
no_pii: SITE DE TEST - N’utilisez pas de véritables données personnelles (il
s’agit d’une démonstration seulement) - SITE DE TEST
- read_more_encrypt: En savoir plus sur la façon dont %{app_name} protège vos
- informations personnelles
review_message: Lorsque vous entrez à nouveau votre mot de passe, %{app_name}
crypte vos données pour vous assurer que personne ne peut y accéder.
verifying: Vérification…
- review:
- dob: Date de naissance
- full_name: Nom complet
- mailing_address: Adresse postale
- ssn: Numéro de sécurité sociale (SSN)
titles:
activated: Votre identité a déjà été vérifiée
come_back_later: Votre lettre est en route
mail:
- resend: Vous voulez une autre lettre?
+ resend: Envoyer une autre lettre?
verify: Vous voulez une lettre?
otp_delivery_method: Comment envoyer un code?
review: Réviser et soumettre
session:
- review: Entrez à nouveau votre mot de passe %{app_name} pour crypter vos données
+ review: 'Saisissez à nouveau votre mot de passe %{app_name}'
unavailable: Nous travaillons à la résolution d’une erreur
troubleshooting:
headings:
diff --git a/db/primary_migrate/20230601195606_add_columns_for_user_suspension.rb b/db/primary_migrate/20230601195606_add_columns_for_user_suspension.rb
new file mode 100644
index 00000000000..8e687d37a07
--- /dev/null
+++ b/db/primary_migrate/20230601195606_add_columns_for_user_suspension.rb
@@ -0,0 +1,6 @@
+class AddColumnsForUserSuspension < ActiveRecord::Migration[7.0]
+ def change
+ add_column :users, :suspended_at, :datetime
+ add_column :users, :reinstated_at, :datetime
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 0135afe0ba2..0443b612784 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[7.0].define(version: 2023_05_03_231037) do
+ActiveRecord::Schema[7.0].define(version: 2023_06_01_195606) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_stat_statements"
enable_extension "pgcrypto"
@@ -590,6 +590,8 @@
t.string "email_language", limit: 10
t.datetime "accepted_terms_at", precision: nil
t.datetime "encrypted_recovery_code_digest_generated_at", precision: nil
+ t.datetime "suspended_at"
+ t.datetime "reinstated_at"
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
t.index ["uuid"], name: "index_users_on_uuid", unique: true
end
diff --git a/lib/identity_config.rb b/lib/identity_config.rb
index 583f5888d03..ca7e8011ed9 100644
--- a/lib/identity_config.rb
+++ b/lib/identity_config.rb
@@ -450,6 +450,8 @@ def self.build_store(config_map)
config.add(:usps_ipp_request_timeout, type: :integer)
config.add(:usps_upload_enabled, type: :boolean)
config.add(:usps_ipp_transliteration_enabled, type: :boolean)
+ config.add(:get_usps_ready_proofing_results_job_cron, type: :string)
+ config.add(:get_usps_waiting_proofing_results_job_cron, type: :string)
config.add(:get_usps_proofing_results_job_cron, type: :string)
config.add(:get_usps_proofing_results_job_reprocess_delay_minutes, type: :integer)
config.add(:get_usps_proofing_results_job_request_delay_milliseconds, type: :integer)
diff --git a/lib/telephony/alert_sender.rb b/lib/telephony/alert_sender.rb
index 7686fc248f3..a8e08bd2983 100644
--- a/lib/telephony/alert_sender.rb
+++ b/lib/telephony/alert_sender.rb
@@ -40,7 +40,7 @@ def send_personal_key_regeneration_notice(to:, country_code:)
end
def send_personal_key_sign_in_notice(to:, country_code:)
- message = I18n.t('telephony.personal_key_sign_in_notice')
+ message = I18n.t('telephony.personal_key_sign_in_notice', app_name: APP_NAME)
response = adapter.send(message: message, to: to, country_code: country_code)
log_response(response, context: __method__.to_s.gsub(/^send_/, ''))
response
diff --git a/spec/components/memorable_date_component_spec.rb b/spec/components/memorable_date_component_spec.rb
index 164ff54991d..02bb5e2f4dd 100644
--- a/spec/components/memorable_date_component_spec.rb
+++ b/spec/components/memorable_date_component_spec.rb
@@ -278,4 +278,17 @@
MemorableDateComponent.extract_date_param('abcd'),
).to be_nil
end
+
+ context 'backend validation error message' do
+ let(:backend_error) { 'backend error' }
+ it 'renders a visible error message element' do
+ allow(form_builder.object).to receive(:errors).and_return(
+ {
+ name => [backend_error],
+ },
+ )
+ expect(rendered).not_to have_css('.usa-error-message.display-none')
+ expect(rendered.css('.usa-error-message')).to have_text(backend_error)
+ end
+ end
end
diff --git a/spec/controllers/concerns/rate_limit_concern_spec.rb b/spec/controllers/concerns/rate_limit_concern_spec.rb
index 848d2904944..d49a1bef4f2 100644
--- a/spec/controllers/concerns/rate_limit_concern_spec.rb
+++ b/spec/controllers/concerns/rate_limit_concern_spec.rb
@@ -6,6 +6,7 @@
module Idv
class StepController < ApplicationController
include RateLimitConcern
+ include IdvSession
def show
render plain: 'Hello'
diff --git a/spec/controllers/idv/document_capture_controller_spec.rb b/spec/controllers/idv/document_capture_controller_spec.rb
index 7224ba5d670..1d7b7d5eebb 100644
--- a/spec/controllers/idv/document_capture_controller_spec.rb
+++ b/spec/controllers/idv/document_capture_controller_spec.rb
@@ -6,8 +6,7 @@
let(:flow_session) do
{ 'document_capture_session_uuid' => 'fd14e181-6fb1-4cdc-92e0-ef66dad0df4e',
:threatmetrix_session_id => 'c90ae7a5-6629-4e77-b97c-f1987c2df7d0',
- :flow_path => 'standard',
- 'Idv::Steps::UploadStep' => true }
+ :flow_path => 'standard' }
end
let(:user) { create(:user) }
@@ -19,9 +18,6 @@
)
end
- let(:default_sdk_version) { IdentityConfig.store.idv_acuant_sdk_version_default }
- let(:alternate_sdk_version) { IdentityConfig.store.idv_acuant_sdk_version_alternate }
-
before do
allow(subject).to receive(:flow_session).and_return(flow_session)
stub_sign_in(user)
@@ -102,6 +98,13 @@
end
end
+ it 'does not use effective user outside of analytics_user in ApplicationControler' do
+ allow(subject).to receive(:analytics_user).and_return(subject.current_user)
+ expect(subject).not_to receive(:effective_user)
+
+ get :show
+ end
+
context 'user is rate_limited' do
it 'redirects to rate limited page' do
user = create(:user)
diff --git a/spec/controllers/idv/hybrid_handoff_controller_spec.rb b/spec/controllers/idv/hybrid_handoff_controller_spec.rb
index 62fa39f37d2..19e8b525529 100644
--- a/spec/controllers/idv/hybrid_handoff_controller_spec.rb
+++ b/spec/controllers/idv/hybrid_handoff_controller_spec.rb
@@ -28,13 +28,19 @@
:confirm_agreement_step_complete,
)
end
+
+ it 'checks that hybrid_handoff is needed' do
+ expect(subject).to have_actions(
+ :before,
+ :confirm_hybrid_handoff_needed,
+ )
+ end
end
describe '#show' do
let(:analytics_name) { 'IdV: doc auth upload visited' }
let(:analytics_args) do
- { flow_path: 'standard',
- step: 'upload',
+ { step: 'upload',
analytics_id: 'Doc Auth',
irs_reproofing: false }
end
@@ -68,25 +74,68 @@
expect(response).to redirect_to(idv_doc_auth_url)
end
end
+
+ context 'hybrid_handoff already visited' do
+ it 'redirects to document_capture in standard flow' do
+ subject.user_session['idv/doc_auth'][:flow_path] = 'standard'
+
+ get :show
+
+ expect(response).to redirect_to(idv_document_capture_url)
+ end
+
+ it 'redirects to link_sent in hybrid flow' do
+ subject.user_session['idv/doc_auth'][:flow_path] = 'hybrid'
+
+ get :show
+
+ expect(response).to redirect_to(idv_link_sent_url)
+ end
+ end
end
describe '#update' do
let(:analytics_name) { 'IdV: doc auth upload submitted' }
- let(:analytics_args) do
- { success: true,
- errors: {},
- destination: :link_sent,
- flow_path: 'hybrid',
- step: 'upload',
- analytics_id: 'Doc Auth',
- irs_reproofing: false,
- skip_upload_step: false }
+
+ context 'hybrid flow' do
+ let(:analytics_args) do
+ { success: true,
+ errors: { message: nil },
+ destination: :link_sent,
+ flow_path: 'hybrid',
+ step: 'upload',
+ analytics_id: 'Doc Auth',
+ irs_reproofing: false,
+ telephony_response: { errors: {},
+ message_id: 'fake-message-id',
+ request_id: 'fake-message-request-id',
+ success: true } }
+ end
+
+ it 'sends analytics_submitted event for hybrid' do
+ put :update, params: { doc_auth: { phone: '202-555-5555' } }
+
+ expect(@analytics).to have_logged_event(analytics_name, analytics_args)
+ end
end
- it 'sends analytics_submitted event' do
- put :update, params: { doc_auth: { phone: '202-555-5555' } }
+ context 'desktop flow' do
+ let(:analytics_args) do
+ { success: true,
+ errors: {},
+ destination: :document_capture,
+ flow_path: 'standard',
+ step: 'upload',
+ analytics_id: 'Doc Auth',
+ irs_reproofing: false,
+ skip_upload_step: false }
+ end
+
+ it 'sends analytics_submitted event for desktop' do
+ put :update, params: { type: 'desktop' }
- expect(@analytics).to have_logged_event(analytics_name, analytics_args)
+ expect(@analytics).to have_logged_event(analytics_name, analytics_args)
+ end
end
end
end
diff --git a/spec/controllers/idv/personal_key_controller_spec.rb b/spec/controllers/idv/personal_key_controller_spec.rb
index abfcd6748c6..c78f25b0b28 100644
--- a/spec/controllers/idv/personal_key_controller_spec.rb
+++ b/spec/controllers/idv/personal_key_controller_spec.rb
@@ -87,6 +87,38 @@ def index
expect(response).to redirect_to account_path
end
end
+
+ context 'profile is pending from a different session' do
+ context 'profile is pending due to fraud review' do
+ before do
+ profile.deactivate_for_fraud_review
+ subject.idv_session.profile_id = nil
+ end
+
+ it 'does not redirect' do
+ get :index
+
+ expect(profile.user.pending_profile?).to eq true
+ expect(profile.fraud_review_pending_at).to_not eq nil
+ expect(response).to_not be_redirect
+ end
+ end
+
+ context 'profile is pending due to in person proofing' do
+ before do
+ profile.update!(deactivation_reason: :in_person_verification_pending)
+ subject.idv_session.profile_id = nil
+ end
+
+ it 'does not redirect' do
+ get :index
+
+ expect(profile.user.pending_profile?).to eq true
+ expect(profile.deactivation_reason).to eq('in_person_verification_pending')
+ expect(response).to_not be_redirect
+ end
+ end
+ end
end
end
@@ -200,12 +232,10 @@ def index
subject.idv_session.create_profile_from_applicant_with_password(password)
end
- context 'with gpo personal key after verification' do
- it 'redirects to doc auth url' do
- patch :update
+ it 'redirects to doc auth url' do
+ patch :update
- expect(response).to redirect_to idv_doc_auth_url
- end
+ expect(response).to redirect_to idv_doc_auth_url
end
end
@@ -239,39 +269,17 @@ def index
context 'with device profiling decisioning enabled' do
before do
- ProofingComponent.create(user: user, threatmetrix: true, threatmetrix_review_status: nil)
allow(IdentityConfig.store).to receive(:proofing_device_profiling).and_return(:enabled)
end
- context 'threatmetrix review status is nil' do
+ context 'fraud_review_pending_at is nil' do
it 'redirects to account path' do
patch :update
+ expect(profile.fraud_review_pending_at).to eq nil
expect(response).to redirect_to account_path
end
- it 'logs key submitted event' do
- patch :update
-
- expect(@analytics).to have_logged_event(
- 'IdV: personal key submitted',
- address_verification_method: nil,
- fraud_review_pending: false,
- fraud_rejection: false,
- deactivation_reason: nil,
- proofing_components: nil,
- )
- end
- end
- context 'device profiling passes' do
- before do
- ProofingComponent.find_by(user: user).update(threatmetrix_review_status: 'pass')
- end
- it 'redirects to account path' do
- patch :update
-
- expect(response).to redirect_to account_path
- end
it 'logs key submitted event' do
patch :update
@@ -286,39 +294,14 @@ def index
end
end
- context 'device profiling gets sent to review' do
- before do
- ProofingComponent.find_by(user: user).update(threatmetrix_review_status: 'review')
- profile.deactivate_for_fraud_review
- end
-
- it 'redirects to idv please call path' do
- patch :update
- expect(response).to redirect_to idv_please_call_path
- end
-
- it 'logs key submitted event' do
- patch :update
-
- expect(@analytics).to have_logged_event(
- 'IdV: personal key submitted',
- fraud_review_pending: true,
- fraud_rejection: false,
- address_verification_method: nil,
- deactivation_reason: nil,
- proofing_components: nil,
- )
- end
- end
-
- context 'device profiling fails' do
+ context 'profile is in fraud_review' do
before do
- ProofingComponent.find_by(user: user).update(threatmetrix_review_status: 'reject')
profile.deactivate_for_fraud_review
end
it 'redirects to idv please call path' do
patch :update
+ expect(profile.fraud_review_pending_at).to_not eq nil
expect(response).to redirect_to idv_please_call_path
end
diff --git a/spec/controllers/idv/review_controller_spec.rb b/spec/controllers/idv/review_controller_spec.rb
index 8e714d7a63f..97d4714ab48 100644
--- a/spec/controllers/idv/review_controller_spec.rb
+++ b/spec/controllers/idv/review_controller_spec.rb
@@ -159,14 +159,31 @@ def show
)
end
+ it 'uses the correct step indicator step' do
+ indicator_step = subject.step_indicator_step
+
+ expect(indicator_step).to eq(:secure_account)
+ end
+
context 'user is in gpo flow' do
- it 'does not display success message' do
+ before do
idv_session.vendor_phone_confirmation = false
idv_session.address_verification_mechanism = 'gpo'
+ end
+ it 'displays info message about sending letter' do
get :new
expect(flash.now[:success]).to be_nil
+ expect(flash.now[:info]).to eq(
+ t('idv.messages.review.gpo_pending'),
+ )
+ end
+
+ it 'uses the correct step indicator step' do
+ indicator_step = subject.step_indicator_step
+
+ expect(indicator_step).to eq(:get_a_letter)
end
end
diff --git a/spec/controllers/idv/ssn_controller_spec.rb b/spec/controllers/idv/ssn_controller_spec.rb
index 31ada7f5c31..1ac5cfd11fb 100644
--- a/spec/controllers/idv/ssn_controller_spec.rb
+++ b/spec/controllers/idv/ssn_controller_spec.rb
@@ -10,6 +10,8 @@
:flow_path => 'standard' }
end
+ let(:ssn) { Idp::Constants::MOCK_IDV_APPLICANT_WITH_SSN[:ssn] }
+
let(:user) { create(:user) }
before do
@@ -84,6 +86,31 @@
end
end
+ context 'with an ssn in session' do
+ let(:referer) { idv_document_capture_url }
+ before do
+ flow_session['pii_from_doc'][:ssn] = ssn
+ request.env['HTTP_REFERER'] = referer
+ end
+
+ context 'referer is not verify_info' do
+ it 'redirects to verify_info' do
+ get :show
+
+ expect(response).to redirect_to(idv_verify_info_url)
+ end
+ end
+
+ context 'referer is verify_info' do
+ let(:referer) { idv_verify_info_url }
+ it 'does not redirect' do
+ get :show
+
+ expect(response).to render_template :show
+ end
+ end
+ end
+
it 'overrides Content Security Policies for ThreatMetrix' do
allow(IdentityConfig.store).to receive(:proofing_device_profiling).
and_return(:enabled)
@@ -108,7 +135,6 @@
describe '#update' do
context 'with valid ssn' do
- let(:ssn) { Idp::Constants::MOCK_IDV_APPLICANT_WITH_SSN[:ssn] }
let(:params) { { doc_auth: { ssn: ssn } } }
let(:analytics_name) { 'IdV: doc auth ssn submitted' }
let(:analytics_args) do
diff --git a/spec/controllers/sign_up/passwords_controller_spec.rb b/spec/controllers/sign_up/passwords_controller_spec.rb
index bda5a03d946..8dcf444c235 100644
--- a/spec/controllers/sign_up/passwords_controller_spec.rb
+++ b/spec/controllers/sign_up/passwords_controller_spec.rb
@@ -10,7 +10,6 @@
password_form: {
password: password,
password_confirmation: password_confirmation,
- confirmation_enabled: true,
},
confirmation_token: token,
}
@@ -77,80 +76,73 @@
stub_attempts_tracker
end
- context 'with temporary param confirmation_enabled' do
- before do
- params.merge(confirmation_enabled: true)
+ context 'with a password that is too short' do
+ let(:password) { 'NewVal' }
+ let(:password_confirmation) { 'NewVal' }
+ let(:errors) do
+ {
+ password:
+ [t(
+ 'errors.attributes.password.too_short.other',
+ count: Devise.password_length.first,
+ )],
+ password_confirmation:
+ ["is too short (minimum is #{Devise.password_length.first} characters)"],
+ }
end
+ let(:error_details) do
+ {
+ password: [:too_short],
+ password_confirmation: [:too_short],
+ }
+ end
+
+ it 'tracks an invalid password event' do
+ expect(@analytics).to receive(:track_event).
+ with(
+ 'User Registration: Email Confirmation',
+ { errors: {}, error_details: nil, success: true, user_id: user.uuid },
+ )
+ expect(@analytics).to receive(:track_event).
+ with('Password Creation', analytics_hash)
+
+ expect(@irs_attempts_api_tracker).to receive(:user_registration_password_submitted).
+ with(
+ success: false,
+ failure_reason: error_details,
+ )
+ expect(@irs_attempts_api_tracker).not_to receive(:user_registration_email_confirmation)
+
+ subject
+ end
+ end
- # modify this test
- context 'with a password that is too short' do
- let(:password) { 'NewVal' }
- let(:password_confirmation) { 'NewVal' }
- let(:errors) do
- {
- password:
- [t(
- 'errors.attributes.password.too_short.other',
- count: Devise.password_length.first,
- )],
- password_confirmation:
- ["is too short (minimum is #{Devise.password_length.first} characters)"],
- }
- end
- let(:error_details) do
- {
- password: [:too_short],
- password_confirmation: [:too_short],
- }
- end
-
- it 'tracks an invalid password event' do
- expect(@analytics).to receive(:track_event).
- with(
- 'User Registration: Email Confirmation',
- { errors: {}, error_details: nil, success: true, user_id: user.uuid },
- )
- expect(@analytics).to receive(:track_event).
- with('Password Creation', analytics_hash)
-
- expect(@irs_attempts_api_tracker).to receive(:user_registration_password_submitted).
- with(
- success: false,
- failure_reason: error_details,
- )
- expect(@irs_attempts_api_tracker).not_to receive(:user_registration_email_confirmation)
-
- subject
- end
+ context 'when password confirmation does not match' do
+ let(:password) { 'NewVal!dPassw0rd' }
+ let(:password_confirmation) { 'bad match password' }
+ let(:errors) do
+ {
+ password_confirmation:
+ [t('errors.messages.password_mismatch')],
+ }
end
+ let(:error_details) do
+ {
+ password_confirmation: [t('errors.messages.password_mismatch')],
+ }
+ end
+
+ it 'tracks invalid password_confirmation error' do
+ expect(@analytics).to receive(:track_event).
+ with(
+ 'User Registration: Email Confirmation',
+ { errors: {}, error_details: nil, success: true, user_id: user.uuid },
+ )
+
+ expect(@analytics).to receive(:track_event).
+ with('Password Creation', analytics_hash)
- context 'when password confirmation does not match' do
- let(:password) { 'NewVal!dPassw0rd' }
- let(:password_confirmation) { 'bad match password' }
- let(:errors) do
- {
- password_confirmation:
- [t('errors.messages.password_mismatch')],
- }
- end
- let(:error_details) do
- {
- password_confirmation: [t('errors.messages.password_mismatch')],
- }
- end
-
- it 'tracks invalid password_confirmation error' do
- expect(@analytics).to receive(:track_event).
- with(
- 'User Registration: Email Confirmation',
- { errors: {}, error_details: nil, success: true, user_id: user.uuid },
- )
-
- expect(@analytics).to receive(:track_event).
- with('Password Creation', analytics_hash)
-
- subject
- end
+ subject
end
end
end
diff --git a/spec/controllers/users/verify_password_controller_spec.rb b/spec/controllers/users/verify_password_controller_spec.rb
index 34c1db19235..55604aa76a5 100644
--- a/spec/controllers/users/verify_password_controller_spec.rb
+++ b/spec/controllers/users/verify_password_controller_spec.rb
@@ -91,14 +91,12 @@
end
context 'without valid password' do
- let(:pii) { { dob: Time.zone.today } }
let(:response_bad) { FormResponse.new(success: false, errors: {}) }
render_views
before do
allow(form).to receive(:submit).and_return(response_bad)
- allow(controller).to receive(:decrypted_pii).and_return(pii)
put :update, params: user_params
end
diff --git a/spec/features/idv/actions/redo_document_capture_action_spec.rb b/spec/features/idv/actions/redo_document_capture_action_spec.rb
index bc45ad8e775..77632d1602e 100644
--- a/spec/features/idv/actions/redo_document_capture_action_spec.rb
+++ b/spec/features/idv/actions/redo_document_capture_action_spec.rb
@@ -32,13 +32,10 @@
DocAuth::Mock::DocAuthMockClient.reset!
attach_and_submit_images
- expect(current_path).to eq(idv_ssn_path)
- fill_out_ssn_form_with_ssn_that_fails_resolution
- click_on t('forms.buttons.submit.update')
expect(current_path).to eq(idv_verify_info_path)
check t('forms.ssn.show')
expect(page).to have_content(DocAuthHelper::SSN_THAT_FAILS_RESOLUTION)
- expect(page).not_to have_css('[role="status"]')
+ expect(page).to have_css('[role="status"]') # We verified your ID
end
it 'shows a troubleshooting option to allow the user to cancel and return to SP' do
@@ -71,13 +68,10 @@
DocAuth::Mock::DocAuthMockClient.reset!
attach_and_submit_images
- expect(current_path).to eq(idv_ssn_path)
- fill_out_ssn_form_with_ssn_that_fails_resolution
- click_on t('forms.buttons.submit.update')
expect(current_path).to eq(idv_verify_info_path)
check t('forms.ssn.show')
expect(page).to have_content(DocAuthHelper::SSN_THAT_FAILS_RESOLUTION)
- expect(page).not_to have_css('[role="status"]')
+ expect(page).to have_css('[role="status"]') # We verified your ID
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 bfc16edd4fb..87c3d502eea 100644
--- a/spec/features/idv/doc_auth/document_capture_spec.rb
+++ b/spec/features/idv/doc_auth/document_capture_spec.rb
@@ -35,7 +35,7 @@
end
it 'shows the new DocumentCapture page for desktop standard flow' do
- expect(page).to have_current_path(idv_document_capture_url)
+ expect(page).to have_current_path(idv_document_capture_path)
expect(page).to have_content(t('doc_auth.headings.document_capture').tr(' ', ' '))
expect(page).to have_content(t('step_indicator.flows.idv.verify_id'))
@@ -48,6 +48,12 @@
irs_reproofing: false,
acuant_sdk_upgrade_ab_test_bucket: :default,
)
+
+ # it redirects here if trying to move earlier in the flow
+ visit(idv_doc_auth_agreement_step)
+ expect(page).to have_current_path(idv_document_capture_path)
+ visit(idv_doc_auth_upload_step)
+ expect(page).to have_current_path(idv_document_capture_path)
end
it 'logs return to sp link click' do
diff --git a/spec/features/idv/doc_auth/hybrid_handoff_spec.rb b/spec/features/idv/doc_auth/hybrid_handoff_spec.rb
index a15d437ba52..92583e99ed4 100644
--- a/spec/features/idv/doc_auth/hybrid_handoff_spec.rb
+++ b/spec/features/idv/doc_auth/hybrid_handoff_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-feature 'doc auth upload step' do
+feature 'doc auth hybrid_handoff step' do
include IdvStepHelper
include DocAuthHelper
include ActionView::Helpers::DateHelper
@@ -19,14 +19,24 @@
allow(IdentityConfig.store).to receive(:doc_auth_hybrid_handoff_controller_enabled).
and_return(new_controller_enabled)
allow_any_instance_of(Idv::HybridHandoffController).to receive(:mobile_device?).and_return(true)
- complete_doc_auth_steps_before_upload_step
allow_any_instance_of(ApplicationController).to receive(:analytics).and_return(fake_analytics)
allow_any_instance_of(ApplicationController).to receive(:irs_attempts_api_tracker).
and_return(fake_attempts_tracker)
end
- context 'on a desktop device', js: true do
+ it 'does not skip ahead in standard desktop flow' do
+ visit(idv_hybrid_handoff_url)
+ expect(page).to have_current_path(idv_doc_auth_welcome_step)
+ complete_welcome_step
+ visit(idv_hybrid_handoff_url)
+ expect(page).to have_current_path(idv_doc_auth_agreement_step)
+ complete_agreement_step
+ expect(page).to have_current_path(idv_hybrid_handoff_path)
+ end
+
+ context 'on a desktop device' do
before do
+ complete_doc_auth_steps_before_upload_step
allow_any_instance_of(
Idv::HybridHandoffController,
).to receive(
@@ -54,9 +64,12 @@
'IdV: doc auth upload submitted',
hash_including(step: 'upload', destination: :document_capture),
)
+
+ visit(idv_hybrid_handoff_url)
+ expect(page).to have_current_path(idv_document_capture_path)
end
- it "defaults phone to user's 2fa phone number" do
+ it "defaults phone to user's 2fa phone number", :js do
field = page.find_field(t('two_factor_authentication.phone_label'))
expect(field.value).to eq('(202) 555-1212')
end
@@ -73,9 +86,12 @@
'IdV: doc auth upload submitted',
hash_including(step: 'upload', destination: :link_sent),
)
+
+ visit(idv_hybrid_handoff_url)
+ expect(page).to have_current_path(idv_link_sent_path)
end
- it 'proceeds to the next page with valid info' do
+ it 'proceeds to the next page with valid info', :js do
expect(fake_attempts_tracker).to receive(
:idv_phone_upload_link_sent,
).with(
@@ -96,7 +112,7 @@
expect(page).to have_current_path(idv_link_sent_path)
end
- it 'does not proceed to the next page with invalid info' do
+ it 'does not proceed to the next page with invalid info', :js do
fill_in :doc_auth_phone, with: ''
click_send_link
diff --git a/spec/features/idv/doc_auth/verify_info_step_spec.rb b/spec/features/idv/doc_auth/verify_info_step_spec.rb
index f601cd5f19d..54d73395bdd 100644
--- a/spec/features/idv/doc_auth/verify_info_step_spec.rb
+++ b/spec/features/idv/doc_auth/verify_info_step_spec.rb
@@ -51,6 +51,10 @@
check t('forms.ssn.show')
expect(page).not_to have_text(DocAuthHelper::GOOD_SSN_MASKED)
expect(page).to have_text(DocAuthHelper::GOOD_SSN)
+
+ # navigating to earlier pages returns here
+ visit(idv_document_capture_url)
+ expect(page).to have_current_path(idv_verify_info_path)
end
it 'allows the user to enter in a new address and displays updated info' do
diff --git a/spec/features/idv/hybrid_mobile/hybrid_mobile_spec.rb b/spec/features/idv/hybrid_mobile/hybrid_mobile_spec.rb
index 1318d633099..c7ad3927831 100644
--- a/spec/features/idv/hybrid_mobile/hybrid_mobile_spec.rb
+++ b/spec/features/idv/hybrid_mobile/hybrid_mobile_spec.rb
@@ -45,8 +45,7 @@
# Confirm that jumping to LinkSent page does not cause errors
visit idv_link_sent_url
expect(page).to have_current_path(root_url)
-
- visit idv_hybrid_mobile_capture_complete_url
+ visit idv_hybrid_mobile_document_capture_url
attach_and_submit_images
diff --git a/spec/features/idv/in_person_spec.rb b/spec/features/idv/in_person_spec.rb
index 1e370990a92..d5dbbace4d5 100644
--- a/spec/features/idv/in_person_spec.rb
+++ b/spec/features/idv/in_person_spec.rb
@@ -388,7 +388,7 @@
t('step_indicator.flows.idv.verify_phone_or_address'),
)
click_on t('idv.buttons.mail.send')
- expect_in_person_gpo_step_indicator_current_step(t('step_indicator.flows.idv.secure_account'))
+ expect_in_person_gpo_step_indicator_current_step(t('step_indicator.flows.idv.get_a_letter'))
complete_review_step
expect_in_person_gpo_step_indicator_current_step(t('step_indicator.flows.idv.get_a_letter'))
diff --git a/spec/features/idv/steps/gpo_step_spec.rb b/spec/features/idv/steps/gpo_step_spec.rb
index 882c561ff6a..81839f8a57d 100644
--- a/spec/features/idv/steps/gpo_step_spec.rb
+++ b/spec/features/idv/steps/gpo_step_spec.rb
@@ -36,9 +36,18 @@
it 'allows the user to resend a letter and redirects to the come back later step' do
complete_idv_and_return_to_gpo_step
+ # Confirm that we show the correct content on
+ # the GPO page for users requesting re-send
+ expect(page).to have_content(t('idv.titles.mail.resend'))
+ expect(page).to have_content(t('idv.messages.gpo.resend_timeframe'))
+ expect(page).to have_content(t('idv.messages.gpo.resend_code_warning'))
+ expect(page).to have_content(t('idv.buttons.mail.resend'))
+ expect(page).to_not have_content(t('idv.messages.gpo.info_alert'))
+
expect { click_on t('idv.buttons.mail.resend') }.
to change { GpoConfirmation.count }.from(1).to(2)
expect_user_to_be_unverified(user)
+
expect(page).to have_content(t('idv.titles.come_back_later'))
expect(page).to have_current_path(idv_come_back_later_path)
diff --git a/spec/features/idv/steps/review_step_spec.rb b/spec/features/idv/steps/review_step_spec.rb
index 0a5f9b5eacb..f781d55951a 100644
--- a/spec/features/idv/steps/review_step_spec.rb
+++ b/spec/features/idv/steps/review_step_spec.rb
@@ -13,15 +13,7 @@
start_idv_from_sp
complete_idv_steps_before_review_step
- click_on t('idv.messages.review.intro')
-
- expect(page).to have_content('FAKEY')
- expect(page).to have_content('MCFAKERSON')
- expect(page).to have_content('1 FAKE RD')
- expect(page).to have_content('GREAT FALLS, MT 59010')
- expect(page).to have_content('October 06, 1938')
- expect(page).to have_content(DocAuthHelper::GOOD_SSN)
- expect(page).to have_content('+1 202-555-1212')
+ expect(page).to have_content(t('idv.messages.review.message', app_name: APP_NAME))
fill_in 'Password', with: 'this is not the right password'
click_idv_continue
diff --git a/spec/features/two_factor_authentication/sign_in_via_personal_key_spec.rb b/spec/features/two_factor_authentication/sign_in_via_personal_key_spec.rb
index e7dee55b7aa..912037e2dfe 100644
--- a/spec/features/two_factor_authentication/sign_in_via_personal_key_spec.rb
+++ b/spec/features/two_factor_authentication/sign_in_via_personal_key_spec.rb
@@ -9,9 +9,6 @@
raw_key = PersonalKeyGenerator.new(user).create
old_key = user.reload.encrypted_recovery_code_digest
- expect(Telephony).to receive(:send_personal_key_sign_in_notice).
- with(to: '+1 (202) 345-6789', country_code: 'US')
-
sign_in_before_2fa(user)
choose_another_security_option('personal_key')
enter_personal_key(personal_key: raw_key)
@@ -19,6 +16,10 @@
expect(user.reload.encrypted_recovery_code_digest).to_not eq old_key
expect(current_path).to eq account_path
+ last_message = Telephony::Test::Message.messages.last
+ expect(last_message.body).to eq t('telephony.personal_key_sign_in_notice', app_name: APP_NAME)
+ expect(last_message.to).to eq user.phone_configurations.take.phone
+
expect_delivered_email_count(1)
expect_delivered_email(
to: [user.email_addresses.first.email],
diff --git a/spec/forms/idv/in_person/address_form_spec.rb b/spec/forms/idv/in_person/address_form_spec.rb
new file mode 100644
index 00000000000..aef7424b8d7
--- /dev/null
+++ b/spec/forms/idv/in_person/address_form_spec.rb
@@ -0,0 +1,103 @@
+require 'rails_helper'
+
+describe Idv::InPerson::AddressForm do
+ let(:pii) { Idp::Constants::MOCK_IDV_APPLICANT_STATE_ID_ADDRESS }
+ context 'test validation for transliteration after form submission' do
+ let(:good_params) do
+ {
+ address1: Faker::Address.street_address,
+ address2: Faker::Address.secondary_address,
+ zipcode: Faker::Address.zip_code,
+ state: Faker::Address.state_abbr,
+ city: Faker::Address.city,
+ }
+ end
+ let(:invalid_char) { '$' }
+ let(:bad_params) do
+ {
+ address1: invalid_char + Faker::Address.street_address,
+ address2: invalid_char + Faker::Address.secondary_address + invalid_char,
+ zipcode: Faker::Address.zip_code,
+ state: Faker::Address.state_abbr,
+ city: invalid_char + Faker::Address.city,
+ }
+ end
+ context 'when usps_ipp_transliteration_enabled is false' do
+ let(:subject) { described_class.new(capture_secondary_id_enabled: true) }
+ before(:each) do
+ allow(IdentityConfig.store).to receive(:usps_ipp_transliteration_enabled).and_return(false)
+ end
+ it 'submit success with good params' do
+ good_params[:same_address_as_id] = true
+ result = subject.submit(good_params)
+ expect(subject.errors.empty?).to be(true)
+ expect(result).to be_kind_of(FormResponse)
+ expect(result.success?).to be(true)
+ end
+
+ it 'submit success with good params' do
+ good_params[:same_address_as_id] = false
+ result = subject.submit(good_params)
+ expect(subject.errors.empty?).to be(true)
+ expect(result).to be_kind_of(FormResponse)
+ expect(result.success?).to be(true)
+ end
+
+ it 'submit success with bad params' do
+ bad_params[:same_address_as_id] = false
+ result = subject.submit(bad_params)
+ expect(subject.errors.empty?).to be(true)
+ expect(result).to be_kind_of(FormResponse)
+ expect(result.success?).to be(true)
+ end
+ end
+ context 'when usps_ipp_transliteration_enabled is enabled ' do
+ let(:subject) { described_class.new(capture_secondary_id_enabled: true) }
+ before(:each) do
+ allow(IdentityConfig.store).to receive(:usps_ipp_transliteration_enabled).and_return(true)
+ end
+ it 'submit success with good params' do
+ good_params[:same_address_as_id] = true
+ result = subject.submit(good_params)
+ expect(subject.errors.empty?).to be(true)
+ expect(result).to be_kind_of(FormResponse)
+ expect(result.success?).to be(true)
+ end
+ it 'submit failure with bad params' do
+ bad_params[:same_address_as_id] = false
+ result = subject.submit(bad_params)
+ expect(subject.errors.empty?).to be(false)
+ expect(subject.errors.to_hash).to include(:address1, :address2, :city)
+ expect(result).to be_kind_of(FormResponse)
+ expect(result.success?).to be(false)
+ expect(result.errors.empty?).to be(true)
+ end
+ end
+ context 'when usps_ipp_transliteration_enabled is enabled and validate on other field' do
+ before(:each) do
+ allow(IdentityConfig.store).to receive(:usps_ipp_transliteration_enabled).and_return(true)
+ end
+ context 'when capture_secondary_id_enabled is true' do
+ let(:subject) { described_class.new(capture_secondary_id_enabled: true) }
+ it 'submit with missing same_address_as_id should be successful' do
+ missing_required_params = good_params.except(:same_address_as_id)
+ result = subject.submit(missing_required_params)
+ expect(subject.errors.empty?).to be(true)
+ expect(result).to be_kind_of(FormResponse)
+ expect(result.success?).to be(true)
+ end
+ end
+ context 'when capture_secondary_id_enabled is false' do
+ let(:subject) { described_class.new(capture_secondary_id_enabled: false) }
+ it 'submit with missing same_address_as_id will fail' do
+ missing_required_params = good_params.except(:same_address_as_id)
+ result = subject.submit(missing_required_params)
+ expect(subject.errors.empty?).to be(false)
+ expect(result).to be_kind_of(FormResponse)
+ expect(result.success?).to be(false)
+ expect(result.errors.keys).to include(:same_address_as_id)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/forms/idv/state_id_form_spec.rb b/spec/forms/idv/state_id_form_spec.rb
new file mode 100644
index 00000000000..30b8142ca27
--- /dev/null
+++ b/spec/forms/idv/state_id_form_spec.rb
@@ -0,0 +1,108 @@
+require 'rails_helper'
+
+describe Idv::StateIdForm do
+ let(:subject) { Idv::StateIdForm.new(pii) }
+ let(:valid_dob) do
+ valid_d = Time.zone.today - IdentityConfig.store.idv_min_age_years.years - 1.day
+ ActionController::Parameters.new(
+ {
+ year: valid_d.year,
+ month: valid_d.month,
+ day: valid_d.mday,
+ },
+ ).permit(:year, :month, :day)
+ end
+ let(:too_young_dob) do
+ (Time.zone.today - IdentityConfig.store.idv_min_age_years.years + 1.day).to_s
+ end
+ let(:good_params) do
+ {
+ first_name: Faker::Name.first_name,
+ last_name: Faker::Name.last_name,
+ dob: valid_dob,
+ identity_doc_address1: Faker::Address.street_address,
+ identity_doc_address2: Faker::Address.secondary_address,
+ identity_doc_zipcode: Faker::Address.zip_code,
+ identity_doc_address_state: Faker::Address.state_abbr,
+ same_address_as_id: 'true',
+ state_id_jurisdiction: 'AL',
+ state_id_number: Faker::IDNumber.valid,
+ }
+ end
+ let(:dob_min_age_name_error_params) do
+ {
+ first_name: Faker::Name.first_name + invalid_char,
+ last_name: Faker::Name.last_name,
+ dob: too_young_dob,
+ identity_doc_address1: Faker::Address.street_address,
+ identity_doc_address2: Faker::Address.secondary_address,
+ identity_doc_zipcode: Faker::Address.zip_code,
+ identity_doc_address_state: Faker::Address.state_abbr,
+ same_address_as_id: 'true',
+ state_id_jurisdiction: 'AL',
+ state_id_number: Faker::IDNumber.valid,
+ }
+ end
+ let(:invalid_char) { '1' }
+ let(:name_error_params) do
+ {
+ first_name: Faker::Name.first_name + invalid_char,
+ last_name: Faker::Name.last_name,
+ dob: valid_dob,
+ identity_doc_address1: Faker::Address.street_address,
+ identity_doc_address2: Faker::Address.secondary_address,
+ identity_doc_zipcode: Faker::Address.zip_code,
+ identity_doc_address_state: Faker::Address.state_abbr,
+ same_address_as_id: 'true',
+ state_id_jurisdiction: 'AL',
+ state_id_number: Faker::IDNumber.valid,
+ }
+ end
+ let(:pii) { nil }
+ describe '#submit' do
+ context 'when the form is valid' do
+ it 'returns a successful form response' do
+ result = subject.submit(good_params)
+ expect(result).to be_kind_of(FormResponse)
+ expect(result.success?).to eq(true)
+ expect(result.errors).to be_empty
+ end
+ end
+
+ context 'when there is an error with name' do
+ it 'returns a single name error when name is wrong' do
+ allow(IdentityConfig.store).to receive(:usps_ipp_transliteration_enabled).and_return(true)
+ result = subject.submit(name_error_params)
+ expect(subject.errors.empty?).to be(false)
+ expect(result).to be_kind_of(FormResponse)
+ expect(result.success?).to eq(false)
+ expect(subject.errors[:first_name]).to eq [
+ I18n.t(
+ 'in_person_proofing.form.state_id.errors.unsupported_chars',
+ char_list: [invalid_char].join(', '),
+ ),
+ ]
+ expect(result.errors.empty?).to be(true)
+ end
+ it 'returns both name and dob error when both fields are invalid' do
+ allow(IdentityConfig.store).to receive(:usps_ipp_transliteration_enabled).and_return(true)
+ result = subject.submit(dob_min_age_name_error_params)
+ expect(subject.errors.empty?).to be(false)
+ expect(result).to be_kind_of(FormResponse)
+ expect(result.success?).to eq(false)
+ expect(subject.errors[:first_name]).to eq [
+ I18n.t(
+ 'in_person_proofing.form.state_id.errors.unsupported_chars',
+ char_list: [invalid_char].join(', '),
+ ),
+ ]
+ expect(subject.errors[:dob]).to eq [
+ I18n.t(
+ 'in_person_proofing.form.state_id.memorable_date.errors.date_of_birth.range_min_age',
+ app_name: APP_NAME,
+ ),
+ ]
+ end
+ end
+ end
+end
diff --git a/spec/forms/password_form_spec.rb b/spec/forms/password_form_spec.rb
index ff96adccd19..b1ec0abe7a6 100644
--- a/spec/forms/password_form_spec.rb
+++ b/spec/forms/password_form_spec.rb
@@ -21,7 +21,6 @@
{
password: password,
password_confirmation: password_confirmation,
- confirmation_enabled: true,
}
end
diff --git a/spec/javascript/packages/document-capture-polling/index-spec.js b/spec/javascript/packages/document-capture-polling/index-spec.js
index 598480cf85b..8cd21f90e78 100644
--- a/spec/javascript/packages/document-capture-polling/index-spec.js
+++ b/spec/javascript/packages/document-capture-polling/index-spec.js
@@ -44,6 +44,10 @@ describe('DocumentCapturePolling', () => {
subject.bind();
});
+ afterEach(() => {
+ subject.bindPromptOnNavigate(false);
+ });
+
it('hides form', () => {
expect(screen.getByText('Submit').closest('.display-none')).to.be.ok();
});
diff --git a/spec/jobs/get_usps_proofing_results_job_spec.rb b/spec/jobs/get_usps_proofing_results_job_spec.rb
index aa24d69faf5..9e2b2779de3 100644
--- a/spec/jobs/get_usps_proofing_results_job_spec.rb
+++ b/spec/jobs/get_usps_proofing_results_job_spec.rb
@@ -43,6 +43,7 @@
status: response['status'],
transaction_end_date_time: anything,
transaction_start_date_time: anything,
+ job_name: 'GetUspsProofingResultsJob',
)
end
end
@@ -58,6 +59,7 @@
wait_until: anything,
service_provider: pending_enrollment.issuer,
timestamp: Time.zone.now,
+ job_name: 'GetUspsProofingResultsJob',
)
else
expect(job_analytics).to have_logged_event(
@@ -67,6 +69,7 @@
wait_until: anything,
service_provider: pending_enrollment.issuer,
timestamp: Time.zone.now,
+ job_name: 'GetUspsProofingResultsJob',
)
end
end
@@ -111,6 +114,7 @@
reason: reason,
response_message: response_message,
response_status_code: response_status_code,
+ job_name: 'GetUspsProofingResultsJob',
),
)
end
@@ -155,6 +159,7 @@
reason: 'Request exception',
response_present: false,
exception_class: error_type.to_s,
+ job_name: 'GetUspsProofingResultsJob',
),
)
end
@@ -317,6 +322,7 @@
'GetUspsProofingResultsJob: Job started',
enrollments_count: 5,
reprocess_delay_minutes: 2.0,
+ job_name: 'GetUspsProofingResultsJob',
)
end
@@ -342,7 +348,8 @@
enrollments_failed: 1,
enrollments_in_progress: 1,
enrollments_passed: 1,
- percent_enrollments_errored: 20,
+ percent_enrollments_errored: 20.00,
+ job_name: 'GetUspsProofingResultsJob',
)
expect(
@@ -369,7 +376,8 @@
enrollments_failed: 0,
enrollments_in_progress: 0,
enrollments_passed: 5,
- percent_enrollments_errored: 0,
+ percent_enrollments_errored: 0.00,
+ job_name: 'GetUspsProofingResultsJob',
)
expect(
@@ -397,7 +405,8 @@
enrollments_failed: 0,
enrollments_in_progress: 0,
enrollments_passed: 0,
- percent_enrollments_errored: 0,
+ percent_enrollments_errored: 0.00,
+ job_name: 'GetUspsProofingResultsJob',
)
expect(
@@ -424,6 +433,7 @@
exception_message: error_message,
exception_class: 'StandardError',
reason: 'Request exception',
+ job_name: 'GetUspsProofingResultsJob',
),
)
end
@@ -492,6 +502,7 @@
service_provider: pending_enrollment.issuer,
timestamp: anything,
wait_until: nil,
+ job_name: 'GetUspsProofingResultsJob',
)
end
end
@@ -560,6 +571,7 @@
service_provider: anything,
timestamp: anything,
wait_until: wait_until,
+ job_name: 'GetUspsProofingResultsJob',
)
end
@@ -585,6 +597,7 @@
service_provider: anything,
timestamp: anything,
wait_until: wait_until,
+ job_name: 'GetUspsProofingResultsJob',
)
end
end
@@ -632,6 +645,7 @@
hash_including(
reason: 'Successful status update',
passed: true,
+ job_name: 'GetUspsProofingResultsJob',
),
)
expect(job_analytics).to have_logged_event(
@@ -641,6 +655,7 @@
service_provider: anything,
timestamp: anything,
wait_until: nil,
+ job_name: 'GetUspsProofingResultsJob',
)
end
end
@@ -668,6 +683,7 @@
hash_including(
passed: false,
reason: 'Failed status',
+ job_name: 'GetUspsProofingResultsJob',
),
)
expect(job_analytics).to have_logged_event(
@@ -677,6 +693,7 @@
service_provider: anything,
timestamp: anything,
wait_until: nil,
+ job_name: 'GetUspsProofingResultsJob',
)
end
end
@@ -705,6 +722,7 @@
fraud_suspected: true,
passed: false,
reason: 'Failed status',
+ job_name: 'GetUspsProofingResultsJob',
),
)
expect(job_analytics).to have_logged_event(
@@ -714,6 +732,7 @@
service_provider: anything,
timestamp: anything,
wait_until: nil,
+ job_name: 'GetUspsProofingResultsJob',
)
end
end
@@ -741,6 +760,7 @@
hash_including(
passed: false,
reason: 'Unsupported ID type',
+ job_name: 'GetUspsProofingResultsJob',
),
)
@@ -751,6 +771,7 @@
service_provider: anything,
timestamp: anything,
wait_until: nil,
+ job_name: 'GetUspsProofingResultsJob',
)
end
end
@@ -779,6 +800,7 @@
reason: 'Enrollment has expired',
transaction_end_date_time: nil,
transaction_start_date_time: nil,
+ job_name: 'GetUspsProofingResultsJob',
),
)
end
@@ -799,6 +821,7 @@
'GetUspsProofingResultsJob: Enrollment incomplete',
hash_including(
response_message: 'More than 30 days have passed since opt-in to IPP',
+ job_name: 'GetUspsProofingResultsJob',
),
)
end
@@ -830,12 +853,16 @@
hash_including(
passed: false,
reason: 'Enrollment has expired',
+ job_name: 'GetUspsProofingResultsJob',
),
)
expect(job_analytics).to have_logged_event(
'GetUspsProofingResultsJob: Unexpected response received',
- hash_including(reason: 'Unexpected number of days before enrollment expired'),
+ hash_including(
+ reason: 'Unexpected number of days before enrollment expired',
+ ),
+ job_name: 'GetUspsProofingResultsJob',
)
end
end
@@ -860,6 +887,7 @@
hash_including(
reason: 'Invalid enrollment code',
response_message: /Enrollment code [0-9]{16} does not exist/,
+ job_name: 'GetUspsProofingResultsJob',
),
)
end
@@ -884,6 +912,7 @@
hash_including(
reason: 'Invalid applicant unique id',
response_message: /Applicant [0-9a-z]{18} does not exist/,
+ job_name: 'GetUspsProofingResultsJob',
),
)
end
@@ -920,6 +949,7 @@
'GetUspsProofingResultsJob: Exception raised',
hash_including(
status: 'Not supported',
+ job_name: 'GetUspsProofingResultsJob',
),
)
end
@@ -1041,6 +1071,7 @@
'GetUspsProofingResultsJob: Enrollment incomplete',
hash_including(
response_message: 'Customer has not been to a post office to complete IPP',
+ job_name: 'GetUspsProofingResultsJob',
),
)
end
@@ -1106,6 +1137,7 @@
hash_including(
passed: false,
reason: 'Provided secondary proof of address',
+ job_name: 'GetUspsProofingResultsJob',
),
)
end
@@ -1125,5 +1157,20 @@
job.perform Time.zone.now
end
end
+
+ describe 'IPP Enrollments Ready Job Enabled' do
+ before do
+ allow(IdentityConfig.store).to(
+ receive(:in_person_enrollments_ready_job_enabled).and_return(true),
+ )
+ allow(IdentityConfig.store).to receive(:usps_mock_fallback).and_return(false)
+ end
+
+ it 'does not request any enrollment records' do
+ # no stubbing means this test will fail if the UspsInPersonProofing::Proofer
+ # tries to connect to the USPS API
+ job.perform Time.zone.now
+ end
+ end
end
end
diff --git a/spec/jobs/get_usps_ready_proofing_results_job_spec.rb b/spec/jobs/get_usps_ready_proofing_results_job_spec.rb
new file mode 100644
index 00000000000..e73f034c826
--- /dev/null
+++ b/spec/jobs/get_usps_ready_proofing_results_job_spec.rb
@@ -0,0 +1,287 @@
+require 'rails_helper'
+
+RSpec.describe GetUspsReadyProofingResultsJob do
+ include UspsIppHelper
+ include ApproximatingHelper
+
+ let(:reprocess_delay_minutes) { 2.0 }
+ let(:request_delay_ms) { 0 }
+ let(:job) { GetUspsReadyProofingResultsJob.new }
+ let(:job_analytics) { FakeAnalytics.new }
+ let(:transaction_start_date_time) do
+ ActiveSupport::TimeZone[-6].strptime(
+ '12/17/2020 033855',
+ '%m/%d/%Y %H%M%S',
+ ).in_time_zone('UTC')
+ end
+ let(:transaction_end_date_time) do
+ ActiveSupport::TimeZone[-6].strptime(
+ '12/17/2020 034055',
+ '%m/%d/%Y %H%M%S',
+ ).in_time_zone('UTC')
+ end
+
+ before do
+ allow(Rails).to receive(:cache).and_return(
+ ActiveSupport::Cache::RedisCacheStore.new(url: IdentityConfig.store.redis_throttle_url),
+ )
+ ActiveJob::Base.queue_adapter = :test
+ allow(job).to receive(:analytics).and_return(job_analytics)
+ allow(IdentityConfig.store).to receive(:get_usps_proofing_results_job_reprocess_delay_minutes).
+ and_return(reprocess_delay_minutes)
+ stub_const(
+ 'GetUspsProofingResultsJob::REQUEST_DELAY_IN_SECONDS',
+ request_delay_ms / GetUspsProofingResultsJob::MILLISECONDS_PER_SECOND,
+ )
+ stub_request_token
+ if respond_to?(:pending_enrollment)
+ pending_enrollment.update(enrollment_established_at: 3.days.ago)
+ end
+ end
+
+ describe '#perform' do
+ describe 'IPP enabled' do
+ describe 'Ready Job enabled' do
+ let!(:pending_enrollments) do
+ [
+ create(
+ :in_person_enrollment, :pending,
+ selected_location_details: { name: 'BALTIMORE' },
+ issuer: 'http://localhost:3000',
+ ready_for_status_check: true
+ ),
+ create(
+ :in_person_enrollment, :pending,
+ selected_location_details: { name: 'FRIENDSHIP' },
+ ready_for_status_check: true
+ ),
+ create(
+ :in_person_enrollment, :pending,
+ selected_location_details: { name: 'WASHINGTON' },
+ ready_for_status_check: true
+ ),
+ create(
+ :in_person_enrollment, :pending,
+ selected_location_details: { name: 'ARLINGTON' },
+ ready_for_status_check: true
+ ),
+ create(
+ :in_person_enrollment, :pending,
+ selected_location_details: { name: 'DEANWOOD' },
+ ready_for_status_check: true
+ ),
+ ]
+ end
+ let(:pending_enrollment) { pending_enrollments[0] }
+
+ before do
+ allow(InPersonEnrollment).to receive(:needs_status_check_on_ready_enrollments).
+ and_return([pending_enrollment])
+ allow(IdentityConfig.store).to receive(:in_person_proofing_enabled).and_return(true)
+ allow(IdentityConfig.store).to(
+ receive(:in_person_enrollments_ready_job_enabled).and_return(true),
+ )
+ end
+
+ it 'requests the enrollments that need their status checked' do
+ stub_request_passed_proofing_results
+
+ freeze_time do
+ job.perform(Time.zone.now)
+
+ expect(InPersonEnrollment).to(
+ have_received(:needs_status_check_on_ready_enrollments).
+ with(...reprocess_delay_minutes.minutes.ago),
+ )
+ end
+ end
+
+ it 'records the last attempted status check regardless of response code and contents' do
+ allow(InPersonEnrollment).to receive(:needs_status_check_on_ready_enrollments).
+ and_return(pending_enrollments)
+ stub_request_proofing_results_with_responses(
+ request_failed_proofing_results_args,
+ request_in_progress_proofing_results_args,
+ request_in_progress_proofing_results_args,
+ request_failed_proofing_results_args,
+ )
+
+ expect(pending_enrollments.pluck(:status_check_attempted_at)).to(
+ all(eq nil),
+ 'failed test precondition:
+ pending enrollments must not set status check attempted time',
+ )
+
+ expect(pending_enrollments.pluck(:status_check_completed_at)).to(
+ all(eq nil),
+ 'failed test precondition:
+ pending enrollments must not set status check completed time',
+ )
+
+ freeze_time do
+ job.perform(Time.zone.now)
+
+ expect(
+ pending_enrollments.
+ map(&:reload).
+ pluck(:status_check_attempted_at),
+ ).to(
+ all(eq Time.zone.now),
+ 'job must update status check attempted time for all pending enrollments',
+ )
+
+ expect(
+ pending_enrollments.
+ map(&:reload).
+ pluck(:status_check_completed_at),
+ ).to(
+ all(eq Time.zone.now),
+ 'job must update status check completed time for all pending enrollments',
+ )
+ end
+ end
+
+ it 'logs a message when the job starts' do
+ allow(InPersonEnrollment).to receive(:needs_status_check_on_ready_enrollments).
+ and_return(pending_enrollments)
+ stub_request_proofing_results_with_responses(
+ request_failed_proofing_results_args,
+ request_in_progress_proofing_results_args,
+ request_in_progress_proofing_results_args,
+ request_failed_proofing_results_args,
+ )
+
+ job.perform(Time.zone.now)
+
+ expect(job_analytics).to have_logged_event(
+ 'GetUspsProofingResultsJob: Job started',
+ enrollments_count: 5,
+ reprocess_delay_minutes: 2.0,
+ job_name: 'GetUspsReadyProofingResultsJob',
+ )
+ end
+
+ it 'logs a message with counts of various outcomes when the job completes (errored > 0)' do
+ allow(InPersonEnrollment).to receive(:needs_status_check_on_ready_enrollments).
+ and_return(pending_enrollments)
+ stub_request_proofing_results_with_responses(
+ request_passed_proofing_results_args,
+ request_in_progress_proofing_results_args,
+ { status: 500 },
+ request_failed_proofing_results_args,
+ request_expired_proofing_results_args,
+ )
+
+ job.perform(Time.zone.now)
+
+ expect(job_analytics).to have_logged_event(
+ 'GetUspsProofingResultsJob: Job completed',
+ duration_seconds: anything,
+ enrollments_checked: 5,
+ enrollments_errored: 1,
+ enrollments_expired: 1,
+ enrollments_failed: 1,
+ enrollments_in_progress: 1,
+ enrollments_passed: 1,
+ percent_enrollments_errored: 20.00,
+ job_name: 'GetUspsReadyProofingResultsJob',
+ )
+
+ expect(
+ job_analytics.events['GetUspsProofingResultsJob: Job completed'].
+ first[:duration_seconds],
+ ).to be >= 0.0
+ end
+
+ it 'logs a message with counts of various outcomes when the job completes (errored = 0)' do
+ allow(InPersonEnrollment).to receive(:needs_status_check_on_ready_enrollments).
+ and_return(pending_enrollments)
+ stub_request_proofing_results_with_responses(
+ request_passed_proofing_results_args,
+ )
+
+ job.perform(Time.zone.now)
+
+ expect(job_analytics).to have_logged_event(
+ 'GetUspsProofingResultsJob: Job completed',
+ duration_seconds: anything,
+ enrollments_checked: 5,
+ enrollments_errored: 0,
+ enrollments_expired: 0,
+ enrollments_failed: 0,
+ enrollments_in_progress: 0,
+ enrollments_passed: 5,
+ percent_enrollments_errored: 0.00,
+ job_name: 'GetUspsReadyProofingResultsJob',
+ )
+
+ expect(
+ job_analytics.events['GetUspsProofingResultsJob: Job completed'].
+ first[:duration_seconds],
+ ).to be >= 0.0
+ end
+
+ it 'logs a message with counts of various outcomes when the job completes
+ (no enrollments)' do
+ allow(InPersonEnrollment).to receive(:needs_status_check_on_ready_enrollments).
+ and_return([])
+ stub_request_proofing_results_with_responses(
+ request_passed_proofing_results_args,
+ )
+
+ job.perform(Time.zone.now)
+
+ expect(job_analytics).to have_logged_event(
+ 'GetUspsProofingResultsJob: Job completed',
+ duration_seconds: anything,
+ enrollments_checked: 0,
+ enrollments_errored: 0,
+ enrollments_expired: 0,
+ enrollments_failed: 0,
+ enrollments_in_progress: 0,
+ enrollments_passed: 0,
+ percent_enrollments_errored: 0.00,
+ job_name: 'GetUspsReadyProofingResultsJob',
+ )
+
+ expect(
+ job_analytics.events['GetUspsProofingResultsJob: Job completed'].
+ first[:duration_seconds],
+ ).to be >= 0.0
+ end
+ end
+ end
+
+ describe 'IPP disabled' do
+ before do
+ allow(IdentityConfig.store).to receive(:in_person_proofing_enabled).and_return(false)
+ allow(IdentityConfig.store).to(
+ receive(:in_person_enrollments_ready_job_enabled).and_return(true),
+ )
+ allow(IdentityConfig.store).to receive(:usps_mock_fallback).and_return(false)
+ end
+
+ it 'does not request any enrollment records' do
+ # no stubbing means this test will fail if the UspsInPersonProofing::Proofer
+ # tries to connect to the USPS API
+ job.perform Time.zone.now
+ end
+ end
+
+ describe 'Ready Job disabled' do
+ before do
+ allow(IdentityConfig.store).to receive(:in_person_proofing_enabled).and_return(true)
+ allow(IdentityConfig.store).to(
+ receive(:in_person_enrollments_ready_job_enabled).and_return(false),
+ )
+ allow(IdentityConfig.store).to receive(:usps_mock_fallback).and_return(false)
+ end
+
+ it 'does not request any enrollment records' do
+ # no stubbing means this test will fail if the UspsInPersonProofing::Proofer
+ # tries to connect to the USPS API
+ job.perform Time.zone.now
+ end
+ end
+ end
+end
diff --git a/spec/jobs/get_usps_waiting_proofing_results_job_spec.rb b/spec/jobs/get_usps_waiting_proofing_results_job_spec.rb
new file mode 100644
index 00000000000..2f21550a9cb
--- /dev/null
+++ b/spec/jobs/get_usps_waiting_proofing_results_job_spec.rb
@@ -0,0 +1,287 @@
+require 'rails_helper'
+
+RSpec.describe GetUspsWaitingProofingResultsJob do
+ include UspsIppHelper
+ include ApproximatingHelper
+
+ let(:reprocess_delay_minutes) { 2.0 }
+ let(:request_delay_ms) { 0 }
+ let(:job) { GetUspsWaitingProofingResultsJob.new }
+ let(:job_analytics) { FakeAnalytics.new }
+ let(:transaction_start_date_time) do
+ ActiveSupport::TimeZone[-6].strptime(
+ '12/17/2020 033855',
+ '%m/%d/%Y %H%M%S',
+ ).in_time_zone('UTC')
+ end
+ let(:transaction_end_date_time) do
+ ActiveSupport::TimeZone[-6].strptime(
+ '12/17/2020 034055',
+ '%m/%d/%Y %H%M%S',
+ ).in_time_zone('UTC')
+ end
+
+ before do
+ allow(Rails).to receive(:cache).and_return(
+ ActiveSupport::Cache::RedisCacheStore.new(url: IdentityConfig.store.redis_throttle_url),
+ )
+ ActiveJob::Base.queue_adapter = :test
+ allow(job).to receive(:analytics).and_return(job_analytics)
+ allow(IdentityConfig.store).to receive(:get_usps_proofing_results_job_reprocess_delay_minutes).
+ and_return(reprocess_delay_minutes)
+ stub_const(
+ 'GetUspsProofingResultsJob::REQUEST_DELAY_IN_SECONDS',
+ request_delay_ms / GetUspsProofingResultsJob::MILLISECONDS_PER_SECOND,
+ )
+ stub_request_token
+ if respond_to?(:pending_enrollment)
+ pending_enrollment.update(enrollment_established_at: 3.days.ago)
+ end
+ end
+
+ describe '#perform' do
+ describe 'IPP enabled' do
+ describe 'Ready Job enabled' do
+ let!(:pending_enrollments) do
+ [
+ create(
+ :in_person_enrollment, :pending,
+ selected_location_details: { name: 'BALTIMORE' },
+ issuer: 'http://localhost:3000',
+ ready_for_status_check: false
+ ),
+ create(
+ :in_person_enrollment, :pending,
+ selected_location_details: { name: 'FRIENDSHIP' },
+ ready_for_status_check: false
+ ),
+ create(
+ :in_person_enrollment, :pending,
+ selected_location_details: { name: 'WASHINGTON' },
+ ready_for_status_check: false
+ ),
+ create(
+ :in_person_enrollment, :pending,
+ selected_location_details: { name: 'ARLINGTON' },
+ ready_for_status_check: false
+ ),
+ create(
+ :in_person_enrollment, :pending,
+ selected_location_details: { name: 'DEANWOOD' },
+ ready_for_status_check: false
+ ),
+ ]
+ end
+ let(:pending_enrollment) { pending_enrollments[0] }
+
+ before do
+ allow(InPersonEnrollment).to receive(:needs_status_check_on_waiting_enrollments).
+ and_return([pending_enrollment])
+ allow(IdentityConfig.store).to receive(:in_person_proofing_enabled).and_return(true)
+ allow(IdentityConfig.store).to(
+ receive(:in_person_enrollments_ready_job_enabled).and_return(true),
+ )
+ end
+
+ it 'requests the enrollments that need their status checked' do
+ stub_request_passed_proofing_results
+
+ freeze_time do
+ job.perform(Time.zone.now)
+
+ expect(InPersonEnrollment).to(
+ have_received(:needs_status_check_on_waiting_enrollments).
+ with(...reprocess_delay_minutes.minutes.ago),
+ )
+ end
+ end
+
+ it 'records the last attempted status check regardless of response code and contents' do
+ allow(InPersonEnrollment).to receive(:needs_status_check_on_waiting_enrollments).
+ and_return(pending_enrollments)
+ stub_request_proofing_results_with_responses(
+ request_failed_proofing_results_args,
+ request_in_progress_proofing_results_args,
+ request_in_progress_proofing_results_args,
+ request_failed_proofing_results_args,
+ )
+
+ expect(pending_enrollments.pluck(:status_check_attempted_at)).to(
+ all(eq nil),
+ 'failed test precondition:
+ pending enrollments must not set status check attempted time',
+ )
+
+ expect(pending_enrollments.pluck(:status_check_completed_at)).to(
+ all(eq nil),
+ 'failed test precondition:
+ pending enrollments must not set status check completed time',
+ )
+
+ freeze_time do
+ job.perform(Time.zone.now)
+
+ expect(
+ pending_enrollments.
+ map(&:reload).
+ pluck(:status_check_attempted_at),
+ ).to(
+ all(eq Time.zone.now),
+ 'job must update status check attempted time for all pending enrollments',
+ )
+
+ expect(
+ pending_enrollments.
+ map(&:reload).
+ pluck(:status_check_completed_at),
+ ).to(
+ all(eq Time.zone.now),
+ 'job must update status check completed time for all pending enrollments',
+ )
+ end
+ end
+
+ it 'logs a message when the job starts' do
+ allow(InPersonEnrollment).to receive(:needs_status_check_on_waiting_enrollments).
+ and_return(pending_enrollments)
+ stub_request_proofing_results_with_responses(
+ request_failed_proofing_results_args,
+ request_in_progress_proofing_results_args,
+ request_in_progress_proofing_results_args,
+ request_failed_proofing_results_args,
+ )
+
+ job.perform(Time.zone.now)
+
+ expect(job_analytics).to have_logged_event(
+ 'GetUspsProofingResultsJob: Job started',
+ enrollments_count: 5,
+ reprocess_delay_minutes: 2.0,
+ job_name: 'GetUspsWaitingProofingResultsJob',
+ )
+ end
+
+ it 'logs a message with counts of various outcomes when the job completes (errored > 0)' do
+ allow(InPersonEnrollment).to receive(:needs_status_check_on_waiting_enrollments).
+ and_return(pending_enrollments)
+ stub_request_proofing_results_with_responses(
+ request_passed_proofing_results_args,
+ request_in_progress_proofing_results_args,
+ { status: 500 },
+ request_failed_proofing_results_args,
+ request_expired_proofing_results_args,
+ )
+
+ job.perform(Time.zone.now)
+
+ expect(job_analytics).to have_logged_event(
+ 'GetUspsProofingResultsJob: Job completed',
+ duration_seconds: anything,
+ enrollments_checked: 5,
+ enrollments_errored: 1,
+ enrollments_expired: 1,
+ enrollments_failed: 1,
+ enrollments_in_progress: 1,
+ enrollments_passed: 1,
+ percent_enrollments_errored: 20.00,
+ job_name: 'GetUspsWaitingProofingResultsJob',
+ )
+
+ expect(
+ job_analytics.events['GetUspsProofingResultsJob: Job completed'].
+ first[:duration_seconds],
+ ).to be >= 0.0
+ end
+
+ it 'logs a message with counts of various outcomes when the job completes (errored = 0)' do
+ allow(InPersonEnrollment).to receive(:needs_status_check_on_waiting_enrollments).
+ and_return(pending_enrollments)
+ stub_request_proofing_results_with_responses(
+ request_passed_proofing_results_args,
+ )
+
+ job.perform(Time.zone.now)
+
+ expect(job_analytics).to have_logged_event(
+ 'GetUspsProofingResultsJob: Job completed',
+ duration_seconds: anything,
+ enrollments_checked: 5,
+ enrollments_errored: 0,
+ enrollments_expired: 0,
+ enrollments_failed: 0,
+ enrollments_in_progress: 0,
+ enrollments_passed: 5,
+ percent_enrollments_errored: 0.00,
+ job_name: 'GetUspsWaitingProofingResultsJob',
+ )
+
+ expect(
+ job_analytics.events['GetUspsProofingResultsJob: Job completed'].
+ first[:duration_seconds],
+ ).to be >= 0.0
+ end
+
+ it 'logs a message with counts of various outcomes when the job completes
+ (no enrollments)' do
+ allow(InPersonEnrollment).to receive(:needs_status_check_on_waiting_enrollments).
+ and_return([])
+ stub_request_proofing_results_with_responses(
+ request_passed_proofing_results_args,
+ )
+
+ job.perform(Time.zone.now)
+
+ expect(job_analytics).to have_logged_event(
+ 'GetUspsProofingResultsJob: Job completed',
+ duration_seconds: anything,
+ enrollments_checked: 0,
+ enrollments_errored: 0,
+ enrollments_expired: 0,
+ enrollments_failed: 0,
+ enrollments_in_progress: 0,
+ enrollments_passed: 0,
+ percent_enrollments_errored: 0.00,
+ job_name: 'GetUspsWaitingProofingResultsJob',
+ )
+
+ expect(
+ job_analytics.events['GetUspsProofingResultsJob: Job completed'].
+ first[:duration_seconds],
+ ).to be >= 0.0
+ end
+ end
+ end
+
+ describe 'IPP disabled' do
+ before do
+ allow(IdentityConfig.store).to receive(:in_person_proofing_enabled).and_return(false)
+ allow(IdentityConfig.store).to(
+ receive(:in_person_enrollments_ready_job_enabled).and_return(true),
+ )
+ allow(IdentityConfig.store).to receive(:usps_mock_fallback).and_return(false)
+ end
+
+ it 'does not request any enrollment records' do
+ # no stubbing means this test will fail if the UspsInPersonProofing::Proofer
+ # tries to connect to the USPS API
+ job.perform Time.zone.now
+ end
+ end
+
+ describe 'Ready Job disabled' do
+ before do
+ allow(IdentityConfig.store).to receive(:in_person_proofing_enabled).and_return(true)
+ allow(IdentityConfig.store).to(
+ receive(:in_person_enrollments_ready_job_enabled).and_return(false),
+ )
+ allow(IdentityConfig.store).to receive(:usps_mock_fallback).and_return(false)
+ end
+
+ it 'does not request any enrollment records' do
+ # no stubbing means this test will fail if the UspsInPersonProofing::Proofer
+ # tries to connect to the USPS API
+ job.perform Time.zone.now
+ end
+ end
+ end
+end
diff --git a/spec/lib/telephony/alert_sender_spec.rb b/spec/lib/telephony/alert_sender_spec.rb
index 1484886511b..a206a54aeff 100644
--- a/spec/lib/telephony/alert_sender_spec.rb
+++ b/spec/lib/telephony/alert_sender_spec.rb
@@ -117,7 +117,9 @@
last_message = Telephony::Test::Message.messages.last
expect(last_message.to).to eq(recipient)
- expect(last_message.body).to eq(I18n.t('telephony.personal_key_sign_in_notice'))
+ expect(last_message.body).to eq(
+ t('telephony.personal_key_sign_in_notice', app_name: APP_NAME),
+ )
end
end
end
diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb
index e451630f1c5..8096d825a83 100644
--- a/spec/models/event_spec.rb
+++ b/spec/models/event_spec.rb
@@ -15,8 +15,11 @@
it 'has a translation for every event type' do
missing_translations = Event.event_types.keys.select do |event_type|
- translation = I18n.t("event_types.#{event_type}", raise: true)
- translation.empty?
+ I18n.t(
+ "event_types.#{event_type}",
+ raise: true,
+ ignore_test_helper_missing_interpolation: true,
+ ).empty?
rescue I18n::MissingTranslationData
true
end
diff --git a/spec/models/in_person_enrollment_spec.rb b/spec/models/in_person_enrollment_spec.rb
index 2098b5662b7..02e8da27762 100644
--- a/spec/models/in_person_enrollment_spec.rb
+++ b/spec/models/in_person_enrollment_spec.rb
@@ -180,6 +180,115 @@
end
end
+ describe 'status checks for ready and waiting enrollments' do
+ let(:check_interval) { ...1.hour.ago }
+ let!(:passed_enrollment) do
+ create(:in_person_enrollment, :passed, ready_for_status_check: true)
+ end
+ let!(:failing_enrollment) do
+ create(:in_person_enrollment, :failed, ready_for_status_check: true)
+ end
+ let!(:expired_enrollment) do
+ create(:in_person_enrollment, :expired, ready_for_status_check: true)
+ end
+ let!(:checked_pending_enrollment) do
+ create(
+ :in_person_enrollment, :pending, status_check_attempted_at: Time.zone.now,
+ ready_for_status_check: true
+ )
+ end
+ let!(:ready_enrollments) do
+ [
+ create(:in_person_enrollment, :pending, ready_for_status_check: true),
+ create(:in_person_enrollment, :pending, ready_for_status_check: true),
+ create(:in_person_enrollment, :pending, ready_for_status_check: true),
+ create(:in_person_enrollment, :pending, ready_for_status_check: true),
+ ]
+ end
+ let!(:needy_enrollments) do
+ [
+ create(:in_person_enrollment, :pending, ready_for_status_check: false),
+ create(:in_person_enrollment, :pending, ready_for_status_check: false),
+ create(:in_person_enrollment, :pending, ready_for_status_check: false),
+ create(:in_person_enrollment, :pending, ready_for_status_check: false),
+ ]
+ end
+
+ it 'returns only pending enrollments that are ready for status check' do
+ expect(InPersonEnrollment.count).to eq(12)
+ ready_results = InPersonEnrollment.needs_status_check_on_ready_enrollments(check_interval)
+ expect(ready_results.length).to eq ready_enrollments.length
+ expect(ready_results.pluck(:id)).to match_array ready_enrollments.pluck(:id)
+ expect(ready_results.pluck(:id)).not_to match_array needy_enrollments.pluck(:id)
+ ready_results.each do |result|
+ expect(result.pending?).to be_truthy
+ end
+ end
+
+ it 'indicates whether a ready enrollment needs a status check' do
+ expect(passed_enrollment.needs_status_check_on_ready_enrollment?(check_interval)).to(
+ be(false),
+ )
+ expect(failing_enrollment.needs_status_check_on_ready_enrollment?(check_interval)).to(
+ be(false),
+ )
+ expect(expired_enrollment.needs_status_check_on_ready_enrollment?(check_interval)).to(
+ be(false),
+ )
+ expect(checked_pending_enrollment.needs_status_check_on_ready_enrollment?(check_interval)).to(
+ be(false),
+ )
+ needy_enrollments.each do |enrollment|
+ expect(enrollment.needs_status_check_on_ready_enrollment?(check_interval)).to(
+ be(false),
+ )
+ end
+ ready_enrollments.each do |enrollment|
+ expect(enrollment.needs_status_check_on_ready_enrollment?(check_interval)).to(
+ be(true),
+ )
+ end
+ end
+
+ it 'returns only pending enrollments that are not ready for status check' do
+ expect(InPersonEnrollment.count).to eq(12)
+ waiting_results = InPersonEnrollment.needs_status_check_on_waiting_enrollments(check_interval)
+ expect(waiting_results.length).to eq needy_enrollments.length
+ expect(waiting_results.pluck(:id)).to match_array needy_enrollments.pluck(:id)
+ expect(waiting_results.pluck(:id)).not_to match_array ready_enrollments.pluck(:id)
+ waiting_results.each do |result|
+ expect(result.pending?).to be_truthy
+ end
+ end
+
+ it 'indicates whether a waiting enrollment needs a status check' do
+ expect(passed_enrollment.needs_status_check_on_waiting_enrollment?(check_interval)).to(
+ be(false),
+ )
+ expect(
+ failing_enrollment.needs_status_check_on_waiting_enrollment?(check_interval),
+ ).to(
+ be(false),
+ )
+ expect(
+ expired_enrollment.needs_status_check_on_waiting_enrollment?(check_interval),
+ ).to(
+ be(false),
+ )
+ expect(
+ checked_pending_enrollment.needs_status_check_on_waiting_enrollment?(check_interval),
+ ).to(
+ be(false),
+ )
+ needy_enrollments.each do |enrollment|
+ expect(enrollment.needs_status_check_on_waiting_enrollment?(check_interval)).to be(true)
+ end
+ ready_enrollments.each do |enrollment|
+ expect(enrollment.needs_status_check_on_waiting_enrollment?(check_interval)).to be(false)
+ end
+ end
+ end
+
describe 'minutes_since_established' do
let(:enrollment) do
create(
diff --git a/spec/models/profile_spec.rb b/spec/models/profile_spec.rb
index 39a1d7b51c6..17af2a7c199 100644
--- a/spec/models/profile_spec.rb
+++ b/spec/models/profile_spec.rb
@@ -319,6 +319,48 @@
end
end
+ describe '#activate_after_password_reset' do
+ it 'activates a profile after password reset' do
+ profile = create(
+ :profile,
+ user: user,
+ active: false,
+ deactivation_reason: :password_reset,
+ )
+
+ profile.activate_after_password_reset
+
+ expect(profile.active).to eq true
+ expect(profile.deactivation_reason).to eq nil
+ end
+
+ it 'does not activate a profile if it has a pending reason' do
+ profile = create(
+ :profile,
+ user: user,
+ active: false,
+ deactivation_reason: :password_reset,
+ fraud_review_pending_at: 1.day.ago,
+ )
+
+ expect { profile.activate_after_password_reset }.to raise_error
+ end
+
+ it 'does not activate a profile with non password_reset deactivation_reason' do
+ profile = create(
+ :profile,
+ user: user,
+ active: false,
+ deactivation_reason: :encryption_error,
+ )
+
+ profile.activate_after_password_reset
+
+ expect(profile.active).to eq false
+ expect(profile.deactivation_reason).to_not eq nil
+ end
+ end
+
describe '#activate_after_passing_review' do
it 'activates a profile if it passes fraud review' do
profile = create(
diff --git a/spec/support/i18n_helper.rb b/spec/support/i18n_helper.rb
new file mode 100644
index 00000000000..139019a3084
--- /dev/null
+++ b/spec/support/i18n_helper.rb
@@ -0,0 +1,13 @@
+module I18n
+ class << self
+ prepend(
+ Module.new do
+ def t(*args, ignore_test_helper_missing_interpolation: false, **kwargs)
+ result = super(*args, **kwargs)
+ return result if ignore_test_helper_missing_interpolation || !result.include?('%{')
+ raise "Missing interpolation in translated string: #{result}"
+ end
+ end,
+ )
+ end
+end
diff --git a/spec/views/idv/review/new.html.erb_spec.rb b/spec/views/idv/review/new.html.erb_spec.rb
index 53e7c6b7892..aee5227aafb 100644
--- a/spec/views/idv/review/new.html.erb_spec.rb
+++ b/spec/views/idv/review/new.html.erb_spec.rb
@@ -11,58 +11,20 @@
allow(view).to receive(:current_user).and_return(user)
allow(view).to receive(:step_indicator_steps).
and_return(Idv::Flows::DocAuthFlow::STEP_INDICATOR_STEPS)
- @applicant = {
- first_name: 'Some',
- last_name: 'One',
- ssn: '666-66-1234',
- dob: dob,
- address1: '123 Main St',
- city: 'Somewhere',
- state: 'MO',
- zipcode: '12345',
- phone: '+1 (213) 555-0000',
- }
+ allow(view).to receive(:step_indicator_step).and_return(:secure_account)
render
end
- it 'renders all steps' do
- expect(rendered).to have_content('Some One')
- expect(rendered).to have_content('123 Main St')
- expect(rendered).to have_content('Somewhere')
- expect(rendered).to have_content('MO')
- expect(rendered).to have_content('12345')
- expect(rendered).to have_content('666-66-1234')
- expect(rendered).to have_content('+1 213-555-0000')
- expect(rendered).to have_content('March 29, 1972')
- end
-
it 'renders the correct content heading' do
expect(rendered).to have_content t('idv.titles.session.review', app_name: APP_NAME)
end
- it 'contains an accordion with verified user information' do
- accordion_selector = generate_class_selector('usa-accordion')
- expect(rendered).to have_xpath("//#{accordion_selector}")
- end
-
- it 'renders the correct header for the accordion' do
- expect(rendered).to have_content(t('idv.messages.review.intro'))
- end
-
it 'shows the step indicator' do
expect(view.content_for(:pre_flash_content)).to have_css(
'.step-indicator__step--current',
text: t('step_indicator.flows.idv.secure_account'),
)
end
-
- context 'with an american-style dob' do
- let(:dob) { '12/31/1970' }
-
- it 'renders correctly' do
- expect(rendered).to have_selector('.h4.text-bold', text: 'December 31, 1970')
- end
- end
end
end
diff --git a/spec/views/phone_setup/spam_protection.html.erb_spec.rb b/spec/views/phone_setup/spam_protection.html.erb_spec.rb
index f79897e0e1b..8d5ef4a66c1 100644
--- a/spec/views/phone_setup/spam_protection.html.erb_spec.rb
+++ b/spec/views/phone_setup/spam_protection.html.erb_spec.rb
@@ -43,6 +43,31 @@
href: two_factor_options_path,
)
end
+
+ it 'does not render cancel option' do
+ expect(rendered).to_not have_link(
+ t('links.cancel'),
+ href: account_path,
+ )
+ end
+ end
+
+ context 'fully registered user adding new phone' do
+ let(:user) { create(:user, :fully_registered) }
+
+ it 'does not render additional troubleshooting option to two factor options' do
+ expect(rendered).to_not have_link(
+ t('two_factor_authentication.login_options_link_text'),
+ href: two_factor_options_path,
+ )
+ end
+
+ it 'renders cancel option' do
+ expect(rendered).to have_link(
+ t('links.cancel'),
+ href: account_path,
+ )
+ end
end
context 'with configured recaptcha site key' do