##// END OF EJS Templates
When creating issues by receiving an email, watchers created via CC in the mail don't get an email notification (#23278)....
Jean-Philippe Lang -
r15227:f416657d5635
parent child
Show More
@@ -1,88 +1,92
1 1 # ActsAsWatchable
2 2 module Redmine
3 3 module Acts
4 4 module Watchable
5 5 def self.included(base)
6 6 base.extend ClassMethods
7 7 end
8 8
9 9 module ClassMethods
10 10 def acts_as_watchable(options = {})
11 11 return if self.included_modules.include?(Redmine::Acts::Watchable::InstanceMethods)
12 12 class_eval do
13 13 has_many :watchers, :as => :watchable, :dependent => :delete_all
14 14 has_many :watcher_users, :through => :watchers, :source => :user, :validate => false
15 15
16 16 scope :watched_by, lambda { |user_id|
17 17 joins(:watchers).
18 18 where("#{Watcher.table_name}.user_id = ?", user_id)
19 19 }
20 20 attr_protected :watcher_ids, :watcher_user_ids
21 21 end
22 22 send :include, Redmine::Acts::Watchable::InstanceMethods
23 23 alias_method_chain :watcher_user_ids=, :uniq_ids
24 24 end
25 25 end
26 26
27 27 module InstanceMethods
28 28 def self.included(base)
29 29 base.extend ClassMethods
30 30 end
31 31
32 32 # Returns an array of users that are proposed as watchers
33 33 def addable_watcher_users
34 34 users = self.project.users.sort - self.watcher_users
35 35 if respond_to?(:visible?)
36 36 users.reject! {|user| !visible?(user)}
37 37 end
38 38 users
39 39 end
40 40
41 41 # Adds user as a watcher
42 42 def add_watcher(user)
43 # Rails does not reset the has_many :through association
44 watcher_users.reset
43 45 self.watchers << Watcher.new(:user => user)
44 46 end
45 47
46 48 # Removes user from the watchers list
47 49 def remove_watcher(user)
48 50 return nil unless user && user.is_a?(User)
51 # Rails does not reset the has_many :through association
52 watcher_users.reset
49 53 watchers.where(:user_id => user.id).delete_all
50 54 end
51 55
52 56 # Adds/removes watcher
53 57 def set_watcher(user, watching=true)
54 58 watching ? add_watcher(user) : remove_watcher(user)
55 59 end
56 60
57 61 # Overrides watcher_user_ids= to make user_ids uniq
58 62 def watcher_user_ids_with_uniq_ids=(user_ids)
59 63 if user_ids.is_a?(Array)
60 64 user_ids = user_ids.uniq
61 65 end
62 66 send :watcher_user_ids_without_uniq_ids=, user_ids
63 67 end
64 68
65 69 # Returns true if object is watched by +user+
66 70 def watched_by?(user)
67 71 !!(user && self.watcher_user_ids.detect {|uid| uid == user.id })
68 72 end
69 73
70 74 def notified_watchers
71 75 notified = watcher_users.active.to_a
72 76 notified.reject! {|user| user.mail.blank? || user.mail_notification == 'none'}
73 77 if respond_to?(:visible?)
74 78 notified.reject! {|user| !visible?(user)}
75 79 end
76 80 notified
77 81 end
78 82
79 83 # Returns an array of watchers' email addresses
80 84 def watcher_recipients
81 85 notified_watchers.collect(&:mail)
82 86 end
83 87
84 88 module ClassMethods; end
85 89 end
86 90 end
87 91 end
88 92 end
@@ -1,1095 +1,1096
1 1 # encoding: utf-8
2 2 #
3 3 # Redmine - project management software
4 4 # Copyright (C) 2006-2016 Jean-Philippe Lang
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; either version 2
9 9 # of the License, or (at your option) any later version.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 19
20 20 require File.expand_path('../../test_helper', __FILE__)
21 21
22 22 class MailHandlerTest < ActiveSupport::TestCase
23 23 fixtures :users, :projects, :enabled_modules, :roles,
24 24 :members, :member_roles, :users,
25 25 :email_addresses, :user_preferences,
26 26 :issues, :issue_statuses,
27 27 :workflows, :trackers, :projects_trackers,
28 28 :versions, :enumerations, :issue_categories,
29 29 :custom_fields, :custom_fields_trackers, :custom_fields_projects,
30 30 :boards, :messages
31 31
32 32 FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures/mail_handler'
33 33
34 34 def setup
35 35 ActionMailer::Base.deliveries.clear
36 36 Setting.notified_events = Redmine::Notifiable.all.collect(&:name)
37 37 end
38 38
39 39 def teardown
40 40 Setting.clear_cache
41 41 end
42 42
43 43 def test_add_issue_with_specific_overrides
44 44 issue = submit_email('ticket_on_given_project.eml',
45 45 :allow_override => ['status', 'start_date', 'due_date', 'assigned_to', 'fixed_version', 'estimated_hours', 'done_ratio']
46 46 )
47 47 assert issue.is_a?(Issue)
48 48 assert !issue.new_record?
49 49 issue.reload
50 50 assert_equal Project.find(2), issue.project
51 51 assert_equal issue.project.trackers.first, issue.tracker
52 52 assert_equal 'New ticket on a given project', issue.subject
53 53 assert_equal User.find_by_login('jsmith'), issue.author
54 54 assert_equal IssueStatus.find_by_name('Resolved'), issue.status
55 55 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
56 56 assert_equal '2010-01-01', issue.start_date.to_s
57 57 assert_equal '2010-12-31', issue.due_date.to_s
58 58 assert_equal User.find_by_login('jsmith'), issue.assigned_to
59 59 assert_equal Version.find_by_name('Alpha'), issue.fixed_version
60 60 assert_equal 2.5, issue.estimated_hours
61 61 assert_equal 30, issue.done_ratio
62 62 # keywords should be removed from the email body
63 63 assert !issue.description.match(/^Project:/i)
64 64 assert !issue.description.match(/^Status:/i)
65 65 assert !issue.description.match(/^Start Date:/i)
66 66 end
67 67
68 68 def test_add_issue_with_all_overrides
69 69 issue = submit_email('ticket_on_given_project.eml', :allow_override => 'all')
70 70 assert issue.is_a?(Issue)
71 71 assert !issue.new_record?
72 72 issue.reload
73 73 assert_equal Project.find(2), issue.project
74 74 assert_equal issue.project.trackers.first, issue.tracker
75 75 assert_equal IssueStatus.find_by_name('Resolved'), issue.status
76 76 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
77 77 assert_equal '2010-01-01', issue.start_date.to_s
78 78 assert_equal '2010-12-31', issue.due_date.to_s
79 79 assert_equal User.find_by_login('jsmith'), issue.assigned_to
80 80 assert_equal Version.find_by_name('Alpha'), issue.fixed_version
81 81 assert_equal 2.5, issue.estimated_hours
82 82 assert_equal 30, issue.done_ratio
83 83 end
84 84
85 85 def test_add_issue_without_overrides_should_ignore_attributes
86 86 WorkflowRule.delete_all
87 87 issue = submit_email('ticket_on_given_project.eml')
88 88 assert issue.is_a?(Issue)
89 89 assert !issue.new_record?
90 90 issue.reload
91 91 assert_equal Project.find(2), issue.project
92 92 assert_equal 'New ticket on a given project', issue.subject
93 93 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
94 94 assert_equal User.find_by_login('jsmith'), issue.author
95 95
96 96 assert_equal issue.project.trackers.first, issue.tracker
97 97 assert_equal 'New', issue.status.name
98 98 assert_not_equal '2010-01-01', issue.start_date.to_s
99 99 assert_nil issue.due_date
100 100 assert_nil issue.assigned_to
101 101 assert_nil issue.fixed_version
102 102 assert_nil issue.estimated_hours
103 103 assert_equal 0, issue.done_ratio
104 104 end
105 105
106 106 def test_add_issue_to_project_specified_by_subaddress
107 107 # This email has redmine+onlinestore@somenet.foo as 'To' header
108 108 issue = submit_email(
109 109 'ticket_on_project_given_by_to_header.eml',
110 110 :issue => {:tracker => 'Support request'},
111 111 :project_from_subaddress => 'redmine@somenet.foo'
112 112 )
113 113 assert issue.is_a?(Issue)
114 114 assert !issue.new_record?
115 115 issue.reload
116 116 assert_equal 'onlinestore', issue.project.identifier
117 117 assert_equal 'Support request', issue.tracker.name
118 118 end
119 119
120 120 def test_add_issue_with_default_tracker
121 121 # This email contains: 'Project: onlinestore'
122 122 issue = submit_email(
123 123 'ticket_on_given_project.eml',
124 124 :issue => {:tracker => 'Support request'}
125 125 )
126 126 assert issue.is_a?(Issue)
127 127 assert !issue.new_record?
128 128 issue.reload
129 129 assert_equal 'Support request', issue.tracker.name
130 130 end
131 131
132 132 def test_add_issue_with_default_version
133 133 # This email contains: 'Project: onlinestore'
134 134 issue = submit_email(
135 135 'ticket_on_given_project.eml',
136 136 :issue => {:fixed_version => 'Alpha'}
137 137 )
138 138 assert issue.is_a?(Issue)
139 139 assert !issue.new_record?
140 140 assert_equal 'Alpha', issue.reload.fixed_version.name
141 141 end
142 142
143 143 def test_add_issue_with_default_assigned_to
144 144 # This email contains: 'Project: onlinestore'
145 145 issue = submit_email(
146 146 'ticket_on_given_project.eml',
147 147 :issue => {:assigned_to => 'jsmith'}
148 148 )
149 149 assert issue.is_a?(Issue)
150 150 assert !issue.new_record?
151 151 assert_equal 'jsmith', issue.reload.assigned_to.login
152 152 end
153 153
154 154 def test_add_issue_with_status_override
155 155 # This email contains: 'Project: onlinestore' and 'Status: Resolved'
156 156 issue = submit_email('ticket_on_given_project.eml', :allow_override => ['status'])
157 157 assert issue.is_a?(Issue)
158 158 assert !issue.new_record?
159 159 issue.reload
160 160 assert_equal Project.find(2), issue.project
161 161 assert_equal IssueStatus.find_by_name("Resolved"), issue.status
162 162 end
163 163
164 164 def test_add_issue_should_accept_is_private_attribute
165 165 issue = submit_email('ticket_on_given_project.eml', :issue => {:is_private => '1'})
166 166 assert issue.is_a?(Issue)
167 167 assert !issue.new_record?
168 168 assert_equal true, issue.reload.is_private
169 169 end
170 170
171 171 def test_add_issue_with_group_assignment
172 172 with_settings :issue_group_assignment => '1' do
173 173 issue = submit_email('ticket_on_given_project.eml', :allow_override => ['assigned_to']) do |email|
174 174 email.gsub!('Assigned to: John Smith', 'Assigned to: B Team')
175 175 end
176 176 assert issue.is_a?(Issue)
177 177 assert !issue.new_record?
178 178 issue.reload
179 179 assert_equal Group.find(11), issue.assigned_to
180 180 end
181 181 end
182 182
183 183 def test_add_issue_with_partial_attributes_override
184 184 issue = submit_email(
185 185 'ticket_with_attributes.eml',
186 186 :issue => {:priority => 'High'},
187 187 :allow_override => ['tracker']
188 188 )
189 189 assert issue.is_a?(Issue)
190 190 assert !issue.new_record?
191 191 issue.reload
192 192 assert_equal 'New ticket on a given project', issue.subject
193 193 assert_equal User.find_by_login('jsmith'), issue.author
194 194 assert_equal Project.find(2), issue.project
195 195 assert_equal 'Feature request', issue.tracker.to_s
196 196 assert_nil issue.category
197 197 assert_equal 'High', issue.priority.to_s
198 198 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
199 199 end
200 200
201 201 def test_add_issue_with_spaces_between_attribute_and_separator
202 202 issue = submit_email(
203 203 'ticket_with_spaces_between_attribute_and_separator.eml',
204 204 :allow_override => 'tracker,category,priority'
205 205 )
206 206 assert issue.is_a?(Issue)
207 207 assert !issue.new_record?
208 208 issue.reload
209 209 assert_equal 'New ticket on a given project', issue.subject
210 210 assert_equal User.find_by_login('jsmith'), issue.author
211 211 assert_equal Project.find(2), issue.project
212 212 assert_equal 'Feature request', issue.tracker.to_s
213 213 assert_equal 'Stock management', issue.category.to_s
214 214 assert_equal 'Urgent', issue.priority.to_s
215 215 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
216 216 end
217 217
218 218 def test_add_issue_with_attachment_to_specific_project
219 219 issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
220 220 assert issue.is_a?(Issue)
221 221 assert !issue.new_record?
222 222 issue.reload
223 223 assert_equal 'Ticket created by email with attachment', issue.subject
224 224 assert_equal User.find_by_login('jsmith'), issue.author
225 225 assert_equal Project.find(2), issue.project
226 226 assert_equal 'This is a new ticket with attachments', issue.description
227 227 # Attachment properties
228 228 assert_equal 1, issue.attachments.size
229 229 assert_equal 'Paella.jpg', issue.attachments.first.filename
230 230 assert_equal 'image/jpeg', issue.attachments.first.content_type
231 231 assert_equal 10790, issue.attachments.first.filesize
232 232 end
233 233
234 234 def test_add_issue_with_custom_fields
235 235 issue = submit_email('ticket_with_custom_fields.eml',
236 236 :issue => {:project => 'onlinestore'}, :allow_override => ['database', 'Searchable_field']
237 237 )
238 238 assert issue.is_a?(Issue)
239 239 assert !issue.new_record?
240 240 issue.reload
241 241 assert_equal 'New ticket with custom field values', issue.subject
242 242 assert_equal 'PostgreSQL', issue.custom_field_value(1)
243 243 assert_equal 'Value for a custom field', issue.custom_field_value(2)
244 244 assert !issue.description.match(/^searchable field:/i)
245 245 end
246 246
247 247 def test_add_issue_with_version_custom_fields
248 248 field = IssueCustomField.create!(:name => 'Affected version', :field_format => 'version', :is_for_all => true, :tracker_ids => [1,2,3])
249 249
250 250 issue = submit_email('ticket_with_custom_fields.eml',
251 251 :issue => {:project => 'ecookbook'}, :allow_override => ['affected version']
252 252 ) do |email|
253 253 email << "Affected version: 1.0\n"
254 254 end
255 255 assert issue.is_a?(Issue)
256 256 assert !issue.new_record?
257 257 issue.reload
258 258 assert_equal '2', issue.custom_field_value(field)
259 259 end
260 260
261 261 def test_add_issue_should_match_assignee_on_display_name
262 262 user = User.generate!(:firstname => 'Foo Bar', :lastname => 'Foo Baz')
263 263 User.add_to_project(user, Project.find(2))
264 264 issue = submit_email('ticket_on_given_project.eml', :allow_override => ['assigned_to']) do |email|
265 265 email.sub!(/^Assigned to.*$/, 'Assigned to: Foo Bar Foo baz')
266 266 end
267 267 assert issue.is_a?(Issue)
268 268 assert_equal user, issue.assigned_to
269 269 end
270 270
271 271 def test_add_issue_should_set_default_start_date
272 272 with_settings :default_issue_start_date_to_creation_date => '1' do
273 273 issue = submit_email('ticket_with_cc.eml', :issue => {:project => 'ecookbook'})
274 274 assert issue.is_a?(Issue)
275 275 assert_equal Date.today, issue.start_date
276 276 end
277 277 end
278 278
279 279 def test_add_issue_should_add_cc_as_watchers
280 user = User.find_by_mail('dlopper@somenet.foo')
280 281 issue = submit_email('ticket_with_cc.eml', :issue => {:project => 'ecookbook'})
281 282 assert issue.is_a?(Issue)
282 283 assert !issue.new_record?
283 issue.reload
284 assert issue.watched_by?(User.find_by_mail('dlopper@somenet.foo'))
284 assert issue.watched_by?(user)
285 285 assert_equal 1, issue.watcher_user_ids.size
286 assert_include user, issue.watcher_users.to_a
286 287 end
287 288
288 289 def test_add_issue_from_additional_email_address
289 290 user = User.find(2)
290 291 user.mail = 'mainaddress@somenet.foo'
291 292 user.save!
292 293 EmailAddress.create!(:user => user, :address => 'jsmith@somenet.foo')
293 294
294 295 issue = submit_email('ticket_on_given_project.eml')
295 296 assert issue
296 297 assert_equal user, issue.author
297 298 end
298 299
299 300 def test_add_issue_by_unknown_user
300 301 assert_no_difference 'User.count' do
301 302 assert_equal false,
302 303 submit_email(
303 304 'ticket_by_unknown_user.eml',
304 305 :issue => {:project => 'ecookbook'}
305 306 )
306 307 end
307 308 end
308 309
309 310 def test_add_issue_by_anonymous_user
310 311 Role.anonymous.add_permission!(:add_issues)
311 312 assert_no_difference 'User.count' do
312 313 issue = submit_email(
313 314 'ticket_by_unknown_user.eml',
314 315 :issue => {:project => 'ecookbook'},
315 316 :unknown_user => 'accept'
316 317 )
317 318 assert issue.is_a?(Issue)
318 319 assert issue.author.anonymous?
319 320 end
320 321 end
321 322
322 323 def test_add_issue_by_anonymous_user_with_no_from_address
323 324 Role.anonymous.add_permission!(:add_issues)
324 325 assert_no_difference 'User.count' do
325 326 issue = submit_email(
326 327 'ticket_by_empty_user.eml',
327 328 :issue => {:project => 'ecookbook'},
328 329 :unknown_user => 'accept'
329 330 )
330 331 assert issue.is_a?(Issue)
331 332 assert issue.author.anonymous?
332 333 end
333 334 end
334 335
335 336 def test_add_issue_by_anonymous_user_on_private_project
336 337 Role.anonymous.add_permission!(:add_issues)
337 338 assert_no_difference 'User.count' do
338 339 assert_no_difference 'Issue.count' do
339 340 assert_equal false,
340 341 submit_email(
341 342 'ticket_by_unknown_user.eml',
342 343 :issue => {:project => 'onlinestore'},
343 344 :unknown_user => 'accept'
344 345 )
345 346 end
346 347 end
347 348 end
348 349
349 350 def test_add_issue_by_anonymous_user_on_private_project_without_permission_check
350 351 assert_no_difference 'User.count' do
351 352 assert_difference 'Issue.count' do
352 353 issue = submit_email(
353 354 'ticket_by_unknown_user.eml',
354 355 :issue => {:project => 'onlinestore'},
355 356 :no_permission_check => '1',
356 357 :unknown_user => 'accept'
357 358 )
358 359 assert issue.is_a?(Issue)
359 360 assert issue.author.anonymous?
360 361 assert !issue.project.is_public?
361 362 end
362 363 end
363 364 end
364 365
365 366 def test_add_issue_by_created_user
366 367 Setting.default_language = 'en'
367 368 assert_difference 'User.count' do
368 369 issue = submit_email(
369 370 'ticket_by_unknown_user.eml',
370 371 :issue => {:project => 'ecookbook'},
371 372 :unknown_user => 'create'
372 373 )
373 374 assert issue.is_a?(Issue)
374 375 assert issue.author.active?
375 376 assert_equal 'john.doe@somenet.foo', issue.author.mail
376 377 assert_equal 'John', issue.author.firstname
377 378 assert_equal 'Doe', issue.author.lastname
378 379
379 380 # account information
380 381 email = ActionMailer::Base.deliveries.first
381 382 assert_not_nil email
382 383 assert email.subject.include?('account activation')
383 384 login = mail_body(email).match(/\* Login: (.*)$/)[1].strip
384 385 password = mail_body(email).match(/\* Password: (.*)$/)[1].strip
385 386 assert_equal issue.author, User.try_to_login(login, password)
386 387 end
387 388 end
388 389
389 390 def test_add_issue_should_send_notification
390 391 issue = submit_email('ticket_on_given_project.eml', :allow_override => 'all')
391 392 assert issue.is_a?(Issue)
392 393 assert !issue.new_record?
393 394
394 395 mail = ActionMailer::Base.deliveries.last
395 396 assert_not_nil mail
396 397 assert mail.subject.include?("##{issue.id}")
397 398 assert mail.subject.include?('New ticket on a given project')
398 399 end
399 400
400 401 def test_created_user_should_be_added_to_groups
401 402 group1 = Group.generate!
402 403 group2 = Group.generate!
403 404
404 405 assert_difference 'User.count' do
405 406 submit_email(
406 407 'ticket_by_unknown_user.eml',
407 408 :issue => {:project => 'ecookbook'},
408 409 :unknown_user => 'create',
409 410 :default_group => "#{group1.name},#{group2.name}"
410 411 )
411 412 end
412 413 user = User.order('id DESC').first
413 414 assert_equal [group1, group2].sort, user.groups.sort
414 415 end
415 416
416 417 def test_created_user_should_not_receive_account_information_with_no_account_info_option
417 418 assert_difference 'User.count' do
418 419 submit_email(
419 420 'ticket_by_unknown_user.eml',
420 421 :issue => {:project => 'ecookbook'},
421 422 :unknown_user => 'create',
422 423 :no_account_notice => '1'
423 424 )
424 425 end
425 426
426 427 # only 1 email for the new issue notification
427 428 assert_equal 1, ActionMailer::Base.deliveries.size
428 429 email = ActionMailer::Base.deliveries.first
429 430 assert_include 'Ticket by unknown user', email.subject
430 431 end
431 432
432 433 def test_created_user_should_have_mail_notification_to_none_with_no_notification_option
433 434 assert_difference 'User.count' do
434 435 submit_email(
435 436 'ticket_by_unknown_user.eml',
436 437 :issue => {:project => 'ecookbook'},
437 438 :unknown_user => 'create',
438 439 :no_notification => '1'
439 440 )
440 441 end
441 442 user = User.order('id DESC').first
442 443 assert_equal 'none', user.mail_notification
443 444 end
444 445
445 446 def test_add_issue_without_from_header
446 447 Role.anonymous.add_permission!(:add_issues)
447 448 assert_equal false, submit_email('ticket_without_from_header.eml')
448 449 end
449 450
450 451 def test_add_issue_with_invalid_attributes
451 452 with_settings :default_issue_start_date_to_creation_date => '0' do
452 453 issue = submit_email(
453 454 'ticket_with_invalid_attributes.eml',
454 455 :allow_override => 'tracker,category,priority'
455 456 )
456 457 assert issue.is_a?(Issue)
457 458 assert !issue.new_record?
458 459 issue.reload
459 460 assert_nil issue.assigned_to
460 461 assert_nil issue.start_date
461 462 assert_nil issue.due_date
462 463 assert_equal 0, issue.done_ratio
463 464 assert_equal 'Normal', issue.priority.to_s
464 465 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
465 466 end
466 467 end
467 468
468 469 def test_add_issue_with_invalid_project_should_be_assigned_to_default_project
469 470 issue = submit_email('ticket_on_given_project.eml', :issue => {:project => 'ecookbook'}, :allow_override => 'project') do |email|
470 471 email.gsub!(/^Project:.+$/, 'Project: invalid')
471 472 end
472 473 assert issue.is_a?(Issue)
473 474 assert !issue.new_record?
474 475 assert_equal 'ecookbook', issue.project.identifier
475 476 end
476 477
477 478 def test_add_issue_with_localized_attributes
478 479 User.find_by_mail('jsmith@somenet.foo').update_attribute 'language', 'fr'
479 480 issue = submit_email(
480 481 'ticket_with_localized_attributes.eml',
481 482 :allow_override => 'tracker,category,priority'
482 483 )
483 484 assert issue.is_a?(Issue)
484 485 assert !issue.new_record?
485 486 issue.reload
486 487 assert_equal 'New ticket on a given project', issue.subject
487 488 assert_equal User.find_by_login('jsmith'), issue.author
488 489 assert_equal Project.find(2), issue.project
489 490 assert_equal 'Feature request', issue.tracker.to_s
490 491 assert_equal 'Stock management', issue.category.to_s
491 492 assert_equal 'Urgent', issue.priority.to_s
492 493 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
493 494 end
494 495
495 496 def test_add_issue_with_japanese_keywords
496 497 ja_dev = "\xe9\x96\x8b\xe7\x99\xba".force_encoding('UTF-8')
497 498 tracker = Tracker.generate!(:name => ja_dev)
498 499 Project.find(1).trackers << tracker
499 500 issue = submit_email(
500 501 'japanese_keywords_iso_2022_jp.eml',
501 502 :issue => {:project => 'ecookbook'},
502 503 :allow_override => 'tracker'
503 504 )
504 505 assert_kind_of Issue, issue
505 506 assert_equal tracker, issue.tracker
506 507 end
507 508
508 509 def test_add_issue_from_apple_mail
509 510 issue = submit_email(
510 511 'apple_mail_with_attachment.eml',
511 512 :issue => {:project => 'ecookbook'}
512 513 )
513 514 assert_kind_of Issue, issue
514 515 assert_equal 1, issue.attachments.size
515 516
516 517 attachment = issue.attachments.first
517 518 assert_equal 'paella.jpg', attachment.filename
518 519 assert_equal 10790, attachment.filesize
519 520 assert File.exist?(attachment.diskfile)
520 521 assert_equal 10790, File.size(attachment.diskfile)
521 522 assert_equal 'caaf384198bcbc9563ab5c058acd73cd', attachment.digest
522 523 end
523 524
524 525 def test_thunderbird_with_attachment_ja
525 526 issue = submit_email(
526 527 'thunderbird_with_attachment_ja.eml',
527 528 :issue => {:project => 'ecookbook'}
528 529 )
529 530 assert_kind_of Issue, issue
530 531 assert_equal 1, issue.attachments.size
531 532 ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88.txt".force_encoding('UTF-8')
532 533 attachment = issue.attachments.first
533 534 assert_equal ja, attachment.filename
534 535 assert_equal 5, attachment.filesize
535 536 assert File.exist?(attachment.diskfile)
536 537 assert_equal 5, File.size(attachment.diskfile)
537 538 assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
538 539 end
539 540
540 541 def test_gmail_with_attachment_ja
541 542 issue = submit_email(
542 543 'gmail_with_attachment_ja.eml',
543 544 :issue => {:project => 'ecookbook'}
544 545 )
545 546 assert_kind_of Issue, issue
546 547 assert_equal 1, issue.attachments.size
547 548 ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88.txt".force_encoding('UTF-8')
548 549 attachment = issue.attachments.first
549 550 assert_equal ja, attachment.filename
550 551 assert_equal 5, attachment.filesize
551 552 assert File.exist?(attachment.diskfile)
552 553 assert_equal 5, File.size(attachment.diskfile)
553 554 assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
554 555 end
555 556
556 557 def test_thunderbird_with_attachment_latin1
557 558 issue = submit_email(
558 559 'thunderbird_with_attachment_iso-8859-1.eml',
559 560 :issue => {:project => 'ecookbook'}
560 561 )
561 562 assert_kind_of Issue, issue
562 563 assert_equal 1, issue.attachments.size
563 564 u = "".force_encoding('UTF-8')
564 565 u1 = "\xc3\x84\xc3\xa4\xc3\x96\xc3\xb6\xc3\x9c\xc3\xbc".force_encoding('UTF-8')
565 566 11.times { u << u1 }
566 567 attachment = issue.attachments.first
567 568 assert_equal "#{u}.png", attachment.filename
568 569 assert_equal 130, attachment.filesize
569 570 assert File.exist?(attachment.diskfile)
570 571 assert_equal 130, File.size(attachment.diskfile)
571 572 assert_equal '4d80e667ac37dddfe05502530f152abb', attachment.digest
572 573 end
573 574
574 575 def test_gmail_with_attachment_latin1
575 576 issue = submit_email(
576 577 'gmail_with_attachment_iso-8859-1.eml',
577 578 :issue => {:project => 'ecookbook'}
578 579 )
579 580 assert_kind_of Issue, issue
580 581 assert_equal 1, issue.attachments.size
581 582 u = "".force_encoding('UTF-8')
582 583 u1 = "\xc3\x84\xc3\xa4\xc3\x96\xc3\xb6\xc3\x9c\xc3\xbc".force_encoding('UTF-8')
583 584 11.times { u << u1 }
584 585 attachment = issue.attachments.first
585 586 assert_equal "#{u}.txt", attachment.filename
586 587 assert_equal 5, attachment.filesize
587 588 assert File.exist?(attachment.diskfile)
588 589 assert_equal 5, File.size(attachment.diskfile)
589 590 assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
590 591 end
591 592
592 593 def test_mail_with_attachment_latin2
593 594 issue = submit_email(
594 595 'ticket_with_text_attachment_iso-8859-2.eml',
595 596 :issue => {:project => 'ecookbook'}
596 597 )
597 598 assert_kind_of Issue, issue
598 599 assert_equal 1, issue.attachments.size
599 600 attachment = issue.attachments.first
600 601 assert_equal 'latin2.txt', attachment.filename
601 602 assert_equal 19, attachment.filesize
602 603 assert File.exist?(attachment.diskfile)
603 604 assert_equal 19, File.size(attachment.diskfile)
604 605 content = "p\xF8\xEDli\xB9 \xBEluou\xE8k\xFD k\xF9n".force_encoding('CP852')
605 606 assert_equal content, File.read(attachment.diskfile).force_encoding('CP852')
606 607 end
607 608
608 609 def test_multiple_inline_text_parts_should_be_appended_to_issue_description
609 610 issue = submit_email('multiple_text_parts.eml', :issue => {:project => 'ecookbook'})
610 611 assert_include 'first', issue.description
611 612 assert_include 'second', issue.description
612 613 assert_include 'third', issue.description
613 614 end
614 615
615 616 def test_attachment_text_part_should_be_added_as_issue_attachment
616 617 issue = submit_email('multiple_text_parts.eml', :issue => {:project => 'ecookbook'})
617 618 assert_not_include 'Plain text attachment', issue.description
618 619 attachment = issue.attachments.detect {|a| a.filename == 'textfile.txt'}
619 620 assert_not_nil attachment
620 621 assert_include 'Plain text attachment', File.read(attachment.diskfile)
621 622 end
622 623
623 624 def test_add_issue_with_iso_8859_1_subject
624 625 issue = submit_email(
625 626 'subject_as_iso-8859-1.eml',
626 627 :issue => {:project => 'ecookbook'}
627 628 )
628 629 str = "Testmail from Webmail: \xc3\xa4 \xc3\xb6 \xc3\xbc...".force_encoding('UTF-8')
629 630 assert_kind_of Issue, issue
630 631 assert_equal str, issue.subject
631 632 end
632 633
633 634 def test_quoted_printable_utf8
634 635 issue = submit_email(
635 636 'quoted_printable_utf8.eml',
636 637 :issue => {:project => 'ecookbook'}
637 638 )
638 639 assert_kind_of Issue, issue
639 640 str = "Freundliche Gr\xc3\xbcsse".force_encoding('UTF-8')
640 641 assert_equal str, issue.description
641 642 end
642 643
643 644 def test_gmail_iso8859_2
644 645 issue = submit_email(
645 646 'gmail-iso8859-2.eml',
646 647 :issue => {:project => 'ecookbook'}
647 648 )
648 649 assert_kind_of Issue, issue
649 650 str = "Na \xc5\xa1triku se su\xc5\xa1i \xc5\xa1osi\xc4\x87.".force_encoding('UTF-8')
650 651 assert issue.description.include?(str)
651 652 end
652 653
653 654 def test_add_issue_with_japanese_subject
654 655 issue = submit_email(
655 656 'subject_japanese_1.eml',
656 657 :issue => {:project => 'ecookbook'}
657 658 )
658 659 assert_kind_of Issue, issue
659 660 ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88".force_encoding('UTF-8')
660 661 assert_equal ja, issue.subject
661 662 end
662 663
663 664 def test_add_issue_with_korean_body
664 665 # Make sure mail bodies with a charset unknown to Ruby
665 666 # but known to the Mail gem 2.5.4 are handled correctly
666 667 kr = "\xEA\xB3\xA0\xEB\xA7\x99\xEC\x8A\xB5\xEB\x8B\x88\xEB\x8B\xA4.".force_encoding('UTF-8')
667 668 issue = submit_email(
668 669 'body_ks_c_5601-1987.eml',
669 670 :issue => {:project => 'ecookbook'}
670 671 )
671 672 assert_kind_of Issue, issue
672 673 assert_equal kr, issue.description
673 674 end
674 675
675 676 def test_add_issue_with_no_subject_header
676 677 issue = submit_email(
677 678 'no_subject_header.eml',
678 679 :issue => {:project => 'ecookbook'}
679 680 )
680 681 assert_kind_of Issue, issue
681 682 assert_equal '(no subject)', issue.subject
682 683 end
683 684
684 685 def test_add_issue_with_mixed_japanese_subject
685 686 issue = submit_email(
686 687 'subject_japanese_2.eml',
687 688 :issue => {:project => 'ecookbook'}
688 689 )
689 690 assert_kind_of Issue, issue
690 691 ja = "Re: \xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88".force_encoding('UTF-8')
691 692 assert_equal ja, issue.subject
692 693 end
693 694
694 695 def test_should_ignore_emails_from_locked_users
695 696 User.find(2).lock!
696 697
697 698 MailHandler.any_instance.expects(:dispatch).never
698 699 assert_no_difference 'Issue.count' do
699 700 assert_equal false, submit_email('ticket_on_given_project.eml')
700 701 end
701 702 end
702 703
703 704 def test_should_ignore_emails_from_emission_address
704 705 Role.anonymous.add_permission!(:add_issues)
705 706 assert_no_difference 'User.count' do
706 707 assert_equal false,
707 708 submit_email(
708 709 'ticket_from_emission_address.eml',
709 710 :issue => {:project => 'ecookbook'},
710 711 :unknown_user => 'create'
711 712 )
712 713 end
713 714 end
714 715
715 716 def test_should_ignore_auto_replied_emails
716 717 MailHandler.any_instance.expects(:dispatch).never
717 718 [
718 719 "Auto-Submitted: auto-replied",
719 720 "Auto-Submitted: Auto-Replied",
720 721 "Auto-Submitted: auto-generated",
721 722 'X-Autoreply: yes'
722 723 ].each do |header|
723 724 raw = IO.read(File.join(FIXTURES_PATH, 'ticket_on_given_project.eml'))
724 725 raw = header + "\n" + raw
725 726
726 727 assert_no_difference 'Issue.count' do
727 728 assert_equal false, MailHandler.receive(raw), "email with #{header} header was not ignored"
728 729 end
729 730 end
730 731 end
731 732
732 733 test "should not ignore Auto-Submitted headers not defined in RFC3834" do
733 734 [
734 735 "Auto-Submitted: auto-forwarded"
735 736 ].each do |header|
736 737 raw = IO.read(File.join(FIXTURES_PATH, 'ticket_on_given_project.eml'))
737 738 raw = header + "\n" + raw
738 739
739 740 assert_difference 'Issue.count', 1 do
740 741 assert_not_nil MailHandler.receive(raw), "email with #{header} header was ignored"
741 742 end
742 743 end
743 744 end
744 745
745 746 def test_add_issue_should_send_email_notification
746 747 Setting.notified_events = ['issue_added']
747 748 # This email contains: 'Project: onlinestore'
748 749 issue = submit_email('ticket_on_given_project.eml')
749 750 assert issue.is_a?(Issue)
750 751 assert_equal 1, ActionMailer::Base.deliveries.size
751 752 end
752 753
753 754 def test_update_issue
754 755 journal = submit_email('ticket_reply.eml')
755 756 assert journal.is_a?(Journal)
756 757 assert_equal User.find_by_login('jsmith'), journal.user
757 758 assert_equal Issue.find(2), journal.journalized
758 759 assert_match /This is reply/, journal.notes
759 760 assert_equal false, journal.private_notes
760 761 assert_equal 'Feature request', journal.issue.tracker.name
761 762 end
762 763
763 764 def test_update_issue_should_accept_issue_id_after_space_inside_brackets
764 765 journal = submit_email('ticket_reply_with_status.eml') do |email|
765 766 assert email.sub!(/^Subject:.*$/, "Subject: Re: [Feature request #2] Add ingredients categories")
766 767 end
767 768 assert journal.is_a?(Journal)
768 769 assert_equal Issue.find(2), journal.journalized
769 770 end
770 771
771 772 def test_update_issue_should_accept_issue_id_inside_brackets
772 773 journal = submit_email('ticket_reply_with_status.eml') do |email|
773 774 assert email.sub!(/^Subject:.*$/, "Subject: Re: [#2] Add ingredients categories")
774 775 end
775 776 assert journal.is_a?(Journal)
776 777 assert_equal Issue.find(2), journal.journalized
777 778 end
778 779
779 780 def test_update_issue_should_ignore_bogus_issue_ids_in_subject
780 781 journal = submit_email('ticket_reply_with_status.eml') do |email|
781 782 assert email.sub!(/^Subject:.*$/, "Subject: Re: [12345#1][bogus#1][Feature request #2] Add ingredients categories")
782 783 end
783 784 assert journal.is_a?(Journal)
784 785 assert_equal Issue.find(2), journal.journalized
785 786 end
786 787
787 788 def test_update_issue_with_attribute_changes
788 789 journal = submit_email('ticket_reply_with_status.eml', :allow_override => ['status','assigned_to','start_date','due_date', 'float field'])
789 790 assert journal.is_a?(Journal)
790 791 issue = Issue.find(journal.issue.id)
791 792 assert_equal User.find_by_login('jsmith'), journal.user
792 793 assert_equal Issue.find(2), journal.journalized
793 794 assert_match /This is reply/, journal.notes
794 795 assert_equal 'Feature request', journal.issue.tracker.name
795 796 assert_equal IssueStatus.find_by_name("Resolved"), issue.status
796 797 assert_equal '2010-01-01', issue.start_date.to_s
797 798 assert_equal '2010-12-31', issue.due_date.to_s
798 799 assert_equal User.find_by_login('jsmith'), issue.assigned_to
799 800 assert_equal "52.6", issue.custom_value_for(CustomField.find_by_name('Float field')).value
800 801 # keywords should be removed from the email body
801 802 assert !journal.notes.match(/^Status:/i)
802 803 assert !journal.notes.match(/^Start Date:/i)
803 804 end
804 805
805 806 def test_update_issue_with_attachment
806 807 assert_difference 'Journal.count' do
807 808 assert_difference 'JournalDetail.count' do
808 809 assert_difference 'Attachment.count' do
809 810 assert_no_difference 'Issue.count' do
810 811 journal = submit_email('ticket_with_attachment.eml') do |raw|
811 812 raw.gsub! /^Subject: .*$/, 'Subject: Re: [Cookbook - Feature #2] (New) Add ingredients categories'
812 813 end
813 814 end
814 815 end
815 816 end
816 817 end
817 818 journal = Journal.order('id DESC').first
818 819 assert_equal Issue.find(2), journal.journalized
819 820 assert_equal 1, journal.details.size
820 821
821 822 detail = journal.details.first
822 823 assert_equal 'attachment', detail.property
823 824 assert_equal 'Paella.jpg', detail.value
824 825 end
825 826
826 827 def test_update_issue_should_send_email_notification
827 828 journal = submit_email('ticket_reply.eml')
828 829 assert journal.is_a?(Journal)
829 830 assert_equal 1, ActionMailer::Base.deliveries.size
830 831 end
831 832
832 833 def test_update_issue_should_not_set_defaults
833 834 journal = submit_email(
834 835 'ticket_reply.eml',
835 836 :issue => {:tracker => 'Support request', :priority => 'High'}
836 837 )
837 838 assert journal.is_a?(Journal)
838 839 assert_match /This is reply/, journal.notes
839 840 assert_equal 'Feature request', journal.issue.tracker.name
840 841 assert_equal 'Normal', journal.issue.priority.name
841 842 end
842 843
843 844 def test_update_issue_should_add_cc_as_watchers
844 845 Watcher.delete_all
845 846 issue = Issue.find(2)
846 847
847 848 assert_difference 'Watcher.count' do
848 849 assert submit_email('issue_update_with_cc.eml')
849 850 end
850 851 issue.reload
851 852 assert_equal 1, issue.watcher_user_ids.size
852 853 assert issue.watched_by?(User.find_by_mail('dlopper@somenet.foo'))
853 854 end
854 855
855 856 def test_update_issue_should_not_add_cc_as_watchers_if_already_watching
856 857 Watcher.delete_all
857 858 issue = Issue.find(2)
858 859 Watcher.create!(:watchable => issue, :user => User.find_by_mail('dlopper@somenet.foo'))
859 860
860 861 assert_no_difference 'Watcher.count' do
861 862 assert submit_email('issue_update_with_cc.eml')
862 863 end
863 864 end
864 865
865 866 def test_replying_to_a_private_note_should_add_reply_as_private
866 867 private_journal = Journal.create!(:notes => 'Private notes', :journalized => Issue.find(1), :private_notes => true, :user_id => 2)
867 868
868 869 assert_difference 'Journal.count' do
869 870 journal = submit_email('ticket_reply.eml') do |email|
870 871 email.sub! %r{^In-Reply-To:.*$}, "In-Reply-To: <redmine.journal-#{private_journal.id}.20060719210421@osiris>"
871 872 end
872 873
873 874 assert_kind_of Journal, journal
874 875 assert_match /This is reply/, journal.notes
875 876 assert_equal true, journal.private_notes
876 877 end
877 878 end
878 879
879 880 def test_reply_to_a_message
880 881 m = submit_email('message_reply.eml')
881 882 assert m.is_a?(Message)
882 883 assert !m.new_record?
883 884 m.reload
884 885 assert_equal 'Reply via email', m.subject
885 886 # The email replies to message #2 which is part of the thread of message #1
886 887 assert_equal Message.find(1), m.parent
887 888 end
888 889
889 890 def test_reply_to_a_message_by_subject
890 891 m = submit_email('message_reply_by_subject.eml')
891 892 assert m.is_a?(Message)
892 893 assert !m.new_record?
893 894 m.reload
894 895 assert_equal 'Reply to the first post', m.subject
895 896 assert_equal Message.find(1), m.parent
896 897 end
897 898
898 899 def test_should_convert_tags_of_html_only_emails
899 900 with_settings :text_formatting => 'textile' do
900 901 issue = submit_email('ticket_html_only.eml', :issue => {:project => 'ecookbook'})
901 902 assert issue.is_a?(Issue)
902 903 assert !issue.new_record?
903 904 issue.reload
904 905 assert_equal 'HTML email', issue.subject
905 906 assert_equal "This is a *html-only* email.\r\n\r\nh1. With a title\r\n\r\nand a paragraph.", issue.description
906 907 end
907 908 end
908 909
909 910 def test_should_handle_outlook_web_access_2010_html_only
910 911 issue = submit_email('outlook_web_access_2010_html_only.eml', :issue => {:project => 'ecookbook'})
911 912 assert issue.is_a?(Issue)
912 913 issue.reload
913 914 assert_equal 'Upgrade Redmine to 3.0.x', issue.subject
914 915 assert_equal "A mess.\r\n\r\n--Geoff Maciolek\r\nMYCOMPANYNAME, LLC", issue.description
915 916 end
916 917
917 918 def test_should_handle_outlook_2010_html_only
918 919 issue = submit_email('outlook_2010_html_only.eml', :issue => {:project => 'ecookbook'})
919 920 assert issue.is_a?(Issue)
920 921 issue.reload
921 922 assert_equal 'Test email', issue.subject
922 923 assert_equal "Simple, unadorned test email generated by Outlook 2010. It is in HTML format, but" +
923 924 " no special formatting has been chosen. I’m going to save this as a draft and then manually" +
924 925 " drop it into the Inbox for scraping by Redmine 3.0.2.", issue.description
925 926 end
926 927
927 928 test "truncate emails with no setting should add the entire email into the issue" do
928 929 with_settings :mail_handler_body_delimiters => '' do
929 930 issue = submit_email('ticket_on_given_project.eml')
930 931 assert_issue_created(issue)
931 932 assert issue.description.include?('---')
932 933 assert issue.description.include?('This paragraph is after the delimiter')
933 934 end
934 935 end
935 936
936 937 test "truncate emails with a single string should truncate the email at the delimiter for the issue" do
937 938 with_settings :mail_handler_body_delimiters => '---' do
938 939 issue = submit_email('ticket_on_given_project.eml')
939 940 assert_issue_created(issue)
940 941 assert issue.description.include?('This paragraph is before delimiters')
941 942 assert issue.description.include?('--- This line starts with a delimiter')
942 943 assert !issue.description.match(/^---$/)
943 944 assert !issue.description.include?('This paragraph is after the delimiter')
944 945 end
945 946 end
946 947
947 948 test "truncate emails with a single quoted reply should truncate the email at the delimiter with the quoted reply symbols (>)" do
948 949 with_settings :mail_handler_body_delimiters => '--- Reply above. Do not remove this line. ---' do
949 950 journal = submit_email('issue_update_with_quoted_reply_above.eml')
950 951 assert journal.is_a?(Journal)
951 952 assert journal.notes.include?('An update to the issue by the sender.')
952 953 assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---"))
953 954 assert !journal.notes.include?('Looks like the JSON api for projects was missed.')
954 955 end
955 956 end
956 957
957 958 test "truncate emails with multiple quoted replies should truncate the email at the delimiter with the quoted reply symbols (>)" do
958 959 with_settings :mail_handler_body_delimiters => '--- Reply above. Do not remove this line. ---' do
959 960 journal = submit_email('issue_update_with_multiple_quoted_reply_above.eml')
960 961 assert journal.is_a?(Journal)
961 962 assert journal.notes.include?('An update to the issue by the sender.')
962 963 assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---"))
963 964 assert !journal.notes.include?('Looks like the JSON api for projects was missed.')
964 965 end
965 966 end
966 967
967 968 test "truncate emails with multiple strings should truncate the email at the first delimiter found (BREAK)" do
968 969 with_settings :mail_handler_body_delimiters => "---\nBREAK" do
969 970 issue = submit_email('ticket_on_given_project.eml')
970 971 assert_issue_created(issue)
971 972 assert issue.description.include?('This paragraph is before delimiters')
972 973 assert !issue.description.include?('BREAK')
973 974 assert !issue.description.include?('This paragraph is between delimiters')
974 975 assert !issue.description.match(/^---$/)
975 976 assert !issue.description.include?('This paragraph is after the delimiter')
976 977 end
977 978 end
978 979
979 980 def test_attachments_that_match_mail_handler_excluded_filenames_should_be_ignored
980 981 with_settings :mail_handler_excluded_filenames => '*.vcf, *.jpg' do
981 982 issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
982 983 assert issue.is_a?(Issue)
983 984 assert !issue.new_record?
984 985 assert_equal 0, issue.reload.attachments.size
985 986 end
986 987 end
987 988
988 989 def test_attachments_that_do_not_match_mail_handler_excluded_filenames_should_be_attached
989 990 with_settings :mail_handler_excluded_filenames => '*.vcf, *.gif' do
990 991 issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
991 992 assert issue.is_a?(Issue)
992 993 assert !issue.new_record?
993 994 assert_equal 1, issue.reload.attachments.size
994 995 end
995 996 end
996 997
997 998 def test_email_with_long_subject_line
998 999 issue = submit_email('ticket_with_long_subject.eml')
999 1000 assert issue.is_a?(Issue)
1000 1001 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]
1001 1002 end
1002 1003
1003 1004 def test_first_keyword_should_be_matched
1004 1005 issue = submit_email('ticket_with_duplicate_keyword.eml', :allow_override => 'priority')
1005 1006 assert issue.is_a?(Issue)
1006 1007 assert_equal 'High', issue.priority.name
1007 1008 end
1008 1009
1009 1010 def test_keyword_after_delimiter_should_be_ignored
1010 1011 with_settings :mail_handler_body_delimiters => "== DELIMITER ==" do
1011 1012 issue = submit_email('ticket_with_keyword_after_delimiter.eml', :allow_override => 'priority')
1012 1013 assert issue.is_a?(Issue)
1013 1014 assert_equal 'Normal', issue.priority.name
1014 1015 end
1015 1016 end
1016 1017
1017 1018 def test_new_user_from_attributes_should_return_valid_user
1018 1019 to_test = {
1019 1020 # [address, name] => [login, firstname, lastname]
1020 1021 ['jsmith@example.net', nil] => ['jsmith@example.net', 'jsmith', '-'],
1021 1022 ['jsmith@example.net', 'John'] => ['jsmith@example.net', 'John', '-'],
1022 1023 ['jsmith@example.net', 'John Smith'] => ['jsmith@example.net', 'John', 'Smith'],
1023 1024 ['jsmith@example.net', 'John Paul Smith'] => ['jsmith@example.net', 'John', 'Paul Smith'],
1024 1025 ['jsmith@example.net', 'AVeryLongFirstnameThatExceedsTheMaximumLength Smith'] => ['jsmith@example.net', 'AVeryLongFirstnameThatExceedsT', 'Smith'],
1025 1026 ['jsmith@example.net', 'John AVeryLongLastnameThatExceedsTheMaximumLength'] => ['jsmith@example.net', 'John', 'AVeryLongLastnameThatExceedsTh']
1026 1027 }
1027 1028
1028 1029 to_test.each do |attrs, expected|
1029 1030 user = MailHandler.new_user_from_attributes(attrs.first, attrs.last)
1030 1031
1031 1032 assert user.valid?, user.errors.full_messages.to_s
1032 1033 assert_equal attrs.first, user.mail
1033 1034 assert_equal expected[0], user.login
1034 1035 assert_equal expected[1], user.firstname
1035 1036 assert_equal expected[2], user.lastname
1036 1037 assert_equal 'only_my_events', user.mail_notification
1037 1038 end
1038 1039 end
1039 1040
1040 1041 def test_new_user_from_attributes_should_use_default_login_if_invalid
1041 1042 user = MailHandler.new_user_from_attributes('foo+bar@example.net')
1042 1043 assert user.valid?
1043 1044 assert user.login =~ /^user[a-f0-9]+$/
1044 1045 assert_equal 'foo+bar@example.net', user.mail
1045 1046 end
1046 1047
1047 1048 def test_new_user_with_utf8_encoded_fullname_should_be_decoded
1048 1049 assert_difference 'User.count' do
1049 1050 issue = submit_email(
1050 1051 'fullname_of_sender_as_utf8_encoded.eml',
1051 1052 :issue => {:project => 'ecookbook'},
1052 1053 :unknown_user => 'create'
1053 1054 )
1054 1055 end
1055 1056 user = User.order('id DESC').first
1056 1057 assert_equal "foo@example.org", user.mail
1057 1058 str1 = "\xc3\x84\xc3\xa4".force_encoding('UTF-8')
1058 1059 str2 = "\xc3\x96\xc3\xb6".force_encoding('UTF-8')
1059 1060 assert_equal str1, user.firstname
1060 1061 assert_equal str2, user.lastname
1061 1062 end
1062 1063
1063 1064 def test_extract_options_from_env_should_return_options
1064 1065 options = MailHandler.extract_options_from_env({
1065 1066 'tracker' => 'defect',
1066 1067 'project' => 'foo',
1067 1068 'unknown_user' => 'create'
1068 1069 })
1069 1070
1070 1071 assert_equal({
1071 1072 :issue => {:tracker => 'defect', :project => 'foo'},
1072 1073 :unknown_user => 'create'
1073 1074 }, options)
1074 1075 end
1075 1076
1076 1077 def test_safe_receive_should_rescue_exceptions_and_return_false
1077 1078 MailHandler.stubs(:receive).raises(Exception.new "Something went wrong")
1078 1079
1079 1080 assert_equal false, MailHandler.safe_receive
1080 1081 end
1081 1082
1082 1083 private
1083 1084
1084 1085 def submit_email(filename, options={})
1085 1086 raw = IO.read(File.join(FIXTURES_PATH, filename))
1086 1087 yield raw if block_given?
1087 1088 MailHandler.receive(raw, options)
1088 1089 end
1089 1090
1090 1091 def assert_issue_created(issue)
1091 1092 assert issue.is_a?(Issue)
1092 1093 assert !issue.new_record?
1093 1094 issue.reload
1094 1095 end
1095 1096 end
@@ -1,192 +1,201
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../test_helper', __FILE__)
19 19
20 20 class WatcherTest < ActiveSupport::TestCase
21 21 fixtures :projects, :users, :email_addresses, :members, :member_roles, :roles, :enabled_modules,
22 22 :issues, :issue_statuses, :enumerations, :trackers, :projects_trackers,
23 23 :boards, :messages,
24 24 :wikis, :wiki_pages,
25 25 :watchers
26 26
27 27 def setup
28 28 @user = User.find(1)
29 29 @issue = Issue.find(1)
30 30 end
31 31
32 32 def test_validate
33 33 user = User.find(5)
34 34 assert !user.active?
35 35 watcher = Watcher.new(:user_id => user.id)
36 36 assert !watcher.save
37 37 end
38 38
39 39 def test_watch
40 40 assert @issue.add_watcher(@user)
41 41 @issue.reload
42 42 assert @issue.watchers.detect { |w| w.user == @user }
43 43 end
44 44
45 45 def test_cant_watch_twice
46 46 assert @issue.add_watcher(@user)
47 47 assert !@issue.add_watcher(@user)
48 48 end
49 49
50 50 def test_watched_by
51 51 assert @issue.add_watcher(@user)
52 52 @issue.reload
53 53 assert @issue.watched_by?(@user)
54 54 assert Issue.watched_by(@user).include?(@issue)
55 55 end
56 56
57 57 def test_watcher_users
58 58 watcher_users = Issue.find(2).watcher_users
59 59 assert_kind_of Array, watcher_users.collect{|w| w}
60 60 assert_kind_of User, watcher_users.first
61 61 end
62 62
63 def test_watcher_users_should_be_reloaded_after_adding_a_watcher
64 issue = Issue.find(2)
65 user = User.generate!
66
67 assert_difference 'issue.watcher_users.to_a.size' do
68 issue.add_watcher user
69 end
70 end
71
63 72 def test_watcher_users_should_not_validate_user
64 73 User.where(:id => 1).update_all("firstname = ''")
65 74 @user.reload
66 75 assert !@user.valid?
67 76
68 77 issue = Issue.new(:project => Project.find(1), :tracker_id => 1, :subject => "test", :author => User.find(2))
69 78 issue.watcher_users << @user
70 79 issue.save!
71 80 assert issue.watched_by?(@user)
72 81 end
73 82
74 83 def test_watcher_user_ids
75 84 assert_equal [1, 3], Issue.find(2).watcher_user_ids.sort
76 85 end
77 86
78 87 def test_watcher_user_ids=
79 88 issue = Issue.new
80 89 issue.watcher_user_ids = ['1', '3']
81 90 assert issue.watched_by?(User.find(1))
82 91 end
83 92
84 93 def test_watcher_user_ids_should_make_ids_uniq
85 94 issue = Issue.new(:project => Project.find(1), :tracker_id => 1, :subject => "test", :author => User.find(2))
86 95 issue.watcher_user_ids = ['1', '3', '1']
87 96 issue.save!
88 97 assert_equal 2, issue.watchers.count
89 98 end
90 99
91 100 def test_addable_watcher_users
92 101 addable_watcher_users = @issue.addable_watcher_users
93 102 assert_kind_of Array, addable_watcher_users
94 103 assert_kind_of User, addable_watcher_users.first
95 104 end
96 105
97 106 def test_addable_watcher_users_should_not_include_user_that_cannot_view_the_object
98 107 issue = Issue.new(:project => Project.find(1), :is_private => true)
99 108 assert_nil issue.addable_watcher_users.detect {|user| !issue.visible?(user)}
100 109 end
101 110
102 111 def test_any_watched_should_return_false_if_no_object_is_watched
103 112 objects = (0..2).map {Issue.generate!}
104 113
105 114 assert_equal false, Watcher.any_watched?(objects, @user)
106 115 end
107 116
108 117 def test_any_watched_should_return_true_if_one_object_is_watched
109 118 objects = (0..2).map {Issue.generate!}
110 119 objects.last.add_watcher(@user)
111 120
112 121 assert_equal true, Watcher.any_watched?(objects, @user)
113 122 end
114 123
115 124 def test_any_watched_should_return_false_with_no_object
116 125 assert_equal false, Watcher.any_watched?([], @user)
117 126 end
118 127
119 128 def test_recipients
120 129 @issue.watchers.delete_all
121 130 @issue.reload
122 131
123 132 assert @issue.watcher_recipients.empty?
124 133 assert @issue.add_watcher(@user)
125 134
126 135 @user.mail_notification = 'all'
127 136 @user.save!
128 137 @issue.reload
129 138 assert @issue.watcher_recipients.include?(@user.mail)
130 139
131 140 @user.mail_notification = 'none'
132 141 @user.save!
133 142 @issue.reload
134 143 assert !@issue.watcher_recipients.include?(@user.mail)
135 144 end
136 145
137 146 def test_unwatch
138 147 assert @issue.add_watcher(@user)
139 148 @issue.reload
140 149 assert_equal 1, @issue.remove_watcher(@user)
141 150 end
142 151
143 152 def test_prune_with_user
144 153 Watcher.delete_all("user_id = 9")
145 154 user = User.find(9)
146 155
147 156 # public
148 157 Watcher.create!(:watchable => Issue.find(1), :user => user)
149 158 Watcher.create!(:watchable => Issue.find(2), :user => user)
150 159 Watcher.create!(:watchable => Message.find(1), :user => user)
151 160 Watcher.create!(:watchable => Wiki.find(1), :user => user)
152 161 Watcher.create!(:watchable => WikiPage.find(2), :user => user)
153 162
154 163 # private project (id: 2)
155 164 Member.create!(:project => Project.find(2), :principal => user, :role_ids => [1])
156 165 Watcher.create!(:watchable => Issue.find(4), :user => user)
157 166 Watcher.create!(:watchable => Message.find(7), :user => user)
158 167 Watcher.create!(:watchable => Wiki.find(2), :user => user)
159 168 Watcher.create!(:watchable => WikiPage.find(3), :user => user)
160 169
161 170 assert_no_difference 'Watcher.count' do
162 171 Watcher.prune(:user => User.find(9))
163 172 end
164 173
165 174 Member.delete_all
166 175
167 176 assert_difference 'Watcher.count', -4 do
168 177 Watcher.prune(:user => User.find(9))
169 178 end
170 179
171 180 assert Issue.find(1).watched_by?(user)
172 181 assert !Issue.find(4).watched_by?(user)
173 182 end
174 183
175 184 def test_prune_with_project
176 185 user = User.find(9)
177 186 Watcher.new(:watchable => Issue.find(4), :user => User.find(9)).save(:validate => false) # project 2
178 187 Watcher.new(:watchable => Issue.find(6), :user => User.find(9)).save(:validate => false) # project 5
179 188
180 189 assert Watcher.prune(:project => Project.find(5)) > 0
181 190 assert Issue.find(4).watched_by?(user)
182 191 assert !Issue.find(6).watched_by?(user)
183 192 end
184 193
185 194 def test_prune_all
186 195 user = User.find(9)
187 196 Watcher.new(:watchable => Issue.find(4), :user => User.find(9)).save(:validate => false)
188 197
189 198 assert Watcher.prune > 0
190 199 assert !Issue.find(4).watched_by?(user)
191 200 end
192 201 end
General Comments 0
You need to be logged in to leave comments. Login now