treequel 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. data/ChangeLog +354 -0
  2. data/LICENSE +27 -0
  3. data/README +66 -0
  4. data/Rakefile +345 -0
  5. data/Rakefile.local +43 -0
  6. data/bin/treeirb +14 -0
  7. data/bin/treequel +229 -0
  8. data/examples/company-directory.rb +112 -0
  9. data/examples/ldap-monitor.rb +143 -0
  10. data/examples/ldap-monitor/public/css/master.css +328 -0
  11. data/examples/ldap-monitor/public/images/card_small.png +0 -0
  12. data/examples/ldap-monitor/public/images/chain_small.png +0 -0
  13. data/examples/ldap-monitor/public/images/globe_small.png +0 -0
  14. data/examples/ldap-monitor/public/images/globe_small_green.png +0 -0
  15. data/examples/ldap-monitor/public/images/plug.png +0 -0
  16. data/examples/ldap-monitor/public/images/shadows/large-30-down.png +0 -0
  17. data/examples/ldap-monitor/public/images/tick.png +0 -0
  18. data/examples/ldap-monitor/public/images/tick_circle.png +0 -0
  19. data/examples/ldap-monitor/public/images/treequel-favicon.png +0 -0
  20. data/examples/ldap-monitor/views/backends.erb +41 -0
  21. data/examples/ldap-monitor/views/connections.erb +74 -0
  22. data/examples/ldap-monitor/views/databases.erb +39 -0
  23. data/examples/ldap-monitor/views/dump_subsystem.erb +14 -0
  24. data/examples/ldap-monitor/views/index.erb +14 -0
  25. data/examples/ldap-monitor/views/layout.erb +35 -0
  26. data/examples/ldap-monitor/views/listeners.erb +30 -0
  27. data/examples/ldap_state.rb +62 -0
  28. data/lib/treequel.rb +145 -0
  29. data/lib/treequel/branch.rb +589 -0
  30. data/lib/treequel/branchcollection.rb +204 -0
  31. data/lib/treequel/branchset.rb +360 -0
  32. data/lib/treequel/constants.rb +604 -0
  33. data/lib/treequel/directory.rb +541 -0
  34. data/lib/treequel/exceptions.rb +32 -0
  35. data/lib/treequel/filter.rb +704 -0
  36. data/lib/treequel/mixins.rb +325 -0
  37. data/lib/treequel/schema.rb +245 -0
  38. data/lib/treequel/schema/attributetype.rb +252 -0
  39. data/lib/treequel/schema/ldapsyntax.rb +96 -0
  40. data/lib/treequel/schema/matchingrule.rb +124 -0
  41. data/lib/treequel/schema/matchingruleuse.rb +124 -0
  42. data/lib/treequel/schema/objectclass.rb +289 -0
  43. data/lib/treequel/sequel_integration.rb +26 -0
  44. data/lib/treequel/utils.rb +169 -0
  45. data/rake/191_compat.rb +26 -0
  46. data/rake/dependencies.rb +76 -0
  47. data/rake/helpers.rb +434 -0
  48. data/rake/hg.rb +261 -0
  49. data/rake/manual.rb +782 -0
  50. data/rake/packaging.rb +135 -0
  51. data/rake/publishing.rb +318 -0
  52. data/rake/rdoc.rb +30 -0
  53. data/rake/style.rb +62 -0
  54. data/rake/svn.rb +668 -0
  55. data/rake/testing.rb +187 -0
  56. data/rake/verifytask.rb +64 -0
  57. data/rake/win32.rb +190 -0
  58. data/spec/lib/constants.rb +93 -0
  59. data/spec/lib/helpers.rb +100 -0
  60. data/spec/treequel/branch_spec.rb +569 -0
  61. data/spec/treequel/branchcollection_spec.rb +213 -0
  62. data/spec/treequel/branchset_spec.rb +376 -0
  63. data/spec/treequel/directory_spec.rb +487 -0
  64. data/spec/treequel/filter_spec.rb +482 -0
  65. data/spec/treequel/mixins_spec.rb +330 -0
  66. data/spec/treequel/schema/attributetype_spec.rb +237 -0
  67. data/spec/treequel/schema/ldapsyntax_spec.rb +83 -0
  68. data/spec/treequel/schema/matchingrule_spec.rb +158 -0
  69. data/spec/treequel/schema/matchingruleuse_spec.rb +137 -0
  70. data/spec/treequel/schema/objectclass_spec.rb +262 -0
  71. data/spec/treequel/schema_spec.rb +118 -0
  72. data/spec/treequel/utils_spec.rb +49 -0
  73. data/spec/treequel_spec.rb +179 -0
  74. metadata +169 -0
@@ -0,0 +1,487 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ BEGIN {
4
+ require 'pathname'
5
+ basedir = Pathname.new( __FILE__ ).dirname.parent.parent
6
+
7
+ libdir = basedir + "lib"
8
+
9
+ $LOAD_PATH.unshift( libdir ) unless $LOAD_PATH.include?( libdir )
10
+ }
11
+
12
+ begin
13
+ require 'spec'
14
+ require 'spec/lib/constants'
15
+ require 'spec/lib/helpers'
16
+
17
+ require 'treequel/directory'
18
+ require 'treequel/branch'
19
+ rescue LoadError
20
+ unless Object.const_defined?( :Gem )
21
+ require 'rubygems'
22
+ retry
23
+ end
24
+ raise
25
+ end
26
+
27
+
28
+ include Treequel::TestConstants
29
+ include Treequel::Constants
30
+
31
+ #####################################################################
32
+ ### C O N T E X T S
33
+ #####################################################################
34
+
35
+ describe Treequel::Directory do
36
+ include Treequel::SpecHelpers
37
+
38
+ before( :all ) do
39
+ setup_logging( :fatal )
40
+ end
41
+
42
+ after( :all ) do
43
+ reset_logging()
44
+ end
45
+
46
+
47
+ before( :each ) do
48
+ @options = {
49
+ :host => TEST_HOST,
50
+ :port => TEST_PORT,
51
+ :base_dn => TEST_BASE_DN,
52
+ :connect_type => :plain,
53
+ }
54
+ @conn = mock( "LDAP connection", :set_option => true, :bound? => false )
55
+ LDAP::SSLConn.stub!( :new ).and_return( @conn )
56
+ @conn.stub!( :root_dse ).and_return( nil )
57
+ end
58
+
59
+
60
+ it "is created with reasonable default options if none are specified" do
61
+ dir = Treequel::Directory.new
62
+
63
+ dir.host.should == 'localhost'
64
+ dir.port.should == 389
65
+ dir.connect_type.should == :tls
66
+ dir.base_dn.should == ''
67
+ end
68
+
69
+ it "is created with the specified options if options are specified" do
70
+ dir = Treequel::Directory.new( @options )
71
+
72
+ dir.host.should == TEST_HOST
73
+ dir.port.should == TEST_PORT
74
+ dir.connect_type.should == @options[:connect_type]
75
+ dir.base_dn.should == TEST_BASE_DN
76
+ end
77
+
78
+ it "binds immediately if user/pass is included in the ldap URI" do
79
+ conn = mock( "LDAP connection", :set_option => true )
80
+
81
+ LDAP::Conn.should_receive( :new ).with( TEST_HOST, TEST_PORT ).
82
+ and_return( conn )
83
+ conn.should_receive( :bind ).with( TEST_BIND_DN, TEST_BIND_PASS )
84
+
85
+ dir = Treequel::Directory.new( @options.merge( :bind_dn => TEST_BIND_DN, :pass => TEST_BIND_PASS ))
86
+ dir.instance_variable_get( :@bound_as ).should == TEST_BIND_DN
87
+ end
88
+
89
+ it "uses the first namingContext from the Root DSE if no base is specified" do
90
+ conn = mock( "LDAP connection", :set_option => true )
91
+ LDAP::Conn.stub!( :new ).and_return( conn )
92
+ conn.should_receive( :root_dse ).and_return( TEST_DSE )
93
+
94
+ @dir = Treequel::Directory.new( @options.merge(:base_dn => nil) )
95
+ @dir.base_dn.should == TEST_BASE_DN
96
+ end
97
+
98
+
99
+ describe "instances without existing connections" do
100
+
101
+ before( :each ) do
102
+ @dir = Treequel::Directory.new( @options )
103
+ @conn = mock( "ldap connection", :set_option => true )
104
+ end
105
+
106
+
107
+ it "stringifies as a description which includes the host, port, connection type and base" do
108
+ @dir.to_s.should =~ /#{Regexp.quote(TEST_HOST)}/
109
+ @dir.to_s.should =~ /#{TEST_PORT}/
110
+ @dir.to_s.should =~ /\b#{@dir.connect_type}\b/
111
+ @dir.to_s.should =~ /#{TEST_BASE_DN}/i
112
+ end
113
+
114
+ it "connects on demand to the configured directory server" do
115
+ LDAP::Conn.should_receive( :new ).with( TEST_HOST, TEST_PORT ).
116
+ and_return( @conn )
117
+ @dir.conn.should == @conn
118
+ end
119
+
120
+ it "connects with TLS on demand to the configured directory server if configured to do so" do
121
+ @dir.connect_type = :tls
122
+ LDAP::SSLConn.should_receive( :new ).with( TEST_HOST, TEST_PORT, true ).
123
+ and_return( @conn )
124
+ @dir.conn.should == @conn
125
+ end
126
+
127
+ it "connects over SSL on demand to the configured directory server if configured to do so" do
128
+ @dir.connect_type = :ssl
129
+ LDAP::SSLConn.should_receive( :new ).with( TEST_HOST, TEST_PORT ).
130
+ and_return( @conn )
131
+ @dir.conn.should == @conn
132
+ end
133
+ end
134
+
135
+ describe "instances with a connection" do
136
+
137
+ before( :each ) do
138
+ @conn = mock( "ldap connection", :bound? => false )
139
+
140
+ @dir = Treequel::Directory.new( @options )
141
+ @dir.instance_variable_set( :@conn, @conn )
142
+
143
+ @schema = mock( "Directory schema" )
144
+ @conn.stub!( :schema ).and_return( :the_schema )
145
+ Treequel::Schema.stub!( :new ).with( :the_schema ).and_return( @schema )
146
+ @schema.stub!( :attribute_types ).and_return({ :cn => :a_value, :ou => :a_value })
147
+ end
148
+
149
+ it "can bind with the given user DN and password" do
150
+ @conn.should_receive( :bind ).with( TEST_BIND_DN, TEST_BIND_PASS )
151
+ @dir.bind( TEST_BIND_DN, TEST_BIND_PASS )
152
+ end
153
+
154
+ it "can bind with the DN of the given Branch (or a quack-alike) and password" do
155
+ branch = stub( "branch", :dn => TEST_BIND_DN )
156
+ @conn.should_receive( :bind ).with( TEST_BIND_DN, TEST_BIND_PASS )
157
+ @dir.bind( branch, TEST_BIND_PASS )
158
+ end
159
+
160
+ it "can temporarily bind as another user for the duration of a block" do
161
+ dupconn = mock( "duplicate connection" )
162
+ @conn.should_receive( :dup ).and_return( dupconn )
163
+ dupconn.should_receive( :bind ).with( TEST_BIND_DN, TEST_BIND_PASS )
164
+ @conn.should_not_receive( :bind )
165
+
166
+ @dir.bound_as( TEST_BIND_DN, TEST_BIND_PASS ) do
167
+ @dir.conn.should == dupconn
168
+ end
169
+
170
+ @dir.conn.should == @conn
171
+ end
172
+
173
+ it "knows if its underlying connection is already bound" do
174
+ @conn.should_receive( :bound? ).and_return( false, true )
175
+ @dir.should_not be_bound()
176
+ @dir.should be_bound()
177
+ end
178
+
179
+
180
+ it "can be unbound, which replaces the bound connection with a duplicate that is unbound" do
181
+ dupconn = mock( "duplicate connection" )
182
+ @conn.should_receive( :bound? ).and_return( true )
183
+ @conn.should_receive( :dup ).and_return( dupconn )
184
+ @conn.should_receive( :unbind )
185
+
186
+ @dir.unbind
187
+
188
+ @dir.conn.should == dupconn
189
+ end
190
+
191
+
192
+ it "doesn't do anything if told to unbind but the current connection is not bound" do
193
+ @conn.should_receive( :bound? ).and_return( false )
194
+ @conn.should_not_receive( :dup )
195
+ @conn.should_not_receive( :unbind )
196
+
197
+ @dir.unbind
198
+
199
+ @dir.conn.should == @conn
200
+ end
201
+
202
+ it "can look up a Branch's corresponding LDAP::Entry hash" do
203
+ branch = mock( "branch" )
204
+
205
+ branch.should_receive( :dn ).at_least( :once ).and_return( TEST_PERSON_DN )
206
+
207
+ @conn.should_receive( :search_ext2 ).
208
+ with( TEST_PERSON_DN, LDAP::LDAP_SCOPE_BASE, '(objectClass=*)' ).
209
+ and_return([ :the_entry ])
210
+
211
+ @dir.get_entry( branch ).should == :the_entry
212
+ end
213
+
214
+ it "can look up a Branch's corresponding LDAP::Entry hash with operational attributes included" do
215
+ branch = mock( "branch" )
216
+
217
+ branch.should_receive( :dn ).at_least( :once ).and_return( TEST_PERSON_DN )
218
+
219
+ @conn.should_receive( :search_ext2 ).
220
+ with( TEST_PERSON_DN, LDAP::LDAP_SCOPE_BASE, '(objectClass=*)', ['*', '+'] ).
221
+ and_return([ :the_extended_entry ])
222
+
223
+ @dir.get_extended_entry( branch ).should == :the_extended_entry
224
+ end
225
+
226
+ it "can search for entries and return them as Sequel::Branch objects" do
227
+ base = TEST_PEOPLE_DN
228
+ filter = '(|(uid=jonlong)(uid=margento))'
229
+ branch = mock( "branch" )
230
+
231
+ found_branch1 = stub( "entry1 branch" )
232
+ found_branch2 = stub( "entry2 branch" )
233
+
234
+ # Do the search
235
+ entries = [
236
+ { 'dn' => ["uid=jonlong,#{TEST_PEOPLE_DN}"] },
237
+ { 'dn' => ["uid=margento,#{TEST_PEOPLE_DN}"] },
238
+ ]
239
+ @conn.should_receive( :search_ext2 ).
240
+ with( base, LDAP::LDAP_SCOPE_BASE, filter, ['*'], false, nil, nil, 0, 0, 0, '', nil ).
241
+ and_return( entries )
242
+
243
+ # Turn found entries into Branch objects
244
+ Treequel::Branch.should_receive( :new_from_entry ).with( entries[0], @dir ).
245
+ and_return( found_branch1 )
246
+ Treequel::Branch.should_receive( :new_from_entry ).with( entries[1], @dir ).
247
+ and_return( found_branch2 )
248
+
249
+ @dir.search( base, :base, filter ).should == [ found_branch1, found_branch2 ]
250
+ end
251
+
252
+
253
+ it "can search for entries and yield them as Sequel::Branch objects" do
254
+ base = TEST_PEOPLE_DN
255
+ filter = '(|(uid=jonlong)(uid=margento))'
256
+ branch = mock( "branch", :dn => "thedn" )
257
+
258
+ found_branch1 = stub( "entry1 branch" )
259
+ found_branch2 = stub( "entry2 branch" )
260
+
261
+ # Do the search
262
+ entries = [
263
+ { 'dn' => ["uid=jonlong,#{TEST_PEOPLE_DN}"] },
264
+ { 'dn' => ["uid=margento,#{TEST_PEOPLE_DN}"] },
265
+ ]
266
+ @conn.should_receive( :search_ext2 ).
267
+ with( base, LDAP::LDAP_SCOPE_BASE, filter, ['*'], false, nil, nil, 0, 0, 0, '', nil ).
268
+ and_return( entries )
269
+
270
+ # Turn found entries into Branch objects
271
+ Treequel::Branch.should_receive( :new_from_entry ).with( entries[0], @dir ).
272
+ and_return( found_branch1 )
273
+ Treequel::Branch.should_receive( :new_from_entry ).with( entries[1], @dir ).
274
+ and_return( found_branch2 )
275
+
276
+ results = []
277
+ @dir.search( base, :base, filter ) do |branch|
278
+ results << branch
279
+ end
280
+
281
+ results.should == [ found_branch1, found_branch2 ]
282
+ end
283
+
284
+
285
+ it "catches plain RuntimeErrors raised by #search2 and re-casts them as " +
286
+ "more-interesting errors" do
287
+ @conn.should_receive( :search_ext2 ).
288
+ and_raise( RuntimeError.new('no result returned by search') )
289
+ @conn.should_receive( :err ).and_return( -1 )
290
+
291
+ expect {
292
+ @dir.search( TEST_BASE_DN, :base, '(objectClass=*)' )
293
+ }.to raise_error( LDAP::ResultError, /can't contact/i )
294
+ end
295
+
296
+ describe "and a custom search results class" do
297
+
298
+ before( :each ) do
299
+ @customclass = Class.new {
300
+ def self::new_from_entry( entry, directory )
301
+ new( entry, directory, 'a_dn' )
302
+ end
303
+ def initialize( entry, directory, dn )
304
+ @entry = entry
305
+ @directory = directory
306
+ @dn = dn
307
+ end
308
+ attr_reader :entry, :directory, :dn
309
+ }
310
+
311
+ end
312
+
313
+ it "can search for entries and return them as instances of a custom class" do
314
+ filter = '(|(uid=jonlong)(uid=margento))'
315
+ base = mock( "branch" )
316
+
317
+ found_branch1 = stub( "entry1 branch" )
318
+ found_branch2 = stub( "entry2 branch" )
319
+
320
+ # Do the search
321
+ entries = [
322
+ { 'dn' => ["uid=jonlong,#{TEST_PEOPLE_DN}"] },
323
+ { 'dn' => ["uid=margento,#{TEST_PEOPLE_DN}"] },
324
+ ]
325
+ @conn.should_receive( :search_ext2 ).
326
+ with( base, LDAP::LDAP_SCOPE_BASE, filter, ['*'],
327
+ false, nil, nil, 0, 0, 0, '', nil ).
328
+ and_return( entries )
329
+
330
+ rval = @dir.search( base, :base, filter, :results_class => @customclass )
331
+
332
+ rval[0].should be_an_instance_of( @customclass )
333
+ rval[0].entry.should == entries[0]
334
+ rval[0].directory.should == @dir
335
+ rval[1].should be_an_instance_of( @customclass )
336
+ rval[1].entry.should == entries[1]
337
+ rval[1].directory.should == @dir
338
+ end
339
+
340
+
341
+ it "returns instances of the base argument if it responds to new_from_entry and no " +
342
+ "custom class is specified" do
343
+
344
+ base = @customclass.new( nil, nil, TEST_PEOPLE_DN )
345
+ filter = '(|(uid=jonlong)(uid=margento))'
346
+ branch = mock( "branch" )
347
+
348
+ found_branch1 = stub( "entry1 branch" )
349
+ found_branch2 = stub( "entry2 branch" )
350
+
351
+ # Do the search
352
+ entries = [
353
+ { 'dn' => ["uid=jonlong,#{TEST_PEOPLE_DN}"] },
354
+ { 'dn' => ["uid=margento,#{TEST_PEOPLE_DN}"] },
355
+ ]
356
+ @conn.should_receive( :search_ext2 ).
357
+ with( TEST_PEOPLE_DN, LDAP::LDAP_SCOPE_BASE, filter, ['*'],
358
+ false, nil, nil, 0, 0, 0, '', nil ).
359
+ and_return( entries )
360
+
361
+ rval = @dir.search( base, :base, filter )
362
+
363
+ rval[0].should be_an_instance_of( @customclass )
364
+ rval[0].entry.should == entries[0]
365
+ rval[0].directory.should == @dir
366
+ rval[1].should be_an_instance_of( @customclass )
367
+ rval[1].entry.should == entries[1]
368
+ rval[1].directory.should == @dir
369
+ end
370
+
371
+ end
372
+
373
+ it "can turn a DN string into an RDN string from its base" do
374
+ @dir.rdn_to( TEST_PERSON_DN ).should == TEST_PERSON_DN.sub( /,#{TEST_BASE_DN}$/, '' )
375
+ end
376
+
377
+ it "can fetch the server's schema" do
378
+ @conn.should_receive( :schema ).and_return( :the_schema )
379
+ Treequel::Schema.should_receive( :new ).with( :the_schema ).
380
+ and_return( :the_parsed_schema )
381
+ @dir.schema.should == :the_parsed_schema
382
+ end
383
+
384
+ it "creates branches for messages that match valid attributeType OIDs" do
385
+ @schema.should_receive( :attribute_types ).
386
+ and_return({ :cn => :a_value, :ou => :a_value })
387
+
388
+ @dir.stub!( :bound? ).and_return( false )
389
+ rval = @dir.ou( :people )
390
+ rval.dn.downcase.should == TEST_PEOPLE_DN.downcase
391
+ end
392
+
393
+ it "doesn't create branches for messages that don't match valid attributeType OIDs" do
394
+ @schema.should_receive( :attribute_types ).
395
+ and_return({ :cn => :a_value, :ou => :a_value })
396
+
397
+ expect { @dir.void('sbc') }.to raise_error( NoMethodError )
398
+ end
399
+
400
+ it "can modify the record corresponding to a Branch in the directory" do
401
+ branch = mock( "branch" )
402
+ branch.should_receive( :dn ).at_least( :once ).and_return( :the_branches_dn )
403
+
404
+ @conn.should_receive( :modify ).with( :the_branches_dn, 'cn' => ['nomblywob'] )
405
+
406
+ @dir.modify( branch, 'cn' => ['nomblywob'] )
407
+ end
408
+
409
+ it "can delete the record corresponding to a Branch from the directory" do
410
+ branch = mock( "branch" )
411
+ branch.should_receive( :dn ).at_least( :once ).and_return( :the_branches_dn )
412
+
413
+ @conn.should_receive( :delete ).once.with( :the_branches_dn )
414
+
415
+ @dir.delete( branch )
416
+ end
417
+
418
+ it "can create an entry for a Branch" do
419
+ newattrs = {
420
+ :cn => 'Chilly T',
421
+ :desc => 'Audi like Jetta',
422
+ :objectClass => :room,
423
+ }
424
+ rdn_attrs = {
425
+ TEST_PERSON_DN_ATTR => [TEST_PERSON_DN_VALUE]
426
+ }
427
+ addattrs = {
428
+ 'cn' => ['Chilly T'],
429
+ 'desc' => ['Audi like Jetta'],
430
+ 'objectClass' => ['room'],
431
+ TEST_PERSON_DN_ATTR => [TEST_PERSON_DN_VALUE],
432
+ }
433
+
434
+ branch = mock( "new person branch" )
435
+ branch.should_receive( :dn ).and_return( TEST_PERSON_DN )
436
+ branch.should_receive( :rdn_attributes ).at_least( :once ).and_return( rdn_attrs )
437
+
438
+ room_objectclass = stub( 'room objectClass', :structural? => true )
439
+ @schema.should_receive( :object_classes ).at_least( :once ).and_return({
440
+ :room => room_objectclass,
441
+ })
442
+
443
+ @conn.should_receive( :add ).with( TEST_PERSON_DN, addattrs )
444
+
445
+ @dir.create( branch, newattrs )
446
+ end
447
+
448
+
449
+ it "can move a record to a new dn within the same branch" do
450
+ @dir.stub!( :bound? ).and_return( false )
451
+ branch = mock( "sibling branch obj" )
452
+ branch.should_receive( :dn ).at_least( :once ).and_return( TEST_PERSON_DN )
453
+ branch.should_receive( :split_dn ).at_least( :once ).
454
+ and_return([ TEST_PERSON_RDN, TEST_PEOPLE_DN ])
455
+
456
+ @conn.should_receive( :modrdn ).with( TEST_PERSON_DN, TEST_PERSON2_RDN, true )
457
+ branch.should_receive( :dn= ).with( TEST_PERSON2_DN )
458
+
459
+ @dir.move( branch, TEST_PERSON2_DN )
460
+ end
461
+
462
+
463
+ ### Datatype conversion
464
+
465
+ it "allows a mapping to be overridden by a block for a valid syntax OID" do
466
+ @dir.add_syntax_mapping( OIDS::BIT_STRING_SYNTAX ) do |unconverted_value|
467
+ unconverted_value.to_sym
468
+ end
469
+ @dir.convert_syntax_value( OIDS::BIT_STRING_SYNTAX, 'a_value' ).should == :a_value
470
+ end
471
+
472
+ it "allows a mapping to be overridden by a Hash for a valid syntax OID" do
473
+ @dir.add_syntax_mapping( OIDS::BOOLEAN_SYNTAX, {'true' => true, 'false' => false} )
474
+ @dir.convert_syntax_value( OIDS::BOOLEAN_SYNTAX, 'true' ).should == true
475
+ end
476
+
477
+ it "allows a mapping to be cleared by adding a nil mapping" do
478
+ @dir.add_syntax_mapping( OIDS::BOOLEAN_SYNTAX, {'true' => true, 'false' => false} )
479
+ @dir.add_syntax_mapping( OIDS::BOOLEAN_SYNTAX )
480
+ @dir.convert_syntax_value( OIDS::BOOLEAN_SYNTAX, 'true' ).should == 'true'
481
+ end
482
+
483
+ end
484
+ end
485
+
486
+
487
+ # vim: set nosta noet ts=4 sw=4: