您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

issue_subtasking_test.rb 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. # Redmine - project management software
  2. # Copyright (C) 2006-2017 Jean-Philippe Lang
  3. #
  4. # This program is free software; you can redistribute it and/or
  5. # modify it under the terms of the GNU General Public License
  6. # as published by the Free Software Foundation; either version 2
  7. # of the License, or (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program; if not, write to the Free Software
  16. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  17. require File.expand_path('../../test_helper', __FILE__)
  18. class IssueSubtaskingTest < ActiveSupport::TestCase
  19. fixtures :projects, :users, :roles, :members, :member_roles,
  20. :trackers, :projects_trackers,
  21. :issue_statuses, :issue_categories, :enumerations,
  22. :issues,
  23. :enabled_modules,
  24. :workflows
  25. def setup
  26. User.current = nil
  27. end
  28. def test_leaf_planning_fields_should_be_editable
  29. issue = Issue.generate!
  30. user = User.find(1)
  31. %w(priority_id done_ratio start_date due_date estimated_hours).each do |attribute|
  32. assert issue.safe_attribute?(attribute, user)
  33. end
  34. end
  35. def test_parent_dates_should_be_read_only_with_parent_issue_dates_set_to_derived
  36. with_settings :parent_issue_dates => 'derived' do
  37. issue = Issue.generate_with_child!
  38. user = User.find(1)
  39. %w(start_date due_date).each do |attribute|
  40. assert !issue.safe_attribute?(attribute, user)
  41. end
  42. end
  43. end
  44. def test_parent_dates_should_be_lowest_start_and_highest_due_dates_with_parent_issue_dates_set_to_derived
  45. with_settings :parent_issue_dates => 'derived' do
  46. parent = Issue.generate!
  47. parent.generate_child!(:start_date => '2010-01-25', :due_date => '2010-02-15')
  48. parent.generate_child!( :due_date => '2010-02-13')
  49. parent.generate_child!(:start_date => '2010-02-01', :due_date => '2010-02-22')
  50. parent.reload
  51. assert_equal Date.parse('2010-01-25'), parent.start_date
  52. assert_equal Date.parse('2010-02-22'), parent.due_date
  53. end
  54. end
  55. def test_reschuling_a_parent_should_reschedule_subtasks_with_parent_issue_dates_set_to_derived
  56. with_settings :parent_issue_dates => 'derived' do
  57. parent = Issue.generate!
  58. c1 = parent.generate_child!(:start_date => '2010-05-12', :due_date => '2010-05-18')
  59. c2 = parent.generate_child!(:start_date => '2010-06-03', :due_date => '2010-06-10')
  60. parent.reload.reschedule_on!(Date.parse('2010-06-02'))
  61. c1.reload
  62. assert_equal [Date.parse('2010-06-02'), Date.parse('2010-06-08')], [c1.start_date, c1.due_date]
  63. c2.reload
  64. assert_equal [Date.parse('2010-06-03'), Date.parse('2010-06-10')], [c2.start_date, c2.due_date] # no change
  65. parent.reload
  66. assert_equal [Date.parse('2010-06-02'), Date.parse('2010-06-10')], [parent.start_date, parent.due_date]
  67. end
  68. end
  69. def test_parent_priority_should_be_read_only_with_parent_issue_priority_set_to_derived
  70. with_settings :parent_issue_priority => 'derived' do
  71. issue = Issue.generate_with_child!
  72. user = User.find(1)
  73. assert !issue.safe_attribute?('priority_id', user)
  74. end
  75. end
  76. def test_parent_priority_should_be_the_highest_open_child_priority
  77. with_settings :parent_issue_priority => 'derived' do
  78. parent = Issue.generate!(:priority => IssuePriority.find_by_name('Normal'))
  79. # Create children
  80. child1 = parent.generate_child!(:priority => IssuePriority.find_by_name('High'))
  81. assert_equal 'High', parent.reload.priority.name
  82. child2 = child1.generate_child!(:priority => IssuePriority.find_by_name('Immediate'))
  83. assert_equal 'Immediate', child1.reload.priority.name
  84. assert_equal 'Immediate', parent.reload.priority.name
  85. child3 = parent.generate_child!(:priority => IssuePriority.find_by_name('Low'))
  86. child4 = parent.generate_child!(:priority => IssuePriority.find_by_name('Urgent'))
  87. assert_equal 'Immediate', parent.reload.priority.name
  88. # Destroy a child
  89. child1.destroy
  90. assert_equal 'Urgent', parent.reload.priority.name
  91. # Close a child
  92. child4.status = IssueStatus.where(:is_closed => true).first
  93. child4.save!
  94. assert_equal 'Low', parent.reload.priority.name
  95. # Update a child
  96. child3.reload.priority = IssuePriority.find_by_name('Normal')
  97. child3.save!
  98. assert_equal 'Normal', parent.reload.priority.name
  99. # Reopen a child
  100. child4.status = IssueStatus.where(:is_closed => false).first
  101. child4.save!
  102. assert_equal 'Urgent', parent.reload.priority.name
  103. end
  104. end
  105. def test_parent_priority_should_be_set_to_default_when_all_children_are_closed
  106. with_settings :parent_issue_priority => 'derived' do
  107. parent = Issue.generate!
  108. child = parent.generate_child!(:priority => IssuePriority.find_by_name('High'))
  109. assert_equal 'High', parent.reload.priority.name
  110. child.status = IssueStatus.where(:is_closed => true).first
  111. child.save!
  112. assert_equal 'Normal', parent.reload.priority.name
  113. end
  114. end
  115. def test_parent_priority_should_be_left_unchanged_when_all_children_are_closed_and_no_default_priority
  116. IssuePriority.update_all :is_default => false
  117. with_settings :parent_issue_priority => 'derived' do
  118. parent = Issue.generate!(:priority => IssuePriority.find_by_name('Normal'))
  119. child = parent.generate_child!(:priority => IssuePriority.find_by_name('High'))
  120. assert_equal 'High', parent.reload.priority.name
  121. child.status = IssueStatus.where(:is_closed => true).first
  122. child.save!
  123. assert_equal 'High', parent.reload.priority.name
  124. end
  125. end
  126. def test_parent_done_ratio_should_be_read_only_with_parent_issue_done_ratio_set_to_derived
  127. with_settings :parent_issue_done_ratio => 'derived' do
  128. issue = Issue.generate_with_child!
  129. user = User.find(1)
  130. assert !issue.safe_attribute?('done_ratio', user)
  131. end
  132. end
  133. def test_parent_done_ratio_should_be_average_done_ratio_of_leaves
  134. with_settings :parent_issue_done_ratio => 'derived' do
  135. parent = Issue.generate!
  136. parent.generate_child!(:done_ratio => 20)
  137. assert_equal 20, parent.reload.done_ratio
  138. parent.generate_child!(:done_ratio => 70)
  139. assert_equal 45, parent.reload.done_ratio
  140. child = parent.generate_child!(:done_ratio => 0)
  141. assert_equal 30, parent.reload.done_ratio
  142. child.generate_child!(:done_ratio => 30)
  143. assert_equal 30, child.reload.done_ratio
  144. assert_equal 40, parent.reload.done_ratio
  145. end
  146. end
  147. def test_parent_done_ratio_should_be_rounded_down_to_the_nearest_integer
  148. with_settings :parent_issue_done_ratio => 'derived' do
  149. parent = Issue.generate!
  150. parent.generate_child!(:done_ratio => 20)
  151. parent.generate_child!(:done_ratio => 20)
  152. parent.generate_child!(:done_ratio => 10)
  153. # (20 + 20 + 10) / 3 = 16.666...
  154. assert_equal 16, parent.reload.done_ratio
  155. end
  156. end
  157. def test_parent_done_ratio_should_be_weighted_by_estimated_times_if_any
  158. with_settings :parent_issue_done_ratio => 'derived' do
  159. parent = Issue.generate!
  160. parent.generate_child!(:estimated_hours => 10, :done_ratio => 20)
  161. assert_equal 20, parent.reload.done_ratio
  162. parent.generate_child!(:estimated_hours => 20, :done_ratio => 50)
  163. assert_equal (50 * 20 + 20 * 10) / 30, parent.reload.done_ratio
  164. end
  165. end
  166. def test_parent_done_ratio_should_be_weighted_by_estimated_times_if_any_with_grandchildren
  167. # parent
  168. # child 1 (2h estd, 0% done)
  169. # child 2 (no estd)
  170. # child a (2h estd, 50% done)
  171. # child b (2h estd, 50% done)
  172. #
  173. # => parent should have a calculated progress of 33%
  174. #
  175. with_settings :parent_issue_done_ratio => 'derived' do
  176. parent = Issue.generate!
  177. parent.generate_child!(:estimated_hours => 2, :done_ratio => 0)
  178. child = parent.generate_child!
  179. child.generate_child!(:estimated_hours => 2, :done_ratio => 50)
  180. child.generate_child!(:estimated_hours => 2, :done_ratio => 50)
  181. assert_equal 50, child.reload.done_ratio
  182. assert_equal 33, parent.reload.done_ratio
  183. end
  184. end
  185. def test_parent_done_ratio_with_child_estimate_to_0_should_reach_100
  186. with_settings :parent_issue_done_ratio => 'derived' do
  187. parent = Issue.generate!
  188. issue1 = parent.generate_child!
  189. issue2 = parent.generate_child!(:estimated_hours => 0)
  190. assert_equal 0, parent.reload.done_ratio
  191. issue1.reload.close!
  192. assert_equal 50, parent.reload.done_ratio
  193. issue2.reload.close!
  194. assert_equal 100, parent.reload.done_ratio
  195. end
  196. end
  197. def test_done_ratio_of_parent_with_a_child_without_estimated_time_should_not_exceed_100
  198. with_settings :parent_issue_done_ratio => 'derived' do
  199. parent = Issue.generate!
  200. parent.generate_child!(:estimated_hours => 40)
  201. parent.generate_child!(:estimated_hours => 40)
  202. parent.generate_child!(:estimated_hours => 20)
  203. parent.generate_child!
  204. parent.reload.children.each(&:close!)
  205. assert_equal 100, parent.reload.done_ratio
  206. end
  207. end
  208. def test_done_ratio_of_parent_with_a_child_with_estimated_time_at_0_should_not_exceed_100
  209. with_settings :parent_issue_done_ratio => 'derived' do
  210. parent = Issue.generate!
  211. parent.generate_child!(:estimated_hours => 40)
  212. parent.generate_child!(:estimated_hours => 40)
  213. parent.generate_child!(:estimated_hours => 20)
  214. parent.generate_child!(:estimated_hours => 0)
  215. parent.reload.children.each(&:close!)
  216. assert_equal 100, parent.reload.done_ratio
  217. end
  218. end
  219. def test_changing_parent_should_update_previous_parent_done_ratio
  220. with_settings :parent_issue_done_ratio => 'derived' do
  221. first_parent = Issue.generate!
  222. second_parent = Issue.generate!
  223. first_parent.generate_child!(:done_ratio => 40)
  224. child = first_parent.generate_child!(:done_ratio => 20)
  225. assert_equal 30, first_parent.reload.done_ratio
  226. assert_equal 0, second_parent.reload.done_ratio
  227. child.update_attributes(:parent_issue_id => second_parent.id)
  228. assert_equal 40, first_parent.reload.done_ratio
  229. assert_equal 20, second_parent.reload.done_ratio
  230. end
  231. end
  232. def test_done_ratio_of_parent_should_reflect_children
  233. root = Issue.generate!
  234. child1 = root.generate_child!
  235. child2 = child1.generate_child!
  236. assert_equal 0, root.done_ratio
  237. assert_equal 0, child1.done_ratio
  238. assert_equal 0, child2.done_ratio
  239. with_settings :issue_done_ratio => 'issue_status' do
  240. status = IssueStatus.find(4)
  241. status.update_attribute :default_done_ratio, 50
  242. child1.update_attribute :status, status
  243. assert_equal 50, child1.done_ratio
  244. root.reload
  245. assert_equal 50, root.done_ratio
  246. end
  247. end
  248. def test_parent_dates_should_be_editable_with_parent_issue_dates_set_to_independent
  249. with_settings :parent_issue_dates => 'independent' do
  250. issue = Issue.generate_with_child!
  251. user = User.find(1)
  252. %w(start_date due_date).each do |attribute|
  253. assert issue.safe_attribute?(attribute, user)
  254. end
  255. end
  256. end
  257. def test_parent_dates_should_not_be_updated_with_parent_issue_dates_set_to_independent
  258. with_settings :parent_issue_dates => 'independent' do
  259. parent = Issue.generate!(:start_date => '2015-07-01', :due_date => '2015-08-01')
  260. parent.generate_child!(:start_date => '2015-06-01', :due_date => '2015-09-01')
  261. parent.reload
  262. assert_equal Date.parse('2015-07-01'), parent.start_date
  263. assert_equal Date.parse('2015-08-01'), parent.due_date
  264. end
  265. end
  266. def test_reschuling_a_parent_should_not_reschedule_subtasks_with_parent_issue_dates_set_to_independent
  267. with_settings :parent_issue_dates => 'independent' do
  268. parent = Issue.generate!(:start_date => '2010-05-01', :due_date => '2010-05-20')
  269. c1 = parent.generate_child!(:start_date => '2010-05-12', :due_date => '2010-05-18')
  270. parent.reload.reschedule_on!(Date.parse('2010-06-01'))
  271. assert_equal Date.parse('2010-06-01'), parent.reload.start_date
  272. c1.reload
  273. assert_equal [Date.parse('2010-05-12'), Date.parse('2010-05-18')], [c1.start_date, c1.due_date]
  274. end
  275. end
  276. def test_parent_priority_should_be_editable_with_parent_issue_priority_set_to_independent
  277. with_settings :parent_issue_priority => 'independent' do
  278. issue = Issue.generate_with_child!
  279. user = User.find(1)
  280. assert issue.safe_attribute?('priority_id', user)
  281. end
  282. end
  283. def test_parent_priority_should_not_be_updated_with_parent_issue_priority_set_to_independent
  284. with_settings :parent_issue_priority => 'independent' do
  285. parent = Issue.generate!(:priority => IssuePriority.find_by_name('Normal'))
  286. child1 = parent.generate_child!(:priority => IssuePriority.find_by_name('High'))
  287. assert_equal 'Normal', parent.reload.priority.name
  288. end
  289. end
  290. def test_parent_done_ratio_should_be_editable_with_parent_issue_done_ratio_set_to_independent
  291. with_settings :parent_issue_done_ratio => 'independent' do
  292. issue = Issue.generate_with_child!
  293. user = User.find(1)
  294. assert issue.safe_attribute?('done_ratio', user)
  295. end
  296. end
  297. def test_parent_done_ratio_should_not_be_updated_with_parent_issue_done_ratio_set_to_independent
  298. with_settings :parent_issue_done_ratio => 'independent' do
  299. parent = Issue.generate!(:done_ratio => 0)
  300. child1 = parent.generate_child!(:done_ratio => 10)
  301. assert_equal 0, parent.reload.done_ratio
  302. end
  303. end
  304. def test_parent_total_estimated_hours_should_be_sum_of_descendants
  305. parent = Issue.generate!
  306. parent.generate_child!(:estimated_hours => nil)
  307. assert_equal 0, parent.reload.total_estimated_hours
  308. parent.generate_child!(:estimated_hours => 5)
  309. assert_equal 5, parent.reload.total_estimated_hours
  310. parent.generate_child!(:estimated_hours => 7)
  311. assert_equal 12, parent.reload.total_estimated_hours
  312. end
  313. def test_open_issue_with_closed_parent_should_not_validate
  314. parent = Issue.generate!(:status_id => 5)
  315. child = Issue.generate!
  316. child.parent_issue_id = parent.id
  317. assert !child.save
  318. assert_include I18n.t("activerecord.errors.messages.open_issue_with_closed_parent"), child.errors.full_messages
  319. end
  320. end