/*
 * 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 2008-2010 Sun Microsystems, Inc.
 *      Portions copyright 2011 profiq s.r.o.
 *      Portions Copyright 2014-2015 ForgeRock AS
 */
package org.opends.server.plugins;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;

import org.forgerock.opendj.config.server.ConfigException;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.ModificationType;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.SearchScope;
import org.opends.server.TestCaseUtils;
import org.opends.server.admin.server.AdminTestCaseUtils;
import org.opends.server.admin.std.meta.ReferentialIntegrityPluginCfgDefn;
import org.opends.server.admin.std.server.ReferentialIntegrityPluginCfg;
import org.opends.server.api.Group;
import org.opends.server.api.plugin.PluginType;
import org.opends.server.controls.SubtreeDeleteControl;
import org.opends.server.core.*;
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.types.AttributeType;
import org.opends.server.types.Attributes;
import org.opends.server.types.Control;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import org.opends.server.types.Modification;
import org.opends.server.types.RDN;
import org.opends.server.types.SearchResultEntry;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

import static org.forgerock.opendj.ldap.ModificationType.*;
import static org.opends.server.protocols.internal.InternalClientConnection.*;
import static org.opends.server.protocols.internal.Requests.*;
import static org.opends.server.util.CollectionUtils.*;
import static org.testng.Assert.*;

/**
 * Unit test to test Referential Integrity plugin.
 */
@SuppressWarnings("javadoc")
public class ReferentialIntegrityPluginTestCase extends PluginTestCase  {

  /** Config DNs and attributes. */
  private DN configDN;
  private String dsConfigAttrType="ds-cfg-attribute-type";
  private String dsConfigBaseDN="ds-cfg-base-dn";
  private String dsConfigUpdateInterval=
                               "ds-cfg-update-interval";
  private String dsConfigEnforceIntegrity = "ds-cfg-check-references";
  private String dsConfigAttrFiltMapping =
    "ds-cfg-check-references-filter-criteria";
  private String dsConfigPluginType = "ds-cfg-plugin-type";

  /** Suffixes to use for non-public naming context tests. */
  private String exSuffix="dc=example,dc=com";
  private String testSuffix="o=test";

  /** Dc=example,dc=com entries. */
  private String user1="uid=user.1, ou=People, ou=dept," + exSuffix;
  private String user2="uid=user.2, ou=People, ou=dept," + exSuffix;
  private String user3="uid=user.3, ou=People, ou=dept," + exSuffix;

  /** Test entry to use for rename tests. */
  private String tuser1="uid=user.1, ou=People, ou=dept," + testSuffix;

  /** Old superior, new superior and new RDN for move tree tests. */
  private String newSuperior="ou=moved dept," + exSuffix;
  private String oldSuperior="ou=people, ou=dept," + exSuffix;
  private String newRdn="ou=moved people";

  /** DNs to verfiy that the moved tree test worked. */
  private String user1_moved= "uid=user.1," + newRdn + ',' +newSuperior;
  private String user2_moved= "uid=user.2," + newRdn + ',' + newSuperior;
  private String user3_moved= "uid=user.3," + newRdn + ',' + newSuperior;

  /** DN to test that the rename test worked. */
  private String tuser1_rename=
                              "cn=new user.1, ou=People, ou=dept," + testSuffix;
  private String tuser1_rdn="cn=new user.1";

  /** Test DNs to add to various groups. */
  private String tuser2="uid=user.2, ou=People, ou=dept," + testSuffix;
  private String tuser3="uid=user.3, ou=People, ou=dept," + testSuffix;

  /** Groups to use for member and uniquemember attributes in dc=example, dc=com suffix. */
  private String group = "cn=group, ou=groups," + exSuffix;
  private String ugroup = "cn=group, ou=unique groups," + exSuffix;

  /** DN to use for seeAlso attributes. */
  private String spPerson = "cn=special person, ou=Special People," + exSuffix;

  /** Same as above but for o=test suffix. */
  private String tgroup = "cn=group, ou=groups," + testSuffix;
  private String tugroup = "cn=group, ou=unique groups," + testSuffix;
  private String tspPerson =
                         "cn=special person, ou=Special People," + testSuffix;


  /**
   * Test that a move to a new superior changes the correct entries under
   * the correct suffixes.
   *
   * @throws Exception If an unexpected result is returned.
   */
  @Test
  public void testModDNMoveTree() throws Exception {
    //Add attributes interested in: member, uniquemember, seealso.
    replaceAttrEntry(configDN, dsConfigAttrType,"member");
    addAttrEntry(configDN, dsConfigAttrType,"uniquemember", "seealso");
    //Add suffixes to make referential changes under:
    //o=test, and cn=group, ou=unique groups, dc=example, dc=com
    replaceAttrEntry(configDN, dsConfigBaseDN, testSuffix);
    addAttrEntry(configDN, dsConfigBaseDN, ugroup);
    //Add DNs to groups and special entries
    addAttrEntry(DN.valueOf(tgroup), "member", user1, user2, user3);
    addAttrEntry(DN.valueOf(tugroup), "uniquemember", user1, user2, user3);
    addAttrEntry(DN.valueOf(ugroup), "uniquemember", user1, user2, user3);
    addAttrEntry(DN.valueOf(spPerson), "seealso", user1, user2, user3);
    //Perform the move.
    doModDN(oldSuperior, newRdn, newSuperior);
    //This group under the suffix all DNs should be moved.
    isMember(tgroup, true, user1_moved, user2_moved, user3_moved);
    //This group under a suffix all DNs should be moved.
    isAttributeValueEntry(tugroup, true, "uniquemember",
                          user1_moved, user2_moved, user3_moved);
     //This group under a suffix all DNs should be moved.
    isAttributeValueEntry(ugroup, true, "uniquemember",
                          user1_moved, user2_moved, user3_moved);
     //This group  not under a suffix, old entries should exist.
    isAttributeValueEntry(spPerson, true,"seealso",
                          user1, user2, user3);
  }


  /**
   * Test that a delete subtree changes the correct entries
   * under the correct suffixes.
   *
   * @throws Exception If an unexpected result is returned.
   */
  @Test
  public void testReferentialDeleteTree() throws Exception {
    // Add attributes interested in: member, uniquemember, seealso.
    replaceAttrEntry(configDN, dsConfigAttrType,"member");
    addAttrEntry(configDN, dsConfigAttrType,"uniquemember", "seealso");

    // Add suffixes to make referential changes under:
    // o=test, and cn=group, ou=unique groups, dc=example, dc=com
    replaceAttrEntry(configDN, dsConfigBaseDN, testSuffix);
    addAttrEntry(configDN, dsConfigBaseDN, ugroup);

    // Add DNs to groups and special entries
    addAttrEntry(DN.valueOf(tgroup), "member", user1, user2, user3);
    addAttrEntry(DN.valueOf(tugroup), "uniquemember", user1, user2, user3);
    addAttrEntry(DN.valueOf(ugroup), "uniquemember", user1, user2, user3);
    addAttrEntry(DN.valueOf(spPerson), "seealso", user1, user2, user3);

    // Check group membership before delete.
    isMember(tgroup, true, user1, user2, user3);

    // Check values exist as before delete.
    isAttributeValueEntry(tgroup, true, "member", user1, user2, user3);
    isAttributeValueEntry(tugroup, true, "uniquemember", user1, user2, user3);
    isAttributeValueEntry(ugroup, true, "uniquemember", user1, user2, user3);
    isAttributeValueEntry(spPerson, true, "seealso", user1, user2, user3);

    // Perform the subtree delete.
    deleteSubtree(oldSuperior);

    // Check that the group cache is updated.
    isMember(tgroup, false, user1, user2, user3);

    // Check values exist as before delete.
    isAttributeValueEntry(tgroup, false, "member", user1, user2, user3);
    isAttributeValueEntry(tugroup, false, "uniquemember", user1, user2, user3);
    isAttributeValueEntry(ugroup, false, "uniquemember", user1, user2, user3);

    // This entry not managed by RI.
    isAttributeValueEntry(spPerson, true, "seealso", user1, user2, user3);
  }

  /**
   * Test that a rename  changes the correct entries under
   * the correct suffixes.
   *
   * @throws Exception If an unexpected result is returned.
   */
  @Test
  public void testModDNMoveEntry() throws Exception {
    //Add attributes interested in: member, uniquemember, seealso.
    replaceAttrEntry(configDN, dsConfigAttrType,"member");
    addAttrEntry(configDN, dsConfigAttrType,"uniquemember", "seealso");
    //Add suffixes to make referential changes under:
    //dc=example,dc=com and o=group, ou=unique groups, o=test
    replaceAttrEntry(configDN, dsConfigBaseDN, exSuffix);
    addAttrEntry(configDN, dsConfigBaseDN, tugroup);
    //Add DNs to groups and special entry
    addAttrEntry(DN.valueOf(group), "member", tuser1, tuser2, tuser3);
    addAttrEntry(DN.valueOf(ugroup), "uniquemember", tuser1, tuser2, tuser3);
    addAttrEntry(DN.valueOf(tugroup), "uniquemember", tuser1, tuser2, tuser3);
    addAttrEntry(DN.valueOf(tspPerson), "seealso", tuser1, tuser2, tuser3);
    //Perform rename.
    doModDN(tuser1,tuser1_rdn, null);
    //Verify that the changes were made.
    isMember(group, true, tuser1_rename, tuser2, tuser3);
    isAttributeValueEntry(ugroup, true, "uniquemember",
                          tuser1_rename, tuser2, tuser3);
    isAttributeValueEntry(tugroup, true, "uniquemember",
                          tuser1_rename, tuser2, tuser3);
    isAttributeValueEntry(tspPerson, true,"seealso",
                          tuser1, tuser2, tuser3);
  }


  /**
   * Test a delete using public naming contexts as base DNs.
   *
   * @throws Exception If an unexpected result is returned.
   */
  @Test
  public void testReferentialDelete() throws Exception {
   replaceAttrEntry(configDN, dsConfigAttrType,"member");
   addAttrEntry(DN.valueOf(tgroup), "member", tuser1, tuser2, tuser3);
   deleteEntries(tuser1, tuser2, tuser3);
   isMember(tgroup, false, tuser1, tuser2, tuser3);
  }


  /**
   * Test that delete using public naming context works in both background
   * processing (set interval to 1 and wait 2 seconds) and foreground.
   * The changes are made without restarting the server.
   *
   * @throws Exception If an unexpected result happens.
   */
  @Test
   public void testReferentialDeleteBackGround() throws Exception {
    replaceAttrEntry(configDN, dsConfigAttrType,"member");
    //Set interval to 1 second, this should start the background thread
    //and put the plugin in background mode.
    replaceAttrEntry(configDN, dsConfigUpdateInterval,"1 seconds");
    addAttrEntry(DN.valueOf(tgroup), "member", tuser1, tuser2, tuser3);
    deleteEntries(tuser1, tuser2, tuser3);
    //Wait two seconds and then check the group.
    Thread.sleep(2000);
    isMember(tgroup, false, tuser1, tuser2, tuser3);
    //Change the interval to zero seconds, this should stop the background thread
    replaceAttrEntry(configDN, dsConfigUpdateInterval,"0 seconds");
    addEntries(tuser1, tuser2, tuser3);
    addAttrEntry(DN.valueOf(tgroup), "member", tuser1, tuser2, tuser3);
    deleteEntries(tuser1, tuser2, tuser3);
    //Don't wait, the changes should be there.
    isMember(tgroup, false, tuser1, tuser2, tuser3);
   }

  /**
   * Test delete using multiple attribute types and public naming contexts.
   *
   * @throws Exception If an unexpected result happened.
   */
  @Test
   public void testReferentialDeleteAttrs() throws Exception {
    replaceAttrEntry(configDN, dsConfigAttrType,"member");
    addAttrEntry(configDN, dsConfigAttrType,"uniquemember", "seealso");
    addAttrEntry(DN.valueOf(tgroup), "member", tuser1, tuser2, tuser3);
    addAttrEntry(DN.valueOf(tugroup), "uniquemember", tuser1, tuser2, tuser3);
    addAttrEntry(DN.valueOf(tspPerson), "seealso", tuser1, tuser2, tuser3);
    deleteEntries(tuser1, tuser2, tuser3);
    isMember(tgroup, false, tuser1, tuser2, tuser3);
    isAttributeValueEntry(tugroup, false, "uniquemember", tuser1, tuser2, tuser3);
    isAttributeValueEntry(tspPerson, false,"seealso", tuser1, tuser2, tuser3);
   }

  /**
   * Check delete with multiple attribute types and multiple suffixes.
   *
   * @throws Exception If an unexpected result happened.
   */
  @Test
   public void testReferentialDeleteAttrsSuffix() throws Exception {
    replaceAttrEntry(configDN, dsConfigAttrType,"member");
    addAttrEntry(configDN, dsConfigAttrType,"uniquemember", "seealso");
    replaceAttrEntry(configDN, dsConfigBaseDN, exSuffix);
    addAttrEntry(configDN, dsConfigBaseDN, tugroup);
    addAttrEntry(DN.valueOf(group), "member", tuser1, tuser2, tuser3);
    addAttrEntry(DN.valueOf(ugroup), "uniquemember", tuser1, tuser2, tuser3);
    addAttrEntry(DN.valueOf(tugroup), "uniquemember", tuser1, tuser2, tuser3);
    addAttrEntry(DN.valueOf(tspPerson), "seealso", tuser1, tuser2, tuser3);
    deleteEntries(tuser1, tuser2, tuser3);
    isMember(group, false, tuser1, tuser2, tuser3);
    isAttributeValueEntry(ugroup, true, "uniquemember",
                          tuser1, tuser2, tuser3);
    isAttributeValueEntry(tugroup, false, "uniquemember",
                          tuser1, tuser2, tuser3);
    isAttributeValueEntry(tspPerson, true,"seealso",
                          tuser1, tuser2, tuser3);
   }

  /**
   * Retrieves a set of valid configuration entries that may be used to
   * initialize the plugin.
   *
   * @return An array of config entries.
   *
   * @throws  Exception  If an unexpected problem occurs.
   *
   */
  @DataProvider(name = "validConfigs")
  public Object[][] getValidConfigs()
          throws Exception
  {
    List<Entry> entries = TestCaseUtils.makeEntries(
            "dn: cn=Referential Integrity,cn=Plugins,cn=config",
            "objectClass: top",
            "objectClass: ds-cfg-plugin",
            "objectClass: ds-cfg-referential-integrity-plugin",
            "cn: Referential Integrity",
            "ds-cfg-java-class: org.opends.server.plugins.ReferentialIntegrityPlugin",
            "ds-cfg-enabled: true",
            "ds-cfg-plugin-type: postOperationDelete",
            "ds-cfg-plugin-type: postOperationModifyDN",
            "ds-cfg-plugin-type: subordinateModifyDN",
            "ds-cfg-attribute-type: member",
            "",
            "dn: cn=Referential Integrity,cn=Plugins,cn=config",
            "objectClass: top",
            "objectClass: ds-cfg-plugin",
            "objectClass: ds-cfg-referential-integrity-plugin",
            "cn: Referential Integrity",
            "ds-cfg-java-class: org.opends.server.plugins.ReferentialIntegrityPlugin",
            "ds-cfg-enabled: true",
            "ds-cfg-plugin-type: postOperationDelete",
            "ds-cfg-plugin-type: postOperationModifyDN",
            "ds-cfg-plugin-type: subordinateModifyDN",
            "ds-cfg-attribute-type: member",
            "ds-cfg-attribute-type: uniqueMember",
            "ds-cfg-attribute-type: seeAlso",
            "",
            "dn: cn=Referential Integrity,cn=Plugins,cn=config",
            "objectClass: top",
            "objectClass: ds-cfg-plugin",
            "objectClass: ds-cfg-referential-integrity-plugin",
            "cn: Referential Integrity",
            "ds-cfg-java-class: org.opends.server.plugins.ReferentialIntegrityPlugin",
            "ds-cfg-enabled: true",
            "ds-cfg-plugin-type: postOperationDelete",
            "ds-cfg-plugin-type: postOperationModifyDN",
            "ds-cfg-plugin-type: subordinateModifyDN",
            "ds-cfg-attribute-type: member",
            "ds-cfg-attribute-type: uniqueMember",
            "ds-cfg-attribute-type: seeAlso",
            "ds-cfg-base-dn: ou=people, dc=example,dc=com",
            "ds-cfg-base-dn: ou=dept, dc=example,dc=com",
            "ds-cfg-base-dn: ou=people, o=test",
            "",
            "dn: cn=Referential Integrity,cn=Plugins,cn=config",
            "objectClass: top",
            "objectClass: ds-cfg-plugin",
            "objectClass: ds-cfg-referential-integrity-plugin",
            "cn: Referential Integrity",
            "ds-cfg-java-class: org.opends.server.plugins.ReferentialIntegrityPlugin",
            "ds-cfg-enabled: true",
            "ds-cfg-plugin-type: postOperationDelete",
            "ds-cfg-plugin-type: postOperationModifyDN",
            "ds-cfg-plugin-type: subordinateModifyDN",
            "ds-cfg-attribute-type: member",
            "ds-cfg-attribute-type: uniqueMember",
            "ds-cfg-attribute-type: seeAlso",
            "ds-cfg-base-dn: ou=people, dc=example,dc=com",
            "ds-cfg-base-dn: ou=dept, dc=example,dc=com",
            "ds-cfg-base-dn: ou=people, o=test",
            "ds-cfg-update-interval: 300 seconds",
            "",
            "dn: cn=Referential Integrity,cn=Plugins,cn=config",
            "objectClass: top",
            "objectClass: ds-cfg-plugin",
            "objectClass: ds-cfg-referential-integrity-plugin",
            "cn: Referential Integrity",
            "ds-cfg-java-class: org.opends.server.plugins.ReferentialIntegrityPlugin",
            "ds-cfg-enabled: true",
            "ds-cfg-plugin-type: postOperationDelete",
            "ds-cfg-plugin-type: postOperationModifyDN",
            "ds-cfg-plugin-type: subordinateModifyDN",
            "ds-cfg-attribute-type: member",
            "ds-cfg-attribute-type: uniqueMember",
            "ds-cfg-attribute-type: seeAlso",
            "ds-cfg-base-dn: ou=people, dc=example,dc=com",
            "ds-cfg-base-dn: ou=dept, dc=example,dc=com",
            "ds-cfg-base-dn: ou=people, o=test",
            "ds-cfg-update-interval: 300 seconds",
            "ds-cfg-log-file: logs/test",
            "",
            "dn: cn=Referential Integrity,cn=Plugins,cn=config",
            "objectClass: top",
            "objectClass: ds-cfg-plugin",
            "objectClass: ds-cfg-referential-integrity-plugin",
            "cn: Referential Integrity",
            "ds-cfg-java-class: org.opends.server.plugins.ReferentialIntegrityPlugin",
            "ds-cfg-enabled: true",
            "ds-cfg-plugin-type: postOperationModifyDN",
            "ds-cfg-plugin-type: subordinateModifyDN",
            "ds-cfg-attribute-type: member",
            "ds-cfg-attribute-type: uniqueMember",
            "ds-cfg-attribute-type: seeAlso",
            "ds-cfg-base-dn: ou=people, dc=example,dc=com",
            "ds-cfg-base-dn: ou=dept, dc=example,dc=com",
            "ds-cfg-base-dn: ou=people, o=test",
            "ds-cfg-update-interval: 300 seconds",
            "ds-cfg-log-file: logs/test",
            "",
            // check-references, default
            "dn: cn=Referential Integrity,cn=Plugins,cn=config",
            "objectClass: top",
            "objectClass: ds-cfg-plugin",
            "objectClass: ds-cfg-referential-integrity-plugin",
            "cn: Referential Integrity",
            "ds-cfg-java-class: org.opends.server.plugins.ReferentialIntegrityPlugin",
            "ds-cfg-enabled: true",
            "ds-cfg-plugin-type: postOperationDelete",
            "ds-cfg-plugin-type: postOperationModifyDN",
            "ds-cfg-plugin-type: subordinateModifyDN",
            "ds-cfg-plugin-type: preOperationAdd",
            "ds-cfg-plugin-type: preOperationModify",
            "ds-cfg-attribute-type: member",
            "ds-cfg-check-references: false",
            "",
            // check-references enabled
            "dn: cn=Referential Integrity,cn=Plugins,cn=config",
            "objectClass: top",
            "objectClass: ds-cfg-plugin",
            "objectClass: ds-cfg-referential-integrity-plugin",
            "cn: Referential Integrity",
            "ds-cfg-java-class: org.opends.server.plugins.ReferentialIntegrityPlugin",
            "ds-cfg-enabled: true",
            "ds-cfg-plugin-type: postOperationDelete",
            "ds-cfg-plugin-type: postOperationModifyDN",
            "ds-cfg-plugin-type: subordinateModifyDN",
            "ds-cfg-plugin-type: preOperationAdd",
            "ds-cfg-plugin-type: preOperationModify",
            "ds-cfg-attribute-type: member",
            "ds-cfg-base-dn: o=test",
            "ds-cfg-base-dn: dc=example, dc=com",
            "ds-cfg-check-references: true",
            "",
            // check-references enabled, check-references-filter-criteria set
            "dn: cn=Referential Integrity,cn=Plugins,cn=config",
            "objectClass: top",
            "objectClass: ds-cfg-plugin",
            "objectClass: ds-cfg-referential-integrity-plugin",
            "cn: Referential Integrity",
            "ds-cfg-java-class: org.opends.server.plugins.ReferentialIntegrityPlugin",
            "ds-cfg-enabled: true",
            "ds-cfg-plugin-type: postOperationDelete",
            "ds-cfg-plugin-type: postOperationModifyDN",
            "ds-cfg-plugin-type: subordinateModifyDN",
            "ds-cfg-plugin-type: preOperationAdd",
            "ds-cfg-plugin-type: preOperationModify",
            "ds-cfg-attribute-type: member",
            "ds-cfg-base-dn: o=test",
            "ds-cfg-base-dn: dc=example, dc=com",
            "ds-cfg-check-references: true",
            "ds-cfg-check-references-filter-criteria: member:(objectclass=person)",
            "",
            // check-references disabled, check-references-filter-criteria set
            "dn: cn=Referential Integrity,cn=Plugins,cn=config",
            "objectClass: top",
            "objectClass: ds-cfg-plugin",
            "objectClass: ds-cfg-referential-integrity-plugin",
            "cn: Referential Integrity",
            "ds-cfg-java-class: org.opends.server.plugins.ReferentialIntegrityPlugin",
            "ds-cfg-enabled: true",
            "ds-cfg-plugin-type: postOperationDelete",
            "ds-cfg-plugin-type: postOperationModifyDN",
            "ds-cfg-plugin-type: subordinateModifyDN",
            "ds-cfg-plugin-type: preOperationAdd",
            "ds-cfg-plugin-type: preOperationModify",
            "ds-cfg-attribute-type: member",
            "ds-cfg-check-references: false",
            "ds-cfg-check-references-filter-criteria: member:(objectclass=person)"
    );
    Object[][] array = new Object[entries.size()][1];
    for (int i=0; i < array.length; i++)
    {
      array[i] = new Object[] { entries.get(i) };
    }

    return array;
  }


  /**
   * Tests the process of initializing the server with valid configurations.
   *
   * @param  e  The configuration entry to use for the initialization.
   *
   * @throws  Exception  If an unexpected problem occurs.
   *
   */
  @Test(dataProvider = "validConfigs")
  public void testInitializeWithValidConfigs(Entry e)
          throws Exception
  {
    HashSet<PluginType> pluginTypes = TestCaseUtils.getPluginTypes(e);
    ReferentialIntegrityPluginCfg configuration =
            AdminTestCaseUtils.getConfiguration(
                    ReferentialIntegrityPluginCfgDefn.getInstance(), e);
    ReferentialIntegrityPlugin plugin = new ReferentialIntegrityPlugin();
    plugin.initializePlugin(pluginTypes, configuration);
    plugin.finalizePlugin();
  }

  /**
   * Retrieves a set of invalid configuration entries that may be used to
   * initialize the plugin.
   *
   * @return An array of config entries.
   *
   * @throws  Exception  If an unexpected problem occurs.
   *
   */
  @DataProvider(name = "invalidConfigs")
  public Object[][] getInValidConfigs()
          throws Exception
  {
    List<Entry> entries = TestCaseUtils.makeEntries(
            "dn: cn=Referential Integrity,cn=Plugins,cn=config",
            "objectClass: top",
            "objectClass: ds-cfg-plugin",
            "objectClass: ds-cfg-referential-integrity-plugin",
            "cn: Referential Integrity",
            "ds-cfg-java-class: org.opends.server.plugins.ReferentialIntegrityPlugin",
            "ds-cfg-enabled: true",
            "ds-cfg-plugin-type: postOperationDelete",
            "ds-cfg-plugin-type: postOperationModifyDN",
            "ds-cfg-plugin-type: subordinateModifyDN",
            "ds-cfg-attribute-type: cn",
            "",
            "dn: cn=Referential Integrity,cn=Plugins,cn=config",
            "objectClass: top",
            "objectClass: ds-cfg-plugin",
            "objectClass: ds-cfg-referential-integrity-plugin",
            "cn: Referential Integrity",
            "ds-cfg-java-class: org.opends.server.plugins.ReferentialIntegrityPlugin",
            "ds-cfg-enabled: true",
            "ds-cfg-plugin-type: postOperationDelete",
            "ds-cfg-plugin-type: postOperationModifyDN",
            "ds-cfg-plugin-type: subordinateModifyDN",
            "ds-cfg-attribute-type: member",
            "ds-cfg-attribute-type: uniqueMember",
            "ds-cfg-attribute-type: sn",
            "",
            "dn: cn=Referential Integrity,cn=Plugins,cn=config",
            "objectClass: top",
            "objectClass: ds-cfg-plugin",
            "objectClass: ds-cfg-referential-integrity-plugin",
            "cn: Referential Integrity",
            "ds-cfg-java-class: org.opends.server.plugins.ReferentialIntegrityPlugin",
            "ds-cfg-enabled: true",
            "ds-cfg-plugin-type: postOperationDelete",
            "ds-cfg-plugin-type: preOperationModifyDN",
            "ds-cfg-plugin-type: subordinateModifyDN",
            "ds-cfg-attribute-type: member",
            "ds-cfg-attribute-type: uniqueMember",
            "ds-cfg-attribute-type: seeAlso",
            "ds-cfg-base-dn: ou=people, dc=example,dc=com",
            "ds-cfg-base-dn: ou=dept, dc=example,dc=com",
            "ds-cfg-base-dn: baddn",
            "",
            "dn: cn=Referential Integrity,cn=Plugins,cn=config",
            "objectClass: top",
            "objectClass: ds-cfg-plugin",
            "objectClass: ds-cfg-referential-integrity-plugin",
            "cn: Referential Integrity",
            "ds-cfg-java-class: org.opends.server.plugins.ReferentialIntegrityPlugin",
            "ds-cfg-enabled: true",
            "ds-cfg-plugin-type: postOperationDelete",
            "ds-cfg-plugin-type: postOperationModifyDN",
            "ds-cfg-plugin-type: subordinateModifyDN",
            "ds-cfg-attribute-type: member",
            "ds-cfg-attribute-type: uniqueMember",
            "ds-cfg-attribute-type: seeAlso",
            "ds-cfg-base-dn: ou=people, dc=example,dc=com",
            "ds-cfg-base-dn: ou=dept, dc=example,dc=com",
            "ds-cfg-base-dn: ou=people, o=test",
            "ds-cfg-update-interval: -5 seconds",
            "",
            "dn: cn=Referential Integrity,cn=Plugins,cn=config",
            "objectClass: top",
            "objectClass: ds-cfg-plugin",
            "objectClass: ds-cfg-referential-integrity-plugin",
            "cn: Referential Integrity",
            "ds-cfg-java-class: org.opends.server.plugins.ReferentialIntegrityPlugin",
            "ds-cfg-enabled: true",
            "ds-cfg-plugin-type: postOperationDelete",
            "ds-cfg-plugin-type: postOperationModifyDN",
            "ds-cfg-plugin-type: subordinateModifyDN",
            "ds-cfg-attribute-type: member",
            "ds-cfg-attribute-type: uniqueMember",
            "ds-cfg-attribute-type: notanattribute",
            "ds-cfg-base-dn: ou=people, dc=example,dc=com",
            "ds-cfg-base-dn: ou=dept, dc=example,dc=com",
            "ds-cfg-base-dn: ou=people, o=test",
            "ds-cfg-update-interval: 300 seconds",
            "ds-cfg-log-file: logs/test",
            "",
            "dn: cn=Referential Integrity,cn=Plugins,cn=config",
            "objectClass: top",
            "objectClass: ds-cfg-plugin",
            "objectClass: ds-cfg-referential-integrity-plugin",
            "cn: Referential Integrity",
            "ds-cfg-java-class: org.opends.server.plugins.ReferentialIntegrityPlugin",
            "ds-cfg-enabled: true",
            "ds-cfg-plugin-type: postOperationModifyDN",
            "ds-cfg-plugin-type: subordinateModifyDN",
            "ds-cfg-attribute-type: member",
            "ds-cfg-attribute-type: uniqueMember",
            "ds-cfg-attribute-type: seeAlso",
            "ds-cfg-base-dn: ou=people, dc=example,dc=com",
            "ds-cfg-base-dn: ou=dept, dc=example,dc=com",
            "ds-cfg-base-dn: ou=people, o=test",
            "ds-cfg-update-interval: 300 seconds",
            "ds-cfg-log-file: /hopefully/doesn't/file/exist",
            "",
            // check-references bad value
            "dn: cn=Referential Integrity,cn=Plugins,cn=config",
            "objectClass: top",
            "objectClass: ds-cfg-plugin",
            "objectClass: ds-cfg-referential-integrity-plugin",
            "cn: Referential Integrity",
            "ds-cfg-java-class: org.opends.server.plugins.ReferentialIntegrityPlugin",
            "ds-cfg-enabled: true",
            "ds-cfg-plugin-type: postOperationDelete",
            "ds-cfg-plugin-type: postOperationModifyDN",
            "ds-cfg-plugin-type: subordinateModifyDN",
            "ds-cfg-plugin-type: preOperationAdd",
            "ds-cfg-plugin-type: preOperationModify",
            "ds-cfg-attribute-type: member",
            "ds-cfg-base-dn: o=test",
            "ds-cfg-base-dn: dc=example, dc=com",
            "ds-cfg-check-references: bad",
            "ds-cfg-check-references-filter-criteria: member:(objectclass=person)",
            "",
            // check-references enabled, attrbute not on the list
            "dn: cn=Referential Integrity,cn=Plugins,cn=config",
            "objectClass: top",
            "objectClass: ds-cfg-plugin",
            "objectClass: ds-cfg-referential-integrity-plugin",
            "cn: Referential Integrity",
            "ds-cfg-java-class: org.opends.server.plugins.ReferentialIntegrityPlugin",
            "ds-cfg-enabled: true",
            "ds-cfg-plugin-type: postOperationDelete",
            "ds-cfg-plugin-type: postOperationModifyDN",
            "ds-cfg-plugin-type: subordinateModifyDN",
            "ds-cfg-plugin-type: preOperationAdd",
            "ds-cfg-plugin-type: preOperationModify",
            "ds-cfg-attribute-type: member",
            "ds-cfg-base-dn: o=test",
            "ds-cfg-base-dn: dc=example, dc=com",
            "ds-cfg-check-references: true",
            "ds-cfg-check-references-filter-criteria: manager:(objectclass=person)",
            "",
            // check-references true, bad filter
            "dn: cn=Referential Integrity,cn=Plugins,cn=config",
            "objectClass: top",
            "objectClass: ds-cfg-plugin",
            "objectClass: ds-cfg-referential-integrity-plugin",
            "cn: Referential Integrity",
            "ds-cfg-java-class: org.opends.server.plugins.ReferentialIntegrityPlugin",
            "ds-cfg-enabled: true",
            "ds-cfg-plugin-type: postOperationDelete",
            "ds-cfg-plugin-type: postOperationModifyDN",
            "ds-cfg-plugin-type: subordinateModifyDN",
            "ds-cfg-plugin-type: preOperationAdd",
            "ds-cfg-plugin-type: preOperationModify",
            "ds-cfg-attribute-type: member",
            "ds-cfg-base-dn: o=test",
            "ds-cfg-base-dn: dc=example, dc=com",
            "ds-cfg-check-references: true",
            "ds-cfg-check-references-filter-criteria: member:bad",
            "",
            // check-references true, attr-filt bad format
            "dn: cn=Referential Integrity,cn=Plugins,cn=config",
            "objectClass: top",
            "objectClass: ds-cfg-plugin",
            "objectClass: ds-cfg-referential-integrity-plugin",
            "cn: Referential Integrity",
            "ds-cfg-java-class: org.opends.server.plugins.ReferentialIntegrityPlugin",
            "ds-cfg-enabled: true",
            "ds-cfg-plugin-type: postOperationDelete",
            "ds-cfg-plugin-type: postOperationModifyDN",
            "ds-cfg-plugin-type: subordinateModifyDN",
            "ds-cfg-plugin-type: preOperationAdd",
            "ds-cfg-plugin-type: preOperationModify",
            "ds-cfg-attribute-type: member",
            "ds-cfg-base-dn: o=test",
            "ds-cfg-base-dn: dc=example, dc=com",
            "ds-cfg-check-references: true",
            "ds-cfg-check-references-filter-criteria: bad",
            "",
            // check-references true, no filter
            "dn: cn=Referential Integrity,cn=Plugins,cn=config",
            "objectClass: top",
            "objectClass: ds-cfg-plugin",
            "objectClass: ds-cfg-referential-integrity-plugin",
            "cn: Referential Integrity",
            "ds-cfg-java-class: org.opends.server.plugins.ReferentialIntegrityPlugin",
            "ds-cfg-enabled: true",
            "ds-cfg-plugin-type: postOperationDelete",
            "ds-cfg-plugin-type: postOperationModifyDN",
            "ds-cfg-plugin-type: subordinateModifyDN",
            "ds-cfg-plugin-type: preOperationAdd",
            "ds-cfg-plugin-type: preOperationModify",
            "ds-cfg-attribute-type: member",
            "ds-cfg-base-dn: o=test",
            "ds-cfg-base-dn: dc=example, dc=com",
            "ds-cfg-check-references: true",
            "ds-cfg-check-references-filter-criteria: member:",
            "",
            // check-references true, null:null
            "dn: cn=Referential Integrity,cn=Plugins,cn=config",
            "objectClass: top",
            "objectClass: ds-cfg-plugin",
            "objectClass: ds-cfg-referential-integrity-plugin",
            "cn: Referential Integrity",
            "ds-cfg-java-class: org.opends.server.plugins.ReferentialIntegrityPlugin",
            "ds-cfg-enabled: true",
            "ds-cfg-plugin-type: postOperationDelete",
            "ds-cfg-plugin-type: postOperationModifyDN",
            "ds-cfg-plugin-type: subordinateModifyDN",
            "ds-cfg-plugin-type: preOperationAdd",
            "ds-cfg-plugin-type: preOperationModify",
            "ds-cfg-attribute-type: member",
            "ds-cfg-base-dn: o=test",
            "ds-cfg-base-dn: dc=example, dc=com",
            "ds-cfg-check-references: true",
            "ds-cfg-check-references-filter-criteria: :"
    );
    Object[][] array = new Object[entries.size()][1];
    for (int i=0; i < array.length; i++)
    {
      array[i] = new Object[] { entries.get(i) };
    }

    return array;
  }


  /**
   * Tests the process of initializing the server with inValid configurations.
   *
   * @param  e  The configuration entry to use for the initialization.
   *
   * @throws  Exception  If an unexpected problem occurs.
   *
   */
  @Test(dataProvider = "invalidConfigs",
        expectedExceptions = { ConfigException.class })
  public void testInitializeWithInValidConfigs(Entry e)
          throws Exception
  {
    HashSet<PluginType> pluginTypes = TestCaseUtils.getPluginTypes(e);
    ReferentialIntegrityPluginCfg configuration =
            AdminTestCaseUtils.getConfiguration(
                    ReferentialIntegrityPluginCfgDefn.getInstance(), e);
    ReferentialIntegrityPlugin plugin = new ReferentialIntegrityPlugin();
    plugin.initializePlugin(pluginTypes, configuration);
    plugin.finalizePlugin();
  }

  /**
   * Ensures that the Directory Server is running.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @BeforeClass
  public void startServer() throws Exception
  {
    TestCaseUtils.startServer();
    configDN= DN.valueOf("cn=Referential Integrity ,cn=Plugins,cn=config");
  }

  /**
   * Clears configuration information before each method run and re-adds
   * entries.
   *
   * @throws Exception If an unexpected problem occurs.
   */
  @BeforeMethod
  public void clearConfigEntries() throws Exception {
    deleteAttrsEntry(configDN, dsConfigBaseDN);
    deleteAttrsEntry(configDN, dsConfigEnforceIntegrity);
    deleteAttrsEntry(configDN, dsConfigAttrFiltMapping);
    //Hopefully put an attribute type there that won't impact the rest of the
    //unit tests.
    replaceAttrEntry(configDN, dsConfigAttrType,"seeAlso");
    replaceAttrEntry(configDN, dsConfigUpdateInterval,"0 seconds");
    TestCaseUtils.initializeTestBackend(true);
    addTestEntries("o=test");
    TestCaseUtils.clearBackend("userRoot", "dc=example,dc=com");
    addTestEntries("dc=example,dc=com");
  }


  /**
   * Clears things up after the unit test is completed.
   *
   * @throws Exception If an unexpected problem occurs.
   *
   */
  @AfterClass
  public void tearDown() throws Exception {
     deleteAttrsEntry(configDN, dsConfigBaseDN);
     deleteAttrsEntry(configDN, dsConfigEnforceIntegrity);
     deleteAttrsEntry(configDN, dsConfigAttrFiltMapping);
    //Hopefully put an attribute type there that won't impact the rest of the
    //unit tests.
    replaceAttrEntry(configDN, dsConfigAttrType,"seeAlso");
    replaceAttrEntry(configDN, dsConfigUpdateInterval,"0 seconds");
    TestCaseUtils.clearBackend("userRoot");
  }

  /**
   * Create entries under the specified suffix and add them to the server.
   * The character argument is used to make the mail attribute unique.
   *
   * @param suffix  The suffix to use in building the entries.
   *
   * @throws Exception If a problem occurs.
   *
   */
  private void addTestEntries(String suffix) throws Exception {
    TestCaseUtils.addEntries(
            "dn: ou=dept," + suffix,
            "objectClass: top",
            "objectClass: organizationalUnit",
            "ou: dept",
            "aci: (targetattr= \"*\")" +
                    "(version 3.0; acl \"allow all\";" +
                    "allow(all) userdn=\"ldap:///anyone\";)",
            "",
            "dn: ou=moved dept," + suffix,
            "objectClass: top",
            "objectClass: organizationalUnit",
            "ou: moved dept",
            "aci: (targetattr= \"*\")" +
                    "(version 3.0; acl \"allow all\";" +
                    "allow(all) userdn=\"ldap:///anyone\";)",
            "",
            "dn: ou=groups," + suffix,
            "objectClass: top",
            "objectClass: organizationalUnit",
            "ou: groups",
            "aci: (targetattr= \"*\")" +
                    "(version 3.0; acl \"allow all\";" +
                    "allow(all) userdn=\"ldap:///anyone\";)",
            "",
            "dn: ou=unique Groups," + suffix,
            "objectClass: top",
            "objectClass: organizationalUnit",
            "ou: unique Groups",
            "aci: (targetattr= \"*\")" +
                    "(version 3.0; acl \"allow all\";" +
                    "allow(all) userdn=\"ldap:///anyone\";)",
            "",
            "dn: ou=People, ou=dept," + suffix,
            "objectClass: top",
            "objectClass: organizationalUnit",
            "ou: People",
            "",
            "dn: ou=Special People," + suffix,
            "objectClass: top",
            "objectClass: organizationalUnit",
            "ou: Special People",
            "aci: (targetattr= \"*\")" +
                    "(version 3.0; acl \"allow all\";" +
                    "allow(all) userdn=\"ldap:///anyone\";)",
            "",
            "dn: cn=special person, ou=Special People," + suffix,
            "objectClass: top",
            "objectClass: person",
            "objectClass: organizationalPerson",
            "objectClass: inetOrgPerson",
            "uid: 1",
            "givenName: User",
            "sn: 1",
            "cn: special person",
            "userPassword: password",
            "mail: user1" +"@test",
            "employeeNumber: 1",
            "mobile: 1-111-1234",
            "pager: 1-111-5678",
            "description: Use for seeAlso attribute",
            "",
            "dn: cn=group, ou=groups," + suffix,
            "objectClass: top",
            "objectClass: groupOfNames",
            "cn: group",
            "aci: (targetattr= \"*\")" +
                    "(version 3.0; acl \"allow all\";" +
                    "allow(all) userdn=\"ldap:///anyone\";)",
            "",
            "dn: cn=group, ou=unique groups," + suffix,
            "objectClass: top",
            "objectClass: groupOfUniqueNames",
            "cn: group",
            "aci: (targetattr= \"*\")" +
                    "(version 3.0; acl \"allow all\";" +
                    "allow(all) userdn=\"ldap:///anyone\";)",
            "",
            "dn: uid=user.1, ou=People, ou=dept," + suffix,
            "objectClass: top",
            "objectClass: person",
            "objectClass: organizationalPerson",
            "objectClass: inetOrgPerson",
            "uid: 1",
            "givenName: User",
            "sn: 1",
            "cn: User 1",
            "userPassword: password",
            "mail: user1" +"@test",
            "employeeNumber: 1",
            "mobile: 1-111-1234",
            "pager: 1-111-5678",
            "telephoneNumber: 1-111-9012",
            "",
            "dn: uid=user.2, ou=People, ou=dept," + suffix,
            "objectClass: top",
            "objectClass: person",
            "objectClass: organizationalPerson",
            "objectClass: inetOrgPerson",
            "uid: 2",
            "givenName: User",
            "sn: 2",
            "cn: User 2",
            "mail: user2" + "@test",
            "userPassword: password",
            "employeeNumber: 2",
            "mobile: 1-222-1234",
            "pager: 1-222-5678",
            "telephoneNumber: 1-222-9012",
            "",
            "dn: uid=user.3, ou=People, ou=dept," + suffix,
            "objectClass: top",
            "objectClass: person",
            "objectClass: organizationalPerson",
            "objectClass: inetOrgPerson",
            "uid: 3",
            "givenName: User",
            "sn: 3",
            "cn: User 3",
            "mail: user3" + "@test",
            "userPassword: password",
            "employeeNumber: 3",
            "mobile: 1-333-1234",
            "pager: 1-333-5678",
            "telephoneNumber: 1-333-9012",
            "",
            "dn: uid=user.4, ou=People, ou=dept," + suffix,
            "objectClass: top",
            "objectClass: person",
            "objectClass: organizationalPerson",
            "objectClass: inetOrgPerson",
            "uid: 4",
            "givenName: User",
            "sn: 4",
            "cn: User 4",
            "mail: user4" + "@test",
            "userPassword: password",
            "employeeNumber: 4",
            "mobile: 1-444-1234",
            "pager: 1-444-5678",
            "telephoneNumber: 1-444-9012",
            "",
            "dn: uid=user.5, ou=People, ou=dept," + suffix,
            "objectClass: top",
            "objectClass: person",
            "objectClass: organizationalPerson",
            "objectClass: inetOrgPerson",
            "uid: 5",
            "givenName: User",
            "sn: 5",
            "cn: User 5",
            "mail: user5" + "@test",
            "userPassword: password",
            "employeeNumber: 5",
            "mobile: 1-555-1234",
            "pager: 1-555-5678",
            "telephoneNumber: 1-555-9012"
    );
  }

  /**
   * Add specified attr type and type values to the entry specified by dn.
   *
   * @param dn The dn of the entry to add the attribute and values to.
   * @param attrName The attribute type to add the values to.
   * @param attrValStrings The values to add to the entry.
   */
  private ModifyOperation
  addAttrEntry(DN dn, String attrName, String... attrValStrings) {
    LinkedList<Modification> mods = newLinkedList(
        new Modification(ADD, Attributes.create(attrName, attrValStrings)));
    return getRootConnection().processModify(dn, mods);
  }

  /**
   * Replace specified attr type and type values to the entry specified by dn.
   *
   * @param dn The dn of the entry to replace the attribute and values to.
   * @param attrName The attribute type to replace the values in.
   * @param attrValStrings The values to replace in the the entry.
   */
  private ModifyOperation  replaceAttrEntry(DN dn, String attrName, String... attrValStrings) {
    LinkedList<Modification> mods = newLinkedList(
        new Modification(REPLACE, Attributes.create(attrName, attrValStrings)));
    return getRootConnection().processModify(dn, mods);
  }

  /**
   * Remove the attributes specified by the attribute type strings from the
   * entry corresponding to the dn argument.
   *
   * @param dn The entry to remove the attributes from.
   * @param attrTypeStrings The attribute type string list to remove from the entry.
   * @throws Exception  If an error occurs.
   */
  private void
  deleteAttrsEntry(DN dn, String... attrTypeStrings) throws Exception {
    LinkedList<Modification> mods = new LinkedList<>();
    for(String attrTypeString : attrTypeStrings) {
      AttributeType attrType = getAttrType(attrTypeString);
      mods.add(new Modification(ModificationType.DELETE,
          Attributes.empty(attrType)));
    }
    getRootConnection().processModify(dn, mods);
  }

  private AttributeType getAttrType(String attrTypeString) {
    return DirectoryServer.getAttributeTypeOrDefault(attrTypeString);
  }

  private void deleteEntries(String... dns) throws Exception{
    InternalClientConnection conn = getRootConnection();
    for(String dn : dns) {
      DeleteOperation op = conn.processDelete(DN.valueOf(dn));
      assertEquals(op.getResultCode(), ResultCode.SUCCESS);
    }
  }

  private void deleteSubtree(String... dns) throws Exception
  {
    Control control = new SubtreeDeleteControl(true);
    List<Control> controls = newArrayList(control);

    for (String dn : dns)
    {
      DeleteOperation op = getRootConnection().processDelete(DN.valueOf(dn), controls);
      assertEquals(op.getResultCode(), ResultCode.SUCCESS);
    }
  }

  /**
   * Check membership in a static group of the specified dns. The expected
   * boolean is used to check if the dns are expected or not expected in the
   * groups.
   *
   * @param group The group to check membership in.
   *
   * @param expected Set to <code>true</code> if the dns are expected in the
   *                 groups.
   *
   * @param dns The dns to check membership for.
   *
   * @throws Exception If an unexpected membership occurs.
   *
   */
  private void isMember(String group, boolean expected, String... dns) throws Exception {
   GroupManager groupManager=DirectoryServer.getGroupManager();
   Group<?> instance=groupManager.getGroupInstance(DN.valueOf(group));
   for(String dn : dns)
   {
     assertEquals(instance.isMember(DN.valueOf(dn)), expected);
   }
  }

  private void isAttributeValueEntry(String entryDN, boolean expected,
                                     String attr,
                                     String... dns)
          throws Exception {
    AttributeType type= getAttrType(attr);
    final SearchRequest request = newSearchRequest(entryDN, SearchScope.BASE_OBJECT, "(" + attr + "=*)");
    InternalSearchOperation operation = getRootConnection().processSearch(request);
    for (SearchResultEntry entry : operation.getSearchEntries()) {
      for(String dn : dns) {
        ByteString value = ByteString.valueOfUtf8(dn);
        assertEquals(entry.hasValue(type, null, value), expected);
      }
    }
  }

  /**
   * Add the entries created using the specified DNs to the server.
   *
   * @param dns The dns to use in entry creation.
   *
   * @throws Exception If an unexpected result happens.
   *
   */
  private void addEntries(String... dns) throws Exception {
    for(String dn : dns) {
      addEntry(dn);
    }
  }

  /**
   * Make a entry with the specified dn.
   *
   * @param dn The dn of the entry.
   * @return The created entry.
   * @throws Exception  If the entry can't be created.
   */
  private Entry addEntry(String dn) throws Exception {
    return TestCaseUtils.addEntry(
            "dn: " + dn,
            "objectClass: top",
            "objectClass: person",
            "objectClass: organizationalPerson",
            "objectClass: inetOrgPerson",
            "uid: 1",
            "givenName: test",
            "sn: 1",
            "cn: test"
    );
  }

  /**
   * Perform modify DN operation.
   *
   * @param dn  The DN to rename or move.
   * @param rdn RDN value.
   * @param newSuperior New superior to move to.
   * @throws Exception If the operation can't be performed.
   */
  private void doModDN(String dn, String rdn, String newSuperior) throws Exception
  {
    InternalClientConnection conn = getRootConnection();
    ModifyDNOperation modDNop;
    if(newSuperior != null)
    {
        modDNop = conn.processModifyDN(DN.valueOf(dn), RDN.decode(rdn), true,
                                       DN.valueOf(newSuperior));
    }
    else
    {
        modDNop = conn.processModifyDN(DN.valueOf(dn), RDN.decode(rdn),
                                       false, null);
    }
    assertEquals(modDNop.getResultCode(), ResultCode.SUCCESS);
  }

  /**
   * Test case:
   * - integrity is enforced on the attribute 'manager'
   * - value of the 'manager' attribute should match the filter:
   *  (objectclass=person)
   * - add a user 'manager' to the 'dc=example,dc=com'
   * - add a user 'employee' with the attribute manager which points to the
   *   entry 'manager'
   * - SUCCESS
   * @throws Exception
   */
  @Test
  public void testEnforceIntegrityAddUserWitManagerFilterNoNC() throws Exception
  {
    replaceAttrEntry(configDN, "ds-cfg-enabled", "false");
    replaceAttrEntry(configDN, dsConfigPluginType,
                               "postoperationdelete",
                               "postoperationmodifydn",
                               "subordinatemodifydn",
                               "subordinatedelete",
                               "preoperationadd",
                               "preoperationmodify");
    deleteAttrsEntry(configDN, dsConfigBaseDN);
    replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true");
    replaceAttrEntry(configDN, dsConfigAttrType, "manager");
    addAttrEntry(configDN, dsConfigAttrFiltMapping,
                           "manager:(objectclass=person)");
    replaceAttrEntry(configDN, "ds-cfg-enabled", "true");

    addEntry("uid=manager,ou=people,ou=dept,o=test");

    TestCaseUtils.addEntry(
      "dn: uid=employee,ou=people,ou=dept,dc=example,dc=com",
      "objectclass: top",
      "objectclass: person",
      "objectclass: organizationalperson",
      "objectclass: inetorgperson",
      "uid: employee",
      "cn: employee",
      "sn: employee",
      "givenname: employee",
      "manager: uid=manager,ou=people,ou=dept,o=test");
  }

  /**
   * Test case:
   * - integrity is enforced on the attribute 'manager'
   * - value of the 'manager' attribute should match the filter:
   *  (objectclass=person)
   * - add a user 'manager' to the 'dc=example,dc=com'
   * - add a user 'employee' with the attribute manager which points to the
   *   entry 'manager'
   * - SUCCESS
   * @throws Exception
   */
  @Test
  public void testEnforceIntegrityAddUserWitManagerFilter() throws Exception
  {
    replaceAttrEntry(configDN, "ds-cfg-enabled", "false");
    replaceAttrEntry(configDN, dsConfigPluginType,
                               "postoperationdelete",
                               "postoperationmodifydn",
                               "subordinatemodifydn",
                               "subordinatedelete",
                               "preoperationadd",
                               "preoperationmodify");
    addAttrEntry(configDN, dsConfigBaseDN, "dc=example,dc=com");
    replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true");
    replaceAttrEntry(configDN, dsConfigAttrType, "manager");
    addAttrEntry(configDN, dsConfigAttrFiltMapping,
                           "manager:(objectclass=person)");
    replaceAttrEntry(configDN, "ds-cfg-enabled", "true");

    addEntry("uid=manager,ou=people,ou=dept,dc=example,dc=com");

    TestCaseUtils.addEntry(
      "dn: uid=employee,ou=people,ou=dept,dc=example,dc=com",
      "objectclass: top",
      "objectclass: person",
      "objectclass: organizationalperson",
      "objectclass: inetorgperson",
      "uid: employee",
      "cn: employee",
      "sn: employee",
      "givenname: employee",
      "manager: uid=manager,ou=people,ou=dept,dc=example,dc=com");
  }

  /**
   * Test case:
   * - integrity is enforced on the attribute 'manager'
   * - value of the 'manager' attribute should match the filter:
   *  (objectclass=person)
   * - add a user 'employee' with the attribute manager which points to the
   *   entry 'manager' which doesn't exist
   * - CONSTRAINT VIOLATION
   * @throws Exception
   */
  @Test
  public void testEnforceIntegrityAddUserWithMissingManagerEntry() throws Exception
  {
    replaceAttrEntry(configDN, "ds-cfg-enabled", "false");
    replaceAttrEntry(configDN, dsConfigPluginType,
                               "postoperationdelete",
                               "postoperationmodifydn",
                               "subordinatemodifydn",
                               "subordinatedelete",
                               "preoperationadd",
                               "preoperationmodify");
    addAttrEntry(configDN, dsConfigBaseDN, "dc=example,dc=com");
    replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true");
    replaceAttrEntry(configDN, dsConfigAttrType, "manager");
    addAttrEntry(configDN, dsConfigAttrFiltMapping,
                           "manager:(objectclass=person)");
    replaceAttrEntry(configDN, "ds-cfg-enabled", "true");

    Entry entry = TestCaseUtils.makeEntry(
      "dn: uid=employee,ou=people,ou=dept,dc=example,dc=com",
      "objectclass: top",
      "objectclass: person",
      "objectclass: organizationalperson",
      "objectclass: inetorgperson",
      "uid: employee",
      "cn: employee",
      "sn: employee",
      "givenname: employee",
      "manager: uid=bad,ou=people,ou=dept,dc=example,dc=com");

    AddOperation addOperation = getRootConnection().processAdd(entry);
    assertEquals(addOperation.getResultCode(), ResultCode.CONSTRAINT_VIOLATION);
  }

  /**
   * Test case:
   * - integrity is enforced on the attribute 'manager'
   * - value of the 'manager' attribute should match the filter:
   *  (objectclass=groupOfNames)
   * - add a user 'manager' with the object class 'person' to the
   *   'dc=example,dc=com'
   * - add a user 'employee' with the attribute manager which points to the
   *   entry 'manager'
   * - CONSTRAINT VIOLATION
   * @throws Exception
   */
  @Test
  public void testEnforceIntegrityAddUserWitManagerFilterMismatch() throws Exception
  {
    replaceAttrEntry(configDN, "ds-cfg-enabled", "false");
    replaceAttrEntry(configDN, dsConfigPluginType,
                               "postoperationdelete",
                               "postoperationmodifydn",
                               "subordinatemodifydn",
                               "subordinatedelete",
                               "preoperationadd",
                               "preoperationmodify");
    addAttrEntry(configDN, dsConfigBaseDN, "dc=example,dc=com");
    replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true");
    replaceAttrEntry(configDN, dsConfigAttrType, "manager");
    addAttrEntry(configDN, dsConfigAttrFiltMapping,
                           "manager:(objectclass=gropuOfNames)");
    replaceAttrEntry(configDN, "ds-cfg-enabled", "true");

    addEntry("uid=manager,ou=people,ou=dept,dc=example,dc=com");

    Entry entry = TestCaseUtils.makeEntry(
      "dn: uid=employee,ou=people,ou=dept,dc=example,dc=com",
      "objectclass: top",
      "objectclass: person",
      "objectclass: organizationalperson",
      "objectclass: inetorgperson",
      "uid: employee",
      "cn: employee",
      "sn: employee",
      "givenname: employee",
      "manager: uid=manager,ou=people,ou=dept,dc=example,dc=com");

    AddOperation addOperation = getRootConnection().processAdd(entry);
    assertEquals(addOperation.getResultCode(), ResultCode.CONSTRAINT_VIOLATION);
  }

  /**
   * Test case:
   * - integrity is enforced on the attribute 'manager'
   * - value of the 'manager' attribute should match the filter:
   *  (objectclass=person)
   * - add a user 'manager' to the 'o=test'
   * - add a user 'employee' with the attribute manager which points to the
   *   entry 'manager'
   * - SUCCESS
   * @throws Exception
   */
  @Test
  public void testEnforceIntegrityAddUserWithManagerNC() throws Exception
  {
    replaceAttrEntry(configDN, "ds-cfg-enabled", "false");
    replaceAttrEntry(configDN, dsConfigPluginType,
                               "postoperationdelete",
                               "postoperationmodifydn",
                               "subordinatemodifydn",
                               "subordinatedelete",
                               "preoperationadd",
                               "preoperationmodify");
    addAttrEntry(configDN, dsConfigBaseDN,
                           "dc=example,dc=com",
                           "o=test");
    replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true");
    replaceAttrEntry(configDN, dsConfigAttrType, "manager");
    addAttrEntry(configDN, dsConfigAttrFiltMapping,
                           "manager:(objectclass=person)");
    replaceAttrEntry(configDN, "ds-cfg-enabled", "true");

    addEntry("uid=manager,ou=people,ou=dept,o=test");

    TestCaseUtils.addEntry(
      "dn: uid=employee,ou=people,ou=dept,dc=example,dc=com",
      "objectclass: top",
      "objectclass: person",
      "objectclass: organizationalperson",
      "objectclass: inetorgperson",
      "uid: employee",
      "cn: employee",
      "sn: employee",
      "givenname: employee",
      "manager: uid=manager,ou=people,ou=dept,o=test");
  }

  /**
   * Test case:
   * - integrity is enforced on the attribute 'member'
   * - value of the 'manager' attribute should match the filter:
   *  (objectclass=person)
   * - add a group 'referent group' to the 'dc=example,dc=com' with the
   *   'member' attribute pointing to the existing user entries
   * - SUCCESS
   * @throws Exception
   */
  @Test
  public void testEnforceIntegrityAddGroupWithFilter() throws Exception
  {
    replaceAttrEntry(configDN, "ds-cfg-enabled", "false");
    replaceAttrEntry(configDN, dsConfigPluginType,
                               "postoperationdelete",
                               "postoperationmodifydn",
                               "subordinatemodifydn",
                               "subordinatedelete",
                               "preoperationadd",
                               "preoperationmodify");
    addAttrEntry(configDN, dsConfigBaseDN, "dc=example,dc=com");
    replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true");
    replaceAttrEntry(configDN, dsConfigAttrType, "member");
    addAttrEntry(configDN, dsConfigAttrFiltMapping,
                           "member:(objectclass=person)");
    replaceAttrEntry(configDN, "ds-cfg-enabled", "true");

    TestCaseUtils.addEntry(
      "dn: cn=referent group,ou=groups,dc=example,dc=com",
      "objectclass: top",
      "objectclass: groupofnames",
      "cn: refetent group",
      "member: uid=user.1,ou=people,ou=dept,dc=example,dc=com",
      "member: uid=user.2,ou=people,ou=dept,dc=example,dc=com",
      "member: uid=user.3,ou=people,ou=dept,dc=example,dc=com",
      "member: uid=user.4,ou=people,ou=dept,dc=example,dc=com",
      "member: uid=user.5,ou=people,ou=dept,dc=example,dc=com"
      );
  }

  /**
   * Test case:
   * - integrity is enforced on the attribute 'member'
   * - value of the 'manager' attribute should match the filter:
   *  (objectclass=person)
   * - add a group 'referent group' to the 'dc=example,dc=com' with the
   *   'member' attribute pointing to the existing user entries and one missing
   * - CONSTRAINT VIOLATION
   * @throws Exception
   */
  @Test
  public void testEnforceIntegrityAddGroupWithMissingMember() throws Exception
  {
    replaceAttrEntry(configDN, "ds-cfg-enabled", "false");
    replaceAttrEntry(configDN, dsConfigPluginType,
                               "postoperationdelete",
                               "postoperationmodifydn",
                               "subordinatemodifydn",
                               "subordinatedelete",
                               "preoperationadd",
                               "preoperationmodify");
    addAttrEntry(configDN, dsConfigBaseDN, "dc=example,dc=com");
    replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true");
    replaceAttrEntry(configDN, dsConfigAttrType, "member");
    addAttrEntry(configDN, dsConfigAttrFiltMapping,
                           "member:(objectclass=person)");
    replaceAttrEntry(configDN, "ds-cfg-enabled", "true");

    Entry entry = TestCaseUtils.makeEntry(
      "dn: cn=referent group,ou=groups,dc=example,dc=com",
      "objectclass: top",
      "objectclass: groupofnames",
      "cn: refetent group",
      "member: uid=user.1,ou=people,ou=dept,dc=example,dc=com",
      "member: uid=user.2,ou=people,ou=dept,dc=example,dc=com",
      "member: uid=bad,ou=people,ou=dept,dc=example,dc=com",
      "member: uid=user.4,ou=people,ou=dept,dc=example,dc=com",
      "member: uid=user.5,ou=people,ou=dept,dc=example,dc=com"
      );

    AddOperation addOperation = getRootConnection().processAdd(entry);
    assertEquals(addOperation.getResultCode(), ResultCode.CONSTRAINT_VIOLATION);
  }

  /**
   * Test case:
   * - integrity is enforced on the attribute 'member'
   * - value of the 'manager' attribute should match the filter:
   *  (objectclass=person)
   * - add a group 'referent group' to the 'dc=example,dc=com' with the
   *   'member' attribute pointing to the existing user entries and one entry
   *   being of object class groupOfNames
   * - CONSTRAINT VIOLATION
   * @throws Exception
   */
  @Test
  public void testEnforceIntegrityAddGroupMemberFilterMismatch() throws Exception
  {
    replaceAttrEntry(configDN, "ds-cfg-enabled", "false");
    replaceAttrEntry(configDN, dsConfigPluginType,
                               "postoperationdelete",
                               "postoperationmodifydn",
                               "subordinatemodifydn",
                               "subordinatedelete",
                               "preoperationadd",
                               "preoperationmodify");
    addAttrEntry(configDN, dsConfigBaseDN, "dc=example,dc=com");
    replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true");
    replaceAttrEntry(configDN, dsConfigAttrType, "member");
    addAttrEntry(configDN, dsConfigAttrFiltMapping,
                           "member:(objectclass=person)");
    replaceAttrEntry(configDN, "ds-cfg-enabled", "true");

    Entry entry = TestCaseUtils.makeEntry(
      "dn: cn=referent group,ou=groups,dc=example,dc=com",
      "objectclass: top",
      "objectclass: groupofnames",
      "cn: refetent group",
      "member: uid=user.1,ou=people,ou=dept,dc=example,dc=com",
      "member: uid=user.2,ou=people,ou=dept,dc=example,dc=com",
      "member: cn=group,ou=groups,dc=example,dc=com",
      "member: uid=user.4,ou=people,ou=dept,dc=example,dc=com",
      "member: uid=user.5,ou=people,ou=dept,dc=example,dc=com"
      );

    AddOperation addOperation = getRootConnection().processAdd(entry);
    assertEquals(addOperation.getResultCode(), ResultCode.CONSTRAINT_VIOLATION);
  }

  /**
   * Test case:
   * - integrity is enforced on the attribute 'member'
   * - value of the 'manager' attribute should match the filter:
   *  (objectclass=person)
   * - add a group 'referent group' to the 'dc=example,dc=com' with the
   *   'member' attribute pointing to the existing user entries with one memeber
   *   belonging to 'o=test' naming context
   * - SUCCESS
   * @throws Exception
   */
  @Test
  public void testEnforceIntegrityAddGroupMemberNC() throws Exception
  {
    replaceAttrEntry(configDN, "ds-cfg-enabled", "false");
    replaceAttrEntry(configDN, dsConfigPluginType,
                               "postoperationdelete",
                               "postoperationmodifydn",
                               "subordinatemodifydn",
                               "subordinatedelete",
                               "preoperationadd",
                               "preoperationmodify");
    addAttrEntry(configDN, dsConfigBaseDN,
                           "dc=example,dc=com",
                           "o=test");
    replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true");
    replaceAttrEntry(configDN, dsConfigAttrType, "member");
    addAttrEntry(configDN, dsConfigAttrFiltMapping,
                           "member:(objectclass=person)");
    replaceAttrEntry(configDN, "ds-cfg-enabled", "true");

    TestCaseUtils.addEntry(
      "dn: cn=referent group,ou=groups,dc=example,dc=com",
      "objectclass: top",
      "objectclass: groupofnames",
      "cn: refetent group",
      "member: uid=user.1,ou=people,ou=dept,dc=example,dc=com",
      "member: uid=user.2,ou=people,ou=dept,dc=example,dc=com",
      "member: uid=user.3,ou=people,ou=dept,o=test",
      "member: uid=user.4,ou=people,ou=dept,dc=example,dc=com",
      "member: uid=user.5,ou=people,ou=dept,dc=example,dc=com"
      );
  }

  /**
   * Test case:
   * - employee entry exists
   * - manager entry exists
   * - add 'manager' attribute to the manager entry
   * @throws Exception
   */
  @Test
  public void testEnforceIntegrityModifyUserAddManagerFilter() throws Exception
  {
    replaceAttrEntry(configDN, "ds-cfg-enabled", "false");
    replaceAttrEntry(configDN, dsConfigPluginType,
                               "postoperationdelete",
                               "postoperationmodifydn",
                               "subordinatemodifydn",
                               "subordinatedelete",
                               "preoperationadd",
                               "preoperationmodify");
    addAttrEntry(configDN, dsConfigBaseDN, "dc=example,dc=com");
    replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true");
    replaceAttrEntry(configDN, dsConfigAttrType, "manager");
    addAttrEntry(configDN, dsConfigAttrFiltMapping,
                           "manager:(objectclass=person)");
    replaceAttrEntry(configDN, "ds-cfg-enabled", "true");

    addEntry("uid=manager,ou=people,ou=dept,dc=example,dc=com");

    ModifyOperation modOperation = addAttrEntry(DN.valueOf(user1),
     "manager", "uid=manager,ou=people,ou=dept,dc=example,dc=com");
    assertEquals(modOperation.getResultCode(), ResultCode.SUCCESS);
  }

  /**
   * Test case:
   * - employee entry exists with 'manager' attribute pointing to the
   *   manager entry
   * - manager entry exists
   * - user.2 entry exists
   * - modify 'manager' attribute to the 'user.2' entry
   * @throws Exception
   */
  @Test
  public void testEnforceIntegrityModifyUserModifyManagerFilter() throws Exception
  {
    replaceAttrEntry(configDN, "ds-cfg-enabled", "false");
    replaceAttrEntry(configDN, dsConfigPluginType,
                               "postoperationdelete",
                               "postoperationmodifydn",
                               "subordinatemodifydn",
                               "subordinatedelete",
                               "preoperationadd",
                               "preoperationmodify");
    addAttrEntry(configDN, dsConfigBaseDN, "dc=example,dc=com");
    replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true");
    replaceAttrEntry(configDN, dsConfigAttrType, "manager");
    addAttrEntry(configDN, dsConfigAttrFiltMapping,
                           "manager:(objectclass=person)");
    replaceAttrEntry(configDN, "ds-cfg-enabled", "true");

    addEntry("uid=manager,ou=people,ou=dept,dc=example,dc=com");

    ModifyOperation modOperation = addAttrEntry(DN.valueOf(user1),
     "manager", "uid=manager,ou=people,ou=dept,dc=example,dc=com");
    assertEquals(modOperation.getResultCode(), ResultCode.SUCCESS);

    modOperation = replaceAttrEntry(DN.valueOf(user1),
                                    "manager", user2);
    assertEquals(modOperation.getResultCode(), ResultCode.SUCCESS);
  }

  /**
   * Test case:
   * - filter is set to posixAccount
   * - employee entry exists
   * - manager entry exists with objectclass person
   * - add 'manager' attribute to the manager entry
   * - constraint violation
   * @throws Exception
   */
  @Test
  public void testEnforceIntegrityModifyUserAddManagerFilterMismatch() throws Exception
  {
    replaceAttrEntry(configDN, "ds-cfg-enabled", "false");
    replaceAttrEntry(configDN, dsConfigPluginType,
                               "postoperationdelete",
                               "postoperationmodifydn",
                               "subordinatemodifydn",
                               "subordinatedelete",
                               "preoperationadd",
                               "preoperationmodify");
    addAttrEntry(configDN, dsConfigBaseDN, "dc=example,dc=com");
    replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true");
    replaceAttrEntry(configDN, dsConfigAttrType, "manager");
    addAttrEntry(configDN, dsConfigAttrFiltMapping,
                           "manager:(objectclass=posixAccount)");
    replaceAttrEntry(configDN, "ds-cfg-enabled", "true");

    addEntry("uid=manager,ou=people,ou=dept,dc=example,dc=com");

    ModifyOperation modOperation = addAttrEntry(DN.valueOf(user1),
     "manager", "uid=manager,ou=people,ou=dept,dc=example,dc=com");
    assertEquals(modOperation.getResultCode(), ResultCode.CONSTRAINT_VIOLATION);
  }

  /**
   * Test case:
   * - employee entry exists in dc=example,dc=com
   * - manager entry exists in o=other
   * - add 'manager' attribute to the employee poiting to the manager
   *   entry
   * - SUCCESS
   * @throws Exception
   */
  @Test
  public void testEnforceIntegrityModifyUserAddManagerNC() throws Exception
  {
    replaceAttrEntry(configDN, "ds-cfg-enabled", "false");
    replaceAttrEntry(configDN, dsConfigPluginType,
                               "postoperationdelete",
                               "postoperationmodifydn",
                               "subordinatemodifydn",
                               "subordinatedelete",
                               "preoperationadd",
                               "preoperationmodify");
    addAttrEntry(configDN, dsConfigBaseDN,
                           "dc=example,dc=com",
                           "o=test");
    replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true");
    replaceAttrEntry(configDN, dsConfigAttrType, "manager");
    addAttrEntry(configDN, dsConfigAttrFiltMapping,
                           "manager:(objectclass=person)");
    replaceAttrEntry(configDN, "ds-cfg-enabled", "true");

    addEntry("uid=manager,ou=people,ou=dept,o=test");

    ModifyOperation modOperation = addAttrEntry(DN.valueOf(user1),
     "manager", "uid=manager,ou=people,ou=dept,o=test");
    assertEquals(modOperation.getResultCode(), ResultCode.SUCCESS);
  }

  /**
   * Test case:
   * - employee entry exists
   * - manager entry does not exist
   * - add 'manager' attribute to the employee
   * - constraint violation
   * @throws Exception
   */
  @Test
  public void testEnforceIntegrityModifyUserAddManagerMissing() throws Exception
  {
    replaceAttrEntry(configDN, "ds-cfg-enabled", "false");
    replaceAttrEntry(configDN, dsConfigPluginType,
                               "postoperationdelete",
                               "postoperationmodifydn",
                               "subordinatemodifydn",
                               "subordinatedelete",
                               "preoperationadd",
                               "preoperationmodify");
    addAttrEntry(configDN, dsConfigBaseDN, "dc=example,dc=com");
    replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true");
    replaceAttrEntry(configDN, dsConfigAttrType, "manager");
    addAttrEntry(configDN, dsConfigAttrFiltMapping,
                           "manager:(objectclass=person)");
    replaceAttrEntry(configDN, "ds-cfg-enabled", "true");

    ModifyOperation modOperation = addAttrEntry(DN.valueOf(user1),
     "manager", "uid=manager,ou=people,ou=dept,dc=example,dc=com");
    assertEquals(modOperation.getResultCode(), ResultCode.CONSTRAINT_VIOLATION);
  }

  @Test
  public void testEnforceIntegrityModifyGroupAddMember() throws Exception
  {
    replaceAttrEntry(configDN, "ds-cfg-enabled", "false");
    replaceAttrEntry(configDN, dsConfigPluginType,
                               "postoperationdelete",
                               "postoperationmodifydn",
                               "subordinatemodifydn",
                               "subordinatedelete",
                               "preoperationadd",
                               "preoperationmodify");
    addAttrEntry(configDN, dsConfigBaseDN, "dc=example,dc=com");
    replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true");
    replaceAttrEntry(configDN, dsConfigAttrType, "member");
    addAttrEntry(configDN, dsConfigAttrFiltMapping,
                           "member:(objectclass=person)");
    replaceAttrEntry(configDN, "ds-cfg-enabled", "true");

    ModifyOperation modOperation = addAttrEntry(DN.valueOf(group),
                                                "member",
                                                user1);
    assertEquals(modOperation.getResultCode(), ResultCode.SUCCESS);
  }

  @Test
  public void testEnforceIntegrityModifyGroupAddMissingMember() throws Exception
  {
    replaceAttrEntry(configDN, "ds-cfg-enabled", "false");
    replaceAttrEntry(configDN, dsConfigPluginType,
                               "postoperationdelete",
                               "postoperationmodifydn",
                               "subordinatemodifydn",
                               "subordinatedelete",
                               "preoperationadd",
                               "preoperationmodify");
    addAttrEntry(configDN, dsConfigBaseDN, "dc=example,dc=com");
    replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true");
    replaceAttrEntry(configDN, dsConfigAttrType, "member");
    addAttrEntry(configDN, dsConfigAttrFiltMapping,
                           "member:(objectclass=person)");
    replaceAttrEntry(configDN, "ds-cfg-enabled", "true");

    ModifyOperation modOperation = addAttrEntry(DN.valueOf(group),
      "member", "uid=user.100,ou=people,ou=dept,dc=example,dc=com");
    assertEquals(modOperation.getResultCode(), ResultCode.CONSTRAINT_VIOLATION);
  }

  @Test
  public void testEnforceIntegrityModifyGroupAddMemberFilterMismatch() throws Exception
  {
    replaceAttrEntry(configDN, "ds-cfg-enabled", "false");
    replaceAttrEntry(configDN, dsConfigPluginType,
                               "postoperationdelete",
                               "postoperationmodifydn",
                               "subordinatemodifydn",
                               "subordinatedelete",
                               "preoperationadd",
                               "preoperationmodify");
    addAttrEntry(configDN, dsConfigBaseDN, "dc=example,dc=com");
    replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true");
    replaceAttrEntry(configDN, dsConfigAttrType, "member");
    addAttrEntry(configDN, dsConfigAttrFiltMapping,
                           "member:(objectclass=posixaccount)");
    replaceAttrEntry(configDN, "ds-cfg-enabled", "true");

    ModifyOperation modOperation = addAttrEntry(DN.valueOf(group),
      "member", "uid=user.100,ou=people,ou=dept,dc=example,dc=com");
    assertEquals(modOperation.getResultCode(), ResultCode.CONSTRAINT_VIOLATION);
  }

  @Test
  public void testEnforceIntegrityModifyGroupAddMemberNC() throws Exception
  {
    replaceAttrEntry(configDN, "ds-cfg-enabled", "false");
    replaceAttrEntry(configDN, dsConfigPluginType,
                               "postoperationdelete",
                               "postoperationmodifydn",
                               "subordinatemodifydn",
                               "subordinatedelete",
                               "preoperationadd",
                               "preoperationmodify");
    addAttrEntry(configDN, dsConfigBaseDN,
                           "dc=example,dc=com",
                           "o=test");
    replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true");
    replaceAttrEntry(configDN, dsConfigAttrType, "member");
    addAttrEntry(configDN, dsConfigAttrFiltMapping,
                           "member:(objectclass=person)");
    replaceAttrEntry(configDN, "ds-cfg-enabled", "true");

    ModifyOperation modOperation = addAttrEntry(DN.valueOf(group),
      "member", "uid=user.1,ou=people,ou=dept,o=test");
    assertEquals(modOperation.getResultCode(), ResultCode.SUCCESS);
  }
}
