comparison .svn/pristine/9c/9cd7e94ac195fc7d935cfe5b519a4f1a68e73a19.svn-base @ 909:cbb26bc654de redmine-1.3

Update to Redmine 1.3-stable branch (Redmine SVN rev 8964)
author Chris Cannam
date Fri, 24 Feb 2012 19:09:32 +0000
parents
children
comparison
equal deleted inserted replaced
908:c6c2cbd0afee 909:cbb26bc654de
1 # encoding: utf-8
2 #
3 # Redmine - project management software
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
5 #
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
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
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
20 require File.expand_path('../../test_helper', __FILE__)
21
22 class MailHandlerTest < ActiveSupport::TestCase
23 fixtures :users, :projects,
24 :enabled_modules,
25 :roles,
26 :members,
27 :member_roles,
28 :users,
29 :issues,
30 :issue_statuses,
31 :workflows,
32 :trackers,
33 :projects_trackers,
34 :versions,
35 :enumerations,
36 :issue_categories,
37 :custom_fields,
38 :custom_fields_trackers,
39 :custom_fields_projects,
40 :boards,
41 :messages
42
43 FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures/mail_handler'
44
45 def setup
46 ActionMailer::Base.deliveries.clear
47 Setting.notified_events = Redmine::Notifiable.all.collect(&:name)
48 end
49
50 def test_add_issue
51 ActionMailer::Base.deliveries.clear
52 # This email contains: 'Project: onlinestore'
53 issue = submit_email('ticket_on_given_project.eml')
54 assert issue.is_a?(Issue)
55 assert !issue.new_record?
56 issue.reload
57 assert_equal Project.find(2), issue.project
58 assert_equal issue.project.trackers.first, issue.tracker
59 assert_equal 'New ticket on a given project', issue.subject
60 assert_equal User.find_by_login('jsmith'), issue.author
61 assert_equal IssueStatus.find_by_name('Resolved'), issue.status
62 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
63 assert_equal '2010-01-01', issue.start_date.to_s
64 assert_equal '2010-12-31', issue.due_date.to_s
65 assert_equal User.find_by_login('jsmith'), issue.assigned_to
66 assert_equal Version.find_by_name('Alpha'), issue.fixed_version
67 assert_equal 2.5, issue.estimated_hours
68 assert_equal 30, issue.done_ratio
69 assert_equal [issue.id, 1, 2], [issue.root_id, issue.lft, issue.rgt]
70 # keywords should be removed from the email body
71 assert !issue.description.match(/^Project:/i)
72 assert !issue.description.match(/^Status:/i)
73 assert !issue.description.match(/^Start Date:/i)
74 # Email notification should be sent
75 mail = ActionMailer::Base.deliveries.last
76 assert_not_nil mail
77 assert mail.subject.include?('New ticket on a given project')
78 end
79
80 def test_add_issue_with_default_tracker
81 # This email contains: 'Project: onlinestore'
82 issue = submit_email('ticket_on_given_project.eml', :issue => {:tracker => 'Support request'})
83 assert issue.is_a?(Issue)
84 assert !issue.new_record?
85 issue.reload
86 assert_equal 'Support request', issue.tracker.name
87 end
88
89 def test_add_issue_with_status
90 # This email contains: 'Project: onlinestore' and 'Status: Resolved'
91 issue = submit_email('ticket_on_given_project.eml')
92 assert issue.is_a?(Issue)
93 assert !issue.new_record?
94 issue.reload
95 assert_equal Project.find(2), issue.project
96 assert_equal IssueStatus.find_by_name("Resolved"), issue.status
97 end
98
99 def test_add_issue_with_attributes_override
100 issue = submit_email('ticket_with_attributes.eml', :allow_override => 'tracker,category,priority')
101 assert issue.is_a?(Issue)
102 assert !issue.new_record?
103 issue.reload
104 assert_equal 'New ticket on a given project', issue.subject
105 assert_equal User.find_by_login('jsmith'), issue.author
106 assert_equal Project.find(2), issue.project
107 assert_equal 'Feature request', issue.tracker.to_s
108 assert_equal 'Stock management', issue.category.to_s
109 assert_equal 'Urgent', issue.priority.to_s
110 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
111 end
112
113 def test_add_issue_with_group_assignment
114 with_settings :issue_group_assignment => '1' do
115 issue = submit_email('ticket_on_given_project.eml') do |email|
116 email.gsub!('Assigned to: John Smith', 'Assigned to: B Team')
117 end
118 assert issue.is_a?(Issue)
119 assert !issue.new_record?
120 issue.reload
121 assert_equal Group.find(11), issue.assigned_to
122 end
123 end
124
125 def test_add_issue_with_partial_attributes_override
126 issue = submit_email('ticket_with_attributes.eml', :issue => {:priority => 'High'}, :allow_override => ['tracker'])
127 assert issue.is_a?(Issue)
128 assert !issue.new_record?
129 issue.reload
130 assert_equal 'New ticket on a given project', issue.subject
131 assert_equal User.find_by_login('jsmith'), issue.author
132 assert_equal Project.find(2), issue.project
133 assert_equal 'Feature request', issue.tracker.to_s
134 assert_nil issue.category
135 assert_equal 'High', issue.priority.to_s
136 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
137 end
138
139 def test_add_issue_with_spaces_between_attribute_and_separator
140 issue = submit_email('ticket_with_spaces_between_attribute_and_separator.eml', :allow_override => 'tracker,category,priority')
141 assert issue.is_a?(Issue)
142 assert !issue.new_record?
143 issue.reload
144 assert_equal 'New ticket on a given project', issue.subject
145 assert_equal User.find_by_login('jsmith'), issue.author
146 assert_equal Project.find(2), issue.project
147 assert_equal 'Feature request', issue.tracker.to_s
148 assert_equal 'Stock management', issue.category.to_s
149 assert_equal 'Urgent', issue.priority.to_s
150 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
151 end
152
153 def test_add_issue_with_attachment_to_specific_project
154 issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
155 assert issue.is_a?(Issue)
156 assert !issue.new_record?
157 issue.reload
158 assert_equal 'Ticket created by email with attachment', issue.subject
159 assert_equal User.find_by_login('jsmith'), issue.author
160 assert_equal Project.find(2), issue.project
161 assert_equal 'This is a new ticket with attachments', issue.description
162 # Attachment properties
163 assert_equal 1, issue.attachments.size
164 assert_equal 'Paella.jpg', issue.attachments.first.filename
165 assert_equal 'image/jpeg', issue.attachments.first.content_type
166 assert_equal 10790, issue.attachments.first.filesize
167 end
168
169 def test_add_issue_with_custom_fields
170 issue = submit_email('ticket_with_custom_fields.eml', :issue => {:project => 'onlinestore'})
171 assert issue.is_a?(Issue)
172 assert !issue.new_record?
173 issue.reload
174 assert_equal 'New ticket with custom field values', issue.subject
175 assert_equal 'Value for a custom field', issue.custom_value_for(CustomField.find_by_name('Searchable field')).value
176 assert !issue.description.match(/^searchable field:/i)
177 end
178
179 def test_add_issue_with_cc
180 issue = submit_email('ticket_with_cc.eml', :issue => {:project => 'ecookbook'})
181 assert issue.is_a?(Issue)
182 assert !issue.new_record?
183 issue.reload
184 assert issue.watched_by?(User.find_by_mail('dlopper@somenet.foo'))
185 assert_equal 1, issue.watcher_user_ids.size
186 end
187
188 def test_add_issue_by_unknown_user
189 assert_no_difference 'User.count' do
190 assert_equal false, submit_email('ticket_by_unknown_user.eml', :issue => {:project => 'ecookbook'})
191 end
192 end
193
194 def test_add_issue_by_anonymous_user
195 Role.anonymous.add_permission!(:add_issues)
196 assert_no_difference 'User.count' do
197 issue = submit_email('ticket_by_unknown_user.eml', :issue => {:project => 'ecookbook'}, :unknown_user => 'accept')
198 assert issue.is_a?(Issue)
199 assert issue.author.anonymous?
200 end
201 end
202
203 def test_add_issue_by_anonymous_user_with_no_from_address
204 Role.anonymous.add_permission!(:add_issues)
205 assert_no_difference 'User.count' do
206 issue = submit_email('ticket_by_empty_user.eml', :issue => {:project => 'ecookbook'}, :unknown_user => 'accept')
207 assert issue.is_a?(Issue)
208 assert issue.author.anonymous?
209 end
210 end
211
212 def test_add_issue_by_anonymous_user_on_private_project
213 Role.anonymous.add_permission!(:add_issues)
214 assert_no_difference 'User.count' do
215 assert_no_difference 'Issue.count' do
216 assert_equal false, submit_email('ticket_by_unknown_user.eml', :issue => {:project => 'onlinestore'}, :unknown_user => 'accept')
217 end
218 end
219 end
220
221 def test_add_issue_by_anonymous_user_on_private_project_without_permission_check
222 assert_no_difference 'User.count' do
223 assert_difference 'Issue.count' do
224 issue = submit_email('ticket_by_unknown_user.eml', :issue => {:project => 'onlinestore'}, :no_permission_check => '1', :unknown_user => 'accept')
225 assert issue.is_a?(Issue)
226 assert issue.author.anonymous?
227 assert !issue.project.is_public?
228 assert_equal [issue.id, 1, 2], [issue.root_id, issue.lft, issue.rgt]
229 end
230 end
231 end
232
233 def test_add_issue_by_created_user
234 Setting.default_language = 'en'
235 assert_difference 'User.count' do
236 issue = submit_email('ticket_by_unknown_user.eml', :issue => {:project => 'ecookbook'}, :unknown_user => 'create')
237 assert issue.is_a?(Issue)
238 assert issue.author.active?
239 assert_equal 'john.doe@somenet.foo', issue.author.mail
240 assert_equal 'John', issue.author.firstname
241 assert_equal 'Doe', issue.author.lastname
242
243 # account information
244 email = ActionMailer::Base.deliveries.first
245 assert_not_nil email
246 assert email.subject.include?('account activation')
247 login = email.body.match(/\* Login: (.*)$/)[1]
248 password = email.body.match(/\* Password: (.*)$/)[1]
249 assert_equal issue.author, User.try_to_login(login, password)
250 end
251 end
252
253 def test_add_issue_without_from_header
254 Role.anonymous.add_permission!(:add_issues)
255 assert_equal false, submit_email('ticket_without_from_header.eml')
256 end
257
258 def test_add_issue_with_invalid_attributes
259 issue = submit_email('ticket_with_invalid_attributes.eml', :allow_override => 'tracker,category,priority')
260 assert issue.is_a?(Issue)
261 assert !issue.new_record?
262 issue.reload
263 assert_nil issue.assigned_to
264 assert_nil issue.start_date
265 assert_nil issue.due_date
266 assert_equal 0, issue.done_ratio
267 assert_equal 'Normal', issue.priority.to_s
268 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
269 end
270
271 def test_add_issue_with_localized_attributes
272 User.find_by_mail('jsmith@somenet.foo').update_attribute 'language', 'fr'
273 issue = submit_email('ticket_with_localized_attributes.eml', :allow_override => 'tracker,category,priority')
274 assert issue.is_a?(Issue)
275 assert !issue.new_record?
276 issue.reload
277 assert_equal 'New ticket on a given project', issue.subject
278 assert_equal User.find_by_login('jsmith'), issue.author
279 assert_equal Project.find(2), issue.project
280 assert_equal 'Feature request', issue.tracker.to_s
281 assert_equal 'Stock management', issue.category.to_s
282 assert_equal 'Urgent', issue.priority.to_s
283 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
284 end
285
286 def test_add_issue_with_japanese_keywords
287 tracker = Tracker.create!(:name => '開発')
288 Project.find(1).trackers << tracker
289 issue = submit_email('japanese_keywords_iso_2022_jp.eml', :issue => {:project => 'ecookbook'}, :allow_override => 'tracker')
290 assert_kind_of Issue, issue
291 assert_equal tracker, issue.tracker
292 end
293
294 def test_add_issue_from_apple_mail
295 issue = submit_email('apple_mail_with_attachment.eml', :issue => {:project => 'ecookbook'})
296 assert_kind_of Issue, issue
297 assert_equal 1, issue.attachments.size
298
299 attachment = issue.attachments.first
300 assert_equal 'paella.jpg', attachment.filename
301 assert_equal 10790, attachment.filesize
302 end
303
304 def test_should_ignore_emails_from_emission_address
305 Role.anonymous.add_permission!(:add_issues)
306 assert_no_difference 'User.count' do
307 assert_equal false, submit_email('ticket_from_emission_address.eml', :issue => {:project => 'ecookbook'}, :unknown_user => 'create')
308 end
309 end
310
311 def test_add_issue_should_send_email_notification
312 Setting.notified_events = ['issue_added']
313 ActionMailer::Base.deliveries.clear
314 # This email contains: 'Project: onlinestore'
315 issue = submit_email('ticket_on_given_project.eml')
316 assert issue.is_a?(Issue)
317 assert_equal 1, ActionMailer::Base.deliveries.size
318 end
319
320 def test_update_issue
321 journal = submit_email('ticket_reply.eml')
322 assert journal.is_a?(Journal)
323 assert_equal User.find_by_login('jsmith'), journal.user
324 assert_equal Issue.find(2), journal.journalized
325 assert_match /This is reply/, journal.notes
326 assert_equal 'Feature request', journal.issue.tracker.name
327 end
328
329 def test_update_issue_with_attribute_changes
330 # This email contains: 'Status: Resolved'
331 journal = submit_email('ticket_reply_with_status.eml')
332 assert journal.is_a?(Journal)
333 issue = Issue.find(journal.issue.id)
334 assert_equal User.find_by_login('jsmith'), journal.user
335 assert_equal Issue.find(2), journal.journalized
336 assert_match /This is reply/, journal.notes
337 assert_equal 'Feature request', journal.issue.tracker.name
338 assert_equal IssueStatus.find_by_name("Resolved"), issue.status
339 assert_equal '2010-01-01', issue.start_date.to_s
340 assert_equal '2010-12-31', issue.due_date.to_s
341 assert_equal User.find_by_login('jsmith'), issue.assigned_to
342 assert_equal "52.6", issue.custom_value_for(CustomField.find_by_name('Float field')).value
343 # keywords should be removed from the email body
344 assert !journal.notes.match(/^Status:/i)
345 assert !journal.notes.match(/^Start Date:/i)
346 end
347
348 def test_update_issue_with_attachment
349 assert_difference 'Journal.count' do
350 assert_difference 'JournalDetail.count' do
351 assert_difference 'Attachment.count' do
352 assert_no_difference 'Issue.count' do
353 journal = submit_email('ticket_with_attachment.eml') do |raw|
354 raw.gsub! /^Subject: .*$/, 'Subject: Re: [Cookbook - Feature #2] (New) Add ingredients categories'
355 end
356 end
357 end
358 end
359 end
360 journal = Journal.first(:order => 'id DESC')
361 assert_equal Issue.find(2), journal.journalized
362 assert_equal 1, journal.details.size
363
364 detail = journal.details.first
365 assert_equal 'attachment', detail.property
366 assert_equal 'Paella.jpg', detail.value
367 end
368
369 def test_update_issue_should_send_email_notification
370 ActionMailer::Base.deliveries.clear
371 journal = submit_email('ticket_reply.eml')
372 assert journal.is_a?(Journal)
373 assert_equal 1, ActionMailer::Base.deliveries.size
374 end
375
376 def test_update_issue_should_not_set_defaults
377 journal = submit_email('ticket_reply.eml', :issue => {:tracker => 'Support request', :priority => 'High'})
378 assert journal.is_a?(Journal)
379 assert_match /This is reply/, journal.notes
380 assert_equal 'Feature request', journal.issue.tracker.name
381 assert_equal 'Normal', journal.issue.priority.name
382 end
383
384 def test_reply_to_a_message
385 m = submit_email('message_reply.eml')
386 assert m.is_a?(Message)
387 assert !m.new_record?
388 m.reload
389 assert_equal 'Reply via email', m.subject
390 # The email replies to message #2 which is part of the thread of message #1
391 assert_equal Message.find(1), m.parent
392 end
393
394 def test_reply_to_a_message_by_subject
395 m = submit_email('message_reply_by_subject.eml')
396 assert m.is_a?(Message)
397 assert !m.new_record?
398 m.reload
399 assert_equal 'Reply to the first post', m.subject
400 assert_equal Message.find(1), m.parent
401 end
402
403 def test_should_strip_tags_of_html_only_emails
404 issue = submit_email('ticket_html_only.eml', :issue => {:project => 'ecookbook'})
405 assert issue.is_a?(Issue)
406 assert !issue.new_record?
407 issue.reload
408 assert_equal 'HTML email', issue.subject
409 assert_equal 'This is a html-only email.', issue.description
410 end
411
412 context "truncate emails based on the Setting" do
413 context "with no setting" do
414 setup do
415 Setting.mail_handler_body_delimiters = ''
416 end
417
418 should "add the entire email into the issue" do
419 issue = submit_email('ticket_on_given_project.eml')
420 assert_issue_created(issue)
421 assert issue.description.include?('---')
422 assert issue.description.include?('This paragraph is after the delimiter')
423 end
424 end
425
426 context "with a single string" do
427 setup do
428 Setting.mail_handler_body_delimiters = '---'
429 end
430 should "truncate the email at the delimiter for the issue" do
431 issue = submit_email('ticket_on_given_project.eml')
432 assert_issue_created(issue)
433 assert issue.description.include?('This paragraph is before delimiters')
434 assert issue.description.include?('--- This line starts with a delimiter')
435 assert !issue.description.match(/^---$/)
436 assert !issue.description.include?('This paragraph is after the delimiter')
437 end
438 end
439
440 context "with a single quoted reply (e.g. reply to a Redmine email notification)" do
441 setup do
442 Setting.mail_handler_body_delimiters = '--- Reply above. Do not remove this line. ---'
443 end
444 should "truncate the email at the delimiter with the quoted reply symbols (>)" do
445 journal = submit_email('issue_update_with_quoted_reply_above.eml')
446 assert journal.is_a?(Journal)
447 assert journal.notes.include?('An update to the issue by the sender.')
448 assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---"))
449 assert !journal.notes.include?('Looks like the JSON api for projects was missed.')
450 end
451 end
452
453 context "with multiple quoted replies (e.g. reply to a reply of a Redmine email notification)" do
454 setup do
455 Setting.mail_handler_body_delimiters = '--- Reply above. Do not remove this line. ---'
456 end
457 should "truncate the email at the delimiter with the quoted reply symbols (>)" do
458 journal = submit_email('issue_update_with_multiple_quoted_reply_above.eml')
459 assert journal.is_a?(Journal)
460 assert journal.notes.include?('An update to the issue by the sender.')
461 assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---"))
462 assert !journal.notes.include?('Looks like the JSON api for projects was missed.')
463 end
464 end
465
466 context "with multiple strings" do
467 setup do
468 Setting.mail_handler_body_delimiters = "---\nBREAK"
469 end
470 should "truncate the email at the first delimiter found (BREAK)" do
471 issue = submit_email('ticket_on_given_project.eml')
472 assert_issue_created(issue)
473 assert issue.description.include?('This paragraph is before delimiters')
474 assert !issue.description.include?('BREAK')
475 assert !issue.description.include?('This paragraph is between delimiters')
476 assert !issue.description.match(/^---$/)
477 assert !issue.description.include?('This paragraph is after the delimiter')
478 end
479 end
480 end
481
482 def test_email_with_long_subject_line
483 issue = submit_email('ticket_with_long_subject.eml')
484 assert issue.is_a?(Issue)
485 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]
486 end
487
488 def test_new_user_from_attributes_should_return_valid_user
489 to_test = {
490 # [address, name] => [login, firstname, lastname]
491 ['jsmith@example.net', nil] => ['jsmith@example.net', 'jsmith', '-'],
492 ['jsmith@example.net', 'John'] => ['jsmith@example.net', 'John', '-'],
493 ['jsmith@example.net', 'John Smith'] => ['jsmith@example.net', 'John', 'Smith'],
494 ['jsmith@example.net', 'John Paul Smith'] => ['jsmith@example.net', 'John', 'Paul Smith'],
495 ['jsmith@example.net', 'AVeryLongFirstnameThatExceedsTheMaximumLength Smith'] => ['jsmith@example.net', 'AVeryLongFirstnameThatExceedsT', 'Smith'],
496 ['jsmith@example.net', 'John AVeryLongLastnameThatExceedsTheMaximumLength'] => ['jsmith@example.net', 'John', 'AVeryLongLastnameThatExceedsTh'],
497 ['alongemailaddressthatexceedsloginlength@example.net', 'John Smith'] => ['alongemailaddressthatexceedslo', 'John', 'Smith']
498 }
499
500 to_test.each do |attrs, expected|
501 user = MailHandler.new_user_from_attributes(attrs.first, attrs.last)
502
503 assert user.valid?
504 assert_equal attrs.first, user.mail
505 assert_equal expected[0], user.login
506 assert_equal expected[1], user.firstname
507 assert_equal expected[2], user.lastname
508 end
509 end
510
511 def test_new_user_from_attributes_should_respect_minimum_password_length
512 with_settings :password_min_length => 15 do
513 user = MailHandler.new_user_from_attributes('jsmith@example.net')
514 assert user.valid?
515 assert user.password.length >= 15
516 end
517 end
518
519 def test_new_user_from_attributes_should_use_default_login_if_invalid
520 MailHandler.new_user_from_attributes('alongemailaddressthatexceedsloginlength-1@example.net').save!
521
522 # another long address that would result in duplicate login
523 user = MailHandler.new_user_from_attributes('alongemailaddressthatexceedsloginlength-2@example.net')
524 assert user.valid?
525 assert user.login =~ /^user[a-f0-9]+$/
526 end
527
528 private
529
530 def submit_email(filename, options={})
531 raw = IO.read(File.join(FIXTURES_PATH, filename))
532 yield raw if block_given?
533 MailHandler.receive(raw, options)
534 end
535
536 def assert_issue_created(issue)
537 assert issue.is_a?(Issue)
538 assert !issue.new_record?
539 issue.reload
540 end
541 end