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.

changeset_test.rb 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. # encoding: utf-8
  2. #
  3. # Redmine - project management software
  4. # Copyright (C) 2006-2014 Jean-Philippe Lang
  5. #
  6. # This program is free software; you can redistribute it and/or
  7. # modify it under the terms of the GNU General Public License
  8. # as published by the Free Software Foundation; either version 2
  9. # of the License, or (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program; if not, write to the Free Software
  18. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. require File.expand_path('../../test_helper', __FILE__)
  20. class ChangesetTest < ActiveSupport::TestCase
  21. fixtures :projects, :repositories,
  22. :issues, :issue_statuses, :issue_categories,
  23. :changesets, :changes,
  24. :enumerations,
  25. :custom_fields, :custom_values,
  26. :users, :members, :member_roles, :trackers,
  27. :enabled_modules, :roles
  28. def test_ref_keywords_any
  29. ActionMailer::Base.deliveries.clear
  30. Setting.commit_ref_keywords = '*'
  31. Setting.commit_update_keywords = [{'keywords' => 'fixes , closes', 'status_id' => '5', 'done_ratio' => '90'}]
  32. c = Changeset.new(:repository => Project.find(1).repository,
  33. :committed_on => Time.now,
  34. :comments => 'New commit (#2). Fixes #1',
  35. :revision => '12345')
  36. assert c.save
  37. assert_equal [1, 2], c.issue_ids.sort
  38. fixed = Issue.find(1)
  39. assert fixed.closed?
  40. assert_equal 90, fixed.done_ratio
  41. assert_equal 1, ActionMailer::Base.deliveries.size
  42. end
  43. def test_ref_keywords
  44. Setting.commit_ref_keywords = 'refs'
  45. Setting.commit_update_keywords = ''
  46. c = Changeset.new(:repository => Project.find(1).repository,
  47. :committed_on => Time.now,
  48. :comments => 'Ignores #2. Refs #1',
  49. :revision => '12345')
  50. assert c.save
  51. assert_equal [1], c.issue_ids.sort
  52. end
  53. def test_ref_keywords_any_only
  54. Setting.commit_ref_keywords = '*'
  55. Setting.commit_update_keywords = ''
  56. c = Changeset.new(:repository => Project.find(1).repository,
  57. :committed_on => Time.now,
  58. :comments => 'Ignores #2. Refs #1',
  59. :revision => '12345')
  60. assert c.save
  61. assert_equal [1, 2], c.issue_ids.sort
  62. end
  63. def test_ref_keywords_any_with_timelog
  64. Setting.commit_ref_keywords = '*'
  65. Setting.commit_logtime_enabled = '1'
  66. {
  67. '2' => 2.0,
  68. '2h' => 2.0,
  69. '2hours' => 2.0,
  70. '15m' => 0.25,
  71. '15min' => 0.25,
  72. '3h15' => 3.25,
  73. '3h15m' => 3.25,
  74. '3h15min' => 3.25,
  75. '3:15' => 3.25,
  76. '3.25' => 3.25,
  77. '3.25h' => 3.25,
  78. '3,25' => 3.25,
  79. '3,25h' => 3.25,
  80. }.each do |syntax, expected_hours|
  81. c = Changeset.new(:repository => Project.find(1).repository,
  82. :committed_on => 24.hours.ago,
  83. :comments => "Worked on this issue #1 @#{syntax}",
  84. :revision => '520',
  85. :user => User.find(2))
  86. assert_difference 'TimeEntry.count' do
  87. c.scan_comment_for_issue_ids
  88. end
  89. assert_equal [1], c.issue_ids.sort
  90. time = TimeEntry.first(:order => 'id desc')
  91. assert_equal 1, time.issue_id
  92. assert_equal 1, time.project_id
  93. assert_equal 2, time.user_id
  94. assert_equal expected_hours, time.hours,
  95. "@#{syntax} should be logged as #{expected_hours} hours but was #{time.hours}"
  96. assert_equal Date.yesterday, time.spent_on
  97. assert time.activity.is_default?
  98. assert time.comments.include?('r520'),
  99. "r520 was expected in time_entry comments: #{time.comments}"
  100. end
  101. end
  102. def test_ref_keywords_closing_with_timelog
  103. Setting.commit_ref_keywords = '*'
  104. Setting.commit_update_keywords = [{'keywords' => 'fixes , closes', 'status_id' => IssueStatus.where(:is_closed => true).first.id.to_s}]
  105. Setting.commit_logtime_enabled = '1'
  106. c = Changeset.new(:repository => Project.find(1).repository,
  107. :committed_on => Time.now,
  108. :comments => 'This is a comment. Fixes #1 @4.5, #2 @1',
  109. :user => User.find(2))
  110. assert_difference 'TimeEntry.count', 2 do
  111. c.scan_comment_for_issue_ids
  112. end
  113. assert_equal [1, 2], c.issue_ids.sort
  114. assert Issue.find(1).closed?
  115. assert Issue.find(2).closed?
  116. times = TimeEntry.all(:order => 'id desc', :limit => 2)
  117. assert_equal [1, 2], times.collect(&:issue_id).sort
  118. end
  119. def test_ref_keywords_any_line_start
  120. Setting.commit_ref_keywords = '*'
  121. c = Changeset.new(:repository => Project.find(1).repository,
  122. :committed_on => Time.now,
  123. :comments => '#1 is the reason of this commit',
  124. :revision => '12345')
  125. assert c.save
  126. assert_equal [1], c.issue_ids.sort
  127. end
  128. def test_ref_keywords_allow_brackets_around_a_issue_number
  129. Setting.commit_ref_keywords = '*'
  130. c = Changeset.new(:repository => Project.find(1).repository,
  131. :committed_on => Time.now,
  132. :comments => '[#1] Worked on this issue',
  133. :revision => '12345')
  134. assert c.save
  135. assert_equal [1], c.issue_ids.sort
  136. end
  137. def test_ref_keywords_allow_brackets_around_multiple_issue_numbers
  138. Setting.commit_ref_keywords = '*'
  139. c = Changeset.new(:repository => Project.find(1).repository,
  140. :committed_on => Time.now,
  141. :comments => '[#1 #2, #3] Worked on these',
  142. :revision => '12345')
  143. assert c.save
  144. assert_equal [1,2,3], c.issue_ids.sort
  145. end
  146. def test_update_keywords_with_multiple_rules
  147. with_settings :commit_update_keywords => [
  148. {'keywords' => 'fixes, closes', 'status_id' => '5'},
  149. {'keywords' => 'resolves', 'status_id' => '3'}
  150. ] do
  151. issue1 = Issue.generate!
  152. issue2 = Issue.generate!
  153. Changeset.generate!(:comments => "Closes ##{issue1.id}\nResolves ##{issue2.id}")
  154. assert_equal 5, issue1.reload.status_id
  155. assert_equal 3, issue2.reload.status_id
  156. end
  157. end
  158. def test_update_keywords_with_multiple_rules_should_match_tracker
  159. with_settings :commit_update_keywords => [
  160. {'keywords' => 'fixes', 'status_id' => '5', 'if_tracker_id' => '2'},
  161. {'keywords' => 'fixes', 'status_id' => '3', 'if_tracker_id' => ''}
  162. ] do
  163. issue1 = Issue.generate!(:tracker_id => 2)
  164. issue2 = Issue.generate!
  165. Changeset.generate!(:comments => "Fixes ##{issue1.id}, ##{issue2.id}")
  166. assert_equal 5, issue1.reload.status_id
  167. assert_equal 3, issue2.reload.status_id
  168. end
  169. end
  170. def test_update_keywords_with_multiple_rules_and_no_match
  171. with_settings :commit_update_keywords => [
  172. {'keywords' => 'fixes', 'status_id' => '5', 'if_tracker_id' => '2'},
  173. {'keywords' => 'fixes', 'status_id' => '3', 'if_tracker_id' => '3'}
  174. ] do
  175. issue1 = Issue.generate!(:tracker_id => 2)
  176. issue2 = Issue.generate!
  177. Changeset.generate!(:comments => "Fixes ##{issue1.id}, ##{issue2.id}")
  178. assert_equal 5, issue1.reload.status_id
  179. assert_equal 1, issue2.reload.status_id # no updates
  180. end
  181. end
  182. def test_commit_referencing_a_subproject_issue
  183. c = Changeset.new(:repository => Project.find(1).repository,
  184. :committed_on => Time.now,
  185. :comments => 'refs #5, a subproject issue',
  186. :revision => '12345')
  187. assert c.save
  188. assert_equal [5], c.issue_ids.sort
  189. assert c.issues.first.project != c.project
  190. end
  191. def test_commit_closing_a_subproject_issue
  192. with_settings :commit_update_keywords => [{'keywords' => 'closes', 'status_id' => '5'}],
  193. :default_language => 'en' do
  194. issue = Issue.find(5)
  195. assert !issue.closed?
  196. assert_difference 'Journal.count' do
  197. c = Changeset.new(:repository => Project.find(1).repository,
  198. :committed_on => Time.now,
  199. :comments => 'closes #5, a subproject issue',
  200. :revision => '12345')
  201. assert c.save
  202. end
  203. assert issue.reload.closed?
  204. journal = Journal.first(:order => 'id DESC')
  205. assert_equal issue, journal.issue
  206. assert_include "Applied in changeset ecookbook:r12345.", journal.notes
  207. end
  208. end
  209. def test_commit_referencing_a_parent_project_issue
  210. # repository of child project
  211. r = Repository::Subversion.create!(
  212. :project => Project.find(3),
  213. :url => 'svn://localhost/test')
  214. c = Changeset.new(:repository => r,
  215. :committed_on => Time.now,
  216. :comments => 'refs #2, an issue of a parent project',
  217. :revision => '12345')
  218. assert c.save
  219. assert_equal [2], c.issue_ids.sort
  220. assert c.issues.first.project != c.project
  221. end
  222. def test_commit_referencing_a_project_with_commit_cross_project_ref_disabled
  223. r = Repository::Subversion.create!(
  224. :project => Project.find(3),
  225. :url => 'svn://localhost/test')
  226. with_settings :commit_cross_project_ref => '0' do
  227. c = Changeset.new(:repository => r,
  228. :committed_on => Time.now,
  229. :comments => 'refs #4, an issue of a different project',
  230. :revision => '12345')
  231. assert c.save
  232. assert_equal [], c.issue_ids
  233. end
  234. end
  235. def test_commit_referencing_a_project_with_commit_cross_project_ref_enabled
  236. r = Repository::Subversion.create!(
  237. :project => Project.find(3),
  238. :url => 'svn://localhost/test')
  239. with_settings :commit_cross_project_ref => '1' do
  240. c = Changeset.new(:repository => r,
  241. :committed_on => Time.now,
  242. :comments => 'refs #4, an issue of a different project',
  243. :revision => '12345')
  244. assert c.save
  245. assert_equal [4], c.issue_ids
  246. end
  247. end
  248. def test_old_commits_should_not_update_issues_nor_log_time
  249. Setting.commit_ref_keywords = '*'
  250. Setting.commit_update_keywords = {'fixes , closes' => {'status_id' => '5', 'done_ratio' => '90'}}
  251. Setting.commit_logtime_enabled = '1'
  252. repository = Project.find(1).repository
  253. repository.created_on = Time.now
  254. repository.save!
  255. c = Changeset.new(:repository => repository,
  256. :committed_on => 1.month.ago,
  257. :comments => 'New commit (#2). Fixes #1 @1h',
  258. :revision => '12345')
  259. assert_no_difference 'TimeEntry.count' do
  260. assert c.save
  261. end
  262. assert_equal [1, 2], c.issue_ids.sort
  263. issue = Issue.find(1)
  264. assert_equal 1, issue.status_id
  265. assert_equal 0, issue.done_ratio
  266. end
  267. def test_text_tag_revision
  268. c = Changeset.new(:revision => '520')
  269. assert_equal 'r520', c.text_tag
  270. end
  271. def test_text_tag_revision_with_same_project
  272. c = Changeset.new(:revision => '520', :repository => Project.find(1).repository)
  273. assert_equal 'r520', c.text_tag(Project.find(1))
  274. end
  275. def test_text_tag_revision_with_different_project
  276. c = Changeset.new(:revision => '520', :repository => Project.find(1).repository)
  277. assert_equal 'ecookbook:r520', c.text_tag(Project.find(2))
  278. end
  279. def test_text_tag_revision_with_repository_identifier
  280. r = Repository::Subversion.create!(
  281. :project_id => 1,
  282. :url => 'svn://localhost/test',
  283. :identifier => 'documents')
  284. c = Changeset.new(:revision => '520', :repository => r)
  285. assert_equal 'documents|r520', c.text_tag
  286. assert_equal 'ecookbook:documents|r520', c.text_tag(Project.find(2))
  287. end
  288. def test_text_tag_hash
  289. c = Changeset.new(
  290. :scmid => '7234cb2750b63f47bff735edc50a1c0a433c2518',
  291. :revision => '7234cb2750b63f47bff735edc50a1c0a433c2518')
  292. assert_equal 'commit:7234cb2750b63f47bff735edc50a1c0a433c2518', c.text_tag
  293. end
  294. def test_text_tag_hash_with_same_project
  295. c = Changeset.new(:revision => '7234cb27', :scmid => '7234cb27', :repository => Project.find(1).repository)
  296. assert_equal 'commit:7234cb27', c.text_tag(Project.find(1))
  297. end
  298. def test_text_tag_hash_with_different_project
  299. c = Changeset.new(:revision => '7234cb27', :scmid => '7234cb27', :repository => Project.find(1).repository)
  300. assert_equal 'ecookbook:commit:7234cb27', c.text_tag(Project.find(2))
  301. end
  302. def test_text_tag_hash_all_number
  303. c = Changeset.new(:scmid => '0123456789', :revision => '0123456789')
  304. assert_equal 'commit:0123456789', c.text_tag
  305. end
  306. def test_text_tag_hash_with_repository_identifier
  307. r = Repository::Subversion.new(
  308. :project_id => 1,
  309. :url => 'svn://localhost/test',
  310. :identifier => 'documents')
  311. c = Changeset.new(:revision => '7234cb27', :scmid => '7234cb27', :repository => r)
  312. assert_equal 'commit:documents|7234cb27', c.text_tag
  313. assert_equal 'ecookbook:commit:documents|7234cb27', c.text_tag(Project.find(2))
  314. end
  315. def test_previous
  316. changeset = Changeset.find_by_revision('3')
  317. assert_equal Changeset.find_by_revision('2'), changeset.previous
  318. end
  319. def test_previous_nil
  320. changeset = Changeset.find_by_revision('1')
  321. assert_nil changeset.previous
  322. end
  323. def test_next
  324. changeset = Changeset.find_by_revision('2')
  325. assert_equal Changeset.find_by_revision('3'), changeset.next
  326. end
  327. def test_next_nil
  328. changeset = Changeset.find_by_revision('10')
  329. assert_nil changeset.next
  330. end
  331. def test_comments_should_be_converted_to_utf8
  332. proj = Project.find(3)
  333. # str = File.read("#{RAILS_ROOT}/test/fixtures/encoding/iso-8859-1.txt")
  334. str = "Texte encod\xe9 en ISO-8859-1."
  335. str.force_encoding("ASCII-8BIT") if str.respond_to?(:force_encoding)
  336. r = Repository::Bazaar.create!(
  337. :project => proj,
  338. :url => '/tmp/test/bazaar',
  339. :log_encoding => 'ISO-8859-1' )
  340. assert r
  341. c = Changeset.new(:repository => r,
  342. :committed_on => Time.now,
  343. :revision => '123',
  344. :scmid => '12345',
  345. :comments => str)
  346. assert( c.save )
  347. str_utf8 = "Texte encod\xc3\xa9 en ISO-8859-1."
  348. str_utf8.force_encoding("UTF-8") if str_utf8.respond_to?(:force_encoding)
  349. assert_equal str_utf8, c.comments
  350. end
  351. def test_invalid_utf8_sequences_in_comments_should_be_replaced_latin1
  352. proj = Project.find(3)
  353. # str = File.read("#{RAILS_ROOT}/test/fixtures/encoding/iso-8859-1.txt")
  354. str1 = "Texte encod\xe9 en ISO-8859-1."
  355. str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test"
  356. str1.force_encoding("UTF-8") if str1.respond_to?(:force_encoding)
  357. str2.force_encoding("ASCII-8BIT") if str2.respond_to?(:force_encoding)
  358. r = Repository::Bazaar.create!(
  359. :project => proj,
  360. :url => '/tmp/test/bazaar',
  361. :log_encoding => 'UTF-8' )
  362. assert r
  363. c = Changeset.new(:repository => r,
  364. :committed_on => Time.now,
  365. :revision => '123',
  366. :scmid => '12345',
  367. :comments => str1,
  368. :committer => str2)
  369. assert( c.save )
  370. assert_equal "Texte encod? en ISO-8859-1.", c.comments
  371. assert_equal "?a?b?c?d?e test", c.committer
  372. end
  373. def test_invalid_utf8_sequences_in_comments_should_be_replaced_ja_jis
  374. proj = Project.find(3)
  375. str = "test\xb5\xfetest\xb5\xfe"
  376. if str.respond_to?(:force_encoding)
  377. str.force_encoding('ASCII-8BIT')
  378. end
  379. r = Repository::Bazaar.create!(
  380. :project => proj,
  381. :url => '/tmp/test/bazaar',
  382. :log_encoding => 'ISO-2022-JP' )
  383. assert r
  384. c = Changeset.new(:repository => r,
  385. :committed_on => Time.now,
  386. :revision => '123',
  387. :scmid => '12345',
  388. :comments => str)
  389. assert( c.save )
  390. assert_equal "test??test??", c.comments
  391. end
  392. def test_comments_should_be_converted_all_latin1_to_utf8
  393. s1 = "\xC2\x80"
  394. s2 = "\xc3\x82\xc2\x80"
  395. s4 = s2.dup
  396. if s1.respond_to?(:force_encoding)
  397. s3 = s1.dup
  398. s1.force_encoding('ASCII-8BIT')
  399. s2.force_encoding('ASCII-8BIT')
  400. s3.force_encoding('ISO-8859-1')
  401. s4.force_encoding('UTF-8')
  402. assert_equal s3.encode('UTF-8'), s4
  403. end
  404. proj = Project.find(3)
  405. r = Repository::Bazaar.create!(
  406. :project => proj,
  407. :url => '/tmp/test/bazaar',
  408. :log_encoding => 'ISO-8859-1' )
  409. assert r
  410. c = Changeset.new(:repository => r,
  411. :committed_on => Time.now,
  412. :revision => '123',
  413. :scmid => '12345',
  414. :comments => s1)
  415. assert( c.save )
  416. assert_equal s4, c.comments
  417. end
  418. def test_invalid_utf8_sequences_in_paths_should_be_replaced
  419. proj = Project.find(3)
  420. str1 = "Texte encod\xe9 en ISO-8859-1"
  421. str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test"
  422. str1.force_encoding("UTF-8") if str1.respond_to?(:force_encoding)
  423. str2.force_encoding("ASCII-8BIT") if str2.respond_to?(:force_encoding)
  424. r = Repository::Bazaar.create!(
  425. :project => proj,
  426. :url => '/tmp/test/bazaar',
  427. :log_encoding => 'UTF-8' )
  428. assert r
  429. cs = Changeset.new(
  430. :repository => r,
  431. :committed_on => Time.now,
  432. :revision => '123',
  433. :scmid => '12345',
  434. :comments => "test")
  435. assert(cs.save)
  436. ch = Change.new(
  437. :changeset => cs,
  438. :action => "A",
  439. :path => str1,
  440. :from_path => str2,
  441. :from_revision => "345")
  442. assert(ch.save)
  443. assert_equal "Texte encod? en ISO-8859-1", ch.path
  444. assert_equal "?a?b?c?d?e test", ch.from_path
  445. end
  446. def test_comments_nil
  447. proj = Project.find(3)
  448. r = Repository::Bazaar.create!(
  449. :project => proj,
  450. :url => '/tmp/test/bazaar',
  451. :log_encoding => 'ISO-8859-1' )
  452. assert r
  453. c = Changeset.new(:repository => r,
  454. :committed_on => Time.now,
  455. :revision => '123',
  456. :scmid => '12345',
  457. :comments => nil,
  458. :committer => nil)
  459. assert( c.save )
  460. assert_equal "", c.comments
  461. assert_equal nil, c.committer
  462. if c.comments.respond_to?(:force_encoding)
  463. assert_equal "UTF-8", c.comments.encoding.to_s
  464. end
  465. end
  466. def test_comments_empty
  467. proj = Project.find(3)
  468. r = Repository::Bazaar.create!(
  469. :project => proj,
  470. :url => '/tmp/test/bazaar',
  471. :log_encoding => 'ISO-8859-1' )
  472. assert r
  473. c = Changeset.new(:repository => r,
  474. :committed_on => Time.now,
  475. :revision => '123',
  476. :scmid => '12345',
  477. :comments => "",
  478. :committer => "")
  479. assert( c.save )
  480. assert_equal "", c.comments
  481. assert_equal "", c.committer
  482. if c.comments.respond_to?(:force_encoding)
  483. assert_equal "UTF-8", c.comments.encoding.to_s
  484. assert_equal "UTF-8", c.committer.encoding.to_s
  485. end
  486. end
  487. def test_identifier
  488. c = Changeset.find_by_revision('1')
  489. assert_equal c.revision, c.identifier
  490. end
  491. end