From e7d1a63f1b251ee4139e50877d33d308d8ec2149 Mon Sep 17 00:00:00 2001
From: Fabio Pistolesi <fabio.pistolesi@forgerock.com>
Date: Fri, 06 Feb 2015 15:43:20 +0000
Subject: [PATCH] OPENDJ-1740 CR-5939 Develop unit tests for pluggable backend and a refactoring on an existing test.

---
 opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/backends/pluggable/PluggableBackendImplTestCase.java |  857 ++++++++++++++++++++++++++++++++++++++++++++++++++
 opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/backends/pluggable/persistit/PersistitTestCase.java  |   88 +++++
 opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/admin/ValidateConfigDefinitionsTest.java             |   20 
 3 files changed, 955 insertions(+), 10 deletions(-)

diff --git a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/admin/ValidateConfigDefinitionsTest.java b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/admin/ValidateConfigDefinitionsTest.java
index ba0438f..b7b0e6c 100644
--- a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/admin/ValidateConfigDefinitionsTest.java
+++ b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/admin/ValidateConfigDefinitionsTest.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2008 Sun Microsystems, Inc.
- *      Portions copyright 2011 ForgeRock AS
+ *      Portions copyright 2011-2015 ForgeRock AS
  */
 package org.opends.server.admin;
 
@@ -74,10 +74,10 @@
 
   // Exceptions to config objects having a differnt objectclass
   private static final List<String> CLASS_OBJECT_CLASS_EXCEPTIONS =
-          Arrays.asList(new String[]{
+          Arrays.asList(
                   "org.opends.server.admin.std.meta.RootCfgDefn",
-                  "org.opends.server.admin.std.meta.GlobalCfgDefn",
-          });
+                  "org.opends.server.admin.std.meta.GlobalCfgDefn"
+          );
 
 
   @Test(dataProvider="enumrateManageObjectDefns")
@@ -114,21 +114,21 @@
 
   // Exceptions to properties ending in -class being exactly 'java-class'.
   private static final List<String> CLASS_PROPERTY_EXCEPTIONS =
-          Arrays.asList(new String[]{
+          Arrays.asList(
                   // e.g. "prop-name-ending-with-class"
-          });
+          );
 
   // Exceptions to properties ending in -enabled being exactly 'enabled'.
   private static final List<String> ENABLED_PROPERTY_EXCEPTIONS =
-          Arrays.asList(new String[]{
+          Arrays.asList(
                   "index-filter-analyzer-enabled",
                   "subordinate-indexes-enabled"
                   // e.g. "prop-name-ending-with-enabled"
-          });
+          );
 
   // Exceptions to properties not starting with the name of their config object
   private static final List<String> OBJECT_PREFIX_PROPERTY_EXCEPTIONS =
-          Arrays.asList(new String[]{
+          Arrays.asList(
                   "backend-id",
                   "plugin-type",
                   "replication-server-id",
@@ -137,7 +137,7 @@
                   "workflow-element-id",
                   "workflow-element"
                   // e.g. "prop-name-starting-with-object-prefix"
-          });
+          );
 
 
   private void validatePropertyDefinition(AbstractManagedObjectDefinition<?, ?> objectDef,
diff --git a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/backends/pluggable/PluggableBackendImplTestCase.java b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/backends/pluggable/PluggableBackendImplTestCase.java
new file mode 100644
index 0000000..a2d00ea
--- /dev/null
+++ b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/backends/pluggable/PluggableBackendImplTestCase.java
@@ -0,0 +1,857 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
+ * or http://forgerock.org/license/CDDLv1.0.html.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at legal-notices/CDDLv1_0.txt.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2015 ForgeRock AS
+ */
+
+package org.opends.server.backends.pluggable;
+
+import static org.forgerock.opendj.ldap.ModificationType.ADD;
+import static org.opends.server.protocols.internal.InternalClientConnection.getRootConnection;
+import static org.opends.server.protocols.internal.Requests.newSearchRequest;
+import static org.opends.server.types.Attributes.create;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+
+import org.assertj.core.api.Assertions;
+import org.forgerock.opendj.ldap.ByteString;
+import org.forgerock.opendj.ldap.ConditionResult;
+import org.forgerock.opendj.ldap.SearchScope;
+import org.opends.server.DirectoryServerTestCase;
+import org.opends.server.TestCaseUtils;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.protocols.internal.InternalSearchOperation;
+import org.opends.server.protocols.internal.SearchRequest;
+import org.opends.server.tools.makeldif.TemplateFile;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.BackupConfig;
+import org.opends.server.types.BackupDirectory;
+import org.opends.server.types.DN;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.Entry;
+import org.opends.server.types.IndexType;
+import org.opends.server.types.LDIFExportConfig;
+import org.opends.server.types.LDIFImportConfig;
+import org.opends.server.types.Modification;
+import org.opends.server.types.RestoreConfig;
+import org.opends.server.types.SearchResultEntry;
+import org.opends.server.workflowelement.localbackend.LocalBackendSearchOperation;
+import org.testng.Reporter;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+/**
+ * BackendImpl Tester.
+ */
+@SuppressWarnings("javadoc")
+@Test(groups = { "precommit", "pluggablebackend" }, sequential = true)
+public abstract class PluggableBackendImplTestCase extends DirectoryServerTestCase
+{
+  protected BackendImpl backend;
+
+  protected List<Entry> topEntries;
+  protected List<Entry> entries;
+  protected List<Entry> workEntries;
+  protected DN testBaseDN;
+  protected DN dnToMod;
+  protected DN dnToDel;
+  protected DN searchDN;
+  protected DN badEntryDN;
+  protected String[] ldifTemplate;
+  protected int ldifNumberOfEntries;
+  protected String backupID;
+  protected String[] backendIndexes = { "sn" };
+  protected String[] backendVlvIndexes = {};
+
+  private AttributeType modifyAttribute;
+  private final ByteString modifyValue = ByteString.valueOf("foo");
+  private String backupPath;
+  private BackupDirectory backupDirectory;
+
+  /**
+   * Configures a backend for the specified backend
+   * 
+   * @param homeDirName
+   *          Directory where database file live
+   * @param testBaseDN
+   *          Root DN for the DIT
+   * @return a backend object.
+   */
+  protected abstract BackendImpl createBackend() throws Exception;
+  
+  @BeforeClass
+  public void setUp() throws Exception
+  {
+    // Need the schema to be available, so make sure the server is started.
+    TestCaseUtils.startServer();
+  
+    testBaseDN = DN.valueOf("dc=test,dc=com");
+    backend = createBackend();
+    backend.initializeBackend();
+  
+    topEntries = TestCaseUtils.makeEntries(
+                "dn: " + testBaseDN,
+                "objectclass: top",
+                "objectclass: domain",
+                "",
+                "dn: ou=People," + testBaseDN,
+                "objectclass: top",
+                "objectclass: organizationalUnit",
+                "ou: People");
+    entries = TestCaseUtils.makeEntries(
+                "dn: uid=user.0,ou=People," + testBaseDN,
+                "objectClass: top",
+                "objectClass: person",
+                "objectClass: organizationalPerson",
+                "objectClass: inetOrgPerson",
+                "givenName: Aaccf",
+                "sn: Amar",
+                "cn: Aaccf Amar",
+                "initials: AQA",
+                "employeeNumber: 0",
+                "uid: user.0",
+                "mail: user.0@example.com",
+                "userPassword: password",
+                "telephoneNumber: 380-535-2354",
+                "homePhone: 707-626-3913",
+                "pager: 456-345-7750",
+                "mobile: 366-674-7274",
+                "street: 99262 Eleventh Street",
+                "l: Salem",
+                "st: NM",
+                "postalCode: 36530",
+                "postalAddress: Aaccf Amar$99262 Eleventh Street$Salem, NM  36530",
+                "description: This is the description for Aaccf Amar.",
+                "",
+                "dn: uid=user.1,ou=People," + testBaseDN,
+                "objectClass: top",
+                "objectClass: person",
+                "objectClass: organizationalPerson",
+                "objectClass: inetOrgPerson",
+                "givenName: Aaren",
+                "givenName;lang-fr: test2",
+                "givenName;lang-cn: test2",
+                "givenName;lang-es: test3",
+                "sn: Atp",
+                "cn: Aaren Atp",
+                "initials: APA",
+                "employeeNumber: 1",
+                "uid: user.1",
+                "mail: user.1@example.com",
+                "userPassword: password",
+                "telephoneNumber: 643-278-6134",
+                "homePhone: 546-786-4099",
+                "pager: 508-261-3187",
+                "mobile: 377-267-7824",
+                "carLicense: 377-267-7824",
+                "street: 78113 Fifth Street",
+                "l: Chico",
+                "st: TN",
+                "postalCode: 72322",
+                "postalAddress: Aaren Atp$78113 Fifth Street$Chico, TN  72322",
+                "description: This is the description for Aaren Atp.",
+                "",
+                "dn: uid=user.2,ou=People," + testBaseDN,
+                "objectClass: top",
+                "objectClass: person",
+                "objectClass: organizationalPerson",
+                "objectClass: inetOrgPerson",
+                "givenName: Aarika",
+                "sn: Atpco",
+                "cn: Aarika Atpco",
+                "initials: ARA",
+                "employeeNumber: 2",
+                "uid: user.2",
+                "mail: user.2@example.com",
+                "userPassword: password",
+                "telephoneNumber: 547-504-3498",
+                "homePhone: 955-899-7308",
+                "pager: 710-832-9316",
+                "mobile: 688-388-4525",
+                "carLicense: 688-388-4525",
+                "street: 59208 Elm Street",
+                "l: Youngstown",
+                "st: HI",
+                "postalCode: 57377",
+                "postalAddress: Aarika Atpco$59208 Elm Street$Youngstown, HI  57377",
+                "description: This is the description for Aarika Atpco.",
+                "",
+                "dn: uid=user.3,ou=People," + testBaseDN,
+                "objectClass: top",
+                "objectClass: person",
+                "objectClass: organizationalPerson",
+                "objectClass: inetOrgPerson",
+                "givenName: Aaron",
+                "sn: Atrc",
+                "cn: Aaron Atrc",
+                "initials: AIA",
+                "employeeNumber: 3",
+                "uid: user.3",
+                "mail: user.3@example.com",
+                "userPassword: password",
+                "telephoneNumber: 128-108-4939",
+                "homePhone: 512-782-9966",
+                "pager: 322-646-5118",
+                "mobile: 360-957-9137",
+                "carLicense: 360-957-9137",
+                "street: 25074 Hill Street",
+                "l: Toledo",
+                "st: OR",
+                "postalCode: 55237",
+                "postalAddress: Aaron Atrc$25074 Hill Street$Toledo, OR  55237",
+                "description: This is the description for Aaron Atrc.",
+                "",
+                "dn: uid=user.4,ou=People," + testBaseDN,
+                "objectClass: top",
+                "objectClass: person",
+                "objectClass: organizationalPerson",
+                "objectClass: inetOrgPerson",
+                "givenName: Aartjan",
+                "sn: Aalders",
+                "cn: Aartjan Aalders",
+                "initials: ALA",
+                "employeeNumber: 4",
+                "uid: user.4",
+                "mail: user.4@example.com",
+                "userPassword: password",
+                "telephoneNumber: 981-148-3303",
+                "homePhone: 196-877-2684",
+                "pager: 910-998-4607",
+                "mobile: 123-239-8262",
+                "carLicense: 123-239-8262",
+                "street: 81512 Sunset Street",
+                "l: Chattanooga",
+                "st: WV",
+                "postalCode: 29530",
+                "postalAddress: Aartjan Aalders$81512 Sunset Street$Chattanooga, WV  29530",
+                "description: This is the description for Aartjan Aalders.",
+                "",
+                "dn: uid=user.5,ou=People," + testBaseDN,
+                "objectClass: top",
+                "objectClass: person",
+                "objectClass: organizationalPerson",
+                "objectClass: inetOrgPerson",
+                "givenName: Abagael",
+                "sn: Aasen",
+                "cn: Abagael Aasen",
+                "initials: AKA",
+                "employeeNumber: 5",
+                "uid: user.5",
+                "mail: user.5@example.com",
+                "userPassword: password",
+                "telephoneNumber: 930-493-2391",
+                "homePhone: 078-254-3960",
+                "pager: 281-936-8197",
+                "mobile: 559-822-7712",
+                "carLicense: 559-822-7712",
+                "street: 31988 Central Street",
+                "l: Chico",
+                "st: MS",
+                "postalCode: 20135",
+                "postalAddress: Abagael Aasen$31988 Central Street$Chico, MS  20135",
+                "description: This is the description for Abagael Aasen.",
+                "",
+                "dn: uid=user.6,ou=People," + testBaseDN,
+                "objectClass: top",
+                "objectClass: person",
+                "objectClass: organizationalPerson",
+                "objectClass: inetOrgPerson",
+                "givenName: Abagail",
+                "sn: Abadines",
+                "cn: Abagail Abadines",
+                "initials: AQA",
+                "employeeNumber: 6",
+                "uid: user.6",
+                "mail: user.6@example.com",
+                "userPassword: password",
+                "telephoneNumber: 110-761-3861",
+                "homePhone: 459-123-0553",
+                "pager: 799-151-2688",
+                "mobile: 953-582-7252",
+                "carLicense: 953-582-7252",
+                "street: 60100 Dogwood Street",
+                "l: Hartford",
+                "st: NE",
+                "postalCode: 79353",
+                "postalAddress: Abagail Abadines$60100 Dogwood Street$Hartford, NE  79353",
+                "description: This is the description for Abagail Abadines.",
+                "",
+                "dn: uid=user.7,ou=People," + testBaseDN,
+                "objectClass: top",
+                "objectClass: person",
+                "objectClass: organizationalPerson",
+                "objectClass: inetOrgPerson",
+                "givenName: Abahri",
+                "sn: Abazari",
+                "cn: Abahri Abazari",
+                "initials: AXA",
+                "employeeNumber: 7",
+                "uid: user.7",
+                "mail: user.7@example.com",
+                "userPassword: password",
+                "telephoneNumber: 594-537-4292",
+                "homePhone: 174-724-6390",
+                "pager: 733-217-8194",
+                "mobile: 879-706-0172",
+                "carLicense: 879-706-0172",
+                "street: 77693 Oak Street",
+                "l: Philadelphia",
+                "st: MN",
+                "postalCode: 78550",
+                "postalAddress: Abahri Abazari$77693 Oak Street$Philadelphia, MN  78550",
+                "description: This is the description for Abahri Abazari.",
+                "",
+                "dn: uid=user.8,ou=People," + testBaseDN,
+                "objectClass: top",
+                "objectClass: person",
+                "objectClass: organizationalPerson",
+                "objectClass: inetOrgPerson",
+                "givenName: Abbas",
+                "sn: Abbatantuono",
+                "cn: Abbas Abbatantuono",
+                "initials: AVA",
+                "employeeNumber: 8",
+                "uid: user.8",
+                "mail: user.8@example.com",
+                "userPassword: password",
+                "telephoneNumber: 246-674-8407",
+                "homePhone: 039-769-3372",
+                "pager: 226-950-2371",
+                "mobile: 587-709-2996",
+                "carLicense: 587-709-2996",
+                "street: 23230 Hill Street",
+                "l: Little Rock",
+                "st: AR",
+                "",
+                "dn: uid=user.9,ou=People," + testBaseDN,
+                "objectClass: top",
+                "objectClass: person",
+                "objectClass: organizationalPerson",
+                "objectClass: inetOrgPerson",
+                "givenName: Abbe",
+                "sn: Abbate",
+                "cn: Abbe Abbate",
+                "initials: AWA",
+                "employeeNumber: 9",
+                "uid: user.9",
+                "mail: user.9@example.com",
+                "userPassword: password",
+                "telephoneNumber: 205-805-3357",
+                "homePhone: 770-780-5917",
+                "pager: 537-074-8005",
+                "mobile: 120-204-7597",
+                "carLicense: 120-204-7597",
+                "street: 47952 Center Street",
+                "l: Butte",
+                "st: TN",
+                "postalCode: 69384",
+                "postalAddress: Abbe Abbate$47952 Center Street$Butte, TN  69384",
+                "description: This is the description for Abbe Abbate.",
+                "",
+                "dn: uid=user.10,ou=People," + testBaseDN,
+                "objectClass: top",
+                "objectClass: person",
+                "objectClass: organizationalPerson",
+                "objectClass: inetOrgPerson",
+                "givenName: Abbey",
+                "sn: Abbie",
+                "cn: Abbey Abbie",
+                "initials: AZA",
+                "employeeNumber: 10",
+                "uid: user.10",
+                "mail: user.10@example.com",
+                "userPassword: password",
+                "telephoneNumber: 457-819-0832",
+                "homePhone: 931-305-5452",
+                "pager: 118-165-7194",
+                "mobile: 553-729-5572",
+                "carLicense: 553-729-5572",
+                "street: 54262 Highland Street",
+                "l: Spartanburg",
+                "st: PA",
+                "postalCode: 38151",
+                "postalAddress: Abbey Abbie$54262 Highland Street$Spartanburg, PA  38151",
+                "description: This is the description for Abbey Abbie.",
+                "",
+                "dn: uid=user.539,ou=People," + testBaseDN,
+                "objectClass: top",
+                "objectClass: person",
+                "objectClass: organizationalPerson",
+                "objectClass: inetOrgPerson",
+                "givenName: Ardyth",
+                "sn: Bainton",
+                "cn: Ardyth Bainton",
+                "initials: AIB",
+                "employeeNumber: 539",
+                "uid: user.539",
+                "mail: user.539@example.com",
+                "userPassword: password",
+                "telephoneNumber: 641-433-7404",
+                "homePhone: 524-765-8780",
+                "pager: 985-331-1308",
+                "mobile: 279-423-0188",
+                "carLicense: 279-423-0188",
+                "street: 81170 Taylor Street",
+                "l: Syracuse",
+                "st: WV",
+                "postalCode: 93507",
+                "postalAddress: Ardyth Bainton$81170 Taylor Street$Syracuse, WV  93507",
+                "description: This is the description for Ardyth Bainton.");
+  
+    workEntries = TestCaseUtils.makeEntries(
+                "dn: uid=user.11,ou=People," + testBaseDN,
+                "objectClass: top",
+                "objectClass: person",
+                "objectClass: organizationalPerson",
+                "objectClass: inetOrgPerson",
+                "givenName: Annalee",
+                "sn: Avard",
+                "cn: Annalee Avard",
+                "initials: ANA",
+                "employeeNumber: 11",
+                "uid: user.11",
+                "mail: user.11@example.com",
+                "userPassword: password",
+                "telephoneNumber: 875-335-2712",
+                "homePhone: 181-995-6635",
+                "pager: 586-905-4185",
+                "mobile: 826-857-7592",
+                "carLicense: 826-857-7592",
+                "street: 46168 Mill Street",
+                "l: Charleston",
+                "st: CO",
+                "postalCode: 60948",
+                "postalAddress: Annalee Avard$46168 Mill Street$Charleston, CO  60948",
+                "description: This is the description for Annalee Avard.",
+                "",
+                "dn: uid=user.12,ou=People," + testBaseDN,
+                "objectClass: top",
+                "objectClass: person",
+                "objectClass: organizationalPerson",
+                "objectClass: inetOrgPerson",
+                "givenName: Andaree",
+                "sn: Asawa",
+                "cn: Andaree Asawa",
+                "initials: AEA",
+                "employeeNumber: 12",
+                "uid: user.12",
+                "mail: user.12@example.com",
+                "userPassword: password",
+                "telephoneNumber: 399-788-7334",
+                "homePhone: 798-076-5683",
+                "pager: 034-026-9411",
+                "mobile: 948-743-9197",
+                "carLicense: 948-743-9197",
+                "street: 81028 Forest Street",
+                "l: Wheeling",
+                "st: IA",
+                "postalCode: 60905",
+                "postalAddress: Andaree Asawa$81028 Forest Street$Wheeling, IA  60905",
+                "description: This is the description for Andaree Asawa.");
+  
+    dnToMod = workEntries.get(0).getName();
+    dnToDel = workEntries.get(1).getName();
+    searchDN = entries.get(8).getName();
+    badEntryDN = testBaseDN.child(DN.valueOf("ou=bogus")).child(DN.valueOf("ou=dummy"));
+    backupID = "backupID1";
+  
+    ldifNumberOfEntries = 20;
+    ldifTemplate = new String [] {
+      "define suffix=" + testBaseDN,
+      "define maildomain=" + testBaseDN,
+      "define numusers = " + ldifNumberOfEntries,
+      "",
+      "branch: [suffix]",
+      "objectClass: domain",
+      "",
+      "branch: ou=People,[suffix]",
+      "objectClass: organizationalUnit",
+      "subordinateTemplate: person:[numusers]",
+      "",
+      "template: person",
+      "rdnAttr: uid",
+      "objectClass: top",
+      "objectClass: person",
+      "objectClass: organizationalPerson",
+      "objectClass: inetOrgPerson",
+      "givenName: ABOVE LIMIT",
+      "sn: <last>",
+      "cn: {givenName} {sn}",
+      "initials: {givenName:1}<random:chars:ABCDEFGHIJKLMNOPQRSTUVWXYZ:1>{sn:1}",
+      "employeeNumber: <sequential:0>",
+      "uid: user.{employeeNumber}",
+      "mail: {uid}@[maildomain]",
+      "userPassword: password",
+      "telephoneNumber: <random:telephone>",
+      "homePhone: <random:telephone>",
+      "pager: <random:telephone>",
+      "mobile: <random:telephone>",
+      "street: <random:numeric:5> <file:streets> Street",
+      "l: <file:cities>",
+      "st: <file:states>",
+      "postalCode: <random:numeric:5>",
+      "postalAddress: {cn}${street}${l}, {st}  {postalCode}",
+      "description: This is the description for {cn}.",
+      ""};
+    // Add suffix and barnch entries
+    ldifNumberOfEntries += 2;
+  }
+
+  @AfterClass
+  public void cleanUp() throws Exception
+  {
+    try
+    {
+      backend.removeBackup(backupDirectory, backupID);
+    }
+    catch (DirectoryException ignore)
+    {
+      /*
+       *  Due to test sequencing (Import, backup, restore, export) in case of intermediate failure
+       *  Backup may or may not be present. -> ignore.
+       */
+      Reporter.log(ignore.getMessage(), true);
+    }
+    finally
+    {
+      backend.finalizeBackend();
+      backend = null;
+    }
+  }
+
+  public void testAdd() throws Exception
+  {
+    addEntriesToBackend(topEntries);
+    addEntriesToBackend(entries);
+    addEntriesToBackend(workEntries);
+  }
+
+  /**
+   * Helper for add entries in a backend.
+   * @throws Exception
+   */
+  private void addEntriesToBackend(List<Entry> entries) throws Exception
+  {
+    for (Entry newEntry : entries)
+    {
+      backend.addEntry(newEntry, null);
+    }
+  }
+
+  @Test(expectedExceptions = DirectoryException.class)
+  public void testAddNoParent() throws Exception
+  {
+    Entry newEntry = TestCaseUtils.makeEntry("dn: " + badEntryDN, "objectclass: ou", "");
+    backend.addEntry(newEntry, null);
+  }
+
+  @Test(dependsOnMethods = "testAdd")
+  public void testUtilityAPIs()
+  {
+    assertEquals(backend.getEntryCount(), getTotalNumberOfLDIFEntries());
+    assertFalse(backend.isIndexed(modifyAttribute, IndexType.EQUALITY));
+    assertTrue(backend.isIndexed(DirectoryServer.getAttributeType(backendIndexes[0]), IndexType.EQUALITY));
+  }
+
+  private int getTotalNumberOfLDIFEntries()
+  {
+    return topEntries.size() + entries.size() + workEntries.size();
+  }
+
+  @Test(dependsOnMethods = "testAdd")
+  public void testHasSubordinates() throws Exception
+  {
+    assertEquals(backend.hasSubordinates(testBaseDN), ConditionResult.TRUE,
+        "Base DN should have subordinates.");
+
+    // Testing ConditionResult.UNDEFINED needs either no entry container or a big DIT...
+    assertEquals(backend.hasSubordinates(DN.valueOf("dc=a")), ConditionResult.UNDEFINED,
+        "Subordinates query on unknown baseDN should return UNDEFINED.");
+
+    assertEquals(backend.numSubordinates(testBaseDN, false), 1);
+    assertEquals(backend.numSubordinates(testBaseDN, true), getTotalNumberOfLDIFEntries() - 1, "Wrong DIT count.");
+    assertEquals(backend.hasSubordinates(searchDN), ConditionResult.FALSE,
+        "Leaf entry should not have any subordinates.");
+  }
+  
+  private List<SearchResultEntry> runSearch(SearchRequest request, boolean useInternalConnection) throws Exception
+  {
+    InternalClientConnection conn = getRootConnection();
+
+    if (useInternalConnection)
+    {
+      InternalSearchOperation search = conn.processSearch(request);
+      return search.getSearchEntries();
+    }
+    else
+    {
+      InternalSearchOperation search = new InternalSearchOperation(conn, -1, -1, request);
+      backend.search(new LocalBackendSearchOperation(search));
+      return search.getSearchEntries();
+    }
+  }
+  
+  @Test(dependsOnMethods = { "testAdd", "testModifyEntry", "testRenameEntry", "testDeleteAlreadyDeletedEntry" })
+  public void testBaseSearch() throws Exception
+  {
+    baseSearch(false);
+    baseSearch(true);
+  }
+  
+  private void baseSearch(boolean useInternalConnection) throws Exception
+  {
+    SearchRequest request = newSearchRequest(testBaseDN, SearchScope.BASE_OBJECT, "objectclass=*");
+    List<SearchResultEntry> result = runSearch(request, useInternalConnection);
+    
+    assertEquals(result.size(), 1, "Base Search should return only one Entry");
+    assertEquals(result.get(0).getName(), testBaseDN, "Base Search on the suffix should return the suffix itself");
+  }
+
+  @Test(dependsOnMethods = { "testAdd", "testModifyEntry", "testRenameEntry", "testDeleteAlreadyDeletedEntry" })
+  public void testOneLevelSearch() throws Exception
+  {
+    oneLevelSearch(false);
+    oneLevelSearch(true);
+  }
+
+  private void oneLevelSearch(boolean useInternalConnection) throws Exception
+  {
+    SearchRequest request = newSearchRequest(testBaseDN, SearchScope.SINGLE_LEVEL, "objectclass=*");
+    List<SearchResultEntry> result = runSearch(request, useInternalConnection);
+  
+    assertEquals(result.size(), 1, "One Level search should return a single child entry");
+    SearchResultEntry resEntry = result.get(0);
+    assertEquals(topEntries.get(1).getName(), resEntry.getName(),
+        "One Level search should return the expected child");
+  }
+
+  @Test(dependsOnMethods = { "testAdd", "testModifyEntry", "testRenameEntry", "testDeleteAlreadyDeletedEntry" })
+  public void testSubTreeSearch() throws Exception
+  {
+    subTreeSearch(false);
+    subTreeSearch(true);
+  }
+
+  private void subTreeSearch(boolean useInternalConnection) throws Exception
+  {
+    SearchRequest request = newSearchRequest(testBaseDN, SearchScope.WHOLE_SUBTREE, "objectclass=*");
+    List<SearchResultEntry> result = runSearch(request, useInternalConnection);
+    
+    // Sum of all entry sets minus a delete
+    assertEquals(result.size(), getTotalNumberOfLDIFEntries() - 1,
+        "Subtree search should return a correct number of entries");
+  }
+
+  @Test(dependsOnMethods = { "testAdd", "testModifyEntry", "testRenameEntry", "testDeleteAlreadyDeletedEntry" })
+  public void testUserEntrySearch() throws Exception
+  {
+    userEntrySearch(false);
+    userEntrySearch(true);
+  }
+
+  private void userEntrySearch(boolean useInternalConnection) throws Exception
+  {
+    SearchRequest request = newSearchRequest(searchDN, SearchScope.BASE_OBJECT, "objectclass=*");
+    List<SearchResultEntry> result = runSearch(request, useInternalConnection);
+    
+    assertEquals(result.size(), 1, "User entry search should return a single child entry");
+    assertEquals(searchDN, result.get(0).getName(), "User entry search should return the expected entry");
+  }
+
+  @Test(dependsOnMethods = { "testAdd", "testModifyEntry", "testRenameEntry", "testDeleteAlreadyDeletedEntry" })
+  public void testGetEntry() throws Exception
+  {
+    Assertions.assertThat(getDbEntries(entries)).isEqualTo(entries);
+  }
+  
+  private List<Entry> getDbEntries(List<Entry> entries) throws DirectoryException
+  {
+    List<Entry> result = new ArrayList<Entry>(entries.size());
+    for (Entry currentEntry : entries)
+    {
+      Entry dbEntry = backend.getEntry(currentEntry.getName());
+      result.add(filterOperationalAttributes(dbEntry));
+    }
+    return result;
+  }
+
+  private Entry filterOperationalAttributes(Entry e)
+  {
+    return new Entry(e.getName(), e.getObjectClasses(), e.getUserAttributes(), null);
+  }
+
+  @Test(dependsOnMethods = { "testAdd", "testModifyEntry" })
+  public void testRenameEntry() throws Exception
+  {
+    // Move the entire subtree to another name and move it back.
+    DN prevDN = DN.valueOf("ou=People," + testBaseDN);
+    DN newDN = DN.valueOf("ou=users," + testBaseDN);
+    Entry renameEntry = backend.getEntry(prevDN);
+
+    renameEntry.setDN(newDN);
+    backend.renameEntry(prevDN, renameEntry, null);
+    Entry dbEntry = backend.getEntry(newDN);
+    assertEquals(dbEntry.getName(), newDN, "Renamed entry is missing.");
+
+    renameEntry.setDN(prevDN);
+    backend.renameEntry(newDN, renameEntry, null);
+    dbEntry = backend.getEntry(prevDN);
+    assertEquals(dbEntry.getName(), prevDN, "Original entry has not been renamed");
+  }
+
+  @Test(dependsOnMethods = "testAdd")
+  public void testModifyEntry() throws Exception
+  {
+    Entry oldEntry = workEntries.get(0);
+    Entry newEntry = oldEntry.duplicate(false);
+
+    modifyAttribute = DirectoryServer.getAttributeType("jpegphoto");
+    newEntry.applyModifications(Arrays.asList(new Modification(ADD, create(modifyAttribute, modifyValue))));
+    
+    backend.replaceEntry(oldEntry, newEntry, null);
+    assertTrue(backend.getEntry(oldEntry.getName()).hasValue(modifyAttribute, null, modifyValue));
+  }
+
+  @Test(dependsOnMethods = { "testAdd", "testRenameEntry", "testHasSubordinates" })
+  public void testDeleteEntry() throws Exception
+  {
+    deleteEntry(dnToDel);
+  }
+  
+  @Test(dependsOnMethods = "testDeleteEntry", expectedExceptions = DirectoryException.class)
+  public void testDeleteAlreadyDeletedEntry() throws Exception
+  {
+    deleteEntry(dnToDel);
+  }
+
+  private void deleteEntry(DN dn) throws Exception
+  {
+    backend.deleteEntry(dn, null);
+    assertNull(backend.getEntry(workEntries.get(1).getName()));
+  }
+  
+  @Test(dependsOnMethods = { "testBaseSearch", "testOneLevelSearch", "testSubTreeSearch", "testUserEntrySearch" })
+  public void testImportLDIF() throws Exception
+  {
+    assertTrue(backend.supportsLDIFImport(), "Import not supported");
+
+    // Import wants the backend to be configured but not initialized. Finalizing resets the status.
+    backend.finalizeBackend();
+
+    assertNotNull(ldifTemplate, "Import requires an LDIF template");
+
+    String makeLDIFPath =
+        System.getProperty(TestCaseUtils.PROPERTY_BUILD_ROOT) + File.separator + "resource" + File.separator
+        + "MakeLDIF";
+    TemplateFile templateFile = new TemplateFile(makeLDIFPath, new Random());
+    templateFile.parse(ldifTemplate, null);
+
+    ByteArrayOutputStream rejectedEntries = new ByteArrayOutputStream();
+
+    LDIFImportConfig importConf = new LDIFImportConfig(templateFile);
+    importConf.setInvokeImportPlugins(true);
+    importConf.setClearBackend(true);
+    importConf.writeRejectedEntries(rejectedEntries);
+    try {
+      backend.importLDIF(importConf);
+    }
+    finally {
+      importConf.close();
+    }
+    assertEquals(rejectedEntries.size(), 0, "No entries should be rejected");
+
+    backend.initializeBackend();
+    assertEquals(backend.getEntryCount(), ldifNumberOfEntries, "Not enough entries in DIT.");
+  }
+
+  @Test(dependsOnMethods = {"testImportLDIF"})
+  public void testPreloadEntryCache()
+  {
+    // There is no backend.isPreloadSupported(), so try and if it fails, just let it go.
+    try
+    {
+      backend.preloadEntryCache();
+    }
+    catch (UnsupportedOperationException uoe)
+    {
+      Reporter.log("Skipping unsupported Cache Preload", true);
+    }
+  }
+
+  @Test(dependsOnMethods = "testPreloadEntryCache")
+  public void testBackup() throws Exception
+  {
+    assertEquals(backend.supportsBackup(), true, "Skip Backup");
+    assertNotNull(backupID, "Need to setup a backupID");
+
+    backupPath = TestCaseUtils.createTemporaryDirectory("backup").getAbsolutePath();
+    backupDirectory = new BackupDirectory(backupPath, testBaseDN);
+    BackupConfig backupConf = new BackupConfig(backupDirectory, backupID, false);
+    backupConf.setFilesToBackupFilter(backend.getRootContainer().getStorage().getFilesToBackupFilter());
+
+    backend.createBackup(backupConf);
+  }
+
+  @Test(dependsOnMethods = "testBackup")
+  public void testRestore() throws Exception
+  {
+    assertTrue(backend.supportsRestore(), "Skip Restore");
+
+    backend.restoreBackup(new RestoreConfig(backupDirectory, backupID, true));
+  }
+
+  @Test(dependsOnMethods = "testRestore")
+  public void testExportLDIF() throws Exception
+  {
+    assertTrue(backend.supportsLDIFExport(), "Export not supported");
+
+    ByteArrayOutputStream ldifData = new ByteArrayOutputStream();
+    LDIFExportConfig exportConfig = new LDIFExportConfig(ldifData);
+    exportConfig.setIncludeOperationalAttributes(true);
+    exportConfig.setIncludeVirtualAttributes(true);
+    try
+    {
+      backend.exportLDIF(exportConfig);
+    }
+    finally
+    {
+      exportConfig.close();
+    }
+
+    String ldifString = ldifData.toString();
+    assertEquals(ldifString.contains(testBaseDN.toString()), true, "Export without rootDN");
+    assertEquals(ldifString.contains(searchDN.toString()), true, "Export without rootDN");
+  }
+}
diff --git a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/backends/pluggable/persistit/PersistitTestCase.java b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/backends/pluggable/persistit/PersistitTestCase.java
new file mode 100644
index 0000000..e087d5c
--- /dev/null
+++ b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/backends/pluggable/persistit/PersistitTestCase.java
@@ -0,0 +1,88 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
+ * or http://forgerock.org/license/CDDLv1.0.html.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at legal-notices/CDDLv1_0.txt.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2015 ForgeRock AS
+ */
+
+package org.opends.server.backends.pluggable.persistit;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertTrue;
+
+import org.forgerock.opendj.config.server.ConfigException;
+import org.opends.server.TestCaseUtils;
+import org.opends.server.admin.std.meta.BackendIndexCfgDefn.IndexType;
+import org.opends.server.admin.std.server.BackendIndexCfg;
+import org.opends.server.admin.std.server.PersistitBackendCfg;
+import org.opends.server.admin.std.server.PluggableBackendCfg;
+import org.opends.server.backends.persistit.PitBackend;
+import org.opends.server.backends.pluggable.PluggableBackendImplTestCase;
+import org.opends.server.core.DirectoryServer;
+import org.testng.annotations.Test;
+
+/**
+ * PersistIt Tester.
+ */
+public class PersistitTestCase extends PluggableBackendImplTestCase
+{
+  @Test
+  public void testPersistitCfg() throws Exception
+  {
+    assertTrue(backend.getRootContainer().isValid());
+  }
+
+  @Override
+  public PitBackend createBackend() throws Exception
+  {
+    PluggableBackendCfg backendCfg = createBackendCfg();
+  
+    PitBackend b = new PitBackend();
+    b.setBackendID(backendCfg.getBackendId());
+    b.configureBackend((PersistitBackendCfg)backendCfg);
+    return b;
+  }
+
+  private PluggableBackendCfg createBackendCfg() throws ConfigException
+  {
+    String homeDirName = "pdb_test";
+    PersistitBackendCfg backendCfg = mock(PersistitBackendCfg.class);
+
+    when(backendCfg.getBackendId()).thenReturn("persTest" + homeDirName);
+    when(backendCfg.getDBDirectory()).thenReturn(homeDirName);
+    when(backendCfg.getDBDirectoryPermissions()).thenReturn("755");
+    when(backendCfg.getDBCacheSize()).thenReturn(0L);
+    when(backendCfg.getDBCachePercent()).thenReturn(20);
+    when(backendCfg.isSubordinateIndexesEnabled()).thenReturn(true);
+    when(backendCfg.getBaseDN()).thenReturn(TestCaseUtils.newSortedSet(testBaseDN));
+    when(backendCfg.dn()).thenReturn(testBaseDN);
+    when(backendCfg.listBackendIndexes()).thenReturn(backendIndexes);
+    when(backendCfg.listBackendVLVIndexes()).thenReturn(backendVlvIndexes);
+    
+    BackendIndexCfg indexCfg = mock(BackendIndexCfg.class);
+    when(indexCfg.getIndexType()).thenReturn(TestCaseUtils.newSortedSet(IndexType.PRESENCE, IndexType.EQUALITY));
+    when(indexCfg.getAttribute()).thenReturn(DirectoryServer.getAttributeType(backendIndexes[0]));
+    when(backendCfg.getBackendIndex(backendIndexes[0])).thenReturn(indexCfg);
+    return backendCfg;
+  }
+}

--
Gitblit v1.10.0