summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/addrbook/test/unit/test_ldapquery.js
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mailnews/addrbook/test/unit/test_ldapquery.js')
-rw-r--r--comm/mailnews/addrbook/test/unit/test_ldapquery.js181
1 files changed, 181 insertions, 0 deletions
diff --git a/comm/mailnews/addrbook/test/unit/test_ldapquery.js b/comm/mailnews/addrbook/test/unit/test_ldapquery.js
new file mode 100644
index 0000000000..90b1f1673d
--- /dev/null
+++ b/comm/mailnews/addrbook/test/unit/test_ldapquery.js
@@ -0,0 +1,181 @@
+/* -*- Mode: JavaScript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Test basic LDAP querying.
+ */
+
+const { LDAPDaemon, LDAPHandlerFn } = ChromeUtils.import(
+ "resource://testing-common/mailnews/Ldapd.jsm"
+);
+const { BinaryServer } = ChromeUtils.import(
+ "resource://testing-common/mailnews/Binaryd.jsm"
+);
+
+/**
+ * Adaptor class to implement nsILDAPMessageListener with a promise.
+ * It should be passed into LDAP functions as a normal listener. The
+ * caller can then await the promise attribute.
+ * Based on the pattern used in PromiseTestUtils.jsm.
+ *
+ * This base class just rejects all callbacks. Derived classes should
+ * implement the callbacks they need to handle.
+ *
+ * @implements {nsILDAPMessageListener}
+ */
+class PromiseListener {
+ constructor() {
+ this.QueryInterface = ChromeUtils.generateQI(["nsILDAPMessageListener"]);
+ this.promise = new Promise((resolve, reject) => {
+ this._resolve = resolve;
+ this._reject = reject;
+ });
+ }
+ onLDAPMessage(message) {
+ this._reject(new Error("Unexpected onLDAPMessage"));
+ }
+ onLDAPInit() {
+ this._reject(new Error("Unexpected onLDAPInit"));
+ }
+ onLDAPError(status, secInfo, location) {
+ this._reject(new Error(`Unexpected onLDAPError (0x${status.toString(16)}`));
+ }
+}
+
+/**
+ * PromiseInitListener resolves the promise when onLDAPInit is called.
+ *
+ * @augments {PromiseListener}
+ */
+class PromiseInitListener extends PromiseListener {
+ onLDAPInit() {
+ this._resolve();
+ }
+}
+
+/**
+ * PromiseBindListener resolves when a bind operation completes.
+ *
+ * @augments {PromiseListener}
+ */
+class PromiseBindListener extends PromiseListener {
+ onLDAPMessage(message) {
+ if (Ci.nsILDAPErrors.SUCCESS != message.errorCode) {
+ this._reject(
+ new Error(`Operation failed (LDAP code ${message.errorCode})`)
+ );
+ }
+ if (Ci.nsILDAPMessage.RES_BIND == message.type) {
+ this._resolve(); // All done.
+ }
+ }
+}
+
+/**
+ * PromiseSearchListener collects search results, returning them via promise
+ * when the search is complete.
+ *
+ * @augments {PromiseListener}
+ */
+class PromiseSearchListener extends PromiseListener {
+ constructor() {
+ super();
+ this._results = [];
+ }
+ onLDAPMessage(message) {
+ if (Ci.nsILDAPMessage.RES_SEARCH_RESULT == message.type) {
+ this._resolve(this._results); // All done.
+ }
+ if (Ci.nsILDAPMessage.RES_SEARCH_ENTRY == message.type) {
+ this._results.push(message);
+ }
+ }
+}
+
+add_task(async function test_basic_query() {
+ // Load in some test contact data (characters from Sherlock Holmes).
+ let raw = await IOUtils.readUTF8(
+ do_get_file(
+ "../../../../mailnews/addrbook/test/unit/data/ldap_contacts.json"
+ ).path
+ );
+ let testContacts = JSON.parse(raw);
+
+ // Set up fake LDAP server, loaded with the test contacts.
+ let daemon = new LDAPDaemon();
+ daemon.add(...Object.values(testContacts));
+ // daemon.setDebug(true);
+ let server = new BinaryServer(LDAPHandlerFn, daemon);
+ server.start();
+
+ // Connect to the fake server.
+ let url = `ldap://localhost:${server.port}`;
+ let ldapURL = Services.io.newURI(url).QueryInterface(Ci.nsILDAPURL);
+ let conn = Cc["@mozilla.org/network/ldap-connection;1"]
+ .createInstance()
+ .QueryInterface(Ci.nsILDAPConnection);
+
+ // Initialisation is async.
+ let initListener = new PromiseInitListener();
+ conn.init(ldapURL, null, initListener, null, Ci.nsILDAPConnection.VERSION3);
+ await initListener.promise;
+
+ // Perform bind.
+ let bindListener = new PromiseBindListener();
+ let bindOp = Cc["@mozilla.org/network/ldap-operation;1"].createInstance(
+ Ci.nsILDAPOperation
+ );
+ bindOp.init(conn, bindListener, null);
+ bindOp.simpleBind(""); // no password
+ await bindListener.promise;
+
+ // Run a search.
+ let searchListener = new PromiseSearchListener();
+ let searchOp = Cc["@mozilla.org/network/ldap-operation;1"].createInstance(
+ Ci.nsILDAPOperation
+ );
+ searchOp.init(conn, searchListener, null);
+ searchOp.searchExt(
+ "", // dn
+ Ci.nsILDAPURL.SCOPE_SUBTREE,
+ "(sn=Holmes)", // filter: Find the Holmes family members.
+ "", // wanted_attributes
+ 0, // timeOut
+ 100 // maxEntriesWanted
+ );
+ let matches = await searchListener.promise;
+
+ // Make sure we got the contacts we expected (just use cn for comparing):
+ const holmesCNs = ["Eurus Holmes", "Mycroft Holmes", "Sherlock Holmes"];
+ const holmesGivenNames = ["Eurus", "Mycroft", "Sherlock"];
+ const nonHolmesCNs = [
+ "Greg Lestrade",
+ "Irene Adler",
+ "Jim Moriarty",
+ "John Watson",
+ "Mary Watson",
+ "Molly Hooper",
+ "Mrs Hudson",
+ ];
+ let cns = matches.map(ent => ent.getValues("cn")[0]);
+ cns.sort();
+ Assert.deepEqual(cns, holmesCNs);
+
+ // Test getValues is case insensitive about the attribute name.
+ let givenNames = matches.map(ent => ent.getValues("givenname")[0]);
+ givenNames.sort();
+ Assert.deepEqual(givenNames, holmesGivenNames);
+ givenNames = matches.map(ent => ent.getValues("givenName")[0]);
+ givenNames.sort();
+ Assert.deepEqual(givenNames, holmesGivenNames);
+ givenNames = matches.map(ent => ent.getValues("GIVENNAME")[0]);
+ givenNames.sort();
+ Assert.deepEqual(givenNames, holmesGivenNames);
+
+ // Sanity check: make sure the non-Holmes contacts were excluded.
+ nonHolmesCNs.forEach(cn => Assert.ok(!cns.includes(cn)));
+
+ server.stop();
+});