2
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-03-05 15:27:34 +08:00
discourse/spec/lib/email/processor_spec.rb
Michael Brown a2b22d182e
FIX: email to a category should not require enabling reply-by-email (#37227)
DEV: email/receiver tests should explicitly test alternative_reply_by_email_addresses

Some of the email/receiver tests were using an alternative reply address in
their data, implicitly testing that functionality.

This commit modifies the test email fixtures to always use the main reply
address, and added a separate test explicitly testing the functionality of
`alternative_reply_by_email_addresses`.

This serves the mission of configuring as little as possible to not rely on
side effects.

DEV: email/processor tests should explicitly configure reply-by-email

Tests that rely on reply-by-email being configured should explicitly do so.

They're now moved to a common context in the same style as email/receiver
specs.

FIX: email to a category should not require enable reply-by-email

This commit removes the side effect of disabling category email address lookups
when `reply_by_email_enabled` is not configured, restoring functionality to the
situation where a category's `email_in` is configured without
`reply_by_email_enabled`.

Added tests for each of these situations.

DEV: separate tests for email receiver by whether reply_by_email should be configured

The impetus for this change is commit e05ef50c introducing an unintended side
effect that *should* have caused tests to fail; specifically the situation
where `email_in` is enabled and a category has an incoming email address
configured, but `reply_by_email_enabled` is *not* enabled.

The change in the referenced commit broke this configuration, but this was not
noticed as the spec always configured `reply_by_email_enabled`.

The only change made in this commit is reorganising the tests such that
reply-by-email is only configured when necessary. Thus, this commit *alone*
will break tests.
2026-02-03 13:01:16 -05:00

229 lines
7.5 KiB
Ruby

# frozen_string_literal: true
require "email/processor"
RSpec.describe Email::Processor do
def configure_reply_by_email
SiteSetting.reply_by_email_address = "reply+%{reply_key}@bar.com"
SiteSetting.manual_polling_enabled = true
SiteSetting.reply_by_email_enabled = true
end
after { Discourse.redis.flushdb }
let(:from) { "foo@bar.com" }
context "with reply_by_email configured" do
before { configure_reply_by_email }
context "when reply via email is too short" do
let(:mail) { file_from_fixtures("chinese_reply.eml", "emails").read }
fab!(:post)
fab!(:user) { Fabricate(:user, email: "discourse@bar.com", refresh_auto_groups: true) }
fab!(:post_reply_key) do
Fabricate(
:post_reply_key,
user: user,
post: post,
reply_key: "4f97315cc828096c9cb34c6f1a0d6fe8",
)
end
before { SiteSetting.min_post_length = 1000 }
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 "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 { Fabricate(:mailinglist_mirror_category) }
it "does not send rejection email" do
SiteSetting.email_in = true
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)
fab!(:post) { Fabricate(:post, topic: topic, created_at: 3.days.ago) }
let(:mail) do
file_from_fixtures("old_destination.eml", "emails").read.gsub(":post_id", 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
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
let(:fake_logger) { FakeLogger.new }
before { Rails.logger.broadcast_to(fake_logger) }
after { Rails.logger.stop_broadcasting_to(fake_logger) }
it "sends a rejection email on an unrecognized error" do
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")
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 "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