treequel 1.2.1 → 1.2.2
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.
- data/History.md +5 -2970
- data/Rakefile +4 -1
- data/lib/treequel/branch.rb +6 -1
- data/lib/treequel/directory.rb +3 -2
- data/lib/treequel/model.rb +1 -1
- data/lib/treequel/monkeypatches.rb +21 -0
- data/lib/treequel.rb +3 -3
- data/spec/lib/helpers.rb +1 -0
- data/spec/lib/matchers.rb +1 -0
- data/spec/treequel/branch_spec.rb +337 -359
- data.tar.gz.sig +1 -0
- metadata +35 -5
- metadata.gz.sig +0 -0
@@ -24,12 +24,12 @@ include Treequel::TestConstants
|
|
24
24
|
include Treequel::Constants
|
25
25
|
|
26
26
|
#####################################################################
|
27
|
-
###
|
27
|
+
### C O N T E X T S
|
28
28
|
#####################################################################
|
29
29
|
|
30
30
|
describe Treequel::Branch do
|
31
31
|
include Treequel::SpecHelpers,
|
32
|
-
|
32
|
+
Treequel::Matchers
|
33
33
|
|
34
34
|
|
35
35
|
before( :all ) do
|
@@ -41,7 +41,8 @@ describe Treequel::Branch do
|
|
41
41
|
end
|
42
42
|
|
43
43
|
before( :each ) do
|
44
|
-
@
|
44
|
+
@conn = mock( "ldap connection object" )
|
45
|
+
@directory = get_fixtured_directory( @conn )
|
45
46
|
end
|
46
47
|
|
47
48
|
after( :each ) do
|
@@ -60,9 +61,9 @@ describe Treequel::Branch do
|
|
60
61
|
}.to raise_error( ArgumentError, /invalid dn/i )
|
61
62
|
end
|
62
63
|
|
63
|
-
it "can be constructed from an entry returned from LDAP::Conn.search_ext2"
|
64
|
+
it "can be constructed from an entry returned from LDAP::Conn.search_ext2" do
|
64
65
|
entry = {
|
65
|
-
'dn'
|
66
|
+
'dn' => [TEST_PERSON_DN],
|
66
67
|
TEST_PERSON_DN_ATTR => TEST_PERSON_DN_VALUE,
|
67
68
|
}
|
68
69
|
branch = Treequel::Branch.new_from_entry( entry, @directory )
|
@@ -71,16 +72,16 @@ describe Treequel::Branch do
|
|
71
72
|
branch.entry.should == entry
|
72
73
|
end
|
73
74
|
|
74
|
-
it "can be constructed from an entry with Symbol keys"
|
75
|
+
it "can be constructed from an entry with Symbol keys" do
|
75
76
|
entry = {
|
76
|
-
:dn
|
77
|
+
:dn => [TEST_PERSON_DN],
|
77
78
|
TEST_PERSON_DN_ATTR => TEST_PERSON_DN_VALUE,
|
78
79
|
}
|
79
80
|
branch = Treequel::Branch.new_from_entry( entry, @directory )
|
80
81
|
|
81
82
|
branch.rdn_attributes.should == { TEST_PERSON_DN_ATTR => [TEST_PERSON_DN_VALUE] }
|
82
83
|
branch.entry.should == {
|
83
|
-
'dn'
|
84
|
+
'dn' => [TEST_PERSON_DN],
|
84
85
|
TEST_PERSON_DN_ATTR => TEST_PERSON_DN_VALUE,
|
85
86
|
}
|
86
87
|
end
|
@@ -110,18 +111,18 @@ describe Treequel::Branch do
|
|
110
111
|
describe "instances" do
|
111
112
|
|
112
113
|
before( :each ) do
|
114
|
+
@entry = {
|
115
|
+
'description' => ["A string", "another string"],
|
116
|
+
'l' => [ 'Antartica', 'Galapagos' ],
|
117
|
+
'objectClass' => ['organizationalUnit'],
|
118
|
+
'rev' => ['03eca02ba232'],
|
119
|
+
'ou' => ['Hosts'],
|
120
|
+
}
|
121
|
+
@conn.stub( :bound? ).and_return( false )
|
122
|
+
@conn.stub( :search_ext2 ).
|
123
|
+
with( TEST_HOSTS_DN, LDAP::LDAP_SCOPE_BASE, "(objectClass=*)" ).
|
124
|
+
and_return([ @entry ])
|
113
125
|
@branch = Treequel::Branch.new( @directory, TEST_HOSTS_DN )
|
114
|
-
|
115
|
-
@schema = mock( "treequel schema" )
|
116
|
-
@entry = mock( "entry object" )
|
117
|
-
@directory.stub( :schema ).and_return( @schema )
|
118
|
-
@directory.stub( :get_entry ).and_return( @entry )
|
119
|
-
@directory.stub( :base_dn ).and_return( TEST_BASE_DN )
|
120
|
-
@schema.stub( :attribute_types ).
|
121
|
-
and_return({ :cn => :a_value, :ou => :a_value })
|
122
|
-
|
123
|
-
@syntax = stub( "attribute ldapSyntax object", :oid => OIDS::STRING_SYNTAX )
|
124
|
-
@attribute_type = mock( "schema attribute type object", :syntax => @syntax )
|
125
126
|
end
|
126
127
|
|
127
128
|
|
@@ -143,7 +144,6 @@ describe Treequel::Branch do
|
|
143
144
|
end
|
144
145
|
|
145
146
|
it "can return itself as an ldap:// URI" do
|
146
|
-
@directory.should_receive( :uri ).and_return( URI.parse("ldap://#{TEST_HOST}/#{TEST_BASE_DN}") )
|
147
147
|
@branch.uri.to_s.should == "ldap://#{TEST_HOST}/#{TEST_HOSTS_DN}?"
|
148
148
|
end
|
149
149
|
|
@@ -173,48 +173,66 @@ describe Treequel::Branch do
|
|
173
173
|
end
|
174
174
|
|
175
175
|
it "knows that it exists in the directory if it can fetch its entry" do
|
176
|
-
@directory.should_receive( :get_entry ).with( @branch ).exactly( :once ).
|
177
|
-
and_return( :the_entry )
|
178
176
|
@branch.exists?.should be_true()
|
179
177
|
end
|
180
178
|
|
181
179
|
it "knows that it doesn't exist in the directory if it can't fetch its entry" do
|
182
|
-
@
|
183
|
-
|
180
|
+
@conn.stub( :search_ext2 ).
|
181
|
+
with( TEST_HOSTS_DN, LDAP::LDAP_SCOPE_BASE, "(objectClass=*)" ).
|
182
|
+
and_return([])
|
184
183
|
@branch.exists?.should be_false()
|
185
184
|
end
|
186
185
|
|
187
186
|
it "fetch their LDAP::Entry from the directory if they don't already have one" do
|
188
|
-
@
|
189
|
-
|
187
|
+
@conn.should_receive( :search_ext2 ).
|
188
|
+
with( TEST_HOSTS_DN, LDAP::LDAP_SCOPE_BASE, "(objectClass=*)" ).
|
189
|
+
exactly( :once ).
|
190
|
+
and_return([ @entry ])
|
190
191
|
|
191
|
-
@branch.entry.should ==
|
192
|
-
@branch.entry.should ==
|
192
|
+
@branch.entry.should == @entry
|
193
|
+
@branch.entry.should == @entry # this should fetch the cached one
|
193
194
|
end
|
194
195
|
|
195
196
|
it "fetch their LDAP::Entry with operational attributes if include_operational_attrs is set" do
|
196
|
-
@
|
197
|
-
|
198
|
-
|
199
|
-
and_return( :the_extended_entry )
|
197
|
+
@conn.should_receive( :search_ext2 ).
|
198
|
+
with( TEST_HOSTS_DN, LDAP::LDAP_SCOPE_BASE, "(objectClass=*)", ["*", "+"] ).once.
|
199
|
+
and_return([ @entry ])
|
200
200
|
|
201
|
-
@branch.
|
201
|
+
@branch.include_operational_attrs = true
|
202
|
+
@branch.entry.should == @entry
|
202
203
|
end
|
203
204
|
|
204
205
|
it "can search its directory for values using itself as a base" do
|
205
|
-
|
206
|
-
|
207
|
-
|
206
|
+
subentry = {'objectClass' => ['ipHost'], 'dn' => [TEST_HOST_DN] }
|
207
|
+
@conn.should_receive( :search_ext2 ).
|
208
|
+
with( TEST_HOSTS_DN, LDAP::LDAP_SCOPE_ONELEVEL, "(objectClass=*)",
|
209
|
+
["*"], false, nil, nil, 0, 0, 0, "", nil ).
|
210
|
+
and_return([ subentry ])
|
211
|
+
|
212
|
+
branches = @branch.search( :one, '(objectClass=*)' )
|
213
|
+
|
214
|
+
branches.should have( 1 ).member
|
215
|
+
branches.first.should be_a( Treequel::Branch )
|
216
|
+
branches.first.dn.should == TEST_HOST_DN
|
208
217
|
end
|
209
218
|
|
210
219
|
it "can search its directory for values with a block" do
|
211
|
-
|
212
|
-
|
220
|
+
subentry = {
|
221
|
+
'objectClass' => ['ipHost'],
|
222
|
+
TEST_HOST_DN_ATTR => [TEST_HOST_DN_VALUE],
|
223
|
+
'dn' => [TEST_HOST_DN],
|
224
|
+
}
|
225
|
+
@conn.should_receive( :search_ext2 ).
|
226
|
+
with( TEST_HOSTS_DN, LDAP::LDAP_SCOPE_ONELEVEL, "(objectClass=*)",
|
227
|
+
["*"], false, nil, nil, 0, 0, 0, "", nil ).
|
228
|
+
and_return([ subentry ])
|
229
|
+
|
213
230
|
yielded_val = nil
|
214
231
|
@branch.search( :one, '(objectClass=*)' ) do |val|
|
215
232
|
yielded_val = val
|
216
233
|
end
|
217
|
-
yielded_val.should
|
234
|
+
yielded_val.should be_a( Treequel::Branch )
|
235
|
+
yielded_val.entry.should == subentry
|
218
236
|
end
|
219
237
|
|
220
238
|
it "clears any cached values if its include_operational_attrs attribute is changed" do
|
@@ -254,9 +272,6 @@ describe Treequel::Branch do
|
|
254
272
|
end
|
255
273
|
|
256
274
|
it "create sub-branches for messages that match valid attributeType OIDs" do
|
257
|
-
@schema.should_receive( :attribute_types ).twice.
|
258
|
-
and_return({ :cn => :a_value, :ou => :a_value })
|
259
|
-
|
260
275
|
rval = @branch.cn( 'rondori' )
|
261
276
|
rval.dn.should == "cn=rondori,#{TEST_HOSTS_DN}"
|
262
277
|
|
@@ -265,9 +280,6 @@ describe Treequel::Branch do
|
|
265
280
|
end
|
266
281
|
|
267
282
|
it "create sub-branches for messages with additional attribute pairs" do
|
268
|
-
@schema.should_receive( :attribute_types ).
|
269
|
-
and_return({ :cn => :a_value, :ou => :a_value, :l => :a_value })
|
270
|
-
|
271
283
|
rval = @branch.cn( 'rondori', :l => 'Portland' )
|
272
284
|
rval.dn.should == "cn=rondori+l=Portland,#{TEST_HOSTS_DN}"
|
273
285
|
|
@@ -276,21 +288,17 @@ describe Treequel::Branch do
|
|
276
288
|
end
|
277
289
|
|
278
290
|
it "don't create sub-branches for messages that don't match valid attributeType OIDs" do
|
279
|
-
@
|
280
|
-
|
281
|
-
|
282
|
-
lambda {
|
291
|
+
@conn.stub( :bound? ).and_return( false )
|
292
|
+
expect {
|
283
293
|
@branch.facelart( 'sbc' )
|
284
|
-
}.
|
294
|
+
}.to raise_exception( NoMethodError, /undefined method.*facelart/i )
|
285
295
|
end
|
286
296
|
|
287
297
|
it "don't create sub-branches for multi-value RDNs with an invalid attribute" do
|
288
|
-
@
|
289
|
-
|
290
|
-
|
291
|
-
lambda {
|
298
|
+
@conn.stub( :bound? ).and_return( false )
|
299
|
+
expect {
|
292
300
|
@branch.cn( 'benchlicker', :facelart => 'sbc' )
|
293
|
-
}.
|
301
|
+
}.to raise_exception( NoMethodError, /invalid secondary attribute.*facelart/i )
|
294
302
|
end
|
295
303
|
|
296
304
|
it "can return all of its immediate children as Branches" do
|
@@ -300,308 +308,256 @@ describe Treequel::Branch do
|
|
300
308
|
end
|
301
309
|
|
302
310
|
it "can return its parent as a Branch" do
|
303
|
-
|
304
|
-
@branch.
|
305
|
-
Treequel::Branch.should_receive( :new ).with( @directory, TEST_BASE_DN ).
|
306
|
-
and_return( parent_branch )
|
307
|
-
@branch.parent.should == parent_branch
|
311
|
+
@branch.parent.should be_a( Treequel::Branch )
|
312
|
+
@branch.parent.dn.should == TEST_BASE_DN
|
308
313
|
end
|
309
314
|
|
310
315
|
|
311
316
|
it "can construct a Treequel::Branchset that uses it as its base" do
|
312
|
-
branchset
|
313
|
-
|
314
|
-
and_return( branchset )
|
315
|
-
|
316
|
-
@branch.branchset.should == branchset
|
317
|
+
@branch.branchset.should be_a( Treequel::Branchset )
|
318
|
+
@branch.branchset.base_dn.should == @branch.dn
|
317
319
|
end
|
318
320
|
|
319
321
|
it "can create a filtered Treequel::Branchset for itself" do
|
320
|
-
branchset =
|
321
|
-
Treequel::Branchset.should_receive( :new ).with( @branch ).
|
322
|
-
and_return( branchset )
|
323
|
-
branchset.should_receive( :filter ).with( {:cn => 'acme'} ).
|
324
|
-
and_return( :a_filtered_branchset )
|
322
|
+
branchset = @branch.filter( :cn => 'acme' )
|
325
323
|
|
326
|
-
|
324
|
+
branchset.should be_a( Treequel::Branchset )
|
325
|
+
branchset.filter_string.should =~ /\(cn=acme\)/
|
327
326
|
end
|
328
327
|
|
329
328
|
it "doesn't restrict the number of arguments passed to #filter (bugfix)" do
|
330
|
-
branchset =
|
331
|
-
Treequel::Branchset.should_receive( :new ).with( @branch ).
|
332
|
-
and_return( branchset )
|
333
|
-
branchset.should_receive( :filter ).with( :uid, [:glumpy, :grumpy, :glee] ).
|
334
|
-
and_return( :a_filtered_branchset )
|
329
|
+
branchset = @branch.filter( :uid => [:rev, :grumpy, :glee] )
|
335
330
|
|
336
|
-
|
331
|
+
branchset.should be_a( Treequel::Branchset )
|
332
|
+
branchset.filter_string.should =~ /\(\|\(uid=rev\)\(uid=grumpy\)\(uid=glee\)\)/i
|
337
333
|
end
|
338
334
|
|
339
335
|
it "can create a scoped Treequel::Branchset for itself" do
|
340
|
-
branchset =
|
341
|
-
Treequel::Branchset.should_receive( :new ).with( @branch ).
|
342
|
-
and_return( branchset )
|
343
|
-
branchset.should_receive( :scope ).with( :onelevel ).
|
344
|
-
and_return( :a_scoped_branchset )
|
336
|
+
branchset = @branch.scope( :onelevel )
|
345
337
|
|
346
|
-
|
338
|
+
branchset.should be_a( Treequel::Branchset )
|
339
|
+
branchset.scope.should == :onelevel
|
347
340
|
end
|
348
341
|
|
349
342
|
it "can create a selective Treequel::Branchset for itself" do
|
350
|
-
branchset =
|
351
|
-
Treequel::Branchset.should_receive( :new ).with( @branch ).
|
352
|
-
and_return( branchset )
|
353
|
-
branchset.should_receive( :select ).with( :uid, :l, :familyName, :givenName ).
|
354
|
-
and_return( :a_selective_branchset )
|
343
|
+
branchset = @branch.select( :uid, :l, :familyName, :givenName )
|
355
344
|
|
356
|
-
|
345
|
+
branchset.should be_a( Treequel::Branchset )
|
346
|
+
branchset.select.should == %w[uid l familyName givenName]
|
357
347
|
end
|
358
348
|
|
359
349
|
it "knows which objectClasses it has" do
|
360
|
-
|
361
|
-
|
362
|
-
@schema.should_receive( :attribute_types ).and_return({ :objectClass => oc_attr })
|
363
|
-
oc_attr.should_receive( :single? ).and_return( false )
|
364
|
-
oc_attr.should_receive( :syntax ).at_least( :once ).
|
365
|
-
and_return( string_syntax )
|
366
|
-
|
367
|
-
@entry.should_receive( :[] ).with( 'objectClass' ).at_least( :once ).
|
368
|
-
and_return([ 'organizationalUnit', 'extensibleObject' ])
|
369
|
-
|
370
|
-
@directory.should_receive( :convert_to_object ).
|
371
|
-
with( OIDS::STRING_SYNTAX, 'organizationalUnit' ).
|
372
|
-
and_return( 'organizationalUnit' )
|
373
|
-
@directory.should_receive( :convert_to_object ).
|
374
|
-
with( OIDS::STRING_SYNTAX, 'extensibleObject' ).
|
375
|
-
and_return( 'extensibleObject' )
|
376
|
-
|
377
|
-
@schema.should_receive( :object_classes ).twice.and_return({
|
378
|
-
:organizationalUnit => :ou_objectclass,
|
379
|
-
:extensibleObject => :extobj_objectclass,
|
380
|
-
})
|
381
|
-
|
382
|
-
@branch.object_classes.should == [ :ou_objectclass, :extobj_objectclass ]
|
350
|
+
@branch.object_classes.
|
351
|
+
should include( @directory.schema.object_classes[:organizationalUnit] )
|
383
352
|
end
|
384
353
|
|
385
354
|
it "knows what operational attributes it has" do
|
386
|
-
op_attrs =
|
387
|
-
|
388
|
-
|
389
|
-
end
|
390
|
-
@schema.should_receive( :operational_attribute_types ).and_return( op_attrs.values )
|
355
|
+
op_attrs = @directory.schema.attribute_types.values.select do |attrtype|
|
356
|
+
attrtype.operational?
|
357
|
+
end.uniq
|
391
358
|
|
392
|
-
@branch.operational_attribute_types.should
|
359
|
+
@branch.operational_attribute_types.should have( op_attrs.length ).members
|
360
|
+
@branch.operational_attribute_types.should include( *op_attrs )
|
393
361
|
end
|
394
362
|
|
395
363
|
it "knows what the OIDs of its operational attributes are" do
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
end
|
400
|
-
@schema.should_receive( :operational_attribute_types ).at_least( :once ).
|
401
|
-
and_return( op_attrs.values )
|
364
|
+
op_oids = @directory.schema.attribute_types.values.select do |attrtype|
|
365
|
+
attrtype.operational?
|
366
|
+
end.uniq.map( &:oid )
|
402
367
|
|
403
|
-
@branch.operational_attribute_oids.should have(
|
404
|
-
@branch.operational_attribute_oids.should include(
|
368
|
+
@branch.operational_attribute_oids.should have( op_oids.length ).members
|
369
|
+
@branch.operational_attribute_oids.should include( *op_oids )
|
405
370
|
end
|
406
371
|
|
407
372
|
it "can return the set of all its MUST attributes' OIDs based on which objectClasses " +
|
408
373
|
"it has" do
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
@branch.should_receive( :object_classes ).and_return([ oc1, oc2 ])
|
413
|
-
oc1.should_receive( :must_oids ).at_least( :once ).and_return([ :oid1, :oid2 ])
|
414
|
-
oc2.should_receive( :must_oids ).at_least( :once ).and_return([ :oid1, :oid3 ])
|
374
|
+
@conn.stub( :search_ext2 ).
|
375
|
+
with( TEST_HOSTS_DN, LDAP::LDAP_SCOPE_BASE, "(objectClass=*)" ).
|
376
|
+
and_return([ {'objectClass' => %w[ipHost device ieee802device]} ])
|
415
377
|
|
416
378
|
must_oids = @branch.must_oids
|
417
379
|
must_oids.should have( 3 ).members
|
418
|
-
must_oids.should include( :
|
380
|
+
must_oids.should include( :objectClass, :cn, :ipHostNumber )
|
419
381
|
end
|
420
382
|
|
421
383
|
it "can return the set of all its MUST attributeTypes based on which objectClasses it has" do
|
422
|
-
|
423
|
-
|
384
|
+
@conn.stub( :search_ext2 ).
|
385
|
+
with( TEST_HOSTS_DN, LDAP::LDAP_SCOPE_BASE, "(objectClass=*)" ).
|
386
|
+
and_return([ {'objectClass' => %w[ipHost device ieee802device]} ])
|
424
387
|
|
425
|
-
|
426
|
-
|
427
|
-
oc2.should_receive( :must ).at_least( :once ).and_return([ :cn, :l ])
|
388
|
+
expected_attrtypes = @directory.schema.attribute_types.
|
389
|
+
values_at( :objectClass, :cn, :ipHostNumber )
|
428
390
|
|
429
391
|
must_attrs = @branch.must_attribute_types
|
430
392
|
must_attrs.should have( 3 ).members
|
431
|
-
must_attrs.should include(
|
393
|
+
must_attrs.should include( *expected_attrtypes )
|
432
394
|
end
|
433
395
|
|
434
396
|
it "can return a Hash pre-populated with pairs that correspond to its MUST attributes" do
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
cn_attrtype.should_receive( :name ).at_least( :once ).and_return( :cn )
|
440
|
-
l_attrtype.should_receive( :name ).at_least( :once ).and_return( :l )
|
441
|
-
objectClass_attrtype.should_receive( :name ).at_least( :once ).and_return( :objectClass )
|
442
|
-
|
443
|
-
@branch.should_receive( :must_attribute_types ).at_least( :once ).
|
444
|
-
and_return([ cn_attrtype, l_attrtype, objectClass_attrtype ])
|
397
|
+
@conn.stub( :search_ext2 ).
|
398
|
+
with( TEST_HOSTS_DN, LDAP::LDAP_SCOPE_BASE, "(objectClass=*)" ).
|
399
|
+
and_return([ {'objectClass' => %w[ipHost device ieee802device]} ])
|
445
400
|
|
446
401
|
@branch.must_attributes_hash.
|
447
|
-
should
|
402
|
+
should include({ :cn => [''], :ipHostNumber => [''], :objectClass => [:top] })
|
448
403
|
end
|
449
404
|
|
450
405
|
|
451
406
|
it "can return the set of all its MAY attributes' OIDs based on which objectClasses " +
|
452
407
|
"it has" do
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
@branch.should_receive( :object_classes ).and_return([ oc1, oc2 ])
|
457
|
-
oc1.should_receive( :may_oids ).at_least( :once ).and_return([ :oid1, :oid2 ])
|
458
|
-
oc2.should_receive( :may_oids ).at_least( :once ).and_return([ :oid1, :oid3 ])
|
408
|
+
@conn.stub( :search_ext2 ).
|
409
|
+
with( TEST_HOSTS_DN, LDAP::LDAP_SCOPE_BASE, "(objectClass=*)" ).
|
410
|
+
and_return([ {'objectClass' => %w[ipHost device ieee802device]} ])
|
459
411
|
|
460
412
|
must_oids = @branch.may_oids
|
461
|
-
must_oids.should have(
|
462
|
-
must_oids.should include( :
|
413
|
+
must_oids.should have( 9 ).members
|
414
|
+
must_oids.should include( :l, :description, :manager, :serialNumber, :seeAlso,
|
415
|
+
:owner, :ou, :o, :macAddress )
|
463
416
|
end
|
464
417
|
|
465
418
|
it "can return the set of all its MAY attributeTypes based on which objectClasses it has" do
|
466
|
-
|
467
|
-
|
419
|
+
@conn.should_receive( :search_ext2 ).
|
420
|
+
with( TEST_HOSTS_DN, LDAP::LDAP_SCOPE_BASE, "(objectClass=*)" ).
|
421
|
+
and_return([ {'objectClass' => %w[ipHost device ieee802device]} ])
|
468
422
|
|
469
|
-
@
|
470
|
-
|
471
|
-
oc2.should_receive( :may ).and_return([ :chunktype ])
|
423
|
+
expected_attrtypes = @directory.schema.attribute_types.values_at( :l, :description,
|
424
|
+
:manager, :serialNumber, :seeAlso, :owner, :ou, :o, :macAddress )
|
472
425
|
|
473
|
-
|
474
|
-
must_attrs.should have( 3 ).members
|
475
|
-
must_attrs.should include( :description, :mobilePhone, :chunktype )
|
426
|
+
@branch.may_attribute_types.should include( *expected_attrtypes )
|
476
427
|
end
|
477
428
|
|
478
429
|
it "can return a Hash pre-populated with pairs that correspond to its MAY attributes" do
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
430
|
+
@conn.should_receive( :search_ext2 ).
|
431
|
+
with( TEST_HOSTS_DN, LDAP::LDAP_SCOPE_BASE, "(objectClass=*)" ).
|
432
|
+
and_return([ {'objectClass' => %w[ipHost device ieee802device]} ])
|
433
|
+
|
434
|
+
@branch.may_attributes_hash.should include({
|
435
|
+
:l => [],
|
436
|
+
:description => [],
|
437
|
+
:manager => [],
|
438
|
+
:serialNumber => [],
|
439
|
+
:seeAlso => [],
|
440
|
+
:owner => [],
|
441
|
+
:ou => [],
|
442
|
+
:o => [],
|
443
|
+
:macAddress => []
|
444
|
+
})
|
490
445
|
end
|
491
446
|
|
492
447
|
it "can return the set of all of its valid attributeTypes, which is a union of its " +
|
493
448
|
"MUST and MAY attributes plus the directory's operational attributes" do
|
494
|
-
@branch.should_receive( :must_attribute_types ).
|
495
|
-
and_return([ :cn, :l, :uid ])
|
496
|
-
@branch.should_receive( :may_attribute_types ).
|
497
|
-
and_return([ :description, :mobilePhone, :chunktype ])
|
498
|
-
@branch.should_receive( :operational_attribute_types ).
|
499
|
-
and_return([ :createTimestamp, :creatorsName ])
|
500
|
-
|
501
449
|
all_attrs = @branch.valid_attribute_types
|
502
450
|
|
503
|
-
all_attrs.should have(
|
504
|
-
all_attrs.should include( :
|
505
|
-
|
451
|
+
all_attrs.should have( 54 ).members
|
452
|
+
all_attrs.should include( @directory.schema.attribute_types[:ou] )
|
453
|
+
all_attrs.should include( @directory.schema.attribute_types[:l] )
|
506
454
|
end
|
507
455
|
|
456
|
+
|
508
457
|
it "can return a Hash pre-populated with pairs that correspond to all of its valid " +
|
509
458
|
"attributes" do
|
510
|
-
@branch.should_receive( :must_attributes_hash ).at_least( :once ).
|
511
|
-
and_return({ :cn => [''], :l => '', :objectClass => [:top] })
|
512
|
-
@branch.should_receive( :may_attributes_hash ).at_least( :once ).
|
513
|
-
and_return({ :description => nil, :givenName => [], :cn => nil })
|
514
|
-
|
515
459
|
@branch.valid_attributes_hash.should == {
|
516
|
-
:
|
517
|
-
:
|
518
|
-
:
|
519
|
-
:
|
520
|
-
:
|
460
|
+
:objectClass => [:top],
|
461
|
+
:ou => [''],
|
462
|
+
:userPassword => [],
|
463
|
+
:searchGuide => [],
|
464
|
+
:seeAlso => [],
|
465
|
+
:businessCategory => [],
|
466
|
+
:x121Address => [],
|
467
|
+
:registeredAddress => [],
|
468
|
+
:destinationIndicator => [],
|
469
|
+
:telexNumber => [],
|
470
|
+
:teletexTerminalIdentifier => [],
|
471
|
+
:telephoneNumber => [],
|
472
|
+
:internationaliSDNNumber => [],
|
473
|
+
:facsimileTelephoneNumber => [],
|
474
|
+
:street => [],
|
475
|
+
:postOfficeBox => [],
|
476
|
+
:postalCode => [],
|
477
|
+
:postalAddress => [],
|
478
|
+
:physicalDeliveryOfficeName => [],
|
479
|
+
:st => [],
|
480
|
+
:l => [],
|
481
|
+
:description => [],
|
482
|
+
:preferredDeliveryMethod => nil,
|
521
483
|
}
|
522
484
|
end
|
523
485
|
|
524
486
|
|
525
487
|
it "can return the set of all of its valid attribute OIDs, which is a union of its " +
|
526
488
|
"MUST and MAY attribute OIDs" do
|
527
|
-
@branch.should_receive( :must_oids ).
|
528
|
-
and_return([ :must_oid1, :must_oid2 ])
|
529
|
-
@branch.should_receive( :may_oids ).
|
530
|
-
and_return([ :may_oid1, :may_oid2, :must_oid1 ])
|
531
|
-
|
532
489
|
all_attr_oids = @branch.valid_attribute_oids
|
533
490
|
|
534
|
-
all_attr_oids.should have(
|
535
|
-
|
491
|
+
all_attr_oids.should have( 23 ).members all_attr_oids.should include(
|
492
|
+
:objectClass, :ou,
|
493
|
+
:userPassword, :searchGuide, :seeAlso, :businessCategory, :x121Address,
|
494
|
+
:registeredAddress, :destinationIndicator, :telexNumber, :teletexTerminalIdentifier,
|
495
|
+
:telephoneNumber, :internationaliSDNNumber, :facsimileTelephoneNumber, :street,
|
496
|
+
:postOfficeBox, :postalCode, :postalAddress, :physicalDeliveryOfficeName, :st, :l,
|
497
|
+
:description, :preferredDeliveryMethod
|
498
|
+
)
|
536
499
|
end
|
537
500
|
|
538
501
|
it "knows if an attribute is valid given its objectClasses" do
|
539
|
-
|
540
|
-
|
541
|
-
@branch.should_receive( :valid_attribute_types ).
|
542
|
-
twice.
|
543
|
-
and_return([ attrtype ])
|
544
|
-
|
545
|
-
attrtype.should_receive( :valid_name? ).with( :uid ).and_return( true )
|
546
|
-
attrtype.should_receive( :valid_name? ).with( :rubberChicken ).and_return( false )
|
547
|
-
|
548
|
-
@branch.valid_attribute?( :uid ).should be_true()
|
502
|
+
@branch.valid_attribute?( :ou ).should be_true()
|
549
503
|
@branch.valid_attribute?( :rubberChicken ).should be_false()
|
550
504
|
end
|
551
505
|
|
552
506
|
it "can be moved to a new location within the directory" do
|
553
|
-
newdn = "ou=
|
554
|
-
@
|
507
|
+
newdn = "ou=oldhosts,#{TEST_BASE_DN}"
|
508
|
+
@conn.should_receive( :modrdn ).
|
509
|
+
with( TEST_HOSTS_DN, "ou=oldhosts", true )
|
555
510
|
@branch.move( newdn )
|
556
511
|
end
|
557
512
|
|
558
513
|
|
559
514
|
it "resets any cached data when its DN changes" do
|
560
|
-
@
|
561
|
-
|
515
|
+
@conn.should_receive( :search_ext2 ).
|
516
|
+
with( TEST_HOSTS_DN, LDAP::LDAP_SCOPE_BASE, "(objectClass=*)" ).
|
517
|
+
and_return([ @entry ])
|
518
|
+
@branch.entry
|
562
519
|
|
520
|
+
@branch.dn = TEST_SUBHOSTS_DN
|
521
|
+
|
522
|
+
@conn.should_receive( :search_ext2 ).
|
523
|
+
with( TEST_SUBHOSTS_DN, LDAP::LDAP_SCOPE_BASE, "(objectClass=*)" ).
|
524
|
+
and_return([ @entry ])
|
563
525
|
@branch.entry
|
564
|
-
@branch.dn = TEST_HOSTS_DN
|
565
|
-
@branch.entry.should == :second_entry
|
566
526
|
end
|
567
527
|
|
568
528
|
|
569
529
|
it "can create children under itself" do
|
570
530
|
newattrs = {
|
571
531
|
:ipHostNumber => '127.0.0.1',
|
572
|
-
:objectClass => [:ipHost],
|
532
|
+
:objectClass => [:ipHost, :device],
|
573
533
|
}
|
574
|
-
@directory.should_receive( :create ).
|
575
|
-
with( an_instance_of(Treequel::Branch), newattrs ).
|
576
|
-
and_return( true )
|
577
534
|
|
578
|
-
@
|
535
|
+
@conn.should_receive( :add ).
|
536
|
+
with( "cn=chillyt,#{TEST_HOSTS_DN}",
|
537
|
+
"ipHostNumber"=>["127.0.0.1"],
|
538
|
+
"objectClass"=>["ipHost", "device"],
|
539
|
+
"cn"=>["chillyt"] )
|
540
|
+
|
541
|
+
res = @branch.cn( :chillyt ).create( newattrs )
|
542
|
+
res.should be_a( Treequel::Branch )
|
543
|
+
res.dn.should == "cn=chillyt,#{TEST_HOSTS_DN}"
|
579
544
|
end
|
580
545
|
|
581
546
|
|
582
547
|
it "can copy itself to a sibling entry" do
|
583
|
-
|
584
|
-
Treequel::Branch.should_receive( :new ).with( @directory, TEST_PERSON2_DN ).
|
585
|
-
and_return( newbranch )
|
586
|
-
@entry.should_receive( :merge ).with( {} ).and_return( :merged_attributes )
|
587
|
-
@directory.should_receive( :create ).with( newbranch, :merged_attributes ).
|
588
|
-
and_return( true )
|
548
|
+
@conn.should_receive( :add ).with( TEST_SUBHOSTS_DN, @entry )
|
589
549
|
|
590
|
-
@branch.copy(
|
550
|
+
newbranch = @branch.copy( TEST_SUBHOSTS_DN )
|
551
|
+
newbranch.dn.should == TEST_SUBHOSTS_DN
|
591
552
|
end
|
592
553
|
|
593
554
|
|
594
555
|
it "can copy itself to a sibling entry with attribute changes" do
|
595
|
-
|
596
|
-
|
597
|
-
newbranch = stub( "copied sibling branch" )
|
598
|
-
Treequel::Branch.should_receive( :new ).with( @directory, TEST_PERSON2_DN ).
|
599
|
-
and_return( newbranch )
|
600
|
-
@entry.should_receive( :merge ).with( newattrs ).and_return( newattrs )
|
601
|
-
@directory.should_receive( :create ).with( newbranch, newattrs ).
|
602
|
-
and_return( true )
|
556
|
+
@conn.should_receive( :add ).
|
557
|
+
with( TEST_SUBHOSTS_DN, @entry.merge('description' => ['Hosts in a subdomain.']) )
|
603
558
|
|
604
|
-
@branch.copy(
|
559
|
+
newbranch = @branch.copy( TEST_SUBHOSTS_DN, :description => ['Hosts in a subdomain.'] )
|
560
|
+
newbranch.dn.should == TEST_SUBHOSTS_DN
|
605
561
|
end
|
606
562
|
|
607
563
|
|
@@ -610,55 +566,47 @@ describe Treequel::Branch do
|
|
610
566
|
:displayName => 'Chilly T. Penguin',
|
611
567
|
:description => "A chilly little penguin.",
|
612
568
|
}
|
613
|
-
|
614
|
-
|
569
|
+
@conn.should_receive( :modify ).
|
570
|
+
with( TEST_HOSTS_DN,
|
571
|
+
'displayName' => ['Chilly T. Penguin'],
|
572
|
+
'description' => ["A chilly little penguin."] )
|
615
573
|
|
616
574
|
@branch.merge( attributes )
|
617
575
|
end
|
618
576
|
|
619
577
|
|
620
578
|
it "can delete all values of one of its entry's individual attributes" do
|
621
|
-
LDAP::Mod.
|
622
|
-
|
623
|
-
@directory.should_receive( :modify ).with( @branch, [:mod_delete] )
|
624
|
-
|
579
|
+
mod = LDAP::Mod.new( LDAP::LDAP_MOD_DELETE, 'displayName', [] )
|
580
|
+
@conn.should_receive( :modify ).with( TEST_HOSTS_DN, [mod] )
|
625
581
|
@branch.delete( :displayName )
|
626
582
|
end
|
627
583
|
|
628
584
|
it "can delete all values of more than one of its entry's individual attributes" do
|
629
|
-
LDAP::Mod.
|
630
|
-
|
631
|
-
|
632
|
-
and_return( :second_mod_delete )
|
633
|
-
@directory.should_receive( :modify ).
|
634
|
-
with( @branch, [:first_mod_delete, :second_mod_delete] )
|
585
|
+
mod1 = LDAP::Mod.new( LDAP::LDAP_MOD_DELETE, 'displayName', [] )
|
586
|
+
mod2 = LDAP::Mod.new( LDAP::LDAP_MOD_DELETE, 'gecos', [] )
|
587
|
+
@conn.should_receive( :modify ).with( TEST_HOSTS_DN, [mod1, mod2] )
|
635
588
|
|
636
589
|
@branch.delete( :displayName, :gecos )
|
637
590
|
end
|
638
591
|
|
639
592
|
it "can delete one particular value of its entry's individual attributes" do
|
640
|
-
LDAP::Mod.
|
641
|
-
|
642
|
-
and_return( :mod_delete )
|
643
|
-
@directory.should_receive( :modify ).with( @branch, [:mod_delete] )
|
593
|
+
mod = LDAP::Mod.new( LDAP::LDAP_MOD_DELETE, 'objectClass', ['apple-user'] )
|
594
|
+
@conn.should_receive( :modify ).with( TEST_HOSTS_DN, [mod] )
|
644
595
|
|
645
596
|
@branch.delete( :objectClass => 'apple-user' )
|
646
597
|
end
|
647
598
|
|
648
599
|
it "can delete particular values of more than one of its entry's individual attributes" do
|
649
|
-
LDAP::Mod.
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
with( LDAP::LDAP_MOD_DELETE, 'cn', [] ).and_return( :second_mod_delete )
|
654
|
-
@directory.should_receive( :modify ).
|
655
|
-
with( @branch, array_including(:first_mod_delete, :second_mod_delete) )
|
600
|
+
mod1 = LDAP::Mod.new( LDAP::LDAP_MOD_DELETE, 'objectClass',
|
601
|
+
['apple-user', 'inetOrgPerson'] )
|
602
|
+
mod2 = LDAP::Mod.new( LDAP::LDAP_MOD_DELETE, 'cn', [] )
|
603
|
+
@conn.should_receive( :modify ).with( TEST_HOSTS_DN, array_including(mod1, mod2) )
|
656
604
|
|
657
605
|
@branch.delete( :objectClass => ['apple-user',:inetOrgPerson], :cn => [] )
|
658
606
|
end
|
659
607
|
|
660
608
|
it "deletes its entry entirely if no attributes are specified" do
|
661
|
-
@
|
609
|
+
@conn.should_receive( :delete ).with( TEST_HOSTS_DN )
|
662
610
|
@branch.delete
|
663
611
|
end
|
664
612
|
|
@@ -669,15 +617,13 @@ describe Treequel::Branch do
|
|
669
617
|
|
670
618
|
|
671
619
|
it "knows how to represent itself as LDIF" do
|
672
|
-
@entry.should_receive( :keys ).and_return([ 'description', 'l' ])
|
673
|
-
@entry.should_receive( :[] ).with( 'description' ).
|
674
|
-
and_return([ 'A chilly little penguin.' ])
|
675
|
-
@entry.should_receive( :[] ).with( 'l' ).
|
676
|
-
and_return([ 'Antartica', 'Galapagos' ])
|
677
|
-
|
678
620
|
ldif = @branch.to_ldif
|
679
621
|
ldif.should =~ /dn: #{TEST_HOSTS_DN_ATTR}=#{TEST_HOSTS_DN_VALUE},#{TEST_BASE_DN}/i
|
680
|
-
ldif.should =~ /description: A
|
622
|
+
ldif.should =~ /description: A string/
|
623
|
+
ldif.should =~ /description: another string/
|
624
|
+
ldif.should =~ /l: Antartica/
|
625
|
+
ldif.should =~ /l: Galapagos/
|
626
|
+
ldif.should =~ /ou: Hosts/
|
681
627
|
end
|
682
628
|
|
683
629
|
|
@@ -688,11 +634,13 @@ describe Treequel::Branch do
|
|
688
634
|
'we see the entire universe.'
|
689
635
|
|
690
636
|
it "knows how to split long lines in LDIF output" do
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
637
|
+
entry = {
|
638
|
+
'description' => [LONG_TEST_VALUE],
|
639
|
+
'l' => [ 'Antartica', 'Galapagos' ]
|
640
|
+
}
|
641
|
+
@conn.stub( :search_ext2 ).
|
642
|
+
with( TEST_HOSTS_DN, LDAP::LDAP_SCOPE_BASE, "(objectClass=*)" ).
|
643
|
+
and_return([ entry ])
|
696
644
|
|
697
645
|
ldif = @branch.to_ldif( 20 )
|
698
646
|
val = ldif[ /(description: (?:[^\n]|\n )+)/, 1 ]
|
@@ -724,11 +672,13 @@ describe Treequel::Branch do
|
|
724
672
|
END_VALUE
|
725
673
|
|
726
674
|
it "knows how to split long binary lines in LDIF output" do
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
675
|
+
entry = {
|
676
|
+
'description' => [LONG_BINARY_TEST_VALUE],
|
677
|
+
'l' => [ 'Antartica', 'Galapagos' ]
|
678
|
+
}
|
679
|
+
@conn.stub( :search_ext2 ).
|
680
|
+
with( TEST_HOSTS_DN, LDAP::LDAP_SCOPE_BASE, "(objectClass=*)" ).
|
681
|
+
and_return([ entry ])
|
732
682
|
|
733
683
|
ldif = @branch.to_ldif( 20 )
|
734
684
|
ldif.scan( /^description/ ).length.should == 1
|
@@ -744,16 +694,12 @@ describe Treequel::Branch do
|
|
744
694
|
|
745
695
|
|
746
696
|
it "knows how to represent itself as a Hash" do
|
747
|
-
@entry.should_receive( :keys ).and_return([ 'description', 'dn', 'l' ])
|
748
|
-
@entry.should_receive( :[] ).with( 'description' ).
|
749
|
-
and_return([ 'A chilly little penguin.' ])
|
750
|
-
@entry.should_receive( :[] ).with( 'l' ).
|
751
|
-
and_return([ 'Antartica', 'Galapagos' ])
|
752
|
-
|
753
697
|
@branch.to_hash.should == {
|
754
|
-
'description' => ['A
|
698
|
+
'description' => ['A string', 'another string'],
|
755
699
|
'l' => [ 'Antartica', 'Galapagos' ],
|
756
|
-
'
|
700
|
+
'objectClass' => ['organizationalUnit'],
|
701
|
+
'ou' => ['Hosts'],
|
702
|
+
'rev' => ['03eca02ba232']
|
757
703
|
}
|
758
704
|
end
|
759
705
|
|
@@ -761,12 +707,11 @@ describe Treequel::Branch do
|
|
761
707
|
"Branch" do
|
762
708
|
other_branch = Treequel::Branch.new( @directory, TEST_SUBHOSTS_DN )
|
763
709
|
|
764
|
-
|
765
|
-
Treequel::Branchset.should_receive( :new ).with( other_branch ).and_return( :other_branchset )
|
766
|
-
Treequel::BranchCollection.should_receive( :new ).with( :branchset, :other_branchset ).
|
767
|
-
and_return( :a_collection )
|
710
|
+
coll = (@branch + other_branch)
|
768
711
|
|
769
|
-
(
|
712
|
+
coll.should be_a( Treequel::BranchCollection )
|
713
|
+
coll.branchsets.should have( 2 ).members
|
714
|
+
coll.branchsets.map( &:base_dn ).should include( TEST_HOSTS_DN, TEST_SUBHOSTS_DN )
|
770
715
|
end
|
771
716
|
|
772
717
|
|
@@ -774,61 +719,69 @@ describe Treequel::Branch do
|
|
774
719
|
describe "index fetch operator" do
|
775
720
|
|
776
721
|
it "fetches a multi-value attribute as an Array of Strings" do
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
@
|
783
|
-
|
784
|
-
|
785
|
-
|
722
|
+
entry = {
|
723
|
+
'description' => ["A string", "another string"],
|
724
|
+
'l' => [ 'Antartica', 'Galapagos' ],
|
725
|
+
'objectClass' => ['organizationalUnit'],
|
726
|
+
}
|
727
|
+
@conn.should_receive( :search_ext2 ).
|
728
|
+
with( TEST_HOSTS_DN, LDAP::LDAP_SCOPE_BASE, "(objectClass=*)" ).
|
729
|
+
and_return([ entry ])
|
730
|
+
|
731
|
+
@branch[ :description ].should include( 'A string', 'another string' )
|
732
|
+
@branch[ :l ].should include( 'Galapagos', 'Antartica' )
|
786
733
|
end
|
787
734
|
|
788
735
|
it "fetches a single-value attribute as a scalar String" do
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
736
|
+
test_dn = "cn=ssh,cn=www,#{TEST_HOSTS_DN}"
|
737
|
+
entry = {
|
738
|
+
'ipServicePort' => ['22'],
|
739
|
+
'ipServiceProtocol' => ['tcp'],
|
740
|
+
'objectClass' => ['ipService'],
|
741
|
+
'cn' => ['www'],
|
742
|
+
}
|
743
|
+
@conn.should_receive( :search_ext2 ).
|
744
|
+
with( test_dn, LDAP::LDAP_SCOPE_BASE, "(objectClass=*)" ).
|
745
|
+
and_return([ entry ])
|
746
|
+
|
747
|
+
branch = Treequel::Branch.new( @directory, test_dn )
|
748
|
+
|
749
|
+
branch[ :ipServicePort ].should == 22
|
750
|
+
branch[ :ipServiceProtocol ].should == ['tcp']
|
798
751
|
end
|
799
752
|
|
800
753
|
it "returns the entry without conversion if there is no such attribute in the schema" do
|
801
|
-
@
|
802
|
-
@entry.should_receive( :[] ).with( 'glumpy' ).at_least( :once ).
|
803
|
-
and_return([ 'glumpa1' ])
|
804
|
-
@branch[ :glumpy ].should == [ 'glumpa1' ]
|
754
|
+
@branch[ :rev ].should == [ '03eca02ba232' ]
|
805
755
|
end
|
806
756
|
|
807
757
|
it "returns nil if record doesn't have the attribute set" do
|
808
|
-
@
|
809
|
-
@branch[ :glumpy ].should == nil
|
758
|
+
@branch[ :cn ].should == nil
|
810
759
|
end
|
811
760
|
|
812
761
|
it "caches the value fetched from its entry" do
|
813
|
-
@
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
2.times { @branch[ :
|
762
|
+
@conn.should_receive( :search_ext2 ).
|
763
|
+
with( TEST_HOSTS_DN, LDAP::LDAP_SCOPE_BASE, "(objectClass=*)" ).
|
764
|
+
exactly( :once ).
|
765
|
+
and_return([ @entry ])
|
766
|
+
|
767
|
+
2.times { @branch[ :description ] }
|
819
768
|
end
|
820
769
|
|
821
|
-
it "
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
770
|
+
it "converts objects via the conversions set in its directory" do
|
771
|
+
test_dn = "cn=ssh,cn=www,#{TEST_HOSTS_DN}"
|
772
|
+
entry = {
|
773
|
+
'ipServicePort' => ['22'],
|
774
|
+
'ipServiceProtocol' => ['tcp'],
|
775
|
+
'objectClass' => ['ipService'],
|
776
|
+
'cn' => ['www'],
|
777
|
+
}
|
778
|
+
@conn.should_receive( :search_ext2 ).
|
779
|
+
with( test_dn, LDAP::LDAP_SCOPE_BASE, "(objectClass=*)" ).
|
780
|
+
and_return([ entry ])
|
781
|
+
|
782
|
+
branch = Treequel::Branch.new( @directory, test_dn )
|
783
|
+
|
784
|
+
branch[ :ipServicePort ].should be_a( Fixnum )
|
832
785
|
end
|
833
786
|
|
834
787
|
end
|
@@ -837,40 +790,65 @@ describe Treequel::Branch do
|
|
837
790
|
describe "index set operator" do
|
838
791
|
|
839
792
|
it "writes a single value attribute via its directory" do
|
840
|
-
|
841
|
-
|
842
|
-
|
793
|
+
test_dn = "cn=ssh,cn=www,#{TEST_HOSTS_DN}"
|
794
|
+
entry = {
|
795
|
+
'ipServicePort' => ['22'],
|
796
|
+
'ipServiceProtocol' => ['tcp'],
|
797
|
+
'objectClass' => ['ipService'],
|
798
|
+
'cn' => ['www'],
|
799
|
+
}
|
800
|
+
@conn.should_receive( :search_ext2 ).
|
801
|
+
with( test_dn, LDAP::LDAP_SCOPE_BASE, "(objectClass=*)" ).
|
802
|
+
and_return([ entry ])
|
803
|
+
branch = Treequel::Branch.new( @directory, test_dn )
|
804
|
+
|
805
|
+
@conn.should_receive( :modify ).
|
806
|
+
with( test_dn, {'ipServicePort' => ['22022']} )
|
807
|
+
|
808
|
+
branch[ :ipServicePort ] = 22022
|
843
809
|
end
|
844
810
|
|
845
|
-
it "
|
846
|
-
|
847
|
-
|
848
|
-
|
811
|
+
it "converts values for non-single attribute types to an Array" do
|
812
|
+
test_dn = "cn=laptops,ou=computerlists,ou=macosxodconfig,#{TEST_BASE_DN}"
|
813
|
+
entry = {
|
814
|
+
'dn' => [test_dn],
|
815
|
+
'cn' => ['laptops'],
|
816
|
+
'objectClass' => ['apple-computer-list'],
|
817
|
+
'apple-computers' => %w[chernobyl beetlejuice toronaga],
|
818
|
+
'apple-generateduid' => ['3f73981a-07bb-11e0-b21a-fb56fb48ba42'],
|
819
|
+
}
|
820
|
+
@conn.should_receive( :search_ext2 ).
|
821
|
+
with( test_dn, LDAP::LDAP_SCOPE_BASE, "(objectClass=*)" ).
|
822
|
+
and_return([ entry ])
|
823
|
+
branch = Treequel::Branch.new( @directory, test_dn )
|
824
|
+
|
825
|
+
@conn.should_receive( :modify ).
|
826
|
+
with( test_dn, "apple-computers" =>
|
827
|
+
["chernobyl", "beetlejuice", "toronaga", "glider", "creeper"] )
|
828
|
+
|
829
|
+
branch[ 'apple-computers' ] += %w[glider creeper]
|
849
830
|
end
|
850
831
|
|
851
|
-
it "
|
852
|
-
|
853
|
-
|
854
|
-
@attribute_type.stub( :syntax ).and_return( @syntax )
|
855
|
-
@directory.stub( :convert_to_object ).and_return {|_,val| val }
|
856
|
-
@entry.should_receive( :[] ).with( 'glorpy' ).and_return( [:firstval], [:secondval] )
|
832
|
+
it "writes multiple attribute values via its directory" do
|
833
|
+
test_dn = "cn=ssh,cn=www,#{TEST_HOSTS_DN}"
|
834
|
+
branch = Treequel::Branch.new( @directory, test_dn )
|
857
835
|
|
858
|
-
@
|
859
|
-
|
860
|
-
|
836
|
+
@conn.should_receive( :modify ).
|
837
|
+
with( test_dn,
|
838
|
+
'ipServicePort' => ['56'],
|
839
|
+
'ipServiceProtocol' => ['udp']
|
840
|
+
)
|
861
841
|
|
862
|
-
|
863
|
-
@branch[ :glorpy ] = 'chunks'
|
864
|
-
@branch[ :glorpy ].should == :secondval
|
842
|
+
branch.merge( :ipServicePort => 56, :ipServiceProtocol => 'udp' )
|
865
843
|
end
|
866
844
|
end
|
867
845
|
|
868
846
|
it "can fetch multiple values via #values_at" do
|
869
|
-
@branch.
|
870
|
-
@branch.should_receive( :[] ).with( :desc ).and_return( :desc_value )
|
871
|
-
@branch.should_receive( :[] ).with( :l ).and_return( :l_value )
|
847
|
+
results = @branch.values_at( TEST_HOSTS_DN_ATTR, :description )
|
872
848
|
|
873
|
-
|
849
|
+
results.should have( 2 ).members
|
850
|
+
results.first.should == [TEST_HOSTS_DN_VALUE]
|
851
|
+
results.last.should == @entry['description']
|
874
852
|
end
|
875
853
|
|
876
854
|
end
|