diff test/unit/project_nested_set_test.rb @ 1115:433d4f72a19b redmine-2.2

Update to Redmine SVN revision 11137 on 2.2-stable branch
author Chris Cannam
date Mon, 07 Jan 2013 12:01:42 +0000
parents cbb26bc654de
children 622f24f53b42
line wrap: on
line diff
--- a/test/unit/project_nested_set_test.rb	Wed Jun 27 14:54:18 2012 +0100
+++ b/test/unit/project_nested_set_test.rb	Mon Jan 07 12:01:42 2013 +0000
@@ -1,5 +1,5 @@
 # Redmine - project management software
-# Copyright (C) 2006-2011  Jean-Philippe Lang
+# Copyright (C) 2006-2012  Jean-Philippe Lang
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
@@ -19,94 +19,123 @@
 
 class ProjectNestedSetTest < ActiveSupport::TestCase
 
-  context "nested set" do
-    setup do
-      Project.delete_all
+  def setup
+    Project.delete_all
 
-      @a = Project.create!(:name => 'Project A', :identifier => 'projecta')
-      @a1 = Project.create!(:name => 'Project A1', :identifier => 'projecta1')
-      @a1.set_parent!(@a)
-      @a2 = Project.create!(:name => 'Project A2', :identifier => 'projecta2')
-      @a2.set_parent!(@a)
+    @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)
 
-      @b = Project.create!(:name => 'Project B', :identifier => 'projectb')
-      @b1 = Project.create!(:name => 'Project B1', :identifier => 'projectb1')
-      @b1.set_parent!(@b)
-      @b11 = Project.create!(:name => 'Project B11', :identifier => 'projectb11')
-      @b11.set_parent!(@b1)
-      @b2 = Project.create!(:name => 'Project B2', :identifier => 'projectb2')
-      @b2.set_parent!(@b)
+    @c = Project.create!(:name => 'C', :identifier => 'projectc')
+    @c1 = Project.create!(:name => 'C1', :identifier => 'projectc1')
+    @c1.set_parent!(@c)
 
-      @c = Project.create!(:name => 'Project C', :identifier => 'projectc')
-      @c1 = Project.create!(:name => 'Project C1', :identifier => 'projectc1')
-      @c1.set_parent!(@c)
+    @b = Project.create!(:name => 'B', :identifier => 'projectb')
+    @b2 = Project.create!(:name => 'B2', :identifier => 'projectb2')
+    @b2.set_parent!(@b)
+    @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].each(&:reload)
+    @a, @a1, @a2, @b, @b1, @b11, @b2, @c, @c1 = *(Project.all.sort_by(&:name))
+  end
+
+  def test_valid_tree
+    assert_valid_nested_set
+  end
+
+  def test_rebuild_should_build_valid_tree
+    Project.update_all "lft = NULL, rgt = NULL"
+
+    Project.rebuild!
+    assert_valid_nested_set
+  end
+
+  def test_rebuild_tree_should_build_valid_tree_even_with_valid_lft_rgt_values
+    Project.update_all "name = 'YY'", {:id => @a.id }
+    # 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
+
+  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'))
     end
+    assert_valid_nested_set
+  end
 
-    context "#create" do
-      should "build valid tree" do
-        assert_nested_set_values({
-          @a   => [nil,   1,  6],
-          @a1  => [@a.id, 2,  3],
-          @a2  => [@a.id, 4,  5],
-          @b   => [nil,   7, 14],
-          @b1  => [@b.id, 8, 11],
-          @b11 => [@b1.id,9, 10],
-          @b2  => [@b.id,12, 13],
-          @c   => [nil,  15, 18],
-          @c1  => [@c.id,16, 17]
-        })
-      end
+  def test_renaming_a_root_to_first_position_should_update_nested_set_order
+    @c.name = '1'
+    @c.save!
+    assert_valid_nested_set
+  end
+
+  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
     end
+    assert_valid_nested_set
+  end
 
-    context "#set_parent!" do
-      should "keep valid tree" do
-        assert_no_difference 'Project.count' do
-          Project.find_by_name('Project B1').set_parent!(Project.find_by_name('Project A2'))
-        end
-        assert_nested_set_values({
-          @a   => [nil,   1, 10],
-          @a2  => [@a.id, 4,  9],
-          @b1  => [@a2.id,5,  8],
-          @b11 => [@b1.id,6,  7],
-          @b   => [nil,  11, 14],
-          @c   => [nil,  15, 18]
-        })
-      end
+  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
 
-    context "#destroy" do
-      context "a root with children" do
-        should "not mess up the tree" do
-          assert_difference 'Project.count', -4 do
-            Project.find_by_name('Project B').destroy
-          end
-          assert_nested_set_values({
-            @a  => [nil,   1,  6],
-            @a1 => [@a.id, 2,  3],
-            @a2 => [@a.id, 4,  5],
-            @c  => [nil,   7, 10],
-            @c1 => [@c.id, 8,  9]
-          })
-        end
-      end
-
-      context "a child with children" do
-        should "not mess up the tree" do
-          assert_difference 'Project.count', -2 do
-            Project.find_by_name('Project B1').destroy
-          end
-          assert_nested_set_values({
-            @a  => [nil,   1,  6],
-            @b  => [nil,   7, 10],
-            @b2 => [@b.id, 8,  9],
-            @c  => [nil,  11, 14]
-          })
-        end
-      end
-    end
-  end
+  private
 
   def assert_nested_set_values(h)
     assert Project.valid?
@@ -115,4 +144,40 @@
       assert_equal expected, [project.parent_id, project.lft, project.rgt], "Unexpected nested set values for #{project.name}"
     end
   end
+
+  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
 end