diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index dba17ec41ac..0e36154b270 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -214,6 +214,29 @@ check_changelog: exit 0 fi +migrate: + stage: test + needs: + - job: install + cache: + - <<: *ruby_cache + variables: + DOCKER_DB_HOST: db-postgres + POSTGRES_DB: identity_idp_test + POSTGRES_USER: postgres_user + POSTGRES_PASSWORD: postgres_password + POSTGRES_HOST_AUTH_METHOD: trust + RAILS_ENV: test + services: + - name: postgres:13.9 + alias: db-postgres + command: ['--fsync=false', '--synchronous_commit=false', '--full_page_writes=false'] + script: + - *bundle_install + - bundle exec rake db:create db:migrate --trace + - git diff db/ + - make lint_database_schema_files + specs: stage: test needs: @@ -319,6 +342,8 @@ js_tests: - yarn test pinpoint-check: + needs: + - job: install stage: test cache: - <<: *ruby_cache @@ -328,6 +353,18 @@ pinpoint-check: - *yarn_install - make lint_country_dialing_codes +audit_packages: + needs: + - job: install + stage: test + cache: + - <<: *ruby_cache + - <<: *yarn_cache + script: + - *bundle_install + - *yarn_install + - make audit + prepare_deploy: # Runs in parallel with tests so we can deploy more quickly after passing stage: test @@ -354,6 +391,7 @@ coverage: - *bundle_install - bundle exec spec/simplecov_merger.rb - mv coverage/coverage/* coverage/ + coverage: '/LOC \(\d+.\d+\%\) covered/' artifacts: reports: coverage_report: @@ -413,7 +451,7 @@ trigger_devops: name: dtzar/helm-kubectl:latest script: - kubectl config get-contexts - - export CONTEXT=$(kubectl config get-contexts | grep review-apps | awk '{print $1}' | head -1) + - export CONTEXT=$(kubectl config get-contexts | grep reviewapp | awk '{print $1}' | head -1) - kubectl config use-context "$CONTEXT" - |- export IDP_CONFIG=$(cat <- helm upgrade --install --namespace review-apps --debug - --set env="reviewapps-$CI_ENVIRONMENT_SLUG" + --set env="reviewapp-$CI_ENVIRONMENT_SLUG" --set idp.image.repository="${ECR_REGISTRY}/identity-idp/review" --set idp.image.tag="${CI_COMMIT_SHA}" --set worker.image.repository="${ECR_REGISTRY}/identity-idp/review" @@ -533,19 +571,19 @@ trigger_devops: --set-json idp.config="$IDP_CONFIG" --set-json worker.config="$WORKER_CONFIG" --set-json pivcac.config="$PIVCAC_CONFIG" - --set-json idp.ingress.hosts="[{\"host\": \"$CI_ENVIRONMENT_SLUG.review-app.identitysandbox.gov\", \"paths\": [{\"path\": \"/\", \"pathType\": \"Prefix\"}]}]" - --set-json pivcac.ingress.hosts="[{\"host\": \"$CI_ENVIRONMENT_SLUG.review-app.pivcac.identitysandbox.gov\", \"paths\": [{\"path\": \"/\", \"pathType\": \"Prefix\"}]}]" - --set-json dashboard.ingress.hosts="[{\"host\": \"$CI_ENVIRONMENT_SLUG-review-app-dashboard.review-app.identitysandbox.gov\", \"paths\": [{\"path\": \"/\", \"pathType\": \"Prefix\"}]}]" + --set-json idp.ingress.hosts="[{\"host\": \"$CI_ENVIRONMENT_SLUG.reviewapp.identitysandbox.gov\", \"paths\": [{\"path\": \"/\", \"pathType\": \"Prefix\"}]}]" + --set-json pivcac.ingress.hosts="[{\"host\": \"$CI_ENVIRONMENT_SLUG.pivcac.reviewapp.identitysandbox.gov\", \"paths\": [{\"path\": \"/\", \"pathType\": \"Prefix\"}]}]" + --set-json dashboard.ingress.hosts="[{\"host\": \"$CI_ENVIRONMENT_SLUG-dashboard.reviewapp.identitysandbox.gov\", \"paths\": [{\"path\": \"/\", \"pathType\": \"Prefix\"}]}]" $CI_ENVIRONMENT_SLUG ./identity-idp-helm-chart - echo "DNS may take a while to propagate, so be patient if it doesn't show up right away" - - echo "To access the rails console, first run 'aws-vault exec sandbox-power -- aws eks update-kubeconfig --name reviewapps'" + - echo "To access the rails console, first run 'aws-vault exec sandbox-power -- aws eks update-kubeconfig --name reviewapp'" - echo "Then run aws-vault exec sandbox-power -- kubectl exec -it service/$CI_ENVIRONMENT_SLUG-login-chart-idp -n review-apps -- /app/bin/rails console" - echo "Address of IDP review app:" - - echo https://$CI_ENVIRONMENT_SLUG.review-app.identitysandbox.gov + - echo https://$CI_ENVIRONMENT_SLUG.reviewapp.identitysandbox.gov - echo "Address of PIVCAC review app:" - - echo https://$CI_ENVIRONMENT_SLUG.review-app.pivcac.identitysandbox.gov + - echo https://$CI_ENVIRONMENT_SLUG.pivcac.reviewapp.identitysandbox.gov - echo "Address of Dashboard review app:" - - echo https://$CI_ENVIRONMENT_SLUG-review-app-dashboard.review-app.identitysandbox.gov + - echo https://$CI_ENVIRONMENT_SLUG-dashboard.reviewapp.identitysandbox.gov review-app: @@ -553,11 +591,11 @@ review-app: allow_failure: true needs: - job: build-review-image - resource_group: $CI_ENVIRONMENT_SLUG.review-app.identitysandbox.gov + resource_group: $CI_ENVIRONMENT_SLUG.reviewapp.identitysandbox.gov extends: .deploy environment: name: review/$CI_COMMIT_REF_NAME - url: https://$CI_ENVIRONMENT_SLUG.review-app.identitysandbox.gov + url: https://$CI_ENVIRONMENT_SLUG.reviewapp.identitysandbox.gov on_stop: stop-review-app auto_stop_in: 2 days rules: @@ -566,9 +604,9 @@ review-app: when: never stop-review-app: - resource_group: $CI_ENVIRONMENT_SLUG.review-app.identitysandbox.gov + resource_group: $CI_ENVIRONMENT_SLUG.reviewapp.identitysandbox.gov script: - - export CONTEXT=$(kubectl config get-contexts | grep review-apps | awk '{print $1}' | head -1) + - export CONTEXT=$(kubectl config get-contexts | grep reviewapp | awk '{print $1}' | head -1) - kubectl config use-context "$CONTEXT" - helm uninstall --namespace review-apps $CI_ENVIRONMENT_SLUG stage: review @@ -590,12 +628,12 @@ deploy_production: allow_failure: true needs: - job: build-review-image - resource_group: $CI_ENVIRONMENT_SLUG.review-app.identitysandbox.gov + resource_group: $CI_ENVIRONMENT_SLUG.reviewapp.identitysandbox.gov extends: .deploy environment: name: production deployment_tier: production - url: https://$CI_ENVIRONMENT_SLUG.review-app.identitysandbox.gov + url: https://$CI_ENVIRONMENT_SLUG.reviewapp.identitysandbox.gov rules: - if: $CI_COMMIT_BRANCH == "main" && $CI_PIPELINE_SOURCE == "push" diff --git a/Gemfile b/Gemfile index 04965f0318a..a6ea2e90942 100644 --- a/Gemfile +++ b/Gemfile @@ -71,7 +71,7 @@ gem 'rqrcode' gem 'ruby-progressbar' gem 'ruby-saml' gem 'safe_target_blank', '>= 1.0.2' -gem 'saml_idp', github: '18F/saml_idp', tag: '0.21.2-18f' +gem 'saml_idp', github: '18F/saml_idp', tag: '0.21.4-18f' gem 'scrypt' gem 'simple_form', '>= 5.0.2' gem 'stringex', require: false diff --git a/Gemfile.lock b/Gemfile.lock index 5255d1ce4a8..0a7fa861485 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -35,10 +35,10 @@ GIT GIT remote: https://github.com/18F/saml_idp.git - revision: 5ad9e188efdfa6597697dd87f9cb9e8efa8d7d09 - tag: 0.21.2-18f + revision: 5e9999ef8e9260cda74cfea0a637f754994e0f9d + tag: 0.21.4-18f specs: - saml_idp (0.21.2.pre.18f) + saml_idp (0.21.4.pre.18f) activesupport builder faraday diff --git a/Makefile b/Makefile index dd275d8babb..a46dd25d3fd 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,7 @@ ARTIFACT_DESTINATION_FILE ?= ./tmp/idp.tar.gz .PHONY: \ analytics_events \ + audit \ brakeman \ build_artifact \ check \ @@ -25,6 +26,7 @@ ARTIFACT_DESTINATION_FILE ?= ./tmp/idp.tar.gz lint_analytics_events \ lint_analytics_events_sorted \ lint_country_dialing_codes \ + lint_database_schema_files \ lint_erb \ lint_font_glyphs \ lint_lockfiles \ @@ -74,11 +76,7 @@ endif make lint_analytics_events_sorted @echo "--- brakeman ---" make brakeman - @echo "--- bundler-audit ---" - bundle exec bundler-audit check --update # JavaScript - @echo "--- yarn audit ---" - yarn audit --groups dependencies; test $$? -le 7 @echo "--- eslint ---" yarn run lint @echo "--- typescript ---" @@ -105,6 +103,12 @@ endif @echo "--- lint migrations ---" make lint_migrations +audit: ## Checks packages for vulnerabilities + @echo "--- bundler-audit ---" + bundle exec bundler-audit check --update + @echo "--- yarn audit ---" + yarn audit --groups dependencies; test $$? -le 7 + lint_erb: ## Lints ERB files bundle exec erblint app/views app/components @@ -253,6 +257,10 @@ update_pinpoint_supported_countries: ## Updates list of countries supported by P lint_country_dialing_codes: update_pinpoint_supported_countries ## Checks that countries supported by Pinpoint for voice and SMS are up to date (! git diff --name-only | grep config/country_dialing_codes.yml) || (echo "Error: Run 'make update_pinpoint_supported_countries' to update country codes"; exit 1) +lint_database_schema_files: ## Checks that database schema files have not changed + (! git diff --name-only | grep db/schema.rb) || (echo "Error: db/schema.rb does not match after running migrations"; exit 1) + (! git diff --name-only | grep db/worker_jobs_schema.rb) || (echo "Error: db/worker_jobs_schema.rb does not match after running migrations"; exit 1) + build_artifact $(ARTIFACT_DESTINATION_FILE): ## Builds zipped tar file artifact with IDP source code and Ruby/JS dependencies @echo "Building artifact into $(ARTIFACT_DESTINATION_FILE)" bundle config set --local cache_all true diff --git a/app/controllers/concerns/idv/verify_info_concern.rb b/app/controllers/concerns/idv/verify_info_concern.rb index 9cf3f52c3e4..7cd09f84114 100644 --- a/app/controllers/concerns/idv/verify_info_concern.rb +++ b/app/controllers/concerns/idv/verify_info_concern.rb @@ -24,13 +24,7 @@ def shared_update idv_session.verify_info_step_document_capture_session_uuid = document_capture_session.uuid - # proof_resolution job expects these values - agent_pii = pii.merge( - uuid: current_user.uuid, - uuid_prefix: ServiceProvider.find_by(issuer: sp_session[:issuer])&.app_id, - ssn: idv_session.ssn, - ) - Idv::Agent.new(agent_pii).proof_resolution( + Idv::Agent.new(pii).proof_resolution( document_capture_session, should_proof_state_id: aamva_state?, trace_id: amzn_trace_id, @@ -164,8 +158,6 @@ def process_async_state(current_async_state) end def async_state_done(current_async_state) - add_proofing_costs(current_async_state.result) - create_fraud_review_request_if_needed(current_async_state.result) form_response = idv_result_to_form_response( @@ -295,34 +287,6 @@ def move_applicant_to_idv_session idv_session.applicant['uuid'] = current_user.uuid end - def add_proofing_costs(results) - return if results[:context][:sp_costs_added] - - results[:context][:stages].each do |stage, hash| - if stage == :resolution - # transaction_id comes from ConversationId - add_cost(:lexis_nexis_resolution, transaction_id: hash[:transaction_id]) - elsif stage == :residential_address - next if pii[:same_address_as_id] == 'true' - next if hash[:vendor_name] == 'ResidentialAddressNotRequired' - add_cost(:lexis_nexis_resolution, transaction_id: hash[:transaction_id]) - elsif stage == :state_id - next if hash[:exception].present? - next if hash[:vendor_name] == 'UnsupportedJurisdiction' - # transaction_id comes from TransactionLocatorId - add_cost(:aamva, transaction_id: hash[:transaction_id]) - elsif stage == :threatmetrix - # transaction_id comes from request_id - if hash[:transaction_id] - add_cost( - :threatmetrix, - transaction_id: hash[:transaction_id], - ) - end - end - end - end - def add_cost(token, transaction_id: nil) Db::SpCost::AddSpCost.call(current_sp, token, transaction_id: transaction_id) end diff --git a/app/controllers/idv/address_controller.rb b/app/controllers/idv/address_controller.rb index 4b13fc9ea2d..b8b1428fa03 100644 --- a/app/controllers/idv/address_controller.rb +++ b/app/controllers/idv/address_controller.rb @@ -20,7 +20,6 @@ def update @address_form = build_address_form form_result = @address_form.submit(profile_params) track_submit_event(form_result) - capture_address_edited(form_result) if form_result.success? success else @@ -69,8 +68,11 @@ def failure end def track_submit_event(form_result) - address_edited = form_result.success? && address_edited? - analytics.idv_address_submitted(**form_result.to_h.merge(address_edited:)) + analytics.idv_address_submitted( + **form_result.to_h.merge( + address_edited: address_edited?, + ), + ) end def address_edited? @@ -80,10 +82,5 @@ def address_edited? def profile_params params.require(:idv_form).permit(Idv::AddressForm::ATTRIBUTES) end - - def capture_address_edited(result) - address_edited = result.to_h[:address_edited] - idv_session.address_edited = true if address_edited - end end end diff --git a/app/controllers/idv/in_person/verify_info_controller.rb b/app/controllers/idv/in_person/verify_info_controller.rb index 3cc2d49ec90..50e06f35e39 100644 --- a/app/controllers/idv/in_person/verify_info_controller.rb +++ b/app/controllers/idv/in_person/verify_info_controller.rb @@ -74,7 +74,7 @@ def prev_url end def pii - user_session.dig('idv/in_person', :pii_from_user) + user_session.dig('idv/in_person', :pii_from_user).merge(ssn: idv_session.ssn) end # override IdvSessionConcern diff --git a/app/controllers/idv/verify_info_controller.rb b/app/controllers/idv/verify_info_controller.rb index b9990d401d5..10b2221c345 100644 --- a/app/controllers/idv/verify_info_controller.rb +++ b/app/controllers/idv/verify_info_controller.rb @@ -79,7 +79,8 @@ def analytics_arguments def pii idv_session.pii_from_doc.to_h.merge( - idv_session.updated_user_address.to_h, + ssn: idv_session.ssn, + **idv_session.updated_user_address.to_h, ).with_indifferent_access end end diff --git a/app/controllers/saml_idp_controller.rb b/app/controllers/saml_idp_controller.rb index e5a7d7e1209..432fdc8e5f9 100644 --- a/app/controllers/saml_idp_controller.rb +++ b/app/controllers/saml_idp_controller.rb @@ -137,6 +137,7 @@ def capture_analytics # Logging to indicate if a validation bug fix will create a potentially breaking change analytics_payload[:encryption_cert_matches_matching_cert] = encryption_cert_matches_matching_cert? + analytics_payload[:cert_error_details] = saml_request.cert_errors end analytics.saml_auth(**analytics_payload) diff --git a/app/controllers/test/oidc_test_controller.rb b/app/controllers/test/oidc_test_controller.rb index dd366676911..246d7321bb1 100644 --- a/app/controllers/test/oidc_test_controller.rb +++ b/app/controllers/test/oidc_test_controller.rb @@ -8,7 +8,7 @@ class OidcTestController < ApplicationController BIOMETRIC_REQUIRED = 'biometric-comparison-required' def initialize - @client_id = 'urn:gov:gsa:openidconnect:sp:test' + @client_id = 'urn:gov:gsa:openidconnect:sp:sinatra' super end diff --git a/app/forms/idv/address_form.rb b/app/forms/idv/address_form.rb index f0fc685c993..db960b5571d 100644 --- a/app/forms/idv/address_form.rb +++ b/app/forms/idv/address_form.rb @@ -30,6 +30,8 @@ def submit(params) end def updated_user_address + return nil unless valid? + Pii::Address.new( address1: address1, address2: address2, diff --git a/app/javascript/packages/document-capture/components/acuant-capture-canvas.jsx b/app/javascript/packages/document-capture/components/acuant-capture-canvas.jsx index f21eb626abb..78b560e8d92 100644 --- a/app/javascript/packages/document-capture/components/acuant-capture-canvas.jsx +++ b/app/javascript/packages/document-capture/components/acuant-capture-canvas.jsx @@ -3,28 +3,10 @@ import { useContext, useEffect, useRef } from 'react'; import { getAssetPath } from '@18f/identity-assets'; import { useI18n } from '@18f/identity-react-i18n'; import AcuantContext from '../context/acuant'; - -/** - * Defines a property on the given object, calling the change callback when that property is set to - * a new value. - * - * @param {any} object Object on which to define property. - * @param {string} property Property name to observe. - * @param {(nextValue: any) => void} onChangeCallback Callback to trigger on change. - */ -export function defineObservableProperty(object, property, onChangeCallback) { - let currentValue; - - Object.defineProperty(object, property, { - get() { - return currentValue; - }, - set(nextValue) { - currentValue = nextValue; - onChangeCallback(nextValue); - }, - }); -} +import { + defineObservableProperty, + stopObservingProperty, +} from '../higher-order/observable-property'; function AcuantCaptureCanvas() { const { isReady, acuantCaptureMode, setAcuantCaptureMode } = useContext(AcuantContext); @@ -43,6 +25,11 @@ function AcuantCaptureCanvas() { cameraRef.current?.addEventListener('acuantcameracreated', onAcuantCameraCreated); return () => { + const canvas = document.getElementById('acuant-ui-canvas'); + if (canvas) { + stopObservingProperty(canvas, 'callback'); + } + cameraRef.current?.removeEventListener('acuantcameracreated', onAcuantCameraCreated); }; }, []); diff --git a/app/javascript/packages/document-capture/higher-order/observable-property.tsx b/app/javascript/packages/document-capture/higher-order/observable-property.tsx new file mode 100644 index 00000000000..c6ed08b349f --- /dev/null +++ b/app/javascript/packages/document-capture/higher-order/observable-property.tsx @@ -0,0 +1,39 @@ +/** + * Defines a property on the given object, calling the change callback when that property is set to + * a new value. + * + * @param object Object on which to define property. + * @param property Property name to observe. + * @param onChangeCallback Callback to trigger on change. + */ +export function defineObservableProperty( + object: any, + property: string, + onChangeCallback: (nextValue: any) => void, +) { + let currentValue: any; + + Object.defineProperty(object, property, { + get() { + return currentValue; + }, + set(nextValue) { + currentValue = nextValue; + onChangeCallback(nextValue); + }, + configurable: true, + }); +} + +/** + * Removes an observable property by removing the defined getter/setter methods + * and replaces the value with the most recent value. + * + * @param object Object on which to remove defined property. + * @param property Property name to remove observer for + */ +export function stopObservingProperty(object: any, property: string) { + const currentValue = object[property]; + + Object.defineProperty(object, property, { value: currentValue, writable: true }); +} diff --git a/app/jobs/reports/combined_invoice_supplement_report_v2.rb b/app/jobs/reports/combined_invoice_supplement_report_v2.rb index cf2aafe3294..0259180d9f2 100644 --- a/app/jobs/reports/combined_invoice_supplement_report_v2.rb +++ b/app/jobs/reports/combined_invoice_supplement_report_v2.rb @@ -97,20 +97,35 @@ def combine_by_iaa_month( 'iaa_ial1_unique_users', 'iaa_ial2_unique_users', 'iaa_ial1_plus_2_unique_users', - 'partner_ial2_unique_users_year1', - 'partner_ial2_unique_users_year2', - 'partner_ial2_unique_users_year3', - 'partner_ial2_unique_users_year4', - 'partner_ial2_unique_users_year5', - 'partner_ial2_unique_users_year_greater_than_5', - 'partner_ial2_unique_users_unknown', - 'partner_ial2_new_unique_users_year1', - 'partner_ial2_new_unique_users_year2', - 'partner_ial2_new_unique_users_year3', - 'partner_ial2_new_unique_users_year4', - 'partner_ial2_new_unique_users_year5', - 'partner_ial2_new_unique_users_year_greater_than_5', - 'partner_ial2_new_unique_users_unknown', + 'partner_ial2_unique_user_events_year1', + 'partner_ial2_unique_user_events_year2', + 'partner_ial2_unique_user_events_year3', + 'partner_ial2_unique_user_events_year4', + 'partner_ial2_unique_user_events_year5', + 'partner_ial2_unique_user_events_year_greater_than_5', + 'partner_ial2_unique_user_events_unknown', + 'partner_ial2_new_unique_user_events_year1', + 'partner_ial2_new_unique_user_events_year2', + 'partner_ial2_new_unique_user_events_year3', + 'partner_ial2_new_unique_user_events_year4', + 'partner_ial2_new_unique_user_events_year5', + 'partner_ial2_new_unique_user_events_year_greater_than_5', + 'partner_ial2_new_unique_user_events_unknown', + + 'issuer_ial2_unique_user_events_year1', + 'issuer_ial2_unique_user_events_year2', + 'issuer_ial2_unique_user_events_year3', + 'issuer_ial2_unique_user_events_year4', + 'issuer_ial2_unique_user_events_year5', + 'issuer_ial2_unique_user_events_year_greater_than_5', + 'issuer_ial2_unique_user_events_unknown', + 'issuer_ial2_new_unique_user_events_year1', + 'issuer_ial2_new_unique_user_events_year2', + 'issuer_ial2_new_unique_user_events_year3', + 'issuer_ial2_new_unique_user_events_year4', + 'issuer_ial2_new_unique_user_events_year5', + 'issuer_ial2_new_unique_user_events_year_greater_than_5', + 'issuer_ial2_new_unique_user_events_unknown', 'issuer_ial1_total_auth_count', 'issuer_ial2_total_auth_count', @@ -119,20 +134,6 @@ def combine_by_iaa_month( 'issuer_ial1_unique_users', 'issuer_ial2_unique_users', 'issuer_ial1_plus_2_unique_users', - 'issuer_ial2_unique_users_year1', - 'issuer_ial2_unique_users_year2', - 'issuer_ial2_unique_users_year3', - 'issuer_ial2_unique_users_year4', - 'issuer_ial2_unique_users_year5', - 'issuer_ial2_unique_users_year_greater_than_5', - 'issuer_ial2_unique_users_unknown', - 'issuer_ial2_new_unique_users_year1', - 'issuer_ial2_new_unique_users_year2', - 'issuer_ial2_new_unique_users_year3', - 'issuer_ial2_new_unique_users_year4', - 'issuer_ial2_new_unique_users_year5', - 'issuer_ial2_new_unique_users_year_greater_than_5', - 'issuer_ial2_new_unique_users_unknown', ] by_issuer_iaa_issuer_year_months.each do |iaa_key, issuer_year_months| issuer_year_months.each do |issuer, year_months_data| @@ -169,20 +170,35 @@ def combine_by_iaa_month( (iaa_ial1_unique_users = extract(iaa_results, :unique_users, ial: 1)), (iaa_ial2_unique_users = extract(iaa_results, :unique_users, ial: 2)), iaa_ial1_unique_users + iaa_ial2_unique_users, - partner_results[:partner_ial2_unique_users_year1] || 0, - partner_results[:partner_ial2_unique_users_year2] || 0, - partner_results[:partner_ial2_unique_users_year3] || 0, - partner_results[:partner_ial2_unique_users_year4] || 0, - partner_results[:partner_ial2_unique_users_year5] || 0, - partner_results[:partner_ial2_unique_users_year_greater_than_5] || 0, - partner_results[:partner_ial2_unique_users_unknown] || 0, - partner_results[:partner_ial2_new_unique_users_year1] || 0, - partner_results[:partner_ial2_new_unique_users_year2] || 0, - partner_results[:partner_ial2_new_unique_users_year3] || 0, - partner_results[:partner_ial2_new_unique_users_year4] || 0, - partner_results[:partner_ial2_new_unique_users_year5] || 0, - partner_results[:partner_ial2_new_unique_users_year_greater_than_5] || 0, - partner_results[:partner_ial2_new_unique_users_unknown] || 0, + partner_results[:partner_ial2_unique_user_events_year1] || 0, + partner_results[:partner_ial2_unique_user_events_year2] || 0, + partner_results[:partner_ial2_unique_user_events_year3] || 0, + partner_results[:partner_ial2_unique_user_events_year4] || 0, + partner_results[:partner_ial2_unique_user_events_year5] || 0, + partner_results[:partner_ial2_unique_user_events_year_greater_than_5] || 0, + partner_results[:partner_ial2_unique_user_events_unknown] || 0, + partner_results[:partner_ial2_new_unique_user_events_year1] || 0, + partner_results[:partner_ial2_new_unique_user_events_year2] || 0, + partner_results[:partner_ial2_new_unique_user_events_year3] || 0, + partner_results[:partner_ial2_new_unique_user_events_year4] || 0, + partner_results[:partner_ial2_new_unique_user_events_year5] || 0, + partner_results[:partner_ial2_new_unique_user_events_year_greater_than_5] || 0, + partner_results[:partner_ial2_new_unique_user_events_unknown] || 0, + + issuer_profile_age_results[:partner_ial2_unique_user_events_year1] || 0, + issuer_profile_age_results[:partner_ial2_unique_user_events_year2] || 0, + issuer_profile_age_results[:partner_ial2_unique_user_events_year3] || 0, + issuer_profile_age_results[:partner_ial2_unique_user_events_year4] || 0, + issuer_profile_age_results[:partner_ial2_unique_user_events_year5] || 0, + issuer_profile_age_results[:partner_ial2_unique_user_events_year_greater_than_5] || 0, # rubocop:disable Layout/LineLength + issuer_profile_age_results[:partner_ial2_unique_user_events_unknown] || 0, + issuer_profile_age_results[:partner_ial2_new_unique_user_events_year1] || 0, + issuer_profile_age_results[:partner_ial2_new_unique_user_events_year2] || 0, + issuer_profile_age_results[:partner_ial2_new_unique_user_events_year3] || 0, + issuer_profile_age_results[:partner_ial2_new_unique_user_events_year4] || 0, + issuer_profile_age_results[:partner_ial2_new_unique_user_events_year5] || 0, + issuer_profile_age_results[:partner_ial2_new_unique_user_events_year_greater_than_5] || 0, # rubocop:disable Layout/LineLength + issuer_profile_age_results[:partner_ial2_new_unique_user_events_unknown] || 0, (ial1_total_auth_count = extract(issuer_results, :total_auth_count, ial: 1)), (ial2_total_auth_count = extract(issuer_results, :total_auth_count, ial: 2)), @@ -191,20 +207,6 @@ def combine_by_iaa_month( (issuer_ial1_unique_users = extract(issuer_results, :unique_users, ial: 1)), (issuer_ial2_unique_users = extract(issuer_results, :unique_users, ial: 2)), issuer_ial1_unique_users + issuer_ial2_unique_users, - issuer_profile_age_results[:partner_ial2_unique_users_year1] || 0, - issuer_profile_age_results[:partner_ial2_unique_users_year2] || 0, - issuer_profile_age_results[:partner_ial2_unique_users_year3] || 0, - issuer_profile_age_results[:partner_ial2_unique_users_year4] || 0, - issuer_profile_age_results[:partner_ial2_unique_users_year5] || 0, - issuer_profile_age_results[:partner_ial2_unique_users_year_greater_than_5] || 0, - issuer_profile_age_results[:partner_ial2_unique_users_unknown] || 0, - issuer_profile_age_results[:partner_ial2_new_unique_users_year1] || 0, - issuer_profile_age_results[:partner_ial2_new_unique_users_year2] || 0, - issuer_profile_age_results[:partner_ial2_new_unique_users_year3] || 0, - issuer_profile_age_results[:partner_ial2_new_unique_users_year4] || 0, - issuer_profile_age_results[:partner_ial2_new_unique_users_year5] || 0, - issuer_profile_age_results[:partner_ial2_new_unique_users_year_greater_than_5] || 0, - issuer_profile_age_results[:partner_ial2_new_unique_users_unknown] || 0, ] end end diff --git a/app/services/analytics_events.rb b/app/services/analytics_events.rb index 476848e51aa..aa1aa55db71 100644 --- a/app/services/analytics_events.rb +++ b/app/services/analytics_events.rb @@ -4979,6 +4979,8 @@ def rules_of_use_visit # @param [String] matching_cert_serial # @param [Boolean|nil] encryption_cert_matches_matching_cert If the encryption certificate # matches the request certificate in a successful, signed request + # @param [Hash] cert_error_details Details for errors that occurred because of an invalid + # signature def saml_auth( success:, errors:, @@ -4995,6 +4997,7 @@ def saml_auth( matching_cert_serial:, encryption_cert_matches_matching_cert: nil, error_details: nil, + cert_error_details: nil, **extra ) track_event( @@ -5014,6 +5017,7 @@ def saml_auth( request_signed:, matching_cert_serial:, encryption_cert_matches_matching_cert:, + cert_error_details:, **extra, ) end diff --git a/app/services/db/monthly_sp_auth_count/new_unique_monthly_user_counts_by_partner.rb b/app/services/db/monthly_sp_auth_count/new_unique_monthly_user_counts_by_partner.rb index ff52df4073e..1419dd15f84 100644 --- a/app/services/db/monthly_sp_auth_count/new_unique_monthly_user_counts_by_partner.rb +++ b/app/services/db/monthly_sp_auth_count/new_unique_monthly_user_counts_by_partner.rb @@ -83,21 +83,21 @@ def call(partner:, issuers:, start_date:, end_date:) iaa_start_date: date_range.begin.to_s, iaa_end_date: date_range.end.to_s, unique_user_proofed_events: this_month_user_proofed_events.count, - partner_ial2_unique_users_year1: unique_profiles_by_age[0].count, - partner_ial2_unique_users_year2: unique_profiles_by_age[1].count, - partner_ial2_unique_users_year3: unique_profiles_by_age[2].count, - partner_ial2_unique_users_year4: unique_profiles_by_age[3].count, - partner_ial2_unique_users_year5: unique_profiles_by_age[4].count, - partner_ial2_unique_users_year_greater_than_5: unique_profiles_by_age[:older].count, - partner_ial2_unique_users_unknown: unique_profiles_by_age[:unknown].count, + partner_ial2_unique_user_events_year1: unique_profiles_by_age[0].count, + partner_ial2_unique_user_events_year2: unique_profiles_by_age[1].count, + partner_ial2_unique_user_events_year3: unique_profiles_by_age[2].count, + partner_ial2_unique_user_events_year4: unique_profiles_by_age[3].count, + partner_ial2_unique_user_events_year5: unique_profiles_by_age[4].count, + partner_ial2_unique_user_events_year_greater_than_5: unique_profiles_by_age[:older].count, # rubocop:disable Layout/LineLength + partner_ial2_unique_user_events_unknown: unique_profiles_by_age[:unknown].count, new_unique_user_proofed_events: new_unique_user_proofed_events.count, - partner_ial2_new_unique_users_year1: new_unique_profiles_by_age[0].count, - partner_ial2_new_unique_users_year2: new_unique_profiles_by_age[1].count, - partner_ial2_new_unique_users_year3: new_unique_profiles_by_age[2].count, - partner_ial2_new_unique_users_year4: new_unique_profiles_by_age[3].count, - partner_ial2_new_unique_users_year5: new_unique_profiles_by_age[4].count, - partner_ial2_new_unique_users_year_greater_than_5: new_unique_profiles_by_age[:older].count, # rubocop:disable Layout/LineLength - partner_ial2_new_unique_users_unknown: new_unique_profiles_by_age[:unknown].count, + partner_ial2_new_unique_user_events_year1: new_unique_profiles_by_age[0].count, + partner_ial2_new_unique_user_events_year2: new_unique_profiles_by_age[1].count, + partner_ial2_new_unique_user_events_year3: new_unique_profiles_by_age[2].count, + partner_ial2_new_unique_user_events_year4: new_unique_profiles_by_age[3].count, + partner_ial2_new_unique_user_events_year5: new_unique_profiles_by_age[4].count, + partner_ial2_new_unique_user_events_year_greater_than_5: new_unique_profiles_by_age[:older].count, # rubocop:disable Layout/LineLength + partner_ial2_new_unique_user_events_unknown: new_unique_profiles_by_age[:unknown].count, } end # rubocop:enable Metrics/BlockLength diff --git a/config/locales/en.yml b/config/locales/en.yml index ff5c1e9270d..3c931ad39a4 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -44,9 +44,9 @@ account.email_language.default: '%{language} (default)' account.email_language.edit_title: Edit email language preference account.email_language.languages_list: '%{app_name} allows you to receive your email communication in %{list}.' account.email_language.name.en: English -account.email_language.name.es: Spanish -account.email_language.name.fr: French -account.email_language.name.zh: Chinese +account.email_language.name.es: Español +account.email_language.name.fr: Français +account.email_language.name.zh: 中文 (简体) account.email_language.sentence_connector: or account.email_language.updated: Your email language preference has been updated. account.forget_all_browsers.longer_description: Once you choose to ‘forget all browsers,’ we’ll need additional information to know that it’s actually you signing in to your account. We’ll ask for a multi-factor authentication method (such as text/SMS code or a security key) each time you want to access your account. diff --git a/config/locales/es.yml b/config/locales/es.yml index 07d343233ef..e32ab74ce44 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -43,10 +43,10 @@ account.connected_apps.description: Con su cuenta de %{app_name}, puede conectar account.email_language.default: '%{language} (predeterminado)' account.email_language.edit_title: Editar la preferencia de idioma del correo electrónico account.email_language.languages_list: '%{app_name} le permite recibir su comunicación por correo electrónico en %{list}.' -account.email_language.name.en: Inglés +account.email_language.name.en: English account.email_language.name.es: Español -account.email_language.name.fr: Francés -account.email_language.name.zh: Chinese +account.email_language.name.fr: Français +account.email_language.name.zh: 中文 (简体) account.email_language.sentence_connector: o account.email_language.updated: Se actualizó su preferencia de idioma del correo electrónico. account.forget_all_browsers.longer_description: Una vez que elija “Olvidar todos los navegadores”, necesitaremos más información para saber que realmente es usted quien está iniciando sesión en su cuenta. Le pediremos un método de autenticación multifactor (como código de texto o de SMS, o una clave de seguridad) cada vez que desee acceder a su cuenta. diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 95cf95f17cd..a70d855ed5f 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -43,10 +43,10 @@ account.connected_apps.description: Avec votre compte %{app_name}, vous pouvez v account.email_language.default: '%{language} (par défaut)' account.email_language.edit_title: Modifier la langue dans laquelle vous préférez recevoir les e-mails account.email_language.languages_list: '%{app_name} vous permet de recevoir des communications par e-mail en %{list}.' -account.email_language.name.en: anglais -account.email_language.name.es: espagnol -account.email_language.name.fr: français -account.email_language.name.zh: Chinese +account.email_language.name.en: English +account.email_language.name.es: Español +account.email_language.name.fr: Français +account.email_language.name.zh: 中文 (简体) account.email_language.sentence_connector: ou account.email_language.updated: Votre langue de préférence pour les e-mails a été mise à jour. account.forget_all_browsers.longer_description: Une fois que vous aurez choisi d’« oublier tous les navigateurs », nous aurons besoin d’informations supplémentaires pour savoir que c’est bien vous qui vous connectez à votre compte. Nous vous demanderons une méthode d’authentification multi-facteurs (comme un code SMS/texto ou une clé de sécurité) chaque fois que vous souhaiterez accéder à votre compte. diff --git a/config/locales/telephony/zh.yml b/config/locales/telephony/zh.yml index 8abcf9d9ae5..214eb886899 100644 --- a/config/locales/telephony/zh.yml +++ b/config/locales/telephony/zh.yml @@ -1,9 +1,9 @@ --- zh: telephony: - account_deleted_notice: This text message confirms you have deleted your %{app_name} account. + account_deleted_notice: 这条信息确认你已删除了你的 %{app_name} 账户。 account_reset_cancellation_notice: 删除你 %{app_name} 账户的请求已被取消。 - account_reset_notice: 按照你的请求,你的 %{app_name} 账户会在 24 小时后删除。不想删除你的账户?登入你的 %{app_name} 账户去取消。 + account_reset_notice: 按照你的请求,你的 %{app_name} 账户会在%{interval}后删除。不想删除你的账户?登入你的 %{app_name} 账户去取消。 authentication_otp: sms: |- %{app_name}: 你的一次性代码是 %{code}。此代码在 %{expiration} 分钟后作废。请勿与任何人分享此代码。 @@ -18,7 +18,7 @@ zh: %{app_name}: 你的一次性代码是 %{code}。此代码在 %{expiration} 分钟后作废。请勿与任何人分享此代码。 @%{domain} #%{code} - voice: 你好! 你的%{format_length}%{format_type}一次性代码是 %{code}。你的一次性代码是 %{code}。 重复一下,你的一次性代码是 %{code}。此代码 %{expiration} 分钟后会作废。 + voice: 你好!你的%{format_length}-%{format_type} %{app_name}一次性代码是,%{code}。你的一次性代码是 ,%{code}。重复一下,你的一次性代码是 %{code}。此代码 %{expiration} 分钟后会作废。 doc_auth_link: |- %{app_name}: %{link} 你在验证身份以访问 %{sp_or_app_name}。拍张你身份证件的照片以继续。 error: diff --git a/config/locales/zh.yml b/config/locales/zh.yml index 65b24b52706..2e4f95e01fa 100644 --- a/config/locales/zh.yml +++ b/config/locales/zh.yml @@ -16,11 +16,11 @@ account_reset.delete_account.info: 被锁在账户之外时,取消账户应当 account_reset.delete_account.title: 删除账户应当是你最后的选择。 account_reset.pending.cancel_request: 取消请求 account_reset.pending.canceled: 我们已取消了你删除帐户的请求。 -account_reset.pending.confirm: 如果现在取消,你如果要删除账户的话,必须提出新请求并再等待24个小时。 +account_reset.pending.confirm: 如果现在取消,你如果要删除账户的话,必须提出新请求并再等待%{interval} 。 account_reset.pending.header: 你已提出删除账户的请求。 -account_reset.pending.wait_html: 要删除账户,有 24 小时的等待期。你会在 %{interval} 收到电邮,向你说明如何完成删除。 +account_reset.pending.wait_html: 要删除你的账户,有一个%{waiting_period}的等待期。你会在 %{interval} 收到电邮,向你说明如何完成删除。 account_reset.recovery_options.check_saved_credential: 查看你是否有已保存的凭据 -account_reset.recovery_options.check_webauthn_platform_info: 如果你在设立账户时设置了人脸或触摸解锁,务必使用你设立 %{app_name} 账户时使用的同一设备。 +account_reset.recovery_options.check_webauthn_platform_info: 如果你设立了人脸或触摸解锁,凭据可能已存在了一个密码管理器中,比如 iCloud Keychain 或者 Google Password Manager。尝试在用那个密码管理器的浏览器上使用人脸或触摸解锁。 account_reset.recovery_options.header: 你确定要删除账户吗? account_reset.recovery_options.help_text: 如果你被锁定在账户外而且仍然需要访问 %{app_name} ,请尝试以下步骤。 account_reset.recovery_options.try_another_device: 使用你也许选择了“记住设备”选项的另一个设备。 @@ -43,9 +43,9 @@ account.connected_apps.description: 使用你的 %{app_name} 账户,你可以 account.email_language.default: '%{language} (默认)' account.email_language.edit_title: 编辑电邮语言选择 account.email_language.languages_list: '%{app_name} 允许你以 %{list}接受电邮沟通。' -account.email_language.name.en: 英文 -account.email_language.name.es: 西班牙文 -account.email_language.name.fr: 法文 +account.email_language.name.en: English +account.email_language.name.es: Español +account.email_language.name.fr: Français account.email_language.name.zh: 中文 (简体) account.email_language.sentence_connector: 或者 account.email_language.updated: 你的电邮语言选择已更新。 @@ -594,7 +594,7 @@ doc_auth.info.capture_status_capturing: 扫描中 doc_auth.info.capture_status_none: 对齐 doc_auth.info.capture_status_small_document: 靠近一些 doc_auth.info.capture_status_tap_to_capture: 点击来扫描 -doc_auth.info.exit.with_sp: 退出 %{app_name} 并返回 %{app_name} +doc_auth.info.exit.with_sp: 退出 %{app_name} 并返回 %{sp_name} doc_auth.info.exit.without_sp: 退出身份验证并到你的账户页面 doc_auth.info.getting_started_html: '%{sp_name} 需要确保你是你,而不是别人冒充你。 了解更多有关验证你身份的信息 %{link_html}' doc_auth.info.getting_started_learn_more: 了解有关验证你身份的更多信息 @@ -709,7 +709,7 @@ errors.manage_authenticator.unique_name_error: 名字已在使用。请使用一 errors.max_password_attempts_reached: 你输入了太多不正确的密码。你可以使用“忘了密码?”链接来重设密码。 errors.messages.already_confirmed: 已确认,请尝试登录 errors.messages.blank: 请填写这一字段。 -errors.messages.blank_cert_element_req: We cannot detect a certificate in your request. +errors.messages.blank_cert_element_req: 我们在你的请求中探查不到证书。 errors.messages.confirmation_code_incorrect: 验证码不对。你打字打对了吗? errors.messages.confirmation_invalid_token: 确认链接有误。链接已过期,或者你已确认了你的账户。 errors.messages.confirmation_period_expired: 过期的确认链接。你可以点击“重新发送确认说明”来得到另一个。 @@ -996,7 +996,7 @@ idv.cancel.description.gpo.warnings: idv.cancel.description.hybrid: 如果你现在取消的话,会被提示切换回电脑继续验证你的身份。 idv.cancel.description.start_over: 如果你重新开始,就会从头重新开始这一流程。 idv.cancel.headings.confirmation.hybrid: 你已取消了在该手机上上传身份证件照片 -idv.cancel.headings.exit.with_sp: 退出 %{app_name} 并返回 %{app_name} +idv.cancel.headings.exit.with_sp: 退出 %{app_name} 并返回 %{sp_name} idv.cancel.headings.exit.without_sp: 退出身份验证并到你的账户页面 idv.cancel.headings.prompt.hybrid: 你确定要取消在该手机上上传身份证件照片吗? idv.cancel.headings.prompt.standard: 取消验证身份? @@ -1019,7 +1019,7 @@ idv.failure.exceptions.internal_error: 处理你的请求时内部出错。请 idv.failure.exceptions.link: 请联系我们。 idv.failure.exceptions.post_office_search_error: 我们这边目前遇到技术困难。请再次尝试搜索一个邮局。如果该问题继续存在,请稍后回来。 idv.failure.exceptions.text_html: 请再试一次。如果这些错误一直出现,%{link_html}。 -idv.failure.exit.with_sp: 退出 %{app_name} 并返回 %{app_name} +idv.failure.exit.with_sp: 退出 %{app_name} 并返回 %{sp_name} idv.failure.exit.without_sp: 退出身份验证并到你的账户页面 idv.failure.gpo.rate_limited.heading: 请稍后再试 idv.failure.phone.heading: 我们无法将该电话号码与其他记录相匹配 @@ -1442,7 +1442,7 @@ openid_connect.authorization.errors.missing_ial: 缺失有效的 IAL 级别 openid_connect.authorization.errors.no_auth: Acr_values 未经授权 openid_connect.authorization.errors.no_valid_acr_values: 未找到可接受的 acr_values openid_connect.authorization.errors.no_valid_scope: 未找到有效的范围值 -openid_connect.authorization.errors.no_valid_vtr: No acceptable vots found +openid_connect.authorization.errors.no_valid_vtr: 未找到可接受的vots openid_connect.authorization.errors.prompt_invalid: 未找到有效的提示值 openid_connect.authorization.errors.redirect_uri_invalid: redirect_uri 有误 openid_connect.authorization.errors.redirect_uri_no_match: redirect_uri 与注册的 redirect_uri 不匹配 @@ -1606,7 +1606,7 @@ two_factor_authentication.aal2_request.phishing_resistant_html: '%{sp_na two_factor_authentication.aal2_request.piv_cac_only_html: '%{sp_name} 要求你的政府雇员身份证件,这是一种高安全水平的身份证实方法。' two_factor_authentication.account_reset.cancel_link: 取消你的请求 two_factor_authentication.account_reset.link: 删除你的账户 -two_factor_authentication.account_reset.pending: 你目前有个待处理的删除账户请求。从你提出请求到完成该流程需要 24 个小时请稍后回来查看。 +two_factor_authentication.account_reset.pending: 你目前有个待处理的删除账户请求。从你提出请求到完成该流程需要 %{interval}。请随后回来查看。 two_factor_authentication.account_reset.successful_cancel: 谢谢。你删除自己 %{app_name} 账户的请求已被取消。 two_factor_authentication.account_reset.text_html: 如果你无法使用上述身份证实方法,可以通过 %{link_html} 重设你的首选。 two_factor_authentication.attempt_remaining_warning_html.one: 你还可以再试 %{count} 次 。 @@ -1774,11 +1774,11 @@ user_mailer.account_reset_complete.subject: 账户已删除 user_mailer.account_reset_granted.button: 是的,继续删除 user_mailer.account_reset_granted.cancel_link_text: 请取消 user_mailer.account_reset_granted.help_html: 如果你不想删除你的账户,%{cancel_account_reset_html}。 -user_mailer.account_reset_granted.intro_html: 你的24小时等待期已结束。请完成流程第 2 步。

如果你无法找到自己的身份证实方法,选择“确认删除”来删除你的 %{app_name} 账户。

账户删除后,将来如果你需要访问使用 %{app_name}的参与这个项目的政府网站,可以使用同一电邮地址来设立一个新 %{app_name} 账户。

+user_mailer.account_reset_granted.intro_html: 你%{waiting_period}的等待期已结束。请完成流程的第 2 步。

如果您无法找到你的身份证实方法,请选择“确认删除”以删除你的 %{app_name} 帐户。

如果你将来需要访问使用 %{app_name} 的参与政府网站,可以在帐户删除后使用相同的电子邮件地址创建一个新的 %{app_name} 帐户.

user_mailer.account_reset_granted.subject: 删除你的 %{app_name} 账户 user_mailer.account_reset_request.cancel: 不想删除你的账户?登入你的 %{app_name} 账户来取消。 -user_mailer.account_reset_request.header: 你的账户会在 24 小时后删除。 -user_mailer.account_reset_request.intro_html: '作为安全措施, %{app_name} 要求一个两步流程来删除账户:

第一步:如果你丢失了身份证实方法但需删除账户,有一个 24 小时的等待期。如果你找到了身份证实方法, 可登入你的 %{app_name} 账户来取消这一请求。

第二步:24 小时等待期过了之后,你会收到一封电邮,请你确认删除 %{app_name} 账户。只有经你确认后,你的账户才会被删除。' +user_mailer.account_reset_request.header: 你的账户会在%{interval}后删除。 +user_mailer.account_reset_request.intro_html: 作为一项安全措施,%{app_name} 要求一个两步流程来删除你的帐户:

第一步:如果你丢失了身份证实方法但需删除账户,有一个%{waiting_period} 的等待期。如果你找到了身份证实方法,可以登录你的 %{app_name} 帐户来取消这个请求。

第二步:%{waiting_period}等待期之后,你会收到一封电邮,请你确认要删除 %{app_name} 账户。只有经你确认后,你的账户才会被删除。 user_mailer.account_reset_request.subject: 如何删除你的 %{app_name} 账户 user_mailer.account_verified.change_password_link: 更改密码 user_mailer.account_verified.contact_link: 联系我们 @@ -1845,9 +1845,9 @@ user_mailer.in_person_verified.greeting: 你好, user_mailer.in_person_verified.intro: 你于 %{date}在 %{location} 邮局成功地验证了身份。 user_mailer.in_person_verified.next_sign_in.with_sp.with_cta: 接下来请点击按钮或复制下面的连接来访问 %{sp_name} 并登录。 user_mailer.in_person_verified.next_sign_in.with_sp.without_cta: 你现在可以从 %{sp_name} 的网站登录。 -user_mailer.in_person_verified.next_sign_in.without_sp: 接下来请点击按钮或复制下面的连接来登录 %{sp_name}。 +user_mailer.in_person_verified.next_sign_in.without_sp: 接下来请点击按钮或复制下面的连接来登录 %{app_name}。 user_mailer.in_person_verified.sign_in: 登录 -user_mailer.in_person_verified.subject: 你在 %{sp_name} 成功地验证了身份 +user_mailer.in_person_verified.subject: 你在 %{app_name} 成功地验证了身份 user_mailer.in_person_verified.warning_contact_us_html: 如果你没有试图亲身验证身份,请登入 重设密码。要报告这件事,联系 %{app_name} 支持 %{app_name}。 user_mailer.letter_reminder_14_days.body_html: "

%{date_letter_was_se\ nt} 日你要求了带有验证码的信

登录%{app_name} 并输入验证码来完成验证你的身份。 %{help_link}.

" @@ -1876,7 +1876,7 @@ user_mailer.new_device_sign_in_before_2fa.reset_password: 重设密码 user_mailer.new_device_sign_in_before_2fa.subject: 你 %{app_name} 账户有新的登录 user_mailer.new_device_sign_in.disavowal_link: 重设你的密码 user_mailer.new_device_sign_in.help_html: 如果你没做此更改, %{disavowal_link_html}。要得到更多帮助,请访问 %{app_name_html} %{help_link_html} 或者 %{contact_link_html}。 -user_mailer.new_device_sign_in.info: '' +user_mailer.new_device_sign_in.info: 你的 %{app_name} 帐号刚刚在一个新设备上用于登录。 user_mailer.new_device_sign_in.subject: 用你 %{app_name} 账户进行的新登录 user_mailer.password_changed.disavowal_link: 重设你的密码 user_mailer.password_changed.help_html: 如果你没做此更改, %{disavowal_link_html}。要得到更多帮助,请访问 %{app_name_html} %{help_link_html} 或者 %{contact_link_html}。 diff --git a/db/schema.rb b/db/schema.rb index 700e9562644..c99936d6691 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -14,7 +14,6 @@ # These are extensions that must be enabled in order to support this database enable_extension "citext" enable_extension "pg_stat_statements" - enable_extension "pgcrypto" enable_extension "plpgsql" create_table "account_reset_requests", force: :cascade do |t| @@ -318,7 +317,7 @@ t.boolean "ready_for_status_check", default: false t.datetime "notification_sent_at", comment: "The time a notification was sent" t.datetime "last_batch_claimed_at" - t.string "sponsor_id", comment: "The identification number for USPS to recognize us and our flow (ex: Enhanced IPP)" + t.string "sponsor_id" t.index ["profile_id"], name: "index_in_person_enrollments_on_profile_id" t.index ["ready_for_status_check"], name: "index_in_person_enrollments_on_ready_for_status_check", where: "(ready_for_status_check = true)" t.index ["status_check_attempted_at"], name: "index_in_person_enrollments_on_status_check_attempted_at", where: "(status = 1)" @@ -657,7 +656,7 @@ add_foreign_key "iaa_gtcs", "partner_accounts" add_foreign_key "iaa_orders", "iaa_gtcs" add_foreign_key "in_person_enrollments", "profiles" - add_foreign_key "in_person_enrollments", "service_providers", column: "issuer", primary_key: "issuer" + add_foreign_key "in_person_enrollments", "service_providers", column: "issuer", primary_key: "issuer", validate: false add_foreign_key "in_person_enrollments", "users" add_foreign_key "integration_usages", "iaa_orders" add_foreign_key "integration_usages", "integrations" diff --git a/spec/controllers/idv/in_person/verify_info_controller_spec.rb b/spec/controllers/idv/in_person/verify_info_controller_spec.rb index 7b090f67b9a..2db02ec4455 100644 --- a/spec/controllers/idv/in_person/verify_info_controller_spec.rb +++ b/spec/controllers/idv/in_person/verify_info_controller_spec.rb @@ -115,117 +115,6 @@ ) end end - - context 'tracks costs' do - let(:review_status) { 'pass' } - let(:async_state) { instance_double(ProofingSessionAsyncResult) } - let(:adjudicated_result) do - { - context: { - stages: { - threatmetrix: { - transaction_id: 1, - }, - resolution: { - transaction_id: 'resolution-mock-transaction-id-123', - vendor_name: 'ResolutionMock', - }, - residential_address: { - transaction_id: 'resolution-mock-transaction-id-123', - vendor_name: 'ResolutionMock', - }, - state_id: { - transaction_id: 'state-id-mock-transaction-id-456', - vendor_name: 'StateIdMock', - }, - }, - }, - } - end - - before do - allow(controller).to receive(:load_async_state).and_return(async_state) - allow(async_state).to receive(:done?).and_return(true) - end - - context 'when same address as id is true and in aamva jurisdiction' do - it 'adds costs to database' do - allow(async_state).to receive(:result).and_return(adjudicated_result) - - get :show - - lexis_nexis_costs = SpCost.where(cost_type: 'lexis_nexis_resolution') - expect(lexis_nexis_costs.count).to eq(1) - - aamva_costs = SpCost.where(cost_type: 'aamva') - expect(aamva_costs.count).to eq(1) - - threatmetrix_costs = SpCost.where(cost_type: 'threatmetrix') - expect(threatmetrix_costs.count).to eq(1) - end - end - - context 'when same address as id is true and not in aamva jurisdiction' do - it 'adds costs to database' do - adjudicated_result[:context][:stages][:state_id][:vendor_name] = 'UnsupportedJurisdiction' - allow(async_state).to receive(:result).and_return(adjudicated_result) - - get :show - - lexis_nexis_costs = SpCost.where(cost_type: 'lexis_nexis_resolution') - expect(lexis_nexis_costs.count).to eq(1) - - aamva_costs = SpCost.where(cost_type: 'aamva') - expect(aamva_costs.count).to eq(0) - - threatmetrix_costs = SpCost.where(cost_type: 'threatmetrix') - expect(threatmetrix_costs.count).to eq(1) - end - end - - context 'when same address as id is false and in aamva jurisdiction' do - let(:pii_from_user) do - { same_address_as_id: 'false' } - end - - it 'adds costs to database' do - allow(async_state).to receive(:result).and_return(adjudicated_result) - - get :show - - lexis_nexis_costs = SpCost.where(cost_type: 'lexis_nexis_resolution') - expect(lexis_nexis_costs.count).to eq(2) - - aamva_costs = SpCost.where(cost_type: 'aamva') - expect(aamva_costs.count).to eq(1) - - threatmetrix_costs = SpCost.where(cost_type: 'threatmetrix') - expect(threatmetrix_costs.count).to eq(1) - end - end - - context 'when same address as id is false and not in aamva jurisdiction' do - let(:pii_from_user) do - { same_address_as_id: 'false' } - end - - it 'adds costs to database' do - adjudicated_result[:context][:stages][:state_id][:vendor_name] = 'UnsupportedJurisdiction' - allow(async_state).to receive(:result).and_return(adjudicated_result) - - get :show - - lexis_nexis_costs = SpCost.where(cost_type: 'lexis_nexis_resolution') - expect(lexis_nexis_costs.count).to eq(2) - - aamva_costs = SpCost.where(cost_type: 'aamva') - expect(aamva_costs.count).to eq(0) - - threatmetrix_costs = SpCost.where(cost_type: 'threatmetrix') - expect(threatmetrix_costs.count).to eq(1) - end - end - end end describe '#update' do @@ -241,17 +130,21 @@ allow(user).to receive(:establishing_in_person_enrollment).and_return(enrollment) end - it 'sets uuid_prefix and state_id_type on pii_from_user' do - expect(Idv::Agent).to receive(:new). - with(hash_including(uuid_prefix: service_provider.app_id)).and_call_original + it 'sets ssn and state_id_type on pii_from_user' do + expect(Idv::Agent).to receive(:new).with( + hash_including( + state_id_type: 'drivers_license', + ssn: Idp::Constants::MOCK_IDV_APPLICANT_SAME_ADDRESS_AS_ID[:ssn], + ), + ).and_call_original + # our test data already has the expected value by default subject.user_session['idv/in_person'][:pii_from_user].delete(:state_id_type) + put :update expect(subject.user_session['idv/in_person'][:pii_from_user][:state_id_type]). to eq 'drivers_license' - expect(subject.user_session['idv/in_person'][:pii_from_user][:uuid_prefix]). - to eq service_provider.app_id end context 'a user does not have an establishing in person enrollment associated with them' do @@ -292,10 +185,7 @@ it 'captures state id address fields in the pii' do expect(Idv::Agent).to receive(:new).with( - Idp::Constants::MOCK_IDV_APPLICANT_STATE_ID_ADDRESS.merge( - uuid_prefix: nil, - uuid: user.uuid, - ), + Idp::Constants::MOCK_IDV_APPLICANT_STATE_ID_ADDRESS, ).and_call_original put :update end diff --git a/spec/controllers/idv/verify_info_controller_spec.rb b/spec/controllers/idv/verify_info_controller_spec.rb index 98eb9ff05d2..93ee6516bf1 100644 --- a/spec/controllers/idv/verify_info_controller_spec.rb +++ b/spec/controllers/idv/verify_info_controller_spec.rb @@ -296,26 +296,14 @@ hash_including(**analytics_args, success: true, analytics_id: 'Doc Auth'), ) end - - it 'records the cost as billable' do - expect { put :show }.to change { SpCost.where(cost_type: 'aamva').count }.by(1) - end end context 'when aamva returns success: false but no exception' do let(:success) { false } - it 'logs a cost' do - expect { put :show }.to change { SpCost.where(cost_type: 'aamva').count }.by(1) - end - end - - context 'when the jurisdiction is unsupported' do - let(:success) { true } - let(:vendor_name) { 'UnsupportedJurisdiction' } - - it 'does not consider the request billable' do - expect { put :show }.to_not change { SpCost.where(cost_type: 'aamva').count } + it 'redirects to the warning URL' do + put :show + expect(response).to redirect_to(idv_session_errors_warning_url) end end @@ -337,10 +325,6 @@ remaining_submit_attempts: kind_of(Numeric), ) end - - it 'does not log a cost' do - expect { put :show }.to change { SpCost.where(cost_type: 'aamva').count }.by(0) - end end end end @@ -375,8 +359,8 @@ expect(Idv::Agent).to receive(:new).with( hash_including( - uuid_prefix: app_id, - uuid: user.uuid, + ssn: Idp::Constants::MOCK_IDV_APPLICANT_WITH_SSN[:ssn], + **Idp::Constants::MOCK_IDV_APPLICANT, ), ).and_call_original @@ -423,43 +407,4 @@ end end end - - describe '#add_proofing_costs' do - let(:sp_costs_added) { nil } - let(:result) do - { - context: { - sp_costs_added:, - stages: { - resolution: { - transaction_id: 'ABCD1234', - }, - residential_address: { - vendor_name: 'ResidentialAddressNotRequired', - }, - state_id: { - transaction_id: 'EFGH5678', - }, - threatmetrix: { - transaction_id: 'IJKL9012', - }, - }, - }, - } - end - - it 'adds proofing costs' do - expect(subject).to receive(:add_cost).exactly(3).times - subject.send(:add_proofing_costs, result) - end - - context 'when proofing costs have already been added' do - let(:sp_costs_added) { true } - - it 'does not add proofing costs' do - expect(subject).not_to receive(:add_cost) - subject.send(:add_proofing_costs, result) - end - end - end end diff --git a/spec/controllers/saml_idp_controller_spec.rb b/spec/controllers/saml_idp_controller_spec.rb index df1f491b1da..309446663f6 100644 --- a/spec/controllers/saml_idp_controller_spec.rb +++ b/spec/controllers/saml_idp_controller_spec.rb @@ -1451,15 +1451,6 @@ def name_id_version(format_urn) ) end - let(:analytics_hash) do - { - success: true, - request_signed: authn_requests_signed, - matching_cert_serial:, - encryption_cert_matches_matching_cert:, - } - end - before do stub_analytics IdentityLinker.new(user, service_provider).link_identity @@ -1471,7 +1462,13 @@ def name_id_version(format_urn) generate_saml_response(user, auth_settings) expect(response.status).to eq(200) - expect(@analytics).to have_logged_event('SAML Auth', hash_including(analytics_hash)) + expect(@analytics).to have_logged_event( + 'SAML Auth', hash_including( + request_signed: false, + matching_cert_serial: nil, + encryption_cert_matches_matching_cert: nil, + ) + ) end end @@ -1479,21 +1476,23 @@ def name_id_version(format_urn) let(:authn_requests_signed) { true } context 'Matching certificate' do - let(:encryption_cert_matches_matching_cert) { true } - let(:matching_cert_serial) { saml_test_sp_cert_serial } - it 'notes that in the analytics event' do user.identities.last.update!(verified_attributes: ['email']) generate_saml_response(user, auth_settings) expect(response.status).to eq(200) - expect(@analytics).to have_logged_event('SAML Auth', hash_including(analytics_hash)) + expect(@analytics).to have_logged_event( + 'SAML Auth', hash_including( + request_signed: authn_requests_signed, + matching_cert_serial: saml_test_sp_cert_serial, + encryption_cert_matches_matching_cert: true, + cert_error_details: nil, + ) + ) end context 'Certificate sig validation fails because of namespace bug' do - let(:encryption_cert_matches_matching_cert) { false } - let(:matching_cert_serial) { nil } - let(:request_sp) { double } + let(:request_sp) { double } before do service_provider.update(certs: ['sp_sinatra_demo', 'saml_test_sp']) @@ -1507,17 +1506,22 @@ def name_id_version(format_urn) generate_saml_response(user, auth_settings) expect(response.status).to eq(200) - expect(@analytics).to have_logged_event('SAML Auth', hash_including(analytics_hash)) + expect(@analytics).to have_logged_event( + 'SAML Auth', hash_including( + request_signed: authn_requests_signed, + matching_cert_serial: nil, + encryption_cert_matches_matching_cert: false, + ) + ) end end end context 'Certificate does not match' do - let(:encryption_cert_matches_matching_cert) { true } let(:service_provider) do create( :service_provider, - certs: ['sp_sinatra_demo', 'saml_test_sp'], + certs: ['saml_test_sp'], active: true, assertion_consumer_logout_service_url: 'https://example.com', ) @@ -1541,9 +1545,22 @@ def name_id_version(format_urn) it 'notes that in the analytics event' do user.identities.last.update!(verified_attributes: ['email']) generate_saml_response(user, auth_settings) + cert_error_details = [ + { + cert: saml_test_sp_cert_serial, + error_code: :fingerprint_mismatch, + }, + ] expect(response.status).to eq(200) - expect(@analytics).to have_logged_event('SAML Auth', hash_including(analytics_hash)) + expect(@analytics).to have_logged_event( + 'SAML Auth', hash_including( + request_signed: authn_requests_signed, + matching_cert_serial:, + encryption_cert_matches_matching_cert: true, + cert_error_details:, + ) + ) end end end diff --git a/spec/features/account_email_language_spec.rb b/spec/features/account_email_language_spec.rb index 8d817c7f285..14f0d9b085b 100644 --- a/spec/features/account_email_language_spec.rb +++ b/spec/features/account_email_language_spec.rb @@ -16,30 +16,31 @@ end it 'lets them view their current email language' do - within(page.find('.profile-info-box', text: 'Language')) do - expect(page).to have_content('Spanish') + within(page.find('.profile-info-box', text: t('i18n.language'))) do + expect(page).to have_content(t("account.email_language.name.#{original_email_language}")) end end context 'changing their email language' do + let('chosen_email_language') { 'fr' } before do - within(page.find('.profile-info-box', text: 'Language')) do - click_link('Edit') + within(page.find('.profile-info-box', text: t('i18n.language'))) do + click_link(t('forms.buttons.edit')) end - choose 'Français' - click_button 'Submit' + choose t("account.email_language.name.#{chosen_email_language}") + click_button t('forms.buttons.submit.default') end it 'reflects the updated language preference' do - within(page.find('.profile-info-box', text: 'Language')) do - expect(page).to have_content('French') + within(page.find('.profile-info-box', text: t('i18n.language'))) do + expect(page).to have_content(t("account.email_language.name.#{chosen_email_language}")) end end it 'respects the language preference in emails, such as password reset emails' do within(page.find('.profile-info-box', text: 'Password')) do - click_link('Edit') + click_link(t('forms.buttons.edit')) end fill_in t('forms.passwords.edit.labels.password'), @@ -49,7 +50,12 @@ click_button 'Update' mail = ActionMailer::Base.deliveries.last - expect(mail.subject).to eq(I18n.t('devise.mailer.password_updated.subject', locale: 'fr')) + expect(mail.subject).to eq( + I18n.t( + 'devise.mailer.password_updated.subject', + locale: chosen_email_language, + ), + ) end end end diff --git a/spec/features/idv/analytics_spec.rb b/spec/features/idv/analytics_spec.rb index 0492d5580d0..a87a9792f7f 100644 --- a/spec/features/idv/analytics_spec.rb +++ b/spec/features/idv/analytics_spec.rb @@ -28,6 +28,22 @@ timed_out: false, transaction_id: 'ddp-mock-transaction-id-123' } end + let(:base_proofing_components) do + { + document_check: 'mock', + document_type: 'state_id', + source_check: 'aamva', + resolution_check: 'lexis_nexis', + threatmetrix: threatmetrix, + threatmetrix_review_status: 'pass', + } + end + let(:lexis_nexis_address_proofing_components) do + base_proofing_components.merge(address_check: 'lexis_nexis_address') + end + let(:gpo_letter_proofing_components) do + base_proofing_components.merge(address_check: 'gpo_letter') + end # rubocop:disable Layout/LineLength # rubocop:disable Layout/MultilineHashKeyLineBreaks @@ -102,62 +118,62 @@ 'IdV: phone of record visited' => { acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: nil, active_profile_idv_level: nil, pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass' } + proofing_components: base_proofing_components }, 'IdV: phone confirmation form' => { success: true, errors: {}, error_details: nil, phone_type: :mobile, types: [:fixed_or_mobile], carrier: 'Test Mobile Carrier', country_code: 'US', area_code: '202', acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: nil, otp_delivery_preference: 'sms', active_profile_idv_level: nil, pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass' } + proofing_components: base_proofing_components }, 'IdV: phone confirmation vendor' => { success: true, errors: {}, error_details: nil, vendor: { exception: nil, vendor_name: 'AddressMock', transaction_id: 'address-mock-transaction-id-123', timed_out: false, reference: '' }, new_phone_added: false, hybrid_handoff_phone_used: false, area_code: '202', country_code: 'US', phone_fingerprint: anything, active_profile_idv_level: nil, pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } + proofing_components: lexis_nexis_address_proofing_components }, 'IdV: phone confirmation otp sent' => { success: true, otp_delivery_preference: :sms, country_code: 'US', area_code: '202', adapter: :test, errors: {}, error_details: nil, phone_fingerprint: anything, rate_limit_exceeded: false, telephony_response: anything, active_profile_idv_level: nil, pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } + proofing_components: lexis_nexis_address_proofing_components }, 'IdV: phone confirmation otp visited' => { active_profile_idv_level: nil, pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } + proofing_components: lexis_nexis_address_proofing_components }, 'IdV: phone confirmation otp submitted' => { success: true, code_expired: false, code_matches: true, otp_delivery_preference: :sms, second_factor_attempts_count: 0, second_factor_locked_at: nil, errors: {}, error_details: nil, acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: nil, active_profile_idv_level: nil, pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } + proofing_components: lexis_nexis_address_proofing_components }, :idv_enter_password_visited => { address_verification_method: 'phone', acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: nil, active_profile_idv_level: nil, pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } + proofing_components: lexis_nexis_address_proofing_components }, :idv_enter_password_submitted => { success: true, acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: nil, fraud_review_pending: false, fraud_rejection: false, gpo_verification_pending: false, in_person_verification_pending: false, deactivation_reason: nil, active_profile_idv_level: 'legacy_unsupervised', pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } + proofing_components: lexis_nexis_address_proofing_components }, 'IdV: final resolution' => { success: true, acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: nil, fraud_review_pending: false, fraud_rejection: false, gpo_verification_pending: false, in_person_verification_pending: false, deactivation_reason: nil, active_profile_idv_level: 'legacy_unsupervised', pending_profile_idv_level: nil, profile_history: match_array(kind_of(Idv::ProfileLogging)), - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } + proofing_components: lexis_nexis_address_proofing_components }, 'IdV: personal key visited' => { address_verification_method: 'phone', encrypted_profiles_missing: false, in_person_verification_pending: false, active_profile_idv_level: 'legacy_unsupervised', pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } + proofing_components: lexis_nexis_address_proofing_components }, 'IdV: personal key acknowledgment toggled' => { checked: true, active_profile_idv_level: 'legacy_unsupervised', pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } + proofing_components: lexis_nexis_address_proofing_components }, 'IdV: personal key submitted' => { address_verification_method: 'phone', fraud_review_pending: false, fraud_rejection: false, in_person_verification_pending: false, deactivation_reason: nil, active_profile_idv_level: 'legacy_unsupervised', pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } + proofing_components: lexis_nexis_address_proofing_components }, } end @@ -228,62 +244,62 @@ 'IdV: phone of record visited' => { acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: nil, active_profile_idv_level: nil, pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass' } + proofing_components: base_proofing_components }, 'IdV: phone confirmation form' => { success: true, errors: {}, error_details: nil, phone_type: :mobile, types: [:fixed_or_mobile], carrier: 'Test Mobile Carrier', country_code: 'US', area_code: '202', acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: nil, otp_delivery_preference: 'sms', active_profile_idv_level: nil, pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass' } + proofing_components: base_proofing_components }, 'IdV: phone confirmation vendor' => { success: true, errors: {}, error_details: nil, vendor: { exception: nil, vendor_name: 'AddressMock', transaction_id: 'address-mock-transaction-id-123', timed_out: false, reference: '' }, new_phone_added: false, hybrid_handoff_phone_used: true, area_code: '202', country_code: 'US', phone_fingerprint: anything, active_profile_idv_level: nil, pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } + proofing_components: lexis_nexis_address_proofing_components }, 'IdV: phone confirmation otp sent' => { success: true, otp_delivery_preference: :sms, country_code: 'US', area_code: '202', adapter: :test, errors: {}, error_details: nil, phone_fingerprint: anything, rate_limit_exceeded: false, telephony_response: anything, active_profile_idv_level: nil, pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } + proofing_components: lexis_nexis_address_proofing_components }, 'IdV: phone confirmation otp visited' => { active_profile_idv_level: nil, pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } + proofing_components: lexis_nexis_address_proofing_components }, 'IdV: phone confirmation otp submitted' => { success: true, code_expired: false, code_matches: true, otp_delivery_preference: :sms, second_factor_attempts_count: 0, second_factor_locked_at: nil, errors: {}, error_details: nil, acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: nil, active_profile_idv_level: nil, pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } + proofing_components: lexis_nexis_address_proofing_components }, :idv_enter_password_visited => { address_verification_method: 'phone', acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: nil, active_profile_idv_level: nil, pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } + proofing_components: lexis_nexis_address_proofing_components }, :idv_enter_password_submitted => { success: true, acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: nil, fraud_review_pending: false, fraud_rejection: false, gpo_verification_pending: false, in_person_verification_pending: false, deactivation_reason: nil, active_profile_idv_level: 'legacy_unsupervised', pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } + proofing_components: lexis_nexis_address_proofing_components }, 'IdV: final resolution' => { success: true, acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: nil, fraud_review_pending: false, fraud_rejection: false, gpo_verification_pending: false, in_person_verification_pending: false, deactivation_reason: nil, active_profile_idv_level: 'legacy_unsupervised', pending_profile_idv_level: nil, profile_history: match_array(kind_of(Idv::ProfileLogging)), - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } + proofing_components: lexis_nexis_address_proofing_components }, 'IdV: personal key visited' => { address_verification_method: 'phone', encrypted_profiles_missing: false, in_person_verification_pending: false, active_profile_idv_level: 'legacy_unsupervised', pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } + proofing_components: lexis_nexis_address_proofing_components }, 'IdV: personal key acknowledgment toggled' => { checked: true, active_profile_idv_level: 'legacy_unsupervised', pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } + proofing_components: lexis_nexis_address_proofing_components }, 'IdV: personal key submitted' => { address_verification_method: 'phone', fraud_review_pending: false, fraud_rejection: false, in_person_verification_pending: false, deactivation_reason: nil, active_profile_idv_level: 'legacy_unsupervised', pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } + proofing_components: lexis_nexis_address_proofing_components }, } end @@ -351,12 +367,12 @@ 'IdV: phone of record visited' => { acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: nil, active_profile_idv_level: nil, pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass' } + proofing_components: base_proofing_components }, 'IdV: USPS address letter requested' => { resend: false, phone_step_attempts: 0, first_letter_requested_at: nil, hours_since_first_letter: 0, acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: nil, active_profile_idv_level: nil, pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass' } + proofing_components: base_proofing_components }, 'IdV: request letter visited' => { letter_already_sent: false, @@ -364,28 +380,28 @@ :idv_enter_password_visited => { address_verification_method: 'gpo', acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: nil, active_profile_idv_level: nil, pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'gpo_letter' } + proofing_components: gpo_letter_proofing_components }, 'IdV: USPS address letter enqueued' => { enqueued_at: Time.zone.now.utc, resend: false, phone_step_attempts: 0, first_letter_requested_at: Time.zone.now.utc, hours_since_first_letter: 0, acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: nil, active_profile_idv_level: nil, pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'gpo_letter' } + proofing_components: gpo_letter_proofing_components }, :idv_enter_password_submitted => { success: true, acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: nil, fraud_review_pending: false, fraud_rejection: false, gpo_verification_pending: true, in_person_verification_pending: false, deactivation_reason: nil, active_profile_idv_level: nil, pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'gpo_letter' } + proofing_components: gpo_letter_proofing_components }, 'IdV: final resolution' => { success: true, acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: nil, fraud_review_pending: false, fraud_rejection: false, gpo_verification_pending: true, in_person_verification_pending: false, deactivation_reason: nil, # NOTE: pending_profile_idv_level should be set here, a nil value is cached for current_user.pending_profile. active_profile_idv_level: nil, pending_profile_idv_level: nil, profile_history: match_array(kind_of(Idv::ProfileLogging)), - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'gpo_letter' } + proofing_components: gpo_letter_proofing_components }, 'IdV: letter enqueued visited' => { active_profile_idv_level: nil, pending_profile_idv_level: 'legacy_unsupervised', - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'gpo_letter' } + proofing_components: gpo_letter_proofing_components }, } end @@ -602,62 +618,62 @@ 'IdV: phone of record visited' => { acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: anything, active_profile_idv_level: nil, pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass' } + proofing_components: base_proofing_components }, 'IdV: phone confirmation form' => { success: true, errors: {}, error_details: nil, phone_type: :mobile, types: [:fixed_or_mobile], carrier: 'Test Mobile Carrier', country_code: 'US', area_code: '202', acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: anything, otp_delivery_preference: 'sms', active_profile_idv_level: nil, pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass' } + proofing_components: base_proofing_components }, 'IdV: phone confirmation vendor' => { success: true, errors: {}, error_details: nil, vendor: { exception: nil, vendor_name: 'AddressMock', transaction_id: 'address-mock-transaction-id-123', timed_out: false, reference: '' }, new_phone_added: false, hybrid_handoff_phone_used: false, area_code: '202', country_code: 'US', phone_fingerprint: anything, active_profile_idv_level: nil, pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } + proofing_components: lexis_nexis_address_proofing_components }, 'IdV: phone confirmation otp sent' => { success: true, otp_delivery_preference: :sms, country_code: 'US', area_code: '202', adapter: :test, errors: {}, error_details: nil, phone_fingerprint: anything, rate_limit_exceeded: false, telephony_response: anything, active_profile_idv_level: nil, pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } + proofing_components: lexis_nexis_address_proofing_components }, 'IdV: phone confirmation otp visited' => { active_profile_idv_level: nil, pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } + proofing_components: lexis_nexis_address_proofing_components }, 'IdV: phone confirmation otp submitted' => { success: true, acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: anything, code_expired: false, code_matches: true, otp_delivery_preference: :sms, second_factor_attempts_count: 0, second_factor_locked_at: nil, errors: {}, error_details: nil, active_profile_idv_level: nil, pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } + proofing_components: lexis_nexis_address_proofing_components }, :idv_enter_password_visited => { address_verification_method: 'phone', acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: anything, active_profile_idv_level: nil, pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } + proofing_components: lexis_nexis_address_proofing_components }, :idv_enter_password_submitted => { success: true, acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: anything, fraud_review_pending: false, fraud_rejection: false, gpo_verification_pending: false, in_person_verification_pending: false, deactivation_reason: nil, active_profile_idv_level: 'unsupervised_with_selfie', pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } + proofing_components: lexis_nexis_address_proofing_components }, 'IdV: final resolution' => { success: true, acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: anything, fraud_review_pending: false, fraud_rejection: false, gpo_verification_pending: false, in_person_verification_pending: false, deactivation_reason: nil, active_profile_idv_level: 'unsupervised_with_selfie', pending_profile_idv_level: nil, profile_history: match_array(kind_of(Idv::ProfileLogging)), - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } + proofing_components: lexis_nexis_address_proofing_components }, 'IdV: personal key visited' => { address_verification_method: 'phone', in_person_verification_pending: false, encrypted_profiles_missing: false, active_profile_idv_level: 'unsupervised_with_selfie', pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } + proofing_components: lexis_nexis_address_proofing_components }, 'IdV: personal key acknowledgment toggled' => { checked: true, active_profile_idv_level: 'unsupervised_with_selfie', pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } + proofing_components: lexis_nexis_address_proofing_components }, 'IdV: personal key submitted' => { address_verification_method: 'phone', fraud_review_pending: false, fraud_rejection: false, in_person_verification_pending: false, deactivation_reason: nil, active_profile_idv_level: 'unsupervised_with_selfie', pending_profile_idv_level: nil, - proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } + proofing_components: lexis_nexis_address_proofing_components }, } end diff --git a/spec/i18n_spec.rb b/spec/i18n_spec.rb index ba45f46f29d..ebcabaded71 100644 --- a/spec/i18n_spec.rb +++ b/spec/i18n_spec.rb @@ -6,7 +6,7 @@ # List of keys allowed to contain different interpolation arguments across locales ALLOWED_INTERPOLATION_MISMATCH_KEYS = [ 'time.formats.event_timestamp_js', -].sort.freeze +].freeze ALLOWED_LEADING_OR_TRAILING_SPACE_KEYS = [ 'datetime.dotiw.last_word_connector', @@ -15,24 +15,7 @@ ].sort.freeze # These are keys with mismatch interpolation for specific locales -ALLOWED_INTERPOLATION_MISMATCH_LOCALE_KEYS = [ - # need to be fixed - 'zh.account_reset.pending.confirm', - 'zh.account_reset.pending.wait_html', - 'zh.account_reset.recovery_options.check_webauthn_platform_info', - 'zh.doc_auth.info.exit.with_sp', - 'zh.idv.cancel.headings.exit.with_sp', - 'zh.idv.failure.exit.with_sp', - 'zh.telephony.account_reset_notice', - 'zh.telephony.confirmation_otp.voice', - 'zh.two_factor_authentication.account_reset.pending', - 'zh.user_mailer.account_reset_granted.intro_html', - 'zh.user_mailer.account_reset_request.header', - 'zh.user_mailer.account_reset_request.intro_html', - 'zh.user_mailer.in_person_verified.next_sign_in.without_sp', - 'zh.user_mailer.in_person_verified.subject', - 'zh.user_mailer.new_device_sign_in.info', -].sort.freeze +ALLOWED_INTERPOLATION_MISMATCH_LOCALE_KEYS = [].freeze PUNCTUATION_PAIRS = { '{' => '}', @@ -70,6 +53,10 @@ class BaseTask { key: 'i18n.locale.es', locales: %i[es fr zh] }, { key: 'i18n.locale.fr', locales: %i[es fr zh] }, { key: 'i18n.locale.zh', locales: %i[es fr zh] }, + { key: 'account.email_language.name.en', locales: %i[es fr zh] }, + { key: 'account.email_language.name.es', locales: %i[es fr zh] }, + { key: 'account.email_language.name.fr', locales: %i[es fr zh] }, + { key: 'account.email_language.name.zh', locales: %i[es fr zh] }, { key: 'account.navigation.menu', locales: %i[fr] }, # "Menu" is "Menu" in French { key: /^countries/ }, # Some countries have the same name across languages { key: 'date.formats.long', locales: %i[es zh] }, @@ -88,11 +75,6 @@ class BaseTask { key: 'time.formats.event_timestamp', locales: %i[zh] }, { key: 'time.formats.full_date', locales: %i[es] }, # format is the same in Spanish and English { key: 'time.formats.sms_date' }, # for us date format - # need to be fixed - { key: 'account.email_language.name.zh', locales: %i[es fr] }, # needs to be translated - { key: 'errors.messages.blank_cert_element_req', locales: %i[zh] }, # needs to be translated - { key: 'openid_connect.authorization.errors.no_valid_vtr', locales: %i[zh] }, # needs to be translated - { key: 'telephony.account_deleted_notice', locales: %i[zh] }, # needs to be translated ].freeze # rubocop:enable Layout/LineLength @@ -260,14 +242,25 @@ def allowed_untranslated_key?(locale, key) keys = interpolation_arguments.group_by { |_k, v| v }. sort_by { |_k, v| v.length * -1 }.drop(1). - map { |x| x[1].flatten }.to_h.keys + flat_map { |x| x[1] }.to_h.keys missing_interpolation_argument_locale_keys += keys end + unallowed_interpolation_mismatch_locale_keys = + missing_interpolation_argument_locale_keys - ALLOWED_INTERPOLATION_MISMATCH_LOCALE_KEYS + + expect(unallowed_interpolation_mismatch_locale_keys).to( + be_empty, + <<~EOS, + There are mismatched interpolation arguments: + #{unallowed_interpolation_mismatch_locale_keys.pretty_inspect} + EOS + ) + unused_allowed_interpolation_mismatch_keys = ALLOWED_INTERPOLATION_MISMATCH_KEYS - missing_interpolation_argument_keys - expect(unused_allowed_interpolation_mismatch_keys.sort).to( + expect(unused_allowed_interpolation_mismatch_keys).to( be_empty, <<~EOS, ALLOWED_INTERPOLATION_MISMATCH_KEYS contains unused allowed interpolation mismatches. diff --git a/spec/javascript/packages/document-capture/components/acuant-capture-canvas-spec.jsx b/spec/javascript/packages/document-capture/components/acuant-capture-canvas-spec.jsx index 5267a6af135..3eaa8b4f8e9 100644 --- a/spec/javascript/packages/document-capture/components/acuant-capture-canvas-spec.jsx +++ b/spec/javascript/packages/document-capture/components/acuant-capture-canvas-spec.jsx @@ -1,33 +1,12 @@ import sinon from 'sinon'; import userEvent from '@testing-library/user-event'; import { AcuantContextProvider, DeviceContext } from '@18f/identity-document-capture'; -import AcuantCaptureCanvas, { - defineObservableProperty, -} from '@18f/identity-document-capture/components/acuant-capture-canvas'; +import AcuantCaptureCanvas from '@18f/identity-document-capture/components/acuant-capture-canvas'; import { render, useAcuant } from '../../../support/document-capture'; describe('document-capture/components/acuant-capture-canvas', () => { const { initialize } = useAcuant(); - describe('defineObservableProperty', () => { - it('behaves like an object', () => { - const object = {}; - defineObservableProperty(object, 'key', () => {}); - object.key = 'value'; - - expect(object.key).to.equal('value'); - }); - - it('calls the callback on changes, with the changed value', () => { - const callback = sinon.spy(); - const object = {}; - defineObservableProperty(object, 'key', callback); - object.key = 'value'; - - expect(callback).to.have.been.calledOnceWithExactly('value'); - }); - }); - it('renders a "take photo" button', async () => { const { getByRole, container } = render( diff --git a/spec/javascript/packages/document-capture/higher-order/observable-property-spec.tsx b/spec/javascript/packages/document-capture/higher-order/observable-property-spec.tsx new file mode 100644 index 00000000000..0e0f66a56e6 --- /dev/null +++ b/spec/javascript/packages/document-capture/higher-order/observable-property-spec.tsx @@ -0,0 +1,44 @@ +import sinon from 'sinon'; +import { + defineObservableProperty, + stopObservingProperty, +} from '@18f/identity-document-capture/higher-order/observable-property'; + +describe('document-capture/higher-order/observable-property', () => { + describe('defineObservableProperty', () => { + it('behaves like an object', () => { + const object = {} as { key?: string }; + defineObservableProperty(object, 'key', () => {}); + object.key = 'value'; + + expect(object.key).to.equal('value'); + }); + + it('calls the callback on changes, with the changed value', () => { + const callback = sinon.spy(); + const object = {} as { key?: string }; + defineObservableProperty(object, 'key', callback); + object.key = 'value'; + + expect(callback).to.have.been.calledOnceWithExactly('value'); + }); + }); + + describe('stopObservingProperty', () => { + it('removes the defined property and set the last value as a plain value', () => { + const object = {} as { key?: string }; + const callback = sinon.spy(); + defineObservableProperty(object, 'key', callback); + + object.key = 'value'; + + stopObservingProperty(object, 'key'); + expect(object.key).to.equal('value'); + + object.key = 'second_value'; + expect(object.key).to.equal('second_value'); + + expect(callback).to.have.been.calledOnceWithExactly('value'); + }); + }); +}); diff --git a/spec/jobs/reports/combined_invoice_supplement_report_v2_spec.rb b/spec/jobs/reports/combined_invoice_supplement_report_v2_spec.rb index 3d8f8ca4c61..52db27cf4d5 100644 --- a/spec/jobs/reports/combined_invoice_supplement_report_v2_spec.rb +++ b/spec/jobs/reports/combined_invoice_supplement_report_v2_spec.rb @@ -99,20 +99,35 @@ expect(row['iaa_ial1_unique_users'].to_i).to eq(1) expect(row['iaa_ial2_unique_users'].to_i).to eq(2) expect(row['iaa_ial1_plus_2_unique_users'].to_i).to eq(3) - expect(row['partner_ial2_unique_users_year1'].to_i).to eq(0) - expect(row['partner_ial2_unique_users_year2'].to_i).to eq(2) - expect(row['partner_ial2_unique_users_year3'].to_i).to eq(0) - expect(row['partner_ial2_unique_users_year4'].to_i).to eq(0) - expect(row['partner_ial2_unique_users_year5'].to_i).to eq(0) - expect(row['partner_ial2_unique_users_year_greater_than_5'].to_i).to eq(0) - expect(row['partner_ial2_unique_users_unknown'].to_i).to eq(0) - expect(row['partner_ial2_new_unique_users_year1'].to_i).to eq(0) - expect(row['partner_ial2_new_unique_users_year2'].to_i).to eq(2) - expect(row['partner_ial2_new_unique_users_year3'].to_i).to eq(0) - expect(row['partner_ial2_new_unique_users_year4'].to_i).to eq(0) - expect(row['partner_ial2_new_unique_users_year5'].to_i).to eq(0) - expect(row['partner_ial2_new_unique_users_year_greater_than_5'].to_i).to eq(0) - expect(row['partner_ial2_new_unique_users_unknown'].to_i).to eq(0) + expect(row['partner_ial2_unique_user_events_year1'].to_i).to eq(0) + expect(row['partner_ial2_unique_user_events_year2'].to_i).to eq(2) + expect(row['partner_ial2_unique_user_events_year3'].to_i).to eq(0) + expect(row['partner_ial2_unique_user_events_year4'].to_i).to eq(0) + expect(row['partner_ial2_unique_user_events_year5'].to_i).to eq(0) + expect(row['partner_ial2_unique_user_events_year_greater_than_5'].to_i).to eq(0) + expect(row['partner_ial2_unique_user_events_unknown'].to_i).to eq(0) + expect(row['partner_ial2_new_unique_user_events_year1'].to_i).to eq(0) + expect(row['partner_ial2_new_unique_user_events_year2'].to_i).to eq(2) + expect(row['partner_ial2_new_unique_user_events_year3'].to_i).to eq(0) + expect(row['partner_ial2_new_unique_user_events_year4'].to_i).to eq(0) + expect(row['partner_ial2_new_unique_user_events_year5'].to_i).to eq(0) + expect(row['partner_ial2_new_unique_user_events_year_greater_than_5'].to_i).to eq(0) + expect(row['partner_ial2_new_unique_user_events_unknown'].to_i).to eq(0) + + expect(row['issuer_ial2_unique_user_events_year1'].to_i).to eq(0) + expect(row['issuer_ial2_unique_user_events_year2'].to_i).to eq(2) + expect(row['issuer_ial2_unique_user_events_year3'].to_i).to eq(0) + expect(row['issuer_ial2_unique_user_events_year4'].to_i).to eq(0) + expect(row['issuer_ial2_unique_user_events_year5'].to_i).to eq(0) + expect(row['issuer_ial2_unique_user_events_year_greater_than_5'].to_i).to eq(0) + expect(row['issuer_ial2_unique_user_events_unknown'].to_i).to eq(0) + expect(row['issuer_ial2_new_unique_user_events_year1'].to_i).to eq(0) + expect(row['issuer_ial2_new_unique_user_events_year2'].to_i).to eq(2) + expect(row['issuer_ial2_new_unique_user_events_year3'].to_i).to eq(0) + expect(row['issuer_ial2_new_unique_user_events_year4'].to_i).to eq(0) + expect(row['issuer_ial2_new_unique_user_events_year5'].to_i).to eq(0) + expect(row['issuer_ial2_new_unique_user_events_year_greater_than_5'].to_i).to eq(0) + expect(row['issuer_ial2_new_unique_user_events_unknown'].to_i).to eq(0) expect(row['issuer_ial1_total_auth_count'].to_i).to eq(7) expect(row['issuer_ial2_total_auth_count'].to_i).to eq(2) @@ -121,20 +136,6 @@ expect(row['issuer_ial1_unique_users'].to_i).to eq(1) expect(row['issuer_ial2_unique_users'].to_i).to eq(2) expect(row['issuer_ial1_plus_2_unique_users'].to_i).to eq(3) - expect(row['issuer_ial2_unique_users_year1'].to_i).to eq(0) - expect(row['issuer_ial2_unique_users_year2'].to_i).to eq(2) - expect(row['issuer_ial2_unique_users_year3'].to_i).to eq(0) - expect(row['issuer_ial2_unique_users_year4'].to_i).to eq(0) - expect(row['issuer_ial2_unique_users_year5'].to_i).to eq(0) - expect(row['issuer_ial2_unique_users_year_greater_than_5'].to_i).to eq(0) - expect(row['issuer_ial2_unique_users_unknown'].to_i).to eq(0) - expect(row['issuer_ial2_new_unique_users_year1'].to_i).to eq(0) - expect(row['issuer_ial2_new_unique_users_year2'].to_i).to eq(2) - expect(row['issuer_ial2_new_unique_users_year3'].to_i).to eq(0) - expect(row['issuer_ial2_new_unique_users_year4'].to_i).to eq(0) - expect(row['issuer_ial2_new_unique_users_year5'].to_i).to eq(0) - expect(row['issuer_ial2_new_unique_users_year_greater_than_5'].to_i).to eq(0) - expect(row['issuer_ial2_new_unique_users_unknown'].to_i).to eq(0) end end end @@ -278,20 +279,35 @@ expect(row['iaa_ial1_unique_users'].to_i).to eq(0) expect(row['iaa_ial2_unique_users'].to_i).to eq(8) expect(row['iaa_ial1_plus_2_unique_users'].to_i).to eq(8) - expect(row['partner_ial2_unique_users_year1'].to_i).to eq(1) - expect(row['partner_ial2_unique_users_year2'].to_i).to eq(2) - expect(row['partner_ial2_unique_users_year3'].to_i).to eq(1) - expect(row['partner_ial2_unique_users_year4'].to_i).to eq(1) - expect(row['partner_ial2_unique_users_year5'].to_i).to eq(1) - expect(row['partner_ial2_unique_users_year_greater_than_5'].to_i).to eq(1) - expect(row['partner_ial2_unique_users_unknown'].to_i).to eq(1) - expect(row['partner_ial2_new_unique_users_year1'].to_i).to eq(1) - expect(row['partner_ial2_new_unique_users_year2'].to_i).to eq(2) - expect(row['partner_ial2_new_unique_users_year3'].to_i).to eq(1) - expect(row['partner_ial2_new_unique_users_year4'].to_i).to eq(1) - expect(row['partner_ial2_new_unique_users_year5'].to_i).to eq(1) - expect(row['partner_ial2_new_unique_users_year_greater_than_5'].to_i).to eq(1) - expect(row['partner_ial2_new_unique_users_unknown'].to_i).to eq(1) + expect(row['partner_ial2_unique_user_events_year1'].to_i).to eq(1) + expect(row['partner_ial2_unique_user_events_year2'].to_i).to eq(2) + expect(row['partner_ial2_unique_user_events_year3'].to_i).to eq(1) + expect(row['partner_ial2_unique_user_events_year4'].to_i).to eq(1) + expect(row['partner_ial2_unique_user_events_year5'].to_i).to eq(1) + expect(row['partner_ial2_unique_user_events_year_greater_than_5'].to_i).to eq(1) + expect(row['partner_ial2_unique_user_events_unknown'].to_i).to eq(1) + expect(row['partner_ial2_new_unique_user_events_year1'].to_i).to eq(1) + expect(row['partner_ial2_new_unique_user_events_year2'].to_i).to eq(2) + expect(row['partner_ial2_new_unique_user_events_year3'].to_i).to eq(1) + expect(row['partner_ial2_new_unique_user_events_year4'].to_i).to eq(1) + expect(row['partner_ial2_new_unique_user_events_year5'].to_i).to eq(1) + expect(row['partner_ial2_new_unique_user_events_year_greater_than_5'].to_i).to eq(1) + expect(row['partner_ial2_new_unique_user_events_unknown'].to_i).to eq(1) + + expect(row['issuer_ial2_unique_user_events_year1'].to_i).to eq(1) + expect(row['issuer_ial2_unique_user_events_year2'].to_i).to eq(0) + expect(row['issuer_ial2_unique_user_events_year3'].to_i).to eq(1) + expect(row['issuer_ial2_unique_user_events_year4'].to_i).to eq(0) + expect(row['issuer_ial2_unique_user_events_year5'].to_i).to eq(1) + expect(row['issuer_ial2_unique_user_events_year_greater_than_5'].to_i).to eq(0) + expect(row['issuer_ial2_unique_user_events_unknown'].to_i).to eq(1) + expect(row['issuer_ial2_new_unique_user_events_year1'].to_i).to eq(1) + expect(row['issuer_ial2_new_unique_user_events_year2'].to_i).to eq(0) + expect(row['issuer_ial2_new_unique_user_events_year3'].to_i).to eq(1) + expect(row['issuer_ial2_new_unique_user_events_year4'].to_i).to eq(0) + expect(row['issuer_ial2_new_unique_user_events_year5'].to_i).to eq(1) + expect(row['issuer_ial2_new_unique_user_events_year_greater_than_5'].to_i).to eq(0) + expect(row['issuer_ial2_new_unique_user_events_unknown'].to_i).to eq(1) expect(row['issuer_ial1_total_auth_count'].to_i).to eq(0) expect(row['issuer_ial2_total_auth_count'].to_i).to eq(4) @@ -300,20 +316,6 @@ expect(row['issuer_ial1_unique_users'].to_i).to eq(0) expect(row['issuer_ial2_unique_users'].to_i).to eq(4) expect(row['issuer_ial1_plus_2_unique_users'].to_i).to eq(4) - expect(row['issuer_ial2_unique_users_year1'].to_i).to eq(1) - expect(row['issuer_ial2_unique_users_year2'].to_i).to eq(0) - expect(row['issuer_ial2_unique_users_year3'].to_i).to eq(1) - expect(row['issuer_ial2_unique_users_year4'].to_i).to eq(0) - expect(row['issuer_ial2_unique_users_year5'].to_i).to eq(1) - expect(row['issuer_ial2_unique_users_year_greater_than_5'].to_i).to eq(0) - expect(row['issuer_ial2_unique_users_unknown'].to_i).to eq(1) - expect(row['issuer_ial2_new_unique_users_year1'].to_i).to eq(1) - expect(row['issuer_ial2_new_unique_users_year2'].to_i).to eq(0) - expect(row['issuer_ial2_new_unique_users_year3'].to_i).to eq(1) - expect(row['issuer_ial2_new_unique_users_year4'].to_i).to eq(0) - expect(row['issuer_ial2_new_unique_users_year5'].to_i).to eq(1) - expect(row['issuer_ial2_new_unique_users_year_greater_than_5'].to_i).to eq(0) - expect(row['issuer_ial2_new_unique_users_unknown'].to_i).to eq(1) end aggregate_failures do @@ -333,20 +335,35 @@ expect(row['iaa_ial1_unique_users'].to_i).to eq(0) expect(row['iaa_ial2_unique_users'].to_i).to eq(8) expect(row['iaa_ial1_plus_2_unique_users'].to_i).to eq(8) - expect(row['partner_ial2_unique_users_year1'].to_i).to eq(1) - expect(row['partner_ial2_unique_users_year2'].to_i).to eq(2) - expect(row['partner_ial2_unique_users_year3'].to_i).to eq(1) - expect(row['partner_ial2_unique_users_year4'].to_i).to eq(1) - expect(row['partner_ial2_unique_users_year5'].to_i).to eq(1) - expect(row['partner_ial2_unique_users_year_greater_than_5'].to_i).to eq(1) - expect(row['partner_ial2_unique_users_unknown'].to_i).to eq(1) - expect(row['partner_ial2_new_unique_users_year1'].to_i).to eq(1) - expect(row['partner_ial2_new_unique_users_year2'].to_i).to eq(2) - expect(row['partner_ial2_new_unique_users_year3'].to_i).to eq(1) - expect(row['partner_ial2_new_unique_users_year4'].to_i).to eq(1) - expect(row['partner_ial2_new_unique_users_year5'].to_i).to eq(1) - expect(row['partner_ial2_new_unique_users_year_greater_than_5'].to_i).to eq(1) - expect(row['partner_ial2_new_unique_users_unknown'].to_i).to eq(1) + expect(row['partner_ial2_unique_user_events_year1'].to_i).to eq(1) + expect(row['partner_ial2_unique_user_events_year2'].to_i).to eq(2) + expect(row['partner_ial2_unique_user_events_year3'].to_i).to eq(1) + expect(row['partner_ial2_unique_user_events_year4'].to_i).to eq(1) + expect(row['partner_ial2_unique_user_events_year5'].to_i).to eq(1) + expect(row['partner_ial2_unique_user_events_year_greater_than_5'].to_i).to eq(1) + expect(row['partner_ial2_unique_user_events_unknown'].to_i).to eq(1) + expect(row['partner_ial2_new_unique_user_events_year1'].to_i).to eq(1) + expect(row['partner_ial2_new_unique_user_events_year2'].to_i).to eq(2) + expect(row['partner_ial2_new_unique_user_events_year3'].to_i).to eq(1) + expect(row['partner_ial2_new_unique_user_events_year4'].to_i).to eq(1) + expect(row['partner_ial2_new_unique_user_events_year5'].to_i).to eq(1) + expect(row['partner_ial2_new_unique_user_events_year_greater_than_5'].to_i).to eq(1) + expect(row['partner_ial2_new_unique_user_events_unknown'].to_i).to eq(1) + + expect(row['issuer_ial2_unique_user_events_year1'].to_i).to eq(0) + expect(row['issuer_ial2_unique_user_events_year2'].to_i).to eq(2) + expect(row['issuer_ial2_unique_user_events_year3'].to_i).to eq(0) + expect(row['issuer_ial2_unique_user_events_year4'].to_i).to eq(1) + expect(row['issuer_ial2_unique_user_events_year5'].to_i).to eq(0) + expect(row['issuer_ial2_unique_user_events_year_greater_than_5'].to_i).to eq(1) + expect(row['issuer_ial2_unique_user_events_unknown'].to_i).to eq(0) + expect(row['issuer_ial2_new_unique_user_events_year1'].to_i).to eq(0) + expect(row['issuer_ial2_new_unique_user_events_year2'].to_i).to eq(2) + expect(row['issuer_ial2_new_unique_user_events_year3'].to_i).to eq(0) + expect(row['issuer_ial2_new_unique_user_events_year4'].to_i).to eq(1) + expect(row['issuer_ial2_new_unique_user_events_year5'].to_i).to eq(0) + expect(row['issuer_ial2_new_unique_user_events_year_greater_than_5'].to_i).to eq(1) + expect(row['issuer_ial2_new_unique_user_events_unknown'].to_i).to eq(0) expect(row['issuer_ial1_total_auth_count'].to_i).to eq(0) expect(row['issuer_ial2_total_auth_count'].to_i).to eq(4) @@ -355,20 +372,6 @@ expect(row['issuer_ial1_unique_users'].to_i).to eq(0) expect(row['issuer_ial2_unique_users'].to_i).to eq(4) expect(row['issuer_ial1_plus_2_unique_users'].to_i).to eq(4) - expect(row['issuer_ial2_unique_users_year1'].to_i).to eq(0) - expect(row['issuer_ial2_unique_users_year2'].to_i).to eq(2) - expect(row['issuer_ial2_unique_users_year3'].to_i).to eq(0) - expect(row['issuer_ial2_unique_users_year4'].to_i).to eq(1) - expect(row['issuer_ial2_unique_users_year5'].to_i).to eq(0) - expect(row['issuer_ial2_unique_users_year_greater_than_5'].to_i).to eq(1) - expect(row['issuer_ial2_unique_users_unknown'].to_i).to eq(0) - expect(row['issuer_ial2_new_unique_users_year1'].to_i).to eq(0) - expect(row['issuer_ial2_new_unique_users_year2'].to_i).to eq(2) - expect(row['issuer_ial2_new_unique_users_year3'].to_i).to eq(0) - expect(row['issuer_ial2_new_unique_users_year4'].to_i).to eq(1) - expect(row['issuer_ial2_new_unique_users_year5'].to_i).to eq(0) - expect(row['issuer_ial2_new_unique_users_year_greater_than_5'].to_i).to eq(1) - expect(row['issuer_ial2_new_unique_users_unknown'].to_i).to eq(0) end end end @@ -471,20 +474,35 @@ expect(row['iaa_ial1_unique_users'].to_i).to eq(0) expect(row['iaa_ial2_unique_users'].to_i).to eq(1) expect(row['iaa_ial1_plus_2_unique_users'].to_i).to eq(1) - expect(row['partner_ial2_unique_users_year1'].to_i).to eq(1) - expect(row['partner_ial2_unique_users_year2'].to_i).to eq(0) - expect(row['partner_ial2_unique_users_year3'].to_i).to eq(0) - expect(row['partner_ial2_unique_users_year4'].to_i).to eq(0) - expect(row['partner_ial2_unique_users_year5'].to_i).to eq(0) - expect(row['partner_ial2_unique_users_year_greater_than_5'].to_i).to eq(0) - expect(row['partner_ial2_unique_users_unknown'].to_i).to eq(0) - expect(row['partner_ial2_new_unique_users_year1'].to_i).to eq(1) - expect(row['partner_ial2_new_unique_users_year2'].to_i).to eq(0) - expect(row['partner_ial2_new_unique_users_year3'].to_i).to eq(0) - expect(row['partner_ial2_new_unique_users_year4'].to_i).to eq(0) - expect(row['partner_ial2_new_unique_users_year5'].to_i).to eq(0) - expect(row['partner_ial2_new_unique_users_year_greater_than_5'].to_i).to eq(0) - expect(row['partner_ial2_new_unique_users_unknown'].to_i).to eq(0) + expect(row['partner_ial2_unique_user_events_year1'].to_i).to eq(1) + expect(row['partner_ial2_unique_user_events_year2'].to_i).to eq(0) + expect(row['partner_ial2_unique_user_events_year3'].to_i).to eq(0) + expect(row['partner_ial2_unique_user_events_year4'].to_i).to eq(0) + expect(row['partner_ial2_unique_user_events_year5'].to_i).to eq(0) + expect(row['partner_ial2_unique_user_events_year_greater_than_5'].to_i).to eq(0) + expect(row['partner_ial2_unique_user_events_unknown'].to_i).to eq(0) + expect(row['partner_ial2_new_unique_user_events_year1'].to_i).to eq(1) + expect(row['partner_ial2_new_unique_user_events_year2'].to_i).to eq(0) + expect(row['partner_ial2_new_unique_user_events_year3'].to_i).to eq(0) + expect(row['partner_ial2_new_unique_user_events_year4'].to_i).to eq(0) + expect(row['partner_ial2_new_unique_user_events_year5'].to_i).to eq(0) + expect(row['partner_ial2_new_unique_user_events_year_greater_than_5'].to_i).to eq(0) + expect(row['partner_ial2_new_unique_user_events_unknown'].to_i).to eq(0) + + expect(row['issuer_ial2_unique_user_events_year1'].to_i).to eq(1) + expect(row['issuer_ial2_unique_user_events_year2'].to_i).to eq(0) + expect(row['issuer_ial2_unique_user_events_year3'].to_i).to eq(0) + expect(row['issuer_ial2_unique_user_events_year4'].to_i).to eq(0) + expect(row['issuer_ial2_unique_user_events_year5'].to_i).to eq(0) + expect(row['issuer_ial2_unique_user_events_year_greater_than_5'].to_i).to eq(0) + expect(row['issuer_ial2_unique_user_events_unknown'].to_i).to eq(0) + expect(row['issuer_ial2_new_unique_user_events_year1'].to_i).to eq(1) + expect(row['issuer_ial2_new_unique_user_events_year2'].to_i).to eq(0) + expect(row['issuer_ial2_new_unique_user_events_year3'].to_i).to eq(0) + expect(row['issuer_ial2_new_unique_user_events_year4'].to_i).to eq(0) + expect(row['issuer_ial2_new_unique_user_events_year5'].to_i).to eq(0) + expect(row['issuer_ial2_new_unique_user_events_year_greater_than_5'].to_i).to eq(0) + expect(row['issuer_ial2_new_unique_user_events_unknown'].to_i).to eq(0) expect(row['issuer_ial1_total_auth_count'].to_i).to eq(0) expect(row['issuer_ial2_total_auth_count'].to_i).to eq(1) @@ -493,20 +511,6 @@ expect(row['issuer_ial1_unique_users'].to_i).to eq(0) expect(row['issuer_ial2_unique_users'].to_i).to eq(1) expect(row['issuer_ial1_plus_2_unique_users'].to_i).to eq(1) - expect(row['issuer_ial2_unique_users_year1'].to_i).to eq(1) - expect(row['issuer_ial2_unique_users_year2'].to_i).to eq(0) - expect(row['issuer_ial2_unique_users_year3'].to_i).to eq(0) - expect(row['issuer_ial2_unique_users_year4'].to_i).to eq(0) - expect(row['issuer_ial2_unique_users_year5'].to_i).to eq(0) - expect(row['issuer_ial2_unique_users_year_greater_than_5'].to_i).to eq(0) - expect(row['issuer_ial2_unique_users_unknown'].to_i).to eq(0) - expect(row['issuer_ial2_new_unique_users_year1'].to_i).to eq(1) - expect(row['issuer_ial2_new_unique_users_year2'].to_i).to eq(0) - expect(row['issuer_ial2_new_unique_users_year3'].to_i).to eq(0) - expect(row['issuer_ial2_new_unique_users_year4'].to_i).to eq(0) - expect(row['issuer_ial2_new_unique_users_year5'].to_i).to eq(0) - expect(row['issuer_ial2_new_unique_users_year_greater_than_5'].to_i).to eq(0) - expect(row['issuer_ial2_new_unique_users_unknown'].to_i).to eq(0) end aggregate_failures do @@ -526,20 +530,35 @@ expect(row['iaa_ial1_unique_users'].to_i).to eq(0) expect(row['iaa_ial2_unique_users'].to_i).to eq(2) expect(row['iaa_ial1_plus_2_unique_users'].to_i).to eq(2) - expect(row['partner_ial2_unique_users_year1'].to_i).to eq(2) - expect(row['partner_ial2_unique_users_year2'].to_i).to eq(1) - expect(row['partner_ial2_unique_users_year3'].to_i).to eq(0) - expect(row['partner_ial2_unique_users_year4'].to_i).to eq(1) - expect(row['partner_ial2_unique_users_year5'].to_i).to eq(0) - expect(row['partner_ial2_unique_users_year_greater_than_5'].to_i).to eq(0) - expect(row['partner_ial2_unique_users_unknown'].to_i).to eq(0) - expect(row['partner_ial2_new_unique_users_year1'].to_i).to eq(1) - expect(row['partner_ial2_new_unique_users_year2'].to_i).to eq(1) - expect(row['partner_ial2_new_unique_users_year3'].to_i).to eq(0) - expect(row['partner_ial2_new_unique_users_year4'].to_i).to eq(1) - expect(row['partner_ial2_new_unique_users_year5'].to_i).to eq(0) - expect(row['partner_ial2_new_unique_users_year_greater_than_5'].to_i).to eq(0) - expect(row['partner_ial2_new_unique_users_unknown'].to_i).to eq(0) + expect(row['partner_ial2_unique_user_events_year1'].to_i).to eq(2) + expect(row['partner_ial2_unique_user_events_year2'].to_i).to eq(1) + expect(row['partner_ial2_unique_user_events_year3'].to_i).to eq(0) + expect(row['partner_ial2_unique_user_events_year4'].to_i).to eq(1) + expect(row['partner_ial2_unique_user_events_year5'].to_i).to eq(0) + expect(row['partner_ial2_unique_user_events_year_greater_than_5'].to_i).to eq(0) + expect(row['partner_ial2_unique_user_events_unknown'].to_i).to eq(0) + expect(row['partner_ial2_new_unique_user_events_year1'].to_i).to eq(1) + expect(row['partner_ial2_new_unique_user_events_year2'].to_i).to eq(1) + expect(row['partner_ial2_new_unique_user_events_year3'].to_i).to eq(0) + expect(row['partner_ial2_new_unique_user_events_year4'].to_i).to eq(1) + expect(row['partner_ial2_new_unique_user_events_year5'].to_i).to eq(0) + expect(row['partner_ial2_new_unique_user_events_year_greater_than_5'].to_i).to eq(0) + expect(row['partner_ial2_new_unique_user_events_unknown'].to_i).to eq(0) + + expect(row['issuer_ial2_unique_user_events_year1'].to_i).to eq(2) + expect(row['issuer_ial2_unique_user_events_year2'].to_i).to eq(1) + expect(row['issuer_ial2_unique_user_events_year3'].to_i).to eq(0) + expect(row['issuer_ial2_unique_user_events_year4'].to_i).to eq(1) + expect(row['issuer_ial2_unique_user_events_year5'].to_i).to eq(0) + expect(row['issuer_ial2_unique_user_events_year_greater_than_5'].to_i).to eq(0) + expect(row['issuer_ial2_unique_user_events_unknown'].to_i).to eq(0) + expect(row['issuer_ial2_new_unique_user_events_year1'].to_i).to eq(1) + expect(row['issuer_ial2_new_unique_user_events_year2'].to_i).to eq(1) + expect(row['issuer_ial2_new_unique_user_events_year3'].to_i).to eq(0) + expect(row['issuer_ial2_new_unique_user_events_year4'].to_i).to eq(1) + expect(row['issuer_ial2_new_unique_user_events_year5'].to_i).to eq(0) + expect(row['issuer_ial2_new_unique_user_events_year_greater_than_5'].to_i).to eq(0) + expect(row['issuer_ial2_new_unique_user_events_unknown'].to_i).to eq(0) expect(row['issuer_ial1_total_auth_count'].to_i).to eq(0) expect(row['issuer_ial2_total_auth_count'].to_i).to eq(4) @@ -548,20 +567,6 @@ expect(row['issuer_ial1_unique_users'].to_i).to eq(0) expect(row['issuer_ial2_unique_users'].to_i).to eq(2) expect(row['issuer_ial1_plus_2_unique_users'].to_i).to eq(2) - expect(row['issuer_ial2_unique_users_year1'].to_i).to eq(2) - expect(row['issuer_ial2_unique_users_year2'].to_i).to eq(1) - expect(row['issuer_ial2_unique_users_year3'].to_i).to eq(0) - expect(row['issuer_ial2_unique_users_year4'].to_i).to eq(1) - expect(row['issuer_ial2_unique_users_year5'].to_i).to eq(0) - expect(row['issuer_ial2_unique_users_year_greater_than_5'].to_i).to eq(0) - expect(row['issuer_ial2_unique_users_unknown'].to_i).to eq(0) - expect(row['issuer_ial2_new_unique_users_year1'].to_i).to eq(1) - expect(row['issuer_ial2_new_unique_users_year2'].to_i).to eq(1) - expect(row['issuer_ial2_new_unique_users_year3'].to_i).to eq(0) - expect(row['issuer_ial2_new_unique_users_year4'].to_i).to eq(1) - expect(row['issuer_ial2_new_unique_users_year5'].to_i).to eq(0) - expect(row['issuer_ial2_new_unique_users_year_greater_than_5'].to_i).to eq(0) - expect(row['issuer_ial2_new_unique_users_unknown'].to_i).to eq(0) end end end diff --git a/spec/services/db/monthly_sp_auth_count/new_unique_monthly_user_counts_by_partner_spec.rb b/spec/services/db/monthly_sp_auth_count/new_unique_monthly_user_counts_by_partner_spec.rb index e87a1ebec49..61f03c07580 100644 --- a/spec/services/db/monthly_sp_auth_count/new_unique_monthly_user_counts_by_partner_spec.rb +++ b/spec/services/db/monthly_sp_auth_count/new_unique_monthly_user_counts_by_partner_spec.rb @@ -259,21 +259,21 @@ iaa_start_date: partner_range.begin.to_s, iaa_end_date: partner_range.end.to_s, unique_user_proofed_events: 8, - partner_ial2_unique_users_year1: 2, - partner_ial2_unique_users_year2: 2, - partner_ial2_unique_users_year3: 1, - partner_ial2_unique_users_year4: 1, - partner_ial2_unique_users_year5: 2, - partner_ial2_unique_users_year_greater_than_5: 0, - partner_ial2_unique_users_unknown: 0, + partner_ial2_unique_user_events_year1: 2, + partner_ial2_unique_user_events_year2: 2, + partner_ial2_unique_user_events_year3: 1, + partner_ial2_unique_user_events_year4: 1, + partner_ial2_unique_user_events_year5: 2, + partner_ial2_unique_user_events_year_greater_than_5: 0, + partner_ial2_unique_user_events_unknown: 0, new_unique_user_proofed_events: 8, - partner_ial2_new_unique_users_year1: 2, - partner_ial2_new_unique_users_year2: 2, - partner_ial2_new_unique_users_year3: 1, - partner_ial2_new_unique_users_year4: 1, - partner_ial2_new_unique_users_year5: 2, - partner_ial2_new_unique_users_year_greater_than_5: 0, - partner_ial2_new_unique_users_unknown: 0, + partner_ial2_new_unique_user_events_year1: 2, + partner_ial2_new_unique_user_events_year2: 2, + partner_ial2_new_unique_user_events_year3: 1, + partner_ial2_new_unique_user_events_year4: 1, + partner_ial2_new_unique_user_events_year5: 2, + partner_ial2_new_unique_user_events_year_greater_than_5: 0, + partner_ial2_new_unique_user_events_unknown: 0, }, { partner: partner_key, @@ -282,21 +282,21 @@ iaa_start_date: partner_range.begin.to_s, iaa_end_date: partner_range.end.to_s, unique_user_proofed_events: 13, - partner_ial2_unique_users_year1: 4, - partner_ial2_unique_users_year2: 4, - partner_ial2_unique_users_year3: 1, - partner_ial2_unique_users_year4: 1, - partner_ial2_unique_users_year5: 0, - partner_ial2_unique_users_year_greater_than_5: 2, - partner_ial2_unique_users_unknown: 1, + partner_ial2_unique_user_events_year1: 4, + partner_ial2_unique_user_events_year2: 4, + partner_ial2_unique_user_events_year3: 1, + partner_ial2_unique_user_events_year4: 1, + partner_ial2_unique_user_events_year5: 0, + partner_ial2_unique_user_events_year_greater_than_5: 2, + partner_ial2_unique_user_events_unknown: 1, new_unique_user_proofed_events: 8, - partner_ial2_new_unique_users_year1: 3, - partner_ial2_new_unique_users_year2: 2, - partner_ial2_new_unique_users_year3: 0, - partner_ial2_new_unique_users_year4: 0, - partner_ial2_new_unique_users_year5: 0, - partner_ial2_new_unique_users_year_greater_than_5: 2, - partner_ial2_new_unique_users_unknown: 1, + partner_ial2_new_unique_user_events_year1: 3, + partner_ial2_new_unique_user_events_year2: 2, + partner_ial2_new_unique_user_events_year3: 0, + partner_ial2_new_unique_user_events_year4: 0, + partner_ial2_new_unique_user_events_year5: 0, + partner_ial2_new_unique_user_events_year_greater_than_5: 2, + partner_ial2_new_unique_user_events_unknown: 1, }, ] expect(results).to match_array(rows) diff --git a/spec/services/idv/agent_spec.rb b/spec/services/idv/agent_spec.rb index 2c042563c51..ed062547aba 100644 --- a/spec/services/idv/agent_spec.rb +++ b/spec/services/idv/agent_spec.rb @@ -33,7 +33,7 @@ context 'proofing state_id enabled' do it 'does not proof state_id if resolution fails' do agent = Idv::Agent.new( - Idp::Constants::MOCK_IDV_APPLICANT.merge(uuid: user.uuid, ssn: '444-55-6666'), + Idp::Constants::MOCK_IDV_APPLICANT.merge(ssn: '444-55-6666'), ) agent.proof_resolution( document_capture_session, @@ -51,7 +51,7 @@ end it 'does proof state_id if resolution succeeds' do - agent = Idv::Agent.new(Idp::Constants::MOCK_IDV_APPLICANT_WITH_SSN.merge(uuid: user.uuid)) + agent = Idv::Agent.new(Idp::Constants::MOCK_IDV_APPLICANT_WITH_SSN) agent.proof_resolution( document_capture_session, should_proof_state_id: true, @@ -76,7 +76,7 @@ context 'proofing state_id disabled' do it 'does not proof state_id if resolution fails' do agent = Idv::Agent.new( - Idp::Constants::MOCK_IDV_APPLICANT.merge(uuid: user.uuid, ssn: '444-55-6666'), + Idp::Constants::MOCK_IDV_APPLICANT.merge(ssn: '444-55-6666'), ) agent.proof_resolution( document_capture_session, @@ -93,7 +93,7 @@ end it 'does not proof state_id if resolution succeeds' do - agent = Idv::Agent.new(Idp::Constants::MOCK_IDV_APPLICANT_WITH_SSN.merge(uuid: user.uuid)) + agent = Idv::Agent.new(Idp::Constants::MOCK_IDV_APPLICANT_WITH_SSN) agent.proof_resolution( document_capture_session, should_proof_state_id: false, @@ -113,7 +113,7 @@ it 'returns a successful result if SSN does not start with 900 but is in SSN allowlist' do agent = Idv::Agent.new( - Idp::Constants::MOCK_IDV_APPLICANT.merge(uuid: user.uuid, ssn: '999-99-9999'), + Idp::Constants::MOCK_IDV_APPLICANT.merge(ssn: '999-99-9999'), ) agent.proof_resolution( @@ -137,7 +137,7 @@ issuer = 'https://rp1.serviceprovider.com/auth/saml/metadata' document_capture_session.update!(issuer: issuer) agent = Idv::Agent.new( - Idp::Constants::MOCK_IDV_APPLICANT.merge(uuid: user.uuid, ssn: '999-99-9999'), + Idp::Constants::MOCK_IDV_APPLICANT.merge(ssn: '999-99-9999'), ) expect(ResolutionProofingJob).to receive(:perform_later).with( @@ -159,10 +159,7 @@ it 'returns an unsuccessful result and notifies exception trackers if an exception occurs' do agent = Idv::Agent.new( - Idp::Constants::MOCK_IDV_APPLICANT_WITH_SSN.merge( - uuid: user.uuid, - first_name: 'Time Exception', - ), + Idp::Constants::MOCK_IDV_APPLICANT_WITH_SSN.merge(first_name: 'Time Exception'), ) agent.proof_resolution( @@ -187,8 +184,7 @@ let(:ipp_enrollment_in_progress) { true } it 'returns a successful result if resolution passes' do - addr = Idp::Constants::MOCK_IDV_APPLICANT_STATE_ID_ADDRESS - agent = Idv::Agent.new(addr.merge(uuid: user.uuid)) + agent = Idv::Agent.new(Idp::Constants::MOCK_IDV_APPLICANT_STATE_ID_ADDRESS) agent.proof_resolution( document_capture_session, should_proof_state_id: true, @@ -217,7 +213,6 @@ it 'proofs addresses successfully with valid information' do agent = Idv::Agent.new( - uuid: SecureRandom.uuid, first_name: 'Fakey', last_name: 'Fakersgerald', dob: 50.years.ago.to_date.to_s,