diff --git a/app/jobs/in_person/email_reminder_job.rb b/app/jobs/in_person/email_reminder_job.rb index 446013a0e23..1fcc68988b8 100644 --- a/app/jobs/in_person/email_reminder_job.rb +++ b/app/jobs/in_person/email_reminder_job.rb @@ -1,5 +1,10 @@ +# frozen_string_literal: true + module InPerson class EmailReminderJob < ApplicationJob + EMAIL_TYPE_EARLY = 'early' + EMAIL_TYPE_LATE = 'late' + queue_as :low include GoodJob::ActiveJobExtensions::Concurrency @@ -14,28 +19,49 @@ class EmailReminderJob < ApplicationJob def perform(_now) return true unless IdentityConfig.store.in_person_proofing_enabled - # final reminder job done first in case of job failure - second_set_enrollments = InPersonEnrollment.needs_late_email_reminder( + # send late emails first in case of job failure + late_enrollments = InPersonEnrollment.needs_late_email_reminder( late_benchmark, final_benchmark, ) - second_set_enrollments.each do |enrollment| - send_reminder_email(enrollment.user, enrollment) - enrollment.update!(late_reminder_sent: true) - end + send_emails_for_enrollments(enrollments: late_enrollments, email_type: EMAIL_TYPE_LATE) - first_set_enrollments = InPersonEnrollment.needs_early_email_reminder( + early_enrollments = InPersonEnrollment.needs_early_email_reminder( early_benchmark, late_benchmark, ) - first_set_enrollments.each do |enrollment| - send_reminder_email(enrollment.user, enrollment) - enrollment.update!(early_reminder_sent: true) - end + send_emails_for_enrollments(enrollments: early_enrollments, email_type: EMAIL_TYPE_EARLY) end private + def analytics(user: AnonymousUser.new) + Analytics.new(user: user, request: nil, session: {}, sp: nil) + end + + def send_emails_for_enrollments(enrollments:, email_type:) + enrollments.each do |enrollment| + send_reminder_email(enrollment.user, enrollment) + rescue StandardError => err + NewRelic::Agent.notice_error(err) + analytics(user: enrollment.user).idv_in_person_email_reminder_job_exception( + enrollment_id: enrollment.id, + exception_class: err.class.to_s, + exception_message: err.message, + ) + else + analytics(user: enrollment.user).idv_in_person_email_reminder_job_email_initiated( + email_type: email_type, + enrollment_id: enrollment.id, + ) + if email_type == EMAIL_TYPE_EARLY + enrollment.update!(early_reminder_sent: true) + elsif email_type == EMAIL_TYPE_LATE + enrollment.update!(late_reminder_sent: true) + end + end + end + def calculate_interval(benchmark) days_until_expired = IdentityConfig.store.in_person_enrollment_validity_in_days.days (Time.zone.now - days_until_expired) + benchmark.days diff --git a/app/services/analytics_events.rb b/app/services/analytics_events.rb index b603263bd25..8c4a00d3209 100644 --- a/app/services/analytics_events.rb +++ b/app/services/analytics_events.rb @@ -3088,6 +3088,25 @@ def idv_in_person_usps_proofing_results_job_exception( ) end + # Tracks exceptions that are raised when running InPerson::EmailReminderJob + # @param [String] enrollment_id + # @param [String] exception_class + # @param [String] exception_message + def idv_in_person_email_reminder_job_exception( + enrollment_id:, + exception_class: nil, + exception_message: nil, + **extra + ) + track_event( + 'InPerson::EmailReminderJob: Exception raised when attempting to send reminder email', + enrollment_id: enrollment_id, + exception_class: exception_class, + exception_message: exception_message, + **extra, + ) + end + # Tracks individual enrollments that are updated during GetUspsProofingResultsJob # @param [String] enrollment_code # @param [String] enrollment_id @@ -3126,6 +3145,22 @@ def idv_in_person_usps_proofing_results_job_email_initiated( ) end + # Tracks emails that are initiated during InPerson::EmailReminderJob + # @param [String] email_type early or late + # @param [String] enrollment_id + def idv_in_person_email_reminder_job_email_initiated( + email_type:, + enrollment_id:, + **extra + ) + track_event( + 'InPerson::EmailReminderJob: Reminder email initiated', + email_type: email_type, + enrollment_id: enrollment_id, + **extra, + ) + end + # Tracks users visiting the recovery options page def account_reset_recovery_options_visit track_event('Account Reset: Recovery Options Visited') diff --git a/spec/jobs/in_person/email_reminder_job_spec.rb b/spec/jobs/in_person/email_reminder_job_spec.rb index 6f46b03ee5c..e192b6d45f7 100644 --- a/spec/jobs/in_person/email_reminder_job_spec.rb +++ b/spec/jobs/in_person/email_reminder_job_spec.rb @@ -2,90 +2,125 @@ RSpec.describe InPerson::EmailReminderJob do let(:job) { InPerson::EmailReminderJob.new } + let(:job_analytics) { FakeAnalytics.new } before do ActiveJob::Base.queue_adapter = :test allow(IdentityConfig.store).to receive(:in_person_proofing_enabled).and_return(true) + allow(job).to receive(:analytics).and_return(job_analytics) end describe '#perform' do - let!(:passed_enrollment) { create(:in_person_enrollment, :passed) } - let!(:failing_enrollment) { create(:in_person_enrollment, :failed) } - let!(:expired_enrollment) { create(:in_person_enrollment, :expired) } - let!(:pending_enrollment_needing_late_reminder) do - create(:in_person_enrollment, :pending, enrollment_established_at: Time.zone.now - 26.days) - end - let!(:pending_enrollment_needing_early_reminder) do - create(:in_person_enrollment, :pending, enrollment_established_at: Time.zone.now - 19.days) - end + context 'with many enrollments' do + let!(:passed_enrollment) { create(:in_person_enrollment, :passed) } + let!(:failing_enrollment) { create(:in_person_enrollment, :failed) } + let!(:expired_enrollment) { create(:in_person_enrollment, :expired) } + let!(:pending_enrollment_needing_late_reminder) do + create(:in_person_enrollment, :pending, enrollment_established_at: Time.zone.now - 26.days) + end + let!(:pending_enrollment_needing_early_reminder) do + create(:in_person_enrollment, :pending, enrollment_established_at: Time.zone.now - 19.days) + end - let!(:pending_enrollment_received_late_reminder) do - create( - :in_person_enrollment, :pending, enrollment_established_at: Time.zone.now - 26.days, - late_reminder_sent: true - ) - end - let!(:pending_enrollment_received_early_reminder) do - create( - :in_person_enrollment, :pending, enrollment_established_at: Time.zone.now - 19.days, - early_reminder_sent: true - ) - end + let!(:pending_enrollment_received_late_reminder) do + create( + :in_person_enrollment, :pending, enrollment_established_at: Time.zone.now - 26.days, + late_reminder_sent: true + ) + end + let!(:pending_enrollment_received_early_reminder) do + create( + :in_person_enrollment, :pending, enrollment_established_at: Time.zone.now - 19.days, + early_reminder_sent: true + ) + end - let!(:pending_enrollments) do - [ - create(:in_person_enrollment, :pending, enrollment_established_at: Time.zone.now), - create(:in_person_enrollment, :pending, created_at: Time.zone.now), - ] - end + let!(:pending_enrollments) do + [ + create(:in_person_enrollment, :pending, enrollment_established_at: Time.zone.now), + create(:in_person_enrollment, :pending, created_at: Time.zone.now), + ] + end - context 'late email reminder' do - it 'queues emails for enrollments that need the late email reminder sent' do - user = pending_enrollment_needing_late_reminder.user - expect do - job.perform(Time.zone.now) - end.to have_enqueued_mail(UserMailer, :in_person_ready_to_verify_reminder).with( - params: { user: user, email_address: user.email_addresses.first }, - args: [{ enrollment: pending_enrollment_needing_late_reminder }], - ) - pending_enrollment_needing_late_reminder.reload - expect(pending_enrollment_needing_late_reminder.late_reminder_sent).to be_truthy + context 'late email reminder' do + it 'queues emails for enrollments that need the late email reminder sent' do + user = pending_enrollment_needing_late_reminder.user + expect do + job.perform(Time.zone.now) + end.to have_enqueued_mail(UserMailer, :in_person_ready_to_verify_reminder).with( + params: { user: user, email_address: user.email_addresses.first }, + args: [{ enrollment: pending_enrollment_needing_late_reminder }], + ) + pending_enrollment_needing_late_reminder.reload + expect(pending_enrollment_needing_late_reminder.late_reminder_sent).to be_truthy + expect(job_analytics).to have_logged_event( + 'InPerson::EmailReminderJob: Reminder email initiated', + email_type: 'late', + enrollment_id: pending_enrollment_needing_late_reminder.id, + ) + end + + it 'does not queue emails for enrollments that had late email reminder sent' do + user = pending_enrollment_received_late_reminder.user + expect do + job.perform(Time.zone.now) + end.not_to have_enqueued_mail(UserMailer, :in_person_ready_to_verify_reminder).with( + params: { user: user, email_address: user.email_addresses.first }, + args: [{ enrollment: pending_enrollment_received_late_reminder }], + ) + expect(pending_enrollment_received_late_reminder.late_reminder_sent).to be_truthy + end end - it 'does not queue emails for enrollments that had late email reminder sent' do - user = pending_enrollment_received_late_reminder.user - expect do - job.perform(Time.zone.now) - end.not_to have_enqueued_mail(UserMailer, :in_person_ready_to_verify_reminder).with( - params: { user: user, email_address: user.email_addresses.first }, - args: [{ enrollment: pending_enrollment_received_late_reminder }], - ) - expect(pending_enrollment_received_late_reminder.late_reminder_sent).to be_truthy + context 'early email reminder' do + it 'queues emails for enrollments that need the early email reminder sent' do + user = pending_enrollment_needing_early_reminder.user + expect do + job.perform(Time.zone.now) + end.to have_enqueued_mail(UserMailer, :in_person_ready_to_verify_reminder).with( + params: { user: user, email_address: user.email_addresses.first }, + args: [{ enrollment: pending_enrollment_needing_early_reminder }], + ) + pending_enrollment_needing_early_reminder.reload + expect(pending_enrollment_needing_early_reminder.early_reminder_sent).to be_truthy + expect(job_analytics).to have_logged_event( + 'InPerson::EmailReminderJob: Reminder email initiated', + email_type: 'early', + enrollment_id: pending_enrollment_needing_early_reminder.id, + ) + end + + it 'does not queue emails for enrollments that had early email reminder sent' do + user = pending_enrollment_received_early_reminder.user + expect do + job.perform(Time.zone.now) + end.not_to have_enqueued_mail(UserMailer, :in_person_ready_to_verify_reminder).with( + params: { user: user, email_address: user.email_addresses.first }, + args: [{ enrollment: pending_enrollment_received_early_reminder }], + ) + expect(pending_enrollment_received_early_reminder.early_reminder_sent).to be_truthy + end end end - context 'early email reminder' do - it 'queues emails for enrollments that need the early email reminder sent' do - user = pending_enrollment_needing_early_reminder.user - expect do - job.perform(Time.zone.now) - end.to have_enqueued_mail(UserMailer, :in_person_ready_to_verify_reminder).with( - params: { user: user, email_address: user.email_addresses.first }, - args: [{ enrollment: pending_enrollment_needing_early_reminder }], - ) - pending_enrollment_needing_early_reminder.reload - expect(pending_enrollment_needing_early_reminder.early_reminder_sent).to be_truthy + context 'with one eligible enrollment' do + let!(:pending_enrollment_needing_late_reminder) do + create(:in_person_enrollment, :pending, enrollment_established_at: Time.zone.now - 26.days) end - it 'does not queue emails for enrollments that had early email reminder sent' do - user = pending_enrollment_received_early_reminder.user - expect do + context 'an error is raised when sending an email' do + let(:error_message) { 'A standard error happened' } + let!(:error) { StandardError.new(error_message) } + + it 'it handles and logs the error' do + allow(UserMailer).to receive(:with).once.and_raise(error) + expect(NewRelic::Agent).to receive(:notice_error).with(error) job.perform(Time.zone.now) - end.not_to have_enqueued_mail(UserMailer, :in_person_ready_to_verify_reminder).with( - params: { user: user, email_address: user.email_addresses.first }, - args: [{ enrollment: pending_enrollment_received_early_reminder }], - ) - expect(pending_enrollment_received_early_reminder.early_reminder_sent).to be_truthy + expect(job_analytics).to have_logged_event( + 'InPerson::EmailReminderJob: Exception raised when attempting to send reminder email', + exception_message: error_message, + ) + end end end end