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 50KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402
  1. # frozen_string_literal: true
  2. # Redmine - project management software
  3. # Copyright (C) 2006-2022 Jean-Philippe Lang
  4. #
  5. # This program is free software; you can redistribute it and/or
  6. # modify it under the terms of the GNU General Public License
  7. # as published by the Free Software Foundation; either version 2
  8. # of the License, or (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, write to the Free Software
  17. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. require File.expand_path('../../test_helper', __FILE__)
  19. class MailHandlerTest < ActiveSupport::TestCase
  20. fixtures :users, :projects, :enabled_modules, :roles,
  21. :members, :member_roles, :users,
  22. :email_addresses, :user_preferences,
  23. :issues, :issue_statuses,
  24. :journals, :journal_details,
  25. :workflows, :trackers, :projects_trackers,
  26. :versions, :enumerations, :issue_categories,
  27. :custom_fields, :custom_fields_trackers, :custom_fields_projects, :custom_values,
  28. :boards, :messages, :watchers
  29. FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures/mail_handler'
  30. def setup
  31. ActionMailer::Base.deliveries.clear
  32. Setting.notified_events = Redmine::Notifiable.all.collect(&:name)
  33. User.current = nil
  34. end
  35. def teardown
  36. Setting.clear_cache
  37. end
  38. def test_add_issue_with_specific_overrides
  39. issue =
  40. submit_email(
  41. 'ticket_on_given_project.eml',
  42. :allow_override =>
  43. ['status', 'start_date', 'due_date', 'assigned_to',
  44. 'fixed_version', 'estimated_hours', 'done_ratio',
  45. 'parent_issue']
  46. )
  47. assert issue.is_a?(Issue)
  48. assert !issue.new_record?
  49. issue.reload
  50. assert_equal Project.find(2), issue.project
  51. assert_equal issue.project.trackers.first, issue.tracker
  52. assert_equal 'New ticket on a given project', issue.subject
  53. assert_equal User.find_by_login('jsmith'), issue.author
  54. assert_equal IssueStatus.find_by_name('Resolved'), issue.status
  55. assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
  56. assert_equal '2010-01-01', issue.start_date.to_s
  57. assert_equal '2010-12-31', issue.due_date.to_s
  58. assert_equal User.find_by_login('jsmith'), issue.assigned_to
  59. assert_equal Version.find_by_name('Alpha'), issue.fixed_version
  60. assert_equal 2.5, issue.estimated_hours
  61. assert_equal 30, issue.done_ratio
  62. assert_equal Issue.find(4), issue.parent
  63. # keywords should be removed from the email body
  64. assert !issue.description.match(/^Project:/i)
  65. assert !issue.description.match(/^Status:/i)
  66. assert !issue.description.match(/^Start Date:/i)
  67. end
  68. def test_add_issue_with_all_overrides
  69. issue = submit_email('ticket_on_given_project.eml', :allow_override => 'all')
  70. assert issue.is_a?(Issue)
  71. assert !issue.new_record?
  72. issue.reload
  73. assert_equal Project.find(2), issue.project
  74. assert_equal issue.project.trackers.first, issue.tracker
  75. assert_equal IssueStatus.find_by_name('Resolved'), issue.status
  76. assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
  77. assert_equal '2010-01-01', issue.start_date.to_s
  78. assert_equal '2010-12-31', issue.due_date.to_s
  79. assert_equal User.find_by_login('jsmith'), issue.assigned_to
  80. assert_equal Version.find_by_name('Alpha'), issue.fixed_version
  81. assert_equal 2.5, issue.estimated_hours
  82. assert_equal 30, issue.done_ratio
  83. assert_equal Issue.find(4), issue.parent
  84. end
  85. def test_add_issue_without_overrides_should_ignore_attributes
  86. WorkflowRule.delete_all
  87. issue = submit_email('ticket_on_given_project.eml')
  88. assert issue.is_a?(Issue)
  89. assert !issue.new_record?
  90. issue.reload
  91. assert_equal Project.find(2), issue.project
  92. assert_equal 'New ticket on a given project', issue.subject
  93. assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
  94. assert_equal User.find_by_login('jsmith'), issue.author
  95. assert_equal issue.project.trackers.first, issue.tracker
  96. assert_equal 'New', issue.status.name
  97. assert_not_equal '2010-01-01', issue.start_date.to_s
  98. assert_nil issue.due_date
  99. assert_nil issue.assigned_to
  100. assert_nil issue.fixed_version
  101. assert_nil issue.estimated_hours
  102. assert_equal 0, issue.done_ratio
  103. assert_nil issue.parent
  104. end
  105. def test_add_issue_to_project_specified_by_subaddress
  106. # This email has redmine+onlinestore@somenet.foo as 'To' header
  107. issue =
  108. submit_email(
  109. 'ticket_on_project_given_by_to_header.eml',
  110. :issue => {:tracker => 'Support request'},
  111. :project_from_subaddress => 'redmine@somenet.foo'
  112. )
  113. assert issue.is_a?(Issue)
  114. assert !issue.new_record?
  115. issue.reload
  116. assert_equal 'onlinestore', issue.project.identifier
  117. assert_equal 'Support request', issue.tracker.name
  118. end
  119. def test_add_issue_with_default_tracker
  120. # This email contains: 'Project: onlinestore'
  121. issue =
  122. submit_email(
  123. 'ticket_on_given_project.eml',
  124. :issue => {:tracker => 'Support request'}
  125. )
  126. assert issue.is_a?(Issue)
  127. assert !issue.new_record?
  128. issue.reload
  129. assert_equal 'Support request', issue.tracker.name
  130. end
  131. def test_add_issue_with_default_version
  132. # This email contains: 'Project: onlinestore'
  133. issue =
  134. submit_email(
  135. 'ticket_on_given_project.eml',
  136. :issue => {:fixed_version => 'Alpha'}
  137. )
  138. assert issue.is_a?(Issue)
  139. assert !issue.new_record?
  140. assert_equal 'Alpha', issue.reload.fixed_version.name
  141. end
  142. def test_add_issue_with_default_assigned_to
  143. # This email contains: 'Project: onlinestore'
  144. issue =
  145. submit_email(
  146. 'ticket_on_given_project.eml',
  147. :issue => {:assigned_to => 'jsmith'}
  148. )
  149. assert issue.is_a?(Issue)
  150. assert !issue.new_record?
  151. assert_equal 'jsmith', issue.reload.assigned_to.login
  152. end
  153. def test_add_issue_with_status_override
  154. # This email contains: 'Project: onlinestore' and 'Status: Resolved'
  155. issue = submit_email('ticket_on_given_project.eml', :allow_override => ['status'])
  156. assert issue.is_a?(Issue)
  157. assert !issue.new_record?
  158. issue.reload
  159. assert_equal Project.find(2), issue.project
  160. assert_equal IssueStatus.find_by_name("Resolved"), issue.status
  161. end
  162. def test_add_issue_should_accept_is_private_attribute
  163. issue = submit_email('ticket_on_given_project.eml', :issue => {:is_private => '1'})
  164. assert issue.is_a?(Issue)
  165. assert !issue.new_record?
  166. assert_equal true, issue.reload.is_private
  167. end
  168. def test_add_issue_with_group_assignment
  169. with_settings :issue_group_assignment => '1' do
  170. issue = submit_email('ticket_on_given_project.eml', :allow_override => ['assigned_to']) do |email|
  171. email.gsub!('Assigned to: John Smith', 'Assigned to: B Team')
  172. end
  173. assert issue.is_a?(Issue)
  174. assert !issue.new_record?
  175. issue.reload
  176. assert_equal Group.find(11), issue.assigned_to
  177. end
  178. end
  179. def test_add_issue_with_partial_attributes_override
  180. issue =
  181. submit_email(
  182. 'ticket_with_attributes.eml',
  183. :issue => {:priority => 'High'},
  184. :allow_override => ['tracker']
  185. )
  186. assert issue.is_a?(Issue)
  187. assert !issue.new_record?
  188. issue.reload
  189. assert_equal 'New ticket on a given project', issue.subject
  190. assert_equal User.find_by_login('jsmith'), issue.author
  191. assert_equal Project.find(2), issue.project
  192. assert_equal 'Feature request', issue.tracker.to_s
  193. assert_nil issue.category
  194. assert_equal 'High', issue.priority.to_s
  195. assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
  196. end
  197. def test_add_issue_with_spaces_between_attribute_and_separator
  198. issue =
  199. submit_email(
  200. 'ticket_with_spaces_between_attribute_and_separator.eml',
  201. :allow_override => 'tracker,category,priority'
  202. )
  203. assert issue.is_a?(Issue)
  204. assert !issue.new_record?
  205. issue.reload
  206. assert_equal 'New ticket on a given project', issue.subject
  207. assert_equal User.find_by_login('jsmith'), issue.author
  208. assert_equal Project.find(2), issue.project
  209. assert_equal 'Feature request', issue.tracker.to_s
  210. assert_equal 'Stock management', issue.category.to_s
  211. assert_equal 'Urgent', issue.priority.to_s
  212. assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
  213. end
  214. def test_add_issue_with_attachment_to_specific_project
  215. issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
  216. assert issue.is_a?(Issue)
  217. assert !issue.new_record?
  218. issue.reload
  219. assert_equal 'Ticket created by email with attachment', issue.subject
  220. assert_equal User.find_by_login('jsmith'), issue.author
  221. assert_equal Project.find(2), issue.project
  222. assert_equal 'This is a new ticket with attachments', issue.description
  223. # Attachment properties
  224. assert_equal 1, issue.attachments.size
  225. assert_equal 'Paella.jpg', issue.attachments.first.filename
  226. assert_equal 'image/jpeg', issue.attachments.first.content_type
  227. assert_equal 10790, issue.attachments.first.filesize
  228. end
  229. def test_add_issue_with_custom_fields
  230. mutiple = IssueCustomField.generate!(:field_format => 'list',
  231. :name => 'OS', :multiple => true,
  232. :possible_values => ['Linux', 'Windows', 'Mac OS X'])
  233. issue =
  234. submit_email(
  235. 'ticket_with_custom_fields.eml',
  236. :issue => {:project => 'onlinestore'},
  237. :allow_override => ['database', 'Searchable_field', 'OS']
  238. )
  239. assert issue.is_a?(Issue)
  240. assert !issue.new_record?
  241. issue.reload
  242. assert_equal 'New ticket with custom field values', issue.subject
  243. assert_equal 'PostgreSQL', issue.custom_field_value(1)
  244. assert_equal 'Value for a custom field', issue.custom_field_value(2)
  245. assert_equal ['Mac OS X', 'Windows'], issue.custom_field_value(mutiple.id).sort
  246. assert !issue.description.match(/^searchable field:/i)
  247. end
  248. def test_add_issue_with_version_custom_fields
  249. field = IssueCustomField.create!(:name => 'Affected version',
  250. :field_format => 'version',
  251. :is_for_all => true,
  252. :tracker_ids => [1, 2, 3])
  253. issue =
  254. submit_email(
  255. 'ticket_with_custom_fields.eml',
  256. :issue => {:project => 'ecookbook'},
  257. :allow_override => ['affected version']
  258. ) do |email|
  259. email << "Affected version: 1.0\n"
  260. end
  261. assert issue.is_a?(Issue)
  262. assert !issue.new_record?
  263. issue.reload
  264. assert_equal '2', issue.custom_field_value(field)
  265. end
  266. def test_add_issue_should_match_assignee_on_display_name
  267. user = User.generate!(:firstname => 'Foo Bar', :lastname => 'Foo Baz')
  268. User.add_to_project(user, Project.find(2))
  269. issue = submit_email('ticket_on_given_project.eml', :allow_override => ['assigned_to']) do |email|
  270. email.sub!(/^Assigned to.*$/, 'Assigned to: Foo Bar Foo baz')
  271. end
  272. assert issue.is_a?(Issue)
  273. assert_equal user, issue.assigned_to
  274. end
  275. def test_add_issue_should_set_default_start_date
  276. with_settings :default_issue_start_date_to_creation_date => '1' do
  277. issue = submit_email('ticket_with_cc.eml', :issue => {:project => 'ecookbook'})
  278. assert issue.is_a?(Issue)
  279. assert_equal Date.today, issue.start_date
  280. end
  281. end
  282. def test_add_issue_should_add_cc_as_watchers
  283. user = User.find_by_mail('dlopper@somenet.foo')
  284. issue = submit_email('ticket_with_cc.eml', :issue => {:project => 'ecookbook'})
  285. assert issue.is_a?(Issue)
  286. assert !issue.new_record?
  287. assert issue.watched_by?(user)
  288. assert_equal 1, issue.watcher_user_ids.size
  289. assert_include user, issue.watcher_users.to_a
  290. end
  291. def test_add_issue_from_additional_email_address
  292. user = User.find(2)
  293. user.mail = 'mainaddress@somenet.foo'
  294. user.save!
  295. EmailAddress.create!(:user => user, :address => 'jsmith@somenet.foo')
  296. issue = submit_email('ticket_on_given_project.eml')
  297. assert issue
  298. assert_equal user, issue.author
  299. end
  300. def test_add_issue_by_unknown_user
  301. assert_no_difference 'User.count' do
  302. assert_equal(
  303. false,
  304. submit_email(
  305. 'ticket_by_unknown_user.eml',
  306. :issue => {:project => 'ecookbook'}
  307. )
  308. )
  309. end
  310. end
  311. def test_add_issue_by_anonymous_user
  312. Role.anonymous.add_permission!(:add_issues)
  313. Role.anonymous.add_permission!(:add_issue_watchers)
  314. assert_no_difference 'User.count' do
  315. issue =
  316. submit_email(
  317. 'ticket_by_unknown_user.eml',
  318. :issue => {:project => 'ecookbook'},
  319. :unknown_user => 'accept'
  320. )
  321. assert issue.is_a?(Issue)
  322. assert issue.author.anonymous?
  323. issue.reload
  324. assert issue.watched_by?(User.find_by_mail('dlopper@somenet.foo'))
  325. assert_equal 1, issue.watchers.size
  326. end
  327. end
  328. def test_add_issue_by_anonymous_user_with_no_from_address
  329. Role.anonymous.add_permission!(:add_issues)
  330. assert_no_difference 'User.count' do
  331. issue =
  332. submit_email(
  333. 'ticket_by_empty_user.eml',
  334. :issue => {:project => 'ecookbook'},
  335. :unknown_user => 'accept'
  336. )
  337. assert issue.is_a?(Issue)
  338. assert issue.author.anonymous?
  339. end
  340. end
  341. def test_add_issue_by_anonymous_user_on_private_project
  342. Role.anonymous.add_permission!(:add_issues)
  343. assert_no_difference 'User.count' do
  344. assert_no_difference 'Issue.count' do
  345. assert_equal(
  346. false,
  347. submit_email(
  348. 'ticket_by_unknown_user.eml',
  349. :issue => {:project => 'onlinestore'},
  350. :unknown_user => 'accept'
  351. )
  352. )
  353. end
  354. end
  355. end
  356. def test_add_issue_by_anonymous_user_on_private_project_without_permission_check
  357. assert_no_difference 'User.count' do
  358. assert_difference 'Issue.count' do
  359. issue =
  360. submit_email(
  361. 'ticket_by_unknown_user.eml',
  362. :issue => {:project => 'onlinestore'},
  363. :no_permission_check => '1',
  364. :unknown_user => 'accept'
  365. )
  366. assert issue.is_a?(Issue)
  367. assert issue.author.anonymous?
  368. assert !issue.project.is_public?
  369. end
  370. end
  371. end
  372. def test_add_issue_by_created_user
  373. Setting.default_language = 'en'
  374. assert_difference 'User.count' do
  375. issue =
  376. submit_email(
  377. 'ticket_by_unknown_user.eml',
  378. :issue => {:project => 'ecookbook'},
  379. :unknown_user => 'create'
  380. )
  381. assert issue.is_a?(Issue)
  382. assert issue.author.active?
  383. assert_equal 'john.doe@somenet.foo', issue.author.mail
  384. assert_equal 'John', issue.author.firstname
  385. assert_equal 'Doe', issue.author.lastname
  386. # account information
  387. email = ActionMailer::Base.deliveries.first
  388. assert_not_nil email
  389. assert email.subject.include?('account activation')
  390. login = mail_body(email).match(/\* Login: (.*)$/)[1].strip
  391. password = mail_body(email).match(/\* Password: (.*)$/)[1].strip
  392. assert_equal issue.author, User.try_to_login(login, password)
  393. end
  394. end
  395. def test_add_issue_should_send_notification
  396. issue = submit_email('ticket_on_given_project.eml', :allow_override => 'all')
  397. assert issue.is_a?(Issue)
  398. assert !issue.new_record?
  399. assert_equal 4, issue.parent_issue_id
  400. assert_equal 2, ActionMailer::Base.deliveries.size
  401. [
  402. [issue.id, 'New ticket on a given project'],
  403. [4, 'Issue on project 2'],
  404. ].each do |issue_id, issue_subject|
  405. mail =
  406. ActionMailer::Base.deliveries.detect do |m|
  407. /##{issue_id}/.match?(m.subject) && /#{issue_subject}/.match?(m.subject)
  408. end
  409. assert_not_nil mail
  410. end
  411. end
  412. def test_created_user_should_be_added_to_groups
  413. group1 = Group.generate!
  414. group2 = Group.generate!
  415. assert_difference 'User.count' do
  416. submit_email(
  417. 'ticket_by_unknown_user.eml',
  418. :issue => {:project => 'ecookbook'},
  419. :unknown_user => 'create',
  420. :default_group => "#{group1.name},#{group2.name}"
  421. )
  422. end
  423. user = User.order('id DESC').first
  424. assert_equal [group1, group2].sort, user.groups.sort
  425. end
  426. def test_created_user_should_not_receive_account_information_with_no_account_info_option
  427. assert_difference 'User.count' do
  428. submit_email(
  429. 'ticket_by_unknown_user.eml',
  430. :issue => {:project => 'ecookbook'},
  431. :unknown_user => 'create',
  432. :no_account_notice => '1'
  433. )
  434. end
  435. # only 2 emails for the new issue notification
  436. assert_equal 2, ActionMailer::Base.deliveries.size
  437. ActionMailer::Base.deliveries.each do |email|
  438. assert_include 'Ticket by unknown user', email.subject
  439. end
  440. end
  441. def test_created_user_should_have_mail_notification_to_none_with_no_notification_option
  442. assert_difference 'User.count' do
  443. submit_email(
  444. 'ticket_by_unknown_user.eml',
  445. :issue => {:project => 'ecookbook'},
  446. :unknown_user => 'create',
  447. :no_notification => '1'
  448. )
  449. end
  450. user = User.order('id DESC').first
  451. assert_equal 'none', user.mail_notification
  452. end
  453. def test_add_issue_without_from_header
  454. Role.anonymous.add_permission!(:add_issues)
  455. assert_equal false, submit_email('ticket_without_from_header.eml')
  456. end
  457. def test_add_issue_with_invalid_attributes
  458. with_settings :default_issue_start_date_to_creation_date => '0' do
  459. issue =
  460. submit_email(
  461. 'ticket_with_invalid_attributes.eml',
  462. :allow_override => 'tracker,category,priority'
  463. )
  464. assert issue.is_a?(Issue)
  465. assert !issue.new_record?
  466. issue.reload
  467. assert_nil issue.assigned_to
  468. assert_nil issue.start_date
  469. assert_nil issue.due_date
  470. assert_equal 0, issue.done_ratio
  471. assert_nil issue.parent
  472. assert_equal 'Normal', issue.priority.to_s
  473. assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
  474. end
  475. end
  476. def test_add_issue_with_invalid_project_should_be_assigned_to_default_project
  477. issue =
  478. submit_email(
  479. 'ticket_on_given_project.eml',
  480. :issue => {:project => 'ecookbook'},
  481. :allow_override => 'project'
  482. ) do |email|
  483. email.gsub!(/^Project:.+$/, 'Project: invalid')
  484. end
  485. assert issue.is_a?(Issue)
  486. assert !issue.new_record?
  487. assert_equal 'ecookbook', issue.project.identifier
  488. end
  489. def test_add_issue_with_private_keyword
  490. User.find_by_mail('jsmith@somenet.foo').update_attribute 'language', 'fr'
  491. # give the user permission to set issues private:
  492. MemberRole.create! member_id: 3, role_id: 1
  493. issue =
  494. submit_email(
  495. 'ticket_with_localized_private_flag.eml',
  496. :allow_override => 'is_private,tracker,category,priority'
  497. )
  498. assert issue.is_a?(Issue)
  499. assert_not issue.new_record?
  500. issue.reload
  501. assert_equal 'New ticket on a given project', issue.subject
  502. assert issue.is_private
  503. end
  504. def test_add_issue_with_localized_attributes
  505. User.find_by_mail('jsmith@somenet.foo').update_attribute 'language', 'fr'
  506. issue =
  507. submit_email(
  508. 'ticket_with_localized_attributes.eml',
  509. :allow_override => 'tracker,category,priority'
  510. )
  511. assert issue.is_a?(Issue)
  512. assert !issue.new_record?
  513. issue.reload
  514. assert_equal 'New ticket on a given project', issue.subject
  515. assert_equal User.find_by_login('jsmith'), issue.author
  516. assert_equal Project.find(2), issue.project
  517. assert_equal 'Feature request', issue.tracker.to_s
  518. assert_equal 'Stock management', issue.category.to_s
  519. assert_equal 'Urgent', issue.priority.to_s
  520. assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
  521. end
  522. def test_add_issue_with_japanese_keywords
  523. tracker = Tracker.generate!(:name => '開発')
  524. Project.find(1).trackers << tracker
  525. issue =
  526. submit_email(
  527. 'japanese_keywords_iso_2022_jp.eml',
  528. :issue => {:project => 'ecookbook'},
  529. :allow_override => 'tracker'
  530. )
  531. assert_kind_of Issue, issue
  532. assert_equal tracker, issue.tracker
  533. end
  534. def test_add_issue_from_apple_mail
  535. set_tmp_attachments_directory
  536. issue =
  537. submit_email(
  538. 'apple_mail_with_attachment.eml',
  539. :issue => {:project => 'ecookbook'}
  540. )
  541. assert_kind_of Issue, issue
  542. assert_equal 1, issue.attachments.size
  543. attachment = issue.attachments.first
  544. assert_equal 'paella.jpg', attachment.filename
  545. assert_equal 10790, attachment.filesize
  546. assert File.exist?(attachment.diskfile)
  547. assert_equal 10790, File.size(attachment.diskfile)
  548. assert_equal '4474dd534c36bdd212e2efc549507377c3e77147c9167b66dedcebfe9da8807f', attachment.digest
  549. end
  550. def test_thunderbird_with_attachment_ja
  551. set_tmp_attachments_directory
  552. issue =
  553. submit_email(
  554. 'thunderbird_with_attachment_ja.eml',
  555. :issue => {:project => 'ecookbook'}
  556. )
  557. assert_kind_of Issue, issue
  558. assert_equal 1, issue.attachments.size
  559. attachment = issue.attachments.first
  560. assert_equal 'テスト.txt', attachment.filename
  561. assert_equal 5, attachment.filesize
  562. assert File.exist?(attachment.diskfile)
  563. assert_equal 5, File.size(attachment.diskfile)
  564. assert_equal 'f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2', attachment.digest
  565. end
  566. def test_invalid_utf8
  567. issue =
  568. submit_email(
  569. 'invalid_utf8.eml',
  570. :issue => {:project => 'ecookbook'}
  571. )
  572. assert_kind_of Issue, issue
  573. assert_equal 'Здравствуйте?', issue.description
  574. end
  575. def test_gmail_with_attachment_ja
  576. set_tmp_attachments_directory
  577. issue =
  578. submit_email(
  579. 'gmail_with_attachment_ja.eml',
  580. :issue => {:project => 'ecookbook'}
  581. )
  582. assert_kind_of Issue, issue
  583. assert_equal 1, issue.attachments.size
  584. attachment = issue.attachments.first
  585. assert_equal 'テスト.txt', attachment.filename
  586. assert_equal 5, attachment.filesize
  587. assert File.exist?(attachment.diskfile)
  588. assert_equal 5, File.size(attachment.diskfile)
  589. assert_equal 'f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2', attachment.digest
  590. end
  591. def test_thunderbird_with_attachment_latin1
  592. set_tmp_attachments_directory
  593. issue =
  594. submit_email(
  595. 'thunderbird_with_attachment_iso-8859-1.eml',
  596. :issue => {:project => 'ecookbook'}
  597. )
  598. assert_kind_of Issue, issue
  599. assert_equal 1, issue.attachments.size
  600. u = +''
  601. u1 = 'ÄäÖöÜü'
  602. 11.times {u << u1}
  603. attachment = issue.attachments.first
  604. assert_equal "#{u}.png", attachment.filename
  605. assert_equal 130, attachment.filesize
  606. assert File.exist?(attachment.diskfile)
  607. assert_equal 130, File.size(attachment.diskfile)
  608. assert_equal '5635d67364de20432247e651dfe86fcb2265ad5e9750bd8bba7319a86363e738', attachment.digest
  609. end
  610. def test_gmail_with_attachment_latin1
  611. set_tmp_attachments_directory
  612. issue =
  613. submit_email(
  614. 'gmail_with_attachment_iso-8859-1.eml',
  615. :issue => {:project => 'ecookbook'}
  616. )
  617. assert_kind_of Issue, issue
  618. assert_equal 1, issue.attachments.size
  619. u = +''
  620. u1 = 'ÄäÖöÜü'
  621. 11.times {u << u1}
  622. attachment = issue.attachments.first
  623. assert_equal "#{u}.txt", attachment.filename
  624. assert_equal 5, attachment.filesize
  625. assert File.exist?(attachment.diskfile)
  626. assert_equal 5, File.size(attachment.diskfile)
  627. assert_equal 'f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2', attachment.digest
  628. end
  629. def test_mail_with_attachment_latin2
  630. set_tmp_attachments_directory
  631. issue =
  632. submit_email(
  633. 'ticket_with_text_attachment_iso-8859-2.eml',
  634. :issue => {:project => 'ecookbook'}
  635. )
  636. assert_kind_of Issue, issue
  637. assert_equal 1, issue.attachments.size
  638. attachment = issue.attachments.first
  639. assert_equal 'latin2.txt', attachment.filename
  640. assert_equal 19, attachment.filesize
  641. assert File.exist?(attachment.diskfile)
  642. assert_equal 19, File.size(attachment.diskfile)
  643. content = (+"p\xF8\xEDli\xB9 \xBEluou\xE8k\xFD k\xF9n").force_encoding('CP852')
  644. assert_equal content, File.read(attachment.diskfile).force_encoding('CP852')
  645. end
  646. def test_empty_attachment_should_not_be_imported
  647. issue =
  648. submit_email(
  649. 'ticket_with_empty_attachment.eml',
  650. :issue => {:project => 'ecookbook'}
  651. )
  652. assert_equal 0, issue.attachments.size
  653. end
  654. def test_multiple_inline_text_parts_should_be_appended_to_issue_description
  655. issue = submit_email('multiple_text_parts.eml', :issue => {:project => 'ecookbook'})
  656. assert_include 'first', issue.description
  657. assert_include 'second', issue.description
  658. assert_include 'third', issue.description
  659. end
  660. def test_empty_text_part_should_not_stop_looking_for_content
  661. issue = submit_email('empty_text_part.eml', :issue => {:project => 'ecookbook'})
  662. assert_equal 'The html part.', issue.description
  663. end
  664. def test_empty_text_and_html_part_should_make_an_empty_description
  665. issue = submit_email('empty_text_and_html_part.eml', :issue => {:project => 'ecookbook'})
  666. assert_equal '', issue.description
  667. end
  668. def test_preferred_body_part_setting
  669. with_settings :mail_handler_preferred_body_part => 'plain' do
  670. issue = submit_email('different_contents_in_text_and_html.eml', :issue => {:project => 'ecookbook'})
  671. assert_equal 'The text part.', issue.description
  672. end
  673. with_settings :mail_handler_preferred_body_part => 'html' do
  674. issue = submit_email('different_contents_in_text_and_html.eml', :issue => {:project => 'ecookbook'})
  675. assert_equal 'The html part.', issue.description
  676. end
  677. end
  678. def test_attachment_text_part_should_be_added_as_issue_attachment
  679. issue = submit_email('multiple_text_parts.eml', :issue => {:project => 'ecookbook'})
  680. assert_not_include 'Plain text attachment', issue.description
  681. attachment = issue.attachments.detect {|a| a.filename == 'textfile.txt'}
  682. assert_not_nil attachment
  683. assert_include 'Plain text attachment', File.read(attachment.diskfile)
  684. end
  685. def test_add_issue_with_iso_8859_1_subject
  686. issue =
  687. submit_email(
  688. 'subject_as_iso-8859-1.eml',
  689. :issue => {:project => 'ecookbook'}
  690. )
  691. assert_kind_of Issue, issue
  692. assert_equal 'Testmail from Webmail: ä ö ü...', issue.subject
  693. end
  694. def test_quoted_printable_utf8
  695. issue =
  696. submit_email(
  697. 'quoted_printable_utf8.eml',
  698. :issue => {:project => 'ecookbook'}
  699. )
  700. assert_kind_of Issue, issue
  701. assert_equal 'Freundliche Grüsse', issue.description
  702. end
  703. def test_gmail_iso8859_2
  704. issue =
  705. submit_email(
  706. 'gmail-iso8859-2.eml',
  707. :issue => {:project => 'ecookbook'}
  708. )
  709. assert_kind_of Issue, issue
  710. assert issue.description.include?('Na štriku se suši šosić.')
  711. end
  712. def test_add_issue_with_japanese_subject
  713. issue =
  714. submit_email(
  715. 'subject_japanese_1.eml',
  716. :issue => {:project => 'ecookbook'}
  717. )
  718. assert_kind_of Issue, issue
  719. assert_equal 'テスト', issue.subject
  720. end
  721. def test_add_issue_with_korean_body
  722. issue =
  723. submit_email(
  724. 'body_ks_c_5601-1987.eml',
  725. :issue => {:project => 'ecookbook'}
  726. )
  727. assert_kind_of Issue, issue
  728. assert_equal '고맙습니다.', issue.description
  729. end
  730. def test_add_issue_with_no_subject_header
  731. with_settings :default_language => 'en' do
  732. issue =
  733. submit_email(
  734. 'no_subject_header.eml',
  735. :issue => {:project => 'ecookbook'}
  736. )
  737. assert_kind_of Issue, issue
  738. assert_equal "(no subject)", issue.subject
  739. end
  740. end
  741. def test_add_issue_with_mixed_japanese_subject
  742. issue =
  743. submit_email(
  744. 'subject_japanese_2.eml',
  745. :issue => {:project => 'ecookbook'}
  746. )
  747. assert_kind_of Issue, issue
  748. assert_equal 'Re: テスト', issue.subject
  749. end
  750. def test_add_issue_with_iso_2022_jp_ms_subject
  751. # CIRCLED DIGIT ONE character is undefined in ISO-2022-JP but
  752. # defined in some vendor-extended variants such as ISO-2022-JP-MS.
  753. # This test makes sure that mail gem replaces an undefined characters
  754. # with a replacement character instead of breaking the whole subject.
  755. issue =
  756. submit_email(
  757. 'subject_japanese_3.eml',
  758. :issue => {:project => 'ecookbook'}
  759. )
  760. assert_kind_of Issue, issue
  761. assert_match /丸数字テスト/, issue.subject
  762. end
  763. def test_should_ignore_emails_from_locked_users
  764. User.find(2).lock!
  765. MailHandler.any_instance.expects(:dispatch).never
  766. assert_no_difference 'Issue.count' do
  767. assert_equal false, submit_email('ticket_on_given_project.eml')
  768. end
  769. end
  770. def test_should_ignore_emails_from_emission_address
  771. emission_addresses = [
  772. 'redmine@example.net',
  773. 'Redmine <redmine@example.net>',
  774. 'redmine@example.net (Redmine)'
  775. ]
  776. Role.anonymous.add_permission!(:add_issues)
  777. emission_addresses.each do |addr|
  778. with_settings :mail_from => addr do
  779. assert_no_difference 'User.count' do
  780. assert_equal(
  781. false,
  782. submit_email(
  783. 'ticket_from_emission_address.eml',
  784. :issue => {:project => 'ecookbook'},
  785. :unknown_user => 'create'
  786. )
  787. )
  788. end
  789. end
  790. end
  791. end
  792. def test_should_ignore_auto_replied_emails
  793. MailHandler.any_instance.expects(:dispatch).never
  794. [
  795. "Auto-Submitted: auto-replied",
  796. "Auto-Submitted: Auto-Replied",
  797. "Auto-Submitted: auto-generated",
  798. 'X-Autoreply: yes'
  799. ].each do |header|
  800. raw = IO.read(File.join(FIXTURES_PATH, 'ticket_on_given_project.eml'))
  801. raw = header + "\n" + raw
  802. assert_no_difference 'Issue.count' do
  803. assert_equal false, MailHandler.receive(raw), "email with #{header} header was not ignored"
  804. end
  805. end
  806. end
  807. test "should not ignore Auto-Submitted headers not defined in RFC3834" do
  808. [
  809. "Auto-Submitted: auto-forwarded"
  810. ].each do |header|
  811. raw = IO.read(File.join(FIXTURES_PATH, 'ticket_on_given_project.eml'))
  812. raw = header + "\n" + raw
  813. assert_difference 'Issue.count', 1 do
  814. assert_not_nil MailHandler.receive(raw), "email with #{header} header was ignored"
  815. end
  816. end
  817. end
  818. def test_add_issue_should_send_email_notification
  819. Setting.notified_events = ['issue_added']
  820. # This email contains: 'Project: onlinestore'
  821. issue = submit_email('ticket_on_given_project.eml')
  822. assert issue.is_a?(Issue)
  823. assert_equal 1, ActionMailer::Base.deliveries.size
  824. end
  825. def test_update_issue
  826. journal = submit_email('ticket_reply.eml')
  827. assert journal.is_a?(Journal)
  828. assert_equal User.find_by_login('jsmith'), journal.user
  829. assert_equal Issue.find(2), journal.journalized
  830. assert_match /This is reply/, journal.notes
  831. assert_equal false, journal.private_notes
  832. assert_equal 'Feature request', journal.issue.tracker.name
  833. end
  834. def test_update_issue_should_accept_issue_id_after_space_inside_brackets
  835. journal = submit_email('ticket_reply_with_status.eml') do |email|
  836. assert email.sub!(/^Subject:.*$/, "Subject: Re: [Feature request #2] Add ingredients categories")
  837. end
  838. assert journal.is_a?(Journal)
  839. assert_equal Issue.find(2), journal.journalized
  840. end
  841. def test_update_issue_should_accept_issue_id_inside_brackets
  842. journal = submit_email('ticket_reply_with_status.eml') do |email|
  843. assert email.sub!(/^Subject:.*$/, "Subject: Re: [#2] Add ingredients categories")
  844. end
  845. assert journal.is_a?(Journal)
  846. assert_equal Issue.find(2), journal.journalized
  847. end
  848. def test_update_issue_should_ignore_bogus_issue_ids_in_subject
  849. journal = submit_email('ticket_reply_with_status.eml') do |email|
  850. assert email.sub!(/^Subject:.*$/, "Subject: Re: [12345#1][bogus#1][Feature request #2] Add ingredients categories")
  851. end
  852. assert journal.is_a?(Journal)
  853. assert_equal Issue.find(2), journal.journalized
  854. end
  855. def test_update_issue_with_attribute_changes
  856. journal = submit_email('ticket_reply_with_status.eml',
  857. :allow_override => ['status', 'assigned_to',
  858. 'start_date', 'due_date',
  859. 'float field'])
  860. assert journal.is_a?(Journal)
  861. issue = Issue.find(journal.issue.id)
  862. assert_equal User.find_by_login('jsmith'), journal.user
  863. assert_equal Issue.find(2), journal.journalized
  864. assert_match /This is reply/, journal.notes
  865. assert_equal 'Feature request', journal.issue.tracker.name
  866. assert_equal IssueStatus.find_by_name("Resolved"), issue.status
  867. assert_equal '2010-01-01', issue.start_date.to_s
  868. assert_equal '2010-12-31', issue.due_date.to_s
  869. assert_equal User.find_by_login('jsmith'), issue.assigned_to
  870. assert_equal "52.6", issue.custom_value_for(CustomField.find_by_name('Float field')).value
  871. # keywords should be removed from the email body
  872. assert !journal.notes.match(/^Status:/i)
  873. assert !journal.notes.match(/^Start Date:/i)
  874. end
  875. def test_update_issue_with_attachment
  876. assert_difference 'Journal.count' do
  877. assert_difference 'JournalDetail.count' do
  878. assert_difference 'Attachment.count' do
  879. assert_no_difference 'Issue.count' do
  880. journal = submit_email('ticket_with_attachment.eml') do |raw|
  881. raw.gsub! /^Subject: .*$/, 'Subject: Re: [Cookbook - Feature #2] (New) Add ingredients categories'
  882. end
  883. end
  884. end
  885. end
  886. end
  887. journal = Journal.order('id DESC').first
  888. assert_equal Issue.find(2), journal.journalized
  889. assert_equal 1, journal.details.size
  890. detail = journal.details.first
  891. assert_equal 'attachment', detail.property
  892. assert_equal 'Paella.jpg', detail.value
  893. end
  894. def test_update_issue_should_discard_all_changes_on_validation_failure
  895. Issue.any_instance.stubs(:valid?).returns(false)
  896. assert_no_difference 'Journal.count' do
  897. assert_no_difference 'JournalDetail.count' do
  898. assert_no_difference 'Attachment.count' do
  899. assert_no_difference 'Issue.count' do
  900. journal = submit_email('ticket_with_attachment.eml') do |raw|
  901. raw.gsub! /^Subject: .*$/, 'Subject: Re: [Cookbook - Feature #2] (New) Add ingredients categories'
  902. end
  903. end
  904. end
  905. end
  906. end
  907. end
  908. def test_update_issue_should_send_email_notification
  909. journal = submit_email('ticket_reply.eml')
  910. assert journal.is_a?(Journal)
  911. assert_equal 3, ActionMailer::Base.deliveries.size
  912. end
  913. def test_update_issue_should_not_set_defaults
  914. journal =
  915. submit_email(
  916. 'ticket_reply.eml',
  917. :issue => {:tracker => 'Support request', :priority => 'High'}
  918. )
  919. assert journal.is_a?(Journal)
  920. assert_match /This is reply/, journal.notes
  921. assert_equal 'Feature request', journal.issue.tracker.name
  922. assert_equal 'Normal', journal.issue.priority.name
  923. end
  924. def test_update_issue_should_add_cc_as_watchers
  925. Watcher.delete_all
  926. issue = Issue.find(2)
  927. assert_difference 'Watcher.count' do
  928. assert submit_email('issue_update_with_cc.eml')
  929. end
  930. issue.reload
  931. assert_equal 1, issue.watcher_user_ids.size
  932. assert issue.watched_by?(User.find_by_mail('dlopper@somenet.foo'))
  933. end
  934. def test_update_issue_should_not_add_cc_as_watchers_if_already_watching
  935. Watcher.delete_all
  936. issue = Issue.find(2)
  937. Watcher.create!(:watchable => issue, :user => User.find_by_mail('dlopper@somenet.foo'))
  938. assert_no_difference 'Watcher.count' do
  939. assert submit_email('issue_update_with_cc.eml')
  940. end
  941. end
  942. def test_replying_to_a_private_note_should_add_reply_as_private
  943. private_journal = Journal.create!(:notes => 'Private notes',
  944. :journalized => Issue.find(1),
  945. :private_notes => true, :user_id => 2)
  946. assert_difference 'Journal.count' do
  947. journal = submit_email('ticket_reply.eml') do |email|
  948. email.sub! %r{^In-Reply-To:.*$}, "In-Reply-To: <redmine.journal-#{private_journal.id}.20060719210421@osiris>"
  949. end
  950. assert_kind_of Journal, journal
  951. assert_match /This is reply/, journal.notes
  952. assert_equal true, journal.private_notes
  953. end
  954. end
  955. def test_reply_to_a_nonexistent_issue
  956. set_tmp_attachments_directory
  957. Issue.find(2).destroy
  958. assert_no_difference 'Issue.count' do
  959. assert_no_difference 'Journal.count' do
  960. journal = submit_email('ticket_reply_with_status.eml')
  961. assert_nil journal
  962. end
  963. end
  964. end
  965. def test_reply_to_an_issue_without_permission
  966. set_tmp_attachments_directory
  967. # "add_issue_notes" permission is explicit required to allow users to add notes
  968. # "edit_issue" permission no longer includes the "add_issue_notes" permission
  969. Role.all.each {|r| r.remove_permission! :add_issue_notes}
  970. assert_no_difference 'Issue.count' do
  971. assert_no_difference 'Journal.count' do
  972. assert_not submit_email('ticket_reply_with_status.eml')
  973. end
  974. end
  975. end
  976. def test_reply_to_a_nonexitent_journal
  977. journal_id = Issue.find(2).journals.last.id
  978. Journal.destroy(journal_id)
  979. assert_no_difference 'Issue.count' do
  980. assert_no_difference 'Journal.count' do
  981. journal = submit_email('ticket_reply.eml') do |email|
  982. email.sub! %r{^In-Reply-To:.*$}, "In-Reply-To: <redmine.journal-#{journal_id}.20060719210421@osiris>"
  983. end
  984. assert_nil journal
  985. end
  986. end
  987. end
  988. def test_reply_to_a_message
  989. m = submit_email('message_reply.eml')
  990. assert m.is_a?(Message)
  991. assert !m.new_record?
  992. m.reload
  993. assert_equal 'Reply via email', m.subject
  994. # The email replies to message #2 which is part of the thread of message #1
  995. assert_equal Message.find(1), m.parent
  996. end
  997. def test_reply_to_a_message_by_subject
  998. m = submit_email('message_reply_by_subject.eml')
  999. assert m.is_a?(Message)
  1000. assert !m.new_record?
  1001. m.reload
  1002. assert_equal 'Reply to the first post', m.subject
  1003. assert_equal Message.find(1), m.parent
  1004. end
  1005. def test_reply_to_a_locked_topic
  1006. # Lock the topic
  1007. topic = Message.find(2).parent
  1008. topic.update_attribute :locked, true
  1009. assert_no_difference('topic.replies_count') do
  1010. m = submit_email('message_reply_by_subject.eml')
  1011. assert_not_kind_of Message, m
  1012. end
  1013. end
  1014. def test_reply_to_a_nonexistent_topic
  1015. Message.find(2).destroy
  1016. assert_no_difference('Message.count') do
  1017. m = submit_email('message_reply_by_subject.eml')
  1018. assert_nil m
  1019. end
  1020. end
  1021. def test_reply_to_a_topic_without_permission
  1022. Role.all.each {|r| r.remove_permission! :add_messages}
  1023. assert_no_difference('Message.count') do
  1024. assert_not submit_email('message_reply_by_subject.eml')
  1025. end
  1026. end
  1027. def test_should_convert_tags_of_html_only_emails
  1028. with_settings :text_formatting => 'textile' do
  1029. issue = submit_email('ticket_html_only.eml', :issue => {:project => 'ecookbook'})
  1030. assert issue.is_a?(Issue)
  1031. assert !issue.new_record?
  1032. issue.reload
  1033. assert_equal 'HTML email', issue.subject
  1034. assert_equal "This is a *html-only* email.\r\n\r\nh1. With a title\r\n\r\nand a paragraph.", issue.description
  1035. end
  1036. end
  1037. def test_should_handle_outlook_web_access_2010_html_only
  1038. issue = submit_email('outlook_web_access_2010_html_only.eml', :issue => {:project => 'ecookbook'})
  1039. assert issue.is_a?(Issue)
  1040. issue.reload
  1041. assert_equal 'Upgrade Redmine to 3.0.x', issue.subject
  1042. assert_equal "A mess.\r\n\r\n--Geoff Maciolek\r\nMYCOMPANYNAME, LLC", issue.description
  1043. end
  1044. def test_should_handle_outlook_2010_html_only
  1045. issue = submit_email('outlook_2010_html_only.eml', :issue => {:project => 'ecookbook'})
  1046. assert issue.is_a?(Issue)
  1047. issue.reload
  1048. assert_equal 'Test email', issue.subject
  1049. assert_equal(
  1050. "Simple, unadorned test email generated by Outlook 2010. It is in HTML format, but" \
  1051. " no special formatting has been chosen. I’m going to save this as a draft and then manually" \
  1052. " drop it into the Inbox for scraping by Redmine 3.0.2.",
  1053. issue.description
  1054. )
  1055. end
  1056. test "truncate emails with no setting should add the entire email into the issue" do
  1057. with_settings :mail_handler_body_delimiters => '' do
  1058. issue = submit_email('ticket_on_given_project.eml')
  1059. assert_issue_created(issue)
  1060. assert issue.description.include?('---')
  1061. assert issue.description.include?('This paragraph is after the delimiter')
  1062. end
  1063. end
  1064. test "truncate emails with a single string should truncate the email at the delimiter for the issue" do
  1065. with_settings :mail_handler_body_delimiters => '---' do
  1066. issue = submit_email('ticket_on_given_project.eml')
  1067. assert_issue_created(issue)
  1068. assert issue.description.include?('This paragraph is before delimiters')
  1069. assert issue.description.include?('--- This line starts with a delimiter')
  1070. assert !issue.description.match(/^---\u00A0$/)
  1071. assert !issue.description.include?('This paragraph is after the delimiter')
  1072. end
  1073. end
  1074. test "truncate emails with a single quoted reply should truncate the email at the delimiter with the quoted reply symbols (>)" do
  1075. with_settings :mail_handler_body_delimiters => '--- Reply above. Do not remove this line. ---' do
  1076. journal = submit_email('issue_update_with_quoted_reply_above.eml')
  1077. assert journal.is_a?(Journal)
  1078. assert journal.notes.include?('An update to the issue by the sender.')
  1079. assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---"))
  1080. assert !journal.notes.include?('Looks like the JSON api for projects was missed.')
  1081. end
  1082. end
  1083. test "truncate emails with multiple quoted replies should truncate the email at the delimiter with the quoted reply symbols (>)" do
  1084. with_settings :mail_handler_body_delimiters => '--- Reply above. Do not remove this line. ---' do
  1085. journal = submit_email('issue_update_with_multiple_quoted_reply_above.eml')
  1086. assert journal.is_a?(Journal)
  1087. assert journal.notes.include?('An update to the issue by the sender.')
  1088. assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---"))
  1089. assert !journal.notes.include?('Looks like the JSON api for projects was missed.')
  1090. end
  1091. end
  1092. test "truncate emails with multiple strings should truncate the email at the first delimiter found (BREAK)" do
  1093. with_settings :mail_handler_body_delimiters => "---\nBREAK" do
  1094. issue = submit_email('ticket_on_given_project.eml')
  1095. assert_issue_created(issue)
  1096. assert issue.description.include?('This paragraph is before delimiters')
  1097. assert !issue.description.include?('BREAK')
  1098. assert !issue.description.include?('This paragraph is between delimiters')
  1099. assert !issue.description.match(/^---$/)
  1100. assert !issue.description.include?('This paragraph is after the delimiter')
  1101. end
  1102. end
  1103. test "truncate emails using a regex delimiter" do
  1104. delimiter = "On .*, .* at .*, .* <.*<mailto:.*>> wrote:"
  1105. with_settings :mail_handler_enable_regex_delimiters => '1', :mail_handler_body_delimiters => delimiter do
  1106. issue = submit_email('ticket_reply_from_mail.eml')
  1107. assert_issue_created(issue)
  1108. assert issue.description.include?('This paragraph is before delimiter')
  1109. assert !issue.description.include?('On Wed, 11 Oct at 1:05 PM, Jon Smith <jsmith@somenet.foo<mailto:jsmith@somenet.foo>> wrote:')
  1110. assert !issue.description.include?('This paragraph is after the delimiter')
  1111. end
  1112. with_settings :mail_handler_enable_regex_delimiters => '0', :mail_handler_body_delimiters => delimiter do
  1113. issue = submit_email('ticket_reply_from_mail.eml')
  1114. assert_issue_created(issue)
  1115. assert issue.description.include?('This paragraph is before delimiter')
  1116. assert issue.description.include?('On Wed, 11 Oct at 1:05 PM, Jon Smith <jsmith@somenet.foo<mailto:jsmith@somenet.foo>> wrote:')
  1117. assert issue.description.include?('This paragraph is after the delimiter')
  1118. end
  1119. end
  1120. def test_attachments_that_match_mail_handler_excluded_filenames_should_be_ignored
  1121. with_settings :mail_handler_excluded_filenames => "*.vcf,\n *.jpg" do
  1122. issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
  1123. assert issue.is_a?(Issue)
  1124. assert !issue.new_record?
  1125. assert_equal 0, issue.reload.attachments.size
  1126. end
  1127. end
  1128. def test_attachments_that_match_mail_handler_excluded_filenames_by_regex_should_be_ignored
  1129. with_settings :mail_handler_excluded_filenames => '.+\.vcf,(pa|nut)ella\.jpg',
  1130. :mail_handler_enable_regex_excluded_filenames => 1 do
  1131. issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
  1132. assert issue.is_a?(Issue)
  1133. assert !issue.new_record?
  1134. assert_equal 0, issue.reload.attachments.size
  1135. end
  1136. end
  1137. def test_attachments_that_do_not_match_mail_handler_excluded_filenames_should_be_attached
  1138. with_settings :mail_handler_excluded_filenames => '*.vcf, *.gif' do
  1139. issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
  1140. assert issue.is_a?(Issue)
  1141. assert !issue.new_record?
  1142. assert_equal 1, issue.reload.attachments.size
  1143. end
  1144. end
  1145. def test_email_with_long_subject_line
  1146. issue = submit_email('ticket_with_long_subject.eml')
  1147. assert issue.is_a?(Issue)
  1148. str =
  1149. 'New ticket on a given project with a very long subject line' \
  1150. ' which exceeds 255 chars and should not be ignored but chopped off.' \
  1151. ' And if the subject line is still not long enough, we just add more text.' \
  1152. ' And more text. Wow, this is really annoying.' \
  1153. ' Especially, if you have nothing to say...'
  1154. assert_equal issue.subject, str[0, 255]
  1155. end
  1156. def test_first_keyword_should_be_matched
  1157. issue = submit_email('ticket_with_duplicate_keyword.eml', :allow_override => 'priority')
  1158. assert issue.is_a?(Issue)
  1159. assert_equal 'High', issue.priority.name
  1160. end
  1161. def test_keyword_after_delimiter_should_be_ignored
  1162. with_settings :mail_handler_body_delimiters => "== DELIMITER ==" do
  1163. issue = submit_email('ticket_with_keyword_after_delimiter.eml', :allow_override => 'priority')
  1164. assert issue.is_a?(Issue)
  1165. assert_equal 'Normal', issue.priority.name
  1166. end
  1167. end
  1168. def test_new_user_from_attributes_should_return_valid_user
  1169. to_test = {
  1170. # [address, name] => [login, firstname, lastname]
  1171. ['jsmith@example.net', nil] => ['jsmith@example.net', 'jsmith', '-'],
  1172. ['jsmith@example.net', 'John'] => ['jsmith@example.net', 'John', '-'],
  1173. ['jsmith@example.net', 'John Smith'] => ['jsmith@example.net', 'John', 'Smith'],
  1174. ['jsmith@example.net', 'John Paul Smith'] => ['jsmith@example.net', 'John', 'Paul Smith'],
  1175. ['jsmith@example.net', 'AVeryLongFirstnameThatExceedsTheMaximumLength Smith'] =>
  1176. ['jsmith@example.net', 'AVeryLongFirstnameThatExceedsT', 'Smith'],
  1177. ['jsmith@example.net', 'John AVeryLongLastnameThatExceedsTheMaximumLength'] =>
  1178. ['jsmith@example.net', 'John', 'AVeryLongLastnameThatExceedsTh']
  1179. }
  1180. to_test.each do |attrs, expected|
  1181. user = MailHandler.new_user_from_attributes(attrs.first, attrs.last)
  1182. assert user.valid?, user.errors.full_messages.to_s
  1183. assert_equal attrs.first, user.mail
  1184. assert_equal expected[0], user.login
  1185. assert_equal expected[1], user.firstname
  1186. assert_equal expected[2], user.lastname
  1187. assert_equal 'only_my_events', user.mail_notification
  1188. end
  1189. end
  1190. def test_new_user_from_attributes_should_use_default_login_if_invalid
  1191. user = MailHandler.new_user_from_attributes('foo+bar@example.net')
  1192. assert user.valid?
  1193. assert user.login =~ /^user[a-f0-9]+$/
  1194. assert_equal 'foo+bar@example.net', user.mail
  1195. end
  1196. def test_new_user_with_utf8_encoded_fullname_should_be_decoded
  1197. assert_difference 'User.count' do
  1198. issue =
  1199. submit_email(
  1200. 'fullname_of_sender_as_utf8_encoded.eml',
  1201. :issue => {:project => 'ecookbook'},
  1202. :unknown_user => 'create'
  1203. )
  1204. end
  1205. user = User.order('id DESC').first
  1206. assert_equal "foo@example.org", user.mail
  1207. assert_equal 'Ää', user.firstname
  1208. assert_equal 'Öö', user.lastname
  1209. end
  1210. def test_new_user_with_fullname_in_parentheses
  1211. assert_difference 'User.count' do
  1212. issue =
  1213. submit_email(
  1214. 'fullname_of_sender_in_parentheses.eml',
  1215. :issue => {:project => 'ecookbook'},
  1216. :unknown_user => 'create'
  1217. )
  1218. end
  1219. user = User.order('id DESC').first
  1220. assert_equal "jdoe@example.net", user.mail
  1221. assert_equal 'John', user.firstname
  1222. assert_equal 'Doe', user.lastname
  1223. end
  1224. def test_extract_options_from_env_should_return_options
  1225. options =
  1226. MailHandler.extract_options_from_env(
  1227. {
  1228. 'tracker' => 'defect',
  1229. 'project' => 'foo',
  1230. 'unknown_user' => 'create',
  1231. 'no_notification' => '1'
  1232. }
  1233. )
  1234. assert_equal(
  1235. {:issue => {:tracker => 'defect', :project => 'foo'},
  1236. :unknown_user => 'create', :no_notification => '1'},
  1237. options
  1238. )
  1239. end
  1240. def test_safe_receive_should_rescue_exceptions_and_return_false
  1241. MailHandler.stubs(:receive).raises(StandardError.new("Something went wrong"))
  1242. assert_equal false, MailHandler.safe_receive
  1243. end
  1244. def test_smine_signature
  1245. issue = submit_email('smime_signature.eml', :issue => {:project => 'onlinestore'})
  1246. assert issue.is_a?(Issue)
  1247. assert !issue.new_record?
  1248. issue.reload
  1249. assert_equal 'Self-Signed S/MIME signature', issue.subject
  1250. assert_equal User.find_by_login('jsmith'), issue.author
  1251. assert_equal Project.find(2), issue.project
  1252. assert_equal 'smime.sh.txt describes how to create Self-Signed S/MIME Certs.', issue.description
  1253. assert_equal 2, issue.attachments.size
  1254. assert_equal 'smime.sh.txt', issue.attachments[0].filename
  1255. assert_equal 'text/plain', issue.attachments[0].content_type
  1256. assert_equal 'smime.p7s', issue.attachments[1].filename
  1257. assert_equal 'application/x-pkcs7-signature', issue.attachments[1].content_type
  1258. end
  1259. private
  1260. def submit_email(filename, options={})
  1261. raw = IO.read(File.join(FIXTURES_PATH, filename))
  1262. yield raw if block_given?
  1263. MailHandler.receive(raw, options)
  1264. end
  1265. def assert_issue_created(issue)
  1266. assert issue.is_a?(Issue)
  1267. assert !issue.new_record?
  1268. issue.reload
  1269. end
  1270. end