better_nested_set 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Binary file
@@ -0,0 +1 @@
1
+ See instructions in ../README
@@ -0,0 +1,25 @@
1
+ ENV['RAILS_ENV'] = 'test'
2
+ require File.expand_path(File.join(File.dirname(__FILE__), '../../../../config/environment.rb'))
3
+ require 'test_help'
4
+
5
+ config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
6
+ ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + '/debug.log')
7
+ cs = ActiveRecord::Base.establish_connection(config[ENV['DB'] || 'mysql'])
8
+ puts "Using #{cs.config[:adapter]} adapter"
9
+ load(File.dirname(__FILE__) + '/schema.rb')
10
+
11
+ Test::Unit::TestCase.fixture_path = File.dirname(__FILE__) + '/fixtures/'
12
+ $LOAD_PATH.unshift(Test::Unit::TestCase.fixture_path)
13
+
14
+ class Test::Unit::TestCase #:nodoc:
15
+ def create_fixtures(*table_names)
16
+ if block_given?
17
+ Fixtures.create_fixtures(Test::Unit::TestCase.fixture_path, table_names) { yield }
18
+ else
19
+ Fixtures.create_fixtures(Test::Unit::TestCase.fixture_path, table_names)
20
+ end
21
+ end
22
+
23
+ self.use_transactional_fixtures = true
24
+ self.use_instantiated_fixtures = false
25
+ end
@@ -0,0 +1,1368 @@
1
+ require File.dirname(__FILE__) + '/abstract_unit'
2
+ require File.dirname(__FILE__) + '/fixtures/mixin'
3
+ require 'pp'
4
+
5
+ class MixinNestedSetTest < Test::Unit::TestCase
6
+ fixtures :mixins
7
+
8
+ def setup
9
+ # force so other tests besides test_destroy_dependent aren't affected
10
+ NestedSetWithStringScope.acts_as_nested_set_options[:dependent] = :delete_all
11
+ end
12
+
13
+ ##########################################
14
+ # HIGH LEVEL TESTS
15
+ ##########################################
16
+ def test_mixing_in_methods
17
+ ns = NestedSet.new
18
+ assert(ns.respond_to?(:all_children)) # test a random method
19
+
20
+ check_method_mixins(ns)
21
+ check_deprecated_method_mixins(ns)
22
+ check_class_method_mixins(NestedSet)
23
+ end
24
+
25
+ def check_method_mixins(obj)
26
+ [:<=>, :all_children, :all_children_count, :ancestors, :before_create, :before_destroy, :check_full_tree,
27
+ :check_subtree, :children, :children_count, :full_set, :leaves, :leaves_count, :left_col_name, :level, :move_to_child_of,
28
+ :move_to_left_of, :move_to_right_of, :parent, :parent_col_name, :renumber_full_tree, :right_col_name,
29
+ :root, :roots, :self_and_ancestors, :self_and_siblings, :siblings].each { |symbol| assert(obj.respond_to?(symbol)) }
30
+ end
31
+
32
+ def check_deprecated_method_mixins(obj)
33
+ [:add_child, :direct_children, :parent_column, :root?, :child?, :unknown?].each { |symbol| assert(obj.respond_to?(symbol)) }
34
+ end
35
+
36
+ def check_class_method_mixins(klass)
37
+ [:root, :roots, :check_all, :renumber_all].each { |symbol| assert(klass.respond_to?(symbol)) }
38
+ end
39
+
40
+ def test_string_scope
41
+ ns = NestedSet.new
42
+ assert_equal("mixins.root_id IS NULL", ns.scope_condition)
43
+
44
+ ns = NestedSetWithStringScope.new
45
+ ns.root_id = 1
46
+ assert_equal("mixins.root_id = 1", ns.scope_condition)
47
+ ns.root_id = 42
48
+ assert_equal("mixins.root_id = 42", ns.scope_condition)
49
+ check_method_mixins ns
50
+ end
51
+
52
+ def test_without_scope_condition
53
+ ns = NestedSet.new
54
+ assert_equal("mixins.root_id IS NULL", ns.scope_condition)
55
+ NestedSet.without_scope_condition do
56
+ assert_equal("(1 = 1)", ns.scope_condition)
57
+ end
58
+ assert_equal("mixins.root_id IS NULL", ns.scope_condition)
59
+ end
60
+
61
+ def test_symbol_scope
62
+ ns = NestedSetWithSymbolScope.new
63
+ ns.root_id = 1
64
+ assert_equal("mixins.root_id = 1", ns.scope_condition)
65
+ ns.root_id = 42
66
+ assert_equal("mixins.root_id = 42", ns.scope_condition)
67
+ check_method_mixins ns
68
+ end
69
+
70
+ def test_protected_attributes
71
+ ns = NestedSet.new(:parent_id => 2, :lft => 3, :rgt => 2)
72
+ [:parent_id, :lft, :rgt].each {|symbol| assert_equal(nil, ns.send(symbol))}
73
+ end
74
+
75
+ def test_really_protected_attributes
76
+ ns = NestedSet.new
77
+ assert_raise(ActiveRecord::ActiveRecordError) {ns.parent_id = 1}
78
+ assert_raise(ActiveRecord::ActiveRecordError) {ns.lft = 1}
79
+ assert_raise(ActiveRecord::ActiveRecordError) {ns.rgt = 1}
80
+ end
81
+
82
+ ##########################################
83
+ # CLASS METHOD TESTS
84
+ ##########################################
85
+ def test_class_root
86
+ NestedSetWithStringScope.roots.each {|r| r.destroy unless r.id == 4001}
87
+ assert_equal([NestedSetWithStringScope.find(4001)], NestedSetWithStringScope.roots)
88
+ NestedSetWithStringScope.find(4001).destroy
89
+ assert_equal(nil, NestedSetWithStringScope.root)
90
+ ns = NestedSetWithStringScope.create(:root_id => 2)
91
+ assert_equal(ns, NestedSetWithStringScope.root)
92
+ end
93
+
94
+ def test_class_root_again
95
+ NestedSetWithStringScope.roots.each {|r| r.destroy unless r.id == 101}
96
+ assert_equal(NestedSetWithStringScope.find(101), NestedSetWithStringScope.root)
97
+ end
98
+
99
+ def test_class_roots
100
+ assert_equal(2, NestedSetWithStringScope.roots.size)
101
+ assert_equal(10, NestedSet.roots.size) # May change if STI behavior changes
102
+ end
103
+
104
+ def test_check_all_1
105
+ assert_nothing_raised {NestedSetWithStringScope.check_all}
106
+ NestedSetWithStringScope.update_all("lft = 3", "id = 103")
107
+ assert_raise(ActiveRecord::ActiveRecordError) {NestedSetWithStringScope.check_all}
108
+ end
109
+
110
+ def test_check_all_2
111
+ NestedSetWithStringScope.update_all("lft = lft + 1", "lft > 11 AND root_id = 101")
112
+ NestedSetWithStringScope.update_all("rgt = rgt + 1", "lft > 11 AND root_id = 101")
113
+ assert_raise(ActiveRecord::ActiveRecordError) {NestedSetWithStringScope.check_all}
114
+ end
115
+
116
+ def test_check_all_3
117
+ NestedSetWithStringScope.update_all("lft = lft + 2", "lft > 11 AND root_id = 101")
118
+ NestedSetWithStringScope.update_all("rgt = rgt + 2", "lft > 11 AND root_id = 101")
119
+ assert_raise(ActiveRecord::ActiveRecordError) {NestedSetWithStringScope.check_all}
120
+ end
121
+
122
+ def test_check_all_4
123
+ ns = NestedSetWithStringScope.create(:root_id => 101) # virtual root
124
+ assert_nothing_raised {NestedSetWithStringScope.check_all}
125
+ NestedSetWithStringScope.update_all("rgt = rgt + 2, lft = lft + 2", "id = #{ns.id}") # create a gap between virtual roots
126
+ assert_nothing_raised {ns.check_subtree}
127
+ assert_raise(ActiveRecord::ActiveRecordError) {NestedSetWithStringScope.check_all}
128
+ end
129
+
130
+ def test_renumber_all
131
+ NestedSetWithStringScope.update_all("lft = NULL, rgt = NULL")
132
+ assert_raise(ActiveRecord::ActiveRecordError) {NestedSetWithStringScope.check_all}
133
+ NestedSetWithStringScope.renumber_all
134
+ assert_nothing_raised(ActiveRecord::ActiveRecordError) {NestedSetWithStringScope.check_all}
135
+ NestedSetWithStringScope.update_all("lft = 1, rgt = 2")
136
+ assert_raise(ActiveRecord::ActiveRecordError) {NestedSetWithStringScope.check_all}
137
+ NestedSetWithStringScope.renumber_all
138
+ assert_nothing_raised(ActiveRecord::ActiveRecordError) {NestedSetWithStringScope.check_all}
139
+ end
140
+
141
+ def test_sql_for
142
+ assert_equal("1 != 1", Category.sql_for([]))
143
+ c = Category.new
144
+ assert_equal("1 != 1", Category.sql_for(c))
145
+ assert_equal("1 != 1", Category.sql_for([c]))
146
+ c.save
147
+ assert_equal("((mixins.lft BETWEEN 1 AND 2))", Category.sql_for(c))
148
+ assert_equal("((mixins.lft BETWEEN 1 AND 2))", Category.sql_for([c]))
149
+ assert_equal("((mixins.lft BETWEEN 1 AND 20))", NestedSetWithStringScope.sql_for(101))
150
+ assert_equal("((mixins.lft BETWEEN 1 AND 20) OR (mixins.lft BETWEEN 4 AND 11))", NestedSetWithStringScope.sql_for([101, set2(3)]))
151
+ assert_equal("((mixins.lft BETWEEN 5 AND 6) OR (mixins.lft BETWEEN 7 AND 8) OR (mixins.lft BETWEEN 9 AND 10))", NestedSetWithStringScope.sql_for(set2(3).children))
152
+ end
153
+
154
+
155
+ ##########################################
156
+ # CALLBACK TESTS
157
+ ##########################################
158
+ # If we change behavior of virtual roots, this test may change
159
+ def test_before_create
160
+ ns = NestedSetWithSymbolScope.create(:root_id => 1234)
161
+ assert_equal(1, ns.lft)
162
+ assert_equal(2, ns.rgt)
163
+ ns = NestedSetWithSymbolScope.create(:root_id => 1234)
164
+ assert_equal(3, ns.lft)
165
+ assert_equal(4, ns.rgt)
166
+ end
167
+
168
+ # test pruning a branch. only works if we allow the deletion of nodes with children
169
+ def test_destroy
170
+ big_tree = NestedSetWithStringScope.find(4001)
171
+
172
+ # Make sure we have the right one
173
+ assert_equal(3, big_tree.direct_children.length)
174
+ assert_equal(10, big_tree.full_set.length)
175
+
176
+ NestedSetWithStringScope.find(4005).destroy
177
+
178
+ big_tree = NestedSetWithStringScope.find(4001)
179
+
180
+ assert_equal(7, big_tree.full_set.length)
181
+ assert_equal(2, big_tree.direct_children.length)
182
+
183
+ assert_equal(1, NestedSetWithStringScope.find(4001).lft)
184
+ assert_equal(2, NestedSetWithStringScope.find(4002).lft)
185
+ assert_equal(3, NestedSetWithStringScope.find(4003).lft)
186
+ assert_equal(4, NestedSetWithStringScope.find(4003).rgt)
187
+ assert_equal(5, NestedSetWithStringScope.find(4004).lft)
188
+ assert_equal(6, NestedSetWithStringScope.find(4004).rgt)
189
+ assert_equal(7, NestedSetWithStringScope.find(4002).rgt)
190
+ assert_equal(8, NestedSetWithStringScope.find(4008).lft)
191
+ assert_equal(9, NestedSetWithStringScope.find(4009).lft)
192
+ assert_equal(10, NestedSetWithStringScope.find(4009).rgt)
193
+ assert_equal(11, NestedSetWithStringScope.find(4010).lft)
194
+ assert_equal(12, NestedSetWithStringScope.find(4010).rgt)
195
+ assert_equal(13, NestedSetWithStringScope.find(4008).rgt)
196
+ assert_equal(14, NestedSetWithStringScope.find(4001).rgt)
197
+ end
198
+
199
+ def test_destroy_2
200
+ assert_nothing_raised {set2(1).check_subtree}
201
+ assert set2(10).destroy
202
+ assert_nothing_raised {set2(1).reload.check_subtree}
203
+ assert set2(9).children.empty?
204
+ assert set2(9).destroy
205
+ assert_equal 15, set2(4).rgt
206
+ assert_nothing_raised {set2(1).reload.check_subtree}
207
+ assert_nothing_raised {NestedSetWithStringScope.check_all}
208
+ end
209
+
210
+ def test_destroy_3
211
+ assert set2(3).destroy
212
+ assert_equal(2, set2(1).children.size)
213
+ assert_equal(0, NestedSetWithStringScope.find(:all, :conditions => "id > 104 and id < 108").size)
214
+ assert_equal(6, set2(1).full_set.size)
215
+ assert_equal(3, set2(2).rgt)
216
+ assert_equal(4, set2(4).lft)
217
+ assert_equal(12, set2(1).rgt)
218
+ assert_nothing_raised {set2(1).check_subtree}
219
+ end
220
+
221
+ def test_destroy_root
222
+ NestedSetWithStringScope.find(4001).destroy
223
+ assert_equal(0, NestedSetWithStringScope.count(:conditions => "root_id = 42"))
224
+ end
225
+
226
+ def test_destroy_dependent
227
+ NestedSetWithStringScope.acts_as_nested_set_options[:dependent] = :destroy
228
+
229
+ big_tree = NestedSetWithStringScope.find(4001)
230
+
231
+ # Make sure we have the right one
232
+ assert_equal(3, big_tree.direct_children.length)
233
+ assert_equal(10, big_tree.full_set.length)
234
+
235
+ NestedSetWithStringScope.find(4005).destroy
236
+
237
+ big_tree = NestedSetWithStringScope.find(4001)
238
+
239
+ assert_equal(7, big_tree.full_set.length)
240
+ assert_equal(2, big_tree.direct_children.length)
241
+
242
+ assert_equal(1, NestedSetWithStringScope.find(4001).lft)
243
+ assert_equal(2, NestedSetWithStringScope.find(4002).lft)
244
+ assert_equal(3, NestedSetWithStringScope.find(4003).lft)
245
+ assert_equal(4, NestedSetWithStringScope.find(4003).rgt)
246
+ assert_equal(5, NestedSetWithStringScope.find(4004).lft)
247
+ assert_equal(6, NestedSetWithStringScope.find(4004).rgt)
248
+ assert_equal(7, NestedSetWithStringScope.find(4002).rgt)
249
+ assert_equal(8, NestedSetWithStringScope.find(4008).lft)
250
+ assert_equal(9, NestedSetWithStringScope.find(4009).lft)
251
+ assert_equal(10, NestedSetWithStringScope.find(4009).rgt)
252
+ assert_equal(11, NestedSetWithStringScope.find(4010).lft)
253
+ assert_equal(12, NestedSetWithStringScope.find(4010).rgt)
254
+ assert_equal(13, NestedSetWithStringScope.find(4008).rgt)
255
+ assert_equal(14, NestedSetWithStringScope.find(4001).rgt)
256
+ end
257
+
258
+ ##########################################
259
+ # QUERY METHOD TESTS
260
+ ##########################################
261
+ def set(id) NestedSet.find(3000 + id) end # helper method
262
+
263
+ def set2(id) NestedSetWithStringScope.find(100 + id) end # helper method
264
+
265
+ def test_root?
266
+ assert NestedSetWithStringScope.find(4001).root?
267
+ assert !NestedSetWithStringScope.find(4002).root?
268
+ end
269
+
270
+ def test_child?
271
+ assert !NestedSetWithStringScope.find(4001).child?
272
+ assert NestedSetWithStringScope.find(4002).child?
273
+ end
274
+
275
+ # Deprecated, delete this test when we nuke the method
276
+ def test_unknown?
277
+ assert !NestedSetWithStringScope.find(4001).unknown?
278
+ assert !NestedSetWithStringScope.find(4002).unknown?
279
+ end
280
+
281
+ # Test the <=> method implicitly
282
+ def test_comparison
283
+ ar = NestedSetWithStringScope.find(:all, :conditions => "root_id = 42", :order => "lft")
284
+ ar2 = NestedSetWithStringScope.find(:all, :conditions => "root_id = 42", :order => "rgt")
285
+ assert_not_equal(ar, ar2)
286
+ assert_equal(ar, ar2.sort)
287
+ end
288
+
289
+ def test_root
290
+ assert_equal(NestedSetWithStringScope.find(4001), NestedSetWithStringScope.find(4007).root)
291
+ assert_equal(set2(1), set2(8).root)
292
+ assert_equal(set2(1), set2(1).root)
293
+ # test virtual roots
294
+ c1, c2, c3 = Category.create, Category.create, Category.create
295
+ c3.move_to_child_of(c2)
296
+ assert_equal(c2, c3.root)
297
+ end
298
+
299
+ def test_roots
300
+ assert_equal([set2(1)], set2(8).roots)
301
+ assert_equal([set2(1)], set2(1).roots)
302
+ assert_equal(NestedSet.find(:all, :conditions => "id > 3000 AND id < 4000").size, set(1).roots.size)
303
+ end
304
+
305
+ def test_parent
306
+ ns = NestedSetWithStringScope.create(:root_id => 45)
307
+ assert_equal(nil, ns.parent)
308
+ assert ns.save
309
+ assert_equal(nil, ns.parent)
310
+ assert_equal(set2(1), set2(2).parent)
311
+ assert_equal(set2(3), set2(7).parent)
312
+ end
313
+
314
+ def test_ancestors
315
+ assert_equal([], set2(1).ancestors)
316
+ assert_equal([set2(1), set2(4), set2(9)], set2(10).ancestors)
317
+ end
318
+
319
+ def test_self_and_ancestors
320
+ assert_equal([set2(1)], set2(1).self_and_ancestors)
321
+ assert_equal([set2(1), set2(4), set2(8)], set2(8).self_and_ancestors)
322
+ assert_equal([set2(1), set2(4), set2(9), set2(10)], set2(10).self_and_ancestors)
323
+ end
324
+
325
+ def test_siblings
326
+ assert_equal([], set2(1).siblings)
327
+ assert_equal([set2(2), set2(4)], set2(3).siblings)
328
+ end
329
+
330
+ def test_first_sibling
331
+ assert set2(2).first_sibling?
332
+ assert_equal(set2(2), set2(2).first_sibling)
333
+ assert_equal(set2(2), set2(3).first_sibling)
334
+ assert_equal(set2(2), set2(4).first_sibling)
335
+ end
336
+
337
+ def test_last_sibling
338
+ assert set2(4).last_sibling?
339
+ assert_equal(set2(4), set2(2).last_sibling)
340
+ assert_equal(set2(4), set2(3).last_sibling)
341
+ assert_equal(set2(4), set2(4).last_sibling)
342
+ end
343
+
344
+ def test_previous_siblings
345
+ assert_equal([], set2(2).previous_siblings)
346
+ assert_equal([set2(2)], set2(3).previous_siblings)
347
+ assert_equal([set2(3), set2(2)], set2(4).previous_siblings)
348
+ end
349
+
350
+ def test_previous_sibling
351
+ assert_equal(nil, set2(2).previous_sibling)
352
+ assert_equal(set2(2), set2(3).previous_sibling)
353
+ assert_equal(set2(3), set2(4).previous_sibling)
354
+ assert_equal([set2(3), set2(2)], set2(4).previous_sibling(2))
355
+ end
356
+
357
+ def test_next_siblings
358
+ assert_equal([], set2(4).next_siblings)
359
+ assert_equal([set2(4)], set2(3).next_siblings)
360
+ assert_equal([set2(3), set2(4)], set2(2).next_siblings)
361
+ end
362
+
363
+ def test_next_sibling
364
+ assert_equal(nil, set2(4).next_sibling)
365
+ assert_equal(set2(4), set2(3).next_sibling)
366
+ assert_equal(set2(3), set2(2).next_sibling)
367
+ assert_equal([set2(3), set2(4)], set2(2).next_sibling(2))
368
+ end
369
+
370
+ def test_self_and_siblings
371
+ assert_equal([set2(1)], set2(1).self_and_siblings)
372
+ assert_equal([set2(2), set2(3), set2(4)], set2(3).self_and_siblings)
373
+ end
374
+
375
+ def test_level
376
+ assert_equal(0, set2(1).level)
377
+ assert_equal(1, set2(3).level)
378
+ assert_equal(3, set2(10).level)
379
+ end
380
+
381
+ def test_all_children_count
382
+ assert_equal(0, set2(10).all_children_count)
383
+ assert_equal(1, set2(3).level)
384
+ assert_equal(3, set2(10).level)
385
+ end
386
+
387
+ def test_full_set
388
+ assert_equal(NestedSetWithStringScope.find(:all, :conditions => "root_id = 101", :order => "lft"), set2(1).full_set)
389
+ new_ns = NestedSetWithStringScope.new(:root_id => 101)
390
+ assert_equal([new_ns], new_ns.full_set)
391
+ assert_equal([set2(4), set2(8), set2(9), set2(10)], set2(4).full_set)
392
+ assert_equal([set2(2)], set2(2).full_set)
393
+ assert_equal([set2(2)], set2(2).full_set(:exclude => nil))
394
+ assert_equal([set2(2)], set2(2).full_set(:exclude => []))
395
+ assert_equal([], set2(1).full_set(:exclude => 101))
396
+ assert_equal([], set2(1).full_set(:exclude => set2(1)))
397
+ ns = NestedSetWithStringScope.create(:root_id => 234)
398
+ assert_equal([], ns.full_set(:exclude => ns))
399
+ assert_equal([set2(4), set2(8), set2(9)], set2(4).full_set(:exclude => set2(10)))
400
+ assert_equal([set2(4), set2(8)], set2(4).full_set(:exclude => set2(9)))
401
+ end
402
+
403
+ def test_all_children
404
+ assert_equal(NestedSetWithStringScope.find(:all, :conditions => "root_id = 101 AND id > 101", :order => "lft"), set2(1).all_children)
405
+ assert_equal([], NestedSetWithStringScope.new(:root_id => 101).all_children)
406
+ assert_equal([set2(8), set2(9), set2(10)], set2(4).all_children)
407
+ assert_equal([set2(8), set2(9)], set2(4).all_children(:exclude => set2(10)))
408
+ assert_equal([set2(8)], set2(4).all_children(:exclude => set2(9)))
409
+ assert_equal([set2(2), set2(4), set2(8)], set2(1).all_children(:exclude => [set2(9), 103]))
410
+ assert_equal([set2(2), set2(4), set2(8)], set2(1).all_children(:exclude => [set2(9), 103, 106]))
411
+ end
412
+
413
+ def test_children
414
+ assert_equal([], set2(10).children)
415
+ assert_equal([], set(1).children)
416
+ assert_equal([set2(2), set2(3), set2(4)], set2(1).children)
417
+ assert_equal([set2(5), set2(6), set2(7)], set2(3).children)
418
+ assert_equal([NestedSetWithStringScope.find(4006), NestedSetWithStringScope.find(4007)], NestedSetWithStringScope.find(4005).children)
419
+ end
420
+
421
+ def test_children_count
422
+ assert_equal(0, set2(10).children_count)
423
+ assert_equal(3, set2(1).children_count)
424
+ end
425
+
426
+ def test_leaves
427
+ assert_equal([set2(10)], set2(9).leaves)
428
+ assert_equal([set2(10)], set2(10).leaves)
429
+ assert_equal([set2(2), set2(5), set2(6), set2(7), set2(8), set2(10)], set2(1).leaves)
430
+ end
431
+
432
+ def test_leaves_count
433
+ assert_equal(1, set2(10).leaves_count)
434
+ assert_equal(1, set2(9).leaves_count)
435
+ assert_equal(6, set2(1).leaves_count)
436
+ end
437
+
438
+ ##########################################
439
+ # CASTING RESULT TESTS
440
+ ##########################################
441
+
442
+ def test_recurse_result_set
443
+ result = []
444
+ NestedSetWithStringScope.recurse_result_set(set2(1).full_set) do |node, level|
445
+ result << [level, node.id]
446
+ end
447
+ expected = [[0, 101], [1, 102], [1, 103], [2, 105], [2, 106], [2, 107], [1, 104], [2, 108], [2, 109], [3, 110]]
448
+ assert_equal expected, result
449
+ end
450
+
451
+ def test_disjointed_result_set
452
+ result_set = set2(1).full_set(:conditions => { :type => 'NestedSetWithStringScope' })
453
+ result = []
454
+ NestedSetWithStringScope.recurse_result_set(result_set) do |node, level|
455
+ result << [level, node.id]
456
+ end
457
+ expected = [[0, 102], [0, 104], [0, 105], [0, 106], [0, 107], [0, 110]]
458
+ assert_equal expected, result
459
+ end
460
+
461
+ def test_result_to_array
462
+ result = NestedSetWithStringScope.result_to_array(set2(1).full_set) do |node, level|
463
+ { :id => node.id, :level => level }
464
+ end
465
+ expected = [{:level=>0, :children=>[{:level=>1, :id=>102}, {:level=>1,
466
+ :children=>[{:level=>2, :id=>105}, {:level=>2, :id=>106}, {:level=>2, :id=>107}], :id=>103}, {:level=>1,
467
+ :children=>[{:level=>2, :id=>108}, {:level=>2, :children=>[{:level=>3, :id=>110}], :id=>109}], :id=>104}], :id=>101}]
468
+ assert_equal expected, result
469
+ end
470
+
471
+ def test_result_to_array_with_method_calls
472
+ result = NestedSetWithStringScope.result_to_array(set2(1).full_set, :only => [:id], :methods => [:children_count])
473
+ expected = [{:children=>[{:children_count=>0, :id=>102}, {:children=>[{:children_count=>0, :id=>105}, {:children_count=>0, :id=>106},
474
+ {:children_count=>0, :id=>107}], :children_count=>3, :id=>103}, {:children=>[{:children_count=>0, :id=>108}, {:children=>[{:children_count=>0, :id=>110}],
475
+ :children_count=>1, :id=>109}], :children_count=>2, :id=>104}], :children_count=>3, :id=>101}]
476
+ assert_equal expected, result
477
+ end
478
+
479
+ def test_disjointed_result_to_array
480
+ result_set = set2(1).full_set(:conditions => { :type => 'NestedSetWithStringScope' })
481
+ result = NestedSetWithStringScope.result_to_array(result_set) do |node, level|
482
+ { :id => node.id, :level => level }
483
+ end
484
+ expected = [{:level=>0, :id=>102}, {:level=>0, :id=>104}, {:level=>0, :id=>105}, {:level=>0, :id=>106}, {:level=>0, :id=>107}, {:level=>0, :id=>110}]
485
+ assert_equal expected, result
486
+ end
487
+
488
+ def test_result_to_array_flat
489
+ result = NestedSetWithStringScope.result_to_array(set2(1).full_set, :nested => false) do |node, level|
490
+ { :id => node.id, :level => level }
491
+ end
492
+ expected = [{:level=>0, :id=>101}, {:level=>0, :id=>103}, {:level=>0, :id=>106}, {:level=>0, :id=>104}, {:level=>0, :id=>109},
493
+ {:level=>0, :id=>102}, {:level=>0, :id=>105}, {:level=>0, :id=>107}, {:level=>0, :id=>108}, {:level=>0, :id=>110}]
494
+ assert_equal expected, result
495
+ end
496
+
497
+ def test_result_to_xml
498
+ result = NestedSetWithStringScope.result_to_xml(set2(3).full_set, :record => 'node', :dasherize => false, :only => [:id]) do |options, subnode|
499
+ options[:builder].tag!('type', subnode[:type])
500
+ end
501
+ expected = '<?xml version="1.0" encoding="UTF-8"?>
502
+ <nodes>
503
+ <node>
504
+ <id type="integer">103</id>
505
+ <type>NS2</type>
506
+ <children>
507
+ <node>
508
+ <id type="integer">105</id>
509
+ <type>NestedSetWithStringScope</type>
510
+ <children>
511
+ </children>
512
+ </node>
513
+ <node>
514
+ <id type="integer">106</id>
515
+ <type>NestedSetWithStringScope</type>
516
+ <children>
517
+ </children>
518
+ </node>
519
+ <node>
520
+ <id type="integer">107</id>
521
+ <type>NestedSetWithStringScope</type>
522
+ <children>
523
+ </children>
524
+ </node>
525
+ </children>
526
+ </node>
527
+ </nodes>'
528
+ assert_equal expected, result.strip
529
+ end
530
+
531
+ def test_disjointed_result_to_xml
532
+ result_set = set2(1).full_set(:conditions => ['type IN(?)', ['NestedSetWithStringScope', 'NS2']])
533
+ result = NestedSetWithStringScope.result_to_xml(result_set, :only => [:id])
534
+ # note how nesting is preserved where possible; this is not always what you want though,
535
+ # so you can force a flattened set with :nested => false instead (see below)
536
+ expected = '<?xml version="1.0" encoding="UTF-8"?>
537
+ <nodes>
538
+ <nested-set-with-string-scope>
539
+ <id type="integer">102</id>
540
+ <children>
541
+ </children>
542
+ </nested-set-with-string-scope>
543
+ <ns2>
544
+ <id type="integer">103</id>
545
+ <children>
546
+ <nested-set-with-string-scope>
547
+ <id type="integer">105</id>
548
+ <children>
549
+ </children>
550
+ </nested-set-with-string-scope>
551
+ <nested-set-with-string-scope>
552
+ <id type="integer">106</id>
553
+ <children>
554
+ </children>
555
+ </nested-set-with-string-scope>
556
+ <nested-set-with-string-scope>
557
+ <id type="integer">107</id>
558
+ <children>
559
+ </children>
560
+ </nested-set-with-string-scope>
561
+ </children>
562
+ </ns2>
563
+ <nested-set-with-string-scope>
564
+ <id type="integer">104</id>
565
+ <children>
566
+ <ns2>
567
+ <id type="integer">108</id>
568
+ <children>
569
+ </children>
570
+ </ns2>
571
+ </children>
572
+ </nested-set-with-string-scope>
573
+ </nodes>'
574
+ assert_equal expected, result.strip
575
+ end
576
+
577
+ def test_result_to_xml_flat
578
+ result = NestedSetWithStringScope.result_to_xml(set2(3).full_set, :record => 'node', :dasherize => false, :only => [:id], :nested => false)
579
+ expected = '<?xml version="1.0" encoding="UTF-8"?>
580
+ <nodes>
581
+ <node>
582
+ <id type="integer">103</id>
583
+ </node>
584
+ <node>
585
+ <id type="integer">105</id>
586
+ </node>
587
+ <node>
588
+ <id type="integer">106</id>
589
+ </node>
590
+ <node>
591
+ <id type="integer">107</id>
592
+ </node>
593
+ </nodes>'
594
+ assert_equal expected, result.strip
595
+ end
596
+
597
+ def test_result_to_attribute_based_xml
598
+ result = NestedSetWithStringScope.result_to_attributes_xml(set2(1).full_set, :record => 'node', :only => [:id, :parent_id])
599
+ expected = '<?xml version="1.0" encoding="UTF-8"?>
600
+ <node id="101" parent_id="0">
601
+ <node id="102" parent_id="101"/>
602
+ <node id="103" parent_id="101">
603
+ <node id="105" parent_id="103"/>
604
+ <node id="106" parent_id="103"/>
605
+ <node id="107" parent_id="103"/>
606
+ </node>
607
+ <node id="104" parent_id="101">
608
+ <node id="108" parent_id="104"/>
609
+ <node id="109" parent_id="104">
610
+ <node id="110" parent_id="109"/>
611
+ </node>
612
+ </node>
613
+ </node>'
614
+ assert_equal expected, result.strip
615
+ end
616
+
617
+ def test_result_to_attribute_based_xml_flat
618
+ result = NestedSetWithStringScope.result_to_attributes_xml(set2(1).full_set, :only => [:id], :nested => false, :skip_instruct => true)
619
+ expected = '<ns1 id="101"/>
620
+ <ns2 id="103"/>
621
+ <nested_set_with_string_scope id="106"/>
622
+ <nested_set_with_string_scope id="104"/>
623
+ <ns1 id="109"/>
624
+ <nested_set_with_string_scope id="102"/>
625
+ <nested_set_with_string_scope id="105"/>
626
+ <nested_set_with_string_scope id="107"/>
627
+ <ns2 id="108"/>
628
+ <nested_set_with_string_scope id="110"/>'
629
+ assert_equal expected, result.strip
630
+ end
631
+
632
+ ##########################################
633
+ # WITH_SCOPE QUERY TESTS
634
+ ##########################################
635
+
636
+ def test_filtered_full_set
637
+ result_set = set2(1).full_set(:conditions => { :type => 'NestedSetWithStringScope' })
638
+ assert_equal [102, 105, 106, 107, 104, 110], result_set.map(&:id)
639
+ end
640
+
641
+ def test_reverse_result_set
642
+ result_set = set2(1).full_set(:reverse => true)
643
+ assert_equal [101, 104, 109, 110, 108, 103, 107, 106, 105, 102], result_set.map(&:id)
644
+ # NestedSetWithStringScope.recurse_result_set(result_set) { |node, level| puts "#{'--' * level}#{node.id}" }
645
+ end
646
+
647
+ def test_reordered_full_set
648
+ result_set = set2(1).full_set(:order => 'id DESC')
649
+ assert_equal [110, 109, 108, 107, 106, 105, 104, 103, 102, 101], result_set.map(&:id)
650
+ end
651
+
652
+ def test_filtered_siblings
653
+ node = set2(2)
654
+ result_set = node.siblings(:conditions => { :type => node[:type] })
655
+ assert_equal [104], result_set.map(&:id)
656
+ end
657
+
658
+ def test_include_option_with_full_set
659
+ result_set = set2(3).full_set(:include => :parent_node)
660
+ assert_equal [[103, 101], [105, 103], [106, 103], [107, 103]], result_set.map { |n| [n.id, n.parent_node.id] }
661
+ end
662
+
663
+ ##########################################
664
+ # FIND UNTIL/THROUGH METHOD TESTS
665
+ ##########################################
666
+
667
+ def test_ancestors_and_self_through
668
+ result = set2(10).ancestors_and_self_through(set2(4))
669
+ assert_equal [104, 109, 110], result.map(&:id)
670
+ result = set2(10).ancestors_through(set2(4))
671
+ assert_equal [104, 109], result.map(&:id)
672
+ end
673
+
674
+ def test_full_set_through
675
+ result = set2(4).full_set_through(set2(10))
676
+ assert_equal [104, 108, 109, 110], result.map(&:id)
677
+ end
678
+
679
+ def test_all_children_through
680
+ result = set2(4).all_children_through(set2(10))
681
+ assert_equal [108, 109, 110], result.map(&:id)
682
+ end
683
+
684
+ def test_siblings_through
685
+ result = set2(5).self_and_siblings_through(set2(7))
686
+ assert_equal [105, 106, 107], result.map(&:id)
687
+ result = set2(7).siblings_through(set2(5))
688
+ assert_equal [105, 106], result.map(&:id)
689
+ end
690
+
691
+ ##########################################
692
+ # FIND CHILD BY ID METHOD TESTS
693
+ ##########################################
694
+
695
+ def test_child_by_id
696
+ assert_equal set2(6), set2(3).child_by_id(set2(6).id)
697
+ assert_nil set2(3).child_by_id(set2(8).id)
698
+ end
699
+
700
+ def test_child_of
701
+ assert set2(6).child_of?(set2(3))
702
+ assert !set2(8).child_of?(set2(3))
703
+ assert set2(6).child_of?(set2(3), :conditions => '1 = 1')
704
+ end
705
+
706
+ def test_direct_child_by_id
707
+ assert_equal set2(9), set2(4).direct_child_by_id(set2(9).id)
708
+ assert_nil set2(4).direct_child_by_id(set2(10).id)
709
+ end
710
+
711
+ def test_direct_child_of
712
+ assert set2(9).direct_child_of?(set2(4))
713
+ assert !set2(10).direct_child_of?(set2(4))
714
+ assert set2(9).direct_child_of?(set2(4), :conditions => '1 = 1')
715
+ end
716
+
717
+ ##########################################
718
+ # INDEX-CHECKING METHOD TESTS
719
+ ##########################################
720
+ def test_check_subtree
721
+ root = set2(1)
722
+ assert_nothing_raised {root.check_subtree}
723
+ # need to use update_all to get around attr_protected
724
+ NestedSetWithStringScope.update_all("rgt = #{root.lft + 1}", "id = #{root.id}")
725
+ assert_raise(ActiveRecord::ActiveRecordError) {root.reload.check_subtree}
726
+ assert_nothing_raised {set2(4).check_subtree}
727
+ NestedSetWithStringScope.update_all("lft = 17", "id = 110")
728
+ assert_raise(ActiveRecord::ActiveRecordError) {set2(4).reload.check_subtree}
729
+ NestedSetWithStringScope.update_all("rgt = 18", "id = 110")
730
+ assert_nothing_raised {set2(10).check_subtree}
731
+ NestedSetWithStringScope.update_all("rgt = NULL", "id = 4002")
732
+ assert_raise(ActiveRecord::ActiveRecordError) {NestedSetWithStringScope.find(4001).reload.check_subtree}
733
+ # this method receives lots of additional testing through tests of check_full_tree and check_all
734
+ end
735
+
736
+ def test_check_full_tree
737
+ assert_nothing_raised {set2(1).check_full_tree}
738
+ assert_nothing_raised {NestedSetWithStringScope.find(4006).check_full_tree}
739
+ NestedSetWithStringScope.update_all("rgt = NULL", "id = 4002")
740
+ assert_raise(ActiveRecord::ActiveRecordError) {NestedSetWithStringScope.find(4006).check_full_tree}
741
+ NestedSetWithStringScope.update_all("rgt = 0", "id = 4001")
742
+ assert_raise(ActiveRecord::ActiveRecordError) {NestedSetWithStringScope.find(4006).check_full_tree}
743
+ NestedSetWithStringScope.update_all("rgt = rgt + 1", "id > 101")
744
+ NestedSetWithStringScope.update_all("lft = lft + 1", "id > 101")
745
+ assert_raise(ActiveRecord::ActiveRecordError) {set2(4).check_full_tree}
746
+ end
747
+
748
+ def test_check_full_tree_orphan
749
+ assert_raise(ActiveRecord::RecordNotFound) {NestedSetWithStringScope.find(99)} # make sure ID 99 doesn't exist
750
+ ns = NestedSetWithStringScope.create(:root_id => 101)
751
+ NestedSetWithStringScope.update_all("parent_id = 99", "id = #{ns.id}")
752
+ assert_raise(ActiveRecord::ActiveRecordError) {set2(3).check_full_tree}
753
+ end
754
+
755
+ def test_check_full_tree_endless_loop
756
+ ns = NestedSetWithStringScope.create(:root_id => 101)
757
+ NestedSetWithStringScope.update_all("parent_id = #{ns.id}", "id = #{ns.id}")
758
+ assert_raise(ActiveRecord::ActiveRecordError) {set2(6).check_full_tree}
759
+ end
760
+
761
+ def test_check_full_tree_virtual_roots
762
+ a = Category.create
763
+ b = Category.create
764
+
765
+ assert_nothing_raised {a.check_full_tree}
766
+ Category.update_all("rgt = rgt + 2, lft = lft + 2", "id = #{b.id}") # create a gap between virtual roots
767
+ assert_raise(ActiveRecord::ActiveRecordError) {a.check_full_tree}
768
+ end
769
+
770
+ # see also the tests of check_all under 'class method tests'
771
+
772
+ ##########################################
773
+ # INDEX-ALTERING (UPDATE) METHOD TESTS
774
+ ##########################################
775
+ def test_move_to_left_of # this method undergoes additional testing elsewhere
776
+ set2(2).move_to_left_of(set2(3)) # should cause no change
777
+ assert_equal(2, set2(2).lft)
778
+ assert_equal(4, set2(3).lft)
779
+ assert_nothing_raised {NestedSetWithStringScope.check_all}
780
+ set2(3).move_to_left_of(set2(2))
781
+ assert_equal(9, set2(3).rgt)
782
+ set2(2).move_to_left_of(set2(3))
783
+ assert_equal(2, set2(2).lft)
784
+ assert_nothing_raised {NestedSetWithStringScope.check_all}
785
+ set2(3).move_to_left_of(102) # pass an ID instead
786
+ assert_equal(2, set2(3).lft)
787
+ assert_nothing_raised {NestedSetWithStringScope.check_all}
788
+ end
789
+
790
+ def test_move_to_right_of # this method undergoes additional testing elsewhere
791
+ set2(3).move_to_right_of(set2(2)) # should cause no change
792
+ set2(4).move_to_right_of(set2(3)) # should cause no change
793
+ assert_equal(11, set2(3).rgt)
794
+ assert_equal(19, set2(4).rgt)
795
+ assert_nothing_raised {NestedSetWithStringScope.check_all}
796
+ set2(3).move_to_right_of(set2(4))
797
+ assert_equal(19, set2(3).rgt)
798
+ set2(4).move_to_right_of(set2(3))
799
+ assert_equal(4, set2(3).lft)
800
+ assert_nothing_raised {NestedSetWithStringScope.check_all}
801
+ set2(3).move_to_right_of(104) # pass an ID instead
802
+ assert_equal(4, set2(4).lft)
803
+ assert_nothing_raised {NestedSetWithStringScope.check_all}
804
+ end
805
+
806
+ def test_adding_children
807
+ assert(set(1).unknown?)
808
+ assert(set(2).unknown?)
809
+ set(1).add_child set(2)
810
+
811
+ # Did we maintain adding the parent_ids?
812
+ assert(set(1).root?)
813
+ assert(set(2).child?)
814
+ assert(set(2).parent_id == set(1).id)
815
+
816
+ # Check boundaries
817
+ assert_equal(set(1).lft, 1)
818
+ assert_equal(set(2).lft, 2)
819
+ assert_equal(set(2).rgt, 3)
820
+ assert_equal(set(1).rgt, 4)
821
+
822
+ # Check children cound
823
+ assert_equal(set(1).all_children_count, 1)
824
+
825
+ set(1).add_child set(3)
826
+
827
+ #check boundries
828
+ assert_equal(set(1).lft, 1)
829
+ assert_equal(set(2).lft, 2)
830
+ assert_equal(set(2).rgt, 3)
831
+ assert_equal(set(3).lft, 4)
832
+ assert_equal(set(3).rgt, 5)
833
+ assert_equal(set(1).rgt, 6)
834
+
835
+ # How is the count looking?
836
+ assert_equal(set(1).all_children_count, 2)
837
+
838
+ set(2).add_child set(4)
839
+
840
+ # boundries
841
+ assert_equal(set(1).lft, 1)
842
+ assert_equal(set(2).lft, 2)
843
+ assert_equal(set(4).lft, 3)
844
+ assert_equal(set(4).rgt, 4)
845
+ assert_equal(set(2).rgt, 5)
846
+ assert_equal(set(3).lft, 6)
847
+ assert_equal(set(3).rgt, 7)
848
+ assert_equal(set(1).rgt, 8)
849
+
850
+ # Children count
851
+ assert_equal(set(1).all_children_count, 3)
852
+ assert_equal(set(2).all_children_count, 1)
853
+ assert_equal(set(3).all_children_count, 0)
854
+ assert_equal(set(4).all_children_count, 0)
855
+
856
+ set(2).add_child set(5)
857
+ set(4).add_child set(6)
858
+
859
+ assert_equal(set(2).all_children_count, 3)
860
+
861
+ # Children accessors
862
+ assert_equal(set(1).full_set.length, 6)
863
+ assert_equal(set(2).full_set.length, 4)
864
+ assert_equal(set(4).full_set.length, 2)
865
+
866
+ assert_equal(set(1).all_children.length, 5)
867
+ assert_equal(set(6).all_children.length, 0)
868
+
869
+ assert_equal(set(1).direct_children.length, 2)
870
+
871
+ assert_nothing_raised {NestedSetWithStringScope.check_all}
872
+ end
873
+
874
+ def test_common_usage
875
+ mixins(:set_1).add_child(mixins(:set_2))
876
+ assert_equal(1, mixins(:set_1).direct_children.length)
877
+
878
+ mixins(:set_2).add_child(mixins(:set_3))
879
+ assert_equal(1, mixins(:set_1).direct_children.length)
880
+
881
+ # Local cache is now out of date!
882
+ # Problem: the update_alls update all objects up the tree
883
+ mixins(:set_1).reload
884
+ assert_equal(2, mixins(:set_1).all_children.length)
885
+
886
+ assert_equal(1, mixins(:set_1).lft)
887
+ assert_equal(2, mixins(:set_2).lft)
888
+ assert_equal(3, mixins(:set_3).lft)
889
+ assert_equal(4, mixins(:set_3).rgt)
890
+ assert_equal(5, mixins(:set_2).rgt)
891
+ assert_equal(6, mixins(:set_1).rgt)
892
+ assert(mixins(:set_1).root?)
893
+
894
+ begin
895
+ mixins(:set_4).add_child(mixins(:set_1))
896
+ fail
897
+ rescue
898
+ end
899
+
900
+ assert_equal(2, mixins(:set_1).all_children.length)
901
+ mixins(:set_1).add_child mixins(:set_4)
902
+ assert_equal(3, mixins(:set_1).all_children.length)
903
+ assert_nothing_raised {NestedSetWithStringScope.check_all}
904
+ end
905
+
906
+ def test_move_to_child_of_1
907
+ bill = NestedSetWithStringScope.new(:root_id => 101, :pos => 2)
908
+ assert_raise(ActiveRecord::ActiveRecordError) { bill.move_to_child_of(set2(1)) }
909
+ assert_raise(ActiveRecord::ActiveRecordError) { set2(1).move_to_child_of(set2(1)) }
910
+ assert_raise(ActiveRecord::ActiveRecordError) { set2(4).move_to_child_of(set2(9)) }
911
+ assert bill.save
912
+ assert_nothing_raised {set2(1).reload.check_subtree}
913
+ assert bill.move_to_left_of(set2(3))
914
+ assert_equal set2(1), bill.parent
915
+ assert_equal 4, bill.lft
916
+ assert_equal 5, bill.rgt
917
+ assert_equal 3, set2(2).reload.rgt
918
+ assert_equal 6, set2(3).reload.lft
919
+ assert_equal 22, set2(1).reload.rgt
920
+ assert_nothing_raised {set2(1).reload.check_subtree}
921
+ assert_nothing_raised {NestedSetWithStringScope.check_all}
922
+ set2(9).move_to_child_of(101) # pass an ID instead
923
+ assert set2(1).children.include?(set2(9))
924
+ assert_equal(18, set2(9).lft) # to the right of existing children?
925
+ assert_nothing_raised {NestedSetWithStringScope.check_all}
926
+ end
927
+
928
+ def test_move_to_child_of_2
929
+ bill = NestedSetWithStringScope.new(:root_id => 101)
930
+ assert_nothing_raised {set2(1).check_subtree}
931
+ assert bill.save
932
+ assert bill.move_to_child_of(set2(10))
933
+ assert_equal set2(10), bill.parent
934
+ assert_equal 17, bill.lft
935
+ assert_equal 18, bill.rgt
936
+ assert_equal 16, set2(10).reload.lft
937
+ assert_equal 19, set2(10).reload.rgt
938
+ assert_equal 15, set2(9).reload.lft
939
+ assert_equal 20, set2(9).reload.rgt
940
+ assert_equal 21, set2(4).reload.rgt
941
+ assert_nothing_raised {set2(9).reload.check_subtree}
942
+ assert_nothing_raised {set2(4).reload.check_subtree}
943
+ assert_nothing_raised {set2(1).reload.check_subtree}
944
+ assert_nothing_raised {NestedSetWithStringScope.check_all}
945
+ end
946
+
947
+ def test_move_to_child_of_3
948
+ bill = NestedSetWithStringScope.new(:root_id => 101)
949
+ assert bill.save
950
+ assert bill.move_to_child_of(set2(3))
951
+ assert_equal(11, bill.lft) # to the right of existing children?
952
+ assert_nothing_raised {set2(1).reload.check_subtree}
953
+ assert_nothing_raised {NestedSetWithStringScope.check_all}
954
+ end
955
+
956
+ def test_move_1
957
+ set2(4).move_to_child_of(set2(3))
958
+ assert_equal(set2(3), set2(4).reload.parent)
959
+ assert_equal(1, set2(1).reload.lft)
960
+ assert_equal(20, set2(1).reload.rgt)
961
+ assert_equal(4, set2(3).reload.lft)
962
+ assert_equal(19, set2(3).reload.rgt)
963
+ assert_nothing_raised {set2(1).reload.check_subtree}
964
+ assert_nothing_raised {NestedSetWithStringScope.check_all}
965
+ end
966
+
967
+ def test_move_2
968
+ initial = set2(1).full_set
969
+ assert_raise(ActiveRecord::ActiveRecordError) { set2(3).move_to_child_of(set2(6)) } # can't set a current child as the parent-- creates a loop
970
+ assert_raise(ActiveRecord::ActiveRecordError) { set2(3).move_to_child_of(set2(3)) }
971
+ set2(2).move_to_child_of(set2(5))
972
+ set2(4).move_to_child_of(set2(2))
973
+ set2(10).move_to_right_of(set2(3))
974
+
975
+ assert_equal 105, set2(2).parent_id
976
+ assert_equal 102, set2(4).parent_id
977
+ assert_equal 101, set2(10).parent_id
978
+ set2(3).reload
979
+ set2(10).reload
980
+ assert_equal 19, set2(10).rgt
981
+ assert_equal 17, set2(3).rgt
982
+ assert_equal 2, set2(3).lft
983
+ set2(1).reload
984
+ assert_nothing_raised {set2(1).check_subtree}
985
+ set2(4).move_to_right_of(set2(3))
986
+ set2(10).move_to_child_of(set2(9))
987
+ set2(2).move_to_left_of(set2(3))
988
+
989
+ # now everything should be back where it started-- check against initial
990
+ final = set2(1).reload.full_set
991
+ assert_equal(initial, final)
992
+ for i in 0..9
993
+ assert_equal(initial[i]['parent_id'], final[i]['parent_id'])
994
+ assert_equal(initial[i]['lft'], final[i]['lft'])
995
+ assert_equal(initial[i]['rgt'], final[i]['rgt'])
996
+ end
997
+ assert_nothing_raised {NestedSetWithStringScope.check_all}
998
+ end
999
+
1000
+ def test_scope_enforcement # prevent moves between trees
1001
+ assert_raise(ActiveRecord::ActiveRecordError) { set(3).move_to_child_of(set2(6)) }
1002
+ ns = NestedSetWithStringScope.create(:root_id => 214)
1003
+ assert_raise(ActiveRecord::ActiveRecordError) { ns.move_to_child_of(set2(1)) }
1004
+ end
1005
+
1006
+ ##########################################
1007
+ # ACTS_AS_LIST-LIKE BEHAVIOUR TESTS
1008
+ ##########################################
1009
+
1010
+ def test_swap
1011
+ set2(5).swap(set2(7))
1012
+ assert_equal [107, 106, 105], set2(3).children.map(&:id)
1013
+ assert_nothing_raised {set2(3).check_full_tree}
1014
+ assert_raise(ActiveRecord::ActiveRecordError) { set2(3).swap(set2(10)) } # isn't a sibling...
1015
+ end
1016
+
1017
+ def test_insert_at
1018
+ child = NestedSetWithStringScope.create(:root_id => 101)
1019
+ child.insert_at(set2(3), :last)
1020
+ assert_equal child, set2(3).children.last
1021
+
1022
+ child = NestedSetWithStringScope.create(:root_id => 101)
1023
+ child.insert_at(set2(3), :first)
1024
+ assert_equal child, set2(3).children.first
1025
+
1026
+ child = NestedSetWithStringScope.create(:root_id => 101)
1027
+ child.insert_at(set2(3), 2)
1028
+ assert_equal child, set2(3).children[2]
1029
+
1030
+ child = NestedSetWithStringScope.create(:root_id => 101)
1031
+ child.insert_at(set2(3), 1000)
1032
+ assert_equal child, set2(3).children.last
1033
+
1034
+ child = NestedSetWithStringScope.create(:root_id => 101)
1035
+ child.insert_at(set2(3), 1)
1036
+ assert_equal child, set2(3).children[1]
1037
+ end
1038
+
1039
+ def test_move_higher
1040
+ set2(7).move_higher
1041
+ assert_equal [105, 107, 106], set2(3).children.map(&:id)
1042
+ set2(7).move_higher
1043
+ assert_equal [107, 105, 106], set2(3).children.map(&:id)
1044
+ set2(7).move_higher
1045
+ assert_equal [107, 105, 106], set2(3).children.map(&:id)
1046
+ end
1047
+
1048
+ def test_move_lower
1049
+ set2(5).move_lower
1050
+ assert_equal [106, 105, 107], set2(3).children.map(&:id)
1051
+ set2(5).move_lower
1052
+ assert_equal [106, 107, 105], set2(3).children.map(&:id)
1053
+ set2(5).move_lower
1054
+ assert_equal [106, 107, 105], set2(3).children.map(&:id)
1055
+ end
1056
+
1057
+ def test_move_to_top
1058
+ set2(7).move_to_top
1059
+ assert_equal [107, 105, 106], set2(3).children.map(&:id)
1060
+ end
1061
+
1062
+ def test_move_to_bottom
1063
+ set2(5).move_to_bottom
1064
+ assert_equal [106, 107, 105], set2(3).children.map(&:id)
1065
+ end
1066
+
1067
+ def test_move_to_position
1068
+ set2(7).move_to_position(:first)
1069
+ assert_equal [107, 105, 106], set2(3).children.map(&:id)
1070
+ set2(7).move_to_position(:last)
1071
+ assert_equal [105, 106, 107], set2(3).children.map(&:id)
1072
+ end
1073
+
1074
+ def test_move_to_position_limits
1075
+ set2(7).move_to_position(0)
1076
+ assert_equal [107, 105, 106], set2(3).children.map(&:id)
1077
+ set2(7).move_to_position(100)
1078
+ assert_equal [105, 106, 107], set2(3).children.map(&:id)
1079
+ end
1080
+
1081
+ def test_move_to_position_index
1082
+ set2(7).move_to_position(0)
1083
+ assert_equal [107, 105, 106], set2(3).children.map(&:id)
1084
+ set2(7).move_to_position(1)
1085
+ assert_equal [105, 107, 106], set2(3).children.map(&:id)
1086
+ set2(7).move_to_position(2)
1087
+ assert_equal [105, 106, 107], set2(3).children.map(&:id)
1088
+ set2(5).move_to_position(2)
1089
+ assert_equal [106, 107, 105], set2(3).children.map(&:id)
1090
+ end
1091
+
1092
+ def test_scoped_move_to_position
1093
+ set2(7).move_to_position(0, :conditions => { :id => [105, 106, 107] })
1094
+ assert_equal [107, 105, 106], set2(3).children.map(&:id)
1095
+ set2(7).move_to_position(1, :conditions => { :id => [105, 107] })
1096
+ assert_equal [105, 107, 106], set2(3).children.map(&:id)
1097
+ set2(7).move_to_position(1, :conditions => { :id => [106, 107] })
1098
+ assert_equal [105, 106, 107], set2(3).children.map(&:id)
1099
+ end
1100
+
1101
+ def test_reorder_children
1102
+ assert_equal [105], set2(3).reorder_children(107, 106, 105).map(&:id)
1103
+ assert_equal [107, 106, 105], set2(3).children.map(&:id)
1104
+ assert_equal [107, 106], set2(3).reorder_children(106, 105, 107).map(&:id)
1105
+ assert_equal [106, 105, 107], set2(3).children.map(&:id)
1106
+ end
1107
+
1108
+ def test_reorder_children_with_random_samples
1109
+ 10.times do
1110
+ child = NestedSetWithStringScope.create(:root_id => 101)
1111
+ child.move_to_child_of set2(3)
1112
+ end
1113
+ ordered_ids = set2(3).children.map(&:id).sort_by { rand }
1114
+ set2(3).reorder_children(ordered_ids)
1115
+ assert_equal ordered_ids, set2(3).children.map(&:id)
1116
+ end
1117
+
1118
+ def test_reorder_children_with_partial_id_set
1119
+ 10.times do
1120
+ child = NestedSetWithStringScope.create(:root_id => 101)
1121
+ child.move_to_child_of set2(3)
1122
+ end
1123
+ child_ids = set2(3).children.map(&:id)
1124
+ set2(3).reorder_children(child_ids.last, child_ids.first)
1125
+ ordered_ids = set2(3).children.map(&:id)
1126
+ assert_equal ordered_ids.first, child_ids.last
1127
+ assert_equal ordered_ids.last, child_ids.first
1128
+ assert_equal child_ids[1, -2], ordered_ids[1, -2]
1129
+ end
1130
+
1131
+ ##########################################
1132
+ # RENUMBERING TESTS
1133
+ ##########################################
1134
+ # see also class method tests of renumber_all
1135
+ def test_renumber_full_tree_1
1136
+ NestedSetWithStringScope.update_all("lft = NULL, rgt = NULL", "root_id = 101")
1137
+ assert_raise(ActiveRecord::ActiveRecordError) {set2(1).check_full_tree}
1138
+ set2(1).renumber_full_tree
1139
+ set2(1).reload
1140
+ assert_equal 1, set2(1).lft
1141
+ assert_equal 20, set2(1).rgt
1142
+ assert_equal 4, set2(3).lft
1143
+ assert_equal 11, set2(3).rgt
1144
+ assert_nothing_raised {NestedSetWithStringScope.check_all}
1145
+ end
1146
+
1147
+ def test_renumber_full_tree_2
1148
+ NestedSetWithStringScope.update_all("lft = lft + 1, rgt = rgt + 1", "root_id = 101")
1149
+ assert_raise(ActiveRecord::ActiveRecordError) {NestedSetWithStringScope.check_all}
1150
+ set2(1).renumber_full_tree
1151
+ assert_nothing_raised(ActiveRecord::ActiveRecordError) {NestedSetWithStringScope.check_all}
1152
+ NestedSetWithStringScope.update_all("rgt = 12", "id = 108")
1153
+ assert_raise(ActiveRecord::ActiveRecordError) {set2(8).check_subtree}
1154
+ set2(8).renumber_full_tree
1155
+ assert_nothing_raised(ActiveRecord::ActiveRecordError) {NestedSetWithStringScope.check_all}
1156
+ end
1157
+
1158
+
1159
+ ##########################################
1160
+ # CONCURRENCY TESTS
1161
+ ##########################################
1162
+ # what happens when multiple objects are being manipulated at the same time?
1163
+ def test_concurrent_save
1164
+ c1, c2, c3 = Category.create, Category.create, Category.create
1165
+ c1.move_to_right_of(c3)
1166
+ c2.save
1167
+ assert_nothing_raised {Category.check_all}
1168
+
1169
+ ns1 = set2(3)
1170
+ ns2 = set2(4)
1171
+ ns2.move_to_left_of(102) # ns1 is now out-of-date
1172
+ ns1.save
1173
+ assert_nothing_raised {set2(1).check_subtree}
1174
+ end
1175
+
1176
+ def test_concurrent_add_add
1177
+ c1 = Category.new
1178
+ c2 = Category.new
1179
+ c1.save
1180
+ c2.save
1181
+ c3 = Category.new
1182
+ c4 = Category.new
1183
+ c4.save # now in the opposite order
1184
+ c3.save
1185
+ assert_nothing_raised {Category.check_all}
1186
+ end
1187
+
1188
+ def test_concurrent_add_delete
1189
+ ns = set2(3)
1190
+ new_ns = NestedSetWithStringScope.create(:root_id => 101)
1191
+ ns.destroy
1192
+ assert_nothing_raised {NestedSetWithStringScope.check_all}
1193
+ end
1194
+
1195
+ def test_concurrent_add_move
1196
+ ns = set2(3)
1197
+ new_ns = NestedSetWithStringScope.create(:root_id => 101)
1198
+ ns.move_to_left_of(102)
1199
+ assert_nothing_raised {NestedSetWithStringScope.check_all}
1200
+ end
1201
+
1202
+ def test_concurrent_delete_add
1203
+ ns = set2(3)
1204
+ new_ns = NestedSetWithStringScope.new(:root_id => 101)
1205
+ ns.destroy
1206
+ new_ns.save
1207
+ assert_nothing_raised {NestedSetWithStringScope.check_all}
1208
+ end
1209
+
1210
+ def test_concurrent_delete_delete
1211
+ ns1 = set2(3)
1212
+ ns2 = set2(4)
1213
+ ns1.destroy
1214
+ ns2.destroy
1215
+ assert_nothing_raised {NestedSetWithStringScope.check_all}
1216
+ end
1217
+
1218
+ def test_concurrent_delete_move
1219
+ ns1 = set2(3)
1220
+ ns2 = set2(4)
1221
+ ns1.destroy
1222
+ ns2.move_to_left_of(102)
1223
+ assert_nothing_raised {NestedSetWithStringScope.check_all}
1224
+ end
1225
+
1226
+ def test_concurrent_move_add
1227
+ ns = set2(3)
1228
+ new_ns = NestedSetWithStringScope.new(:root_id => 101)
1229
+ ns.move_to_left_of(102)
1230
+ new_ns.save
1231
+ assert_nothing_raised {NestedSetWithStringScope.check_all}
1232
+ end
1233
+
1234
+ def test_concurrent_move_delete
1235
+ ns1 = set2(3)
1236
+ ns2 = set2(4)
1237
+ ns2.move_to_left_of(102)
1238
+ ns1.destroy
1239
+ assert_nothing_raised {NestedSetWithStringScope.check_all}
1240
+ end
1241
+
1242
+ def test_concurrent_move_move
1243
+ ns1 = set2(3)
1244
+ ns2 = set2(4)
1245
+ ns1.move_to_left_of(102)
1246
+ ns2.move_to_child_of(102)
1247
+ assert_nothing_raised {NestedSetWithStringScope.check_all}
1248
+ end
1249
+
1250
+ ##########################################
1251
+ # CALLBACK TESTS
1252
+ ##########################################
1253
+ # Because the nested set code relies heavily on callbacks, we
1254
+ # want to ensure that we aren't causing problems for user-defined callbacks
1255
+ def test_callbacks
1256
+ # 1) Do all user-defined callbacks work?
1257
+ $callbacks = []
1258
+ ns = NS2.new(:root_id => 101) # NS2 adds symbols to $callbacks when the callbacks fire
1259
+ assert_equal([], $callbacks)
1260
+ ns.save!
1261
+ assert_equal([:before_save, :before_create, :after_create, :after_save], $callbacks)
1262
+ $callbacks = []
1263
+ ns.pos = 2
1264
+ ns.save!
1265
+ assert_equal([:before_save, :before_update, :after_update, :after_save], $callbacks)
1266
+ $callbacks = []
1267
+ ns.destroy
1268
+ assert_equal([:before_destroy, :after_destroy], $callbacks)
1269
+ end
1270
+
1271
+ def test_callbacks2
1272
+ # 2) Do our callbacks still work, even when a programmer defines
1273
+ # their own callbacks in the overwriteable style?
1274
+ # (the NS2 model defines callbacks in the overwritable style)
1275
+ ns = NS2.create(:root_id => 101)
1276
+ assert ns.lft != nil && ns.rgt != nil
1277
+ child_ns = NS2.create(:root_id => 101)
1278
+ child_ns.move_to_child_of(ns)
1279
+ id = child_ns.id
1280
+ ns.destroy
1281
+ assert_equal(nil, NS2.find(:first, :conditions => "id = #{id}"))
1282
+ # lots of implicit testing occurs in other test methods
1283
+ end
1284
+
1285
+ ##########################################
1286
+ # BUG-SPECIFIC TESTS
1287
+ ##########################################
1288
+ def test_ticket_17
1289
+ main = Category.new
1290
+ main.save
1291
+ sub = Category.new
1292
+ sub.save
1293
+ sub.move_to_child_of main
1294
+ sub.save
1295
+ main.save
1296
+
1297
+ assert_equal(1, main.all_children_count)
1298
+ assert_equal([main, sub], main.full_set)
1299
+ assert_equal([sub], main.all_children)
1300
+
1301
+ assert_equal(1, main.lft)
1302
+ assert_equal(2, sub.lft)
1303
+ assert_equal(3, sub.rgt)
1304
+ assert_equal(4, main.rgt)
1305
+ end
1306
+
1307
+ def test_ticket_19
1308
+ # this test currently relies on the fact that objects are reloaded at the beginning of the move_to methods
1309
+ root = Category.create
1310
+ first = Category.create
1311
+ second = Category.create
1312
+ first.move_to_child_of(root)
1313
+ second.move_to_child_of(root)
1314
+
1315
+ # now we should have the situation described in the ticket
1316
+ assert_nothing_raised {first.move_to_child_of(second)}
1317
+ assert_raise(ActiveRecord::ActiveRecordError) {second.move_to_child_of(first)} # try illegal move
1318
+ first.move_to_child_of(root) # move it back
1319
+ assert_nothing_raised {first.move_to_child_of(second)} # try it the other way-- first is now on the other side of second
1320
+ assert_nothing_raised {Category.check_all}
1321
+ end
1322
+
1323
+ # Note that single-table inheritance recieves extensive implicit testing,
1324
+ # because one of the fixture trees contains a hodge-podge of classes.
1325
+ def test_ticket_10
1326
+ assert_equal(9, set2(1).all_children.size)
1327
+ NS2.find(103).move_to_right_of(104)
1328
+ assert_equal(4, set2(4).lft)
1329
+ assert_equal(10, set2(9).rgt)
1330
+ NS2.find(103).destroy
1331
+ assert_equal(12, set2(1).rgt)
1332
+ assert_equal(6, NestedSetWithStringScope.count(:conditions => "root_id = 101"))
1333
+ assert_nothing_raised {NestedSetWithStringScope.check_all}
1334
+ end
1335
+
1336
+ # the next virtual root was not starting with the correct lft value
1337
+ def test_ticket_29
1338
+ first = Category.create
1339
+ second = Category.create
1340
+ Category.renumber_all
1341
+ second.reload
1342
+ assert_equal(3, second.lft)
1343
+ end
1344
+
1345
+ end
1346
+
1347
+
1348
+
1349
+ ###################################################################
1350
+ ## Tests that don't pass yet or haven't been finished
1351
+
1352
+ ## make #destroy set left & rgt to nil?
1353
+
1354
+ #def test_find_insertion_point
1355
+ # bill = NestedSetWithStringScope.create(:pos => 2, :root_id => 101)
1356
+ # assert_equal 3, bill.find_insertion_point(set2(1))
1357
+ # assert_equal 4, bill.find_insertion_point(set2(3))
1358
+ # aalfred = NestedSetWithStringScope.create(:pos => 0, :root_id => 101)
1359
+ # assert_equal 1, aalfred.find_insertion_point(set2(1))
1360
+ # assert_equal 2, aalfred.find_insertion_point(set2(2))
1361
+ # assert_equal 12, aalfred.find_insertion_point(set2(4))
1362
+ # zed = NestedSetWithStringScope.create(:pos => 99, :root_id => 101)
1363
+ # assert_equal 19, zed.find_insertion_point(set2(1))
1364
+ # assert_equal 17, zed.find_insertion_point(set2(9))
1365
+ # assert_equal 16, zed.find_insertion_point(set2(10))
1366
+ # assert_equal 10, set2(4).find_insertion_point(set2(3))
1367
+ #end
1368
+ #