discourse/spec/lib/email/processor_spec.rb
David Battersby 1de8361d2e
FIX: Prevent Email Processor errors when mail is blank or nil (#21292)
Currently processing emails that are blank or have a nil value for the mail will cause several errors.

This update allows emails with blank body or missing sender to log the blank email error to the mail logs rather than throwing an error.
2023-05-18 10:39:37 +08:00

231 lines
7.3 KiB
Ruby

# frozen_string_literal: true
require "email/processor"
RSpec.describe Email::Processor do
after { Discourse.redis.flushdb }
let(:from) { "foo@bar.com" }
context "when reply via email is too short" do
let(:mail) { file_from_fixtures("chinese_reply.eml", "emails").read }
fab!(:post) { Fabricate(:post) }
fab!(:user) { Fabricate(:user, email: "discourse@bar.com") }
fab!(:post_reply_key) do
Fabricate(
:post_reply_key,
user: user,
post: post,
reply_key: "4f97315cc828096c9cb34c6f1a0d6fe8",
)
end
before do
SiteSetting.email_in = true
SiteSetting.reply_by_email_address = "reply+%{reply_key}@bar.com"
SiteSetting.min_post_length = 1000
end
it "rejects reply and sends an email with custom error message" do
processor = Email::Processor.new(mail)
processor.process!
rejection_raw = ActionMailer::Base.deliveries.first.body.raw_source
count = SiteSetting.min_post_length
destination = processor.receiver.mail.to
former_title = processor.receiver.mail.subject
expect(rejection_raw.gsub(/\r/, "")).to eq(
I18n.t(
"system_messages.email_reject_post_too_short.text_body_template",
count: count,
destination: destination,
former_title: former_title,
).gsub(/\r/, ""),
)
end
end
describe "when mail is not set" do
it "does not raise an error" do
expect { Email::Processor.process!(nil) }.not_to raise_error
expect { Email::Processor.process!("") }.not_to raise_error
end
end
describe "rate limits" do
let(:mail) { "From: #{from}\nTo: bar@foo.com\nSubject: FOO BAR\n\nFoo foo bar bar?" }
let(:limit_exceeded) { RateLimiter::LimitExceeded.new(10) }
before { Email::Receiver.any_instance.expects(:process!).raises(limit_exceeded) }
it "enqueues a background job by default" do
expect_enqueued_with(job: :process_email, args: { mail: mail }) do
Email::Processor.process!(mail, retry_on_rate_limit: true)
end
end
it "doesn't enqueue a background job when retry is disabled" do
expect_not_enqueued_with(job: :process_email, args: { mail: mail }) do
expect { Email::Processor.process!(mail, retry_on_rate_limit: false) }.to raise_error(
limit_exceeded,
)
end
end
end
describe "known error" do
let(:mail) { "From: #{from}\nTo: bar@foo.com" }
let(:mail2) { "From: #{from}\nTo: foo@foo.com" }
let(:mail3) { "From: #{from}\nTo: foobar@foo.com" }
it "only sends one rejection email per day" do
key = "rejection_email:#{[from]}:email_reject_empty:#{Date.today}"
Discourse.redis.expire(key, 0)
expect { Email::Processor.process!(mail) }.to change { EmailLog.count }.by(1)
expect { Email::Processor.process!(mail2) }.not_to change { EmailLog.count }
freeze_time(Date.today + 1)
key = "rejection_email:#{[from]}:email_reject_empty:#{Date.today}"
Discourse.redis.expire(key, 0)
expect { Email::Processor.process!(mail3) }.to change { EmailLog.count }.by(1)
end
end
describe "unrecognized error" do
let(:mail) do
"Date: Fri, 15 Jan 2016 00:12:43 +0100\nFrom: #{from}\nTo: bar@foo.com\nSubject: FOO BAR\n\nFoo foo bar bar?"
end
let(:mail2) do
"Date: Fri, 15 Jan 2016 00:12:43 +0100\nFrom: #{from}\nTo: foo@foo.com\nSubject: BAR BAR\n\nBar bar bar bar?"
end
it "sends a rejection email on an unrecognized error" do
begin
@orig_logger = Rails.logger
Rails.logger = @fake_logger = FakeLogger.new
Email::Processor.any_instance.stubs(:can_send_rejection_email?).returns(true)
Email::Receiver.any_instance.stubs(:process_internal).raises("boom")
Email::Processor.process!(mail)
errors = @fake_logger.errors
expect(errors.size).to eq(1)
expect(errors.first).to include("boom")
incoming_email = IncomingEmail.last
expect(incoming_email.error).to eq("RuntimeError")
expect(incoming_email.rejection_message).to be_present
expect(EmailLog.last.email_type).to eq("email_reject_unrecognized_error")
ensure
Rails.logger = @orig_logger
end
end
it "sends more than one rejection email per day" do
Email::Receiver.any_instance.stubs(:process_internal).raises("boom")
key = "rejection_email:#{[from]}:email_reject_unrecognized_error:#{Date.today}"
Discourse.redis.expire(key, 0)
expect { Email::Processor.process!(mail) }.to change { EmailLog.count }.by(1)
expect { Email::Processor.process!(mail2) }.to change { EmailLog.count }.by(1)
end
end
describe "from reply to email address" do
let(:mail) do
"Date: Fri, 15 Jan 2016 00:12:43 +0100\nFrom: reply@bar.com\nTo: reply@bar.com\nSubject: FOO BAR\n\nFoo foo bar bar?"
end
it "ignores the email" do
Email::Receiver
.any_instance
.stubs(:process_internal)
.raises(Email::Receiver::FromReplyByAddressError.new)
expect { Email::Processor.process!(mail) }.not_to change { EmailLog.count }
end
end
describe "mailinglist mirror" do
before do
SiteSetting.email_in = true
Fabricate(:mailinglist_mirror_category)
end
it "does not send rejection email" do
Email::Receiver.any_instance.stubs(:process_internal).raises("boom")
email = <<~EMAIL
From: foo@example.com
To: list@example.com
Subject: Hello world
EMAIL
expect { Email::Processor.process!(email) }.to_not change { EmailLog.count }
end
end
describe "when replying to a post that is too old" do
fab!(:user) { Fabricate(:user, email: "discourse@bar.com") }
fab!(:topic) { Fabricate(:topic) }
fab!(:post) { Fabricate(:post, topic: topic, created_at: 3.days.ago) }
let(:mail) do
file_from_fixtures("old_destination.eml", "emails")
.read
.gsub("424242", topic.id.to_s)
.gsub("123456", post.id.to_s)
end
it "rejects the email with the right response" do
SiteSetting.disallow_reply_by_email_after_days = 2
processor = Email::Processor.new(mail)
processor.process!
rejection_raw = ActionMailer::Base.deliveries.first.body.to_s
expect(rejection_raw).to eq(
I18n.t(
"system_messages.email_reject_old_destination.text_body_template",
destination: '["reply+4f97315cc828096c9cb34c6f1a0d6fe8@bar.com"]',
former_title: "Some Old Post",
short_url: "#{Discourse.base_url}/p/#{post.id}",
number_of_days: 2,
),
)
end
end
describe "when group email recipients exceeds maximum_recipients_per_new_group_email site setting" do
let(:mail) { file_from_fixtures("cc.eml", "emails").read }
it "rejects the email with the right response" do
SiteSetting.maximum_recipients_per_new_group_email = 3
processor = Email::Processor.new(mail)
processor.process!
rejection_raw = ActionMailer::Base.deliveries.first.body.to_s
expect(rejection_raw).to eq(
I18n.t(
"system_messages.email_reject_too_many_recipients.text_body_template",
destination: '["someone@else.com"]',
former_title: "The more, the merrier",
max_recipients_count: 3,
base_url: Discourse.base_url,
),
)
end
end
end