|
| 1 | +# frozen_string_literal: true |
| 2 | + |
| 3 | +require 'test_helper' |
| 4 | + |
| 5 | +describe 'AdoptableTag with dependent: :adopt' do |
| 6 | + before do |
| 7 | + AdoptableTag.delete_all |
| 8 | + AdoptableTag.hierarchy_class.delete_all |
| 9 | + end |
| 10 | + |
| 11 | + it 'moves children to grandparent when parent is destroyed' do |
| 12 | + p1 = AdoptableTag.create!(name: 'p1') |
| 13 | + p2 = AdoptableTag.create!(name: 'p2', parent: p1) |
| 14 | + p3 = AdoptableTag.create!(name: 'p3', parent: p2) |
| 15 | + p4 = AdoptableTag.create!(name: 'p4', parent: p3) |
| 16 | + |
| 17 | + # Verify initial structure: p1 -> p2 -> p3 -> p4 |
| 18 | + assert_equal p2, p3.parent |
| 19 | + assert_equal p3, p4.parent |
| 20 | + assert_equal p1, p2.parent |
| 21 | + |
| 22 | + # Destroy p3 |
| 23 | + p3.destroy |
| 24 | + |
| 25 | + # After destroying p3, p4 should be adopted by p2 (p3's parent) |
| 26 | + p4.reload |
| 27 | + p2.reload |
| 28 | + assert_equal p2, p4.parent, 'p4 should be moved to p2 (grandparent)' |
| 29 | + assert_equal p1, p2.parent, 'p2 should still have p1 as parent' |
| 30 | + assert_equal [p4], p2.children.to_a, 'p2 should have p4 as child' |
| 31 | + end |
| 32 | + |
| 33 | + it 'moves children to root when parent without grandparent is destroyed' do |
| 34 | + p1 = AdoptableTag.create!(name: 'p1') |
| 35 | + p2 = AdoptableTag.create!(name: 'p2', parent: p1) |
| 36 | + p3 = AdoptableTag.create!(name: 'p3', parent: p2) |
| 37 | + |
| 38 | + # Verify initial structure: p1 -> p2 -> p3 |
| 39 | + assert_equal p1, p2.parent |
| 40 | + assert_equal p2, p3.parent |
| 41 | + |
| 42 | + # Destroy p1 (root node) |
| 43 | + p1.destroy |
| 44 | + |
| 45 | + # After destroying p1, p2 should become root, and p3 should still be child of p2 |
| 46 | + p2.reload |
| 47 | + p3.reload |
| 48 | + assert_nil p2.parent, 'p2 should become root' |
| 49 | + assert_equal p2, p3.parent, 'p3 should still have p2 as parent' |
| 50 | + assert p2.root?, 'p2 should be a root node' |
| 51 | + assert_equal [p3], p2.children.to_a, 'p2 should have p3 as child' |
| 52 | + end |
| 53 | + |
| 54 | + it 'handles multiple children being adopted' do |
| 55 | + p1 = AdoptableTag.create!(name: 'p1') |
| 56 | + p2 = AdoptableTag.create!(name: 'p2', parent: p1) |
| 57 | + c1 = AdoptableTag.create!(name: 'c1', parent: p2) |
| 58 | + c2 = AdoptableTag.create!(name: 'c2', parent: p2) |
| 59 | + c3 = AdoptableTag.create!(name: 'c3', parent: p2) |
| 60 | + |
| 61 | + # Verify initial structure: p1 -> p2 -> [c1, c2, c3] |
| 62 | + assert_equal [c1, c2, c3].sort, p2.children.to_a.sort |
| 63 | + |
| 64 | + # Destroy p2 |
| 65 | + p2.destroy |
| 66 | + |
| 67 | + # All children should be adopted by p1 |
| 68 | + p1.reload |
| 69 | + c1.reload |
| 70 | + c2.reload |
| 71 | + c3.reload |
| 72 | + |
| 73 | + assert_equal p1, c1.parent, 'c1 should be moved to p1' |
| 74 | + assert_equal p1, c2.parent, 'c2 should be moved to p1' |
| 75 | + assert_equal p1, c3.parent, 'c3 should be moved to p1' |
| 76 | + assert_equal [c1, c2, c3].sort, p1.children.to_a.sort, 'p1 should have all three children' |
| 77 | + end |
| 78 | + |
| 79 | + it 'maintains hierarchy relationships after adoption' do |
| 80 | + p1 = AdoptableTag.create!(name: 'p1') |
| 81 | + p2 = AdoptableTag.create!(name: 'p2', parent: p1) |
| 82 | + p3 = AdoptableTag.create!(name: 'p3', parent: p2) |
| 83 | + p4 = AdoptableTag.create!(name: 'p4', parent: p3) |
| 84 | + p5 = AdoptableTag.create!(name: 'p5', parent: p4) |
| 85 | + |
| 86 | + # Verify initial structure: p1 -> p2 -> p3 -> p4 -> p5 |
| 87 | + assert_equal %w[p1 p2 p3 p4 p5], p5.ancestry_path |
| 88 | + |
| 89 | + # Destroy p3 |
| 90 | + p3.destroy |
| 91 | + |
| 92 | + # After adoption, p4 and p5 should still maintain their relationship |
| 93 | + p4.reload |
| 94 | + p5.reload |
| 95 | + assert_equal p2, p4.parent, 'p4 should be adopted by p2' |
| 96 | + assert_equal p4, p5.parent, 'p5 should still have p4 as parent' |
| 97 | + assert_equal %w[p1 p2 p4 p5], p5.ancestry_path, 'ancestry path should be updated correctly' |
| 98 | + end |
| 99 | + |
| 100 | + it 'handles deep nested structures correctly' do |
| 101 | + root = AdoptableTag.create!(name: 'root') |
| 102 | + level1 = AdoptableTag.create!(name: 'level1', parent: root) |
| 103 | + level2 = AdoptableTag.create!(name: 'level2', parent: level1) |
| 104 | + level3 = AdoptableTag.create!(name: 'level3', parent: level2) |
| 105 | + level4 = AdoptableTag.create!(name: 'level4', parent: level3) |
| 106 | + |
| 107 | + # Destroy level2 |
| 108 | + level2.destroy |
| 109 | + |
| 110 | + # level3 should be adopted by level1, and level4 should still be child of level3 |
| 111 | + level1.reload |
| 112 | + level3.reload |
| 113 | + level4.reload |
| 114 | + |
| 115 | + assert_equal level1, level3.parent, 'level3 should be adopted by level1' |
| 116 | + assert_equal level3, level4.parent, 'level4 should still have level3 as parent' |
| 117 | + assert_equal %w[root level1 level3 level4], level4.ancestry_path |
| 118 | + end |
| 119 | + |
| 120 | + it 'handles destroying a node with no children' do |
| 121 | + p1 = AdoptableTag.create!(name: 'p1') |
| 122 | + p2 = AdoptableTag.create!(name: 'p2', parent: p1) |
| 123 | + leaf = AdoptableTag.create!(name: 'leaf', parent: p2) |
| 124 | + |
| 125 | + # Destroy leaf (has no children) |
| 126 | + leaf.destroy |
| 127 | + |
| 128 | + # Should not raise any errors |
| 129 | + p1.reload |
| 130 | + p2.reload |
| 131 | + assert_equal [p2], p1.children.to_a |
| 132 | + assert_equal [], p2.children.to_a |
| 133 | + end |
| 134 | + |
| 135 | + it 'works with find_or_create_by_path' do |
| 136 | + level3 = AdoptableTag.find_or_create_by_path(%w[root level1 level2 level3]) |
| 137 | + root = level3.root |
| 138 | + level1 = root.children.find_by(name: 'level1') |
| 139 | + level2 = level1.children.find_by(name: 'level2') |
| 140 | + |
| 141 | + # Destroy level2 |
| 142 | + level2.destroy |
| 143 | + |
| 144 | + # level3 should be adopted by level1 |
| 145 | + level1.reload |
| 146 | + level3.reload |
| 147 | + assert_equal level1, level3.parent |
| 148 | + assert_equal %w[root level1 level3], level3.ancestry_path |
| 149 | + end |
| 150 | +end |
| 151 | + |
| 152 | + |
0 commit comments