##// END OF EJS Templates
Fixed "column specified more than once" error with SQLServer (#6579)....
Jean-Philippe Lang -
r12878:96baa905bf5b
parent child
Show More
@@ -1,183 +1,183
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 ProjectNestedSetTest < ActiveSupport::TestCase
20 class ProjectNestedSetTest < ActiveSupport::TestCase
21
21
22 def setup
22 def setup
23 Project.delete_all
23 Project.delete_all
24
24
25 @a = Project.create!(:name => 'A', :identifier => 'projecta')
25 @a = Project.create!(:name => 'A', :identifier => 'projecta')
26 @a1 = Project.create!(:name => 'A1', :identifier => 'projecta1')
26 @a1 = Project.create!(:name => 'A1', :identifier => 'projecta1')
27 @a1.set_parent!(@a)
27 @a1.set_parent!(@a)
28 @a2 = Project.create!(:name => 'A2', :identifier => 'projecta2')
28 @a2 = Project.create!(:name => 'A2', :identifier => 'projecta2')
29 @a2.set_parent!(@a)
29 @a2.set_parent!(@a)
30
30
31 @c = Project.create!(:name => 'C', :identifier => 'projectc')
31 @c = Project.create!(:name => 'C', :identifier => 'projectc')
32 @c1 = Project.create!(:name => 'C1', :identifier => 'projectc1')
32 @c1 = Project.create!(:name => 'C1', :identifier => 'projectc1')
33 @c1.set_parent!(@c)
33 @c1.set_parent!(@c)
34
34
35 @b = Project.create!(:name => 'B', :identifier => 'projectb')
35 @b = Project.create!(:name => 'B', :identifier => 'projectb')
36 @b2 = Project.create!(:name => 'B2', :identifier => 'projectb2')
36 @b2 = Project.create!(:name => 'B2', :identifier => 'projectb2')
37 @b2.set_parent!(@b)
37 @b2.set_parent!(@b)
38 @b1 = Project.create!(:name => 'B1', :identifier => 'projectb1')
38 @b1 = Project.create!(:name => 'B1', :identifier => 'projectb1')
39 @b1.set_parent!(@b)
39 @b1.set_parent!(@b)
40 @b11 = Project.create!(:name => 'B11', :identifier => 'projectb11')
40 @b11 = Project.create!(:name => 'B11', :identifier => 'projectb11')
41 @b11.set_parent!(@b1)
41 @b11.set_parent!(@b1)
42
42
43 @a, @a1, @a2, @b, @b1, @b11, @b2, @c, @c1 = *(Project.all.sort_by(&:name))
43 @a, @a1, @a2, @b, @b1, @b11, @b2, @c, @c1 = *(Project.all.sort_by(&:name))
44 end
44 end
45
45
46 def test_valid_tree
46 def test_valid_tree
47 assert_valid_nested_set
47 assert_valid_nested_set
48 end
48 end
49
49
50 def test_rebuild_should_build_valid_tree
50 def test_rebuild_should_build_valid_tree
51 Project.update_all "lft = NULL, rgt = NULL"
51 Project.update_all "lft = NULL, rgt = NULL"
52
52
53 Project.rebuild_tree!
53 Project.rebuild_tree!
54 assert_valid_nested_set
54 assert_valid_nested_set
55 end
55 end
56
56
57 def test_rebuild_tree_should_build_valid_tree_even_with_valid_lft_rgt_values
57 def test_rebuild_tree_should_build_valid_tree_even_with_valid_lft_rgt_values
58 Project.where({:id => @a.id }).update_all("name = 'YY'")
58 Project.where({:id => @a.id }).update_all("name = 'YY'")
59 # lft and rgt values are still valid (Project.rebuild! would not update anything)
59 # lft and rgt values are still valid (Project.rebuild! would not update anything)
60 # but projects are not ordered properly (YY is in the first place)
60 # but projects are not ordered properly (YY is in the first place)
61
61
62 Project.rebuild_tree!
62 Project.rebuild_tree!
63 assert_valid_nested_set
63 assert_valid_nested_set
64 end
64 end
65
65
66 def test_moving_a_child_to_a_different_parent_should_keep_valid_tree
66 def test_moving_a_child_to_a_different_parent_should_keep_valid_tree
67 assert_no_difference 'Project.count' do
67 assert_no_difference 'Project.count' do
68 Project.find_by_name('B1').set_parent!(Project.find_by_name('A2'))
68 Project.find_by_name('B1').set_parent!(Project.find_by_name('A2'))
69 end
69 end
70 assert_valid_nested_set
70 assert_valid_nested_set
71 end
71 end
72
72
73 def test_renaming_a_root_to_first_position_should_update_nested_set_order
73 def test_renaming_a_root_to_first_position_should_update_nested_set_order
74 @c.name = '1'
74 @c.name = '1'
75 @c.save!
75 @c.save!
76 assert_valid_nested_set
76 assert_valid_nested_set
77 end
77 end
78
78
79 def test_renaming_a_root_to_middle_position_should_update_nested_set_order
79 def test_renaming_a_root_to_middle_position_should_update_nested_set_order
80 @a.name = 'BA'
80 @a.name = 'BA'
81 @a.save!
81 @a.save!
82 assert_valid_nested_set
82 assert_valid_nested_set
83 end
83 end
84
84
85 def test_renaming_a_root_to_last_position_should_update_nested_set_order
85 def test_renaming_a_root_to_last_position_should_update_nested_set_order
86 @a.name = 'D'
86 @a.name = 'D'
87 @a.save!
87 @a.save!
88 assert_valid_nested_set
88 assert_valid_nested_set
89 end
89 end
90
90
91 def test_renaming_a_root_to_same_position_should_update_nested_set_order
91 def test_renaming_a_root_to_same_position_should_update_nested_set_order
92 @c.name = 'D'
92 @c.name = 'D'
93 @c.save!
93 @c.save!
94 assert_valid_nested_set
94 assert_valid_nested_set
95 end
95 end
96
96
97 def test_renaming_a_child_should_update_nested_set_order
97 def test_renaming_a_child_should_update_nested_set_order
98 @a1.name = 'A3'
98 @a1.name = 'A3'
99 @a1.save!
99 @a1.save!
100 assert_valid_nested_set
100 assert_valid_nested_set
101 end
101 end
102
102
103 def test_renaming_a_child_with_child_should_update_nested_set_order
103 def test_renaming_a_child_with_child_should_update_nested_set_order
104 @b1.name = 'B3'
104 @b1.name = 'B3'
105 @b1.save!
105 @b1.save!
106 assert_valid_nested_set
106 assert_valid_nested_set
107 end
107 end
108
108
109 def test_adding_a_root_to_first_position_should_update_nested_set_order
109 def test_adding_a_root_to_first_position_should_update_nested_set_order
110 project = Project.create!(:name => '1', :identifier => 'projectba')
110 project = Project.create!(:name => '1', :identifier => 'projectba')
111 assert_valid_nested_set
111 assert_valid_nested_set
112 end
112 end
113
113
114 def test_adding_a_root_to_middle_position_should_update_nested_set_order
114 def test_adding_a_root_to_middle_position_should_update_nested_set_order
115 project = Project.create!(:name => 'BA', :identifier => 'projectba')
115 project = Project.create!(:name => 'BA', :identifier => 'projectba')
116 assert_valid_nested_set
116 assert_valid_nested_set
117 end
117 end
118
118
119 def test_adding_a_root_to_last_position_should_update_nested_set_order
119 def test_adding_a_root_to_last_position_should_update_nested_set_order
120 project = Project.create!(:name => 'Z', :identifier => 'projectba')
120 project = Project.create!(:name => 'Z', :identifier => 'projectba')
121 assert_valid_nested_set
121 assert_valid_nested_set
122 end
122 end
123
123
124 def test_destroying_a_root_with_children_should_keep_valid_tree
124 def test_destroying_a_root_with_children_should_keep_valid_tree
125 assert_difference 'Project.count', -4 do
125 assert_difference 'Project.count', -4 do
126 Project.find_by_name('B').destroy
126 Project.find_by_name('B').destroy
127 end
127 end
128 assert_valid_nested_set
128 assert_valid_nested_set
129 end
129 end
130
130
131 def test_destroying_a_child_with_children_should_keep_valid_tree
131 def test_destroying_a_child_with_children_should_keep_valid_tree
132 assert_difference 'Project.count', -2 do
132 assert_difference 'Project.count', -2 do
133 Project.find_by_name('B1').destroy
133 Project.find_by_name('B1').destroy
134 end
134 end
135 assert_valid_nested_set
135 assert_valid_nested_set
136 end
136 end
137
137
138 private
138 private
139
139
140 def assert_nested_set_values(h)
140 def assert_nested_set_values(h)
141 assert Project.valid?
141 assert Project.valid?
142 h.each do |project, expected|
142 h.each do |project, expected|
143 project.reload
143 project.reload
144 assert_equal expected, [project.parent_id, project.lft, project.rgt], "Unexpected nested set values for #{project.name}"
144 assert_equal expected, [project.parent_id, project.lft, project.rgt], "Unexpected nested set values for #{project.name}"
145 end
145 end
146 end
146 end
147
147
148 def assert_valid_nested_set
148 def assert_valid_nested_set
149 projects = Project.all
149 projects = Project.all
150 lft_rgt = projects.map {|p| [p.lft, p.rgt]}.flatten
150 lft_rgt = projects.map {|p| [p.lft, p.rgt]}.flatten
151 assert_equal projects.size * 2, lft_rgt.uniq.size
151 assert_equal projects.size * 2, lft_rgt.uniq.size
152 assert_equal 1, lft_rgt.min
152 assert_equal 1, lft_rgt.min
153 assert_equal projects.size * 2, lft_rgt.max
153 assert_equal projects.size * 2, lft_rgt.max
154
154
155 projects.each do |project|
155 projects.each do |project|
156 # lft should always be < rgt
156 # lft should always be < rgt
157 assert project.lft < project.rgt, "lft=#{project.lft} was not < rgt=#{project.rgt} for project #{project.name}"
157 assert project.lft < project.rgt, "lft=#{project.lft} was not < rgt=#{project.rgt} for project #{project.name}"
158 if project.parent_id
158 if project.parent_id
159 # child lft/rgt values must be greater/lower
159 # child lft/rgt values must be greater/lower
160 assert_not_nil project.parent, "parent was nil for project #{project.name}"
160 assert_not_nil project.parent, "parent was nil for project #{project.name}"
161 assert project.lft > project.parent.lft, "lft=#{project.lft} was not > parent.lft=#{project.parent.lft} for project #{project.name}"
161 assert project.lft > project.parent.lft, "lft=#{project.lft} was not > parent.lft=#{project.parent.lft} for project #{project.name}"
162 assert project.rgt < project.parent.rgt, "rgt=#{project.rgt} was not < parent.rgt=#{project.parent.rgt} for project #{project.name}"
162 assert project.rgt < project.parent.rgt, "rgt=#{project.rgt} was not < parent.rgt=#{project.parent.rgt} for project #{project.name}"
163 end
163 end
164 # no overlapping lft/rgt values
164 # no overlapping lft/rgt values
165 overlapping = projects.detect {|other|
165 overlapping = projects.detect {|other|
166 other != project && (
166 other != project && (
167 (other.lft > project.lft && other.lft < project.rgt && other.rgt > project.rgt) ||
167 (other.lft > project.lft && other.lft < project.rgt && other.rgt > project.rgt) ||
168 (other.rgt > project.lft && other.rgt < project.rgt && other.lft < project.lft)
168 (other.rgt > project.lft && other.rgt < project.rgt && other.lft < project.lft)
169 )
169 )
170 }
170 }
171 assert_nil overlapping, (overlapping && "Project #{overlapping.name} (#{overlapping.lft}/#{overlapping.rgt}) overlapped #{project.name} (#{project.lft}/#{project.rgt})")
171 assert_nil overlapping, (overlapping && "Project #{overlapping.name} (#{overlapping.lft}/#{overlapping.rgt}) overlapped #{project.name} (#{project.lft}/#{project.rgt})")
172 end
172 end
173
173
174 # root projects sorted alphabetically
174 # root projects sorted alphabetically
175 assert_equal Project.roots.map(&:name).sort, Project.roots.sort_by(&:lft).map(&:name), "Root projects were not properly sorted"
175 assert_equal Project.roots.map(&:name).sort, Project.roots.sort_by(&:lft).map(&:name), "Root projects were not properly sorted"
176 projects.each do |project|
176 projects.each do |project|
177 if project.children.any?
177 if project.children.any?
178 # sibling projects sorted alphabetically
178 # sibling projects sorted alphabetically
179 assert_equal project.children.map(&:name).sort, project.children.order('lft').map(&:name), "Project #{project.name}'s children were not properly sorted"
179 assert_equal project.children.map(&:name).sort, project.children.sort_by(&:lft).map(&:name), "Project #{project.name}'s children were not properly sorted"
180 end
180 end
181 end
181 end
182 end
182 end
183 end
183 end
General Comments 0
You need to be logged in to leave comments. Login now