2016-01-18 18:57:55 -05:00
require " rails_helper "
require " email/receiver "
2013-06-10 16:46:08 -04:00
describe Email :: Receiver do
2013-06-13 18:11:10 -04:00
before do
2016-01-18 18:57:55 -05:00
SiteSetting . email_in = true
SiteSetting . reply_by_email_address = " reply+%{reply_key}@bar.com "
2013-06-13 18:11:10 -04:00
end
2013-06-10 16:46:08 -04:00
2016-01-18 18:57:55 -05:00
def process ( email_name )
2016-03-07 10:56:17 -05:00
Email :: Receiver . new ( email ( email_name ) ) . process!
2016-01-18 18:57:55 -05:00
end
2014-08-26 20:08:53 -04:00
2016-01-18 18:57:55 -05:00
it " raises an EmptyEmailError when 'mail_string' is blank " do
expect { Email :: Receiver . new ( nil ) } . to raise_error ( Email :: Receiver :: EmptyEmailError )
expect { Email :: Receiver . new ( " " ) } . to raise_error ( Email :: Receiver :: EmptyEmailError )
end
2014-08-28 15:09:42 -04:00
2016-04-18 16:58:30 -04:00
it " raises a ScreenedEmailError when email address is screened " do
ScreenedEmail . expects ( :should_block? ) . with ( " screened@mail.com " ) . returns ( true )
expect { process ( :screened_email ) } . to raise_error ( Email :: Receiver :: ScreenedEmailError )
end
it " raises an UserNotFoundError when staged users are disabled " do
2016-03-23 13:56:03 -04:00
SiteSetting . enable_staged_users = false
expect { process ( :user_not_found ) } . to raise_error ( Email :: Receiver :: UserNotFoundError )
end
2016-01-18 18:57:55 -05:00
it " raises an AutoGeneratedEmailError when the mail is auto generated " do
expect { process ( :auto_generated_precedence ) } . to raise_error ( Email :: Receiver :: AutoGeneratedEmailError )
expect { process ( :auto_generated_header ) } . to raise_error ( Email :: Receiver :: AutoGeneratedEmailError )
end
2014-11-25 11:44:59 -05:00
2016-01-18 18:57:55 -05:00
it " raises a NoBodyDetectedError when the body is blank " do
expect { process ( :no_body ) } . to raise_error ( Email :: Receiver :: NoBodyDetectedError )
end
2014-12-01 13:21:14 -05:00
2016-01-18 18:57:55 -05:00
it " raises an InactiveUserError when the sender is inactive " do
Fabricate ( :user , email : " inactive@bar.com " , active : false )
expect { process ( :inactive_sender ) } . to raise_error ( Email :: Receiver :: InactiveUserError )
end
2015-11-18 15:22:50 -05:00
2016-02-11 04:39:57 -05:00
it " raises a BlockedUserError when the sender has been blocked " do
Fabricate ( :user , email : " blocked@bar.com " , blocked : true )
expect { process ( :blocked_sender ) } . to raise_error ( Email :: Receiver :: BlockedUserError )
end
2016-01-18 18:57:55 -05:00
skip " doesn't raise an InactiveUserError when the sender is staged " do
Fabricate ( :user , email : " staged@bar.com " , active : false , staged : true )
expect { process ( :staged_sender ) } . not_to raise_error
end
2015-11-18 15:22:50 -05:00
2016-01-18 18:57:55 -05:00
it " raises a BadDestinationAddress when destinations aren't matching any of the incoming emails " do
expect { process ( :bad_destinations ) } . to raise_error ( Email :: Receiver :: BadDestinationAddress )
end
2015-11-18 15:22:50 -05:00
2016-05-02 17:15:32 -04:00
it " raises a BouncerEmailError when email is a bounced email " do
2016-04-07 10:21:17 -04:00
expect { process ( :bounced_email ) } . to raise_error ( Email :: Receiver :: BouncedEmailError )
2016-05-02 17:15:32 -04:00
expect ( IncomingEmail . last . is_bounce ) . to eq ( true )
2016-04-07 10:21:17 -04:00
end
2016-04-20 15:29:27 -04:00
it " raises an AutoGeneratedEmailReplyError when email contains a marked reply " do
expect { process ( :bounced_email_2 ) } . to raise_error ( Email :: Receiver :: AutoGeneratedEmailReplyError )
2016-04-07 10:21:17 -04:00
end
2016-05-02 17:15:32 -04:00
context " bounces to VERP " do
let ( :bounce_key ) { " 14b08c855160d67f2e0c2f8ef36e251e " }
let ( :bounce_key_2 ) { " b542fb5a9bacda6d28cc061d18e4eb83 " }
let! ( :user ) { Fabricate ( :user , email : " foo@bar.com " , active : true ) }
let! ( :email_log ) { Fabricate ( :email_log , user : user , bounce_key : bounce_key ) }
let! ( :email_log_2 ) { Fabricate ( :email_log , user : user , bounce_key : bounce_key_2 ) }
before do
$redis . del ( " bounce_score: #{ user . email } : #{ Date . today } " )
$redis . del ( " bounce_score: #{ user . email } : #{ 2 . days . from_now . to_date } " )
end
it " deals with soft bounces " do
expect { process ( :soft_bounce_via_verp ) } . to raise_error ( Email :: Receiver :: BouncedEmailError )
email_log . reload
expect ( email_log . bounced ) . to eq ( true )
expect ( email_log . user . active ) . to eq ( true )
expect ( email_log . user . user_stat . bounce_score ) . to eq ( 1 )
end
it " deals with hard bounces " do
expect { process ( :hard_bounce_via_verp ) } . to raise_error ( Email :: Receiver :: BouncedEmailError )
email_log . reload
expect ( email_log . bounced ) . to eq ( true )
expect ( email_log . user . active ) . to eq ( true )
expect ( email_log . user . user_stat . bounce_score ) . to eq ( 2 )
Timecop . freeze ( 2 . days . from_now ) do
expect { process ( :hard_bounce_via_verp_2 ) } . to raise_error ( Email :: Receiver :: BouncedEmailError )
email_log_2 . reload
expect ( email_log_2 . bounced ) . to eq ( true )
expect ( email_log_2 . user . active ) . to eq ( false )
expect ( email_log_2 . user . user_stat . bounce_score ) . to eq ( 4 )
end
end
end
2016-01-18 18:57:55 -05:00
context " reply " do
2015-11-18 15:22:50 -05:00
2016-01-18 18:57:55 -05:00
let ( :reply_key ) { " 4f97315cc828096c9cb34c6f1a0d6fe8 " }
let ( :user ) { Fabricate ( :user , email : " discourse@bar.com " ) }
let ( :topic ) { create_topic ( user : user ) }
let ( :post ) { create_post ( topic : topic , user : user ) }
let! ( :email_log ) { Fabricate ( :email_log , reply_key : reply_key , user : user , topic : topic , post : post ) }
2014-11-25 11:44:59 -05:00
2016-03-14 13:18:58 -04:00
it " uses MD5 of 'mail_string' there is no message_id " do
mail_string = email ( :missing_message_id )
expect { Email :: Receiver . new ( mail_string ) . process! } . to change { IncomingEmail . count }
expect ( IncomingEmail . last . message_id ) . to eq ( Digest :: MD5 . hexdigest ( mail_string ) )
end
2016-01-18 18:57:55 -05:00
it " raises a ReplyUserNotMatchingError when the email address isn't matching the one we sent the notification to " do
expect { process ( :reply_user_not_matching ) } . to raise_error ( Email :: Receiver :: ReplyUserNotMatchingError )
2014-11-25 11:44:59 -05:00
end
2016-01-18 18:57:55 -05:00
it " raises a TopicNotFoundError when the topic was deleted " do
topic . update_columns ( deleted_at : 1 . day . ago )
expect { process ( :reply_user_matching ) } . to raise_error ( Email :: Receiver :: TopicNotFoundError )
2015-12-09 12:44:01 -05:00
end
2016-01-18 18:57:55 -05:00
it " raises a TopicClosedError when the topic was closed " do
topic . update_columns ( closed : true )
expect { process ( :reply_user_matching ) } . to raise_error ( Email :: Receiver :: TopicClosedError )
2015-12-09 12:44:01 -05:00
end
2016-01-18 18:57:55 -05:00
it " raises an InvalidPost when there was an error while creating the post " do
expect { process ( :too_small ) } . to raise_error ( Email :: Receiver :: InvalidPost )
2014-11-25 11:44:59 -05:00
end
2016-01-18 18:57:55 -05:00
it " raises an InvalidPost when there are too may mentions " do
SiteSetting . max_mentions_per_post = 1
Fabricate ( :user , username : " user1 " )
Fabricate ( :user , username : " user2 " )
expect { process ( :too_many_mentions ) } . to raise_error ( Email :: Receiver :: InvalidPost )
2014-11-25 11:44:59 -05:00
end
2016-01-18 18:57:55 -05:00
it " raises an InvalidPostAction when they aren't allowed to like a post " do
topic . update_columns ( archived : true )
expect { process ( :like ) } . to raise_error ( Email :: Receiver :: InvalidPostAction )
2014-11-25 11:44:59 -05:00
end
2016-01-18 18:57:55 -05:00
it " works " do
expect { process ( :text_reply ) } . to change { topic . posts . count }
expect ( topic . posts . last . raw ) . to eq ( " This is a text reply :) " )
expect ( topic . posts . last . via_email ) . to eq ( true )
expect ( topic . posts . last . cooked ) . not_to match ( / <br / )
2014-11-25 11:44:59 -05:00
2016-01-18 18:57:55 -05:00
expect { process ( :html_reply ) } . to change { topic . posts . count }
expect ( topic . posts . last . raw ) . to eq ( " This is a <b>HTML</b> reply ;) " )
2014-11-25 11:44:59 -05:00
2016-01-18 18:57:55 -05:00
expect { process ( :hebrew_reply ) } . to change { topic . posts . count }
expect ( topic . posts . last . raw ) . to eq ( " שלום! מה שלומך היום? " )
2014-11-25 11:44:59 -05:00
2016-01-18 18:57:55 -05:00
expect { process ( :chinese_reply ) } . to change { topic . posts . count }
expect ( topic . posts . last . raw ) . to eq ( " 您好! 你今天好吗? " )
2016-03-11 12:51:53 -05:00
expect { process ( :reply_with_weird_encoding ) } . to change { topic . posts . count }
expect ( topic . posts . last . raw ) . to eq ( " This is a reply with a weird encoding. " )
2014-11-25 11:44:59 -05:00
end
2016-01-18 18:57:55 -05:00
it " prefers text over html " do
expect { process ( :text_and_html_reply ) } . to change { topic . posts . count }
expect ( topic . posts . last . raw ) . to eq ( " This is the *text* part. " )
2014-10-14 06:12:01 -04:00
end
2016-01-18 18:57:55 -05:00
it " removes the 'on <date>, <contact> wrote' quoting line " do
expect { process ( :on_date_contact_wrote ) } . to change { topic . posts . count }
2016-03-17 18:10:46 -04:00
expect ( topic . posts . last . raw ) . to eq ( " This is the actual reply. " )
2014-08-28 15:09:42 -04:00
end
2014-01-16 21:24:32 -05:00
2016-01-18 18:57:55 -05:00
it " removes the 'Previous Replies' marker " do
expect { process ( :previous_replies ) } . to change { topic . posts . count }
expect ( topic . posts . last . raw ) . to eq ( " This will not include the previous discussion that is present in this email. " )
2014-08-26 20:08:53 -04:00
end
2016-01-18 18:57:55 -05:00
it " handles multiple paragraphs " do
expect { process ( :paragraphs ) } . to change { topic . posts . count }
expect ( topic . posts . last . raw ) . to eq ( " Do you like liquorice? \n \n I really like them. One could even say that I am *addicted* to liquorice. Anf if \n you can mix it up with some anise, then I'm in heaven ;) " )
2013-08-21 16:54:01 -04:00
end
2013-07-24 14:22:32 -04:00
2016-02-24 11:40:57 -05:00
it " handles invalid from header " do
expect { process ( :invalid_from ) } . to change { topic . posts . count }
expect ( topic . posts . last . raw ) . to eq ( " This email was sent with an invalid from header field. " )
end
2016-04-11 16:47:34 -04:00
it " doesn't raise an AutoGeneratedEmailError when the mail is auto generated but is whitelisted " do
SiteSetting . auto_generated_whitelist = " foo@bar.com|discourse@bar.com "
2016-04-20 15:29:27 -04:00
expect { process ( :auto_generated_whitelisted ) } . to change { topic . posts . count }
end
it " doesn't raise an AutoGeneratedEmailError when block_auto_generated_emails is disabled " do
SiteSetting . block_auto_generated_emails = false
expect { process ( :auto_generated_unblocked ) } . to change { topic . posts . count }
2016-04-11 16:47:34 -04:00
end
2016-01-20 04:25:25 -05:00
describe 'Unsubscribing via email' do
let ( :last_email ) { ActionMailer :: Base . deliveries . last }
describe 'unsubscribe_subject.eml' do
it 'sends an email asking the user to confirm the unsubscription' do
expect { process ( " unsubscribe_subject " ) } . to change { ActionMailer :: Base . deliveries . count } . by ( 1 )
expect ( last_email . to . length ) . to eq 1
expect ( last_email . from . length ) . to eq 1
expect ( last_email . from ) . to include " noreply@ #{ Discourse . current_hostname } "
expect ( last_email . to ) . to include " discourse@bar.com "
expect ( last_email . subject ) . to eq I18n . t ( :" unsubscribe_mailer.subject_template " ) . gsub ( " %{site_title} " , SiteSetting . title )
end
it 'does nothing unless unsubscribe_via_email is turned on' do
SiteSetting . stubs ( " unsubscribe_via_email " ) . returns ( false )
before_deliveries = ActionMailer :: Base . deliveries . count
expect { process ( " unsubscribe_subject " ) } . to raise_error { Email :: Receiver :: BadDestinationAddress }
expect ( before_deliveries ) . to eq ActionMailer :: Base . deliveries . count
end
end
describe 'unsubscribe_body.eml' do
it 'sends an email asking the user to confirm the unsubscription' do
expect { process ( " unsubscribe_body " ) } . to change { ActionMailer :: Base . deliveries . count } . by ( 1 )
expect ( last_email . to . length ) . to eq 1
expect ( last_email . from . length ) . to eq 1
expect ( last_email . from ) . to include " noreply@ #{ Discourse . current_hostname } "
expect ( last_email . to ) . to include " discourse@bar.com "
expect ( last_email . subject ) . to eq I18n . t ( :" unsubscribe_mailer.subject_template " ) . gsub ( " %{site_title} " , SiteSetting . title )
end
it 'does nothing unless unsubscribe_via_email is turned on' do
SiteSetting . stubs ( :unsubscribe_via_email ) . returns ( false )
before_deliveries = ActionMailer :: Base . deliveries . count
expect { process ( " unsubscribe_body " ) } . to raise_error { Email :: Receiver :: InvalidPost }
expect ( before_deliveries ) . to eq ActionMailer :: Base . deliveries . count
end
end
end
2016-01-18 18:57:55 -05:00
it " handles inline reply " do
expect { process ( :inline_reply ) } . to change { topic . posts . count }
2016-01-29 19:29:31 -05:00
expect ( topic . posts . last . raw ) . to eq ( " > WAT <https://bar.com/users/wat> November 28 \n > \n > This is the previous post. \n \n And this is *my* reply :+1: " )
2013-07-24 14:22:32 -04:00
end
2016-01-18 18:57:55 -05:00
it " retrieves the first part of multiple replies " do
expect { process ( :inline_mixed_replies ) } . to change { topic . posts . count }
2016-01-29 19:29:31 -05:00
expect ( topic . posts . last . raw ) . to eq ( " > WAT <https://bar.com/users/wat> November 28 \n > \n > This is the previous post. \n \n And this is *my* reply :+1: \n \n > This is another post. \n \n And this is **another** reply. " )
2013-07-24 14:22:32 -04:00
end
2014-08-26 20:08:53 -04:00
2016-01-29 19:29:31 -05:00
it " strips mobile/webmail signatures " do
2016-01-18 18:57:55 -05:00
expect { process ( :iphone_signature ) } . to change { topic . posts . count }
expect ( topic . posts . last . raw ) . to eq ( " This is not the signature you're looking for. " )
2014-08-26 20:08:53 -04:00
end
2016-01-18 18:57:55 -05:00
it " strips 'original message' context " do
2016-03-17 18:10:46 -04:00
expect { process ( :original_message ) } . to change { topic . posts . count }
expect ( topic . posts . last . raw ) . to eq ( " This is a reply :) " )
end
it " add the 'elided' part of the original message only for private messages " do
topic . update_columns ( category_id : nil , archetype : Archetype . private_message )
topic . allowed_users << user
topic . save
2016-01-18 18:57:55 -05:00
expect { process ( :original_message ) } . to change { topic . posts . count }
2016-03-11 11:51:16 -05:00
expect ( topic . posts . last . raw ) . to eq ( " This is a reply :) \n \n <details class='elided'> \n <summary title='Show trimmed content'>& # 183;& # 183;& # 183;</summary> \n ---Original Message--- \n This part should not be included \n </details> " )
2014-12-04 11:45:31 -05:00
end
2016-01-29 19:29:31 -05:00
it " supports attached images " do
expect { process ( :no_body_with_image ) } . to change { topic . posts . count }
2016-01-18 18:57:55 -05:00
expect ( topic . posts . last . raw ) . to match ( / <img / )
2013-07-24 14:22:32 -04:00
2016-01-29 19:29:31 -05:00
expect { process ( :inline_image ) } . to change { topic . posts . count }
2016-01-18 18:57:55 -05:00
expect ( topic . posts . last . raw ) . to match ( / Before \ s+<img.+ \ s+After /m )
2014-10-25 10:36:59 -04:00
end
2016-01-29 19:29:31 -05:00
it " supports attachments " do
SiteSetting . authorized_extensions = " txt "
expect { process ( :attached_txt_file ) } . to change { topic . posts . count }
expect ( topic . posts . last . raw ) . to match ( / text \ .txt / )
end
2016-01-18 18:57:55 -05:00
it " supports liking via email " do
expect { process ( :like ) } . to change ( PostAction , :count )
2014-10-25 10:36:59 -04:00
end
2016-01-18 18:57:55 -05:00
it " ensures posts aren't dated in the future " do
expect { process ( :from_the_future ) } . to change { topic . posts . count }
expect ( topic . posts . last . created_at ) . to be_within ( 1 . minute ) . of ( DateTime . now )
2014-10-27 02:58:31 -04:00
end
end
2016-01-18 18:57:55 -05:00
context " new message to a group " do
2015-12-21 11:54:02 -05:00
2016-02-24 13:47:58 -05:00
let! ( :group ) { Fabricate ( :group , incoming_email : " team@bar.com|meat@bar.com " ) }
2013-06-25 14:05:14 -04:00
2016-01-18 18:57:55 -05:00
it " handles encoded display names " do
expect { process ( :encoded_display_name ) } . to change ( Topic , :count )
2014-08-26 20:08:53 -04:00
2016-01-18 18:57:55 -05:00
topic = Topic . last
2016-02-01 06:16:15 -05:00
expect ( topic . title ) . to eq ( " I need help " )
2016-01-18 18:57:55 -05:00
expect ( topic . private_message? ) . to eq ( true )
expect ( topic . allowed_groups ) . to include ( group )
2013-06-25 14:05:14 -04:00
2016-01-18 18:57:55 -05:00
user = topic . user
expect ( user . staged ) . to eq ( true )
2016-01-20 09:37:34 -05:00
expect ( user . username ) . to eq ( " random.name " )
2016-01-18 18:57:55 -05:00
expect ( user . name ) . to eq ( " Случайная Имя " )
2013-06-25 14:05:14 -04:00
end
2014-08-26 20:08:53 -04:00
2016-02-01 06:16:15 -05:00
it " handles email with no subject " do
expect { process ( :no_subject ) } . to change ( Topic , :count )
expect ( Topic . last . title ) . to eq ( " Incoming email from some@one.com " )
end
2016-01-20 17:08:27 -05:00
it " invites everyone in the chain but emails configured as 'incoming' (via reply, group or category) " do
2016-01-18 18:57:55 -05:00
expect { process ( :cc ) } . to change ( Topic , :count )
emails = Topic . last . allowed_users . pluck ( :email )
2016-01-20 17:08:27 -05:00
expect ( emails . size ) . to eq ( 3 )
expect ( emails ) . to include ( " someone@else.com " , " discourse@bar.com " , " wat@bar.com " )
2014-03-28 09:57:12 -04:00
end
2016-01-20 16:52:08 -05:00
it " associates email replies using both 'In-Reply-To' and 'References' headers " do
expect { process ( :email_reply_1 ) } . to change ( Topic , :count )
topic = Topic . last
expect { process ( :email_reply_2 ) } . to change { topic . posts . count }
expect { process ( :email_reply_3 ) } . to change { topic . posts . count }
2016-01-20 17:08:27 -05:00
# Why 5 when we only processed 3 emails?
2016-01-20 16:52:08 -05:00
# - 3 of them are indeed "regular" posts generated from the emails
2016-01-20 17:08:27 -05:00
# - The 2 others are "small action" posts automatically added because
# we invited 2 users (two@foo.com and three@foo.com)
expect ( topic . posts . count ) . to eq ( 5 )
2016-01-20 16:52:08 -05:00
# trash all but the 1st post
topic . ordered_posts [ 1 .. - 1 ] . each ( & :trash! )
expect { process ( :email_reply_4 ) } . to change { topic . posts . count }
end
2016-02-29 16:39:24 -05:00
it " supports any kind of attachments when 'allow_all_attachments_for_group_messages' is enabled " do
SiteSetting . allow_all_attachments_for_group_messages = true
expect { process ( :attached_rb_file ) } . to change ( Topic , :count )
expect ( Post . last . raw ) . to match ( / discourse \ .rb / )
end
2013-06-10 16:46:08 -04:00
end
2016-01-18 18:57:55 -05:00
context " new topic in a category " do
2015-12-15 18:43:05 -05:00
2016-02-24 13:47:58 -05:00
let! ( :category ) { Fabricate ( :category , email_in : " category@bar.com|category@foo.com " , email_in_allow_strangers : false ) }
2015-12-15 18:43:05 -05:00
2016-01-18 18:57:55 -05:00
it " raises a StrangersNotAllowedError when 'email_in_allow_strangers' is disabled " do
2016-02-22 13:57:53 -05:00
expect { process ( :new_user ) } . to raise_error ( Email :: Receiver :: StrangersNotAllowedError )
2014-02-27 07:44:21 -05:00
end
2016-01-18 18:57:55 -05:00
it " raises an InsufficientTrustLevelError when user's trust level isn't enough " do
2016-02-22 13:57:53 -05:00
Fabricate ( :user , email : " existing@bar.com " , trust_level : 3 )
2016-01-18 18:57:55 -05:00
SiteSetting . email_in_min_trust = 4
2016-02-22 13:57:53 -05:00
expect { process ( :existing_user ) } . to raise_error ( Email :: Receiver :: InsufficientTrustLevelError )
2014-02-24 11:36:53 -05:00
end
2016-02-22 13:57:53 -05:00
it " works " do
user = Fabricate ( :user , email : " existing@bar.com " , trust_level : SiteSetting . email_in_min_trust )
2016-01-18 18:57:55 -05:00
group = Fabricate ( :group )
2015-04-10 05:29:45 -04:00
2016-01-18 18:57:55 -05:00
group . add ( user )
group . save
2015-04-10 05:29:45 -04:00
2016-02-22 13:57:53 -05:00
category . set_permissions ( group = > :create_post )
2015-04-10 05:29:45 -04:00
category . save
2016-02-22 13:57:53 -05:00
# raises an InvalidAccess when the user doesn't have the privileges to create a topic
expect { process ( :existing_user ) } . to raise_error ( Discourse :: InvalidAccess )
2014-02-27 10:36:33 -05:00
2016-02-22 13:57:53 -05:00
category . update_columns ( email_in_allow_strangers : true )
# allows new user to create a topic
expect { process ( :new_user ) } . to change ( Topic , :count )
2015-12-10 17:49:16 -05:00
end
2016-04-11 12:20:26 -04:00
it " works when approving is enabled " do
SiteSetting . approve_unless_trust_level = 4
Fabricate ( :user , email : " tl3@bar.com " , trust_level : TrustLevel [ 3 ] )
Fabricate ( :user , email : " tl4@bar.com " , trust_level : TrustLevel [ 4 ] )
category . set_permissions ( Group [ :trust_level_4 ] = > :full )
category . save
Group . refresh_automatic_group! ( :trust_level_4 )
expect { process ( :tl3_user ) } . to_not change ( Topic , :count )
expect { process ( :tl4_user ) } . to change ( Topic , :count )
end
2015-12-10 17:49:16 -05:00
end
2013-06-10 16:46:08 -04:00
end