diff --git a/spec/controllers/account_reset/cancel_controller_spec.rb b/spec/controllers/account_reset/cancel_controller_spec.rb index 100d1c1e899..8d4030d9422 100644 --- a/spec/controllers/account_reset/cancel_controller_spec.rb +++ b/spec/controllers/account_reset/cancel_controller_spec.rb @@ -9,51 +9,49 @@ it 'logs a good token to the analytics' do token = create_account_reset_request_for(user) session[:cancel_token] = token - stub_analytics - analytics_hash = { + + post :create + + expect(@analytics).to have_logged_event( + 'Account Reset: cancel', success: true, errors: {}, user_id: user.uuid, message_id: 'fake-message-id', request_id: 'fake-message-request-id', - } - - post :create - - expect(@analytics).to have_logged_event('Account Reset: cancel', analytics_hash) + ) end it 'logs a bad token to the analytics' do stub_analytics - analytics_hash = { + session[:cancel_token] = 'FOO' + + post :create + + expect(@analytics).to have_logged_event( + 'Account Reset: cancel', success: false, errors: { token: [t('errors.account_reset.cancel_token_invalid', app_name: APP_NAME)] }, error_details: { token: { cancel_token_invalid: true }, }, user_id: 'anonymous-uuid', - } - - session[:cancel_token] = 'FOO' - - post :create - - expect(@analytics).to have_logged_event('Account Reset: cancel', analytics_hash) + ) end it 'logs a missing token to the analytics' do stub_analytics - analytics_hash = { + + post :create + + expect(@analytics).to have_logged_event( + 'Account Reset: cancel', success: false, errors: { token: [t('errors.account_reset.cancel_token_missing', app_name: APP_NAME)] }, error_details: { token: { blank: true } }, user_id: 'anonymous-uuid', - } - - post :create - - expect(@analytics).to have_logged_event('Account Reset: cancel', analytics_hash) + ) end it 'redirects to the root without a flash message when the token is missing or invalid' do @@ -87,18 +85,18 @@ describe '#show' do it 'redirects to root if the token does not match one in the DB' do stub_analytics - properties = { + + get :show, params: { token: 'FOO' } + + expect(@analytics).to have_logged_event( + 'Account Reset: cancel token validation', user_id: 'anonymous-uuid', success: false, errors: { token: [t('errors.account_reset.cancel_token_invalid', app_name: APP_NAME)] }, error_details: { token: { cancel_token_invalid: true }, }, - } - - get :show, params: { token: 'FOO' } - - expect(@analytics).to have_logged_event('Account Reset: cancel token validation', properties) + ) expect(response).to redirect_to(root_url) expect(flash[:error]).to eq t('errors.account_reset.cancel_token_invalid', app_name: APP_NAME) end diff --git a/spec/controllers/account_reset/delete_account_controller_spec.rb b/spec/controllers/account_reset/delete_account_controller_spec.rb index 18883e53b82..e838933b9ce 100644 --- a/spec/controllers/account_reset/delete_account_controller_spec.rb +++ b/spec/controllers/account_reset/delete_account_controller_spec.rb @@ -16,9 +16,12 @@ create_list(:webauthn_configuration, 2, user: user) create_account_reset_request_for(user) grant_request(user) - session[:granted_token] = AccountResetRequest.first.granted_token - properties = { + + delete :delete + + expect(@analytics).to have_logged_event( + 'Account Reset: delete', user_id: user.uuid, success: true, errors: {}, @@ -29,17 +32,17 @@ }, account_age_in_days: 0, account_confirmed_at: user.confirmed_at, - } - - delete :delete - - expect(@analytics).to have_logged_event('Account Reset: delete', properties) + ) expect(response).to redirect_to account_reset_confirm_delete_account_url end it 'redirects to root if the token does not match one in the DB' do session[:granted_token] = 'foo' - properties = { + + delete :delete + + expect(@analytics).to have_logged_event( + 'Account Reset: delete', user_id: 'anonymous-uuid', success: false, errors: invalid_token_error, @@ -47,17 +50,16 @@ mfa_method_counts: {}, account_age_in_days: 0, account_confirmed_at: kind_of(Time), - } - - delete :delete - - expect(@analytics).to have_logged_event('Account Reset: delete', properties) + ) expect(response).to redirect_to(root_url) expect(flash[:error]).to eq(invalid_token_message) end it 'displays a flash and redirects to root if the token is missing' do - properties = { + delete :delete + + expect(@analytics).to have_logged_event( + 'Account Reset: delete', user_id: 'anonymous-uuid', success: false, errors: { token: [t('errors.account_reset.granted_token_missing', app_name: APP_NAME)] }, @@ -65,11 +67,7 @@ mfa_method_counts: {}, account_age_in_days: 0, account_confirmed_at: kind_of(Time), - } - - delete :delete - - expect(@analytics).to have_logged_event('Account Reset: delete', properties) + ) expect(response).to redirect_to(root_url) expect(flash[:error]).to eq t( 'errors.account_reset.granted_token_missing', @@ -82,7 +80,13 @@ create_account_reset_request_for(user) grant_request(user) - properties = { + travel_to(Time.zone.now + 2.days) do + session[:granted_token] = AccountResetRequest.first.granted_token + delete :delete + end + + expect(@analytics).to have_logged_event( + 'Account Reset: delete', user_id: user.uuid, success: false, errors: { token: [t('errors.account_reset.granted_token_expired', app_name: APP_NAME)] }, @@ -90,14 +94,7 @@ mfa_method_counts: {}, account_age_in_days: 2, account_confirmed_at: kind_of(Time), - } - - travel_to(Time.zone.now + 2.days) do - session[:granted_token] = AccountResetRequest.first.granted_token - delete :delete - end - - expect(@analytics).to have_logged_event('Account Reset: delete', properties) + ) expect(response).to redirect_to(root_url) expect(flash[:error]).to eq( t('errors.account_reset.granted_token_expired', app_name: APP_NAME), @@ -107,16 +104,15 @@ describe '#show' do it 'redirects to root if the token does not match one in the DB' do - properties = { + get :show, params: { token: 'FOO' } + + expect(@analytics).to have_logged_event( + 'Account Reset: granted token validation', user_id: 'anonymous-uuid', success: false, errors: invalid_token_error, error_details: { token: { granted_token_invalid: true } }, - } - - get :show, params: { token: 'FOO' } - - expect(@analytics).to have_logged_event('Account Reset: granted token validation', properties) + ) expect(response).to redirect_to(root_url) expect(flash[:error]).to eq(invalid_token_message) end @@ -126,18 +122,17 @@ create_account_reset_request_for(user) grant_request(user) - properties = { - user_id: user.uuid, - success: false, - errors: { token: [t('errors.account_reset.granted_token_expired', app_name: APP_NAME)] }, - error_details: { token: { granted_token_expired: true } }, - } - travel_to(Time.zone.now + 2.days) do get :show, params: { token: AccountResetRequest.first.granted_token } end - expect(@analytics).to have_logged_event('Account Reset: granted token validation', properties) + expect(@analytics).to have_logged_event( + 'Account Reset: granted token validation', + user_id: user.uuid, + success: false, + errors: { token: [t('errors.account_reset.granted_token_expired', app_name: APP_NAME)] }, + error_details: { token: { granted_token_expired: true } }, + ) expect(response).to redirect_to(root_url) expect(flash[:error]).to eq( t('errors.account_reset.granted_token_expired', app_name: APP_NAME), diff --git a/spec/controllers/account_reset/request_controller_spec.rb b/spec/controllers/account_reset/request_controller_spec.rb index dc1b79e5d44..a55fcb3ba4d 100644 --- a/spec/controllers/account_reset/request_controller_spec.rb +++ b/spec/controllers/account_reset/request_controller_spec.rb @@ -96,28 +96,30 @@ describe '#create' do it 'logs totp user in the analytics' do stub_sign_in_before_2fa(user) - stub_analytics - attributes = { + + post :create + + expect(@analytics).to have_logged_event( + 'Account Reset: request', success: true, sms_phone: false, totp: true, piv_cac: false, email_addresses: 1, errors: {}, - } - - post :create - - expect(@analytics).to have_logged_event('Account Reset: request', attributes) + ) end it 'logs sms user in the analytics' do user = create(:user, :fully_registered) stub_sign_in_before_2fa(user) - stub_analytics - attributes = { + + post :create + + expect(@analytics).to have_logged_event( + 'Account Reset: request', success: true, sms_phone: true, totp: false, @@ -126,30 +128,25 @@ request_id: 'fake-message-request-id', message_id: 'fake-message-id', errors: {}, - } - - post :create - - expect(@analytics).to have_logged_event('Account Reset: request', attributes) + ) end it 'logs PIV/CAC user in the analytics' do user = create(:user, :with_piv_or_cac, :with_backup_code) stub_sign_in_before_2fa(user) - stub_analytics - attributes = { + + post :create + + expect(@analytics).to have_logged_event( + 'Account Reset: request', success: true, sms_phone: false, totp: false, piv_cac: true, email_addresses: 1, errors: {}, - } - - post :create - - expect(@analytics).to have_logged_event('Account Reset: request', attributes) + ) end it 'redirects to root if user not signed in' do diff --git a/spec/controllers/accounts/personal_keys_controller_spec.rb b/spec/controllers/accounts/personal_keys_controller_spec.rb index ca0a4a9343e..0c678e9ec69 100644 --- a/spec/controllers/accounts/personal_keys_controller_spec.rb +++ b/spec/controllers/accounts/personal_keys_controller_spec.rb @@ -47,15 +47,15 @@ it 'tracks CSRF errors' do stub_sign_in stub_analytics - analytics_hash = { - controller: 'accounts/personal_keys#create', - user_signed_in: true, - } allow(controller).to receive(:create).and_raise(ActionController::InvalidAuthenticityToken) post :create - expect(@analytics).to have_logged_event('Invalid Authenticity Token', analytics_hash) + expect(@analytics).to have_logged_event( + 'Invalid Authenticity Token', + controller: 'accounts/personal_keys#create', + user_signed_in: true, + ) expect(response).to redirect_to new_user_session_url expect(flash[:error]).to eq t('errors.general') end diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index 47d25168277..be7775ef85d 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -90,13 +90,15 @@ def index it 'tracks the InvalidAuthenticityToken event and does not sign the user out' do sign_in_as_user expect(subject.current_user).to be_present - stub_analytics - event_properties = { controller: 'anonymous#index', user_signed_in: true } get :index - expect(@analytics).to have_logged_event('Invalid Authenticity Token', event_properties) + expect(@analytics).to have_logged_event( + 'Invalid Authenticity Token', + controller: 'anonymous#index', + user_signed_in: true, + ) expect(flash[:error]).to eq t('errors.general') expect(response).to redirect_to(root_url) expect(subject.current_user).to be_present @@ -145,13 +147,16 @@ def index request.env['HTTP_REFERER'] = referer sign_in_as_user expect(subject.current_user).to be_present - stub_analytics - event_properties = { controller: 'anonymous#index', user_signed_in: true, referer: referer } get :index - expect(@analytics).to have_logged_event('Unsafe Redirect', event_properties) + expect(@analytics).to have_logged_event( + 'Unsafe Redirect', + controller: 'anonymous#index', + user_signed_in: true, + referer:, + ) expect(flash[:error]).to eq t('errors.general') expect(response).to redirect_to(root_url) expect(subject.current_user).to be_present diff --git a/spec/controllers/idv/otp_verification_controller_spec.rb b/spec/controllers/idv/otp_verification_controller_spec.rb index a18879a1988..54b335e922a 100644 --- a/spec/controllers/idv/otp_verification_controller_spec.rb +++ b/spec/controllers/idv/otp_verification_controller_spec.rb @@ -160,19 +160,17 @@ it 'tracks an analytics event' do put :update, params: otp_code_param - expected_result = { - success: true, - errors: {}, - code_expired: false, - code_matches: true, - otp_delivery_preference: :sms, - second_factor_attempts_count: 0, - **ab_test_args, - } - expect(@analytics).to have_logged_event( 'IdV: phone confirmation otp submitted', - hash_including(expected_result), + hash_including( + success: true, + errors: {}, + code_expired: false, + code_matches: true, + otp_delivery_preference: :sms, + second_factor_attempts_count: 0, + **ab_test_args, + ), ) end end diff --git a/spec/controllers/idv/phone_controller_spec.rb b/spec/controllers/idv/phone_controller_spec.rb index a8975ddfa36..c34bfa95667 100644 --- a/spec/controllers/idv/phone_controller_spec.rb +++ b/spec/controllers/idv/phone_controller_spec.rb @@ -293,26 +293,24 @@ put :create, params: improbable_phone_form - result = { - success: false, - errors: { - phone: [improbable_phone_message], - otp_delivery_preference: [improbable_otp_message], - }, - error_details: { - phone: { improbable_phone: true }, - otp_delivery_preference: { inclusion: true }, - }, - carrier: 'Test Mobile Carrier', - phone_type: :mobile, - otp_delivery_preference: '🎷', - types: [], - **ab_test_args, - } - expect(@analytics).to have_logged_event( 'IdV: phone confirmation form', - hash_including(result), + hash_including( + success: false, + errors: { + phone: [improbable_phone_message], + otp_delivery_preference: [improbable_otp_message], + }, + error_details: { + phone: { improbable_phone: true }, + otp_delivery_preference: { inclusion: true }, + }, + carrier: 'Test Mobile Carrier', + phone_type: :mobile, + otp_delivery_preference: '🎷', + types: [], + **ab_test_args, + ), ) expect(subject.idv_session.vendor_phone_confirmation).to be_falsy @@ -342,21 +340,19 @@ it 'tracks events with valid phone' do put :create, params: phone_params - result = { - success: true, - errors: {}, - area_code: '703', - country_code: 'US', - carrier: 'Test Mobile Carrier', - phone_type: :mobile, - otp_delivery_preference: 'sms', - types: [:fixed_or_mobile], - **ab_test_args, - } - expect(@analytics).to have_logged_event( 'IdV: phone confirmation form', - hash_including(result), + hash_including( + success: true, + errors: {}, + area_code: '703', + country_code: 'US', + carrier: 'Test Mobile Carrier', + phone_type: :mobile, + otp_delivery_preference: 'sms', + types: [:fixed_or_mobile], + **ab_test_args, + ), ) end @@ -436,23 +432,6 @@ it 'tracks event with valid phone' do proofing_phone = Phonelib.parse(good_phone) - result = { - success: true, - new_phone_added: true, - hybrid_handoff_phone_used: false, - errors: {}, - phone_fingerprint: Pii::Fingerprinter.fingerprint(proofing_phone.e164), - country_code: proofing_phone.country, - area_code: proofing_phone.area_code, - vendor: { - vendor_name: 'AddressMock', - exception: nil, - timed_out: false, - transaction_id: 'address-mock-transaction-id-123', - reference: '', - }, - } - put :create, params: { idv_phone_form: { phone: good_phone } } expect(@analytics).to have_logged_event( @@ -466,7 +445,22 @@ expect(@analytics).to have_logged_event( 'IdV: phone confirmation vendor', - hash_including(result), + hash_including( + success: true, + new_phone_added: true, + hybrid_handoff_phone_used: false, + errors: {}, + phone_fingerprint: Pii::Fingerprinter.fingerprint(proofing_phone.e164), + country_code: proofing_phone.country, + area_code: proofing_phone.area_code, + vendor: { + vendor_name: 'AddressMock', + exception: nil, + timed_out: false, + transaction_id: 'address-mock-transaction-id-123', + reference: '', + }, + ), ) end end @@ -518,25 +512,6 @@ it 'tracks event with invalid phone' do proofing_phone = Phonelib.parse(bad_phone) - result = { - success: false, - new_phone_added: true, - hybrid_handoff_phone_used: false, - phone_fingerprint: Pii::Fingerprinter.fingerprint(proofing_phone.e164), - country_code: proofing_phone.country, - area_code: proofing_phone.area_code, - errors: { - phone: ['The phone number could not be verified.'], - }, - vendor: { - vendor_name: 'AddressMock', - exception: nil, - timed_out: false, - transaction_id: 'address-mock-transaction-id-123', - reference: '', - }, - } - put :create, params: { idv_phone_form: { phone: bad_phone } } expect(@analytics).to have_logged_event( @@ -550,7 +525,24 @@ expect(@analytics).to have_logged_event( 'IdV: phone confirmation vendor', - hash_including(result), + hash_including( + success: false, + new_phone_added: true, + hybrid_handoff_phone_used: false, + phone_fingerprint: Pii::Fingerprinter.fingerprint(proofing_phone.e164), + country_code: proofing_phone.country, + area_code: proofing_phone.area_code, + errors: { + phone: ['The phone number could not be verified.'], + }, + vendor: { + vendor_name: 'AddressMock', + exception: nil, + timed_out: false, + transaction_id: 'address-mock-transaction-id-123', + reference: '', + }, + ), ) end diff --git a/spec/controllers/idv/resend_otp_controller_spec.rb b/spec/controllers/idv/resend_otp_controller_spec.rb index fd937efe100..71c12351174 100644 --- a/spec/controllers/idv/resend_otp_controller_spec.rb +++ b/spec/controllers/idv/resend_otp_controller_spec.rb @@ -52,32 +52,22 @@ it 'tracks an analytics event' do post :create - expected_result = { - success: true, - phone_fingerprint: Pii::Fingerprinter.fingerprint(Phonelib.parse(phone).e164), - errors: {}, - otp_delivery_preference: :sms, - country_code: 'US', - area_code: '225', - rate_limit_exceeded: false, - telephony_response: instance_of(Telephony::Response), - } - expect(@analytics).to have_logged_event( 'IdV: phone confirmation otp resent', - hash_including(expected_result), + hash_including( + success: true, + phone_fingerprint: Pii::Fingerprinter.fingerprint(Phonelib.parse(phone).e164), + errors: {}, + otp_delivery_preference: :sms, + country_code: 'US', + area_code: '225', + rate_limit_exceeded: false, + telephony_response: instance_of(Telephony::Response), + ), ) end context 'Telephony raises an exception' do - let(:telephony_error_analytics_hash) do - { - error: 'Telephony::TelephonyError', - message: 'error message', - context: 'idv', - country: 'US', - } - end let(:telephony_error) do Telephony::TelephonyError.new('error message') end @@ -104,7 +94,11 @@ ), ) expect(@analytics).to have_logged_event( - 'Vendor Phone Validation failed', telephony_error_analytics_hash + 'Vendor Phone Validation failed', + error: 'Telephony::TelephonyError', + message: 'error message', + context: 'idv', + country: 'US', ) end end diff --git a/spec/controllers/saml_idp_controller_spec.rb b/spec/controllers/saml_idp_controller_spec.rb index f74107a4c00..20886440bdf 100644 --- a/spec/controllers/saml_idp_controller_spec.rb +++ b/spec/controllers/saml_idp_controller_spec.rb @@ -19,8 +19,10 @@ delete :logout, params: { path_year: path_year } - result = { sp_initiated: false, oidc: false, saml_request_valid: true } - expect(@analytics).to have_logged_event('Logout Initiated', hash_including(result)) + expect(@analytics).to have_logged_event( + 'Logout Initiated', + hash_including(sp_initiated: false, oidc: false, saml_request_valid: true), + ) end it 'tracks the event when sp initiated' do @@ -29,8 +31,10 @@ delete :logout, params: { SAMLRequest: 'foo', path_year: path_year } - result = { sp_initiated: true, oidc: false, saml_request_valid: true } - expect(@analytics).to have_logged_event('Logout Initiated', hash_including(result)) + expect(@analytics).to have_logged_event( + 'Logout Initiated', + hash_including(sp_initiated: true, oidc: false, saml_request_valid: true), + ) end it 'tracks the event when the saml request is invalid' do @@ -38,8 +42,10 @@ delete :logout, params: { SAMLRequest: 'foo', path_year: path_year } - result = { sp_initiated: true, oidc: false, saml_request_valid: false } - expect(@analytics).to have_logged_event('Logout Initiated', hash_including(result)) + expect(@analytics).to have_logged_event( + 'Logout Initiated', + hash_including(sp_initiated: true, oidc: false, saml_request_valid: false), + ) end let(:service_provider) do @@ -176,8 +182,7 @@ post :remotelogout, params: { SAMLRequest: 'foo', path_year: path_year } - result = { saml_request_valid: false } - expect(@analytics).to have_logged_event('Remote Logout initiated', result) + expect(@analytics).to have_logged_event('Remote Logout initiated', saml_request_valid: false) end let(:agency) { create(:agency) } @@ -977,24 +982,24 @@ def name_id_version(format_urn) expect(controller).to render_template('saml_idp/auth/error') expect(response.status).to eq(400) expect(response.body).to include(t('errors.messages.unauthorized_authn_context')) - - analytics_hash = { - success: false, - errors: { authn_context: [t('errors.messages.unauthorized_authn_context')] }, - error_details: { authn_context: { unauthorized_authn_context: true } }, - nameid_format: Saml::Idp::Constants::NAME_ID_FORMAT_PERSISTENT, - authn_context: ['http://idmanagement.gov/ns/assurance/loa/5'], - authn_context_comparison: 'exact', - service_provider: 'http://localhost:3000', - request_signed: true, - requested_ial: 'http://idmanagement.gov/ns/assurance/loa/5', - endpoint: "/api/saml/auth#{path_year}", - idv: false, - finish_profile: false, - matching_cert_serial: saml_test_sp_cert_serial, - } - - expect(@analytics).to have_logged_event('SAML Auth', hash_including(analytics_hash)) + expect(@analytics).to have_logged_event( + 'SAML Auth', + hash_including( + success: false, + errors: { authn_context: [t('errors.messages.unauthorized_authn_context')] }, + error_details: { authn_context: { unauthorized_authn_context: true } }, + nameid_format: Saml::Idp::Constants::NAME_ID_FORMAT_PERSISTENT, + authn_context: ['http://idmanagement.gov/ns/assurance/loa/5'], + authn_context_comparison: 'exact', + service_provider: 'http://localhost:3000', + request_signed: true, + requested_ial: 'http://idmanagement.gov/ns/assurance/loa/5', + endpoint: "/api/saml/auth#{path_year}", + idv: false, + finish_profile: false, + matching_cert_serial: saml_test_sp_cert_serial, + ), + ) end end @@ -1202,22 +1207,22 @@ def name_id_version(format_urn) expect(controller).to render_template('saml_idp/auth/error') expect(response.status).to eq(400) expect(response.body).to include(t('errors.messages.unauthorized_service_provider')) - - analytics_hash = { - success: false, - errors: { service_provider: [t('errors.messages.unauthorized_service_provider')] }, - error_details: { service_provider: { unauthorized_service_provider: true } }, - nameid_format: Saml::Idp::Constants::NAME_ID_FORMAT_PERSISTENT, - authn_context: request_authn_contexts, - authn_context_comparison: 'exact', - request_signed: true, - requested_ial: Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, - endpoint: "/api/saml/auth#{path_year}", - idv: false, - finish_profile: false, - } - - expect(@analytics).to have_logged_event('SAML Auth', hash_including(analytics_hash)) + expect(@analytics).to have_logged_event( + 'SAML Auth', + hash_including( + success: false, + errors: { service_provider: [t('errors.messages.unauthorized_service_provider')] }, + error_details: { service_provider: { unauthorized_service_provider: true } }, + nameid_format: Saml::Idp::Constants::NAME_ID_FORMAT_PERSISTENT, + authn_context: request_authn_contexts, + authn_context_comparison: 'exact', + request_signed: true, + requested_ial: Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, + endpoint: "/api/saml/auth#{path_year}", + idv: false, + finish_profile: false, + ), + ) end end @@ -1241,28 +1246,28 @@ def name_id_version(format_urn) expect(response.status).to eq(400) expect(response.body).to include(t('errors.messages.unauthorized_authn_context')) expect(response.body).to include(t('errors.messages.unauthorized_service_provider')) - - analytics_hash = { - success: false, - errors: { - service_provider: [t('errors.messages.unauthorized_service_provider')], - authn_context: [t('errors.messages.unauthorized_authn_context')], - }, - error_details: { - authn_context: { unauthorized_authn_context: true }, - service_provider: { unauthorized_service_provider: true }, - }, - nameid_format: Saml::Idp::Constants::NAME_ID_FORMAT_PERSISTENT, - authn_context: ['http://idmanagement.gov/ns/assurance/loa/5'], - authn_context_comparison: 'exact', - request_signed: true, - requested_ial: 'http://idmanagement.gov/ns/assurance/loa/5', - endpoint: "/api/saml/auth#{path_year}", - idv: false, - finish_profile: false, - } - - expect(@analytics).to have_logged_event('SAML Auth', hash_including(analytics_hash)) + expect(@analytics).to have_logged_event( + 'SAML Auth', + hash_including( + success: false, + errors: { + service_provider: [t('errors.messages.unauthorized_service_provider')], + authn_context: [t('errors.messages.unauthorized_authn_context')], + }, + error_details: { + authn_context: { unauthorized_authn_context: true }, + service_provider: { unauthorized_service_provider: true }, + }, + nameid_format: Saml::Idp::Constants::NAME_ID_FORMAT_PERSISTENT, + authn_context: ['http://idmanagement.gov/ns/assurance/loa/5'], + authn_context_comparison: 'exact', + request_signed: true, + requested_ial: 'http://idmanagement.gov/ns/assurance/loa/5', + endpoint: "/api/saml/auth#{path_year}", + idv: false, + finish_profile: false, + ), + ) end end @@ -1599,22 +1604,24 @@ def name_id_version(format_urn) it 'notes it in the analytics event' do generate_saml_response(user, saml_settings) - analytics_hash = { - success: false, - errors: { service_provider: ['We cannot detect a certificate in your request.'] }, - error_details: { service_provider: { blank_cert_element_req: true } }, - nameid_format: Saml::Idp::Constants::NAME_ID_FORMAT_PERSISTENT, - authn_context: [Saml::Idp::Constants::DEFAULT_AAL_AUTHN_CONTEXT_CLASSREF], - authn_context_comparison: 'exact', - service_provider: 'http://localhost:3000', - request_signed: true, - requested_ial: 'none', - endpoint: "/api/saml/auth#{path_year}", - idv: false, - finish_profile: false, - } - - expect(@analytics).to have_logged_event('SAML Auth', hash_including(analytics_hash)) + + expect(@analytics).to have_logged_event( + 'SAML Auth', + hash_including( + success: false, + errors: { service_provider: ['We cannot detect a certificate in your request.'] }, + error_details: { service_provider: { blank_cert_element_req: true } }, + nameid_format: Saml::Idp::Constants::NAME_ID_FORMAT_PERSISTENT, + authn_context: [Saml::Idp::Constants::DEFAULT_AAL_AUTHN_CONTEXT_CLASSREF], + authn_context_comparison: 'exact', + service_provider: 'http://localhost:3000', + request_signed: true, + requested_ial: 'none', + endpoint: "/api/saml/auth#{path_year}", + idv: false, + finish_profile: false, + ), + ) end it 'returns a 400' do @@ -1645,22 +1652,23 @@ def name_id_version(format_urn) expect(response.status).to eq(200) - analytics_hash = { - success: true, - errors: {}, - nameid_format: Saml::Idp::Constants::NAME_ID_FORMAT_PERSISTENT, - authn_context: [Saml::Idp::Constants::DEFAULT_AAL_AUTHN_CONTEXT_CLASSREF], - authn_context_comparison: 'exact', - requested_ial: 'none', - service_provider: 'http://localhost:3000', - endpoint: "/api/saml/auth#{path_year}", - idv: false, - finish_profile: false, - request_signed: true, - matching_cert_serial: saml_test_sp_cert_serial, - } - - expect(@analytics).to have_logged_event('SAML Auth', hash_including(analytics_hash)) + expect(@analytics).to have_logged_event( + 'SAML Auth', + hash_including( + success: true, + errors: {}, + nameid_format: Saml::Idp::Constants::NAME_ID_FORMAT_PERSISTENT, + authn_context: [Saml::Idp::Constants::DEFAULT_AAL_AUTHN_CONTEXT_CLASSREF], + authn_context_comparison: 'exact', + requested_ial: 'none', + service_provider: 'http://localhost:3000', + endpoint: "/api/saml/auth#{path_year}", + idv: false, + finish_profile: false, + request_signed: true, + matching_cert_serial: saml_test_sp_cert_serial, + ), + ) end end @@ -2304,23 +2312,6 @@ def name_id_version(format_urn) and_return(SecureRandom.uuid) stub_requested_attributes - analytics_hash = { - success: true, - errors: {}, - nameid_format: Saml::Idp::Constants::NAME_ID_FORMAT_PERSISTENT, - authn_context: [ - Saml::Idp::Constants::AAL2_AUTHN_CONTEXT_CLASSREF, - Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF, - ], - authn_context_comparison: 'exact', - requested_ial: Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF, - service_provider: 'http://localhost:3000', - endpoint: "/api/saml/auth#{path_year}", - idv: true, - finish_profile: false, - request_signed: false, - } - get :auth, params: { path_year: path_year } expect(@analytics).to have_logged_event( @@ -2336,7 +2327,25 @@ def name_id_version(format_urn) user_fully_authenticated: true, } ) - expect(@analytics).to have_logged_event('SAML Auth', hash_including(analytics_hash)) + expect(@analytics).to have_logged_event( + 'SAML Auth', + hash_including( + success: true, + errors: {}, + nameid_format: Saml::Idp::Constants::NAME_ID_FORMAT_PERSISTENT, + authn_context: [ + Saml::Idp::Constants::AAL2_AUTHN_CONTEXT_CLASSREF, + Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF, + ], + authn_context_comparison: 'exact', + requested_ial: Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF, + service_provider: 'http://localhost:3000', + endpoint: "/api/saml/auth#{path_year}", + idv: true, + finish_profile: false, + request_signed: false, + ), + ) end end @@ -2353,26 +2362,10 @@ def stub_requested_attributes context 'user is not redirected to IdV' do it 'tracks the authentication without IdV redirection event' do user = create(:user, :fully_registered) - stub_analytics session[:sign_in_flow] = :sign_in allow(controller).to receive(:identity_needs_verification?).and_return(false) - analytics_hash = { - success: true, - errors: {}, - nameid_format: Saml::Idp::Constants::NAME_ID_FORMAT_PERSISTENT, - authn_context: request_authn_contexts, - authn_context_comparison: 'exact', - requested_ial: Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, - service_provider: 'http://localhost:3000', - endpoint: "/api/saml/auth#{path_year}", - idv: false, - finish_profile: false, - request_signed: true, - matching_cert_serial: saml_test_sp_cert_serial, - } - generate_saml_response(user) expect(@analytics).to have_logged_event( @@ -2387,7 +2380,20 @@ def stub_requested_attributes ) expect(@analytics).to have_logged_event( 'SAML Auth', - hash_including(analytics_hash), + hash_including( + success: true, + errors: {}, + nameid_format: Saml::Idp::Constants::NAME_ID_FORMAT_PERSISTENT, + authn_context: request_authn_contexts, + authn_context_comparison: 'exact', + requested_ial: Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, + service_provider: 'http://localhost:3000', + endpoint: "/api/saml/auth#{path_year}", + idv: false, + finish_profile: false, + request_signed: true, + matching_cert_serial: saml_test_sp_cert_serial, + ), ) expect(@analytics).to have_logged_event( 'SP redirect initiated', @@ -2405,27 +2411,11 @@ def stub_requested_attributes context 'user has not finished verifying profile' do it 'tracks the authentication with finish_profile==true' do user = create(:user, :fully_registered) - stub_analytics session[:sign_in_flow] = :sign_in allow(controller).to receive(:identity_needs_verification?).and_return(false) allow(controller).to receive(:user_has_pending_profile?).and_return(true) - analytics_hash = { - success: true, - errors: {}, - nameid_format: Saml::Idp::Constants::NAME_ID_FORMAT_PERSISTENT, - authn_context: request_authn_contexts, - authn_context_comparison: 'exact', - requested_ial: Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, - service_provider: 'http://localhost:3000', - endpoint: "/api/saml/auth#{path_year}", - idv: false, - finish_profile: true, - request_signed: true, - matching_cert_serial: saml_test_sp_cert_serial, - } - generate_saml_response(user) expect(@analytics).to have_logged_event( @@ -2440,7 +2430,20 @@ def stub_requested_attributes ) expect(@analytics).to have_logged_event( 'SAML Auth', - hash_including(analytics_hash), + hash_including( + success: true, + errors: {}, + nameid_format: Saml::Idp::Constants::NAME_ID_FORMAT_PERSISTENT, + authn_context: request_authn_contexts, + authn_context_comparison: 'exact', + requested_ial: Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, + service_provider: 'http://localhost:3000', + endpoint: "/api/saml/auth#{path_year}", + idv: false, + finish_profile: true, + request_signed: true, + matching_cert_serial: saml_test_sp_cert_serial, + ), ) expect(@analytics).to have_logged_event( 'SP redirect initiated', diff --git a/spec/controllers/sign_up/cancellations_controller_spec.rb b/spec/controllers/sign_up/cancellations_controller_spec.rb index bd756793e80..25c02fcfd5e 100644 --- a/spec/controllers/sign_up/cancellations_controller_spec.rb +++ b/spec/controllers/sign_up/cancellations_controller_spec.rb @@ -5,12 +5,12 @@ it 'tracks the event in analytics when referer is nil' do stub_sign_in stub_analytics - properties = { request_came_from: 'no referer' } get :new expect(@analytics).to have_logged_event( - 'User registration: cancellation visited', properties + 'User registration: cancellation visited', + request_came_from: 'no referer', ) end @@ -18,12 +18,12 @@ stub_sign_in stub_analytics request.env['HTTP_REFERER'] = 'http://example.com/' - properties = { request_came_from: 'users/sessions#new' } get :new expect(@analytics).to have_logged_event( - 'User registration: cancellation visited', properties + 'User registration: cancellation visited', + request_came_from: 'users/sessions#new', ) end end @@ -108,11 +108,13 @@ user = create(:user) stub_sign_in_before_2fa(user) stub_analytics - properties = { request_came_from: 'no referer' } delete :destroy - expect(@analytics).to have_logged_event('Account Deletion Requested', properties) + expect(@analytics).to have_logged_event( + 'Account Deletion Requested', + request_came_from: 'no referer', + ) end it 'tracks the event in analytics when referer is present' do @@ -120,11 +122,13 @@ stub_sign_in_before_2fa(user) stub_analytics request.env['HTTP_REFERER'] = 'http://example.com/' - properties = { request_came_from: 'users/sessions#new' } delete :destroy - expect(@analytics).to have_logged_event('Account Deletion Requested', properties) + expect(@analytics).to have_logged_event( + 'Account Deletion Requested', + request_came_from: 'users/sessions#new', + ) end it 'calls ParseControllerFromReferer' do diff --git a/spec/controllers/sign_up/email_confirmations_controller_spec.rb b/spec/controllers/sign_up/email_confirmations_controller_spec.rb index e8deadb0252..c34979b5665 100644 --- a/spec/controllers/sign_up/email_confirmations_controller_spec.rb +++ b/spec/controllers/sign_up/email_confirmations_controller_spec.rb @@ -61,17 +61,13 @@ it 'tracks already confirmed token' do email_address = create(:email_address, confirmation_token: 'foo') - analytics_hash = { - success: false, - errors: { email: [t('errors.messages.already_confirmed')] }, - user_id: email_address.user.uuid, - } - get :create, params: { confirmation_token: 'foo' } expect(@analytics).to have_logged_event( 'User Registration: Email Confirmation', - analytics_hash, + success: false, + errors: { email: [t('errors.messages.already_confirmed')] }, + user_id: email_address.user.uuid, ) end @@ -86,18 +82,14 @@ user: build(:user, email: nil), ) - analytics_hash = { - success: false, - errors: { confirmation_token: [t('errors.messages.expired')] }, - error_details: { confirmation_token: { expired: true } }, - user_id: email_address.user.uuid, - } - get :create, params: { confirmation_token: 'foo' } expect(@analytics).to have_logged_event( 'User Registration: Email Confirmation', - analytics_hash, + success: false, + errors: { confirmation_token: [t('errors.messages.expired')] }, + error_details: { confirmation_token: { expired: true } }, + user_id: email_address.user.uuid, ) expect(flash[:error]).to eq t('errors.messages.confirmation_period_expired') expect(response).to redirect_to sign_up_register_url @@ -113,18 +105,14 @@ ) user = email_address.user - analytics_hash = { - success: false, - errors: { confirmation_token: [t('errors.messages.expired')] }, - error_details: { confirmation_token: { expired: true } }, - user_id: user.uuid, - } - get :create, params: { confirmation_token: 'foo' } expect(@analytics).to have_logged_event( 'User Registration: Email Confirmation', - analytics_hash, + success: false, + errors: { confirmation_token: [t('errors.messages.expired')] }, + error_details: { confirmation_token: { expired: true } }, + user_id: user.uuid, ) expect(flash[:error]).to eq t('errors.messages.confirmation_period_expired') expect(response).to redirect_to sign_up_register_url @@ -179,20 +167,15 @@ user: build(:user, email: nil), ) user = email_address.user - stub_analytics - analytics_hash = { - success: true, - errors: {}, - user_id: user.uuid, - } - get :create, params: { confirmation_token: 'foo' } expect(@analytics).to have_logged_event( 'User Registration: Email Confirmation', - analytics_hash, + success: true, + errors: {}, + user_id: user.uuid, ) end end diff --git a/spec/controllers/sign_up/passwords_controller_spec.rb b/spec/controllers/sign_up/passwords_controller_spec.rb index d118b1fc11a..bf20d2ce5eb 100644 --- a/spec/controllers/sign_up/passwords_controller_spec.rb +++ b/spec/controllers/sign_up/passwords_controller_spec.rb @@ -20,13 +20,6 @@ context 'with valid password' do let!(:user) { create(:user, :unconfirmed, confirmation_token: token) } - let(:analytics_hash) do - { - success: true, - errors: {}, - user_id: user.uuid, - } - end before do stub_analytics @@ -37,11 +30,16 @@ expect(@analytics).to have_logged_event( 'User Registration: Email Confirmation', - analytics_hash, + success: true, + errors: {}, + user_id: user.uuid, ) expect(@analytics).to have_logged_event( 'Password Creation', - analytics_hash.merge({ request_id_present: false }), + success: true, + errors: {}, + user_id: user.uuid, + request_id_present: false, ) end diff --git a/spec/controllers/two_factor_authentication/backup_code_verification_controller_spec.rb b/spec/controllers/two_factor_authentication/backup_code_verification_controller_spec.rb index 1d8fe72e365..de8667bcfcd 100644 --- a/spec/controllers/two_factor_authentication/backup_code_verification_controller_spec.rb +++ b/spec/controllers/two_factor_authentication/backup_code_verification_controller_spec.rb @@ -11,12 +11,12 @@ it 'tracks the page visit' do stub_sign_in_before_2fa(user) stub_analytics - analytics_hash = { context: 'authentication' } get :show expect(@analytics).to have_logged_event( - 'Multi-Factor Authentication: enter backup code visited', analytics_hash + 'Multi-Factor Authentication: enter backup code visited', + context: 'authentication', ) end end diff --git a/spec/controllers/two_factor_authentication/otp_verification_controller_spec.rb b/spec/controllers/two_factor_authentication/otp_verification_controller_spec.rb index 2b44ce2fa31..cf02c712fd8 100644 --- a/spec/controllers/two_factor_authentication/otp_verification_controller_spec.rb +++ b/spec/controllers/two_factor_authentication/otp_verification_controller_spec.rb @@ -48,9 +48,12 @@ stub_sign_in_before_2fa(user) parsed_phone = Phonelib.parse(subject.current_user.default_phone_configuration.phone) subject.user_session[:mfa_selections] = ['sms'] - stub_analytics - analytics_hash = { + + get :show, params: { otp_delivery_preference: 'sms' } + + expect(@analytics).to have_logged_event( + 'Multi-Factor Authentication: enter OTP visited', context: 'authentication', multi_factor_auth_method: 'sms', confirmation_for_add_phone: false, @@ -60,13 +63,6 @@ phone_fingerprint: Pii::Fingerprinter.fingerprint(parsed_phone.e164), enabled_mfa_methods_count: 1, in_account_creation_flow: false, - } - - get :show, params: { otp_delivery_preference: 'sms' } - - expect(@analytics).to have_logged_event( - 'Multi-Factor Authentication: enter OTP visited', - analytics_hash, ) end diff --git a/spec/controllers/two_factor_authentication/personal_key_verification_controller_spec.rb b/spec/controllers/two_factor_authentication/personal_key_verification_controller_spec.rb index 6f932eab621..076d8fb73f9 100644 --- a/spec/controllers/two_factor_authentication/personal_key_verification_controller_spec.rb +++ b/spec/controllers/two_factor_authentication/personal_key_verification_controller_spec.rb @@ -19,12 +19,12 @@ user = build(:user, :with_personal_key, password: ControllerHelper::VALID_PASSWORD) stub_sign_in_before_2fa(user) stub_analytics - analytics_hash = { context: 'authentication' } get :show expect(@analytics).to have_logged_event( - 'Multi-Factor Authentication: enter personal key visited', analytics_hash + 'Multi-Factor Authentication: enter personal key visited', + context: 'authentication', ) end diff --git a/spec/controllers/two_factor_authentication/webauthn_verification_controller_spec.rb b/spec/controllers/two_factor_authentication/webauthn_verification_controller_spec.rb index 1fd64925b50..d3532f82289 100644 --- a/spec/controllers/two_factor_authentication/webauthn_verification_controller_spec.rb +++ b/spec/controllers/two_factor_authentication/webauthn_verification_controller_spec.rb @@ -45,13 +45,11 @@ it 'tracks an analytics event' do get :show, params: { platform: true } - result = { - context: 'authentication', - multi_factor_auth_method: 'webauthn_platform', - } + expect(@analytics).to have_logged_event( 'Multi-Factor Authentication: enter webAuthn authentication visited', - result, + context: 'authentication', + multi_factor_auth_method: 'webauthn_platform', ) end diff --git a/spec/controllers/users/edit_phone_controller_spec.rb b/spec/controllers/users/edit_phone_controller_spec.rb index 89434f408f6..b2e6695c91e 100644 --- a/spec/controllers/users/edit_phone_controller_spec.rb +++ b/spec/controllers/users/edit_phone_controller_spec.rb @@ -12,20 +12,20 @@ context 'when the user submits a valid otp delivery preference' do it 'updates the phone configuration and redirects' do stub_analytics - attributes = { - success: true, - errors: {}, - delivery_preference: 'voice', - make_default_number: true, - phone_configuration_id: phone_configuration.id, - } put :update, params: { id: phone_configuration.id, edit_phone_form: { delivery_preference: 'voice' }, } - expect(@analytics).to have_logged_event('Phone Number Change: Form submitted', attributes) + expect(@analytics).to have_logged_event( + 'Phone Number Change: Form submitted', + success: true, + errors: {}, + delivery_preference: 'voice', + make_default_number: true, + phone_configuration_id: phone_configuration.id, + ) expect(response).to redirect_to(account_url) expect(phone_configuration.reload.delivery_preference).to eq('voice') end @@ -34,21 +34,21 @@ context 'when the user submits an invalid delivery preference' do it 'renders the edit screen' do stub_analytics - attributes = { - success: false, - errors: hash_including(:delivery_preference), - error_details: { delivery_preference: { inclusion: true } }, - delivery_preference: 'noise', - make_default_number: true, - phone_configuration_id: phone_configuration.id, - } put :update, params: { id: phone_configuration.id, edit_phone_form: { delivery_preference: 'noise' }, } - expect(@analytics).to have_logged_event('Phone Number Change: Form submitted', attributes) + expect(@analytics).to have_logged_event( + 'Phone Number Change: Form submitted', + success: false, + errors: hash_including(:delivery_preference), + error_details: { delivery_preference: { inclusion: true } }, + delivery_preference: 'noise', + make_default_number: true, + phone_configuration_id: phone_configuration.id, + ) expect(response).to render_template(:edit) expect(phone_configuration.reload.delivery_preference).to eq('sms') end @@ -63,16 +63,15 @@ stub_sign_in(user.reload) stub_analytics - attributes = { - success: true, - phone_configuration_id: phone_configuration.id, - } - expect(PushNotification::HttpPush).to receive(:deliver). with(PushNotification::RecoveryInformationChangedEvent.new(user: user)) delete :destroy, params: { id: phone_configuration.id } - expect(@analytics).to have_logged_event('Phone Number Deletion: Submitted', attributes) + expect(@analytics).to have_logged_event( + 'Phone Number Deletion: Submitted', + success: true, + phone_configuration_id: phone_configuration.id, + ) expect(response).to redirect_to(account_url) expect(flash[:success]).to eq(t('two_factor_authentication.phone.delete.success')) expect(PhoneConfiguration.find_by(id: phone_configuration.id)).to eq(nil) diff --git a/spec/controllers/users/personal_keys_controller_spec.rb b/spec/controllers/users/personal_keys_controller_spec.rb index 6551e3c06ef..2a406eaf597 100644 --- a/spec/controllers/users/personal_keys_controller_spec.rb +++ b/spec/controllers/users/personal_keys_controller_spec.rb @@ -26,21 +26,19 @@ stub_sign_in controller.user_session[:personal_key] = 'foo' stub_analytics - analytics_hash = { personal_key_present: true } get :show - expect(@analytics).to have_logged_event('Personal key viewed', analytics_hash) + expect(@analytics).to have_logged_event('Personal key viewed', personal_key_present: true) end it 'tracks the page visit when there is no personal key in the user session' do stub_sign_in stub_analytics - analytics_hash = { personal_key_present: false } get :show - expect(@analytics).to have_logged_event('Personal key viewed', analytics_hash) + expect(@analytics).to have_logged_event('Personal key viewed', personal_key_present: false) end it 'does not generate a new personal key to avoid CSRF attacks' do @@ -116,15 +114,15 @@ it 'tracks CSRF errors' do stub_sign_in stub_analytics - analytics_hash = { - controller: 'users/personal_keys#update', - user_signed_in: true, - } allow(controller).to receive(:update).and_raise(ActionController::InvalidAuthenticityToken) post :update - expect(@analytics).to have_logged_event('Invalid Authenticity Token', analytics_hash) + expect(@analytics).to have_logged_event( + 'Invalid Authenticity Token', + controller: 'users/personal_keys#update', + user_signed_in: true, + ) expect(response).to redirect_to new_user_session_url expect(flash[:error]).to eq t('errors.general') end diff --git a/spec/controllers/users/phone_setup_controller_spec.rb b/spec/controllers/users/phone_setup_controller_spec.rb index 7564e9e131f..a60983492ca 100644 --- a/spec/controllers/users/phone_setup_controller_spec.rb +++ b/spec/controllers/users/phone_setup_controller_spec.rb @@ -59,9 +59,17 @@ it 'tracks an event when the number is invalid' do sign_in(user) - stub_analytics - result = { + + post :create, params: { + new_phone_form: { + phone: '703-555-010', + international_code: 'US', + }, + } + + expect(@analytics).to have_logged_event( + 'Multi-Factor Authentication: phone setup', success: false, errors: { phone: [ @@ -79,16 +87,7 @@ carrier: 'Test Mobile Carrier', phone_type: :mobile, types: [], - } - - post :create, params: { - new_phone_form: { - phone: '703-555-010', - international_code: 'US', - }, - } - - expect(@analytics).to have_logged_event('Multi-Factor Authentication: phone setup', result) + ) expect(response).to render_template(:index) expect(flash[:error]).to be_blank end @@ -142,18 +141,7 @@ it 'prompts to confirm the number' do sign_in(user) - stub_analytics - result = { - success: true, - errors: {}, - otp_delivery_preference: 'voice', - area_code: '703', - carrier: 'Test Mobile Carrier', - country_code: 'US', - phone_type: :mobile, - types: [:fixed_or_mobile], - } post( :create, @@ -163,14 +151,23 @@ }, ) - expect(@analytics).to have_logged_event('Multi-Factor Authentication: phone setup', result) + expect(@analytics).to have_logged_event( + 'Multi-Factor Authentication: phone setup', + success: true, + errors: {}, + otp_delivery_preference: 'voice', + area_code: '703', + carrier: 'Test Mobile Carrier', + country_code: 'US', + phone_type: :mobile, + types: [:fixed_or_mobile], + ) expect(response).to redirect_to( otp_send_path( otp_delivery_selection_form: { otp_delivery_preference: 'voice', otp_make_default_number: false }, ), ) - expect(subject.user_session[:context]).to eq 'confirmation' end end @@ -178,10 +175,18 @@ context 'with SMS' do it 'prompts to confirm the number' do sign_in(user) - stub_analytics - result = { + post( + :create, + params: { + new_phone_form: { phone: '703-555-0100', + international_code: 'US' }, + }, + ) + + expect(@analytics).to have_logged_event( + 'Multi-Factor Authentication: phone setup', success: true, errors: {}, otp_delivery_preference: 'sms', @@ -190,24 +195,13 @@ country_code: 'US', phone_type: :mobile, types: [:fixed_or_mobile], - } - - post( - :create, - params: { - new_phone_form: { phone: '703-555-0100', - international_code: 'US' }, - }, ) - - expect(@analytics).to have_logged_event('Multi-Factor Authentication: phone setup', result) expect(response).to redirect_to( otp_send_path( otp_delivery_selection_form: { otp_delivery_preference: 'sms', otp_make_default_number: false }, ), ) - expect(subject.user_session[:context]).to eq 'confirmation' end end @@ -215,18 +209,7 @@ context 'without selection' do it 'prompts to confirm via SMS by default' do sign_in(user) - stub_analytics - result = { - success: true, - errors: {}, - otp_delivery_preference: 'sms', - area_code: '703', - carrier: 'Test Mobile Carrier', - country_code: 'US', - phone_type: :mobile, - types: [:fixed_or_mobile], - } patch( :create, @@ -236,14 +219,23 @@ }, ) - expect(@analytics).to have_logged_event('Multi-Factor Authentication: phone setup', result) + expect(@analytics).to have_logged_event( + 'Multi-Factor Authentication: phone setup', + success: true, + errors: {}, + otp_delivery_preference: 'sms', + area_code: '703', + carrier: 'Test Mobile Carrier', + country_code: 'US', + phone_type: :mobile, + types: [:fixed_or_mobile], + ) expect(response).to redirect_to( otp_send_path( otp_delivery_selection_form: { otp_delivery_preference: 'sms', otp_make_default_number: false }, ), ) - expect(subject.user_session[:context]).to eq 'confirmation' end end diff --git a/spec/controllers/users/reset_passwords_controller_spec.rb b/spec/controllers/users/reset_passwords_controller_spec.rb index 6d849bb151b..9f08e5edf0b 100644 --- a/spec/controllers/users/reset_passwords_controller_spec.rb +++ b/spec/controllers/users/reset_passwords_controller_spec.rb @@ -25,18 +25,16 @@ before do session[:reset_password_token] = token end - let(:analytics_hash) do - { - success: false, - errors: { user: ['invalid_token'] }, - error_details: { user: { blank: true } }, - } - end it 'redirects to page where user enters email for password reset token' do get :edit - expect(@analytics).to have_logged_event('Password Reset: Token Submitted', analytics_hash) + expect(@analytics).to have_logged_event( + 'Password Reset: Token Submitted', + success: false, + errors: { user: ['invalid_token'] }, + error_details: { user: { blank: true } }, + ) expect(response).to redirect_to new_user_password_path expect(flash[:error]).to eq t('devise.passwords.invalid_token') end @@ -47,14 +45,6 @@ before do session[:reset_password_token] = token end - let(:analytics_hash) do - { - success: false, - errors: { user: ['token_expired'] }, - error_details: { user: { token_expired: true } }, - user_id: '123', - } - end let(:user) { instance_double('User', uuid: '123') } before do @@ -64,14 +54,6 @@ end context 'no user matches token' do - let(:analytics_hash) do - { - success: false, - errors: { user: ['invalid_token'] }, - error_details: { user: { blank: true } }, - } - end - before do session[:reset_password_token] = 'bar' end @@ -79,21 +61,18 @@ it 'redirects to page where user enters email for password reset token' do get :edit - expect(@analytics).to have_logged_event('Password Reset: Token Submitted', analytics_hash) + expect(@analytics).to have_logged_event( + 'Password Reset: Token Submitted', + success: false, + errors: { user: ['invalid_token'] }, + error_details: { user: { blank: true } }, + ) expect(response).to redirect_to new_user_password_path expect(flash[:error]).to eq t('devise.passwords.invalid_token') end end context 'token expired' do - let(:analytics_hash) do - { - success: false, - errors: { user: ['token_expired'] }, - error_details: { user: { token_expired: true } }, - user_id: '123', - } - end let(:user) { instance_double('User', uuid: '123') } before do @@ -104,7 +83,13 @@ it 'redirects to page where user enters email for password reset token' do get :edit - expect(@analytics).to have_logged_event('Password Reset: Token Submitted', analytics_hash) + expect(@analytics).to have_logged_event( + 'Password Reset: Token Submitted', + success: false, + errors: { user: ['token_expired'] }, + error_details: { user: { token_expired: true } }, + user_id: '123', + ) expect(response).to redirect_to new_user_password_path expect(flash[:error]).to eq t('devise.passwords.token_expired') end @@ -182,7 +167,8 @@ get :edit, params: { reset_password_token: raw_reset_token } put :update, params: { reset_password_form: params } - analytics_hash = { + expect(@analytics).to have_logged_event( + 'Password Reset: Password Submitted', success: false, errors: { password: [password_error_message], @@ -201,11 +187,6 @@ profile_deactivated: false, pending_profile_invalidated: false, pending_profile_pending_reasons: '', - } - - expect(@analytics).to have_logged_event( - 'Password Reset: Password Submitted', - analytics_hash, ) expect(response).to redirect_to new_user_password_path expect(flash[:error]).to eq t('devise.passwords.token_expired') @@ -232,7 +213,11 @@ password_confirmation: password_confirmation, reset_password_token: raw_reset_token, } - analytics_hash = { + + put :update, params: { reset_password_form: form_params } + + expect(@analytics).to have_logged_event( + 'Password Reset: Password Submitted', success: false, errors: { password: [password_error_message], @@ -249,13 +234,6 @@ profile_deactivated: false, pending_profile_invalidated: false, pending_profile_pending_reasons: '', - } - - put :update, params: { reset_password_form: form_params } - - expect(@analytics).to have_logged_event( - 'Password Reset: Password Submitted', - analytics_hash, ) expect(assigns(:forbidden_passwords)).to all(be_a(String)) expect(response).to render_template(:edit) @@ -282,7 +260,11 @@ password_confirmation: password_confirmation, reset_password_token: raw_reset_token, } - analytics_hash = { + + put :update, params: { reset_password_form: form_params } + + expect(@analytics).to have_logged_event( + 'Password Reset: Password Submitted', success: false, errors: { password_confirmation: [t('errors.messages.password_mismatch')], @@ -294,13 +276,6 @@ profile_deactivated: false, pending_profile_invalidated: false, pending_profile_pending_reasons: '', - } - - put :update, params: { reset_password_form: form_params } - - expect(@analytics).to have_logged_event( - 'Password Reset: Password Submitted', - analytics_hash, ) expect(assigns(:forbidden_passwords)).to all(be_a(String)) expect(response).to render_template(:edit) @@ -366,7 +341,8 @@ get :edit, params: { reset_password_token: raw_reset_token } put :update, params: { reset_password_form: params } - analytics_hash = { + expect(@analytics).to have_logged_event( + 'Password Reset: Password Submitted', success: true, errors: {}, error_details: {}, @@ -374,11 +350,6 @@ profile_deactivated: false, pending_profile_invalidated: false, pending_profile_pending_reasons: '', - } - - expect(@analytics).to have_logged_event( - 'Password Reset: Password Submitted', - analytics_hash, ) expect(user.events.password_changed.size).to be 1 @@ -418,7 +389,8 @@ put :update, params: { reset_password_form: params } - analytics_hash = { + expect(@analytics).to have_logged_event( + 'Password Reset: Password Submitted', success: true, errors: {}, error_details: {}, @@ -426,11 +398,6 @@ profile_deactivated: true, pending_profile_invalidated: false, pending_profile_pending_reasons: '', - } - - expect(@analytics).to have_logged_event( - 'Password Reset: Password Submitted', - analytics_hash, ) expect(user.active_profile.present?).to eq false expect(response).to redirect_to new_user_session_path @@ -467,7 +434,8 @@ get :edit, params: { reset_password_token: raw_reset_token } put :update, params: { reset_password_form: params } - analytics_hash = { + expect(@analytics).to have_logged_event( + 'Password Reset: Password Submitted', success: true, errors: {}, error_details: {}, @@ -475,11 +443,6 @@ profile_deactivated: false, pending_profile_invalidated: false, pending_profile_pending_reasons: '', - } - - expect(@analytics).to have_logged_event( - 'Password Reset: Password Submitted', - analytics_hash, ) expect(user.reload.confirmed?).to eq true expect(response).to redirect_to new_user_session_path @@ -502,17 +465,15 @@ expect(ActionMailer::Base.deliveries.last.subject). to eq t('anonymous_mailer.password_reset_missing_user.subject') - - analytics_hash = { + expect(@analytics).to have_logged_event( + 'Password Reset: Email Submitted', success: true, errors: {}, error_details: {}, user_id: 'nonexistent-uuid', confirmed: false, active_profile: false, - } - - expect(@analytics).to have_logged_event('Password Reset: Email Submitted', analytics_hash) + ) expect(response).to redirect_to forgot_password_path end end @@ -521,16 +482,6 @@ let(:email) { 'test@example.com' } let(:email_param) { { email: email } } let!(:user) { create(:user, :fully_registered, **email_param) } - let(:analytics_hash) do - { - success: true, - errors: {}, - error_details: {}, - user_id: user.uuid, - confirmed: true, - active_profile: false, - } - end before do stub_analytics @@ -541,23 +492,21 @@ put :create, params: { password_reset_email_form: email_param } end.to change { ActionMailer::Base.deliveries.count }.by(1) - expect(@analytics).to have_logged_event('Password Reset: Email Submitted', analytics_hash) - expect(response).to redirect_to forgot_password_path - end - end - - context 'user exists but is unconfirmed' do - let(:user) { create(:user, :unconfirmed) } - let(:analytics_hash) do - { + expect(@analytics).to have_logged_event( + 'Password Reset: Email Submitted', success: true, errors: {}, error_details: {}, user_id: user.uuid, - confirmed: false, + confirmed: true, active_profile: false, - } + ) + expect(response).to redirect_to forgot_password_path end + end + + context 'user exists but is unconfirmed' do + let(:user) { create(:user, :unconfirmed) } let(:params) do { password_reset_email_form: { @@ -574,7 +523,15 @@ expect { put :create, params: params }. to change { ActionMailer::Base.deliveries.count }.by(1) - expect(@analytics).to have_logged_event('Password Reset: Email Submitted', analytics_hash) + expect(@analytics).to have_logged_event( + 'Password Reset: Email Submitted', + success: true, + errors: {}, + error_details: {}, + user_id: user.uuid, + confirmed: false, + active_profile: false, + ) expect(ActionMailer::Base.deliveries.last.subject). to eq t('anonymous_mailer.password_reset_missing_user.subject') @@ -585,23 +542,21 @@ context 'user is verified' do it 'captures in analytics that the user was verified' do stub_analytics - user = create(:user, :fully_registered) create(:profile, :active, :verified, user: user) - analytics_hash = { + params = { password_reset_email_form: { email: user.email } } + put :create, params: params + + expect(@analytics).to have_logged_event( + 'Password Reset: Email Submitted', success: true, errors: {}, error_details: {}, user_id: user.uuid, confirmed: true, active_profile: true, - } - - params = { password_reset_email_form: { email: user.email } } - put :create, params: params - - expect(@analytics).to have_logged_event('Password Reset: Email Submitted', analytics_hash) + ) end end @@ -609,20 +564,19 @@ it 'displays an error and tracks event' do stub_analytics - analytics_hash = { + params = { password_reset_email_form: { email: 'foo' } } + expect { put :create, params: params }. + to change { ActionMailer::Base.deliveries.count }.by(0) + + expect(@analytics).to have_logged_event( + 'Password Reset: Email Submitted', success: false, errors: { email: [t('valid_email.validations.email.invalid')] }, error_details: { email: { invalid: true } }, user_id: 'nonexistent-uuid', confirmed: false, active_profile: false, - } - - params = { password_reset_email_form: { email: 'foo' } } - expect { put :create, params: params }. - to change { ActionMailer::Base.deliveries.count }.by(0) - - expect(@analytics).to have_logged_event('Password Reset: Email Submitted', analytics_hash) + ) expect(response).to render_template :new end end diff --git a/spec/controllers/users/sessions_controller_spec.rb b/spec/controllers/users/sessions_controller_spec.rb index 62b03f7ddb8..387df9187b0 100644 --- a/spec/controllers/users/sessions_controller_spec.rb +++ b/spec/controllers/users/sessions_controller_spec.rb @@ -542,12 +542,14 @@ it 'tracks CSRF errors' do user = create(:user, :fully_registered) stub_analytics - analytics_hash = { controller: 'users/sessions#create' } allow(controller).to receive(:create).and_raise(ActionController::InvalidAuthenticityToken) post :create, params: { user: { email: user.email, password: user.password } } - expect(@analytics).to have_logged_event('Invalid Authenticity Token', analytics_hash) + expect(@analytics).to have_logged_event( + 'Invalid Authenticity Token', + controller: 'users/sessions#create', + ) expect(response).to redirect_to new_user_session_url expect(flash[:error]).to eq t('errors.general') end @@ -555,13 +557,15 @@ it 'redirects back to home page if CSRF error and referer is invalid' do user = create(:user, :fully_registered) stub_analytics - analytics_hash = { controller: 'users/sessions#create' } allow(controller).to receive(:create).and_raise(ActionController::InvalidAuthenticityToken) request.env['HTTP_REFERER'] = '@@@' post :create, params: { user: { email: user.email, password: user.password } } - expect(@analytics).to have_logged_event('Invalid Authenticity Token', analytics_hash) + expect(@analytics).to have_logged_event( + 'Invalid Authenticity Token', + controller: 'users/sessions#create', + ) expect(response).to redirect_to new_user_session_url expect(flash[:error]).to eq t('errors.general') end diff --git a/spec/controllers/users/two_factor_authentication_controller_spec.rb b/spec/controllers/users/two_factor_authentication_controller_spec.rb index 3e8336b8162..d70776e330f 100644 --- a/spec/controllers/users/two_factor_authentication_controller_spec.rb +++ b/spec/controllers/users/two_factor_authentication_controller_spec.rb @@ -326,7 +326,12 @@ def index it 'tracks the analytics events' do stub_analytics - analytics_hash = { + get :send_code, params: { + otp_delivery_selection_form: { **otp_preference_sms, resend: 'true' }, + } + + expect(@analytics).to have_logged_event( + 'OTP: Delivery Selection', success: true, errors: {}, **otp_preference_sms, @@ -334,13 +339,7 @@ def index context: 'authentication', country_code: 'US', area_code: '202', - } - - get :send_code, params: { - otp_delivery_selection_form: { **otp_preference_sms, resend: 'true' }, - } - - expect(@analytics).to have_logged_event('OTP: Delivery Selection', analytics_hash) + ) expect(@analytics).to have_logged_event( 'Telephony: OTP sent', hash_including( @@ -492,7 +491,13 @@ def index it 'tracks the event' do stub_analytics - analytics_hash = { + get :send_code, params: { + otp_delivery_selection_form: { otp_delivery_preference: 'voice', + otp_make_default_number: nil }, + } + + expect(@analytics).to have_logged_event( + 'OTP: Delivery Selection', success: true, errors: {}, otp_delivery_preference: 'voice', @@ -500,14 +505,7 @@ def index context: 'authentication', country_code: 'US', area_code: '202', - } - - get :send_code, params: { - otp_delivery_selection_form: { otp_delivery_preference: 'voice', - otp_make_default_number: nil }, - } - - expect(@analytics).to have_logged_event('OTP: Delivery Selection', analytics_hash) + ) expect(@analytics).to have_logged_event( 'Telephony: OTP sent', hash_including( diff --git a/spec/controllers/users/two_factor_authentication_setup_controller_spec.rb b/spec/controllers/users/two_factor_authentication_setup_controller_spec.rb index 4f49c06b505..e22cfc60ffd 100644 --- a/spec/controllers/users/two_factor_authentication_setup_controller_spec.rb +++ b/spec/controllers/users/two_factor_authentication_setup_controller_spec.rb @@ -128,21 +128,20 @@ stub_sign_in_before_2fa stub_analytics - result = { - enabled_mfa_methods_count: 0, - selection: ['voice', 'auth_app'], - success: true, - selected_mfa_count: 2, - errors: {}, - } - patch :create, params: { two_factor_options_form: { selection: ['voice', 'auth_app'], }, } - expect(@analytics).to have_logged_event('User Registration: 2FA Setup', result) + expect(@analytics).to have_logged_event( + 'User Registration: 2FA Setup', + enabled_mfa_methods_count: 0, + selection: ['voice', 'auth_app'], + success: true, + selected_mfa_count: 2, + errors: {}, + ) end context 'when multi selection with phone first' do diff --git a/spec/requests/rack_attack_spec.rb b/spec/requests/rack_attack_spec.rb index 742fd1bd6eb..c14757e68cd 100644 --- a/spec/requests/rack_attack_spec.rb +++ b/spec/requests/rack_attack_spec.rb @@ -306,7 +306,6 @@ it 'throttles with a custom response' do analytics = FakeAnalytics.new allow(Analytics).to receive(:new).and_return(analytics) - analytics_hash = { type: 'logins/email+ip' } Rack::Attack::SIGN_IN_PATHS.each do |path| (logins_per_email_and_ip_limit + 1).times do |index| @@ -315,7 +314,7 @@ }, headers: { REMOTE_ADDR: '1.2.3.4' } end - expect(analytics).to have_logged_event('Rate Limit Triggered', analytics_hash) + expect(analytics).to have_logged_event('Rate Limit Triggered', type: 'logins/email+ip') expect(response.status).to eq(429) expect(response.body). to include('Please wait a few minutes before you try again.')