aboutsummaryrefslogtreecommitdiffstats
path: root/unit-tests.lua
diff options
context:
space:
mode:
Diffstat (limited to 'unit-tests.lua')
-rw-r--r--unit-tests.lua395
1 files changed, 395 insertions, 0 deletions
diff --git a/unit-tests.lua b/unit-tests.lua
new file mode 100644
index 0000000..11dfcef
--- /dev/null
+++ b/unit-tests.lua
@@ -0,0 +1,395 @@
+#!/usr/bin/lua
+---------------------------------------------------------------------
+-- LuaLDAP test file.
+-- This test will create a copy of an existing entry on the
+-- directory to work on. This new entry will be modified,
+-- renamed and deleted at the end.
+---------------------------------------------------------------------
+
+--
+DN_PAT = "^([^,=]+)%=([^,]+)%,?(.*)$"
+
+---------------------------------------------------------------------
+-- Print attributes.
+---------------------------------------------------------------------
+function print_attrs (dn, attrs)
+ if not dn then
+ io.write ("nil\n")
+ return
+ end
+ io.write (string.format ("\t[%s]\n", dn))
+ for name, values in pairs (attrs) do
+ io.write ("["..name.."] : ")
+ local tv = type (values)
+ if tv == "string" then
+ io.write (values)
+ elseif tv == "table" then
+ local n = table.getn (values)
+ for i = 1, n-1 do
+ io.write (values[i]..",")
+ end
+ io.write (values[n])
+ end
+ io.write ("\n")
+ end
+end
+
+---------------------------------------------------------------------
+-- clone a table.
+---------------------------------------------------------------------
+function clone (tab)
+ local new = {}
+ for i, v in pairs (tab) do
+ new[i] = v
+ end
+ return new
+end
+
+
+---------------------------------------------------------------------
+-- checks for a value and throw an error if it is not the expected.
+---------------------------------------------------------------------
+function assert2 (expected, value, msg)
+ if not msg then
+ msg = ''
+ else
+ msg = tostring(msg)..'\n'
+ end
+ local ret = assert (value == expected,
+ msg.."wrong value (["..tostring(value).."] instead of "..
+ tostring(expected)..")")
+ io.write('.')
+ return ret
+end
+
+---------------------------------------------------------------------
+-- object test.
+---------------------------------------------------------------------
+function test_object (obj, objmethods)
+ -- checking object type.
+ assert2 ("userdata", type(obj), "incorrect object type")
+ -- trying to get metatable.
+ assert2 ("LuaLDAP: you're not allowed to get this metatable",
+ getmetatable(obj), "error permitting access to object's metatable")
+ -- trying to set metatable.
+ assert2 (false, pcall (setmetatable, ENV, {}))
+ -- checking existence of object's methods.
+ for i = 1, table.getn (objmethods) do
+ local method = obj[objmethods[i]]
+ assert2 ("function", type(method))
+ assert2 (false, pcall (method), "no 'self' parameter accepted")
+ end
+ return obj
+end
+
+CONN_OK = function (obj, err)
+ if obj == nil then
+ error (err, 2)
+ end
+ return test_object (obj, { "close", "add", "compare", "delete", "modify", "rename", "search", })
+end
+
+---------------------------------------------------------------------
+-- basic checking test.
+---------------------------------------------------------------------
+function basic_test ()
+ local ld = CONN_OK (lualdap.open_simple { uri = HOSTNAME, who = WHO, password = PASSWORD })
+ assert2 (1, ld:close(), "couldn't close connection")
+ -- trying to close without a connection.
+ assert2 (false, pcall (ld.close))
+ -- trying to close an invalid connection.
+ assert2 (false, pcall (ld.close, io.output()))
+ -- trying to use a closed connection.
+ local _,_,rdn_name,rdn_value = string.find (BASE, DN_PAT)
+ assert2 (false, pcall (ld.compare, ld, BASE, rdn_name, rdn_value),
+ "permitting the use of a closed connection")
+ -- it is ok to close a closed object, but nil is returned instead of 1.
+ assert2 (nil, ld:close())
+ -- trying to connect to an invalid host.
+ assert2 (nil, lualdap.open_simple {uri = "unknown-server"}, "this should be an error")
+ -- reopen the connection.
+ -- first, try using TLS
+ local ok = lualdap.open_simple {uri = HOSTNAME, who = WHO, password = PASSWORD, starttls = true }
+ if not ok then
+ -- second, try without TLS
+ io.write ("\nWarning! Couldn't connect with TLS. Trying again without it.")
+ ok = lualdap.open_simple {uri = HOSTNAME, who = WHO, password = PASSWORD, starttls = false }
+ end
+ LD = CONN_OK (ok)
+ CLOSED_LD = ld
+ collectgarbage()
+end
+
+
+---------------------------------------------------------------------
+-- checks return value which should be a function AND also its return value.
+---------------------------------------------------------------------
+function check_future (ret, method, ...)
+ local ok, f = pcall (method, unpack (arg))
+ assert (ok, f)
+ assert2 ("function", type(f))
+ assert2 (ret, f())
+ io.write('.')
+end
+
+
+---------------------------------------------------------------------
+-- checking compare operation.
+---------------------------------------------------------------------
+function compare_test ()
+ local _,_,rdn_name,rdn_value = string.find (BASE, DN_PAT)
+ assert (type(rdn_name) == "string", "could not extract RDN name")
+ assert (type(rdn_value) == "string", "could not extract RDN value")
+ -- comparing against the correct value.
+ check_future (true, LD.compare, LD, BASE, rdn_name, rdn_value)
+ -- comparing against a wrong value.
+ check_future (false, LD.compare, LD, BASE, rdn_name, rdn_value..'_')
+ -- comparing against an incorrect attribute name.
+ check_future (nil, LD.compare, LD, BASE, rdn_name..'x', rdn_value)
+ -- comparing on a wrong base.
+ check_future (nil, LD.compare, LD, 'qwerty', rdn_name, rdn_value)
+ -- comparing with a closed connection.
+ assert2 (false, pcall (LD.compare, CLOSED_LD, BASE, rdn_name, rdn_value))
+ -- comparing with an invalid userdata.
+ assert2 (false, pcall (LD.compare, io.output(), BASE, rdn_name, rdn_value))
+end
+
+
+---------------------------------------------------------------------
+-- checking basic search operation.
+---------------------------------------------------------------------
+function search_test_1 ()
+ local _,_,rdn = string.find (WHO, "^([^,]+)%,.*$")
+ local iter = LD:search {
+ base = BASE,
+ scope = "onelevel",
+ sizelimit = 1,
+ filter = "("..rdn..")",
+ }
+ assert2 ("function", type(iter))
+ collectgarbage()
+ CONN_OK (LD)
+ local dn, entry = iter ()
+ assert2 ("string", type(dn))
+ assert2 ("table", type(entry))
+ collectgarbage()
+ assert2 ("function", type(iter))
+ CONN_OK (LD)
+
+ DN, ENTRY = LD:search {
+ base = BASE,
+ scope = "onelevel",
+ sizelimit = 1,
+ filter = "("..rdn..")",
+ }()
+ collectgarbage()
+ assert2 ("string", type(DN))
+ assert2 ("table", type(ENTRY))
+end
+
+
+---------------------------------------------------------------------
+-- checking add operation.
+---------------------------------------------------------------------
+function add_test ()
+ -- clone an entry.
+ NEW = clone (ENTRY)
+ local _,_,rdn_name, rdn_value, parent_dn = string.find (DN, DN_PAT)
+ NEW[rdn_name] = rdn_value.."_copy"
+ NEW_DN = string.format ("%s=%s,%s", rdn_name, NEW[rdn_name], parent_dn)
+ -- trying to insert an entry with a wrong connection.
+ assert2 (false, pcall (LD.add, CLOSED_LD, NEW_DN, NEW))
+ -- trying to insert an entry with an invalid connection.
+ assert2 (false, pcall (LD.add, io.output(), NEW_DN, NEW))
+ -- trying to insert an entry with a wrong DN.
+ local wrong_dn = string.format ("%s_x=%s,%s", rdn_name, NEW_DN, parent_dn)
+ --assert2 (nil, LD:add (wrong_dn, NEW))
+ check_future (nil, LD.add, LD, wrong_dn, NEW)
+ -- trying to insert the clone on the LDAP data base.
+ check_future (true, LD.add, LD, NEW_DN, NEW)
+ -- trying to reinsert the clone entry on the directory.
+ check_future (nil, LD.add, LD, NEW_DN, NEW)
+end
+
+
+---------------------------------------------------------------------
+-- checking modify operation.
+---------------------------------------------------------------------
+function modify_test ()
+ -- modifying without connection.
+ assert2 (false, pcall (LD.modify, nil, NEW_DN, {}))
+ -- modifying with a closed connection.
+ assert2 (false, pcall (LD.modify, CLOSED_LD, NEW_DN, {}))
+ -- modifying with an invalid userdata.
+ assert2 (false, pcall (LD.modify, io.output(), NEW_DN, {}))
+ -- checking invalid DN.
+ assert2 (false, pcall (LD.modify, LD, {}))
+ -- no modification to apply.
+ check_future (true, LD.modify, LD, NEW_DN)
+ -- forgotten operation on modifications table.
+ local a_attr, a_value = next (ENTRY)
+ assert2 (false, pcall (LD.modify, LD, NEW_DN, { [a_attr] = "abc"}))
+ -- modifying an unknown entry.
+ local _,_, rdn_name, rdn_value, parent_dn = string.find (NEW_DN, DN_PAT)
+ local new_rdn = rdn_name..'='..rdn_value..'_'
+ local new_dn = string.format ("%s,%s", new_rdn, parent_dn)
+ check_future (nil, LD.modify, LD, new_dn)
+ -- trying to create an undefined attribute.
+ check_future (nil, LD.modify, LD, NEW_DN, {'+', unknown_attribute = 'a'})
+end
+
+
+---------------------------------------------------------------------
+function count (tab)
+ local counter = 0
+ for dn, entry in LD:search (tab) do
+ counter = counter + 1
+ end
+ return counter
+end
+
+
+---------------------------------------------------------------------
+-- checking advanced search operation.
+---------------------------------------------------------------------
+function search_test_2 ()
+ local _,_,rdn = string.find (WHO, "^([^,]+)%,.*$")
+ local iter = LD:search {
+ base = BASE,
+ scope = "onelevel",
+ sizelimit = 1,
+ filter = "("..rdn..")",
+ }
+ assert2 ("function", type(iter))
+ collectgarbage ()
+ assert2 ("function", type(iter))
+ local dn, entry = iter ()
+ assert2 ("string", type(dn))
+ assert2 ("table", type(entry))
+ collectgarbage ()
+ assert2 ("function", type(iter))
+ iter = nil
+ collectgarbage ()
+
+ -- checking no search specification.
+ assert2 (false, pcall (LD.search, LD))
+ -- checking invalid scope.
+ assert2 (false, pcall (LD.search, LD, { scope = 'BASE', base = BASE, }))
+ -- checking invalid base.
+ check_future (nil, LD.search, LD, { base = "invalid", scope = "base", })
+ -- checking filter.
+ local _,_, rdn_name, rdn_value, parent_dn = string.find (NEW_DN, DN_PAT)
+ local filter = string.format ("(%s=%s)", rdn_name, rdn_value)
+ assert (count { base = BASE, scope = "subtree", filter = filter, } == 1)
+ -- checking sizelimit.
+ assert (count { base = BASE, scope = "subtree", sizelimit = 1, } == 1)
+ -- checking attrsonly parameter.
+ for dn, entry in LD:search { base = BASE, scope = "subtree", attrsonly = true, } do
+ for attr, value in pairs (entry) do
+ assert (value == true, "attrsonly failed")
+ end
+ end
+ -- checking reuse of search object.
+ local iter = assert (LD:search { base = BASE, scope = "base", })
+ assert (type(iter) == "function")
+ local dn, e1 = iter()
+ assert (type(dn) == "string")
+ assert (type(e1) == "table")
+ dn, e1 = iter()
+ assert (type(dn) == "nil")
+ assert (type(e1) == "nil")
+ assert2 (false, pcall (iter))
+ iter = nil
+ -- checking collecting search objects.
+ local dn, entry = LD:search { base = BASE, scope = "base" }()
+ collectgarbage()
+end
+
+
+---------------------------------------------------------------------
+-- checking rename operation.
+---------------------------------------------------------------------
+function rename_test ()
+ local _,_, rdn_name, rdn_value, parent_dn = string.find (NEW_DN, DN_PAT)
+ local new_rdn = rdn_name..'='..rdn_value..'_'
+ local new_dn = string.format ("%s,%s", new_rdn, parent_dn)
+ -- trying to rename with no parent.
+ check_future (true, LD.rename, LD, NEW_DN, new_rdn, nil)
+ -- trying to rename an invalid dn.
+ check_future (nil, LD.rename, LD, NEW_DN, new_rdn, nil)
+ -- trying to rename with the same parent.
+ check_future (true, LD.rename, LD, new_dn, rdn_name..'='..rdn_value, parent_dn)
+ -- trying to rename to an inexistent parent.
+ check_future (nil, LD.rename, LD, NEW_DN, new_rdn, new_dn)
+ -- mal-formed DN.
+ assert2 (false, pcall (LD.rename, LD, ""))
+ -- trying to rename with a closed connection.
+ assert2 (false, pcall (LD.rename, CLOSED_LD, NEW_DN, new_rdn, nil))
+ -- trying to rename with an invalid connection.
+ assert2 (false, pcall (LD.rename, io.output(), NEW_DN, new_rdn, nil))
+end
+
+
+---------------------------------------------------------------------
+-- checking delete operation.
+---------------------------------------------------------------------
+function delete_test ()
+ -- trying to delete with a closed connection.
+ assert2 (false, pcall (LD.delete, CLOSED_LD, NEW_DN))
+ -- trying to delete with an invalid connection.
+ assert2 (false, pcall (LD.delete, io.output(), NEW_DN))
+ -- trying to delete new entry.
+ check_future (true, LD.delete, LD, NEW_DN)
+ -- trying to delete an already deleted entry.
+ check_future (nil, LD.delete, LD, NEW_DN)
+ -- mal-formed DN.
+ check_future (nil, LD.delete, LD, "")
+ -- no DN.
+ assert2 (false, pcall (LD.delete, LD))
+end
+
+
+---------------------------------------------------------------------
+-- checking close operation.
+---------------------------------------------------------------------
+function close_test ()
+ assert (LD:close () == 1, "couldn't close connection")
+end
+
+
+---------------------------------------------------------------------
+tests = {
+ { "basic checking", basic_test },
+ { "checking compare operation", compare_test },
+ { "checking basic search operation", search_test_1 },
+ { "checking add operation", add_test },
+ { "checking modify operation", modify_test },
+ { "checking advanced search operation", search_test_2 },
+ { "checking rename operation", rename_test },
+ { "checking delete operation", delete_test },
+ { "closing everything", close_test },
+}
+
+---------------------------------------------------------------------
+-- Main
+---------------------------------------------------------------------
+
+if table.getn(arg) < 1 then
+ print (string.format ("Usage %s host[:port] base [who [password]]", arg[0]))
+ os.exit()
+end
+
+HOSTNAME = arg[1]
+BASE = arg[2]
+WHO = arg[3]
+PASSWORD = arg[4]
+
+require"lualdap"
+assert (type(lualdap)=="table", "couldn't load LDAP library")
+
+for i = 1, table.getn (tests) do
+ local t = tests[i]
+ io.write (t[1].." ...")
+ t[2] ()
+ io.write (" OK !\n")
+end