| 
									
										
										
										
											2010-01-31 10:39:42 +00:00
										 |  |  | # Redmine - project management software | 
					
						
							| 
									
										
										
										
											2012-05-05 12:56:53 +00:00
										 |  |  | # Copyright (C) 2006-2012  Jean-Philippe Lang | 
					
						
							| 
									
										
										
										
											2010-01-31 10:39:42 +00:00
										 |  |  | # | 
					
						
							|  |  |  | # This program is free software; you can redistribute it and/or | 
					
						
							|  |  |  | # modify it under the terms of the GNU General Public License | 
					
						
							|  |  |  | # as published by the Free Software Foundation; either version 2 | 
					
						
							|  |  |  | # of the License, or (at your option) any later version. | 
					
						
							| 
									
										
										
										
											2011-08-30 05:49:57 +00:00
										 |  |  | # | 
					
						
							| 
									
										
										
										
											2010-01-31 10:39:42 +00:00
										 |  |  | # This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  | # but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  | # GNU General Public License for more details. | 
					
						
							| 
									
										
										
										
											2011-08-30 05:49:57 +00:00
										 |  |  | # | 
					
						
							| 
									
										
										
										
											2010-01-31 10:39:42 +00:00
										 |  |  | # You should have received a copy of the GNU General Public License | 
					
						
							|  |  |  | # along with this program; if not, write to the Free Software | 
					
						
							|  |  |  | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-12-12 23:24:34 +00:00
										 |  |  | require File.expand_path('../../test_helper', __FILE__) | 
					
						
							| 
									
										
										
										
											2010-01-31 10:39:42 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | class ProjectNestedSetTest < ActiveSupport::TestCase | 
					
						
							| 
									
										
										
										
											2011-08-30 05:49:57 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-10 16:50:15 +00:00
										 |  |  |   def setup | 
					
						
							|  |  |  |     Project.delete_all | 
					
						
							| 
									
										
										
										
											2011-08-30 05:49:57 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-10 16:50:15 +00:00
										 |  |  |     @a = Project.create!(:name => 'A', :identifier => 'projecta') | 
					
						
							|  |  |  |     @a1 = Project.create!(:name => 'A1', :identifier => 'projecta1') | 
					
						
							|  |  |  |     @a1.set_parent!(@a) | 
					
						
							|  |  |  |     @a2 = Project.create!(:name => 'A2', :identifier => 'projecta2') | 
					
						
							|  |  |  |     @a2.set_parent!(@a) | 
					
						
							| 
									
										
										
										
											2011-08-30 05:49:57 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-23 17:36:04 +00:00
										 |  |  |     @c = Project.create!(:name => 'C', :identifier => 'projectc') | 
					
						
							|  |  |  |     @c1 = Project.create!(:name => 'C1', :identifier => 'projectc1') | 
					
						
							|  |  |  |     @c1.set_parent!(@c) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-10 16:50:15 +00:00
										 |  |  |     @b = Project.create!(:name => 'B', :identifier => 'projectb') | 
					
						
							| 
									
										
										
										
											2012-11-23 17:36:04 +00:00
										 |  |  |     @b2 = Project.create!(:name => 'B2', :identifier => 'projectb2') | 
					
						
							|  |  |  |     @b2.set_parent!(@b) | 
					
						
							| 
									
										
										
										
											2012-08-10 16:50:15 +00:00
										 |  |  |     @b1 = Project.create!(:name => 'B1', :identifier => 'projectb1') | 
					
						
							|  |  |  |     @b1.set_parent!(@b) | 
					
						
							|  |  |  |     @b11 = Project.create!(:name => 'B11', :identifier => 'projectb11') | 
					
						
							|  |  |  |     @b11.set_parent!(@b1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @a, @a1, @a2, @b, @b1, @b11, @b2, @c, @c1 = *(Project.all.sort_by(&:name)) | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   def test_valid_tree | 
					
						
							|  |  |  |     assert_valid_nested_set | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-23 17:39:38 +00:00
										 |  |  |   def test_rebuild_should_build_valid_tree | 
					
						
							|  |  |  |     Project.update_all "lft = NULL, rgt = NULL" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Project.rebuild! | 
					
						
							|  |  |  |     assert_valid_nested_set | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-10 16:50:15 +00:00
										 |  |  |   def test_moving_a_child_to_a_different_parent_should_keep_valid_tree | 
					
						
							|  |  |  |     assert_no_difference 'Project.count' do | 
					
						
							|  |  |  |       Project.find_by_name('B1').set_parent!(Project.find_by_name('A2')) | 
					
						
							| 
									
										
										
										
											2011-01-06 21:17:08 +00:00
										 |  |  |     end | 
					
						
							| 
									
										
										
										
											2012-08-10 16:50:15 +00:00
										 |  |  |     assert_valid_nested_set | 
					
						
							|  |  |  |   end | 
					
						
							| 
									
										
										
										
											2011-08-30 05:49:57 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-10 16:50:15 +00:00
										 |  |  |   def test_renaming_a_root_to_first_position_should_update_nested_set_order | 
					
						
							|  |  |  |     @c.name = '1' | 
					
						
							|  |  |  |     @c.save! | 
					
						
							|  |  |  |     assert_valid_nested_set | 
					
						
							|  |  |  |   end | 
					
						
							| 
									
										
										
										
											2011-08-30 05:49:57 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-10 16:50:15 +00:00
										 |  |  |   def test_renaming_a_root_to_middle_position_should_update_nested_set_order | 
					
						
							|  |  |  |     @a.name = 'BA' | 
					
						
							|  |  |  |     @a.save! | 
					
						
							|  |  |  |     assert_valid_nested_set | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   def test_renaming_a_root_to_last_position_should_update_nested_set_order | 
					
						
							|  |  |  |     @a.name = 'D' | 
					
						
							|  |  |  |     @a.save! | 
					
						
							|  |  |  |     assert_valid_nested_set | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   def test_renaming_a_root_to_same_position_should_update_nested_set_order | 
					
						
							|  |  |  |     @c.name = 'D' | 
					
						
							|  |  |  |     @c.save! | 
					
						
							|  |  |  |     assert_valid_nested_set | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   def test_renaming_a_child_should_update_nested_set_order | 
					
						
							|  |  |  |     @a1.name = 'A3' | 
					
						
							|  |  |  |     @a1.save! | 
					
						
							|  |  |  |     assert_valid_nested_set | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   def test_renaming_a_child_with_child_should_update_nested_set_order | 
					
						
							|  |  |  |     @b1.name = 'B3' | 
					
						
							|  |  |  |     @b1.save! | 
					
						
							|  |  |  |     assert_valid_nested_set | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   def test_adding_a_root_to_first_position_should_update_nested_set_order | 
					
						
							|  |  |  |     project = Project.create!(:name => '1', :identifier => 'projectba') | 
					
						
							|  |  |  |     assert_valid_nested_set | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   def test_adding_a_root_to_middle_position_should_update_nested_set_order | 
					
						
							|  |  |  |     project = Project.create!(:name => 'BA', :identifier => 'projectba') | 
					
						
							|  |  |  |     assert_valid_nested_set | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   def test_adding_a_root_to_last_position_should_update_nested_set_order | 
					
						
							|  |  |  |     project = Project.create!(:name => 'Z', :identifier => 'projectba') | 
					
						
							|  |  |  |     assert_valid_nested_set | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   def test_destroying_a_root_with_children_should_keep_valid_tree | 
					
						
							|  |  |  |     assert_difference 'Project.count', -4 do | 
					
						
							|  |  |  |       Project.find_by_name('B').destroy | 
					
						
							| 
									
										
										
										
											2011-01-06 21:17:08 +00:00
										 |  |  |     end | 
					
						
							| 
									
										
										
										
											2012-08-10 16:50:15 +00:00
										 |  |  |     assert_valid_nested_set | 
					
						
							| 
									
										
										
										
											2011-01-06 21:17:08 +00:00
										 |  |  |   end | 
					
						
							| 
									
										
										
										
											2011-08-30 05:49:57 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-10 16:50:15 +00:00
										 |  |  |   def test_destroying_a_child_with_children_should_keep_valid_tree | 
					
						
							|  |  |  |     assert_difference 'Project.count', -2 do | 
					
						
							|  |  |  |       Project.find_by_name('B1').destroy | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |     assert_valid_nested_set | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   private | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-01-06 21:17:08 +00:00
										 |  |  |   def assert_nested_set_values(h) | 
					
						
							|  |  |  |     assert Project.valid? | 
					
						
							|  |  |  |     h.each do |project, expected| | 
					
						
							|  |  |  |       project.reload | 
					
						
							|  |  |  |       assert_equal expected, [project.parent_id, project.lft, project.rgt], "Unexpected nested set values for #{project.name}" | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2010-01-31 10:39:42 +00:00
										 |  |  |   end | 
					
						
							| 
									
										
										
										
											2012-08-10 16:50:15 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |   def assert_valid_nested_set | 
					
						
							|  |  |  |     projects = Project.all | 
					
						
							|  |  |  |     lft_rgt = projects.map {|p| [p.lft, p.rgt]}.flatten | 
					
						
							|  |  |  |     assert_equal projects.size * 2, lft_rgt.uniq.size | 
					
						
							|  |  |  |     assert_equal 1, lft_rgt.min | 
					
						
							|  |  |  |     assert_equal projects.size * 2, lft_rgt.max | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     projects.each do |project| | 
					
						
							|  |  |  |       # lft should always be < rgt | 
					
						
							|  |  |  |       assert project.lft < project.rgt, "lft=#{project.lft} was not < rgt=#{project.rgt} for project #{project.name}" | 
					
						
							|  |  |  |       if project.parent_id | 
					
						
							|  |  |  |         # child lft/rgt values must be greater/lower | 
					
						
							|  |  |  |         assert_not_nil project.parent, "parent was nil for project #{project.name}" | 
					
						
							|  |  |  |         assert project.lft > project.parent.lft, "lft=#{project.lft} was not > parent.lft=#{project.parent.lft} for project #{project.name}" | 
					
						
							|  |  |  |         assert project.rgt < project.parent.rgt, "rgt=#{project.rgt} was not < parent.rgt=#{project.parent.rgt} for project #{project.name}" | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |       # no overlapping lft/rgt values | 
					
						
							|  |  |  |       overlapping = projects.detect {|other|  | 
					
						
							|  |  |  |         other != project && ( | 
					
						
							|  |  |  |           (other.lft > project.lft && other.lft < project.rgt && other.rgt > project.rgt) || | 
					
						
							|  |  |  |           (other.rgt > project.lft && other.rgt < project.rgt && other.lft < project.lft) | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       assert_nil overlapping, (overlapping && "Project #{overlapping.name} (#{overlapping.lft}/#{overlapping.rgt}) overlapped #{project.name} (#{project.lft}/#{project.rgt})") | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # root projects sorted alphabetically | 
					
						
							|  |  |  |     assert_equal Project.roots.map(&:name).sort, Project.roots.sort_by(&:lft).map(&:name), "Root projects were not properly sorted" | 
					
						
							|  |  |  |     projects.each do |project| | 
					
						
							|  |  |  |       if project.children.any? | 
					
						
							|  |  |  |         # sibling projects sorted alphabetically | 
					
						
							|  |  |  |         assert_equal project.children.map(&:name).sort, project.children.order('lft').map(&:name), "Project #{project.name}'s children were not properly sorted" | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							| 
									
										
										
										
											2011-01-06 21:17:08 +00:00
										 |  |  | end |