Chris@0: # encoding: utf-8 Chris@0: # Chris@0: # Redmine - project management software Chris@0: # Copyright (C) 2006-2009 Jean-Philippe Lang Chris@0: # Chris@0: # This program is free software; you can redistribute it and/or Chris@0: # modify it under the terms of the GNU General Public License Chris@0: # as published by the Free Software Foundation; either version 2 Chris@0: # of the License, or (at your option) any later version. Chris@0: # Chris@0: # This program is distributed in the hope that it will be useful, Chris@0: # but WITHOUT ANY WARRANTY; without even the implied warranty of Chris@0: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Chris@0: # GNU General Public License for more details. Chris@0: # Chris@0: # You should have received a copy of the GNU General Public License Chris@0: # along with this program; if not, write to the Free Software Chris@0: # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Chris@0: Chris@0: require File.dirname(__FILE__) + '/../test_helper' Chris@0: Chris@0: class MailHandlerTest < ActiveSupport::TestCase Chris@0: fixtures :users, :projects, Chris@0: :enabled_modules, Chris@0: :roles, Chris@0: :members, Chris@0: :member_roles, chris@37: :users, Chris@0: :issues, Chris@0: :issue_statuses, Chris@0: :workflows, Chris@0: :trackers, Chris@0: :projects_trackers, chris@37: :versions, Chris@0: :enumerations, Chris@0: :issue_categories, Chris@0: :custom_fields, Chris@0: :custom_fields_trackers, Chris@0: :boards, Chris@0: :messages Chris@0: Chris@0: FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures/mail_handler' Chris@0: Chris@0: def setup Chris@0: ActionMailer::Base.deliveries.clear chris@37: Setting.notified_events = Redmine::Notifiable.all.collect(&:name) Chris@0: end Chris@0: Chris@0: def test_add_issue Chris@0: ActionMailer::Base.deliveries.clear Chris@0: # This email contains: 'Project: onlinestore' Chris@0: issue = submit_email('ticket_on_given_project.eml') Chris@0: assert issue.is_a?(Issue) Chris@0: assert !issue.new_record? Chris@0: issue.reload Chris@0: assert_equal 'New ticket on a given project', issue.subject Chris@0: assert_equal User.find_by_login('jsmith'), issue.author Chris@0: assert_equal Project.find(2), issue.project Chris@0: assert_equal IssueStatus.find_by_name('Resolved'), issue.status Chris@0: assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.') Chris@0: assert_equal '2010-01-01', issue.start_date.to_s Chris@0: assert_equal '2010-12-31', issue.due_date.to_s Chris@0: assert_equal User.find_by_login('jsmith'), issue.assigned_to chris@37: assert_equal Version.find_by_name('alpha'), issue.fixed_version chris@37: assert_equal 2.5, issue.estimated_hours chris@37: assert_equal 30, issue.done_ratio Chris@0: # keywords should be removed from the email body Chris@0: assert !issue.description.match(/^Project:/i) Chris@0: assert !issue.description.match(/^Status:/i) Chris@0: # Email notification should be sent Chris@0: mail = ActionMailer::Base.deliveries.last Chris@0: assert_not_nil mail Chris@0: assert mail.subject.include?('New ticket on a given project') Chris@0: end Chris@0: Chris@0: def test_add_issue_with_status Chris@0: # This email contains: 'Project: onlinestore' and 'Status: Resolved' Chris@0: issue = submit_email('ticket_on_given_project.eml') Chris@0: assert issue.is_a?(Issue) Chris@0: assert !issue.new_record? Chris@0: issue.reload Chris@0: assert_equal Project.find(2), issue.project Chris@0: assert_equal IssueStatus.find_by_name("Resolved"), issue.status Chris@0: end Chris@0: Chris@0: def test_add_issue_with_attributes_override Chris@0: issue = submit_email('ticket_with_attributes.eml', :allow_override => 'tracker,category,priority') Chris@0: assert issue.is_a?(Issue) Chris@0: assert !issue.new_record? Chris@0: issue.reload Chris@0: assert_equal 'New ticket on a given project', issue.subject Chris@0: assert_equal User.find_by_login('jsmith'), issue.author Chris@0: assert_equal Project.find(2), issue.project Chris@0: assert_equal 'Feature request', issue.tracker.to_s Chris@0: assert_equal 'Stock management', issue.category.to_s Chris@0: assert_equal 'Urgent', issue.priority.to_s Chris@0: assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.') Chris@0: end Chris@0: Chris@0: def test_add_issue_with_partial_attributes_override Chris@0: issue = submit_email('ticket_with_attributes.eml', :issue => {:priority => 'High'}, :allow_override => ['tracker']) Chris@0: assert issue.is_a?(Issue) Chris@0: assert !issue.new_record? Chris@0: issue.reload Chris@0: assert_equal 'New ticket on a given project', issue.subject Chris@0: assert_equal User.find_by_login('jsmith'), issue.author Chris@0: assert_equal Project.find(2), issue.project Chris@0: assert_equal 'Feature request', issue.tracker.to_s Chris@0: assert_nil issue.category Chris@0: assert_equal 'High', issue.priority.to_s Chris@0: assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.') Chris@0: end Chris@0: Chris@0: def test_add_issue_with_spaces_between_attribute_and_separator Chris@0: issue = submit_email('ticket_with_spaces_between_attribute_and_separator.eml', :allow_override => 'tracker,category,priority') Chris@0: assert issue.is_a?(Issue) Chris@0: assert !issue.new_record? Chris@0: issue.reload Chris@0: assert_equal 'New ticket on a given project', issue.subject Chris@0: assert_equal User.find_by_login('jsmith'), issue.author Chris@0: assert_equal Project.find(2), issue.project Chris@0: assert_equal 'Feature request', issue.tracker.to_s Chris@0: assert_equal 'Stock management', issue.category.to_s Chris@0: assert_equal 'Urgent', issue.priority.to_s Chris@0: assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.') Chris@0: end Chris@0: Chris@0: Chris@0: def test_add_issue_with_attachment_to_specific_project Chris@0: issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'}) Chris@0: assert issue.is_a?(Issue) Chris@0: assert !issue.new_record? Chris@0: issue.reload Chris@0: assert_equal 'Ticket created by email with attachment', issue.subject Chris@0: assert_equal User.find_by_login('jsmith'), issue.author Chris@0: assert_equal Project.find(2), issue.project Chris@0: assert_equal 'This is a new ticket with attachments', issue.description Chris@0: # Attachment properties Chris@0: assert_equal 1, issue.attachments.size Chris@0: assert_equal 'Paella.jpg', issue.attachments.first.filename Chris@0: assert_equal 'image/jpeg', issue.attachments.first.content_type Chris@0: assert_equal 10790, issue.attachments.first.filesize Chris@0: end Chris@0: Chris@0: def test_add_issue_with_custom_fields Chris@0: issue = submit_email('ticket_with_custom_fields.eml', :issue => {:project => 'onlinestore'}) Chris@0: assert issue.is_a?(Issue) Chris@0: assert !issue.new_record? Chris@0: issue.reload Chris@0: assert_equal 'New ticket with custom field values', issue.subject Chris@0: assert_equal 'Value for a custom field', issue.custom_value_for(CustomField.find_by_name('Searchable field')).value Chris@0: assert !issue.description.match(/^searchable field:/i) Chris@0: end Chris@0: Chris@0: def test_add_issue_with_cc Chris@0: issue = submit_email('ticket_with_cc.eml', :issue => {:project => 'ecookbook'}) Chris@0: assert issue.is_a?(Issue) Chris@0: assert !issue.new_record? Chris@0: issue.reload Chris@0: assert issue.watched_by?(User.find_by_mail('dlopper@somenet.foo')) chris@37: assert_equal 1, issue.watcher_user_ids.size Chris@0: end Chris@0: Chris@0: def test_add_issue_by_unknown_user Chris@0: assert_no_difference 'User.count' do Chris@0: assert_equal false, submit_email('ticket_by_unknown_user.eml', :issue => {:project => 'ecookbook'}) Chris@0: end Chris@0: end Chris@0: Chris@0: def test_add_issue_by_anonymous_user Chris@0: Role.anonymous.add_permission!(:add_issues) Chris@0: assert_no_difference 'User.count' do Chris@0: issue = submit_email('ticket_by_unknown_user.eml', :issue => {:project => 'ecookbook'}, :unknown_user => 'accept') Chris@0: assert issue.is_a?(Issue) Chris@0: assert issue.author.anonymous? Chris@0: end Chris@0: end Chris@0: Chris@0: def test_add_issue_by_anonymous_user_with_no_from_address Chris@0: Role.anonymous.add_permission!(:add_issues) Chris@0: assert_no_difference 'User.count' do Chris@0: issue = submit_email('ticket_by_empty_user.eml', :issue => {:project => 'ecookbook'}, :unknown_user => 'accept') Chris@0: assert issue.is_a?(Issue) Chris@0: assert issue.author.anonymous? Chris@0: end Chris@0: end Chris@0: Chris@0: def test_add_issue_by_anonymous_user_on_private_project Chris@0: Role.anonymous.add_permission!(:add_issues) Chris@0: assert_no_difference 'User.count' do Chris@0: assert_no_difference 'Issue.count' do Chris@0: assert_equal false, submit_email('ticket_by_unknown_user.eml', :issue => {:project => 'onlinestore'}, :unknown_user => 'accept') Chris@0: end Chris@0: end Chris@0: end Chris@0: Chris@0: def test_add_issue_by_anonymous_user_on_private_project_without_permission_check Chris@0: assert_no_difference 'User.count' do Chris@0: assert_difference 'Issue.count' do Chris@0: issue = submit_email('ticket_by_unknown_user.eml', :issue => {:project => 'onlinestore'}, :no_permission_check => '1', :unknown_user => 'accept') Chris@0: assert issue.is_a?(Issue) Chris@0: assert issue.author.anonymous? Chris@0: assert !issue.project.is_public? Chris@0: end Chris@0: end Chris@0: end Chris@0: Chris@0: def test_add_issue_by_created_user Chris@0: Setting.default_language = 'en' Chris@0: assert_difference 'User.count' do Chris@0: issue = submit_email('ticket_by_unknown_user.eml', :issue => {:project => 'ecookbook'}, :unknown_user => 'create') Chris@0: assert issue.is_a?(Issue) Chris@0: assert issue.author.active? Chris@0: assert_equal 'john.doe@somenet.foo', issue.author.mail Chris@0: assert_equal 'John', issue.author.firstname Chris@0: assert_equal 'Doe', issue.author.lastname Chris@0: Chris@0: # account information Chris@0: email = ActionMailer::Base.deliveries.first Chris@0: assert_not_nil email Chris@0: assert email.subject.include?('account activation') Chris@0: login = email.body.match(/\* Login: (.*)$/)[1] Chris@0: password = email.body.match(/\* Password: (.*)$/)[1] Chris@0: assert_equal issue.author, User.try_to_login(login, password) Chris@0: end Chris@0: end Chris@0: Chris@0: def test_add_issue_without_from_header Chris@0: Role.anonymous.add_permission!(:add_issues) Chris@0: assert_equal false, submit_email('ticket_without_from_header.eml') Chris@0: end chris@37: chris@37: def test_add_issue_with_invalid_attributes chris@37: issue = submit_email('ticket_with_invalid_attributes.eml', :allow_override => 'tracker,category,priority') chris@37: assert issue.is_a?(Issue) chris@37: assert !issue.new_record? chris@37: issue.reload chris@37: assert_nil issue.assigned_to chris@37: assert_nil issue.start_date chris@37: assert_nil issue.due_date chris@37: assert_equal 0, issue.done_ratio chris@37: assert_equal 'Normal', issue.priority.to_s chris@37: assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.') chris@37: end chris@37: chris@37: def test_add_issue_with_localized_attributes chris@37: User.find_by_mail('jsmith@somenet.foo').update_attribute 'language', 'fr' chris@37: issue = submit_email('ticket_with_localized_attributes.eml', :allow_override => 'tracker,category,priority') chris@37: assert issue.is_a?(Issue) chris@37: assert !issue.new_record? chris@37: issue.reload chris@37: assert_equal 'New ticket on a given project', issue.subject chris@37: assert_equal User.find_by_login('jsmith'), issue.author chris@37: assert_equal Project.find(2), issue.project chris@37: assert_equal 'Feature request', issue.tracker.to_s chris@37: assert_equal 'Stock management', issue.category.to_s chris@37: assert_equal 'Urgent', issue.priority.to_s chris@37: assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.') chris@37: end Chris@0: Chris@0: def test_add_issue_with_japanese_keywords Chris@0: tracker = Tracker.create!(:name => '開発') Chris@0: Project.find(1).trackers << tracker Chris@0: issue = submit_email('japanese_keywords_iso_2022_jp.eml', :issue => {:project => 'ecookbook'}, :allow_override => 'tracker') Chris@0: assert_kind_of Issue, issue Chris@0: assert_equal tracker, issue.tracker Chris@0: end Chris@0: Chris@0: def test_should_ignore_emails_from_emission_address Chris@0: Role.anonymous.add_permission!(:add_issues) Chris@0: assert_no_difference 'User.count' do Chris@0: assert_equal false, submit_email('ticket_from_emission_address.eml', :issue => {:project => 'ecookbook'}, :unknown_user => 'create') Chris@0: end Chris@0: end Chris@0: Chris@0: def test_add_issue_should_send_email_notification chris@37: Setting.notified_events = ['issue_added'] Chris@0: ActionMailer::Base.deliveries.clear Chris@0: # This email contains: 'Project: onlinestore' Chris@0: issue = submit_email('ticket_on_given_project.eml') Chris@0: assert issue.is_a?(Issue) Chris@0: assert_equal 1, ActionMailer::Base.deliveries.size Chris@0: end Chris@0: Chris@0: def test_add_issue_note Chris@0: journal = submit_email('ticket_reply.eml') Chris@0: assert journal.is_a?(Journal) Chris@0: assert_equal User.find_by_login('jsmith'), journal.user Chris@0: assert_equal Issue.find(2), journal.journalized Chris@0: assert_match /This is reply/, journal.notes Chris@0: end Chris@0: Chris@0: def test_add_issue_note_with_attribute_changes Chris@0: # This email contains: 'Status: Resolved' Chris@0: journal = submit_email('ticket_reply_with_status.eml') Chris@0: assert journal.is_a?(Journal) Chris@0: issue = Issue.find(journal.issue.id) Chris@0: assert_equal User.find_by_login('jsmith'), journal.user Chris@0: assert_equal Issue.find(2), journal.journalized Chris@0: assert_match /This is reply/, journal.notes Chris@0: assert_equal IssueStatus.find_by_name("Resolved"), issue.status Chris@0: assert_equal '2010-01-01', issue.start_date.to_s Chris@0: assert_equal '2010-12-31', issue.due_date.to_s Chris@0: assert_equal User.find_by_login('jsmith'), issue.assigned_to chris@37: assert_equal 'Updated custom value', issue.custom_value_for(CustomField.find_by_name('Searchable field')).value Chris@0: end Chris@0: Chris@0: def test_add_issue_note_should_send_email_notification Chris@0: ActionMailer::Base.deliveries.clear Chris@0: journal = submit_email('ticket_reply.eml') Chris@0: assert journal.is_a?(Journal) Chris@0: assert_equal 1, ActionMailer::Base.deliveries.size Chris@0: end Chris@0: Chris@0: def test_reply_to_a_message Chris@0: m = submit_email('message_reply.eml') Chris@0: assert m.is_a?(Message) Chris@0: assert !m.new_record? Chris@0: m.reload Chris@0: assert_equal 'Reply via email', m.subject Chris@0: # The email replies to message #2 which is part of the thread of message #1 Chris@0: assert_equal Message.find(1), m.parent Chris@0: end Chris@0: Chris@0: def test_reply_to_a_message_by_subject Chris@0: m = submit_email('message_reply_by_subject.eml') Chris@0: assert m.is_a?(Message) Chris@0: assert !m.new_record? Chris@0: m.reload Chris@0: assert_equal 'Reply to the first post', m.subject Chris@0: assert_equal Message.find(1), m.parent Chris@0: end Chris@0: Chris@0: def test_should_strip_tags_of_html_only_emails Chris@0: issue = submit_email('ticket_html_only.eml', :issue => {:project => 'ecookbook'}) Chris@0: assert issue.is_a?(Issue) Chris@0: assert !issue.new_record? Chris@0: issue.reload Chris@0: assert_equal 'HTML email', issue.subject Chris@0: assert_equal 'This is a html-only email.', issue.description Chris@0: end Chris@0: Chris@0: context "truncate emails based on the Setting" do Chris@0: context "with no setting" do Chris@0: setup do Chris@0: Setting.mail_handler_body_delimiters = '' Chris@0: end Chris@0: Chris@0: should "add the entire email into the issue" do Chris@0: issue = submit_email('ticket_on_given_project.eml') Chris@0: assert_issue_created(issue) Chris@0: assert issue.description.include?('---') Chris@0: assert issue.description.include?('This paragraph is after the delimiter') Chris@0: end Chris@0: end Chris@0: Chris@0: context "with a single string" do Chris@0: setup do Chris@0: Setting.mail_handler_body_delimiters = '---' Chris@0: end Chris@0: Chris@0: should "truncate the email at the delimiter for the issue" do Chris@0: issue = submit_email('ticket_on_given_project.eml') Chris@0: assert_issue_created(issue) Chris@0: assert issue.description.include?('This paragraph is before delimiters') Chris@0: assert issue.description.include?('--- This line starts with a delimiter') Chris@0: assert !issue.description.match(/^---$/) Chris@0: assert !issue.description.include?('This paragraph is after the delimiter') Chris@0: end Chris@0: end Chris@0: chris@37: context "with a single quoted reply (e.g. reply to a Redmine email notification)" do chris@37: setup do chris@37: Setting.mail_handler_body_delimiters = '--- Reply above. Do not remove this line. ---' chris@37: end chris@37: chris@37: should "truncate the email at the delimiter with the quoted reply symbols (>)" do chris@37: journal = submit_email('issue_update_with_quoted_reply_above.eml') chris@37: assert journal.is_a?(Journal) chris@37: assert journal.notes.include?('An update to the issue by the sender.') chris@37: assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---")) chris@37: assert !journal.notes.include?('Looks like the JSON api for projects was missed.') chris@37: chris@37: end chris@37: chris@37: end chris@37: chris@37: context "with multiple quoted replies (e.g. reply to a reply of a Redmine email notification)" do chris@37: setup do chris@37: Setting.mail_handler_body_delimiters = '--- Reply above. Do not remove this line. ---' chris@37: end chris@37: chris@37: should "truncate the email at the delimiter with the quoted reply symbols (>)" do chris@37: journal = submit_email('issue_update_with_multiple_quoted_reply_above.eml') chris@37: assert journal.is_a?(Journal) chris@37: assert journal.notes.include?('An update to the issue by the sender.') chris@37: assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---")) chris@37: assert !journal.notes.include?('Looks like the JSON api for projects was missed.') chris@37: chris@37: end chris@37: chris@37: end chris@37: Chris@0: context "with multiple strings" do Chris@0: setup do Chris@0: Setting.mail_handler_body_delimiters = "---\nBREAK" Chris@0: end Chris@0: Chris@0: should "truncate the email at the first delimiter found (BREAK)" do Chris@0: issue = submit_email('ticket_on_given_project.eml') Chris@0: assert_issue_created(issue) Chris@0: assert issue.description.include?('This paragraph is before delimiters') Chris@0: assert !issue.description.include?('BREAK') Chris@0: assert !issue.description.include?('This paragraph is between delimiters') Chris@0: assert !issue.description.match(/^---$/) Chris@0: assert !issue.description.include?('This paragraph is after the delimiter') Chris@0: end Chris@0: end Chris@0: end Chris@0: Chris@0: def test_email_with_long_subject_line Chris@0: issue = submit_email('ticket_with_long_subject.eml') Chris@0: assert issue.is_a?(Issue) Chris@0: assert_equal issue.subject, 'New ticket on a given project with a very long subject line which exceeds 255 chars and should not be ignored but chopped off. And if the subject line is still not long enough, we just add more text. And more text. Wow, this is really annoying. Especially, if you have nothing to say...'[0,255] Chris@0: end Chris@0: Chris@0: private Chris@0: Chris@0: def submit_email(filename, options={}) Chris@0: raw = IO.read(File.join(FIXTURES_PATH, filename)) Chris@0: MailHandler.receive(raw, options) Chris@0: end Chris@0: Chris@0: def assert_issue_created(issue) Chris@0: assert issue.is_a?(Issue) Chris@0: assert !issue.new_record? Chris@0: issue.reload Chris@0: end Chris@0: end