Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 37 additions & 11 deletions app/jobs/in_person/email_reminder_job.rb
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down
35 changes: 35 additions & 0 deletions app/services/analytics_events.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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')
Expand Down
167 changes: 101 additions & 66 deletions spec/jobs/in_person/email_reminder_job_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down