treequel 1.0.4 → 1.1.0

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.
Files changed (53) hide show
  1. data/ChangeLog +130 -1
  2. data/Rakefile +8 -3
  3. data/Rakefile.local +2 -0
  4. data/bin/treeirb +6 -2
  5. data/bin/treequel +5 -4
  6. data/lib/treequel/branch.rb +133 -72
  7. data/lib/treequel/branchcollection.rb +16 -5
  8. data/lib/treequel/branchset.rb +37 -6
  9. data/lib/treequel/constants.rb +12 -0
  10. data/lib/treequel/directory.rb +96 -41
  11. data/lib/treequel/filter.rb +42 -1
  12. data/lib/treequel/model/objectclass.rb +111 -0
  13. data/lib/treequel/model.rb +363 -0
  14. data/lib/treequel/monkeypatches.rb +65 -0
  15. data/lib/treequel/schema/attributetype.rb +108 -18
  16. data/lib/treequel/schema/ldapsyntax.rb +15 -0
  17. data/lib/treequel/schema/matchingrule.rb +24 -0
  18. data/lib/treequel/schema/matchingruleuse.rb +24 -0
  19. data/lib/treequel/schema/objectclass.rb +70 -5
  20. data/lib/treequel/schema/table.rb +5 -15
  21. data/lib/treequel/schema.rb +64 -1
  22. data/lib/treequel.rb +5 -5
  23. data/rake/documentation.rb +27 -0
  24. data/rake/hg.rb +14 -2
  25. data/rake/manual.rb +1 -1
  26. data/spec/lib/constants.rb +9 -7
  27. data/spec/lib/control_behavior.rb +1 -0
  28. data/spec/lib/matchers.rb +1 -0
  29. data/spec/treequel/branch_spec.rb +229 -20
  30. data/spec/treequel/branchcollection_spec.rb +73 -39
  31. data/spec/treequel/branchset_spec.rb +59 -8
  32. data/spec/treequel/control_spec.rb +1 -0
  33. data/spec/treequel/controls/contentsync_spec.rb +1 -0
  34. data/spec/treequel/controls/pagedresults_spec.rb +1 -0
  35. data/spec/treequel/controls/sortedresults_spec.rb +1 -0
  36. data/spec/treequel/directory_spec.rb +46 -10
  37. data/spec/treequel/filter_spec.rb +28 -5
  38. data/spec/treequel/mixins_spec.rb +7 -14
  39. data/spec/treequel/model/objectclass_spec.rb +330 -0
  40. data/spec/treequel/model_spec.rb +433 -0
  41. data/spec/treequel/monkeypatches_spec.rb +118 -0
  42. data/spec/treequel/schema/attributetype_spec.rb +116 -0
  43. data/spec/treequel/schema/ldapsyntax_spec.rb +8 -0
  44. data/spec/treequel/schema/matchingrule_spec.rb +6 -1
  45. data/spec/treequel/schema/matchingruleuse_spec.rb +5 -0
  46. data/spec/treequel/schema/objectclass_spec.rb +41 -3
  47. data/spec/treequel/schema/table_spec.rb +31 -18
  48. data/spec/treequel/schema_spec.rb +13 -16
  49. data/spec/treequel_spec.rb +22 -0
  50. data.tar.gz.sig +1 -0
  51. metadata +40 -7
  52. metadata.gz.sig +0 -0
  53. data/spec/treequel/utils_spec.rb +0 -49
@@ -6,6 +6,7 @@ BEGIN {
6
6
 
7
7
  libdir = basedir + "lib"
8
8
 
9
+ $LOAD_PATH.unshift( basedir ) unless $LOAD_PATH.include?( basedir )
9
10
  $LOAD_PATH.unshift( libdir ) unless $LOAD_PATH.include?( libdir )
10
11
  }
11
12
 
@@ -29,6 +30,7 @@ describe Treequel::Branch do
29
30
  include Treequel::SpecHelpers,
30
31
  Treequel::Matchers
31
32
 
33
+
32
34
  before( :all ) do
33
35
  setup_logging( :fatal )
34
36
  end
@@ -68,6 +70,12 @@ describe Treequel::Branch do
68
70
  branch.entry.should == entry
69
71
  end
70
72
 
73
+ it "raises an exception if constructed with something other than a Hash entry" do
74
+ expect {
75
+ Treequel::Branch.new( @directory, TEST_PEOPLE_DN, 18 )
76
+ }.to raise_exception( ArgumentError, /can't cast/i )
77
+ end
78
+
71
79
  it "can be configured to include operational attributes for all future instances" do
72
80
  Treequel::Branch.include_operational_attrs = false
73
81
  Treequel::Branch.new( @directory, TEST_PEOPLE_DN ).include_operational_attrs?.should be_false
@@ -89,7 +97,8 @@ describe Treequel::Branch do
89
97
  @schema.stub!( :attribute_types ).
90
98
  and_return({ :cn => :a_value, :ou => :a_value })
91
99
 
92
- @attribute_type = mock( "schema attribute type object" )
100
+ @syntax = stub( "attribute ldapSyntax object", :oid => OIDS::STRING_SYNTAX )
101
+ @attribute_type = mock( "schema attribute type object", :syntax => @syntax )
93
102
  end
94
103
 
95
104
 
@@ -110,6 +119,11 @@ describe Treequel::Branch do
110
119
  @branch.split_dn( 2 ).should include( TEST_HOSTS_RDN, TEST_BASE_DN )
111
120
  end
112
121
 
122
+ it "can return itself as an ldap:// URI" do
123
+ @directory.should_receive( :uri ).and_return( URI.parse("ldap://#{TEST_HOST}/#{TEST_BASE_DN}") )
124
+ @branch.uri.to_s.should == "ldap://#{TEST_HOST}/#{TEST_HOSTS_DN}?"
125
+ end
126
+
113
127
  it "are Comparable if they are siblings" do
114
128
  sibling = Treequel::Branch.new( @directory, TEST_PEOPLE_DN )
115
129
 
@@ -182,6 +196,19 @@ describe Treequel::Branch do
182
196
  end
183
197
 
184
198
 
199
+ it "can fetch a child entry by RDN" do
200
+ res = @branch.get_child( 'cn=surprise' )
201
+ res.should be_a( Treequel::Branch )
202
+ res.dn.should == [ 'cn=surprise', @branch.dn ].join( ',' )
203
+ end
204
+
205
+ it "can fetch a child entry by RDN if its DN is the empty String" do
206
+ @branch.dn = ''
207
+ res = @branch.get_child( 'cn=surprise' )
208
+ res.should be_a( Treequel::Branch )
209
+ res.dn.should == 'cn=surprise'
210
+ end
211
+
185
212
  it "create sub-branches for messages that match valid attributeType OIDs" do
186
213
  @schema.should_receive( :attribute_types ).twice.
187
214
  and_return({ :cn => :a_value, :ou => :a_value })
@@ -287,17 +314,19 @@ describe Treequel::Branch do
287
314
 
288
315
  it "knows which objectClasses it has" do
289
316
  oc_attr = mock( "objectClass attributeType object" )
317
+ string_syntax = stub( "string ldapSyntax object", :oid => OIDS::STRING_SYNTAX )
290
318
  @schema.should_receive( :attribute_types ).and_return({ :objectClass => oc_attr })
291
319
  oc_attr.should_receive( :single? ).and_return( false )
292
- oc_attr.should_receive( :syntax_oid ).and_return( OIDS::STRING_SYNTAX )
320
+ oc_attr.should_receive( :syntax ).at_least( :once ).
321
+ and_return( string_syntax )
293
322
 
294
323
  @entry.should_receive( :[] ).with( 'objectClass' ).at_least( :once ).
295
324
  and_return([ 'organizationalUnit', 'extensibleObject' ])
296
325
 
297
- @directory.should_receive( :convert_syntax_value ).
326
+ @directory.should_receive( :convert_to_object ).
298
327
  with( OIDS::STRING_SYNTAX, 'organizationalUnit' ).
299
328
  and_return( 'organizationalUnit' )
300
- @directory.should_receive( :convert_syntax_value ).
329
+ @directory.should_receive( :convert_to_object ).
301
330
  with( OIDS::STRING_SYNTAX, 'extensibleObject' ).
302
331
  and_return( 'extensibleObject' )
303
332
 
@@ -309,6 +338,42 @@ describe Treequel::Branch do
309
338
  @branch.object_classes.should == [ :ou_objectclass, :extobj_objectclass ]
310
339
  end
311
340
 
341
+ it "knows what operational attributes it has" do
342
+ op_attrs = MINIMAL_OPERATIONAL_ATTRIBUTES.inject({}) do |hash, oa|
343
+ hash[ oa ] = mock("#{oa} attributeType object")
344
+ hash
345
+ end
346
+ @schema.should_receive( :operational_attribute_types ).and_return( op_attrs.values )
347
+
348
+ @branch.operational_attribute_types.should == op_attrs.values
349
+ end
350
+
351
+ it "knows what the OIDs of its operational attributes are" do
352
+ op_attrs = MINIMAL_OPERATIONAL_ATTRIBUTES.inject({}) do |hash, oa|
353
+ hash[ oa ] = stub("#{oa} attributeType object", :oid => :an_oid )
354
+ hash
355
+ end
356
+ @schema.should_receive( :operational_attribute_types ).at_least( :once ).
357
+ and_return( op_attrs.values )
358
+
359
+ @branch.operational_attribute_oids.should have( op_attrs.length ).members
360
+ @branch.operational_attribute_oids.should include( :an_oid )
361
+ end
362
+
363
+ it "can return the set of all its MUST attributes' OIDs based on which objectClasses " +
364
+ "it has" do
365
+ oc1 = mock( "first objectclass" )
366
+ oc2 = mock( "second objectclass" )
367
+
368
+ @branch.should_receive( :object_classes ).and_return([ oc1, oc2 ])
369
+ oc1.should_receive( :must_oids ).at_least( :once ).and_return([ :oid1, :oid2 ])
370
+ oc2.should_receive( :must_oids ).at_least( :once ).and_return([ :oid1, :oid3 ])
371
+
372
+ must_oids = @branch.must_oids
373
+ must_oids.should have( 3 ).members
374
+ must_oids.should include( :oid1, :oid2, :oid3 )
375
+ end
376
+
312
377
  it "can return the set of all its MUST attributeTypes based on which objectClasses it has" do
313
378
  oc1 = mock( "first objectclass" )
314
379
  oc2 = mock( "second objectclass" )
@@ -323,7 +388,7 @@ describe Treequel::Branch do
323
388
  end
324
389
 
325
390
  it "can return a Hash pre-populated with pairs that correspond to its MUST attributes" do
326
- cn_attrtype = mock( "cn attribute type", :single? => true )
391
+ cn_attrtype = mock( "cn attribute type", :single? => false )
327
392
  l_attrtype = mock( "l attribute type", :single? => true )
328
393
  objectClass_attrtype = mock( "objectClass attribute type", :single? => false )
329
394
 
@@ -335,10 +400,24 @@ describe Treequel::Branch do
335
400
  and_return([ cn_attrtype, l_attrtype, objectClass_attrtype ])
336
401
 
337
402
  @branch.must_attributes_hash.
338
- should == { :cn => '', :l => '', :objectClass => [:top] }
403
+ should == { :cn => [''], :l => '', :objectClass => [:top] }
339
404
  end
340
405
 
341
406
 
407
+ it "can return the set of all its MAY attributes' OIDs based on which objectClasses " +
408
+ "it has" do
409
+ oc1 = mock( "first objectclass" )
410
+ oc2 = mock( "second objectclass" )
411
+
412
+ @branch.should_receive( :object_classes ).and_return([ oc1, oc2 ])
413
+ oc1.should_receive( :may_oids ).at_least( :once ).and_return([ :oid1, :oid2 ])
414
+ oc2.should_receive( :may_oids ).at_least( :once ).and_return([ :oid1, :oid3 ])
415
+
416
+ must_oids = @branch.may_oids
417
+ must_oids.should have( 3 ).members
418
+ must_oids.should include( :oid1, :oid2, :oid3 )
419
+ end
420
+
342
421
  it "can return the set of all its MAY attributeTypes based on which objectClasses it has" do
343
422
  oc1 = mock( "first objectclass" )
344
423
  oc2 = mock( "second objectclass" )
@@ -352,20 +431,67 @@ describe Treequel::Branch do
352
431
  must_attrs.should include( :description, :mobilePhone, :chunktype )
353
432
  end
354
433
 
434
+ it "can return a Hash pre-populated with pairs that correspond to its MAY attributes" do
435
+ cn_attrtype = mock( "cn attribute type", :single? => false )
436
+ l_attrtype = mock( "l attribute type", :single? => true )
437
+
438
+ cn_attrtype.should_receive( :name ).at_least( :once ).and_return( :cn )
439
+ l_attrtype.should_receive( :name ).at_least( :once ).and_return( :l )
440
+
441
+ @branch.should_receive( :may_attribute_types ).at_least( :once ).
442
+ and_return([ cn_attrtype, l_attrtype ])
443
+
444
+ @branch.may_attributes_hash.
445
+ should == { :cn => [], :l => nil }
446
+ end
447
+
355
448
  it "can return the set of all of its valid attributeTypes, which is a union of its " +
356
- "MUST and MAY attributes" do
449
+ "MUST and MAY attributes plus the directory's operational attributes" do
357
450
  @branch.should_receive( :must_attribute_types ).
358
451
  and_return([ :cn, :l, :uid ])
359
452
  @branch.should_receive( :may_attribute_types ).
360
453
  and_return([ :description, :mobilePhone, :chunktype ])
454
+ @branch.should_receive( :operational_attribute_types ).
455
+ and_return([ :createTimestamp, :creatorsName ])
361
456
 
362
457
  all_attrs = @branch.valid_attribute_types
363
458
 
364
- all_attrs.should have( 6 ).members
365
- all_attrs.should include( :cn, :uid, :l, :description, :mobilePhone, :chunktype )
459
+ all_attrs.should have( 8 ).members
460
+ all_attrs.should include( :cn, :uid, :l, :description, :mobilePhone,
461
+ :chunktype, :createTimestamp, :creatorsName )
462
+ end
463
+
464
+ it "can return a Hash pre-populated with pairs that correspond to all of its valid " +
465
+ "attributes" do
466
+ @branch.should_receive( :must_attributes_hash ).at_least( :once ).
467
+ and_return({ :cn => [''], :l => '', :objectClass => [:top] })
468
+ @branch.should_receive( :may_attributes_hash ).at_least( :once ).
469
+ and_return({ :description => nil, :givenName => [], :cn => nil })
470
+
471
+ @branch.valid_attributes_hash.should == {
472
+ :cn => [''],
473
+ :l => '',
474
+ :objectClass => [:top],
475
+ :description => nil,
476
+ :givenName => [],
477
+ }
478
+ end
479
+
480
+
481
+ it "can return the set of all of its valid attribute OIDs, which is a union of its " +
482
+ "MUST and MAY attribute OIDs" do
483
+ @branch.should_receive( :must_oids ).
484
+ and_return([ :must_oid1, :must_oid2 ])
485
+ @branch.should_receive( :may_oids ).
486
+ and_return([ :may_oid1, :may_oid2, :must_oid1 ])
487
+
488
+ all_attr_oids = @branch.valid_attribute_oids
489
+
490
+ all_attr_oids.should have( 4 ).members
491
+ all_attr_oids.should include( :must_oid1, :must_oid2, :may_oid1, :may_oid2 )
366
492
  end
367
493
 
368
- it "knows if a attribute is valid given its objectClasses" do
494
+ it "knows if an attribute is valid given its objectClasses" do
369
495
  attrtype = mock( "attribute type object" )
370
496
 
371
497
  @branch.should_receive( :valid_attribute_types ).
@@ -487,6 +613,12 @@ describe Treequel::Branch do
487
613
  @branch.delete( :objectClass => ['apple-user',:inetOrgPerson], :cn => [] )
488
614
  end
489
615
 
616
+ it "deletes its entry entirely if no attributes are specified" do
617
+ @directory.should_receive( :delete ).with( @branch )
618
+ @branch.delete
619
+ end
620
+
621
+
490
622
  it "knows how to represent its DN as an RFC1781-style UFN" do
491
623
  @branch.to_ufn.should =~ /Hosts, acme\.com/i
492
624
  end
@@ -505,6 +637,82 @@ describe Treequel::Branch do
505
637
  end
506
638
 
507
639
 
640
+ LONG_TEST_VALUE = 'A poet once said, "The whole universe is in ' +
641
+ 'a glass of wine." We will probably never know in what sense ' +
642
+ 'he meant that, for poets do not write to be understood. But ' +
643
+ 'it is true that if we look at a glass of wine closely enough ' +
644
+ 'we see the entire universe.'
645
+
646
+ it "knows how to split long lines in LDIF output" do
647
+ @entry.should_receive( :keys ).and_return([ 'description', 'l' ])
648
+ @entry.should_receive( :[] ).with( 'description' ).
649
+ and_return([ LONG_TEST_VALUE ])
650
+ @entry.should_receive( :[] ).with( 'l' ).
651
+ and_return([ 'Antartica', 'Galapagos' ])
652
+
653
+ ldif = @branch.to_ldif( 20 )
654
+ val = ldif[ /(description: (?:[^\n]|\n )+)/, 1 ]
655
+ lines = val.split( /\n/ )
656
+
657
+ lines.first.should =~ /.{20}/
658
+ lines[1..-2].each do |line|
659
+ line.should =~ / .{19}/
660
+ end
661
+ lines.last.should =~ / .{1,19}/
662
+ end
663
+
664
+
665
+ LONG_BINARY_TEST_VALUE = ( <<-END_VALUE ).gsub( /^\t{2}/, '' )
666
+ Once there came a man
667
+ Who said,
668
+ "Range me all men of the world in rows."
669
+ And instantly
670
+ There was terrific clamour among the people
671
+ Against being ranged in rows.
672
+ There was a loud quarrel, world-wide.
673
+ It endured for ages;
674
+ And blood was shed
675
+ By those who would not stand in rows,
676
+ And by those who pined to stand in rows.
677
+ Eventually, the man went to death, weeping.
678
+ And those who staid in bloody scuffle
679
+ Knew not the great simplicity.
680
+ END_VALUE
681
+
682
+ it "knows how to split long binary lines in LDIF output" do
683
+ @entry.should_receive( :keys ).and_return([ 'description', 'l' ])
684
+ @entry.should_receive( :[] ).with( 'description' ).
685
+ and_return([ LONG_BINARY_TEST_VALUE ])
686
+ @entry.should_receive( :[] ).with( 'l' ).
687
+ and_return([ 'Antartica', 'Galapagos' ])
688
+
689
+ ldif = @branch.to_ldif( 20 )
690
+ ldif.scan( /^description/ ).length.should == 1
691
+
692
+ val = ldif[ /^(description:: (?:[^\n]|\n )+)/, 1 ]
693
+ lines = val.split( /\n/ )
694
+ lines.first.should =~ /.{20}/
695
+ lines[1..-2].each do |line|
696
+ line.should =~ / .{19}/
697
+ end
698
+ lines.last.should =~ / .{1,19}/
699
+ end
700
+
701
+
702
+ it "knows how to represent itself as a Hash" do
703
+ @entry.should_receive( :keys ).and_return([ 'description', 'dn', 'l' ])
704
+ @entry.should_receive( :[] ).with( 'description' ).
705
+ and_return([ 'A chilly little penguin.' ])
706
+ @entry.should_receive( :[] ).with( 'l' ).
707
+ and_return([ 'Antartica', 'Galapagos' ])
708
+
709
+ @branch.to_hash.should == {
710
+ 'description' => ['A chilly little penguin.'],
711
+ 'l' => [ 'Antartica', 'Galapagos' ],
712
+ 'dn' => TEST_HOSTS_DN,
713
+ }
714
+ end
715
+
508
716
  it "returns a Treequel::BranchCollection with equivalent Branchsets if added to another " +
509
717
  "Branch" do
510
718
  other_branch = Treequel::Branch.new( @directory, TEST_SUBHOSTS_DN )
@@ -527,8 +735,8 @@ describe Treequel::Branch do
527
735
  @entry.should_receive( :[] ).with( 'glumpy' ).at_least( :once ).
528
736
  and_return([ 'glumpa1', 'glumpa2' ])
529
737
 
530
- @attribute_type.stub!( :syntax_oid ).and_return( OIDS::STRING_SYNTAX )
531
- @directory.stub!( :convert_syntax_value ).and_return {|_,str| str }
738
+ @attribute_type.stub!( :syntax ).and_return( @syntax )
739
+ @directory.stub!( :convert_to_object ).and_return {|_,str| str }
532
740
 
533
741
  @branch[ :glumpy ].should == [ 'glumpa1', 'glumpa2' ]
534
742
  end
@@ -539,8 +747,8 @@ describe Treequel::Branch do
539
747
  @entry.should_receive( :[] ).with( 'glumpy' ).at_least( :once ).
540
748
  and_return([ 'glumpa1' ])
541
749
 
542
- @attribute_type.stub!( :syntax_oid ).and_return( OIDS::STRING_SYNTAX )
543
- @directory.stub!( :convert_syntax_value ).and_return {|_,str| str }
750
+ @attribute_type.stub!( :syntax ).and_return( @syntax )
751
+ @directory.stub!( :convert_to_object ).and_return {|_,str| str }
544
752
 
545
753
  @branch[ :glumpy ].should == 'glumpa1'
546
754
  end
@@ -560,8 +768,8 @@ describe Treequel::Branch do
560
768
  it "caches the value fetched from its entry" do
561
769
  @schema.stub!( :attribute_types ).and_return({ :glump => @attribute_type })
562
770
  @attribute_type.stub!( :single? ).and_return( true )
563
- @attribute_type.stub!( :syntax_oid ).and_return( OIDS::STRING_SYNTAX )
564
- @directory.stub!( :convert_syntax_value ).and_return {|_,str| str }
771
+ @attribute_type.stub!( :syntax ).and_return( @syntax )
772
+ @directory.stub!( :convert_to_object ).and_return {|_,str| str }
565
773
  @entry.should_receive( :[] ).with( 'glump' ).once.and_return( [:a_value] )
566
774
  2.times { @branch[ :glump ] }
567
775
  end
@@ -571,8 +779,8 @@ describe Treequel::Branch do
571
779
  @attribute_type.should_receive( :single? ).and_return( true )
572
780
  @entry.should_receive( :[] ).with( 'bvector' ).at_least( :once ).
573
781
  and_return([ '010011010101B' ])
574
- @attribute_type.should_receive( :syntax_oid ).and_return( OIDS::BIT_STRING_SYNTAX )
575
- @directory.should_receive( :convert_syntax_value ).
782
+ @syntax.stub!( :oid ).and_return( OIDS::BIT_STRING_SYNTAX )
783
+ @directory.should_receive( :convert_to_object ).
576
784
  with( OIDS::BIT_STRING_SYNTAX, '010011010101B' ).
577
785
  and_return( 1237 )
578
786
 
@@ -599,11 +807,12 @@ describe Treequel::Branch do
599
807
  it "clears the cache after a successful write" do
600
808
  @schema.stub!( :attribute_types ).and_return({ :glorpy => @attribute_type })
601
809
  @attribute_type.stub!( :single? ).and_return( true )
602
- @attribute_type.stub!( :syntax_oid ).and_return( OIDS::STRING_SYNTAX )
603
- @directory.stub!( :convert_syntax_value ).and_return {|_,val| val }
810
+ @attribute_type.stub!( :syntax ).and_return( @syntax )
811
+ @directory.stub!( :convert_to_object ).and_return {|_,val| val }
604
812
  @entry.should_receive( :[] ).with( 'glorpy' ).and_return( [:firstval], [:secondval] )
605
813
 
606
814
  @directory.should_receive( :modify ).with( @branch, {'glorpy' => ['chunks']} )
815
+ @directory.stub!( :convert_to_attribute ).and_return {|_,val| val }
607
816
  @entry.should_receive( :[]= ).with( 'glorpy', ['chunks'] )
608
817
 
609
818
  @branch[ :glorpy ].should == :firstval
@@ -6,22 +6,15 @@ BEGIN {
6
6
 
7
7
  libdir = basedir + "lib"
8
8
 
9
+ $LOAD_PATH.unshift( basedir ) unless $LOAD_PATH.include?( basedir )
9
10
  $LOAD_PATH.unshift( libdir ) unless $LOAD_PATH.include?( libdir )
10
11
  }
11
12
 
12
- begin
13
- require 'spec'
14
- require 'spec/lib/constants'
15
- require 'spec/lib/helpers'
13
+ require 'spec'
14
+ require 'spec/lib/constants'
15
+ require 'spec/lib/helpers'
16
16
 
17
- require 'treequel/branchcollection'
18
- rescue LoadError
19
- unless Object.const_defined?( :Gem )
20
- require 'rubygems'
21
- retry
22
- end
23
- raise
24
- end
17
+ require 'treequel/branchcollection'
25
18
 
26
19
 
27
20
  include Treequel::TestConstants
@@ -42,7 +35,7 @@ describe Treequel::BranchCollection do
42
35
  end
43
36
 
44
37
  before( :each ) do
45
- @directory = mock( "treequel directory ")
38
+ @directory = mock( "treequel directory", :registered_controls => [] )
46
39
  end
47
40
 
48
41
  it "can be instantiated without any branchsets" do
@@ -51,8 +44,10 @@ describe Treequel::BranchCollection do
51
44
  end
52
45
 
53
46
  it "can be instantiated with one or more branchsets" do
54
- branchset1 = stub( "branchset 1", :dn => 'cn=example1,dc=acme,dc=com', :each => 1 )
55
- branchset2 = stub( "branchset 2", :dn => 'cn=example2,dc=acme,dc=com', :each => 1 )
47
+ branch1 = stub( "branch for branchset 1", :directory => @directory )
48
+ branchset1 = Treequel::Branchset.new( branch1 )
49
+ branch2 = stub( "branch for branchset 2", :directory => @directory )
50
+ branchset2 = Treequel::Branchset.new( branch2 )
56
51
 
57
52
  collection = Treequel::BranchCollection.new( branchset1, branchset2 )
58
53
 
@@ -60,17 +55,17 @@ describe Treequel::BranchCollection do
60
55
  end
61
56
 
62
57
  it "wraps any object that doesn't have an #each in a Branchset" do
63
- non_branchset1 = stub( "non-branchset 1" )
64
- non_branchset2 = stub( "non-branchset 2" )
65
- branchset1 = stub( "branchset 1", :dn => 'cn=example1,dc=acme,dc=com' )
66
- branchset2 = stub( "branchset 2", :dn => 'cn=example2,dc=acme,dc=com' )
58
+ branch1 = stub( "branch for branchset 1", :directory => @directory )
59
+ branchset1 = Treequel::Branchset.new( branch1 )
60
+ branch2 = stub( "branch for branchset 2", :directory => @directory )
61
+ branchset2 = Treequel::Branchset.new( branch2 )
67
62
 
68
- Treequel::Branchset.should_receive( :new ).with( non_branchset1 ).
63
+ Treequel::Branchset.should_receive( :new ).with( :non_branchset1 ).
69
64
  and_return( branchset1 )
70
- Treequel::Branchset.should_receive( :new ).with( non_branchset2 ).
65
+ Treequel::Branchset.should_receive( :new ).with( :non_branchset2 ).
71
66
  and_return( branchset2 )
72
67
 
73
- collection = Treequel::BranchCollection.new( non_branchset1, non_branchset2 )
68
+ collection = Treequel::BranchCollection.new( :non_branchset1, :non_branchset2 )
74
69
 
75
70
  collection.branchsets.should include( branchset1, branchset2 )
76
71
  end
@@ -99,8 +94,13 @@ describe Treequel::BranchCollection do
99
94
  describe "instance with two Branchsets" do
100
95
 
101
96
  before( :each ) do
102
- @branchset1 = mock( "branchset 1", :dn => 'cn=example1,dc=acme,dc=com', :each => 1 )
103
- @branchset2 = mock( "branchset 2", :dn => 'cn=example2,dc=acme,dc=com', :each => 1 )
97
+ # @branchset1 = mock( "branchset 1", :dn => 'cn=example1,dc=acme,dc=com', :each => 1 )
98
+ # @branchset2 = mock( "branchset 2", :dn => 'cn=example2,dc=acme,dc=com', :each => 1 )
99
+ @branch1 = mock( "branch1", :directory => @directory )
100
+ @branchset1 = Treequel::Branchset.new( @branch1 )
101
+
102
+ @branch2 = mock( "branch2", :directory => @directory )
103
+ @branchset2 = Treequel::Branchset.new( @branch2 )
104
104
 
105
105
  @collection = Treequel::BranchCollection.new( @branchset1, @branchset2 )
106
106
  end
@@ -134,26 +134,51 @@ describe Treequel::BranchCollection do
134
134
  end
135
135
 
136
136
  it "returns a clone of itself with an additional Branchset if a Branchset is added to it" do
137
- branchset3 = mock( "branchset 3", :dn => 'cn=example3,dc=acme,dc=com', :each => 1 )
137
+ branch3 = mock( "branch 3", :directory => @directory )
138
+ branchset3 = Treequel::Branchset.new( branch3 )
139
+
138
140
  new_collection = @collection + branchset3
139
141
 
140
142
  new_collection.should be_an_instance_of( Treequel::BranchCollection )
141
143
  new_collection.should include( @branchset1, @branchset2, branchset3 )
142
144
  end
143
145
 
144
- it "returns a clone of itself with an additional Branchset if a Branch is added to it" do
145
- branchset3 = mock( "branchset 3", :dn => 'cn=example3,dc=acme,dc=com', :each => 1 )
146
- branch3 = mock( "branch 3", :branchset => branchset3 )
147
- new_collection = @collection + branch3
146
+ it "returns all of the results from each of its branchsets plus the added branch if a " +
147
+ "Branch is added to it" do
148
+ @branchset1.should_receive( :each ).and_yield( :bs1_stuff )
149
+ @branchset2.should_receive( :each ).and_yield( :bs2_stuff )
150
+ added_branch = stub( "added branch", :directory => @directory )
151
+ added_branch.stub!( :to_ary ).and_return( [added_branch] )
148
152
 
149
- new_collection.should be_an_instance_of( Treequel::BranchCollection )
150
- new_collection.should include( @branchset1, @branchset2, branchset3 )
153
+ results = @collection + added_branch
154
+
155
+ results.should have( 3 ).members
156
+ results.should include( :bs1_stuff, :bs2_stuff, added_branch )
157
+ end
158
+
159
+ it "returns all of the results from each of its branchsets minus the subtracted branch " +
160
+ "if a Branch is subtracted from it" do
161
+ results_branch1 = stub( "results branch 1", :dn => TEST_PERSON_DN )
162
+ results_branch2 = stub( "results branch 2", :dn => TEST_PERSON2_DN )
163
+ subtracted_branch = stub( "subtracted branch", :dn => TEST_PERSON_DN )
164
+
165
+ @branchset1.should_receive( :each ).and_yield( results_branch1 )
166
+ @branchset2.should_receive( :each ).and_yield( results_branch2 )
167
+
168
+ results = @collection - subtracted_branch
169
+
170
+ results.should have( 1 ).members
171
+ results.should_not include( subtracted_branch )
172
+ results.should_not include( results_branch1 )
151
173
  end
152
174
 
153
175
  it "returns a clone of itself with both collections' Branchsets if a BranchCollection is " +
154
176
  "added to it" do
155
- branchset3 = stub( "branchset 3", :dn => 'cn=example3,dc=acme,dc=com', :each => 1 )
156
- branchset4 = stub( "branchset 4", :dn => 'cn=example4,dc=acme,dc=com', :each => 1 )
177
+ branch3 = stub( "branch for branchset 3", :directory => @directory )
178
+ branchset3 = Treequel::Branchset.new( branch3 )
179
+ branch4 = stub( "branch for branchset 4", :directory => @directory )
180
+ branchset4 = Treequel::Branchset.new( branch4 )
181
+
157
182
  other_collection = Treequel::BranchCollection.new( branchset3, branchset4 )
158
183
 
159
184
  new_collection = @collection + other_collection
@@ -164,8 +189,11 @@ describe Treequel::BranchCollection do
164
189
 
165
190
  it "returns a new BranchCollection with the union of Branchsets if it is ORed with " +
166
191
  "another BranchCollection" do
167
- branchset3 = stub( "branchset 3", :dn => 'cn=example3,dc=acme,dc=com', :each => 1 )
168
- branchset4 = stub( "branchset 4", :dn => 'cn=example4,dc=acme,dc=com', :each => 1 )
192
+ branch3 = stub( "branch for branchset 3", :directory => @directory )
193
+ branchset3 = Treequel::Branchset.new( branch3 )
194
+ branch4 = stub( "branch for branchset 4", :directory => @directory )
195
+ branchset4 = Treequel::Branchset.new( branch4 )
196
+
169
197
  other_collection = Treequel::BranchCollection.new( branchset3, branchset4 )
170
198
 
171
199
  new_collection = @collection | other_collection
@@ -176,8 +204,11 @@ describe Treequel::BranchCollection do
176
204
 
177
205
  it "returns a new BranchCollection with the intersection of Branchsets if it is ANDed with " +
178
206
  "another BranchCollection" do
179
- branchset3 = stub( "branchset 3", :dn => 'cn=example3,dc=acme,dc=com', :each => 1 )
180
- branchset4 = stub( "branchset 4", :dn => 'cn=example4,dc=acme,dc=com', :each => 1 )
207
+ branch3 = stub( "branch for branchset 3", :directory => @directory )
208
+ branchset3 = Treequel::Branchset.new( branch3 )
209
+ branch4 = stub( "branch for branchset 4", :directory => @directory )
210
+ branchset4 = Treequel::Branchset.new( branch4 )
211
+
181
212
  other_collection = Treequel::BranchCollection.new( @branchset2, branchset3, branchset4 )
182
213
  @collection << branchset4
183
214
 
@@ -188,8 +219,11 @@ describe Treequel::BranchCollection do
188
219
  end
189
220
 
190
221
  it "can create a clone of itself with filtered branchsets" do
191
- filtered_branchset1 = stub( "branchset 3", :dn => 'cn=example3,dc=acme,dc=com', :each => 1 )
192
- filtered_branchset2 = stub( "branchset 4", :dn => 'cn=example4,dc=acme,dc=com', :each => 1 )
222
+ branch3 = stub( "branch for branchset 3", :directory => @directory )
223
+ filtered_branchset1 = Treequel::Branchset.new( branch3 )
224
+ branch4 = stub( "branch for branchset 4", :directory => @directory )
225
+ filtered_branchset2 = Treequel::Branchset.new( branch4 )
226
+
193
227
  @branchset1.should_receive( :filter ).with( :cn => 'chunkalicious' ).
194
228
  and_return( filtered_branchset1 )
195
229
  @branchset2.should_receive( :filter ).with( :cn => 'chunkalicious' ).