Add tests for all PLG emails

This commit is contained in:
Seth Call 2025-10-17 08:15:46 -05:00
parent fe6157e8cf
commit 1dd15fb0aa
7 changed files with 84 additions and 49 deletions

View File

@ -3,7 +3,14 @@
execute "ALTER TABLE users ADD COLUMN trial_expires_reminder1_sent_at TIMESTAMP" execute "ALTER TABLE users ADD COLUMN trial_expires_reminder1_sent_at TIMESTAMP"
execute "ALTER TABLE users ADD COLUMN trial_expires_reminder2_sent_at TIMESTAMP" execute "ALTER TABLE users ADD COLUMN trial_expires_reminder2_sent_at TIMESTAMP"
execute "ALTER TABLE users ADD COLUMN trial_expires_reminder3_sent_at TIMESTAMP" execute "ALTER TABLE users ADD COLUMN trial_expires_reminder3_sent_at TIMESTAMP"
end
# slide in some more production indexes
execute "CREATE INDEX index_users_on_first_music_session_at ON users USING btree (first_music_session_at)"
# subscription_sync_code
execute "CREATE INDEX index_users_on_subscription_sync_code ON users USING btree (subscription_sync_code)"
# first_certified_gear_at
execute "CREATE INDEX index_users_on_first_certified_gear_at ON users USING btree (first_certified_gear_at)"
end
def self.down def self.down
execute "ALTER TABLE users DROP COLUMN trial_expires_reminder1_sent_at" execute "ALTER TABLE users DROP COLUMN trial_expires_reminder1_sent_at"
execute "ALTER TABLE users DROP COLUMN trial_expires_reminder2_sent_at" execute "ALTER TABLE users DROP COLUMN trial_expires_reminder2_sent_at"

View File

@ -6,19 +6,20 @@ module JamRuby
begin begin
cutoff_date = Date.parse(Rails.application.config.profile_complete_reminders_effective_from_date) # Define a cutoff date for the profile completion emails cutoff_date = Date.parse(Rails.application.config.profile_complete_reminders_effective_from_date) # Define a cutoff date for the profile completion emails
#If the user has not updated their profile 1 day after signup, then send reminder email1 #If the user has not updated their profile 1 day after signup, then send reminder email1
reminder1_users.find_each do |user| reminder1_users(cutoff_date).find_each do |user|
puts "reminder1_users user: #{user.id}"
UserMailer.profile_complete_reminder1(user).deliver_now UserMailer.profile_complete_reminder1(user).deliver_now
User.where(id: user.id).update_all(profile_complete_reminder1_sent_at: Time.now) User.where(id: user.id).update_all(profile_complete_reminder1_sent_at: Time.now)
end end
#If the user has not updated their profile 3 days after signup, then send reminder email2 #If the user has not updated their profile 3 days after signup, then send reminder email2
reminder2_users.find_each do |user| reminder2_users(cutoff_date).find_each do |user|
UserMailer.profile_complete_reminder2(user).deliver_now UserMailer.profile_complete_reminder2(user).deliver_now
User.where(id: user.id).update_all(profile_complete_reminder2_sent_at: Time.now) User.where(id: user.id).update_all(profile_complete_reminder2_sent_at: Time.now)
end end
#If the user has not updated their profile 5 days after signup, then send reminder email3 #If the user has not updated their profile 5 days after signup, then send reminder email3
reminder3_users.find_each do |user| reminder3_users(cutoff_date).find_each do |user|
UserMailer.profile_complete_reminder3(user).deliver_now UserMailer.profile_complete_reminder3(user).deliver_now
User.where(id: user.id).update_all(profile_complete_reminder3_sent_at: Time.now) User.where(id: user.id).update_all(profile_complete_reminder3_sent_at: Time.now)
end end
@ -33,17 +34,19 @@ module JamRuby
end end
def self.reminder1_users(cutoff_date) def self.reminder1_users(cutoff_date)
EmailProfileReminder.prospect_users.where("users.created_at > ? AND users.created_at::date <= ? AND users.profile_complete_reminder1_sent_at IS NULL", cutoff_date, 1.day.ago.to_date) # ensure that the user has had the account for at least one day
puts "reminder1_users cutoff_date: #{cutoff_date}"
EmailProfileReminder.prospect_users.where("users.created_at > ? AND users.profile_complete_reminder1_sent_at IS NULL AND (NOW() - users.created_at) > INTERVAL '1 day'", cutoff_date)
end end
def self.reminder2_users(cutoff_date) def self.reminder2_users(cutoff_date)
EmailProfileReminder.prospect_users.where("users.created_at > ? AND GREATEST(users.created_at::date, users.profile_complete_reminder1_sent_at::date) <= ? AND users.profile_complete_reminder1_sent_at IS NOT NULL AND users.profile_complete_reminder2_sent_at IS NULL", cutoff_date, 3.days.ago.to_date) # ensure that the user has had the account for at least three days and guard against rapid back-to-back emails
EmailProfileReminder.prospect_users.where("users.created_at > ? AND users.profile_complete_reminder1_sent_at < ? AND users.profile_complete_reminder1_sent_at IS NOT NULL AND users.profile_complete_reminder2_sent_at IS NULL", cutoff_date, 2.days.ago)
end end
def self.reminder3_users(cutoff_date) def self.reminder3_users(cutoff_date)
EmailProfileReminder.prospect_users.where("users.created_at > ? AND GREATEST(users.created_at::date, users.profile_complete_reminder2_sent_at::date) <= ? AND users.profile_complete_reminder2_sent_at IS NOT NULL AND users.profile_complete_reminder3_sent_at IS NULL", cutoff_date, 5.days.ago.to_date) # ensure that user has had the profile for 5 days and guard against rapid back-to-back emails
EmailProfileReminder.prospect_users.where("users.created_at > ? AND users.profile_complete_reminder2_sent_at < ? AND users.profile_complete_reminder2_sent_at IS NOT NULL AND users.profile_complete_reminder3_sent_at IS NULL", cutoff_date, 2.days.ago)
end end
end end
end end

View File

@ -32,15 +32,15 @@ module JamRuby
end end
def self.reminder1_users(cutoff_date) def self.reminder1_users(cutoff_date)
GearSetupReminder.prospect_users.where("users.created_at::date = ? AND users.created_at > ? AND users.gear_setup_reminder1_sent_at IS NULL", 1.day.ago.to_date, cutoff_date) GearSetupReminder.prospect_users.where("users.created_at > ? AND users.gear_setup_reminder1_sent_at IS NULL AND (NOW() - users.created_at) > INTERVAL '1 day'", cutoff_date)
end end
def self.reminder2_users(cutoff_date) def self.reminder2_users(cutoff_date)
GearSetupReminder.prospect_users.where("users.created_at::date = ? AND users.created_at > ? AND users.gear_setup_reminder1_sent_at IS NOT NULL AND users.gear_setup_reminder2_sent_at IS NULL", 3.days.ago.to_date, cutoff_date) GearSetupReminder.prospect_users.where("users.created_at > ? AND users.gear_setup_reminder1_sent_at IS NOT NULL AND users.gear_setup_reminder2_sent_at IS NULL AND users.gear_setup_reminder1_sent_at < ?", cutoff_date, 2.days.ago)
end end
def self.reminder3_users(cutoff_date) def self.reminder3_users(cutoff_date)
GearSetupReminder.prospect_users.where("users.created_at::date = ? AND users.created_at > ? AND users.gear_setup_reminder2_sent_at IS NOT NULL AND users.gear_setup_reminder3_sent_at IS NULL", 5.days.ago.to_date, cutoff_date) GearSetupReminder.prospect_users.where("users.created_at > ? AND users.gear_setup_reminder2_sent_at IS NOT NULL AND users.gear_setup_reminder3_sent_at IS NULL AND users.gear_setup_reminder2_sent_at < ?", cutoff_date, 2.days.ago)
end end
end end

View File

@ -36,11 +36,11 @@ module JamRuby
end end
def self.reminder2_users(cutoff_date) def self.reminder2_users(cutoff_date)
GroupSessionReminder.prospect_users.where("users.created_at > ? AND users.group_session_reminder1_sent_at IS NOT NULL AND users.group_session_reminder2_sent_at IS NULL AND users.first_music_session_at::date = ?", cutoff_date, 3.days.ago.to_date) GroupSessionReminder.prospect_users.where("users.created_at > ? AND users.group_session_reminder1_sent_at IS NOT NULL AND users.group_session_reminder2_sent_at IS NULL AND users.first_music_session_at < ? AND group_session_reminder1_sent_at < ?", cutoff_date, 3.days.ago, 1.day.ago)
end end
def self.reminder3_users(cutoff_date) def self.reminder3_users(cutoff_date)
GroupSessionReminder.prospect_users.where("users.created_at > ? AND users.group_session_reminder2_sent_at IS NOT NULL AND users.group_session_reminder3_sent_at IS NULL AND users.first_music_session_at::date = ?", cutoff_date, 5.days.ago.to_date) GroupSessionReminder.prospect_users.where("users.created_at > ? AND users.group_session_reminder2_sent_at IS NOT NULL AND users.group_session_reminder3_sent_at IS NULL AND users.first_music_session_at < ? AND group_session_reminder2_sent_at < ?", cutoff_date, 5.days.ago, 1.day.ago)
end end
end end

View File

@ -27,19 +27,19 @@ module JamRuby
end end
def self.prospect_users def self.prospect_users
User.where("users.first_music_session_at IS NULL") User.where("users.first_music_session_at IS NULL AND first_certified_gear_at IS NOT NULL")
end end
def self.reminder1_users(cutoff_date) def self.reminder1_users(cutoff_date)
TestGearReminder.prospect_users.where("users.first_certified_gear_at::date = ? AND users.created_at >= ? AND users.test_gear_reminder1_sent_at IS NULL", 1.day.ago.to_date, cutoff_date) TestGearReminder.prospect_users.where("users.created_at > ? AND users.test_gear_reminder1_sent_at IS NULL AND (NOW() - users.first_certified_gear_at) > INTERVAL '1 day'", cutoff_date)
end end
def self.reminder2_users(cutoff_date) def self.reminder2_users(cutoff_date)
TestGearReminder.prospect_users.where("users.first_certified_gear_at::date = ? AND users.created_at >= ? AND users.test_gear_reminder1_sent_at IS NOT NULL AND users.test_gear_reminder2_sent_at IS NULL", 3.days.ago.to_date, cutoff_date) TestGearReminder.prospect_users.where("users.created_at > ? AND users.test_gear_reminder1_sent_at IS NOT NULL AND users.test_gear_reminder2_sent_at IS NULL AND users.test_gear_reminder1_sent_at < ?", cutoff_date, 2.days.ago)
end end
def self.reminder3_users(cutoff_date) def self.reminder3_users(cutoff_date)
TestGearReminder.prospect_users.where("users.first_certified_gear_at::date = ? AND users.created_at > ? AND users.test_gear_reminder2_sent_at IS NOT NULL AND users.test_gear_reminder3_sent_at IS NULL", 5.days.ago.to_date, cutoff_date) TestGearReminder.prospect_users.where("users.created_at > ? AND users.test_gear_reminder2_sent_at IS NOT NULL AND users.test_gear_reminder3_sent_at IS NULL AND users.test_gear_reminder2_sent_at < ?", cutoff_date, 2.days.ago)
end end
end end
end end

View File

@ -8,24 +8,26 @@ module JamRuby
class TrialExpiresReminder class TrialExpiresReminder
@@log = Logging.logger[TrialExpiresReminder] @@log = Logging.logger[TrialExpiresReminder]
FIRST_NOTIFICATION_CHECK = 1.day.ago.to_date FIRST_NOTIFICATION_CHECK = 1.day.ago
SECOND_NOTIFICATION_CHECK = 5.days.ago.to_date # unused here, but just as a form of simple documentation...
THIRD_NOTIFICATION_CHECK = 9.days.ago.to_date # SECOND_NOTIFICATION_CHECK = 4.days (from the previous reminder)
# THIRD_NOTIFICATION_CHECK = 4.days (from the previous reminder)
def self.prospect_users(cutoff_date) def self.prospect_users(cutoff_date)
User.where("(users.subscription_trial_ends_at IS NOT NULL AND users.subscription_trial_ends_at > ?)", cutoff_date) User.where("(users.subscription_trial_ends_at IS NOT NULL AND users.created_at > ?)", cutoff_date)
end end
# trial_ended | in_trial
def self.reminder1_users(cutoff_date) def self.reminder1_users(cutoff_date)
prospect_users(cutoff_date).where("users.subscription_last_checked_at < ? AND users.subscription_trial_ends_at::date = ? AND users.trial_expires_reminder1_sent_at IS NULL", 1.day.ago, FIRST_NOTIFICATION_CHECK) prospect_users(cutoff_date).where("users.subscription_sync_code = 'trial_ended' AND users.subscription_trial_ends_at < ? AND users.trial_expires_reminder1_sent_at IS NULL", FIRST_NOTIFICATION_CHECK)
end end
def self.reminder2_users(cutoff_date) def self.reminder2_users(cutoff_date)
prospect_users(cutoff_date).where("users.subscription_last_checked_at < ? AND users.subscription_trial_ends_at::date = ? AND trial_expires_reminder1_sent_at IS NOT NULL AND users.trial_expires_reminder2_sent_at IS NULL", 1.day.ago, SECOND_NOTIFICATION_CHECK) prospect_users(cutoff_date).where("users.subscription_sync_code = 'trial_ended' AND trial_expires_reminder1_sent_at IS NOT NULL AND users.trial_expires_reminder2_sent_at IS NULL AND (NOW() - trial_expires_reminder1_sent_at) > INTERVAL '4 days'")
end end
def self.reminder3_users(cutoff_date) def self.reminder3_users(cutoff_date)
prospect_users(cutoff_date).where("users.subscription_last_checked_at < ? AND users.subscription_trial_ends_at::date = ? AND trial_expires_reminder2_sent_at IS NOT NULL AND users.trial_expires_reminder3_sent_at IS NULL", 1.day.ago, THIRD_NOTIFICATION_CHECK) prospect_users(cutoff_date).where("users.subscription_sync_code = 'trial_ended' AND trial_expires_reminder2_sent_at IS NOT NULL AND users.trial_expires_reminder3_sent_at IS NULL AND (NOW() - trial_expires_reminder2_sent_at) > INTERVAL '4 days'")
end end
def self.send_reminders def self.send_reminders

View File

@ -19,13 +19,19 @@ describe TrialExpiresReminder do
ActionMailer::Base.deliveries.clear ActionMailer::Base.deliveries.clear
end end
def no_more_emails_sent
UserMailer.deliveries.clear
TrialExpiresReminder.send_reminders
expect(ActionMailer::Base.deliveries.count).to eq(0)
end
it "sends reminder emails to users whose trials are about to expire" do it "sends reminder emails to users whose trials are about to expire" do
user1.subscription_trial_ends_at = 1.days.from_now user1.subscription_trial_ends_at = 1.days.from_now
user1.subscription_last_checked_at = 2.days.ago user1.subscription_sync_code = 'trial_ended'
user1.save! user1.save!
user2.subscription_trial_ends_at = 1.days.ago user2.subscription_trial_ends_at = 2.days.ago
user2.subscription_last_checked_at = 2.days.ago user2.subscription_sync_code = 'trial_ended'
user2.save! user2.save!
TrialExpiresReminder.send_reminders TrialExpiresReminder.send_reminders
@ -36,11 +42,14 @@ describe TrialExpiresReminder do
expect(ActionMailer::Base.deliveries.last.subject).to include("Your free gold trial has expired, but you have great options to keep playing!") expect(ActionMailer::Base.deliveries.last.subject).to include("Your free gold trial has expired, but you have great options to keep playing!")
expect(user2.reload.trial_expires_reminder1_sent_at).not_to be_nil expect(user2.reload.trial_expires_reminder1_sent_at).not_to be_nil
no_more_emails_sent
end end
it "does not send reminder emails to users who have already received them" do it "does not send reminder emails to users who have already received them" do
user1.subscription_trial_ends_at = 1.days.ago user1.reload
user1.subscription_last_checked_at = 2.days.ago user1.subscription_trial_ends_at = 1.days.ago + 1.hour
user1.subscription_sync_code = 'trial_ended'
user1.trial_expires_reminder1_sent_at = Time.now user1.trial_expires_reminder1_sent_at = Time.now
user1.save! user1.save!
@ -50,31 +59,38 @@ describe TrialExpiresReminder do
end end
it "sends the second reminder email to users whose trials are about to expire" do it "sends the second reminder email to users whose trials are about to expire" do
user1.subscription_trial_ends_at = 4.days.ago user1.reload
user1.subscription_last_checked_at = 1.days.ago user2.reload
user1.trial_expires_reminder1_sent_at = Time.now
# pretend that the first reminder email was sent 2 days ago
user1.subscription_trial_ends_at = 4.days.ago + 1.hour
user1.subscription_sync_code = 'trial_ended'
user1.trial_expires_reminder1_sent_at = 5.days.ago
user1.save! user1.save!
user2.subscription_trial_ends_at = 5.days.ago user2.subscription_trial_ends_at = 4.days.ago + 1.hour
user2.subscription_last_checked_at = 1.days.ago user2.subscription_sync_code = 'trial_ended'
user2.trial_expires_reminder1_sent_at = Time.now user2.trial_expires_reminder1_sent_at = Time.now
user2.save! user2.save!
TrialExpiresReminder.send_reminders TrialExpiresReminder.send_reminders
expect(ActionMailer::Base.deliveries.count).to eq(1) expect(ActionMailer::Base.deliveries.count).to eq(1)
expect(ActionMailer::Base.deliveries.map(&:to).flatten).to include(user2.email) expect(ActionMailer::Base.deliveries.map(&:to).flatten).to include(user1.email)
# Check if the second reminder email was sent by verifying the subject # Check if the second reminder email was sent by verifying the subject
expect(ActionMailer::Base.deliveries.last.subject).to include("Dont forget to check your options to keep playing") expect(ActionMailer::Base.deliveries.last.subject).to include("Dont forget to check your options to keep playing")
expect(user2.reload.trial_expires_reminder2_sent_at).not_to be_nil expect(user1.reload.trial_expires_reminder2_sent_at).not_to be_nil
no_more_emails_sent
end end
it "sends the third reminder email to users whose trials are about to expire" do it "sends the third reminder email to users whose trials are about to expire" do
user1.subscription_trial_ends_at = 10.days.ago user1.reload
user1.subscription_last_checked_at = 1.days.ago user1.subscription_trial_ends_at = 10.days.ago + 1.hour
user1.trial_expires_reminder1_sent_at = 6.days.ago user1.subscription_sync_code = 'trial_ended'
user1.trial_expires_reminder2_sent_at = 4.days.ago user1.trial_expires_reminder1_sent_at = 8.days.ago
user1.trial_expires_reminder2_sent_at = 9.days.ago
user1.save! user1.save!
TrialExpiresReminder.send_reminders TrialExpiresReminder.send_reminders
@ -85,22 +101,28 @@ describe TrialExpiresReminder do
expect(ActionMailer::Base.deliveries.last.subject).to include("One last reminder!") expect(ActionMailer::Base.deliveries.last.subject).to include("One last reminder!")
expect(user1.reload.trial_expires_reminder3_sent_at).not_to be_nil expect(user1.reload.trial_expires_reminder3_sent_at).not_to be_nil
no_more_emails_sent
end end
it "sends first and second and third reminder emails to users whose trials are about to expire" do it "sends first and second and third reminder emails to users whose trials are about to expire" do
user1.reload
user2.reload
user3.reload
user1.subscription_trial_ends_at = 2.days.ago user1.subscription_trial_ends_at = 2.days.ago
user1.subscription_last_checked_at = 1.days.ago user1.subscription_sync_code = 'trial_ended'
user1.save! user1.save!
user2.subscription_trial_ends_at = 6.days.ago user2.subscription_trial_ends_at = 2.days.ago
user2.subscription_last_checked_at = 1.days.ago user2.trial_expires_reminder1_sent_at = 5.days.ago
user2.trial_expires_reminder1_sent_at = 4.days.ago user2.subscription_sync_code = 'trial_ended'
user2.save! user2.save!
user3.subscription_trial_ends_at = 10.days.ago user3.subscription_trial_ends_at = 2.days.ago
user3.subscription_last_checked_at = 2.days.ago user3.trial_expires_reminder1_sent_at = 8.days.ago
user3.trial_expires_reminder1_sent_at = 6.days.ago user3.trial_expires_reminder2_sent_at = 9.days.ago
user3.trial_expires_reminder2_sent_at = 4.days.ago user3.subscription_sync_code = 'trial_ended'
user3.save! user3.save!
TrialExpiresReminder.send_reminders TrialExpiresReminder.send_reminders
@ -112,5 +134,6 @@ describe TrialExpiresReminder do
expect(ActionMailer::Base.deliveries.count).to eq(3) expect(ActionMailer::Base.deliveries.count).to eq(3)
expect(ActionMailer::Base.deliveries.map(&:to).flatten).to include(user1.email, user2.email, user3.email) expect(ActionMailer::Base.deliveries.map(&:to).flatten).to include(user1.email, user2.email, user3.email)
no_more_emails_sent
end end
end end