treequel 1.2.1 → 1.2.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|