treequel 1.2.2 → 1.3.0pre384
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.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/treequel/model_spec.rb
CHANGED
@@ -59,6 +59,7 @@ describe Treequel::Model do
|
|
59
59
|
and_return( [:inetOrgPerson] )
|
60
60
|
mixin.should_receive( :model_bases ).at_least( :once ).
|
61
61
|
and_return( [] )
|
62
|
+
|
62
63
|
Treequel::Model.register_mixin( mixin )
|
63
64
|
Treequel::Model.mixins_for_objectclasses( :inetOrgPerson ).should include( mixin )
|
64
65
|
end
|
@@ -69,6 +70,7 @@ describe Treequel::Model do
|
|
69
70
|
and_return( [:inetOrgPerson, :organizationalPerson] )
|
70
71
|
mixin.should_receive( :model_bases ).at_least( :once ).
|
71
72
|
and_return( [] )
|
73
|
+
|
72
74
|
Treequel::Model.register_mixin( mixin )
|
73
75
|
Treequel::Model.mixins_for_objectclasses( :inetOrgPerson, :organizationalPerson ).
|
74
76
|
should include( mixin )
|
@@ -80,6 +82,7 @@ describe Treequel::Model do
|
|
80
82
|
and_return( [] )
|
81
83
|
mixin.should_receive( :model_bases ).at_least( :once ).
|
82
84
|
and_return( [TEST_PEOPLE_DN] )
|
85
|
+
|
83
86
|
Treequel::Model.register_mixin( mixin )
|
84
87
|
Treequel::Model.mixins_for_dn( TEST_PEOPLE_DN ).should include( mixin )
|
85
88
|
end
|
@@ -90,6 +93,7 @@ describe Treequel::Model do
|
|
90
93
|
and_return( [] )
|
91
94
|
mixin.should_receive( :model_bases ).at_least( :once ).
|
92
95
|
and_return( [TEST_PEOPLE_DN] )
|
96
|
+
|
93
97
|
Treequel::Model.register_mixin( mixin )
|
94
98
|
Treequel::Model.mixins_for_dn( TEST_PERSON_DN ).should include( mixin )
|
95
99
|
end
|
@@ -101,7 +105,6 @@ describe Treequel::Model do
|
|
101
105
|
mixin.should_receive( :model_bases ).at_least( :once ).and_return( [] )
|
102
106
|
|
103
107
|
Treequel::Model.register_mixin( mixin )
|
104
|
-
|
105
108
|
Treequel::Model.mixins_for_dn( TEST_PERSON_DN ).should include( mixin )
|
106
109
|
end
|
107
110
|
|
@@ -142,6 +145,29 @@ describe Treequel::Model do
|
|
142
145
|
obj.should_not be_a( mixin3 )
|
143
146
|
end
|
144
147
|
|
148
|
+
it "extends dups of new instances with registered mixins" do
|
149
|
+
mixin1 = Module.new do
|
150
|
+
extend Treequel::Model::ObjectClass
|
151
|
+
model_bases TEST_HOSTS_DN, TEST_SUBHOSTS_DN
|
152
|
+
model_objectclasses :ipHost
|
153
|
+
end
|
154
|
+
mixin2 = Module.new do
|
155
|
+
extend Treequel::Model::ObjectClass
|
156
|
+
model_bases TEST_HOSTS_DN
|
157
|
+
model_objectclasses :device
|
158
|
+
end
|
159
|
+
mixin3 = Module.new do
|
160
|
+
extend Treequel::Model::ObjectClass
|
161
|
+
model_objectclasses :person
|
162
|
+
end
|
163
|
+
|
164
|
+
obj = Treequel::Model.new( @directory, TEST_SUBHOST_DN, @simple_entry ).dup
|
165
|
+
|
166
|
+
obj.should be_a( mixin1 )
|
167
|
+
obj.should_not be_a( mixin2 )
|
168
|
+
obj.should_not be_a( mixin3 )
|
169
|
+
end
|
170
|
+
|
145
171
|
it "extends new instances with mixins that are implied by objectClass SUP attributes, too" do
|
146
172
|
inherited_mixin = Module.new do
|
147
173
|
extend Treequel::Model::ObjectClass
|
@@ -184,6 +210,37 @@ describe Treequel::Model do
|
|
184
210
|
obj.should_not be_a( mixin3 )
|
185
211
|
end
|
186
212
|
|
213
|
+
it "applies applicable mixins to instances which have objectClasses added to them" do
|
214
|
+
mixin1 = Module.new do
|
215
|
+
extend Treequel::Model::ObjectClass
|
216
|
+
model_bases TEST_HOSTS_DN, TEST_SUBHOSTS_DN
|
217
|
+
model_objectclasses :ipHost
|
218
|
+
end
|
219
|
+
mixin2 = Module.new do
|
220
|
+
extend Treequel::Model::ObjectClass
|
221
|
+
model_bases TEST_HOSTS_DN
|
222
|
+
model_objectclasses :device
|
223
|
+
end
|
224
|
+
|
225
|
+
@conn.stub( :search_ext2 ).
|
226
|
+
with( TEST_HOST_DN, LDAP::LDAP_SCOPE_BASE, "(objectClass=*)" ).
|
227
|
+
and_return( [] )
|
228
|
+
obj = Treequel::Model.new( @directory, TEST_HOST_DN )
|
229
|
+
|
230
|
+
obj.extensions.should be_empty()
|
231
|
+
|
232
|
+
obj.object_class += [ :ipHost ]
|
233
|
+
obj.extensions.should have( 1 ).member
|
234
|
+
obj.should be_a( mixin1 )
|
235
|
+
obj.should_not be_a( mixin2 )
|
236
|
+
|
237
|
+
obj.object_class += [ :device ]
|
238
|
+
obj.extensions.should have( 2 ).members
|
239
|
+
obj.should be_a( mixin1 )
|
240
|
+
obj.should be_a( mixin2 )
|
241
|
+
|
242
|
+
end
|
243
|
+
|
187
244
|
it "doesn't try to apply objectclasses to non-existant entries" do
|
188
245
|
mixin1 = Module.new do
|
189
246
|
extend Treequel::Model::ObjectClass
|
@@ -241,23 +298,31 @@ describe Treequel::Model do
|
|
241
298
|
@directory.should_receive( :get_entry ).with( @obj ).and_return( @entry )
|
242
299
|
@obj.cn.should == ['Slappy the Frog']
|
243
300
|
end
|
301
|
+
|
244
302
|
end
|
245
303
|
|
246
304
|
|
247
|
-
describe "objects
|
305
|
+
describe "objects loaded from entries" do
|
248
306
|
|
249
307
|
before( :all ) do
|
250
308
|
@entry = {
|
251
|
-
'dn'
|
252
|
-
'uid'
|
253
|
-
'cn'
|
254
|
-
'givenName'
|
255
|
-
'sn'
|
256
|
-
'l'
|
257
|
-
'title'
|
258
|
-
'displayName'
|
259
|
-
'logonTime'
|
260
|
-
'
|
309
|
+
'dn' => ['uid=slappy,ou=people,dc=acme,dc=com'],
|
310
|
+
'uid' => ['slappy'],
|
311
|
+
'cn' => ['Slappy the Frog'],
|
312
|
+
'givenName' => ['Slappy'],
|
313
|
+
'sn' => ['Frog'],
|
314
|
+
'l' => ['a forest in England'],
|
315
|
+
'title' => ['Forest Fire Prevention Advocate'],
|
316
|
+
'displayName' => ['Slappy the Frog'],
|
317
|
+
'logonTime' => ['1293167318'],
|
318
|
+
'uidNumber' => ['1121'],
|
319
|
+
'gidNumber' => ['200'],
|
320
|
+
'homeDirectory' => ['/u/j/jrandom'],
|
321
|
+
'description' => [
|
322
|
+
'Smokey the Bear is much more intense in person.',
|
323
|
+
'Alright.'
|
324
|
+
],
|
325
|
+
'objectClass' => %w[
|
261
326
|
top
|
262
327
|
person
|
263
328
|
organizationalPerson
|
@@ -275,103 +340,63 @@ describe Treequel::Model do
|
|
275
340
|
|
276
341
|
|
277
342
|
it "provides readers for valid attributes" do
|
278
|
-
attrtype = stub( "Treequel attributeType object", :name => :uid )
|
279
|
-
|
280
|
-
@obj.should_receive( :valid_attribute_type ).with( :uid ).and_return( attrtype )
|
281
|
-
@obj.should_receive( :[] ).with( :uid ).and_return( ['slappy'] )
|
282
|
-
|
283
343
|
@obj.uid.should == ['slappy']
|
284
344
|
end
|
285
345
|
|
286
|
-
it "
|
287
|
-
|
288
|
-
|
289
|
-
@obj.should_receive( :valid_attribute_type ).with( :given_name ).and_return( nil )
|
290
|
-
@obj.should_receive( :valid_attribute_type ).with( :givenName ).and_return( attrtype )
|
291
|
-
@obj.should_receive( :[] ).with( :givenName ).and_return( ['Slappy'] )
|
346
|
+
it "provides readers for single-letter attributes" do
|
347
|
+
@obj.l.should == ['a forest in England']
|
348
|
+
end
|
292
349
|
|
350
|
+
it "normalizes underbarred readers for camelCased attributes" do
|
293
351
|
@obj.given_name.should == ['Slappy']
|
294
352
|
end
|
295
353
|
|
296
354
|
it "falls through to branch-traversal for a reader with arguments" do
|
297
|
-
@obj.
|
298
|
-
|
299
|
-
|
300
|
-
@obj.should_receive( :traverse_branch ).
|
301
|
-
with( :dc, :admin, {} ).and_return( :a_child_branch )
|
302
|
-
|
303
|
-
@obj.dc( :admin ).should == :a_child_branch
|
355
|
+
result = @obj.dc( :admin )
|
356
|
+
result.should be_a( Treequel::Model )
|
357
|
+
result.dn.should == "dc=admin,#{@entry['dn'].first}"
|
304
358
|
end
|
305
359
|
|
306
360
|
it "accommodates branch-traversal from its auto-generated readers" do
|
307
|
-
@obj.
|
308
|
-
@obj.uid.should == ['slappy']
|
309
|
-
|
361
|
+
@obj.uid # Generate the reader, which should then do traversal, too
|
310
362
|
@obj.uid( :slappy ).should be_a( Treequel::Model )
|
311
363
|
end
|
312
364
|
|
313
365
|
it "provides writers for valid singular attributes" do
|
314
|
-
|
315
|
-
|
316
|
-
@obj.should_receive( :valid_attribute_type ).with( :logonTime ).and_return( attrtype )
|
317
|
-
@obj.should_receive( :[]= ).with( :logonTime, 'stampley' )
|
318
|
-
|
319
|
-
@obj.logonTime = 'stampley'
|
366
|
+
@obj.logonTime.should equal( 1293167318 )
|
320
367
|
end
|
321
368
|
|
322
369
|
it "provides writers for valid non-singular attributes that accept a non-array" do
|
323
|
-
attrtype = stub( "Treequel attributeType object", :name => :uid, :single? => false )
|
324
|
-
|
325
|
-
@obj.should_receive( :valid_attribute_type ).with( :uid ).and_return( attrtype )
|
326
|
-
@obj.should_receive( :[]= ).with( :uid, ['stampley'] )
|
327
|
-
|
328
370
|
@obj.uid = 'stampley'
|
371
|
+
@obj.uid.should == ['stampley']
|
329
372
|
end
|
330
373
|
|
331
374
|
it "provides a predicate that tests true for valid singular attributes that are set" do
|
332
|
-
|
333
|
-
|
334
|
-
@obj.should_receive( :valid_attribute_type ).with( :activated ).and_return( attrtype )
|
335
|
-
@obj.should_receive( :[] ).with( :activated ).and_return( :a_time_object )
|
336
|
-
|
337
|
-
@obj.should be_activated()
|
375
|
+
@obj.display_name?.should be_true()
|
338
376
|
end
|
339
377
|
|
340
378
|
it "provides a predicate that tests false for valid singular attributes that are not set" do
|
341
|
-
|
342
|
-
|
343
|
-
@obj.should_receive( :valid_attribute_type ).with( :deactivated ).and_return( attrtype )
|
344
|
-
@obj.should_receive( :[] ).with( :deactivated ).and_return( nil )
|
345
|
-
|
346
|
-
@obj.should_not be_deactivated()
|
379
|
+
@obj.delete( :displayName )
|
380
|
+
@obj.display_name?.should be_false()
|
347
381
|
end
|
348
382
|
|
349
383
|
it "provides a predicate that tests true for valid non-singular attributes that have " +
|
350
384
|
"at least one value" do
|
351
|
-
|
352
|
-
|
353
|
-
@obj.should_receive( :valid_attribute_type ).with( :description ).and_return( attrtype )
|
354
|
-
@obj.should_receive( :[] ).with( :description ).
|
355
|
-
and_return([ 'Racoon City', 'St-Michael Clock Tower' ])
|
385
|
+
@obj.should have_given_name()
|
386
|
+
end
|
356
387
|
|
357
|
-
|
388
|
+
it "provides a predicate that tests true for single-letter non-singular attributes " +
|
389
|
+
"that have at least one value" do
|
390
|
+
@obj.should have_l()
|
358
391
|
end
|
359
392
|
|
360
393
|
it "provides a predicate that tests false for valid non-singular attributes that don't " +
|
361
394
|
"have at least one value" do
|
362
|
-
|
363
|
-
|
364
|
-
@obj.should_receive( :valid_attribute_type ).with( :locality_name ).and_return( attrtype )
|
365
|
-
@obj.should_receive( :[] ).with( :l ).
|
366
|
-
and_return([])
|
367
|
-
|
368
|
-
@obj.should_not have_locality_name()
|
395
|
+
@obj.delete( :givenName )
|
396
|
+
@obj.should_not have_given_name()
|
369
397
|
end
|
370
398
|
|
371
399
|
it "falls through to the default proxy method for invalid attributes" do
|
372
|
-
@obj.stub( :valid_attribute_type ).and_return( nil )
|
373
|
-
@entry.should_not_receive( :[] )
|
374
|
-
|
375
400
|
expect {
|
376
401
|
@obj.nonexistant
|
377
402
|
}.to raise_exception( NoMethodError, /undefined method/i )
|
@@ -379,26 +404,214 @@ describe Treequel::Model do
|
|
379
404
|
|
380
405
|
it "adds the objectClass attribute to the attribute list when executing a search that " +
|
381
406
|
"contains a select" do
|
382
|
-
@
|
383
|
-
|
384
|
-
|
385
|
-
|
407
|
+
@conn.should_receive( :search_ext2 ).
|
408
|
+
with( @entry['dn'].first, LDAP::LDAP_SCOPE_ONELEVEL, "(cn=magnelion)",
|
409
|
+
["cn", "objectClass"], false, nil, nil, 0, 0, 0, "", nil ).
|
410
|
+
and_return( [] )
|
411
|
+
@obj.search( :one, '(cn=magnelion)', :selectattrs => ['cn'] )
|
386
412
|
end
|
387
413
|
|
388
414
|
it "doesn't add the objectClass attribute to the attribute list when the search " +
|
389
415
|
"doesn't contain a select" do
|
390
|
-
@
|
391
|
-
|
392
|
-
|
393
|
-
|
416
|
+
@conn.should_receive( :search_ext2 ).
|
417
|
+
with( @entry['dn'].first, LDAP::LDAP_SCOPE_ONELEVEL, "(cn=ephelion)",
|
418
|
+
['*'], false, nil, nil, 0, 0, 0, "", nil ).
|
419
|
+
and_return( [] )
|
420
|
+
@obj.search( :one, '(cn=ephelion)' )
|
394
421
|
end
|
395
422
|
|
396
423
|
it "knows which attribute methods it responds to" do
|
397
|
-
@directory.stub( :convert_to_object ).and_return {|oid,str| str }
|
398
424
|
@obj.should respond_to( :cn )
|
399
425
|
@obj.should_not respond_to( :humpsize )
|
400
426
|
end
|
401
427
|
|
428
|
+
it "defers writing modifications via #[]= back to the directory" do
|
429
|
+
@conn.should_not_receive( :modify )
|
430
|
+
@obj[ :uid ] = 'slippy'
|
431
|
+
@obj.uid.should == ['slippy']
|
432
|
+
end
|
433
|
+
|
434
|
+
it "defers writing modifications via #merge back to the directory" do
|
435
|
+
@conn.should_not_receive( :modify )
|
436
|
+
@obj.merge( :uid => 'slippy', :givenName => 'Slippy' )
|
437
|
+
@obj.uid.should == ['slippy']
|
438
|
+
@obj.given_name.should == ['Slippy']
|
439
|
+
end
|
440
|
+
|
441
|
+
it "defers writing modifications via #delete back to the directory" do
|
442
|
+
@conn.should_not_receive( :modify )
|
443
|
+
@obj.delete( :uid, :givenName )
|
444
|
+
@obj.uid.should == []
|
445
|
+
@obj.given_name.should == []
|
446
|
+
end
|
447
|
+
|
448
|
+
it "knows if any validation errors have been encountered" do
|
449
|
+
@obj.errors.should be_a( Hash )
|
450
|
+
end
|
451
|
+
|
452
|
+
|
453
|
+
context "with no modified attributes" do
|
454
|
+
|
455
|
+
it "knows that it hasn't been modified" do
|
456
|
+
@obj.should_not be_modified()
|
457
|
+
end
|
458
|
+
|
459
|
+
it "doesn't write anything to the directory when its #save method is called" do
|
460
|
+
@conn.should_not_receive( :modify )
|
461
|
+
@obj.save
|
462
|
+
end
|
463
|
+
|
464
|
+
end
|
465
|
+
|
466
|
+
|
467
|
+
context "with a single modified attribute" do
|
468
|
+
|
469
|
+
before( :each ) do
|
470
|
+
@obj.uid = 'slippy'
|
471
|
+
end
|
472
|
+
|
473
|
+
it "knows that is has been modified" do
|
474
|
+
@obj.should be_modified()
|
475
|
+
end
|
476
|
+
|
477
|
+
it "can return the modification as a list of LDAP::Mod objects" do
|
478
|
+
result = @obj.modifications
|
479
|
+
|
480
|
+
result.should be_an( Array )
|
481
|
+
result.should have( 2 ).members
|
482
|
+
result.should include( ldap_mod_add 'uid', 'slippy' )
|
483
|
+
result.should include( ldap_mod_delete 'uid', 'slappy' )
|
484
|
+
end
|
485
|
+
|
486
|
+
it "reverts the attribute if its #revert method is called" do
|
487
|
+
@conn.should_receive( :search_ext2 ).
|
488
|
+
with( @entry['dn'].first, LDAP::LDAP_SCOPE_BASE, "(objectClass=*)" ).
|
489
|
+
and_return([ @entry ])
|
490
|
+
|
491
|
+
@obj.revert
|
492
|
+
|
493
|
+
@obj.uid.should == @entry['uid']
|
494
|
+
@obj.should_not be_modified()
|
495
|
+
end
|
496
|
+
|
497
|
+
it "updates the modified attribute when saved" do
|
498
|
+
@conn.should_receive( :modify ).
|
499
|
+
with( "uid=slappy,ou=people,dc=acme,dc=com",
|
500
|
+
[ldap_mod_delete(:uid, 'slappy'), ldap_mod_add(:uid, 'slippy')] )
|
501
|
+
@obj.save
|
502
|
+
end
|
503
|
+
|
504
|
+
end
|
505
|
+
|
506
|
+
|
507
|
+
context "with several modified attributes" do
|
508
|
+
|
509
|
+
before( :each ) do
|
510
|
+
@obj.uid = 'fappy'
|
511
|
+
@obj.given_name = 'Fappy'
|
512
|
+
@obj.display_name = 'Fappy the Bear'
|
513
|
+
@obj.delete( :l )
|
514
|
+
@obj.delete( :description => 'Alright.' )
|
515
|
+
@obj.description << "The new mascot."
|
516
|
+
end
|
517
|
+
|
518
|
+
it "knows that is has been modified" do
|
519
|
+
@obj.should be_modified()
|
520
|
+
end
|
521
|
+
|
522
|
+
it "returns the modified values via its accessors" do
|
523
|
+
@obj.uid.should == ['fappy']
|
524
|
+
@obj.given_name.should == ['Fappy']
|
525
|
+
@obj.display_name.should == 'Fappy the Bear' # SINGLE
|
526
|
+
@obj.l.should == []
|
527
|
+
@obj.description.should have( 2 ).members
|
528
|
+
@obj.description.should include(
|
529
|
+
"Smokey the Bear is much more intense in person.",
|
530
|
+
"The new mascot."
|
531
|
+
)
|
532
|
+
end
|
533
|
+
|
534
|
+
it "can return the modifications as a list of LDAP::Mod objects" do
|
535
|
+
result = @obj.modifications
|
536
|
+
|
537
|
+
result.should be_an( Array )
|
538
|
+
result.should have( 9 ).members
|
539
|
+
result.should include( ldap_mod_delete :uid, 'slappy' )
|
540
|
+
result.should include( ldap_mod_add :uid, 'fappy' )
|
541
|
+
result.should include( ldap_mod_delete :givenName, 'Slappy' )
|
542
|
+
result.should include( ldap_mod_add :givenName, 'Fappy' )
|
543
|
+
result.should include( ldap_mod_delete :displayName, 'Slappy the Frog' )
|
544
|
+
result.should include( ldap_mod_add :displayName, 'Fappy the Bear' )
|
545
|
+
result.should include( ldap_mod_add :description, 'The new mascot.' )
|
546
|
+
result.should include( ldap_mod_delete :description, 'Alright.' )
|
547
|
+
result.should include( ldap_mod_delete :l, 'a forest in England' )
|
548
|
+
|
549
|
+
end
|
550
|
+
|
551
|
+
it "reverts all of the attributes if its #revert method is called" do
|
552
|
+
@conn.should_receive( :search_ext2 ).
|
553
|
+
with( @entry['dn'].first, LDAP::LDAP_SCOPE_BASE, "(objectClass=*)" ).
|
554
|
+
and_return([ @entry ])
|
555
|
+
|
556
|
+
@obj.revert
|
557
|
+
|
558
|
+
@obj.uid.should == @entry['uid']
|
559
|
+
@obj.given_name.should == @entry['givenName']
|
560
|
+
@obj.display_name.should == @entry['displayName'].first # SINGLE
|
561
|
+
@obj.l.should == @entry['l']
|
562
|
+
@obj.should_not be_modified()
|
563
|
+
end
|
564
|
+
|
565
|
+
end
|
566
|
+
|
567
|
+
end
|
568
|
+
|
569
|
+
describe "objects created in memory" do
|
570
|
+
|
571
|
+
before( :all ) do
|
572
|
+
@entry = {
|
573
|
+
'uid' => ['jrandom'],
|
574
|
+
'cn' => ['James'],
|
575
|
+
'sn' => ['Hacker'],
|
576
|
+
'l' => ['a forest in England'],
|
577
|
+
'displayName' => ['J. Random Hacker'],
|
578
|
+
'uidNumber' => ['1121'],
|
579
|
+
'gidNumber' => ['200'],
|
580
|
+
'homeDirectory' => ['/u/j/jrandom'],
|
581
|
+
'objectClass' => %w[
|
582
|
+
person
|
583
|
+
inetOrgPerson
|
584
|
+
posixAccount
|
585
|
+
],
|
586
|
+
}
|
587
|
+
end
|
588
|
+
|
589
|
+
before( :each ) do
|
590
|
+
@obj = Treequel::Model.new( @directory, TEST_PERSON_DN, @entry )
|
591
|
+
end
|
592
|
+
|
593
|
+
|
594
|
+
it "creates the entry in the directory when saved" do
|
595
|
+
@conn.stub( :search_ext2 ).
|
596
|
+
with( TEST_PERSON_DN, LDAP::LDAP_SCOPE_BASE, '(objectClass=*)').
|
597
|
+
and_return( [] )
|
598
|
+
|
599
|
+
@conn.should_receive( :add ).
|
600
|
+
with( TEST_PERSON_DN, [
|
601
|
+
ldap_mod_add("cn", "James"),
|
602
|
+
ldap_mod_add("displayName", "J. Random Hacker"),
|
603
|
+
ldap_mod_add("gidNumber", "200"),
|
604
|
+
ldap_mod_add("homeDirectory", "/u/j/jrandom"),
|
605
|
+
ldap_mod_add("l", "a forest in England"),
|
606
|
+
ldap_mod_add("objectClass", "inetOrgPerson", "person", "posixAccount"),
|
607
|
+
ldap_mod_add("sn", "Hacker"),
|
608
|
+
ldap_mod_add("uid", "jrandom"),
|
609
|
+
ldap_mod_add("uidNumber", "1121")
|
610
|
+
] )
|
611
|
+
|
612
|
+
@obj.save
|
613
|
+
end
|
614
|
+
|
402
615
|
end
|
403
616
|
|
404
617
|
end
|