2010-01-31 10:39:42 +00:00
# Redmine - project management software
2014-01-29 22:45:39 +00:00
# Copyright (C) 2006-2014 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 )
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 "
2014-01-21 06:29:48 +00:00
Project . rebuild_tree!
2012-11-23 17:39:38 +00:00
assert_valid_nested_set
end
2012-11-23 17:50:46 +00:00
def test_rebuild_tree_should_build_valid_tree_even_with_valid_lft_rgt_values
2014-01-09 04:23:36 +00:00
Project . where ( { :id = > @a . id } ) . update_all ( " name = 'YY' " )
2012-11-23 17:50:46 +00:00
# lft and rgt values are still valid (Project.rebuild! would not update anything)
# but projects are not ordered properly (YY is in the first place)
Project . rebuild_tree!
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
2014-05-24 16:09:04 +00:00
assert_equal project . children . map ( & :name ) . sort , project . children . sort_by ( & :lft ) . map ( & :name ) , " Project #{ project . name } 's children were not properly sorted "
2012-08-10 16:50:15 +00:00
end
end
end
2011-01-06 21:17:08 +00:00
end