summaryrefslogtreecommitdiff
path: root/ldb/tests
diff options
context:
space:
mode:
Diffstat (limited to 'ldb/tests')
-rw-r--r--ldb/tests/init.ldif40
-rwxr-xr-xldb/tests/init_slapd.sh41
-rwxr-xr-xldb/tests/kill_slapd.sh12
-rwxr-xr-xldb/tests/ldapi_url.sh11
-rw-r--r--ldb/tests/photo.ldif5
-rwxr-xr-xldb/tests/python/api.py463
-rwxr-xr-xldb/tests/python/ldap.py1017
-rw-r--r--ldb/tests/samba4.pngbin0 -> 6239 bytes
-rw-r--r--ldb/tests/sample_module.c38
-rw-r--r--ldb/tests/schema-tests/schema-add-test.ldif66
-rw-r--r--ldb/tests/schema-tests/schema-mod-test-1.ldif5
-rw-r--r--ldb/tests/schema-tests/schema-mod-test-2.ldif5
-rw-r--r--ldb/tests/schema-tests/schema-mod-test-3.ldif5
-rw-r--r--ldb/tests/schema-tests/schema-mod-test-4.ldif5
-rw-r--r--ldb/tests/schema-tests/schema-mod-test-5.ldif5
-rw-r--r--ldb/tests/schema-tests/schema.ldif100
-rw-r--r--ldb/tests/slapd.conf26
-rwxr-xr-xldb/tests/start_slapd.sh14
-rw-r--r--ldb/tests/test-attribs.ldif6
-rw-r--r--ldb/tests/test-config.ldif67
-rw-r--r--ldb/tests/test-default-config.ldif17
-rwxr-xr-xldb/tests/test-extended.sh69
-rwxr-xr-xldb/tests/test-generic.sh128
-rw-r--r--ldb/tests/test-index.ldif7
-rwxr-xr-xldb/tests/test-ldap.sh54
-rw-r--r--ldb/tests/test-modify.ldif23
-rwxr-xr-xldb/tests/test-schema.sh34
-rwxr-xr-xldb/tests/test-soloading.sh32
-rwxr-xr-xldb/tests/test-sqlite3.sh25
-rw-r--r--ldb/tests/test-tdb-features.sh160
-rwxr-xr-xldb/tests/test-tdb.sh31
-rw-r--r--ldb/tests/test-wildcard.ldif5
-rw-r--r--ldb/tests/test-wrong_attributes.ldif3
-rw-r--r--ldb/tests/test.ldif411
-rw-r--r--ldb/tests/testdata.txt8
-rw-r--r--ldb/tests/testsearch.txt5
36 files changed, 2943 insertions, 0 deletions
diff --git a/ldb/tests/init.ldif b/ldb/tests/init.ldif
new file mode 100644
index 00000000..2e0b83c7
--- /dev/null
+++ b/ldb/tests/init.ldif
@@ -0,0 +1,40 @@
+dn: o=University of Michigan,c=TEST
+objectclass: organization
+objectclass: domainRelatedObject
+l: Ann Arbor, Michigan
+st: Michigan
+o: University of Michigan
+o: UMICH
+o: UM
+o: U-M
+o: U of M
+description: The University of Michigan at Ann Arbor
+seeAlso:
+postaladdress: University of Michigan $ 535 W. William St. $ Ann Arbor, MI 481
+ 09 $ US
+telephonenumber: +1 313 764-1817
+associateddomain: example.com
+
+dn: ou=People,o=University of Michigan,c=TEST
+objectclass: organizationalUnit
+objectclass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+
+dn: ou=Ldb Test,ou=People,o=University of Michigan,c=TEST
+objectclass: organizationalUnit
+objectclass: extensibleObject
+ou: People
+ou: Ldb Test
+uidNumber: 0
+gidNumber: 0
+
+dn: ou=LdbTspace,ou=People,o=University of Michigan,c=TEST
+objectclass: organizationalUnit
+objectclass: extensibleObject
+ou: People
+ou: LdbTspace
+description: test white space removal in comparisons
+uidNumber: 0
+gidNumber: 0
diff --git a/ldb/tests/init_slapd.sh b/ldb/tests/init_slapd.sh
new file mode 100755
index 00000000..cf06acd0
--- /dev/null
+++ b/ldb/tests/init_slapd.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+if [ -z "$LDBDIR" ]; then
+ LDBDIR=`dirname $0`/..
+ export LDBDIR
+fi
+
+rm -rf tests/tmp/db
+mkdir -p tests/tmp/db
+
+if [ -f tests/tmp/slapd.pid ]; then
+ kill `cat tests/tmp/slapd.pid`
+ sleep 1
+fi
+if [ -f tests/tmp/slapd.pid ]; then
+ kill -9 `cat tests/tmp/slapd.pid`
+ rm -f tests/tmp/slapd.pid
+fi
+
+# we don't consider a slapadd failure as a test suite failure, as it
+# has nothing to do with ldb
+
+MODCONF=tests/tmp/modules.conf
+rm -f $MODCONF
+touch $MODCONF || exit 1
+
+slaptest -u -f $LDBDIR/tests/slapd.conf > /dev/null 2>&1 || {
+ echo "enabling sladp modules"
+cat > $MODCONF <<EOF
+modulepath /usr/lib/ldap
+moduleload back_bdb
+EOF
+}
+
+slaptest -u -f $LDBDIR/tests/slapd.conf || {
+ echo "slaptest failed - skipping ldap tests"
+ exit 0
+}
+
+slapadd -f $LDBDIR/tests/slapd.conf < $LDBDIR/tests/init.ldif || exit 0
+
diff --git a/ldb/tests/kill_slapd.sh b/ldb/tests/kill_slapd.sh
new file mode 100755
index 00000000..91beb108
--- /dev/null
+++ b/ldb/tests/kill_slapd.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+if [ -z "$LDBDIR" ]; then
+ LDBDIR=`dirname $0`/..
+ export LDBDIR
+fi
+
+if [ -f tests/tmp/slapd.pid ]; then
+ echo "killing slapd process `cat tests/tmp/slapd.pid`"
+ kill -9 `cat tests/tmp/slapd.pid`
+ rm -f tests/tmp/slapd.pid
+fi
diff --git a/ldb/tests/ldapi_url.sh b/ldb/tests/ldapi_url.sh
new file mode 100755
index 00000000..fef6c35f
--- /dev/null
+++ b/ldb/tests/ldapi_url.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+# aargh, did LDAP ever have to expose this crap to users ...
+
+BASE=`pwd`
+
+TMPDIR=$BASE/tests/tmp
+
+LDAPI_ESCAPE=`echo $TMPDIR/ldapi | sed 's|/|%2F|g'`
+
+echo "ldapi://$LDAPI_ESCAPE"
diff --git a/ldb/tests/photo.ldif b/ldb/tests/photo.ldif
new file mode 100644
index 00000000..28981b1f
--- /dev/null
+++ b/ldb/tests/photo.ldif
@@ -0,0 +1,5 @@
+dn: cn=Hampster Ursula,ou=Alumni Association,ou=People,o=University of Michigan,c=TEST
+changetype: modify
+add: jpegPhoto
+jpegPhoto:< file://tests/tmp/samba4.png
+
diff --git a/ldb/tests/python/api.py b/ldb/tests/python/api.py
new file mode 100755
index 00000000..1ae3fde7
--- /dev/null
+++ b/ldb/tests/python/api.py
@@ -0,0 +1,463 @@
+#!/usr/bin/python
+# Simple tests for the ldb python bindings.
+# Copyright (C) 2007 Jelmer Vernooij <jelmer@samba.org>
+
+import sys
+import unittest
+
+# Required for the standalone LDB build
+sys.path.append("swig")
+sys.path.append("build/lib.linux-i686-2.4")
+
+import ldb
+
+class NoContextTests(unittest.TestCase):
+ def test_valid_attr_name(self):
+ self.assertTrue(ldb.valid_attr_name("foo"))
+ self.assertFalse(ldb.valid_attr_name("24foo"))
+
+ def test_timestring(self):
+ self.assertEquals("19700101000000.0Z", ldb.timestring(0))
+ self.assertEquals("20071119191012.0Z", ldb.timestring(1195499412))
+
+ def test_string_to_time(self):
+ self.assertEquals(0, ldb.string_to_time("19700101000000.0Z"))
+ self.assertEquals(1195499412, ldb.string_to_time("20071119191012.0Z"))
+
+
+class SimpleLdb(unittest.TestCase):
+ def test_connect(self):
+ ldb.Ldb("foo.tdb")
+
+ def test_connect_none(self):
+ ldb.Ldb()
+
+ def test_connect_later(self):
+ x = ldb.Ldb()
+ x.connect("foo.tdb")
+
+ def test_repr(self):
+ x = ldb.Ldb()
+ self.assertTrue(repr(x).startswith("<ldb connection"))
+
+ def test_set_create_perms(self):
+ x = ldb.Ldb()
+ x.set_create_perms(0600)
+
+ def test_set_modules_dir(self):
+ x = ldb.Ldb()
+ x.set_modules_dir("/tmp")
+
+ def test_search(self):
+ l = ldb.Ldb("foo.tdb")
+ self.assertEquals(len(l.search()), 1)
+
+ def test_search_controls(self):
+ l = ldb.Ldb("foo.tdb")
+ self.assertEquals(len(l.search(controls=["paged_results:1:5"])), 1)
+
+ def test_search_attrs(self):
+ l = ldb.Ldb("foo.tdb")
+ self.assertEquals(len(l.search(ldb.Dn(l, ""), ldb.SCOPE_SUBTREE, "(dc=*)", ["dc"])), 0)
+
+ def test_search_string_dn(self):
+ l = ldb.Ldb("foo.tdb")
+ self.assertEquals(len(l.search("", ldb.SCOPE_SUBTREE, "(dc=*)", ["dc"])), 0)
+
+ def test_search_attr_string(self):
+ l = ldb.Ldb("foo.tdb")
+ self.assertRaises(TypeError, l.search, attrs="dc")
+
+ def test_opaque(self):
+ l = ldb.Ldb("foo.tdb")
+ l.set_opaque("my_opaque", l)
+ self.assertTrue(l.get_opaque("my_opaque") is not None)
+ self.assertEquals(None, l.get_opaque("unknown"))
+
+ def test_parse_control_strings(self):
+ l = ldb.Ldb("foo.tdb")
+ self.assertRaises(ldb.LdbError, l.parse_control_strings, ["foo", "bar"])
+ self.assertTrue(l.parse_control_strings(["paged_results:1:5"]) is not None)
+
+ def test_search_scope_base(self):
+ l = ldb.Ldb("foo.tdb")
+ self.assertEquals(len(l.search(ldb.Dn(l, "dc=foo"),
+ ldb.SCOPE_ONELEVEL)), 0)
+
+ def test_delete(self):
+ l = ldb.Ldb("foo.tdb")
+ self.assertRaises(ldb.LdbError, lambda: l.delete(ldb.Dn(l, "dc=foo")))
+
+ def test_contains(self):
+ l = ldb.Ldb("foo.tdb")
+ self.assertFalse(ldb.Dn(l, "dc=foo") in l)
+ l = ldb.Ldb("foo.tdb")
+ m = ldb.Message()
+ m.dn = ldb.Dn(l, "dc=foo")
+ m["b"] = ["a"]
+ l.add(m)
+ try:
+ self.assertTrue(ldb.Dn(l, "dc=foo") in l)
+ finally:
+ l.delete(m.dn)
+
+ def test_get_config_basedn(self):
+ l = ldb.Ldb("foo.tdb")
+ self.assertEquals(None, l.get_config_basedn())
+
+ def test_get_root_basedn(self):
+ l = ldb.Ldb("foo.tdb")
+ self.assertEquals(None, l.get_root_basedn())
+
+ def test_get_schema_basedn(self):
+ l = ldb.Ldb("foo.tdb")
+ self.assertEquals(None, l.get_schema_basedn())
+
+ def test_get_default_basedn(self):
+ l = ldb.Ldb("foo.tdb")
+ self.assertEquals(None, l.get_default_basedn())
+
+ def test_add(self):
+ l = ldb.Ldb("foo.tdb")
+ m = ldb.Message()
+ m.dn = ldb.Dn(l, "dc=foo")
+ m["bla"] = "bla"
+ self.assertEquals(len(l.search()), 1)
+ l.add(m)
+ try:
+ self.assertEquals(len(l.search()), 2)
+ finally:
+ l.delete(ldb.Dn(l, "dc=foo"))
+
+ def test_add_dict(self):
+ l = ldb.Ldb("foo.tdb")
+ m = {"dn": ldb.Dn(l, "dc=foo"),
+ "bla": "bla"}
+ self.assertEquals(len(l.search()), 1)
+ l.add(m)
+ try:
+ self.assertEquals(len(l.search()), 2)
+ finally:
+ l.delete(ldb.Dn(l, "dc=foo"))
+
+ def test_add_dict_string_dn(self):
+ l = ldb.Ldb("foo.tdb")
+ m = {"dn": "dc=foo", "bla": "bla"}
+ self.assertEquals(len(l.search()), 1)
+ l.add(m)
+ try:
+ self.assertEquals(len(l.search()), 2)
+ finally:
+ l.delete(ldb.Dn(l, "dc=foo"))
+
+ def test_rename(self):
+ l = ldb.Ldb("foo.tdb")
+ m = ldb.Message()
+ m.dn = ldb.Dn(l, "dc=foo")
+ m["bla"] = "bla"
+ self.assertEquals(len(l.search()), 1)
+ l.add(m)
+ try:
+ l.rename(ldb.Dn(l, "dc=foo"), ldb.Dn(l, "dc=bar"))
+ self.assertEquals(len(l.search()), 2)
+ finally:
+ l.delete(ldb.Dn(l, "dc=bar"))
+
+ def test_rename_string_dns(self):
+ l = ldb.Ldb("foo.tdb")
+ m = ldb.Message()
+ m.dn = ldb.Dn(l, "dc=foo")
+ m["bla"] = "bla"
+ self.assertEquals(len(l.search()), 1)
+ l.add(m)
+ try:
+ l.rename("dc=foo", "dc=bar")
+ self.assertEquals(len(l.search()), 2)
+ finally:
+ l.delete(ldb.Dn(l, "dc=bar"))
+
+ def test_modify_delete(self):
+ l = ldb.Ldb("foo.tdb")
+ m = ldb.Message()
+ m.dn = ldb.Dn(l, "dc=modifydelete")
+ m["bla"] = ["1234"]
+ l.add(m)
+ rm = l.search(m.dn)[0]
+ self.assertEquals(["1234"], list(rm["bla"]))
+ try:
+ m = ldb.Message()
+ m.dn = ldb.Dn(l, "dc=modifydelete")
+ m["bla"] = ldb.MessageElement([], ldb.CHANGETYPE_DELETE, "bla")
+ l.modify(m)
+ rm = l.search(m.dn)[0]
+ self.assertEquals(1, len(rm))
+ finally:
+ l.delete(ldb.Dn(l, "dc=modifydelete"))
+
+ def test_modify_add(self):
+ l = ldb.Ldb("foo.tdb")
+ m = ldb.Message()
+ m.dn = ldb.Dn(l, "dc=add")
+ m["bla"] = ["1234"]
+ l.add(m)
+ try:
+ m = ldb.Message()
+ m.dn = ldb.Dn(l, "dc=add")
+ m["bla"] = ldb.MessageElement(["456"], ldb.CHANGETYPE_ADD, "bla")
+ l.modify(m)
+ rm = l.search(m.dn)[0]
+ self.assertEquals(2, len(rm))
+ self.assertEquals(["1234", "456"], list(rm["bla"]))
+ finally:
+ l.delete(ldb.Dn(l, "dc=add"))
+
+ def test_modify_modify(self):
+ l = ldb.Ldb("foo.tdb")
+ m = ldb.Message()
+ m.dn = ldb.Dn(l, "dc=modify2")
+ m["bla"] = ["1234", "456"]
+ l.add(m)
+ try:
+ m = ldb.Message()
+ m.dn = ldb.Dn(l, "dc=modify2")
+ m["bla"] = ldb.MessageElement(["456"], ldb.CHANGETYPE_MODIFY, "bla")
+ l.modify(m)
+ rm = l.search(m.dn)[0]
+ self.assertEquals(2, len(rm))
+ self.assertEquals(["1234"], list(rm["bla"]))
+ finally:
+ l.delete(ldb.Dn(l, "dc=modify2"))
+
+ def test_transaction_commit(self):
+ l = ldb.Ldb("foo.tdb")
+ l.transaction_start()
+ m = ldb.Message(ldb.Dn(l, "dc=foo"))
+ m["foo"] = ["bar"]
+ l.add(m)
+ l.transaction_commit()
+ l.delete(m.dn)
+
+ def test_transaction_cancel(self):
+ l = ldb.Ldb("foo.tdb")
+ l.transaction_start()
+ m = ldb.Message(ldb.Dn(l, "dc=foo"))
+ m["foo"] = ["bar"]
+ l.add(m)
+ l.transaction_cancel()
+ self.assertEquals(0, len(l.search(ldb.Dn(l, "dc=foo"))))
+
+ def test_set_debug(self):
+ def my_report_fn(level, text):
+ pass
+ l = ldb.Ldb("foo.tdb")
+ l.set_debug(my_report_fn)
+
+
+class DnTests(unittest.TestCase):
+ def setUp(self):
+ self.ldb = ldb.Ldb("foo.tdb")
+
+ def test_eq_str(self):
+ x = ldb.Dn(self.ldb, "dc=foo,bar=bloe")
+ self.assertEquals("dc=foo,bar=bloe", x)
+
+ def test_str(self):
+ x = ldb.Dn(self.ldb, "dc=foo,bar=bloe")
+ self.assertEquals(x.__str__(), "dc=foo,bar=bloe")
+
+ def test_repr(self):
+ x = ldb.Dn(self.ldb, "dc=foo,bla=blie")
+ self.assertEquals(x.__repr__(), "Dn('dc=foo,bla=blie')")
+
+ def test_get_casefold(self):
+ x = ldb.Dn(self.ldb, "dc=foo,bar=bloe")
+ self.assertEquals(x.get_casefold(), "DC=FOO,BAR=bloe")
+
+ def test_validate(self):
+ x = ldb.Dn(self.ldb, "dc=foo,bar=bloe")
+ self.assertTrue(x.validate())
+
+ def test_parent(self):
+ x = ldb.Dn(self.ldb, "dc=foo,bar=bloe")
+ self.assertEquals("bar=bloe", x.parent().__str__())
+
+ def test_compare(self):
+ x = ldb.Dn(self.ldb, "dc=foo,bar=bloe")
+ y = ldb.Dn(self.ldb, "dc=foo,bar=bloe")
+ self.assertEquals(x, y)
+ z = ldb.Dn(self.ldb, "dc=foo,bar=blie")
+ self.assertNotEquals(z, y)
+
+ def test_is_valid(self):
+ x = ldb.Dn(self.ldb, "dc=foo,dc=bloe")
+ self.assertTrue(x.is_valid())
+ x = ldb.Dn(self.ldb, "")
+ # is_valid()'s return values appears to be a side effect of
+ # some other ldb functions. yuck.
+ # self.assertFalse(x.is_valid())
+
+ def test_is_special(self):
+ x = ldb.Dn(self.ldb, "dc=foo,bar=bloe")
+ self.assertFalse(x.is_special())
+ x = ldb.Dn(self.ldb, "@FOOBAR")
+ self.assertTrue(x.is_special())
+
+ def test_check_special(self):
+ x = ldb.Dn(self.ldb, "dc=foo,bar=bloe")
+ self.assertFalse(x.check_special("FOOBAR"))
+ x = ldb.Dn(self.ldb, "@FOOBAR")
+ self.assertTrue(x.check_special("@FOOBAR"))
+
+ def test_len(self):
+ x = ldb.Dn(self.ldb, "dc=foo,bar=bloe")
+ self.assertEquals(2, len(x))
+ x = ldb.Dn(self.ldb, "dc=foo")
+ self.assertEquals(1, len(x))
+
+ def test_add_child(self):
+ x = ldb.Dn(self.ldb, "dc=foo,bar=bloe")
+ self.assertTrue(x.add_child(ldb.Dn(self.ldb, "bla=bloe")))
+ self.assertEquals("bla=bloe,dc=foo,bar=bloe", x.__str__())
+
+ def test_add_base(self):
+ x = ldb.Dn(self.ldb, "dc=foo,bar=bloe")
+ self.assertTrue(x.add_base(ldb.Dn(self.ldb, "bla=bloe")))
+ self.assertEquals("dc=foo,bar=bloe,bla=bloe", x.__str__())
+
+ def test_add(self):
+ x = ldb.Dn(self.ldb, "dc=foo")
+ y = ldb.Dn(self.ldb, "bar=bla")
+ self.assertEquals("dc=foo,bar=bla", str(y + x))
+
+ def test_parse_ldif(self):
+ msgs = self.ldb.parse_ldif("dn: foo=bar\n")
+ msg = msgs.next()
+ self.assertEquals("foo=bar", str(msg[1].dn))
+ self.assertTrue(isinstance(msg[1], ldb.Message))
+
+ def test_parse_ldif_more(self):
+ msgs = self.ldb.parse_ldif("dn: foo=bar\n\n\ndn: bar=bar")
+ msg = msgs.next()
+ self.assertEquals("foo=bar", str(msg[1].dn))
+ msg = msgs.next()
+ self.assertEquals("bar=bar", str(msg[1].dn))
+
+ def test_canonical_string(self):
+ x = ldb.Dn(self.ldb, "dc=foo,bar=bloe")
+ self.assertEquals("/bloe/foo", x.canonical_str())
+
+ def test_canonical_ex_string(self):
+ x = ldb.Dn(self.ldb, "dc=foo,bar=bloe")
+ self.assertEquals("/bloe\nfoo", x.canonical_ex_str())
+
+
+class LdbMsgTests(unittest.TestCase):
+ def setUp(self):
+ self.msg = ldb.Message()
+
+ def test_init_dn(self):
+ self.msg = ldb.Message(ldb.Dn(ldb.Ldb(), "dc=foo"))
+ self.assertEquals("dc=foo", str(self.msg.dn))
+
+ def test_iter_items(self):
+ self.assertEquals(0, len(self.msg.items()))
+ self.msg.dn = ldb.Dn(ldb.Ldb("foo.tdb"), "dc=foo")
+ self.assertEquals(1, len(self.msg.items()))
+
+ def test_repr(self):
+ self.msg.dn = ldb.Dn(ldb.Ldb("foo.tdb"), "dc=foo")
+ self.msg["dc"] = "foo"
+ self.assertEquals("Message({'dn': Dn('dc=foo'), 'dc': MessageElement(['foo'])})", repr(self.msg))
+
+ def test_len(self):
+ self.assertEquals(0, len(self.msg))
+
+ def test_notpresent(self):
+ self.assertRaises(KeyError, lambda: self.msg["foo"])
+
+ def test_del(self):
+ del self.msg["foo"]
+
+ def test_add_value(self):
+ self.assertEquals(0, len(self.msg))
+ self.msg["foo"] = ["foo"]
+ self.assertEquals(1, len(self.msg))
+
+ def test_add_value_multiple(self):
+ self.assertEquals(0, len(self.msg))
+ self.msg["foo"] = ["foo", "bla"]
+ self.assertEquals(1, len(self.msg))
+ self.assertEquals(["foo", "bla"], list(self.msg["foo"]))
+
+ def test_set_value(self):
+ self.msg["foo"] = ["fool"]
+ self.assertEquals(["fool"], list(self.msg["foo"]))
+ self.msg["foo"] = ["bar"]
+ self.assertEquals(["bar"], list(self.msg["foo"]))
+
+ def test_keys(self):
+ self.msg.dn = ldb.Dn(ldb.Ldb("foo.tdb"), "@BASEINFO")
+ self.msg["foo"] = ["bla"]
+ self.msg["bar"] = ["bla"]
+ self.assertEquals(["dn", "foo", "bar"], self.msg.keys())
+
+ def test_dn(self):
+ self.msg.dn = ldb.Dn(ldb.Ldb("foo.tdb"), "@BASEINFO")
+ self.assertEquals("@BASEINFO", self.msg.dn.__str__())
+
+ def test_get_dn(self):
+ self.msg.dn = ldb.Dn(ldb.Ldb("foo.tdb"), "@BASEINFO")
+ self.assertEquals("@BASEINFO", self.msg.get("dn").__str__())
+
+ def test_get_other(self):
+ self.msg["foo"] = ["bar"]
+ self.assertEquals("bar", self.msg.get("foo")[0])
+
+ def test_get_unknown(self):
+ self.assertRaises(KeyError, self.msg.get, "lalalala")
+
+
+class MessageElementTests(unittest.TestCase):
+ def test_cmp_element(self):
+ x = ldb.MessageElement(["foo"])
+ y = ldb.MessageElement(["foo"])
+ z = ldb.MessageElement(["bzr"])
+ self.assertEquals(x, y)
+ self.assertNotEquals(x, z)
+
+ def test_create_iterable(self):
+ x = ldb.MessageElement(["foo"])
+ self.assertEquals(["foo"], list(x))
+
+ def test_repr(self):
+ x = ldb.MessageElement(["foo"])
+ self.assertEquals("MessageElement(['foo'])", repr(x))
+ x = ldb.MessageElement(["foo", "bla"])
+ self.assertEquals("MessageElement(['foo','bla'])", repr(x))
+
+ def test_get_item(self):
+ x = ldb.MessageElement(["foo", "bar"])
+ self.assertEquals("foo", x[0])
+ self.assertEquals("bar", x[1])
+ self.assertRaises(KeyError, lambda: x[-1])
+
+ def test_len(self):
+ x = ldb.MessageElement(["foo", "bar"])
+ self.assertEquals(2, len(x))
+
+ def test_eq(self):
+ x = ldb.MessageElement(["foo", "bar"])
+ self.assertEquals(["foo", "bar"], x)
+ x = ldb.MessageElement(["foo"])
+ self.assertEquals("foo", x)
+
+class ExampleModule:
+ name = "example"
+
+class ModuleTests(unittest.TestCase):
+ def test_register_module(self):
+ ldb.register_module(ExampleModule())
+
+if __name__ == '__main__':
+ import unittest
+ unittest.TestProgram()
diff --git a/ldb/tests/python/ldap.py b/ldb/tests/python/ldap.py
new file mode 100755
index 00000000..bc6f80e8
--- /dev/null
+++ b/ldb/tests/python/ldap.py
@@ -0,0 +1,1017 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# This is a port of the original in testprogs/ejs/ldap.js
+
+import getopt
+import optparse
+import sys
+import time
+
+sys.path.append("bin/python")
+
+import samba.getopt as options
+
+from samba.auth import system_session
+from ldb import (SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError,
+ LDB_ERR_NO_SUCH_OBJECT, LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS,
+ LDB_ERR_ENTRY_ALREADY_EXISTS, LDB_ERR_UNWILLING_TO_PERFORM,
+ LDB_ERR_NOT_ALLOWED_ON_NON_LEAF, LDB_ERR_OTHER, LDB_ERR_INVALID_DN_SYNTAX)
+from samba import Ldb
+from subunit import SubunitTestRunner
+from samba import param
+import unittest
+
+parser = optparse.OptionParser("ldap [options] <host>")
+sambaopts = options.SambaOptions(parser)
+parser.add_option_group(sambaopts)
+parser.add_option_group(options.VersionOptions(parser))
+# use command line creds if available
+credopts = options.CredentialsOptions(parser)
+parser.add_option_group(credopts)
+opts, args = parser.parse_args()
+
+if len(args) < 1:
+ parser.print_usage()
+ sys.exit(1)
+
+host = args[0]
+
+lp = sambaopts.get_loadparm()
+creds = credopts.get_credentials(lp)
+
+class BasicTests(unittest.TestCase):
+ def delete_force(self, ldb, dn):
+ try:
+ ldb.delete(dn)
+ except LdbError, (num, _):
+ self.assertEquals(num, LDB_ERR_NO_SUCH_OBJECT)
+
+ def find_basedn(self, ldb):
+ res = ldb.search(base="", expression="", scope=SCOPE_BASE,
+ attrs=["defaultNamingContext"])
+ self.assertEquals(len(res), 1)
+ return res[0]["defaultNamingContext"][0]
+
+ def find_configurationdn(self, ldb):
+ res = ldb.search(base="", expression="", scope=SCOPE_BASE, attrs=["configurationNamingContext"])
+ self.assertEquals(len(res), 1)
+ return res[0]["configurationNamingContext"][0]
+
+ def find_schemadn(self, ldb):
+ res = ldb.search(base="", expression="", scope=SCOPE_BASE, attrs=["schemaNamingContext"])
+ self.assertEquals(len(res), 1)
+ return res[0]["schemaNamingContext"][0]
+
+ def setUp(self):
+ self.ldb = ldb
+ self.gc_ldb = gc_ldb
+ self.base_dn = self.find_basedn(ldb)
+ self.configuration_dn = self.find_configurationdn(ldb)
+ self.schema_dn = self.find_schemadn(ldb)
+
+ print "baseDN: %s\n" % self.base_dn
+
+ self.delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
+ self.delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
+
+ def test_group_add_invalid_member(self):
+ """Testing group add with invalid member"""
+ try:
+ self.ldb.add({
+ "dn": "cn=ldaptestgroup,cn=uSers," + self.base_dn,
+ "objectclass": "group",
+ "member": "cn=ldaptestuser,cn=useRs," + self.base_dn})
+ self.fail()
+ except LdbError, (num, _):
+ self.assertEquals(num, LDB_ERR_NO_SUCH_OBJECT)
+
+ def test_all(self):
+ """Basic tests"""
+
+ self.delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
+
+ print "Testing user add"
+ ldb.add({
+ "dn": "cn=ldaptestuser,cn=uSers," + self.base_dn,
+ "objectclass": ["user", "person"],
+ "cN": "LDAPtestUSER",
+ "givenname": "ldap",
+ "sn": "testy"})
+
+ ldb.add({
+ "dn": "cn=ldaptestgroup,cn=uSers," + self.base_dn,
+ "objectclass": "group",
+ "member": "cn=ldaptestuser,cn=useRs," + self.base_dn})
+
+ self.delete_force(ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
+ ldb.add({
+ "dn": "cn=ldaptestcomputer,cn=computers," + self.base_dn,
+ "objectclass": "computer",
+ "cN": "LDAPtestCOMPUTER"})
+
+ self.delete_force(self.ldb, "cn=ldaptest2computer,cn=computers," + self.base_dn)
+ ldb.add({"dn": "cn=ldaptest2computer,cn=computers," + self.base_dn,
+ "objectClass": "computer",
+ "cn": "LDAPtest2COMPUTER",
+ "userAccountControl": "4096",
+ "displayname": "ldap testy"})
+
+ self.delete_force(self.ldb, "cn=ldaptestcomputer3,cn=computers," + self.base_dn)
+ try:
+ ldb.add({"dn": "cn=ldaptestcomputer3,cn=computers," + self.base_dn,
+ "objectClass": "computer",
+ "cn": "LDAPtest2COMPUTER"
+ })
+ self.fail()
+ except LdbError, (num, _):
+ self.assertEquals(num, LDB_ERR_INVALID_DN_SYNTAX)
+
+ self.delete_force(self.ldb, "cn=ldaptestcomputer3,cn=computers," + self.base_dn)
+ try:
+ ldb.add({"dn": "cn=ldaptestcomputer3,cn=computers," + self.base_dn,
+ "objectClass": "computer",
+ "cn": "ldaptestcomputer3",
+ "sAMAccountType": "805306368"
+ })
+ self.fail()
+ except LdbError, (num, _):
+ self.assertEquals(num, LDB_ERR_UNWILLING_TO_PERFORM)
+
+ self.delete_force(self.ldb, "cn=ldaptestcomputer3,cn=computers," + self.base_dn)
+ try:
+ ldb.add({"dn": "cn=ldaptestcomputer3,cn=computers," + self.base_dn,
+ "objectClass": "computer",
+ "cn": "ldaptestcomputer3",
+ "userAccountControl": "0"
+ })
+ self.fail()
+ except LdbError, (num, _):
+ self.assertEquals(num, LDB_ERR_UNWILLING_TO_PERFORM)
+
+ self.delete_force(self.ldb, "cn=ldaptestuser7,cn=users," + self.base_dn)
+ try:
+ ldb.add({"dn": "cn=ldaptestuser7,cn=users," + self.base_dn,
+ "objectClass": "user",
+ "cn": "LDAPtestuser7",
+ "userAccountControl": "0"
+ })
+ self.fail()
+ except LdbError, (num, _):
+ self.assertEquals(num, LDB_ERR_UNWILLING_TO_PERFORM)
+
+ self.delete_force(self.ldb, "cn=ldaptestuser7,cn=users," + self.base_dn)
+
+ ldb.add({"dn": "cn=ldaptestuser7,cn=users," + self.base_dn,
+ "objectClass": "user",
+ "cn": "LDAPtestuser7",
+ "userAccountControl": "2"
+ })
+
+ self.delete_force(self.ldb, "cn=ldaptestuser7,cn=users," + self.base_dn)
+
+ self.delete_force(self.ldb, "cn=ldaptestcomputer3,cn=computers," + self.base_dn)
+ ldb.add({"dn": "cn=ldaptestcomputer3,cn=computers," + self.base_dn,
+ "objectClass": "computer",
+ "cn": "LDAPtestCOMPUTER3"
+ })
+
+ print "Testing ldb.search for (&(cn=ldaptestcomputer3)(objectClass=user))";
+ res = ldb.search(self.base_dn, expression="(&(cn=ldaptestcomputer3)(objectClass=user))");
+ self.assertEquals(len(res), 1, "Found only %d for (&(cn=ldaptestcomputer3)(objectClass=user))" % len(res))
+
+ self.assertEquals(str(res[0].dn), ("CN=ldaptestcomputer3,CN=Computers," + self.base_dn));
+ self.assertEquals(res[0]["cn"][0], "ldaptestcomputer3");
+ self.assertEquals(res[0]["name"][0], "ldaptestcomputer3");
+ self.assertEquals(res[0]["objectClass"][0], "top");
+ self.assertEquals(res[0]["objectClass"][1], "person");
+ self.assertEquals(res[0]["objectClass"][2], "organizationalPerson");
+ self.assertEquals(res[0]["objectClass"][3], "user");
+ self.assertEquals(res[0]["objectClass"][4], "computer");
+ self.assertTrue("objectGUID" in res[0])
+ self.assertTrue("whenCreated" in res[0])
+ self.assertEquals(res[0]["objectCategory"][0], ("CN=Computer,CN=Schema,CN=Configuration," + self.base_dn));
+ self.assertEquals(int(res[0]["primaryGroupID"][0]), 513);
+ self.assertEquals(int(res[0]["sAMAccountType"][0]), 805306368);
+ self.assertEquals(int(res[0]["userAccountControl"][0]), 546);
+
+ self.delete_force(self.ldb, "cn=ldaptestcomputer3,cn=computers," + self.base_dn)
+
+ print "Testing attribute or value exists behaviour"
+ try:
+ ldb.modify_ldif("""
+dn: cn=ldaptest2computer,cn=computers,""" + self.base_dn + """
+changetype: modify
+replace: servicePrincipalName
+servicePrincipalName: host/ldaptest2computer
+servicePrincipalName: host/ldaptest2computer
+servicePrincipalName: cifs/ldaptest2computer
+""")
+ self.fail()
+ except LdbError, (num, msg):
+ self.assertEquals(num, LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS)
+
+ ldb.modify_ldif("""
+dn: cn=ldaptest2computer,cn=computers,""" + self.base_dn + """
+changetype: modify
+replace: servicePrincipalName
+servicePrincipalName: host/ldaptest2computer
+servicePrincipalName: cifs/ldaptest2computer
+""")
+ try:
+ ldb.modify_ldif("""
+dn: cn=ldaptest2computer,cn=computers,""" + self.base_dn + """
+changetype: modify
+add: servicePrincipalName
+servicePrincipalName: host/ldaptest2computer
+""")
+ self.fail()
+ except LdbError, (num, msg):
+ self.assertEquals(num, LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS)
+
+ print "Testing ranged results"
+ ldb.modify_ldif("""
+dn: cn=ldaptest2computer,cn=computers,""" + self.base_dn + """
+changetype: modify
+replace: servicePrincipalName
+""")
+
+ ldb.modify_ldif("""
+dn: cn=ldaptest2computer,cn=computers,""" + self.base_dn + """
+changetype: modify
+add: servicePrincipalName
+servicePrincipalName: host/ldaptest2computer0
+servicePrincipalName: host/ldaptest2computer1
+servicePrincipalName: host/ldaptest2computer2
+servicePrincipalName: host/ldaptest2computer3
+servicePrincipalName: host/ldaptest2computer4
+servicePrincipalName: host/ldaptest2computer5
+servicePrincipalName: host/ldaptest2computer6
+servicePrincipalName: host/ldaptest2computer7
+servicePrincipalName: host/ldaptest2computer8
+servicePrincipalName: host/ldaptest2computer9
+servicePrincipalName: host/ldaptest2computer10
+servicePrincipalName: host/ldaptest2computer11
+servicePrincipalName: host/ldaptest2computer12
+servicePrincipalName: host/ldaptest2computer13
+servicePrincipalName: host/ldaptest2computer14
+servicePrincipalName: host/ldaptest2computer15
+servicePrincipalName: host/ldaptest2computer16
+servicePrincipalName: host/ldaptest2computer17
+servicePrincipalName: host/ldaptest2computer18
+servicePrincipalName: host/ldaptest2computer19
+servicePrincipalName: host/ldaptest2computer20
+servicePrincipalName: host/ldaptest2computer21
+servicePrincipalName: host/ldaptest2computer22
+servicePrincipalName: host/ldaptest2computer23
+servicePrincipalName: host/ldaptest2computer24
+servicePrincipalName: host/ldaptest2computer25
+servicePrincipalName: host/ldaptest2computer26
+servicePrincipalName: host/ldaptest2computer27
+servicePrincipalName: host/ldaptest2computer28
+servicePrincipalName: host/ldaptest2computer29
+""")
+
+ res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE,
+ attrs=["servicePrincipalName;range=0-*"])
+ self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)")
+ #print len(res[0]["servicePrincipalName;range=0-*"])
+ self.assertEquals(len(res[0]["servicePrincipalName;range=0-*"]), 30)
+
+ res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=0-19"])
+ self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)")
+ # print res[0]["servicePrincipalName;range=0-19"].length
+ self.assertEquals(len(res[0]["servicePrincipalName;range=0-19"]), 20)
+
+
+ res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=0-30"])
+ self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)")
+ self.assertEquals(len(res[0]["servicePrincipalName;range=0-*"]), 30)
+
+ res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=0-40"])
+ self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)")
+ self.assertEquals(len(res[0]["servicePrincipalName;range=0-*"]), 30)
+
+ res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=30-40"])
+ self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)")
+ self.assertEquals(len(res[0]["servicePrincipalName;range=30-*"]), 0)
+
+
+ res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=10-40"])
+ self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)")
+ self.assertEquals(len(res[0]["servicePrincipalName;range=10-*"]), 20)
+ # pos_11 = res[0]["servicePrincipalName;range=10-*"][18]
+
+ res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=11-40"])
+ self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)")
+ self.assertEquals(len(res[0]["servicePrincipalName;range=11-*"]), 19)
+ # print res[0]["servicePrincipalName;range=11-*"][18]
+ # print pos_11
+ # self.assertEquals((res[0]["servicePrincipalName;range=11-*"][18]), pos_11)
+
+ res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=11-15"])
+ self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)")
+ self.assertEquals(len(res[0]["servicePrincipalName;range=11-15"]), 5)
+ # self.assertEquals(res[0]["servicePrincipalName;range=11-15"][4], pos_11)
+
+ res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName"])
+ self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)")
+ # print res[0]["servicePrincipalName"][18]
+ # print pos_11
+ self.assertEquals(len(res[0]["servicePrincipalName"]), 30)
+ # self.assertEquals(res[0]["servicePrincipalName"][18], pos_11)
+
+ self.delete_force(self.ldb, "cn=ldaptestuser2,cn=users," + self.base_dn)
+ ldb.add({
+ "dn": "cn=ldaptestuser2,cn=useRs," + self.base_dn,
+ "objectClass": ["person", "user"],
+ "cn": "LDAPtestUSER2",
+ "givenname": "testy",
+ "sn": "ldap user2"})
+
+ print "Testing Ambigious Name Resolution"
+ # Testing ldb.search for (&(anr=ldap testy)(objectClass=user))
+ res = ldb.search(expression="(&(anr=ldap testy)(objectClass=user))")
+ self.assertEquals(len(res), 3, "Found only %d of 3 for (&(anr=ldap testy)(objectClass=user))" % len(res))
+
+ # Testing ldb.search for (&(anr=testy ldap)(objectClass=user))
+ res = ldb.search(expression="(&(anr=testy ldap)(objectClass=user))")
+ self.assertEquals(len(res), 2, "Found only %d of 2 for (&(anr=testy ldap)(objectClass=user))" % len(res))
+
+ # Testing ldb.search for (&(anr=ldap)(objectClass=user))
+ res = ldb.search(expression="(&(anr=ldap)(objectClass=user))")
+ self.assertEquals(len(res), 4, "Found only %d of 4 for (&(anr=ldap)(objectClass=user))" % len(res))
+
+ # Testing ldb.search for (&(anr==ldap)(objectClass=user))
+ res = ldb.search(expression="(&(anr==ldap)(objectClass=user))")
+ self.assertEquals(len(res), 1, "Could not find (&(anr==ldap)(objectClass=user)). Found only %d for (&(anr=ldap)(objectClass=user))" % len(res))
+
+ self.assertEquals(str(res[0].dn), ("CN=ldaptestuser,CN=Users," + self.base_dn))
+ self.assertEquals(res[0]["cn"][0], "ldaptestuser")
+ self.assertEquals(res[0]["name"], "ldaptestuser")
+
+ # Testing ldb.search for (&(anr=testy)(objectClass=user))
+ res = ldb.search(expression="(&(anr=testy)(objectClass=user))")
+ self.assertEquals(len(res), 2, "Found only %d for (&(anr=testy)(objectClass=user))" % len(res))
+
+ # Testing ldb.search for (&(anr=testy ldap)(objectClass=user))
+ res = ldb.search(expression="(&(anr=testy ldap)(objectClass=user))")
+ self.assertEquals(len(res), 2, "Found only %d for (&(anr=testy ldap)(objectClass=user))" % len(res))
+
+ # Testing ldb.search for (&(anr==testy ldap)(objectClass=user))
+# this test disabled for the moment, as anr with == tests are not understood
+# res = ldb.search(expression="(&(anr==testy ldap)(objectClass=user))")
+# self.assertEquals(len(res), 1, "Found only %d for (&(anr==testy ldap)(objectClass=user))" % len(res))
+
+ self.assertEquals(str(res[0].dn), ("CN=ldaptestuser,CN=Users," + self.base_dn))
+ self.assertEquals(res[0]["cn"][0], "ldaptestuser")
+ self.assertEquals(res[0]["name"][0], "ldaptestuser")
+
+ # Testing ldb.search for (&(anr==testy ldap)(objectClass=user))
+# res = ldb.search(expression="(&(anr==testy ldap)(objectClass=user))")
+# self.assertEquals(len(res), 1, "Could not find (&(anr==testy ldap)(objectClass=user))")
+
+ self.assertEquals(str(res[0].dn), ("CN=ldaptestuser,CN=Users," + self.base_dn))
+ self.assertEquals(res[0]["cn"][0], "ldaptestuser")
+ self.assertEquals(res[0]["name"][0], "ldaptestuser")
+
+ # Testing ldb.search for (&(anr=testy ldap user)(objectClass=user))
+ res = ldb.search(expression="(&(anr=testy ldap user)(objectClass=user))")
+ self.assertEquals(len(res), 1, "Could not find (&(anr=testy ldap user)(objectClass=user))")
+
+ self.assertEquals(str(res[0].dn), ("CN=ldaptestuser2,CN=Users," + self.base_dn))
+ self.assertEquals(res[0]["cn"], "ldaptestuser2")
+ self.assertEquals(res[0]["name"], "ldaptestuser2")
+
+ # Testing ldb.search for (&(anr==testy ldap user2)(objectClass=user))
+# res = ldb.search(expression="(&(anr==testy ldap user2)(objectClass=user))")
+# self.assertEquals(len(res), 1, "Could not find (&(anr==testy ldap user2)(objectClass=user))")
+
+ self.assertEquals(str(res[0].dn), ("CN=ldaptestuser2,CN=Users," + self.base_dn))
+ self.assertEquals(res[0]["cn"], "ldaptestuser2")
+ self.assertEquals(res[0]["name"], "ldaptestuser2")
+
+ # Testing ldb.search for (&(anr==ldap user2)(objectClass=user))
+# res = ldb.search(expression="(&(anr==ldap user2)(objectClass=user))")
+# self.assertEquals(len(res), 1, "Could not find (&(anr==ldap user2)(objectClass=user))")
+
+ self.assertEquals(str(res[0].dn), ("CN=ldaptestuser2,CN=Users," + self.base_dn))
+ self.assertEquals(res[0]["cn"], "ldaptestuser2")
+ self.assertEquals(res[0]["name"], "ldaptestuser2")
+
+ # Testing ldb.search for (&(anr==not ldap user2)(objectClass=user))
+# res = ldb.search(expression="(&(anr==not ldap user2)(objectClass=user))")
+# self.assertEquals(len(res), 0, "Must not find (&(anr==not ldap user2)(objectClass=user))")
+
+ # Testing ldb.search for (&(anr=not ldap user2)(objectClass=user))
+ res = ldb.search(expression="(&(anr=not ldap user2)(objectClass=user))")
+ self.assertEquals(len(res), 0, "Must not find (&(anr=not ldap user2)(objectClass=user))")
+
+ # Testing ldb.search for (&(anr="testy ldap")(objectClass=user)) (ie, with quotes)
+# res = ldb.search(expression="(&(anr==\"testy ldap\")(objectClass=user))")
+# self.assertEquals(len(res), 0, "Found (&(anr==\"testy ldap\")(objectClass=user))")
+
+ print "Testing Group Modifies"
+ ldb.modify_ldif("""
+dn: cn=ldaptestgroup,cn=users,""" + self.base_dn + """
+changetype: modify
+add: member
+member: cn=ldaptestuser2,cn=users,""" + self.base_dn + """
+member: cn=ldaptestcomputer,cn=computers,""" + self.base_dn + """
+""")
+
+ self.delete_force(ldb, "cn=ldaptestuser3,cn=users," + self.base_dn)
+
+ print "Testing adding non-existent user to a group"
+ try:
+ ldb.modify_ldif("""
+dn: cn=ldaptestgroup,cn=users,""" + self.base_dn + """
+changetype: modify
+add: member
+member: cn=ldaptestuser3,cn=users,""" + self.base_dn + """
+""")
+ self.fail()
+ except LdbError, (num, _):
+ self.assertEquals(num, LDB_ERR_NO_SUCH_OBJECT)
+
+ print "Testing Renames"
+
+ ldb.rename("cn=ldaptestuser2,cn=users," + self.base_dn, "cn=ldaptestuser3,cn=users," + self.base_dn)
+
+ ldb.rename("cn=ldaptestuser3,cn=users," + self.base_dn, "cn=ldaptestuser3,cn=users," + self.base_dn)
+
+ ldb.rename("cn=ldaptestuser3,cn=users," + self.base_dn, "cn=ldaptestUSER3,cn=users," + self.base_dn)
+
+ print "Testing ldb.search for (&(cn=ldaptestuser3)(objectClass=user))"
+ res = ldb.search(expression="(&(cn=ldaptestuser3)(objectClass=user))")
+ self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestuser3)(objectClass=user))")
+
+ self.assertEquals(str(res[0].dn), ("CN=ldaptestUSER3,CN=Users," + self.base_dn))
+ self.assertEquals(res[0]["cn"], "ldaptestUSER3")
+ self.assertEquals(res[0]["name"], "ldaptestUSER3")
+
+ #"Testing ldb.search for (&(&(cn=ldaptestuser3)(userAccountControl=*))(objectClass=user))"
+ res = ldb.search(expression="(&(&(cn=ldaptestuser3)(userAccountControl=*))(objectClass=user))")
+ self.assertEquals(len(res), 1, "(&(&(cn=ldaptestuser3)(userAccountControl=*))(objectClass=user))")
+
+ self.assertEquals(str(res[0].dn), ("CN=ldaptestUSER3,CN=Users," + self.base_dn))
+ self.assertEquals(res[0]["cn"], "ldaptestUSER3")
+ self.assertEquals(res[0]["name"], "ldaptestUSER3")
+
+ #"Testing ldb.search for (&(&(cn=ldaptestuser3)(userAccountControl=546))(objectClass=user))"
+ res = ldb.search(expression="(&(&(cn=ldaptestuser3)(userAccountControl=546))(objectClass=user))")
+ self.assertEquals(len(res), 1, "(&(&(cn=ldaptestuser3)(userAccountControl=546))(objectClass=user))")
+
+ self.assertEquals(str(res[0].dn), ("CN=ldaptestUSER3,CN=Users," + self.base_dn))
+ self.assertEquals(res[0]["cn"], "ldaptestUSER3")
+ self.assertEquals(res[0]["name"], "ldaptestUSER3")
+
+ #"Testing ldb.search for (&(&(cn=ldaptestuser3)(userAccountControl=547))(objectClass=user))"
+ res = ldb.search(expression="(&(&(cn=ldaptestuser3)(userAccountControl=547))(objectClass=user))")
+ self.assertEquals(len(res), 0, "(&(&(cn=ldaptestuser3)(userAccountControl=547))(objectClass=user))")
+
+ # This is a Samba special, and does not exist in real AD
+ # print "Testing ldb.search for (dn=CN=ldaptestUSER3,CN=Users," + self.base_dn + ")"
+ # res = ldb.search("(dn=CN=ldaptestUSER3,CN=Users," + self.base_dn + ")")
+ # if (res.error != 0 || len(res) != 1) {
+ # print "Could not find (dn=CN=ldaptestUSER3,CN=Users," + self.base_dn + ")"
+ # self.assertEquals(len(res), 1)
+ # }
+ # self.assertEquals(res[0].dn, ("CN=ldaptestUSER3,CN=Users," + self.base_dn))
+ # self.assertEquals(res[0].cn, "ldaptestUSER3")
+ # self.assertEquals(res[0].name, "ldaptestUSER3")
+
+ print "Testing ldb.search for (distinguishedName=CN=ldaptestUSER3,CN=Users," + self.base_dn + ")"
+ res = ldb.search(expression="(distinguishedName=CN=ldaptestUSER3,CN=Users," + self.base_dn + ")")
+ self.assertEquals(len(res), 1, "Could not find (dn=CN=ldaptestUSER3,CN=Users," + self.base_dn + ")")
+ self.assertEquals(str(res[0].dn), ("CN=ldaptestUSER3,CN=Users," + self.base_dn))
+ self.assertEquals(res[0]["cn"], "ldaptestUSER3")
+ self.assertEquals(res[0]["name"], "ldaptestUSER3")
+
+ # ensure we cannot add it again
+ try:
+ ldb.add({"dn": "cn=ldaptestuser3,cn=userS," + self.base_dn,
+ "objectClass": ["person", "user"],
+ "cn": "LDAPtestUSER3"})
+ self.fail()
+ except LdbError, (num, _):
+ self.assertEquals(num, LDB_ERR_ENTRY_ALREADY_EXISTS)
+
+ # rename back
+ ldb.rename("cn=ldaptestuser3,cn=users," + self.base_dn, "cn=ldaptestuser2,cn=users," + self.base_dn)
+
+ # ensure we cannnot rename it twice
+ try:
+ ldb.rename("cn=ldaptestuser3,cn=users," + self.base_dn,
+ "cn=ldaptestuser2,cn=users," + self.base_dn)
+ self.fail()
+ except LdbError, (num, _):
+ self.assertEquals(num, LDB_ERR_NO_SUCH_OBJECT)
+
+ # ensure can now use that name
+ ldb.add({"dn": "cn=ldaptestuser3,cn=users," + self.base_dn,
+ "objectClass": ["person", "user"],
+ "cn": "LDAPtestUSER3"})
+
+ # ensure we now cannnot rename
+ try:
+ ldb.rename("cn=ldaptestuser2,cn=users," + self.base_dn, "cn=ldaptestuser3,cn=users," + self.base_dn)
+ self.fail()
+ except LdbError, (num, _):
+ self.assertEquals(num, LDB_ERR_ENTRY_ALREADY_EXISTS)
+ try:
+ ldb.rename("cn=ldaptestuser3,cn=users," + self.base_dn, "cn=ldaptestuser3,cn=configuration," + self.base_dn)
+ self.fail()
+ except LdbError, (num, _):
+ self.assertTrue(num in (71, 64))
+
+ ldb.rename("cn=ldaptestuser3,cn=users," + self.base_dn, "cn=ldaptestuser5,cn=users," + self.base_dn)
+
+ ldb.delete("cn=ldaptestuser5,cn=users," + self.base_dn)
+
+ self.delete_force(ldb, "cn=ldaptestgroup2,cn=users," + self.base_dn)
+
+ ldb.rename("cn=ldaptestgroup,cn=users," + self.base_dn, "cn=ldaptestgroup2,cn=users," + self.base_dn)
+
+ print "Testing subtree Renames"
+
+ ldb.add({"dn": "cn=ldaptestcontainer," + self.base_dn,
+ "objectClass": "container"})
+
+ self.delete_force(self.ldb, "cn=ldaptestuser4,cn=ldaptestcontainer," + self.base_dn)
+ ldb.add({"dn": "CN=ldaptestuser4,CN=ldaptestcontainer," + self.base_dn,
+ "objectClass": ["person", "user"],
+ "cn": "LDAPtestUSER4"})
+
+ ldb.modify_ldif("""
+dn: cn=ldaptestgroup2,cn=users,""" + self.base_dn + """
+changetype: modify
+add: member
+member: cn=ldaptestuser4,cn=ldaptestcontainer,""" + self.base_dn + """
+""")
+
+ print "Testing ldb.rename of cn=ldaptestcontainer," + self.base_dn + " to cn=ldaptestcontainer2," + self.base_dn
+ ldb.rename("CN=ldaptestcontainer," + self.base_dn, "CN=ldaptestcontainer2," + self.base_dn)
+
+ print "Testing ldb.search for (&(cn=ldaptestuser4)(objectClass=user))"
+ res = ldb.search(expression="(&(cn=ldaptestuser4)(objectClass=user))")
+ self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestuser4)(objectClass=user))")
+
+ print "Testing subtree ldb.search for (&(cn=ldaptestuser4)(objectClass=user)) in (just renamed from) cn=ldaptestcontainer," + self.base_dn
+ try:
+ ldb.search("cn=ldaptestcontainer," + self.base_dn,
+ expression="(&(cn=ldaptestuser4)(objectClass=user))",
+ scope=SCOPE_SUBTREE)
+ self.fail()
+ except LdbError, (num, _):
+ self.assertEquals(num, LDB_ERR_NO_SUCH_OBJECT)
+
+ print "Testing one-level ldb.search for (&(cn=ldaptestuser4)(objectClass=user)) in (just renamed from) cn=ldaptestcontainer," + self.base_dn
+ try:
+ res = ldb.search("cn=ldaptestcontainer," + self.base_dn,
+ expression="(&(cn=ldaptestuser4)(objectClass=user))", scope=SCOPE_ONELEVEL)
+ self.fail()
+ except LdbError, (num, _):
+ self.assertEquals(num, LDB_ERR_NO_SUCH_OBJECT)
+
+ print "Testing ldb.search for (&(cn=ldaptestuser4)(objectClass=user)) in renamed container"
+ res = ldb.search("cn=ldaptestcontainer2," + self.base_dn, expression="(&(cn=ldaptestuser4)(objectClass=user))", scope=SCOPE_SUBTREE)
+ self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestuser4)(objectClass=user)) under cn=ldaptestcontainer2," + self.base_dn)
+
+ self.assertEquals(str(res[0].dn), ("CN=ldaptestuser4,CN=ldaptestcontainer2," + self.base_dn))
+ self.assertEquals(res[0]["memberOf"][0].upper(), ("CN=ldaptestgroup2,CN=Users," + self.base_dn).upper())
+
+ time.sleep(4)
+
+ print "Testing ldb.search for (&(member=CN=ldaptestuser4,CN=ldaptestcontainer2," + self.base_dn + ")(objectclass=group)) to check subtree renames and linked attributes"
+ res = ldb.search(self.base_dn, expression="(&(member=CN=ldaptestuser4,CN=ldaptestcontainer2," + self.base_dn + ")(objectclass=group))", scope=SCOPE_SUBTREE)
+ self.assertEquals(len(res), 1, "Could not find (&(member=CN=ldaptestuser4,CN=ldaptestcontainer2," + self.base_dn + ")(objectclass=group)), perhaps linked attributes are not conistant with subtree renames?")
+
+ print "Testing ldb.rename (into itself) of cn=ldaptestcontainer2," + self.base_dn + " to cn=ldaptestcontainer,cn=ldaptestcontainer2," + self.base_dn
+ try:
+ ldb.rename("cn=ldaptestcontainer2," + self.base_dn, "cn=ldaptestcontainer,cn=ldaptestcontainer2," + self.base_dn)
+ self.fail()
+ except LdbError, (num, _):
+ self.assertEquals(num, LDB_ERR_UNWILLING_TO_PERFORM)
+
+ print "Testing ldb.rename (into non-existent container) of cn=ldaptestcontainer2," + self.base_dn + " to cn=ldaptestcontainer,cn=ldaptestcontainer3," + self.base_dn
+ try:
+ ldb.rename("cn=ldaptestcontainer2," + self.base_dn, "cn=ldaptestcontainer,cn=ldaptestcontainer3," + self.base_dn)
+ self.fail()
+ except LdbError, (num, _):
+ self.assertTrue(num in (LDB_ERR_UNWILLING_TO_PERFORM, LDB_ERR_OTHER))
+
+ print "Testing delete (should fail, not a leaf node) of renamed cn=ldaptestcontainer2," + self.base_dn
+ try:
+ ldb.delete("cn=ldaptestcontainer2," + self.base_dn)
+ self.fail()
+ except LdbError, (num, _):
+ self.assertEquals(num, LDB_ERR_NOT_ALLOWED_ON_NON_LEAF)
+
+ print "Testing base ldb.search for CN=ldaptestuser4,CN=ldaptestcontainer2," + self.base_dn
+ res = ldb.search(expression="(objectclass=*)", base=("CN=ldaptestuser4,CN=ldaptestcontainer2," + self.base_dn), scope=SCOPE_BASE)
+ self.assertEquals(len(res), 1)
+ res = ldb.search(expression="(cn=ldaptestuser40)", base=("CN=ldaptestuser4,CN=ldaptestcontainer2," + self.base_dn), scope=SCOPE_BASE)
+ self.assertEquals(len(res), 0)
+
+ print "Testing one-level ldb.search for (&(cn=ldaptestuser4)(objectClass=user)) in cn=ldaptestcontainer2," + self.base_dn
+ res = ldb.search(expression="(&(cn=ldaptestuser4)(objectClass=user))", base=("cn=ldaptestcontainer2," + self.base_dn), scope=SCOPE_ONELEVEL)
+ # FIXME: self.assertEquals(len(res), 0)
+
+ print "Testing one-level ldb.search for (&(cn=ldaptestuser4)(objectClass=user)) in cn=ldaptestcontainer2," + self.base_dn
+ res = ldb.search(expression="(&(cn=ldaptestuser4)(objectClass=user))", base=("cn=ldaptestcontainer2," + self.base_dn), scope=SCOPE_SUBTREE)
+ # FIXME: self.assertEquals(len(res), 0)
+
+ print "Testing delete of subtree renamed "+("CN=ldaptestuser4,CN=ldaptestcontainer2," + self.base_dn)
+ ldb.delete(("CN=ldaptestuser4,CN=ldaptestcontainer2," + self.base_dn))
+ print "Testing delete of renamed cn=ldaptestcontainer2," + self.base_dn
+ ldb.delete("cn=ldaptestcontainer2," + self.base_dn)
+
+ self.delete_force(self.ldb, "cn=ldaptestutf8user èùéìòà ,cn=users," + self.base_dn)
+ ldb.add({"dn": "cn=ldaptestutf8user èùéìòà ,cn=users," + self.base_dn, "objectClass": "user"})
+
+ self.delete_force(self.ldb, "cn=ldaptestutf8user2 èùéìòà ,cn=users," + self.base_dn)
+ ldb.add({"dn": "cn=ldaptestutf8user2 èùéìòà ,cn=users," + self.base_dn, "objectClass": "user"})
+
+ print "Testing ldb.search for (&(cn=ldaptestuser)(objectClass=user))"
+ res = ldb.search(expression="(&(cn=ldaptestuser)(objectClass=user))")
+ self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestuser)(objectClass=user))")
+
+ self.assertEquals(str(res[0].dn), ("CN=ldaptestuser,CN=Users," + self.base_dn))
+ self.assertEquals(res[0]["cn"], "ldaptestuser")
+ self.assertEquals(res[0]["name"], "ldaptestuser")
+ self.assertEquals(res[0]["objectClass"], ["top", "person", "organizationalPerson", "user"])
+ self.assertTrue("objectGUID" in res[0])
+ self.assertTrue("whenCreated" in res[0])
+ self.assertEquals(res[0]["objectCategory"], ("CN=Person,CN=Schema,CN=Configuration," + self.base_dn))
+ self.assertEquals(int(res[0]["sAMAccountType"][0]), 805306368)
+ self.assertEquals(int(res[0]["userAccountControl"][0]), 546)
+ self.assertEquals(res[0]["memberOf"][0].upper(), ("CN=ldaptestgroup2,CN=Users," + self.base_dn).upper())
+ self.assertEquals(len(res[0]["memberOf"]), 1)
+
+ print "Testing ldb.search for (&(cn=ldaptestuser)(objectCategory=cn=person,cn=schema,cn=configuration," + self.base_dn + "))"
+ res2 = ldb.search(expression="(&(cn=ldaptestuser)(objectCategory=cn=person,cn=schema,cn=configuration," + self.base_dn + "))")
+ self.assertEquals(len(res2), 1, "Could not find (&(cn=ldaptestuser)(objectCategory=cn=person,cn=schema,cn=configuration," + self.base_dn + "))")
+
+ self.assertEquals(res[0].dn, res2[0].dn)
+
+ print "Testing ldb.search for (&(cn=ldaptestuser)(objectCategory=PerSon))"
+ res3 = ldb.search(expression="(&(cn=ldaptestuser)(objectCategory=PerSon))")
+ self.assertEquals(len(res3), 1, "Could not find (&(cn=ldaptestuser)(objectCategory=PerSon)): matched %d" % len(res3))
+
+ self.assertEquals(res[0].dn, res3[0].dn)
+
+ if gc_ldb is not None:
+ print "Testing ldb.search for (&(cn=ldaptestuser)(objectCategory=PerSon)) in Global Catalog"
+ res3gc = gc_ldb.search(expression="(&(cn=ldaptestuser)(objectCategory=PerSon))")
+ self.assertEquals(len(res3gc), 1)
+
+ self.assertEquals(res[0].dn, res3gc[0].dn)
+
+ print "Testing ldb.search for (&(cn=ldaptestuser)(objectCategory=PerSon)) in with 'phantom root' control"
+
+ res3control = gc_ldb.search(self.base_dn, expression="(&(cn=ldaptestuser)(objectCategory=PerSon))", scope=SCOPE_SUBTREE, attrs=["cn"], controls=["search_options:1:2"])
+ self.assertEquals(len(res3control), 1, "Could not find (&(cn=ldaptestuser)(objectCategory=PerSon)) in Global Catalog")
+
+ self.assertEquals(res[0].dn, res3control[0].dn)
+
+ ldb.delete(res[0].dn)
+
+ print "Testing ldb.search for (&(cn=ldaptestcomputer)(objectClass=user))"
+ res = ldb.search(expression="(&(cn=ldaptestcomputer)(objectClass=user))")
+ self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestuser)(objectClass=user))")
+
+ self.assertEquals(str(res[0].dn), ("CN=ldaptestcomputer,CN=Computers," + self.base_dn))
+ self.assertEquals(res[0]["cn"], "ldaptestcomputer")
+ self.assertEquals(res[0]["name"], "ldaptestcomputer")
+ self.assertEquals(res[0]["objectClass"], ["top", "person", "organizationalPerson", "user", "computer"])
+ self.assertTrue("objectGUID" in res[0])
+ self.assertTrue("whenCreated" in res[0])
+ self.assertEquals(res[0]["objectCategory"], ("CN=Computer,CN=Schema,CN=Configuration," + self.base_dn))
+ self.assertEquals(int(res[0]["primaryGroupID"][0]), 513)
+ self.assertEquals(int(res[0]["sAMAccountType"][0]), 805306368)
+ self.assertEquals(int(res[0]["userAccountControl"][0]), 546)
+ self.assertEquals(res[0]["memberOf"][0].upper(), ("CN=ldaptestgroup2,CN=Users," + self.base_dn).upper())
+ self.assertEquals(len(res[0]["memberOf"]), 1)
+
+ print "Testing ldb.search for (&(cn=ldaptestcomputer)(objectCategory=cn=computer,cn=schema,cn=configuration," + self.base_dn + "))"
+ res2 = ldb.search(expression="(&(cn=ldaptestcomputer)(objectCategory=cn=computer,cn=schema,cn=configuration," + self.base_dn + "))")
+ self.assertEquals(len(res2), 1, "Could not find (&(cn=ldaptestcomputer)(objectCategory=cn=computer,cn=schema,cn=configuration," + self.base_dn + "))")
+
+ self.assertEquals(res[0].dn, res2[0].dn)
+
+ if gc_ldb is not None:
+ print "Testing ldb.search for (&(cn=ldaptestcomputer)(objectCategory=cn=computer,cn=schema,cn=configuration," + self.base_dn + ")) in Global Catlog"
+ res2gc = gc_ldb.search(expression="(&(cn=ldaptestcomputer)(objectCategory=cn=computer,cn=schema,cn=configuration," + self.base_dn + "))")
+ self.assertEquals(len(res2gc), 1, "Could not find (&(cn=ldaptestcomputer)(objectCategory=cn=computer,cn=schema,cn=configuration," + self.base_dn + ")) in Global Catlog")
+
+ self.assertEquals(res[0].dn, res2gc[0].dn)
+
+ print "Testing ldb.search for (&(cn=ldaptestcomputer)(objectCategory=compuTER))"
+ res3 = ldb.search(expression="(&(cn=ldaptestcomputer)(objectCategory=compuTER))")
+ self.assertEquals(len(res3), 1, "Could not find (&(cn=ldaptestcomputer)(objectCategory=compuTER))")
+
+ self.assertEquals(res[0].dn, res3[0].dn)
+
+ if gc_ldb is not None:
+ print "Testing ldb.search for (&(cn=ldaptestcomputer)(objectCategory=compuTER)) in Global Catalog"
+ res3gc = gc_ldb.search(expression="(&(cn=ldaptestcomputer)(objectCategory=compuTER))")
+ self.assertEquals(len(res3gc), 1, "Could not find (&(cn=ldaptestcomputer)(objectCategory=compuTER)) in Global Catalog")
+
+ self.assertEquals(res[0].dn, res3gc[0].dn)
+
+ print "Testing ldb.search for (&(cn=ldaptestcomp*r)(objectCategory=compuTER))"
+ res4 = ldb.search(expression="(&(cn=ldaptestcomp*r)(objectCategory=compuTER))")
+ self.assertEquals(len(res4), 1, "Could not find (&(cn=ldaptestcomp*r)(objectCategory=compuTER))")
+
+ self.assertEquals(res[0].dn, res4[0].dn)
+
+ print "Testing ldb.search for (&(cn=ldaptestcomput*)(objectCategory=compuTER))"
+ res5 = ldb.search(expression="(&(cn=ldaptestcomput*)(objectCategory=compuTER))")
+ self.assertEquals(len(res5), 1, "Could not find (&(cn=ldaptestcomput*)(objectCategory=compuTER))")
+
+ self.assertEquals(res[0].dn, res5[0].dn)
+
+ print "Testing ldb.search for (&(cn=*daptestcomputer)(objectCategory=compuTER))"
+ res6 = ldb.search(expression="(&(cn=*daptestcomputer)(objectCategory=compuTER))")
+ self.assertEquals(len(res6), 1, "Could not find (&(cn=*daptestcomputer)(objectCategory=compuTER))")
+
+ self.assertEquals(res[0].dn, res6[0].dn)
+
+ ldb.delete(res[0].dn)
+
+ print "Testing ldb.search for (&(cn=ldaptest2computer)(objectClass=user))"
+ res = ldb.search(expression="(&(cn=ldaptest2computer)(objectClass=user))")
+ self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptest2computer)(objectClass=user))")
+
+ self.assertEquals(res[0].dn, ("CN=ldaptest2computer,CN=Computers," + self.base_dn))
+ self.assertEquals(res[0]["cn"], "ldaptest2computer")
+ self.assertEquals(res[0]["name"], "ldaptest2computer")
+ self.assertEquals(res[0]["objectClass"], ["top", "person", "organizationalPerson", "user", "computer"])
+ self.assertTrue("objectGUID" in res[0])
+ self.assertTrue("whenCreated" in res[0])
+ self.assertEquals(res[0]["objectCategory"][0], "CN=Computer,CN=Schema,CN=Configuration," + self.base_dn)
+ self.assertEquals(int(res[0]["sAMAccountType"][0]), 805306369)
+ self.assertEquals(int(res[0]["userAccountControl"][0]), 4096)
+
+ ldb.delete(res[0].dn)
+
+ attrs = ["cn", "name", "objectClass", "objectGUID", "whenCreated", "nTSecurityDescriptor", "memberOf"]
+ print "Testing ldb.search for (&(cn=ldaptestUSer2)(objectClass=user))"
+ res = ldb.search(self.base_dn, expression="(&(cn=ldaptestUSer2)(objectClass=user))", scope=SCOPE_SUBTREE, attrs=attrs)
+ self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestUSer2)(objectClass=user))")
+
+ self.assertEquals(res[0].dn, ("CN=ldaptestuser2,CN=Users," + self.base_dn))
+ self.assertEquals(res[0]["cn"], "ldaptestuser2")
+ self.assertEquals(res[0]["name"], "ldaptestuser2")
+ self.assertEquals(res[0]["objectClass"], ["top", "person", "organizationalPerson", "user"])
+ self.assertTrue("objectGUID" in res[0])
+ self.assertTrue("whenCreated" in res[0])
+ self.assertTrue("nTSecurityDescriptor" in res[0])
+ self.assertEquals(res[0]["memberOf"][0].upper(), ("CN=ldaptestgroup2,CN=Users," + self.base_dn).upper())
+
+ attrs = ["cn", "name", "objectClass", "objectGUID", "whenCreated", "nTSecurityDescriptor", "member"]
+ print "Testing ldb.search for (&(cn=ldaptestgroup2)(objectClass=group))"
+ res = ldb.search(self.base_dn, expression="(&(cn=ldaptestgroup2)(objectClass=group))", scope=SCOPE_SUBTREE, attrs=attrs)
+ self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestgroup2)(objectClass=group))")
+
+ self.assertEquals(res[0].dn, ("CN=ldaptestgroup2,CN=Users," + self.base_dn))
+ self.assertEquals(res[0]["cn"], "ldaptestgroup2")
+ self.assertEquals(res[0]["name"], "ldaptestgroup2")
+ self.assertEquals(res[0]["objectClass"], ["top", "group"])
+ self.assertTrue("objectGuid" not in res[0])
+ self.assertTrue("whenCreated" in res[0])
+ self.assertTrue("nTSecurityDescriptor" in res[0])
+ self.assertEquals(res[0]["member"][0].upper(), ("CN=ldaptestuser2,CN=Users," + self.base_dn).upper())
+
+ ldb.modify_ldif("""
+dn: cn=ldaptestgroup2,cn=users,""" + self.base_dn + """
+changetype: modify
+replace: member
+member: CN=ldaptestuser2,CN=Users,""" + self.base_dn + """
+member: CN=ldaptestutf8user èùéìòà,CN=Users,""" + self.base_dn + """
+""")
+
+ print "Testing Linked attribute behaviours"
+ ldb.modify_ldif("""
+dn: cn=ldaptestgroup2,cn=users,""" + self.base_dn + """
+changetype: modify
+delete: member
+""")
+
+ ldb.modify_ldif("""
+dn: cn=ldaptestgroup2,cn=users,""" + self.base_dn + """
+changetype: modify
+add: member
+member: CN=ldaptestuser2,CN=Users,""" + self.base_dn + """
+member: CN=ldaptestutf8user èùéìòà,CN=Users,""" + self.base_dn + """
+""")
+
+ ldb.modify_ldif("""
+dn: cn=ldaptestgroup2,cn=users,""" + self.base_dn + """
+changetype: modify
+replace: member
+""")
+
+ ldb.modify_ldif("""
+dn: cn=ldaptestgroup2,cn=users,""" + self.base_dn + """
+changetype: modify
+add: member
+member: CN=ldaptestuser2,CN=Users,""" + self.base_dn + """
+member: CN=ldaptestutf8user èùéìòà,CN=Users,""" + self.base_dn + """
+""")
+
+ ldb.modify_ldif("""
+dn: cn=ldaptestgroup2,cn=users,""" + self.base_dn + """
+changetype: modify
+delete: member
+member: CN=ldaptestutf8user èùéìòà,CN=Users,""" + self.base_dn + """
+""")
+
+ res = ldb.search(self.base_dn, expression="(&(cn=ldaptestgroup2)(objectClass=group))", scope=SCOPE_SUBTREE, attrs=attrs)
+ self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestgroup2)(objectClass=group))")
+
+ self.assertEquals(res[0].dn, ("CN=ldaptestgroup2,CN=Users," + self.base_dn))
+ self.assertEquals(res[0]["member"][0], ("CN=ldaptestuser2,CN=Users," + self.base_dn))
+ self.assertEquals(len(res[0]["member"]), 1)
+
+ ldb.delete(("CN=ldaptestuser2,CN=Users," + self.base_dn))
+
+ time.sleep(4)
+
+ attrs = ["cn", "name", "objectClass", "objectGUID", "whenCreated", "nTSecurityDescriptor", "member"]
+ print "Testing ldb.search for (&(cn=ldaptestgroup2)(objectClass=group)) to check linked delete"
+ res = ldb.search(self.base_dn, expression="(&(cn=ldaptestgroup2)(objectClass=group))", scope=SCOPE_SUBTREE, attrs=attrs)
+ self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestgroup2)(objectClass=group)) to check linked delete")
+
+ self.assertEquals(res[0].dn, ("CN=ldaptestgroup2,CN=Users," + self.base_dn))
+ self.assertTrue("member" not in res[0])
+
+ print "Testing ldb.search for (&(cn=ldaptestutf8user ÈÙÉÌÒÀ)(objectClass=user))"
+ res = ldb.search(expression="(&(cn=ldaptestutf8user ÈÙÉÌÒÀ)(objectClass=user))")
+ self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestutf8user ÈÙÉÌÒÀ)(objectClass=user))")
+
+ self.assertEquals(res[0].dn, ("CN=ldaptestutf8user èùéìòà,CN=Users," + self.base_dn))
+ self.assertEquals(res[0]["cn"], "ldaptestutf8user èùéìòà")
+ self.assertEquals(res[0]["name"], "ldaptestutf8user èùéìòà")
+ self.assertEquals(res[0]["objectClass"], ["top", "person", "organizationalPerson", "user"])
+ self.assertTrue("objectGUID" in res[0])
+ self.assertTrue("whenCreated" in res[0])
+
+ ldb.delete(res[0].dn)
+
+ print "Testing ldb.search for (&(cn=ldaptestutf8user2*)(objectClass=user))"
+ res = ldb.search(expression="(&(cn=ldaptestutf8user2*)(objectClass=user))")
+ self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestutf8user2*)(objectClass=user))")
+
+ ldb.delete(res[0].dn)
+
+ ldb.delete(("CN=ldaptestgroup2,CN=Users," + self.base_dn))
+
+ print "Testing ldb.search for (&(cn=ldaptestutf8user2 ÈÙÉÌÒÀ)(objectClass=user))"
+ res = ldb.search(expression="(&(cn=ldaptestutf8user ÈÙÉÌÒÀ)(objectClass=user))")
+
+ #FIXME: self.assert len(res) == 1, "Could not find (expect space collapse, win2k3 fails) (&(cn=ldaptestutf8user2 ÈÙÉÌÒÀ)(objectClass=user))"
+
+ print "Testing that we can't get at the configuration DN from the main search base"
+ res = ldb.search(self.base_dn, expression="objectClass=crossRef", scope=SCOPE_SUBTREE, attrs=["cn"])
+ self.assertEquals(len(res), 0)
+
+ print "Testing that we can get at the configuration DN from the main search base on the LDAP port with the 'phantom root' search_options control"
+ res = ldb.search(self.base_dn, expression="objectClass=crossRef", scope=SCOPE_SUBTREE, attrs=["cn"], controls=["search_options:1:2"])
+ self.assertTrue(len(res) > 0)
+
+ if gc_ldb is not None:
+ print "Testing that we can get at the configuration DN from the main search base on the GC port with the search_options control == 0"
+
+ res = gc_ldb.search(self.base_dn, expression="objectClass=crossRef", scope=SCOPE_SUBTREE, attrs=["cn"], controls=["search_options:1:0"])
+ self.assertTrue(len(res) > 0)
+
+ print "Testing that we do find configuration elements in the global catlog"
+ res = gc_ldb.search(self.base_dn, expression="objectClass=crossRef", scope=SCOPE_SUBTREE, attrs=["cn"])
+ self.assertTrue(len(res) > 0)
+
+ print "Testing that we do find configuration elements and user elements at the same time"
+ res = gc_ldb.search(self.base_dn, expression="(|(objectClass=crossRef)(objectClass=person))", scope=SCOPE_SUBTREE, attrs=["cn"])
+ self.assertTrue(len(res) > 0)
+
+ print "Testing that we do find configuration elements in the global catlog, with the configuration basedn"
+ res = gc_ldb.search(self.configuration_dn, expression="objectClass=crossRef", scope=SCOPE_SUBTREE, attrs=["cn"])
+ self.assertTrue(len(res) > 0)
+
+ print "Testing that we can get at the configuration DN on the main LDAP port"
+ res = ldb.search(self.configuration_dn, expression="objectClass=crossRef", scope=SCOPE_SUBTREE, attrs=["cn"])
+ self.assertTrue(len(res) > 0)
+
+ print "Testing objectCategory canonacolisation"
+ res = ldb.search(self.configuration_dn, expression="objectCategory=ntDsDSA", scope=SCOPE_SUBTREE, attrs=["cn"])
+ self.assertTrue(len(res) > 0, "Didn't find any records with objectCategory=ntDsDSA")
+ self.assertTrue(len(res) != 0)
+
+ res = ldb.search(self.configuration_dn, expression="objectCategory=CN=ntDs-DSA," + self.schema_dn, scope=SCOPE_SUBTREE, attrs=["cn"])
+ self.assertTrue(len(res) > 0, "Didn't find any records with objectCategory=CN=ntDs-DSA," + self.schema_dn)
+ self.assertTrue(len(res) != 0)
+
+ print "Testing objectClass attribute order on "+ self.base_dn
+ res = ldb.search(expression="objectClass=domain", base=self.base_dn,
+ scope=SCOPE_BASE, attrs=["objectClass"])
+ self.assertEquals(len(res), 1)
+
+ self.assertEquals(res[0]["objectClass"], ["top", "domain", "domainDNS"])
+
+ # check enumeration
+
+ print "Testing ldb.search for objectCategory=person"
+ res = ldb.search(self.base_dn, expression="objectCategory=person", scope=SCOPE_SUBTREE, attrs=["cn"])
+ self.assertTrue(len(res) > 0)
+
+ print "Testing ldb.search for objectCategory=person with domain scope control"
+ res = ldb.search(self.base_dn, expression="objectCategory=person", scope=SCOPE_SUBTREE, attrs=["cn"], controls=["domain_scope:1"])
+ self.assertTrue(len(res) > 0)
+
+ print "Testing ldb.search for objectCategory=user"
+ res = ldb.search(self.base_dn, expression="objectCategory=user", scope=SCOPE_SUBTREE, attrs=["cn"])
+ self.assertTrue(len(res) > 0)
+
+ print "Testing ldb.search for objectCategory=user with domain scope control"
+ res = ldb.search(self.base_dn, expression="objectCategory=user", scope=SCOPE_SUBTREE, attrs=["cn"], controls=["domain_scope:1"])
+ self.assertTrue(len(res) > 0)
+
+ print "Testing ldb.search for objectCategory=group"
+ res = ldb.search(self.base_dn, expression="objectCategory=group", scope=SCOPE_SUBTREE, attrs=["cn"])
+ self.assertTrue(len(res) > 0)
+
+ print "Testing ldb.search for objectCategory=group with domain scope control"
+ res = ldb.search(self.base_dn, expression="objectCategory=group", scope=SCOPE_SUBTREE, attrs=["cn"], controls=["domain_scope:1"])
+ self.assertTrue(len(res) > 0)
+
+
+class BaseDnTests(unittest.TestCase):
+ def setUp(self):
+ self.ldb = ldb
+
+ def test_rootdse_attrs(self):
+ """Testing for all rootDSE attributes"""
+ res = self.ldb.search(scope=SCOPE_BASE, attrs=[])
+ self.assertEquals(len(res), 1)
+
+ def test_highestcommittedusn(self):
+ """Testing for highestCommittedUSN"""
+ res = self.ldb.search("", scope=SCOPE_BASE, attrs=["highestCommittedUSN"])
+ self.assertEquals(len(res), 1)
+ self.assertTrue(int(res[0]["highestCommittedUSN"][0]) != 0)
+
+ def test_netlogon(self):
+ """Testing for netlogon via LDAP"""
+ res = self.ldb.search("", scope=SCOPE_BASE, attrs=["netlogon"])
+ self.assertEquals(len(res), 0)
+
+ def test_netlogon_highestcommitted_usn(self):
+ """Testing for netlogon and highestCommittedUSN via LDAP"""
+ res = self.ldb.search("", scope=SCOPE_BASE,
+ attrs=["netlogon", "highestCommittedUSN"])
+ self.assertEquals(len(res), 0)
+
+class SchemaTests(unittest.TestCase):
+ def find_schemadn(self, ldb):
+ res = ldb.search(base="", expression="", scope=SCOPE_BASE, attrs=["schemaNamingContext"])
+ self.assertEquals(len(res), 1)
+ return res[0]["schemaNamingContext"][0]
+
+ def setUp(self):
+ self.ldb = ldb
+ self.schema_dn = self.find_schemadn(ldb)
+
+ def test_generated_schema(self):
+ """Testing we can read the generated schema via LDAP"""
+ res = self.ldb.search("cn=aggregate,"+self.schema_dn, scope=SCOPE_BASE,
+ attrs=["objectClasses", "attributeTypes", "dITContentRules"])
+ self.assertEquals(len(res), 1)
+ self.assertTrue("dITContentRules" in res[0])
+ self.assertTrue("objectClasses" in res[0])
+ self.assertTrue("attributeTypes" in res[0])
+
+ def test_generated_schema_is_operational(self):
+ """Testing we don't get the generated schema via LDAP by default"""
+ res = self.ldb.search("cn=aggregate,"+self.schema_dn, scope=SCOPE_BASE,
+ attrs=["*"])
+ self.assertEquals(len(res), 1)
+ self.assertFalse("dITContentRules" in res[0])
+ self.assertFalse("objectClasses" in res[0])
+ self.assertFalse("attributeTypes" in res[0])
+
+if not "://" in host:
+ host = "ldap://%s" % host
+
+ldb = Ldb(host, credentials=creds, session_info=system_session(), lp=lp)
+gc_ldb = Ldb("%s:3268" % host, credentials=creds,
+ session_info=system_session(), lp=lp)
+
+runner = SubunitTestRunner()
+rc = 0
+if not runner.run(unittest.makeSuite(BaseDnTests)).wasSuccessful():
+ rc = 1
+if not runner.run(unittest.makeSuite(BasicTests)).wasSuccessful():
+ rc = 1
+if not runner.run(unittest.makeSuite(SchemaTests)).wasSuccessful():
+ rc = 1
+sys.exit(rc)
diff --git a/ldb/tests/samba4.png b/ldb/tests/samba4.png
new file mode 100644
index 00000000..c8096889
--- /dev/null
+++ b/ldb/tests/samba4.png
Binary files differ
diff --git a/ldb/tests/sample_module.c b/ldb/tests/sample_module.c
new file mode 100644
index 00000000..1a9e72c9
--- /dev/null
+++ b/ldb/tests/sample_module.c
@@ -0,0 +1,38 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "ldb_includes.h"
+#include "ldb.h"
+#include "ldb_errors.h"
+
+int sample_add(struct ldb_module *mod, struct ldb_request *req)
+{
+ ldb_msg_add_fmt(req->op.add.message, "touchedBy", "sample");
+
+ return ldb_next_request(mod, req);
+}
+
+const struct ldb_module_ops ldb_sample_module_ops = {
+ .name = "sample",
+ .add = sample_add,
+};
diff --git a/ldb/tests/schema-tests/schema-add-test.ldif b/ldb/tests/schema-tests/schema-add-test.ldif
new file mode 100644
index 00000000..472ab48f
--- /dev/null
+++ b/ldb/tests/schema-tests/schema-add-test.ldif
@@ -0,0 +1,66 @@
+dn: CN=Users,DC=schema,DC=test
+objectClass: top
+objectClass: container
+cn: Users
+description: Default container for upgraded user accounts
+instanceType: 4
+whenCreated: 20050116175504.0Z
+whenChanged: 20050116175504.0Z
+uSNCreated: 1
+uSNChanged: 1
+showInAdvancedViewOnly: FALSE
+name: Users
+objectGUID: b847056a-9934-d87b-8a1a-99fabe0863c8
+systemFlags: 0x8c000000
+objectCategory: CN=Container,CN=Schema,CN=Configuration,DC=schema,DC=test
+isCriticalSystemObject: TRUE
+nTSecurityDescriptor: foo
+
+dn: CN=Administrator,CN=Users,DC=schema,DC=test
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: user
+cn: Administrator
+description: Built-in account for administering the computer/domain
+instanceType: 4
+whenCreated: 20050116175504.0Z
+whenChanged: 20050116175504.0Z
+uSNCreated: 1
+memberOf: CN=Group Policy Creator Owners,CN=Users,DC=schema,DC=test
+memberOf: CN=Domain Admins,CN=Users,DC=schema,DC=test
+memberOf: CN=Enterprise Admins,CN=Users,DC=schema,DC=test
+memberOf: CN=Schema Admins,CN=Users,DC=schema,DC=test
+memberOf: CN=Administrators,CN=Builtin,DC=schema,DC=test
+uSNChanged: 1
+name: Administrator
+objectGUID: 6c02f98c-46c6-aa38-5f13-a510cac04e6c
+userAccountControl: 0x10200
+badPwdCount: 0
+codePage: 0
+countryCode: 0
+badPasswordTime: 0
+lastLogoff: 0
+lastLogon: 0
+pwdLastSet: 0
+primaryGroupID: 513
+objectSid: S-1-5-21-43662522-77495566-38969261-500
+adminCount: 1
+accountExpires: 9223372036854775807
+logonCount: 0
+sAMAccountName: Administrator
+sAMAccountType: 0x30000000
+objectCategory: CN=Person,CN=Schema,CN=Configuration,DC=schema,DC=test
+isCriticalSystemObject: TRUE
+unicodePwd: samba
+nTSecurityDescriptor: foo
+
+dn: CN=Test,CN=Users,DC=schema,DC=test
+objectClass: top
+objectClass: test
+cn: Test
+description: This is a test
+objectCategory: CN=Test,CN=Schema,CN=Configuration,DC=schema,DC=test
+nTSecurityDescriptor: foo
+instanceType: 4
+
diff --git a/ldb/tests/schema-tests/schema-mod-test-1.ldif b/ldb/tests/schema-tests/schema-mod-test-1.ldif
new file mode 100644
index 00000000..b9767244
--- /dev/null
+++ b/ldb/tests/schema-tests/schema-mod-test-1.ldif
@@ -0,0 +1,5 @@
+dn: CN=Test,CN=Users,DC=schema,DC=test
+changetype: modify
+replace: description
+description: this test must not fail
+
diff --git a/ldb/tests/schema-tests/schema-mod-test-2.ldif b/ldb/tests/schema-tests/schema-mod-test-2.ldif
new file mode 100644
index 00000000..fa193af6
--- /dev/null
+++ b/ldb/tests/schema-tests/schema-mod-test-2.ldif
@@ -0,0 +1,5 @@
+dn: CN=Test,CN=Users,DC=schema,DC=test
+changetype: modify
+delete: description
+# this test must not fail
+
diff --git a/ldb/tests/schema-tests/schema-mod-test-3.ldif b/ldb/tests/schema-tests/schema-mod-test-3.ldif
new file mode 100644
index 00000000..8ab7798f
--- /dev/null
+++ b/ldb/tests/schema-tests/schema-mod-test-3.ldif
@@ -0,0 +1,5 @@
+dn: CN=Test,CN=Users,DC=schema,DC=test
+changetype: modify
+add: description
+description: this test must not fail
+
diff --git a/ldb/tests/schema-tests/schema-mod-test-4.ldif b/ldb/tests/schema-tests/schema-mod-test-4.ldif
new file mode 100644
index 00000000..cbf0e60b
--- /dev/null
+++ b/ldb/tests/schema-tests/schema-mod-test-4.ldif
@@ -0,0 +1,5 @@
+dn: CN=Test,CN=Users,DC=schema,DC=test
+changetype: modify
+add: foo
+foo: this test must fail
+
diff --git a/ldb/tests/schema-tests/schema-mod-test-5.ldif b/ldb/tests/schema-tests/schema-mod-test-5.ldif
new file mode 100644
index 00000000..bc64e9ed
--- /dev/null
+++ b/ldb/tests/schema-tests/schema-mod-test-5.ldif
@@ -0,0 +1,5 @@
+dn: CN=Test,CN=Users,DC=schema,DC=test
+changetype: modify
+delete: nTSecurityDescriptor
+# this test must fail
+
diff --git a/ldb/tests/schema-tests/schema.ldif b/ldb/tests/schema-tests/schema.ldif
new file mode 100644
index 00000000..4ab19328
--- /dev/null
+++ b/ldb/tests/schema-tests/schema.ldif
@@ -0,0 +1,100 @@
+dn: @INDEXLIST
+@IDXATTR: name
+@IDXATTR: sAMAccountName
+@IDXATTR: objectSid
+@IDXATTR: objectClass
+@IDXATTR: member
+@IDXATTR: uidNumber
+@IDXATTR: gidNumber
+@IDXATTR: unixName
+@IDXATTR: privilege
+@IDXATTR: lDAPDisplayName
+
+dn: @ATTRIBUTES
+realm: CASE_INSENSITIVE
+userPrincipalName: CASE_INSENSITIVE
+servicePrincipalName: CASE_INSENSITIVE
+name: CASE_INSENSITIVE
+dn: CASE_INSENSITIVE
+sAMAccountName: CASE_INSENSITIVE
+objectClass: CASE_INSENSITIVE
+unicodePwd: HIDDEN
+ntPwdHash: HIDDEN
+ntPwdHistory: HIDDEN
+lmPwdHash: HIDDEN
+lmPwdHistory: HIDDEN
+createTimestamp: HIDDEN
+modifyTimestamp: HIDDEN
+
+dn: @MODULES
+@LIST: timestamps,schema
+
+dn: CN=Top,CN=Schema,CN=Configuration,DC=schema,DC=test
+objectClass: top
+objectClass: classSchema
+lDAPDisplayName: top
+cn: Top
+uSNCreated: 1
+uSNChanged: 1
+subClassOf: top
+systemMustContain: objectClass
+systemMayContain: structuralObjectClass
+systemMayContain: createTimeStamp
+systemMayContain: modifyTimeStamp
+systemMayContain: creatorsName
+systemMayContain: modifiersName
+systemMayContain: hasSubordinates
+systemMayContain: subschemaSubentry
+systemMayContain: collectiveSubentry
+systemMayContain: entryUUID
+systemMayContain: entryCSN
+systemMayContain: namingCSN
+systemMayContain: superiorUUID
+systemMayContain: contextCSN
+systemMayContain: whenCreated
+systemMayContain: whenChanged
+systemMayContain: uSNCreated
+systemMayContain: uSNChanged
+systemMayContain: distinguishedName
+systemMayContain: name
+systemMayContain: cn
+systemMayContain: userPassword
+systemMayContain: labeledURI
+
+dn: CN=Class-Schema,CN=Schema,CN=Configuration,DC=schema,DC=test
+objectClass: top
+objectClass: classSchema
+lDAPDisplayName: classSchema
+cn: Class-Schema
+uSNCreated: 2
+uSNChanged: 2
+lDAPDisplayName: classSchema
+subClassOf: top
+systemMustContain: cn
+systemMustContain: subClassOf
+systemMayContain: systemPossSuperiors
+systemMayContain: systemOnly
+systemMayContain: systemMustContain
+systemMayContain: systemMayContain
+systemMayContain: systemAuxiliaryClass
+systemMayContain: possSuperiors
+systemMayContain: mustContain
+systemMayContain: mayContain
+systemMayContain: lDAPDisplayName
+systemMayContain: auxiliaryClass
+
+dn: CN=Attribute-Schema,CN=Schema,CN=Configuration,DC=schema,DC=test
+objectClass: top
+objectClass: classSchema
+cn: Attribute-Schema
+uSNCreated: 3
+uSNChanged: 3
+lDAPDisplayName: attributeSchema
+subClassOf: top
+systemMustContain: oMSyntax
+systemMustContain: lDAPDisplayName
+systemMustContain: isSingleValued
+systemMustContain: cn
+systemMustContain: attributeSyntax
+systemMustContain: attributeID
+
diff --git a/ldb/tests/slapd.conf b/ldb/tests/slapd.conf
new file mode 100644
index 00000000..fa2789d8
--- /dev/null
+++ b/ldb/tests/slapd.conf
@@ -0,0 +1,26 @@
+loglevel 0
+
+include tests/schema/core.schema
+include tests/schema/cosine.schema
+include tests/schema/inetorgperson.schema
+include tests/schema/openldap.schema
+include tests/schema/nis.schema
+
+
+pidfile tests/tmp/slapd.pid
+argsfile tests/tmp/slapd.args
+
+access to * by * write
+
+allow update_anon bind_anon_dn
+
+include tests/tmp/modules.conf
+
+defaultsearchbase "o=University of Michigan,c=TEST"
+
+backend bdb
+database bdb
+suffix "o=University of Michigan,c=TEST"
+directory tests/tmp/db
+index objectClass eq
+index uid eq
diff --git a/ldb/tests/start_slapd.sh b/ldb/tests/start_slapd.sh
new file mode 100755
index 00000000..11679d47
--- /dev/null
+++ b/ldb/tests/start_slapd.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+if [ -z "$LDBDIR" ]; then
+ LDBDIR=`dirname $0`/..
+ export LDBDIR
+fi
+
+mkdir -p $LDBDIR/tests/tmp/db
+
+# running slapd in the background (with &) means it stays in the same process group, so it can be
+# killed by timelimit
+slapd -d0 -f $LDBDIR/tests/slapd.conf -h "`$LDBDIR/tests/ldapi_url.sh`" $* &
+
+sleep 2
diff --git a/ldb/tests/test-attribs.ldif b/ldb/tests/test-attribs.ldif
new file mode 100644
index 00000000..79508c4b
--- /dev/null
+++ b/ldb/tests/test-attribs.ldif
@@ -0,0 +1,6 @@
+dn: @ATTRIBUTES
+uid: CASE_INSENSITIVE
+cn: CASE_INSENSITIVE
+ou: CASE_INSENSITIVE
+dn: CASE_INSENSITIVE
+
diff --git a/ldb/tests/test-config.ldif b/ldb/tests/test-config.ldif
new file mode 100644
index 00000000..7926a9e3
--- /dev/null
+++ b/ldb/tests/test-config.ldif
@@ -0,0 +1,67 @@
+##############################
+# global configuration options
+dn: cn=Global,cn=Config,cn=Samba
+objectclass: globalconfig
+LocalConfigCn: cn=%U,cn=Config,cn=Samba
+LocalConfigCn;1: cn=%U,cn=Config,cn=Samba
+LocalConfigCn;2: cn=%I,cn=Config,cn=Samba
+LocalConfigCn;3: cn=%M,cn=Config,cn=Samba
+
+#############
+dn: cn=Protocol,cn=Global,cn=Config,cn=Samba
+maxXmit: 7000
+
+################################
+dn: cn=Volker,cn=Config,cn=Samba
+Workgroup: VNET3
+UnixCharset: UTF8
+Security: user
+Interfaces: vmnet* eth*
+NetbiosName: blu
+GuestAccount: tridge
+
+#################################
+dn: cn=Volker,cn=Config,cn=Samba
+Workgroup: VNET3
+UnixCharset: UTF8
+Security: user
+Interfaces: vmnet* eth*
+NetbiosName: blu
+GuestAccount: tridge
+Include: cn=%U,cn=MyConfig,cn=Config,cn=Samba
+
+#### ((objectClass=fileshare)(cn=test))
+##############################
+# [test] share
+dn: cn=test,cn=Shares,cn=Config,cn=Samba
+objectclass: fileshare
+cn: test
+Comment: a test share
+Path: /home/tridge/samba4/prefix/test
+ReadOnly: no
+
+#####################################
+# [msdn] a remote proxy share, stored
+# on \\msdn\test
+dn: cn=msdn,cn=Shares,cn=Config,cn=Samba
+objectclass: fileshare
+cn: msdn
+NtvfsHandler: cifs
+ReadOnly: no
+_CifsServer: msdn
+_CifsUser: administrator
+_CifsPassword: penguin
+_CifsDomain: winxp
+_CifsShare: test
+
+
+##############################
+# [VisualC] share
+dn: cn=visualc,cn=Shares,cn=Config,cn=Samba
+objectclass: fileshare
+cn: VisualC
+Comment: VisualC development
+Path: /home/tridge/VisualC
+ReadOnly: no
+NtvfsHandler: simple
+
diff --git a/ldb/tests/test-default-config.ldif b/ldb/tests/test-default-config.ldif
new file mode 100644
index 00000000..87b7bcd3
--- /dev/null
+++ b/ldb/tests/test-default-config.ldif
@@ -0,0 +1,17 @@
+##############################
+# global configuration options
+dn: cn=Global,cn=DefaultConfig,cn=Samba
+objectclass: globalconfig
+Workgroup: WORKGROUP
+UnixCharset: UTF8
+Security: user
+NetbiosName: blu
+GuestAccount: nobody
+
+##############################
+# [_default_] share
+dn: cn=_default_,cn=Shares,cn=DefaultConfig,cn=Samba
+objectclass: fileshare
+cn: _default_
+Path: /tmp
+ReadOnly: yes
diff --git a/ldb/tests/test-extended.sh b/ldb/tests/test-extended.sh
new file mode 100755
index 00000000..a84e3b78
--- /dev/null
+++ b/ldb/tests/test-extended.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+echo "Running extended search tests"
+
+mv $LDB_URL $LDB_URL.1
+
+cat <<EOF | bin/ldbadd || exit 1
+dn: cn=testrec1,cn=TEST
+i1: 1
+i2: 0
+i3: 1234
+i4: 0x7003004
+
+dn: cn=testrec2,cn=TEST
+i1: 0x800000
+
+dn: cn=testrec3,cn=TEST
+i1: 0x101010101
+i1: 7
+
+dn: cn=auser1,cn=TEST
+groupType: 2147483648
+samAccountType: 805306368
+
+dn: cn=auser2,cn=TEST
+groupType: 2147483648
+samAccountType: 805306369
+
+dn: cn=auser3,cn=TEST
+groupType: 2147483649
+samAccountType: 805306370
+
+dn: cn=auser4,cn=TEST
+groupType: 2147483649
+samAccountType: 805306369
+EOF
+
+checkcount() {
+ count=$1
+ expression="$2"
+ n=`bin/ldbsearch "$expression" | grep '^dn' | wc -l`
+ if [ $n != $count ]; then
+ echo "Got $n but expected $count for $expression"
+ bin/ldbsearch "$expression"
+ exit 1
+ fi
+ echo "OK: $count $expression"
+}
+
+checkcount 1 '(i3=1234)'
+checkcount 0 '(i3=12345)'
+
+checkcount 2 '(i1:1.2.840.113556.1.4.803:=1)'
+checkcount 1 '(i1:1.2.840.113556.1.4.803:=3)'
+checkcount 1 '(i1:1.2.840.113556.1.4.803:=7)'
+checkcount 0 '(i1:1.2.840.113556.1.4.803:=15)'
+checkcount 1 '(i1:1.2.840.113556.1.4.803:=0x800000)'
+checkcount 1 '(i1:1.2.840.113556.1.4.803:=8388608)'
+
+checkcount 2 '(i1:1.2.840.113556.1.4.804:=1)'
+checkcount 2 '(i1:1.2.840.113556.1.4.804:=3)'
+checkcount 2 '(i1:1.2.840.113556.1.4.804:=7)'
+checkcount 2 '(i1:1.2.840.113556.1.4.804:=15)'
+checkcount 1 '(i1:1.2.840.113556.1.4.804:=0x800000)'
+checkcount 1 '(i1:1.2.840.113556.1.4.804:=8388608)'
+
+# this is one that w2k gives
+checkcount 3 '(|(|(&(!(groupType:1.2.840.113556.1.4.803:=1))(groupType:1.2.840.113556.1.4.803:=2147483648)(groupType:1.2.840.113556.1.4.804:=10))(samAccountType=805306368))(samAccountType=805306369))'
+
diff --git a/ldb/tests/test-generic.sh b/ldb/tests/test-generic.sh
new file mode 100755
index 00000000..14337cc1
--- /dev/null
+++ b/ldb/tests/test-generic.sh
@@ -0,0 +1,128 @@
+#!/bin/sh
+
+if [ -z "$LDB_SPECIALS" ]; then
+ LDB_SPECIALS=1
+ export LDB_SPECIALS
+fi
+
+echo "LDB_URL: $LDB_URL"
+
+echo "Adding base elements"
+$VALGRIND ldbadd $LDBDIR/tests/test.ldif || exit 1
+
+echo "Adding again - should fail"
+ldbadd $LDBDIR/tests/test.ldif 2> /dev/null && {
+ echo "Should have failed to add again - gave $?"
+ exit 1
+}
+
+echo "Modifying elements"
+$VALGRIND ldbmodify $LDBDIR/tests/test-modify.ldif || exit 1
+
+echo "Showing modified record"
+$VALGRIND ldbsearch '(uid=uham)' || exit 1
+
+echo "Rename entry"
+OLDDN="cn=Ursula Hampster,ou=Alumni Association,ou=People,o=University of Michigan,c=TEST"
+NEWDN="cn=Hampster Ursula,ou=Alumni Association,ou=People,o=University of Michigan,c=TEST"
+$VALGRIND ldbrename "$OLDDN" "$NEWDN" || exit 1
+
+echo "Showing renamed record"
+$VALGRIND ldbsearch '(uid=uham)' || exit 1
+
+echo "Starting ldbtest"
+$VALGRIND ldbtest --num-records 100 --num-searches 10 || exit 1
+
+if [ $LDB_SPECIALS = 1 ]; then
+ echo "Adding index"
+ $VALGRIND ldbadd $LDBDIR/tests/test-index.ldif || exit 1
+fi
+
+echo "Adding bad attributes - should fail"
+$VALGRIND ldbadd $LDBDIR/tests/test-wrong_attributes.ldif && {
+ echo "Should fhave failed - gave $?"
+ exit 1
+}
+
+echo "testing indexed search"
+$VALGRIND ldbsearch '(uid=uham)' || exit 1
+$VALGRIND ldbsearch '(&(objectclass=person)(objectclass=person)(objectclass=top))' || exit 1
+$VALGRIND ldbsearch '(&(uid=uham)(uid=uham))' || exit 1
+$VALGRIND ldbsearch '(|(uid=uham)(uid=uham))' || exit 1
+$VALGRIND ldbsearch '(|(uid=uham)(uid=uham)(objectclass=OpenLDAPperson))' || exit 1
+$VALGRIND ldbsearch '(&(uid=uham)(uid=uham)(!(objectclass=xxx)))' || exit 1
+$VALGRIND ldbsearch '(&(objectclass=person)(uid=uham)(!(uid=uhamxx)))' uid \* \+ dn || exit 1
+$VALGRIND ldbsearch '(&(uid=uham)(uid=uha*)(title=*))' uid || exit 1
+
+# note that the "((" is treated as an attribute not an expression
+# this matches the openldap ldapsearch behaviour of looking for a '='
+# to see if the first argument is an expression or not
+$VALGRIND ldbsearch '((' uid || exit 1
+$VALGRIND ldbsearch '(objectclass=)' uid || exit 1
+$VALGRIND ldbsearch -b 'cn=Hampster Ursula,ou=Alumni Association,ou=People,o=University of Michigan,c=TEST' -s base "" sn || exit 1
+
+echo "Test wildcard match"
+$VALGRIND ldbadd $LDBDIR/tests/test-wildcard.ldif || exit 1
+$VALGRIND ldbsearch '(cn=test*multi)' || exit 1
+$VALGRIND ldbsearch '(cn=*test*multi*)' || exit 1
+$VALGRIND ldbsearch '(cn=*test_multi)' || exit 1
+$VALGRIND ldbsearch '(cn=test_multi*)' || exit 1
+$VALGRIND ldbsearch '(cn=test*multi*test*multi)' || exit 1
+$VALGRIND ldbsearch '(cn=test*multi*test*multi*multi_*)' || exit 1
+
+echo "Starting ldbtest indexed"
+$VALGRIND ldbtest --num-records 100 --num-searches 500 || exit 1
+
+echo "Testing one level search"
+count=`$VALGRIND ldbsearch -b 'ou=Groups,o=University of Michigan,c=TEST' -s one 'objectclass=*' none |grep '^dn' | wc -l`
+if [ $count != 3 ]; then
+ echo returned $count records - expected 3
+ exit 1
+fi
+
+echo "Testing binary file attribute value"
+mkdir -p tests/tmp
+cp $LDBDIR/tests/samba4.png tests/tmp/samba4.png
+$VALGRIND ldbmodify $LDBDIR/tests/photo.ldif || exit 1
+count=`$VALGRIND ldbsearch '(cn=Hampster Ursula)' jpegPhoto | grep '^dn' | wc -l`
+if [ $count != 1 ]; then
+ echo returned $count records - expected 1
+ exit 1
+fi
+rm -f tests/tmp/samba4.png
+
+echo "*TODO* Testing UTF8 upper lower case searches !!"
+
+echo "Testing compare"
+count=`$VALGRIND ldbsearch '(cn>=t)' cn | grep '^dn' | wc -l`
+if [ $count != 2 ]; then
+ echo returned $count records - expected 2
+ echo "this fails on openLdap ..."
+fi
+
+count=`$VALGRIND ldbsearch '(cn<=t)' cn | grep '^dn' | wc -l`
+if [ $count != 13 ]; then
+ echo returned $count records - expected 13
+ echo "this fails on opsnLdap ..."
+fi
+
+checkcount() {
+ count=$1
+ scope=$2
+ basedn=$3
+ expression="$4"
+ n=`bin/ldbsearch -s "$scope" -b "$basedn" "$expression" | grep '^dn' | wc -l`
+ if [ $n != $count ]; then
+ echo "Got $n but expected $count for $expression"
+ bin/ldbsearch "$expression"
+ exit 1
+ fi
+ echo "OK: $count $expression"
+}
+
+checkcount 0 'base' '' '(uid=uham)'
+checkcount 0 'one' '' '(uid=uham)'
+
+checkcount 1 'base' 'cn=Hampster Ursula,ou=Alumni Association,ou=People,o=University of Michigan,c=TEST' '(uid=uham)'
+checkcount 1 'one' 'ou=Alumni Association,ou=People,o=University of Michigan,c=TEST' '(uid=uham)'
+checkcount 1 'one' 'ou=People,o=University of Michigan,c=TEST' '(ou=ldb test)'
diff --git a/ldb/tests/test-index.ldif b/ldb/tests/test-index.ldif
new file mode 100644
index 00000000..26817364
--- /dev/null
+++ b/ldb/tests/test-index.ldif
@@ -0,0 +1,7 @@
+dn: @INDEXLIST
+@IDXATTR: uid
+@IDXATTR: objectclass
+
+dn: @ATTRIBUTES
+uid: CASE_INSENSITIVE
+
diff --git a/ldb/tests/test-ldap.sh b/ldb/tests/test-ldap.sh
new file mode 100755
index 00000000..14cfb5f9
--- /dev/null
+++ b/ldb/tests/test-ldap.sh
@@ -0,0 +1,54 @@
+#!/bin/sh
+
+PATH=/usr/local/sbin:/usr/sbin:/sbin:$PATH
+export PATH
+SCHEMA_NEEDED="core nis cosine inetorgperson openldap"
+
+# setup needed schema files
+for f in $SCHEMA_NEEDED; do
+ if [ ! -r tests/schema/$f.schema ]; then
+ mkdir -p tests/schema
+ if [ -r /etc/ldap/schema/$f.schema ]; then
+ ln -s /etc/ldap/schema/$f.schema tests/schema/$f.schema
+ continue;
+ fi
+ if [ -r /etc/openldap/schema/$f.schema ]; then
+ ln -s /etc/openldap/schema/$f.schema tests/schema/$f.schema
+ continue;
+ fi
+
+ echo "SKIPPING TESTS: you need the following OpenLDAP schema files"
+ for f in $SCHEMA_NEEDED; do
+ echo " $f.schema"
+ done
+ exit 0
+ fi
+done
+
+if [ -z "$LDBDIR" ]; then
+ LDBDIR=`dirname $0`/..
+ export LDBDIR
+fi
+
+LDB_URL=`$LDBDIR/tests/ldapi_url.sh`
+export LDB_URL
+
+PATH=bin:$PATH
+export PATH
+
+LDB_SPECIALS=0
+export LDB_SPECIALS
+
+if $LDBDIR/tests/init_slapd.sh &&
+ $LDBDIR/tests/start_slapd.sh &&
+ $LDBDIR/tests/test-generic.sh; then
+ echo "ldap tests passed";
+ ret=0
+else
+ echo "ldap tests failed";
+ ret=$?
+fi
+
+#$LDBDIR/tests/kill_slapd.sh
+
+exit $ret
diff --git a/ldb/tests/test-modify.ldif b/ldb/tests/test-modify.ldif
new file mode 100644
index 00000000..e5b9ca40
--- /dev/null
+++ b/ldb/tests/test-modify.ldif
@@ -0,0 +1,23 @@
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=University of Michiga
+ n,c=TEST
+changetype: modify
+add: drink
+drink: mango lassi
+-
+add: drink
+drink: lemonade
+-
+delete: pager
+-
+replace: telephonenumber
+telephonenumber: +61 2 6260 6012
+telephonenumber: +61 412 666 929
+-
+delete: telephonenumber
+telephonenumber: +61 2 6260 6012
+-
+delete: telephonenumber
+telephonenumber: +61 412 666 929
+-
+add: telephonenumber
+telephonenumber: +61 412 666 929
diff --git a/ldb/tests/test-schema.sh b/ldb/tests/test-schema.sh
new file mode 100755
index 00000000..2f10fb45
--- /dev/null
+++ b/ldb/tests/test-schema.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+LDB_URL="tdb://schema.ldb"
+export LDB_URL
+
+rm -f schema.ldb
+
+echo "LDB_URL: $LDB_URL"
+
+echo "Adding schema"
+$VALGRIND bin/ldbadd $LDBDIR/tests/schema-tests/schema.ldif || exit 1
+
+echo "Adding few test elements (no failure expected here)"
+$VALGRIND bin/ldbadd $LDBDIR/tests/schema-tests/schema-add-test.ldif || exit 1
+
+echo "Modifying elements (2 failures expected here)"
+
+$VALGRIND bin/ldbmodify $LDBDIR/tests/schema-tests/schema-mod-test-1.ldif || exit 1
+$VALGRIND bin/ldbmodify $LDBDIR/tests/schema-tests/schema-mod-test-2.ldif || exit 1
+$VALGRIND bin/ldbmodify $LDBDIR/tests/schema-tests/schema-mod-test-3.ldif || exit 1
+$VALGRIND bin/ldbmodify $LDBDIR/tests/schema-tests/schema-mod-test-4.ldif
+if [ "$?" == "0" ]; then
+ echo "test failed!"
+ exit 1
+fi
+$VALGRIND bin/ldbmodify $LDBDIR/tests/schema-tests/schema-mod-test-5.ldif
+if [ "$?" == "0" ]; then
+ echo "test failed!"
+ exit 1
+fi
+
+echo "Showing modified record"
+$VALGRIND bin/ldbsearch '(cn=Test)' || exit 1
+
diff --git a/ldb/tests/test-soloading.sh b/ldb/tests/test-soloading.sh
new file mode 100755
index 00000000..da6d5754
--- /dev/null
+++ b/ldb/tests/test-soloading.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+if [ -n "$TEST_DATA_PREFIX" ]; then
+ LDB_URL="$TEST_DATA_PREFIX/tdbtest.ldb"
+else
+ LDB_URL="tdbtest.ldb"
+fi
+export LDB_URL
+
+PATH=bin:$PATH
+export PATH
+
+rm -f $LDB_URL*
+
+if [ -z "$LDBDIR" ]; then
+ LDBDIR=`dirname $0`/..
+ export LDBDIR
+fi
+
+cat <<EOF | $VALGRIND ldbadd || exit 1
+dn: @MODULES
+@LIST: sample
+EOF
+
+cat <<EOF | $VALGRIND ldbadd || exit 1
+dn: dc=bar
+dc: bar
+someThing: someThingElse
+EOF
+
+$VALGRIND ldbsearch "(touchedBy=sample)" | grep "touchedBy: sample" || exit 1
+
diff --git a/ldb/tests/test-sqlite3.sh b/ldb/tests/test-sqlite3.sh
new file mode 100755
index 00000000..0cef318d
--- /dev/null
+++ b/ldb/tests/test-sqlite3.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+
+LDB_URL="sqlite3://sqltest.ldb"
+export LDB_URL
+
+rm -f sqltest.ldb
+
+if [ -z "$LDBDIR" ]; then
+ LDBDIR=`dirname $0`/..
+ export LDBDIR
+fi
+
+PATH=bin:$PATH
+export PATH
+
+LDB_SPECIALS=0
+export LDB_SPECIALS
+
+$LDBDIR/tests/test-generic.sh
+
+#. $LDBDIR/tests/test-extended.sh
+
+#. $LDBDIR/tests/test-tdb-features.sh
+
diff --git a/ldb/tests/test-tdb-features.sh b/ldb/tests/test-tdb-features.sh
new file mode 100644
index 00000000..fc0959c5
--- /dev/null
+++ b/ldb/tests/test-tdb-features.sh
@@ -0,0 +1,160 @@
+#!/bin/sh
+
+echo "Running tdb feature tests"
+
+mv $LDB_URL $LDB_URL.2
+
+checkcount() {
+ count=$1
+ expression="$2"
+ n=`bin/ldbsearch "$expression" | grep '^dn' | wc -l`
+ if [ $n != $count ]; then
+ echo "Got $n but expected $count for $expression"
+ $VALGRIND bin/ldbsearch "$expression"
+ exit 1
+ fi
+ echo "OK: $count $expression"
+}
+
+echo "Testing case sensitive search"
+cat <<EOF | $VALGRIND bin/ldbadd || exit 1
+dn: cn=t1,cn=TEST
+objectClass: testclass
+test: foo
+EOF
+checkcount 1 '(test=foo)'
+checkcount 0 '(test=FOO)'
+checkcount 0 '(test=FO*)'
+
+echo "Making case insensitive"
+cat <<EOF | $VALGRIND bin/ldbmodify || exit 1
+dn: @ATTRIBUTES
+changetype: add
+add: test
+test: CASE_INSENSITIVE
+EOF
+
+echo $ldif | $VALGRIND bin/ldbmodify || exit 1
+checkcount 1 '(test=foo)'
+checkcount 1 '(test=FOO)'
+checkcount 1 '(test=fo*)'
+
+echo "adding i"
+cat <<EOF | $VALGRIND bin/ldbmodify || exit 1
+dn: cn=t1,cn=TEST
+changetype: modify
+add: i
+i: 0x100
+EOF
+checkcount 1 '(i=0x100)'
+checkcount 0 '(i=256)'
+
+echo "marking i as INTEGER"
+cat <<EOF | $VALGRIND bin/ldbmodify || exit 1
+dn: @ATTRIBUTES
+changetype: modify
+add: i
+i: INTEGER
+EOF
+checkcount 1 '(i=0x100)'
+checkcount 1 '(i=256)'
+
+echo "adding j"
+cat <<EOF | $VALGRIND bin/ldbmodify || exit 1
+dn: cn=t1,cn=TEST
+changetype: modify
+add: j
+j: 0x100
+EOF
+checkcount 1 '(j=0x100)'
+checkcount 0 '(j=256)'
+
+echo "Adding wildcard attribute"
+cat <<EOF | $VALGRIND bin/ldbmodify || exit 1
+dn: @ATTRIBUTES
+changetype: modify
+add: *
+*: INTEGER
+EOF
+checkcount 1 '(j=0x100)'
+checkcount 1 '(j=256)'
+
+echo "Testing class search"
+checkcount 0 '(objectClass=otherclass)'
+checkcount 1 '(objectClass=testclass)'
+
+echo "Adding index"
+cat <<EOF | $VALGRIND bin/ldbadd || exit 1
+dn: @INDEXLIST
+@IDXATTR: i
+@IDXATTR: test
+EOF
+checkcount 1 '(i=0x100)'
+checkcount 1 '(i=256)'
+checkcount 0 '(i=-256)'
+checkcount 1 '(test=foo)'
+checkcount 1 '(test=FOO)'
+checkcount 1 '(test=*f*o)'
+
+echo "making test case sensitive"
+cat <<EOF | $VALGRIND bin/ldbmodify || exit 1
+dn: @ATTRIBUTES
+changetype: modify
+replace: test
+test: NONE
+EOF
+checkcount 1 '(test=foo)'
+checkcount 0 '(test=FOO)'
+checkcount 1 '(test=f*o*)'
+
+checkone() {
+ count=$1
+ base="$2"
+ expression="$3"
+ n=`bin/ldbsearch -s one -b "$base" "$expression" | grep '^dn' | wc -l`
+ if [ $n != $count ]; then
+ echo "Got $n but expected $count for $expression"
+ $VALGRIND bin/ldbsearch -s one -b "$base" "$expression"
+ exit 1
+ fi
+ echo "OK: $count $expression"
+}
+
+echo "Removing wildcard attribute"
+cat <<EOF | $VALGRIND bin/ldbmodify || exit 1
+dn: @ATTRIBUTES
+changetype: modify
+delete: *
+*: INTEGER
+EOF
+
+echo "Adding one level indexes"
+cat <<EOF | $VALGRIND bin/ldbmodify || exit 1
+dn: @INDEXLIST
+changetype: modify
+add: @IDXONE
+@IDXONE: 1
+EOF
+
+echo "Testing one level indexed search"
+cat <<EOF | $VALGRIND bin/ldbadd || exit 1
+dn: cn=one,cn=t1,cn=TEST
+objectClass: oneclass
+cn: one
+test: one
+EOF
+checkone 1 "cn=t1,cn=TEST" '(test=one)'
+cat <<EOF | $VALGRIND bin/ldbadd || exit 1
+dn: cn=two,cn=t1,cn=TEST
+objectClass: oneclass
+cn: two
+test: one
+
+dn: cn=three,cn=t1,cn=TEST
+objectClass: oneclass
+cn: three
+test: one
+EOF
+checkone 3 "cn=t1,cn=TEST" '(test=one)'
+checkone 1 "cn=t1,cn=TEST" '(cn=two)'
+
diff --git a/ldb/tests/test-tdb.sh b/ldb/tests/test-tdb.sh
new file mode 100755
index 00000000..7c4f5205
--- /dev/null
+++ b/ldb/tests/test-tdb.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+if [ -n "$TEST_DATA_PREFIX" ]; then
+ LDB_URL="$TEST_DATA_PREFIX/tdbtest.ldb"
+else
+ LDB_URL="tdbtest.ldb"
+fi
+export LDB_URL
+
+PATH=bin:$PATH
+export PATH
+
+rm -f $LDB_URL*
+
+if [ -z "$LDBDIR" ]; then
+ LDBDIR=`dirname $0`/..
+ export LDBDIR
+fi
+
+cat <<EOF | $VALGRIND ldbadd || exit 1
+dn: @MODULES
+@LIST: rdn_name
+EOF
+
+$VALGRIND ldbadd $LDBDIR/tests/init.ldif || exit 1
+
+. $LDBDIR/tests/test-generic.sh
+
+. $LDBDIR/tests/test-extended.sh
+
+. $LDBDIR/tests/test-tdb-features.sh
diff --git a/ldb/tests/test-wildcard.ldif b/ldb/tests/test-wildcard.ldif
new file mode 100644
index 00000000..222512ee
--- /dev/null
+++ b/ldb/tests/test-wildcard.ldif
@@ -0,0 +1,5 @@
+dn: cn=test_multi_test_multi_test_multi,o=University of Michigan,c=TEST
+objectclass: person
+cn: test_multi_test_multi_test_multi
+sn: multi_test
+description: test multi wildcards matching
diff --git a/ldb/tests/test-wrong_attributes.ldif b/ldb/tests/test-wrong_attributes.ldif
new file mode 100644
index 00000000..27f45f0e
--- /dev/null
+++ b/ldb/tests/test-wrong_attributes.ldif
@@ -0,0 +1,3 @@
+dn: @ATTRIBUTES
+uid: CASE_INTENSIVE
+
diff --git a/ldb/tests/test.ldif b/ldb/tests/test.ldif
new file mode 100644
index 00000000..e53fadc7
--- /dev/null
+++ b/ldb/tests/test.ldif
@@ -0,0 +1,411 @@
+dn: ou=Groups,o=University of Michigan,c=TEST
+objectclass: organizationalUnit
+ou: Groups
+
+dn: ou=Information Technology Division,ou=People,o=University of Michigan,c=TEST
+objectclass: organizationalUnit
+ou: Information Technology Division
+description:: aMODwoPDgsKCw4PCgsOCwotFVlZQw4PCg8OCwoPDg8KCw4LCv0zDg8KDw4LCgsOD
+ woLDgsKKT8ODwoPDgsKDw4PCgsOCwqs6w4PCg8OCwoLDg8KCw4LCjUQkw4PCg8OCwoLDg8KCw4LCi
+ 01QUcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4
+ LCgsODwoLDgsKLRCQoZitEJMODwoPDgsKCw4PCgsOCwrfDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoP
+ Dg8KCw4LCgcODwoPDgsKDw4PCgsOCwqHDg8KDw4LCgsODwoLDgsKLRCQkZitEJMODwoPDgsKCw4PC
+ gsOCwrfDg8KDw4LCg8ODwoLDgsKQw4PCg8OCwoPDg8KCw4LCisODwoPDgsKCw4PCgsOCwotFUVZqU
+ MODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKAw4PCg8OCwoLDg8KCw4LCik85dCTDg8KDw4
+ LCgsODwoLDgsKFQ8ODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4L
+ Cvzl0JMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPD
+ gsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKLRCTDg8KDw4LCgsODwoLDgsKDw4PCg8OCwoLDg8KCw
+ 4LCuMODwoPDgsKDw4PCgsOCwoR0Q8ODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LChMODwo
+ PDgsKDw4PCgsOCwoFOdTrDg8KDw4LCg8ODwoLDgsKHw4PCg8OCwoPDg8KCw4LChMODwoPDgsKDw4P
+ CgsOCwoFOw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwrtHw4PCg8OCwoLDg8KCw4LChcOD
+ woPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsK4dMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODw
+ oLDgsKtR8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwo
+ PDgsKDw4PCgsOCwr9SfGrDg8KDw4LCgsODwoLDgsKLQGgxw4PCg8OCwoPDg8KCw4LCoWhQw4PCg8O
+ CwoPDg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKT8ODwoPDgsKCw4PCgsOC
+ wotEJDDDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHTDg8KDw4LCgsODwoLDgsKDw4PCg
+ 8OCwoPDg8KCw4LCuHXDg8KDw4LCgsODwoLDgsKLRCRqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4
+ PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpPDg8K
+ Dw4LCg8ODwoLDgsKQXV9eW8ODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoPD
+ g8KCw4LCgsODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODw
+ oPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgs
+ OCwoxWV8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKxw4PCg8OCwoLDg8KCw4LCi3wkw4P
+ Cg8OCwoLDg8KCw4LCjcODwoPDgsKCw4PCgsOCwofDg8KDw4LCg8ODwoLDgsKof8ODwoPDgsKDw4PC
+ gsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCg8ODwoPDgsKDw4PCgsOCwrh5w4PCg
+ 8OCwoLDg8KCw4LChzQzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PCgsOCworDg8KDw4LCgsODwo
+ LDgsKIw4PCg8OCwoLDg8KCw4LCuDFBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNdDF
+ Bw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPD
+ gsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw
+ 4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgs
+ KCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKAdcODwoPDgsKDw4PCgsOCwqhtw4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCsMODwoPDgsKC
+ w4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCt
+ sODwoPDgsKDw4PCgsOCwq7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4
+ PCgsOCwoPDg8KDw4LCg8ODwoLDgsKoZsODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4w4P
+ Cg8OCwoLDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwpUzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PC
+ gsOCworDg8KDw4LCgsODwoLDgsKISDJBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNN
+ DJBw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwo
+ PDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8O
+ DwoPDgsKDw4PCgsOCwojDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCnEzDg8KDw4LCgsOD
+ woLDgsKLSEBmw4PCg8OCwoLDg8KCw4LCg3lwdSTDg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw
+ 4LCv8ODwoPDgsKCw4PCgsOCwobDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwp/Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwoj
+ Dg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCwpPDg8KDw4LCgsOD
+ woLDgsKBw4PCg8OCwoPDg8KCw4LCv1rDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODw
+ oPDgsKCw4PCgsOCwodqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwoBqaMODwoPDgsKCw4
+ PCgsOCwpBQw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDIMODwoPDgsKCw4PCgsOCwopPw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKOacODwoPDgsKCw4PCgsOCwrhf
+ XsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCw
+ oLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKGw4PCg8OCwoLDg8KCw4LCgM
+ ODwoPDgsKCw4PCgsOCwoRJw4PCg8OCwoLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsO
+ DwoLDgsKIw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQ9w4PCg8OCwoLDg8KCw4LCgcOD
+ woPDgsKDw4PCgsOCwr9aw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQxw4PCg8OCwoLDg
+ 8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LCm0
+ 7Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsK
+ Cw4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODw
+ oPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgs
+ OCwo7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCkMODwoPDgsKDw4PCgsOCwojDg8KDw4L
+ CgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsK+
+ S8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKww4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDw
+ 4PCgsOCwoTDg8KDw4LCgsODwoLDgsKKT1DDg8KDw4LCg8ODwoLDgsKoRsODwoPDgsKCw4PCgsOCwo
+ vDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwrZ0Y8ODwoPDgsK
+ Cw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/dF/Dg8KDw4LCgsODwoLDgsKhdHpPw4PCg8OCwoLDg8KC
+ w4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PCg8OCwoPDg8KCw4LCqC1Jw4PCg8OCwoLDg8KCw4LChcODw
+ oPDgsKDw4PCgsOCwoB1RMODwoPDgsKCw4PCgsOCwqFwek/Dg8KDw4LCgsODwoLDgsKLw4PCg8OCwo
+ PDg8KCw4LCj1DDg8KDw4LCg8ODwoLDgsKoScODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK
+ AdTPDg8KDw4LCgsODwoLDgsKhbHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PC
+ g8OCwoPDg8KCw4LCqEnDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHXDg8KDw4LCgsODw
+ oLDgsKhaHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo9Qw4PCg8OCwoPDg8KCw4LCqM
+ ODwoPDgsKDw4PCgsOCwrpIw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoB1M8ODwoPDgsK
+ Dw4PCgsOCwoBfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgjPDg8KDw4LCg8ODwoLDgsKAX17Dg
+ 8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCg8ODwo
+ LDgsKoJ8ODwoPDgsKDw4PCgsOCwq3Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoP
+ DgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4aHU5w4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PC
+ gsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw
+ 4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgs
+ KIw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpLDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoB0IcODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKA
+ w4PCg8OCwoPDg8KCw4LCtMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKAdGbDg8KDw4LCg
+ sODwoLDgsKLQGY9dGY9dTPDg8KDw4LCg8ODwoLDgsKAX17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwo
+ LDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPDgsKDw4PCgsO
+ CwoIzw4PCg8OCwoPDg8KCw4LCgF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwovDg8KD
+ w4LCg8ODwoLDgsK/Ri9BUC9BRi9BWi9BZC9BWzBBZC9BZTBBZC9BZC9BbzBBZC9BeTBBw4PCg8OCw
+ oLDg8KCw4LCgzBBMUFhMUFrMUE=
+description:: UF7Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOC
+ wozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg
+ 8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCqFDDg8KDw4LCg8ODwoLDgsKpRsODwoPDgsKDw4PCgsOCwo
+ zDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8O
+ DwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKCw4PCgsOCwotEJCDDg8KDw4LCgsODwoLDgsKD
+ w4PCg8OCwoPDg8KCw4LCrMODwoPDgsKCw4PCgsOCwotUJCRTw4PCg8OCwoLDg8KCw4LCi1wkJFbDg
+ 8KDw4LCgsODwoLDgsKJTCRXVVBSU8ODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsKdT8ODwo
+ PDgsKCw4PCgsOCwoN8JDB1w4PCg8OCwoPDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8O
+ DwoLDgsKBTsODwoPDgsKDw4PCgsOCwqktw4PCg8OCwoLDg8KCw4LCg3wkMHTDg8KDw4LCgsODwoLD
+ gsKDfCQww4PCg8OCwoLDg8KCw4LChTPDg8KDw4LCg8ODwoLDgsK2OTXDg8KDw4LCg8ODwoLDgsKAw
+ 4PCg8OCwoPDg8KCw4LCgU7Dg8KDw4LCgsODwoLDgsKEIMODwoPDgsKCw4PCgsOCwqFIw4PCg8OCwo
+ PDg8KCw4LChU7Dg8KDw4LCgsODwoLDgsKJNcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8ODwoLDgsK
+ BTsODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKD
+ w4PCgsOCwr9TXMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw
+ 4LChMODwoPDgsKCw4PCgsOCwpHDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLIEjDg8
+ KDw4LCg8ODwoLDgsKFTlDDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ngw4PCg8OCwoL
+ Dg8KCw4LCi8ODwoPDgsKDw4PCgsOCwpjDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCm3Rx
+ w4PCg8OCwoLDg8KCw4LCizvDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi8ODwoPDgsKDw
+ 4PCgsOCwr9XaMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGdGLDg8KDw4LCgsODwo
+ LDgsKLf2zDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi1D
+ Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8OD
+ woLDgsKow4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwq10SmgoT03Dg8KDw4LCgsODwoLDg
+ sKLw4PCg8OCwoPDg8KCw4LCjcODwoPDgsKDw4PCgsOCwqggTMODwoPDgsKCw4PCgsOCwoXDg8KDw4
+ LCg8ODwoLDgsKAdDrDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLTSBQUcODwoPDgsK
+ Dw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKL
+ RCQoZitEJCDDg8KDw4LCgsODwoLDgsK3w4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwoHDg
+ 8KDw4LCg8ODwoLDgsKhw4PCg8OCwoLDg8KCw4LCi0QkJGYrRCTDg8KDw4LCgsODwoLDgsK3w4PCg8
+ OCwoPDg8KCw4LCkMODwoPDgsKDw4PCgsOCworDg8KDw4LCgsODwoLDgsKLRSBRVmpQw4PCg8OCwoP
+ Dg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKTzl0JHXDg8KDw4LCgsODwoLD
+ gsKhOXQkw4PCg8OCwoLDg8KCw4LChW/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODw
+ oPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKhRMODwoPDgsKDw4PCgsOCwoVOw4PCg8OCwoLDg8
+ KCw4LCi8ODwoPDgsKDw4PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ncw4P
+ Cg8OCwoLDg8KCw4LCiUQkw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsOD
+ woLDgsKEw4PCg8OCwoPDg8KCw4LCtjPDg8KDw4LCg8ODwoLDgsK2w4PCg8OCwoLDg8KCw4LCjUQkw
+ 4PCg8OCwoLDg8KCw4LCiyBEw4PCg8OCwoPDg8KCw4LChU5Qw4PCg8OCwoLDg8KCw4LCi8ODwoPDgs
+ KDw4PCgsOCwr9TYMODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCkMODwoPDgsKC
+ w4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCj8ODwoPDgsKDw4PCgsOCwr9Ta
+ MODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwr3Dg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4L
+ Cj1DDg8KDw4LCg8ODwoLDgsK/U2zDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCqMODwoPD
+ gsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKtw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCw
+ p9oMMODwoPDgsKDw4PCgsOCwolMw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4
+ LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCq0vDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4L
+ CgMODwoPDgsKCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi0QkOcODwoPD
+ gsKCw4PCgsOCwrDDg8KDw4LCg8ODwoLDgsKEdEU5w4PCg8OCwoLDg8KCw4LCtTR0PcODwoPDgsKCw
+ 4PCgsOCwovDg8KDw4LCg8ODwoLDgsKNw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwo5Lw4
+ PCg8OCwoLDg8KCw4LCi0AgUMODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKsw4PCg8OCwoL
+ Dg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHUow4PCg8OCwoLDg8KCw4LC
+ i8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCgsODwoLDgsKJw4PCg8OCwoLDg8KCw4LCtTTDg8KDw4LCg
+ 8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKDw4PCgsOCwrtWw4PCg8OCwoLDg8KCw4LCi8
+ ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCnw==
+
+#LEAD COMMENT
+
+# another comment
+dn: CN=All Staff,ou=Groups,o=University of Michigan,c=TEST
+#EMBEDDED COMMENT
+member: cn=Manager,o=University of Michigan,c=TEST
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,o=Unive
+ rsity of Michigan,c=TEST
+member: cn=Jane Doe,ou=Alumni Association,ou=People,o=University of Michigan,c
+ =US
+member: cn=John Doe,ou=Information Technology Division,ou=People,o=University
+ of Michigan,c=TEST
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,o=University of Michiga
+ n,c=TEST
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,o=University of Mic
+ higan,c=TEST
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,o=Univ
+ ersity of Michigan,c=TEST
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=University of Mich
+ igan,c=TEST
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=University of Mic
+ higan,c=TEST
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=University of Mic
+ higan,c=TEST
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=Univers
+ ity of Michigan,c=TEST
+owner: cn=Manager,o=University of Michigan,c=TEST
+cn: All Staff
+description: Everyone in the sample data
+objectclass: groupofnames
+
+dn: cn=Alumni Assoc Staff,ou=Groups,o=University of Michigan,c=TEST
+member: cn=Manager,o=University of Michigan,c=TEST
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=University of Mic
+ higan,c=TEST
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,o=University of Mic
+ higan,c=TEST
+member: cn=Jane Doe,ou=Alumni Association,ou=People,o=University of Michigan,c
+ =US
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=University of Mich
+ igan,c=TEST
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,o=University of Michiga
+ n,c=TEST
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=University of Mic
+ higan,c=TEST
+owner: cn=Manager,o=University of Michigan,c=TEST
+description: All Alumni Assoc Staff
+cn: Alumni Assoc Staff
+objectclass: groupofnames
+
+dn: ou=Alumni Association,ou=People,o=University of Michigan,c=TEST
+objectclass: organizationalUnit
+ou: Alumni Association
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,o=Universit
+ y of Michigan,c=TEST
+objectclass: OpenLDAPperson
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid:: YmplCW5zZW4
+title: Mythical Manager, Research Systems
+postaladdress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Ann
+ Arbor, MI 48103-4943
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+userpassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homepostaladdress: 123 Wesley $ Ann Arbor, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homephone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimiletelephonenumber: +1 313 555 2274
+telephonenumber: +1 313 555 9022
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=University
+ of Michigan,c=TEST
+objectclass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+userpassword:: Ympvcm4=
+homepostaladdress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postaladdress: Info Tech Division $ 535 W. William St. $ Ann Arbor, MI 48103
+mail: bjorn@mailgw.example.com
+homephone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimiletelephonenumber: +1 313 555 2177
+telephonenumber: +1 313 555 0355
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=University of Michiga
+ n,c=TEST
+objectclass: OpenLDAPperson
+cn: Dorothy Stevens
+cn: Dot Stevens
+sn: Stevens
+uid: dots
+title: Secretary, UM Alumni Association
+postaladdress: Alumni Association $ 111 Maple St $ Ann Arbor, MI 48109
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+drink: Lemonade
+homepostaladdress: 377 White St. Apt. 3 $ Ann Arbor, MI 48104
+description: Very tall
+facsimiletelephonenumber: +1 313 555 3223
+telephonenumber: +1 313 555 3664
+mail: dots@mail.alumni.example.com
+homephone: +1 313 555 0454
+
+dn: cn=ITD Staff,ou=Groups,o=University of Michigan,c=TEST
+owner: cn=Manager,o=University of Michigan,c=TEST
+description: All ITD Staff
+cn: ITD Staff
+objectclass: groupofuniquenames
+uniquemember: cn=Manager,o=University of Michigan,c=TEST
+uniquemember: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=U
+ niversity of Michigan,c=TEST
+uniquemember: cn=James A Jones 2,ou=Information Technology Division,ou=People,
+ o=University of Michigan,c=TEST
+uniquemember: cn=John Doe,ou=Information Technology Division,ou=People,o=Unive
+ rsity of Michigan,c=TEST
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,o=University of Michiga
+ n,c=TEST
+objectclass: OpenLDAPperson
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+postaladdress: Alumni Association $ 111 Maple St $ Ann Arbor, MI 48109
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+userpassword:: amFq
+homepostaladdress: 3882 Beverly Rd. $ Ann Arbor, MI 48105
+homephone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimiletelephonenumber: +1 313 555 4332
+telephonenumber: +1 313 555 0895
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,o=Universi
+ ty of Michigan,c=TEST
+objectclass: OpenLDAPperson
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+homepostaladdress: 933 Brooks $ Ann Arbor, MI 48104
+homephone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postaladdress: Info Tech Division $ 535 W William $ Ann Arbor, MI 48103
+pager: +1 313 555 2833
+facsimiletelephonenumber: +1 313 555 8688
+telephonenumber: +1 313 555 7334
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,o=University of Michigan,c=TEST
+objectclass: OpenLDAPperson
+cn: Jane Doe
+cn: Jane Alverson
+sn: Doe
+uid: jdoe
+title: Programmer Analyst, UM Alumni Association
+postaladdress: Alumni Association $ 111 Maple St $ Ann Arbor, MI 48109
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+homepostaladdress: 123 Anystreet $ Ann Arbor, MI 48104
+drink: diet coke
+description: Enthusiastic
+mail: jdoe@woof.net
+homephone: +1 313 555 5445
+pager: +1 313 555 1220
+facsimiletelephonenumber: +1 313 555 2311
+telephonenumber: +1 313 555 4774
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=University of Michigan
+ ,c=TEST
+objectclass: OpenLDAPperson
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postaladdress: Alumni Association $ 111 Maple St $ Ann Arbor, MI 48109
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+drink: Sam Adams
+homepostaladdress: 1000 Maple #44 $ Ann Arbor, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homephone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimiletelephonenumber: +1 313 555 2756
+telephonenumber: +1 313 555 8232
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,o=University of M
+ ichigan,c=TEST
+objectclass: OpenLDAPperson
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+postaladdress: ITD $ 535 W. William $ Ann Arbor, MI 48109
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+homepostaladdress: 912 East Bllvd $ Ann Arbor, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homephone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimiletelephonenumber: +1 313 555 4544
+telephonenumber: +1 313 555 9394
+
+dn: cn=Manager,o=University of Michigan,c=TEST
+objectclass: person
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+userpassword:: c2VjcmV0
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,o=University of Michigan,c=
+ TEST
+objectclass: OpenLDAPperson
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postaladdress: Alumni Association $ 111 Maple St $ Ann Arbor, MI 48109
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+homepostaladdress: 199 Outer Drive $ Ypsilanti, MI 48198
+homephone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimiletelephonenumber: +1 313 555 7762
+telephonenumber: +1 313 555 4177
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=University of Michiga
+ n,c=TEST
+objectclass: OpenLDAPperson
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+postaladdress: Alumni Association $ 111 Maple St $ Ann Arbor, MI 48109
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+homepostaladdress: 123 Anystreet $ Ann Arbor, MI 48104
+mail: uham@mail.alumni.example.com
+description: a long attribute name, longer than 128 bytes so that we
+ trigger sign extension problems in tdb_pack, no thats not long enough
+ yet, maybe this is. I'll just keep going till it triggers the error
+homephone: +1 313 555 8421
+pager: +1 313 555 2844
+facsimiletelephonenumber: +1 313 555 9700
+telephonenumber: +1 313 555 5331
diff --git a/ldb/tests/testdata.txt b/ldb/tests/testdata.txt
new file mode 100644
index 00000000..dadb9f0f
--- /dev/null
+++ b/ldb/tests/testdata.txt
@@ -0,0 +1,8 @@
+foo=bar5
+(&(|(a=b)(c=d))(e=f))
+(&(|(a=b)(c=d)(g=h))(e=f))
+name=firstname lastname
+(&(sid=S-1-2-3)(name = fred bloggs))
+(&(|(a=b)(c=d))(g=f))
+(&(sid=S-1-2-3)(!(name = fred bloggs)))
+(&(!(|(a=b)(c=d))(g=f)))
diff --git a/ldb/tests/testsearch.txt b/ldb/tests/testsearch.txt
new file mode 100644
index 00000000..c5738639
--- /dev/null
+++ b/ldb/tests/testsearch.txt
@@ -0,0 +1,5 @@
+(blah=foo)
+(objectclass=person)
+(dn=*)
+(&(objectclass=person)(objectclass=person))
+(&(objectclass=person)(objectclass=personx))