You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

mail_handler_test.rb 30KB

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