diff --git a/app/views/event_disavowal/new.html.erb b/app/views/event_disavowal/new.html.erb
index 7c1456793ce..6a93cc4e865 100644
--- a/app/views/event_disavowal/new.html.erb
+++ b/app/views/event_disavowal/new.html.erb
@@ -12,8 +12,10 @@
input_html: { value: @disavowal_token, name: :disavowal_token } %>
<%= render PasswordToggleComponent.new(
form: f,
- label: t('forms.passwords.edit.labels.password'),
- required: true,
+ field_options: {
+ label: t('forms.passwords.edit.labels.password'),
+ required: true,
+ },
) %>
<%= render 'devise/shared/password_strength', forbidden_passwords: @forbidden_passwords %>
<%= f.submit t('forms.passwords.edit.buttons.submit'), class: 'margin-bottom-4' %>
diff --git a/app/views/idv/doc_auth/welcome.html.erb b/app/views/idv/doc_auth/welcome.html.erb
index f769b53d429..081304c7fda 100644
--- a/app/views/idv/doc_auth/welcome.html.erb
+++ b/app/views/idv/doc_auth/welcome.html.erb
@@ -25,12 +25,26 @@
<%= t('doc_auth.instructions.welcome') %>
<%= render ProcessListComponent.new(heading_level: :h3, class: 'margin-y-3') do |c| %>
- <%= c.item(heading: t('doc_auth.instructions.bullet1')) %>
+ <%= c.item(heading: t('doc_auth.instructions.bullet1')) do %>
+
<%= t('doc_auth.instructions.text1') %>
+ <% end %>
<%= c.item(heading: t('doc_auth.instructions.bullet2')) do %>
<%= t('doc_auth.instructions.text2') %>
<% end %>
<%= c.item(heading: t('doc_auth.instructions.bullet3')) do %>
-
<%= t('doc_auth.instructions.text3') %>
+
+ <%= t('doc_auth.instructions.text3') %>
+ <%= new_window_link_to(
+ t('idv.troubleshooting.options.learn_more_address_verification_options'),
+ help_center_redirect_path(
+ category: 'verify-your-identity',
+ article: 'phone-number-and-phone-plan-in-your-name',
+ flow: :idv,
+ step: :welcome,
+ location: 'you_will_need',
+ ),
+ ) %>
+
<% end %>
<% end %>
diff --git a/app/views/idv/review/new.html.erb b/app/views/idv/review/new.html.erb
index 6da2bea0e4b..a86b480a389 100644
--- a/app/views/idv/review/new.html.erb
+++ b/app/views/idv/review/new.html.erb
@@ -23,15 +23,16 @@
<%= simple_form_for(
current_user,
url: idv_review_path,
- html: { autocomplete: 'off', method: :put, class: 'margin-top-6' },
+ html: { autocomplete: 'off', method: :put, class: 'margin-top-4' },
) do |f| %>
<%= render PasswordToggleComponent.new(
form: f,
- label: t('idv.form.password'),
- required: true,
- wrapper_html: { class: 'margin-bottom-0' },
+ field_options: {
+ label: t('idv.form.password'),
+ required: true,
+ },
) %>
-
+
<%= link_to(t('idv.forgot_password.link_text'), idv_forgot_password_url, class: 'margin-left-1') %>
<%= render AccordionComponent.new do |c| %>
diff --git a/app/views/layouts/account_side_nav.html.erb b/app/views/layouts/account_side_nav.html.erb
index c010436c021..a8417b65cda 100644
--- a/app/views/layouts/account_side_nav.html.erb
+++ b/app/views/layouts/account_side_nav.html.erb
@@ -7,7 +7,7 @@
<% end %>
<% content_for :content do %>
-
+
-
+
<%= render 'accounts/side_nav' %>
-
+
<%= yield %>
-
+
<% end %>
<%= javascript_packs_tag_once('navigation') %>
-<%= render template: 'layouts/base', locals: { disable_card: true } %>
+<%= render template: 'layouts/base', locals: { disable_card: true, user_main_tag: false } %>
diff --git a/app/views/layouts/base.html.erb b/app/views/layouts/base.html.erb
index 8ace59a8926..8ced147618e 100644
--- a/app/views/layouts/base.html.erb
+++ b/app/views/layouts/base.html.erb
@@ -46,7 +46,7 @@
<% if IdentityConfig.store.newrelic_browser_key.present? && IdentityConfig.store.newrelic_browser_app_id.present? %>
- <%= render 'shared/newrelic/browser_instrumentation' %>
+ <%= render 'shared/newrelic/browser_instrumentation' %>
<% end %>
<%= yield(:head) if content_for?(:head) %>
@@ -57,13 +57,18 @@
<%= yield(:mobile_nav) if content_for?(:mobile_nav) %>
<%= render 'shared/banner' %>
-
+
+ <%= content_tag(
+ local_assigns[:user_main_tag] == false ? 'div' : 'main',
+ class: 'site-wrap bg-primary-lighter',
+ id: local_assigns[:user_main_tag] == false ? nil : 'main-content',
+ ) do %>
<%= yield(:pre_flash_content) if content_for?(:pre_flash_content) %>
<%= render FlashComponent.new(flash: flash) %>
<%= content_for?(:content) ? yield(:content) : yield %>
-
+ <% end %>
<%= render 'shared/footer_lite' %>
diff --git a/app/views/mfa_confirmation/new.html.erb b/app/views/mfa_confirmation/new.html.erb
index a0aa93a3dba..6a6c2371313 100644
--- a/app/views/mfa_confirmation/new.html.erb
+++ b/app/views/mfa_confirmation/new.html.erb
@@ -11,9 +11,9 @@
<%= simple_form_for(
current_user,
url: reauthn_user_password_path,
- html: { autocomplete: 'off', method: 'post', class: 'margin-top-6' },
+ html: { autocomplete: 'off', method: 'post', class: 'margin-top-4' },
) do |f| %>
- <%= render PasswordToggleComponent.new(form: f, required: true) %>
+ <%= render PasswordToggleComponent.new(form: f, field_options: { required: true }) %>
<%= f.submit t('forms.buttons.continue'), class: 'display-block margin-y-5' %>
<% end %>
<%= render 'shared/cancel', link: account_path %>
diff --git a/app/views/password_capture/new.html.erb b/app/views/password_capture/new.html.erb
index f7e6016a4b7..35b36d39423 100644
--- a/app/views/password_capture/new.html.erb
+++ b/app/views/password_capture/new.html.erb
@@ -7,12 +7,14 @@
as: :user,
url: capture_password_url,
method: :post,
- html: { autocomplete: 'off', class: 'margin-top-6' },
+ html: { autocomplete: 'off', class: 'margin-top-4' },
) do |f| %>
<%= render PasswordToggleComponent.new(
form: f,
- label: t('account.index.password'),
- required: true,
+ field_options: {
+ label: t('account.index.password'),
+ required: true,
+ },
) %>
<%= f.submit t('forms.buttons.submit.default'), class: 'display-block margin-y-5' %>
<% end %>
diff --git a/app/views/shared/_ssn_field.html.erb b/app/views/shared/_ssn_field.html.erb
index 522a2ba6f60..a90d956379b 100644
--- a/app/views/shared/_ssn_field.html.erb
+++ b/app/views/shared/_ssn_field.html.erb
@@ -5,18 +5,19 @@ locals:
<%# maxlength set and includes '-' delimiters to work around cleave bug %>
<%= render PasswordToggleComponent.new(
- name: :ssn,
form: f,
- as: :password,
- label: t('idv.form.ssn_label_html'),
toggle_label: t('forms.ssn.show'),
- toggle_position: :bottom,
- hint: t('forms.example') + ' 123-45-6789',
- required: true,
- pattern: '^\d{3}-?\d{2}-?\d{4}$',
- maxlength: 11,
- input_html: { aria: { invalid: false }, class: 'ssn-toggle usa-input', value: '' },
- error_messages: { patternMismatch: t('idv.errors.pattern_mismatch.ssn') },
+ field_options: {
+ name: :ssn,
+ as: :password,
+ label: t('idv.form.ssn_label_html'),
+ hint: t('forms.example') + ' 123-45-6789',
+ required: true,
+ pattern: '^\d{3}-?\d{2}-?\d{4}$',
+ maxlength: 11,
+ input_html: { aria: { invalid: false }, class: 'ssn-toggle usa-input', value: '' },
+ error_messages: { patternMismatch: t('idv.errors.pattern_mismatch.ssn') },
+ },
) %>
<%= javascript_packs_tag_once('ssn-field') %>
diff --git a/app/views/sign_up/passwords/new.html.erb b/app/views/sign_up/passwords/new.html.erb
index 3b11dedb889..62b21871791 100644
--- a/app/views/sign_up/passwords/new.html.erb
+++ b/app/views/sign_up/passwords/new.html.erb
@@ -13,14 +13,16 @@
) do |f| %>
<%= render PasswordToggleComponent.new(
form: f,
- label: t('forms.password'),
- required: true,
- input_html: { aria: { describedby: 'password-description' } },
+ field_options: {
+ label: t('forms.password'),
+ required: true,
+ input_html: { aria: { describedby: 'password-description' } },
+ },
) %>
<%= render 'devise/shared/password_strength', forbidden_passwords: @forbidden_passwords %>
<%= hidden_field_tag :confirmation_token, @confirmation_token, id: 'confirmation_token' %>
<%= f.input :request_id, as: :hidden, input_html: { value: params[:request_id] || request_id } %>
- <%= f.submit t('forms.buttons.continue'), class: 'margin-bottom-5' %>
+ <%= f.submit t('forms.buttons.continue'), class: 'display-block margin-y-5' %>
<% end %>
<%= render 'shared/password_accordion' %>
diff --git a/app/views/users/delete/show.html.erb b/app/views/users/delete/show.html.erb
index d4424f8cb30..238265e1bb9 100644
--- a/app/views/users/delete/show.html.erb
+++ b/app/views/users/delete/show.html.erb
@@ -24,12 +24,14 @@
<%= render PasswordToggleComponent.new(
form: f,
- name: :password,
- label: t('idv.form.password'),
- required: true,
+ field_options: {
+ name: :password,
+ label: t('idv.form.password'),
+ required: true,
+ },
) %>
- <%= f.submit t('users.delete.actions.delete'), danger: true, class: 'margin-top-1 margin-bottom-2' %>
+ <%= f.submit t('users.delete.actions.delete'), danger: true, class: 'margin-top-5 margin-bottom-2' %>
<% end %>
<%= link_to(
diff --git a/app/views/users/passwords/edit.html.erb b/app/views/users/passwords/edit.html.erb
index 943b2130cab..c59fba7e63f 100644
--- a/app/views/users/passwords/edit.html.erb
+++ b/app/views/users/passwords/edit.html.erb
@@ -13,13 +13,15 @@
<%= f.error_notification %>
<%= render PasswordToggleComponent.new(
form: f,
- name: :password,
- label: t('forms.passwords.edit.labels.password'),
- required: true,
- input_html: { aria: { describedby: 'password-description' } },
+ field_options: {
+ name: :password,
+ label: t('forms.passwords.edit.labels.password'),
+ required: true,
+ input_html: { aria: { describedby: 'password-description' } },
+ },
) %>
<%= render 'devise/shared/password_strength', forbidden_passwords: @forbidden_passwords %>
- <%= f.submit t('forms.buttons.submit.update'), class: 'margin-top-2 margin-bottom-4' %>
+ <%= f.submit t('forms.buttons.submit.update'), class: 'display-block margin-top-5 margin-bottom-4' %>
<% end %>
<%= render 'shared/password_accordion' %>
diff --git a/app/views/users/verify_password/new.html.erb b/app/views/users/verify_password/new.html.erb
index 414ac55d7f6..b3d8de22f83 100644
--- a/app/views/users/verify_password/new.html.erb
+++ b/app/views/users/verify_password/new.html.erb
@@ -12,11 +12,13 @@
) do |f| %>
<%= render PasswordToggleComponent.new(
form: f,
- name: :password,
- label: t('idv.form.password'),
- required: true,
+ field_options: {
+ name: :password,
+ label: t('idv.form.password'),
+ required: true,
+ },
) %>
- <%= f.submit t('forms.buttons.continue') %>
+ <%= f.submit t('forms.buttons.continue'), class: 'margin-top-5' %>
<% end %>
diff --git a/config/application.yml.default b/config/application.yml.default
index c673ada06ce..0c142fadc86 100644
--- a/config/application.yml.default
+++ b/config/application.yml.default
@@ -44,6 +44,7 @@ address_identity_proofing_supported_country_codes: '["AS", "GU", "MP", "PR", "US
arcgis_api_root_url: 'https://gis.gsa.gov'
arcgis_api_username: ''
arcgis_api_password: ''
+arcgis_search_enabled: false
asset_host: ''
async_wait_timeout_seconds: 60
async_stale_job_timeout_seconds: 300
@@ -212,7 +213,9 @@ phone_setups_per_ip_track_only_mode: false
pii_lock_timeout_in_minutes: 30
pinpoint_sms_sender_id: 'aaa'
pinpoint_sms_configs: '[]'
+pinpoint_sms_pool_size: 5
pinpoint_voice_configs: '[]'
+pinpoint_voice_pool_size: 5
piv_cac_service_url: https://localhost:8443/
piv_cac_service_timeout: 5.0
piv_cac_verify_token_url: https://localhost:8443/
diff --git a/config/initializers/job_configurations.rb b/config/initializers/job_configurations.rb
index f3074992986..6e27918a9a5 100644
--- a/config/initializers/job_configurations.rb
+++ b/config/initializers/job_configurations.rb
@@ -22,24 +22,6 @@
cron: cron_5m,
args: -> { [Time.zone.now] },
},
- # Send OMB Fitara report to s3
- omb_fitara_report: {
- class: 'Reports::OmbFitaraReport',
- cron: cron_24h,
- args: -> { [Time.zone.today] },
- },
- # Send Unique Monthly Auths Report to S3
- unique_monthly_auths: {
- class: 'Reports::UniqueMonthlyAuthsReport',
- cron: cron_24h,
- args: -> { [Time.zone.today] },
- },
- # Send Unique Yearly Auths Report to S3
- unique_yearly_auths: {
- class: 'Reports::UniqueYearlyAuthsReport',
- cron: cron_24h,
- args: -> { [Time.zone.today] },
- },
# Send Agency User Counts Report to S3
agency_user_counts: {
class: 'Reports::AgencyUserCountsReport',
@@ -64,30 +46,12 @@
cron: cron_24h,
args: -> { [Time.zone.today] },
},
- # Send Doc Auth Funnel Report to S3
- doc_auth_funnel_report: {
- class: 'Reports::DocAuthFunnelReport',
- cron: cron_24h,
- args: -> { [Time.zone.today] },
- },
# Proofing Costs Report to S3
proofing_costs: {
class: 'Reports::ProofingCostsReport',
cron: cron_24h,
args: -> { [Time.zone.today] },
},
- # Doc auth drop off rates per sprint to S3
- doc_auth_dropoff_per_sprint: {
- class: 'Reports::DocAuthDropOffRatesPerSprintReport',
- cron: cron_24h,
- args: -> { [Time.zone.today] },
- },
- # SP Costs Report to S3
- sp_costs: {
- class: 'Reports::SpCostReport',
- cron: cron_24h,
- args: -> { [Time.zone.today] },
- },
# Agency Invoice Supplement Report to S3
sp_invoice_supplement_by_iaa: {
class: 'Reports::AgencyInvoiceIaaSupplementReport',
@@ -111,12 +75,6 @@
cron: cron_24h,
args: -> { [Time.zone.today] },
},
- # Total SP Costs Report to S3
- total_sp_costs: {
- class: 'Reports::TotalSpCostReport',
- cron: cron_24h,
- args: -> { [Time.zone.today] },
- },
# Total IAL2 Costs Report to S3
total_ial2_costs: {
class: 'Reports::TotalIal2CostsReport',
@@ -129,24 +87,6 @@
cron: cron_24h,
args: -> { [Time.zone.today] },
},
- # SP Active Users Report to S3
- sp_active_users_period_pf_performance: {
- class: 'Reports::SpActiveUsersOverPeriodOfPerformanceReport',
- cron: cron_24h,
- args: -> { [Time.zone.today] },
- },
- # Doc auth drop off rates report
- doc_auth_dropoff_rates: {
- class: 'Reports::DocAuthDropOffRatesReport',
- cron: cron_24h,
- args: -> { [Time.zone.today] },
- },
- # IAA Billing Report
- iaa_billing_report: {
- class: 'Reports::IaaBillingReport',
- cron: cron_24h,
- args: -> { [Time.zone.today] },
- },
# Send deleted user accounts to S3
deleted_user_accounts: {
class: 'Reports::DeletedUserAccountsReport',
@@ -217,6 +157,12 @@
class: 'ThreatMetrixJsVerificationJob',
cron: cron_1h,
},
+ # Batch up IRS Attempts API events
+ irs_attempt_events_aggregator: {
+ class: 'IrsAttemptsEventsBatchJob',
+ cron: cron_1h,
+ args: -> { [timestamp: Time.zone.now - 1.hour] },
+ },
}
end
# rubocop:enable Metrics/BlockLength
diff --git a/config/locales/doc_auth/en.yml b/config/locales/doc_auth/en.yml
index 60d1c8a9cf9..431f1c48f87 100644
--- a/config/locales/doc_auth/en.yml
+++ b/config/locales/doc_auth/en.yml
@@ -177,9 +177,9 @@ en:
welcome_html: '%{sp_name} needs to make sure you are you — not someone
pretending to be you.'
instructions:
- bullet1: Your State-issued ID
- bullet2: Your Social Security number
- bullet3: A phone number on a phone plan associated with your name
+ bullet1: State-issued ID
+ bullet2: Social Security number
+ bullet3: Phone number
consent: By checking this box, you are letting %{app_name} ask for, use, keep,
and share your personal information. We will only use it to verify your
identity.
@@ -193,11 +193,11 @@ en:
switch_back_image: Arrow pointing from phone to computer
test_ssn: In the test environment only SSNs that begin with “900-” or “666-” are
considered valid. Do not enter real PII in this field.
+ text1: Your ID cannot be expired.
text2: You will not need the card with you.
- text3: You do not need to be the primary account holder on your phone plan. If
- your phone number doesn’t work, we can send you a verification code by
- mail.
- welcome: 'To verify your identity, you will need:'
+ text3: We’ll call or text this number to verify your identity. If we can’t
+ verify your phone number, you can verify by mail instead.
+ welcome: 'You will need your:'
tips:
capture_troubleshooting_blurry: The photo you added is too blurry.
capture_troubleshooting_clean: Make sure that the barcode is not damaged or
diff --git a/config/locales/doc_auth/es.yml b/config/locales/doc_auth/es.yml
index a72259a3bd9..9de1454fb34 100644
--- a/config/locales/doc_auth/es.yml
+++ b/config/locales/doc_auth/es.yml
@@ -156,7 +156,7 @@ es:
estado?'
upload_from_phone: Tome una foto con un teléfono móvil para cargar su identificación
verify_identity: Verifique su identidad
- welcome: Empiece con la verificación de su identidad
+ welcome: Comience a verificar su identidad
info:
camera_required: Su teléfono móvil debe tener una cámara y un navegador web.
capture_status_big_document: Demasiado cerca
@@ -207,12 +207,12 @@ es:
upload_no_image_storage: No almacenamos imágenes que cargue. Solo verificamos su identidad.
verify_identity: Le preguntaremos sus datos personales para verificar su
identidad comparándola con los registros públicos.
- welcome_html: '%{sp_name} necesita asegurarse de que sea usted y no alguien que
- se haga pasar por usted.'
+ welcome_html: '%{sp_name} necesita asegurarse de que es usted y no es alguien
+ que se hace pasar por usted.'
instructions:
- bullet1: Su documento de identidad emitido por el estado.
- bullet2: Su Número de Seguridad Social.
- bullet3: Un número de teléfono con un plan tarifario vinculado a su nombre.
+ bullet1: Documento de identidad emitido por el estado.
+ bullet2: Número de seguro social
+ bullet3: Número de teléfono
consent: Al marcar esta casilla, usted permite que %{app_name} solicite,
utilice, conserve y comparta su información personal. Solo la
utilizaremos para verificar su identidad.
@@ -228,11 +228,12 @@ es:
switch_back_image: Flecha que apunta del teléfono a la computadora
test_ssn: En el entorno de prueba solo los SSN que comienzan con “900-” o “666-”
se consideran válidos. No ingrese PII real en este campo.
- text2: No es necesario disponer de la credencial.
- text3: No es necesario que usted sea el titular principal de la cuenta de su
- plan de teléfono. Si su número de teléfono no funciona, podemos enviarle
- un código de verificación por correo.
- welcome: 'Deberá contar con lo siguiente para verificar su identidad:'
+ text1: Su documento de identidad no puede estar caducado.
+ text2: No necesitará la tarjeta con usted.
+ text3: Le llamaremos o enviaremos un mensaje de texto a este número para
+ verificar su identidad. Si no podemos verificar su número de teléfono,
+ puede hacerlo por correo.
+ welcome: 'Necesitará su:'
tips:
capture_troubleshooting_blurry: La foto que adjuntó está demasiado borrosa.
capture_troubleshooting_clean: Asegúrese de que el código de barras no esté
diff --git a/config/locales/doc_auth/fr.yml b/config/locales/doc_auth/fr.yml
index 199c991e56d..8f9f82f1201 100644
--- a/config/locales/doc_auth/fr.yml
+++ b/config/locales/doc_auth/fr.yml
@@ -221,9 +221,9 @@ fr:
welcome_html: '%{sp_name} doit s’assurer que vous êtes bien vous, et non
quelqu’un qui se fait passer pour vous.'
instructions:
- bullet1: Votre carte d’identité émise par l’État
- bullet2: Votre numéro de sécurité sociale
- bullet3: Un numéro de téléphone associé à un forfait téléphonique à votre nom
+ bullet1: Carte d’identité délivrée par l’État
+ bullet2: Numéro de sécurité sociale
+ bullet3: Numéro de téléphone
consent: En cochant cette case, vous autorisez %{app_name} à demander, utiliser,
conserver et partager vos renseignements personnels. Nous ne les
utiliserons que pour vérifier votre identité.
@@ -239,12 +239,12 @@ fr:
test_ssn: Dans l’environnement de test seuls les SSN commençant par “900-” ou
“900-” sont considérés comme valides. N’entrez pas de vrais PII dans ce
champ.
- text2: Vous n’aurez pas besoin de la carte avec vous.
- text3: Il n’est pas nécessaire que vous soyez le titulaire principal du compte
- de votre forfait téléphonique. Si votre numéro de téléphone ne
- fonctionne pas, nous pouvons vous envoyer un code de vérification par
- courrier.
- welcome: 'Pour vérifier votre identité, vous aurez besoin de:'
+ text1: Votre carte d’identité ne doit pas être expirée.
+ text2: Vous n’aurez pas besoin de la carte sur vous.
+ text3: Nous appellerons ou enverrons un SMS à ce numéro pour vérifier votre
+ identité. Si nous ne pouvons pas vérifier votre numéro de téléphone,
+ vous pouvez le faire par courrier.
+ welcome: 'Vous aurez besoin de votre:'
tips:
capture_troubleshooting_blurry: La photo que vous avez ajoutée est trop floue.
capture_troubleshooting_clean: Assurez-vous que le code-barres ne soit pas
diff --git a/config/newrelic.yml b/config/newrelic.yml
index 16875625c8a..47e2184e600 100644
--- a/config/newrelic.yml
+++ b/config/newrelic.yml
@@ -1,4 +1,6 @@
common: &default_settings
+ code_level_metrics:
+ enabled: false
application_logging:
forwarding:
enabled: false
diff --git a/config/routes.rb b/config/routes.rb
index e5544dc37bd..af2f5ef6310 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -350,6 +350,7 @@
as: :in_person_ready_to_verify
get '/in_person/usps_locations' => 'in_person/usps_locations#index'
put '/in_person/usps_locations' => 'in_person/usps_locations#update'
+ post '/in_person/addresses' => 'in_person/address_search#index'
get '/in_person/:step' => 'in_person#show', as: :in_person_step
put '/in_person/:step' => 'in_person#update'
diff --git a/db/primary_migrate/20221104210336_drop_email_addresses_user_id_last_sign_in_at_desc_index.rb b/db/primary_migrate/20221104210336_drop_email_addresses_user_id_last_sign_in_at_desc_index.rb
new file mode 100644
index 00000000000..505d8a4a80a
--- /dev/null
+++ b/db/primary_migrate/20221104210336_drop_email_addresses_user_id_last_sign_in_at_desc_index.rb
@@ -0,0 +1,5 @@
+class DropEmailAddressesUserIdLastSignInAtDescIndex < ActiveRecord::Migration[7.0]
+ def change
+ remove_index :email_addresses, name: "index_email_addresses_on_user_id_and_last_sign_in_at"
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 3bc7ee214c0..31a8c6afe9b 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: 2022_11_04_204944) do
+ActiveRecord::Schema[7.0].define(version: 2022_11_04_210336) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_stat_statements"
enable_extension "pgcrypto"
@@ -210,7 +210,6 @@
t.index ["confirmation_token"], name: "index_email_addresses_on_confirmation_token", unique: true
t.index ["email_fingerprint", "user_id"], name: "index_email_addresses_on_email_fingerprint_and_user_id", unique: true
t.index ["email_fingerprint"], name: "index_email_addresses_on_email_fingerprint", unique: true, where: "(confirmed_at IS NOT NULL)"
- t.index ["user_id", "last_sign_in_at"], name: "index_email_addresses_on_user_id_and_last_sign_in_at", order: { last_sign_in_at: :desc }
t.index ["user_id"], name: "index_email_addresses_on_user_id"
end
diff --git a/lib/base16.rb b/lib/base16.rb
new file mode 100644
index 00000000000..6ffc5fa64c4
--- /dev/null
+++ b/lib/base16.rb
@@ -0,0 +1,10 @@
+# See https://www.rfc-editor.org/rfc/rfc4648#section-8
+class Base16
+ def self.encode16(str)
+ str.unpack1('H*').tap(&:upcase!)
+ end
+
+ def self.decode16(str)
+ [str].pack('H*')
+ end
+end
diff --git a/lib/identity_config.rb b/lib/identity_config.rb
index 17652ad527b..6c331fa6129 100644
--- a/lib/identity_config.rb
+++ b/lib/identity_config.rb
@@ -108,6 +108,7 @@ def self.build_store(config_map)
config.add(:arcgis_api_root_url, type: :string)
config.add(:arcgis_api_username, type: :string)
config.add(:arcgis_api_password, type: :string)
+ config.add(:arcgis_search_enabled, type: :boolean)
config.add(:aws_http_retry_limit, type: :integer)
config.add(:aws_http_retry_max_delay, type: :integer)
config.add(:aws_http_timeout, type: :integer)
@@ -297,7 +298,9 @@ def self.build_store(config_map)
config.add(:pii_lock_timeout_in_minutes, type: :integer)
config.add(:pinpoint_sms_configs, type: :json)
config.add(:pinpoint_sms_sender_id, type: :string, allow_nil: true)
+ config.add(:pinpoint_sms_pool_size, type: :integer)
config.add(:pinpoint_voice_configs, type: :json)
+ config.add(:pinpoint_voice_pool_size, type: :integer)
config.add(:piv_cac_service_url)
config.add(:piv_cac_service_timeout, type: :float)
config.add(:piv_cac_verify_token_secret)
diff --git a/lib/tasks/attempts.rake b/lib/tasks/attempts.rake
index f4df1bf7447..59c1c60f030 100644
--- a/lib/tasks/attempts.rake
+++ b/lib/tasks/attempts.rake
@@ -1,3 +1,5 @@
+require 'base16'
+
namespace :attempts do
desc 'Retrieve events via the API'
task fetch_events: :environment do
@@ -13,29 +15,27 @@ namespace :attempts do
"Bearer #{IdentityConfig.store.irs_attempt_api_csp_id} #{auth_token}"
end
- encrypted_data = Base64.strict_decode64(resp.body)
iv = Base64.strict_decode64(resp.headers['x-payload-iv'])
encrypted_key = Base64.strict_decode64(resp.headers['x-payload-key'])
private_key = OpenSSL::PKey::RSA.new(File.read(private_key_path))
key = private_key.private_decrypt(encrypted_key)
decrypted = IrsAttemptsApi::EnvelopeEncryptor.decrypt(
- encrypted_data: encrypted_data, key: key, iv: iv,
+ encrypted_data: resp.body, key: key, iv: iv,
)
- events = JSON.parse(decrypted)
+ events = decrypted.split("\r\n")
+ puts "Found #{events.count} events"
if File.exist?(private_key_path)
puts events.any? ? 'Decrypted events:' : 'No events returned.'
- events.each do |_jti, jwes|
- jwes.each do |_key_id, jwe|
- begin
- pp JSON.parse(JWE.decrypt(jwe, private_key))
- rescue
- puts 'Failed to parse/decrypt event!'
- end
- puts "\n"
+ events.each do |jwe|
+ begin
+ pp JSON.parse(JWE.decrypt(jwe, private_key))
+ rescue
+ puts 'Failed to parse/decrypt event!'
end
+ puts "\n"
end
else
puts "No decryption key in #{private_key_path}; cannot decrypt events."
diff --git a/lib/tasks/dev.rake b/lib/tasks/dev.rake
index 24bf2ec4be5..b59ee15299f 100644
--- a/lib/tasks/dev.rake
+++ b/lib/tasks/dev.rake
@@ -93,6 +93,8 @@ namespace :dev do
enrollment_status = InPersonEnrollment.statuses[raw_enrollment_status]
is_established = ['pending', 'passed', 'failed', 'expired'].include?(raw_enrollment_status)
+ create_in_usps = !!ENV['CREATE_PENDING_ENROLLMENT_IN_USPS']
+
InPersonEnrollment.transaction do
(0...num_users).each do |n|
email_addr = "testuser#{n}@example.com"
@@ -101,22 +103,48 @@ namespace :dev do
if is_established
unless raw_enrollment_status == 'pending' && !user.pending_in_person_enrollment.nil?
profile = Profile.new(user: user)
+
+ # Convert index to a string of letters to be a valid last name for the USPS API
+ usps_compatible_number_alternative = n.to_s.chars.map do |c|
+ ('a'.ord + c.to_i).chr
+ end.join('')
+
pii = Pii::Attributes.new_from_hash(
first_name: 'Test',
- last_name: "User #{n}",
+ last_name: "User #{usps_compatible_number_alternative}",
dob: '1970-05-01',
ssn: "666-#{n}", # doesn't need to be legit 9 digits, just unique
+ address1: '1200 FORESTVILLE DR',
+ city: 'GREAT FALLS',
+ state: 'VA',
+ zipcode: '22066',
)
personal_key = profile.encrypt_pii(pii, pw)
- enrollment = InPersonEnrollment.create!(
- user: user,
- profile: profile,
- status: enrollment_status,
- enrollment_established_at: Time.zone.now - random.rand(0..5).days,
- unique_id: SecureRandom.hex(9),
- enrollment_code: SecureRandom.hex(16),
- )
- enrollment.profile.activate if raw_enrollment_status == 'passed'
+
+ if raw_enrollment_status === 'pending' && create_in_usps
+ enrollment = InPersonEnrollment.find_or_initialize_by(
+ user: user,
+ status: :establishing,
+ profile: profile,
+ )
+ enrollment.save!
+
+ UspsInPersonProofing::EnrollmentHelper.schedule_in_person_enrollment(
+ user,
+ pii,
+ )
+ else
+ enrollment = InPersonEnrollment.create!(
+ user: user,
+ profile: profile,
+ status: enrollment_status,
+ enrollment_established_at: Time.zone.now - random.rand(0..5).days,
+ unique_id: SecureRandom.hex(9),
+ enrollment_code: SecureRandom.hex(16),
+ )
+
+ enrollment.profile.activate if raw_enrollment_status == 'passed'
+ end
Rails.logger.warn "email=#{email_addr} personal_key=#{personal_key}"
end
else
diff --git a/lib/telephony/pinpoint/sms_sender.rb b/lib/telephony/pinpoint/sms_sender.rb
index a4f77d31932..ce63b395533 100644
--- a/lib/telephony/pinpoint/sms_sender.rb
+++ b/lib/telephony/pinpoint/sms_sender.rb
@@ -13,6 +13,20 @@ class SmsSender
'UNKNOWN_FAILURE' => UnknownFailureError,
}.freeze
+ # One connection pool per config (aka per-region)
+ # @param [Hash
>]
+ CLIENT_POOL = Hash.new do |h, sms_config|
+ h[sms_config] = ConnectionPool.new(size: IdentityConfig.store.pinpoint_voice_pool_size) do
+ credentials = AwsCredentialBuilder.new(sms_config).call
+
+ Aws::Pinpoint::Client.new(
+ region: sms_config.region,
+ retry_limit: 0,
+ credentials: credentials,
+ )
+ end
+ end
+
# rubocop:disable Metrics/BlockLength
# rubocop:disable Lint/UnusedMethodArgument
# @return [Response]
@@ -25,52 +39,51 @@ def send(message:, to:, country_code:, otp: nil)
sender_id = Telephony.config.country_sender_ids[country_code.to_s]
Telephony.config.pinpoint.sms_configs.each do |sms_config|
start = Time.zone.now
- client = build_client(sms_config)
- next if client.nil?
-
- sender_config = build_sender_config(country_code, sms_config, sender_id)
-
- pinpoint_response = client.send_messages(
- application_id: sms_config.application_id,
- message_request: {
- addresses: {
- to => {
- channel_type: 'SMS',
+ CLIENT_POOL[sms_config].with do |client|
+ sender_config = build_sender_config(country_code, sms_config, sender_id)
+
+ pinpoint_response = client.send_messages(
+ application_id: sms_config.application_id,
+ message_request: {
+ addresses: {
+ to => {
+ channel_type: 'SMS',
+ },
+ },
+ message_configuration: {
+ sms_message: {
+ body: message,
+ message_type: 'TRANSACTIONAL',
+ }.merge(sender_config),
},
},
- message_configuration: {
- sms_message: {
- body: message,
- message_type: 'TRANSACTIONAL',
- }.merge(sender_config),
+ )
+ finish = Time.zone.now
+ response = build_response(pinpoint_response, start: start, finish: finish)
+ if response.success? ||
+ response.error.is_a?(OptOutError) ||
+ response.error.is_a?(PermanentFailureError)
+ return response
+ end
+ PinpointHelper.notify_pinpoint_failover(
+ error: response.error,
+ region: sms_config.region,
+ channel: :sms,
+ extra: response.extra,
+ )
+ rescue Aws::Pinpoint::Errors::ServiceError,
+ Seahorse::Client::NetworkingError => e
+ finish = Time.zone.now
+ response = handle_pinpoint_error(e)
+ PinpointHelper.notify_pinpoint_failover(
+ error: e,
+ region: sms_config.region,
+ channel: :sms,
+ extra: {
+ duration_ms: Util.duration_ms(start: start, finish: finish),
},
- },
- )
- finish = Time.zone.now
- response = build_response(pinpoint_response, start: start, finish: finish)
- if response.success? ||
- response.error.is_a?(OptOutError) ||
- response.error.is_a?(PermanentFailureError)
- return response
+ )
end
- PinpointHelper.notify_pinpoint_failover(
- error: response.error,
- region: sms_config.region,
- channel: :sms,
- extra: response.extra,
- )
- rescue Aws::Pinpoint::Errors::ServiceError,
- Seahorse::Client::NetworkingError => e
- finish = Time.zone.now
- response = handle_pinpoint_error(e)
- PinpointHelper.notify_pinpoint_failover(
- error: e,
- region: sms_config.region,
- channel: :sms,
- extra: {
- duration_ms: Util.duration_ms(start: start, finish: finish),
- },
- )
end
response || PinpointHelper.handle_config_failure(:sms)
end
@@ -87,20 +100,20 @@ def phone_info(phone_number)
Telephony.config.pinpoint.sms_configs.each do |sms_config|
error = nil
- client = build_client(sms_config)
- next if client.nil?
- response = client.phone_number_validate(
- number_validate_request: { phone_number: phone_number },
- )
+ CLIENT_POOL[sms_config].with do |client|
+ response = client.phone_number_validate(
+ number_validate_request: { phone_number: phone_number },
+ )
+ rescue Seahorse::Client::NetworkingError,
+ Aws::Pinpoint::Errors::ServiceError => error
+ PinpointHelper.notify_pinpoint_failover(
+ error: error,
+ region: sms_config.region,
+ channel: :sms,
+ extra: {},
+ )
+ end
break if response
- rescue Seahorse::Client::NetworkingError,
- Aws::Pinpoint::Errors::ServiceError => error
- PinpointHelper.notify_pinpoint_failover(
- error: error,
- region: sms_config.region,
- channel: :sms,
- extra: {},
- )
end
type = case response&.number_validate_response&.phone_type
@@ -123,19 +136,6 @@ def phone_info(phone_number)
)
end
- # @api private
- # @param [PinpointSmsConfig] sms_config
- # @return [nil, Aws::Pinpoint::Client]
- def build_client(sms_config)
- credentials = AwsCredentialBuilder.new(sms_config).call
- return if credentials.nil?
- Aws::Pinpoint::Client.new(
- region: sms_config.region,
- retry_limit: 0,
- credentials: credentials,
- )
- end
-
def origination_number(country_code, sms_config)
if sms_config.country_code_longcode_pool&.dig(country_code).present?
sms_config.country_code_longcode_pool[country_code].sample
diff --git a/lib/telephony/pinpoint/voice_sender.rb b/lib/telephony/pinpoint/voice_sender.rb
index 025d1beb4d5..3f678af2bc4 100644
--- a/lib/telephony/pinpoint/voice_sender.rb
+++ b/lib/telephony/pinpoint/voice_sender.rb
@@ -3,6 +3,19 @@
module Telephony
module Pinpoint
class VoiceSender
+ # One connection pool per config (aka per-region)
+ CLIENT_POOL = Hash.new do |h, voice_config|
+ h[voice_config] = ConnectionPool.new(size: IdentityConfig.store.pinpoint_voice_pool_size) do
+ credentials = AwsCredentialBuilder.new(voice_config).call
+
+ Aws::PinpointSMSVoice::Client.new(
+ region: voice_config.region,
+ retry_limit: 0,
+ credentials: credentials,
+ )
+ end
+ end
+
# rubocop:disable Lint/UnusedMethodArgument
# rubocop:disable Metrics/BlockLength
def send(message:, to:, country_code:, otp: nil)
@@ -11,49 +24,47 @@ def send(message:, to:, country_code:, otp: nil)
end
language_code, voice_id = language_code_and_voice_id
-
last_error = nil
Telephony.config.pinpoint.voice_configs.each do |voice_config|
start = Time.zone.now
- client = build_client(voice_config)
- next if client.nil?
+ CLIENT_POOL[voice_config].with do |client|
+ origination_phone_number = voice_config.longcode_pool.sample
- origination_phone_number = voice_config.longcode_pool.sample
-
- response = client.send_voice_message(
- content: {
- ssml_message: {
- text: message,
- language_code: language_code,
- voice_id: voice_id,
+ response = client.send_voice_message(
+ content: {
+ ssml_message: {
+ text: message,
+ language_code: language_code,
+ voice_id: voice_id,
+ },
},
- },
- destination_phone_number: to,
- origination_phone_number: origination_phone_number,
- )
- finish = Time.zone.now
- return Response.new(
- success: true,
- error: nil,
- extra: {
- message_id: response.message_id,
- duration_ms: Util.duration_ms(start: start, finish: finish),
+ destination_phone_number: to,
origination_phone_number: origination_phone_number,
- },
- )
- rescue Aws::PinpointSMSVoice::Errors::ServiceError,
- Seahorse::Client::NetworkingError => e
- finish = Time.zone.now
- last_error = handle_pinpoint_error(e)
- PinpointHelper.notify_pinpoint_failover(
- error: e,
- region: voice_config.region,
- channel: :voice,
- extra: {
- message_id: response&.message_id,
- duration_ms: Util.duration_ms(start: start, finish: finish),
- },
- )
+ )
+ finish = Time.zone.now
+ return Response.new(
+ success: true,
+ error: nil,
+ extra: {
+ message_id: response.message_id,
+ duration_ms: Util.duration_ms(start: start, finish: finish),
+ origination_phone_number: origination_phone_number,
+ },
+ )
+ rescue Aws::PinpointSMSVoice::Errors::ServiceError,
+ Seahorse::Client::NetworkingError => e
+ finish = Time.zone.now
+ last_error = handle_pinpoint_error(e)
+ PinpointHelper.notify_pinpoint_failover(
+ error: e,
+ region: voice_config.region,
+ channel: :voice,
+ extra: {
+ message_id: response&.message_id,
+ duration_ms: Util.duration_ms(start: start, finish: finish),
+ },
+ )
+ end
end
last_error || PinpointHelper.handle_config_failure(:voice)
@@ -61,20 +72,6 @@ def send(message:, to:, country_code:, otp: nil)
# rubocop:enable Metrics/BlockLength
# rubocop:enable Lint/UnusedMethodArgument
- # @api private
- # @param [PinpointVoiceConfiguration] voice_config
- # @return [nil, Aws::PinpointSMSVoice::Client]
- def build_client(voice_config)
- credentials = AwsCredentialBuilder.new(voice_config).call
- return if credentials.nil?
-
- Aws::PinpointSMSVoice::Client.new(
- region: voice_config.region,
- retry_limit: 0,
- credentials: credentials,
- )
- end
-
private
def handle_pinpoint_error(err)
diff --git a/spec/components/password_toggle_component_spec.rb b/spec/components/password_toggle_component_spec.rb
index 36ae51bfec0..6d076c64ff4 100644
--- a/spec/components/password_toggle_component_spec.rb
+++ b/spec/components/password_toggle_component_spec.rb
@@ -11,7 +11,7 @@
subject(:rendered) { render_inline PasswordToggleComponent.new(form: form, **options) }
it 'renders default markup' do
- expect(rendered).to have_css('lg-password-toggle.password-toggle--toggle-top')
+ expect(rendered).to have_css('lg-password-toggle')
expect(rendered).to have_field(t('components.password_toggle.label'), type: :password)
expect(rendered).to have_field(t('components.password_toggle.toggle_label'), type: :checkbox)
end
@@ -23,17 +23,6 @@
expect(rendered).to have_css("[aria-controls='#{input_id}']")
end
- describe '#label' do
- context 'with custom label' do
- let(:label) { 'Custom Label' }
- let(:options) { { label: label } }
-
- it 'renders custom field label' do
- expect(rendered).to have_field(label, type: :password)
- end
- end
- end
-
describe '#toggle_label' do
context 'with custom label' do
let(:toggle_label) { 'Custom Toggle Label' }
@@ -45,24 +34,6 @@
end
end
- describe '#toggle_position' do
- context 'with top toggle position' do
- let(:options) { { toggle_position: :top } }
-
- it 'renders modifier class' do
- expect(rendered).to have_css('lg-password-toggle.password-toggle--toggle-top')
- end
- end
-
- context 'with bottom toggle position' do
- let(:options) { { toggle_position: :bottom } }
-
- it 'renders modifier class' do
- expect(rendered).to have_css('lg-password-toggle.password-toggle--toggle-bottom')
- end
- end
- end
-
describe '#toggle_id' do
it 'is unique across instances' do
toggle_one = PasswordToggleComponent.new(form: form)
@@ -85,17 +56,25 @@
end
end
- describe '#field' do
- context 'with field options' do
- let(:options) do
- { input_html: { class: 'my-custom-field', data: { foo: 'bar' } }, required: true }
- end
+ context 'with tag options' do
+ let(:options) do
+ { class: 'my-custom-field', data: { foo: 'bar' } }
+ end
- it 'forwards field options' do
- expect(rendered).to have_css(
- '.password-toggle__input.my-custom-field[data-foo="bar"][required]',
- )
- end
+ it 'forwards options to rendered tag' do
+ expect(rendered).to have_css('lg-password-toggle.my-custom-field[data-foo="bar"]')
+ end
+ end
+
+ context 'with field options' do
+ let(:label) { 'Custom Label' }
+ let(:options) do
+ { field_options: { label: label, required: true } }
+ end
+
+ it 'forwards options to rendered field' do
+ expect(rendered).to have_css('.password-toggle__input[required]')
+ expect(rendered).to have_field(label, type: :password)
end
end
end
diff --git a/spec/components/previews/password_toggle_component_preview.rb b/spec/components/previews/password_toggle_component_preview.rb
index 8dca3099461..95c80ec9333 100644
--- a/spec/components/previews/password_toggle_component_preview.rb
+++ b/spec/components/previews/password_toggle_component_preview.rb
@@ -7,13 +7,12 @@ def default
# @param label text
# @param toggle_label text
- # @param toggle_position select [~,top,bottom]
- def workbench(label: nil, toggle_label: nil, toggle_position: 'top')
+ def workbench(label: nil, toggle_label: nil)
render(
PasswordToggleComponent.new(
form: form_builder,
- **{ label: label, toggle_label: toggle_label }.compact,
- toggle_position: toggle_position.to_sym,
+ **{ toggle_label: toggle_label }.compact,
+ field_options: { label: label }.compact,
),
)
end
diff --git a/spec/controllers/api/irs_attempts_api_controller_spec.rb b/spec/controllers/api/irs_attempts_api_controller_spec.rb
index 177f9ffa9ed..6e1a5d4cb3f 100644
--- a/spec/controllers/api/irs_attempts_api_controller_spec.rb
+++ b/spec/controllers/api/irs_attempts_api_controller_spec.rb
@@ -57,6 +57,15 @@
end
end
+ context 'with a timestamp including a fractional second' do
+ let(:timestamp) { '2022-11-08T18:00:00.000Z' }
+
+ it 'accepts the timestamp as valid' do
+ post :create, params: { timestamp: timestamp }
+ expect(response.status).to eq(200)
+ end
+ end
+
it 'renders a 404 if disabled' do
allow(IdentityConfig.store).to receive(:irs_attempt_api_enabled).and_return(false)
diff --git a/spec/controllers/idv/capture_doc_controller_spec.rb b/spec/controllers/idv/capture_doc_controller_spec.rb
index 0faa9705452..b4e8d15962f 100644
--- a/spec/controllers/idv/capture_doc_controller_spec.rb
+++ b/spec/controllers/idv/capture_doc_controller_spec.rb
@@ -133,11 +133,32 @@
expect(response).to_not be_not_found
end
+ it 'tracks expected events for irs reproofing' do
+ allow_any_instance_of(UserDecorator).to receive(:reproof_for_irs?).and_return(true)
+ mock_next_step(:capture_complete)
+ result = {
+ step: 'capture_complete',
+ flow_path: 'hybrid',
+ irs_reproofing: true,
+ step_count: 1,
+ analytics_id: 'Doc Auth',
+ }
+
+ get :show, params: { step: 'capture_complete' }
+
+ expect(@irs_attempts_api_tracker).not_to have_received(:idv_phone_upload_link_used)
+
+ expect(@analytics).to have_received(:track_event).with(
+ 'IdV: doc auth capture_complete visited', result
+ )
+ end
+
it 'tracks expected events' do
mock_next_step(:capture_complete)
result = {
step: 'capture_complete',
flow_path: 'hybrid',
+ irs_reproofing: false,
step_count: 1,
analytics_id: 'Doc Auth',
}
diff --git a/spec/controllers/idv/doc_auth_controller_spec.rb b/spec/controllers/idv/doc_auth_controller_spec.rb
index ff71cd4dfac..57eb3dcfa91 100644
--- a/spec/controllers/idv/doc_auth_controller_spec.rb
+++ b/spec/controllers/idv/doc_auth_controller_spec.rb
@@ -94,7 +94,8 @@
end
it 'tracks analytics' do
- result = { step: 'welcome', flow_path: 'standard', step_count: 1, analytics_id: 'Doc Auth' }
+ result = { step: 'welcome', flow_path: 'standard', irs_reproofing: false, step_count: 1,
+ analytics_id: 'Doc Auth' }
get :show, params: { step: 'welcome' }
@@ -164,6 +165,7 @@
flow_path: 'standard',
step_count: 1,
pii_like_keypaths: [[:errors, :ssn], [:error_details, :ssn]],
+ irs_reproofing: false,
analytics_id: 'Doc Auth',
}
@@ -200,6 +202,7 @@
},
step: 'welcome',
flow_path: 'standard',
+ irs_reproofing: false,
step_count: 1,
analytics_id: 'Doc Auth',
}
diff --git a/spec/controllers/idv/in_person/address_search_controller_spec.rb b/spec/controllers/idv/in_person/address_search_controller_spec.rb
new file mode 100644
index 00000000000..5c6e51aa85f
--- /dev/null
+++ b/spec/controllers/idv/in_person/address_search_controller_spec.rb
@@ -0,0 +1,86 @@
+require 'rails_helper'
+
+describe Idv::InPerson::AddressSearchController do
+ include IdvHelper
+
+ let(:user) { create(:user) }
+ let(:sp) { nil }
+ let(:arcgis_search_enabled) { true }
+
+ before do
+ stub_analytics
+ stub_sign_in(user) if user
+ allow(IdentityConfig.store).to receive(:arcgis_search_enabled).
+ and_return(arcgis_search_enabled)
+ allow(controller).to receive(:current_sp).and_return(sp)
+ end
+
+ describe '#index' do
+ let(:geocoder) { double('Geocoder') }
+ let(:suggestions) do
+ [
+ OpenStruct.new({ magic_key: 'a' }),
+ ]
+ end
+
+ let(:addresses) do
+ [
+ { name: 'Address 1' },
+ { name: 'Address 2' },
+ { name: 'Address 3' },
+ { name: 'Address 4' },
+ ]
+ end
+ subject(:response) { get :index }
+
+ before do
+ allow(controller).to receive(:geocoder).and_return(geocoder)
+ allow(geocoder).to receive(:find_address_candidates).and_return(addresses)
+ allow(geocoder).to receive(:suggest).and_return(suggestions)
+ end
+
+ context 'with successful fetch' do
+ it 'gets successful response' do
+ response = get :index
+ json = response.body
+ addresses = JSON.parse(json)
+ expect(addresses.length).to eq 1
+ end
+
+ context 'with no suggestions' do
+ let(:suggestions) do
+ []
+ end
+
+ it 'returns empty array' do
+ response = get :index
+ json = response.body
+ addresses = JSON.parse(json)
+ expect(addresses.length).to eq 0
+ end
+ end
+ end
+
+ context 'with unsuccessful fetch' do
+ before do
+ exception = Faraday::ConnectionFailed.new('error')
+ allow(geocoder).to receive(:suggest).and_raise(exception)
+ end
+
+ it 'gets an empty pilot response' do
+ response = get :index
+ json = response.body
+ addresses = JSON.parse(json)
+ expect(addresses.length).to eq 0
+ end
+ end
+
+ context 'with feature disabled' do
+ let(:arcgis_search_enabled) { false }
+
+ it 'renders 404' do
+ expect(response.status).to eq(404)
+ end
+ end
+ end
+end
diff --git a/spec/controllers/sign_up/email_confirmations_controller_spec.rb b/spec/controllers/sign_up/email_confirmations_controller_spec.rb
index 6f778361212..829308be21d 100644
--- a/spec/controllers/sign_up/email_confirmations_controller_spec.rb
+++ b/spec/controllers/sign_up/email_confirmations_controller_spec.rb
@@ -2,26 +2,35 @@
describe SignUp::EmailConfirmationsController do
describe '#create' do
- before do
- stub_analytics
- end
-
- it 'tracks nil email confirmation token' do
- analytics_hash = {
+ let(:token_not_found_error) { { confirmation_token: [:not_found] } }
+ let(:token_expired_error) { { confirmation_token: [:expired] } }
+ let(:analytics_token_error_hash) do
+ {
success: false,
- error_details: { confirmation_token: [:not_found] },
+ error_details: token_not_found_error,
errors: { confirmation_token: ['not found'] },
user_id: nil,
}
+ end
+ let(:attempts_tracker_error_hash) do
+ {
+ email: nil,
+ success: false,
+ failure_reason: token_not_found_error,
+ }
+ end
+ before do
+ stub_analytics
+ stub_attempts_tracker
+ end
+
+ it 'tracks nil email confirmation token' do
expect(@analytics).to receive(:track_event).
- with('User Registration: Email Confirmation', analytics_hash)
+ with('User Registration: Email Confirmation', analytics_token_error_hash)
- expect_any_instance_of(IrsAttemptsApi::Tracker).to receive(:track_event).with(
- :user_registration_email_confirmation,
- email: nil,
- success: false,
- failure_reason: { confirmation_token: [:not_found] },
+ expect(@irs_attempts_api_tracker).to receive(:user_registration_email_confirmation).with(
+ **attempts_tracker_error_hash,
)
get :create, params: { confirmation_token: nil }
@@ -31,21 +40,11 @@
end
it 'tracks blank email confirmation token' do
- analytics_hash = {
- success: false,
- error_details: { confirmation_token: [:not_found] },
- errors: { confirmation_token: ['not found'] },
- user_id: nil,
- }
-
expect(@analytics).to receive(:track_event).
- with('User Registration: Email Confirmation', analytics_hash)
+ with('User Registration: Email Confirmation', analytics_token_error_hash)
- expect_any_instance_of(IrsAttemptsApi::Tracker).to receive(:track_event).with(
- :user_registration_email_confirmation,
- email: nil,
- success: false,
- failure_reason: { confirmation_token: [:not_found] },
+ expect(@irs_attempts_api_tracker).to receive(:user_registration_email_confirmation).with(
+ **attempts_tracker_error_hash,
)
get :create, params: { confirmation_token: '' }
@@ -55,21 +54,11 @@
end
it 'tracks confirmation token as a single-quoted empty string' do
- analytics_hash = {
- success: false,
- error_details: { confirmation_token: [:not_found] },
- errors: { confirmation_token: ['not found'] },
- user_id: nil,
- }
-
expect(@analytics).to receive(:track_event).
- with('User Registration: Email Confirmation', analytics_hash)
+ with('User Registration: Email Confirmation', analytics_token_error_hash)
- expect_any_instance_of(IrsAttemptsApi::Tracker).to receive(:track_event).with(
- :user_registration_email_confirmation,
- email: nil,
- success: false,
- failure_reason: { confirmation_token: [:not_found] },
+ expect(@irs_attempts_api_tracker).to receive(:user_registration_email_confirmation).with(
+ **attempts_tracker_error_hash,
)
get :create, params: { confirmation_token: "''" }
@@ -79,21 +68,11 @@
end
it 'tracks confirmation token as a double-quoted empty string' do
- analytics_hash = {
- success: false,
- error_details: { confirmation_token: [:not_found] },
- errors: { confirmation_token: ['not found'] },
- user_id: nil,
- }
-
expect(@analytics).to receive(:track_event).
- with('User Registration: Email Confirmation', analytics_hash)
+ with('User Registration: Email Confirmation', analytics_token_error_hash)
- expect_any_instance_of(IrsAttemptsApi::Tracker).to receive(:track_event).with(
- :user_registration_email_confirmation,
- email: nil,
- success: false,
- failure_reason: { confirmation_token: [:not_found] },
+ expect(@irs_attempts_api_tracker).to receive(:user_registration_email_confirmation).with(
+ **attempts_tracker_error_hash,
)
get :create, params: { confirmation_token: '""' }
@@ -115,8 +94,7 @@
expect(@analytics).to receive(:track_event).
with('User Registration: Email Confirmation', analytics_hash)
- expect_any_instance_of(IrsAttemptsApi::Tracker).to receive(:track_event).with(
- :user_registration_email_confirmation,
+ expect(@irs_attempts_api_tracker).to receive(:user_registration_email_confirmation).with(
email: email_address.email,
success: false,
failure_reason: { email: [:already_confirmed] },
@@ -139,18 +117,17 @@
analytics_hash = {
success: false,
errors: { confirmation_token: [t('errors.messages.expired')] },
- error_details: { confirmation_token: [:expired] },
+ error_details: token_expired_error,
user_id: email_address.user.uuid,
}
expect(@analytics).to receive(:track_event).
with('User Registration: Email Confirmation', analytics_hash)
- expect_any_instance_of(IrsAttemptsApi::Tracker).to receive(:track_event).with(
- :user_registration_email_confirmation,
+ expect(@irs_attempts_api_tracker).to receive(:user_registration_email_confirmation).with(
email: email_address.email,
success: false,
- failure_reason: { confirmation_token: [:expired] },
+ failure_reason: token_expired_error,
)
get :create, params: { confirmation_token: 'foo' }
@@ -172,18 +149,17 @@
analytics_hash = {
success: false,
errors: { confirmation_token: [t('errors.messages.expired')] },
- error_details: { confirmation_token: [:expired] },
+ error_details: token_expired_error,
user_id: user.uuid,
}
expect(@analytics).to receive(:track_event).
with('User Registration: Email Confirmation', analytics_hash)
- expect_any_instance_of(IrsAttemptsApi::Tracker).to receive(:track_event).with(
- :user_registration_email_confirmation,
+ expect(@irs_attempts_api_tracker).to receive(:user_registration_email_confirmation).with(
email: email_address.email,
success: false,
- failure_reason: { confirmation_token: [:expired] },
+ failure_reason: token_expired_error,
)
get :create, params: { confirmation_token: 'foo' }
@@ -204,6 +180,7 @@
user = email_address.user
stub_analytics
+ stub_attempts_tracker
analytics_hash = {
success: true,
@@ -215,8 +192,7 @@
expect(@analytics).to receive(:track_event).
with('User Registration: Email Confirmation', analytics_hash)
- expect_any_instance_of(IrsAttemptsApi::Tracker).to receive(:track_event).with(
- :user_registration_email_confirmation,
+ expect(@irs_attempts_api_tracker).to receive(:user_registration_email_confirmation).with(
email: email_address.email,
success: true,
failure_reason: nil,
diff --git a/spec/controllers/sign_up/passwords_controller_spec.rb b/spec/controllers/sign_up/passwords_controller_spec.rb
index 35efcd64623..68f3a5f796e 100644
--- a/spec/controllers/sign_up/passwords_controller_spec.rb
+++ b/spec/controllers/sign_up/passwords_controller_spec.rb
@@ -29,10 +29,7 @@
success_properties,
)
- expect(@irs_attempts_api_tracker).to receive(:user_registration_email_confirmation).with(
- email: user.email_addresses.first.email,
- **success_properties,
- )
+ expect(@irs_attempts_api_tracker).not_to receive(:user_registration_email_confirmation)
post :create, params: {
password_form: { password: 'NewVal!dPassw0rd' },
@@ -102,10 +99,7 @@
success: false,
failure_reason: password_short_error,
)
- expect(@irs_attempts_api_tracker).to receive(:user_registration_email_confirmation).with(
- email: user.email_addresses.first.email,
- **success_properties,
- )
+ expect(@irs_attempts_api_tracker).not_to receive(:user_registration_email_confirmation)
post :create, params: { password_form: { password: 'NewVal' }, confirmation_token: token }
end
diff --git a/spec/features/idv/doc_auth/welcome_step_spec.rb b/spec/features/idv/doc_auth/welcome_step_spec.rb
index 24bf28d20e6..d2f838cd820 100644
--- a/spec/features/idv/doc_auth/welcome_step_spec.rb
+++ b/spec/features/idv/doc_auth/welcome_step_spec.rb
@@ -50,7 +50,9 @@ def expect_doc_auth_upload_step
end
it 'logs missing items troubleshooting link click' do
- click_on t('idv.troubleshooting.options.learn_more_address_verification_options')
+ within '.troubleshooting-options' do
+ click_on t('idv.troubleshooting.options.learn_more_address_verification_options')
+ end
expect(fake_analytics).to have_logged_event(
'External Redirect',
@@ -64,6 +66,23 @@ def expect_doc_auth_upload_step
)
end
+ it 'logs "you will need" learn more link click' do
+ within '.usa-process-list' do
+ click_on t('idv.troubleshooting.options.learn_more_address_verification_options')
+ end
+
+ expect(fake_analytics).to have_logged_event(
+ 'External Redirect',
+ step: 'welcome',
+ location: 'you_will_need',
+ flow: 'idv',
+ redirect_url: MarketingSite.help_center_article_url(
+ category: 'verify-your-identity',
+ article: 'phone-number-and-phone-plan-in-your-name',
+ ),
+ )
+ end
+
context 'during the acuant maintenance window' do
context 'during the acuant maintenance window' do
let(:maintenance_window) do
diff --git a/spec/features/reports/authorization_count_spec.rb b/spec/features/reports/authorization_count_spec.rb
index c36510d62c6..525a4e0f340 100644
--- a/spec/features/reports/authorization_count_spec.rb
+++ b/spec/features/reports/authorization_count_spec.rb
@@ -284,9 +284,6 @@ def visit_idp_from_ial2_saml_sp(issuer:)
end
def expect_ial1_count_only(issuer)
- expect(ial1_monthly_auth_count(issuer)).to eq(1)
- expect(ial2_monthly_auth_count(issuer)).to eq(0)
-
ial1_return_logs = SpReturnLog.where(issuer: issuer, billable: true, ial: 1)
ial2_return_logs = SpReturnLog.where(issuer: issuer, billable: true, ial: 2)
expect(ial1_return_logs.count).to eq(1)
@@ -294,9 +291,6 @@ def expect_ial1_count_only(issuer)
end
def expect_ial2_count_only(issuer)
- expect(ial1_monthly_auth_count(issuer)).to eq(0)
- expect(ial2_monthly_auth_count(issuer)).to eq(1)
-
ial1_return_logs = SpReturnLog.where(issuer: issuer, billable: true, ial: 1)
ial2_return_logs = SpReturnLog.where(issuer: issuer, billable: true, ial: 2)
expect(ial1_return_logs.count).to eq(0)
@@ -304,25 +298,13 @@ def expect_ial2_count_only(issuer)
end
def expect_ial1_and_ial2_count(issuer)
- expect(ial1_monthly_auth_count(issuer)).to eq(1)
- expect(ial2_monthly_auth_count(issuer)).to eq(1)
-
ial1_return_logs = SpReturnLog.where(issuer: issuer, billable: true, ial: 1)
ial2_return_logs = SpReturnLog.where(issuer: issuer, billable: true, ial: 2)
expect(ial1_return_logs.count).to eq(1)
expect(ial2_return_logs.count).to eq(1)
end
- def ial2_monthly_auth_count(client_id)
- Db::MonthlySpAuthCount::SpMonthTotalAuthCounts.call(today, client_id, 2)
- end
-
- def ial1_monthly_auth_count(client_id)
- Db::MonthlySpAuthCount::SpMonthTotalAuthCounts.call(today, client_id, 1)
- end
-
def reset_monthly_auth_count_and_login(user)
- MonthlySpAuthCount.delete_all
SpReturnLog.delete_all
visit api_saml_logout2022_path
sign_in_live_with_2fa(user)
diff --git a/spec/features/reports/doc_auth_drop_off_rates_per_sprint_report_spec.rb b/spec/features/reports/doc_auth_drop_off_rates_per_sprint_report_spec.rb
deleted file mode 100644
index 852498a7834..00000000000
--- a/spec/features/reports/doc_auth_drop_off_rates_per_sprint_report_spec.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-require 'rails_helper'
-
-feature 'Doc auth drop offs per sprint report' do
- it 'does not throw an error' do
- expect(Reports::DocAuthDropOffRatesPerSprintReport.new.perform(Time.zone.today)).to be_present
- end
-end
diff --git a/spec/features/reports/doc_auth_drop_off_rates_report_spec.rb b/spec/features/reports/doc_auth_drop_off_rates_report_spec.rb
deleted file mode 100644
index 22e4af809c9..00000000000
--- a/spec/features/reports/doc_auth_drop_off_rates_report_spec.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-require 'rails_helper'
-
-feature 'Doc auth drop off rates report' do
- it 'does not throw an error' do
- expect(Reports::DocAuthDropOffRatesReport.new.perform(Time.zone.today)).to be_present
- end
-
- it 'has all the steps in the funnel report in the left most column and justified' do
- report = Reports::DocAuthDropOffRatesReport.new.perform(Time.zone.today)
- Db::DocAuthLog::DropOffRatesHelper::STEPS.each do |step|
- expect(report.include?(format("\n%20s", step))).to be true
- end
- end
-end
diff --git a/spec/features/reports/doc_auth_funnel_report_spec.rb b/spec/features/reports/doc_auth_funnel_report_spec.rb
deleted file mode 100644
index b69b77c7ad6..00000000000
--- a/spec/features/reports/doc_auth_funnel_report_spec.rb
+++ /dev/null
@@ -1,139 +0,0 @@
-require 'rails_helper'
-
-feature 'Doc Auth Funnel report' do
- include IdvStepHelper
- include DocAuthHelper
-
- let(:subject) { Db::DocAuthLog::DocAuthFunnelSummaryStats }
- let(:user) { create(:user, :signed_up) }
- let(:user2) { create(:user, :signed_up) }
- let(:issuer) { 'foo' }
- let(:summary1) do
- {
- 'total_verified_users_count' => 0,
- 'total_verify_attempted_users_count' => 1,
- }
- end
- let(:summary2) do
- {
- 'total_verified_users_count' => 0,
- 'total_verify_attempted_users_count' => 2,
- }
- end
- let(:verify_funnel) do
- {
- 'back_image_error_count_average' => 0.0,
- 'back_image_submit_count_average' => 0.0,
- 'back_image_view_count_average' => 0.0,
- 'back_image_view_percent' => 0.0,
- 'capture_complete_view_count_average' => 0.0,
- 'capture_complete_view_percent' => 0.0,
- 'capture_mobile_back_image_view_count_average' => 0.0,
- 'capture_mobile_back_image_view_percent' => 0.0,
- 'capture_mobile_back_image_error_count_average' => 0.0,
- 'capture_mobile_back_image_submit_count_average' => 0.0,
- 'choose_method_view_count_average' => 0.0,
- 'choose_method_view_percent' => 0.0,
- 'document_capture_error_count_average' => 0.0,
- 'document_capture_submit_count_average' => 1.0,
- 'document_capture_view_count_average' => 1.0,
- 'document_capture_view_percent' => 100.0,
- 'email_sent_view_count_average' => 0.0,
- 'email_sent_view_percent' => 0.0,
- 'encrypt_view_count_average' => 0.0,
- 'encrypt_view_percent' => 0.0,
- 'enter_info_view_count_average' => 0.0,
- 'enter_info_view_percent' => 0.0,
- 'front_image_error_count_average' => 0.0,
- 'front_image_submit_count_average' => 0.0,
- 'front_image_view_count_average' => 0.0,
- 'front_image_view_percent' => 0.0,
- 'link_sent_view_count_average' => 0.0,
- 'link_sent_view_percent' => 0.0,
- 'mobile_back_image_view_count_average' => 0.0,
- 'mobile_back_image_view_percent' => 0.0,
- 'mobile_back_image_error_count_average' => 0.0,
- 'mobile_back_image_submit_count_average' => 0.0,
- 'mobile_front_image_view_count_average' => 0.0,
- 'mobile_front_image_view_percent' => 0.0,
- 'mobile_front_image_error_count_average' => 0.0,
- 'mobile_front_image_submit_count_average' => 0.0,
- 'present_cac_error_count_average' => 0.0,
- 'present_cac_submit_count_average' => 0.0,
- 'present_cac_view_count_average' => 0.0,
- 'present_cac_view_percent' => 0.0,
- 'selfie_error_count_average' => 0.0,
- 'selfie_submit_count_average' => 0.0,
- 'selfie_view_count_average' => 0.0,
- 'selfie_view_percent' => 0.0,
- 'send_link_view_count_average' => 0.0,
- 'send_link_view_percent' => 0.0,
- 'ssn_view_count_average' => 1.0,
- 'ssn_view_percent' => 100.0,
- 'success_view_count_average' => 0.0,
- 'success_view_percent' => 0.0,
- 'upload_view_count_average' => 1.0,
- 'upload_view_percent' => 100.0,
- 'usps_address_view_count_average' => 0.0,
- 'usps_address_view_percent' => 0.0,
- 'usps_letter_sent_submit_count_average' => 0.0,
- 'usps_letter_sent_error_count_average' => 0.0,
- 'verified_view_count_average' => 0.0,
- 'verified_view_percent' => 0.0,
- 'verify_error_count_average' => 0.0,
- # Bug: We're currently double-counting these views (LG-6538)
- 'verify_phone_view_count_average' => 2.0,
- 'verify_phone_view_percent' => 100.0,
- 'verify_submit_count_average' => 1.0,
- 'verify_view_count_average' => 1.0,
- 'verify_view_percent' => 100.0,
- 'welcome_view_count_average' => 1.0,
- 'welcome_view_percent' => 100.0,
- 'agreement_view_count_average' => 1.0,
- 'agreement_view_percent' => 100.0,
- 'verify_phone_submit_count_average' => 0.0,
- 'verify_phone_submit_percent' => 0.0,
- 'document_capture_submit_percent' => 100.0,
- 'verify_submit_percent' => 100.0,
- 'back_image_submit_percent' => 0.0,
- 'capture_mobile_back_image_submit_percent' => 0.0,
- 'mobile_back_image_submit_percent' => 0.0,
- }
- end
-
- it 'works for no records' do
- expect(subject.new.call).to eq({})
- end
-
- it 'works for one flow', js: true do
- sign_in_and_2fa_user(user)
- complete_all_doc_auth_steps
-
- expect(subject.new.call).to eq(verify_funnel.merge(summary1))
-
- Funnel::DocAuth::ResetSteps.call(user.id)
- expect(subject.new.call).to_not eq(verify_funnel.merge(summary1))
- end
-
- it 'works for two flows', js: true do
- sign_in_and_2fa_user(user)
- complete_all_doc_auth_steps
- sign_in_and_2fa_user(user2)
- complete_all_doc_auth_steps
-
- expect(subject.new.call).to eq(verify_funnel.merge(summary2))
- end
-
- it 'does not create a doc_auth_log entry without a welcome first' do
- Funnel::DocAuth::RegisterStep.new(user.id, issuer).call('upload', :view, true)
-
- expect(DocAuthLog.count).to eq(0)
- end
-
- it 'does create a doc_auth_log entry when a welcome is first' do
- Funnel::DocAuth::RegisterStep.new(user.id, issuer).call('welcome', :view, true)
- Funnel::DocAuth::RegisterStep.new(user.id, issuer).call('upload', :view, true)
-
- expect(DocAuthLog.count).to eq(1)
- end
-end
diff --git a/spec/features/reports/omb_fitara_report_spec.rb b/spec/features/reports/omb_fitara_report_spec.rb
deleted file mode 100644
index 742e3538208..00000000000
--- a/spec/features/reports/omb_fitara_report_spec.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-require 'rails_helper'
-
-feature 'OMB Fitara compliance officer runs report' do
- it 'works in july' do
- travel_to(Date.new(2019, 7, 2)) do
- visit sign_up_email_path
- sign_up_and_2fa_ial1_user
-
- results = '{"counts":[{"month":"201907","count":1},{"month":"201906","count":0}]}'
- expect(Reports::OmbFitaraReport.new.perform(Time.zone.today)).to eq(results)
- end
- end
-
- it 'works in december' do
- travel_to(Date.new(2019, 12, 2)) do
- visit sign_up_email_path
- sign_up_and_2fa_ial1_user
-
- results = '{"counts":[{"month":"201912","count":1},{"month":"201911","count":0}]}'
- expect(Reports::OmbFitaraReport.new.perform(Time.zone.today)).to eq(results)
- end
- end
-
- it 'works in january' do
- travel_to(Date.new(2019, 1, 2)) do
- visit sign_up_email_path
- sign_up_and_2fa_ial1_user
-
- results = '{"counts":[{"month":"201901","count":1},{"month":"201812","count":0}]}'
- expect(Reports::OmbFitaraReport.new.perform(Time.zone.today)).to eq(results)
- end
- end
-
- describe '.generate_s3_paths' do
- let(:report_name) { 'omb-fitara-report' }
-
- it 'generates paths with date or latest prefix' do
- expect(Identity::Hostdata).to receive(:env).and_return('ci')
-
- travel_to(Date.new(2018, 1, 2)) do
- expect(Reports::OmbFitaraReport.new.send(:generate_s3_paths, report_name, 'json')).
- to eq(
- ['ci/omb-fitara-report/latest.omb-fitara-report.json',
- 'ci/omb-fitara-report/2018/2018-01-02.omb-fitara-report.json'],
- )
- end
- end
- end
-end
diff --git a/spec/fixtures/proofing/lexis_nexis/phone_finder/fail_response.json b/spec/fixtures/proofing/lexis_nexis/phone_finder/rdp1_fail_response.json
similarity index 100%
rename from spec/fixtures/proofing/lexis_nexis/phone_finder/fail_response.json
rename to spec/fixtures/proofing/lexis_nexis/phone_finder/rdp1_fail_response.json
diff --git a/spec/fixtures/proofing/lexis_nexis/phone_finder/response.json b/spec/fixtures/proofing/lexis_nexis/phone_finder/rdp1_response.json
similarity index 100%
rename from spec/fixtures/proofing/lexis_nexis/phone_finder/response.json
rename to spec/fixtures/proofing/lexis_nexis/phone_finder/rdp1_response.json
diff --git a/spec/fixtures/proofing/lexis_nexis/phone_finder/rdp2_fail_response.json b/spec/fixtures/proofing/lexis_nexis/phone_finder/rdp2_fail_response.json
new file mode 100644
index 00000000000..a80b511ef73
--- /dev/null
+++ b/spec/fixtures/proofing/lexis_nexis/phone_finder/rdp2_fail_response.json
@@ -0,0 +1,695 @@
+{
+ "Status": {
+ "ConversationId": "3100000000000",
+ "RequestId": "2xxxxxx",
+ "TransactionStatus": "failed",
+ "TransactionReasonCode": {
+ "Code": "phone_finder_fail",
+ "Description": "Phone Finder Fail"
+ }
+ },
+ "Products": [
+ {
+ "ProductType": "PhoneFinder",
+ "ExecutedStepName": "PhoneFinder",
+ "ProductConfigurationName": "xxxxxPhoneFinder",
+ "ProductStatus": "fail",
+ "ProductReason": {
+ "Code": "phone_finder_fail",
+ "Description": "Phone Finder Fail"
+ },
+ "Items": [
+ {
+ "ItemName": "PrepaidPhoneNumber",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "LastChangeDateIMEI",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "NoContractCarrier",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "VOIPPhone",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "LastChangeDateIMSI",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "SurnameMismatch",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "LostStolen",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "PhoneReturnedOften",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "WirelessServiceType",
+ "ItemStatus": "fail",
+ "ItemReason": {
+ "Code": "WirelessServiceType.LOW",
+ "Description": "Phone Service Type is Wireless"
+ }
+ },
+ {
+ "ItemName": "SourceSelfReported",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "LandlineServiceType",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "CableServiceType",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "UnknownServiceType",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "1stFirstSeenDate",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "2ndFirstSeenDate",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "PhonePorted2ndRange",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "3rdFirstSeenDate",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "PhonePorted3rdRange",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "PhonePorted",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "FrequentlyPhonePorted",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "PhoneSpoofedNumOfDays",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "SpoofingPhoneNumber",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "SpoofingDestination",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "PhoneAssociatedWithMoreThanXIdentities",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "EmailCount(Month)",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "DeviceCount(Month)",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "PhoneHighCountryCount",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "RecentPhoneFirstSeen",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "DigitalIDBadReputation",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "PhoneNumberAssociatedWithFraud",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "PhoneInRejectedTransaction",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "PhoneSearch",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "PhoneOnGlobalBlacklist",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "PhoneOTPRequest",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "PhoneNotActive",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "SpoofedPhoneNumber",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "SubjectIsBusiness",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "PhoneForwarding",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "SubjectDeceased",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "NoFirstSeenDate",
+ "ItemStatus": "fail",
+ "ItemReason": {
+ "Code": "NoFirstSeenDate.MEDIUM",
+ "Description": "No First Seen Date associated to Phone #"
+ }
+ },
+ {
+ "ItemName": "CommercialAddress",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "NoLastSeenDate",
+ "ItemStatus": "fail",
+ "ItemReason": {
+ "Code": "NoLastSeenDate.LOW",
+ "Description": "No Last Seen Date associated to Phone #"
+ }
+ },
+ {
+ "ItemName": "MismatchAreaCodeAndAddressArea",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "NoFirstAndLastSeenDate",
+ "ItemStatus": "fail",
+ "ItemReason": {
+ "Code": "NoFirstAndLastSeenDate.HIGH",
+ "Description": "No First Seen and Last Seen Date associated to Phone #"
+ }
+ },
+ {
+ "ItemName": "NotCurrentAddress",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "LastSeenDateAge",
+ "ItemStatus": "pass"
+ }
+ ],
+ "ParameterDetails": [
+ {
+ "Group": "IDENTITY_RECORDS",
+ "Name": "Consumer_LexIDs",
+ "Values": [
+ {
+ "Value": "0"
+ }
+ ]
+ },
+ {
+ "Group": "IDENTITY_RECORDS",
+ "Name": "Deceased_Indicators",
+ "Values": [
+ {
+ "Value": "N"
+ }
+ ]
+ },
+ {
+ "Group": "IDENTITY_RECORDS",
+ "Name": "Consumer_Full_Names",
+ "Values": [
+ {
+ "Value": "PII"
+ }
+ ]
+ },
+ {
+ "Group": "IDENTITY_RECORDS",
+ "Name": "Consumer_Street_1s",
+ "Values": [
+ {
+ "Value": ""
+ }
+ ]
+ },
+ {
+ "Group": "IDENTITY_RECORDS",
+ "Name": "Consumer_Street_2s",
+ "Values": [
+ {
+ "Value": ""
+ }
+ ]
+ },
+ {
+ "Group": "IDENTITY_RECORDS",
+ "Name": "Consumer_StateCityZIPs",
+ "Values": [
+ {
+ "Value": "PII"
+ }
+ ]
+ },
+ {
+ "Group": "IDENTITY_RECORDS",
+ "Name": "Consumer_Time_With_Phones",
+ "Values": [
+ {
+ "Value": ""
+ }
+ ]
+ },
+ {
+ "Group": "IDENTITY_RECORDS",
+ "Name": "Consumer_Time_Last_Seen_With_Phones",
+ "Values": [
+ {
+ "Value": ""
+ }
+ ]
+ },
+ {
+ "Group": "IDENTITY_RECORDS",
+ "Name": "Consumer_Address_Types",
+ "Values": [
+ {
+ "Value": ""
+ }
+ ]
+ },
+ {
+ "Group": "IDENTITY_RECORDS",
+ "Name": "Consumer_Address_Statuses",
+ "Values": [
+ {
+ "Value": ""
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Call_Forwarding_Indicator",
+ "Values": [
+ {
+ "Value": ""
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Number",
+ "Values": [
+ {
+ "Value": "xxxxxxxx"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Type",
+ "Values": [
+ {
+ "Value": "POSSIBLE WIRELESS"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Carrier",
+ "Values": [
+ {
+ "Value": "VERIZON WIRELESS"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Listing_Name",
+ "Values": [
+ {
+ "Value": "PII"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Status",
+ "Values": [
+ {
+ "Value": "ACTIVE"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Listing_Type",
+ "Values": [
+ {
+ "Value": "RESIDENTIAL"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Address_Type",
+ "Values": [
+ {
+ "Value": ""
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Port_Code",
+ "Values": [
+ {
+ "Value": "Ported"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Caller_ID",
+ "Values": [
+ {
+ "Value": "PII"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Port_Count",
+ "Values": [
+ {
+ "Value": "1"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Activation_Year",
+ "Values": [
+ {
+ "Value": "2018"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Phone_Verification_Indicator",
+ "Values": [
+ {
+ "Value": "1"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Activation_Month",
+ "Values": [
+ {
+ "Value": "07"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Activation_Day",
+ "Values": [
+ {
+ "Value": "04"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_No_Contract_Carrier",
+ "Values": [
+ {
+ "Value": "0"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Operating_Company",
+ "Values": [
+ {
+ "Value": "VERIZON WIRELESS"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Phone_Verification_Msg",
+ "Values": [
+ {
+ "Value": "Input phone number matches name"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Origination_Spoof_Count",
+ "Values": [
+ {
+ "Value": "0"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Spoofed_Count",
+ "Values": [
+ {
+ "Value": "0"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Destination_Spoof_Count",
+ "Values": [
+ {
+ "Value": "0"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Risk_Indicator_Status",
+ "Values": [
+ {
+ "Value": "FAIL"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Risk_Indicator_Messages",
+ "Values": [
+ {
+ "Value": "No First Seen Date associated to Phone #"
+ },
+ {
+ "Value": "No Last Seen Date associated to Phone #"
+ },
+ {
+ "Value": "No First Seen and Last Seen Date associated to Phone #"
+ },
+ {
+ "Value": "Phone Service Type is Wireless"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Prepaid",
+ "Values": [
+ {
+ "Value": "0"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "OTP_Count",
+ "Values": [
+ {
+ "Value": "1"
+ }
+ ]
+ },
+ {
+ "Group": "IDENTITY_RECORDS",
+ "Name": "Consumer_Ownership_at_Carrier_Indicators",
+ "Values": [
+ {
+ "Value": "0"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "OTP_Last_Year",
+ "Values": [
+ {
+ "Value": "2022"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "OTP_Last_Month",
+ "Values": [
+ {
+ "Value": "02"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "OTP_Last_Day",
+ "Values": [
+ {
+ "Value": "01"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "OTP_Last_Status",
+ "Values": [
+ {
+ "Value": "1"
+ }
+ ]
+ },
+ {
+ "Group": "IDENTITY_RECORDS",
+ "Name": "Consumer_Source_Counts",
+ "Values": [
+ {
+ "Value": "1"
+ }
+ ]
+ },
+ {
+ "Group": "IDENTITY_RECORDS",
+ "Name": "Consumer_Self_Reported_Only",
+ "Values": [
+ {
+ "Value": "0"
+ }
+ ]
+ },
+ {
+ "Group": "IDENTITY_RECORDS",
+ "Name": "Consumer_to_Phone_Sources",
+ "Values": [
+ {
+ "Value": "WirelessPhoneContent"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Source_Count",
+ "Values": [
+ {
+ "Value": "0"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Self_Reported_Only",
+ "Values": [
+ {
+ "Value": "0"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Port_Status",
+ "Values": [
+ {
+ "Value": ""
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "OTP_History_Statuses",
+ "Values": [
+ {
+ "Value": "1"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "OTP_History_Year",
+ "Values": [
+ {
+ "Value": "2022"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "OTP_History_Month",
+ "Values": [
+ {
+ "Value": "02"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "OTP_History_Day",
+ "Values": [
+ {
+ "Value": "01"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_OTP_Risk_Exceeded",
+ "Values": [
+ {
+ "Value": "0"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/spec/fixtures/proofing/lexis_nexis/phone_finder/rdp2_response.json b/spec/fixtures/proofing/lexis_nexis/phone_finder/rdp2_response.json
new file mode 100644
index 00000000000..5e567fe4ee0
--- /dev/null
+++ b/spec/fixtures/proofing/lexis_nexis/phone_finder/rdp2_response.json
@@ -0,0 +1,594 @@
+{
+ "Status": {
+ "ConversationId": "31000000000000",
+ "RequestId": "1000000",
+ "TransactionStatus": "passed"
+ },
+ "Products": [
+ {
+ "ProductType": "PhoneFinder",
+ "ExecutedStepName": "Execute_PhoneFinder",
+ "ProductConfigurationName": "",
+ "ProductStatus": "pass",
+ "Items": [
+ {
+ "ItemName": "PrepaidPhoneNumber",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "LastChangeDateIMEI",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "NoContractCarrier",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "VOIPPhone",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "LastChangeDateIMSI",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "SurnameMismatch",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "LostStolen",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "PhoneReturnedOften",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "WirelessServiceType",
+ "ItemStatus": "fail",
+ "ItemReason": {
+ "Code": "WirelessServiceType.LOW",
+ "Description": "Phone Service Type is Wireless"
+ }
+ },
+ {
+ "ItemName": "SourceSelfReported",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "LandlineServiceType",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "CableServiceType",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "1stFirstSeenDate",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "2ndFirstSeenDate",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "3rdFirstSeenDate",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "PhonePorted",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "FrequentlyPhonePorted",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "PhoneSpoofedNumOfDays",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "SpoofingPhoneNumber",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "SpoofingDestination",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "PhoneAssociatedWithMoreThanXIdentities",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "EmailCount(Month)",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "DeviceCount(Month)",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "PhoneHighCountryCount",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "RecentPhoneFirstSeen",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "DigitalIDBadReputation",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "PhoneNumberAssociatedWithFraud",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "PhoneInRejectedTransaction",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "PhoneSearch",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "PhoneOnGlobalBlacklist",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "PhoneOTPRequest",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "PhoneNotActive",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "SpoofedPhoneNumber",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "SubjectIsBusiness",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "PhoneForwarding",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "SubjectDeceased",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "NoFirstSeenDate",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "CommercialAddress",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "NoLastSeenDate",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "MismatchAreaCodeAndAddressArea",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "NoFirstAndLastSeenDate",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "NotCurrentAddress",
+ "ItemStatus": "pass"
+ },
+ {
+ "ItemName": "LastSeenDateAge",
+ "ItemStatus": "pass"
+ }
+ ],
+ "ParameterDetails": [
+ {
+ "Group": "IDENTITY_RECORDS",
+ "Name": "Consumer_LexIDs",
+ "Values": [
+ {
+ "Value": "1145063437"
+ },
+ {
+ "Value": "0"
+ }
+ ]
+ },
+ {
+ "Group": "IDENTITY_RECORDS",
+ "Name": "Deceased_Indicators",
+ "Values": [
+ {
+ "Value": "N"
+ },
+ {
+ "Value": "N"
+ }
+ ]
+ },
+ {
+ "Group": "IDENTITY_RECORDS",
+ "Name": "Consumer_Full_Names",
+ "Values": [
+ {
+ "Value": "PII NAME"
+ },
+ {
+ "Value": "PII NAME"
+ }
+ ]
+ },
+ {
+ "Group": "IDENTITY_RECORDS",
+ "Name": "Consumer_Street_1s",
+ "Values": [
+ {
+ "Value": "123 PII ST"
+ },
+ {
+ "Value": ""
+ }
+ ]
+ },
+ {
+ "Group": "IDENTITY_RECORDS",
+ "Name": "Consumer_Street_2s",
+ "Values": [
+ {
+ "Value": ""
+ },
+ {
+ "Value": ""
+ }
+ ]
+ },
+ {
+ "Group": "IDENTITY_RECORDS",
+ "Name": "Consumer_StateCityZIPs",
+ "Values": [
+ {
+ "Value": "PII, HI 55555"
+ },
+ {
+ "Value": "PII, HI 55555"
+ }
+ ]
+ },
+ {
+ "Group": "IDENTITY_RECORDS",
+ "Name": "Consumer_Time_With_Phones",
+ "Values": [
+ {
+ "Value": "36"
+ },
+ {
+ "Value": ""
+ }
+ ]
+ },
+ {
+ "Group": "IDENTITY_RECORDS",
+ "Name": "Consumer_Time_Last_Seen_With_Phones",
+ "Values": [
+ {
+ "Value": "4"
+ },
+ {
+ "Value": ""
+ }
+ ]
+ },
+ {
+ "Group": "IDENTITY_RECORDS",
+ "Name": "Consumer_Address_Types",
+ "Values": [
+ {
+ "Value": "Residential"
+ },
+ {
+ "Value": ""
+ }
+ ]
+ },
+ {
+ "Group": "IDENTITY_RECORDS",
+ "Name": "Consumer_Address_Statuses",
+ "Values": [
+ {
+ "Value": ""
+ },
+ {
+ "Value": ""
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Call_Forwarding_Indicator",
+ "Values": [
+ {
+ "Value": ""
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Number",
+ "Values": [
+ {
+ "Value": "5555551212"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Type",
+ "Values": [
+ {
+ "Value": "POSSIBLE WIRELESS"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Carrier",
+ "Values": [
+ {
+ "Value": "PII COMPANY"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Listing_Name",
+ "Values": [
+ {
+ "Value": "PII NAME"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Status",
+ "Values": [
+ {
+ "Value": "ACTIVE"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Listing_Type",
+ "Values": [
+ {
+ "Value": "RESIDENTIAL"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Address_Type",
+ "Values": [
+ {
+ "Value": ""
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Port_Code",
+ "Values": [
+ {
+ "Value": "Not Ported"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Caller_ID",
+ "Values": [
+ {
+ "Value": "PII NAME"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Port_Count",
+ "Values": [
+ {
+ "Value": "0"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Phone_Verification_Indicator",
+ "Values": [
+ {
+ "Value": "1"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_No_Contract_Carrier",
+ "Values": [
+ {
+ "Value": "0"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Operating_Company",
+ "Values": [
+ {
+ "Value": "PII COMPANY"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Phone_Verification_Msg",
+ "Values": [
+ {
+ "Value": "Input phone number matches last name"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Origination_Spoof_Count",
+ "Values": [
+ {
+ "Value": "0"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Spoofed_Count",
+ "Values": [
+ {
+ "Value": "0"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Destination_Spoof_Count",
+ "Values": [
+ {
+ "Value": "0"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Risk_Indicator_Status",
+ "Values": [
+ {
+ "Value": "WARN"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Risk_Indicator_Messages",
+ "Values": [
+ {
+ "Value": "Phone Service Type is Wireless"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Prepaid",
+ "Values": [
+ {
+ "Value": "0"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "OTP_Count",
+ "Values": [
+ {
+ "Value": "0"
+ }
+ ]
+ },
+ {
+ "Group": "IDENTITY_RECORDS",
+ "Name": "Consumer_Ownership_at_Carrier_Indicators",
+ "Values": [
+ {
+ "Value": "0"
+ },
+ {
+ "Value": "0"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "OTP_Last_Status",
+ "Values": [
+ {
+ "Value": "0"
+ }
+ ]
+ },
+ {
+ "Group": "IDENTITY_RECORDS",
+ "Name": "Consumer_Source_Counts",
+ "Values": [
+ {
+ "Value": "3"
+ },
+ {
+ "Value": "1"
+ }
+ ]
+ },
+ {
+ "Group": "IDENTITY_RECORDS",
+ "Name": "Consumer_Self_Reported_Only",
+ "Values": [
+ {
+ "Value": "0"
+ },
+ {
+ "Value": "0"
+ }
+ ]
+ },
+ {
+ "Group": "IDENTITY_RECORDS",
+ "Name": "Consumer_to_Phone_Sources",
+ "Values": [
+ {
+ "Value": "WirelessPhoneContent"
+ },
+ {
+ "Value": "CreditInquiry"
+ },
+ {
+ "Value": "LNRSInquiry"
+ },
+ {
+ "Value": "WirelessPhoneContent"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Source_Count",
+ "Values": [
+ {
+ "Value": "0"
+ }
+ ]
+ },
+ {
+ "Group": "PRIMARY_PHONE",
+ "Name": "Primary_Phone_Self_Reported_Only",
+ "Values": [
+ {
+ "Value": "0"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/spec/jobs/irs_attempts_events_batch_job_spec.rb b/spec/jobs/irs_attempts_events_batch_job_spec.rb
new file mode 100644
index 00000000000..404df075f3c
--- /dev/null
+++ b/spec/jobs/irs_attempts_events_batch_job_spec.rb
@@ -0,0 +1,75 @@
+require 'rails_helper'
+
+RSpec.describe IrsAttemptsEventsBatchJob, type: :job do
+ describe '#perform' do
+ context 'IRS attempts API is enabled' do
+ let(:start_time) { Time.new(2020, 1, 1, 12, 0, 0, 'UTC') }
+ let(:private_key) { OpenSSL::PKey::RSA.new(4096) }
+ let(:events) do
+ [
+ {
+ event_key: 'key1',
+ jwe: 'some_event_data_encrypted_with_jwe',
+ timestamp: start_time + 10.minutes,
+ },
+ {
+ event_key: 'key2',
+ jwe: 'some_other_event_data_encrypted_with_jwe',
+ timestamp: start_time + 15.minutes,
+ },
+ ]
+ end
+
+ before do
+ allow(IdentityConfig.store).to receive(:irs_attempt_api_enabled).and_return(true)
+ allow(IdentityConfig.store).to receive(:irs_attempt_api_public_key).
+ and_return(Base64.strict_encode64(private_key.public_key.to_der))
+
+ Dir.mktmpdir do |dir|
+ @dir_path = dir
+ end
+
+ travel_to start_time + 1.hour
+
+ redis_client = IrsAttemptsApi::RedisClient.new
+ events.each do |event|
+ redis_client.write_event(
+ event_key: event[:event_key], jwe: event[:jwe],
+ timestamp: event[:timestamp]
+ )
+ end
+ end
+
+ it 'batches and writes attempt events to an encrypted file' do
+ result = IrsAttemptsEventsBatchJob.perform_now(timestamp: start_time, dir_path: @dir_path)
+ expect(result[:file_path]).not_to be_nil
+
+ file_data = File.open(result[:file_path], 'rb') do |file|
+ file.read
+ end
+
+ final_key = private_key.private_decrypt(result[:encryptor_result].encrypted_key)
+
+ decrypted_result = IrsAttemptsApi::EnvelopeEncryptor.decrypt(
+ encrypted_data: file_data,
+ key: final_key, iv: result[:encryptor_result].iv
+ )
+
+ events_jwes = events.pluck(:jwe)
+
+ expect(decrypted_result).to eq(events_jwes.join("\r\n"))
+ end
+ end
+
+ context 'IRS attempts API is not enabled' do
+ before do
+ allow(IdentityConfig.store).to receive(:irs_attempt_api_enabled).and_return(false)
+ end
+
+ it 'returns nil' do
+ file_path = IrsAttemptsEventsBatchJob.perform_now
+ expect(file_path).to eq(nil)
+ end
+ end
+ end
+end
diff --git a/spec/jobs/reports/iaa_billing_report_spec.rb b/spec/jobs/reports/iaa_billing_report_spec.rb
deleted file mode 100644
index 2599989423b..00000000000
--- a/spec/jobs/reports/iaa_billing_report_spec.rb
+++ /dev/null
@@ -1,190 +0,0 @@
-require 'rails_helper'
-
-describe Reports::IaaBillingReport do
- subject { described_class.new }
-
- let(:issuer) { 'foo' }
- let(:issuer2) { 'foo2' }
- let(:issuer3) { 'foo3' }
- let(:iaa) { 'iaa' }
- let(:iaa2) { 'iaa2' }
- let(:iaa_start_date1_str) { '2020-02-01' }
- let(:iaa_end_date1_str) { '2021-02-01' }
- let(:iaa_start_date2_str) { '2020-05-01' }
- let(:iaa_end_date2_str) { '2021-05-01' }
- let(:iaa_start_date1) { Time.zone.parse(iaa_start_date1_str) }
- let(:iaa_end_date1) { Time.zone.parse(iaa_end_date1_str) }
- let(:iaa_start_date2) { Time.zone.parse(iaa_start_date2_str) }
- let(:iaa_end_date2) { Time.zone.parse(iaa_end_date2_str) }
-
- let(:results_for_1_iaa) do
- [
- {
- iaa: 'iaa',
- iaa_start_date: iaa_start_date1_str,
- iaa_end_date: iaa_end_date1_str,
- ial2_active_count: 0,
- auth_counts:
- [
- {
- issuer: 'foo',
- ial: 1,
- count: 0,
- },
- {
- issuer: 'foo',
- ial: 2,
- count: 0,
- },
- {
- issuer: 'foo2',
- ial: 1,
- count: 0,
- },
- {
- issuer: 'foo2',
- ial: 2,
- count: 0,
- },
- ],
- },
- ]
- end
- let(:results_for_2_iaas_1) do
- {
- iaa: 'iaa',
- iaa_start_date: iaa_start_date1_str,
- iaa_end_date: iaa_end_date1_str,
- ial2_active_count: 1,
- auth_counts:
- [
- {
- issuer: 'foo',
- ial: 1,
- count: 7,
- },
- {
- issuer: 'foo',
- ial: 2,
- count: 0,
- },
- ],
- }
- end
- let(:results_for_2_iaas_2) do
- {
- iaa: 'iaa2',
- iaa_start_date: iaa_start_date2_str,
- iaa_end_date: iaa_end_date2_str,
- ial2_active_count: 1,
- auth_counts:
- [
- {
- issuer: 'foo2',
- ial: 1,
- count: 0,
- },
- {
- issuer: 'foo2',
- ial: 2,
- count: 3,
- },
- {
- issuer: 'foo3',
- ial: 1,
- count: 0,
- },
- {
- issuer: 'foo3',
- ial: 2,
- count: 0,
- },
- ],
- }
- end
-
- let(:now) { Time.zone.parse('2020-06-15') }
-
- before do
- Agreements::IntegrationUsage.delete_all
- Agreements::Integration.delete_all
- ServiceProvider.delete_all
- travel_to(now)
- end
-
- it 'works with no SPs' do
- expect(subject.perform(Time.zone.today)).to eq([].to_json)
- end
-
- it 'ignores sps without an IAA or a start date or an end date' do
- ServiceProvider.create(issuer: issuer, friendly_name: issuer, ial: 1)
- ServiceProvider.create(
- issuer: issuer2, friendly_name: issuer2, ial: 1, iaa: iaa,
- iaa_start_date: iaa_start_date1
- )
- ServiceProvider.create(
- issuer: issuer3, friendly_name: issuer3, ial: 1, iaa: iaa2,
- iaa_end_date: iaa_end_date1
- )
-
- expect(subject.perform(Time.zone.today)).to eq([].to_json)
- end
-
- it 'rolls up 2 issuers in a single IAA' do
- ServiceProvider.create(
- issuer: issuer, friendly_name: issuer, ial: 1, iaa: iaa,
- iaa_start_date: iaa_start_date1, iaa_end_date: iaa_end_date1
- )
- ServiceProvider.create(
- issuer: issuer2, friendly_name: issuer2, ial: 2, iaa: iaa,
- iaa_start_date: iaa_start_date1, iaa_end_date: iaa_end_date1
- )
-
- expect(subject.perform(Time.zone.today)).to eq(results_for_1_iaa.to_json)
- end
-
- it 'works with multiple IAAs and issuers' do
- last_month = now.last_month
- today_year_month = now.strftime('%Y%m')
-
- ServiceProvider.create(
- issuer: issuer, friendly_name: issuer, ial: 1, iaa: iaa,
- iaa_start_date: iaa_start_date1, iaa_end_date: iaa_end_date1
- )
- ServiceProvider.create(
- issuer: issuer2, friendly_name: issuer2, ial: 2, iaa: iaa2,
- iaa_start_date: iaa_start_date2, iaa_end_date: iaa_end_date2
- )
- ServiceProvider.create(
- issuer: issuer3, friendly_name: issuer3, ial: 2, iaa: iaa2,
- iaa_start_date: iaa_start_date2, iaa_end_date: iaa_end_date2
- )
- ServiceProviderIdentity.create(
- user_id: 1, service_provider: issuer, uuid: 'a',
- last_ial2_authenticated_at: now - 1.hour
- )
- ServiceProviderIdentity.create(
- user_id: 2, service_provider: issuer2, uuid: 'b',
- last_ial2_authenticated_at: last_month
- )
- ServiceProviderIdentity.create(
- user_id: 3, service_provider: issuer3, uuid: 'c',
- last_ial2_authenticated_at: now - 1.hour
- )
- create(:profile, :active, :verified, user_id: 1)
- create(:profile, :active, :verified, user_id: 2)
- create(:profile, :active, :verified, user_id: 3)
- MonthlySpAuthCount.create(
- issuer: issuer, ial: 1, year_month: today_year_month, user_id: 2,
- auth_count: 7
- )
- MonthlySpAuthCount.create(
- issuer: issuer2, ial: 2, year_month: today_year_month, user_id: 3,
- auth_count: 3
- )
-
- tuples = subject.perform(Time.zone.today)
- expect(tuples).to include(results_for_2_iaas_1.to_json)
- expect(tuples).to include(results_for_2_iaas_2.to_json)
- end
-end
diff --git a/spec/jobs/reports/sp_active_users_over_period_of_performance_report_spec.rb b/spec/jobs/reports/sp_active_users_over_period_of_performance_report_spec.rb
deleted file mode 100644
index ad36992ce09..00000000000
--- a/spec/jobs/reports/sp_active_users_over_period_of_performance_report_spec.rb
+++ /dev/null
@@ -1,64 +0,0 @@
-require 'rails_helper'
-
-describe Reports::SpActiveUsersOverPeriodOfPerformanceReport do
- subject { described_class.new }
-
- let(:issuer) { 'foo' }
- let(:issuer2) { 'foo2' }
- let(:app_id) { 'app' }
-
- it 'is empty' do
- expect(subject.perform(Time.zone.today)).to eq('[]')
- end
-
- it 'returns total active user counts per sp broken down by ial1 and ial2' do
- now = Time.zone.now
- service_provider = ServiceProvider.create(
- issuer: issuer,
- friendly_name: issuer,
- app_id: app_id,
- iaa_start_date: now - 6.months,
- iaa_end_date: now + 6.months,
- )
- ServiceProviderIdentity.create(
- user_id: 1, service_provider: issuer, uuid: 'foo1',
- last_ial1_authenticated_at: now, last_ial2_authenticated_at: now
- )
- ServiceProviderIdentity.create(
- user_id: 2, service_provider: issuer, uuid: 'foo2',
- last_ial1_authenticated_at: now
- )
- ServiceProviderIdentity.create(
- user_id: 3, service_provider: issuer, uuid: 'foo3',
- last_ial2_authenticated_at: now
- )
- ServiceProviderIdentity.create(
- user_id: 4, service_provider: issuer, uuid: 'foo4',
- last_ial2_authenticated_at: now
- )
-
- result = subject.perform(Time.zone.today)
-
- expect(JSON.parse(result, symbolize_names: true)).to eq(
- [{
- issuer: issuer,
- app_id: app_id,
- iaa: service_provider.iaa,
- total_ial1_active: 2,
- total_ial2_active: 3,
- iaa_start_date: service_provider.iaa_start_date.to_s,
- iaa_end_date: service_provider.iaa_end_date.to_s,
- }],
- )
- end
-
- describe '#good_job_concurrency_key' do
- let(:date) { Time.zone.today }
-
- it 'is the job name and the date' do
- job = described_class.new(date)
- expect(job.good_job_concurrency_key).
- to eq("#{described_class::REPORT_NAME}-#{date}")
- end
- end
-end
diff --git a/spec/jobs/reports/sp_cost_report_spec.rb b/spec/jobs/reports/sp_cost_report_spec.rb
deleted file mode 100644
index bc301c94bb3..00000000000
--- a/spec/jobs/reports/sp_cost_report_spec.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-require 'rails_helper'
-
-describe Reports::SpCostReport do
- subject { described_class.new }
-
- let(:issuer1) { 'issuer1' }
- let(:app_id1) { 'app_id1' }
- let(:issuer2) { 'issuer2' }
- let(:app_id2) { 'app_id2' }
-
- it 'is empty' do
- expect(subject.perform(Time.zone.today)).to eq('[]')
- end
-
- it 'totals the cost per sp' do
- ::SpCost.create(issuer: issuer1, ial: 1, agency_id: 2, cost_type: 'foo')
- ::SpCost.create(issuer: issuer1, ial: 1, agency_id: 2, cost_type: 'foo')
- ::SpCost.create(issuer: issuer2, ial: 2, agency_id: 3, cost_type: 'bar')
- ServiceProvider.create(issuer: issuer1, friendly_name: issuer1, app_id: app_id1)
- ServiceProvider.create(issuer: issuer2, friendly_name: issuer2, app_id: app_id2)
- expect(JSON.parse(subject.perform(Time.zone.today))).to eq(
- [{
- 'issuer' => 'issuer1',
- 'ial' => 1,
- 'app_id' => app_id1,
- 'cost_type' => 'foo',
- 'count' => 2,
- },
- {
- 'issuer' => 'issuer2',
- 'ial' => 2,
- 'app_id' => app_id2,
- 'cost_type' => 'bar',
- 'count' => 1,
- }],
- )
- end
-
- describe '#good_job_concurrency_key' do
- let(:date) { Time.zone.today }
-
- it 'is the job name and the date' do
- job = described_class.new(date)
- expect(job.good_job_concurrency_key).
- to eq("#{described_class::REPORT_NAME}-#{date}")
- end
- end
-end
diff --git a/spec/jobs/reports/total_sp_cost_report_spec.rb b/spec/jobs/reports/total_sp_cost_report_spec.rb
deleted file mode 100644
index 60b0a3c5a5e..00000000000
--- a/spec/jobs/reports/total_sp_cost_report_spec.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-require 'rails_helper'
-
-describe Reports::TotalSpCostReport do
- subject { described_class.new }
-
- let(:issuer1) { 'issuer1' }
- let(:issuer2) { 'issuer2' }
-
- it 'is empty' do
- expect(subject.perform(Time.zone.today)).to eq('[]')
- end
-
- it 'totals the cost per sp' do
- ::SpCost.create(issuer: '', ial: 1, agency_id: 0, cost_type: 'foo')
- ::SpCost.create(issuer: issuer1, ial: 1, agency_id: 2, cost_type: 'foo')
- ::SpCost.create(issuer: issuer1, ial: 1, agency_id: 2, cost_type: 'foo')
- ::SpCost.create(issuer: issuer2, ial: 2, agency_id: 3, cost_type: 'bar')
- expect(JSON.parse(subject.perform(Time.zone.today))).to eq(
- [{
- 'cost_type' => 'bar',
- 'count' => 1,
- },
- {
- 'cost_type' => 'foo',
- 'count' => 3,
- }],
- )
- end
-
- describe '#good_job_concurrency_key' do
- let(:date) { Time.zone.today }
-
- it 'is the job name and the date' do
- job = described_class.new(date)
- expect(job.good_job_concurrency_key).
- to eq("#{described_class::REPORT_NAME}-#{date}")
- end
- end
-end
diff --git a/spec/jobs/reports/unique_monthly_auths_report_spec.rb b/spec/jobs/reports/unique_monthly_auths_report_spec.rb
deleted file mode 100644
index 3a3fd174f01..00000000000
--- a/spec/jobs/reports/unique_monthly_auths_report_spec.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-require 'rails_helper'
-
-describe Reports::UniqueMonthlyAuthsReport do
- subject { described_class.new }
-
- let(:issuer) { 'foo' }
- let(:app_id) { 'app_id' }
- let(:year_month) { '201901' }
-
- it 'is empty' do
- expect(subject.perform(Time.zone.today)).to eq('[]')
- end
-
- it 'returns 1 unique despite the count for the user being 7' do
- ServiceProvider.create(issuer: issuer, friendly_name: issuer, app_id: app_id)
- MonthlySpAuthCount.create(
- issuer: issuer, ial: 1, year_month: '201901', user_id: 2,
- auth_count: 7
- )
- result = [{ issuer: 'foo', year_month: '201901', app_id: app_id, total: 1 }].to_json
-
- expect(subject.perform(Time.zone.today)).to eq(result)
- end
-end
diff --git a/spec/jobs/reports/unique_yearly_auths_report_spec.rb b/spec/jobs/reports/unique_yearly_auths_report_spec.rb
deleted file mode 100644
index 3dde809a03f..00000000000
--- a/spec/jobs/reports/unique_yearly_auths_report_spec.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-require 'rails_helper'
-
-describe Reports::UniqueYearlyAuthsReport do
- subject { described_class.new }
-
- let(:issuer) { 'foo' }
- let(:year_month) { '201901' }
- let(:year) { '2019' }
- let(:app_id) { 'app_id' }
-
- it 'is empty' do
- expect(subject.perform(Time.zone.today)).to eq('[]')
- end
-
- it 'returns 1 unique despite the count for the user being 7' do
- ServiceProvider.create(issuer: issuer, friendly_name: issuer, app_id: app_id)
- MonthlySpAuthCount.create(
- issuer: 'foo', ial: 1, year_month: year_month, user_id: 2,
- auth_count: 7
- )
- result = [{ issuer: 'foo', app_id: app_id, year: year, total: 1 }].to_json
-
- expect(subject.perform(Time.zone.today)).to eq(result)
- end
-end
diff --git a/spec/lib/base16_spec.rb b/spec/lib/base16_spec.rb
new file mode 100644
index 00000000000..69130cb4e37
--- /dev/null
+++ b/spec/lib/base16_spec.rb
@@ -0,0 +1,46 @@
+require 'rails_helper'
+require 'base16'
+
+RSpec.describe Base16 do
+ context 'with reasonable inputs' do
+ context 'given "Hello, World"' do
+ let(:input) { 'Hello, world' }
+
+ it 'returns a known value' do
+ encoded = described_class.encode16(input)
+ # The IRS confirmed this value:
+ expect(encoded).to eq '48656C6C6F2C20776F726C64'
+
+ decoded = described_class.decode16(encoded)
+ expect(decoded).to eq input
+ end
+
+ it 'returns a value with uppercase letters' do
+ encoded = described_class.encode16(input)
+ expect(encoded).to eq(encoded.upcase)
+ end
+ end
+
+ context 'given a sequence of zeroes' do
+ let(:input) { "\x00" }
+ it 'does not truncate them' do
+ encoded = described_class.encode16(input)
+ expect(encoded).to eq '00'
+
+ decoded = described_class.decode16(encoded)
+ expect(decoded).to eq(input)
+ end
+ end
+ end
+
+ context 'with a less reasonable input' do
+ context 'given a zany-face emoji' do
+ let(:input) { '🤪' }
+ it 'returns the same bytes' do
+ encoded = described_class.encode16(input)
+ decoded = described_class.decode16(encoded).force_encoding('UTF-8')
+ expect(decoded).to eq input
+ end
+ end
+ end
+end
diff --git a/spec/lib/tasks/dev_rake_spec.rb b/spec/lib/tasks/dev_rake_spec.rb
index 9f1bc3b6244..aab89907403 100644
--- a/spec/lib/tasks/dev_rake_spec.rb
+++ b/spec/lib/tasks/dev_rake_spec.rb
@@ -2,6 +2,8 @@
require 'rake'
describe 'dev rake tasks' do
+ include UspsIppHelper
+
before do
Rake.application.rake_require 'tasks/dev'
Rake::Task.define_task(:environment)
@@ -22,6 +24,7 @@
prev_enrollment_status = nil
prev_verified = nil
prev_scrypt_cost = nil
+ prev_pending_in_usps = nil
before(:each) do
prev_num_users = ENV['NUM_USERS']
@@ -29,6 +32,7 @@
prev_enrollment_status = ENV['ENROLLMENT_STATUS']
prev_verified = ENV['VERIFIED']
prev_scrypt_cost = ENV['SCRYPT_COST']
+ prev_pending_in_usps = ENV['CREATE_PENDING_ENROLLMENT_IN_USPS']
ENV['PROGRESS'] = 'no'
ENV['NUM_USERS'] = '10'
@@ -43,6 +47,7 @@
ENV['ENROLLMENT_STATUS'] = prev_enrollment_status
ENV['VERIFIED'] = prev_verified
ENV['SCRYPT_COST'] = prev_scrypt_cost
+ ENV['CREATE_PENDING_ENROLLMENT_IN_USPS'] = prev_pending_in_usps
end
describe 'dev:random_users' do
@@ -309,6 +314,35 @@
expect(last_record.profile).to be_instance_of(Profile)
expect(last_record.profile.active).to be(true)
end
+
+ it 'creates the enrollment in USPS IPPaaS when CREATE_PENDING_ENROLLMENT_IN_USPS is truthy' do
+ ENV['CREATE_PENDING_ENROLLMENT_IN_USPS'] = '1'
+ stub_request_token
+ stub_request_enroll
+
+ expect(User.count).to eq 0
+ expect(InPersonEnrollment.count).to eq 0
+
+ Rake::Task['dev:random_in_person_users'].invoke
+
+ expect(User.count).to eq 10
+ expect(InPersonEnrollment.count).to eq 10
+ expect(InPersonEnrollment.distinct.count('user_id')).to eq 10
+ expect(InPersonEnrollment.pending.count).to eq 10
+
+ # Spot check attributes on last record
+ last_record = InPersonEnrollment.last
+ expect(last_record.attributes).to include(
+ 'status' => 'pending',
+ 'enrollment_established_at' => respond_to(:to_date),
+ 'unique_id' => an_instance_of(String),
+
+ # Check for the enrollment code in the stubbed response
+ 'enrollment_code' => '2048702198804353',
+ )
+ expect(last_record.profile).to be_instance_of(Profile)
+ expect(last_record.profile.active).to be(false)
+ end
end
end
end
diff --git a/spec/lib/telephony/pinpoint/sms_sender_spec.rb b/spec/lib/telephony/pinpoint/sms_sender_spec.rb
index 9c5dc89e846..b244a1323d3 100644
--- a/spec/lib/telephony/pinpoint/sms_sender_spec.rb
+++ b/spec/lib/telephony/pinpoint/sms_sender_spec.rb
@@ -378,24 +378,6 @@ def ==(other)
expect(response.error).to eq(Telephony::TelephonyError.new(raised_error_message))
end
end
-
- context 'when all sms configs fail to build' do
- let(:raised_error_message) { 'Failed to load AWS config' }
- let(:mock_client) { nil }
- let(:backup_mock_client) { nil }
-
- it 'logs a warning and returns an error' do
- expect(Telephony.config.logger).to receive(:warn).once
-
- response = subject.send(
- message: 'This is a test!',
- to: '+1 (123) 456-7890',
- country_code: 'US',
- )
- expect(response.success?).to eq(false)
- expect(response.error).to eq(Telephony::UnknownFailureError.new(raised_error_message))
- end
- end
end
context 'when the exception message contains a phone number' do
@@ -434,11 +416,12 @@ def ==(other)
end
def mock_build_client(client = mock_client)
- expect(sms_sender).to receive(:build_client).with(sms_config).and_return(client)
+ Telephony::Pinpoint::SmsSender::CLIENT_POOL[sms_config] = FakeConnectionPool.new { client }
end
def mock_build_backup_client(client = backup_mock_client)
- allow(sms_sender).to receive(:build_client).with(backup_sms_config).and_return(client)
+ Telephony::Pinpoint::SmsSender::CLIENT_POOL[backup_sms_config] =
+ FakeConnectionPool.new { client }
end
describe '#phone_info' do
@@ -457,10 +440,15 @@ def mock_build_backup_client(client = backup_mock_client)
sms.application_id = 'backup-sms-application-id'
end
+ Telephony::Pinpoint::SmsSender::CLIENT_POOL.clear
mock_build_client(pinpoint_client)
mock_build_backup_client(pinpoint_client)
end
+ after do
+ Telephony::Pinpoint::SmsSender::CLIENT_POOL.clear
+ end
+
context 'successful network requests' do
before do
pinpoint_client.stub_responses(
@@ -538,14 +526,5 @@ def mock_build_backup_client(client = backup_mock_client)
expect(phone_info.error).to be_kind_of(Seahorse::Client::NetworkingError)
end
end
-
- context 'when all sms configs fail to build' do
- let(:pinpoint_client) { nil }
-
- it 'returns unknown' do
- expect(phone_info.type).to eq(:unknown)
- expect(phone_info.error).to be_kind_of(Telephony::UnknownFailureError)
- end
- end
end
end
diff --git a/spec/lib/telephony/pinpoint/voice_sender_spec.rb b/spec/lib/telephony/pinpoint/voice_sender_spec.rb
index cd88cd99cc1..8902b50c9ab 100644
--- a/spec/lib/telephony/pinpoint/voice_sender_spec.rb
+++ b/spec/lib/telephony/pinpoint/voice_sender_spec.rb
@@ -12,13 +12,13 @@
let(:backup_voice_config) { Telephony.config.pinpoint.voice_configs.last }
def mock_build_client
- allow(voice_sender).
- to receive(:build_client).with(voice_config).and_return(pinpoint_client)
+ Telephony::Pinpoint::VoiceSender::CLIENT_POOL[voice_config] =
+ FakeConnectionPool.new { pinpoint_client }
end
def mock_build_backup_client
- allow(voice_sender).
- to receive(:build_client).with(backup_voice_config).and_return(backup_pinpoint_client)
+ Telephony::Pinpoint::VoiceSender::CLIENT_POOL[backup_voice_config] =
+ FakeConnectionPool.new { backup_pinpoint_client }
end
describe '#send' do
@@ -46,9 +46,14 @@ def mock_build_backup_client
# More deterministic sending phone
Telephony.config.pinpoint.voice_configs.first.longcode_pool = [sending_phone]
+ Telephony::Pinpoint::VoiceSender::CLIENT_POOL.clear
mock_build_client
end
+ after do
+ Telephony::Pinpoint::VoiceSender::CLIENT_POOL.clear
+ end
+
it 'initializes a pinpoint sms and voice client and uses that to send a message' do
expect(pinpoint_client).to receive(:send_voice_message).
with(expected_message).
@@ -244,23 +249,5 @@ def mock_build_backup_client
end
end
end
-
- context 'when all voice configs fail to build' do
- let(:raised_error_message) { 'Failed to load AWS config' }
- let(:pinpoint_client) { nil }
- let(:backup_pinpoint_client) { nil }
-
- it 'logs a warning and returns an error' do
- expect(Telephony.config.logger).to receive(:warn)
-
- response = subject.send(
- message: 'This is a test!',
- to: '+1 (123) 456-7890',
- country_code: 'US',
- )
- expect(response.success?).to eq(false)
- expect(response.error).to eq(Telephony::UnknownFailureError.new(raised_error_message))
- end
- end
end
end
diff --git a/spec/models/monthly_auth_count_spec.rb b/spec/models/monthly_auth_count_spec.rb
deleted file mode 100644
index 11238c40694..00000000000
--- a/spec/models/monthly_auth_count_spec.rb
+++ /dev/null
@@ -1,50 +0,0 @@
-require 'rails_helper'
-
-describe MonthlySpAuthCount do
- let(:user_id) { 1 }
- let(:issuer) { 'foo' }
- let(:service_provider) { build(:service_provider, issuer: issuer) }
- let(:ial) { 1 }
-
- describe '.increment' do
- it 'sets the monthly count to 1' do
- year_month = current_year_month
- MonthlySpAuthCount.increment(user_id: user_id, service_provider: service_provider, ial: ial)
-
- monthly_auth_count = MonthlySpAuthCount.first
- expect(monthly_auth_count.user_id).to eq(user_id)
- expect(monthly_auth_count.issuer).to eq(issuer)
- expect(monthly_auth_count.year_month).to eq(year_month)
- expect(monthly_auth_count.auth_count).to eq(1)
- end
-
- it 'updates the monthly count to 2' do
- year_month = current_year_month
- MonthlySpAuthCount.increment(user_id: user_id, service_provider: service_provider, ial: ial)
- MonthlySpAuthCount.increment(user_id: user_id, service_provider: service_provider, ial: ial)
-
- monthly_auth_count = MonthlySpAuthCount.first
- expect(monthly_auth_count.user_id).to eq(user_id)
- expect(monthly_auth_count.issuer).to eq(issuer)
- expect(monthly_auth_count.year_month).to eq(year_month)
- expect(monthly_auth_count.auth_count).to eq(2)
- end
-
- it 'updates the monthly count to 3' do
- year_month = current_year_month
- MonthlySpAuthCount.increment(user_id: user_id, service_provider: service_provider, ial: ial)
- MonthlySpAuthCount.increment(user_id: user_id, service_provider: service_provider, ial: ial)
- MonthlySpAuthCount.increment(user_id: user_id, service_provider: service_provider, ial: ial)
-
- monthly_auth_count = MonthlySpAuthCount.first
- expect(monthly_auth_count.user_id).to eq(user_id)
- expect(monthly_auth_count.issuer).to eq(issuer)
- expect(monthly_auth_count.year_month).to eq(year_month)
- expect(monthly_auth_count.auth_count).to eq(3)
- end
- end
-
- def current_year_month
- Time.zone.today.strftime('%Y%m')
- end
-end
diff --git a/spec/services/db/identity/sp_active_user_counts_within_iaa_window_spec.rb b/spec/services/db/identity/sp_active_user_counts_within_iaa_window_spec.rb
deleted file mode 100644
index 6c9d515e364..00000000000
--- a/spec/services/db/identity/sp_active_user_counts_within_iaa_window_spec.rb
+++ /dev/null
@@ -1,118 +0,0 @@
-require 'rails_helper'
-
-describe Db::Identity::SpActiveUserCountsWithinIaaWindow do
- subject { described_class }
-
- it 'is empty' do
- expect(subject.call.ntuples).to eq(0)
- end
-
- context 'with data' do
- let(:service_provider_no_start_end) do
- create(
- :service_provider,
- issuer: 'issuer1',
- iaa_start_date: nil,
- iaa_end_date: nil,
- )
- end
-
- let(:service_provider_april_to_april) do
- create(
- :service_provider,
- issuer: 'issuer2',
- iaa: 'iaa_abcdef',
- iaa_start_date: Date.new(2021, 4, 1),
- iaa_end_date: Date.new(2022, 4, 1),
- )
- end
-
- let(:service_provider_september_to_september) do
- create(
- :service_provider,
- issuer: 'issuer3',
- iaa_start_date: Date.new(2021, 9, 1),
- iaa_end_date: Date.new(2022, 9, 1),
- )
- end
-
- let(:inside_april_to_april) { Date.new(2021, 5, 1) }
- let(:inside_september_to_september) { Date.new(2021, 10, 1) }
- let(:outside_iaas) { Date.new(2020, 1, 1) }
-
- before do
- # SP without start/end dates, one IAL 1 login (skipped)
- create(
- :service_provider_identity,
- service_provider_record: service_provider_no_start_end,
- last_ial1_authenticated_at: inside_april_to_april,
- )
-
- # April-April SP
- # one IAL1 login within IAA window
- create(
- :service_provider_identity,
- service_provider_record: service_provider_april_to_april,
- last_ial1_authenticated_at: inside_april_to_april,
- )
- # one IAL1 login outside IAA window (skipped)
- create(
- :service_provider_identity,
- service_provider_record: service_provider_april_to_april,
- last_ial1_authenticated_at: outside_iaas,
- )
-
- # September-September SP
- # has two IAL1 logins within IAA window
- 2.times do
- create(
- :service_provider_identity,
- service_provider_record: service_provider_september_to_september,
- last_ial1_authenticated_at: inside_september_to_september,
- )
- end
- # has two IAL2 logins within IAA window
- 3.times do
- create(
- :service_provider_identity,
- service_provider_record: service_provider_september_to_september,
- last_ial2_authenticated_at: inside_september_to_september,
- )
- end
- # has one IAL2 login outside IAA window (skipped)
- create(
- :service_provider_identity,
- service_provider_record: service_provider_september_to_september,
- last_ial2_authenticated_at: outside_iaas,
- )
- end
-
- it 'returns active user counts by SP with the IAA start/end, counted by IAL1 level' do
- result = subject.call
-
- expect(result.ntuples).to eq(2)
-
- april = result.first
- expect(april.symbolize_keys).to eq(
- issuer: service_provider_april_to_april.issuer,
- app_id: service_provider_april_to_april.app_id,
- iaa: service_provider_april_to_april.iaa,
- total_ial1_active: 1,
- total_ial2_active: 0,
- iaa_start_date: service_provider_april_to_april.iaa_start_date.to_s,
- iaa_end_date: service_provider_april_to_april.iaa_end_date.to_s,
- )
-
- september = result.to_a.last
- expect(september.symbolize_keys).to eq(
- issuer: service_provider_september_to_september.issuer,
- app_id: service_provider_september_to_september.app_id,
- iaa: service_provider_september_to_september.iaa,
- total_ial1_active: 2,
- total_ial2_active: 3,
- iaa_start_date: service_provider_september_to_september.iaa_start_date.to_s,
- iaa_end_date: service_provider_september_to_september.iaa_end_date.to_s,
- )
- end
- end
-end
diff --git a/spec/services/db/monthly_auth_count/unique_monthly_auth_counts_spec.rb b/spec/services/db/monthly_auth_count/unique_monthly_auth_counts_spec.rb
deleted file mode 100644
index 8ee8e27799e..00000000000
--- a/spec/services/db/monthly_auth_count/unique_monthly_auth_counts_spec.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-require 'rails_helper'
-
-describe Db::MonthlySpAuthCount::UniqueMonthlyAuthCounts do
- subject { described_class }
-
- let(:issuer) { 'foo' }
- let(:app_id) { 'app_id' }
- let(:year_month) { '201901' }
-
- it 'is empty' do
- expect(subject.call.ntuples).to eq(0)
- end
-
- it 'returns 1 unique despite the count for the user being 7' do
- ServiceProvider.create(issuer: issuer, friendly_name: issuer, app_id: app_id)
- MonthlySpAuthCount.create(
- issuer: issuer, ial: 1, year_month: year_month, user_id: 2,
- auth_count: 7
- )
- result = { issuer: issuer, year_month: year_month, app_id: app_id, total: 1 }.to_json
-
- expect(subject.call.ntuples).to eq(1)
- expect(subject.call[0].to_json).to eq(result)
- end
-end
diff --git a/spec/services/db/monthly_auth_count/unique_yearly_auth_counts_spec.rb b/spec/services/db/monthly_auth_count/unique_yearly_auth_counts_spec.rb
deleted file mode 100644
index e1000be65c6..00000000000
--- a/spec/services/db/monthly_auth_count/unique_yearly_auth_counts_spec.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-require 'rails_helper'
-
-describe Db::MonthlySpAuthCount::UniqueYearlyAuthCounts do
- subject { described_class }
-
- let(:issuer) { 'foo' }
- let(:app_id) { 'app_id' }
- let(:year_month) { '201901' }
- let(:year) { '2019' }
-
- it 'is empty' do
- expect(subject.call.ntuples).to eq(0)
- end
-
- it 'returns 1 unique despite the count for the user being 7' do
- ServiceProvider.create(issuer: issuer, friendly_name: issuer, app_id: app_id)
- MonthlySpAuthCount.create(
- issuer: issuer, ial: 1, year_month: year_month, user_id: 2,
- auth_count: 7
- )
- result = { issuer: issuer, app_id: app_id, year: year, total: 1 }.to_json
-
- expect(subject.call.ntuples).to eq(1)
- expect(subject.call[0].to_json).to eq(result)
- end
-end
diff --git a/spec/services/encryption/contextless_kms_client_spec.rb b/spec/services/encryption/contextless_kms_client_spec.rb
index 984d3ed7307..1247bbafbad 100644
--- a/spec/services/encryption/contextless_kms_client_spec.rb
+++ b/spec/services/encryption/contextless_kms_client_spec.rb
@@ -8,7 +8,7 @@
before do
stub_const(
'Encryption::ContextlessKmsClient::KMS_CLIENT_POOL',
- AwsKmsClientHelper::FakeConnectionPool.new { Aws::KMS::Client.new },
+ FakeConnectionPool.new { Aws::KMS::Client.new },
)
end
diff --git a/spec/services/encryption/multi_region_kms_client_spec.rb b/spec/services/encryption/multi_region_kms_client_spec.rb
index 8ba7e7c7901..d82e7e0766c 100644
--- a/spec/services/encryption/multi_region_kms_client_spec.rb
+++ b/spec/services/encryption/multi_region_kms_client_spec.rb
@@ -10,9 +10,7 @@
stub_const(
'Encryption::MultiRegionKmsClient::KMS_REGION_CLIENT_POOL',
Hash.new do |h, region|
- h[region] = AwsKmsClientHelper::FakeConnectionPool.new do
- Aws::KMS::Client.new(region: region)
- end
+ h[region] = FakeConnectionPool.new { Aws::KMS::Client.new(region: region) }
end,
)
diff --git a/spec/services/funnel/registration/range_registered_count_spec.rb b/spec/services/funnel/registration/range_registered_count_spec.rb
deleted file mode 100644
index 271fb357b69..00000000000
--- a/spec/services/funnel/registration/range_registered_count_spec.rb
+++ /dev/null
@@ -1,58 +0,0 @@
-require 'rails_helper'
-
-describe Funnel::Registration::RangeRegisteredCount do
- let(:analytics) { FakeAnalytics.new }
- subject { described_class }
-
- let(:start) { '2019-01-01 00:00:00' }
- let(:finish) { '2019-12-31 23:59:50' }
-
- it 'returns 0 when there are no records' do
- expect(subject.call(start, finish)).to eq(0)
- end
-
- it 'returns 1 when the record is mid range' do
- register_user(2019, 6, 1)
-
- expect(subject.call(start, finish)).to eq(1)
- end
-
- it 'returns 1 when the record is in same month is upper range' do
- register_user(2019, 12, 30)
-
- expect(subject.call(start, finish)).to eq(1)
- end
-
- it 'returns 1 when the record is in same month is lower range' do
- register_user(2019, 1, 2)
-
- expect(subject.call(start, finish)).to eq(1)
- end
-
- it 'returns 0 when the record is lower than lower range' do
- register_user(2018, 12, 31)
-
- expect(subject.call(start, finish)).to eq(0)
- end
-
- it 'returns 0 when the record is higher than higher range' do
- register_user(2020, 1, 2)
-
- expect(subject.call(start, finish)).to eq(0)
- end
-
- it 'returns 2 when 2 records are in range' do
- register_user(2019, 1, 2)
- register_user(2019, 12, 30)
-
- expect(subject.call(start, finish)).to eq(2)
- end
-
- def register_user(year, month, day)
- travel_to Date.new(year, month, day) do
- user = create(:user)
- user_id = user.id
- Funnel::Registration::AddMfa.call(user_id, 'backup_codes', analytics)
- end
- end
-end
diff --git a/spec/services/irs_attempts_api/envelope_encryptor_spec.rb b/spec/services/irs_attempts_api/envelope_encryptor_spec.rb
index 17012b5c5a0..42a5d75ffcb 100644
--- a/spec/services/irs_attempts_api/envelope_encryptor_spec.rb
+++ b/spec/services/irs_attempts_api/envelope_encryptor_spec.rb
@@ -12,7 +12,7 @@
expect(result.encrypted_data).to_not eq text
expect(result.encrypted_data).to_not include(text)
- expect(Base64.decode64(result.encrypted_data)).to_not include(text)
+ expect(Base16.decode16(result.encrypted_data)).to_not include(text)
end
it 'filename includes digest and truncated timestamp' do
diff --git a/spec/services/proofing/lexis_nexis/phone_finder/proofing_spec.rb b/spec/services/proofing/lexis_nexis/phone_finder/proofing_spec.rb
index fc53bf4ebbd..80bb7215be0 100644
--- a/spec/services/proofing/lexis_nexis/phone_finder/proofing_spec.rb
+++ b/spec/services/proofing/lexis_nexis/phone_finder/proofing_spec.rb
@@ -28,9 +28,19 @@
describe '#proof' do
context 'when the response is a success' do
- it 'is a successful result' do
+ it 'is a successful rdp1 result' do
stub_request(:post, verification_request.url).
- to_return(body: LexisNexisFixtures.phone_finder_success_response_json, status: 200)
+ to_return(body: LexisNexisFixtures.phone_finder_rdp1_success_response_json, status: 200)
+
+ result = instance.proof(applicant)
+
+ expect(result.success?).to eq(true)
+ expect(result.errors).to eq({})
+ end
+
+ it 'is a successful rdp2 result' do
+ stub_request(:post, verification_request.url).
+ to_return(body: LexisNexisFixtures.phone_finder_rdp2_success_response_json, status: 200)
result = instance.proof(applicant)
@@ -39,10 +49,10 @@
end
end
- context 'when the response is a failure' do
+ context 'when the rdp1 response is a failure' do
it 'is a failure result' do
stub_request(:post, verification_request.url).
- to_return(body: LexisNexisFixtures.phone_finder_fail_response_json, status: 200)
+ to_return(body: LexisNexisFixtures.phone_finder_rdp1_fail_response_json, status: 200)
result = instance.proof(applicant)
@@ -56,6 +66,22 @@
end
end
+ context 'when the rdp2 response is a failure' do
+ it 'is a failure result' do
+ stub_request(:post, verification_request.url).
+ to_return(body: LexisNexisFixtures.phone_finder_rdp2_fail_response_json, status: 200)
+
+ result = instance.proof(applicant)
+ result_json_hash = result.errors[:PhoneFinder].first
+
+ expect(result.success?).to eq(false)
+ expect(result_json_hash['ProductStatus']).to eq('fail')
+ expect(result_json_hash['Items'].class).to eq(Array)
+ # check that key contaning PII is removed and not logged
+ expect(result_json_hash['ParameterDetails']).to eq(nil)
+ end
+ end
+
context 'when the request times out' do
it 'retuns a timeout result' do
stub_request(:post, verification_request.url).to_timeout
diff --git a/spec/services/proofing/lexis_nexis/phone_finder/verification_request_spec.rb b/spec/services/proofing/lexis_nexis/phone_finder/verification_request_spec.rb
index bea0dca2be4..95e73649315 100644
--- a/spec/services/proofing/lexis_nexis/phone_finder/verification_request_spec.rb
+++ b/spec/services/proofing/lexis_nexis/phone_finder/verification_request_spec.rb
@@ -12,7 +12,7 @@
phone: '5551231234',
}
end
- let(:response_body) { LexisNexisFixtures.phone_finder_success_response_json }
+ let(:response_body) { LexisNexisFixtures.phone_finder_rdp1_success_response_json }
subject { described_class.new(applicant: applicant, config: LexisNexisFixtures.example_config) }
it_behaves_like 'a lexisnexis request'
diff --git a/spec/services/user_event_creator_spec.rb b/spec/services/user_event_creator_spec.rb
index 5e37bff8a3e..954f5213e70 100644
--- a/spec/services/user_event_creator_spec.rb
+++ b/spec/services/user_event_creator_spec.rb
@@ -33,7 +33,7 @@
describe '#create_user_event' do
context 'when a device exists for the user' do
it 'updates the device and creates an event' do
- event = subject.create_user_event(event_type, user)
+ event, _disavowal_token = subject.create_user_event(event_type, user)
expect(event.event_type).to eq(event_type)
expect(event.ip).to eq(ip_address)
@@ -49,7 +49,7 @@
it 'creates a device and creates an event' do
expect(UserAlerts::AlertUserAboutNewDevice).to_not receive(:call)
- event = subject.create_user_event(event_type, user)
+ event, _disavowal_token = subject.create_user_event(event_type, user)
expect(event.event_type).to eq(event_type)
expect(event.ip).to eq(ip_address)
@@ -62,7 +62,7 @@
allow(UserAlerts::AlertUserAboutNewDevice).to receive(:call)
create(:device, user: user)
- event = subject.create_user_event(event_type, user)
+ event, _disavowal_token = subject.create_user_event(event_type, user)
expect(event).to be_a(Event)
expect(UserAlerts::AlertUserAboutNewDevice).to have_received(:call).
@@ -76,7 +76,7 @@
it 'creates a device and creates an event' do
expect(UserAlerts::AlertUserAboutNewDevice).to_not receive(:call)
- event = subject.create_user_event(event_type, user)
+ event, _disavowal_token = subject.create_user_event(event_type, user)
expect(event.event_type).to eq(event_type)
expect(event.ip).to eq(ip_address)
@@ -88,7 +88,7 @@
let(:existing_device_cookie) { nil }
it 'assigns one to the device' do
- event = subject.create_user_event(event_type, user)
+ event, _disavowal_token = subject.create_user_event(event_type, user)
expect(event.device.cookie_uuid.length).to eq(UserEventCreator::COOKIE_LENGTH)
end
@@ -98,7 +98,7 @@
allow(UserAlerts::AlertUserAboutNewDevice).to receive(:call)
create(:device, user: user)
- event = subject.create_user_event(event_type, user)
+ event, _disavowal_token = subject.create_user_event(event_type, user)
expect(event).to be_a(Event)
expect(UserAlerts::AlertUserAboutNewDevice).to have_received(:call).
@@ -109,10 +109,10 @@
describe '#create_user_event_with_disavowal' do
it 'creates a device with a disavowal' do
- event = subject.create_user_event_with_disavowal(event_type, user)
+ event, disavowal_token = subject.create_user_event_with_disavowal(event_type, user)
- expect(event.disavowal_token).to_not be_nil
- expect(event.disavowal_token_fingerprint).to_not be_nil
+ expect(event.disavowal_token_fingerprint).
+ to eq(Pii::Fingerprinter.fingerprint(disavowal_token))
end
end
@@ -121,7 +121,7 @@
let(:event_type) { :password_invalidated }
it 'creates an event without a device and without an IP address' do
- event = subject.create_out_of_band_user_event(event_type)
+ event, _disavowal_token = subject.create_out_of_band_user_event(event_type)
expect(event.event_type).to eq(event_type.to_s)
expect(event.ip).to be_blank
diff --git a/spec/support/aws_kms_client.rb b/spec/support/aws_kms_client.rb
index 4f7f1220616..4a9e2d5ff38 100644
--- a/spec/support/aws_kms_client.rb
+++ b/spec/support/aws_kms_client.rb
@@ -1,16 +1,4 @@
module AwsKmsClientHelper
- # The real ConnectionPools persist clients across specs which
- # makes stubbing via the Aws.config unreliable
- class FakeConnectionPool
- def initialize(&block)
- @block = block
- end
-
- def with
- yield @block.call
- end
- end
-
def stub_aws_kms_client(random_key = random_str, ciphered_key = random_str)
aws_key_id = IdentityConfig.store.aws_kms_key_id
Aws.config[:kms] = {
diff --git a/spec/support/fake_connection_pool.rb b/spec/support/fake_connection_pool.rb
new file mode 100644
index 00000000000..8a4e63a296c
--- /dev/null
+++ b/spec/support/fake_connection_pool.rb
@@ -0,0 +1,12 @@
+# The real ConnectionPools persist clients across specs which
+# makes stubbing via the Aws.config unreliable, so we use this to help mock
+# specific objects
+class FakeConnectionPool
+ def initialize(&block)
+ @block = block
+ end
+
+ def with
+ yield @block.call
+ end
+end
diff --git a/spec/support/lexis_nexis_fixtures.rb b/spec/support/lexis_nexis_fixtures.rb
index 574bb82a6c5..15d1355c593 100644
--- a/spec/support/lexis_nexis_fixtures.rb
+++ b/spec/support/lexis_nexis_fixtures.rb
@@ -89,13 +89,23 @@ def phone_finder_request_json
JSON.parse(raw).to_json
end
- def phone_finder_success_response_json
- raw = read_fixture_file_at_path('phone_finder/response.json')
+ def phone_finder_rdp1_success_response_json
+ raw = read_fixture_file_at_path('phone_finder/rdp1_response.json')
JSON.parse(raw).to_json
end
- def phone_finder_fail_response_json
- raw = read_fixture_file_at_path('phone_finder/fail_response.json')
+ def phone_finder_rdp2_success_response_json
+ raw = read_fixture_file_at_path('phone_finder/rdp2_response.json')
+ JSON.parse(raw).to_json
+ end
+
+ def phone_finder_rdp1_fail_response_json
+ raw = read_fixture_file_at_path('phone_finder/rdp1_fail_response.json')
+ JSON.parse(raw).to_json
+ end
+
+ def phone_finder_rdp2_fail_response_json
+ raw = read_fixture_file_at_path('phone_finder/rdp2_fail_response.json')
JSON.parse(raw).to_json
end