it|s all
[simply.git] / test / unit / issue_nested_set_test.rb
blobcda8c4e587920395ceae4135b6885a67adb583cd
1 # redMine - project management software
2 # Copyright (C) 2006-2007  Jean-Philippe Lang
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.
18 require File.dirname(__FILE__) + '/../test_helper'
20 class IssueNestedSetTest < ActiveSupport::TestCase
21   fixtures :projects, :users, :members, :member_roles, :roles,
22            :trackers, :projects_trackers,
23            :versions,
24            :issue_statuses, :issue_categories, :issue_relations, :workflows, 
25            :enumerations,
26            :issues,
27            :custom_fields, :custom_fields_projects, :custom_fields_trackers, :custom_values,
28            :time_entries
30   self.use_transactional_fixtures = false
31   
32   def test_create_root_issue
33     issue1 = create_issue!
34     issue2 = create_issue!
35     issue1.reload
36     issue2.reload
37     
38     assert_equal [issue1.id, nil, 1, 2], [issue1.root_id, issue1.parent_id, issue1.lft, issue1.rgt]
39     assert_equal [issue2.id, nil, 1, 2], [issue2.root_id, issue2.parent_id, issue2.lft, issue2.rgt]
40   end
41   
42   def test_create_child_issue
43     parent = create_issue!
44     child =  create_issue!(:parent_issue_id => parent.id)
45     parent.reload
46     child.reload
47     
48     assert_equal [parent.id, nil, 1, 4], [parent.root_id, parent.parent_id, parent.lft, parent.rgt]
49     assert_equal [parent.id, parent.id, 2, 3], [child.root_id, child.parent_id, child.lft, child.rgt]
50   end
51   
52   def test_creating_a_child_in_different_project_should_not_validate
53     issue = create_issue!
54     child = Issue.new(:project_id => 2, :tracker_id => 1, :author_id => 1, :subject => 'child', :parent_issue_id => issue.id)
55     assert !child.save
56     assert_not_nil child.errors.on(:parent_issue_id)
57   end
58   
59   def test_move_a_root_to_child
60     parent1 = create_issue!
61     parent2 = create_issue!
62     child = create_issue!(:parent_issue_id => parent1.id)
63     
64     parent2.parent_issue_id = parent1.id
65     parent2.save!
66     child.reload
67     parent1.reload
68     parent2.reload
69     
70     assert_equal [parent1.id, 1, 6], [parent1.root_id, parent1.lft, parent1.rgt]
71     assert_equal [parent1.id, 4, 5], [parent2.root_id, parent2.lft, parent2.rgt]
72     assert_equal [parent1.id, 2, 3], [child.root_id, child.lft, child.rgt]
73   end
74   
75   def test_move_a_child_to_root
76     parent1 = create_issue!
77     parent2 = create_issue!
78     child =   create_issue!(:parent_issue_id => parent1.id)
79     
80     child.parent_issue_id = nil
81     child.save!
82     child.reload
83     parent1.reload
84     parent2.reload
85     
86     assert_equal [parent1.id, 1, 2], [parent1.root_id, parent1.lft, parent1.rgt]
87     assert_equal [parent2.id, 1, 2], [parent2.root_id, parent2.lft, parent2.rgt]
88     assert_equal [child.id, 1, 2], [child.root_id, child.lft, child.rgt]
89   end
90   
91   def test_move_a_child_to_another_issue
92     parent1 = create_issue!
93     parent2 = create_issue!
94     child =   create_issue!(:parent_issue_id => parent1.id)
95     
96     child.parent_issue_id = parent2.id
97     child.save!
98     child.reload
99     parent1.reload
100     parent2.reload
101     
102     assert_equal [parent1.id, 1, 2], [parent1.root_id, parent1.lft, parent1.rgt]
103     assert_equal [parent2.id, 1, 4], [parent2.root_id, parent2.lft, parent2.rgt]
104     assert_equal [parent2.id, 2, 3], [child.root_id, child.lft, child.rgt]
105   end
106   
107   def test_move_a_child_with_descendants_to_another_issue
108     parent1 = create_issue!
109     parent2 = create_issue!
110     child =   create_issue!(:parent_issue_id => parent1.id)
111     grandchild = create_issue!(:parent_issue_id => child.id)
112     
113     parent1.reload
114     parent2.reload
115     child.reload
116     grandchild.reload
117     
118     assert_equal [parent1.id, 1, 6], [parent1.root_id, parent1.lft, parent1.rgt]
119     assert_equal [parent2.id, 1, 2], [parent2.root_id, parent2.lft, parent2.rgt]
120     assert_equal [parent1.id, 2, 5], [child.root_id, child.lft, child.rgt]
121     assert_equal [parent1.id, 3, 4], [grandchild.root_id, grandchild.lft, grandchild.rgt]
122     
123     child.reload.parent_issue_id = parent2.id
124     child.save!
125     child.reload
126     grandchild.reload
127     parent1.reload
128     parent2.reload
129     
130     assert_equal [parent1.id, 1, 2], [parent1.root_id, parent1.lft, parent1.rgt]
131     assert_equal [parent2.id, 1, 6], [parent2.root_id, parent2.lft, parent2.rgt]
132     assert_equal [parent2.id, 2, 5], [child.root_id, child.lft, child.rgt]
133     assert_equal [parent2.id, 3, 4], [grandchild.root_id, grandchild.lft, grandchild.rgt]
134   end
135   
136   def test_move_a_child_with_descendants_to_another_project
137     parent1 = create_issue!
138     child =   create_issue!(:parent_issue_id => parent1.id)
139     grandchild = create_issue!(:parent_issue_id => child.id)
140         
141     assert child.reload.move_to_project(Project.find(2))
142     child.reload
143     grandchild.reload
144     parent1.reload
145     
146     assert_equal [1, parent1.id, 1, 2], [parent1.project_id, parent1.root_id, parent1.lft, parent1.rgt]
147     assert_equal [2, child.id, 1, 4], [child.project_id, child.root_id, child.lft, child.rgt]
148     assert_equal [2, child.id, 2, 3], [grandchild.project_id, grandchild.root_id, grandchild.lft, grandchild.rgt]
149   end
150   
151   def test_invalid_move_to_another_project
152     parent1 = create_issue!
153     child =   create_issue!(:parent_issue_id => parent1.id)
154     grandchild = create_issue!(:parent_issue_id => child.id, :tracker_id => 2)
155     Project.find(2).tracker_ids = [1]
156     
157     parent1.reload
158     assert_equal [1, parent1.id, 1, 6], [parent1.project_id, parent1.root_id, parent1.lft, parent1.rgt]
159     
160     # child can not be moved to Project 2 because its child is on a disabled tracker
161     assert_equal false, Issue.find(child.id).move_to_project(Project.find(2))
162     child.reload
163     grandchild.reload
164     parent1.reload
165     
166     # no change
167     assert_equal [1, parent1.id, 1, 6], [parent1.project_id, parent1.root_id, parent1.lft, parent1.rgt]
168     assert_equal [1, parent1.id, 2, 5], [child.project_id, child.root_id, child.lft, child.rgt]
169     assert_equal [1, parent1.id, 3, 4], [grandchild.project_id, grandchild.root_id, grandchild.lft, grandchild.rgt]
170   end
171   
172   def test_moving_an_issue_to_a_descendant_should_not_validate
173     parent1 = create_issue!
174     parent2 = create_issue!
175     child =   create_issue!(:parent_issue_id => parent1.id)
176     grandchild = create_issue!(:parent_issue_id => child.id)
177     
178     child.reload
179     child.parent_issue_id = grandchild.id
180     assert !child.save
181     assert_not_nil child.errors.on(:parent_issue_id)
182   end
183   
184   def test_moving_an_issue_should_keep_valid_relations_only
185     issue1 = create_issue!
186     issue2 = create_issue!
187     issue3 = create_issue!(:parent_issue_id => issue2.id)
188     issue4 = create_issue!
189     r1 = IssueRelation.create!(:issue_from => issue1, :issue_to => issue2, :relation_type => IssueRelation::TYPE_PRECEDES)
190     r2 = IssueRelation.create!(:issue_from => issue1, :issue_to => issue3, :relation_type => IssueRelation::TYPE_PRECEDES)
191     r3 = IssueRelation.create!(:issue_from => issue2, :issue_to => issue4, :relation_type => IssueRelation::TYPE_PRECEDES)
192     issue2.reload
193     issue2.parent_issue_id = issue1.id
194     issue2.save!
195     assert !IssueRelation.exists?(r1.id)
196     assert !IssueRelation.exists?(r2.id)
197     assert IssueRelation.exists?(r3.id)
198   end
199   
200   def test_destroy_should_destroy_children
201     issue1 = create_issue!
202     issue2 = create_issue!
203     issue3 = create_issue!(:parent_issue_id => issue2.id)
204     issue4 = create_issue!(:parent_issue_id => issue1.id)
205     issue2.reload.destroy
206     issue1.reload
207     issue4.reload
208     assert !Issue.exists?(issue2.id)
209     assert !Issue.exists?(issue3.id)
210     assert_equal [issue1.id, 1, 4], [issue1.root_id, issue1.lft, issue1.rgt]
211     assert_equal [issue1.id, 2, 3], [issue4.root_id, issue4.lft, issue4.rgt]
212   end
213   
214   def test_parent_priority_should_be_the_highest_child_priority
215     parent = create_issue!(:priority => IssuePriority.find_by_name('Normal'))
216     # Create children
217     child1 = create_issue!(:priority => IssuePriority.find_by_name('High'), :parent_issue_id => parent.id)
218     assert_equal 'High', parent.reload.priority.name
219     child2 = create_issue!(:priority => IssuePriority.find_by_name('Immediate'), :parent_issue_id => child1.id)
220     assert_equal 'Immediate', child1.reload.priority.name
221     assert_equal 'Immediate', parent.reload.priority.name
222     child3 = create_issue!(:priority => IssuePriority.find_by_name('Low'), :parent_issue_id => parent.id)
223     assert_equal 'Immediate', parent.reload.priority.name
224     # Destroy a child
225     child1.destroy
226     assert_equal 'Low', parent.reload.priority.name
227     # Update a child
228     child3.reload.priority = IssuePriority.find_by_name('Normal')
229     child3.save!
230     assert_equal 'Normal', parent.reload.priority.name
231   end
232   
233   def test_parent_dates_should_be_lowest_start_and_highest_due_dates
234     parent = create_issue!
235     create_issue!(:start_date => '2010-01-25', :due_date => '2010-02-15', :parent_issue_id => parent.id)
236     create_issue!(                             :due_date => '2010-02-13', :parent_issue_id => parent.id)
237     create_issue!(:start_date => '2010-02-01', :due_date => '2010-02-22', :parent_issue_id => parent.id)
238     parent.reload
239     assert_equal Date.parse('2010-01-25'), parent.start_date
240     assert_equal Date.parse('2010-02-22'), parent.due_date
241   end
242   
243   def test_parent_done_ratio_should_be_average_done_ratio_of_leaves
244     parent = create_issue!
245     create_issue!(:done_ratio => 20, :parent_issue_id => parent.id)
246     assert_equal 20, parent.reload.done_ratio
247     create_issue!(:done_ratio => 70, :parent_issue_id => parent.id)
248     assert_equal 45, parent.reload.done_ratio
249     
250     child = create_issue!(:done_ratio => 0, :parent_issue_id => parent.id)
251     assert_equal 30, parent.reload.done_ratio
252     
253     create_issue!(:done_ratio => 30, :parent_issue_id => child.id)
254     assert_equal 30, child.reload.done_ratio
255     assert_equal 40, parent.reload.done_ratio
256   end
257   
258   def test_parent_done_ratio_should_be_weighted_by_estimated_times_if_any
259     parent = create_issue!
260     create_issue!(:estimated_hours => 10, :done_ratio => 20, :parent_issue_id => parent.id)
261     assert_equal 20, parent.reload.done_ratio
262     create_issue!(:estimated_hours => 20, :done_ratio => 50, :parent_issue_id => parent.id)
263     assert_equal (50 * 20 + 20 * 10) / 30, parent.reload.done_ratio
264   end
265   
266   def test_parent_estimate_should_be_sum_of_leaves
267     parent = create_issue!
268     create_issue!(:estimated_hours => nil, :parent_issue_id => parent.id)
269     assert_equal nil, parent.reload.estimated_hours
270     create_issue!(:estimated_hours => 5, :parent_issue_id => parent.id)
271     assert_equal 5, parent.reload.estimated_hours
272     create_issue!(:estimated_hours => 7, :parent_issue_id => parent.id)
273     assert_equal 12, parent.reload.estimated_hours
274   end
276   def test_move_parent_updates_old_parent_attributes
277     first_parent = create_issue!
278     second_parent = create_issue!
279     child = create_issue!(:estimated_hours => 5, :parent_issue_id => first_parent.id)
280     assert_equal 5, first_parent.reload.estimated_hours
281     child.update_attributes(:estimated_hours => 7, :parent_issue_id => second_parent.id)
282     assert_equal 7, second_parent.reload.estimated_hours
283     assert_nil first_parent.reload.estimated_hours
284   end
286   def test_reschuling_a_parent_should_reschedule_subtasks
287     parent = create_issue!
288     c1 = create_issue!(:start_date => '2010-05-12', :due_date => '2010-05-18', :parent_issue_id => parent.id)
289     c2 = create_issue!(:start_date => '2010-06-03', :due_date => '2010-06-10', :parent_issue_id => parent.id)
290     parent.reload
291     parent.reschedule_after(Date.parse('2010-06-02'))
292     c1.reload
293     assert_equal [Date.parse('2010-06-02'), Date.parse('2010-06-08')], [c1.start_date, c1.due_date]
294     c2.reload
295     assert_equal [Date.parse('2010-06-03'), Date.parse('2010-06-10')], [c2.start_date, c2.due_date] # no change
296     parent.reload
297     assert_equal [Date.parse('2010-06-02'), Date.parse('2010-06-10')], [parent.start_date, parent.due_date]
298   end
299   
300   def test_project_copy_should_copy_issue_tree
301     p = Project.create!(:name => 'Tree copy', :identifier => 'tree-copy', :tracker_ids => [1, 2])
302     i1 = create_issue!(:project_id => p.id, :subject => 'i1')
303     i2 = create_issue!(:project_id => p.id, :subject => 'i2', :parent_issue_id => i1.id)
304     i3 = create_issue!(:project_id => p.id, :subject => 'i3', :parent_issue_id => i1.id)
305     i4 = create_issue!(:project_id => p.id, :subject => 'i4', :parent_issue_id => i2.id)
306     i5 = create_issue!(:project_id => p.id, :subject => 'i5')
307     c = Project.new(:name => 'Copy', :identifier => 'copy', :tracker_ids => [1, 2])
308     c.copy(p, :only => 'issues')
309     c.reload
310     
311     assert_equal 5, c.issues.count
312     ic1, ic2, ic3, ic4, ic5 = c.issues.find(:all, :order => 'subject')
313     assert ic1.root?
314     assert_equal ic1, ic2.parent
315     assert_equal ic1, ic3.parent
316     assert_equal ic2, ic4.parent
317     assert ic5.root?
318   end
319   
320   # Helper that creates an issue with default attributes
321   def create_issue!(attributes={})
322     Issue.create!({:project_id => 1, :tracker_id => 1, :author_id => 1, :subject => 'test'}.merge(attributes))
323   end