##// END OF EJS Templates
Insert children issues to respect same order as ids....
Jean-Philippe Lang -
r13460:daef6a8037ab
parent child
Show More
@@ -1,195 +1,195
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2014 Jean-Philippe Lang
2 # Copyright (C) 2006-2014 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
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
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.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 module Redmine
18 module Redmine
19 module NestedSet
19 module NestedSet
20 module IssueNestedSet
20 module IssueNestedSet
21 def self.included(base)
21 def self.included(base)
22 base.class_eval do
22 base.class_eval do
23 belongs_to :parent, :class_name => self.name
23 belongs_to :parent, :class_name => self.name
24
24
25 before_create :add_to_nested_set, :if => lambda {|issue| issue.parent.present?}
25 before_create :add_to_nested_set, :if => lambda {|issue| issue.parent.present?}
26 after_create :add_as_root, :if => lambda {|issue| issue.parent.blank?}
26 after_create :add_as_root, :if => lambda {|issue| issue.parent.blank?}
27 before_update :handle_parent_change, :if => lambda {|issue| issue.parent_id_changed?}
27 before_update :handle_parent_change, :if => lambda {|issue| issue.parent_id_changed?}
28 before_destroy :destroy_children
28 before_destroy :destroy_children
29 end
29 end
30 base.extend ClassMethods
30 base.extend ClassMethods
31 base.send :include, Redmine::NestedSet::Traversing
31 base.send :include, Redmine::NestedSet::Traversing
32 end
32 end
33
33
34 private
34 private
35
35
36 def target_lft
36 def target_lft
37 scope_for_max_rgt = self.class.where(:root_id => root_id).where(:parent_id => parent_id)
37 scope_for_max_rgt = self.class.where(:root_id => root_id).where(:parent_id => parent_id)
38 if id
38 if id
39 #scope_for_max_rgt = scope_for_max_rgt.where("id < ?", id)
39 scope_for_max_rgt = scope_for_max_rgt.where("id < ?", id)
40 end
40 end
41 max_rgt = scope_for_max_rgt.maximum(:rgt)
41 max_rgt = scope_for_max_rgt.maximum(:rgt)
42 if max_rgt
42 if max_rgt
43 max_rgt + 1
43 max_rgt + 1
44 elsif parent
44 elsif parent
45 parent.lft + 1
45 parent.lft + 1
46 else
46 else
47 1
47 1
48 end
48 end
49 end
49 end
50
50
51 def add_to_nested_set(lock=true)
51 def add_to_nested_set(lock=true)
52 lock_nested_set if lock
52 lock_nested_set if lock
53 parent.send :reload_nested_set_values
53 parent.send :reload_nested_set_values
54 self.root_id = parent.root_id
54 self.root_id = parent.root_id
55 self.lft = target_lft
55 self.lft = target_lft
56 self.rgt = lft + 1
56 self.rgt = lft + 1
57 self.class.where(:root_id => root_id).where("lft >= ? OR rgt >= ?", lft, lft).update_all([
57 self.class.where(:root_id => root_id).where("lft >= ? OR rgt >= ?", lft, lft).update_all([
58 "lft = CASE WHEN lft >= :lft THEN lft + 2 ELSE lft END, " +
58 "lft = CASE WHEN lft >= :lft THEN lft + 2 ELSE lft END, " +
59 "rgt = CASE WHEN rgt >= :lft THEN rgt + 2 ELSE rgt END",
59 "rgt = CASE WHEN rgt >= :lft THEN rgt + 2 ELSE rgt END",
60 {:lft => lft}
60 {:lft => lft}
61 ])
61 ])
62 end
62 end
63
63
64 def add_as_root
64 def add_as_root
65 self.root_id = id
65 self.root_id = id
66 self.lft = 1
66 self.lft = 1
67 self.rgt = 2
67 self.rgt = 2
68 self.class.where(:id => id).update_all(:root_id => root_id, :lft => lft, :rgt => rgt)
68 self.class.where(:id => id).update_all(:root_id => root_id, :lft => lft, :rgt => rgt)
69 end
69 end
70
70
71 def handle_parent_change
71 def handle_parent_change
72 lock_nested_set
72 lock_nested_set
73 reload_nested_set_values
73 reload_nested_set_values
74 if parent_id_was
74 if parent_id_was
75 remove_from_nested_set
75 remove_from_nested_set
76 end
76 end
77 if parent
77 if parent
78 move_to_nested_set
78 move_to_nested_set
79 end
79 end
80 reload_nested_set_values
80 reload_nested_set_values
81 end
81 end
82
82
83 def move_to_nested_set
83 def move_to_nested_set
84 if parent
84 if parent
85 previous_root_id = root_id
85 previous_root_id = root_id
86 self.root_id = parent.root_id
86 self.root_id = parent.root_id
87
87
88 lft_after_move = target_lft
88 lft_after_move = target_lft
89 self.class.where(:root_id => parent.root_id).update_all([
89 self.class.where(:root_id => parent.root_id).update_all([
90 "lft = CASE WHEN lft >= :lft THEN lft + :shift ELSE lft END, " +
90 "lft = CASE WHEN lft >= :lft THEN lft + :shift ELSE lft END, " +
91 "rgt = CASE WHEN rgt >= :lft THEN rgt + :shift ELSE rgt END",
91 "rgt = CASE WHEN rgt >= :lft THEN rgt + :shift ELSE rgt END",
92 {:lft => lft_after_move, :shift => (rgt - lft + 1)}
92 {:lft => lft_after_move, :shift => (rgt - lft + 1)}
93 ])
93 ])
94
94
95 self.class.where(:root_id => previous_root_id).update_all([
95 self.class.where(:root_id => previous_root_id).update_all([
96 "root_id = :root_id, lft = lft + :shift, rgt = rgt + :shift",
96 "root_id = :root_id, lft = lft + :shift, rgt = rgt + :shift",
97 {:root_id => parent.root_id, :shift => lft_after_move - lft}
97 {:root_id => parent.root_id, :shift => lft_after_move - lft}
98 ])
98 ])
99
99
100 self.lft, self.rgt = lft_after_move, (rgt - lft + lft_after_move)
100 self.lft, self.rgt = lft_after_move, (rgt - lft + lft_after_move)
101 parent.send :reload_nested_set_values
101 parent.send :reload_nested_set_values
102 end
102 end
103 end
103 end
104
104
105 def remove_from_nested_set
105 def remove_from_nested_set
106 self.class.where(:root_id => root_id).where("lft >= ? AND rgt <= ?", lft, rgt).
106 self.class.where(:root_id => root_id).where("lft >= ? AND rgt <= ?", lft, rgt).
107 update_all(["root_id = :id, lft = lft - :shift, rgt = rgt - :shift", {:id => id, :shift => lft - 1}])
107 update_all(["root_id = :id, lft = lft - :shift, rgt = rgt - :shift", {:id => id, :shift => lft - 1}])
108
108
109 self.class.where(:root_id => root_id).update_all([
109 self.class.where(:root_id => root_id).update_all([
110 "lft = CASE WHEN lft >= :lft THEN lft - :shift ELSE lft END, " +
110 "lft = CASE WHEN lft >= :lft THEN lft - :shift ELSE lft END, " +
111 "rgt = CASE WHEN rgt >= :lft THEN rgt - :shift ELSE rgt END",
111 "rgt = CASE WHEN rgt >= :lft THEN rgt - :shift ELSE rgt END",
112 {:lft => lft, :shift => rgt - lft + 1}
112 {:lft => lft, :shift => rgt - lft + 1}
113 ])
113 ])
114 self.root_id = id
114 self.root_id = id
115 self.lft, self.rgt = 1, (rgt - lft + 1)
115 self.lft, self.rgt = 1, (rgt - lft + 1)
116 end
116 end
117
117
118 def destroy_children
118 def destroy_children
119 unless @without_nested_set_update
119 unless @without_nested_set_update
120 lock_nested_set
120 lock_nested_set
121 reload_nested_set_values
121 reload_nested_set_values
122 end
122 end
123 children.each {|c| c.send :destroy_without_nested_set_update}
123 children.each {|c| c.send :destroy_without_nested_set_update}
124 reload
124 reload
125 unless @without_nested_set_update
125 unless @without_nested_set_update
126 self.class.where(:root_id => root_id).where("lft > ? OR rgt > ?", lft, lft).update_all([
126 self.class.where(:root_id => root_id).where("lft > ? OR rgt > ?", lft, lft).update_all([
127 "lft = CASE WHEN lft > :lft THEN lft - :shift ELSE lft END, " +
127 "lft = CASE WHEN lft > :lft THEN lft - :shift ELSE lft END, " +
128 "rgt = CASE WHEN rgt > :lft THEN rgt - :shift ELSE rgt END",
128 "rgt = CASE WHEN rgt > :lft THEN rgt - :shift ELSE rgt END",
129 {:lft => lft, :shift => rgt - lft + 1}
129 {:lft => lft, :shift => rgt - lft + 1}
130 ])
130 ])
131 end
131 end
132 end
132 end
133
133
134 def destroy_without_nested_set_update
134 def destroy_without_nested_set_update
135 @without_nested_set_update = true
135 @without_nested_set_update = true
136 destroy
136 destroy
137 end
137 end
138
138
139 def reload_nested_set_values
139 def reload_nested_set_values
140 self.root_id, self.lft, self.rgt = self.class.where(:id => id).pluck(:root_id, :lft, :rgt).first
140 self.root_id, self.lft, self.rgt = self.class.where(:id => id).pluck(:root_id, :lft, :rgt).first
141 end
141 end
142
142
143 def save_nested_set_values
143 def save_nested_set_values
144 self.class.where(:id => id).update_all(:root_id => root_id, :lft => lft, :rgt => rgt)
144 self.class.where(:id => id).update_all(:root_id => root_id, :lft => lft, :rgt => rgt)
145 end
145 end
146
146
147 def move_possible?(issue)
147 def move_possible?(issue)
148 !is_or_is_ancestor_of?(issue)
148 !is_or_is_ancestor_of?(issue)
149 end
149 end
150
150
151 def lock_nested_set
151 def lock_nested_set
152 lock = true
152 lock = true
153 if self.class.connection.adapter_name =~ /sqlserver/i
153 if self.class.connection.adapter_name =~ /sqlserver/i
154 lock = "WITH (ROWLOCK HOLDLOCK UPDLOCK)"
154 lock = "WITH (ROWLOCK HOLDLOCK UPDLOCK)"
155 end
155 end
156 sets_to_lock = [id, parent_id].compact
156 sets_to_lock = [id, parent_id].compact
157 self.class.reorder(:id).where("root_id IN (SELECT root_id FROM #{self.class.table_name} WHERE id IN (?))", sets_to_lock).lock(lock).ids
157 self.class.reorder(:id).where("root_id IN (SELECT root_id FROM #{self.class.table_name} WHERE id IN (?))", sets_to_lock).lock(lock).ids
158 end
158 end
159
159
160 def nested_set_scope
160 def nested_set_scope
161 self.class.order(:lft).where(:root_id => root_id)
161 self.class.order(:lft).where(:root_id => root_id)
162 end
162 end
163
163
164 def same_nested_set_scope?(issue)
164 def same_nested_set_scope?(issue)
165 root_id == issue.root_id
165 root_id == issue.root_id
166 end
166 end
167
167
168 module ClassMethods
168 module ClassMethods
169 def rebuild_tree!
169 def rebuild_tree!
170 transaction do
170 transaction do
171 reorder(:id).lock.ids
171 reorder(:id).lock.ids
172 update_all(:root_id => nil, :lft => nil, :rgt => nil)
172 update_all(:root_id => nil, :lft => nil, :rgt => nil)
173 where(:parent_id => nil).update_all(["root_id = id, lft = ?, rgt = ?", 1, 2])
173 where(:parent_id => nil).update_all(["root_id = id, lft = ?, rgt = ?", 1, 2])
174 roots_with_children = joins("JOIN #{table_name} parent ON parent.id = #{table_name}.parent_id AND parent.id = parent.root_id").uniq.pluck("parent.id")
174 roots_with_children = joins("JOIN #{table_name} parent ON parent.id = #{table_name}.parent_id AND parent.id = parent.root_id").uniq.pluck("parent.id")
175 roots_with_children.each do |root_id|
175 roots_with_children.each do |root_id|
176 rebuild_nodes(root_id)
176 rebuild_nodes(root_id)
177 end
177 end
178 end
178 end
179 end
179 end
180
180
181 private
181 private
182
182
183 def rebuild_nodes(parent_id = nil)
183 def rebuild_nodes(parent_id = nil)
184 nodes = where(:parent_id => parent_id, :rgt => nil, :lft => nil).order(:id).to_a
184 nodes = where(:parent_id => parent_id, :rgt => nil, :lft => nil).order(:id).to_a
185
185
186 nodes.each do |node|
186 nodes.each do |node|
187 node.send :add_to_nested_set, false
187 node.send :add_to_nested_set, false
188 node.send :save_nested_set_values
188 node.send :save_nested_set_values
189 rebuild_nodes node.id
189 rebuild_nodes node.id
190 end
190 end
191 end
191 end
192 end
192 end
193 end
193 end
194 end
194 end
195 end
195 end
@@ -1,426 +1,426
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2014 Jean-Philippe Lang
2 # Copyright (C) 2006-2014 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
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
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.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class IssueNestedSetTest < ActiveSupport::TestCase
20 class IssueNestedSetTest < ActiveSupport::TestCase
21 fixtures :projects, :users, :roles,
21 fixtures :projects, :users, :roles,
22 :trackers, :projects_trackers,
22 :trackers, :projects_trackers,
23 :issue_statuses, :issue_categories, :issue_relations,
23 :issue_statuses, :issue_categories, :issue_relations,
24 :enumerations,
24 :enumerations,
25 :issues
25 :issues
26
26
27 def test_new_record_is_leaf
27 def test_new_record_is_leaf
28 i = Issue.new
28 i = Issue.new
29 assert i.leaf?
29 assert i.leaf?
30 end
30 end
31
31
32 def test_create_root_issue
32 def test_create_root_issue
33 lft1 = new_issue_lft
33 lft1 = new_issue_lft
34 issue1 = Issue.generate!
34 issue1 = Issue.generate!
35 lft2 = new_issue_lft
35 lft2 = new_issue_lft
36 issue2 = Issue.generate!
36 issue2 = Issue.generate!
37 issue1.reload
37 issue1.reload
38 issue2.reload
38 issue2.reload
39 assert_equal [issue1.id, nil, lft1, lft1 + 1], [issue1.root_id, issue1.parent_id, issue1.lft, issue1.rgt]
39 assert_equal [issue1.id, nil, lft1, lft1 + 1], [issue1.root_id, issue1.parent_id, issue1.lft, issue1.rgt]
40 assert_equal [issue2.id, nil, lft2, lft2 + 1], [issue2.root_id, issue2.parent_id, issue2.lft, issue2.rgt]
40 assert_equal [issue2.id, nil, lft2, lft2 + 1], [issue2.root_id, issue2.parent_id, issue2.lft, issue2.rgt]
41 end
41 end
42
42
43 def test_create_child_issue
43 def test_create_child_issue
44 lft = new_issue_lft
44 lft = new_issue_lft
45 parent = Issue.generate!
45 parent = Issue.generate!
46 child = parent.generate_child!
46 child = parent.generate_child!
47 parent.reload
47 parent.reload
48 child.reload
48 child.reload
49 assert_equal [parent.id, nil, lft, lft + 3], [parent.root_id, parent.parent_id, parent.lft, parent.rgt]
49 assert_equal [parent.id, nil, lft, lft + 3], [parent.root_id, parent.parent_id, parent.lft, parent.rgt]
50 assert_equal [parent.id, parent.id, lft + 1, lft + 2], [child.root_id, child.parent_id, child.lft, child.rgt]
50 assert_equal [parent.id, parent.id, lft + 1, lft + 2], [child.root_id, child.parent_id, child.lft, child.rgt]
51 end
51 end
52
52
53 def test_creating_a_child_in_a_subproject_should_validate
53 def test_creating_a_child_in_a_subproject_should_validate
54 issue = Issue.generate!
54 issue = Issue.generate!
55 child = Issue.new(:project_id => 3, :tracker_id => 2, :author_id => 1,
55 child = Issue.new(:project_id => 3, :tracker_id => 2, :author_id => 1,
56 :subject => 'child', :parent_issue_id => issue.id)
56 :subject => 'child', :parent_issue_id => issue.id)
57 assert_save child
57 assert_save child
58 assert_equal issue, child.reload.parent
58 assert_equal issue, child.reload.parent
59 end
59 end
60
60
61 def test_creating_a_child_in_an_invalid_project_should_not_validate
61 def test_creating_a_child_in_an_invalid_project_should_not_validate
62 issue = Issue.generate!
62 issue = Issue.generate!
63 child = Issue.new(:project_id => 2, :tracker_id => 1, :author_id => 1,
63 child = Issue.new(:project_id => 2, :tracker_id => 1, :author_id => 1,
64 :subject => 'child', :parent_issue_id => issue.id)
64 :subject => 'child', :parent_issue_id => issue.id)
65 assert !child.save
65 assert !child.save
66 assert_not_equal [], child.errors[:parent_issue_id]
66 assert_not_equal [], child.errors[:parent_issue_id]
67 end
67 end
68
68
69 def test_move_a_root_to_child
69 def test_move_a_root_to_child
70 lft = new_issue_lft
70 lft = new_issue_lft
71 parent1 = Issue.generate!
71 parent1 = Issue.generate!
72 parent2 = Issue.generate!
72 parent2 = Issue.generate!
73 child = parent1.generate_child!
73 child = parent1.generate_child!
74 parent2.parent_issue_id = parent1.id
74 parent2.parent_issue_id = parent1.id
75 parent2.save!
75 parent2.save!
76 child.reload
76 child.reload
77 parent1.reload
77 parent1.reload
78 parent2.reload
78 parent2.reload
79 assert_equal [parent1.id, lft, lft + 5], [parent1.root_id, parent1.lft, parent1.rgt]
79 assert_equal [parent1.id, lft, lft + 5], [parent1.root_id, parent1.lft, parent1.rgt]
80 assert_equal [parent1.id, lft + 3, lft + 4], [parent2.root_id, parent2.lft, parent2.rgt]
80 assert_equal [parent1.id, lft + 1, lft + 2], [parent2.root_id, parent2.lft, parent2.rgt]
81 assert_equal [parent1.id, lft + 1, lft + 2], [child.root_id, child.lft, child.rgt]
81 assert_equal [parent1.id, lft + 3, lft + 4], [child.root_id, child.lft, child.rgt]
82 end
82 end
83
83
84 def test_move_a_child_to_root
84 def test_move_a_child_to_root
85 lft1 = new_issue_lft
85 lft1 = new_issue_lft
86 parent1 = Issue.generate!
86 parent1 = Issue.generate!
87 lft2 = new_issue_lft
87 lft2 = new_issue_lft
88 parent2 = Issue.generate!
88 parent2 = Issue.generate!
89 lft3 = new_issue_lft
89 lft3 = new_issue_lft
90 child = parent1.generate_child!
90 child = parent1.generate_child!
91 child.parent_issue_id = nil
91 child.parent_issue_id = nil
92 child.save!
92 child.save!
93 child.reload
93 child.reload
94 parent1.reload
94 parent1.reload
95 parent2.reload
95 parent2.reload
96 assert_equal [parent1.id, lft1, lft1 + 1], [parent1.root_id, parent1.lft, parent1.rgt]
96 assert_equal [parent1.id, lft1, lft1 + 1], [parent1.root_id, parent1.lft, parent1.rgt]
97 assert_equal [parent2.id, lft2, lft2 + 1], [parent2.root_id, parent2.lft, parent2.rgt]
97 assert_equal [parent2.id, lft2, lft2 + 1], [parent2.root_id, parent2.lft, parent2.rgt]
98 assert_equal [child.id, lft3, lft3 + 1], [child.root_id, child.lft, child.rgt]
98 assert_equal [child.id, lft3, lft3 + 1], [child.root_id, child.lft, child.rgt]
99 end
99 end
100
100
101 def test_move_a_child_to_another_issue
101 def test_move_a_child_to_another_issue
102 lft1 = new_issue_lft
102 lft1 = new_issue_lft
103 parent1 = Issue.generate!
103 parent1 = Issue.generate!
104 lft2 = new_issue_lft
104 lft2 = new_issue_lft
105 parent2 = Issue.generate!
105 parent2 = Issue.generate!
106 child = parent1.generate_child!
106 child = parent1.generate_child!
107 child.parent_issue_id = parent2.id
107 child.parent_issue_id = parent2.id
108 child.save!
108 child.save!
109 child.reload
109 child.reload
110 parent1.reload
110 parent1.reload
111 parent2.reload
111 parent2.reload
112 assert_equal [parent1.id, lft1, lft1 + 1], [parent1.root_id, parent1.lft, parent1.rgt]
112 assert_equal [parent1.id, lft1, lft1 + 1], [parent1.root_id, parent1.lft, parent1.rgt]
113 assert_equal [parent2.id, lft2, lft2 + 3], [parent2.root_id, parent2.lft, parent2.rgt]
113 assert_equal [parent2.id, lft2, lft2 + 3], [parent2.root_id, parent2.lft, parent2.rgt]
114 assert_equal [parent2.id, lft2 + 1, lft2 + 2], [child.root_id, child.lft, child.rgt]
114 assert_equal [parent2.id, lft2 + 1, lft2 + 2], [child.root_id, child.lft, child.rgt]
115 end
115 end
116
116
117 def test_move_a_child_with_descendants_to_another_issue
117 def test_move_a_child_with_descendants_to_another_issue
118 lft1 = new_issue_lft
118 lft1 = new_issue_lft
119 parent1 = Issue.generate!
119 parent1 = Issue.generate!
120 lft2 = new_issue_lft
120 lft2 = new_issue_lft
121 parent2 = Issue.generate!
121 parent2 = Issue.generate!
122 child = parent1.generate_child!
122 child = parent1.generate_child!
123 grandchild = child.generate_child!
123 grandchild = child.generate_child!
124 parent1.reload
124 parent1.reload
125 parent2.reload
125 parent2.reload
126 child.reload
126 child.reload
127 grandchild.reload
127 grandchild.reload
128 assert_equal [parent1.id, lft1, lft1 + 5], [parent1.root_id, parent1.lft, parent1.rgt]
128 assert_equal [parent1.id, lft1, lft1 + 5], [parent1.root_id, parent1.lft, parent1.rgt]
129 assert_equal [parent2.id, lft2, lft2 + 1], [parent2.root_id, parent2.lft, parent2.rgt]
129 assert_equal [parent2.id, lft2, lft2 + 1], [parent2.root_id, parent2.lft, parent2.rgt]
130 assert_equal [parent1.id, lft1 + 1, lft1 + 4], [child.root_id, child.lft, child.rgt]
130 assert_equal [parent1.id, lft1 + 1, lft1 + 4], [child.root_id, child.lft, child.rgt]
131 assert_equal [parent1.id, lft1 + 2, lft1 + 3], [grandchild.root_id, grandchild.lft, grandchild.rgt]
131 assert_equal [parent1.id, lft1 + 2, lft1 + 3], [grandchild.root_id, grandchild.lft, grandchild.rgt]
132 child.reload.parent_issue_id = parent2.id
132 child.reload.parent_issue_id = parent2.id
133 child.save!
133 child.save!
134 child.reload
134 child.reload
135 grandchild.reload
135 grandchild.reload
136 parent1.reload
136 parent1.reload
137 parent2.reload
137 parent2.reload
138 assert_equal [parent1.id, lft1, lft1 + 1], [parent1.root_id, parent1.lft, parent1.rgt]
138 assert_equal [parent1.id, lft1, lft1 + 1], [parent1.root_id, parent1.lft, parent1.rgt]
139 assert_equal [parent2.id, lft2, lft2 + 5], [parent2.root_id, parent2.lft, parent2.rgt]
139 assert_equal [parent2.id, lft2, lft2 + 5], [parent2.root_id, parent2.lft, parent2.rgt]
140 assert_equal [parent2.id, lft2 + 1, lft2 + 4], [child.root_id, child.lft, child.rgt]
140 assert_equal [parent2.id, lft2 + 1, lft2 + 4], [child.root_id, child.lft, child.rgt]
141 assert_equal [parent2.id, lft2 + 2, lft2 + 3], [grandchild.root_id, grandchild.lft, grandchild.rgt]
141 assert_equal [parent2.id, lft2 + 2, lft2 + 3], [grandchild.root_id, grandchild.lft, grandchild.rgt]
142 end
142 end
143
143
144 def test_move_a_child_with_descendants_to_another_project
144 def test_move_a_child_with_descendants_to_another_project
145 lft1 = new_issue_lft
145 lft1 = new_issue_lft
146 parent1 = Issue.generate!
146 parent1 = Issue.generate!
147 child = parent1.generate_child!
147 child = parent1.generate_child!
148 grandchild = child.generate_child!
148 grandchild = child.generate_child!
149 lft4 = new_issue_lft
149 lft4 = new_issue_lft
150 child.reload
150 child.reload
151 child.project = Project.find(2)
151 child.project = Project.find(2)
152 assert child.save
152 assert child.save
153 child.reload
153 child.reload
154 grandchild.reload
154 grandchild.reload
155 parent1.reload
155 parent1.reload
156 assert_equal [1, parent1.id, lft1, lft1 + 1], [parent1.project_id, parent1.root_id, parent1.lft, parent1.rgt]
156 assert_equal [1, parent1.id, lft1, lft1 + 1], [parent1.project_id, parent1.root_id, parent1.lft, parent1.rgt]
157 assert_equal [2, child.id, lft4, lft4 + 3],
157 assert_equal [2, child.id, lft4, lft4 + 3],
158 [child.project_id, child.root_id, child.lft, child.rgt]
158 [child.project_id, child.root_id, child.lft, child.rgt]
159 assert_equal [2, child.id, lft4 + 1, lft4 + 2],
159 assert_equal [2, child.id, lft4 + 1, lft4 + 2],
160 [grandchild.project_id, grandchild.root_id, grandchild.lft, grandchild.rgt]
160 [grandchild.project_id, grandchild.root_id, grandchild.lft, grandchild.rgt]
161 end
161 end
162
162
163 def test_moving_an_issue_to_a_descendant_should_not_validate
163 def test_moving_an_issue_to_a_descendant_should_not_validate
164 parent1 = Issue.generate!
164 parent1 = Issue.generate!
165 parent2 = Issue.generate!
165 parent2 = Issue.generate!
166 child = parent1.generate_child!
166 child = parent1.generate_child!
167 grandchild = child.generate_child!
167 grandchild = child.generate_child!
168
168
169 child.reload
169 child.reload
170 child.parent_issue_id = grandchild.id
170 child.parent_issue_id = grandchild.id
171 assert !child.save
171 assert !child.save
172 assert_not_equal [], child.errors[:parent_issue_id]
172 assert_not_equal [], child.errors[:parent_issue_id]
173 end
173 end
174
174
175 def test_updating_a_root_issue_should_not_trigger_update_nested_set_attributes_on_parent_change
175 def test_updating_a_root_issue_should_not_trigger_update_nested_set_attributes_on_parent_change
176 issue = Issue.find(Issue.generate!.id)
176 issue = Issue.find(Issue.generate!.id)
177 issue.parent_issue_id = ""
177 issue.parent_issue_id = ""
178 issue.expects(:update_nested_set_attributes_on_parent_change).never
178 issue.expects(:update_nested_set_attributes_on_parent_change).never
179 issue.save!
179 issue.save!
180 end
180 end
181
181
182 def test_updating_a_child_issue_should_not_trigger_update_nested_set_attributes_on_parent_change
182 def test_updating_a_child_issue_should_not_trigger_update_nested_set_attributes_on_parent_change
183 issue = Issue.find(Issue.generate!(:parent_issue_id => 1).id)
183 issue = Issue.find(Issue.generate!(:parent_issue_id => 1).id)
184 issue.parent_issue_id = "1"
184 issue.parent_issue_id = "1"
185 issue.expects(:update_nested_set_attributes_on_parent_change).never
185 issue.expects(:update_nested_set_attributes_on_parent_change).never
186 issue.save!
186 issue.save!
187 end
187 end
188
188
189 def test_moving_a_root_issue_should_trigger_update_nested_set_attributes_on_parent_change
189 def test_moving_a_root_issue_should_trigger_update_nested_set_attributes_on_parent_change
190 issue = Issue.find(Issue.generate!.id)
190 issue = Issue.find(Issue.generate!.id)
191 issue.parent_issue_id = "1"
191 issue.parent_issue_id = "1"
192 issue.expects(:update_nested_set_attributes_on_parent_change).once
192 issue.expects(:update_nested_set_attributes_on_parent_change).once
193 issue.save!
193 issue.save!
194 end
194 end
195
195
196 def test_moving_a_child_issue_to_another_parent_should_trigger_update_nested_set_attributes_on_parent_change
196 def test_moving_a_child_issue_to_another_parent_should_trigger_update_nested_set_attributes_on_parent_change
197 issue = Issue.find(Issue.generate!(:parent_issue_id => 1).id)
197 issue = Issue.find(Issue.generate!(:parent_issue_id => 1).id)
198 issue.parent_issue_id = "2"
198 issue.parent_issue_id = "2"
199 issue.expects(:update_nested_set_attributes_on_parent_change).once
199 issue.expects(:update_nested_set_attributes_on_parent_change).once
200 issue.save!
200 issue.save!
201 end
201 end
202
202
203 def test_moving_a_child_issue_to_root_should_trigger_update_nested_set_attributes_on_parent_change
203 def test_moving_a_child_issue_to_root_should_trigger_update_nested_set_attributes_on_parent_change
204 issue = Issue.find(Issue.generate!(:parent_issue_id => 1).id)
204 issue = Issue.find(Issue.generate!(:parent_issue_id => 1).id)
205 issue.parent_issue_id = ""
205 issue.parent_issue_id = ""
206 issue.expects(:update_nested_set_attributes_on_parent_change).once
206 issue.expects(:update_nested_set_attributes_on_parent_change).once
207 issue.save!
207 issue.save!
208 end
208 end
209
209
210 def test_destroy_should_destroy_children
210 def test_destroy_should_destroy_children
211 lft1 = new_issue_lft
211 lft1 = new_issue_lft
212 issue1 = Issue.generate!
212 issue1 = Issue.generate!
213 issue2 = Issue.generate!
213 issue2 = Issue.generate!
214 issue3 = issue2.generate_child!
214 issue3 = issue2.generate_child!
215 issue4 = issue1.generate_child!
215 issue4 = issue1.generate_child!
216 issue3.init_journal(User.find(2))
216 issue3.init_journal(User.find(2))
217 issue3.subject = 'child with journal'
217 issue3.subject = 'child with journal'
218 issue3.save!
218 issue3.save!
219 assert_difference 'Issue.count', -2 do
219 assert_difference 'Issue.count', -2 do
220 assert_difference 'Journal.count', -1 do
220 assert_difference 'Journal.count', -1 do
221 assert_difference 'JournalDetail.count', -1 do
221 assert_difference 'JournalDetail.count', -1 do
222 Issue.find(issue2.id).destroy
222 Issue.find(issue2.id).destroy
223 end
223 end
224 end
224 end
225 end
225 end
226 issue1.reload
226 issue1.reload
227 issue4.reload
227 issue4.reload
228 assert !Issue.exists?(issue2.id)
228 assert !Issue.exists?(issue2.id)
229 assert !Issue.exists?(issue3.id)
229 assert !Issue.exists?(issue3.id)
230 assert_equal [issue1.id, lft1, lft1 + 3], [issue1.root_id, issue1.lft, issue1.rgt]
230 assert_equal [issue1.id, lft1, lft1 + 3], [issue1.root_id, issue1.lft, issue1.rgt]
231 assert_equal [issue1.id, lft1 + 1, lft1 + 2], [issue4.root_id, issue4.lft, issue4.rgt]
231 assert_equal [issue1.id, lft1 + 1, lft1 + 2], [issue4.root_id, issue4.lft, issue4.rgt]
232 end
232 end
233
233
234 def test_destroy_child_should_update_parent
234 def test_destroy_child_should_update_parent
235 lft1 = new_issue_lft
235 lft1 = new_issue_lft
236 issue = Issue.generate!
236 issue = Issue.generate!
237 child1 = issue.generate_child!
237 child1 = issue.generate_child!
238 child2 = issue.generate_child!
238 child2 = issue.generate_child!
239 issue.reload
239 issue.reload
240 assert_equal [issue.id, lft1, lft1 + 5], [issue.root_id, issue.lft, issue.rgt]
240 assert_equal [issue.id, lft1, lft1 + 5], [issue.root_id, issue.lft, issue.rgt]
241 child2.reload.destroy
241 child2.reload.destroy
242 issue.reload
242 issue.reload
243 assert_equal [issue.id, lft1, lft1 + 3], [issue.root_id, issue.lft, issue.rgt]
243 assert_equal [issue.id, lft1, lft1 + 3], [issue.root_id, issue.lft, issue.rgt]
244 end
244 end
245
245
246 def test_destroy_parent_issue_updated_during_children_destroy
246 def test_destroy_parent_issue_updated_during_children_destroy
247 parent = Issue.generate!
247 parent = Issue.generate!
248 parent.generate_child!(:start_date => Date.today)
248 parent.generate_child!(:start_date => Date.today)
249 parent.generate_child!(:start_date => 2.days.from_now)
249 parent.generate_child!(:start_date => 2.days.from_now)
250
250
251 assert_difference 'Issue.count', -3 do
251 assert_difference 'Issue.count', -3 do
252 Issue.find(parent.id).destroy
252 Issue.find(parent.id).destroy
253 end
253 end
254 end
254 end
255
255
256 def test_destroy_child_issue_with_children
256 def test_destroy_child_issue_with_children
257 root = Issue.generate!
257 root = Issue.generate!
258 child = root.generate_child!
258 child = root.generate_child!
259 leaf = child.generate_child!
259 leaf = child.generate_child!
260 leaf.init_journal(User.find(2))
260 leaf.init_journal(User.find(2))
261 leaf.subject = 'leaf with journal'
261 leaf.subject = 'leaf with journal'
262 leaf.save!
262 leaf.save!
263
263
264 assert_difference 'Issue.count', -2 do
264 assert_difference 'Issue.count', -2 do
265 assert_difference 'Journal.count', -1 do
265 assert_difference 'Journal.count', -1 do
266 assert_difference 'JournalDetail.count', -1 do
266 assert_difference 'JournalDetail.count', -1 do
267 Issue.find(child.id).destroy
267 Issue.find(child.id).destroy
268 end
268 end
269 end
269 end
270 end
270 end
271
271
272 root = Issue.find(root.id)
272 root = Issue.find(root.id)
273 assert root.leaf?, "Root issue is not a leaf (lft: #{root.lft}, rgt: #{root.rgt})"
273 assert root.leaf?, "Root issue is not a leaf (lft: #{root.lft}, rgt: #{root.rgt})"
274 end
274 end
275
275
276 def test_destroy_issue_with_grand_child
276 def test_destroy_issue_with_grand_child
277 lft1 = new_issue_lft
277 lft1 = new_issue_lft
278 parent = Issue.generate!
278 parent = Issue.generate!
279 issue = parent.generate_child!
279 issue = parent.generate_child!
280 child = issue.generate_child!
280 child = issue.generate_child!
281 grandchild1 = child.generate_child!
281 grandchild1 = child.generate_child!
282 grandchild2 = child.generate_child!
282 grandchild2 = child.generate_child!
283 assert_difference 'Issue.count', -4 do
283 assert_difference 'Issue.count', -4 do
284 Issue.find(issue.id).destroy
284 Issue.find(issue.id).destroy
285 parent.reload
285 parent.reload
286 assert_equal [lft1, lft1 + 1], [parent.lft, parent.rgt]
286 assert_equal [lft1, lft1 + 1], [parent.lft, parent.rgt]
287 end
287 end
288 end
288 end
289
289
290 def test_parent_priority_should_be_the_highest_child_priority
290 def test_parent_priority_should_be_the_highest_child_priority
291 parent = Issue.generate!(:priority => IssuePriority.find_by_name('Normal'))
291 parent = Issue.generate!(:priority => IssuePriority.find_by_name('Normal'))
292 # Create children
292 # Create children
293 child1 = parent.generate_child!(:priority => IssuePriority.find_by_name('High'))
293 child1 = parent.generate_child!(:priority => IssuePriority.find_by_name('High'))
294 assert_equal 'High', parent.reload.priority.name
294 assert_equal 'High', parent.reload.priority.name
295 child2 = child1.generate_child!(:priority => IssuePriority.find_by_name('Immediate'))
295 child2 = child1.generate_child!(:priority => IssuePriority.find_by_name('Immediate'))
296 assert_equal 'Immediate', child1.reload.priority.name
296 assert_equal 'Immediate', child1.reload.priority.name
297 assert_equal 'Immediate', parent.reload.priority.name
297 assert_equal 'Immediate', parent.reload.priority.name
298 child3 = parent.generate_child!(:priority => IssuePriority.find_by_name('Low'))
298 child3 = parent.generate_child!(:priority => IssuePriority.find_by_name('Low'))
299 assert_equal 'Immediate', parent.reload.priority.name
299 assert_equal 'Immediate', parent.reload.priority.name
300 # Destroy a child
300 # Destroy a child
301 child1.destroy
301 child1.destroy
302 assert_equal 'Low', parent.reload.priority.name
302 assert_equal 'Low', parent.reload.priority.name
303 # Update a child
303 # Update a child
304 child3.reload.priority = IssuePriority.find_by_name('Normal')
304 child3.reload.priority = IssuePriority.find_by_name('Normal')
305 child3.save!
305 child3.save!
306 assert_equal 'Normal', parent.reload.priority.name
306 assert_equal 'Normal', parent.reload.priority.name
307 end
307 end
308
308
309 def test_parent_dates_should_be_lowest_start_and_highest_due_dates
309 def test_parent_dates_should_be_lowest_start_and_highest_due_dates
310 parent = Issue.generate!
310 parent = Issue.generate!
311 parent.generate_child!(:start_date => '2010-01-25', :due_date => '2010-02-15')
311 parent.generate_child!(:start_date => '2010-01-25', :due_date => '2010-02-15')
312 parent.generate_child!( :due_date => '2010-02-13')
312 parent.generate_child!( :due_date => '2010-02-13')
313 parent.generate_child!(:start_date => '2010-02-01', :due_date => '2010-02-22')
313 parent.generate_child!(:start_date => '2010-02-01', :due_date => '2010-02-22')
314 parent.reload
314 parent.reload
315 assert_equal Date.parse('2010-01-25'), parent.start_date
315 assert_equal Date.parse('2010-01-25'), parent.start_date
316 assert_equal Date.parse('2010-02-22'), parent.due_date
316 assert_equal Date.parse('2010-02-22'), parent.due_date
317 end
317 end
318
318
319 def test_parent_done_ratio_should_be_average_done_ratio_of_leaves
319 def test_parent_done_ratio_should_be_average_done_ratio_of_leaves
320 parent = Issue.generate!
320 parent = Issue.generate!
321 parent.generate_child!(:done_ratio => 20)
321 parent.generate_child!(:done_ratio => 20)
322 assert_equal 20, parent.reload.done_ratio
322 assert_equal 20, parent.reload.done_ratio
323 parent.generate_child!(:done_ratio => 70)
323 parent.generate_child!(:done_ratio => 70)
324 assert_equal 45, parent.reload.done_ratio
324 assert_equal 45, parent.reload.done_ratio
325
325
326 child = parent.generate_child!(:done_ratio => 0)
326 child = parent.generate_child!(:done_ratio => 0)
327 assert_equal 30, parent.reload.done_ratio
327 assert_equal 30, parent.reload.done_ratio
328
328
329 child.generate_child!(:done_ratio => 30)
329 child.generate_child!(:done_ratio => 30)
330 assert_equal 30, child.reload.done_ratio
330 assert_equal 30, child.reload.done_ratio
331 assert_equal 40, parent.reload.done_ratio
331 assert_equal 40, parent.reload.done_ratio
332 end
332 end
333
333
334 def test_parent_done_ratio_should_be_weighted_by_estimated_times_if_any
334 def test_parent_done_ratio_should_be_weighted_by_estimated_times_if_any
335 parent = Issue.generate!
335 parent = Issue.generate!
336 parent.generate_child!(:estimated_hours => 10, :done_ratio => 20)
336 parent.generate_child!(:estimated_hours => 10, :done_ratio => 20)
337 assert_equal 20, parent.reload.done_ratio
337 assert_equal 20, parent.reload.done_ratio
338 parent.generate_child!(:estimated_hours => 20, :done_ratio => 50)
338 parent.generate_child!(:estimated_hours => 20, :done_ratio => 50)
339 assert_equal (50 * 20 + 20 * 10) / 30, parent.reload.done_ratio
339 assert_equal (50 * 20 + 20 * 10) / 30, parent.reload.done_ratio
340 end
340 end
341
341
342 def test_parent_done_ratio_with_child_estimate_to_0_should_reach_100
342 def test_parent_done_ratio_with_child_estimate_to_0_should_reach_100
343 parent = Issue.generate!
343 parent = Issue.generate!
344 issue1 = parent.generate_child!
344 issue1 = parent.generate_child!
345 issue2 = parent.generate_child!(:estimated_hours => 0)
345 issue2 = parent.generate_child!(:estimated_hours => 0)
346 assert_equal 0, parent.reload.done_ratio
346 assert_equal 0, parent.reload.done_ratio
347 issue1.reload.close!
347 issue1.reload.close!
348 assert_equal 50, parent.reload.done_ratio
348 assert_equal 50, parent.reload.done_ratio
349 issue2.reload.close!
349 issue2.reload.close!
350 assert_equal 100, parent.reload.done_ratio
350 assert_equal 100, parent.reload.done_ratio
351 end
351 end
352
352
353 def test_parent_estimate_should_be_sum_of_leaves
353 def test_parent_estimate_should_be_sum_of_leaves
354 parent = Issue.generate!
354 parent = Issue.generate!
355 parent.generate_child!(:estimated_hours => nil)
355 parent.generate_child!(:estimated_hours => nil)
356 assert_equal nil, parent.reload.estimated_hours
356 assert_equal nil, parent.reload.estimated_hours
357 parent.generate_child!(:estimated_hours => 5)
357 parent.generate_child!(:estimated_hours => 5)
358 assert_equal 5, parent.reload.estimated_hours
358 assert_equal 5, parent.reload.estimated_hours
359 parent.generate_child!(:estimated_hours => 7)
359 parent.generate_child!(:estimated_hours => 7)
360 assert_equal 12, parent.reload.estimated_hours
360 assert_equal 12, parent.reload.estimated_hours
361 end
361 end
362
362
363 def test_done_ratio_of_parent_with_a_child_without_estimated_time_should_not_exceed_100
363 def test_done_ratio_of_parent_with_a_child_without_estimated_time_should_not_exceed_100
364 parent = Issue.generate!
364 parent = Issue.generate!
365 parent.generate_child!(:estimated_hours => 40)
365 parent.generate_child!(:estimated_hours => 40)
366 parent.generate_child!(:estimated_hours => 40)
366 parent.generate_child!(:estimated_hours => 40)
367 parent.generate_child!(:estimated_hours => 20)
367 parent.generate_child!(:estimated_hours => 20)
368 parent.generate_child!
368 parent.generate_child!
369 parent.reload.children.each(&:close!)
369 parent.reload.children.each(&:close!)
370 assert_equal 100, parent.reload.done_ratio
370 assert_equal 100, parent.reload.done_ratio
371 end
371 end
372
372
373 def test_done_ratio_of_parent_with_a_child_with_estimated_time_at_0_should_not_exceed_100
373 def test_done_ratio_of_parent_with_a_child_with_estimated_time_at_0_should_not_exceed_100
374 parent = Issue.generate!
374 parent = Issue.generate!
375 parent.generate_child!(:estimated_hours => 40)
375 parent.generate_child!(:estimated_hours => 40)
376 parent.generate_child!(:estimated_hours => 40)
376 parent.generate_child!(:estimated_hours => 40)
377 parent.generate_child!(:estimated_hours => 20)
377 parent.generate_child!(:estimated_hours => 20)
378 parent.generate_child!(:estimated_hours => 0)
378 parent.generate_child!(:estimated_hours => 0)
379 parent.reload.children.each(&:close!)
379 parent.reload.children.each(&:close!)
380 assert_equal 100, parent.reload.done_ratio
380 assert_equal 100, parent.reload.done_ratio
381 end
381 end
382
382
383 def test_move_parent_updates_old_parent_attributes
383 def test_move_parent_updates_old_parent_attributes
384 first_parent = Issue.generate!
384 first_parent = Issue.generate!
385 second_parent = Issue.generate!
385 second_parent = Issue.generate!
386 child = first_parent.generate_child!(:estimated_hours => 5)
386 child = first_parent.generate_child!(:estimated_hours => 5)
387 assert_equal 5, first_parent.reload.estimated_hours
387 assert_equal 5, first_parent.reload.estimated_hours
388 child.update_attributes(:estimated_hours => 7, :parent_issue_id => second_parent.id)
388 child.update_attributes(:estimated_hours => 7, :parent_issue_id => second_parent.id)
389 assert_equal 7, second_parent.reload.estimated_hours
389 assert_equal 7, second_parent.reload.estimated_hours
390 assert_nil first_parent.reload.estimated_hours
390 assert_nil first_parent.reload.estimated_hours
391 end
391 end
392
392
393 def test_reschuling_a_parent_should_reschedule_subtasks
393 def test_reschuling_a_parent_should_reschedule_subtasks
394 parent = Issue.generate!
394 parent = Issue.generate!
395 c1 = parent.generate_child!(:start_date => '2010-05-12', :due_date => '2010-05-18')
395 c1 = parent.generate_child!(:start_date => '2010-05-12', :due_date => '2010-05-18')
396 c2 = parent.generate_child!(:start_date => '2010-06-03', :due_date => '2010-06-10')
396 c2 = parent.generate_child!(:start_date => '2010-06-03', :due_date => '2010-06-10')
397 parent.reload
397 parent.reload
398 parent.reschedule_on!(Date.parse('2010-06-02'))
398 parent.reschedule_on!(Date.parse('2010-06-02'))
399 c1.reload
399 c1.reload
400 assert_equal [Date.parse('2010-06-02'), Date.parse('2010-06-08')], [c1.start_date, c1.due_date]
400 assert_equal [Date.parse('2010-06-02'), Date.parse('2010-06-08')], [c1.start_date, c1.due_date]
401 c2.reload
401 c2.reload
402 assert_equal [Date.parse('2010-06-03'), Date.parse('2010-06-10')], [c2.start_date, c2.due_date] # no change
402 assert_equal [Date.parse('2010-06-03'), Date.parse('2010-06-10')], [c2.start_date, c2.due_date] # no change
403 parent.reload
403 parent.reload
404 assert_equal [Date.parse('2010-06-02'), Date.parse('2010-06-10')], [parent.start_date, parent.due_date]
404 assert_equal [Date.parse('2010-06-02'), Date.parse('2010-06-10')], [parent.start_date, parent.due_date]
405 end
405 end
406
406
407 def test_project_copy_should_copy_issue_tree
407 def test_project_copy_should_copy_issue_tree
408 p = Project.create!(:name => 'Tree copy', :identifier => 'tree-copy', :tracker_ids => [1, 2])
408 p = Project.create!(:name => 'Tree copy', :identifier => 'tree-copy', :tracker_ids => [1, 2])
409 i1 = Issue.generate!(:project => p, :subject => 'i1')
409 i1 = Issue.generate!(:project => p, :subject => 'i1')
410 i2 = i1.generate_child!(:project => p, :subject => 'i2')
410 i2 = i1.generate_child!(:project => p, :subject => 'i2')
411 i3 = i1.generate_child!(:project => p, :subject => 'i3')
411 i3 = i1.generate_child!(:project => p, :subject => 'i3')
412 i4 = i2.generate_child!(:project => p, :subject => 'i4')
412 i4 = i2.generate_child!(:project => p, :subject => 'i4')
413 i5 = Issue.generate!(:project => p, :subject => 'i5')
413 i5 = Issue.generate!(:project => p, :subject => 'i5')
414 c = Project.new(:name => 'Copy', :identifier => 'copy', :tracker_ids => [1, 2])
414 c = Project.new(:name => 'Copy', :identifier => 'copy', :tracker_ids => [1, 2])
415 c.copy(p, :only => 'issues')
415 c.copy(p, :only => 'issues')
416 c.reload
416 c.reload
417
417
418 assert_equal 5, c.issues.count
418 assert_equal 5, c.issues.count
419 ic1, ic2, ic3, ic4, ic5 = c.issues.order('subject').to_a
419 ic1, ic2, ic3, ic4, ic5 = c.issues.order('subject').to_a
420 assert ic1.root?
420 assert ic1.root?
421 assert_equal ic1, ic2.parent
421 assert_equal ic1, ic2.parent
422 assert_equal ic1, ic3.parent
422 assert_equal ic1, ic3.parent
423 assert_equal ic2, ic4.parent
423 assert_equal ic2, ic4.parent
424 assert ic5.root?
424 assert ic5.root?
425 end
425 end
426 end
426 end
General Comments 0
You need to be logged in to leave comments. Login now