Merge pull request #2614 from riking/email-tests

Email tests, and include posting error reason
This commit is contained in:
Robin Ward 2014-08-01 16:33:00 -04:00
commit 6eb478f5fa
9 changed files with 452 additions and 84 deletions

View File

@ -24,40 +24,52 @@ module Jobs
mail_string = mail.pop
Email::Receiver.new(mail_string).process
rescue => e
message_template = nil
case e
when Email::Receiver::UserNotSufficientTrustLevelError
message_template = :email_reject_trust_level
when Email::Receiver::UserNotFoundError
message_template = :email_reject_no_account
when Email::Receiver::EmptyEmailError
message_template = :email_reject_empty
when Email::Receiver::EmailUnparsableError
message_template = :email_reject_parsing
when Email::Receiver::EmailLogNotFound
message_template = :email_reject_reply_key
when ActiveRecord::Rollback
message_template = :email_reject_post_error
when Email::Receiver::InvalidPost
# TODO there is a message in this exception, place it in email
message_template = :email_reject_post_error
else
message_template = nil
end
if message_template
# inform the user about the rejection
message = Mail::Message.new(mail_string)
client_message = RejectionMailer.send_rejection(message.from, message.body, message.subject, message.to, message_template)
Email::Sender.new(client_message, message_template).send
else
Discourse.handle_exception(e, error_context(@args, "Unrecognized error type when processing incoming email", mail: mail_string))
end
handle_failure(mail_string, e)
ensure
mail.delete
end
end
def handle_failure(mail_string, e)
template_args = {}
case e
when Email::Receiver::UserNotSufficientTrustLevelError
message_template = :email_reject_trust_level
when Email::Receiver::UserNotFoundError
message_template = :email_reject_no_account
when Email::Receiver::EmptyEmailError
message_template = :email_reject_empty
when Email::Receiver::EmailUnparsableError
message_template = :email_reject_parsing
when Email::Receiver::EmailLogNotFound
message_template = :email_reject_reply_key
when ActiveRecord::Rollback
message_template = :email_reject_post_error
when Email::Receiver::InvalidPost
if e.message.length < 6
message_template = :email_reject_post_error
else
message_template = :email_reject_post_error_specified
template_args[:post_error] = e.message
end
else
message_template = nil
end
if message_template
# inform the user about the rejection
message = Mail::Message.new(mail_string)
template_args[:former_title] = message.subject
template_args[:destination] = message.to
client_message = RejectionMailer.send_rejection(message_template, message.from, template_args)
Email::Sender.new(client_message, message_template).send
else
Discourse.handle_exception(e, error_context(@args, "Unrecognized error type when processing incoming email", mail: mail_string))
end
end
def poll_pop3s
if !SiteSetting.pop3s_polling_insecure
Net::POP3.enable_ssl(OpenSSL::SSL::VERIFY_NONE)

View File

@ -3,12 +3,27 @@ require_dependency 'email/message_builder'
class RejectionMailer < ActionMailer::Base
include Email::BuildEmailHelper
def send_rejection(message_from, message_body, message_subject, forum_address, template)
build_email(message_from,
template: "system_messages.#{template}",
source: message_body,
former_title: message_subject,
destination: forum_address)
DISALLOWED_TEMPLATE_ARGS = [:to, :from, :site_name, :base_url,
:user_preferences_url,
:include_respond_instructions, :html_override,
:add_unsubscribe_link, :respond_instructions,
:style, :body, :post_id, :topic_id, :subject,
:template, :allow_reply_by_email,
:private_reply, :from_alias]
# Send an email rejection message.
#
# template - i18n key under system_messages
# message_from - Who to send the rejection messsage to
# template_args - arguments to pass to i18n for interpolation into the message
# Certain keys are disallowed in template_args to avoid confusing the
# BuildEmailHelper. You can see the list in DISALLOWED_TEMPLATE_ARGS.
def send_rejection(template, message_from, template_args)
if template_args.keys.any? { |k| DISALLOWED_TEMPLATE_ARGS.include? k }
raise ArgumentError.new('Reserved key in template arguments')
end
build_email(message_from, template_args.merge(template: "system_messages.#{template}"))
end
end

View File

@ -1419,7 +1419,18 @@ en:
text_body_template: |
We're sorry, but your email message to %{destination} (titled %{former_title}) didn't work.
Some possible causes are: complex formatting, message too large, message too small. Please try again.
Some possible causes are: complex formatting, message too large, message too small. Please try again, or post via the website if this continues.
email_reject_post_error_specified:
subject_template: "Email issue -- Posting error"
text_body_template: |
We're sorry, but your email message to %{destination} (titled %{former_title}) didn't work.
Rejection message:
%{post_error}
Please attempt to fix the errors and try again.
email_reject_reply_key:
subject_template: "Email issue -- Bad Reply Key"

160
spec/fixtures/emails/bottom_reply.eml vendored Normal file
View File

@ -0,0 +1,160 @@
Received: by 10.107.19.29 with SMTP id b29csp111716ioj;
Wed, 30 Jul 2014 17:52:05 -0700 (PDT)
X-Received: by 10.194.238.6 with SMTP id vg6mr11340975wjc.24.1406767925330;
Wed, 30 Jul 2014 17:52:05 -0700 (PDT)
Received: from localhost (localhost [127.0.0.1])
by bendel.debian.org (Postfix) with QMQP
id 18F5C417; Thu, 31 Jul 2014 00:52:04 +0000 (UTC)
Old-Return-Path: <vorlon@debian.org>
X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on bendel.debian.org
X-Spam-Level:
X-Spam-Status: No, score=-25.9 required=4.0 tests=FOURLA,LDOSUBSCRIBER,
LDO_WHITELIST,MURPHY_DEBIAN_MESSAGE,PGPSIGNATURE autolearn=unavailable
version=3.3.2
X-Original-To: lists-debian-ctte@bendel.debian.org
Delivered-To: lists-debian-ctte@bendel.debian.org
Received: from localhost (localhost [127.0.0.1])
by bendel.debian.org (Postfix) with ESMTP id CE6CDEE
for <lists-debian-ctte@bendel.debian.org>; Thu, 31 Jul 2014 00:51:52 +0000 (UTC)
X-Virus-Scanned: at lists.debian.org with policy bank en-lt
X-Amavis-Spam-Status: No, score=-11.9 tagged_above=-10000 required=5.3
tests=[BAYES_00=-2, FOURLA=0.1, LDO_WHITELIST=-5, PGPSIGNATURE=-5]
autolearn=ham
Received: from bendel.debian.org ([127.0.0.1])
by localhost (lists.debian.org [127.0.0.1]) (amavisd-new, port 2525)
with ESMTP id SB451DwGZCOe for <lists-debian-ctte@bendel.debian.org>;
Thu, 31 Jul 2014 00:51:47 +0000 (UTC)
X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_BL_NJABL=-1.5 CL_IP_EQ_HELO_IP=-2 (check from: .debian. - helo: .becquer.dodds. - helo-domain: .dodds.) FROM/MX_MATCHES_NOT_HELO(DOMAIN)=0; rate: -5
Received: from becquer.dodds.net (becquer.dodds.net [207.224.24.209])
by bendel.debian.org (Postfix) with ESMTP id 8E89A2B4
for <debian-ctte@lists.debian.org>; Thu, 31 Jul 2014 00:51:47 +0000 (UTC)
Received: from virgil.dodds.net (unknown [192.168.15.59])
by becquer.dodds.net (Postfix) with ESMTPA id 9B0A9256EB
for <debian-ctte@lists.debian.org>; Wed, 30 Jul 2014 17:51:19 -0700 (PDT)
Received: by virgil.dodds.net (Postfix, from userid 1000)
id 942FB60199; Wed, 30 Jul 2014 17:51:15 -0700 (PDT)
Date: Wed, 30 Jul 2014 17:51:15 -0700
From: Jake <jake@adventuretime.ooo>
To: incoming+amazing@appmail.adventuretime.ooo
Subject: Re: Next Debian CTTE IRC Meeting at date -d'Thu Jul 31 17:00:00 UTC
2014'
Message-ID: <20140731005115.GA19044@virgil.dodds.net>
Mail-Followup-To: debian-ctte@lists.debian.org
References: <20140730213924.GA12356@teltox.donarmstrong.com>
MIME-Version: 1.0
Content-Type: multipart/signed; micalg=pgp-sha256;
protocol="application/pgp-signature"; boundary="qMm9M+Fa2AknHoGS"
Content-Disposition: inline
In-Reply-To: <20140730213924.GA12356@teltox.donarmstrong.com>
User-Agent: Mutt/1.5.23 (2014-03-12)
X-Debian-Message: Signature check passed for Debian member
X-Rc-Virus: 2007-09-13_01
X-Rc-Spam: 2008-11-04_01
Resent-Message-ID: <c_LGA2C2wUK.A.URD.zMZ2TB@bendel>
Resent-From: debian-ctte@lists.debian.org
X-Mailing-List: <debian-ctte@lists.debian.org> archive/latest/4791
X-Loop: debian-ctte@lists.debian.org
List-Id: <debian-ctte.lists.debian.org>
List-Post: <mailto:debian-ctte@lists.debian.org>
List-Help: <mailto:debian-ctte-request@lists.debian.org?subject=help>
List-Subscribe: <mailto:debian-ctte-request@lists.debian.org?subject=subscribe>
List-Unsubscribe: <mailto:debian-ctte-request@lists.debian.org?subject=unsubscribe>
Precedence: list
Resent-Sender: debian-ctte-request@lists.debian.org
Resent-Date: Thu, 31 Jul 2014 00:52:04 +0000 (UTC)
--qMm9M+Fa2AknHoGS
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable
On Wed, Jul 30, 2014 at 02:39:24PM -0700, Don Armstrong wrote:
> The next Debian CTTE IRC meeting is at=20
> date -d 'Thu Jul 31 17:00:00 UTC 2014' on irc.debian.org in
> #debian-ctte.
> The current meeting agenda is here, and more up-to-date ones may be
> found in the git repository.
> #startmeeting
> #topic Who is here?
> #topic Next Meeting?
> #topic #717076 Decide between libjpeg-turbo and libjpeg8 et al.
This has been voted on; should probably be removed from the agenda, someone
just needs to confirm the vote results and get it on the website. (AIUI the
archive has already begun moving on accordingly.)
> #topic #636783 constitution: super-majority bug
> #topic #636783 constitution: casting vote
> #topic #636783 constitution: minimum discussion period
> #topic #636783 constitution: TC member retirement/rollover
> #topic #636783 constitution: TC chair retirement/rollover
> #topic #681419 Depends: foo | foo-nonfree
> #topic #741573 menu systems and mime-support
> #topic #746715 init system fallout
Also voted and just needs to be confirmed.
> #topic #750135 Maintainer of aptitude package
>=20
> #topic #752400 Advice on util-linux
This has been closed by mutual agreement of the people involved and doesn't
require any action from the TC. Removed from the agenda.
There's also bug #744246, which was assigned to the TC at my request but
without any preamble so it may have escaped notice. However, that situation
has been evolving constructively among the related parties (apparently, an
informal comment from a member of the release team was mistaken for a
release team position, so that's now being revisited), so I don't believe
this is anything we need to put on the agenda for tomorrow despite being on
the open bug list.
--=20
Steve Langasek Give me a lever long enough and a Free OS
Debian Developer to set it on, and I can move the world.
Ubuntu Developer http://www.debian.org/
slangasek@ubuntu.com vorlon@debian.org
--qMm9M+Fa2AknHoGS
Content-Type: application/pgp-signature; name="signature.asc"
Content-Description: Digital signature
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1
iQIcBAEBCAAGBQJT2ZMDAAoJEFaNMPMhshM9GLsP/244S3wtYZEeVgJWIdB5PE0A
sZVezEA692y++/0oVZVecwV67yBOyfSjPPetdAph2UDMRtfurwxfj2BkbOFA2+Y6
++MErbmC3V7IGpd/L/fFGdXgvMQT2MNBpw0fnMA7bLpNjCvoj+Hr1HXRUcWoJSlj
WmHWwWSTVRcHg8a3iWYJzY6XfLyEEgHlahrlKvJExsTx/9mc1qg7g8KGdnhzHFBl
ttdH2fxpAk/624dReCcw5RKmOLfZ1HsEl9XcVe1cb4K+MDaQiXmoEK5v3xaNz1tS
NK5v2D5gDs229zoxKzQnnzOPLHxqI5E0L9PpI/mu4T9z7H2bHR3U5BvhnT99t5uw
ydf2cZNGY0uFCV3Rvn07BfAIW5WSXhOfN/5IymRKmdhjsTiwZ/wFjFrK8tVjtERu
yeyA7RIYiblGCEKYIYLWSxhoXeEdmAdfp6EA2/IA1CpgMB+ZdSfaeMgFY7xosgmG
ax3NTnaKyhr1QEUJ2gjAwHnKjuGbRVDAinYrSvP0o8Bh9sAs2BN2negWBCZVwwkN
S9hWTjVqsBmpaPOt5SEDwDo9O9dfzkmaamDsxOuUEz9F7v5jYg0mxA/WbogGty9R
vOMKxdxRkzflL/CferVbkzL/EkZRDfWDp9SleZggrpz7miiNDbS7jdRzJ4EttmJ8
gHBAVrOzcnbIPOIkk9pw
=KXIu
-----END PGP SIGNATURE-----
--qMm9M+Fa2AknHoGS--
--
To UNSUBSCRIBE, email to debian-ctte-REQUEST@lists.debian.org
with a subject of "unsubscribe". Trouble? Contact listmaster@lists.debian.org
Archive: https://lists.debian.org/20140731005115.GA19044@virgil.dodds.net

21
spec/fixtures/emails/empty.eml vendored Normal file
View File

@ -0,0 +1,21 @@
Return-Path: <jake@adventuretime.ooo>
Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400
Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for <reply+59d8df8370b7e95c5a49fbf86aeb2c93@discourse.example.com>; Thu, 13 Jun 2013 17:03:50 -0400
Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for <reply+59d8df8370b7e95c5a49fbf86aeb2c93@discourse.example.com>; Thu, 13 Jun 2013 14:03:48 -0700
Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700
Date: Thu, 13 Jun 2013 17:03:48 -0400
From: Jake the Dog <jake@adventuretime.ooo>
To: reply+59d8df8370b7e95c5a49fbf86aeb2c93@discourse.example.com
Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux'
Mime-Version: 1.0
Content-Type: text/plain;
charset=ISO-8859-1
Content-Transfer-Encoding: 7bit
X-Sieve: CMU Sieve 2.2
X-Received: by 10.0.0.1 with SMTP id n7mr11234144ipb.85.1371157428600; Thu,
13 Jun 2013 14:03:48 -0700 (PDT)
X-Scanned-By: MIMEDefang 2.69 on IPv6:2001:470:1d:165::1

View File

@ -0,0 +1,5 @@
<p>Hey folks,</p>
<p>I was thinking. Wouldn't it be great if we could post topics via email? Yes it would!</p>
<p>Jakie</p>

View File

@ -0,0 +1,4 @@
<p>I could not disagree more. I am obviously biased but adventure time is the
greatest show ever created. Everyone should watch it.</p>
<ul><li>Jake out</li></ul>

View File

@ -0,0 +1,40 @@
Return-Path: <jake@adventuretime.ooo>
Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400
Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for <reply+59d8df8370b7e95c5a49fbf86aeb2c93@discourse.example.com>; Thu, 13 Jun 2013 17:03:50 -0400
Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for <reply+59d8df8370b7e95c5a49fbf86aeb2c93@discourse.example.com>; Thu, 13 Jun 2013 14:03:48 -0700
Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700
Date: Thu, 13 Jun 2013 17:03:48 -0400
From: Jake the Dog <jake@adventuretime.ooo>
To: reply+QQd8df8370b7e95c5a49fbf86aeb2c93@discourse.example.com
Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux'
Mime-Version: 1.0
Content-Type: text/plain;
charset=ISO-8859-1
Content-Transfer-Encoding: 7bit
X-Sieve: CMU Sieve 2.2
X-Received: by 10.0.0.1 with SMTP id n7mr11234144ipb.85.1371157428600; Thu,
13 Jun 2013 14:03:48 -0700 (PDT)
X-Scanned-By: MIMEDefang 2.69 on IPv6:2001:470:1d:165::1
I could not disagree more. I am obviously biased but adventure time is the
greatest show ever created. Everyone should watch it.
- Jake out
On Sun, Jun 9, 2013 at 1:39 PM, eviltrout via Discourse Meta
<reply+59d8df8370b7e95c5a49fbf86aeb2c93@discourse.example.com> wrote:
>
>
>
> eviltrout posted in 'Adventure Time Sux' on Discourse Meta:
>
> ---
> hey guys everyone knows adventure time sucks!
>
> ---
> Please visit this link to respond: http://localhost:3000/t/adventure-time-sux/1234/3
>
> To unsubscribe from these emails, visit your [user preferences](http://localhost:3000/user_preferences).
>

View File

@ -41,70 +41,170 @@ describe Jobs::PollMailbox do
end
describe "processing email" do
# Testing mock for the email objects that you get
# from Net::POP3.start { |pop| pop.mails }
class MockPop3EmailObject
def initialize(mail_string)
@message = mail_string
@delete_called = 0
end
let!(:receiver) { mock }
let!(:email_string) { fixture_file("emails/valid_incoming.eml") }
let!(:email) { mock }
def pop
@message
end
def delete
@delete_called += 1
end
# call 'assert email.deleted?' at the end of the test
def deleted?
@delete_called == 1
end
end
def expect_success
poller.expects(:handle_failure).never
end
def expect_exception(clazz)
poller.expects(:handle_failure).with(anything, instance_of(clazz))
end
describe "processing emails" do
let(:category) { Fabricate(:category) }
let(:user) { Fabricate(:user) }
before do
email.stubs(:pop).returns(email_string)
Email::Receiver.expects(:new).with(email_string).returns(receiver)
SiteSetting.email_in = true
SiteSetting.reply_by_email_address = "reply+%{reply_key}@appmail.adventuretime.ooo"
category.email_in = 'incoming+amazing@appmail.adventuretime.ooo'
category.save
user.change_trust_level! :regular
user.username = 'Jake'
user.email = 'jake@adventuretime.ooo'
user.save
end
describe "all goes fine" do
describe "a valid incoming email" do
let(:email) {
# this string replacing is kinda dumb
str = fixture_file('emails/valid_incoming.eml')
str = str.gsub("FROM", 'jake@adventuretime.ooo').gsub("TO", 'incoming+amazing@appmail.adventuretime.ooo')
MockPop3EmailObject.new str
}
let(:expected_post) { fixture_file('emails/valid_incoming.cooked') }
it "email gets deleted" do
receiver.expects(:process)
email.expects(:delete)
it "posts a new topic with the correct content" do
expect_success
poller.handle_mail(email)
topic = Topic.where(category: category).where.not(id: category.topic_id).last
topic.should be_present
topic.title.should == "We should have a post-by-email-feature"
post = topic.posts.first
post.cooked.strip.should == expected_post.strip
email.should be_deleted
end
end
describe "raises Untrusted error" do
describe "with insufficient trust" do
before do
user.change_trust_level! :newuser
end
it "sends a reply and deletes the email" do
receiver.expects(:process).raises(Email::Receiver::UserNotSufficientTrustLevelError)
email.expects(:delete)
message = Mail::Message.new(email_string)
Mail::Message.expects(:new).with(email_string).returns(message)
client_message = mock
sender_object = mock
RejectionMailer.expects(:send_rejection).with(
message.from, message.body, message.subject, message.to, :email_reject_trust_level
).returns(client_message)
Email::Sender.expects(:new).with(client_message, :email_reject_trust_level).returns(sender_object)
sender_object.expects(:send)
poller.handle_mail(email)
end
end
describe "raises error" do
[ Email::Receiver::ProcessingError,
Email::Receiver::EmailUnparsableError,
Email::Receiver::EmptyEmailError,
Email::Receiver::UserNotFoundError,
Email::Receiver::EmailLogNotFound,
ActiveRecord::Rollback,
TypeError
].each do |exception|
it "deletes email on #{exception}" do
receiver.expects(:process).raises(exception)
email.expects(:delete)
Discourse.stubs(:handle_exception)
it "raises a UserNotSufficientTrustLevelError" do
expect_exception Email::Receiver::UserNotSufficientTrustLevelError
poller.handle_mail(email)
end
it "posts the topic if allow_strangers is true" do
begin
category.email_in_allow_strangers = true
category.save
expect_success
poller.handle_mail(email)
topic = Topic.where(category: category).where.not(id: category.topic_id).last
topic.should be_present
topic.title.should == "We should have a post-by-email-feature"
ensure
category.email_in_allow_strangers = false
category.save
end
end
end
end
describe "a valid reply" do
let(:email) { MockPop3EmailObject.new fixture_file('emails/valid_reply.eml')}
let(:expected_post) { fixture_file('emails/valid_reply.cooked')}
let(:topic) { Fabricate(:topic) }
let(:first_post) { Fabricate(:post, topic: topic, post_number: 1)}
before do
first_post.save
EmailLog.create(to_address: 'jake@email.example.com',
email_type: 'user_posted',
reply_key: '59d8df8370b7e95c5a49fbf86aeb2c93',
user: user,
post: first_post,
topic: topic)
end
it "creates a new post" do
expect_success
poller.handle_mail(email)
new_post = Post.find_by(topic: topic, post_number: 2)
assert new_post.present?
assert_equal expected_post.strip, new_post.cooked.strip
email.should be_deleted
end
describe "with the wrong reply key" do
let(:email) { MockPop3EmailObject.new fixture_file('emails/wrong_reply_key.eml')}
it "raises an EmailLogNotFound error" do
expect_exception Email::Receiver::EmailLogNotFound
poller.handle_mail(email)
email.should be_deleted
end
end
end
describe "in failure conditions" do
it "a valid reply without an email log raises an EmailLogNotFound error" do
email = MockPop3EmailObject.new fixture_file('emails/valid_reply.eml')
expect_exception Email::Receiver::EmailLogNotFound
poller.handle_mail(email)
email.should be_deleted
end
it "a no content reply raises an EmailUnparsableError" do
email = MockPop3EmailObject.new fixture_file('emails/no_content_reply.eml')
expect_exception Email::Receiver::EmailUnparsableError
poller.handle_mail(email)
email.should be_deleted
end
it "a fully empty email raises an EmptyEmailError" do
email = MockPop3EmailObject.new fixture_file('emails/empty.eml')
expect_exception Email::Receiver::EmptyEmailError
poller.handle_mail(email)
email.should be_deleted
end
end
end