diff --git a/app/services/idv/analytics_events_enhancer.rb b/app/services/idv/analytics_events_enhancer.rb index 41c8c2994e9..8084338b99a 100644 --- a/app/services/idv/analytics_events_enhancer.rb +++ b/app/services/idv/analytics_events_enhancer.rb @@ -1,75 +1,53 @@ # frozen_string_literal: true module Idv + # For events beginning with +idv_+, add additional information to the event + # when a User object is available: + # - +proofing_components+: User's current proofing components + # - +active_profile_idv_level+: ID verification level of user's active profile. + # - +pending_profile_idv_level+: ID verification level of user's pending profile. + # Generally, analytics events that are called in contexts where there is no expectation + # of an {Idv::Session} being present or may be excessively noisy are opted-out. + # (e.g., jobs, client-generated events, action scripts). + # + # Additionally, +profile_history+, the list of a User's profiles + # (sorted by creation date, oldest to newest), may be added to events, but this is opt-in only. + # See #METHODS_WITH_PROFILE_HISTORY for the list of included events. module AnalyticsEventsEnhancer - IGNORED_METHODS = %i[ + EXCLUDED_FRONTEND_EVENT_METHODS = %i[ idv_acuant_sdk_loaded - idv_address_submitted - idv_address_visit - idv_back_image_added - idv_back_image_clicked idv_barcode_warning_continue_clicked idv_barcode_warning_retake_photos_clicked idv_capture_troubleshooting_dismissed idv_consent_checkbox_toggled - idv_doc_auth_agreement_submitted - idv_doc_auth_agreement_visited - idv_doc_auth_capture_complete_visited - idv_doc_auth_document_capture_submitted - idv_doc_auth_document_capture_visited - idv_doc_auth_exception_visited - idv_doc_auth_failed_image_resubmitted - idv_doc_auth_how_to_verify_submitted - idv_doc_auth_how_to_verify_visited - idv_doc_auth_hybrid_handoff_submitted - idv_doc_auth_hybrid_handoff_visited - idv_doc_auth_link_sent_submitted - idv_doc_auth_link_sent_visited - idv_doc_auth_redo_ssn_submitted - idv_doc_auth_socure_webhook_received - idv_doc_auth_ssn_submitted - idv_doc_auth_ssn_visited - idv_doc_auth_submitted_image_upload_form - idv_doc_auth_submitted_image_upload_vendor - idv_doc_auth_submitted_pii_validation - idv_doc_auth_verify_proofing_results - idv_doc_auth_verify_submitted - idv_doc_auth_verify_visited - idv_doc_auth_warning_visited - idv_doc_auth_welcome_submitted - idv_doc_auth_welcome_visited - idv_front_image_added - idv_front_image_clicked - idv_gpo_confirm_start_over_before_letter_visited - idv_gpo_confirm_start_over_visited + idv_image_capture_failed + idv_in_person_location_submitted + idv_in_person_ready_to_verify_sp_link_clicked + idv_in_person_ready_to_verify_what_to_bring_link_clicked + idv_sdk_error_before_init + idv_sdk_selfie_image_capture_closed_without_photo + idv_sdk_selfie_image_capture_failed + idv_sdk_selfie_image_capture_initialized + idv_sdk_selfie_image_capture_opened + idv_sdk_selfie_image_re_taken + idv_sdk_selfie_image_taken + idv_selfie_image_added + idv_verify_in_person_troubleshooting_option_clicked + ].freeze + + EXCLUDED_JOB_EVENT_METHODS = %i[ idv_gpo_expired idv_gpo_reminder_email_sent - idv_image_capture_failed idv_in_person_email_reminder_job_email_initiated idv_in_person_email_reminder_job_exception - idv_in_person_location_submitted - idv_in_person_location_visited - idv_in_person_locations_request_failure - idv_in_person_locations_searched - idv_in_person_prepare_submitted - idv_in_person_prepare_visited - idv_in_person_proofing_address_visited idv_in_person_proofing_enrollments_ready_for_status_check_job_completed idv_in_person_proofing_enrollments_ready_for_status_check_job_ingestion_error idv_in_person_proofing_enrollments_ready_for_status_check_job_started - idv_in_person_proofing_nontransliterable_characters_submitted - idv_in_person_proofing_residential_address_submitted - idv_in_person_proofing_state_id_submitted - idv_in_person_proofing_state_id_visited - idv_in_person_ready_to_verify_sp_link_clicked - idv_in_person_ready_to_verify_what_to_bring_link_clicked idv_in_person_send_proofing_notification_attempted idv_in_person_send_proofing_notification_job_completed idv_in_person_send_proofing_notification_job_exception idv_in_person_send_proofing_notification_job_skipped idv_in_person_send_proofing_notification_job_started - idv_in_person_switch_back_submitted - idv_in_person_switch_back_visited idv_in_person_usps_proofing_enrollment_code_email_received idv_in_person_usps_proofing_results_job_completed idv_in_person_usps_proofing_results_job_deadline_passed_email_exception @@ -82,32 +60,21 @@ module AnalyticsEventsEnhancer idv_in_person_usps_proofing_results_job_started idv_in_person_usps_proofing_results_job_unexpected_response idv_in_person_usps_proofing_results_job_user_sent_to_fraud_review - idv_in_person_usps_request_enroll_exception idv_ipp_deactivated_for_never_visiting_post_office - idv_link_sent_capture_doc_polling_complete - idv_link_sent_capture_doc_polling_started - idv_mail_only_warning_visited - idv_native_camera_forced - idv_not_verified_visited - idv_phone_use_different - idv_request_letter_visited - idv_sdk_selfie_image_capture_closed_without_photo - idv_sdk_selfie_image_capture_failed - idv_sdk_selfie_image_capture_opened - idv_selfie_image_added - idv_session_error_visited - idv_socure_document_request_submitted + idv_socure_reason_code_download + idv_socure_shadow_mode_proofing_result + idv_socure_shadow_mode_proofing_result_missing idv_socure_verification_data_requested - idv_threatmetrix_response_body idv_usps_auth_token_refresh_job_completed idv_usps_auth_token_refresh_job_network_error idv_usps_auth_token_refresh_job_started - idv_verify_by_mail_enter_code_submitted - idv_verify_by_mail_enter_code_visited - idv_verify_in_person_troubleshooting_option_clicked - idv_warning_action_triggered - idv_warning_shown - ].to_set.freeze + ].freeze + + IGNORED_METHODS = [ + *EXCLUDED_FRONTEND_EVENT_METHODS, + *EXCLUDED_JOB_EVENT_METHODS, + :idv_threatmetrix_response_body, # Prevent duplication when doing joins across events + ].uniq.freeze STANDARD_ARGUMENTS = %i[ proofing_components @@ -121,7 +88,7 @@ module AnalyticsEventsEnhancer idv_final idv_please_call_visited idv_start_over - ].to_set.freeze + ].freeze def self.included(_mod) raise 'this mixin is intended to be prepended, not included' diff --git a/lib/analytics_events_documenter.rb b/lib/analytics_events_documenter.rb index 60161c85626..6200eab1216 100644 --- a/lib/analytics_events_documenter.rb +++ b/lib/analytics_events_documenter.rb @@ -15,6 +15,10 @@ class AnalyticsEventsDocumenter DOCUMENTATION_OPTIONAL_PARAMS = %w[ pii_like_keypaths + active_profile_idv_level + pending_profile_idv_level + proofing_components + profile_history ].freeze attr_reader :database_path, :class_name diff --git a/spec/features/idv/analytics_spec.rb b/spec/features/idv/analytics_spec.rb index c74a2975ad8..cc498b88dce 100644 --- a/spec/features/idv/analytics_spec.rb +++ b/spec/features/idv/analytics_spec.rb @@ -10,6 +10,7 @@ let(:proofing_device_profiling) { :enabled } let(:threatmetrix) { true } let(:idv_level) { 'in_person' } + let(:threatmetrix_response_body) do { account_lex_id: 'super-cool-test-lex-id', @@ -24,6 +25,7 @@ tmx_summary_reason_code: ['Identity_Negative_History'], } end + let(:threatmetrix_response) do { client: nil, @@ -37,6 +39,7 @@ transaction_id: 'ddp-mock-transaction-id-123', } end + let(:base_proofing_components) do { document_check: 'mock', @@ -47,9 +50,11 @@ 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 @@ -216,19 +221,24 @@ success: true, errors: {}, user_id: user.uuid, submit_attempts: 1, remaining_submit_attempts: 3, flow_path: 'standard', attention_with_barcode: false, front_image_fingerprint: an_instance_of(String), back_image_fingerprint: an_instance_of(String), liveness_checking_required: boolean, classification_info: {}, id_issued_status: 'present', id_expiration_status: 'present' }, 'IdV: doc auth document_capture submitted' => { - success: true, errors: {}, flow_path: 'standard', step: 'document_capture', analytics_id: 'Doc Auth', selfie_check_required: boolean, liveness_checking_required: boolean + success: true, errors: {}, flow_path: 'standard', step: 'document_capture', analytics_id: 'Doc Auth', selfie_check_required: boolean, liveness_checking_required: boolean, + proofing_components: { document_check: 'mock', document_type: 'state_id' } }, 'IdV: doc auth ssn visited' => { - flow_path: 'standard', step: 'ssn', analytics_id: 'Doc Auth' + flow_path: 'standard', step: 'ssn', analytics_id: 'Doc Auth', + proofing_components: { document_check: 'mock', document_type: 'state_id' } }, 'IdV: doc auth ssn submitted' => { - success: true, errors: {}, flow_path: 'standard', step: 'ssn', analytics_id: 'Doc Auth' + success: true, errors: {}, flow_path: 'standard', step: 'ssn', analytics_id: 'Doc Auth', + proofing_components: { document_check: 'mock', document_type: 'state_id' } }, 'IdV: doc auth verify visited' => { - flow_path: 'standard', step: 'verify', analytics_id: 'Doc Auth' + flow_path: 'standard', step: 'verify', analytics_id: 'Doc Auth', + proofing_components: { document_check: 'mock', document_type: 'state_id' } }, 'IdV: doc auth verify submitted' => { - flow_path: 'standard', step: 'verify', analytics_id: 'Doc Auth' + flow_path: 'standard', step: 'verify', analytics_id: 'Doc Auth', + proofing_components: { document_check: 'mock', document_type: 'state_id' } }, idv_threatmetrix_response_body: ( if threatmetrix_response_body.present? @@ -237,10 +247,10 @@ ), 'IdV: doc auth verify proofing results' => { success: true, errors: {}, flow_path: 'standard', address_edited: false, address_line2_present: false, analytics_id: 'Doc Auth', step: 'verify', - proofing_results: doc_auth_verify_proofing_results + proofing_results: doc_auth_verify_proofing_results, + proofing_components: base_proofing_components }, 'IdV: phone of record visited' => { - proofing_components: base_proofing_components, }, 'IdV: phone confirmation form' => { @@ -295,6 +305,7 @@ }.compact end + # TODO: Add ["IdV: doc auth link_sent visited", "IdV: doc auth capture_complete visited", "IdV: doc auth link_sent submitted"] let(:happy_hybrid_path_events) do { 'IdV: intro visited' => {}, @@ -339,16 +350,20 @@ success: true, errors: {}, flow_path: 'hybrid', step: 'document_capture', analytics_id: 'Doc Auth', selfie_check_required: boolean, liveness_checking_required: boolean }, 'IdV: doc auth ssn visited' => { - flow_path: 'hybrid', step: 'ssn', analytics_id: 'Doc Auth' + flow_path: 'hybrid', step: 'ssn', analytics_id: 'Doc Auth', + proofing_components: { document_check: 'mock', document_type: 'state_id' } }, 'IdV: doc auth ssn submitted' => { - success: true, errors: {}, flow_path: 'hybrid', step: 'ssn', analytics_id: 'Doc Auth' + success: true, errors: {}, flow_path: 'hybrid', step: 'ssn', analytics_id: 'Doc Auth', + proofing_components: { document_check: 'mock', document_type: 'state_id' } }, 'IdV: doc auth verify visited' => { - flow_path: 'hybrid', step: 'verify', analytics_id: 'Doc Auth' + flow_path: 'hybrid', step: 'verify', analytics_id: 'Doc Auth', + proofing_components: { document_check: 'mock', document_type: 'state_id' } }, 'IdV: doc auth verify submitted' => { - flow_path: 'hybrid', step: 'verify', analytics_id: 'Doc Auth' + flow_path: 'hybrid', step: 'verify', analytics_id: 'Doc Auth', + proofing_components: { document_check: 'mock', document_type: 'state_id' } }, idv_threatmetrix_response_body: ( if threatmetrix_response_body.present? @@ -357,10 +372,10 @@ ), 'IdV: doc auth verify proofing results' => { success: true, errors: {}, flow_path: 'hybrid', address_edited: false, address_line2_present: false, analytics_id: 'Doc Auth', step: 'verify', - proofing_results: doc_auth_verify_proofing_results + proofing_results: doc_auth_verify_proofing_results, + proofing_components: base_proofing_components }, 'IdV: phone of record visited' => { - proofing_components: base_proofing_components, }, 'IdV: phone confirmation form' => { @@ -415,6 +430,7 @@ }.compact end + # TODO: Add ["IdV: consent checkbox toggled"] let(:gpo_path_events) do { 'IdV: intro visited' => {}, @@ -453,19 +469,24 @@ success: true, errors: {}, user_id: user.uuid, submit_attempts: 1, remaining_submit_attempts: 3, flow_path: 'standard', attention_with_barcode: false, front_image_fingerprint: an_instance_of(String), back_image_fingerprint: an_instance_of(String), liveness_checking_required: boolean, classification_info: {}, id_issued_status: 'present', id_expiration_status: 'present' }, 'IdV: doc auth document_capture submitted' => { - success: true, errors: {}, flow_path: 'standard', step: 'document_capture', analytics_id: 'Doc Auth', selfie_check_required: boolean, liveness_checking_required: boolean + success: true, errors: {}, flow_path: 'standard', step: 'document_capture', analytics_id: 'Doc Auth', selfie_check_required: boolean, liveness_checking_required: boolean, + proofing_components: { document_check: 'mock', document_type: 'state_id' } }, 'IdV: doc auth ssn visited' => { - flow_path: 'standard', step: 'ssn', analytics_id: 'Doc Auth' + flow_path: 'standard', step: 'ssn', analytics_id: 'Doc Auth', + proofing_components: { document_check: 'mock', document_type: 'state_id' } }, 'IdV: doc auth ssn submitted' => { - success: true, errors: {}, flow_path: 'standard', step: 'ssn', analytics_id: 'Doc Auth' + success: true, errors: {}, flow_path: 'standard', step: 'ssn', analytics_id: 'Doc Auth', + proofing_components: { document_check: 'mock', document_type: 'state_id' } }, 'IdV: doc auth verify visited' => { - flow_path: 'standard', step: 'verify', analytics_id: 'Doc Auth' + flow_path: 'standard', step: 'verify', analytics_id: 'Doc Auth', + proofing_components: { document_check: 'mock', document_type: 'state_id' } }, 'IdV: doc auth verify submitted' => { - flow_path: 'standard', step: 'verify', analytics_id: 'Doc Auth' + flow_path: 'standard', step: 'verify', analytics_id: 'Doc Auth', + proofing_components: { document_check: 'mock', document_type: 'state_id' } }, idv_threatmetrix_response_body: ( if threatmetrix_response_body.present? @@ -474,7 +495,8 @@ ), 'IdV: doc auth verify proofing results' => { success: true, errors: {}, flow_path: 'standard', address_edited: false, address_line2_present: false, analytics_id: 'Doc Auth', step: 'verify', - proofing_results: doc_auth_verify_proofing_results + proofing_results: doc_auth_verify_proofing_results, + proofing_components: base_proofing_components }, 'IdV: phone of record visited' => { proofing_components: base_proofing_components, @@ -483,7 +505,9 @@ resend: false, phone_step_attempts: 0, hours_since_first_letter: 0, proofing_components: base_proofing_components }, - 'IdV: request letter visited' => {}, + 'IdV: request letter visited' => { + proofing_components: base_proofing_components, + }, :idv_enter_password_visited => { address_verification_method: 'gpo', proofing_components: gpo_letter_proofing_components, @@ -509,6 +533,7 @@ }.compact end + # TODO: Add ["IdV: consent checkbox toggled", "IdV: doc auth image upload vendor pii validation", "IdV: in person proofing location search submitted", "IdV: phone of record visited"] let(:in_person_path_events) do { 'IdV: doc auth welcome visited' => { @@ -558,28 +583,28 @@ flow_path: 'standard', opted_in_to_in_person_proofing: false }, 'IdV: in person proofing state_id visited' => { - step: 'state_id', flow_path: 'standard', analytics_id: 'In Person Proofing' + step: 'state_id', flow_path: 'standard', analytics_id: 'In Person Proofing', proofing_components: { document_check: 'usps' } }, 'IdV: in person proofing state_id submitted' => { - success: true, flow_path: 'standard', step: 'state_id', analytics_id: 'In Person Proofing', errors: {}, birth_year: '1938', document_zip_code: '12345' + success: true, flow_path: 'standard', step: 'state_id', analytics_id: 'In Person Proofing', errors: {}, birth_year: '1938', document_zip_code: '12345', proofing_components: { document_check: 'usps' } }, 'IdV: in person proofing address visited' => { - step: 'address', flow_path: 'standard', analytics_id: 'In Person Proofing' + step: 'address', flow_path: 'standard', analytics_id: 'In Person Proofing', proofing_components: { document_check: 'usps' } }, 'IdV: in person proofing residential address submitted' => { - success: true, step: 'address', flow_path: 'standard', analytics_id: 'In Person Proofing', errors: {}, current_address_zip_code: '59010' + success: true, step: 'address', flow_path: 'standard', analytics_id: 'In Person Proofing', errors: {}, current_address_zip_code: '59010', proofing_components: { document_check: 'usps' } }, 'IdV: doc auth ssn visited' => { - analytics_id: 'In Person Proofing', step: 'ssn', flow_path: 'standard' + analytics_id: 'In Person Proofing', step: 'ssn', flow_path: 'standard', proofing_components: { document_check: 'usps' } }, 'IdV: doc auth ssn submitted' => { - analytics_id: 'In Person Proofing', success: true, step: 'ssn', flow_path: 'standard', errors: {} + analytics_id: 'In Person Proofing', success: true, step: 'ssn', flow_path: 'standard', errors: {}, proofing_components: { document_check: 'usps' } }, 'IdV: doc auth verify visited' => { - analytics_id: 'In Person Proofing', step: 'verify', flow_path: 'standard' + analytics_id: 'In Person Proofing', step: 'verify', flow_path: 'standard', proofing_components: { document_check: 'usps' } }, 'IdV: doc auth verify submitted' => { - analytics_id: 'In Person Proofing', step: 'verify', flow_path: 'standard' + analytics_id: 'In Person Proofing', step: 'verify', flow_path: 'standard', proofing_components: { document_check: 'usps' } }, idv_threatmetrix_response_body: ( if threatmetrix_response_body.present? @@ -588,7 +613,8 @@ ), 'IdV: doc auth verify proofing results' => { success: true, errors: {}, flow_path: 'standard', address_edited: false, address_line2_present: false, analytics_id: 'In Person Proofing', step: 'verify', - proofing_results: in_person_path_proofing_results + proofing_results: in_person_path_proofing_results, + proofing_components: { document_check: 'usps', resolution_check: 'lexis_nexis', source_check: 'StateIdMock', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass' } }, 'IdV: phone confirmation form' => { success: true, errors: {}, phone_type: :mobile, types: [:fixed_or_mobile], carrier: 'Test Mobile Carrier', country_code: 'US', area_code: '202', otp_delivery_preference: 'sms', @@ -687,22 +713,27 @@ success: true, errors: {}, user_id: user.uuid, submit_attempts: 1, remaining_submit_attempts: 3, flow_path: 'standard', attention_with_barcode: false, front_image_fingerprint: an_instance_of(String), back_image_fingerprint: an_instance_of(String), selfie_image_fingerprint: an_instance_of(String), liveness_checking_required: boolean, classification_info: {}, id_issued_status: 'present', id_expiration_status: 'present' }, 'IdV: doc auth document_capture submitted' => { - success: true, errors: {}, flow_path: 'standard', step: 'document_capture', analytics_id: 'Doc Auth', selfie_check_required: boolean, liveness_checking_required: true + success: true, errors: {}, flow_path: 'standard', step: 'document_capture', analytics_id: 'Doc Auth', selfie_check_required: boolean, liveness_checking_required: true, + proofing_components: { document_check: 'mock', document_type: 'state_id' } }, :idv_selfie_image_added => { acuant_version: kind_of(String), captureAttempts: 1, fingerprint: 'aIzxkX_iMtoxFOURZr55qkshs53emQKUOr7VfTf6G1Q', flow_path: 'standard', height: 38, mimeType: 'image/png', size: 3694, source: 'upload', width: 284, liveness_checking_required: boolean, selfie_attempts: 0 }, 'IdV: doc auth ssn visited' => { - flow_path: 'standard', step: 'ssn', analytics_id: 'Doc Auth' + flow_path: 'standard', step: 'ssn', analytics_id: 'Doc Auth', + proofing_components: { document_check: 'mock', document_type: 'state_id' } }, 'IdV: doc auth ssn submitted' => { - success: true, errors: {}, flow_path: 'standard', step: 'ssn', analytics_id: 'Doc Auth' + success: true, errors: {}, flow_path: 'standard', step: 'ssn', analytics_id: 'Doc Auth', + proofing_components: { document_check: 'mock', document_type: 'state_id' } }, 'IdV: doc auth verify visited' => { - flow_path: 'standard', step: 'verify', analytics_id: 'Doc Auth' + flow_path: 'standard', step: 'verify', analytics_id: 'Doc Auth', + proofing_components: { document_check: 'mock', document_type: 'state_id' } }, 'IdV: doc auth verify submitted' => { - flow_path: 'standard', step: 'verify', analytics_id: 'Doc Auth' + flow_path: 'standard', step: 'verify', analytics_id: 'Doc Auth', + proofing_components: { document_check: 'mock', document_type: 'state_id' } }, idv_threatmetrix_response_body: ( if threatmetrix_response_body.present? @@ -711,10 +742,10 @@ ), 'IdV: doc auth verify proofing results' => { success: true, errors: {}, flow_path: 'standard', address_edited: false, address_line2_present: false, analytics_id: 'Doc Auth', step: 'verify', - proofing_results: doc_auth_verify_proofing_results + proofing_results: doc_auth_verify_proofing_results, + proofing_components: base_proofing_components }, 'IdV: phone of record visited' => { - proofing_components: base_proofing_components, }, 'IdV: phone confirmation form' => { @@ -826,6 +857,7 @@ let(:proofing_device_profiling) { :disabled } let(:threatmetrix) { false } let(:threatmetrix_response_body) { nil } + let(:threatmetrix_response) do { client: 'tmx_disabled', @@ -906,6 +938,7 @@ let(:proofing_device_profiling) { :disabled } let(:threatmetrix) { false } let(:threatmetrix_response_body) { nil } + let(:threatmetrix_response) do { client: 'tmx_disabled', @@ -932,6 +965,7 @@ context 'GPO path' do before do + fake_analytics.events.clear sign_in_and_2fa_user(user) visit_idp_from_sp_with_ial2(:oidc) complete_welcome_step @@ -946,8 +980,10 @@ end it 'records all of the events' do - gpo_path_events.each do |event, attributes| - expect(fake_analytics).to have_logged_event(event, attributes) + aggregate_failures 'analytics_events' do + gpo_path_events.each do |event, attributes| + expect(fake_analytics).to have_logged_event(event, attributes) + end end end @@ -955,6 +991,7 @@ let(:proofing_device_profiling) { :disabled } let(:threatmetrix) { false } let(:threatmetrix_response_body) { nil } + let(:threatmetrix_response) do { client: 'tmx_disabled', @@ -970,8 +1007,10 @@ end it 'records all of the events' do - gpo_path_events.each do |event, attributes| - expect(fake_analytics).to have_logged_event(event, attributes) + aggregate_failures 'analytics events' do + gpo_path_events.each do |event, attributes| + expect(fake_analytics).to have_logged_event(event, attributes) + end end end end @@ -1018,6 +1057,7 @@ let(:proofing_device_profiling) { :disabled } let(:threatmetrix) { false } let(:threatmetrix_response_body) { nil } + let(:threatmetrix_response) do { client: 'tmx_disabled', @@ -1077,6 +1117,7 @@ let(:proofing_device_profiling) { :disabled } let(:threatmetrix) { false } let(:threatmetrix_response_body) { nil } + let(:threatmetrix_response) do { client: 'tmx_disabled', @@ -1158,6 +1199,7 @@ let(:proofing_device_profiling) { :disabled } let(:threatmetrix) { false } let(:threatmetrix_response_body) { nil } + let(:threatmetrix_response) do { client: 'tmx_disabled', @@ -1221,6 +1263,7 @@ let(:idv_level) { 'legacy_in_person' } let(:threatmetrix) { false } let(:threatmetrix_response_body) { nil } + let(:threatmetrix_response) do { client: 'tmx_disabled', diff --git a/spec/services/idv/analytics_events_enhancer_spec.rb b/spec/services/idv/analytics_events_enhancer_spec.rb index a553f623b4e..8cd8994461c 100644 --- a/spec/services/idv/analytics_events_enhancer_spec.rb +++ b/spec/services/idv/analytics_events_enhancer_spec.rb @@ -211,4 +211,33 @@ def track_event(_event, **kwargs) end end end + + describe 'valid configuration' do + let(:explicitly_ignored_methods) do + described_class.const_get(:IGNORED_METHODS).sort + end + + let(:explicitly_enhanced_methods) do + described_class.const_get(:METHODS_WITH_PROFILE_HISTORY).sort + end + + let(:explicitly_referenced_methods) do + [*explicitly_ignored_methods, *explicitly_enhanced_methods].sort + end + + let(:idv_event_methods) do + AnalyticsEvents.instance_methods(false) + .filter { |n| n.start_with?('idv_') } + .sort + end + + it 'only references known AnalyticsEvents methods' do + found_methods = (idv_event_methods & explicitly_referenced_methods).sort + expect(found_methods).to eq(explicitly_referenced_methods) + end + + it 'does not both ignore and enhance the same method' do + expect(explicitly_ignored_methods).to_not include(*explicitly_enhanced_methods) + end + end end diff --git a/spec/support/fake_analytics.rb b/spec/support/fake_analytics.rb index ce3ee4b6170..0ec98262613 100644 --- a/spec/support/fake_analytics.rb +++ b/spec/support/fake_analytics.rb @@ -1,3 +1,5 @@ +require 'analytics_events_documenter' + class FakeAnalytics < Analytics PiiDetected = Class.new(StandardError).freeze @@ -73,6 +75,10 @@ def track_event(event, original_attributes = {}) module UndocumentedParamsChecker mattr_accessor :asts mattr_accessor :docstrings + DOCUMENTATION_OPTIONAL_PARAMS = [ + :user_id, + *AnalyticsEventsDocumenter::DOCUMENTATION_OPTIONAL_PARAMS.map(&:to_sym), + ].uniq.freeze def track_event(event, original_attributes = {}) method_name = caller @@ -91,9 +97,9 @@ def track_event(event, original_attributes = {}) .map(&:last) extra_keywords = original_attributes.keys \ - - [:pii_like_keypaths, :user_id] \ - - param_names \ - - option_param_names(analytics_method) + - DOCUMENTATION_OPTIONAL_PARAMS \ + - param_names \ + - option_param_names(analytics_method) if extra_keywords.present? raise UndocumentedParams, <<~ERROR