DEV: Remove the use of stubs and mocks in `Jobs::UserEmail` tests.

We can only be sure that an email is sent when we get a mailer in
`ActionMailer::Deliveries`. A couple of tests were actually incorrect
because it didn't flow through our email sender where there are more
conditions in determining whether an email is sent or not.
This commit is contained in:
Guo Xiang Tan 2019-03-12 09:39:16 +08:00
parent 32bae48fd3
commit 34b29f62db
3 changed files with 134 additions and 57 deletions

View File

@ -54,7 +54,10 @@ module Jobs
) )
if message if message
Email::Sender.new(message, type, user).send Email::Sender.new(message, type, user).send(
is_critical: self.class == Jobs::CriticalUserEmail
)
if (b = user.user_stat.bounce_score) > SiteSetting.bounce_score_erode_on_send if (b = user.user_stat.bounce_score) > SiteSetting.bounce_score_erode_on_send
# erode bounce score each time we send an email # erode bounce score each time we send an email
# this means that we are punished a lot less for bounces # this means that we are punished a lot less for bounces

View File

@ -21,8 +21,13 @@ module Email
@user = user @user = user
end end
def send def send(is_critical: false)
return if SiteSetting.disable_emails == "yes" && @email_type.to_s != "admin_login" if SiteSetting.disable_emails == "yes" &&
@email_type.to_s != "admin_login" &&
!is_critical
return
end
return if ActionMailer::Base::NullMail === @message return if ActionMailer::Base::NullMail === @message
return if ActionMailer::Base::NullMail === (@message.message rescue nil) return if ActionMailer::Base::NullMail === (@message.message rescue nil)

View File

@ -11,7 +11,6 @@ describe Jobs::UserEmail do
let(:staged) { Fabricate(:user, staged: true, last_seen_at: 11.minutes.ago) } let(:staged) { Fabricate(:user, staged: true, last_seen_at: 11.minutes.ago) }
let(:suspended) { Fabricate(:user, last_seen_at: 10.minutes.ago, suspended_at: 5.minutes.ago, suspended_till: 7.days.from_now) } let(:suspended) { Fabricate(:user, last_seen_at: 10.minutes.ago, suspended_at: 5.minutes.ago, suspended_till: 7.days.from_now) }
let(:anonymous) { Fabricate(:anonymous, last_seen_at: 11.minutes.ago) } let(:anonymous) { Fabricate(:anonymous, last_seen_at: 11.minutes.ago) }
let(:mailer) { Mail::Message.new(to: user.email) }
it "raises an error when there is no user" do it "raises an error when there is no user" do
expect { Jobs::UserEmail.new.execute(type: :digest) }.to raise_error(Discourse::InvalidParameters) expect { Jobs::UserEmail.new.execute(type: :digest) }.to raise_error(Discourse::InvalidParameters)
@ -26,13 +25,15 @@ describe Jobs::UserEmail do
end end
it "doesn't call the mailer when the user is missing" do it "doesn't call the mailer when the user is missing" do
UserNotifications.expects(:digest).never
Jobs::UserEmail.new.execute(type: :digest, user_id: 1234) Jobs::UserEmail.new.execute(type: :digest, user_id: 1234)
expect(ActionMailer::Base.deliveries).to eq([])
end end
it "doesn't call the mailer when the user is staged" do it "doesn't call the mailer when the user is staged" do
UserNotifications.expects(:digest).never
Jobs::UserEmail.new.execute(type: :digest, user_id: staged.id) Jobs::UserEmail.new.execute(type: :digest, user_id: staged.id)
expect(ActionMailer::Base.deliveries).to eq([])
end end
context "bounce score" do context "bounce score" do
@ -45,55 +46,48 @@ describe Jobs::UserEmail do
email_log = EmailLog.where(user_id: user.id).last email_log = EmailLog.where(user_id: user.id).last
expect(email_log.email_type).to eq("signup") expect(email_log.email_type).to eq("signup")
expect(ActionMailer::Base.deliveries.first.to).to contain_exactly(
user.email
)
end end
end end
context 'to_address' do context 'to_address' do
it 'overwrites a to_address when present' do it 'overwrites a to_address when present' do
UserNotifications.expects(:confirm_new_email).returns(mailer)
Email::Sender.any_instance.expects(:send)
Jobs::UserEmail.new.execute(type: :confirm_new_email, user_id: user.id, to_address: 'jake@adventuretime.ooo') Jobs::UserEmail.new.execute(type: :confirm_new_email, user_id: user.id, to_address: 'jake@adventuretime.ooo')
expect(mailer.to).to eq(['jake@adventuretime.ooo'])
expect(ActionMailer::Base.deliveries.first.to).to contain_exactly(
'jake@adventuretime.ooo'
)
end end
end end
context "disable_emails setting" do context "disable_emails setting" do
it "sends when no" do it "sends when no" do
SiteSetting.disable_emails = 'no' SiteSetting.disable_emails = 'no'
Email::Sender.any_instance.expects(:send).once
Jobs::UserEmail.new.execute(type: :confirm_new_email, user_id: user.id) Jobs::UserEmail.new.execute(type: :confirm_new_email, user_id: user.id)
expect(ActionMailer::Base.deliveries.first.to).to contain_exactly(
user.email
)
end end
it "does not send an email when yes" do it "does not send an email when yes" do
SiteSetting.disable_emails = 'yes' SiteSetting.disable_emails = 'yes'
Email::Sender.any_instance.expects(:send).never
Jobs::UserEmail.new.execute(type: :confirm_new_email, user_id: user.id) Jobs::UserEmail.new.execute(type: :confirm_new_email, user_id: user.id)
expect(ActionMailer::Base.deliveries).to eq([])
end end
it "sends when critical" do it "sends when critical" do
SiteSetting.disable_emails = 'yes' SiteSetting.disable_emails = 'yes'
Email::Sender.any_instance.expects(:send)
Jobs::CriticalUserEmail.new.execute(type: :confirm_new_email, user_id: user.id) Jobs::CriticalUserEmail.new.execute(type: :confirm_new_email, user_id: user.id)
end
end
context "recently seen" do expect(ActionMailer::Base.deliveries.first.to).to contain_exactly(
let(:post) { Fabricate(:post, user: user) } user.email
)
it "doesn't send an email to a user that's been recently seen" do
user.update_column(:last_seen_at, 9.minutes.ago)
Email::Sender.any_instance.expects(:send).never
Jobs::UserEmail.new.execute(type: :user_replied, user_id: user.id, post_id: post.id)
end
it "does send an email to a user that's been recently seen but has email_always set" do
user.update_attributes(last_seen_at: 9.minutes.ago)
user.user_option.update_attributes(email_always: true)
PostTiming.create!(topic_id: post.topic_id, post_number: post.post_number, user_id: user.id, msecs: 100)
Email::Sender.any_instance.expects(:send)
Jobs::UserEmail.new.execute(type: :user_replied, user_id: user.id, post_id: post.id)
end end
end end
@ -145,49 +139,65 @@ describe Jobs::UserEmail do
context 'args' do context 'args' do
it 'passes a token as an argument when a token is present' do it 'passes a token as an argument when a token is present' do
UserNotifications.expects(:forgot_password).with(user, email_token: 'asdfasdf').returns(mailer)
Email::Sender.any_instance.expects(:send)
Jobs::UserEmail.new.execute(type: :forgot_password, user_id: user.id, email_token: 'asdfasdf') Jobs::UserEmail.new.execute(type: :forgot_password, user_id: user.id, email_token: 'asdfasdf')
mail = ActionMailer::Base.deliveries.first
expect(mail.to).to contain_exactly(user.email)
expect(mail.body).to include("asdfasdf")
end end
context "post" do context "post" do
let(:post) { Fabricate(:post, user: user) } let(:post) { Fabricate(:post, user: user) }
it 'passes a post as an argument when a post_id is present' do
UserNotifications.expects(:user_private_message).with(user, post: post).returns(mailer)
Email::Sender.any_instance.expects(:send)
Jobs::UserEmail.new.execute(type: :user_private_message, user_id: user.id, post_id: post.id)
end
it "doesn't send the email if you've seen the post" do it "doesn't send the email if you've seen the post" do
Email::Sender.any_instance.expects(:send).never
PostTiming.record_timing(topic_id: post.topic_id, user_id: user.id, post_number: post.post_number, msecs: 6666) PostTiming.record_timing(topic_id: post.topic_id, user_id: user.id, post_number: post.post_number, msecs: 6666)
Jobs::UserEmail.new.execute(type: :user_private_message, user_id: user.id, post_id: post.id) Jobs::UserEmail.new.execute(type: :user_private_message, user_id: user.id, post_id: post.id)
expect(ActionMailer::Base.deliveries).to eq([])
end end
it "doesn't send the email if the user deleted the post" do it "doesn't send the email if the user deleted the post" do
Email::Sender.any_instance.expects(:send).never
post.update_column(:user_deleted, true) post.update_column(:user_deleted, true)
Jobs::UserEmail.new.execute(type: :user_private_message, user_id: user.id, post_id: post.id) Jobs::UserEmail.new.execute(type: :user_private_message, user_id: user.id, post_id: post.id)
expect(ActionMailer::Base.deliveries).to eq([])
end end
it "doesn't send the email if user of the post has been deleted" do it "doesn't send the email if user of the post has been deleted" do
Email::Sender.any_instance.expects(:send).never
post.update_attributes!(user_id: nil) post.update_attributes!(user_id: nil)
Jobs::UserEmail.new.execute(type: :user_replied, user_id: user.id, post_id: post.id) Jobs::UserEmail.new.execute(type: :user_replied, user_id: user.id, post_id: post.id)
expect(ActionMailer::Base.deliveries).to eq([])
end end
context 'user is suspended' do context 'user is suspended' do
it "doesn't send email for a pm from a regular user" do it "doesn't send email for a pm from a regular user" do
Email::Sender.any_instance.expects(:send).never
Jobs::UserEmail.new.execute(type: :user_private_message, user_id: suspended.id, post_id: post.id) Jobs::UserEmail.new.execute(type: :user_private_message, user_id: suspended.id, post_id: post.id)
expect(ActionMailer::Base.deliveries).to eq([])
end end
it "does send an email for a pm from a staff user" do it "does send an email for a pm from a staff user" do
pm_from_staff = Fabricate(:post, user: Fabricate(:moderator)) pm_from_staff = Fabricate(:post, user: Fabricate(:moderator))
pm_from_staff.topic.topic_allowed_users.create!(user_id: suspended.id) pm_from_staff.topic.topic_allowed_users.create!(user_id: suspended.id)
Email::Sender.any_instance.expects(:send)
Jobs::UserEmail.new.execute(type: :user_private_message, user_id: suspended.id, post_id: pm_from_staff.id) pm_notification = Fabricate(:notification,
user: suspended,
topic: pm_from_staff.topic,
post_number: pm_from_staff.post_number,
data: { original_post_id: pm_from_staff.id }.to_json
)
Jobs::UserEmail.new.execute(type: :user_private_message,
user_id: suspended.id,
post_id: pm_from_staff.id,
notification_id: pm_notification.id
)
expect(ActionMailer::Base.deliveries.first.to).to contain_exactly(
suspended.email
)
end end
end end
@ -195,15 +205,17 @@ describe Jobs::UserEmail do
before { SiteSetting.allow_anonymous_posting = true } before { SiteSetting.allow_anonymous_posting = true }
it "doesn't send email for a pm from a regular user" do it "doesn't send email for a pm from a regular user" do
Email::Sender.any_instance.expects(:send).never
Jobs::UserEmail.new.execute(type: :user_private_message, user_id: anonymous.id, post_id: post.id) Jobs::UserEmail.new.execute(type: :user_private_message, user_id: anonymous.id, post_id: post.id)
expect(ActionMailer::Base.deliveries).to eq([])
end end
it "doesn't send email for a pm from a staff user" do it "doesn't send email for a pm from a staff user" do
pm_from_staff = Fabricate(:post, user: Fabricate(:moderator)) pm_from_staff = Fabricate(:post, user: Fabricate(:moderator))
pm_from_staff.topic.topic_allowed_users.create!(user_id: anonymous.id) pm_from_staff.topic.topic_allowed_users.create!(user_id: anonymous.id)
Email::Sender.any_instance.expects(:send).never
Jobs::UserEmail.new.execute(type: :user_private_message, user_id: anonymous.id, post_id: pm_from_staff.id) Jobs::UserEmail.new.execute(type: :user_private_message, user_id: anonymous.id, post_id: pm_from_staff.id)
expect(ActionMailer::Base.deliveries).to eq([])
end end
end end
end end
@ -244,17 +256,61 @@ describe Jobs::UserEmail do
end end
it "does send the email if the notification has been seen but the user is set for email_always" do it "does send the email if the notification has been seen but the user is set for email_always" do
Email::Sender.any_instance.expects(:send)
notification.update_column(:read, true) notification.update_column(:read, true)
user.user_option.update_column(:email_always, true) user.user_option.update_column(:email_always, true)
Jobs::UserEmail.new.execute(type: :user_mentioned, user_id: user.id, notification_id: notification.id)
Jobs::UserEmail.new.execute(type: :user_mentioned,
user_id: user.id,
post_id: post.id,
notification_id: notification.id
)
expect(ActionMailer::Base.deliveries.first.to).to contain_exactly(
user.email
)
end end
it "does send the email if the user is using daily mailing list mode" do it "does send the email if the user is using daily mailing list mode" do
Email::Sender.any_instance.expects(:send)
user.user_option.update(mailing_list_mode: true, mailing_list_mode_frequency: 0) user.user_option.update(mailing_list_mode: true, mailing_list_mode_frequency: 0)
Jobs::UserEmail.new.execute(type: :user_mentioned, user_id: user.id, notification_id: notification.id) Jobs::UserEmail.new.execute(type: :user_mentioned,
user_id: user.id,
post_id: post.id,
notification_id: notification.id
)
expect(ActionMailer::Base.deliveries.first.to).to contain_exactly(
user.email
)
end
context "recently seen" do
it "doesn't send an email to a user that's been recently seen" do
user.update!(last_seen_at: 9.minutes.ago)
Jobs::UserEmail.new.execute(type: :user_replied,
user_id: user.id,
post_id: post.id,
notification_id: notification.id
)
expect(ActionMailer::Base.deliveries).to eq([])
end
it "does send an email to a user that's been recently seen but has email_always set" do
user.update!(last_seen_at: 9.minutes.ago)
user.user_option.update!(email_always: true)
Jobs::UserEmail.new.execute(type: :user_replied,
user_id: user.id,
post_id: post.id,
notification_id: notification.id
)
expect(ActionMailer::Base.deliveries.first.to).to contain_exactly(
user.email
)
end
end end
context 'max_emails_per_day_per_user limit is reached' do context 'max_emails_per_day_per_user limit is reached' do
@ -361,7 +417,6 @@ describe Jobs::UserEmail do
end end
it "doesn't send the mail if the user is using individual mailing list mode" do it "doesn't send the mail if the user is using individual mailing list mode" do
Email::Sender.any_instance.expects(:send).never
user.user_option.update(mailing_list_mode: true, mailing_list_mode_frequency: 1) user.user_option.update(mailing_list_mode: true, mailing_list_mode_frequency: 1)
# sometimes, we pass the notification_id # sometimes, we pass the notification_id
Jobs::UserEmail.new.execute(type: :user_mentioned, user_id: user.id, notification_id: notification.id, post_id: post.id) Jobs::UserEmail.new.execute(type: :user_mentioned, user_id: user.id, notification_id: notification.id, post_id: post.id)
@ -373,10 +428,11 @@ describe Jobs::UserEmail do
post = Fabricate(:post) post = Fabricate(:post)
post.topic.destroy post.topic.destroy
Jobs::UserEmail.new.execute(type: :user_mentioned, user_id: user.id, notification_type: "posted", post_id: post.id) Jobs::UserEmail.new.execute(type: :user_mentioned, user_id: user.id, notification_type: "posted", post_id: post.id)
expect(ActionMailer::Base.deliveries).to eq([])
end end
it "doesn't send the mail if the user is using individual mailing list mode with no echo" do it "doesn't send the mail if the user is using individual mailing list mode with no echo" do
Email::Sender.any_instance.expects(:send).never
user.user_option.update(mailing_list_mode: true, mailing_list_mode_frequency: 2) user.user_option.update(mailing_list_mode: true, mailing_list_mode_frequency: 2)
# sometimes, we pass the notification_id # sometimes, we pass the notification_id
Jobs::UserEmail.new.execute(type: :user_mentioned, user_id: user.id, notification_id: notification.id, post_id: post.id) Jobs::UserEmail.new.execute(type: :user_mentioned, user_id: user.id, notification_id: notification.id, post_id: post.id)
@ -388,12 +444,15 @@ describe Jobs::UserEmail do
post = Fabricate(:post) post = Fabricate(:post)
post.topic.destroy post.topic.destroy
Jobs::UserEmail.new.execute(type: :user_mentioned, user_id: user.id, notification_type: "posted", post_id: post.id) Jobs::UserEmail.new.execute(type: :user_mentioned, user_id: user.id, notification_type: "posted", post_id: post.id)
expect(ActionMailer::Base.deliveries).to eq([])
end end
it "doesn't send the email if the post has been user deleted" do it "doesn't send the email if the post has been user deleted" do
Email::Sender.any_instance.expects(:send).never
post.update_column(:user_deleted, true) post.update_column(:user_deleted, true)
Jobs::UserEmail.new.execute(type: :user_mentioned, user_id: user.id, notification_id: notification.id, post_id: post.id) Jobs::UserEmail.new.execute(type: :user_mentioned, user_id: user.id, notification_id: notification.id, post_id: post.id)
expect(ActionMailer::Base.deliveries).to eq([])
end end
context 'user is suspended' do context 'user is suspended' do
@ -450,8 +509,13 @@ describe Jobs::UserEmail do
before { SiteSetting.allow_anonymous_posting = true } before { SiteSetting.allow_anonymous_posting = true }
it "doesn't send email for a pm from a regular user" do it "doesn't send email for a pm from a regular user" do
Email::Sender.any_instance.expects(:send).never Jobs::UserEmail.new.execute(type: :user_private_message,
Jobs::UserEmail.new.execute(type: :user_private_message, user_id: anonymous.id, notification_id: notification.id) user_id: anonymous.id,
post_id: post.id,
notification_id: notification.id
)
expect(ActionMailer::Base.deliveries).to eq([])
end end
it "doesn't send email for a pm from staff" do it "doesn't send email for a pm from staff" do
@ -463,8 +527,13 @@ describe Jobs::UserEmail do
post_number: pm_from_staff.post_number, post_number: pm_from_staff.post_number,
data: { original_post_id: pm_from_staff.id }.to_json data: { original_post_id: pm_from_staff.id }.to_json
) )
Email::Sender.any_instance.expects(:send).never Jobs::UserEmail.new.execute(type: :user_private_message,
Jobs::UserEmail.new.execute(type: :user_private_message, user_id: anonymous.id, notification_id: pm_notification.id) user_id: anonymous.id,
post_id: pm_from_staff.id,
notification_id: pm_notification.id
)
expect(ActionMailer::Base.deliveries).to eq([])
end end
end end
end end