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

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480
  1. # frozen_string_literal: true
  2. # Redmine - project management software
  3. # Copyright (C) 2006- 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_relative '../test_helper'
  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, :news, :comments
  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_no_issue_on_closed_project_without_permission_check
  373. Project.find(2).close
  374. assert_no_difference 'User.count' do
  375. assert_no_difference 'Issue.count' do
  376. submit_email(
  377. 'ticket_by_unknown_user.eml',
  378. :issue => {:project => 'onlinestore'},
  379. :no_permission_check => '1',
  380. :unknown_user => 'accept'
  381. )
  382. end
  383. end
  384. ensure
  385. Project.find(2).reopen
  386. end
  387. def test_no_issue_on_closed_project_without_issue_tracking_module
  388. assert_no_difference 'User.count' do
  389. assert_no_difference 'Issue.count' do
  390. submit_email(
  391. 'ticket_by_unknown_user.eml',
  392. :issue => {:project => 'subproject2'},
  393. :no_permission_check => '1',
  394. :unknown_user => 'accept'
  395. )
  396. end
  397. end
  398. end
  399. def test_add_issue_by_created_user
  400. Setting.default_language = 'en'
  401. assert_difference 'User.count' do
  402. issue =
  403. submit_email(
  404. 'ticket_by_unknown_user.eml',
  405. :issue => {:project => 'ecookbook'},
  406. :unknown_user => 'create'
  407. )
  408. assert issue.is_a?(Issue)
  409. assert issue.author.active?
  410. assert_equal 'john.doe@somenet.foo', issue.author.mail
  411. assert_equal 'John', issue.author.firstname
  412. assert_equal 'Doe', issue.author.lastname
  413. # account information
  414. email = ActionMailer::Base.deliveries.first
  415. assert_not_nil email
  416. assert email.subject.include?('account activation')
  417. login = mail_body(email).match(/\* Login: (.*)$/)[1].strip
  418. password = mail_body(email).match(/\* Password: (.*)$/)[1].strip
  419. assert_equal issue.author, User.try_to_login(login, password)
  420. end
  421. end
  422. def test_add_issue_should_send_notification
  423. issue = submit_email('ticket_on_given_project.eml', :allow_override => 'all')
  424. assert issue.is_a?(Issue)
  425. assert !issue.new_record?
  426. assert_equal 4, issue.parent_issue_id
  427. assert_equal 2, ActionMailer::Base.deliveries.size
  428. [
  429. [issue.id, 'New ticket on a given project'],
  430. [4, 'Issue on project 2'],
  431. ].each do |issue_id, issue_subject|
  432. mail =
  433. ActionMailer::Base.deliveries.detect do |m|
  434. /##{issue_id}/.match?(m.subject) && /#{issue_subject}/.match?(m.subject)
  435. end
  436. assert_not_nil mail
  437. end
  438. end
  439. def test_created_user_should_be_added_to_groups
  440. group1 = Group.generate!
  441. group2 = Group.generate!
  442. assert_difference 'User.count' do
  443. submit_email(
  444. 'ticket_by_unknown_user.eml',
  445. :issue => {:project => 'ecookbook'},
  446. :unknown_user => 'create',
  447. :default_group => "#{group1.name},#{group2.name}"
  448. )
  449. end
  450. user = User.order('id DESC').first
  451. assert_equal [group1, group2].sort, user.groups.sort
  452. end
  453. def test_created_user_should_not_receive_account_information_with_no_account_info_option
  454. assert_difference 'User.count' do
  455. submit_email(
  456. 'ticket_by_unknown_user.eml',
  457. :issue => {:project => 'ecookbook'},
  458. :unknown_user => 'create',
  459. :no_account_notice => '1'
  460. )
  461. end
  462. # only 2 emails for the new issue notification
  463. assert_equal 2, ActionMailer::Base.deliveries.size
  464. ActionMailer::Base.deliveries.each do |email|
  465. assert_include 'Ticket by unknown user', email.subject
  466. end
  467. end
  468. def test_created_user_should_have_mail_notification_to_none_with_no_notification_option
  469. assert_difference 'User.count' do
  470. submit_email(
  471. 'ticket_by_unknown_user.eml',
  472. :issue => {:project => 'ecookbook'},
  473. :unknown_user => 'create',
  474. :no_notification => '1'
  475. )
  476. end
  477. user = User.order('id DESC').first
  478. assert_equal 'none', user.mail_notification
  479. end
  480. def test_add_issue_without_from_header
  481. Role.anonymous.add_permission!(:add_issues)
  482. assert_equal false, submit_email('ticket_without_from_header.eml')
  483. end
  484. def test_add_issue_with_invalid_attributes
  485. with_settings :default_issue_start_date_to_creation_date => '0' do
  486. issue =
  487. submit_email(
  488. 'ticket_with_invalid_attributes.eml',
  489. :allow_override => 'tracker,category,priority'
  490. )
  491. assert issue.is_a?(Issue)
  492. assert !issue.new_record?
  493. issue.reload
  494. assert_nil issue.assigned_to
  495. assert_nil issue.start_date
  496. assert_nil issue.due_date
  497. assert_equal 0, issue.done_ratio
  498. assert_nil issue.parent
  499. assert_equal 'Normal', issue.priority.to_s
  500. assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
  501. end
  502. end
  503. def test_add_issue_with_invalid_project_should_be_assigned_to_default_project
  504. issue =
  505. submit_email(
  506. 'ticket_on_given_project.eml',
  507. :issue => {:project => 'ecookbook'},
  508. :allow_override => 'project'
  509. ) do |email|
  510. email.gsub!(/^Project:.+$/, 'Project: invalid')
  511. end
  512. assert issue.is_a?(Issue)
  513. assert !issue.new_record?
  514. assert_equal 'ecookbook', issue.project.identifier
  515. end
  516. def test_add_issue_with_private_keyword
  517. User.find_by_mail('jsmith@somenet.foo').update_attribute 'language', 'fr'
  518. # give the user permission to set issues private:
  519. MemberRole.create! member_id: 3, role_id: 1
  520. issue =
  521. submit_email(
  522. 'ticket_with_localized_private_flag.eml',
  523. :allow_override => 'is_private,tracker,category,priority'
  524. )
  525. assert issue.is_a?(Issue)
  526. assert_not issue.new_record?
  527. issue.reload
  528. assert_equal 'New ticket on a given project', issue.subject
  529. assert issue.is_private
  530. end
  531. def test_add_issue_with_localized_attributes
  532. User.find_by_mail('jsmith@somenet.foo').update_attribute 'language', 'fr'
  533. issue =
  534. submit_email(
  535. 'ticket_with_localized_attributes.eml',
  536. :allow_override => 'tracker,category,priority'
  537. )
  538. assert issue.is_a?(Issue)
  539. assert !issue.new_record?
  540. issue.reload
  541. assert_equal 'New ticket on a given project', issue.subject
  542. assert_equal User.find_by_login('jsmith'), issue.author
  543. assert_equal Project.find(2), issue.project
  544. assert_equal 'Feature request', issue.tracker.to_s
  545. assert_equal 'Stock management', issue.category.to_s
  546. assert_equal 'Urgent', issue.priority.to_s
  547. assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
  548. end
  549. def test_add_issue_with_japanese_keywords
  550. tracker = Tracker.generate!(:name => '開発')
  551. Project.find(1).trackers << tracker
  552. issue =
  553. submit_email(
  554. 'japanese_keywords_iso_2022_jp.eml',
  555. :issue => {:project => 'ecookbook'},
  556. :allow_override => 'tracker'
  557. )
  558. assert_kind_of Issue, issue
  559. assert_equal tracker, issue.tracker
  560. end
  561. def test_add_issue_from_apple_mail
  562. set_tmp_attachments_directory
  563. issue =
  564. submit_email(
  565. 'apple_mail_with_attachment.eml',
  566. :issue => {:project => 'ecookbook'}
  567. )
  568. assert_kind_of Issue, issue
  569. assert_equal 1, issue.attachments.size
  570. attachment = issue.attachments.first
  571. assert_equal 'paella.jpg', attachment.filename
  572. assert_equal 10790, attachment.filesize
  573. assert File.exist?(attachment.diskfile)
  574. assert_equal 10790, File.size(attachment.diskfile)
  575. assert_equal '4474dd534c36bdd212e2efc549507377c3e77147c9167b66dedcebfe9da8807f', attachment.digest
  576. end
  577. def test_thunderbird_with_attachment_ja
  578. set_tmp_attachments_directory
  579. issue =
  580. submit_email(
  581. 'thunderbird_with_attachment_ja.eml',
  582. :issue => {:project => 'ecookbook'}
  583. )
  584. assert_kind_of Issue, issue
  585. assert_equal 1, issue.attachments.size
  586. attachment = issue.attachments.first
  587. assert_equal 'テスト.txt', attachment.filename
  588. assert_equal 5, attachment.filesize
  589. assert File.exist?(attachment.diskfile)
  590. assert_equal 5, File.size(attachment.diskfile)
  591. assert_equal 'f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2', attachment.digest
  592. end
  593. def test_invalid_utf8
  594. issue =
  595. submit_email(
  596. 'invalid_utf8.eml',
  597. :issue => {:project => 'ecookbook'}
  598. )
  599. assert_kind_of Issue, issue
  600. assert_equal 'Здравствуйте?', issue.description
  601. end
  602. def test_gmail_with_attachment_ja
  603. set_tmp_attachments_directory
  604. issue =
  605. submit_email(
  606. 'gmail_with_attachment_ja.eml',
  607. :issue => {:project => 'ecookbook'}
  608. )
  609. assert_kind_of Issue, issue
  610. assert_equal 1, issue.attachments.size
  611. attachment = issue.attachments.first
  612. assert_equal 'テスト.txt', attachment.filename
  613. assert_equal 5, attachment.filesize
  614. assert File.exist?(attachment.diskfile)
  615. assert_equal 5, File.size(attachment.diskfile)
  616. assert_equal 'f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2', attachment.digest
  617. end
  618. def test_thunderbird_with_attachment_latin1
  619. set_tmp_attachments_directory
  620. issue =
  621. submit_email(
  622. 'thunderbird_with_attachment_iso-8859-1.eml',
  623. :issue => {:project => 'ecookbook'}
  624. )
  625. assert_kind_of Issue, issue
  626. assert_equal 1, issue.attachments.size
  627. u = +''
  628. u1 = 'ÄäÖöÜü'
  629. 11.times {u << u1}
  630. attachment = issue.attachments.first
  631. assert_equal "#{u}.png", attachment.filename
  632. assert_equal 130, attachment.filesize
  633. assert File.exist?(attachment.diskfile)
  634. assert_equal 130, File.size(attachment.diskfile)
  635. assert_equal '5635d67364de20432247e651dfe86fcb2265ad5e9750bd8bba7319a86363e738', attachment.digest
  636. end
  637. def test_gmail_with_attachment_latin1
  638. set_tmp_attachments_directory
  639. issue =
  640. submit_email(
  641. 'gmail_with_attachment_iso-8859-1.eml',
  642. :issue => {:project => 'ecookbook'}
  643. )
  644. assert_kind_of Issue, issue
  645. assert_equal 1, issue.attachments.size
  646. u = +''
  647. u1 = 'ÄäÖöÜü'
  648. 11.times {u << u1}
  649. attachment = issue.attachments.first
  650. assert_equal "#{u}.txt", attachment.filename
  651. assert_equal 5, attachment.filesize
  652. assert File.exist?(attachment.diskfile)
  653. assert_equal 5, File.size(attachment.diskfile)
  654. assert_equal 'f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2', attachment.digest
  655. end
  656. def test_mail_with_attachment_latin2
  657. set_tmp_attachments_directory
  658. issue =
  659. submit_email(
  660. 'ticket_with_text_attachment_iso-8859-2.eml',
  661. :issue => {:project => 'ecookbook'}
  662. )
  663. assert_kind_of Issue, issue
  664. assert_equal 1, issue.attachments.size
  665. attachment = issue.attachments.first
  666. assert_equal 'latin2.txt', attachment.filename
  667. assert_equal 19, attachment.filesize
  668. assert File.exist?(attachment.diskfile)
  669. assert_equal 19, File.size(attachment.diskfile)
  670. content = (+"p\xF8\xEDli\xB9 \xBEluou\xE8k\xFD k\xF9n").force_encoding('CP852')
  671. assert_equal content, File.read(attachment.diskfile).force_encoding('CP852')
  672. end
  673. def test_empty_attachment_should_not_be_imported
  674. issue =
  675. submit_email(
  676. 'ticket_with_empty_attachment.eml',
  677. :issue => {:project => 'ecookbook'}
  678. )
  679. assert_equal 0, issue.attachments.size
  680. end
  681. def test_multiple_inline_text_parts_should_be_appended_to_issue_description
  682. issue = submit_email('multiple_text_parts.eml', :issue => {:project => 'ecookbook'})
  683. assert_include 'first', issue.description
  684. assert_include 'second', issue.description
  685. assert_include 'third', issue.description
  686. end
  687. def test_empty_text_part_should_not_stop_looking_for_content
  688. issue = submit_email('empty_text_part.eml', :issue => {:project => 'ecookbook'})
  689. assert_equal 'The html part.', issue.description
  690. end
  691. def test_empty_text_and_html_part_should_make_an_empty_description
  692. issue = submit_email('empty_text_and_html_part.eml', :issue => {:project => 'ecookbook'})
  693. assert_equal '', issue.description
  694. end
  695. def test_preferred_body_part_setting
  696. with_settings :mail_handler_preferred_body_part => 'plain' do
  697. issue = submit_email('different_contents_in_text_and_html.eml', :issue => {:project => 'ecookbook'})
  698. assert_equal 'The text part.', issue.description
  699. end
  700. with_settings :mail_handler_preferred_body_part => 'html' do
  701. issue = submit_email('different_contents_in_text_and_html.eml', :issue => {:project => 'ecookbook'})
  702. assert_equal 'The html part.', issue.description
  703. end
  704. end
  705. def test_attachment_text_part_should_be_added_as_issue_attachment
  706. issue = submit_email('multiple_text_parts.eml', :issue => {:project => 'ecookbook'})
  707. assert_not_include 'Plain text attachment', issue.description
  708. attachment = issue.attachments.detect {|a| a.filename == 'textfile.txt'}
  709. assert_not_nil attachment
  710. assert_include 'Plain text attachment', File.read(attachment.diskfile)
  711. end
  712. def test_add_issue_with_iso_8859_1_subject
  713. issue =
  714. submit_email(
  715. 'subject_as_iso-8859-1.eml',
  716. :issue => {:project => 'ecookbook'}
  717. )
  718. assert_kind_of Issue, issue
  719. assert_equal 'Testmail from Webmail: ä ö ü...', issue.subject
  720. end
  721. def test_quoted_printable_utf8
  722. issue =
  723. submit_email(
  724. 'quoted_printable_utf8.eml',
  725. :issue => {:project => 'ecookbook'}
  726. )
  727. assert_kind_of Issue, issue
  728. assert_equal 'Freundliche Grüsse', issue.description
  729. end
  730. def test_gmail_iso8859_2
  731. issue =
  732. submit_email(
  733. 'gmail-iso8859-2.eml',
  734. :issue => {:project => 'ecookbook'}
  735. )
  736. assert_kind_of Issue, issue
  737. assert issue.description.include?('Na štriku se suši šosić.')
  738. end
  739. def test_add_issue_with_japanese_subject
  740. issue =
  741. submit_email(
  742. 'subject_japanese_1.eml',
  743. :issue => {:project => 'ecookbook'}
  744. )
  745. assert_kind_of Issue, issue
  746. assert_equal 'テスト', issue.subject
  747. end
  748. def test_add_issue_with_korean_body
  749. issue =
  750. submit_email(
  751. 'body_ks_c_5601-1987.eml',
  752. :issue => {:project => 'ecookbook'}
  753. )
  754. assert_kind_of Issue, issue
  755. assert_equal '고맙습니다.', issue.description
  756. end
  757. def test_add_issue_with_no_subject_header
  758. with_settings :default_language => 'en' do
  759. issue =
  760. submit_email(
  761. 'no_subject_header.eml',
  762. :issue => {:project => 'ecookbook'}
  763. )
  764. assert_kind_of Issue, issue
  765. assert_equal "(no subject)", issue.subject
  766. end
  767. end
  768. def test_add_issue_with_mixed_japanese_subject
  769. issue =
  770. submit_email(
  771. 'subject_japanese_2.eml',
  772. :issue => {:project => 'ecookbook'}
  773. )
  774. assert_kind_of Issue, issue
  775. assert_equal 'Re: テスト', issue.subject
  776. end
  777. def test_add_issue_with_iso_2022_jp_ms_subject
  778. # CIRCLED DIGIT ONE character is undefined in ISO-2022-JP but
  779. # defined in some vendor-extended variants such as ISO-2022-JP-MS.
  780. # This test makes sure that mail gem replaces an undefined characters
  781. # with a replacement character instead of breaking the whole subject.
  782. issue =
  783. submit_email(
  784. 'subject_japanese_3.eml',
  785. :issue => {:project => 'ecookbook'}
  786. )
  787. assert_kind_of Issue, issue
  788. assert_match /丸数字テスト/, issue.subject
  789. end
  790. def test_should_ignore_emails_from_locked_users
  791. User.find(2).lock!
  792. MailHandler.any_instance.expects(:dispatch).never
  793. assert_no_difference 'Issue.count' do
  794. assert_equal false, submit_email('ticket_on_given_project.eml')
  795. end
  796. end
  797. def test_should_ignore_emails_from_emission_address
  798. emission_addresses = [
  799. 'redmine@example.net',
  800. 'Redmine <redmine@example.net>',
  801. 'redmine@example.net (Redmine)'
  802. ]
  803. Role.anonymous.add_permission!(:add_issues)
  804. emission_addresses.each do |addr|
  805. with_settings :mail_from => addr do
  806. assert_no_difference 'User.count' do
  807. assert_equal(
  808. false,
  809. submit_email(
  810. 'ticket_from_emission_address.eml',
  811. :issue => {:project => 'ecookbook'},
  812. :unknown_user => 'create'
  813. )
  814. )
  815. end
  816. end
  817. end
  818. end
  819. def test_should_ignore_auto_replied_emails
  820. MailHandler.any_instance.expects(:dispatch).never
  821. [
  822. "Auto-Submitted: auto-replied",
  823. "Auto-Submitted: Auto-Replied",
  824. "Auto-Submitted: auto-generated",
  825. 'X-Autoreply: yes'
  826. ].each do |header|
  827. raw = IO.read(File.join(FIXTURES_PATH, 'ticket_on_given_project.eml'))
  828. raw = header + "\n" + raw
  829. assert_no_difference 'Issue.count' do
  830. assert_equal false, MailHandler.receive(raw), "email with #{header} header was not ignored"
  831. end
  832. end
  833. end
  834. test "should not ignore Auto-Submitted headers not defined in RFC3834" do
  835. [
  836. "Auto-Submitted: auto-forwarded"
  837. ].each do |header|
  838. raw = IO.read(File.join(FIXTURES_PATH, 'ticket_on_given_project.eml'))
  839. raw = header + "\n" + raw
  840. assert_difference 'Issue.count', 1 do
  841. assert_not_nil MailHandler.receive(raw), "email with #{header} header was ignored"
  842. end
  843. end
  844. end
  845. def test_add_issue_should_send_email_notification
  846. Setting.notified_events = ['issue_added']
  847. # This email contains: 'Project: onlinestore'
  848. issue = submit_email('ticket_on_given_project.eml')
  849. assert issue.is_a?(Issue)
  850. assert_equal 1, ActionMailer::Base.deliveries.size
  851. end
  852. def test_update_issue
  853. journal = submit_email('ticket_reply.eml')
  854. assert journal.is_a?(Journal)
  855. assert_equal User.find_by_login('jsmith'), journal.user
  856. assert_equal Issue.find(2), journal.journalized
  857. assert_match /This is reply/, journal.notes
  858. assert_equal false, journal.private_notes
  859. assert_equal 'Feature request', journal.issue.tracker.name
  860. end
  861. def test_update_issue_should_accept_issue_id_after_space_inside_brackets
  862. journal = submit_email('ticket_reply_with_status.eml') do |email|
  863. assert email.sub!(/^Subject:.*$/, "Subject: Re: [Feature request #2] Add ingredients categories")
  864. end
  865. assert journal.is_a?(Journal)
  866. assert_equal Issue.find(2), journal.journalized
  867. end
  868. def test_update_issue_should_accept_issue_id_inside_brackets
  869. journal = submit_email('ticket_reply_with_status.eml') do |email|
  870. assert email.sub!(/^Subject:.*$/, "Subject: Re: [#2] Add ingredients categories")
  871. end
  872. assert journal.is_a?(Journal)
  873. assert_equal Issue.find(2), journal.journalized
  874. end
  875. def test_update_issue_should_ignore_bogus_issue_ids_in_subject
  876. journal = submit_email('ticket_reply_with_status.eml') do |email|
  877. assert email.sub!(/^Subject:.*$/, "Subject: Re: [12345#1][bogus#1][Feature request #2] Add ingredients categories")
  878. end
  879. assert journal.is_a?(Journal)
  880. assert_equal Issue.find(2), journal.journalized
  881. end
  882. def test_update_issue_with_attribute_changes
  883. journal = submit_email('ticket_reply_with_status.eml',
  884. :allow_override => ['status', 'assigned_to',
  885. 'start_date', 'due_date',
  886. 'float field'])
  887. assert journal.is_a?(Journal)
  888. issue = Issue.find(journal.issue.id)
  889. assert_equal User.find_by_login('jsmith'), journal.user
  890. assert_equal Issue.find(2), journal.journalized
  891. assert_match /This is reply/, journal.notes
  892. assert_equal 'Feature request', journal.issue.tracker.name
  893. assert_equal IssueStatus.find_by_name("Resolved"), issue.status
  894. assert_equal '2010-01-01', issue.start_date.to_s
  895. assert_equal '2010-12-31', issue.due_date.to_s
  896. assert_equal User.find_by_login('jsmith'), issue.assigned_to
  897. assert_equal "52.6", issue.custom_value_for(CustomField.find_by_name('Float field')).value
  898. # keywords should be removed from the email body
  899. assert !journal.notes.match(/^Status:/i)
  900. assert !journal.notes.match(/^Start Date:/i)
  901. end
  902. def test_update_issue_with_attachment
  903. assert_difference 'Journal.count' do
  904. assert_difference 'JournalDetail.count' do
  905. assert_difference 'Attachment.count' do
  906. assert_no_difference 'Issue.count' do
  907. journal = submit_email('ticket_with_attachment.eml') do |raw|
  908. raw.gsub! /^Subject: .*$/, 'Subject: Re: [Cookbook - Feature #2] (New) Add ingredients categories'
  909. end
  910. end
  911. end
  912. end
  913. end
  914. journal = Journal.order('id DESC').first
  915. assert_equal Issue.find(2), journal.journalized
  916. assert_equal 1, journal.details.size
  917. detail = journal.details.first
  918. assert_equal 'attachment', detail.property
  919. assert_equal 'Paella.jpg', detail.value
  920. end
  921. def test_update_issue_should_discard_all_changes_on_validation_failure
  922. Issue.any_instance.stubs(:valid?).returns(false)
  923. assert_no_difference 'Journal.count' do
  924. assert_no_difference 'JournalDetail.count' do
  925. assert_no_difference 'Attachment.count' do
  926. assert_no_difference 'Issue.count' do
  927. journal = submit_email('ticket_with_attachment.eml') do |raw|
  928. raw.gsub! /^Subject: .*$/, 'Subject: Re: [Cookbook - Feature #2] (New) Add ingredients categories'
  929. end
  930. end
  931. end
  932. end
  933. end
  934. end
  935. def test_update_issue_should_send_email_notification
  936. journal = submit_email('ticket_reply.eml')
  937. assert journal.is_a?(Journal)
  938. assert_equal 3, ActionMailer::Base.deliveries.size
  939. end
  940. def test_update_issue_should_not_set_defaults
  941. journal =
  942. submit_email(
  943. 'ticket_reply.eml',
  944. :issue => {:tracker => 'Support request', :priority => 'High'}
  945. )
  946. assert journal.is_a?(Journal)
  947. assert_match /This is reply/, journal.notes
  948. assert_equal 'Feature request', journal.issue.tracker.name
  949. assert_equal 'Normal', journal.issue.priority.name
  950. end
  951. def test_update_issue_should_add_cc_as_watchers
  952. Watcher.delete_all
  953. issue = Issue.find(2)
  954. assert_difference 'Watcher.count' do
  955. assert submit_email('issue_update_with_cc.eml')
  956. end
  957. issue.reload
  958. assert_equal 1, issue.watcher_user_ids.size
  959. assert issue.watched_by?(User.find_by_mail('dlopper@somenet.foo'))
  960. end
  961. def test_update_issue_should_not_add_cc_as_watchers_if_already_watching
  962. Watcher.delete_all
  963. issue = Issue.find(2)
  964. Watcher.create!(:watchable => issue, :user => User.find_by_mail('dlopper@somenet.foo'))
  965. assert_no_difference 'Watcher.count' do
  966. assert submit_email('issue_update_with_cc.eml')
  967. end
  968. end
  969. def test_replying_to_a_private_note_should_add_reply_as_private
  970. private_journal = Journal.create!(:notes => 'Private notes',
  971. :journalized => Issue.find(1),
  972. :private_notes => true, :user_id => 2)
  973. assert_difference 'Journal.count' do
  974. journal = submit_email('ticket_reply.eml') do |email|
  975. email.sub! %r{^In-Reply-To:.*$}, "In-Reply-To: <redmine.journal-#{private_journal.id}.20060719210421@osiris>"
  976. end
  977. assert_kind_of Journal, journal
  978. assert_match /This is reply/, journal.notes
  979. assert_equal true, journal.private_notes
  980. end
  981. end
  982. def test_reply_to_a_nonexistent_issue
  983. set_tmp_attachments_directory
  984. Issue.find(2).destroy
  985. assert_no_difference 'Issue.count' do
  986. assert_no_difference 'Journal.count' do
  987. journal = submit_email('ticket_reply_with_status.eml')
  988. assert_not journal
  989. end
  990. end
  991. end
  992. def test_reply_to_an_issue_without_permission
  993. set_tmp_attachments_directory
  994. # "add_issue_notes" permission is explicit required to allow users to add notes
  995. # "edit_issue" permission no longer includes the "add_issue_notes" permission
  996. Role.all.each {|r| r.remove_permission! :add_issue_notes}
  997. assert_no_difference 'Issue.count' do
  998. assert_no_difference 'Journal.count' do
  999. assert_not submit_email('ticket_reply_with_status.eml')
  1000. end
  1001. end
  1002. end
  1003. def test_reply_to_a_nonexitent_journal
  1004. journal_id = Issue.find(2).journals.last.id
  1005. Journal.destroy(journal_id)
  1006. assert_no_difference 'Issue.count' do
  1007. assert_no_difference 'Journal.count' do
  1008. journal = submit_email('ticket_reply.eml') do |email|
  1009. email.sub! %r{^In-Reply-To:.*$}, "In-Reply-To: <redmine.journal-#{journal_id}.20060719210421@osiris>"
  1010. end
  1011. assert_not journal
  1012. end
  1013. end
  1014. end
  1015. def test_reply_to_a_nonexitent_journal_with_subject_fallback
  1016. journal_id = Issue.find(2).journals.last.id
  1017. Journal.destroy(journal_id)
  1018. assert_no_difference 'Issue.count' do
  1019. assert_difference 'Journal.count', 1 do
  1020. journal = submit_email('ticket_reply.eml') do |email|
  1021. email.sub! %r{^In-Reply-To:.*$}, "In-Reply-To: <redmine.journal-#{journal_id}.20060719210421@osiris>"
  1022. email.sub! %r{^Subject:.*$}, "Subject: Re: [Feature request #2] Add ingredients categories"
  1023. end
  1024. assert_kind_of Journal, journal
  1025. assert_equal Issue.find(2), journal.journalized
  1026. end
  1027. end
  1028. end
  1029. def test_reply_to_a_message
  1030. m = submit_email('message_reply.eml')
  1031. assert m.is_a?(Message)
  1032. assert !m.new_record?
  1033. m.reload
  1034. assert_equal 'Reply via email', m.subject
  1035. # The email replies to message #2 which is part of the thread of message #1
  1036. assert_equal Message.find(1), m.parent
  1037. end
  1038. def test_reply_to_a_message_by_subject
  1039. m = submit_email('message_reply_by_subject.eml')
  1040. assert m.is_a?(Message)
  1041. assert !m.new_record?
  1042. m.reload
  1043. assert_equal 'Reply to the first post', m.subject
  1044. assert_equal Message.find(1), m.parent
  1045. end
  1046. def test_reply_to_a_locked_topic
  1047. # Lock the topic
  1048. topic = Message.find(2).parent
  1049. topic.update_attribute :locked, true
  1050. assert_no_difference('topic.replies_count') do
  1051. m = submit_email('message_reply_by_subject.eml')
  1052. assert_not_kind_of Message, m
  1053. end
  1054. end
  1055. def test_reply_to_a_nonexistent_topic
  1056. Message.find(2).destroy
  1057. assert_no_difference('Message.count') do
  1058. m = submit_email('message_reply_by_subject.eml')
  1059. assert_not m
  1060. end
  1061. end
  1062. def test_reply_to_a_topic_without_permission
  1063. Role.all.each {|r| r.remove_permission! :add_messages}
  1064. assert_no_difference('Message.count') do
  1065. assert_not submit_email('message_reply_by_subject.eml')
  1066. end
  1067. end
  1068. def test_reply_to_a_news
  1069. m = submit_email('news_reply.eml')
  1070. assert m.is_a?(Comment)
  1071. assert !m.new_record?
  1072. m.reload
  1073. assert_equal News.find(1), m.commented
  1074. assert_equal "This is a reply to a news.", m.content
  1075. end
  1076. def test_reply_to_a_news_comment
  1077. m = submit_email('news_comment_reply.eml')
  1078. assert m.is_a?(Comment)
  1079. assert !m.new_record?
  1080. m.reload
  1081. assert_equal News.find(1), m.commented
  1082. assert_equal "This is a reply to a comment.", m.content
  1083. end
  1084. def test_reply_to_a_nonexistant_news
  1085. News.find(1).destroy
  1086. assert_no_difference('Comment.count') do
  1087. assert_not submit_email('news_reply.eml')
  1088. assert_not submit_email('news_comment_reply.eml')
  1089. end
  1090. end
  1091. def test_reply_to_a_news_without_permission
  1092. Role.all.each {|r| r.remove_permission! :comment_news}
  1093. assert_no_difference('Comment.count') do
  1094. assert_not submit_email('news_reply.eml')
  1095. assert_not submit_email('news_comment_reply.eml')
  1096. end
  1097. end
  1098. def test_should_convert_tags_of_html_only_emails
  1099. with_settings :text_formatting => 'textile' do
  1100. issue = submit_email('ticket_html_only.eml', :issue => {:project => 'ecookbook'})
  1101. assert issue.is_a?(Issue)
  1102. assert !issue.new_record?
  1103. issue.reload
  1104. assert_equal 'HTML email', issue.subject
  1105. assert_equal "This is a *html-only* email.\r\n\r\nh1. With a title\r\n\r\nand a paragraph.", issue.description
  1106. end
  1107. end
  1108. def test_should_handle_outlook_web_access_2010_html_only
  1109. issue = submit_email('outlook_web_access_2010_html_only.eml', :issue => {:project => 'ecookbook'})
  1110. assert issue.is_a?(Issue)
  1111. issue.reload
  1112. assert_equal 'Upgrade Redmine to 3.0.x', issue.subject
  1113. assert_equal "A mess.\r\n\r\n--Geoff Maciolek\r\nMYCOMPANYNAME, LLC", issue.description
  1114. end
  1115. def test_should_handle_outlook_2010_html_only
  1116. issue = submit_email('outlook_2010_html_only.eml', :issue => {:project => 'ecookbook'})
  1117. assert issue.is_a?(Issue)
  1118. issue.reload
  1119. assert_equal 'Test email', issue.subject
  1120. assert_equal(
  1121. "Simple, unadorned test email generated by Outlook 2010. It is in HTML format, but" \
  1122. " no special formatting has been chosen. I’m going to save this as a draft and then manually" \
  1123. " drop it into the Inbox for scraping by Redmine 3.0.2.",
  1124. issue.description
  1125. )
  1126. end
  1127. test "truncate emails with no setting should add the entire email into the issue" do
  1128. with_settings :mail_handler_body_delimiters => '' do
  1129. issue = submit_email('ticket_on_given_project.eml')
  1130. assert_issue_created(issue)
  1131. assert issue.description.include?('---')
  1132. assert issue.description.include?('This paragraph is after the delimiter')
  1133. end
  1134. end
  1135. test "truncate emails with a single string should truncate the email at the delimiter for the issue" do
  1136. with_settings :mail_handler_body_delimiters => '---' do
  1137. issue = submit_email('ticket_on_given_project.eml')
  1138. assert_issue_created(issue)
  1139. assert issue.description.include?('This paragraph is before delimiters')
  1140. assert issue.description.include?('--- This line starts with a delimiter')
  1141. assert !issue.description.match(/^---\u00A0$/)
  1142. assert !issue.description.include?('This paragraph is after the delimiter')
  1143. end
  1144. end
  1145. test "truncate emails with a single quoted reply should truncate the email at the delimiter with the quoted reply symbols (>)" do
  1146. with_settings :mail_handler_body_delimiters => '--- Reply above. Do not remove this line. ---' do
  1147. journal = submit_email('issue_update_with_quoted_reply_above.eml')
  1148. assert journal.is_a?(Journal)
  1149. assert journal.notes.include?('An update to the issue by the sender.')
  1150. assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---"))
  1151. assert !journal.notes.include?('Looks like the JSON api for projects was missed.')
  1152. end
  1153. end
  1154. test "truncate emails with multiple quoted replies should truncate the email at the delimiter with the quoted reply symbols (>)" do
  1155. with_settings :mail_handler_body_delimiters => '--- Reply above. Do not remove this line. ---' do
  1156. journal = submit_email('issue_update_with_multiple_quoted_reply_above.eml')
  1157. assert journal.is_a?(Journal)
  1158. assert journal.notes.include?('An update to the issue by the sender.')
  1159. assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---"))
  1160. assert !journal.notes.include?('Looks like the JSON api for projects was missed.')
  1161. end
  1162. end
  1163. test "truncate emails with multiple strings should truncate the email at the first delimiter found (BREAK)" do
  1164. with_settings :mail_handler_body_delimiters => "---\nBREAK" do
  1165. issue = submit_email('ticket_on_given_project.eml')
  1166. assert_issue_created(issue)
  1167. assert issue.description.include?('This paragraph is before delimiters')
  1168. assert !issue.description.include?('BREAK')
  1169. assert !issue.description.include?('This paragraph is between delimiters')
  1170. assert !issue.description.match(/^---$/)
  1171. assert !issue.description.include?('This paragraph is after the delimiter')
  1172. end
  1173. end
  1174. test "truncate emails using a regex delimiter" do
  1175. delimiter = "On .*, .* at .*, .* <.*<mailto:.*>> wrote:"
  1176. with_settings :mail_handler_enable_regex_delimiters => '1', :mail_handler_body_delimiters => delimiter do
  1177. issue = submit_email('ticket_reply_from_mail.eml')
  1178. assert_issue_created(issue)
  1179. assert issue.description.include?('This paragraph is before delimiter')
  1180. assert !issue.description.include?('On Wed, 11 Oct at 1:05 PM, Jon Smith <jsmith@somenet.foo<mailto:jsmith@somenet.foo>> wrote:')
  1181. assert !issue.description.include?('This paragraph is after the delimiter')
  1182. end
  1183. with_settings :mail_handler_enable_regex_delimiters => '0', :mail_handler_body_delimiters => delimiter do
  1184. issue = submit_email('ticket_reply_from_mail.eml')
  1185. assert_issue_created(issue)
  1186. assert issue.description.include?('This paragraph is before delimiter')
  1187. assert issue.description.include?('On Wed, 11 Oct at 1:05 PM, Jon Smith <jsmith@somenet.foo<mailto:jsmith@somenet.foo>> wrote:')
  1188. assert issue.description.include?('This paragraph is after the delimiter')
  1189. end
  1190. end
  1191. def test_attachments_that_match_mail_handler_excluded_filenames_should_be_ignored
  1192. with_settings :mail_handler_excluded_filenames => "*.vcf,\n *.jpg" do
  1193. issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
  1194. assert issue.is_a?(Issue)
  1195. assert !issue.new_record?
  1196. assert_equal 0, issue.reload.attachments.size
  1197. end
  1198. end
  1199. def test_attachments_that_match_mail_handler_excluded_filenames_by_regex_should_be_ignored
  1200. with_settings :mail_handler_excluded_filenames => '.+\.vcf,(pa|nut)ella\.jpg',
  1201. :mail_handler_enable_regex_excluded_filenames => 1 do
  1202. issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
  1203. assert issue.is_a?(Issue)
  1204. assert !issue.new_record?
  1205. assert_equal 0, issue.reload.attachments.size
  1206. end
  1207. end
  1208. def test_attachments_that_do_not_match_mail_handler_excluded_filenames_should_be_attached
  1209. with_settings :mail_handler_excluded_filenames => '*.vcf, *.gif' do
  1210. issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
  1211. assert issue.is_a?(Issue)
  1212. assert !issue.new_record?
  1213. assert_equal 1, issue.reload.attachments.size
  1214. end
  1215. end
  1216. def test_email_with_long_subject_line
  1217. issue = submit_email('ticket_with_long_subject.eml')
  1218. assert issue.is_a?(Issue)
  1219. str =
  1220. 'New ticket on a given project with a very long subject line' \
  1221. ' which exceeds 255 chars and should not be ignored but chopped off.' \
  1222. ' And if the subject line is still not long enough, we just add more text.' \
  1223. ' And more text. Wow, this is really annoying.' \
  1224. ' Especially, if you have nothing to say...'
  1225. assert_equal issue.subject, str[0, 255]
  1226. end
  1227. def test_first_keyword_should_be_matched
  1228. issue = submit_email('ticket_with_duplicate_keyword.eml', :allow_override => 'priority')
  1229. assert issue.is_a?(Issue)
  1230. assert_equal 'High', issue.priority.name
  1231. end
  1232. def test_keyword_after_delimiter_should_be_ignored
  1233. with_settings :mail_handler_body_delimiters => "== DELIMITER ==" do
  1234. issue = submit_email('ticket_with_keyword_after_delimiter.eml', :allow_override => 'priority')
  1235. assert issue.is_a?(Issue)
  1236. assert_equal 'Normal', issue.priority.name
  1237. end
  1238. end
  1239. def test_new_user_from_attributes_should_return_valid_user
  1240. to_test = {
  1241. # [address, name] => [login, firstname, lastname]
  1242. ['jsmith@example.net', nil] => ['jsmith@example.net', 'jsmith', '-'],
  1243. ['jsmith@example.net', 'John'] => ['jsmith@example.net', 'John', '-'],
  1244. ['jsmith@example.net', 'John Smith'] => ['jsmith@example.net', 'John', 'Smith'],
  1245. ['jsmith@example.net', 'John Paul Smith'] => ['jsmith@example.net', 'John', 'Paul Smith'],
  1246. ['jsmith@example.net', 'AVeryLongFirstnameThatExceedsTheMaximumLength Smith'] =>
  1247. ['jsmith@example.net', 'AVeryLongFirstnameThatExceedsT', 'Smith'],
  1248. ['jsmith@example.net', 'John AVeryLongLastnameThatExceedsTheMaximumLength'] =>
  1249. ['jsmith@example.net', 'John', 'AVeryLongLastnameThatExceedsTh']
  1250. }
  1251. to_test.each do |attrs, expected|
  1252. user = MailHandler.new_user_from_attributes(attrs.first, attrs.last)
  1253. assert user.valid?, user.errors.full_messages.to_s
  1254. assert_equal attrs.first, user.mail
  1255. assert_equal expected[0], user.login
  1256. assert_equal expected[1], user.firstname
  1257. assert_equal expected[2], user.lastname
  1258. assert_equal 'only_my_events', user.mail_notification
  1259. end
  1260. end
  1261. def test_new_user_from_attributes_should_use_default_login_if_invalid
  1262. user = MailHandler.new_user_from_attributes('foo+bar@example.net')
  1263. assert user.valid?
  1264. assert user.login =~ /^user[a-f0-9]+$/
  1265. assert_equal 'foo+bar@example.net', user.mail
  1266. end
  1267. def test_new_user_with_utf8_encoded_fullname_should_be_decoded
  1268. assert_difference 'User.count' do
  1269. issue =
  1270. submit_email(
  1271. 'fullname_of_sender_as_utf8_encoded.eml',
  1272. :issue => {:project => 'ecookbook'},
  1273. :unknown_user => 'create'
  1274. )
  1275. end
  1276. user = User.order('id DESC').first
  1277. assert_equal "foo@example.org", user.mail
  1278. assert_equal 'Ää', user.firstname
  1279. assert_equal 'Öö', user.lastname
  1280. end
  1281. def test_new_user_with_fullname_in_parentheses
  1282. assert_difference 'User.count' do
  1283. issue =
  1284. submit_email(
  1285. 'fullname_of_sender_in_parentheses.eml',
  1286. :issue => {:project => 'ecookbook'},
  1287. :unknown_user => 'create'
  1288. )
  1289. end
  1290. user = User.order('id DESC').first
  1291. assert_equal "jdoe@example.net", user.mail
  1292. assert_equal 'John', user.firstname
  1293. assert_equal 'Doe', user.lastname
  1294. end
  1295. def test_extract_options_from_env_should_return_options
  1296. options =
  1297. MailHandler.extract_options_from_env(
  1298. {
  1299. 'tracker' => 'defect',
  1300. 'project' => 'foo',
  1301. 'unknown_user' => 'create',
  1302. 'no_notification' => '1'
  1303. }
  1304. )
  1305. assert_equal(
  1306. {:issue => {:tracker => 'defect', :project => 'foo'},
  1307. :unknown_user => 'create', :no_notification => '1'},
  1308. options
  1309. )
  1310. end
  1311. def test_safe_receive_should_rescue_exceptions_and_return_false
  1312. MailHandler.stubs(:receive).raises(StandardError.new("Something went wrong"))
  1313. assert_equal false, MailHandler.safe_receive
  1314. end
  1315. def test_smine_signature
  1316. issue = submit_email('smime_signature.eml', :issue => {:project => 'onlinestore'})
  1317. assert issue.is_a?(Issue)
  1318. assert !issue.new_record?
  1319. issue.reload
  1320. assert_equal 'Self-Signed S/MIME signature', issue.subject
  1321. assert_equal User.find_by_login('jsmith'), issue.author
  1322. assert_equal Project.find(2), issue.project
  1323. assert_equal 'smime.sh.txt describes how to create Self-Signed S/MIME Certs.', issue.description
  1324. assert_equal 2, issue.attachments.size
  1325. assert_equal 'smime.sh.txt', issue.attachments[0].filename
  1326. assert_equal 'text/plain', issue.attachments[0].content_type
  1327. assert_equal 'smime.p7s', issue.attachments[1].filename
  1328. assert_equal 'application/x-pkcs7-signature', issue.attachments[1].content_type
  1329. end
  1330. private
  1331. def submit_email(filename, options={})
  1332. raw = IO.read(File.join(FIXTURES_PATH, filename))
  1333. yield raw if block_given?
  1334. MailHandler.receive(raw, options)
  1335. end
  1336. def assert_issue_created(issue)
  1337. assert issue.is_a?(Issue)
  1338. assert !issue.new_record?
  1339. issue.reload
  1340. end
  1341. end