3
# Unix SMB/CIFS implementation.
4
# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2005-2008
5
# Copyright (C) Martin Kuehl <mkhl@samba.org> 2006
7
# This is a Python port of the original in testprogs/ejs/samba3sam.js
9
# This program is free software; you can redistribute it and/or modify
10
# it under the terms of the GNU General Public License as published by
11
# the Free Software Foundation; either version 3 of the License, or
12
# (at your option) any later version.
14
# This program is distributed in the hope that it will be useful,
15
# but WITHOUT ANY WARRANTY; without even the implied warranty of
16
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
# GNU General Public License for more details.
19
# You should have received a copy of the GNU General Public License
20
# along with this program. If not, see <http://www.gnu.org/licenses/>.
23
"""Tests for the samba3sam LDB module, which maps Samba3 LDAP to AD LDAP."""
27
from ldb import SCOPE_DEFAULT, SCOPE_BASE, SCOPE_SUBTREE
28
from samba import Ldb, substitute_var
29
from samba.tests import LdbTestCase, TestCaseInTempDir, cmdline_loadparm
30
import samba.dcerpc.security
33
datadir = os.path.join(os.path.dirname(__file__),
34
"../../../../../testdata/samba3")
36
def read_datafile(filename):
37
return open(os.path.join(datadir, filename), 'r').read()
39
def ldb_debug(l, text):
43
class MapBaseTestCase(TestCaseInTempDir):
44
"""Base test case for mapping tests."""
46
def setup_modules(self, ldb, s3, s4):
47
ldb.add({"dn": "@MAP=samba3sam",
49
"@TO": "sambaDomainName=TESTS," + s3.basedn})
51
ldb.add({"dn": "@MODULES",
52
"@LIST": "rootdse,paged_results,server_sort,asq,samldb,password_hash,operational,objectguid,rdn_name,samba3sam,partition"})
54
ldb.add({"dn": "@PARTITION",
55
"partition": ["%s:%s" % (s4.basedn, s4.url),
56
"%s:%s" % (s3.basedn, s3.url)],
57
"replicateEntries": ["@ATTRIBUTES", "@INDEXLIST"]})
60
super(MapBaseTestCase, self).setUp()
62
def make_dn(basedn, rdn):
63
return "%s,sambaDomainName=TESTS,%s" % (rdn, basedn)
65
def make_s4dn(basedn, rdn):
66
return "%s,%s" % (rdn, basedn)
68
self.ldbfile = os.path.join(self.tempdir, "test.ldb")
69
self.ldburl = "tdb://" + self.ldbfile
71
tempdir = self.tempdir
74
"""Simple helper class that contains data for a specific SAM
76
def __init__(self, file, basedn, dn):
77
self.file = os.path.join(tempdir, file)
78
self.url = "tdb://" + self.file
80
self.substvars = {"BASEDN": self.basedn}
81
self.db = Ldb(lp=cmdline_loadparm)
85
return self._dn(self.basedn, rdn)
88
return self.db.connect(self.url)
90
def setup_data(self, path):
91
self.add_ldif(read_datafile(path))
93
def subst(self, text):
94
return substitute_var(text, self.substvars)
96
def add_ldif(self, ldif):
97
self.db.add_ldif(self.subst(ldif))
99
def modify_ldif(self, ldif):
100
self.db.modify_ldif(self.subst(ldif))
102
self.samba4 = Target("samba4.ldb", "dc=vernstok,dc=nl", make_s4dn)
103
self.samba3 = Target("samba3.ldb", "cn=Samba3Sam", make_dn)
104
self.templates = Target("templates.ldb", "cn=templates", None)
106
self.samba3.connect()
107
self.templates.connect()
108
self.samba4.connect()
111
os.unlink(self.ldbfile)
112
os.unlink(self.samba3.file)
113
os.unlink(self.templates.file)
114
os.unlink(self.samba4.file)
115
super(MapBaseTestCase, self).tearDown()
117
def assertSidEquals(self, text, ndr_sid):
118
sid_obj1 = samba.ndr.ndr_unpack(samba.dcerpc.security.dom_sid,
120
sid_obj2 = samba.dcerpc.security.dom_sid(text)
121
self.assertEquals(sid_obj1, sid_obj2)
124
class Samba3SamTestCase(MapBaseTestCase):
127
super(Samba3SamTestCase, self).setUp()
128
ldb = Ldb(self.ldburl, lp=cmdline_loadparm)
129
self.samba3.setup_data("samba3.ldif")
130
self.templates.setup_data("provision_samba3sam_templates.ldif")
131
ldif = read_datafile("provision_samba3sam.ldif")
132
ldb.add_ldif(self.samba4.subst(ldif))
133
self.setup_modules(ldb, self.samba3, self.samba4)
135
self.ldb = Ldb(self.ldburl, lp=cmdline_loadparm)
137
def test_search_non_mapped(self):
138
"""Looking up by non-mapped attribute"""
139
msg = self.ldb.search(expression="(cn=Administrator)")
140
self.assertEquals(len(msg), 1)
141
self.assertEquals(msg[0]["cn"], "Administrator")
143
def test_search_non_mapped(self):
144
"""Looking up by mapped attribute"""
145
msg = self.ldb.search(expression="(name=Backup Operators)")
146
self.assertEquals(len(msg), 1)
147
self.assertEquals(str(msg[0]["name"]), "Backup Operators")
149
def test_old_name_of_renamed(self):
150
"""Looking up by old name of renamed attribute"""
151
msg = self.ldb.search(expression="(displayName=Backup Operators)")
152
self.assertEquals(len(msg), 0)
154
def test_mapped_containing_sid(self):
155
"""Looking up mapped entry containing SID"""
156
msg = self.ldb.search(expression="(cn=Replicator)")
157
self.assertEquals(len(msg), 1)
158
self.assertEquals(str(msg[0].dn),
159
"cn=Replicator,ou=Groups,dc=vernstok,dc=nl")
160
self.assertTrue("objectSid" in msg[0])
161
self.assertSidEquals("S-1-5-21-4231626423-2410014848-2360679739-552",
163
oc = set(msg[0]["objectClass"])
164
self.assertEquals(oc, set(["group"]))
166
def test_search_by_objclass(self):
167
"""Looking up by objectClass"""
168
msg = self.ldb.search(expression="(|(objectClass=user)(cn=Administrator))")
169
self.assertEquals(set([str(m.dn) for m in msg]),
170
set(["unixName=Administrator,ou=Users,dc=vernstok,dc=nl",
171
"unixName=nobody,ou=Users,dc=vernstok,dc=nl"]))
173
def test_s3sam_modify(self):
174
# Adding a record that will be fallbacked
175
self.ldb.add({"dn": "cn=Foo",
179
"showInAdvancedViewOnly": "TRUE"}
182
# Checking for existence of record (local)
183
# TODO: This record must be searched in the local database, which is
184
# currently only supported for base searches
185
# msg = ldb.search(expression="(cn=Foo)", ['foo','blah','cn','showInAdvancedViewOnly')]
186
# TODO: Actually, this version should work as well but doesn't...
189
msg = self.ldb.search(expression="(cn=Foo)", base="cn=Foo",
191
attrs=['foo','blah','cn','showInAdvancedViewOnly'])
192
self.assertEquals(len(msg), 1)
193
self.assertEquals(str(msg[0]["showInAdvancedViewOnly"]), "TRUE")
194
self.assertEquals(str(msg[0]["foo"]), "bar")
195
self.assertEquals(str(msg[0]["blah"]), "Blie")
197
# Adding record that will be mapped
198
self.ldb.add({"dn": "cn=Niemand,cn=Users,dc=vernstok,dc=nl",
199
"objectClass": "user",
201
"sambaUnicodePwd": "geheim",
204
# Checking for existence of record (remote)
205
msg = self.ldb.search(expression="(unixName=bin)",
206
attrs=['unixName','cn','dn', 'sambaUnicodePwd'])
207
self.assertEquals(len(msg), 1)
208
self.assertEquals(str(msg[0]["cn"]), "Niemand")
209
self.assertEquals(str(msg[0]["sambaUnicodePwd"]), "geheim")
211
# Checking for existence of record (local && remote)
212
msg = self.ldb.search(expression="(&(unixName=bin)(sambaUnicodePwd=geheim))",
213
attrs=['unixName','cn','dn', 'sambaUnicodePwd'])
214
self.assertEquals(len(msg), 1) # TODO: should check with more records
215
self.assertEquals(str(msg[0]["cn"]), "Niemand")
216
self.assertEquals(str(msg[0]["unixName"]), "bin")
217
self.assertEquals(str(msg[0]["sambaUnicodePwd"]), "geheim")
219
# Checking for existence of record (local || remote)
220
msg = self.ldb.search(expression="(|(unixName=bin)(sambaUnicodePwd=geheim))",
221
attrs=['unixName','cn','dn', 'sambaUnicodePwd'])
222
#print "got %d replies" % len(msg)
223
self.assertEquals(len(msg), 1) # TODO: should check with more records
224
self.assertEquals(str(msg[0]["cn"]), "Niemand")
225
self.assertEquals(str(msg[0]["unixName"]), "bin")
226
self.assertEquals(str(msg[0]["sambaUnicodePwd"]), "geheim")
228
# Checking for data in destination database
229
msg = self.samba3.db.search(expression="(cn=Niemand)")
230
self.assertTrue(len(msg) >= 1)
231
self.assertEquals(str(msg[0]["sambaSID"]),
232
"S-1-5-21-4231626423-2410014848-2360679739-2001")
233
self.assertEquals(str(msg[0]["displayName"]), "Niemand")
235
# Adding attribute...
236
self.ldb.modify_ldif("""
237
dn: cn=Niemand,cn=Users,dc=vernstok,dc=nl
243
# Checking whether changes are still there...
244
msg = self.ldb.search(expression="(cn=Niemand)")
245
self.assertTrue(len(msg) >= 1)
246
self.assertEquals(str(msg[0]["cn"]), "Niemand")
247
self.assertEquals(str(msg[0]["description"]), "Blah")
249
# Modifying attribute...
250
self.ldb.modify_ldif("""
251
dn: cn=Niemand,cn=Users,dc=vernstok,dc=nl
257
# Checking whether changes are still there...
258
msg = self.ldb.search(expression="(cn=Niemand)")
259
self.assertTrue(len(msg) >= 1)
260
self.assertEquals(str(msg[0]["description"]), "Blie")
262
# Deleting attribute...
263
self.ldb.modify_ldif("""
264
dn: cn=Niemand,cn=Users,dc=vernstok,dc=nl
269
# Checking whether changes are no longer there...
270
msg = self.ldb.search(expression="(cn=Niemand)")
271
self.assertTrue(len(msg) >= 1)
272
self.assertTrue(not "description" in msg[0])
275
self.ldb.rename("cn=Niemand,cn=Users,dc=vernstok,dc=nl",
276
"cn=Niemand2,cn=Users,dc=vernstok,dc=nl")
278
# Checking whether DN has changed...
279
msg = self.ldb.search(expression="(cn=Niemand2)")
280
self.assertEquals(len(msg), 1)
281
self.assertEquals(str(msg[0].dn),
282
"cn=Niemand2,cn=Users,dc=vernstok,dc=nl")
285
self.ldb.delete("cn=Niemand2,cn=Users,dc=vernstok,dc=nl")
287
# Checking whether record is gone...
288
msg = self.ldb.search(expression="(cn=Niemand2)")
289
self.assertEquals(len(msg), 0)
292
class MapTestCase(MapBaseTestCase):
295
super(MapTestCase, self).setUp()
296
ldb = Ldb(self.ldburl, lp=cmdline_loadparm)
297
self.templates.setup_data("provision_samba3sam_templates.ldif")
298
ldif = read_datafile("provision_samba3sam.ldif")
299
ldb.add_ldif(self.samba4.subst(ldif))
300
self.setup_modules(ldb, self.samba3, self.samba4)
302
self.ldb = Ldb(self.ldburl, lp=cmdline_loadparm)
304
def test_map_search(self):
305
"""Running search tests on mapped data."""
307
"dn": "sambaDomainName=TESTS," + self.samba3.basedn,
308
"objectclass": ["sambaDomain", "top"],
309
"sambaSID": "S-1-5-21-4231626423-2410014848-2360679739",
310
"sambaNextRid": "2000",
311
"sambaDomainName": "TESTS"
314
# Add a set of split records
315
self.ldb.add_ldif("""
316
dn: """+ self.samba4.dn("cn=X") + """
325
objectSid: S-1-5-21-4231626423-2410014848-2360679739-552
326
primaryGroupID: 1-5-21-4231626423-2410014848-2360679739-512
331
"dn": self.samba4.dn("cn=Y"),
332
"objectClass": "top",
342
"dn": self.samba4.dn("cn=Z"),
343
"objectClass": "top",
352
# Add a set of remote records
355
"dn": self.samba3.dn("cn=A"),
356
"objectClass": "posixAccount",
359
"sambaBadPasswordCount": "x",
360
"sambaLogonTime": "x",
362
"sambaSID": "S-1-5-21-4231626423-2410014848-2360679739-552",
363
"sambaPrimaryGroupSID": "S-1-5-21-4231626423-2410014848-2360679739-512"})
366
"dn": self.samba3.dn("cn=B"),
367
"objectClass": "top",
370
"sambaBadPasswordCount": "x",
371
"sambaLogonTime": "y",
375
"dn": self.samba3.dn("cn=C"),
376
"objectClass": "top",
379
"sambaBadPasswordCount": "y",
380
"sambaLogonTime": "z",
383
# Testing search by DN
385
# Search remote record by local DN
386
dn = self.samba4.dn("cn=A")
387
res = self.ldb.search(dn, scope=SCOPE_BASE,
388
attrs=["dnsHostName", "lastLogon"])
389
self.assertEquals(len(res), 1)
390
self.assertEquals(str(res[0].dn), dn)
391
self.assertTrue(not "dnsHostName" in res[0])
392
self.assertEquals(str(res[0]["lastLogon"]), "x")
394
# Search remote record by remote DN
395
dn = self.samba3.dn("cn=A")
396
res = self.samba3.db.search(dn, scope=SCOPE_BASE,
397
attrs=["dnsHostName", "lastLogon", "sambaLogonTime"])
398
self.assertEquals(len(res), 1)
399
self.assertEquals(str(res[0].dn), dn)
400
self.assertTrue(not "dnsHostName" in res[0])
401
self.assertTrue(not "lastLogon" in res[0])
402
self.assertEquals(str(res[0]["sambaLogonTime"]), "x")
404
# Search split record by local DN
405
dn = self.samba4.dn("cn=X")
406
res = self.ldb.search(dn, scope=SCOPE_BASE,
407
attrs=["dnsHostName", "lastLogon"])
408
self.assertEquals(len(res), 1)
409
self.assertEquals(str(res[0].dn), dn)
410
self.assertEquals(str(res[0]["dnsHostName"]), "x")
411
self.assertEquals(str(res[0]["lastLogon"]), "x")
413
# Search split record by remote DN
414
dn = self.samba3.dn("cn=X")
415
res = self.samba3.db.search(dn, scope=SCOPE_BASE,
416
attrs=["dnsHostName", "lastLogon", "sambaLogonTime"])
417
self.assertEquals(len(res), 1)
418
self.assertEquals(str(res[0].dn), dn)
419
self.assertTrue(not "dnsHostName" in res[0])
420
self.assertTrue(not "lastLogon" in res[0])
421
self.assertEquals(str(res[0]["sambaLogonTime"]), "x")
423
# Testing search by attribute
425
# Search by ignored attribute
426
res = self.ldb.search(expression="(revision=x)", scope=SCOPE_DEFAULT,
427
attrs=["dnsHostName", "lastLogon"])
428
self.assertEquals(len(res), 2)
429
self.assertEquals(str(res[0].dn), self.samba4.dn("cn=Y"))
430
self.assertEquals(str(res[0]["dnsHostName"]), "y")
431
self.assertEquals(str(res[0]["lastLogon"]), "y")
432
self.assertEquals(str(res[1].dn), self.samba4.dn("cn=X"))
433
self.assertEquals(str(res[1]["dnsHostName"]), "x")
434
self.assertEquals(str(res[1]["lastLogon"]), "x")
436
# Search by kept attribute
437
res = self.ldb.search(expression="(description=y)",
438
scope=SCOPE_DEFAULT, attrs=["dnsHostName", "lastLogon"])
439
self.assertEquals(len(res), 2)
440
self.assertEquals(str(res[0].dn), self.samba4.dn("cn=Z"))
441
self.assertEquals(str(res[0]["dnsHostName"]), "z")
442
self.assertEquals(str(res[0]["lastLogon"]), "z")
443
self.assertEquals(str(res[1].dn), self.samba4.dn("cn=C"))
444
self.assertTrue(not "dnsHostName" in res[1])
445
self.assertEquals(str(res[1]["lastLogon"]), "z")
447
# Search by renamed attribute
448
res = self.ldb.search(expression="(badPwdCount=x)", scope=SCOPE_DEFAULT,
449
attrs=["dnsHostName", "lastLogon"])
450
self.assertEquals(len(res), 2)
451
self.assertEquals(str(res[0].dn), self.samba4.dn("cn=B"))
452
self.assertTrue(not "dnsHostName" in res[0])
453
self.assertEquals(str(res[0]["lastLogon"]), "y")
454
self.assertEquals(str(res[1].dn), self.samba4.dn("cn=A"))
455
self.assertTrue(not "dnsHostName" in res[1])
456
self.assertEquals(str(res[1]["lastLogon"]), "x")
458
# Search by converted attribute
460
# Using the SID directly in the parse tree leads to conversion
461
# errors, letting the search fail with no results.
462
#res = self.ldb.search("(objectSid=S-1-5-21-4231626423-2410014848-2360679739-552)", scope=SCOPE_DEFAULT, attrs)
463
res = self.ldb.search(expression="(objectSid=*)", base=None, scope=SCOPE_DEFAULT, attrs=["dnsHostName", "lastLogon", "objectSid"])
464
self.assertEquals(len(res), 3)
465
self.assertEquals(str(res[0].dn), self.samba4.dn("cn=X"))
466
self.assertEquals(str(res[0]["dnsHostName"]), "x")
467
self.assertEquals(str(res[0]["lastLogon"]), "x")
468
self.assertSidEquals("S-1-5-21-4231626423-2410014848-2360679739-552",
470
self.assertTrue("objectSid" in res[0])
471
self.assertEquals(str(res[1].dn), self.samba4.dn("cn=A"))
472
self.assertTrue(not "dnsHostName" in res[1])
473
self.assertEquals(str(res[1]["lastLogon"]), "x")
474
self.assertSidEquals("S-1-5-21-4231626423-2410014848-2360679739-552",
476
self.assertTrue("objectSid" in res[1])
478
# Search by generated attribute
479
# In most cases, this even works when the mapping is missing
480
# a `convert_operator' by enumerating the remote db.
481
res = self.ldb.search(expression="(primaryGroupID=512)",
482
attrs=["dnsHostName", "lastLogon", "primaryGroupID"])
483
self.assertEquals(len(res), 1)
484
self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A"))
485
self.assertTrue(not "dnsHostName" in res[0])
486
self.assertEquals(str(res[0]["lastLogon"]), "x")
487
self.assertEquals(str(res[0]["primaryGroupID"]), "512")
489
# TODO: There should actually be two results, A and X. The
490
# primaryGroupID of X seems to get corrupted somewhere, and the
491
# objectSid isn't available during the generation of remote (!) data,
492
# which can be observed with the following search. Also note that Xs
493
# objectSid seems to be fine in the previous search for objectSid... */
494
#res = ldb.search(expression="(primaryGroupID=*)", NULL, ldb. SCOPE_DEFAULT, attrs)
495
#print len(res) + " results found"
496
#for i in range(len(res)):
497
# for (obj in res[i]) {
498
# print obj + ": " + res[i][obj]
503
# Search by remote name of renamed attribute */
504
res = self.ldb.search(expression="(sambaBadPasswordCount=*)",
505
attrs=["dnsHostName", "lastLogon"])
506
self.assertEquals(len(res), 0)
508
# Search by objectClass
509
attrs = ["dnsHostName", "lastLogon", "objectClass"]
510
res = self.ldb.search(expression="(objectClass=user)", attrs=attrs)
511
self.assertEquals(len(res), 2)
512
self.assertEquals(str(res[0].dn), self.samba4.dn("cn=X"))
513
self.assertEquals(str(res[0]["dnsHostName"]), "x")
514
self.assertEquals(str(res[0]["lastLogon"]), "x")
515
self.assertEquals(str(res[0]["objectClass"][0]), "user")
516
self.assertEquals(str(res[1].dn), self.samba4.dn("cn=A"))
517
self.assertTrue(not "dnsHostName" in res[1])
518
self.assertEquals(str(res[1]["lastLogon"]), "x")
519
self.assertEquals(str(res[1]["objectClass"][0]), "user")
521
# Prove that the objectClass is actually used for the search
522
res = self.ldb.search(expression="(|(objectClass=user)(badPwdCount=x))",
524
self.assertEquals(len(res), 3)
525
self.assertEquals(str(res[0].dn), self.samba4.dn("cn=B"))
526
self.assertTrue(not "dnsHostName" in res[0])
527
self.assertEquals(str(res[0]["lastLogon"]), "y")
528
self.assertEquals(set(res[0]["objectClass"]), set(["top"]))
529
self.assertEquals(str(res[1].dn), self.samba4.dn("cn=X"))
530
self.assertEquals(str(res[1]["dnsHostName"]), "x")
531
self.assertEquals(str(res[1]["lastLogon"]), "x")
532
self.assertEquals(str(res[1]["objectClass"][0]), "user")
533
self.assertEquals(str(res[2].dn), self.samba4.dn("cn=A"))
534
self.assertTrue(not "dnsHostName" in res[2])
535
self.assertEquals(str(res[2]["lastLogon"]), "x")
536
self.assertEquals(res[2]["objectClass"][0], "user")
538
# Testing search by parse tree
540
# Search by conjunction of local attributes
541
res = self.ldb.search(expression="(&(codePage=x)(revision=x))",
542
attrs=["dnsHostName", "lastLogon"])
543
self.assertEquals(len(res), 2)
544
self.assertEquals(str(res[0].dn), self.samba4.dn("cn=Y"))
545
self.assertEquals(str(res[0]["dnsHostName"]), "y")
546
self.assertEquals(str(res[0]["lastLogon"]), "y")
547
self.assertEquals(str(res[1].dn), self.samba4.dn("cn=X"))
548
self.assertEquals(str(res[1]["dnsHostName"]), "x")
549
self.assertEquals(str(res[1]["lastLogon"]), "x")
551
# Search by conjunction of remote attributes
552
res = self.ldb.search(expression="(&(lastLogon=x)(description=x))",
553
attrs=["dnsHostName", "lastLogon"])
554
self.assertEquals(len(res), 2)
555
self.assertEquals(str(res[0].dn), self.samba4.dn("cn=X"))
556
self.assertEquals(str(res[0]["dnsHostName"]), "x")
557
self.assertEquals(str(res[0]["lastLogon"]), "x")
558
self.assertEquals(str(res[1].dn), self.samba4.dn("cn=A"))
559
self.assertTrue(not "dnsHostName" in res[1])
560
self.assertEquals(str(res[1]["lastLogon"]), "x")
562
# Search by conjunction of local and remote attribute
563
res = self.ldb.search(expression="(&(codePage=x)(description=x))",
564
attrs=["dnsHostName", "lastLogon"])
565
self.assertEquals(len(res), 2)
566
self.assertEquals(str(res[0].dn), self.samba4.dn("cn=Y"))
567
self.assertEquals(str(res[0]["dnsHostName"]), "y")
568
self.assertEquals(str(res[0]["lastLogon"]), "y")
569
self.assertEquals(str(res[1].dn), self.samba4.dn("cn=X"))
570
self.assertEquals(str(res[1]["dnsHostName"]), "x")
571
self.assertEquals(str(res[1]["lastLogon"]), "x")
573
# Search by conjunction of local and remote attribute w/o match
574
attrs = ["dnsHostName", "lastLogon"]
575
res = self.ldb.search(expression="(&(codePage=x)(nextRid=x))",
577
self.assertEquals(len(res), 0)
578
res = self.ldb.search(expression="(&(revision=x)(lastLogon=z))",
580
self.assertEquals(len(res), 0)
582
# Search by disjunction of local attributes
583
res = self.ldb.search(expression="(|(revision=x)(dnsHostName=x))",
584
attrs=["dnsHostName", "lastLogon"])
585
self.assertEquals(len(res), 2)
586
self.assertEquals(str(res[0].dn), self.samba4.dn("cn=Y"))
587
self.assertEquals(str(res[0]["dnsHostName"]), "y")
588
self.assertEquals(str(res[0]["lastLogon"]), "y")
589
self.assertEquals(str(res[1].dn), self.samba4.dn("cn=X"))
590
self.assertEquals(str(res[1]["dnsHostName"]), "x")
591
self.assertEquals(str(res[1]["lastLogon"]), "x")
593
# Search by disjunction of remote attributes
594
res = self.ldb.search(expression="(|(badPwdCount=x)(lastLogon=x))",
595
attrs=["dnsHostName", "lastLogon"])
596
self.assertEquals(len(res), 3)
597
self.assertEquals(str(res[0].dn), self.samba4.dn("cn=B"))
598
self.assertFalse("dnsHostName" in res[0])
599
self.assertEquals(str(res[0]["lastLogon"]), "y")
600
self.assertEquals(str(res[1].dn), self.samba4.dn("cn=X"))
601
self.assertEquals(str(res[1]["dnsHostName"]), "x")
602
self.assertEquals(str(res[1]["lastLogon"]), "x")
603
self.assertEquals(str(res[2].dn), self.samba4.dn("cn=A"))
604
self.assertFalse("dnsHostName" in res[2])
605
self.assertEquals(str(res[2]["lastLogon"]), "x")
607
# Search by disjunction of local and remote attribute
608
res = self.ldb.search(expression="(|(revision=x)(lastLogon=y))",
609
attrs=["dnsHostName", "lastLogon"])
610
self.assertEquals(len(res), 3)
611
self.assertEquals(str(res[0].dn), self.samba4.dn("cn=Y"))
612
self.assertEquals(str(res[0]["dnsHostName"]), "y")
613
self.assertEquals(str(res[0]["lastLogon"]), "y")
614
self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B"))
615
self.assertFalse("dnsHostName" in res[1])
616
self.assertEquals(str(res[1]["lastLogon"]), "y")
617
self.assertEquals(str(res[2].dn), self.samba4.dn("cn=X"))
618
self.assertEquals(str(res[2]["dnsHostName"]), "x")
619
self.assertEquals(str(res[2]["lastLogon"]), "x")
621
# Search by disjunction of local and remote attribute w/o match
622
res = self.ldb.search(expression="(|(codePage=y)(nextRid=z))",
623
attrs=["dnsHostName", "lastLogon"])
624
self.assertEquals(len(res), 0)
626
# Search by negated local attribute
627
res = self.ldb.search(expression="(!(revision=x))",
628
attrs=["dnsHostName", "lastLogon"])
629
self.assertEquals(len(res), 5)
630
self.assertEquals(str(res[0].dn), self.samba4.dn("cn=B"))
631
self.assertTrue(not "dnsHostName" in res[0])
632
self.assertEquals(str(res[0]["lastLogon"]), "y")
633
self.assertEquals(str(res[1].dn), self.samba4.dn("cn=A"))
634
self.assertTrue(not "dnsHostName" in res[1])
635
self.assertEquals(str(res[1]["lastLogon"]), "x")
636
self.assertEquals(str(res[2].dn), self.samba4.dn("cn=Z"))
637
self.assertEquals(str(res[2]["dnsHostName"]), "z")
638
self.assertEquals(str(res[2]["lastLogon"]), "z")
639
self.assertEquals(str(res[3].dn), self.samba4.dn("cn=C"))
640
self.assertTrue(not "dnsHostName" in res[3])
641
self.assertEquals(str(res[3]["lastLogon"]), "z")
643
# Search by negated remote attribute
644
res = self.ldb.search(expression="(!(description=x))",
645
attrs=["dnsHostName", "lastLogon"])
646
self.assertEquals(len(res), 3)
647
self.assertEquals(str(res[0].dn), self.samba4.dn("cn=Z"))
648
self.assertEquals(str(res[0]["dnsHostName"]), "z")
649
self.assertEquals(str(res[0]["lastLogon"]), "z")
650
self.assertEquals(str(res[1].dn), self.samba4.dn("cn=C"))
651
self.assertTrue(not "dnsHostName" in res[1])
652
self.assertEquals(str(res[1]["lastLogon"]), "z")
654
# Search by negated conjunction of local attributes
655
res = self.ldb.search(expression="(!(&(codePage=x)(revision=x)))",
656
attrs=["dnsHostName", "lastLogon"])
657
self.assertEquals(len(res), 5)
658
self.assertEquals(str(res[0].dn), self.samba4.dn("cn=B"))
659
self.assertTrue(not "dnsHostName" in res[0])
660
self.assertEquals(str(res[0]["lastLogon"]), "y")
661
self.assertEquals(str(res[1].dn), self.samba4.dn("cn=A"))
662
self.assertTrue(not "dnsHostName" in res[1])
663
self.assertEquals(str(res[1]["lastLogon"]), "x")
664
self.assertEquals(str(res[2].dn), self.samba4.dn("cn=Z"))
665
self.assertEquals(str(res[2]["dnsHostName"]), "z")
666
self.assertEquals(str(res[2]["lastLogon"]), "z")
667
self.assertEquals(str(res[3].dn), self.samba4.dn("cn=C"))
668
self.assertTrue(not "dnsHostName" in res[3])
669
self.assertEquals(str(res[3]["lastLogon"]), "z")
671
# Search by negated conjunction of remote attributes
672
res = self.ldb.search(expression="(!(&(lastLogon=x)(description=x)))",
673
attrs=["dnsHostName", "lastLogon"])
674
self.assertEquals(len(res), 5)
675
self.assertEquals(str(res[0].dn), self.samba4.dn("cn=Y"))
676
self.assertEquals(str(res[0]["dnsHostName"]), "y")
677
self.assertEquals(str(res[0]["lastLogon"]), "y")
678
self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B"))
679
self.assertTrue(not "dnsHostName" in res[1])
680
self.assertEquals(str(res[1]["lastLogon"]), "y")
681
self.assertEquals(str(res[2].dn), self.samba4.dn("cn=Z"))
682
self.assertEquals(str(res[2]["dnsHostName"]), "z")
683
self.assertEquals(str(res[2]["lastLogon"]), "z")
684
self.assertEquals(str(res[3].dn), self.samba4.dn("cn=C"))
685
self.assertTrue(not "dnsHostName" in res[3])
686
self.assertEquals(str(res[3]["lastLogon"]), "z")
688
# Search by negated conjunction of local and remote attribute
689
res = self.ldb.search(expression="(!(&(codePage=x)(description=x)))",
690
attrs=["dnsHostName", "lastLogon"])
691
self.assertEquals(len(res), 5)
692
self.assertEquals(str(res[0].dn), self.samba4.dn("cn=B"))
693
self.assertTrue(not "dnsHostName" in res[0])
694
self.assertEquals(str(res[0]["lastLogon"]), "y")
695
self.assertEquals(str(res[1].dn), self.samba4.dn("cn=A"))
696
self.assertTrue(not "dnsHostName" in res[1])
697
self.assertEquals(str(res[1]["lastLogon"]), "x")
698
self.assertEquals(str(res[2].dn), self.samba4.dn("cn=Z"))
699
self.assertEquals(str(res[2]["dnsHostName"]), "z")
700
self.assertEquals(str(res[2]["lastLogon"]), "z")
701
self.assertEquals(str(res[3].dn), self.samba4.dn("cn=C"))
702
self.assertTrue(not "dnsHostName" in res[3])
703
self.assertEquals(str(res[3]["lastLogon"]), "z")
705
# Search by negated disjunction of local attributes
706
res = self.ldb.search(expression="(!(|(revision=x)(dnsHostName=x)))",
707
attrs=["dnsHostName", "lastLogon"])
708
self.assertEquals(str(res[0].dn), self.samba4.dn("cn=B"))
709
self.assertTrue(not "dnsHostName" in res[0])
710
self.assertEquals(str(res[0]["lastLogon"]), "y")
711
self.assertEquals(str(res[1].dn), self.samba4.dn("cn=A"))
712
self.assertTrue(not "dnsHostName" in res[1])
713
self.assertEquals(str(res[1]["lastLogon"]), "x")
714
self.assertEquals(str(res[2].dn), self.samba4.dn("cn=Z"))
715
self.assertEquals(str(res[2]["dnsHostName"]), "z")
716
self.assertEquals(str(res[2]["lastLogon"]), "z")
717
self.assertEquals(str(res[3].dn), self.samba4.dn("cn=C"))
718
self.assertTrue(not "dnsHostName" in res[3])
719
self.assertEquals(str(res[3]["lastLogon"]), "z")
721
# Search by negated disjunction of remote attributes
722
res = self.ldb.search(expression="(!(|(badPwdCount=x)(lastLogon=x)))",
723
attrs=["dnsHostName", "lastLogon"])
724
self.assertEquals(len(res), 4)
725
self.assertEquals(str(res[0].dn), self.samba4.dn("cn=Y"))
726
self.assertEquals(str(res[0]["dnsHostName"]), "y")
727
self.assertEquals(str(res[0]["lastLogon"]), "y")
728
self.assertEquals(str(res[1].dn), self.samba4.dn("cn=Z"))
729
self.assertEquals(str(res[1]["dnsHostName"]), "z")
730
self.assertEquals(str(res[1]["lastLogon"]), "z")
731
self.assertEquals(str(res[2].dn), self.samba4.dn("cn=C"))
732
self.assertTrue(not "dnsHostName" in res[2])
733
self.assertEquals(str(res[2]["lastLogon"]), "z")
735
# Search by negated disjunction of local and remote attribute
736
res = self.ldb.search(expression="(!(|(revision=x)(lastLogon=y)))",
737
attrs=["dnsHostName", "lastLogon"])
738
self.assertEquals(len(res), 4)
739
self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A"))
740
self.assertTrue(not "dnsHostName" in res[0])
741
self.assertEquals(str(res[0]["lastLogon"]), "x")
742
self.assertEquals(str(res[1].dn), self.samba4.dn("cn=Z"))
743
self.assertEquals(str(res[1]["dnsHostName"]), "z")
744
self.assertEquals(str(res[1]["lastLogon"]), "z")
745
self.assertEquals(str(res[2].dn), self.samba4.dn("cn=C"))
746
self.assertTrue(not "dnsHostName" in res[2])
747
self.assertEquals(str(res[2]["lastLogon"]), "z")
749
# Search by complex parse tree
750
res = self.ldb.search(expression="(|(&(revision=x)(dnsHostName=x))(!(&(description=x)(nextRid=y)))(badPwdCount=y))", attrs=["dnsHostName", "lastLogon"])
751
self.assertEquals(len(res), 6)
752
self.assertEquals(str(res[0].dn), self.samba4.dn("cn=B"))
753
self.assertTrue(not "dnsHostName" in res[0])
754
self.assertEquals(str(res[0]["lastLogon"]), "y")
755
self.assertEquals(str(res[1].dn), self.samba4.dn("cn=X"))
756
self.assertEquals(str(res[1]["dnsHostName"]), "x")
757
self.assertEquals(str(res[1]["lastLogon"]), "x")
758
self.assertEquals(str(res[2].dn), self.samba4.dn("cn=A"))
759
self.assertTrue(not "dnsHostName" in res[2])
760
self.assertEquals(str(res[2]["lastLogon"]), "x")
761
self.assertEquals(str(res[3].dn), self.samba4.dn("cn=Z"))
762
self.assertEquals(str(res[3]["dnsHostName"]), "z")
763
self.assertEquals(str(res[3]["lastLogon"]), "z")
764
self.assertEquals(str(res[4].dn), self.samba4.dn("cn=C"))
765
self.assertTrue(not "dnsHostName" in res[4])
766
self.assertEquals(str(res[4]["lastLogon"]), "z")
769
dns = [self.samba4.dn("cn=%s" % n) for n in ["A","B","C","X","Y","Z"]]
773
def test_map_modify_local(self):
774
"""Modification of local records."""
776
dn = "cn=test,dc=idealx,dc=org"
777
self.ldb.add({"dn": dn,
781
"description": "test"})
783
attrs = ["foo", "revision", "description"]
784
res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
785
self.assertEquals(len(res), 1)
786
self.assertEquals(str(res[0].dn), dn)
787
self.assertEquals(str(res[0]["foo"]), "bar")
788
self.assertEquals(str(res[0]["revision"]), "1")
789
self.assertEquals(str(res[0]["description"]), "test")
790
# Check it's not in the local db
791
res = self.samba4.db.search(expression="(cn=test)",
792
scope=SCOPE_DEFAULT, attrs=attrs)
793
self.assertEquals(len(res), 0)
794
# Check it's not in the remote db
795
res = self.samba3.db.search(expression="(cn=test)",
796
scope=SCOPE_DEFAULT, attrs=attrs)
797
self.assertEquals(len(res), 0)
799
# Modify local record
807
self.ldb.modify_ldif(ldif)
809
res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
810
self.assertEquals(len(res), 1)
811
self.assertEquals(str(res[0].dn), dn)
812
self.assertEquals(str(res[0]["foo"]), "baz")
813
self.assertEquals(str(res[0]["revision"]), "1")
814
self.assertEquals(str(res[0]["description"]), "foo")
816
# Rename local record
817
dn2 = "cn=toast,dc=idealx,dc=org"
818
self.ldb.rename(dn, dn2)
820
res = self.ldb.search(dn2, scope=SCOPE_BASE, attrs=attrs)
821
self.assertEquals(len(res), 1)
822
self.assertEquals(str(res[0].dn), dn2)
823
self.assertEquals(str(res[0]["foo"]), "baz")
824
self.assertEquals(str(res[0]["revision"]), "1")
825
self.assertEquals(str(res[0]["description"]), "foo")
827
# Delete local record
830
res = self.ldb.search(dn2, scope=SCOPE_BASE)
831
self.assertEquals(len(res), 0)
833
def test_map_modify_remote_remote(self):
834
"""Modification of remote data of remote records"""
836
dn = self.samba4.dn("cn=test")
837
dn2 = self.samba3.dn("cn=test")
838
self.samba3.db.add({"dn": dn2,
840
"description": "foo",
841
"sambaBadPasswordCount": "3",
842
"sambaNextRid": "1001"})
844
res = self.samba3.db.search(dn2, scope=SCOPE_BASE,
845
attrs=["description", "sambaBadPasswordCount", "sambaNextRid"])
846
self.assertEquals(len(res), 1)
847
self.assertEquals(str(res[0].dn), dn2)
848
self.assertEquals(str(res[0]["description"]), "foo")
849
self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "3")
850
self.assertEquals(str(res[0]["sambaNextRid"]), "1001")
852
attrs = ["description", "badPwdCount", "nextRid"]
853
res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs, expression="")
854
self.assertEquals(len(res), 1)
855
self.assertEquals(str(res[0].dn), dn)
856
self.assertEquals(str(res[0]["description"]), "foo")
857
self.assertEquals(str(res[0]["badPwdCount"]), "3")
858
self.assertEquals(str(res[0]["nextRid"]), "1001")
860
res = self.samba4.db.search(dn, scope=SCOPE_BASE, attrs=attrs)
861
self.assertEquals(len(res), 0)
863
# Modify remote data of remote record
871
self.ldb.modify_ldif(ldif)
873
res = self.ldb.search(dn, scope=SCOPE_BASE,
874
attrs=["description", "badPwdCount", "nextRid"])
875
self.assertEquals(len(res), 1)
876
self.assertEquals(str(res[0].dn), dn)
877
self.assertEquals(str(res[0]["description"]), "test")
878
self.assertEquals(str(res[0]["badPwdCount"]), "4")
879
self.assertEquals(str(res[0]["nextRid"]), "1001")
881
res = self.samba3.db.search(dn2, scope=SCOPE_BASE,
882
attrs=["description", "sambaBadPasswordCount", "sambaNextRid"])
883
self.assertEquals(len(res), 1)
884
self.assertEquals(str(res[0].dn), dn2)
885
self.assertEquals(str(res[0]["description"]), "test")
886
self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "4")
887
self.assertEquals(str(res[0]["sambaNextRid"]), "1001")
889
# Rename remote record
890
dn2 = self.samba4.dn("cn=toast")
891
self.ldb.rename(dn, dn2)
894
res = self.ldb.search(dn, scope=SCOPE_BASE,
895
attrs=["description", "badPwdCount", "nextRid"])
896
self.assertEquals(len(res), 1)
897
self.assertEquals(str(res[0].dn), dn)
898
self.assertEquals(str(res[0]["description"]), "test")
899
self.assertEquals(str(res[0]["badPwdCount"]), "4")
900
self.assertEquals(str(res[0]["nextRid"]), "1001")
902
dn2 = self.samba3.dn("cn=toast")
903
res = self.samba3.db.search(dn2, scope=SCOPE_BASE,
904
attrs=["description", "sambaBadPasswordCount", "sambaNextRid"])
905
self.assertEquals(len(res), 1)
906
self.assertEquals(str(res[0].dn), dn2)
907
self.assertEquals(str(res[0]["description"]), "test")
908
self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "4")
909
self.assertEquals(str(res[0]["sambaNextRid"]), "1001")
911
# Delete remote record
913
# Check in mapped db that it's removed
914
res = self.ldb.search(dn, scope=SCOPE_BASE)
915
self.assertEquals(len(res), 0)
917
res = self.samba3.db.search(dn2, scope=SCOPE_BASE)
918
self.assertEquals(len(res), 0)
920
def test_map_modify_remote_local(self):
921
"""Modification of local data of remote records"""
922
# Add remote record (same as before)
923
dn = self.samba4.dn("cn=test")
924
dn2 = self.samba3.dn("cn=test")
925
self.samba3.db.add({"dn": dn2,
927
"description": "foo",
928
"sambaBadPasswordCount": "3",
929
"sambaNextRid": "1001"})
931
# Modify local data of remote record
940
self.ldb.modify_ldif(ldif)
942
attrs = ["revision", "description"]
943
res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
944
self.assertEquals(len(res), 1)
945
self.assertEquals(str(res[0].dn), dn)
946
self.assertEquals(str(res[0]["description"]), "test")
947
self.assertEquals(str(res[0]["revision"]), "1")
949
res = self.samba3.db.search(dn2, scope=SCOPE_BASE, attrs=attrs)
950
self.assertEquals(len(res), 1)
951
self.assertEquals(str(res[0].dn), dn2)
952
self.assertEquals(str(res[0]["description"]), "test")
953
self.assertTrue(not "revision" in res[0])
955
res = self.samba4.db.search(dn, scope=SCOPE_BASE, attrs=attrs)
956
self.assertEquals(len(res), 1)
957
self.assertEquals(str(res[0].dn), dn)
958
self.assertTrue(not "description" in res[0])
959
self.assertEquals(str(res[0]["revision"]), "1")
961
# Delete (newly) split record
964
def test_map_modify_split(self):
965
"""Testing modification of split records"""
967
dn = self.samba4.dn("cn=test")
968
dn2 = self.samba3.dn("cn=test")
972
"description": "foo",
977
attrs = ["description", "badPwdCount", "nextRid", "revision"]
978
res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
979
self.assertEquals(len(res), 1)
980
self.assertEquals(str(res[0].dn), dn)
981
self.assertEquals(str(res[0]["description"]), "foo")
982
self.assertEquals(str(res[0]["badPwdCount"]), "3")
983
self.assertEquals(str(res[0]["nextRid"]), "1001")
984
self.assertEquals(str(res[0]["revision"]), "1")
986
res = self.samba4.db.search(dn, scope=SCOPE_BASE, attrs=attrs)
987
self.assertEquals(len(res), 1)
988
self.assertEquals(str(res[0].dn), dn)
989
self.assertTrue(not "description" in res[0])
990
self.assertTrue(not "badPwdCount" in res[0])
991
self.assertTrue(not "nextRid" in res[0])
992
self.assertEquals(str(res[0]["revision"]), "1")
994
attrs = ["description", "sambaBadPasswordCount", "sambaNextRid",
996
res = self.samba3.db.search(dn2, scope=SCOPE_BASE, attrs=attrs)
997
self.assertEquals(len(res), 1)
998
self.assertEquals(str(res[0].dn), dn2)
999
self.assertEquals(str(res[0]["description"]), "foo")
1000
self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "3")
1001
self.assertEquals(str(res[0]["sambaNextRid"]), "1001")
1002
self.assertTrue(not "revision" in res[0])
1004
# Modify of split record
1007
replace: description
1009
replace: badPwdCount
1014
self.ldb.modify_ldif(ldif)
1015
# Check in mapped db
1016
attrs = ["description", "badPwdCount", "nextRid", "revision"]
1017
res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
1018
self.assertEquals(len(res), 1)
1019
self.assertEquals(str(res[0].dn), dn)
1020
self.assertEquals(str(res[0]["description"]), "test")
1021
self.assertEquals(str(res[0]["badPwdCount"]), "4")
1022
self.assertEquals(str(res[0]["nextRid"]), "1001")
1023
self.assertEquals(str(res[0]["revision"]), "2")
1025
res = self.samba4.db.search(dn, scope=SCOPE_BASE, attrs=attrs)
1026
self.assertEquals(len(res), 1)
1027
self.assertEquals(str(res[0].dn), dn)
1028
self.assertTrue(not "description" in res[0])
1029
self.assertTrue(not "badPwdCount" in res[0])
1030
self.assertTrue(not "nextRid" in res[0])
1031
self.assertEquals(str(res[0]["revision"]), "2")
1032
# Check in remote db
1033
attrs = ["description", "sambaBadPasswordCount", "sambaNextRid",
1035
res = self.samba3.db.search(dn2, scope=SCOPE_BASE, attrs=attrs)
1036
self.assertEquals(len(res), 1)
1037
self.assertEquals(str(res[0].dn), dn2)
1038
self.assertEquals(str(res[0]["description"]), "test")
1039
self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "4")
1040
self.assertEquals(str(res[0]["sambaNextRid"]), "1001")
1041
self.assertTrue(not "revision" in res[0])
1043
# Rename split record
1044
dn2 = self.samba4.dn("cn=toast")
1045
self.ldb.rename(dn, dn2)
1046
# Check in mapped db
1048
attrs = ["description", "badPwdCount", "nextRid", "revision"]
1049
res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
1050
self.assertEquals(len(res), 1)
1051
self.assertEquals(str(res[0].dn), dn)
1052
self.assertEquals(str(res[0]["description"]), "test")
1053
self.assertEquals(str(res[0]["badPwdCount"]), "4")
1054
self.assertEquals(str(res[0]["nextRid"]), "1001")
1055
self.assertEquals(str(res[0]["revision"]), "2")
1057
res = self.samba4.db.search(dn, scope=SCOPE_BASE, attrs=attrs)
1058
self.assertEquals(len(res), 1)
1059
self.assertEquals(str(res[0].dn), dn)
1060
self.assertTrue(not "description" in res[0])
1061
self.assertTrue(not "badPwdCount" in res[0])
1062
self.assertTrue(not "nextRid" in res[0])
1063
self.assertEquals(str(res[0]["revision"]), "2")
1064
# Check in remote db
1065
dn2 = self.samba3.dn("cn=toast")
1066
res = self.samba3.db.search(dn2, scope=SCOPE_BASE,
1067
attrs=["description", "sambaBadPasswordCount", "sambaNextRid",
1069
self.assertEquals(len(res), 1)
1070
self.assertEquals(str(res[0].dn), dn2)
1071
self.assertEquals(str(res[0]["description"]), "test")
1072
self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "4")
1073
self.assertEquals(str(res[0]["sambaNextRid"]), "1001")
1074
self.assertTrue(not "revision" in res[0])
1076
# Delete split record
1078
# Check in mapped db
1079
res = self.ldb.search(dn, scope=SCOPE_BASE)
1080
self.assertEquals(len(res), 0)
1082
res = self.samba4.db.search(dn, scope=SCOPE_BASE)
1083
self.assertEquals(len(res), 0)
1084
# Check in remote db
1085
res = self.samba3.db.search(dn2, scope=SCOPE_BASE)
1086
self.assertEquals(len(res), 0)