annotate test/unit/mail_handler_test.rb @ 1516:b450a9d58aed redmine-2.4

Update to Redmine SVN revision 13356 on 2.4-stable branch
author Chris Cannam
date Tue, 09 Sep 2014 09:28:31 +0100
parents e248c7af89ec
children dffacf8a6908
rev   line source
Chris@0 1 # encoding: utf-8
Chris@0 2 #
Chris@0 3 # Redmine - project management software
Chris@1494 4 # Copyright (C) 2006-2014 Jean-Philippe Lang
Chris@0 5 #
Chris@0 6 # This program is free software; you can redistribute it and/or
Chris@0 7 # modify it under the terms of the GNU General Public License
Chris@0 8 # as published by the Free Software Foundation; either version 2
Chris@0 9 # of the License, or (at your option) any later version.
Chris@441 10 #
Chris@0 11 # This program is distributed in the hope that it will be useful,
Chris@0 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
Chris@0 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Chris@0 14 # GNU General Public License for more details.
Chris@441 15 #
Chris@0 16 # You should have received a copy of the GNU General Public License
Chris@0 17 # along with this program; if not, write to the Free Software
Chris@0 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Chris@0 19
Chris@119 20 require File.expand_path('../../test_helper', __FILE__)
Chris@0 21
Chris@0 22 class MailHandlerTest < ActiveSupport::TestCase
Chris@1115 23 fixtures :users, :projects, :enabled_modules, :roles,
Chris@1115 24 :members, :member_roles, :users,
Chris@1115 25 :issues, :issue_statuses,
Chris@1115 26 :workflows, :trackers, :projects_trackers,
Chris@1115 27 :versions, :enumerations, :issue_categories,
Chris@1115 28 :custom_fields, :custom_fields_trackers, :custom_fields_projects,
Chris@1115 29 :boards, :messages
Chris@441 30
Chris@0 31 FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures/mail_handler'
Chris@441 32
Chris@0 33 def setup
Chris@0 34 ActionMailer::Base.deliveries.clear
chris@37 35 Setting.notified_events = Redmine::Notifiable.all.collect(&:name)
Chris@0 36 end
Chris@441 37
Chris@1115 38 def teardown
Chris@1115 39 Setting.clear_cache
Chris@1115 40 end
Chris@1115 41
Chris@0 42 def test_add_issue
Chris@0 43 ActionMailer::Base.deliveries.clear
Chris@0 44 # This email contains: 'Project: onlinestore'
Chris@0 45 issue = submit_email('ticket_on_given_project.eml')
Chris@0 46 assert issue.is_a?(Issue)
Chris@0 47 assert !issue.new_record?
Chris@0 48 issue.reload
Chris@119 49 assert_equal Project.find(2), issue.project
Chris@119 50 assert_equal issue.project.trackers.first, issue.tracker
Chris@0 51 assert_equal 'New ticket on a given project', issue.subject
Chris@0 52 assert_equal User.find_by_login('jsmith'), issue.author
Chris@0 53 assert_equal IssueStatus.find_by_name('Resolved'), issue.status
Chris@0 54 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
Chris@0 55 assert_equal '2010-01-01', issue.start_date.to_s
Chris@0 56 assert_equal '2010-12-31', issue.due_date.to_s
Chris@0 57 assert_equal User.find_by_login('jsmith'), issue.assigned_to
Chris@507 58 assert_equal Version.find_by_name('Alpha'), issue.fixed_version
chris@37 59 assert_equal 2.5, issue.estimated_hours
chris@37 60 assert_equal 30, issue.done_ratio
Chris@119 61 assert_equal [issue.id, 1, 2], [issue.root_id, issue.lft, issue.rgt]
Chris@0 62 # keywords should be removed from the email body
Chris@0 63 assert !issue.description.match(/^Project:/i)
Chris@0 64 assert !issue.description.match(/^Status:/i)
Chris@441 65 assert !issue.description.match(/^Start Date:/i)
Chris@0 66 # Email notification should be sent
Chris@0 67 mail = ActionMailer::Base.deliveries.last
Chris@0 68 assert_not_nil mail
Chris@0 69 assert mail.subject.include?('New ticket on a given project')
Chris@0 70 end
Chris@441 71
Chris@119 72 def test_add_issue_with_default_tracker
Chris@119 73 # This email contains: 'Project: onlinestore'
Chris@1115 74 issue = submit_email(
Chris@1115 75 'ticket_on_given_project.eml',
Chris@1115 76 :issue => {:tracker => 'Support request'}
Chris@1115 77 )
Chris@119 78 assert issue.is_a?(Issue)
Chris@119 79 assert !issue.new_record?
Chris@119 80 issue.reload
Chris@119 81 assert_equal 'Support request', issue.tracker.name
Chris@119 82 end
Chris@0 83
Chris@0 84 def test_add_issue_with_status
Chris@0 85 # This email contains: 'Project: onlinestore' and 'Status: Resolved'
Chris@0 86 issue = submit_email('ticket_on_given_project.eml')
Chris@0 87 assert issue.is_a?(Issue)
Chris@0 88 assert !issue.new_record?
Chris@0 89 issue.reload
Chris@0 90 assert_equal Project.find(2), issue.project
Chris@0 91 assert_equal IssueStatus.find_by_name("Resolved"), issue.status
Chris@0 92 end
Chris@441 93
Chris@0 94 def test_add_issue_with_attributes_override
Chris@1115 95 issue = submit_email(
Chris@1115 96 'ticket_with_attributes.eml',
Chris@1115 97 :allow_override => 'tracker,category,priority'
Chris@1115 98 )
Chris@0 99 assert issue.is_a?(Issue)
Chris@0 100 assert !issue.new_record?
Chris@0 101 issue.reload
Chris@0 102 assert_equal 'New ticket on a given project', issue.subject
Chris@0 103 assert_equal User.find_by_login('jsmith'), issue.author
Chris@0 104 assert_equal Project.find(2), issue.project
Chris@0 105 assert_equal 'Feature request', issue.tracker.to_s
Chris@0 106 assert_equal 'Stock management', issue.category.to_s
Chris@0 107 assert_equal 'Urgent', issue.priority.to_s
Chris@0 108 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
Chris@0 109 end
Chris@441 110
Chris@909 111 def test_add_issue_with_group_assignment
Chris@909 112 with_settings :issue_group_assignment => '1' do
Chris@909 113 issue = submit_email('ticket_on_given_project.eml') do |email|
Chris@909 114 email.gsub!('Assigned to: John Smith', 'Assigned to: B Team')
Chris@909 115 end
Chris@909 116 assert issue.is_a?(Issue)
Chris@909 117 assert !issue.new_record?
Chris@909 118 issue.reload
Chris@909 119 assert_equal Group.find(11), issue.assigned_to
Chris@909 120 end
Chris@909 121 end
Chris@909 122
Chris@0 123 def test_add_issue_with_partial_attributes_override
Chris@1115 124 issue = submit_email(
Chris@1115 125 'ticket_with_attributes.eml',
Chris@1115 126 :issue => {:priority => 'High'},
Chris@1115 127 :allow_override => ['tracker']
Chris@1115 128 )
Chris@0 129 assert issue.is_a?(Issue)
Chris@0 130 assert !issue.new_record?
Chris@0 131 issue.reload
Chris@0 132 assert_equal 'New ticket on a given project', issue.subject
Chris@0 133 assert_equal User.find_by_login('jsmith'), issue.author
Chris@0 134 assert_equal Project.find(2), issue.project
Chris@0 135 assert_equal 'Feature request', issue.tracker.to_s
Chris@0 136 assert_nil issue.category
Chris@0 137 assert_equal 'High', issue.priority.to_s
Chris@0 138 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
Chris@0 139 end
Chris@441 140
Chris@0 141 def test_add_issue_with_spaces_between_attribute_and_separator
Chris@1115 142 issue = submit_email(
Chris@1115 143 'ticket_with_spaces_between_attribute_and_separator.eml',
Chris@1115 144 :allow_override => 'tracker,category,priority'
Chris@1115 145 )
Chris@0 146 assert issue.is_a?(Issue)
Chris@0 147 assert !issue.new_record?
Chris@0 148 issue.reload
Chris@0 149 assert_equal 'New ticket on a given project', issue.subject
Chris@0 150 assert_equal User.find_by_login('jsmith'), issue.author
Chris@0 151 assert_equal Project.find(2), issue.project
Chris@0 152 assert_equal 'Feature request', issue.tracker.to_s
Chris@0 153 assert_equal 'Stock management', issue.category.to_s
Chris@0 154 assert_equal 'Urgent', issue.priority.to_s
Chris@0 155 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
Chris@0 156 end
Chris@0 157
Chris@0 158 def test_add_issue_with_attachment_to_specific_project
Chris@0 159 issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
Chris@0 160 assert issue.is_a?(Issue)
Chris@0 161 assert !issue.new_record?
Chris@0 162 issue.reload
Chris@0 163 assert_equal 'Ticket created by email with attachment', issue.subject
Chris@0 164 assert_equal User.find_by_login('jsmith'), issue.author
Chris@0 165 assert_equal Project.find(2), issue.project
Chris@0 166 assert_equal 'This is a new ticket with attachments', issue.description
Chris@0 167 # Attachment properties
Chris@0 168 assert_equal 1, issue.attachments.size
Chris@0 169 assert_equal 'Paella.jpg', issue.attachments.first.filename
Chris@0 170 assert_equal 'image/jpeg', issue.attachments.first.content_type
Chris@0 171 assert_equal 10790, issue.attachments.first.filesize
Chris@0 172 end
Chris@441 173
Chris@0 174 def test_add_issue_with_custom_fields
Chris@0 175 issue = submit_email('ticket_with_custom_fields.eml', :issue => {:project => 'onlinestore'})
Chris@0 176 assert issue.is_a?(Issue)
Chris@0 177 assert !issue.new_record?
Chris@0 178 issue.reload
Chris@0 179 assert_equal 'New ticket with custom field values', issue.subject
Chris@1115 180 assert_equal 'PostgreSQL', issue.custom_field_value(1)
Chris@1115 181 assert_equal 'Value for a custom field', issue.custom_field_value(2)
Chris@0 182 assert !issue.description.match(/^searchable field:/i)
Chris@0 183 end
Chris@441 184
Chris@1115 185 def test_add_issue_with_version_custom_fields
Chris@1115 186 field = IssueCustomField.create!(:name => 'Affected version', :field_format => 'version', :is_for_all => true, :tracker_ids => [1,2,3])
Chris@1115 187
Chris@1115 188 issue = submit_email('ticket_with_custom_fields.eml', :issue => {:project => 'ecookbook'}) do |email|
Chris@1115 189 email << "Affected version: 1.0\n"
Chris@1115 190 end
Chris@1115 191 assert issue.is_a?(Issue)
Chris@1115 192 assert !issue.new_record?
Chris@1115 193 issue.reload
Chris@1115 194 assert_equal '2', issue.custom_field_value(field)
Chris@1115 195 end
Chris@1115 196
Chris@1115 197 def test_add_issue_should_match_assignee_on_display_name
Chris@1115 198 user = User.generate!(:firstname => 'Foo Bar', :lastname => 'Foo Baz')
Chris@1115 199 User.add_to_project(user, Project.find(2))
Chris@1115 200 issue = submit_email('ticket_on_given_project.eml') do |email|
Chris@1115 201 email.sub!(/^Assigned to.*$/, 'Assigned to: Foo Bar Foo baz')
Chris@1115 202 end
Chris@1115 203 assert issue.is_a?(Issue)
Chris@1115 204 assert_equal user, issue.assigned_to
Chris@1115 205 end
Chris@1115 206
Chris@0 207 def test_add_issue_with_cc
Chris@0 208 issue = submit_email('ticket_with_cc.eml', :issue => {:project => 'ecookbook'})
Chris@0 209 assert issue.is_a?(Issue)
Chris@0 210 assert !issue.new_record?
Chris@0 211 issue.reload
Chris@0 212 assert issue.watched_by?(User.find_by_mail('dlopper@somenet.foo'))
chris@37 213 assert_equal 1, issue.watcher_user_ids.size
Chris@0 214 end
Chris@441 215
Chris@0 216 def test_add_issue_by_unknown_user
Chris@0 217 assert_no_difference 'User.count' do
Chris@1115 218 assert_equal false,
Chris@1115 219 submit_email(
Chris@1115 220 'ticket_by_unknown_user.eml',
Chris@1115 221 :issue => {:project => 'ecookbook'}
Chris@1115 222 )
Chris@0 223 end
Chris@0 224 end
Chris@441 225
Chris@0 226 def test_add_issue_by_anonymous_user
Chris@0 227 Role.anonymous.add_permission!(:add_issues)
Chris@0 228 assert_no_difference 'User.count' do
Chris@1115 229 issue = submit_email(
Chris@1115 230 'ticket_by_unknown_user.eml',
Chris@1115 231 :issue => {:project => 'ecookbook'},
Chris@1115 232 :unknown_user => 'accept'
Chris@1115 233 )
Chris@0 234 assert issue.is_a?(Issue)
Chris@0 235 assert issue.author.anonymous?
Chris@0 236 end
Chris@0 237 end
Chris@0 238
Chris@0 239 def test_add_issue_by_anonymous_user_with_no_from_address
Chris@0 240 Role.anonymous.add_permission!(:add_issues)
Chris@0 241 assert_no_difference 'User.count' do
Chris@1115 242 issue = submit_email(
Chris@1115 243 'ticket_by_empty_user.eml',
Chris@1115 244 :issue => {:project => 'ecookbook'},
Chris@1115 245 :unknown_user => 'accept'
Chris@1115 246 )
Chris@0 247 assert issue.is_a?(Issue)
Chris@0 248 assert issue.author.anonymous?
Chris@0 249 end
Chris@0 250 end
Chris@441 251
Chris@0 252 def test_add_issue_by_anonymous_user_on_private_project
Chris@0 253 Role.anonymous.add_permission!(:add_issues)
Chris@0 254 assert_no_difference 'User.count' do
Chris@0 255 assert_no_difference 'Issue.count' do
Chris@1115 256 assert_equal false,
Chris@1115 257 submit_email(
Chris@1115 258 'ticket_by_unknown_user.eml',
Chris@1115 259 :issue => {:project => 'onlinestore'},
Chris@1115 260 :unknown_user => 'accept'
Chris@1115 261 )
Chris@0 262 end
Chris@0 263 end
Chris@0 264 end
Chris@441 265
Chris@0 266 def test_add_issue_by_anonymous_user_on_private_project_without_permission_check
Chris@0 267 assert_no_difference 'User.count' do
Chris@0 268 assert_difference 'Issue.count' do
Chris@1115 269 issue = submit_email(
Chris@1115 270 'ticket_by_unknown_user.eml',
Chris@1115 271 :issue => {:project => 'onlinestore'},
Chris@1115 272 :no_permission_check => '1',
Chris@1115 273 :unknown_user => 'accept'
Chris@1115 274 )
Chris@0 275 assert issue.is_a?(Issue)
Chris@0 276 assert issue.author.anonymous?
Chris@0 277 assert !issue.project.is_public?
Chris@119 278 assert_equal [issue.id, 1, 2], [issue.root_id, issue.lft, issue.rgt]
Chris@0 279 end
Chris@0 280 end
Chris@0 281 end
Chris@441 282
Chris@0 283 def test_add_issue_by_created_user
Chris@0 284 Setting.default_language = 'en'
Chris@0 285 assert_difference 'User.count' do
Chris@1115 286 issue = submit_email(
Chris@1115 287 'ticket_by_unknown_user.eml',
Chris@1115 288 :issue => {:project => 'ecookbook'},
Chris@1115 289 :unknown_user => 'create'
Chris@1115 290 )
Chris@0 291 assert issue.is_a?(Issue)
Chris@0 292 assert issue.author.active?
Chris@0 293 assert_equal 'john.doe@somenet.foo', issue.author.mail
Chris@0 294 assert_equal 'John', issue.author.firstname
Chris@0 295 assert_equal 'Doe', issue.author.lastname
Chris@441 296
Chris@0 297 # account information
Chris@0 298 email = ActionMailer::Base.deliveries.first
Chris@0 299 assert_not_nil email
Chris@0 300 assert email.subject.include?('account activation')
Chris@1115 301 login = mail_body(email).match(/\* Login: (.*)$/)[1].strip
Chris@1115 302 password = mail_body(email).match(/\* Password: (.*)$/)[1].strip
Chris@0 303 assert_equal issue.author, User.try_to_login(login, password)
Chris@0 304 end
Chris@0 305 end
Chris@441 306
Chris@1464 307 def test_created_user_should_be_added_to_groups
Chris@1464 308 group1 = Group.generate!
Chris@1464 309 group2 = Group.generate!
Chris@1464 310
Chris@1464 311 assert_difference 'User.count' do
Chris@1464 312 submit_email(
Chris@1464 313 'ticket_by_unknown_user.eml',
Chris@1464 314 :issue => {:project => 'ecookbook'},
Chris@1464 315 :unknown_user => 'create',
Chris@1464 316 :default_group => "#{group1.name},#{group2.name}"
Chris@1464 317 )
Chris@1464 318 end
Chris@1464 319 user = User.order('id DESC').first
Chris@1464 320 assert_same_elements [group1, group2], user.groups
Chris@1464 321 end
Chris@1464 322
Chris@1464 323 def test_created_user_should_not_receive_account_information_with_no_account_info_option
Chris@1464 324 assert_difference 'User.count' do
Chris@1464 325 submit_email(
Chris@1464 326 'ticket_by_unknown_user.eml',
Chris@1464 327 :issue => {:project => 'ecookbook'},
Chris@1464 328 :unknown_user => 'create',
Chris@1464 329 :no_account_notice => '1'
Chris@1464 330 )
Chris@1464 331 end
Chris@1464 332
Chris@1464 333 # only 1 email for the new issue notification
Chris@1464 334 assert_equal 1, ActionMailer::Base.deliveries.size
Chris@1464 335 email = ActionMailer::Base.deliveries.first
Chris@1464 336 assert_include 'Ticket by unknown user', email.subject
Chris@1464 337 end
Chris@1464 338
Chris@1464 339 def test_created_user_should_have_mail_notification_to_none_with_no_notification_option
Chris@1464 340 assert_difference 'User.count' do
Chris@1464 341 submit_email(
Chris@1464 342 'ticket_by_unknown_user.eml',
Chris@1464 343 :issue => {:project => 'ecookbook'},
Chris@1464 344 :unknown_user => 'create',
Chris@1464 345 :no_notification => '1'
Chris@1464 346 )
Chris@1464 347 end
Chris@1464 348 user = User.order('id DESC').first
Chris@1464 349 assert_equal 'none', user.mail_notification
Chris@1464 350 end
Chris@1464 351
Chris@0 352 def test_add_issue_without_from_header
Chris@0 353 Role.anonymous.add_permission!(:add_issues)
Chris@0 354 assert_equal false, submit_email('ticket_without_from_header.eml')
Chris@0 355 end
chris@37 356
chris@37 357 def test_add_issue_with_invalid_attributes
Chris@1115 358 issue = submit_email(
Chris@1115 359 'ticket_with_invalid_attributes.eml',
Chris@1115 360 :allow_override => 'tracker,category,priority'
Chris@1115 361 )
chris@37 362 assert issue.is_a?(Issue)
chris@37 363 assert !issue.new_record?
chris@37 364 issue.reload
chris@37 365 assert_nil issue.assigned_to
chris@37 366 assert_nil issue.start_date
chris@37 367 assert_nil issue.due_date
chris@37 368 assert_equal 0, issue.done_ratio
chris@37 369 assert_equal 'Normal', issue.priority.to_s
chris@37 370 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
chris@37 371 end
chris@37 372
Chris@1464 373 def test_add_issue_with_invalid_project_should_be_assigned_to_default_project
Chris@1464 374 issue = submit_email('ticket_on_given_project.eml', :issue => {:project => 'ecookbook'}, :allow_override => 'project') do |email|
Chris@1464 375 email.gsub!(/^Project:.+$/, 'Project: invalid')
Chris@1464 376 end
Chris@1464 377 assert issue.is_a?(Issue)
Chris@1464 378 assert !issue.new_record?
Chris@1464 379 assert_equal 'ecookbook', issue.project.identifier
Chris@1464 380 end
Chris@1464 381
chris@37 382 def test_add_issue_with_localized_attributes
chris@37 383 User.find_by_mail('jsmith@somenet.foo').update_attribute 'language', 'fr'
Chris@1115 384 issue = submit_email(
Chris@1115 385 'ticket_with_localized_attributes.eml',
Chris@1115 386 :allow_override => 'tracker,category,priority'
Chris@1115 387 )
chris@37 388 assert issue.is_a?(Issue)
chris@37 389 assert !issue.new_record?
chris@37 390 issue.reload
chris@37 391 assert_equal 'New ticket on a given project', issue.subject
chris@37 392 assert_equal User.find_by_login('jsmith'), issue.author
chris@37 393 assert_equal Project.find(2), issue.project
chris@37 394 assert_equal 'Feature request', issue.tracker.to_s
chris@37 395 assert_equal 'Stock management', issue.category.to_s
chris@37 396 assert_equal 'Urgent', issue.priority.to_s
chris@37 397 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
chris@37 398 end
Chris@441 399
Chris@0 400 def test_add_issue_with_japanese_keywords
Chris@1115 401 ja_dev = "\xe9\x96\x8b\xe7\x99\xba"
Chris@1115 402 ja_dev.force_encoding('UTF-8') if ja_dev.respond_to?(:force_encoding)
Chris@1115 403 tracker = Tracker.create!(:name => ja_dev)
Chris@0 404 Project.find(1).trackers << tracker
Chris@1115 405 issue = submit_email(
Chris@1115 406 'japanese_keywords_iso_2022_jp.eml',
Chris@1115 407 :issue => {:project => 'ecookbook'},
Chris@1115 408 :allow_override => 'tracker'
Chris@1115 409 )
Chris@0 410 assert_kind_of Issue, issue
Chris@0 411 assert_equal tracker, issue.tracker
Chris@0 412 end
Chris@0 413
Chris@909 414 def test_add_issue_from_apple_mail
Chris@1115 415 issue = submit_email(
Chris@1115 416 'apple_mail_with_attachment.eml',
Chris@1115 417 :issue => {:project => 'ecookbook'}
Chris@1115 418 )
Chris@909 419 assert_kind_of Issue, issue
Chris@909 420 assert_equal 1, issue.attachments.size
Chris@909 421
Chris@909 422 attachment = issue.attachments.first
Chris@909 423 assert_equal 'paella.jpg', attachment.filename
Chris@909 424 assert_equal 10790, attachment.filesize
Chris@1115 425 assert File.exist?(attachment.diskfile)
Chris@1115 426 assert_equal 10790, File.size(attachment.diskfile)
Chris@1115 427 assert_equal 'caaf384198bcbc9563ab5c058acd73cd', attachment.digest
Chris@1115 428 end
Chris@1115 429
Chris@1115 430 def test_thunderbird_with_attachment_ja
Chris@1115 431 issue = submit_email(
Chris@1115 432 'thunderbird_with_attachment_ja.eml',
Chris@1115 433 :issue => {:project => 'ecookbook'}
Chris@1115 434 )
Chris@1115 435 assert_kind_of Issue, issue
Chris@1115 436 assert_equal 1, issue.attachments.size
Chris@1115 437 ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88.txt"
Chris@1115 438 ja.force_encoding('UTF-8') if ja.respond_to?(:force_encoding)
Chris@1115 439 attachment = issue.attachments.first
Chris@1115 440 assert_equal ja, attachment.filename
Chris@1115 441 assert_equal 5, attachment.filesize
Chris@1115 442 assert File.exist?(attachment.diskfile)
Chris@1115 443 assert_equal 5, File.size(attachment.diskfile)
Chris@1115 444 assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
Chris@1115 445 end
Chris@1115 446
Chris@1115 447 def test_gmail_with_attachment_ja
Chris@1115 448 issue = submit_email(
Chris@1115 449 'gmail_with_attachment_ja.eml',
Chris@1115 450 :issue => {:project => 'ecookbook'}
Chris@1115 451 )
Chris@1115 452 assert_kind_of Issue, issue
Chris@1115 453 assert_equal 1, issue.attachments.size
Chris@1115 454 ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88.txt"
Chris@1115 455 ja.force_encoding('UTF-8') if ja.respond_to?(:force_encoding)
Chris@1115 456 attachment = issue.attachments.first
Chris@1115 457 assert_equal ja, attachment.filename
Chris@1115 458 assert_equal 5, attachment.filesize
Chris@1115 459 assert File.exist?(attachment.diskfile)
Chris@1115 460 assert_equal 5, File.size(attachment.diskfile)
Chris@1115 461 assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
Chris@1115 462 end
Chris@1115 463
Chris@1115 464 def test_thunderbird_with_attachment_latin1
Chris@1115 465 issue = submit_email(
Chris@1115 466 'thunderbird_with_attachment_iso-8859-1.eml',
Chris@1115 467 :issue => {:project => 'ecookbook'}
Chris@1115 468 )
Chris@1115 469 assert_kind_of Issue, issue
Chris@1115 470 assert_equal 1, issue.attachments.size
Chris@1115 471 u = ""
Chris@1115 472 u.force_encoding('UTF-8') if u.respond_to?(:force_encoding)
Chris@1115 473 u1 = "\xc3\x84\xc3\xa4\xc3\x96\xc3\xb6\xc3\x9c\xc3\xbc"
Chris@1115 474 u1.force_encoding('UTF-8') if u1.respond_to?(:force_encoding)
Chris@1115 475 11.times { u << u1 }
Chris@1115 476 attachment = issue.attachments.first
Chris@1115 477 assert_equal "#{u}.png", attachment.filename
Chris@1115 478 assert_equal 130, attachment.filesize
Chris@1115 479 assert File.exist?(attachment.diskfile)
Chris@1115 480 assert_equal 130, File.size(attachment.diskfile)
Chris@1115 481 assert_equal '4d80e667ac37dddfe05502530f152abb', attachment.digest
Chris@1115 482 end
Chris@1115 483
Chris@1115 484 def test_gmail_with_attachment_latin1
Chris@1115 485 issue = submit_email(
Chris@1115 486 'gmail_with_attachment_iso-8859-1.eml',
Chris@1115 487 :issue => {:project => 'ecookbook'}
Chris@1115 488 )
Chris@1115 489 assert_kind_of Issue, issue
Chris@1115 490 assert_equal 1, issue.attachments.size
Chris@1115 491 u = ""
Chris@1115 492 u.force_encoding('UTF-8') if u.respond_to?(:force_encoding)
Chris@1115 493 u1 = "\xc3\x84\xc3\xa4\xc3\x96\xc3\xb6\xc3\x9c\xc3\xbc"
Chris@1115 494 u1.force_encoding('UTF-8') if u1.respond_to?(:force_encoding)
Chris@1115 495 11.times { u << u1 }
Chris@1115 496 attachment = issue.attachments.first
Chris@1115 497 assert_equal "#{u}.txt", attachment.filename
Chris@1115 498 assert_equal 5, attachment.filesize
Chris@1115 499 assert File.exist?(attachment.diskfile)
Chris@1115 500 assert_equal 5, File.size(attachment.diskfile)
Chris@1115 501 assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
Chris@1115 502 end
Chris@1115 503
Chris@1464 504 def test_multiple_inline_text_parts_should_be_appended_to_issue_description
Chris@1464 505 issue = submit_email('multiple_text_parts.eml', :issue => {:project => 'ecookbook'})
Chris@1464 506 assert_include 'first', issue.description
Chris@1464 507 assert_include 'second', issue.description
Chris@1464 508 assert_include 'third', issue.description
Chris@1464 509 end
Chris@1464 510
Chris@1464 511 def test_attachment_text_part_should_be_added_as_issue_attachment
Chris@1464 512 issue = submit_email('multiple_text_parts.eml', :issue => {:project => 'ecookbook'})
Chris@1464 513 assert_not_include 'Plain text attachment', issue.description
Chris@1464 514 attachment = issue.attachments.detect {|a| a.filename == 'textfile.txt'}
Chris@1464 515 assert_not_nil attachment
Chris@1464 516 assert_include 'Plain text attachment', File.read(attachment.diskfile)
Chris@1464 517 end
Chris@1464 518
Chris@1115 519 def test_add_issue_with_iso_8859_1_subject
Chris@1115 520 issue = submit_email(
Chris@1115 521 'subject_as_iso-8859-1.eml',
Chris@1115 522 :issue => {:project => 'ecookbook'}
Chris@1115 523 )
Chris@1115 524 str = "Testmail from Webmail: \xc3\xa4 \xc3\xb6 \xc3\xbc..."
Chris@1115 525 str.force_encoding('UTF-8') if str.respond_to?(:force_encoding)
Chris@1115 526 assert_kind_of Issue, issue
Chris@1115 527 assert_equal str, issue.subject
Chris@1115 528 end
Chris@1115 529
Chris@1115 530 def test_add_issue_with_japanese_subject
Chris@1115 531 issue = submit_email(
Chris@1115 532 'subject_japanese_1.eml',
Chris@1115 533 :issue => {:project => 'ecookbook'}
Chris@1115 534 )
Chris@1115 535 assert_kind_of Issue, issue
Chris@1115 536 ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88"
Chris@1115 537 ja.force_encoding('UTF-8') if ja.respond_to?(:force_encoding)
Chris@1115 538 assert_equal ja, issue.subject
Chris@1115 539 end
Chris@1115 540
Chris@1115 541 def test_add_issue_with_no_subject_header
Chris@1115 542 issue = submit_email(
Chris@1115 543 'no_subject_header.eml',
Chris@1115 544 :issue => {:project => 'ecookbook'}
Chris@1115 545 )
Chris@1115 546 assert_kind_of Issue, issue
Chris@1115 547 assert_equal '(no subject)', issue.subject
Chris@1115 548 end
Chris@1115 549
Chris@1115 550 def test_add_issue_with_mixed_japanese_subject
Chris@1115 551 issue = submit_email(
Chris@1115 552 'subject_japanese_2.eml',
Chris@1115 553 :issue => {:project => 'ecookbook'}
Chris@1115 554 )
Chris@1115 555 assert_kind_of Issue, issue
Chris@1115 556 ja = "Re: \xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88"
Chris@1115 557 ja.force_encoding('UTF-8') if ja.respond_to?(:force_encoding)
Chris@1115 558 assert_equal ja, issue.subject
Chris@1115 559 end
Chris@1115 560
Chris@1115 561 def test_should_ignore_emails_from_locked_users
Chris@1115 562 User.find(2).lock!
Chris@1115 563
Chris@1115 564 MailHandler.any_instance.expects(:dispatch).never
Chris@1115 565 assert_no_difference 'Issue.count' do
Chris@1115 566 assert_equal false, submit_email('ticket_on_given_project.eml')
Chris@1115 567 end
Chris@909 568 end
Chris@909 569
Chris@0 570 def test_should_ignore_emails_from_emission_address
Chris@0 571 Role.anonymous.add_permission!(:add_issues)
Chris@0 572 assert_no_difference 'User.count' do
Chris@1115 573 assert_equal false,
Chris@1115 574 submit_email(
Chris@1115 575 'ticket_from_emission_address.eml',
Chris@1115 576 :issue => {:project => 'ecookbook'},
Chris@1115 577 :unknown_user => 'create'
Chris@1115 578 )
Chris@1115 579 end
Chris@1115 580 end
Chris@1115 581
Chris@1115 582 def test_should_ignore_auto_replied_emails
Chris@1115 583 MailHandler.any_instance.expects(:dispatch).never
Chris@1115 584 [
Chris@1115 585 "X-Auto-Response-Suppress: OOF",
Chris@1115 586 "Auto-Submitted: auto-replied",
Chris@1115 587 "Auto-Submitted: Auto-Replied",
Chris@1115 588 "Auto-Submitted: auto-generated"
Chris@1115 589 ].each do |header|
Chris@1115 590 raw = IO.read(File.join(FIXTURES_PATH, 'ticket_on_given_project.eml'))
Chris@1115 591 raw = header + "\n" + raw
Chris@1115 592
Chris@1115 593 assert_no_difference 'Issue.count' do
Chris@1115 594 assert_equal false, MailHandler.receive(raw), "email with #{header} header was not ignored"
Chris@1115 595 end
Chris@0 596 end
Chris@0 597 end
Chris@0 598
Chris@0 599 def test_add_issue_should_send_email_notification
chris@37 600 Setting.notified_events = ['issue_added']
Chris@0 601 ActionMailer::Base.deliveries.clear
Chris@0 602 # This email contains: 'Project: onlinestore'
Chris@0 603 issue = submit_email('ticket_on_given_project.eml')
Chris@0 604 assert issue.is_a?(Issue)
Chris@0 605 assert_equal 1, ActionMailer::Base.deliveries.size
Chris@0 606 end
Chris@441 607
Chris@909 608 def test_update_issue
Chris@0 609 journal = submit_email('ticket_reply.eml')
Chris@0 610 assert journal.is_a?(Journal)
Chris@0 611 assert_equal User.find_by_login('jsmith'), journal.user
Chris@0 612 assert_equal Issue.find(2), journal.journalized
Chris@0 613 assert_match /This is reply/, journal.notes
Chris@1115 614 assert_equal false, journal.private_notes
Chris@119 615 assert_equal 'Feature request', journal.issue.tracker.name
Chris@0 616 end
Chris@0 617
Chris@909 618 def test_update_issue_with_attribute_changes
Chris@0 619 # This email contains: 'Status: Resolved'
Chris@0 620 journal = submit_email('ticket_reply_with_status.eml')
Chris@0 621 assert journal.is_a?(Journal)
Chris@0 622 issue = Issue.find(journal.issue.id)
Chris@0 623 assert_equal User.find_by_login('jsmith'), journal.user
Chris@0 624 assert_equal Issue.find(2), journal.journalized
Chris@0 625 assert_match /This is reply/, journal.notes
Chris@119 626 assert_equal 'Feature request', journal.issue.tracker.name
Chris@0 627 assert_equal IssueStatus.find_by_name("Resolved"), issue.status
Chris@0 628 assert_equal '2010-01-01', issue.start_date.to_s
Chris@0 629 assert_equal '2010-12-31', issue.due_date.to_s
Chris@0 630 assert_equal User.find_by_login('jsmith'), issue.assigned_to
Chris@119 631 assert_equal "52.6", issue.custom_value_for(CustomField.find_by_name('Float field')).value
Chris@441 632 # keywords should be removed from the email body
Chris@441 633 assert !journal.notes.match(/^Status:/i)
Chris@441 634 assert !journal.notes.match(/^Start Date:/i)
Chris@0 635 end
Chris@0 636
Chris@909 637 def test_update_issue_with_attachment
Chris@909 638 assert_difference 'Journal.count' do
Chris@909 639 assert_difference 'JournalDetail.count' do
Chris@909 640 assert_difference 'Attachment.count' do
Chris@909 641 assert_no_difference 'Issue.count' do
Chris@909 642 journal = submit_email('ticket_with_attachment.eml') do |raw|
Chris@909 643 raw.gsub! /^Subject: .*$/, 'Subject: Re: [Cookbook - Feature #2] (New) Add ingredients categories'
Chris@909 644 end
Chris@909 645 end
Chris@909 646 end
Chris@909 647 end
Chris@909 648 end
Chris@909 649 journal = Journal.first(:order => 'id DESC')
Chris@909 650 assert_equal Issue.find(2), journal.journalized
Chris@909 651 assert_equal 1, journal.details.size
Chris@909 652
Chris@909 653 detail = journal.details.first
Chris@909 654 assert_equal 'attachment', detail.property
Chris@909 655 assert_equal 'Paella.jpg', detail.value
Chris@909 656 end
Chris@909 657
Chris@909 658 def test_update_issue_should_send_email_notification
Chris@0 659 ActionMailer::Base.deliveries.clear
Chris@0 660 journal = submit_email('ticket_reply.eml')
Chris@0 661 assert journal.is_a?(Journal)
Chris@0 662 assert_equal 1, ActionMailer::Base.deliveries.size
Chris@0 663 end
Chris@441 664
Chris@909 665 def test_update_issue_should_not_set_defaults
Chris@1115 666 journal = submit_email(
Chris@1115 667 'ticket_reply.eml',
Chris@1115 668 :issue => {:tracker => 'Support request', :priority => 'High'}
Chris@1115 669 )
Chris@119 670 assert journal.is_a?(Journal)
Chris@119 671 assert_match /This is reply/, journal.notes
Chris@119 672 assert_equal 'Feature request', journal.issue.tracker.name
Chris@119 673 assert_equal 'Normal', journal.issue.priority.name
Chris@119 674 end
Chris@441 675
Chris@1115 676 def test_replying_to_a_private_note_should_add_reply_as_private
Chris@1115 677 private_journal = Journal.create!(:notes => 'Private notes', :journalized => Issue.find(1), :private_notes => true, :user_id => 2)
Chris@1115 678
Chris@1115 679 assert_difference 'Journal.count' do
Chris@1115 680 journal = submit_email('ticket_reply.eml') do |email|
Chris@1115 681 email.sub! %r{^In-Reply-To:.*$}, "In-Reply-To: <redmine.journal-#{private_journal.id}.20060719210421@osiris>"
Chris@1115 682 end
Chris@1115 683
Chris@1115 684 assert_kind_of Journal, journal
Chris@1115 685 assert_match /This is reply/, journal.notes
Chris@1115 686 assert_equal true, journal.private_notes
Chris@1115 687 end
Chris@1115 688 end
Chris@1115 689
Chris@0 690 def test_reply_to_a_message
Chris@0 691 m = submit_email('message_reply.eml')
Chris@0 692 assert m.is_a?(Message)
Chris@0 693 assert !m.new_record?
Chris@0 694 m.reload
Chris@0 695 assert_equal 'Reply via email', m.subject
Chris@0 696 # The email replies to message #2 which is part of the thread of message #1
Chris@0 697 assert_equal Message.find(1), m.parent
Chris@0 698 end
Chris@441 699
Chris@0 700 def test_reply_to_a_message_by_subject
Chris@0 701 m = submit_email('message_reply_by_subject.eml')
Chris@0 702 assert m.is_a?(Message)
Chris@0 703 assert !m.new_record?
Chris@0 704 m.reload
Chris@0 705 assert_equal 'Reply to the first post', m.subject
Chris@0 706 assert_equal Message.find(1), m.parent
Chris@0 707 end
Chris@441 708
Chris@0 709 def test_should_strip_tags_of_html_only_emails
Chris@0 710 issue = submit_email('ticket_html_only.eml', :issue => {:project => 'ecookbook'})
Chris@0 711 assert issue.is_a?(Issue)
Chris@0 712 assert !issue.new_record?
Chris@0 713 issue.reload
Chris@0 714 assert_equal 'HTML email', issue.subject
Chris@0 715 assert_equal 'This is a html-only email.', issue.description
Chris@0 716 end
Chris@0 717
Chris@1464 718 test "truncate emails with no setting should add the entire email into the issue" do
Chris@1464 719 with_settings :mail_handler_body_delimiters => '' do
Chris@1464 720 issue = submit_email('ticket_on_given_project.eml')
Chris@1464 721 assert_issue_created(issue)
Chris@1464 722 assert issue.description.include?('---')
Chris@1464 723 assert issue.description.include?('This paragraph is after the delimiter')
Chris@1464 724 end
Chris@1464 725 end
Chris@0 726
Chris@1464 727 test "truncate emails with a single string should truncate the email at the delimiter for the issue" do
Chris@1464 728 with_settings :mail_handler_body_delimiters => '---' do
Chris@1464 729 issue = submit_email('ticket_on_given_project.eml')
Chris@1464 730 assert_issue_created(issue)
Chris@1464 731 assert issue.description.include?('This paragraph is before delimiters')
Chris@1464 732 assert issue.description.include?('--- This line starts with a delimiter')
Chris@1464 733 assert !issue.description.match(/^---$/)
Chris@1464 734 assert !issue.description.include?('This paragraph is after the delimiter')
Chris@0 735 end
Chris@1464 736 end
Chris@0 737
Chris@1464 738 test "truncate emails with a single quoted reply should truncate the email at the delimiter with the quoted reply symbols (>)" do
Chris@1464 739 with_settings :mail_handler_body_delimiters => '--- Reply above. Do not remove this line. ---' do
Chris@1464 740 journal = submit_email('issue_update_with_quoted_reply_above.eml')
Chris@1464 741 assert journal.is_a?(Journal)
Chris@1464 742 assert journal.notes.include?('An update to the issue by the sender.')
Chris@1464 743 assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---"))
Chris@1464 744 assert !journal.notes.include?('Looks like the JSON api for projects was missed.')
Chris@0 745 end
Chris@1464 746 end
Chris@0 747
Chris@1464 748 test "truncate emails with multiple quoted replies should truncate the email at the delimiter with the quoted reply symbols (>)" do
Chris@1464 749 with_settings :mail_handler_body_delimiters => '--- Reply above. Do not remove this line. ---' do
Chris@1464 750 journal = submit_email('issue_update_with_multiple_quoted_reply_above.eml')
Chris@1464 751 assert journal.is_a?(Journal)
Chris@1464 752 assert journal.notes.include?('An update to the issue by the sender.')
Chris@1464 753 assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---"))
Chris@1464 754 assert !journal.notes.include?('Looks like the JSON api for projects was missed.')
Chris@441 755 end
Chris@1464 756 end
chris@37 757
Chris@1464 758 test "truncate emails with multiple strings should truncate the email at the first delimiter found (BREAK)" do
Chris@1464 759 with_settings :mail_handler_body_delimiters => "---\nBREAK" do
Chris@1464 760 issue = submit_email('ticket_on_given_project.eml')
Chris@1464 761 assert_issue_created(issue)
Chris@1464 762 assert issue.description.include?('This paragraph is before delimiters')
Chris@1464 763 assert !issue.description.include?('BREAK')
Chris@1464 764 assert !issue.description.include?('This paragraph is between delimiters')
Chris@1464 765 assert !issue.description.match(/^---$/)
Chris@1464 766 assert !issue.description.include?('This paragraph is after the delimiter')
chris@37 767 end
Chris@1464 768 end
chris@37 769
Chris@1464 770 def test_attachments_that_match_mail_handler_excluded_filenames_should_be_ignored
Chris@1464 771 with_settings :mail_handler_excluded_filenames => '*.vcf, *.jpg' do
Chris@1464 772 issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
Chris@1464 773 assert issue.is_a?(Issue)
Chris@1464 774 assert !issue.new_record?
Chris@1464 775 assert_equal 0, issue.reload.attachments.size
Chris@1464 776 end
Chris@1464 777 end
Chris@1464 778
Chris@1464 779 def test_attachments_that_do_not_match_mail_handler_excluded_filenames_should_be_attached
Chris@1464 780 with_settings :mail_handler_excluded_filenames => '*.vcf, *.gif' do
Chris@1464 781 issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
Chris@1464 782 assert issue.is_a?(Issue)
Chris@1464 783 assert !issue.new_record?
Chris@1464 784 assert_equal 1, issue.reload.attachments.size
Chris@0 785 end
Chris@0 786 end
Chris@441 787
Chris@0 788 def test_email_with_long_subject_line
Chris@0 789 issue = submit_email('ticket_with_long_subject.eml')
Chris@0 790 assert issue.is_a?(Issue)
Chris@0 791 assert_equal issue.subject, 'New ticket on a given project with a very long subject line which exceeds 255 chars and should not be ignored but chopped off. And if the subject line is still not long enough, we just add more text. And more text. Wow, this is really annoying. Especially, if you have nothing to say...'[0,255]
Chris@0 792 end
Chris@0 793
Chris@909 794 def test_new_user_from_attributes_should_return_valid_user
Chris@909 795 to_test = {
Chris@909 796 # [address, name] => [login, firstname, lastname]
Chris@909 797 ['jsmith@example.net', nil] => ['jsmith@example.net', 'jsmith', '-'],
Chris@909 798 ['jsmith@example.net', 'John'] => ['jsmith@example.net', 'John', '-'],
Chris@909 799 ['jsmith@example.net', 'John Smith'] => ['jsmith@example.net', 'John', 'Smith'],
Chris@909 800 ['jsmith@example.net', 'John Paul Smith'] => ['jsmith@example.net', 'John', 'Paul Smith'],
Chris@909 801 ['jsmith@example.net', 'AVeryLongFirstnameThatExceedsTheMaximumLength Smith'] => ['jsmith@example.net', 'AVeryLongFirstnameThatExceedsT', 'Smith'],
Chris@1115 802 ['jsmith@example.net', 'John AVeryLongLastnameThatExceedsTheMaximumLength'] => ['jsmith@example.net', 'John', 'AVeryLongLastnameThatExceedsTh']
Chris@909 803 }
Chris@909 804
Chris@909 805 to_test.each do |attrs, expected|
Chris@909 806 user = MailHandler.new_user_from_attributes(attrs.first, attrs.last)
Chris@909 807
Chris@1115 808 assert user.valid?, user.errors.full_messages.to_s
Chris@909 809 assert_equal attrs.first, user.mail
Chris@909 810 assert_equal expected[0], user.login
Chris@909 811 assert_equal expected[1], user.firstname
Chris@909 812 assert_equal expected[2], user.lastname
Chris@1464 813 assert_equal 'only_my_events', user.mail_notification
Chris@909 814 end
Chris@909 815 end
Chris@1115 816
Chris@909 817 def test_new_user_from_attributes_should_use_default_login_if_invalid
Chris@1115 818 user = MailHandler.new_user_from_attributes('foo+bar@example.net')
Chris@909 819 assert user.valid?
Chris@909 820 assert user.login =~ /^user[a-f0-9]+$/
Chris@1115 821 assert_equal 'foo+bar@example.net', user.mail
Chris@1115 822 end
Chris@1115 823
Chris@1115 824 def test_new_user_with_utf8_encoded_fullname_should_be_decoded
Chris@1115 825 assert_difference 'User.count' do
Chris@1115 826 issue = submit_email(
Chris@1115 827 'fullname_of_sender_as_utf8_encoded.eml',
Chris@1115 828 :issue => {:project => 'ecookbook'},
Chris@1115 829 :unknown_user => 'create'
Chris@1115 830 )
Chris@1115 831 end
Chris@1115 832
Chris@1115 833 user = User.first(:order => 'id DESC')
Chris@1115 834 assert_equal "foo@example.org", user.mail
Chris@1115 835 str1 = "\xc3\x84\xc3\xa4"
Chris@1115 836 str2 = "\xc3\x96\xc3\xb6"
Chris@1115 837 str1.force_encoding('UTF-8') if str1.respond_to?(:force_encoding)
Chris@1115 838 str2.force_encoding('UTF-8') if str2.respond_to?(:force_encoding)
Chris@1115 839 assert_equal str1, user.firstname
Chris@1115 840 assert_equal str2, user.lastname
Chris@909 841 end
Chris@909 842
Chris@1464 843 def test_extract_options_from_env_should_return_options
Chris@1464 844 options = MailHandler.extract_options_from_env({
Chris@1464 845 'tracker' => 'defect',
Chris@1464 846 'project' => 'foo',
Chris@1464 847 'unknown_user' => 'create'
Chris@1464 848 })
Chris@1464 849
Chris@1464 850 assert_equal({
Chris@1464 851 :issue => {:tracker => 'defect', :project => 'foo'},
Chris@1464 852 :unknown_user => 'create'
Chris@1464 853 }, options)
Chris@1464 854 end
Chris@1464 855
Chris@0 856 private
Chris@441 857
Chris@0 858 def submit_email(filename, options={})
Chris@0 859 raw = IO.read(File.join(FIXTURES_PATH, filename))
Chris@909 860 yield raw if block_given?
Chris@0 861 MailHandler.receive(raw, options)
Chris@0 862 end
Chris@0 863
Chris@0 864 def assert_issue_created(issue)
Chris@0 865 assert issue.is_a?(Issue)
Chris@0 866 assert !issue.new_record?
Chris@0 867 issue.reload
Chris@0 868 end
Chris@0 869 end