comparison test/unit/project_nested_set_test.rb @ 1337:077b8890835a cannam

Merge from live branch
author Chris Cannam
date Thu, 20 Jun 2013 13:14:02 +0100
parents 433d4f72a19b
children 622f24f53b42
comparison
equal deleted inserted replaced
1304:6137548ba453 1337:077b8890835a
1 # Redmine - project management software 1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang 2 # Copyright (C) 2006-2012 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.
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 context "nested set" do 22 def setup
23 setup do 23 Project.delete_all
24 Project.delete_all
25 24
26 @a = Project.create!(:name => 'Project A', :identifier => 'projecta') 25 @a = Project.create!(:name => 'A', :identifier => 'projecta')
27 @a1 = Project.create!(:name => 'Project A1', :identifier => 'projecta1') 26 @a1 = Project.create!(:name => 'A1', :identifier => 'projecta1')
28 @a1.set_parent!(@a) 27 @a1.set_parent!(@a)
29 @a2 = Project.create!(:name => 'Project A2', :identifier => 'projecta2') 28 @a2 = Project.create!(:name => 'A2', :identifier => 'projecta2')
30 @a2.set_parent!(@a) 29 @a2.set_parent!(@a)
31 30
32 @b = Project.create!(:name => 'Project B', :identifier => 'projectb') 31 @c = Project.create!(:name => 'C', :identifier => 'projectc')
33 @b1 = Project.create!(:name => 'Project B1', :identifier => 'projectb1') 32 @c1 = Project.create!(:name => 'C1', :identifier => 'projectc1')
34 @b1.set_parent!(@b) 33 @c1.set_parent!(@c)
35 @b11 = Project.create!(:name => 'Project B11', :identifier => 'projectb11')
36 @b11.set_parent!(@b1)
37 @b2 = Project.create!(:name => 'Project B2', :identifier => 'projectb2')
38 @b2.set_parent!(@b)
39 34
40 @c = Project.create!(:name => 'Project C', :identifier => 'projectc') 35 @b = Project.create!(:name => 'B', :identifier => 'projectb')
41 @c1 = Project.create!(:name => 'Project C1', :identifier => 'projectc1') 36 @b2 = Project.create!(:name => 'B2', :identifier => 'projectb2')
42 @c1.set_parent!(@c) 37 @b2.set_parent!(@b)
38 @b1 = Project.create!(:name => 'B1', :identifier => 'projectb1')
39 @b1.set_parent!(@b)
40 @b11 = Project.create!(:name => 'B11', :identifier => 'projectb11')
41 @b11.set_parent!(@b1)
43 42
44 [@a, @a1, @a2, @b, @b1, @b11, @b2, @c, @c1].each(&:reload) 43 @a, @a1, @a2, @b, @b1, @b11, @b2, @c, @c1 = *(Project.all.sort_by(&:name))
44 end
45
46 def test_valid_tree
47 assert_valid_nested_set
48 end
49
50 def test_rebuild_should_build_valid_tree
51 Project.update_all "lft = NULL, rgt = NULL"
52
53 Project.rebuild!
54 assert_valid_nested_set
55 end
56
57 def test_rebuild_tree_should_build_valid_tree_even_with_valid_lft_rgt_values
58 Project.update_all "name = 'YY'", {:id => @a.id }
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)
61
62 Project.rebuild_tree!
63 assert_valid_nested_set
64 end
65
66 def test_moving_a_child_to_a_different_parent_should_keep_valid_tree
67 assert_no_difference 'Project.count' do
68 Project.find_by_name('B1').set_parent!(Project.find_by_name('A2'))
45 end 69 end
70 assert_valid_nested_set
71 end
46 72
47 context "#create" do 73 def test_renaming_a_root_to_first_position_should_update_nested_set_order
48 should "build valid tree" do 74 @c.name = '1'
49 assert_nested_set_values({ 75 @c.save!
50 @a => [nil, 1, 6], 76 assert_valid_nested_set
51 @a1 => [@a.id, 2, 3], 77 end
52 @a2 => [@a.id, 4, 5], 78
53 @b => [nil, 7, 14], 79 def test_renaming_a_root_to_middle_position_should_update_nested_set_order
54 @b1 => [@b.id, 8, 11], 80 @a.name = 'BA'
55 @b11 => [@b1.id,9, 10], 81 @a.save!
56 @b2 => [@b.id,12, 13], 82 assert_valid_nested_set
57 @c => [nil, 15, 18], 83 end
58 @c1 => [@c.id,16, 17] 84
59 }) 85 def test_renaming_a_root_to_last_position_should_update_nested_set_order
60 end 86 @a.name = 'D'
87 @a.save!
88 assert_valid_nested_set
89 end
90
91 def test_renaming_a_root_to_same_position_should_update_nested_set_order
92 @c.name = 'D'
93 @c.save!
94 assert_valid_nested_set
95 end
96
97 def test_renaming_a_child_should_update_nested_set_order
98 @a1.name = 'A3'
99 @a1.save!
100 assert_valid_nested_set
101 end
102
103 def test_renaming_a_child_with_child_should_update_nested_set_order
104 @b1.name = 'B3'
105 @b1.save!
106 assert_valid_nested_set
107 end
108
109 def test_adding_a_root_to_first_position_should_update_nested_set_order
110 project = Project.create!(:name => '1', :identifier => 'projectba')
111 assert_valid_nested_set
112 end
113
114 def test_adding_a_root_to_middle_position_should_update_nested_set_order
115 project = Project.create!(:name => 'BA', :identifier => 'projectba')
116 assert_valid_nested_set
117 end
118
119 def test_adding_a_root_to_last_position_should_update_nested_set_order
120 project = Project.create!(:name => 'Z', :identifier => 'projectba')
121 assert_valid_nested_set
122 end
123
124 def test_destroying_a_root_with_children_should_keep_valid_tree
125 assert_difference 'Project.count', -4 do
126 Project.find_by_name('B').destroy
61 end 127 end
128 assert_valid_nested_set
129 end
62 130
63 context "#set_parent!" do 131 def test_destroying_a_child_with_children_should_keep_valid_tree
64 should "keep valid tree" do 132 assert_difference 'Project.count', -2 do
65 assert_no_difference 'Project.count' do 133 Project.find_by_name('B1').destroy
66 Project.find_by_name('Project B1').set_parent!(Project.find_by_name('Project A2'))
67 end
68 assert_nested_set_values({
69 @a => [nil, 1, 10],
70 @a2 => [@a.id, 4, 9],
71 @b1 => [@a2.id,5, 8],
72 @b11 => [@b1.id,6, 7],
73 @b => [nil, 11, 14],
74 @c => [nil, 15, 18]
75 })
76 end
77 end 134 end
135 assert_valid_nested_set
136 end
78 137
79 context "#destroy" do 138 private
80 context "a root with children" do
81 should "not mess up the tree" do
82 assert_difference 'Project.count', -4 do
83 Project.find_by_name('Project B').destroy
84 end
85 assert_nested_set_values({
86 @a => [nil, 1, 6],
87 @a1 => [@a.id, 2, 3],
88 @a2 => [@a.id, 4, 5],
89 @c => [nil, 7, 10],
90 @c1 => [@c.id, 8, 9]
91 })
92 end
93 end
94
95 context "a child with children" do
96 should "not mess up the tree" do
97 assert_difference 'Project.count', -2 do
98 Project.find_by_name('Project B1').destroy
99 end
100 assert_nested_set_values({
101 @a => [nil, 1, 6],
102 @b => [nil, 7, 10],
103 @b2 => [@b.id, 8, 9],
104 @c => [nil, 11, 14]
105 })
106 end
107 end
108 end
109 end
110 139
111 def assert_nested_set_values(h) 140 def assert_nested_set_values(h)
112 assert Project.valid? 141 assert Project.valid?
113 h.each do |project, expected| 142 h.each do |project, expected|
114 project.reload 143 project.reload
115 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}"
116 end 145 end
117 end 146 end
147
148 def assert_valid_nested_set
149 projects = Project.all
150 lft_rgt = projects.map {|p| [p.lft, p.rgt]}.flatten
151 assert_equal projects.size * 2, lft_rgt.uniq.size
152 assert_equal 1, lft_rgt.min
153 assert_equal projects.size * 2, lft_rgt.max
154
155 projects.each do |project|
156 # lft should always be < rgt
157 assert project.lft < project.rgt, "lft=#{project.lft} was not < rgt=#{project.rgt} for project #{project.name}"
158 if project.parent_id
159 # child lft/rgt values must be greater/lower
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}"
162 assert project.rgt < project.parent.rgt, "rgt=#{project.rgt} was not < parent.rgt=#{project.parent.rgt} for project #{project.name}"
163 end
164 # no overlapping lft/rgt values
165 overlapping = projects.detect {|other|
166 other != project && (
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)
169 )
170 }
171 assert_nil overlapping, (overlapping && "Project #{overlapping.name} (#{overlapping.lft}/#{overlapping.rgt}) overlapped #{project.name} (#{project.lft}/#{project.rgt})")
172 end
173
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"
176 projects.each do |project|
177 if project.children.any?
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"
180 end
181 end
182 end
118 end 183 end