##// END OF EJS Templates
Misc tests....
Jean-Philippe Lang -
r13352:fcf9cf7bb17e
parent child
Show More
@@ -1,550 +1,550
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2014 Jean-Philippe Lang
2 # Copyright (C) 2006-2014 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 include ActionView::Helpers::SanitizeHelper
19 include ActionView::Helpers::SanitizeHelper
20 include Redmine::I18n
20 include Redmine::I18n
21
21
22 class UnauthorizedAction < StandardError; end
22 class UnauthorizedAction < StandardError; end
23 class MissingInformation < StandardError; end
23 class MissingInformation < StandardError; end
24
24
25 attr_reader :email, :user
25 attr_reader :email, :user
26
26
27 def self.receive(email, options={})
27 def self.receive(email, options={})
28 @@handler_options = options.deep_dup
28 @@handler_options = options.deep_dup
29
29
30 @@handler_options[:issue] ||= {}
30 @@handler_options[:issue] ||= {}
31
31
32 if @@handler_options[:allow_override].is_a?(String)
32 if @@handler_options[:allow_override].is_a?(String)
33 @@handler_options[:allow_override] = @@handler_options[:allow_override].split(',').collect(&:strip)
33 @@handler_options[:allow_override] = @@handler_options[:allow_override].split(',').collect(&:strip)
34 end
34 end
35 @@handler_options[:allow_override] ||= []
35 @@handler_options[:allow_override] ||= []
36 # Project needs to be overridable if not specified
36 # Project needs to be overridable if not specified
37 @@handler_options[:allow_override] << 'project' unless @@handler_options[:issue].has_key?(:project)
37 @@handler_options[:allow_override] << 'project' unless @@handler_options[:issue].has_key?(:project)
38 # Status overridable by default
38 # Status overridable by default
39 @@handler_options[:allow_override] << 'status' unless @@handler_options[:issue].has_key?(:status)
39 @@handler_options[:allow_override] << 'status' unless @@handler_options[:issue].has_key?(:status)
40
40
41 @@handler_options[:no_account_notice] = (@@handler_options[:no_account_notice].to_s == '1')
41 @@handler_options[:no_account_notice] = (@@handler_options[:no_account_notice].to_s == '1')
42 @@handler_options[:no_notification] = (@@handler_options[:no_notification].to_s == '1')
42 @@handler_options[:no_notification] = (@@handler_options[:no_notification].to_s == '1')
43 @@handler_options[:no_permission_check] = (@@handler_options[:no_permission_check].to_s == '1')
43 @@handler_options[:no_permission_check] = (@@handler_options[:no_permission_check].to_s == '1')
44
44
45 email.force_encoding('ASCII-8BIT')
45 email.force_encoding('ASCII-8BIT')
46 super(email)
46 super(email)
47 end
47 end
48
48
49 # Receives an email and rescues any exception
49 # Receives an email and rescues any exception
50 def self.safe_receive(*args)
50 def self.safe_receive(*args)
51 receive(*args)
51 receive(*args)
52 rescue => e
52 rescue Exception => e
53 logger.error "An unexpected error occurred when receiving email: #{e.message}" if logger
53 logger.error "An unexpected error occurred when receiving email: #{e.message}" if logger
54 return false
54 return false
55 end
55 end
56
56
57 # Extracts MailHandler options from environment variables
57 # Extracts MailHandler options from environment variables
58 # Use when receiving emails with rake tasks
58 # Use when receiving emails with rake tasks
59 def self.extract_options_from_env(env)
59 def self.extract_options_from_env(env)
60 options = {:issue => {}}
60 options = {:issue => {}}
61 %w(project status tracker category priority).each do |option|
61 %w(project status tracker category priority).each do |option|
62 options[:issue][option.to_sym] = env[option] if env[option]
62 options[:issue][option.to_sym] = env[option] if env[option]
63 end
63 end
64 %w(allow_override unknown_user no_permission_check no_account_notice default_group).each do |option|
64 %w(allow_override unknown_user no_permission_check no_account_notice default_group).each do |option|
65 options[option.to_sym] = env[option] if env[option]
65 options[option.to_sym] = env[option] if env[option]
66 end
66 end
67 options
67 options
68 end
68 end
69
69
70 def logger
70 def logger
71 Rails.logger
71 Rails.logger
72 end
72 end
73
73
74 cattr_accessor :ignored_emails_headers
74 cattr_accessor :ignored_emails_headers
75 @@ignored_emails_headers = {
75 @@ignored_emails_headers = {
76 'X-Auto-Response-Suppress' => 'oof',
76 'X-Auto-Response-Suppress' => 'oof',
77 'Auto-Submitted' => /\Aauto-(replied|generated)/
77 'Auto-Submitted' => /\Aauto-(replied|generated)/
78 }
78 }
79
79
80 # Processes incoming emails
80 # Processes incoming emails
81 # Returns the created object (eg. an issue, a message) or false
81 # Returns the created object (eg. an issue, a message) or false
82 def receive(email)
82 def receive(email)
83 @email = email
83 @email = email
84 sender_email = email.from.to_a.first.to_s.strip
84 sender_email = email.from.to_a.first.to_s.strip
85 # Ignore emails received from the application emission address to avoid hell cycles
85 # Ignore emails received from the application emission address to avoid hell cycles
86 if sender_email.downcase == Setting.mail_from.to_s.strip.downcase
86 if sender_email.downcase == Setting.mail_from.to_s.strip.downcase
87 if logger
87 if logger
88 logger.info "MailHandler: ignoring email from Redmine emission address [#{sender_email}]"
88 logger.info "MailHandler: ignoring email from Redmine emission address [#{sender_email}]"
89 end
89 end
90 return false
90 return false
91 end
91 end
92 # Ignore auto generated emails
92 # Ignore auto generated emails
93 self.class.ignored_emails_headers.each do |key, ignored_value|
93 self.class.ignored_emails_headers.each do |key, ignored_value|
94 value = email.header[key]
94 value = email.header[key]
95 if value
95 if value
96 value = value.to_s.downcase
96 value = value.to_s.downcase
97 if (ignored_value.is_a?(Regexp) && value.match(ignored_value)) || value == ignored_value
97 if (ignored_value.is_a?(Regexp) && value.match(ignored_value)) || value == ignored_value
98 if logger
98 if logger
99 logger.info "MailHandler: ignoring email with #{key}:#{value} header"
99 logger.info "MailHandler: ignoring email with #{key}:#{value} header"
100 end
100 end
101 return false
101 return false
102 end
102 end
103 end
103 end
104 end
104 end
105 @user = User.find_by_mail(sender_email) if sender_email.present?
105 @user = User.find_by_mail(sender_email) if sender_email.present?
106 if @user && !@user.active?
106 if @user && !@user.active?
107 if logger
107 if logger
108 logger.info "MailHandler: ignoring email from non-active user [#{@user.login}]"
108 logger.info "MailHandler: ignoring email from non-active user [#{@user.login}]"
109 end
109 end
110 return false
110 return false
111 end
111 end
112 if @user.nil?
112 if @user.nil?
113 # Email was submitted by an unknown user
113 # Email was submitted by an unknown user
114 case @@handler_options[:unknown_user]
114 case @@handler_options[:unknown_user]
115 when 'accept'
115 when 'accept'
116 @user = User.anonymous
116 @user = User.anonymous
117 when 'create'
117 when 'create'
118 @user = create_user_from_email
118 @user = create_user_from_email
119 if @user
119 if @user
120 if logger
120 if logger
121 logger.info "MailHandler: [#{@user.login}] account created"
121 logger.info "MailHandler: [#{@user.login}] account created"
122 end
122 end
123 add_user_to_group(@@handler_options[:default_group])
123 add_user_to_group(@@handler_options[:default_group])
124 unless @@handler_options[:no_account_notice]
124 unless @@handler_options[:no_account_notice]
125 Mailer.account_information(@user, @user.password).deliver
125 Mailer.account_information(@user, @user.password).deliver
126 end
126 end
127 else
127 else
128 if logger
128 if logger
129 logger.error "MailHandler: could not create account for [#{sender_email}]"
129 logger.error "MailHandler: could not create account for [#{sender_email}]"
130 end
130 end
131 return false
131 return false
132 end
132 end
133 else
133 else
134 # Default behaviour, emails from unknown users are ignored
134 # Default behaviour, emails from unknown users are ignored
135 if logger
135 if logger
136 logger.info "MailHandler: ignoring email from unknown user [#{sender_email}]"
136 logger.info "MailHandler: ignoring email from unknown user [#{sender_email}]"
137 end
137 end
138 return false
138 return false
139 end
139 end
140 end
140 end
141 User.current = @user
141 User.current = @user
142 dispatch
142 dispatch
143 end
143 end
144
144
145 private
145 private
146
146
147 MESSAGE_ID_RE = %r{^<?redmine\.([a-z0-9_]+)\-(\d+)\.\d+(\.[a-f0-9]+)?@}
147 MESSAGE_ID_RE = %r{^<?redmine\.([a-z0-9_]+)\-(\d+)\.\d+(\.[a-f0-9]+)?@}
148 ISSUE_REPLY_SUBJECT_RE = %r{\[(?:[^\]]*\s+)?#(\d+)\]}
148 ISSUE_REPLY_SUBJECT_RE = %r{\[(?:[^\]]*\s+)?#(\d+)\]}
149 MESSAGE_REPLY_SUBJECT_RE = %r{\[[^\]]*msg(\d+)\]}
149 MESSAGE_REPLY_SUBJECT_RE = %r{\[[^\]]*msg(\d+)\]}
150
150
151 def dispatch
151 def dispatch
152 headers = [email.in_reply_to, email.references].flatten.compact
152 headers = [email.in_reply_to, email.references].flatten.compact
153 subject = email.subject.to_s
153 subject = email.subject.to_s
154 if headers.detect {|h| h.to_s =~ MESSAGE_ID_RE}
154 if headers.detect {|h| h.to_s =~ MESSAGE_ID_RE}
155 klass, object_id = $1, $2.to_i
155 klass, object_id = $1, $2.to_i
156 method_name = "receive_#{klass}_reply"
156 method_name = "receive_#{klass}_reply"
157 if self.class.private_instance_methods.collect(&:to_s).include?(method_name)
157 if self.class.private_instance_methods.collect(&:to_s).include?(method_name)
158 send method_name, object_id
158 send method_name, object_id
159 else
159 else
160 # ignoring it
160 # ignoring it
161 end
161 end
162 elsif m = subject.match(ISSUE_REPLY_SUBJECT_RE)
162 elsif m = subject.match(ISSUE_REPLY_SUBJECT_RE)
163 receive_issue_reply(m[1].to_i)
163 receive_issue_reply(m[1].to_i)
164 elsif m = subject.match(MESSAGE_REPLY_SUBJECT_RE)
164 elsif m = subject.match(MESSAGE_REPLY_SUBJECT_RE)
165 receive_message_reply(m[1].to_i)
165 receive_message_reply(m[1].to_i)
166 else
166 else
167 dispatch_to_default
167 dispatch_to_default
168 end
168 end
169 rescue ActiveRecord::RecordInvalid => e
169 rescue ActiveRecord::RecordInvalid => e
170 # TODO: send a email to the user
170 # TODO: send a email to the user
171 logger.error e.message if logger
171 logger.error e.message if logger
172 false
172 false
173 rescue MissingInformation => e
173 rescue MissingInformation => e
174 logger.error "MailHandler: missing information from #{user}: #{e.message}" if logger
174 logger.error "MailHandler: missing information from #{user}: #{e.message}" if logger
175 false
175 false
176 rescue UnauthorizedAction => e
176 rescue UnauthorizedAction => e
177 logger.error "MailHandler: unauthorized attempt from #{user}" if logger
177 logger.error "MailHandler: unauthorized attempt from #{user}" if logger
178 false
178 false
179 end
179 end
180
180
181 def dispatch_to_default
181 def dispatch_to_default
182 receive_issue
182 receive_issue
183 end
183 end
184
184
185 # Creates a new issue
185 # Creates a new issue
186 def receive_issue
186 def receive_issue
187 project = target_project
187 project = target_project
188 # check permission
188 # check permission
189 unless @@handler_options[:no_permission_check]
189 unless @@handler_options[:no_permission_check]
190 raise UnauthorizedAction unless user.allowed_to?(:add_issues, project)
190 raise UnauthorizedAction unless user.allowed_to?(:add_issues, project)
191 end
191 end
192
192
193 issue = Issue.new(:author => user, :project => project)
193 issue = Issue.new(:author => user, :project => project)
194 issue.safe_attributes = issue_attributes_from_keywords(issue)
194 issue.safe_attributes = issue_attributes_from_keywords(issue)
195 issue.safe_attributes = {'custom_field_values' => custom_field_values_from_keywords(issue)}
195 issue.safe_attributes = {'custom_field_values' => custom_field_values_from_keywords(issue)}
196 issue.subject = cleaned_up_subject
196 issue.subject = cleaned_up_subject
197 if issue.subject.blank?
197 if issue.subject.blank?
198 issue.subject = '(no subject)'
198 issue.subject = '(no subject)'
199 end
199 end
200 issue.description = cleaned_up_text_body
200 issue.description = cleaned_up_text_body
201 issue.start_date ||= Date.today if Setting.default_issue_start_date_to_creation_date?
201 issue.start_date ||= Date.today if Setting.default_issue_start_date_to_creation_date?
202
202
203 # add To and Cc as watchers before saving so the watchers can reply to Redmine
203 # add To and Cc as watchers before saving so the watchers can reply to Redmine
204 add_watchers(issue)
204 add_watchers(issue)
205 issue.save!
205 issue.save!
206 add_attachments(issue)
206 add_attachments(issue)
207 logger.info "MailHandler: issue ##{issue.id} created by #{user}" if logger
207 logger.info "MailHandler: issue ##{issue.id} created by #{user}" if logger
208 issue
208 issue
209 end
209 end
210
210
211 # Adds a note to an existing issue
211 # Adds a note to an existing issue
212 def receive_issue_reply(issue_id, from_journal=nil)
212 def receive_issue_reply(issue_id, from_journal=nil)
213 issue = Issue.find_by_id(issue_id)
213 issue = Issue.find_by_id(issue_id)
214 return unless issue
214 return unless issue
215 # check permission
215 # check permission
216 unless @@handler_options[:no_permission_check]
216 unless @@handler_options[:no_permission_check]
217 unless user.allowed_to?(:add_issue_notes, issue.project) ||
217 unless user.allowed_to?(:add_issue_notes, issue.project) ||
218 user.allowed_to?(:edit_issues, issue.project)
218 user.allowed_to?(:edit_issues, issue.project)
219 raise UnauthorizedAction
219 raise UnauthorizedAction
220 end
220 end
221 end
221 end
222
222
223 # ignore CLI-supplied defaults for new issues
223 # ignore CLI-supplied defaults for new issues
224 @@handler_options[:issue].clear
224 @@handler_options[:issue].clear
225
225
226 journal = issue.init_journal(user)
226 journal = issue.init_journal(user)
227 if from_journal && from_journal.private_notes?
227 if from_journal && from_journal.private_notes?
228 # If the received email was a reply to a private note, make the added note private
228 # If the received email was a reply to a private note, make the added note private
229 issue.private_notes = true
229 issue.private_notes = true
230 end
230 end
231 issue.safe_attributes = issue_attributes_from_keywords(issue)
231 issue.safe_attributes = issue_attributes_from_keywords(issue)
232 issue.safe_attributes = {'custom_field_values' => custom_field_values_from_keywords(issue)}
232 issue.safe_attributes = {'custom_field_values' => custom_field_values_from_keywords(issue)}
233 journal.notes = cleaned_up_text_body
233 journal.notes = cleaned_up_text_body
234 add_attachments(issue)
234 add_attachments(issue)
235 issue.save!
235 issue.save!
236 if logger
236 if logger
237 logger.info "MailHandler: issue ##{issue.id} updated by #{user}"
237 logger.info "MailHandler: issue ##{issue.id} updated by #{user}"
238 end
238 end
239 journal
239 journal
240 end
240 end
241
241
242 # Reply will be added to the issue
242 # Reply will be added to the issue
243 def receive_journal_reply(journal_id)
243 def receive_journal_reply(journal_id)
244 journal = Journal.find_by_id(journal_id)
244 journal = Journal.find_by_id(journal_id)
245 if journal && journal.journalized_type == 'Issue'
245 if journal && journal.journalized_type == 'Issue'
246 receive_issue_reply(journal.journalized_id, journal)
246 receive_issue_reply(journal.journalized_id, journal)
247 end
247 end
248 end
248 end
249
249
250 # Receives a reply to a forum message
250 # Receives a reply to a forum message
251 def receive_message_reply(message_id)
251 def receive_message_reply(message_id)
252 message = Message.find_by_id(message_id)
252 message = Message.find_by_id(message_id)
253 if message
253 if message
254 message = message.root
254 message = message.root
255
255
256 unless @@handler_options[:no_permission_check]
256 unless @@handler_options[:no_permission_check]
257 raise UnauthorizedAction unless user.allowed_to?(:add_messages, message.project)
257 raise UnauthorizedAction unless user.allowed_to?(:add_messages, message.project)
258 end
258 end
259
259
260 if !message.locked?
260 if !message.locked?
261 reply = Message.new(:subject => cleaned_up_subject.gsub(%r{^.*msg\d+\]}, '').strip,
261 reply = Message.new(:subject => cleaned_up_subject.gsub(%r{^.*msg\d+\]}, '').strip,
262 :content => cleaned_up_text_body)
262 :content => cleaned_up_text_body)
263 reply.author = user
263 reply.author = user
264 reply.board = message.board
264 reply.board = message.board
265 message.children << reply
265 message.children << reply
266 add_attachments(reply)
266 add_attachments(reply)
267 reply
267 reply
268 else
268 else
269 if logger
269 if logger
270 logger.info "MailHandler: ignoring reply from [#{sender_email}] to a locked topic"
270 logger.info "MailHandler: ignoring reply from [#{sender_email}] to a locked topic"
271 end
271 end
272 end
272 end
273 end
273 end
274 end
274 end
275
275
276 def add_attachments(obj)
276 def add_attachments(obj)
277 if email.attachments && email.attachments.any?
277 if email.attachments && email.attachments.any?
278 email.attachments.each do |attachment|
278 email.attachments.each do |attachment|
279 next unless accept_attachment?(attachment)
279 next unless accept_attachment?(attachment)
280 obj.attachments << Attachment.create(:container => obj,
280 obj.attachments << Attachment.create(:container => obj,
281 :file => attachment.decoded,
281 :file => attachment.decoded,
282 :filename => attachment.filename,
282 :filename => attachment.filename,
283 :author => user,
283 :author => user,
284 :content_type => attachment.mime_type)
284 :content_type => attachment.mime_type)
285 end
285 end
286 end
286 end
287 end
287 end
288
288
289 # Returns false if the +attachment+ of the incoming email should be ignored
289 # Returns false if the +attachment+ of the incoming email should be ignored
290 def accept_attachment?(attachment)
290 def accept_attachment?(attachment)
291 @excluded ||= Setting.mail_handler_excluded_filenames.to_s.split(',').map(&:strip).reject(&:blank?)
291 @excluded ||= Setting.mail_handler_excluded_filenames.to_s.split(',').map(&:strip).reject(&:blank?)
292 @excluded.each do |pattern|
292 @excluded.each do |pattern|
293 regexp = %r{\A#{Regexp.escape(pattern).gsub("\\*", ".*")}\z}i
293 regexp = %r{\A#{Regexp.escape(pattern).gsub("\\*", ".*")}\z}i
294 if attachment.filename.to_s =~ regexp
294 if attachment.filename.to_s =~ regexp
295 logger.info "MailHandler: ignoring attachment #{attachment.filename} matching #{pattern}"
295 logger.info "MailHandler: ignoring attachment #{attachment.filename} matching #{pattern}"
296 return false
296 return false
297 end
297 end
298 end
298 end
299 true
299 true
300 end
300 end
301
301
302 # Adds To and Cc as watchers of the given object if the sender has the
302 # Adds To and Cc as watchers of the given object if the sender has the
303 # appropriate permission
303 # appropriate permission
304 def add_watchers(obj)
304 def add_watchers(obj)
305 if user.allowed_to?("add_#{obj.class.name.underscore}_watchers".to_sym, obj.project)
305 if user.allowed_to?("add_#{obj.class.name.underscore}_watchers".to_sym, obj.project)
306 addresses = [email.to, email.cc].flatten.compact.uniq.collect {|a| a.strip.downcase}
306 addresses = [email.to, email.cc].flatten.compact.uniq.collect {|a| a.strip.downcase}
307 unless addresses.empty?
307 unless addresses.empty?
308 User.active.where('LOWER(mail) IN (?)', addresses).each do |w|
308 User.active.where('LOWER(mail) IN (?)', addresses).each do |w|
309 obj.add_watcher(w)
309 obj.add_watcher(w)
310 end
310 end
311 end
311 end
312 end
312 end
313 end
313 end
314
314
315 def get_keyword(attr, options={})
315 def get_keyword(attr, options={})
316 @keywords ||= {}
316 @keywords ||= {}
317 if @keywords.has_key?(attr)
317 if @keywords.has_key?(attr)
318 @keywords[attr]
318 @keywords[attr]
319 else
319 else
320 @keywords[attr] = begin
320 @keywords[attr] = begin
321 if (options[:override] || @@handler_options[:allow_override].include?(attr.to_s)) &&
321 if (options[:override] || @@handler_options[:allow_override].include?(attr.to_s)) &&
322 (v = extract_keyword!(cleaned_up_text_body, attr, options[:format]))
322 (v = extract_keyword!(cleaned_up_text_body, attr, options[:format]))
323 v
323 v
324 elsif !@@handler_options[:issue][attr].blank?
324 elsif !@@handler_options[:issue][attr].blank?
325 @@handler_options[:issue][attr]
325 @@handler_options[:issue][attr]
326 end
326 end
327 end
327 end
328 end
328 end
329 end
329 end
330
330
331 # Destructively extracts the value for +attr+ in +text+
331 # Destructively extracts the value for +attr+ in +text+
332 # Returns nil if no matching keyword found
332 # Returns nil if no matching keyword found
333 def extract_keyword!(text, attr, format=nil)
333 def extract_keyword!(text, attr, format=nil)
334 keys = [attr.to_s.humanize]
334 keys = [attr.to_s.humanize]
335 if attr.is_a?(Symbol)
335 if attr.is_a?(Symbol)
336 if user && user.language.present?
336 if user && user.language.present?
337 keys << l("field_#{attr}", :default => '', :locale => user.language)
337 keys << l("field_#{attr}", :default => '', :locale => user.language)
338 end
338 end
339 if Setting.default_language.present?
339 if Setting.default_language.present?
340 keys << l("field_#{attr}", :default => '', :locale => Setting.default_language)
340 keys << l("field_#{attr}", :default => '', :locale => Setting.default_language)
341 end
341 end
342 end
342 end
343 keys.reject! {|k| k.blank?}
343 keys.reject! {|k| k.blank?}
344 keys.collect! {|k| Regexp.escape(k)}
344 keys.collect! {|k| Regexp.escape(k)}
345 format ||= '.+'
345 format ||= '.+'
346 keyword = nil
346 keyword = nil
347 regexp = /^(#{keys.join('|')})[ \t]*:[ \t]*(#{format})\s*$/i
347 regexp = /^(#{keys.join('|')})[ \t]*:[ \t]*(#{format})\s*$/i
348 if m = text.match(regexp)
348 if m = text.match(regexp)
349 keyword = m[2].strip
349 keyword = m[2].strip
350 text.sub!(regexp, '')
350 text.sub!(regexp, '')
351 end
351 end
352 keyword
352 keyword
353 end
353 end
354
354
355 def target_project
355 def target_project
356 # TODO: other ways to specify project:
356 # TODO: other ways to specify project:
357 # * parse the email To field
357 # * parse the email To field
358 # * specific project (eg. Setting.mail_handler_target_project)
358 # * specific project (eg. Setting.mail_handler_target_project)
359 target = Project.find_by_identifier(get_keyword(:project))
359 target = Project.find_by_identifier(get_keyword(:project))
360 if target.nil?
360 if target.nil?
361 # Invalid project keyword, use the project specified as the default one
361 # Invalid project keyword, use the project specified as the default one
362 default_project = @@handler_options[:issue][:project]
362 default_project = @@handler_options[:issue][:project]
363 if default_project.present?
363 if default_project.present?
364 target = Project.find_by_identifier(default_project)
364 target = Project.find_by_identifier(default_project)
365 end
365 end
366 end
366 end
367 raise MissingInformation.new('Unable to determine target project') if target.nil?
367 raise MissingInformation.new('Unable to determine target project') if target.nil?
368 target
368 target
369 end
369 end
370
370
371 # Returns a Hash of issue attributes extracted from keywords in the email body
371 # Returns a Hash of issue attributes extracted from keywords in the email body
372 def issue_attributes_from_keywords(issue)
372 def issue_attributes_from_keywords(issue)
373 assigned_to = (k = get_keyword(:assigned_to, :override => true)) && find_assignee_from_keyword(k, issue)
373 assigned_to = (k = get_keyword(:assigned_to, :override => true)) && find_assignee_from_keyword(k, issue)
374
374
375 attrs = {
375 attrs = {
376 'tracker_id' => (k = get_keyword(:tracker)) && issue.project.trackers.named(k).first.try(:id),
376 'tracker_id' => (k = get_keyword(:tracker)) && issue.project.trackers.named(k).first.try(:id),
377 'status_id' => (k = get_keyword(:status)) && IssueStatus.named(k).first.try(:id),
377 'status_id' => (k = get_keyword(:status)) && IssueStatus.named(k).first.try(:id),
378 'priority_id' => (k = get_keyword(:priority)) && IssuePriority.named(k).first.try(:id),
378 'priority_id' => (k = get_keyword(:priority)) && IssuePriority.named(k).first.try(:id),
379 'category_id' => (k = get_keyword(:category)) && issue.project.issue_categories.named(k).first.try(:id),
379 'category_id' => (k = get_keyword(:category)) && issue.project.issue_categories.named(k).first.try(:id),
380 'assigned_to_id' => assigned_to.try(:id),
380 'assigned_to_id' => assigned_to.try(:id),
381 'fixed_version_id' => (k = get_keyword(:fixed_version, :override => true)) &&
381 'fixed_version_id' => (k = get_keyword(:fixed_version, :override => true)) &&
382 issue.project.shared_versions.named(k).first.try(:id),
382 issue.project.shared_versions.named(k).first.try(:id),
383 'start_date' => get_keyword(:start_date, :override => true, :format => '\d{4}-\d{2}-\d{2}'),
383 'start_date' => get_keyword(:start_date, :override => true, :format => '\d{4}-\d{2}-\d{2}'),
384 'due_date' => get_keyword(:due_date, :override => true, :format => '\d{4}-\d{2}-\d{2}'),
384 'due_date' => get_keyword(:due_date, :override => true, :format => '\d{4}-\d{2}-\d{2}'),
385 'estimated_hours' => get_keyword(:estimated_hours, :override => true),
385 'estimated_hours' => get_keyword(:estimated_hours, :override => true),
386 'done_ratio' => get_keyword(:done_ratio, :override => true, :format => '(\d|10)?0')
386 'done_ratio' => get_keyword(:done_ratio, :override => true, :format => '(\d|10)?0')
387 }.delete_if {|k, v| v.blank? }
387 }.delete_if {|k, v| v.blank? }
388
388
389 if issue.new_record? && attrs['tracker_id'].nil?
389 if issue.new_record? && attrs['tracker_id'].nil?
390 attrs['tracker_id'] = issue.project.trackers.first.try(:id)
390 attrs['tracker_id'] = issue.project.trackers.first.try(:id)
391 end
391 end
392
392
393 attrs
393 attrs
394 end
394 end
395
395
396 # Returns a Hash of issue custom field values extracted from keywords in the email body
396 # Returns a Hash of issue custom field values extracted from keywords in the email body
397 def custom_field_values_from_keywords(customized)
397 def custom_field_values_from_keywords(customized)
398 customized.custom_field_values.inject({}) do |h, v|
398 customized.custom_field_values.inject({}) do |h, v|
399 if keyword = get_keyword(v.custom_field.name, :override => true)
399 if keyword = get_keyword(v.custom_field.name, :override => true)
400 h[v.custom_field.id.to_s] = v.custom_field.value_from_keyword(keyword, customized)
400 h[v.custom_field.id.to_s] = v.custom_field.value_from_keyword(keyword, customized)
401 end
401 end
402 h
402 h
403 end
403 end
404 end
404 end
405
405
406 # Returns the text/plain part of the email
406 # Returns the text/plain part of the email
407 # If not found (eg. HTML-only email), returns the body with tags removed
407 # If not found (eg. HTML-only email), returns the body with tags removed
408 def plain_text_body
408 def plain_text_body
409 return @plain_text_body unless @plain_text_body.nil?
409 return @plain_text_body unless @plain_text_body.nil?
410
410
411 parts = if (text_parts = email.all_parts.select {|p| p.mime_type == 'text/plain'}).present?
411 parts = if (text_parts = email.all_parts.select {|p| p.mime_type == 'text/plain'}).present?
412 text_parts
412 text_parts
413 elsif (html_parts = email.all_parts.select {|p| p.mime_type == 'text/html'}).present?
413 elsif (html_parts = email.all_parts.select {|p| p.mime_type == 'text/html'}).present?
414 html_parts
414 html_parts
415 else
415 else
416 [email]
416 [email]
417 end
417 end
418
418
419 parts.reject! do |part|
419 parts.reject! do |part|
420 part.attachment?
420 part.attachment?
421 end
421 end
422
422
423 @plain_text_body = parts.map do |p|
423 @plain_text_body = parts.map do |p|
424 body_charset = Mail::RubyVer.respond_to?(:pick_encoding) ?
424 body_charset = Mail::RubyVer.respond_to?(:pick_encoding) ?
425 Mail::RubyVer.pick_encoding(p.charset).to_s : p.charset
425 Mail::RubyVer.pick_encoding(p.charset).to_s : p.charset
426 Redmine::CodesetUtil.to_utf8(p.body.decoded, body_charset)
426 Redmine::CodesetUtil.to_utf8(p.body.decoded, body_charset)
427 end.join("\r\n")
427 end.join("\r\n")
428
428
429 # strip html tags and remove doctype directive
429 # strip html tags and remove doctype directive
430 if parts.any? {|p| p.mime_type == 'text/html'}
430 if parts.any? {|p| p.mime_type == 'text/html'}
431 @plain_text_body = strip_tags(@plain_text_body.strip)
431 @plain_text_body = strip_tags(@plain_text_body.strip)
432 @plain_text_body.sub! %r{^<!DOCTYPE .*$}, ''
432 @plain_text_body.sub! %r{^<!DOCTYPE .*$}, ''
433 end
433 end
434
434
435 @plain_text_body
435 @plain_text_body
436 end
436 end
437
437
438 def cleaned_up_text_body
438 def cleaned_up_text_body
439 @cleaned_up_text_body ||= cleanup_body(plain_text_body)
439 @cleaned_up_text_body ||= cleanup_body(plain_text_body)
440 end
440 end
441
441
442 def cleaned_up_subject
442 def cleaned_up_subject
443 subject = email.subject.to_s
443 subject = email.subject.to_s
444 subject.strip[0,255]
444 subject.strip[0,255]
445 end
445 end
446
446
447 def self.full_sanitizer
447 def self.full_sanitizer
448 @full_sanitizer ||= HTML::FullSanitizer.new
448 @full_sanitizer ||= HTML::FullSanitizer.new
449 end
449 end
450
450
451 def self.assign_string_attribute_with_limit(object, attribute, value, limit=nil)
451 def self.assign_string_attribute_with_limit(object, attribute, value, limit=nil)
452 limit ||= object.class.columns_hash[attribute.to_s].limit || 255
452 limit ||= object.class.columns_hash[attribute.to_s].limit || 255
453 value = value.to_s.slice(0, limit)
453 value = value.to_s.slice(0, limit)
454 object.send("#{attribute}=", value)
454 object.send("#{attribute}=", value)
455 end
455 end
456
456
457 # Returns a User from an email address and a full name
457 # Returns a User from an email address and a full name
458 def self.new_user_from_attributes(email_address, fullname=nil)
458 def self.new_user_from_attributes(email_address, fullname=nil)
459 user = User.new
459 user = User.new
460
460
461 # Truncating the email address would result in an invalid format
461 # Truncating the email address would result in an invalid format
462 user.mail = email_address
462 user.mail = email_address
463 assign_string_attribute_with_limit(user, 'login', email_address, User::LOGIN_LENGTH_LIMIT)
463 assign_string_attribute_with_limit(user, 'login', email_address, User::LOGIN_LENGTH_LIMIT)
464
464
465 names = fullname.blank? ? email_address.gsub(/@.*$/, '').split('.') : fullname.split
465 names = fullname.blank? ? email_address.gsub(/@.*$/, '').split('.') : fullname.split
466 assign_string_attribute_with_limit(user, 'firstname', names.shift, 30)
466 assign_string_attribute_with_limit(user, 'firstname', names.shift, 30)
467 assign_string_attribute_with_limit(user, 'lastname', names.join(' '), 30)
467 assign_string_attribute_with_limit(user, 'lastname', names.join(' '), 30)
468 user.lastname = '-' if user.lastname.blank?
468 user.lastname = '-' if user.lastname.blank?
469 user.language = Setting.default_language
469 user.language = Setting.default_language
470 user.generate_password = true
470 user.generate_password = true
471 user.mail_notification = 'only_my_events'
471 user.mail_notification = 'only_my_events'
472
472
473 unless user.valid?
473 unless user.valid?
474 user.login = "user#{Redmine::Utils.random_hex(6)}" unless user.errors[:login].blank?
474 user.login = "user#{Redmine::Utils.random_hex(6)}" unless user.errors[:login].blank?
475 user.firstname = "-" unless user.errors[:firstname].blank?
475 user.firstname = "-" unless user.errors[:firstname].blank?
476 (puts user.errors[:lastname];user.lastname = "-") unless user.errors[:lastname].blank?
476 (puts user.errors[:lastname];user.lastname = "-") unless user.errors[:lastname].blank?
477 end
477 end
478
478
479 user
479 user
480 end
480 end
481
481
482 # Creates a User for the +email+ sender
482 # Creates a User for the +email+ sender
483 # Returns the user or nil if it could not be created
483 # Returns the user or nil if it could not be created
484 def create_user_from_email
484 def create_user_from_email
485 from = email.header['from'].to_s
485 from = email.header['from'].to_s
486 addr, name = from, nil
486 addr, name = from, nil
487 if m = from.match(/^"?(.+?)"?\s+<(.+@.+)>$/)
487 if m = from.match(/^"?(.+?)"?\s+<(.+@.+)>$/)
488 addr, name = m[2], m[1]
488 addr, name = m[2], m[1]
489 end
489 end
490 if addr.present?
490 if addr.present?
491 user = self.class.new_user_from_attributes(addr, name)
491 user = self.class.new_user_from_attributes(addr, name)
492 if @@handler_options[:no_notification]
492 if @@handler_options[:no_notification]
493 user.mail_notification = 'none'
493 user.mail_notification = 'none'
494 end
494 end
495 if user.save
495 if user.save
496 user
496 user
497 else
497 else
498 logger.error "MailHandler: failed to create User: #{user.errors.full_messages}" if logger
498 logger.error "MailHandler: failed to create User: #{user.errors.full_messages}" if logger
499 nil
499 nil
500 end
500 end
501 else
501 else
502 logger.error "MailHandler: failed to create User: no FROM address found" if logger
502 logger.error "MailHandler: failed to create User: no FROM address found" if logger
503 nil
503 nil
504 end
504 end
505 end
505 end
506
506
507 # Adds the newly created user to default group
507 # Adds the newly created user to default group
508 def add_user_to_group(default_group)
508 def add_user_to_group(default_group)
509 if default_group.present?
509 if default_group.present?
510 default_group.split(',').each do |group_name|
510 default_group.split(',').each do |group_name|
511 if group = Group.named(group_name).first
511 if group = Group.named(group_name).first
512 group.users << @user
512 group.users << @user
513 elsif logger
513 elsif logger
514 logger.warn "MailHandler: could not add user to [#{group_name}], group not found"
514 logger.warn "MailHandler: could not add user to [#{group_name}], group not found"
515 end
515 end
516 end
516 end
517 end
517 end
518 end
518 end
519
519
520 # Removes the email body of text after the truncation configurations.
520 # Removes the email body of text after the truncation configurations.
521 def cleanup_body(body)
521 def cleanup_body(body)
522 delimiters = Setting.mail_handler_body_delimiters.to_s.split(/[\r\n]+/).reject(&:blank?).map {|s| Regexp.escape(s)}
522 delimiters = Setting.mail_handler_body_delimiters.to_s.split(/[\r\n]+/).reject(&:blank?).map {|s| Regexp.escape(s)}
523 unless delimiters.empty?
523 unless delimiters.empty?
524 regex = Regexp.new("^[> ]*(#{ delimiters.join('|') })\s*[\r\n].*", Regexp::MULTILINE)
524 regex = Regexp.new("^[> ]*(#{ delimiters.join('|') })\s*[\r\n].*", Regexp::MULTILINE)
525 body = body.gsub(regex, '')
525 body = body.gsub(regex, '')
526 end
526 end
527 body.strip
527 body.strip
528 end
528 end
529
529
530 def find_assignee_from_keyword(keyword, issue)
530 def find_assignee_from_keyword(keyword, issue)
531 keyword = keyword.to_s.downcase
531 keyword = keyword.to_s.downcase
532 assignable = issue.assignable_users
532 assignable = issue.assignable_users
533 assignee = nil
533 assignee = nil
534 assignee ||= assignable.detect {|a|
534 assignee ||= assignable.detect {|a|
535 a.mail.to_s.downcase == keyword ||
535 a.mail.to_s.downcase == keyword ||
536 a.login.to_s.downcase == keyword
536 a.login.to_s.downcase == keyword
537 }
537 }
538 if assignee.nil? && keyword.match(/ /)
538 if assignee.nil? && keyword.match(/ /)
539 firstname, lastname = *(keyword.split) # "First Last Throwaway"
539 firstname, lastname = *(keyword.split) # "First Last Throwaway"
540 assignee ||= assignable.detect {|a|
540 assignee ||= assignable.detect {|a|
541 a.is_a?(User) && a.firstname.to_s.downcase == firstname &&
541 a.is_a?(User) && a.firstname.to_s.downcase == firstname &&
542 a.lastname.to_s.downcase == lastname
542 a.lastname.to_s.downcase == lastname
543 }
543 }
544 end
544 end
545 if assignee.nil?
545 if assignee.nil?
546 assignee ||= assignable.detect {|a| a.name.downcase == keyword}
546 assignee ||= assignable.detect {|a| a.name.downcase == keyword}
547 end
547 end
548 assignee
548 assignee
549 end
549 end
550 end
550 end
@@ -1,952 +1,958
1 # encoding: utf-8
1 # encoding: utf-8
2 #
2 #
3 # Redmine - project management software
3 # Redmine - project management software
4 # Copyright (C) 2006-2014 Jean-Philippe Lang
4 # Copyright (C) 2006-2014 Jean-Philippe Lang
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
19
20 require File.expand_path('../../test_helper', __FILE__)
20 require File.expand_path('../../test_helper', __FILE__)
21
21
22 class MailHandlerTest < ActiveSupport::TestCase
22 class MailHandlerTest < ActiveSupport::TestCase
23 fixtures :users, :projects, :enabled_modules, :roles,
23 fixtures :users, :projects, :enabled_modules, :roles,
24 :members, :member_roles, :users,
24 :members, :member_roles, :users,
25 :issues, :issue_statuses,
25 :issues, :issue_statuses,
26 :workflows, :trackers, :projects_trackers,
26 :workflows, :trackers, :projects_trackers,
27 :versions, :enumerations, :issue_categories,
27 :versions, :enumerations, :issue_categories,
28 :custom_fields, :custom_fields_trackers, :custom_fields_projects,
28 :custom_fields, :custom_fields_trackers, :custom_fields_projects,
29 :boards, :messages
29 :boards, :messages
30
30
31 FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures/mail_handler'
31 FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures/mail_handler'
32
32
33 def setup
33 def setup
34 ActionMailer::Base.deliveries.clear
34 ActionMailer::Base.deliveries.clear
35 Setting.notified_events = Redmine::Notifiable.all.collect(&:name)
35 Setting.notified_events = Redmine::Notifiable.all.collect(&:name)
36 end
36 end
37
37
38 def teardown
38 def teardown
39 Setting.clear_cache
39 Setting.clear_cache
40 end
40 end
41
41
42 def test_add_issue
42 def test_add_issue
43 ActionMailer::Base.deliveries.clear
43 ActionMailer::Base.deliveries.clear
44 lft1 = new_issue_lft
44 lft1 = new_issue_lft
45 # This email contains: 'Project: onlinestore'
45 # This email contains: 'Project: onlinestore'
46 issue = submit_email('ticket_on_given_project.eml')
46 issue = submit_email('ticket_on_given_project.eml')
47 assert issue.is_a?(Issue)
47 assert issue.is_a?(Issue)
48 assert !issue.new_record?
48 assert !issue.new_record?
49 issue.reload
49 issue.reload
50 assert_equal Project.find(2), issue.project
50 assert_equal Project.find(2), issue.project
51 assert_equal issue.project.trackers.first, issue.tracker
51 assert_equal issue.project.trackers.first, issue.tracker
52 assert_equal 'New ticket on a given project', issue.subject
52 assert_equal 'New ticket on a given project', issue.subject
53 assert_equal User.find_by_login('jsmith'), issue.author
53 assert_equal User.find_by_login('jsmith'), issue.author
54 assert_equal IssueStatus.find_by_name('Resolved'), issue.status
54 assert_equal IssueStatus.find_by_name('Resolved'), issue.status
55 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
55 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
56 assert_equal '2010-01-01', issue.start_date.to_s
56 assert_equal '2010-01-01', issue.start_date.to_s
57 assert_equal '2010-12-31', issue.due_date.to_s
57 assert_equal '2010-12-31', issue.due_date.to_s
58 assert_equal User.find_by_login('jsmith'), issue.assigned_to
58 assert_equal User.find_by_login('jsmith'), issue.assigned_to
59 assert_equal Version.find_by_name('Alpha'), issue.fixed_version
59 assert_equal Version.find_by_name('Alpha'), issue.fixed_version
60 assert_equal 2.5, issue.estimated_hours
60 assert_equal 2.5, issue.estimated_hours
61 assert_equal 30, issue.done_ratio
61 assert_equal 30, issue.done_ratio
62 assert_equal [issue.id, lft1, lft1 + 1], [issue.root_id, issue.lft, issue.rgt]
62 assert_equal [issue.id, lft1, lft1 + 1], [issue.root_id, issue.lft, issue.rgt]
63 # keywords should be removed from the email body
63 # keywords should be removed from the email body
64 assert !issue.description.match(/^Project:/i)
64 assert !issue.description.match(/^Project:/i)
65 assert !issue.description.match(/^Status:/i)
65 assert !issue.description.match(/^Status:/i)
66 assert !issue.description.match(/^Start Date:/i)
66 assert !issue.description.match(/^Start Date:/i)
67 # Email notification should be sent
67 # Email notification should be sent
68 mail = ActionMailer::Base.deliveries.last
68 mail = ActionMailer::Base.deliveries.last
69 assert_not_nil mail
69 assert_not_nil mail
70 assert mail.subject.include?("##{issue.id}")
70 assert mail.subject.include?("##{issue.id}")
71 assert mail.subject.include?('New ticket on a given project')
71 assert mail.subject.include?('New ticket on a given project')
72 end
72 end
73
73
74 def test_add_issue_with_default_tracker
74 def test_add_issue_with_default_tracker
75 # This email contains: 'Project: onlinestore'
75 # This email contains: 'Project: onlinestore'
76 issue = submit_email(
76 issue = submit_email(
77 'ticket_on_given_project.eml',
77 'ticket_on_given_project.eml',
78 :issue => {:tracker => 'Support request'}
78 :issue => {:tracker => 'Support request'}
79 )
79 )
80 assert issue.is_a?(Issue)
80 assert issue.is_a?(Issue)
81 assert !issue.new_record?
81 assert !issue.new_record?
82 issue.reload
82 issue.reload
83 assert_equal 'Support request', issue.tracker.name
83 assert_equal 'Support request', issue.tracker.name
84 end
84 end
85
85
86 def test_add_issue_with_status
86 def test_add_issue_with_status
87 # This email contains: 'Project: onlinestore' and 'Status: Resolved'
87 # This email contains: 'Project: onlinestore' and 'Status: Resolved'
88 issue = submit_email('ticket_on_given_project.eml')
88 issue = submit_email('ticket_on_given_project.eml')
89 assert issue.is_a?(Issue)
89 assert issue.is_a?(Issue)
90 assert !issue.new_record?
90 assert !issue.new_record?
91 issue.reload
91 issue.reload
92 assert_equal Project.find(2), issue.project
92 assert_equal Project.find(2), issue.project
93 assert_equal IssueStatus.find_by_name("Resolved"), issue.status
93 assert_equal IssueStatus.find_by_name("Resolved"), issue.status
94 end
94 end
95
95
96 def test_add_issue_with_attributes_override
96 def test_add_issue_with_attributes_override
97 issue = submit_email(
97 issue = submit_email(
98 'ticket_with_attributes.eml',
98 'ticket_with_attributes.eml',
99 :allow_override => 'tracker,category,priority'
99 :allow_override => 'tracker,category,priority'
100 )
100 )
101 assert issue.is_a?(Issue)
101 assert issue.is_a?(Issue)
102 assert !issue.new_record?
102 assert !issue.new_record?
103 issue.reload
103 issue.reload
104 assert_equal 'New ticket on a given project', issue.subject
104 assert_equal 'New ticket on a given project', issue.subject
105 assert_equal User.find_by_login('jsmith'), issue.author
105 assert_equal User.find_by_login('jsmith'), issue.author
106 assert_equal Project.find(2), issue.project
106 assert_equal Project.find(2), issue.project
107 assert_equal 'Feature request', issue.tracker.to_s
107 assert_equal 'Feature request', issue.tracker.to_s
108 assert_equal 'Stock management', issue.category.to_s
108 assert_equal 'Stock management', issue.category.to_s
109 assert_equal 'Urgent', issue.priority.to_s
109 assert_equal 'Urgent', issue.priority.to_s
110 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
110 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
111 end
111 end
112
112
113 def test_add_issue_with_group_assignment
113 def test_add_issue_with_group_assignment
114 with_settings :issue_group_assignment => '1' do
114 with_settings :issue_group_assignment => '1' do
115 issue = submit_email('ticket_on_given_project.eml') do |email|
115 issue = submit_email('ticket_on_given_project.eml') do |email|
116 email.gsub!('Assigned to: John Smith', 'Assigned to: B Team')
116 email.gsub!('Assigned to: John Smith', 'Assigned to: B Team')
117 end
117 end
118 assert issue.is_a?(Issue)
118 assert issue.is_a?(Issue)
119 assert !issue.new_record?
119 assert !issue.new_record?
120 issue.reload
120 issue.reload
121 assert_equal Group.find(11), issue.assigned_to
121 assert_equal Group.find(11), issue.assigned_to
122 end
122 end
123 end
123 end
124
124
125 def test_add_issue_with_partial_attributes_override
125 def test_add_issue_with_partial_attributes_override
126 issue = submit_email(
126 issue = submit_email(
127 'ticket_with_attributes.eml',
127 'ticket_with_attributes.eml',
128 :issue => {:priority => 'High'},
128 :issue => {:priority => 'High'},
129 :allow_override => ['tracker']
129 :allow_override => ['tracker']
130 )
130 )
131 assert issue.is_a?(Issue)
131 assert issue.is_a?(Issue)
132 assert !issue.new_record?
132 assert !issue.new_record?
133 issue.reload
133 issue.reload
134 assert_equal 'New ticket on a given project', issue.subject
134 assert_equal 'New ticket on a given project', issue.subject
135 assert_equal User.find_by_login('jsmith'), issue.author
135 assert_equal User.find_by_login('jsmith'), issue.author
136 assert_equal Project.find(2), issue.project
136 assert_equal Project.find(2), issue.project
137 assert_equal 'Feature request', issue.tracker.to_s
137 assert_equal 'Feature request', issue.tracker.to_s
138 assert_nil issue.category
138 assert_nil issue.category
139 assert_equal 'High', issue.priority.to_s
139 assert_equal 'High', issue.priority.to_s
140 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
140 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
141 end
141 end
142
142
143 def test_add_issue_with_spaces_between_attribute_and_separator
143 def test_add_issue_with_spaces_between_attribute_and_separator
144 issue = submit_email(
144 issue = submit_email(
145 'ticket_with_spaces_between_attribute_and_separator.eml',
145 'ticket_with_spaces_between_attribute_and_separator.eml',
146 :allow_override => 'tracker,category,priority'
146 :allow_override => 'tracker,category,priority'
147 )
147 )
148 assert issue.is_a?(Issue)
148 assert issue.is_a?(Issue)
149 assert !issue.new_record?
149 assert !issue.new_record?
150 issue.reload
150 issue.reload
151 assert_equal 'New ticket on a given project', issue.subject
151 assert_equal 'New ticket on a given project', issue.subject
152 assert_equal User.find_by_login('jsmith'), issue.author
152 assert_equal User.find_by_login('jsmith'), issue.author
153 assert_equal Project.find(2), issue.project
153 assert_equal Project.find(2), issue.project
154 assert_equal 'Feature request', issue.tracker.to_s
154 assert_equal 'Feature request', issue.tracker.to_s
155 assert_equal 'Stock management', issue.category.to_s
155 assert_equal 'Stock management', issue.category.to_s
156 assert_equal 'Urgent', issue.priority.to_s
156 assert_equal 'Urgent', issue.priority.to_s
157 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
157 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
158 end
158 end
159
159
160 def test_add_issue_with_attachment_to_specific_project
160 def test_add_issue_with_attachment_to_specific_project
161 issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
161 issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
162 assert issue.is_a?(Issue)
162 assert issue.is_a?(Issue)
163 assert !issue.new_record?
163 assert !issue.new_record?
164 issue.reload
164 issue.reload
165 assert_equal 'Ticket created by email with attachment', issue.subject
165 assert_equal 'Ticket created by email with attachment', issue.subject
166 assert_equal User.find_by_login('jsmith'), issue.author
166 assert_equal User.find_by_login('jsmith'), issue.author
167 assert_equal Project.find(2), issue.project
167 assert_equal Project.find(2), issue.project
168 assert_equal 'This is a new ticket with attachments', issue.description
168 assert_equal 'This is a new ticket with attachments', issue.description
169 # Attachment properties
169 # Attachment properties
170 assert_equal 1, issue.attachments.size
170 assert_equal 1, issue.attachments.size
171 assert_equal 'Paella.jpg', issue.attachments.first.filename
171 assert_equal 'Paella.jpg', issue.attachments.first.filename
172 assert_equal 'image/jpeg', issue.attachments.first.content_type
172 assert_equal 'image/jpeg', issue.attachments.first.content_type
173 assert_equal 10790, issue.attachments.first.filesize
173 assert_equal 10790, issue.attachments.first.filesize
174 end
174 end
175
175
176 def test_add_issue_with_custom_fields
176 def test_add_issue_with_custom_fields
177 issue = submit_email('ticket_with_custom_fields.eml', :issue => {:project => 'onlinestore'})
177 issue = submit_email('ticket_with_custom_fields.eml', :issue => {:project => 'onlinestore'})
178 assert issue.is_a?(Issue)
178 assert issue.is_a?(Issue)
179 assert !issue.new_record?
179 assert !issue.new_record?
180 issue.reload
180 issue.reload
181 assert_equal 'New ticket with custom field values', issue.subject
181 assert_equal 'New ticket with custom field values', issue.subject
182 assert_equal 'PostgreSQL', issue.custom_field_value(1)
182 assert_equal 'PostgreSQL', issue.custom_field_value(1)
183 assert_equal 'Value for a custom field', issue.custom_field_value(2)
183 assert_equal 'Value for a custom field', issue.custom_field_value(2)
184 assert !issue.description.match(/^searchable field:/i)
184 assert !issue.description.match(/^searchable field:/i)
185 end
185 end
186
186
187 def test_add_issue_with_version_custom_fields
187 def test_add_issue_with_version_custom_fields
188 field = IssueCustomField.create!(:name => 'Affected version', :field_format => 'version', :is_for_all => true, :tracker_ids => [1,2,3])
188 field = IssueCustomField.create!(:name => 'Affected version', :field_format => 'version', :is_for_all => true, :tracker_ids => [1,2,3])
189
189
190 issue = submit_email('ticket_with_custom_fields.eml', :issue => {:project => 'ecookbook'}) do |email|
190 issue = submit_email('ticket_with_custom_fields.eml', :issue => {:project => 'ecookbook'}) do |email|
191 email << "Affected version: 1.0\n"
191 email << "Affected version: 1.0\n"
192 end
192 end
193 assert issue.is_a?(Issue)
193 assert issue.is_a?(Issue)
194 assert !issue.new_record?
194 assert !issue.new_record?
195 issue.reload
195 issue.reload
196 assert_equal '2', issue.custom_field_value(field)
196 assert_equal '2', issue.custom_field_value(field)
197 end
197 end
198
198
199 def test_add_issue_should_match_assignee_on_display_name
199 def test_add_issue_should_match_assignee_on_display_name
200 user = User.generate!(:firstname => 'Foo Bar', :lastname => 'Foo Baz')
200 user = User.generate!(:firstname => 'Foo Bar', :lastname => 'Foo Baz')
201 User.add_to_project(user, Project.find(2))
201 User.add_to_project(user, Project.find(2))
202 issue = submit_email('ticket_on_given_project.eml') do |email|
202 issue = submit_email('ticket_on_given_project.eml') do |email|
203 email.sub!(/^Assigned to.*$/, 'Assigned to: Foo Bar Foo baz')
203 email.sub!(/^Assigned to.*$/, 'Assigned to: Foo Bar Foo baz')
204 end
204 end
205 assert issue.is_a?(Issue)
205 assert issue.is_a?(Issue)
206 assert_equal user, issue.assigned_to
206 assert_equal user, issue.assigned_to
207 end
207 end
208
208
209 def test_add_issue_should_set_default_start_date
209 def test_add_issue_should_set_default_start_date
210 with_settings :default_issue_start_date_to_creation_date => '1' do
210 with_settings :default_issue_start_date_to_creation_date => '1' do
211 issue = submit_email('ticket_with_cc.eml', :issue => {:project => 'ecookbook'})
211 issue = submit_email('ticket_with_cc.eml', :issue => {:project => 'ecookbook'})
212 assert issue.is_a?(Issue)
212 assert issue.is_a?(Issue)
213 assert_equal Date.today, issue.start_date
213 assert_equal Date.today, issue.start_date
214 end
214 end
215 end
215 end
216
216
217 def test_add_issue_with_cc
217 def test_add_issue_with_cc
218 issue = submit_email('ticket_with_cc.eml', :issue => {:project => 'ecookbook'})
218 issue = submit_email('ticket_with_cc.eml', :issue => {:project => 'ecookbook'})
219 assert issue.is_a?(Issue)
219 assert issue.is_a?(Issue)
220 assert !issue.new_record?
220 assert !issue.new_record?
221 issue.reload
221 issue.reload
222 assert issue.watched_by?(User.find_by_mail('dlopper@somenet.foo'))
222 assert issue.watched_by?(User.find_by_mail('dlopper@somenet.foo'))
223 assert_equal 1, issue.watcher_user_ids.size
223 assert_equal 1, issue.watcher_user_ids.size
224 end
224 end
225
225
226 def test_add_issue_by_unknown_user
226 def test_add_issue_by_unknown_user
227 assert_no_difference 'User.count' do
227 assert_no_difference 'User.count' do
228 assert_equal false,
228 assert_equal false,
229 submit_email(
229 submit_email(
230 'ticket_by_unknown_user.eml',
230 'ticket_by_unknown_user.eml',
231 :issue => {:project => 'ecookbook'}
231 :issue => {:project => 'ecookbook'}
232 )
232 )
233 end
233 end
234 end
234 end
235
235
236 def test_add_issue_by_anonymous_user
236 def test_add_issue_by_anonymous_user
237 Role.anonymous.add_permission!(:add_issues)
237 Role.anonymous.add_permission!(:add_issues)
238 assert_no_difference 'User.count' do
238 assert_no_difference 'User.count' do
239 issue = submit_email(
239 issue = submit_email(
240 'ticket_by_unknown_user.eml',
240 'ticket_by_unknown_user.eml',
241 :issue => {:project => 'ecookbook'},
241 :issue => {:project => 'ecookbook'},
242 :unknown_user => 'accept'
242 :unknown_user => 'accept'
243 )
243 )
244 assert issue.is_a?(Issue)
244 assert issue.is_a?(Issue)
245 assert issue.author.anonymous?
245 assert issue.author.anonymous?
246 end
246 end
247 end
247 end
248
248
249 def test_add_issue_by_anonymous_user_with_no_from_address
249 def test_add_issue_by_anonymous_user_with_no_from_address
250 Role.anonymous.add_permission!(:add_issues)
250 Role.anonymous.add_permission!(:add_issues)
251 assert_no_difference 'User.count' do
251 assert_no_difference 'User.count' do
252 issue = submit_email(
252 issue = submit_email(
253 'ticket_by_empty_user.eml',
253 'ticket_by_empty_user.eml',
254 :issue => {:project => 'ecookbook'},
254 :issue => {:project => 'ecookbook'},
255 :unknown_user => 'accept'
255 :unknown_user => 'accept'
256 )
256 )
257 assert issue.is_a?(Issue)
257 assert issue.is_a?(Issue)
258 assert issue.author.anonymous?
258 assert issue.author.anonymous?
259 end
259 end
260 end
260 end
261
261
262 def test_add_issue_by_anonymous_user_on_private_project
262 def test_add_issue_by_anonymous_user_on_private_project
263 Role.anonymous.add_permission!(:add_issues)
263 Role.anonymous.add_permission!(:add_issues)
264 assert_no_difference 'User.count' do
264 assert_no_difference 'User.count' do
265 assert_no_difference 'Issue.count' do
265 assert_no_difference 'Issue.count' do
266 assert_equal false,
266 assert_equal false,
267 submit_email(
267 submit_email(
268 'ticket_by_unknown_user.eml',
268 'ticket_by_unknown_user.eml',
269 :issue => {:project => 'onlinestore'},
269 :issue => {:project => 'onlinestore'},
270 :unknown_user => 'accept'
270 :unknown_user => 'accept'
271 )
271 )
272 end
272 end
273 end
273 end
274 end
274 end
275
275
276 def test_add_issue_by_anonymous_user_on_private_project_without_permission_check
276 def test_add_issue_by_anonymous_user_on_private_project_without_permission_check
277 lft1 = new_issue_lft
277 lft1 = new_issue_lft
278 assert_no_difference 'User.count' do
278 assert_no_difference 'User.count' do
279 assert_difference 'Issue.count' do
279 assert_difference 'Issue.count' do
280 issue = submit_email(
280 issue = submit_email(
281 'ticket_by_unknown_user.eml',
281 'ticket_by_unknown_user.eml',
282 :issue => {:project => 'onlinestore'},
282 :issue => {:project => 'onlinestore'},
283 :no_permission_check => '1',
283 :no_permission_check => '1',
284 :unknown_user => 'accept'
284 :unknown_user => 'accept'
285 )
285 )
286 assert issue.is_a?(Issue)
286 assert issue.is_a?(Issue)
287 assert issue.author.anonymous?
287 assert issue.author.anonymous?
288 assert !issue.project.is_public?
288 assert !issue.project.is_public?
289 assert_equal [issue.id, lft1, lft1 + 1], [issue.root_id, issue.lft, issue.rgt]
289 assert_equal [issue.id, lft1, lft1 + 1], [issue.root_id, issue.lft, issue.rgt]
290 end
290 end
291 end
291 end
292 end
292 end
293
293
294 def test_add_issue_by_created_user
294 def test_add_issue_by_created_user
295 Setting.default_language = 'en'
295 Setting.default_language = 'en'
296 assert_difference 'User.count' do
296 assert_difference 'User.count' do
297 issue = submit_email(
297 issue = submit_email(
298 'ticket_by_unknown_user.eml',
298 'ticket_by_unknown_user.eml',
299 :issue => {:project => 'ecookbook'},
299 :issue => {:project => 'ecookbook'},
300 :unknown_user => 'create'
300 :unknown_user => 'create'
301 )
301 )
302 assert issue.is_a?(Issue)
302 assert issue.is_a?(Issue)
303 assert issue.author.active?
303 assert issue.author.active?
304 assert_equal 'john.doe@somenet.foo', issue.author.mail
304 assert_equal 'john.doe@somenet.foo', issue.author.mail
305 assert_equal 'John', issue.author.firstname
305 assert_equal 'John', issue.author.firstname
306 assert_equal 'Doe', issue.author.lastname
306 assert_equal 'Doe', issue.author.lastname
307
307
308 # account information
308 # account information
309 email = ActionMailer::Base.deliveries.first
309 email = ActionMailer::Base.deliveries.first
310 assert_not_nil email
310 assert_not_nil email
311 assert email.subject.include?('account activation')
311 assert email.subject.include?('account activation')
312 login = mail_body(email).match(/\* Login: (.*)$/)[1].strip
312 login = mail_body(email).match(/\* Login: (.*)$/)[1].strip
313 password = mail_body(email).match(/\* Password: (.*)$/)[1].strip
313 password = mail_body(email).match(/\* Password: (.*)$/)[1].strip
314 assert_equal issue.author, User.try_to_login(login, password)
314 assert_equal issue.author, User.try_to_login(login, password)
315 end
315 end
316 end
316 end
317
317
318 def test_created_user_should_be_added_to_groups
318 def test_created_user_should_be_added_to_groups
319 group1 = Group.generate!
319 group1 = Group.generate!
320 group2 = Group.generate!
320 group2 = Group.generate!
321
321
322 assert_difference 'User.count' do
322 assert_difference 'User.count' do
323 submit_email(
323 submit_email(
324 'ticket_by_unknown_user.eml',
324 'ticket_by_unknown_user.eml',
325 :issue => {:project => 'ecookbook'},
325 :issue => {:project => 'ecookbook'},
326 :unknown_user => 'create',
326 :unknown_user => 'create',
327 :default_group => "#{group1.name},#{group2.name}"
327 :default_group => "#{group1.name},#{group2.name}"
328 )
328 )
329 end
329 end
330 user = User.order('id DESC').first
330 user = User.order('id DESC').first
331 assert_equal [group1, group2].sort, user.groups.sort
331 assert_equal [group1, group2].sort, user.groups.sort
332 end
332 end
333
333
334 def test_created_user_should_not_receive_account_information_with_no_account_info_option
334 def test_created_user_should_not_receive_account_information_with_no_account_info_option
335 assert_difference 'User.count' do
335 assert_difference 'User.count' do
336 submit_email(
336 submit_email(
337 'ticket_by_unknown_user.eml',
337 'ticket_by_unknown_user.eml',
338 :issue => {:project => 'ecookbook'},
338 :issue => {:project => 'ecookbook'},
339 :unknown_user => 'create',
339 :unknown_user => 'create',
340 :no_account_notice => '1'
340 :no_account_notice => '1'
341 )
341 )
342 end
342 end
343
343
344 # only 1 email for the new issue notification
344 # only 1 email for the new issue notification
345 assert_equal 1, ActionMailer::Base.deliveries.size
345 assert_equal 1, ActionMailer::Base.deliveries.size
346 email = ActionMailer::Base.deliveries.first
346 email = ActionMailer::Base.deliveries.first
347 assert_include 'Ticket by unknown user', email.subject
347 assert_include 'Ticket by unknown user', email.subject
348 end
348 end
349
349
350 def test_created_user_should_have_mail_notification_to_none_with_no_notification_option
350 def test_created_user_should_have_mail_notification_to_none_with_no_notification_option
351 assert_difference 'User.count' do
351 assert_difference 'User.count' do
352 submit_email(
352 submit_email(
353 'ticket_by_unknown_user.eml',
353 'ticket_by_unknown_user.eml',
354 :issue => {:project => 'ecookbook'},
354 :issue => {:project => 'ecookbook'},
355 :unknown_user => 'create',
355 :unknown_user => 'create',
356 :no_notification => '1'
356 :no_notification => '1'
357 )
357 )
358 end
358 end
359 user = User.order('id DESC').first
359 user = User.order('id DESC').first
360 assert_equal 'none', user.mail_notification
360 assert_equal 'none', user.mail_notification
361 end
361 end
362
362
363 def test_add_issue_without_from_header
363 def test_add_issue_without_from_header
364 Role.anonymous.add_permission!(:add_issues)
364 Role.anonymous.add_permission!(:add_issues)
365 assert_equal false, submit_email('ticket_without_from_header.eml')
365 assert_equal false, submit_email('ticket_without_from_header.eml')
366 end
366 end
367
367
368 def test_add_issue_with_invalid_attributes
368 def test_add_issue_with_invalid_attributes
369 with_settings :default_issue_start_date_to_creation_date => '0' do
369 with_settings :default_issue_start_date_to_creation_date => '0' do
370 issue = submit_email(
370 issue = submit_email(
371 'ticket_with_invalid_attributes.eml',
371 'ticket_with_invalid_attributes.eml',
372 :allow_override => 'tracker,category,priority'
372 :allow_override => 'tracker,category,priority'
373 )
373 )
374 assert issue.is_a?(Issue)
374 assert issue.is_a?(Issue)
375 assert !issue.new_record?
375 assert !issue.new_record?
376 issue.reload
376 issue.reload
377 assert_nil issue.assigned_to
377 assert_nil issue.assigned_to
378 assert_nil issue.start_date
378 assert_nil issue.start_date
379 assert_nil issue.due_date
379 assert_nil issue.due_date
380 assert_equal 0, issue.done_ratio
380 assert_equal 0, issue.done_ratio
381 assert_equal 'Normal', issue.priority.to_s
381 assert_equal 'Normal', issue.priority.to_s
382 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
382 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
383 end
383 end
384 end
384 end
385
385
386 def test_add_issue_with_invalid_project_should_be_assigned_to_default_project
386 def test_add_issue_with_invalid_project_should_be_assigned_to_default_project
387 issue = submit_email('ticket_on_given_project.eml', :issue => {:project => 'ecookbook'}, :allow_override => 'project') do |email|
387 issue = submit_email('ticket_on_given_project.eml', :issue => {:project => 'ecookbook'}, :allow_override => 'project') do |email|
388 email.gsub!(/^Project:.+$/, 'Project: invalid')
388 email.gsub!(/^Project:.+$/, 'Project: invalid')
389 end
389 end
390 assert issue.is_a?(Issue)
390 assert issue.is_a?(Issue)
391 assert !issue.new_record?
391 assert !issue.new_record?
392 assert_equal 'ecookbook', issue.project.identifier
392 assert_equal 'ecookbook', issue.project.identifier
393 end
393 end
394
394
395 def test_add_issue_with_localized_attributes
395 def test_add_issue_with_localized_attributes
396 User.find_by_mail('jsmith@somenet.foo').update_attribute 'language', 'fr'
396 User.find_by_mail('jsmith@somenet.foo').update_attribute 'language', 'fr'
397 issue = submit_email(
397 issue = submit_email(
398 'ticket_with_localized_attributes.eml',
398 'ticket_with_localized_attributes.eml',
399 :allow_override => 'tracker,category,priority'
399 :allow_override => 'tracker,category,priority'
400 )
400 )
401 assert issue.is_a?(Issue)
401 assert issue.is_a?(Issue)
402 assert !issue.new_record?
402 assert !issue.new_record?
403 issue.reload
403 issue.reload
404 assert_equal 'New ticket on a given project', issue.subject
404 assert_equal 'New ticket on a given project', issue.subject
405 assert_equal User.find_by_login('jsmith'), issue.author
405 assert_equal User.find_by_login('jsmith'), issue.author
406 assert_equal Project.find(2), issue.project
406 assert_equal Project.find(2), issue.project
407 assert_equal 'Feature request', issue.tracker.to_s
407 assert_equal 'Feature request', issue.tracker.to_s
408 assert_equal 'Stock management', issue.category.to_s
408 assert_equal 'Stock management', issue.category.to_s
409 assert_equal 'Urgent', issue.priority.to_s
409 assert_equal 'Urgent', issue.priority.to_s
410 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
410 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
411 end
411 end
412
412
413 def test_add_issue_with_japanese_keywords
413 def test_add_issue_with_japanese_keywords
414 ja_dev = "\xe9\x96\x8b\xe7\x99\xba".force_encoding('UTF-8')
414 ja_dev = "\xe9\x96\x8b\xe7\x99\xba".force_encoding('UTF-8')
415 tracker = Tracker.generate!(:name => ja_dev)
415 tracker = Tracker.generate!(:name => ja_dev)
416 Project.find(1).trackers << tracker
416 Project.find(1).trackers << tracker
417 issue = submit_email(
417 issue = submit_email(
418 'japanese_keywords_iso_2022_jp.eml',
418 'japanese_keywords_iso_2022_jp.eml',
419 :issue => {:project => 'ecookbook'},
419 :issue => {:project => 'ecookbook'},
420 :allow_override => 'tracker'
420 :allow_override => 'tracker'
421 )
421 )
422 assert_kind_of Issue, issue
422 assert_kind_of Issue, issue
423 assert_equal tracker, issue.tracker
423 assert_equal tracker, issue.tracker
424 end
424 end
425
425
426 def test_add_issue_from_apple_mail
426 def test_add_issue_from_apple_mail
427 issue = submit_email(
427 issue = submit_email(
428 'apple_mail_with_attachment.eml',
428 'apple_mail_with_attachment.eml',
429 :issue => {:project => 'ecookbook'}
429 :issue => {:project => 'ecookbook'}
430 )
430 )
431 assert_kind_of Issue, issue
431 assert_kind_of Issue, issue
432 assert_equal 1, issue.attachments.size
432 assert_equal 1, issue.attachments.size
433
433
434 attachment = issue.attachments.first
434 attachment = issue.attachments.first
435 assert_equal 'paella.jpg', attachment.filename
435 assert_equal 'paella.jpg', attachment.filename
436 assert_equal 10790, attachment.filesize
436 assert_equal 10790, attachment.filesize
437 assert File.exist?(attachment.diskfile)
437 assert File.exist?(attachment.diskfile)
438 assert_equal 10790, File.size(attachment.diskfile)
438 assert_equal 10790, File.size(attachment.diskfile)
439 assert_equal 'caaf384198bcbc9563ab5c058acd73cd', attachment.digest
439 assert_equal 'caaf384198bcbc9563ab5c058acd73cd', attachment.digest
440 end
440 end
441
441
442 def test_thunderbird_with_attachment_ja
442 def test_thunderbird_with_attachment_ja
443 issue = submit_email(
443 issue = submit_email(
444 'thunderbird_with_attachment_ja.eml',
444 'thunderbird_with_attachment_ja.eml',
445 :issue => {:project => 'ecookbook'}
445 :issue => {:project => 'ecookbook'}
446 )
446 )
447 assert_kind_of Issue, issue
447 assert_kind_of Issue, issue
448 assert_equal 1, issue.attachments.size
448 assert_equal 1, issue.attachments.size
449 ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88.txt".force_encoding('UTF-8')
449 ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88.txt".force_encoding('UTF-8')
450 attachment = issue.attachments.first
450 attachment = issue.attachments.first
451 assert_equal ja, attachment.filename
451 assert_equal ja, attachment.filename
452 assert_equal 5, attachment.filesize
452 assert_equal 5, attachment.filesize
453 assert File.exist?(attachment.diskfile)
453 assert File.exist?(attachment.diskfile)
454 assert_equal 5, File.size(attachment.diskfile)
454 assert_equal 5, File.size(attachment.diskfile)
455 assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
455 assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
456 end
456 end
457
457
458 def test_gmail_with_attachment_ja
458 def test_gmail_with_attachment_ja
459 issue = submit_email(
459 issue = submit_email(
460 'gmail_with_attachment_ja.eml',
460 'gmail_with_attachment_ja.eml',
461 :issue => {:project => 'ecookbook'}
461 :issue => {:project => 'ecookbook'}
462 )
462 )
463 assert_kind_of Issue, issue
463 assert_kind_of Issue, issue
464 assert_equal 1, issue.attachments.size
464 assert_equal 1, issue.attachments.size
465 ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88.txt".force_encoding('UTF-8')
465 ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88.txt".force_encoding('UTF-8')
466 attachment = issue.attachments.first
466 attachment = issue.attachments.first
467 assert_equal ja, attachment.filename
467 assert_equal ja, attachment.filename
468 assert_equal 5, attachment.filesize
468 assert_equal 5, attachment.filesize
469 assert File.exist?(attachment.diskfile)
469 assert File.exist?(attachment.diskfile)
470 assert_equal 5, File.size(attachment.diskfile)
470 assert_equal 5, File.size(attachment.diskfile)
471 assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
471 assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
472 end
472 end
473
473
474 def test_thunderbird_with_attachment_latin1
474 def test_thunderbird_with_attachment_latin1
475 issue = submit_email(
475 issue = submit_email(
476 'thunderbird_with_attachment_iso-8859-1.eml',
476 'thunderbird_with_attachment_iso-8859-1.eml',
477 :issue => {:project => 'ecookbook'}
477 :issue => {:project => 'ecookbook'}
478 )
478 )
479 assert_kind_of Issue, issue
479 assert_kind_of Issue, issue
480 assert_equal 1, issue.attachments.size
480 assert_equal 1, issue.attachments.size
481 u = "".force_encoding('UTF-8')
481 u = "".force_encoding('UTF-8')
482 u1 = "\xc3\x84\xc3\xa4\xc3\x96\xc3\xb6\xc3\x9c\xc3\xbc".force_encoding('UTF-8')
482 u1 = "\xc3\x84\xc3\xa4\xc3\x96\xc3\xb6\xc3\x9c\xc3\xbc".force_encoding('UTF-8')
483 11.times { u << u1 }
483 11.times { u << u1 }
484 attachment = issue.attachments.first
484 attachment = issue.attachments.first
485 assert_equal "#{u}.png", attachment.filename
485 assert_equal "#{u}.png", attachment.filename
486 assert_equal 130, attachment.filesize
486 assert_equal 130, attachment.filesize
487 assert File.exist?(attachment.diskfile)
487 assert File.exist?(attachment.diskfile)
488 assert_equal 130, File.size(attachment.diskfile)
488 assert_equal 130, File.size(attachment.diskfile)
489 assert_equal '4d80e667ac37dddfe05502530f152abb', attachment.digest
489 assert_equal '4d80e667ac37dddfe05502530f152abb', attachment.digest
490 end
490 end
491
491
492 def test_gmail_with_attachment_latin1
492 def test_gmail_with_attachment_latin1
493 issue = submit_email(
493 issue = submit_email(
494 'gmail_with_attachment_iso-8859-1.eml',
494 'gmail_with_attachment_iso-8859-1.eml',
495 :issue => {:project => 'ecookbook'}
495 :issue => {:project => 'ecookbook'}
496 )
496 )
497 assert_kind_of Issue, issue
497 assert_kind_of Issue, issue
498 assert_equal 1, issue.attachments.size
498 assert_equal 1, issue.attachments.size
499 u = "".force_encoding('UTF-8')
499 u = "".force_encoding('UTF-8')
500 u1 = "\xc3\x84\xc3\xa4\xc3\x96\xc3\xb6\xc3\x9c\xc3\xbc".force_encoding('UTF-8')
500 u1 = "\xc3\x84\xc3\xa4\xc3\x96\xc3\xb6\xc3\x9c\xc3\xbc".force_encoding('UTF-8')
501 11.times { u << u1 }
501 11.times { u << u1 }
502 attachment = issue.attachments.first
502 attachment = issue.attachments.first
503 assert_equal "#{u}.txt", attachment.filename
503 assert_equal "#{u}.txt", attachment.filename
504 assert_equal 5, attachment.filesize
504 assert_equal 5, attachment.filesize
505 assert File.exist?(attachment.diskfile)
505 assert File.exist?(attachment.diskfile)
506 assert_equal 5, File.size(attachment.diskfile)
506 assert_equal 5, File.size(attachment.diskfile)
507 assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
507 assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
508 end
508 end
509
509
510 def test_multiple_inline_text_parts_should_be_appended_to_issue_description
510 def test_multiple_inline_text_parts_should_be_appended_to_issue_description
511 issue = submit_email('multiple_text_parts.eml', :issue => {:project => 'ecookbook'})
511 issue = submit_email('multiple_text_parts.eml', :issue => {:project => 'ecookbook'})
512 assert_include 'first', issue.description
512 assert_include 'first', issue.description
513 assert_include 'second', issue.description
513 assert_include 'second', issue.description
514 assert_include 'third', issue.description
514 assert_include 'third', issue.description
515 end
515 end
516
516
517 def test_attachment_text_part_should_be_added_as_issue_attachment
517 def test_attachment_text_part_should_be_added_as_issue_attachment
518 issue = submit_email('multiple_text_parts.eml', :issue => {:project => 'ecookbook'})
518 issue = submit_email('multiple_text_parts.eml', :issue => {:project => 'ecookbook'})
519 assert_not_include 'Plain text attachment', issue.description
519 assert_not_include 'Plain text attachment', issue.description
520 attachment = issue.attachments.detect {|a| a.filename == 'textfile.txt'}
520 attachment = issue.attachments.detect {|a| a.filename == 'textfile.txt'}
521 assert_not_nil attachment
521 assert_not_nil attachment
522 assert_include 'Plain text attachment', File.read(attachment.diskfile)
522 assert_include 'Plain text attachment', File.read(attachment.diskfile)
523 end
523 end
524
524
525 def test_add_issue_with_iso_8859_1_subject
525 def test_add_issue_with_iso_8859_1_subject
526 issue = submit_email(
526 issue = submit_email(
527 'subject_as_iso-8859-1.eml',
527 'subject_as_iso-8859-1.eml',
528 :issue => {:project => 'ecookbook'}
528 :issue => {:project => 'ecookbook'}
529 )
529 )
530 str = "Testmail from Webmail: \xc3\xa4 \xc3\xb6 \xc3\xbc...".force_encoding('UTF-8')
530 str = "Testmail from Webmail: \xc3\xa4 \xc3\xb6 \xc3\xbc...".force_encoding('UTF-8')
531 assert_kind_of Issue, issue
531 assert_kind_of Issue, issue
532 assert_equal str, issue.subject
532 assert_equal str, issue.subject
533 end
533 end
534
534
535 def test_quoted_printable_utf8
535 def test_quoted_printable_utf8
536 issue = submit_email(
536 issue = submit_email(
537 'quoted_printable_utf8.eml',
537 'quoted_printable_utf8.eml',
538 :issue => {:project => 'ecookbook'}
538 :issue => {:project => 'ecookbook'}
539 )
539 )
540 assert_kind_of Issue, issue
540 assert_kind_of Issue, issue
541 str = "Freundliche Gr\xc3\xbcsse".force_encoding('UTF-8')
541 str = "Freundliche Gr\xc3\xbcsse".force_encoding('UTF-8')
542 assert_equal str, issue.description
542 assert_equal str, issue.description
543 end
543 end
544
544
545 def test_gmail_iso8859_2
545 def test_gmail_iso8859_2
546 issue = submit_email(
546 issue = submit_email(
547 'gmail-iso8859-2.eml',
547 'gmail-iso8859-2.eml',
548 :issue => {:project => 'ecookbook'}
548 :issue => {:project => 'ecookbook'}
549 )
549 )
550 assert_kind_of Issue, issue
550 assert_kind_of Issue, issue
551 str = "Na \xc5\xa1triku se su\xc5\xa1i \xc5\xa1osi\xc4\x87.".force_encoding('UTF-8')
551 str = "Na \xc5\xa1triku se su\xc5\xa1i \xc5\xa1osi\xc4\x87.".force_encoding('UTF-8')
552 assert issue.description.include?(str)
552 assert issue.description.include?(str)
553 end
553 end
554
554
555 def test_add_issue_with_japanese_subject
555 def test_add_issue_with_japanese_subject
556 issue = submit_email(
556 issue = submit_email(
557 'subject_japanese_1.eml',
557 'subject_japanese_1.eml',
558 :issue => {:project => 'ecookbook'}
558 :issue => {:project => 'ecookbook'}
559 )
559 )
560 assert_kind_of Issue, issue
560 assert_kind_of Issue, issue
561 ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88".force_encoding('UTF-8')
561 ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88".force_encoding('UTF-8')
562 assert_equal ja, issue.subject
562 assert_equal ja, issue.subject
563 end
563 end
564
564
565 def test_add_issue_with_korean_body
565 def test_add_issue_with_korean_body
566 # Make sure mail bodies with a charset unknown to Ruby
566 # Make sure mail bodies with a charset unknown to Ruby
567 # but known to the Mail gem 2.5.4 are handled correctly
567 # but known to the Mail gem 2.5.4 are handled correctly
568 kr = "\xEA\xB3\xA0\xEB\xA7\x99\xEC\x8A\xB5\xEB\x8B\x88\xEB\x8B\xA4.".force_encoding('UTF-8')
568 kr = "\xEA\xB3\xA0\xEB\xA7\x99\xEC\x8A\xB5\xEB\x8B\x88\xEB\x8B\xA4.".force_encoding('UTF-8')
569 issue = submit_email(
569 issue = submit_email(
570 'body_ks_c_5601-1987.eml',
570 'body_ks_c_5601-1987.eml',
571 :issue => {:project => 'ecookbook'}
571 :issue => {:project => 'ecookbook'}
572 )
572 )
573 assert_kind_of Issue, issue
573 assert_kind_of Issue, issue
574 assert_equal kr, issue.description
574 assert_equal kr, issue.description
575 end
575 end
576
576
577 def test_add_issue_with_no_subject_header
577 def test_add_issue_with_no_subject_header
578 issue = submit_email(
578 issue = submit_email(
579 'no_subject_header.eml',
579 'no_subject_header.eml',
580 :issue => {:project => 'ecookbook'}
580 :issue => {:project => 'ecookbook'}
581 )
581 )
582 assert_kind_of Issue, issue
582 assert_kind_of Issue, issue
583 assert_equal '(no subject)', issue.subject
583 assert_equal '(no subject)', issue.subject
584 end
584 end
585
585
586 def test_add_issue_with_mixed_japanese_subject
586 def test_add_issue_with_mixed_japanese_subject
587 issue = submit_email(
587 issue = submit_email(
588 'subject_japanese_2.eml',
588 'subject_japanese_2.eml',
589 :issue => {:project => 'ecookbook'}
589 :issue => {:project => 'ecookbook'}
590 )
590 )
591 assert_kind_of Issue, issue
591 assert_kind_of Issue, issue
592 ja = "Re: \xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88".force_encoding('UTF-8')
592 ja = "Re: \xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88".force_encoding('UTF-8')
593 assert_equal ja, issue.subject
593 assert_equal ja, issue.subject
594 end
594 end
595
595
596 def test_should_ignore_emails_from_locked_users
596 def test_should_ignore_emails_from_locked_users
597 User.find(2).lock!
597 User.find(2).lock!
598
598
599 MailHandler.any_instance.expects(:dispatch).never
599 MailHandler.any_instance.expects(:dispatch).never
600 assert_no_difference 'Issue.count' do
600 assert_no_difference 'Issue.count' do
601 assert_equal false, submit_email('ticket_on_given_project.eml')
601 assert_equal false, submit_email('ticket_on_given_project.eml')
602 end
602 end
603 end
603 end
604
604
605 def test_should_ignore_emails_from_emission_address
605 def test_should_ignore_emails_from_emission_address
606 Role.anonymous.add_permission!(:add_issues)
606 Role.anonymous.add_permission!(:add_issues)
607 assert_no_difference 'User.count' do
607 assert_no_difference 'User.count' do
608 assert_equal false,
608 assert_equal false,
609 submit_email(
609 submit_email(
610 'ticket_from_emission_address.eml',
610 'ticket_from_emission_address.eml',
611 :issue => {:project => 'ecookbook'},
611 :issue => {:project => 'ecookbook'},
612 :unknown_user => 'create'
612 :unknown_user => 'create'
613 )
613 )
614 end
614 end
615 end
615 end
616
616
617 def test_should_ignore_auto_replied_emails
617 def test_should_ignore_auto_replied_emails
618 MailHandler.any_instance.expects(:dispatch).never
618 MailHandler.any_instance.expects(:dispatch).never
619 [
619 [
620 "X-Auto-Response-Suppress: OOF",
620 "X-Auto-Response-Suppress: OOF",
621 "Auto-Submitted: auto-replied",
621 "Auto-Submitted: auto-replied",
622 "Auto-Submitted: Auto-Replied",
622 "Auto-Submitted: Auto-Replied",
623 "Auto-Submitted: auto-generated"
623 "Auto-Submitted: auto-generated"
624 ].each do |header|
624 ].each do |header|
625 raw = IO.read(File.join(FIXTURES_PATH, 'ticket_on_given_project.eml'))
625 raw = IO.read(File.join(FIXTURES_PATH, 'ticket_on_given_project.eml'))
626 raw = header + "\n" + raw
626 raw = header + "\n" + raw
627
627
628 assert_no_difference 'Issue.count' do
628 assert_no_difference 'Issue.count' do
629 assert_equal false, MailHandler.receive(raw), "email with #{header} header was not ignored"
629 assert_equal false, MailHandler.receive(raw), "email with #{header} header was not ignored"
630 end
630 end
631 end
631 end
632 end
632 end
633
633
634 test "should not ignore Auto-Submitted headers not defined in RFC3834" do
634 test "should not ignore Auto-Submitted headers not defined in RFC3834" do
635 [
635 [
636 "Auto-Submitted: auto-forwarded"
636 "Auto-Submitted: auto-forwarded"
637 ].each do |header|
637 ].each do |header|
638 raw = IO.read(File.join(FIXTURES_PATH, 'ticket_on_given_project.eml'))
638 raw = IO.read(File.join(FIXTURES_PATH, 'ticket_on_given_project.eml'))
639 raw = header + "\n" + raw
639 raw = header + "\n" + raw
640
640
641 assert_difference 'Issue.count', 1 do
641 assert_difference 'Issue.count', 1 do
642 assert_not_nil MailHandler.receive(raw), "email with #{header} header was ignored"
642 assert_not_nil MailHandler.receive(raw), "email with #{header} header was ignored"
643 end
643 end
644 end
644 end
645 end
645 end
646
646
647 def test_add_issue_should_send_email_notification
647 def test_add_issue_should_send_email_notification
648 Setting.notified_events = ['issue_added']
648 Setting.notified_events = ['issue_added']
649 ActionMailer::Base.deliveries.clear
649 ActionMailer::Base.deliveries.clear
650 # This email contains: 'Project: onlinestore'
650 # This email contains: 'Project: onlinestore'
651 issue = submit_email('ticket_on_given_project.eml')
651 issue = submit_email('ticket_on_given_project.eml')
652 assert issue.is_a?(Issue)
652 assert issue.is_a?(Issue)
653 assert_equal 1, ActionMailer::Base.deliveries.size
653 assert_equal 1, ActionMailer::Base.deliveries.size
654 end
654 end
655
655
656 def test_update_issue
656 def test_update_issue
657 journal = submit_email('ticket_reply.eml')
657 journal = submit_email('ticket_reply.eml')
658 assert journal.is_a?(Journal)
658 assert journal.is_a?(Journal)
659 assert_equal User.find_by_login('jsmith'), journal.user
659 assert_equal User.find_by_login('jsmith'), journal.user
660 assert_equal Issue.find(2), journal.journalized
660 assert_equal Issue.find(2), journal.journalized
661 assert_match /This is reply/, journal.notes
661 assert_match /This is reply/, journal.notes
662 assert_equal false, journal.private_notes
662 assert_equal false, journal.private_notes
663 assert_equal 'Feature request', journal.issue.tracker.name
663 assert_equal 'Feature request', journal.issue.tracker.name
664 end
664 end
665
665
666 def test_update_issue_should_accept_issue_id_after_space_inside_brackets
666 def test_update_issue_should_accept_issue_id_after_space_inside_brackets
667 journal = submit_email('ticket_reply_with_status.eml') do |email|
667 journal = submit_email('ticket_reply_with_status.eml') do |email|
668 assert email.sub!(/^Subject:.*$/, "Subject: Re: [Feature request #2] Add ingredients categories")
668 assert email.sub!(/^Subject:.*$/, "Subject: Re: [Feature request #2] Add ingredients categories")
669 end
669 end
670 assert journal.is_a?(Journal)
670 assert journal.is_a?(Journal)
671 assert_equal Issue.find(2), journal.journalized
671 assert_equal Issue.find(2), journal.journalized
672 end
672 end
673
673
674 def test_update_issue_should_accept_issue_id_inside_brackets
674 def test_update_issue_should_accept_issue_id_inside_brackets
675 journal = submit_email('ticket_reply_with_status.eml') do |email|
675 journal = submit_email('ticket_reply_with_status.eml') do |email|
676 assert email.sub!(/^Subject:.*$/, "Subject: Re: [#2] Add ingredients categories")
676 assert email.sub!(/^Subject:.*$/, "Subject: Re: [#2] Add ingredients categories")
677 end
677 end
678 assert journal.is_a?(Journal)
678 assert journal.is_a?(Journal)
679 assert_equal Issue.find(2), journal.journalized
679 assert_equal Issue.find(2), journal.journalized
680 end
680 end
681
681
682 def test_update_issue_should_ignore_bogus_issue_ids_in_subject
682 def test_update_issue_should_ignore_bogus_issue_ids_in_subject
683 journal = submit_email('ticket_reply_with_status.eml') do |email|
683 journal = submit_email('ticket_reply_with_status.eml') do |email|
684 assert email.sub!(/^Subject:.*$/, "Subject: Re: [12345#1][bogus#1][Feature request #2] Add ingredients categories")
684 assert email.sub!(/^Subject:.*$/, "Subject: Re: [12345#1][bogus#1][Feature request #2] Add ingredients categories")
685 end
685 end
686 assert journal.is_a?(Journal)
686 assert journal.is_a?(Journal)
687 assert_equal Issue.find(2), journal.journalized
687 assert_equal Issue.find(2), journal.journalized
688 end
688 end
689
689
690 def test_update_issue_with_attribute_changes
690 def test_update_issue_with_attribute_changes
691 # This email contains: 'Status: Resolved'
691 # This email contains: 'Status: Resolved'
692 journal = submit_email('ticket_reply_with_status.eml')
692 journal = submit_email('ticket_reply_with_status.eml')
693 assert journal.is_a?(Journal)
693 assert journal.is_a?(Journal)
694 issue = Issue.find(journal.issue.id)
694 issue = Issue.find(journal.issue.id)
695 assert_equal User.find_by_login('jsmith'), journal.user
695 assert_equal User.find_by_login('jsmith'), journal.user
696 assert_equal Issue.find(2), journal.journalized
696 assert_equal Issue.find(2), journal.journalized
697 assert_match /This is reply/, journal.notes
697 assert_match /This is reply/, journal.notes
698 assert_equal 'Feature request', journal.issue.tracker.name
698 assert_equal 'Feature request', journal.issue.tracker.name
699 assert_equal IssueStatus.find_by_name("Resolved"), issue.status
699 assert_equal IssueStatus.find_by_name("Resolved"), issue.status
700 assert_equal '2010-01-01', issue.start_date.to_s
700 assert_equal '2010-01-01', issue.start_date.to_s
701 assert_equal '2010-12-31', issue.due_date.to_s
701 assert_equal '2010-12-31', issue.due_date.to_s
702 assert_equal User.find_by_login('jsmith'), issue.assigned_to
702 assert_equal User.find_by_login('jsmith'), issue.assigned_to
703 assert_equal "52.6", issue.custom_value_for(CustomField.find_by_name('Float field')).value
703 assert_equal "52.6", issue.custom_value_for(CustomField.find_by_name('Float field')).value
704 # keywords should be removed from the email body
704 # keywords should be removed from the email body
705 assert !journal.notes.match(/^Status:/i)
705 assert !journal.notes.match(/^Status:/i)
706 assert !journal.notes.match(/^Start Date:/i)
706 assert !journal.notes.match(/^Start Date:/i)
707 end
707 end
708
708
709 def test_update_issue_with_attachment
709 def test_update_issue_with_attachment
710 assert_difference 'Journal.count' do
710 assert_difference 'Journal.count' do
711 assert_difference 'JournalDetail.count' do
711 assert_difference 'JournalDetail.count' do
712 assert_difference 'Attachment.count' do
712 assert_difference 'Attachment.count' do
713 assert_no_difference 'Issue.count' do
713 assert_no_difference 'Issue.count' do
714 journal = submit_email('ticket_with_attachment.eml') do |raw|
714 journal = submit_email('ticket_with_attachment.eml') do |raw|
715 raw.gsub! /^Subject: .*$/, 'Subject: Re: [Cookbook - Feature #2] (New) Add ingredients categories'
715 raw.gsub! /^Subject: .*$/, 'Subject: Re: [Cookbook - Feature #2] (New) Add ingredients categories'
716 end
716 end
717 end
717 end
718 end
718 end
719 end
719 end
720 end
720 end
721 journal = Journal.order('id DESC').first
721 journal = Journal.order('id DESC').first
722 assert_equal Issue.find(2), journal.journalized
722 assert_equal Issue.find(2), journal.journalized
723 assert_equal 1, journal.details.size
723 assert_equal 1, journal.details.size
724
724
725 detail = journal.details.first
725 detail = journal.details.first
726 assert_equal 'attachment', detail.property
726 assert_equal 'attachment', detail.property
727 assert_equal 'Paella.jpg', detail.value
727 assert_equal 'Paella.jpg', detail.value
728 end
728 end
729
729
730 def test_update_issue_should_send_email_notification
730 def test_update_issue_should_send_email_notification
731 ActionMailer::Base.deliveries.clear
731 ActionMailer::Base.deliveries.clear
732 journal = submit_email('ticket_reply.eml')
732 journal = submit_email('ticket_reply.eml')
733 assert journal.is_a?(Journal)
733 assert journal.is_a?(Journal)
734 assert_equal 1, ActionMailer::Base.deliveries.size
734 assert_equal 1, ActionMailer::Base.deliveries.size
735 end
735 end
736
736
737 def test_update_issue_should_not_set_defaults
737 def test_update_issue_should_not_set_defaults
738 journal = submit_email(
738 journal = submit_email(
739 'ticket_reply.eml',
739 'ticket_reply.eml',
740 :issue => {:tracker => 'Support request', :priority => 'High'}
740 :issue => {:tracker => 'Support request', :priority => 'High'}
741 )
741 )
742 assert journal.is_a?(Journal)
742 assert journal.is_a?(Journal)
743 assert_match /This is reply/, journal.notes
743 assert_match /This is reply/, journal.notes
744 assert_equal 'Feature request', journal.issue.tracker.name
744 assert_equal 'Feature request', journal.issue.tracker.name
745 assert_equal 'Normal', journal.issue.priority.name
745 assert_equal 'Normal', journal.issue.priority.name
746 end
746 end
747
747
748 def test_replying_to_a_private_note_should_add_reply_as_private
748 def test_replying_to_a_private_note_should_add_reply_as_private
749 private_journal = Journal.create!(:notes => 'Private notes', :journalized => Issue.find(1), :private_notes => true, :user_id => 2)
749 private_journal = Journal.create!(:notes => 'Private notes', :journalized => Issue.find(1), :private_notes => true, :user_id => 2)
750
750
751 assert_difference 'Journal.count' do
751 assert_difference 'Journal.count' do
752 journal = submit_email('ticket_reply.eml') do |email|
752 journal = submit_email('ticket_reply.eml') do |email|
753 email.sub! %r{^In-Reply-To:.*$}, "In-Reply-To: <redmine.journal-#{private_journal.id}.20060719210421@osiris>"
753 email.sub! %r{^In-Reply-To:.*$}, "In-Reply-To: <redmine.journal-#{private_journal.id}.20060719210421@osiris>"
754 end
754 end
755
755
756 assert_kind_of Journal, journal
756 assert_kind_of Journal, journal
757 assert_match /This is reply/, journal.notes
757 assert_match /This is reply/, journal.notes
758 assert_equal true, journal.private_notes
758 assert_equal true, journal.private_notes
759 end
759 end
760 end
760 end
761
761
762 def test_reply_to_a_message
762 def test_reply_to_a_message
763 m = submit_email('message_reply.eml')
763 m = submit_email('message_reply.eml')
764 assert m.is_a?(Message)
764 assert m.is_a?(Message)
765 assert !m.new_record?
765 assert !m.new_record?
766 m.reload
766 m.reload
767 assert_equal 'Reply via email', m.subject
767 assert_equal 'Reply via email', m.subject
768 # The email replies to message #2 which is part of the thread of message #1
768 # The email replies to message #2 which is part of the thread of message #1
769 assert_equal Message.find(1), m.parent
769 assert_equal Message.find(1), m.parent
770 end
770 end
771
771
772 def test_reply_to_a_message_by_subject
772 def test_reply_to_a_message_by_subject
773 m = submit_email('message_reply_by_subject.eml')
773 m = submit_email('message_reply_by_subject.eml')
774 assert m.is_a?(Message)
774 assert m.is_a?(Message)
775 assert !m.new_record?
775 assert !m.new_record?
776 m.reload
776 m.reload
777 assert_equal 'Reply to the first post', m.subject
777 assert_equal 'Reply to the first post', m.subject
778 assert_equal Message.find(1), m.parent
778 assert_equal Message.find(1), m.parent
779 end
779 end
780
780
781 def test_should_strip_tags_of_html_only_emails
781 def test_should_strip_tags_of_html_only_emails
782 issue = submit_email('ticket_html_only.eml', :issue => {:project => 'ecookbook'})
782 issue = submit_email('ticket_html_only.eml', :issue => {:project => 'ecookbook'})
783 assert issue.is_a?(Issue)
783 assert issue.is_a?(Issue)
784 assert !issue.new_record?
784 assert !issue.new_record?
785 issue.reload
785 issue.reload
786 assert_equal 'HTML email', issue.subject
786 assert_equal 'HTML email', issue.subject
787 assert_equal 'This is a html-only email.', issue.description
787 assert_equal 'This is a html-only email.', issue.description
788 end
788 end
789
789
790 test "truncate emails with no setting should add the entire email into the issue" do
790 test "truncate emails with no setting should add the entire email into the issue" do
791 with_settings :mail_handler_body_delimiters => '' do
791 with_settings :mail_handler_body_delimiters => '' do
792 issue = submit_email('ticket_on_given_project.eml')
792 issue = submit_email('ticket_on_given_project.eml')
793 assert_issue_created(issue)
793 assert_issue_created(issue)
794 assert issue.description.include?('---')
794 assert issue.description.include?('---')
795 assert issue.description.include?('This paragraph is after the delimiter')
795 assert issue.description.include?('This paragraph is after the delimiter')
796 end
796 end
797 end
797 end
798
798
799 test "truncate emails with a single string should truncate the email at the delimiter for the issue" do
799 test "truncate emails with a single string should truncate the email at the delimiter for the issue" do
800 with_settings :mail_handler_body_delimiters => '---' do
800 with_settings :mail_handler_body_delimiters => '---' do
801 issue = submit_email('ticket_on_given_project.eml')
801 issue = submit_email('ticket_on_given_project.eml')
802 assert_issue_created(issue)
802 assert_issue_created(issue)
803 assert issue.description.include?('This paragraph is before delimiters')
803 assert issue.description.include?('This paragraph is before delimiters')
804 assert issue.description.include?('--- This line starts with a delimiter')
804 assert issue.description.include?('--- This line starts with a delimiter')
805 assert !issue.description.match(/^---$/)
805 assert !issue.description.match(/^---$/)
806 assert !issue.description.include?('This paragraph is after the delimiter')
806 assert !issue.description.include?('This paragraph is after the delimiter')
807 end
807 end
808 end
808 end
809
809
810 test "truncate emails with a single quoted reply should truncate the email at the delimiter with the quoted reply symbols (>)" do
810 test "truncate emails with a single quoted reply should truncate the email at the delimiter with the quoted reply symbols (>)" do
811 with_settings :mail_handler_body_delimiters => '--- Reply above. Do not remove this line. ---' do
811 with_settings :mail_handler_body_delimiters => '--- Reply above. Do not remove this line. ---' do
812 journal = submit_email('issue_update_with_quoted_reply_above.eml')
812 journal = submit_email('issue_update_with_quoted_reply_above.eml')
813 assert journal.is_a?(Journal)
813 assert journal.is_a?(Journal)
814 assert journal.notes.include?('An update to the issue by the sender.')
814 assert journal.notes.include?('An update to the issue by the sender.')
815 assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---"))
815 assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---"))
816 assert !journal.notes.include?('Looks like the JSON api for projects was missed.')
816 assert !journal.notes.include?('Looks like the JSON api for projects was missed.')
817 end
817 end
818 end
818 end
819
819
820 test "truncate emails with multiple quoted replies should truncate the email at the delimiter with the quoted reply symbols (>)" do
820 test "truncate emails with multiple quoted replies should truncate the email at the delimiter with the quoted reply symbols (>)" do
821 with_settings :mail_handler_body_delimiters => '--- Reply above. Do not remove this line. ---' do
821 with_settings :mail_handler_body_delimiters => '--- Reply above. Do not remove this line. ---' do
822 journal = submit_email('issue_update_with_multiple_quoted_reply_above.eml')
822 journal = submit_email('issue_update_with_multiple_quoted_reply_above.eml')
823 assert journal.is_a?(Journal)
823 assert journal.is_a?(Journal)
824 assert journal.notes.include?('An update to the issue by the sender.')
824 assert journal.notes.include?('An update to the issue by the sender.')
825 assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---"))
825 assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---"))
826 assert !journal.notes.include?('Looks like the JSON api for projects was missed.')
826 assert !journal.notes.include?('Looks like the JSON api for projects was missed.')
827 end
827 end
828 end
828 end
829
829
830 test "truncate emails with multiple strings should truncate the email at the first delimiter found (BREAK)" do
830 test "truncate emails with multiple strings should truncate the email at the first delimiter found (BREAK)" do
831 with_settings :mail_handler_body_delimiters => "---\nBREAK" do
831 with_settings :mail_handler_body_delimiters => "---\nBREAK" do
832 issue = submit_email('ticket_on_given_project.eml')
832 issue = submit_email('ticket_on_given_project.eml')
833 assert_issue_created(issue)
833 assert_issue_created(issue)
834 assert issue.description.include?('This paragraph is before delimiters')
834 assert issue.description.include?('This paragraph is before delimiters')
835 assert !issue.description.include?('BREAK')
835 assert !issue.description.include?('BREAK')
836 assert !issue.description.include?('This paragraph is between delimiters')
836 assert !issue.description.include?('This paragraph is between delimiters')
837 assert !issue.description.match(/^---$/)
837 assert !issue.description.match(/^---$/)
838 assert !issue.description.include?('This paragraph is after the delimiter')
838 assert !issue.description.include?('This paragraph is after the delimiter')
839 end
839 end
840 end
840 end
841
841
842 def test_attachments_that_match_mail_handler_excluded_filenames_should_be_ignored
842 def test_attachments_that_match_mail_handler_excluded_filenames_should_be_ignored
843 with_settings :mail_handler_excluded_filenames => '*.vcf, *.jpg' do
843 with_settings :mail_handler_excluded_filenames => '*.vcf, *.jpg' do
844 issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
844 issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
845 assert issue.is_a?(Issue)
845 assert issue.is_a?(Issue)
846 assert !issue.new_record?
846 assert !issue.new_record?
847 assert_equal 0, issue.reload.attachments.size
847 assert_equal 0, issue.reload.attachments.size
848 end
848 end
849 end
849 end
850
850
851 def test_attachments_that_do_not_match_mail_handler_excluded_filenames_should_be_attached
851 def test_attachments_that_do_not_match_mail_handler_excluded_filenames_should_be_attached
852 with_settings :mail_handler_excluded_filenames => '*.vcf, *.gif' do
852 with_settings :mail_handler_excluded_filenames => '*.vcf, *.gif' do
853 issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
853 issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
854 assert issue.is_a?(Issue)
854 assert issue.is_a?(Issue)
855 assert !issue.new_record?
855 assert !issue.new_record?
856 assert_equal 1, issue.reload.attachments.size
856 assert_equal 1, issue.reload.attachments.size
857 end
857 end
858 end
858 end
859
859
860 def test_email_with_long_subject_line
860 def test_email_with_long_subject_line
861 issue = submit_email('ticket_with_long_subject.eml')
861 issue = submit_email('ticket_with_long_subject.eml')
862 assert issue.is_a?(Issue)
862 assert issue.is_a?(Issue)
863 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]
863 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]
864 end
864 end
865
865
866 def test_first_keyword_should_be_matched
866 def test_first_keyword_should_be_matched
867 issue = submit_email('ticket_with_duplicate_keyword.eml', :allow_override => 'priority')
867 issue = submit_email('ticket_with_duplicate_keyword.eml', :allow_override => 'priority')
868 assert issue.is_a?(Issue)
868 assert issue.is_a?(Issue)
869 assert_equal 'High', issue.priority.name
869 assert_equal 'High', issue.priority.name
870 end
870 end
871
871
872 def test_keyword_after_delimiter_should_be_ignored
872 def test_keyword_after_delimiter_should_be_ignored
873 with_settings :mail_handler_body_delimiters => "== DELIMITER ==" do
873 with_settings :mail_handler_body_delimiters => "== DELIMITER ==" do
874 issue = submit_email('ticket_with_keyword_after_delimiter.eml', :allow_override => 'priority')
874 issue = submit_email('ticket_with_keyword_after_delimiter.eml', :allow_override => 'priority')
875 assert issue.is_a?(Issue)
875 assert issue.is_a?(Issue)
876 assert_equal 'Normal', issue.priority.name
876 assert_equal 'Normal', issue.priority.name
877 end
877 end
878 end
878 end
879
879
880 def test_new_user_from_attributes_should_return_valid_user
880 def test_new_user_from_attributes_should_return_valid_user
881 to_test = {
881 to_test = {
882 # [address, name] => [login, firstname, lastname]
882 # [address, name] => [login, firstname, lastname]
883 ['jsmith@example.net', nil] => ['jsmith@example.net', 'jsmith', '-'],
883 ['jsmith@example.net', nil] => ['jsmith@example.net', 'jsmith', '-'],
884 ['jsmith@example.net', 'John'] => ['jsmith@example.net', 'John', '-'],
884 ['jsmith@example.net', 'John'] => ['jsmith@example.net', 'John', '-'],
885 ['jsmith@example.net', 'John Smith'] => ['jsmith@example.net', 'John', 'Smith'],
885 ['jsmith@example.net', 'John Smith'] => ['jsmith@example.net', 'John', 'Smith'],
886 ['jsmith@example.net', 'John Paul Smith'] => ['jsmith@example.net', 'John', 'Paul Smith'],
886 ['jsmith@example.net', 'John Paul Smith'] => ['jsmith@example.net', 'John', 'Paul Smith'],
887 ['jsmith@example.net', 'AVeryLongFirstnameThatExceedsTheMaximumLength Smith'] => ['jsmith@example.net', 'AVeryLongFirstnameThatExceedsT', 'Smith'],
887 ['jsmith@example.net', 'AVeryLongFirstnameThatExceedsTheMaximumLength Smith'] => ['jsmith@example.net', 'AVeryLongFirstnameThatExceedsT', 'Smith'],
888 ['jsmith@example.net', 'John AVeryLongLastnameThatExceedsTheMaximumLength'] => ['jsmith@example.net', 'John', 'AVeryLongLastnameThatExceedsTh']
888 ['jsmith@example.net', 'John AVeryLongLastnameThatExceedsTheMaximumLength'] => ['jsmith@example.net', 'John', 'AVeryLongLastnameThatExceedsTh']
889 }
889 }
890
890
891 to_test.each do |attrs, expected|
891 to_test.each do |attrs, expected|
892 user = MailHandler.new_user_from_attributes(attrs.first, attrs.last)
892 user = MailHandler.new_user_from_attributes(attrs.first, attrs.last)
893
893
894 assert user.valid?, user.errors.full_messages.to_s
894 assert user.valid?, user.errors.full_messages.to_s
895 assert_equal attrs.first, user.mail
895 assert_equal attrs.first, user.mail
896 assert_equal expected[0], user.login
896 assert_equal expected[0], user.login
897 assert_equal expected[1], user.firstname
897 assert_equal expected[1], user.firstname
898 assert_equal expected[2], user.lastname
898 assert_equal expected[2], user.lastname
899 assert_equal 'only_my_events', user.mail_notification
899 assert_equal 'only_my_events', user.mail_notification
900 end
900 end
901 end
901 end
902
902
903 def test_new_user_from_attributes_should_use_default_login_if_invalid
903 def test_new_user_from_attributes_should_use_default_login_if_invalid
904 user = MailHandler.new_user_from_attributes('foo+bar@example.net')
904 user = MailHandler.new_user_from_attributes('foo+bar@example.net')
905 assert user.valid?
905 assert user.valid?
906 assert user.login =~ /^user[a-f0-9]+$/
906 assert user.login =~ /^user[a-f0-9]+$/
907 assert_equal 'foo+bar@example.net', user.mail
907 assert_equal 'foo+bar@example.net', user.mail
908 end
908 end
909
909
910 def test_new_user_with_utf8_encoded_fullname_should_be_decoded
910 def test_new_user_with_utf8_encoded_fullname_should_be_decoded
911 assert_difference 'User.count' do
911 assert_difference 'User.count' do
912 issue = submit_email(
912 issue = submit_email(
913 'fullname_of_sender_as_utf8_encoded.eml',
913 'fullname_of_sender_as_utf8_encoded.eml',
914 :issue => {:project => 'ecookbook'},
914 :issue => {:project => 'ecookbook'},
915 :unknown_user => 'create'
915 :unknown_user => 'create'
916 )
916 )
917 end
917 end
918 user = User.order('id DESC').first
918 user = User.order('id DESC').first
919 assert_equal "foo@example.org", user.mail
919 assert_equal "foo@example.org", user.mail
920 str1 = "\xc3\x84\xc3\xa4".force_encoding('UTF-8')
920 str1 = "\xc3\x84\xc3\xa4".force_encoding('UTF-8')
921 str2 = "\xc3\x96\xc3\xb6".force_encoding('UTF-8')
921 str2 = "\xc3\x96\xc3\xb6".force_encoding('UTF-8')
922 assert_equal str1, user.firstname
922 assert_equal str1, user.firstname
923 assert_equal str2, user.lastname
923 assert_equal str2, user.lastname
924 end
924 end
925
925
926 def test_extract_options_from_env_should_return_options
926 def test_extract_options_from_env_should_return_options
927 options = MailHandler.extract_options_from_env({
927 options = MailHandler.extract_options_from_env({
928 'tracker' => 'defect',
928 'tracker' => 'defect',
929 'project' => 'foo',
929 'project' => 'foo',
930 'unknown_user' => 'create'
930 'unknown_user' => 'create'
931 })
931 })
932
932
933 assert_equal({
933 assert_equal({
934 :issue => {:tracker => 'defect', :project => 'foo'},
934 :issue => {:tracker => 'defect', :project => 'foo'},
935 :unknown_user => 'create'
935 :unknown_user => 'create'
936 }, options)
936 }, options)
937 end
937 end
938
938
939 def test_safe_receive_should_rescue_exceptions_and_return_false
940 MailHandler.stubs(:receive).raises(Exception.new "Something went wrong")
941
942 assert_equal false, MailHandler.safe_receive
943 end
944
939 private
945 private
940
946
941 def submit_email(filename, options={})
947 def submit_email(filename, options={})
942 raw = IO.read(File.join(FIXTURES_PATH, filename))
948 raw = IO.read(File.join(FIXTURES_PATH, filename))
943 yield raw if block_given?
949 yield raw if block_given?
944 MailHandler.receive(raw, options)
950 MailHandler.receive(raw, options)
945 end
951 end
946
952
947 def assert_issue_created(issue)
953 def assert_issue_created(issue)
948 assert issue.is_a?(Issue)
954 assert issue.is_a?(Issue)
949 assert !issue.new_record?
955 assert !issue.new_record?
950 issue.reload
956 issue.reload
951 end
957 end
952 end
958 end
@@ -1,776 +1,789
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2014 Jean-Philippe Lang
2 # Copyright (C) 2006-2014 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.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class MailerTest < ActiveSupport::TestCase
20 class MailerTest < ActiveSupport::TestCase
21 include Redmine::I18n
21 include Redmine::I18n
22 include ActionDispatch::Assertions::SelectorAssertions
22 include ActionDispatch::Assertions::SelectorAssertions
23 fixtures :projects, :enabled_modules, :issues, :users, :members,
23 fixtures :projects, :enabled_modules, :issues, :users, :members,
24 :member_roles, :roles, :documents, :attachments, :news,
24 :member_roles, :roles, :documents, :attachments, :news,
25 :tokens, :journals, :journal_details, :changesets,
25 :tokens, :journals, :journal_details, :changesets,
26 :trackers, :projects_trackers,
26 :trackers, :projects_trackers,
27 :issue_statuses, :enumerations, :messages, :boards, :repositories,
27 :issue_statuses, :enumerations, :messages, :boards, :repositories,
28 :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions,
28 :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions,
29 :versions,
29 :versions,
30 :comments
30 :comments
31
31
32 def setup
32 def setup
33 ActionMailer::Base.deliveries.clear
33 ActionMailer::Base.deliveries.clear
34 Setting.host_name = 'mydomain.foo'
34 Setting.host_name = 'mydomain.foo'
35 Setting.protocol = 'http'
35 Setting.protocol = 'http'
36 Setting.plain_text_mail = '0'
36 Setting.plain_text_mail = '0'
37 User.current = nil
37 User.current = nil
38 end
38 end
39
39
40 def test_generated_links_in_emails
40 def test_generated_links_in_emails
41 Setting.default_language = 'en'
41 Setting.default_language = 'en'
42 Setting.host_name = 'mydomain.foo'
42 Setting.host_name = 'mydomain.foo'
43 Setting.protocol = 'https'
43 Setting.protocol = 'https'
44
44
45 journal = Journal.find(3)
45 journal = Journal.find(3)
46 assert Mailer.deliver_issue_edit(journal)
46 assert Mailer.deliver_issue_edit(journal)
47
47
48 mail = last_email
48 mail = last_email
49 assert_not_nil mail
49 assert_not_nil mail
50
50
51 assert_select_email do
51 assert_select_email do
52 # link to the main ticket
52 # link to the main ticket
53 assert_select 'a[href=?]',
53 assert_select 'a[href=?]',
54 'https://mydomain.foo/issues/2#change-3',
54 'https://mydomain.foo/issues/2#change-3',
55 :text => 'Feature request #2: Add ingredients categories'
55 :text => 'Feature request #2: Add ingredients categories'
56 # link to a referenced ticket
56 # link to a referenced ticket
57 assert_select 'a[href=?][title=?]',
57 assert_select 'a[href=?][title=?]',
58 'https://mydomain.foo/issues/1',
58 'https://mydomain.foo/issues/1',
59 "#{ESCAPED_UCANT} print recipes (New)",
59 "#{ESCAPED_UCANT} print recipes (New)",
60 :text => '#1'
60 :text => '#1'
61 # link to a changeset
61 # link to a changeset
62 assert_select 'a[href=?][title=?]',
62 assert_select 'a[href=?][title=?]',
63 'https://mydomain.foo/projects/ecookbook/repository/revisions/2',
63 'https://mydomain.foo/projects/ecookbook/repository/revisions/2',
64 'This commit fixes #1, #2 and references #1 &amp; #3',
64 'This commit fixes #1, #2 and references #1 &amp; #3',
65 :text => 'r2'
65 :text => 'r2'
66 # link to a description diff
66 # link to a description diff
67 assert_select 'a[href=?][title=?]',
67 assert_select 'a[href=?][title=?]',
68 'https://mydomain.foo/journals/diff/3?detail_id=4',
68 'https://mydomain.foo/journals/diff/3?detail_id=4',
69 'View differences',
69 'View differences',
70 :text => 'diff'
70 :text => 'diff'
71 # link to an attachment
71 # link to an attachment
72 assert_select 'a[href=?]',
72 assert_select 'a[href=?]',
73 'https://mydomain.foo/attachments/download/4/source.rb',
73 'https://mydomain.foo/attachments/download/4/source.rb',
74 :text => 'source.rb'
74 :text => 'source.rb'
75 end
75 end
76 end
76 end
77
77
78 def test_generated_links_with_prefix
78 def test_generated_links_with_prefix
79 Setting.default_language = 'en'
79 Setting.default_language = 'en'
80 relative_url_root = Redmine::Utils.relative_url_root
80 relative_url_root = Redmine::Utils.relative_url_root
81 Setting.host_name = 'mydomain.foo/rdm'
81 Setting.host_name = 'mydomain.foo/rdm'
82 Setting.protocol = 'http'
82 Setting.protocol = 'http'
83
83
84 journal = Journal.find(3)
84 journal = Journal.find(3)
85 assert Mailer.deliver_issue_edit(journal)
85 assert Mailer.deliver_issue_edit(journal)
86
86
87 mail = last_email
87 mail = last_email
88 assert_not_nil mail
88 assert_not_nil mail
89
89
90 assert_select_email do
90 assert_select_email do
91 # link to the main ticket
91 # link to the main ticket
92 assert_select 'a[href=?]',
92 assert_select 'a[href=?]',
93 'http://mydomain.foo/rdm/issues/2#change-3',
93 'http://mydomain.foo/rdm/issues/2#change-3',
94 :text => 'Feature request #2: Add ingredients categories'
94 :text => 'Feature request #2: Add ingredients categories'
95 # link to a referenced ticket
95 # link to a referenced ticket
96 assert_select 'a[href=?][title=?]',
96 assert_select 'a[href=?][title=?]',
97 'http://mydomain.foo/rdm/issues/1',
97 'http://mydomain.foo/rdm/issues/1',
98 "#{ESCAPED_UCANT} print recipes (New)",
98 "#{ESCAPED_UCANT} print recipes (New)",
99 :text => '#1'
99 :text => '#1'
100 # link to a changeset
100 # link to a changeset
101 assert_select 'a[href=?][title=?]',
101 assert_select 'a[href=?][title=?]',
102 'http://mydomain.foo/rdm/projects/ecookbook/repository/revisions/2',
102 'http://mydomain.foo/rdm/projects/ecookbook/repository/revisions/2',
103 'This commit fixes #1, #2 and references #1 &amp; #3',
103 'This commit fixes #1, #2 and references #1 &amp; #3',
104 :text => 'r2'
104 :text => 'r2'
105 # link to a description diff
105 # link to a description diff
106 assert_select 'a[href=?][title=?]',
106 assert_select 'a[href=?][title=?]',
107 'http://mydomain.foo/rdm/journals/diff/3?detail_id=4',
107 'http://mydomain.foo/rdm/journals/diff/3?detail_id=4',
108 'View differences',
108 'View differences',
109 :text => 'diff'
109 :text => 'diff'
110 # link to an attachment
110 # link to an attachment
111 assert_select 'a[href=?]',
111 assert_select 'a[href=?]',
112 'http://mydomain.foo/rdm/attachments/download/4/source.rb',
112 'http://mydomain.foo/rdm/attachments/download/4/source.rb',
113 :text => 'source.rb'
113 :text => 'source.rb'
114 end
114 end
115 end
115 end
116
116
117 def test_issue_edit_should_generate_url_with_hostname_for_relations
117 def test_issue_edit_should_generate_url_with_hostname_for_relations
118 journal = Journal.new(:journalized => Issue.find(1), :user => User.find(1), :created_on => Time.now)
118 journal = Journal.new(:journalized => Issue.find(1), :user => User.find(1), :created_on => Time.now)
119 journal.details << JournalDetail.new(:property => 'relation', :prop_key => 'label_relates_to', :value => 2)
119 journal.details << JournalDetail.new(:property => 'relation', :prop_key => 'label_relates_to', :value => 2)
120 Mailer.deliver_issue_edit(journal)
120 Mailer.deliver_issue_edit(journal)
121 assert_not_nil last_email
121 assert_not_nil last_email
122 assert_select_email do
122 assert_select_email do
123 assert_select 'a[href=?]', 'http://mydomain.foo/issues/2', :text => 'Feature request #2'
123 assert_select 'a[href=?]', 'http://mydomain.foo/issues/2', :text => 'Feature request #2'
124 end
124 end
125 end
125 end
126
126
127 def test_generated_links_with_prefix_and_no_relative_url_root
127 def test_generated_links_with_prefix_and_no_relative_url_root
128 Setting.default_language = 'en'
128 Setting.default_language = 'en'
129 relative_url_root = Redmine::Utils.relative_url_root
129 relative_url_root = Redmine::Utils.relative_url_root
130 Setting.host_name = 'mydomain.foo/rdm'
130 Setting.host_name = 'mydomain.foo/rdm'
131 Setting.protocol = 'http'
131 Setting.protocol = 'http'
132 Redmine::Utils.relative_url_root = nil
132 Redmine::Utils.relative_url_root = nil
133
133
134 journal = Journal.find(3)
134 journal = Journal.find(3)
135 assert Mailer.deliver_issue_edit(journal)
135 assert Mailer.deliver_issue_edit(journal)
136
136
137 mail = last_email
137 mail = last_email
138 assert_not_nil mail
138 assert_not_nil mail
139
139
140 assert_select_email do
140 assert_select_email do
141 # link to the main ticket
141 # link to the main ticket
142 assert_select 'a[href=?]',
142 assert_select 'a[href=?]',
143 'http://mydomain.foo/rdm/issues/2#change-3',
143 'http://mydomain.foo/rdm/issues/2#change-3',
144 :text => 'Feature request #2: Add ingredients categories'
144 :text => 'Feature request #2: Add ingredients categories'
145 # link to a referenced ticket
145 # link to a referenced ticket
146 assert_select 'a[href=?][title=?]',
146 assert_select 'a[href=?][title=?]',
147 'http://mydomain.foo/rdm/issues/1',
147 'http://mydomain.foo/rdm/issues/1',
148 "#{ESCAPED_UCANT} print recipes (New)",
148 "#{ESCAPED_UCANT} print recipes (New)",
149 :text => '#1'
149 :text => '#1'
150 # link to a changeset
150 # link to a changeset
151 assert_select 'a[href=?][title=?]',
151 assert_select 'a[href=?][title=?]',
152 'http://mydomain.foo/rdm/projects/ecookbook/repository/revisions/2',
152 'http://mydomain.foo/rdm/projects/ecookbook/repository/revisions/2',
153 'This commit fixes #1, #2 and references #1 &amp; #3',
153 'This commit fixes #1, #2 and references #1 &amp; #3',
154 :text => 'r2'
154 :text => 'r2'
155 # link to a description diff
155 # link to a description diff
156 assert_select 'a[href=?][title=?]',
156 assert_select 'a[href=?][title=?]',
157 'http://mydomain.foo/rdm/journals/diff/3?detail_id=4',
157 'http://mydomain.foo/rdm/journals/diff/3?detail_id=4',
158 'View differences',
158 'View differences',
159 :text => 'diff'
159 :text => 'diff'
160 # link to an attachment
160 # link to an attachment
161 assert_select 'a[href=?]',
161 assert_select 'a[href=?]',
162 'http://mydomain.foo/rdm/attachments/download/4/source.rb',
162 'http://mydomain.foo/rdm/attachments/download/4/source.rb',
163 :text => 'source.rb'
163 :text => 'source.rb'
164 end
164 end
165 ensure
165 ensure
166 # restore it
166 # restore it
167 Redmine::Utils.relative_url_root = relative_url_root
167 Redmine::Utils.relative_url_root = relative_url_root
168 end
168 end
169
169
170 def test_email_headers
170 def test_email_headers
171 issue = Issue.find(1)
171 issue = Issue.find(1)
172 Mailer.deliver_issue_add(issue)
172 Mailer.deliver_issue_add(issue)
173 mail = last_email
173 mail = last_email
174 assert_not_nil mail
174 assert_not_nil mail
175 assert_equal 'OOF', mail.header['X-Auto-Response-Suppress'].to_s
175 assert_equal 'OOF', mail.header['X-Auto-Response-Suppress'].to_s
176 assert_equal 'auto-generated', mail.header['Auto-Submitted'].to_s
176 assert_equal 'auto-generated', mail.header['Auto-Submitted'].to_s
177 assert_equal '<redmine.example.net>', mail.header['List-Id'].to_s
177 assert_equal '<redmine.example.net>', mail.header['List-Id'].to_s
178 end
178 end
179
179
180 def test_email_headers_should_include_sender
180 def test_email_headers_should_include_sender
181 issue = Issue.find(1)
181 issue = Issue.find(1)
182 Mailer.deliver_issue_add(issue)
182 Mailer.deliver_issue_add(issue)
183 mail = last_email
183 mail = last_email
184 assert_equal issue.author.login, mail.header['X-Redmine-Sender'].to_s
184 assert_equal issue.author.login, mail.header['X-Redmine-Sender'].to_s
185 end
185 end
186
186
187 def test_plain_text_mail
187 def test_plain_text_mail
188 Setting.plain_text_mail = 1
188 Setting.plain_text_mail = 1
189 journal = Journal.find(2)
189 journal = Journal.find(2)
190 Mailer.deliver_issue_edit(journal)
190 Mailer.deliver_issue_edit(journal)
191 mail = last_email
191 mail = last_email
192 assert_equal "text/plain; charset=UTF-8", mail.content_type
192 assert_equal "text/plain; charset=UTF-8", mail.content_type
193 assert_equal 0, mail.parts.size
193 assert_equal 0, mail.parts.size
194 assert !mail.encoded.include?('href')
194 assert !mail.encoded.include?('href')
195 end
195 end
196
196
197 def test_html_mail
197 def test_html_mail
198 Setting.plain_text_mail = 0
198 Setting.plain_text_mail = 0
199 journal = Journal.find(2)
199 journal = Journal.find(2)
200 Mailer.deliver_issue_edit(journal)
200 Mailer.deliver_issue_edit(journal)
201 mail = last_email
201 mail = last_email
202 assert_equal 2, mail.parts.size
202 assert_equal 2, mail.parts.size
203 assert mail.encoded.include?('href')
203 assert mail.encoded.include?('href')
204 end
204 end
205
205
206 def test_from_header
206 def test_from_header
207 with_settings :mail_from => 'redmine@example.net' do
207 with_settings :mail_from => 'redmine@example.net' do
208 Mailer.test_email(User.find(1)).deliver
208 Mailer.test_email(User.find(1)).deliver
209 end
209 end
210 mail = last_email
210 mail = last_email
211 assert_equal 'redmine@example.net', mail.from_addrs.first
211 assert_equal 'redmine@example.net', mail.from_addrs.first
212 end
212 end
213
213
214 def test_from_header_with_phrase
214 def test_from_header_with_phrase
215 with_settings :mail_from => 'Redmine app <redmine@example.net>' do
215 with_settings :mail_from => 'Redmine app <redmine@example.net>' do
216 Mailer.test_email(User.find(1)).deliver
216 Mailer.test_email(User.find(1)).deliver
217 end
217 end
218 mail = last_email
218 mail = last_email
219 assert_equal 'redmine@example.net', mail.from_addrs.first
219 assert_equal 'redmine@example.net', mail.from_addrs.first
220 assert_equal 'Redmine app <redmine@example.net>', mail.header['From'].to_s
220 assert_equal 'Redmine app <redmine@example.net>', mail.header['From'].to_s
221 end
221 end
222
222
223 def test_should_not_send_email_without_recipient
223 def test_should_not_send_email_without_recipient
224 news = News.first
224 news = News.first
225 user = news.author
225 user = news.author
226 # Remove members except news author
226 # Remove members except news author
227 news.project.memberships.each {|m| m.destroy unless m.user == user}
227 news.project.memberships.each {|m| m.destroy unless m.user == user}
228
228
229 user.pref.no_self_notified = false
229 user.pref.no_self_notified = false
230 user.pref.save
230 user.pref.save
231 User.current = user
231 User.current = user
232 Mailer.news_added(news.reload).deliver
232 Mailer.news_added(news.reload).deliver
233 assert_equal 1, last_email.bcc.size
233 assert_equal 1, last_email.bcc.size
234
234
235 # nobody to notify
235 # nobody to notify
236 user.pref.no_self_notified = true
236 user.pref.no_self_notified = true
237 user.pref.save
237 user.pref.save
238 User.current = user
238 User.current = user
239 ActionMailer::Base.deliveries.clear
239 ActionMailer::Base.deliveries.clear
240 Mailer.news_added(news.reload).deliver
240 Mailer.news_added(news.reload).deliver
241 assert ActionMailer::Base.deliveries.empty?
241 assert ActionMailer::Base.deliveries.empty?
242 end
242 end
243
243
244 def test_issue_add_message_id
244 def test_issue_add_message_id
245 issue = Issue.find(2)
245 issue = Issue.find(2)
246 Mailer.deliver_issue_add(issue)
246 Mailer.deliver_issue_add(issue)
247 mail = last_email
247 mail = last_email
248 assert_match /^redmine\.issue-2\.20060719190421\.[a-f0-9]+@example\.net/, mail.message_id
248 assert_match /^redmine\.issue-2\.20060719190421\.[a-f0-9]+@example\.net/, mail.message_id
249 assert_include "redmine.issue-2.20060719190421@example.net", mail.references
249 assert_include "redmine.issue-2.20060719190421@example.net", mail.references
250 end
250 end
251
251
252 def test_issue_edit_message_id
252 def test_issue_edit_message_id
253 journal = Journal.find(3)
253 journal = Journal.find(3)
254 journal.issue = Issue.find(2)
254 journal.issue = Issue.find(2)
255
255
256 Mailer.deliver_issue_edit(journal)
256 Mailer.deliver_issue_edit(journal)
257 mail = last_email
257 mail = last_email
258 assert_match /^redmine\.journal-3\.\d+\.[a-f0-9]+@example\.net/, mail.message_id
258 assert_match /^redmine\.journal-3\.\d+\.[a-f0-9]+@example\.net/, mail.message_id
259 assert_include "redmine.issue-2.20060719190421@example.net", mail.references
259 assert_include "redmine.issue-2.20060719190421@example.net", mail.references
260 assert_select_email do
260 assert_select_email do
261 # link to the update
261 # link to the update
262 assert_select "a[href=?]",
262 assert_select "a[href=?]",
263 "http://mydomain.foo/issues/#{journal.journalized_id}#change-#{journal.id}"
263 "http://mydomain.foo/issues/#{journal.journalized_id}#change-#{journal.id}"
264 end
264 end
265 end
265 end
266
266
267 def test_message_posted_message_id
267 def test_message_posted_message_id
268 message = Message.find(1)
268 message = Message.find(1)
269 Mailer.message_posted(message).deliver
269 Mailer.message_posted(message).deliver
270 mail = last_email
270 mail = last_email
271 assert_match /^redmine\.message-1\.\d+\.[a-f0-9]+@example\.net/, mail.message_id
271 assert_match /^redmine\.message-1\.\d+\.[a-f0-9]+@example\.net/, mail.message_id
272 assert_include "redmine.message-1.20070512151532@example.net", mail.references
272 assert_include "redmine.message-1.20070512151532@example.net", mail.references
273 assert_select_email do
273 assert_select_email do
274 # link to the message
274 # link to the message
275 assert_select "a[href=?]",
275 assert_select "a[href=?]",
276 "http://mydomain.foo/boards/#{message.board.id}/topics/#{message.id}",
276 "http://mydomain.foo/boards/#{message.board.id}/topics/#{message.id}",
277 :text => message.subject
277 :text => message.subject
278 end
278 end
279 end
279 end
280
280
281 def test_reply_posted_message_id
281 def test_reply_posted_message_id
282 message = Message.find(3)
282 message = Message.find(3)
283 Mailer.message_posted(message).deliver
283 Mailer.message_posted(message).deliver
284 mail = last_email
284 mail = last_email
285 assert_match /^redmine\.message-3\.\d+\.[a-f0-9]+@example\.net/, mail.message_id
285 assert_match /^redmine\.message-3\.\d+\.[a-f0-9]+@example\.net/, mail.message_id
286 assert_include "redmine.message-1.20070512151532@example.net", mail.references
286 assert_include "redmine.message-1.20070512151532@example.net", mail.references
287 assert_select_email do
287 assert_select_email do
288 # link to the reply
288 # link to the reply
289 assert_select "a[href=?]",
289 assert_select "a[href=?]",
290 "http://mydomain.foo/boards/#{message.board.id}/topics/#{message.root.id}?r=#{message.id}#message-#{message.id}",
290 "http://mydomain.foo/boards/#{message.board.id}/topics/#{message.root.id}?r=#{message.id}#message-#{message.id}",
291 :text => message.subject
291 :text => message.subject
292 end
292 end
293 end
293 end
294
294
295 test "#issue_add should notify project members" do
295 test "#issue_add should notify project members" do
296 issue = Issue.find(1)
296 issue = Issue.find(1)
297 assert Mailer.deliver_issue_add(issue)
297 assert Mailer.deliver_issue_add(issue)
298 assert last_email.bcc.include?('dlopper@somenet.foo')
298 assert last_email.bcc.include?('dlopper@somenet.foo')
299 end
299 end
300
300
301 test "#issue_add should not notify project members that are not allow to view the issue" do
301 test "#issue_add should not notify project members that are not allow to view the issue" do
302 issue = Issue.find(1)
302 issue = Issue.find(1)
303 Role.find(2).remove_permission!(:view_issues)
303 Role.find(2).remove_permission!(:view_issues)
304 assert Mailer.deliver_issue_add(issue)
304 assert Mailer.deliver_issue_add(issue)
305 assert !last_email.bcc.include?('dlopper@somenet.foo')
305 assert !last_email.bcc.include?('dlopper@somenet.foo')
306 end
306 end
307
307
308 test "#issue_add should notify issue watchers" do
308 test "#issue_add should notify issue watchers" do
309 issue = Issue.find(1)
309 issue = Issue.find(1)
310 user = User.find(9)
310 user = User.find(9)
311 # minimal email notification options
311 # minimal email notification options
312 user.pref.no_self_notified = '1'
312 user.pref.no_self_notified = '1'
313 user.pref.save
313 user.pref.save
314 user.mail_notification = false
314 user.mail_notification = false
315 user.save
315 user.save
316
316
317 Watcher.create!(:watchable => issue, :user => user)
317 Watcher.create!(:watchable => issue, :user => user)
318 assert Mailer.deliver_issue_add(issue)
318 assert Mailer.deliver_issue_add(issue)
319 assert last_email.bcc.include?(user.mail)
319 assert last_email.bcc.include?(user.mail)
320 end
320 end
321
321
322 test "#issue_add should not notify watchers not allowed to view the issue" do
322 test "#issue_add should not notify watchers not allowed to view the issue" do
323 issue = Issue.find(1)
323 issue = Issue.find(1)
324 user = User.find(9)
324 user = User.find(9)
325 Watcher.create!(:watchable => issue, :user => user)
325 Watcher.create!(:watchable => issue, :user => user)
326 Role.non_member.remove_permission!(:view_issues)
326 Role.non_member.remove_permission!(:view_issues)
327 assert Mailer.deliver_issue_add(issue)
327 assert Mailer.deliver_issue_add(issue)
328 assert !last_email.bcc.include?(user.mail)
328 assert !last_email.bcc.include?(user.mail)
329 end
329 end
330
330
331 def test_issue_add_should_include_enabled_fields
331 def test_issue_add_should_include_enabled_fields
332 Setting.default_language = 'en'
332 Setting.default_language = 'en'
333 issue = Issue.find(2)
333 issue = Issue.find(2)
334 assert Mailer.deliver_issue_add(issue)
334 assert Mailer.deliver_issue_add(issue)
335 assert_mail_body_match '* Target version: 1.0', last_email
335 assert_mail_body_match '* Target version: 1.0', last_email
336 assert_select_email do
336 assert_select_email do
337 assert_select 'li', :text => 'Target version: 1.0'
337 assert_select 'li', :text => 'Target version: 1.0'
338 end
338 end
339 end
339 end
340
340
341 def test_issue_add_should_not_include_disabled_fields
341 def test_issue_add_should_not_include_disabled_fields
342 Setting.default_language = 'en'
342 Setting.default_language = 'en'
343 issue = Issue.find(2)
343 issue = Issue.find(2)
344 tracker = issue.tracker
344 tracker = issue.tracker
345 tracker.core_fields -= ['fixed_version_id']
345 tracker.core_fields -= ['fixed_version_id']
346 tracker.save!
346 tracker.save!
347 assert Mailer.deliver_issue_add(issue)
347 assert Mailer.deliver_issue_add(issue)
348 assert_mail_body_no_match 'Target version', last_email
348 assert_mail_body_no_match 'Target version', last_email
349 assert_select_email do
349 assert_select_email do
350 assert_select 'li', :text => /Target version/, :count => 0
350 assert_select 'li', :text => /Target version/, :count => 0
351 end
351 end
352 end
352 end
353
353
354 # test mailer methods for each language
354 # test mailer methods for each language
355 def test_issue_add
355 def test_issue_add
356 issue = Issue.find(1)
356 issue = Issue.find(1)
357 valid_languages.each do |lang|
357 valid_languages.each do |lang|
358 Setting.default_language = lang.to_s
358 Setting.default_language = lang.to_s
359 assert Mailer.deliver_issue_add(issue)
359 assert Mailer.deliver_issue_add(issue)
360 end
360 end
361 end
361 end
362
362
363 def test_issue_edit
363 def test_issue_edit
364 journal = Journal.find(1)
364 journal = Journal.find(1)
365 valid_languages.each do |lang|
365 valid_languages.each do |lang|
366 Setting.default_language = lang.to_s
366 Setting.default_language = lang.to_s
367 assert Mailer.deliver_issue_edit(journal)
367 assert Mailer.deliver_issue_edit(journal)
368 end
368 end
369 end
369 end
370
370
371 def test_issue_edit_should_send_private_notes_to_users_with_permission_only
371 def test_issue_edit_should_send_private_notes_to_users_with_permission_only
372 journal = Journal.find(1)
372 journal = Journal.find(1)
373 journal.private_notes = true
373 journal.private_notes = true
374 journal.save!
374 journal.save!
375
375
376 Role.find(2).add_permission! :view_private_notes
376 Role.find(2).add_permission! :view_private_notes
377 Mailer.deliver_issue_edit(journal)
377 Mailer.deliver_issue_edit(journal)
378 assert_equal %w(dlopper@somenet.foo jsmith@somenet.foo), ActionMailer::Base.deliveries.last.bcc.sort
378 assert_equal %w(dlopper@somenet.foo jsmith@somenet.foo), ActionMailer::Base.deliveries.last.bcc.sort
379
379
380 Role.find(2).remove_permission! :view_private_notes
380 Role.find(2).remove_permission! :view_private_notes
381 Mailer.deliver_issue_edit(journal)
381 Mailer.deliver_issue_edit(journal)
382 assert_equal %w(jsmith@somenet.foo), ActionMailer::Base.deliveries.last.bcc.sort
382 assert_equal %w(jsmith@somenet.foo), ActionMailer::Base.deliveries.last.bcc.sort
383 end
383 end
384
384
385 def test_issue_edit_should_send_private_notes_to_watchers_with_permission_only
385 def test_issue_edit_should_send_private_notes_to_watchers_with_permission_only
386 Issue.find(1).set_watcher(User.find_by_login('someone'))
386 Issue.find(1).set_watcher(User.find_by_login('someone'))
387 journal = Journal.find(1)
387 journal = Journal.find(1)
388 journal.private_notes = true
388 journal.private_notes = true
389 journal.save!
389 journal.save!
390
390
391 Role.non_member.add_permission! :view_private_notes
391 Role.non_member.add_permission! :view_private_notes
392 Mailer.deliver_issue_edit(journal)
392 Mailer.deliver_issue_edit(journal)
393 assert_include 'someone@foo.bar', ActionMailer::Base.deliveries.last.bcc.sort
393 assert_include 'someone@foo.bar', ActionMailer::Base.deliveries.last.bcc.sort
394
394
395 Role.non_member.remove_permission! :view_private_notes
395 Role.non_member.remove_permission! :view_private_notes
396 Mailer.deliver_issue_edit(journal)
396 Mailer.deliver_issue_edit(journal)
397 assert_not_include 'someone@foo.bar', ActionMailer::Base.deliveries.last.bcc.sort
397 assert_not_include 'someone@foo.bar', ActionMailer::Base.deliveries.last.bcc.sort
398 end
398 end
399
399
400 def test_issue_edit_should_mark_private_notes
400 def test_issue_edit_should_mark_private_notes
401 journal = Journal.find(2)
401 journal = Journal.find(2)
402 journal.private_notes = true
402 journal.private_notes = true
403 journal.save!
403 journal.save!
404
404
405 with_settings :default_language => 'en' do
405 with_settings :default_language => 'en' do
406 Mailer.deliver_issue_edit(journal)
406 Mailer.deliver_issue_edit(journal)
407 end
407 end
408 assert_mail_body_match '(Private notes)', last_email
408 assert_mail_body_match '(Private notes)', last_email
409 end
409 end
410
410
411 def test_issue_edit_with_relation_should_notify_users_who_can_see_the_related_issue
411 def test_issue_edit_with_relation_should_notify_users_who_can_see_the_related_issue
412 issue = Issue.generate!
412 issue = Issue.generate!
413 issue.init_journal(User.find(1))
413 issue.init_journal(User.find(1))
414 private_issue = Issue.generate!(:is_private => true)
414 private_issue = Issue.generate!(:is_private => true)
415 IssueRelation.create!(:issue_from => issue, :issue_to => private_issue, :relation_type => 'relates')
415 IssueRelation.create!(:issue_from => issue, :issue_to => private_issue, :relation_type => 'relates')
416 issue.reload
416 issue.reload
417 assert_equal 1, issue.journals.size
417 assert_equal 1, issue.journals.size
418 journal = issue.journals.first
418 journal = issue.journals.first
419 ActionMailer::Base.deliveries.clear
419 ActionMailer::Base.deliveries.clear
420
420
421 Mailer.deliver_issue_edit(journal)
421 Mailer.deliver_issue_edit(journal)
422 last_email.bcc.each do |email|
422 last_email.bcc.each do |email|
423 user = User.find_by_mail(email)
423 user = User.find_by_mail(email)
424 assert private_issue.visible?(user), "Issue was not visible to #{user}"
424 assert private_issue.visible?(user), "Issue was not visible to #{user}"
425 end
425 end
426 end
426 end
427
427
428 def test_document_added
428 def test_document_added
429 document = Document.find(1)
429 document = Document.find(1)
430 valid_languages.each do |lang|
430 valid_languages.each do |lang|
431 Setting.default_language = lang.to_s
431 Setting.default_language = lang.to_s
432 assert Mailer.document_added(document).deliver
432 assert Mailer.document_added(document).deliver
433 end
433 end
434 end
434 end
435
435
436 def test_attachments_added
436 def test_attachments_added
437 attachements = [ Attachment.find_by_container_type('Document') ]
437 attachements = [ Attachment.find_by_container_type('Document') ]
438 valid_languages.each do |lang|
438 valid_languages.each do |lang|
439 Setting.default_language = lang.to_s
439 Setting.default_language = lang.to_s
440 assert Mailer.attachments_added(attachements).deliver
440 assert Mailer.attachments_added(attachements).deliver
441 end
441 end
442 end
442 end
443
443
444 def test_version_file_added
444 def test_version_file_added
445 attachements = [ Attachment.find_by_container_type('Version') ]
445 attachements = [ Attachment.find_by_container_type('Version') ]
446 assert Mailer.attachments_added(attachements).deliver
446 assert Mailer.attachments_added(attachements).deliver
447 assert_not_nil last_email.bcc
447 assert_not_nil last_email.bcc
448 assert last_email.bcc.any?
448 assert last_email.bcc.any?
449 assert_select_email do
449 assert_select_email do
450 assert_select "a[href=?]", "http://mydomain.foo/projects/ecookbook/files"
450 assert_select "a[href=?]", "http://mydomain.foo/projects/ecookbook/files"
451 end
451 end
452 end
452 end
453
453
454 def test_project_file_added
454 def test_project_file_added
455 attachements = [ Attachment.find_by_container_type('Project') ]
455 attachements = [ Attachment.find_by_container_type('Project') ]
456 assert Mailer.attachments_added(attachements).deliver
456 assert Mailer.attachments_added(attachements).deliver
457 assert_not_nil last_email.bcc
457 assert_not_nil last_email.bcc
458 assert last_email.bcc.any?
458 assert last_email.bcc.any?
459 assert_select_email do
459 assert_select_email do
460 assert_select "a[href=?]", "http://mydomain.foo/projects/ecookbook/files"
460 assert_select "a[href=?]", "http://mydomain.foo/projects/ecookbook/files"
461 end
461 end
462 end
462 end
463
463
464 def test_news_added
464 def test_news_added
465 news = News.first
465 news = News.first
466 valid_languages.each do |lang|
466 valid_languages.each do |lang|
467 Setting.default_language = lang.to_s
467 Setting.default_language = lang.to_s
468 assert Mailer.news_added(news).deliver
468 assert Mailer.news_added(news).deliver
469 end
469 end
470 end
470 end
471
471
472 def test_news_added_should_notify_project_news_watchers
472 def test_news_added_should_notify_project_news_watchers
473 user1 = User.generate!
473 user1 = User.generate!
474 user2 = User.generate!
474 user2 = User.generate!
475 news = News.find(1)
475 news = News.find(1)
476 news.project.enabled_module('news').add_watcher(user1)
476 news.project.enabled_module('news').add_watcher(user1)
477
477
478 Mailer.news_added(news).deliver
478 Mailer.news_added(news).deliver
479 assert_include user1.mail, last_email.bcc
479 assert_include user1.mail, last_email.bcc
480 assert_not_include user2.mail, last_email.bcc
480 assert_not_include user2.mail, last_email.bcc
481 end
481 end
482
482
483 def test_news_comment_added
483 def test_news_comment_added
484 comment = Comment.find(2)
484 comment = Comment.find(2)
485 valid_languages.each do |lang|
485 valid_languages.each do |lang|
486 Setting.default_language = lang.to_s
486 Setting.default_language = lang.to_s
487 assert Mailer.news_comment_added(comment).deliver
487 assert Mailer.news_comment_added(comment).deliver
488 end
488 end
489 end
489 end
490
490
491 def test_message_posted
491 def test_message_posted
492 message = Message.first
492 message = Message.first
493 recipients = ([message.root] + message.root.children).collect {|m| m.author.mail if m.author}
493 recipients = ([message.root] + message.root.children).collect {|m| m.author.mail if m.author}
494 recipients = recipients.compact.uniq
494 recipients = recipients.compact.uniq
495 valid_languages.each do |lang|
495 valid_languages.each do |lang|
496 Setting.default_language = lang.to_s
496 Setting.default_language = lang.to_s
497 assert Mailer.message_posted(message).deliver
497 assert Mailer.message_posted(message).deliver
498 end
498 end
499 end
499 end
500
500
501 def test_wiki_content_added
501 def test_wiki_content_added
502 content = WikiContent.find(1)
502 content = WikiContent.find(1)
503 valid_languages.each do |lang|
503 valid_languages.each do |lang|
504 Setting.default_language = lang.to_s
504 Setting.default_language = lang.to_s
505 assert_difference 'ActionMailer::Base.deliveries.size' do
505 assert_difference 'ActionMailer::Base.deliveries.size' do
506 assert Mailer.wiki_content_added(content).deliver
506 assert Mailer.wiki_content_added(content).deliver
507 assert_select_email do
507 assert_select_email do
508 assert_select 'a[href=?]',
508 assert_select 'a[href=?]',
509 'http://mydomain.foo/projects/ecookbook/wiki/CookBook_documentation',
509 'http://mydomain.foo/projects/ecookbook/wiki/CookBook_documentation',
510 :text => 'CookBook documentation'
510 :text => 'CookBook documentation'
511 end
511 end
512 end
512 end
513 end
513 end
514 end
514 end
515
515
516 def test_wiki_content_updated
516 def test_wiki_content_updated
517 content = WikiContent.find(1)
517 content = WikiContent.find(1)
518 valid_languages.each do |lang|
518 valid_languages.each do |lang|
519 Setting.default_language = lang.to_s
519 Setting.default_language = lang.to_s
520 assert_difference 'ActionMailer::Base.deliveries.size' do
520 assert_difference 'ActionMailer::Base.deliveries.size' do
521 assert Mailer.wiki_content_updated(content).deliver
521 assert Mailer.wiki_content_updated(content).deliver
522 assert_select_email do
522 assert_select_email do
523 assert_select 'a[href=?]',
523 assert_select 'a[href=?]',
524 'http://mydomain.foo/projects/ecookbook/wiki/CookBook_documentation',
524 'http://mydomain.foo/projects/ecookbook/wiki/CookBook_documentation',
525 :text => 'CookBook documentation'
525 :text => 'CookBook documentation'
526 end
526 end
527 end
527 end
528 end
528 end
529 end
529 end
530
530
531 def test_account_information
531 def test_account_information
532 user = User.find(2)
532 user = User.find(2)
533 valid_languages.each do |lang|
533 valid_languages.each do |lang|
534 user.update_attribute :language, lang.to_s
534 user.update_attribute :language, lang.to_s
535 user.reload
535 user.reload
536 assert Mailer.account_information(user, 'pAsswORd').deliver
536 assert Mailer.account_information(user, 'pAsswORd').deliver
537 end
537 end
538 end
538 end
539
539
540 def test_lost_password
540 def test_lost_password
541 token = Token.find(2)
541 token = Token.find(2)
542 valid_languages.each do |lang|
542 valid_languages.each do |lang|
543 token.user.update_attribute :language, lang.to_s
543 token.user.update_attribute :language, lang.to_s
544 token.reload
544 token.reload
545 assert Mailer.lost_password(token).deliver
545 assert Mailer.lost_password(token).deliver
546 end
546 end
547 end
547 end
548
548
549 def test_register
549 def test_register
550 token = Token.find(1)
550 token = Token.find(1)
551 Setting.host_name = 'redmine.foo'
551 Setting.host_name = 'redmine.foo'
552 Setting.protocol = 'https'
552 Setting.protocol = 'https'
553
553
554 valid_languages.each do |lang|
554 valid_languages.each do |lang|
555 token.user.update_attribute :language, lang.to_s
555 token.user.update_attribute :language, lang.to_s
556 token.reload
556 token.reload
557 ActionMailer::Base.deliveries.clear
557 ActionMailer::Base.deliveries.clear
558 assert Mailer.register(token).deliver
558 assert Mailer.register(token).deliver
559 mail = last_email
559 mail = last_email
560 assert_select_email do
560 assert_select_email do
561 assert_select "a[href=?]",
561 assert_select "a[href=?]",
562 "https://redmine.foo/account/activate?token=#{token.value}",
562 "https://redmine.foo/account/activate?token=#{token.value}",
563 :text => "https://redmine.foo/account/activate?token=#{token.value}"
563 :text => "https://redmine.foo/account/activate?token=#{token.value}"
564 end
564 end
565 end
565 end
566 end
566 end
567
567
568 def test_test
568 def test_test
569 user = User.find(1)
569 user = User.find(1)
570 valid_languages.each do |lang|
570 valid_languages.each do |lang|
571 user.update_attribute :language, lang.to_s
571 user.update_attribute :language, lang.to_s
572 assert Mailer.test_email(user).deliver
572 assert Mailer.test_email(user).deliver
573 end
573 end
574 end
574 end
575
575
576 def test_reminders
576 def test_reminders
577 Mailer.reminders(:days => 42)
577 Mailer.reminders(:days => 42)
578 assert_equal 1, ActionMailer::Base.deliveries.size
578 assert_equal 1, ActionMailer::Base.deliveries.size
579 mail = last_email
579 mail = last_email
580 assert mail.bcc.include?('dlopper@somenet.foo')
580 assert mail.bcc.include?('dlopper@somenet.foo')
581 assert_mail_body_match 'Bug #3: Error 281 when updating a recipe', mail
581 assert_mail_body_match 'Bug #3: Error 281 when updating a recipe', mail
582 assert_equal '1 issue(s) due in the next 42 days', mail.subject
582 assert_equal '1 issue(s) due in the next 42 days', mail.subject
583 end
583 end
584
584
585 def test_reminders_should_not_include_closed_issues
585 def test_reminders_should_not_include_closed_issues
586 with_settings :default_language => 'en' do
586 with_settings :default_language => 'en' do
587 Issue.create!(:project_id => 1, :tracker_id => 1, :status_id => 5,
587 Issue.create!(:project_id => 1, :tracker_id => 1, :status_id => 5,
588 :subject => 'Closed issue', :assigned_to_id => 3,
588 :subject => 'Closed issue', :assigned_to_id => 3,
589 :due_date => 5.days.from_now,
589 :due_date => 5.days.from_now,
590 :author_id => 2)
590 :author_id => 2)
591 ActionMailer::Base.deliveries.clear
591 ActionMailer::Base.deliveries.clear
592
592
593 Mailer.reminders(:days => 42)
593 Mailer.reminders(:days => 42)
594 assert_equal 1, ActionMailer::Base.deliveries.size
594 assert_equal 1, ActionMailer::Base.deliveries.size
595 mail = last_email
595 mail = last_email
596 assert mail.bcc.include?('dlopper@somenet.foo')
596 assert mail.bcc.include?('dlopper@somenet.foo')
597 assert_mail_body_no_match 'Closed issue', mail
597 assert_mail_body_no_match 'Closed issue', mail
598 end
598 end
599 end
599 end
600
600
601 def test_reminders_for_users
601 def test_reminders_for_users
602 Mailer.reminders(:days => 42, :users => ['5'])
602 Mailer.reminders(:days => 42, :users => ['5'])
603 assert_equal 0, ActionMailer::Base.deliveries.size # No mail for dlopper
603 assert_equal 0, ActionMailer::Base.deliveries.size # No mail for dlopper
604 Mailer.reminders(:days => 42, :users => ['3'])
604 Mailer.reminders(:days => 42, :users => ['3'])
605 assert_equal 1, ActionMailer::Base.deliveries.size # No mail for dlopper
605 assert_equal 1, ActionMailer::Base.deliveries.size # No mail for dlopper
606 mail = last_email
606 mail = last_email
607 assert mail.bcc.include?('dlopper@somenet.foo')
607 assert mail.bcc.include?('dlopper@somenet.foo')
608 assert_mail_body_match 'Bug #3: Error 281 when updating a recipe', mail
608 assert_mail_body_match 'Bug #3: Error 281 when updating a recipe', mail
609 end
609 end
610
610
611 def test_reminder_should_include_issues_assigned_to_groups
611 def test_reminder_should_include_issues_assigned_to_groups
612 with_settings :default_language => 'en' do
612 with_settings :default_language => 'en' do
613 group = Group.generate!
613 group = Group.generate!
614 group.users << User.find(2)
614 group.users << User.find(2)
615 group.users << User.find(3)
615 group.users << User.find(3)
616
616
617 Issue.create!(:project_id => 1, :tracker_id => 1, :status_id => 1,
617 Issue.create!(:project_id => 1, :tracker_id => 1, :status_id => 1,
618 :subject => 'Assigned to group', :assigned_to => group,
618 :subject => 'Assigned to group', :assigned_to => group,
619 :due_date => 5.days.from_now,
619 :due_date => 5.days.from_now,
620 :author_id => 2)
620 :author_id => 2)
621 ActionMailer::Base.deliveries.clear
621 ActionMailer::Base.deliveries.clear
622
622
623 Mailer.reminders(:days => 7)
623 Mailer.reminders(:days => 7)
624 assert_equal 2, ActionMailer::Base.deliveries.size
624 assert_equal 2, ActionMailer::Base.deliveries.size
625 assert_equal %w(dlopper@somenet.foo jsmith@somenet.foo), ActionMailer::Base.deliveries.map(&:bcc).flatten.sort
625 assert_equal %w(dlopper@somenet.foo jsmith@somenet.foo), ActionMailer::Base.deliveries.map(&:bcc).flatten.sort
626 ActionMailer::Base.deliveries.each do |mail|
626 ActionMailer::Base.deliveries.each do |mail|
627 assert_mail_body_match 'Assigned to group', mail
627 assert_mail_body_match 'Assigned to group', mail
628 end
628 end
629 end
629 end
630 end
630 end
631
631
632 def test_mailer_should_not_change_locale
632 def test_mailer_should_not_change_locale
633 Setting.default_language = 'en'
633 Setting.default_language = 'en'
634 # Set current language to italian
634 # Set current language to italian
635 set_language_if_valid 'it'
635 set_language_if_valid 'it'
636 # Send an email to a french user
636 # Send an email to a french user
637 user = User.find(1)
637 user = User.find(1)
638 user.language = 'fr'
638 user.language = 'fr'
639 Mailer.account_activated(user).deliver
639 Mailer.account_activated(user).deliver
640 mail = last_email
640 mail = last_email
641 assert_mail_body_match 'Votre compte', mail
641 assert_mail_body_match 'Votre compte', mail
642
642
643 assert_equal :it, current_language
643 assert_equal :it, current_language
644 end
644 end
645
645
646 def test_with_deliveries_off
646 def test_with_deliveries_off
647 Mailer.with_deliveries false do
647 Mailer.with_deliveries false do
648 Mailer.test_email(User.find(1)).deliver
648 Mailer.test_email(User.find(1)).deliver
649 end
649 end
650 assert ActionMailer::Base.deliveries.empty?
650 assert ActionMailer::Base.deliveries.empty?
651 # should restore perform_deliveries
651 # should restore perform_deliveries
652 assert ActionMailer::Base.perform_deliveries
652 assert ActionMailer::Base.perform_deliveries
653 end
653 end
654
654
655 def test_token_for_should_strip_trailing_gt_from_address_with_full_name
655 def test_token_for_should_strip_trailing_gt_from_address_with_full_name
656 with_settings :mail_from => "Redmine Mailer<no-reply@redmine.org>" do
656 with_settings :mail_from => "Redmine Mailer<no-reply@redmine.org>" do
657 assert_match /\Aredmine.issue-\d+\.\d+\.[0-9a-f]+@redmine.org\z/, Mailer.token_for(Issue.generate!)
657 assert_match /\Aredmine.issue-\d+\.\d+\.[0-9a-f]+@redmine.org\z/, Mailer.token_for(Issue.generate!)
658 end
658 end
659 end
659 end
660
660
661 def test_layout_should_include_the_emails_header
661 def test_layout_should_include_the_emails_header
662 with_settings :emails_header => "*Header content*" do
662 with_settings :emails_header => "*Header content*" do
663 with_settings :plain_text_mail => 0 do
663 with_settings :plain_text_mail => 0 do
664 assert Mailer.test_email(User.find(1)).deliver
664 assert Mailer.test_email(User.find(1)).deliver
665 assert_select_email do
665 assert_select_email do
666 assert_select ".header" do
666 assert_select ".header" do
667 assert_select "strong", :text => "Header content"
667 assert_select "strong", :text => "Header content"
668 end
668 end
669 end
669 end
670 end
670 end
671 with_settings :plain_text_mail => 1 do
671 with_settings :plain_text_mail => 1 do
672 assert Mailer.test_email(User.find(1)).deliver
672 assert Mailer.test_email(User.find(1)).deliver
673 mail = last_email
673 mail = last_email
674 assert_not_nil mail
674 assert_not_nil mail
675 assert_include "*Header content*", mail.body.decoded
675 assert_include "*Header content*", mail.body.decoded
676 end
676 end
677 end
677 end
678 end
678 end
679
679
680 def test_layout_should_not_include_empty_emails_header
680 def test_layout_should_not_include_empty_emails_header
681 with_settings :emails_header => "", :plain_text_mail => 0 do
681 with_settings :emails_header => "", :plain_text_mail => 0 do
682 assert Mailer.test_email(User.find(1)).deliver
682 assert Mailer.test_email(User.find(1)).deliver
683 assert_select_email do
683 assert_select_email do
684 assert_select ".header", false
684 assert_select ".header", false
685 end
685 end
686 end
686 end
687 end
687 end
688
688
689 def test_layout_should_include_the_emails_footer
689 def test_layout_should_include_the_emails_footer
690 with_settings :emails_footer => "*Footer content*" do
690 with_settings :emails_footer => "*Footer content*" do
691 with_settings :plain_text_mail => 0 do
691 with_settings :plain_text_mail => 0 do
692 assert Mailer.test_email(User.find(1)).deliver
692 assert Mailer.test_email(User.find(1)).deliver
693 assert_select_email do
693 assert_select_email do
694 assert_select ".footer" do
694 assert_select ".footer" do
695 assert_select "strong", :text => "Footer content"
695 assert_select "strong", :text => "Footer content"
696 end
696 end
697 end
697 end
698 end
698 end
699 with_settings :plain_text_mail => 1 do
699 with_settings :plain_text_mail => 1 do
700 assert Mailer.test_email(User.find(1)).deliver
700 assert Mailer.test_email(User.find(1)).deliver
701 mail = last_email
701 mail = last_email
702 assert_not_nil mail
702 assert_not_nil mail
703 assert_include "\n-- \n", mail.body.decoded
703 assert_include "\n-- \n", mail.body.decoded
704 assert_include "*Footer content*", mail.body.decoded
704 assert_include "*Footer content*", mail.body.decoded
705 end
705 end
706 end
706 end
707 end
707 end
708
708
709 def test_layout_should_not_include_empty_emails_footer
709 def test_layout_should_not_include_empty_emails_footer
710 with_settings :emails_footer => "" do
710 with_settings :emails_footer => "" do
711 with_settings :plain_text_mail => 0 do
711 with_settings :plain_text_mail => 0 do
712 assert Mailer.test_email(User.find(1)).deliver
712 assert Mailer.test_email(User.find(1)).deliver
713 assert_select_email do
713 assert_select_email do
714 assert_select ".footer", false
714 assert_select ".footer", false
715 end
715 end
716 end
716 end
717 with_settings :plain_text_mail => 1 do
717 with_settings :plain_text_mail => 1 do
718 assert Mailer.test_email(User.find(1)).deliver
718 assert Mailer.test_email(User.find(1)).deliver
719 mail = last_email
719 mail = last_email
720 assert_not_nil mail
720 assert_not_nil mail
721 assert_not_include "\n-- \n", mail.body.decoded
721 assert_not_include "\n-- \n", mail.body.decoded
722 end
722 end
723 end
723 end
724 end
724 end
725
725
726 def test_should_escape_html_templates_only
726 def test_should_escape_html_templates_only
727 Issue.generate!(:project_id => 1, :tracker_id => 1, :subject => 'Subject with a <tag>')
727 Issue.generate!(:project_id => 1, :tracker_id => 1, :subject => 'Subject with a <tag>')
728 mail = last_email
728 mail = last_email
729 assert_equal 2, mail.parts.size
729 assert_equal 2, mail.parts.size
730 assert_include '<tag>', text_part.body.encoded
730 assert_include '<tag>', text_part.body.encoded
731 assert_include '&lt;tag&gt;', html_part.body.encoded
731 assert_include '&lt;tag&gt;', html_part.body.encoded
732 end
732 end
733
733
734 def test_should_raise_delivery_errors_when_raise_delivery_errors_is_true
734 def test_should_raise_delivery_errors_when_raise_delivery_errors_is_true
735 mail = Mailer.test_email(User.find(1))
735 mail = Mailer.test_email(User.find(1))
736 mail.delivery_method.stubs(:deliver!).raises(Exception.new("delivery error"))
736 mail.delivery_method.stubs(:deliver!).raises(Exception.new("delivery error"))
737
737
738 ActionMailer::Base.raise_delivery_errors = true
738 ActionMailer::Base.raise_delivery_errors = true
739 assert_raise Exception, "delivery error" do
739 assert_raise Exception, "delivery error" do
740 mail.deliver
740 mail.deliver
741 end
741 end
742 ensure
742 ensure
743 ActionMailer::Base.raise_delivery_errors = false
743 ActionMailer::Base.raise_delivery_errors = false
744 end
744 end
745
745
746 def test_should_log_delivery_errors_when_raise_delivery_errors_is_false
746 def test_should_log_delivery_errors_when_raise_delivery_errors_is_false
747 mail = Mailer.test_email(User.find(1))
747 mail = Mailer.test_email(User.find(1))
748 mail.delivery_method.stubs(:deliver!).raises(Exception.new("delivery error"))
748 mail.delivery_method.stubs(:deliver!).raises(Exception.new("delivery error"))
749
749
750 Rails.logger.expects(:error).with("Email delivery error: delivery error")
750 Rails.logger.expects(:error).with("Email delivery error: delivery error")
751 ActionMailer::Base.raise_delivery_errors = false
751 ActionMailer::Base.raise_delivery_errors = false
752 assert_nothing_raised do
752 assert_nothing_raised do
753 mail.deliver
753 mail.deliver
754 end
754 end
755 end
755 end
756
756
757 def test_mail_should_return_a_mail_message
757 def test_mail_should_return_a_mail_message
758 assert_kind_of ::Mail::Message, Mailer.test_email(User.find(1))
758 assert_kind_of ::Mail::Message, Mailer.test_email(User.find(1))
759 end
759 end
760
760
761 def test_with_synched_deliveries_should_yield_with_synced_deliveries
762 ActionMailer::Base.delivery_method = :async_smtp
763 ActionMailer::Base.async_smtp_settings = {:foo => 'bar'}
764
765 Mailer.with_synched_deliveries do
766 assert_equal :smtp, ActionMailer::Base.delivery_method
767 assert_equal({:foo => 'bar'}, ActionMailer::Base.smtp_settings)
768 end
769 assert_equal :async_smtp, ActionMailer::Base.delivery_method
770 ensure
771 ActionMailer::Base.delivery_method = :test
772 end
773
761 private
774 private
762
775
763 def last_email
776 def last_email
764 mail = ActionMailer::Base.deliveries.last
777 mail = ActionMailer::Base.deliveries.last
765 assert_not_nil mail
778 assert_not_nil mail
766 mail
779 mail
767 end
780 end
768
781
769 def text_part
782 def text_part
770 last_email.parts.detect {|part| part.content_type.include?('text/plain')}
783 last_email.parts.detect {|part| part.content_type.include?('text/plain')}
771 end
784 end
772
785
773 def html_part
786 def html_part
774 last_email.parts.detect {|part| part.content_type.include?('text/html')}
787 last_email.parts.detect {|part| part.content_type.include?('text/html')}
775 end
788 end
776 end
789 end
General Comments 0
You need to be logged in to leave comments. Login now