treequel 1.2.2 → 1.3.0pre384
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +0 -0
- data/ChangeLog +3374 -0
- data/History.md +39 -0
- data/LICENSE +27 -0
- data/README.md +25 -2
- data/Rakefile +64 -29
- data/bin/treequel +16 -25
- data/bin/treewhat +318 -0
- data/lib/treequel.rb +13 -3
- data/lib/treequel/behavior/control.rb +40 -0
- data/lib/treequel/branch.rb +56 -28
- data/lib/treequel/branchset.rb +10 -2
- data/lib/treequel/controls/pagedresults.rb +4 -2
- data/lib/treequel/directory.rb +40 -50
- data/lib/treequel/exceptions.rb +18 -0
- data/lib/treequel/mixins.rb +44 -14
- data/lib/treequel/model.rb +338 -21
- data/lib/treequel/model/errors.rb +79 -0
- data/lib/treequel/model/objectclass.rb +26 -2
- data/lib/treequel/model/schemavalidations.rb +69 -0
- data/lib/treequel/monkeypatches.rb +99 -17
- data/lib/treequel/schema.rb +19 -5
- data/spec/lib/constants.rb +20 -2
- data/spec/lib/helpers.rb +25 -8
- data/spec/treequel/branch_spec.rb +73 -10
- data/spec/treequel/controls/contentsync_spec.rb +2 -11
- data/spec/treequel/controls/pagedresults_spec.rb +25 -9
- data/spec/treequel/controls/sortedresults_spec.rb +8 -10
- data/spec/treequel/directory_spec.rb +74 -63
- data/spec/treequel/model/errors_spec.rb +77 -0
- data/spec/treequel/model/objectclass_spec.rb +107 -35
- data/spec/treequel/model/schemavalidations_spec.rb +112 -0
- data/spec/treequel/model_spec.rb +294 -81
- data/spec/treequel/monkeypatches_spec.rb +49 -3
- metadata +28 -16
- metadata.gz.sig +0 -0
- data/spec/lib/control_behavior.rb +0 -47
data/spec/lib/helpers.rb
CHANGED
@@ -12,7 +12,6 @@ BEGIN {
|
|
12
12
|
|
13
13
|
require 'rspec'
|
14
14
|
|
15
|
-
require 'yaml'
|
16
15
|
require 'treequel'
|
17
16
|
|
18
17
|
require 'spec/lib/constants'
|
@@ -23,10 +22,6 @@ require 'spec/lib/matchers'
|
|
23
22
|
module Treequel::SpecHelpers
|
24
23
|
include Treequel::TestConstants
|
25
24
|
|
26
|
-
SCHEMA_DUMPFILE = Pathname( __FILE__ ).dirname.parent + 'data' + 'schema.yml'
|
27
|
-
SCHEMAHASH = LDAP::Schema.new( YAML.load_file(SCHEMA_DUMPFILE) )
|
28
|
-
SCHEMA = Treequel::Schema.new( SCHEMAHASH )
|
29
|
-
|
30
25
|
class ArrayLogger
|
31
26
|
### Create a new ArrayLogger that will append content to +array+.
|
32
27
|
def initialize( array )
|
@@ -74,8 +69,8 @@ module Treequel::SpecHelpers
|
|
74
69
|
def setup_logging( level=Logger::FATAL )
|
75
70
|
|
76
71
|
# Turn symbol-style level config into Logger's expected Fixnum level
|
77
|
-
if Treequel::
|
78
|
-
level = Treequel::
|
72
|
+
if Treequel::LOG_LEVELS.key?( level.to_s )
|
73
|
+
level = Treequel::LOG_LEVELS[ level.to_s ]
|
79
74
|
end
|
80
75
|
|
81
76
|
logger = Logger.new( $stderr )
|
@@ -98,14 +93,36 @@ module Treequel::SpecHelpers
|
|
98
93
|
### external data.
|
99
94
|
def get_fixtured_directory( conn )
|
100
95
|
LDAP::SSLConn.stub( :new ).and_return( @conn )
|
101
|
-
conn.stub( :
|
96
|
+
conn.stub( :search_ext2 ).
|
97
|
+
with( "", 0, "(objectClass=*)", ["+"], false, nil, nil, 0, 0, 0, "", nil ).
|
98
|
+
and_return( TEST_DSE )
|
102
99
|
conn.stub( :set_option )
|
100
|
+
|
101
|
+
# Avoid parsing the whole schema with every example
|
103
102
|
directory = Treequel.directory( TEST_LDAPURI )
|
104
103
|
directory.stub( :schema ).and_return( SCHEMA )
|
105
104
|
|
106
105
|
return directory
|
107
106
|
end
|
108
107
|
|
108
|
+
### Shorthand method for creating LDAP::Mod DELETE objects
|
109
|
+
def ldap_mod_delete( attribute, *values )
|
110
|
+
return LDAP::Mod.new( LDAP::LDAP_MOD_DELETE, attribute.to_s, values.flatten )
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
### Shorthand method for creating LDAP::Mod REPLACE objects
|
115
|
+
def ldap_mod_replace( attribute, *values )
|
116
|
+
return LDAP::Mod.new( LDAP::LDAP_MOD_REPLACE, attribute.to_s, values.flatten )
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
### Shorthand method for creating LDAP::Mod ADD objects
|
121
|
+
def ldap_mod_add( attribute, *values )
|
122
|
+
return LDAP::Mod.new( LDAP::LDAP_MOD_ADD, attribute.to_s, values.flatten )
|
123
|
+
end
|
124
|
+
|
125
|
+
|
109
126
|
end
|
110
127
|
|
111
128
|
|
@@ -41,7 +41,7 @@ describe Treequel::Branch do
|
|
41
41
|
end
|
42
42
|
|
43
43
|
before( :each ) do
|
44
|
-
@conn = mock( "ldap connection object" )
|
44
|
+
@conn = mock( "ldap connection object", :bound? => false )
|
45
45
|
@directory = get_fixtured_directory( @conn )
|
46
46
|
end
|
47
47
|
|
@@ -69,7 +69,8 @@ describe Treequel::Branch do
|
|
69
69
|
branch = Treequel::Branch.new_from_entry( entry, @directory )
|
70
70
|
|
71
71
|
branch.rdn_attributes.should == { TEST_PERSON_DN_ATTR => [TEST_PERSON_DN_VALUE] }
|
72
|
-
branch.entry.should ==
|
72
|
+
branch.entry.should == { TEST_PERSON_DN_ATTR => TEST_PERSON_DN_VALUE }
|
73
|
+
branch.dn.should == entry['dn'].first
|
73
74
|
end
|
74
75
|
|
75
76
|
it "can be constructed from an entry with Symbol keys" do
|
@@ -81,9 +82,9 @@ describe Treequel::Branch do
|
|
81
82
|
|
82
83
|
branch.rdn_attributes.should == { TEST_PERSON_DN_ATTR => [TEST_PERSON_DN_VALUE] }
|
83
84
|
branch.entry.should == {
|
84
|
-
'dn' => [TEST_PERSON_DN],
|
85
85
|
TEST_PERSON_DN_ATTR => TEST_PERSON_DN_VALUE,
|
86
86
|
}
|
87
|
+
branch.dn.should == entry[:dn].first
|
87
88
|
end
|
88
89
|
|
89
90
|
it "can be instantiated with a Hash with Symbol keys" do
|
@@ -232,7 +233,10 @@ describe Treequel::Branch do
|
|
232
233
|
yielded_val = val
|
233
234
|
end
|
234
235
|
yielded_val.should be_a( Treequel::Branch )
|
235
|
-
yielded_val.entry.should ==
|
236
|
+
yielded_val.entry.should == {
|
237
|
+
'objectClass' => ['ipHost'],
|
238
|
+
TEST_HOST_DN_ATTR => [TEST_HOST_DN_VALUE],
|
239
|
+
}
|
236
240
|
end
|
237
241
|
|
238
242
|
it "clears any cached values if its include_operational_attrs attribute is changed" do
|
@@ -313,6 +317,11 @@ describe Treequel::Branch do
|
|
313
317
|
end
|
314
318
|
|
315
319
|
|
320
|
+
it "returns nil if a Branch for the base DN is asked for its parent" do
|
321
|
+
@branch.parent.parent.should be_nil()
|
322
|
+
end
|
323
|
+
|
324
|
+
|
316
325
|
it "can construct a Treequel::Branchset that uses it as its base" do
|
317
326
|
@branch.branchset.should be_a( Treequel::Branchset )
|
318
327
|
@branch.branchset.base_dn.should == @branch.dn
|
@@ -325,6 +334,14 @@ describe Treequel::Branch do
|
|
325
334
|
branchset.filter_string.should =~ /\(cn=acme\)/
|
326
335
|
end
|
327
336
|
|
337
|
+
it "can create a Treequel::Branchset from itself that returns instances of another class" do
|
338
|
+
otherclass = Class.new( Treequel::Branch )
|
339
|
+
branchset = @branch.as( otherclass )
|
340
|
+
|
341
|
+
branchset.should be_a( Treequel::Branchset )
|
342
|
+
branchset.branch.should be_a( otherclass )
|
343
|
+
end
|
344
|
+
|
328
345
|
it "doesn't restrict the number of arguments passed to #filter (bugfix)" do
|
329
346
|
branchset = @branch.filter( :uid => [:rev, :grumpy, :glee] )
|
330
347
|
|
@@ -399,7 +416,7 @@ describe Treequel::Branch do
|
|
399
416
|
and_return([ {'objectClass' => %w[ipHost device ieee802device]} ])
|
400
417
|
|
401
418
|
@branch.must_attributes_hash.
|
402
|
-
should include({ :cn => [''], :ipHostNumber => [''], :objectClass => [
|
419
|
+
should include({ :cn => [''], :ipHostNumber => [''], :objectClass => ['top'] })
|
403
420
|
end
|
404
421
|
|
405
422
|
|
@@ -457,7 +474,7 @@ describe Treequel::Branch do
|
|
457
474
|
it "can return a Hash pre-populated with pairs that correspond to all of its valid " +
|
458
475
|
"attributes" do
|
459
476
|
@branch.valid_attributes_hash.should == {
|
460
|
-
:objectClass => [
|
477
|
+
:objectClass => ['top'],
|
461
478
|
:ou => [''],
|
462
479
|
:userPassword => [],
|
463
480
|
:searchGuide => [],
|
@@ -535,8 +552,7 @@ describe Treequel::Branch do
|
|
535
552
|
@conn.should_receive( :add ).
|
536
553
|
with( "cn=chillyt,#{TEST_HOSTS_DN}",
|
537
554
|
"ipHostNumber"=>["127.0.0.1"],
|
538
|
-
"objectClass"=>["ipHost", "device"]
|
539
|
-
"cn"=>["chillyt"] )
|
555
|
+
"objectClass"=>["ipHost", "device"] )
|
540
556
|
|
541
557
|
res = @branch.cn( :chillyt ).create( newattrs )
|
542
558
|
res.should be_a( Treequel::Branch )
|
@@ -596,6 +612,13 @@ describe Treequel::Branch do
|
|
596
612
|
@branch.delete( :objectClass => 'apple-user' )
|
597
613
|
end
|
598
614
|
|
615
|
+
it "can delete one particular non-String value of its entry's individual attributes" do
|
616
|
+
mod = LDAP::Mod.new( LDAP::LDAP_MOD_DELETE, 'pwdChangedTime', ['20000101201501Z'] )
|
617
|
+
@conn.should_receive( :modify ).with( TEST_HOSTS_DN, [mod] )
|
618
|
+
|
619
|
+
@branch.delete( :pwdChangedTime => Time.gm(2000,"jan",1,20,15,1) )
|
620
|
+
end
|
621
|
+
|
599
622
|
it "can delete particular values of more than one of its entry's individual attributes" do
|
600
623
|
mod1 = LDAP::Mod.new( LDAP::LDAP_MOD_DELETE, 'objectClass',
|
601
624
|
['apple-user', 'inetOrgPerson'] )
|
@@ -732,6 +755,17 @@ describe Treequel::Branch do
|
|
732
755
|
@branch[ :l ].should include( 'Galapagos', 'Antartica' )
|
733
756
|
end
|
734
757
|
|
758
|
+
it "fetches an empty Array if a record doesn't have an attribute set" do
|
759
|
+
@branch[ :cn ].should == []
|
760
|
+
end
|
761
|
+
|
762
|
+
it "fetches an empty Array for an attribute if the entry doesn't exist" do
|
763
|
+
@conn.stub( :search_ext2 ).
|
764
|
+
with( TEST_HOSTS_DN, LDAP::LDAP_SCOPE_BASE, "(objectClass=*)" ).
|
765
|
+
and_return( [] )
|
766
|
+
@branch[ :cn ].should == []
|
767
|
+
end
|
768
|
+
|
735
769
|
it "fetches a single-value attribute as a scalar String" do
|
736
770
|
test_dn = "cn=ssh,cn=www,#{TEST_HOSTS_DN}"
|
737
771
|
entry = {
|
@@ -754,8 +788,8 @@ describe Treequel::Branch do
|
|
754
788
|
@branch[ :rev ].should == [ '03eca02ba232' ]
|
755
789
|
end
|
756
790
|
|
757
|
-
it "returns nil if record doesn't have
|
758
|
-
@branch[ :
|
791
|
+
it "returns nil if a record doesn't have a SINGLE-type attribute set" do
|
792
|
+
@branch[ :displayName ].should == nil
|
759
793
|
end
|
760
794
|
|
761
795
|
it "caches the value fetched from its entry" do
|
@@ -767,6 +801,35 @@ describe Treequel::Branch do
|
|
767
801
|
2.times { @branch[ :description ] }
|
768
802
|
end
|
769
803
|
|
804
|
+
it "freezes the values fetched from its entry by default to prevent accidental " +
|
805
|
+
"in-place modification" do
|
806
|
+
@conn.should_receive( :search_ext2 ).
|
807
|
+
with( TEST_HOSTS_DN, LDAP::LDAP_SCOPE_BASE, "(objectClass=*)" ).
|
808
|
+
exactly( :once ).
|
809
|
+
and_return([ @entry ])
|
810
|
+
|
811
|
+
expect {
|
812
|
+
@branch[ :description ] << "Another description"
|
813
|
+
}.to raise_error( /can't modify frozen/i )
|
814
|
+
end
|
815
|
+
|
816
|
+
it "doesn't freeze the values fetched from its entry if it's told not to" do
|
817
|
+
@conn.should_receive( :search_ext2 ).
|
818
|
+
with( TEST_HOSTS_DN, LDAP::LDAP_SCOPE_BASE, "(objectClass=*)" ).
|
819
|
+
exactly( :once ).
|
820
|
+
and_return([ @entry ])
|
821
|
+
|
822
|
+
begin
|
823
|
+
Treequel::Branch.freeze_converted_values = false
|
824
|
+
|
825
|
+
expect {
|
826
|
+
@branch[ :description ] << "Another description"
|
827
|
+
}.to_not raise_error()
|
828
|
+
ensure
|
829
|
+
Treequel::Branch.freeze_converted_values = true
|
830
|
+
end
|
831
|
+
end
|
832
|
+
|
770
833
|
it "converts objects via the conversions set in its directory" do
|
771
834
|
test_dn = "cn=ssh,cn=www,#{TEST_HOSTS_DN}"
|
772
835
|
entry = {
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
BEGIN {
|
4
4
|
require 'pathname'
|
5
|
-
basedir = Pathname.new( __FILE__ ).dirname.parent.parent
|
5
|
+
basedir = Pathname.new( __FILE__ ).dirname.parent.parent.parent
|
6
6
|
|
7
7
|
libdir = basedir + "lib"
|
8
8
|
|
@@ -12,26 +12,17 @@ BEGIN {
|
|
12
12
|
|
13
13
|
require 'rspec'
|
14
14
|
|
15
|
-
require 'spec/lib/constants'
|
16
15
|
require 'spec/lib/helpers'
|
17
|
-
require 'spec/lib/control_behavior'
|
18
16
|
|
19
17
|
require 'treequel'
|
18
|
+
require 'treequel/behavior/control'
|
20
19
|
require 'treequel/controls/contentsync'
|
21
20
|
|
22
|
-
include Treequel::TestConstants
|
23
|
-
include Treequel::Constants
|
24
21
|
|
25
22
|
#####################################################################
|
26
23
|
### C O N T E X T S
|
27
24
|
#####################################################################
|
28
25
|
describe Treequel::ContentSyncControl do
|
29
|
-
include Treequel::SpecHelpers
|
30
|
-
|
31
|
-
before( :each ) do
|
32
|
-
# Used by the shared behavior
|
33
|
-
@control = Treequel::ContentSyncControl
|
34
|
-
end
|
35
26
|
|
36
27
|
it_should_behave_like "A Treequel::Control"
|
37
28
|
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
BEGIN {
|
4
4
|
require 'pathname'
|
5
|
-
basedir = Pathname.new( __FILE__ ).dirname.parent.parent
|
5
|
+
basedir = Pathname.new( __FILE__ ).dirname.parent.parent.parent
|
6
6
|
|
7
7
|
libdir = basedir + "lib"
|
8
8
|
|
@@ -12,11 +12,10 @@ BEGIN {
|
|
12
12
|
|
13
13
|
require 'rspec'
|
14
14
|
|
15
|
-
require 'spec/lib/constants'
|
16
15
|
require 'spec/lib/helpers'
|
17
|
-
require 'spec/lib/control_behavior'
|
18
16
|
|
19
17
|
require 'treequel'
|
18
|
+
require 'treequel/behavior/control'
|
20
19
|
require 'treequel/controls/pagedresults'
|
21
20
|
|
22
21
|
|
@@ -24,19 +23,19 @@ require 'treequel/controls/pagedresults'
|
|
24
23
|
### C O N T E X T S
|
25
24
|
#####################################################################
|
26
25
|
describe Treequel::PagedResultsControl do
|
27
|
-
include Treequel::SpecHelpers
|
28
26
|
|
29
27
|
before( :all ) do
|
30
28
|
setup_logging( :fatal )
|
31
29
|
end
|
32
30
|
|
33
31
|
before( :each ) do
|
34
|
-
@
|
35
|
-
@
|
32
|
+
@conn = mock( "ldap connection object" )
|
33
|
+
@conn.stub( :bound? ).and_return( false )
|
34
|
+
@directory = get_fixtured_directory( @conn )
|
35
|
+
@directory.register_controls( Treequel::PagedResultsControl )
|
36
36
|
|
37
|
-
@branch
|
38
|
-
@
|
39
|
-
@branchset = Treequel::Branchset.new( @branch )
|
37
|
+
@branch = Treequel::Branch.new( @directory, TEST_PEOPLE_DN )
|
38
|
+
@branchset = @branch.branchset
|
40
39
|
end
|
41
40
|
|
42
41
|
after( :all ) do
|
@@ -80,6 +79,23 @@ describe Treequel::PagedResultsControl do
|
|
80
79
|
paged_branchset.paged_results_setsize.should == nil
|
81
80
|
end
|
82
81
|
|
82
|
+
it "knows that there are (potentially) more paged results if the cookie isn't set" do
|
83
|
+
paged_branchset = @branchset.with_paged_results( 25 )
|
84
|
+
paged_branchset.should_not be_done_paging()
|
85
|
+
end
|
86
|
+
|
87
|
+
it "knows that there are more paged results if the cookie is set" do
|
88
|
+
paged_branchset = @branchset.with_paged_results( 25 )
|
89
|
+
paged_branchset.paged_results_cookie = "\230\t\000\000\000\000\000\000"
|
90
|
+
paged_branchset.should_not be_done_paging()
|
91
|
+
end
|
92
|
+
|
93
|
+
it "knows that there are no more paged results if the cookie is blank" do
|
94
|
+
paged_branchset = @branchset.with_paged_results( 25 )
|
95
|
+
paged_branchset.paged_results_cookie = ''
|
96
|
+
paged_branchset.should be_done_paging()
|
97
|
+
end
|
98
|
+
|
83
99
|
it "injects the correct server-control structure into the search when iterating" do
|
84
100
|
oid = Treequel::PagedResultsControl::OID
|
85
101
|
expected_asn1_string = "0\005\002\001\031\004\000"
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
BEGIN {
|
4
4
|
require 'pathname'
|
5
|
-
basedir = Pathname.new( __FILE__ ).dirname.parent.parent
|
5
|
+
basedir = Pathname.new( __FILE__ ).dirname.parent.parent.parent
|
6
6
|
|
7
7
|
libdir = basedir + "lib"
|
8
8
|
|
@@ -12,12 +12,11 @@ BEGIN {
|
|
12
12
|
|
13
13
|
require 'rspec'
|
14
14
|
|
15
|
-
require 'spec/lib/constants'
|
16
15
|
require 'spec/lib/helpers'
|
17
|
-
require 'spec/lib/control_behavior'
|
18
16
|
|
19
17
|
require 'treequel'
|
20
18
|
require 'treequel/branchset'
|
19
|
+
require 'treequel/behavior/control'
|
21
20
|
require 'treequel/controls/sortedresults'
|
22
21
|
|
23
22
|
|
@@ -25,20 +24,19 @@ require 'treequel/controls/sortedresults'
|
|
25
24
|
### C O N T E X T S
|
26
25
|
#####################################################################
|
27
26
|
describe Treequel::SortedResultsControl do
|
28
|
-
include Treequel::SpecHelpers
|
29
27
|
|
30
28
|
before( :all ) do
|
31
29
|
setup_logging( :fatal )
|
32
30
|
end
|
33
31
|
|
34
32
|
before( :each ) do
|
35
|
-
@
|
36
|
-
@
|
37
|
-
|
38
|
-
@
|
39
|
-
@directory.stub( :registered_controls ).and_return([ Treequel::SortedResultsControl ])
|
40
|
-
@branchset = Treequel::Branchset.new( @branch )
|
33
|
+
@conn = mock( "ldap connection object" )
|
34
|
+
@conn.stub( :bound? ).and_return( false )
|
35
|
+
@directory = get_fixtured_directory( @conn )
|
36
|
+
@directory.register_controls( Treequel::SortedResultsControl )
|
41
37
|
|
38
|
+
@branch = Treequel::Branch.new( @directory, TEST_PEOPLE_DN )
|
39
|
+
@branchset = @branch.branchset
|
42
40
|
end
|
43
41
|
|
44
42
|
after( :all ) do
|
@@ -46,17 +46,22 @@ describe Treequel::Directory do
|
|
46
46
|
}
|
47
47
|
@conn = mock( "LDAP connection", :set_option => true, :bound? => false )
|
48
48
|
LDAP::SSLConn.stub( :new ).and_return( @conn )
|
49
|
-
|
49
|
+
|
50
|
+
@conn.stub( :schema ).and_return( SCHEMAHASH )
|
50
51
|
end
|
51
52
|
|
52
53
|
|
53
54
|
it "is created with reasonable default options if none are specified" do
|
55
|
+
@conn.stub( :search_ext2 ).
|
56
|
+
with( "", 0, "(objectClass=*)", ["+"], false, nil, nil, 0, 0, 0, "", nil ).
|
57
|
+
and_return( TEST_DSE )
|
58
|
+
|
54
59
|
dir = Treequel::Directory.new
|
55
60
|
|
56
61
|
dir.host.should == 'localhost'
|
57
62
|
dir.port.should == 389
|
58
63
|
dir.connect_type.should == :tls
|
59
|
-
dir.base_dn.should == ''
|
64
|
+
dir.base_dn.should == 'dc=acme,dc=com'
|
60
65
|
end
|
61
66
|
|
62
67
|
it "is created with the specified options if options are specified" do
|
@@ -80,36 +85,37 @@ describe Treequel::Directory do
|
|
80
85
|
end
|
81
86
|
|
82
87
|
it "uses the first namingContext from the Root DSE if no base is specified" do
|
83
|
-
|
84
|
-
|
85
|
-
|
88
|
+
LDAP::Conn.stub( :new ).and_return( @conn )
|
89
|
+
@conn.stub( :search_ext2 ).
|
90
|
+
with( "", 0, "(objectClass=*)", ["+"], false, nil, nil, 0, 0, 0, "", nil ).
|
91
|
+
and_return( TEST_DSE )
|
86
92
|
|
87
|
-
|
88
|
-
|
93
|
+
dir = Treequel::Directory.new( @options.merge(:base_dn => nil) )
|
94
|
+
dir.base_dn.should == TEST_BASE_DN
|
89
95
|
end
|
90
96
|
|
91
97
|
it "can return its root element as a Branch instance" do
|
92
|
-
|
93
|
-
|
94
|
-
|
98
|
+
dir = Treequel::Directory.new( @options )
|
99
|
+
dir.base.should be_a( Treequel::Branch )
|
100
|
+
dir.base.dn.should == TEST_BASE_DN
|
95
101
|
end
|
96
102
|
|
97
103
|
it "can return its root element as an instance of its results class if it's been set" do
|
98
104
|
subtype = Class.new( Treequel::Branch )
|
99
|
-
|
105
|
+
dir = Treequel::Directory.new( @options )
|
100
106
|
|
101
|
-
|
107
|
+
dir.results_class = subtype
|
102
108
|
|
103
|
-
|
104
|
-
|
109
|
+
dir.base.should be_a( subtype )
|
110
|
+
dir.base.dn.should == TEST_BASE_DN
|
105
111
|
end
|
106
112
|
|
107
113
|
|
108
114
|
describe "instances without existing connections" do
|
109
115
|
|
110
116
|
before( :each ) do
|
117
|
+
@conn = mock( "ldap connection", :bound? => false, :set_option => true )
|
111
118
|
@dir = Treequel::Directory.new( @options )
|
112
|
-
@conn = mock( "ldap connection", :set_option => true )
|
113
119
|
end
|
114
120
|
|
115
121
|
|
@@ -344,6 +350,35 @@ describe Treequel::Directory do
|
|
344
350
|
}.to raise_error( LDAP::ResultError, /can't contact/i )
|
345
351
|
end
|
346
352
|
|
353
|
+
|
354
|
+
it "can reconnect if its underlying connection goes away" do
|
355
|
+
@conn.stub( :search_ext2 ).and_raise( LDAP::ResultError.new("Can't contact LDAP server") )
|
356
|
+
|
357
|
+
second_conn = mock( "LDAP connection", :set_option => true, :bound? => false )
|
358
|
+
LDAP::Conn.should_receive( :new ).and_return( second_conn )
|
359
|
+
second_conn.should_receive( :search_ext2 ).and_return([])
|
360
|
+
|
361
|
+
already_tried_reconnect = false
|
362
|
+
begin
|
363
|
+
@dir.search( TEST_PEOPLE_DN, :base, '(objectClass=*)' )
|
364
|
+
rescue
|
365
|
+
unless already_tried_reconnect
|
366
|
+
already_tried_reconnect = true
|
367
|
+
@dir.reconnect and retry
|
368
|
+
end
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
it "re-raises an exception rescued during a reconnect as a RuntimeError" do
|
373
|
+
LDAP::Conn.should_receive( :new ).
|
374
|
+
and_raise( LDAP::ResultError.new("Can't contact LDAP server") )
|
375
|
+
|
376
|
+
expect {
|
377
|
+
@dir.reconnect
|
378
|
+
}.to raise_exception( RuntimeError, /couldn't reconnect/i )
|
379
|
+
end
|
380
|
+
|
381
|
+
|
347
382
|
describe "and a custom search results class" do
|
348
383
|
|
349
384
|
before( :each ) do
|
@@ -482,63 +517,31 @@ describe Treequel::Directory do
|
|
482
517
|
:desc => 'Audi like Jetta',
|
483
518
|
:objectClass => :room,
|
484
519
|
}
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
520
|
+
|
521
|
+
branch = Treequel::Branch.new( @directory, TEST_PERSON_DN, newattrs )
|
522
|
+
|
523
|
+
@conn.should_receive( :add ).with( TEST_PERSON_DN, {
|
489
524
|
'cn' => ['Chilly T'],
|
490
525
|
'desc' => ['Audi like Jetta'],
|
491
526
|
'objectClass' => ['room'],
|
492
|
-
TEST_PERSON_DN_ATTR => [TEST_PERSON_DN_VALUE],
|
493
|
-
}
|
494
|
-
|
495
|
-
branch = mock( "new person branch" )
|
496
|
-
branch.should_receive( :dn ).and_return( TEST_PERSON_DN )
|
497
|
-
branch.should_receive( :rdn_attributes ).at_least( :once ).and_return( rdn_attrs )
|
498
|
-
|
499
|
-
room_objectclass = stub( 'room objectClass', :structural? => true )
|
500
|
-
@schema.should_receive( :object_classes ).at_least( :once ).and_return({
|
501
|
-
:room => room_objectclass,
|
502
527
|
})
|
503
528
|
|
504
|
-
@conn.should_receive( :add ).with( TEST_PERSON_DN, addattrs )
|
505
|
-
|
506
529
|
@dir.create( branch, newattrs )
|
507
530
|
end
|
508
531
|
|
509
532
|
|
510
|
-
it "
|
511
|
-
|
512
|
-
|
513
|
-
:
|
514
|
-
:
|
515
|
-
|
516
|
-
}
|
517
|
-
rdn_attrs = {
|
518
|
-
TEST_PERSON_DN_ATTR => [TEST_PERSON_DN_VALUE]
|
519
|
-
}
|
520
|
-
addattrs = {
|
521
|
-
'cn' => ['Chilly T'],
|
522
|
-
'desc' => ['Audi like Jetta'],
|
523
|
-
'objectClass' => ['room'],
|
524
|
-
TEST_PERSON_DN_ATTR => [TEST_PERSON_DN_VALUE],
|
525
|
-
}
|
526
|
-
|
527
|
-
branch = mock( "new person branch" )
|
528
|
-
branch.should_receive( :dn ).and_return( TEST_PERSON_DN )
|
529
|
-
branch.should_receive( :rdn_attributes ).at_least( :once ).and_return( rdn_attrs )
|
530
|
-
|
531
|
-
room_objectclass = stub( 'room objectClass', :structural? => true )
|
532
|
-
@schema.should_receive( :object_classes ).at_least( :once ).and_return({
|
533
|
-
:room => room_objectclass,
|
534
|
-
})
|
533
|
+
it "can create an entry with a DN and LDAP::Mod objects instead of an attribute hash" do
|
534
|
+
mods = [
|
535
|
+
ldap_mod_add( :cn, 'Chilly T' ),
|
536
|
+
ldap_mod_add( :desc, 'Audi like Jetta' ),
|
537
|
+
ldap_mod_add( :objectClass, 'room' ),
|
538
|
+
]
|
535
539
|
|
536
|
-
@conn.should_receive( :add ).with( TEST_PERSON_DN,
|
540
|
+
@conn.should_receive( :add ).with( TEST_PERSON_DN, mods )
|
537
541
|
|
538
|
-
@dir.create(
|
542
|
+
@dir.create( TEST_PERSON_DN, mods )
|
539
543
|
end
|
540
544
|
|
541
|
-
|
542
545
|
it "can move a record to a new dn within the same branch" do
|
543
546
|
@dir.stub( :bound? ).and_return( false )
|
544
547
|
branch = mock( "sibling branch obj" )
|
@@ -596,7 +599,9 @@ describe Treequel::Directory do
|
|
596
599
|
describe "to a server that supports controls introspection" do
|
597
600
|
before( :each ) do
|
598
601
|
@control = Module.new { include Treequel::Control }
|
599
|
-
@conn.
|
602
|
+
@conn.stub( :search_ext2 ).
|
603
|
+
with( "", 0, "(objectClass=*)", ["+"], false, nil, nil, 0, 0, 0, "", nil ).
|
604
|
+
and_return( TEST_DSE )
|
600
605
|
end
|
601
606
|
|
602
607
|
|
@@ -635,7 +640,9 @@ describe Treequel::Directory do
|
|
635
640
|
|
636
641
|
describe "to a server that supports extensions introspection" do
|
637
642
|
before( :each ) do
|
638
|
-
@conn.
|
643
|
+
@conn.stub( :search_ext2 ).
|
644
|
+
with( "", 0, "(objectClass=*)", ["+"], false, nil, nil, 0, 0, 0, "", nil ).
|
645
|
+
and_return( TEST_DSE )
|
639
646
|
end
|
640
647
|
|
641
648
|
|
@@ -653,7 +660,9 @@ describe Treequel::Directory do
|
|
653
660
|
|
654
661
|
describe "to a server that supports features introspection" do
|
655
662
|
before( :each ) do
|
656
|
-
@conn.
|
663
|
+
@conn.stub( :search_ext2 ).
|
664
|
+
with( "", 0, "(objectClass=*)", ["+"], false, nil, nil, 0, 0, 0, "", nil ).
|
665
|
+
and_return( TEST_DSE )
|
657
666
|
end
|
658
667
|
|
659
668
|
|
@@ -670,7 +679,9 @@ describe Treequel::Directory do
|
|
670
679
|
|
671
680
|
describe "to a server that doesn't support features introspection" do
|
672
681
|
before( :each ) do
|
673
|
-
@conn.
|
682
|
+
@conn.stub( :search_ext2 ).
|
683
|
+
with( "", 0, "(objectClass=*)", ["+"], false, nil, nil, 0, 0, 0, "", nil ).
|
684
|
+
and_return( TEST_DSE )
|
674
685
|
end
|
675
686
|
|
676
687
|
|