##// END OF EJS Templates
Adds To and Cc as watchers when submitting an issue by email (#2245)....
Jean-Philippe Lang -
r2075:84e70634fbd0
parent child
Show More
@@ -0,0 +1,40
1 Return-Path: <JSmith@somenet.foo>
2 Received: from osiris ([127.0.0.1])
3 by OSIRIS
4 with hMailServer ; Sun, 22 Jun 2008 12:28:07 +0200
5 Message-ID: <000501c8d452$a95cd7e0$0a00a8c0@osiris>
6 From: "John Smith" <JSmith@somenet.foo>
7 To: <redmine@somenet.foo>
8 Cc: <DLopper@somenet.foo>
9 Subject: New ticket on a given project
10 Date: Sun, 22 Jun 2008 12:28:07 +0200
11 MIME-Version: 1.0
12 Content-Type: text/plain;
13 format=flowed;
14 charset="iso-8859-1";
15 reply-type=original
16 Content-Transfer-Encoding: 7bit
17 X-Priority: 3
18 X-MSMail-Priority: Normal
19 X-Mailer: Microsoft Outlook Express 6.00.2900.2869
20 X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2869
21
22 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas imperdiet
23 turpis et odio. Integer eget pede vel dolor euismod varius. Phasellus
24 blandit eleifend augue. Nulla facilisi. Duis id diam. Class aptent taciti
25 sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In
26 in urna sed tellus aliquet lobortis. Morbi scelerisque tortor in dolor. Cras
27 sagittis odio eu lacus. Aliquam sem tortor, consequat sit amet, vestibulum
28 id, iaculis at, lectus. Fusce tortor libero, congue ut, euismod nec, luctus
29 eget, eros. Pellentesque tortor enim, feugiat in, dignissim eget, tristique
30 sed, mauris. Pellentesque habitant morbi tristique senectus et netus et
31 malesuada fames ac turpis egestas. Quisque sit amet libero. In hac habitasse
32 platea dictumst.
33
34 Nulla et nunc. Duis pede. Donec et ipsum. Nam ut dui tincidunt neque
35 sollicitudin iaculis. Duis vitae dolor. Vestibulum eget massa. Sed lorem.
36 Nullam volutpat cursus erat. Cras felis dolor, lacinia quis, rutrum et,
37 dictum et, ligula. Sed erat nibh, gravida in, accumsan non, placerat sed,
38 massa. Sed sodales, ante fermentum ultricies sollicitudin, massa leo
39 pulvinar dui, a gravida orci mi eget odio. Nunc a lacus.
40
@@ -1,160 +1,176
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class MailHandler < ActionMailer::Base
18 class MailHandler < ActionMailer::Base
19
19
20 class UnauthorizedAction < StandardError; end
20 class UnauthorizedAction < StandardError; end
21 class MissingInformation < StandardError; end
21 class MissingInformation < StandardError; end
22
22
23 attr_reader :email, :user
23 attr_reader :email, :user
24
24
25 def self.receive(email, options={})
25 def self.receive(email, options={})
26 @@handler_options = options.dup
26 @@handler_options = options.dup
27
27
28 @@handler_options[:issue] ||= {}
28 @@handler_options[:issue] ||= {}
29
29
30 @@handler_options[:allow_override] = @@handler_options[:allow_override].split(',').collect(&:strip) if @@handler_options[:allow_override].is_a?(String)
30 @@handler_options[:allow_override] = @@handler_options[:allow_override].split(',').collect(&:strip) if @@handler_options[:allow_override].is_a?(String)
31 @@handler_options[:allow_override] ||= []
31 @@handler_options[:allow_override] ||= []
32 # Project needs to be overridable if not specified
32 # Project needs to be overridable if not specified
33 @@handler_options[:allow_override] << 'project' unless @@handler_options[:issue].has_key?(:project)
33 @@handler_options[:allow_override] << 'project' unless @@handler_options[:issue].has_key?(:project)
34 # Status overridable by default
34 # Status overridable by default
35 @@handler_options[:allow_override] << 'status' unless @@handler_options[:issue].has_key?(:status)
35 @@handler_options[:allow_override] << 'status' unless @@handler_options[:issue].has_key?(:status)
36 super email
36 super email
37 end
37 end
38
38
39 # Processes incoming emails
39 # Processes incoming emails
40 def receive(email)
40 def receive(email)
41 @email = email
41 @email = email
42 @user = User.find_active(:first, :conditions => ["LOWER(mail) = ?", email.from.first.to_s.strip.downcase])
42 @user = User.find_active(:first, :conditions => ["LOWER(mail) = ?", email.from.first.to_s.strip.downcase])
43 unless @user
43 unless @user
44 # Unknown user => the email is ignored
44 # Unknown user => the email is ignored
45 # TODO: ability to create the user's account
45 # TODO: ability to create the user's account
46 logger.info "MailHandler: email submitted by unknown user [#{email.from.first}]" if logger && logger.info
46 logger.info "MailHandler: email submitted by unknown user [#{email.from.first}]" if logger && logger.info
47 return false
47 return false
48 end
48 end
49 User.current = @user
49 User.current = @user
50 dispatch
50 dispatch
51 end
51 end
52
52
53 private
53 private
54
54
55 ISSUE_REPLY_SUBJECT_RE = %r{\[[^\]]+#(\d+)\]}
55 ISSUE_REPLY_SUBJECT_RE = %r{\[[^\]]+#(\d+)\]}
56
56
57 def dispatch
57 def dispatch
58 if m = email.subject.match(ISSUE_REPLY_SUBJECT_RE)
58 if m = email.subject.match(ISSUE_REPLY_SUBJECT_RE)
59 receive_issue_update(m[1].to_i)
59 receive_issue_update(m[1].to_i)
60 else
60 else
61 receive_issue
61 receive_issue
62 end
62 end
63 rescue ActiveRecord::RecordInvalid => e
63 rescue ActiveRecord::RecordInvalid => e
64 # TODO: send a email to the user
64 # TODO: send a email to the user
65 logger.error e.message if logger
65 logger.error e.message if logger
66 false
66 false
67 rescue MissingInformation => e
67 rescue MissingInformation => e
68 logger.error "MailHandler: missing information from #{user}: #{e.message}" if logger
68 logger.error "MailHandler: missing information from #{user}: #{e.message}" if logger
69 false
69 false
70 rescue UnauthorizedAction => e
70 rescue UnauthorizedAction => e
71 logger.error "MailHandler: unauthorized attempt from #{user}" if logger
71 logger.error "MailHandler: unauthorized attempt from #{user}" if logger
72 false
72 false
73 end
73 end
74
74
75 # Creates a new issue
75 # Creates a new issue
76 def receive_issue
76 def receive_issue
77 project = target_project
77 project = target_project
78 tracker = (get_keyword(:tracker) && project.trackers.find_by_name(get_keyword(:tracker))) || project.trackers.find(:first)
78 tracker = (get_keyword(:tracker) && project.trackers.find_by_name(get_keyword(:tracker))) || project.trackers.find(:first)
79 category = (get_keyword(:category) && project.issue_categories.find_by_name(get_keyword(:category)))
79 category = (get_keyword(:category) && project.issue_categories.find_by_name(get_keyword(:category)))
80 priority = (get_keyword(:priority) && Enumeration.find_by_opt_and_name('IPRI', get_keyword(:priority)))
80 priority = (get_keyword(:priority) && Enumeration.find_by_opt_and_name('IPRI', get_keyword(:priority)))
81 status = (get_keyword(:status) && IssueStatus.find_by_name(get_keyword(:status)))
81 status = (get_keyword(:status) && IssueStatus.find_by_name(get_keyword(:status)))
82
82
83 # check permission
83 # check permission
84 raise UnauthorizedAction unless user.allowed_to?(:add_issues, project)
84 raise UnauthorizedAction unless user.allowed_to?(:add_issues, project)
85 issue = Issue.new(:author => user, :project => project, :tracker => tracker, :category => category, :priority => priority)
85 issue = Issue.new(:author => user, :project => project, :tracker => tracker, :category => category, :priority => priority)
86 # check workflow
86 # check workflow
87 if status && issue.new_statuses_allowed_to(user).include?(status)
87 if status && issue.new_statuses_allowed_to(user).include?(status)
88 issue.status = status
88 issue.status = status
89 end
89 end
90 issue.subject = email.subject.chomp.toutf8
90 issue.subject = email.subject.chomp.toutf8
91 issue.description = email.plain_text_body.chomp
91 issue.description = email.plain_text_body.chomp
92 issue.save!
92 issue.save!
93 add_attachments(issue)
93 add_attachments(issue)
94 logger.info "MailHandler: issue ##{issue.id} created by #{user}" if logger && logger.info
94 logger.info "MailHandler: issue ##{issue.id} created by #{user}" if logger && logger.info
95 # send notification before adding watchers since they were cc'ed
95 Mailer.deliver_issue_add(issue) if Setting.notified_events.include?('issue_added')
96 Mailer.deliver_issue_add(issue) if Setting.notified_events.include?('issue_added')
97 # add To and Cc as watchers
98 add_watchers(issue)
99
96 issue
100 issue
97 end
101 end
98
102
99 def target_project
103 def target_project
100 # TODO: other ways to specify project:
104 # TODO: other ways to specify project:
101 # * parse the email To field
105 # * parse the email To field
102 # * specific project (eg. Setting.mail_handler_target_project)
106 # * specific project (eg. Setting.mail_handler_target_project)
103 target = Project.find_by_identifier(get_keyword(:project))
107 target = Project.find_by_identifier(get_keyword(:project))
104 raise MissingInformation.new('Unable to determine target project') if target.nil?
108 raise MissingInformation.new('Unable to determine target project') if target.nil?
105 target
109 target
106 end
110 end
107
111
108 # Adds a note to an existing issue
112 # Adds a note to an existing issue
109 def receive_issue_update(issue_id)
113 def receive_issue_update(issue_id)
110 status = (get_keyword(:status) && IssueStatus.find_by_name(get_keyword(:status)))
114 status = (get_keyword(:status) && IssueStatus.find_by_name(get_keyword(:status)))
111
115
112 issue = Issue.find_by_id(issue_id)
116 issue = Issue.find_by_id(issue_id)
113 return unless issue
117 return unless issue
114 # check permission
118 # check permission
115 raise UnauthorizedAction unless user.allowed_to?(:add_issue_notes, issue.project) || user.allowed_to?(:edit_issues, issue.project)
119 raise UnauthorizedAction unless user.allowed_to?(:add_issue_notes, issue.project) || user.allowed_to?(:edit_issues, issue.project)
116 raise UnauthorizedAction unless status.nil? || user.allowed_to?(:edit_issues, issue.project)
120 raise UnauthorizedAction unless status.nil? || user.allowed_to?(:edit_issues, issue.project)
117
121
118 # add the note
122 # add the note
119 journal = issue.init_journal(user, email.plain_text_body.chomp)
123 journal = issue.init_journal(user, email.plain_text_body.chomp)
120 add_attachments(issue)
124 add_attachments(issue)
121 # check workflow
125 # check workflow
122 if status && issue.new_statuses_allowed_to(user).include?(status)
126 if status && issue.new_statuses_allowed_to(user).include?(status)
123 issue.status = status
127 issue.status = status
124 end
128 end
125 issue.save!
129 issue.save!
126 logger.info "MailHandler: issue ##{issue.id} updated by #{user}" if logger && logger.info
130 logger.info "MailHandler: issue ##{issue.id} updated by #{user}" if logger && logger.info
127 Mailer.deliver_issue_edit(journal) if Setting.notified_events.include?('issue_updated')
131 Mailer.deliver_issue_edit(journal) if Setting.notified_events.include?('issue_updated')
128 journal
132 journal
129 end
133 end
130
134
131 def add_attachments(obj)
135 def add_attachments(obj)
132 if email.has_attachments?
136 if email.has_attachments?
133 email.attachments.each do |attachment|
137 email.attachments.each do |attachment|
134 Attachment.create(:container => obj,
138 Attachment.create(:container => obj,
135 :file => attachment,
139 :file => attachment,
136 :author => user,
140 :author => user,
137 :content_type => attachment.content_type)
141 :content_type => attachment.content_type)
138 end
142 end
139 end
143 end
140 end
144 end
141
145
146 # Adds To and Cc as watchers of the given object if the sender has the
147 # appropriate permission
148 def add_watchers(obj)
149 if user.allowed_to?("add_#{obj.class.name.underscore}_watchers".to_sym, obj.project)
150 addresses = [email.to, email.cc].flatten.compact.uniq.collect {|a| a.strip.downcase}
151 unless addresses.empty?
152 watchers = User.find_active(:all, :conditions => ['LOWER(mail) IN (?)', addresses])
153 watchers.each {|w| obj.add_watcher(w)}
154 end
155 end
156 end
157
142 def get_keyword(attr)
158 def get_keyword(attr)
143 if @@handler_options[:allow_override].include?(attr.to_s) && email.plain_text_body =~ /^#{attr}:[ \t]*(.+)$/i
159 if @@handler_options[:allow_override].include?(attr.to_s) && email.plain_text_body =~ /^#{attr}:[ \t]*(.+)$/i
144 $1.strip
160 $1.strip
145 elsif !@@handler_options[:issue][attr].blank?
161 elsif !@@handler_options[:issue][attr].blank?
146 @@handler_options[:issue][attr]
162 @@handler_options[:issue][attr]
147 end
163 end
148 end
164 end
149 end
165 end
150
166
151 class TMail::Mail
167 class TMail::Mail
152 # Returns body of the first plain text part found if any
168 # Returns body of the first plain text part found if any
153 def plain_text_body
169 def plain_text_body
154 return @plain_text_body unless @plain_text_body.nil?
170 return @plain_text_body unless @plain_text_body.nil?
155 p = self.parts.collect {|c| (c.respond_to?(:parts) && !c.parts.empty?) ? c.parts : c}.flatten
171 p = self.parts.collect {|c| (c.respond_to?(:parts) && !c.parts.empty?) ? c.parts : c}.flatten
156 plain = p.detect {|c| c.content_type == 'text/plain'}
172 plain = p.detect {|c| c.content_type == 'text/plain'}
157 @plain_text_body = plain.nil? ? self.body : plain.body
173 @plain_text_body = plain.nil? ? self.body : plain.body
158 end
174 end
159 end
175 end
160
176
@@ -1,128 +1,139
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.dirname(__FILE__) + '/../test_helper'
18 require File.dirname(__FILE__) + '/../test_helper'
19
19
20 class MailHandlerTest < Test::Unit::TestCase
20 class MailHandlerTest < Test::Unit::TestCase
21 fixtures :users, :projects,
21 fixtures :users, :projects,
22 :enabled_modules,
22 :enabled_modules,
23 :roles,
23 :roles,
24 :members,
24 :members,
25 :issues,
25 :issues,
26 :issue_statuses,
27 :workflows,
26 :trackers,
28 :trackers,
27 :projects_trackers,
29 :projects_trackers,
28 :enumerations,
30 :enumerations,
29 :issue_categories
31 :issue_categories
30
32
31 FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures/mail_handler'
33 FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures/mail_handler'
32
34
33 def setup
35 def setup
34 ActionMailer::Base.deliveries.clear
36 ActionMailer::Base.deliveries.clear
35 end
37 end
36
38
37 def test_add_issue
39 def test_add_issue
38 # This email contains: 'Project: onlinestore'
40 # This email contains: 'Project: onlinestore'
39 issue = submit_email('ticket_on_given_project.eml')
41 issue = submit_email('ticket_on_given_project.eml')
40 assert issue.is_a?(Issue)
42 assert issue.is_a?(Issue)
41 assert !issue.new_record?
43 assert !issue.new_record?
42 issue.reload
44 issue.reload
43 assert_equal 'New ticket on a given project', issue.subject
45 assert_equal 'New ticket on a given project', issue.subject
44 assert_equal User.find_by_login('jsmith'), issue.author
46 assert_equal User.find_by_login('jsmith'), issue.author
45 assert_equal Project.find(2), issue.project
47 assert_equal Project.find(2), issue.project
46 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
48 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
47 end
49 end
48
50
49 def test_add_issue_with_status
51 def test_add_issue_with_status
50 # This email contains: 'Project: onlinestore' and 'Status: Resolved'
52 # This email contains: 'Project: onlinestore' and 'Status: Resolved'
51 issue = submit_email('ticket_on_given_project.eml')
53 issue = submit_email('ticket_on_given_project.eml')
52 assert issue.is_a?(Issue)
54 assert issue.is_a?(Issue)
53 assert !issue.new_record?
55 assert !issue.new_record?
54 issue.reload
56 issue.reload
55 assert_equal Project.find(2), issue.project
57 assert_equal Project.find(2), issue.project
56 assert_equal IssueStatus.find_by_name("Resolved"), issue.status
58 assert_equal IssueStatus.find_by_name("Resolved"), issue.status
57 end
59 end
58
60
59 def test_add_issue_with_attributes_override
61 def test_add_issue_with_attributes_override
60 issue = submit_email('ticket_with_attributes.eml', :allow_override => 'tracker,category,priority')
62 issue = submit_email('ticket_with_attributes.eml', :allow_override => 'tracker,category,priority')
61 assert issue.is_a?(Issue)
63 assert issue.is_a?(Issue)
62 assert !issue.new_record?
64 assert !issue.new_record?
63 issue.reload
65 issue.reload
64 assert_equal 'New ticket on a given project', issue.subject
66 assert_equal 'New ticket on a given project', issue.subject
65 assert_equal User.find_by_login('jsmith'), issue.author
67 assert_equal User.find_by_login('jsmith'), issue.author
66 assert_equal Project.find(2), issue.project
68 assert_equal Project.find(2), issue.project
67 assert_equal 'Feature request', issue.tracker.to_s
69 assert_equal 'Feature request', issue.tracker.to_s
68 assert_equal 'Stock management', issue.category.to_s
70 assert_equal 'Stock management', issue.category.to_s
69 assert_equal 'Urgent', issue.priority.to_s
71 assert_equal 'Urgent', issue.priority.to_s
70 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
72 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
71 end
73 end
72
74
73 def test_add_issue_with_partial_attributes_override
75 def test_add_issue_with_partial_attributes_override
74 issue = submit_email('ticket_with_attributes.eml', :issue => {:priority => 'High'}, :allow_override => ['tracker'])
76 issue = submit_email('ticket_with_attributes.eml', :issue => {:priority => 'High'}, :allow_override => ['tracker'])
75 assert issue.is_a?(Issue)
77 assert issue.is_a?(Issue)
76 assert !issue.new_record?
78 assert !issue.new_record?
77 issue.reload
79 issue.reload
78 assert_equal 'New ticket on a given project', issue.subject
80 assert_equal 'New ticket on a given project', issue.subject
79 assert_equal User.find_by_login('jsmith'), issue.author
81 assert_equal User.find_by_login('jsmith'), issue.author
80 assert_equal Project.find(2), issue.project
82 assert_equal Project.find(2), issue.project
81 assert_equal 'Feature request', issue.tracker.to_s
83 assert_equal 'Feature request', issue.tracker.to_s
82 assert_nil issue.category
84 assert_nil issue.category
83 assert_equal 'High', issue.priority.to_s
85 assert_equal 'High', issue.priority.to_s
84 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
86 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
85 end
87 end
86
88
87 def test_add_issue_with_attachment_to_specific_project
89 def test_add_issue_with_attachment_to_specific_project
88 issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
90 issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
89 assert issue.is_a?(Issue)
91 assert issue.is_a?(Issue)
90 assert !issue.new_record?
92 assert !issue.new_record?
91 issue.reload
93 issue.reload
92 assert_equal 'Ticket created by email with attachment', issue.subject
94 assert_equal 'Ticket created by email with attachment', issue.subject
93 assert_equal User.find_by_login('jsmith'), issue.author
95 assert_equal User.find_by_login('jsmith'), issue.author
94 assert_equal Project.find(2), issue.project
96 assert_equal Project.find(2), issue.project
95 assert_equal 'This is a new ticket with attachments', issue.description
97 assert_equal 'This is a new ticket with attachments', issue.description
96 # Attachment properties
98 # Attachment properties
97 assert_equal 1, issue.attachments.size
99 assert_equal 1, issue.attachments.size
98 assert_equal 'Paella.jpg', issue.attachments.first.filename
100 assert_equal 'Paella.jpg', issue.attachments.first.filename
99 assert_equal 'image/jpeg', issue.attachments.first.content_type
101 assert_equal 'image/jpeg', issue.attachments.first.content_type
100 assert_equal 10790, issue.attachments.first.filesize
102 assert_equal 10790, issue.attachments.first.filesize
101 end
103 end
102
104
105 def test_add_issue_with_cc
106 issue = submit_email('ticket_with_cc.eml', :issue => {:project => 'ecookbook'})
107 assert issue.is_a?(Issue)
108 assert !issue.new_record?
109 issue.reload
110 assert issue.watched_by?(User.find_by_mail('dlopper@somenet.foo'))
111 assert_equal 1, issue.watchers.size
112 end
113
103 def test_add_issue_note
114 def test_add_issue_note
104 journal = submit_email('ticket_reply.eml')
115 journal = submit_email('ticket_reply.eml')
105 assert journal.is_a?(Journal)
116 assert journal.is_a?(Journal)
106 assert_equal User.find_by_login('jsmith'), journal.user
117 assert_equal User.find_by_login('jsmith'), journal.user
107 assert_equal Issue.find(2), journal.journalized
118 assert_equal Issue.find(2), journal.journalized
108 assert_match /This is reply/, journal.notes
119 assert_match /This is reply/, journal.notes
109 end
120 end
110
121
111 def test_add_issue_note_with_status_change
122 def test_add_issue_note_with_status_change
112 # This email contains: 'Status: Resolved'
123 # This email contains: 'Status: Resolved'
113 journal = submit_email('ticket_reply_with_status.eml')
124 journal = submit_email('ticket_reply_with_status.eml')
114 assert journal.is_a?(Journal)
125 assert journal.is_a?(Journal)
115 issue = Issue.find(journal.issue.id)
126 issue = Issue.find(journal.issue.id)
116 assert_equal User.find_by_login('jsmith'), journal.user
127 assert_equal User.find_by_login('jsmith'), journal.user
117 assert_equal Issue.find(2), journal.journalized
128 assert_equal Issue.find(2), journal.journalized
118 assert_match /This is reply/, journal.notes
129 assert_match /This is reply/, journal.notes
119 assert_equal IssueStatus.find_by_name("Resolved"), issue.status
130 assert_equal IssueStatus.find_by_name("Resolved"), issue.status
120 end
131 end
121
132
122 private
133 private
123
134
124 def submit_email(filename, options={})
135 def submit_email(filename, options={})
125 raw = IO.read(File.join(FIXTURES_PATH, filename))
136 raw = IO.read(File.join(FIXTURES_PATH, filename))
126 MailHandler.receive(raw, options)
137 MailHandler.receive(raw, options)
127 end
138 end
128 end
139 end
General Comments 0
You need to be logged in to leave comments. Login now