mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

gbellato
12.26.2006 28bdeff49edcd4e892755355c23cc13a4df48ed9
Add some unit tests for 
- synchronization msg serialization/un-serialization (classes that inherit from SynchronizationMsg)
- modify conflicts resolution (Historical class)
3 files added
1 files modified
592 ■■■■■ changed files
opends/ext/testng/testng.xml 1 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/ModifyConflictTest.java 276 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/SynchronizationMsgTest.java 278 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/SynchronizationTestCase.java 37 ●●●●● patch | view | raw | blame | history
opends/ext/testng/testng.xml
@@ -4,6 +4,7 @@
        <package name="org.opends.server.protocols.asn1"/>
        <package name="org.opends.server.core"/>
        <package name="org.opends.server.backends.jeb"/>
        <package name="org.opends.server.synchronization"/>
    </packages>
    
    <test name="precommit">
opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/ModifyConflictTest.java
New file
@@ -0,0 +1,276 @@
/*
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  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
 *
 *
 *      Portions Copyright 2006 Sun Microsystems, Inc.
 */
package org.opends.server.synchronization;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ModifyOperation;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import org.opends.server.types.Modification;
import org.opends.server.types.ModificationType;
import org.opends.server.types.ObjectClass;
import static org.opends.server.synchronization.SynchMessages.SYNCHRONIZATION;
/*
 * Test the conflict resolution for modify operations
 * This is still a work in progress.
 * currently implemented tests
 *  - check that an replace with a smaller csn is ignored
 * should test :
 *  - conflict with multi-valued attributes
 *  - conflict with single-valued attributes
 *  - conflict with options
 *  - conflict with binary attributes
 *  - Replace, add, delete attribute, delete attribute value
 *  - conflict on the objectclass attribute
 */
public class ModifyConflictTest extends SynchronizationTestCase
{
  /**
   * Test that conflict between a modify-replace and modify-add
   * for multi-valued attributes are handled correctly.
   */
  @Test()
  public void replaceAndAdd()
         throws Exception
  {
    /*
     * Objectclass and DN do not have any impact on the modifty conflict
     * resolution for the description attribute.
     * Always use the same values for all these tests.
     */
    DN dn = DN.decode("dc=com");
    Map<ObjectClass, String> objectClasses = new HashMap<ObjectClass, String>();
    ObjectClass org = DirectoryServer.getObjectClass("organization");
    objectClasses.put(org, "organization");
    /*
     * start with a new entry with an empty description
     */
    Entry entry = new Entry(dn, objectClasses, null, null);
    Historical hist = Historical.load(entry);
    /*
     * simulate a modify-replace done at time t10
     */
    testModify(entry, hist, "description", ModificationType.REPLACE,
               "init value", 10, true);
    /*
     * Now simulate an add at an earlier date that the previous replace
     * conflict resolution should remove it.
     */
    testModify(entry, hist, "description", ModificationType.ADD,
               "older value", 1, false);
    /*
     * Now simulate an add at an earlier date that the previous replace
     * conflict resolution should remove it.
     * (a second time to make sure...)
     */
    testModify(entry, hist, "description", ModificationType.ADD,
               "older value", 2, false);
    /*
     * Now simulate an add at a later date that the previous replace.
     * conflict resolution should keep it
     */
    testModify(entry, hist, "description", ModificationType.ADD,
               "new value", 11, true);
  }
  /**
   * Test that conflict between a modify-delete-attribute and modify-add
   * for multi-valued attributes are handled correctly.
   */
  @Test()
  public void deleteAndAdd()
         throws Exception
  {
    /*
     * Objectclass and DN do not have any impact on the modifty conflict
     * resolution for the description attribute.
     * Always use the same values for all these tests.
     */
    DN dn = DN.decode("dc=com");
    Map<ObjectClass, String> objectClasses = new HashMap<ObjectClass, String>();
    ObjectClass org = DirectoryServer.getObjectClass("organization");
    objectClasses.put(org, "organization");
    /*
     * start with a new entry with an empty description
     */
    Entry entry = new Entry(dn, objectClasses, null, null);
    Historical hist = Historical.load(entry);
    /*
     * simulate a delete of the whole description attribute done at time t10
     */
    testModify(entry, hist, "description", ModificationType.DELETE,
               null, 10, true);
    /*
     * Now simulate an add at an earlier date that the previous delete.
     * The conflict resolution should detect that this add must be ignored.
     */
    testModify(entry, hist, "description",  ModificationType.ADD,
               "older value", 1, false);
    /*
     * Now simulate an add at an earlier date that the previous delete.
     * The conflict resolution should detect that this add must be ignored.
     * (a second time to make sure that historical information is kept...)
     */
    testModify(entry, hist, "description", ModificationType.ADD,
               "older value", 2, false);
    /*
     * Now simulate an add at a later date that the previous delete.
     * conflict resolution should keep it
     */
    testModify(entry, hist, "description", ModificationType.ADD,
               "new value", 11, true);
  }
  /**
  * Test that conflict between a modify-add and modify-add
  * for multi-valued attributes are handled correctly.
  */
 @Test()
 public void addAndAdd()
        throws Exception
 {
   /*
    * Objectclass and DN do not have any impact on the modifty conflict
    * resolution for the description attribute.
    * Always use the same values for all these tests.
    */
   DN dn = DN.decode("dc=com");
   Map<ObjectClass, String> objectClasses = new HashMap<ObjectClass, String>();
   ObjectClass org = DirectoryServer.getObjectClass("organization");
   objectClasses.put(org, "organization");
   /*
    * start with a new entry with an empty description
    */
   Entry entry = new Entry(dn, objectClasses, null, null);
   Historical hist = Historical.load(entry);
   /*
    * simulate a add of the description attribute done at time t10
    */
   testModify(entry, hist, "description", ModificationType.ADD,
              "init value", 10, true);
   /*
    * Now simulate an add at an earlier date that the previous add.
    * The conflict resolution should detect that this add must be kept.
    */
   testModify(entry, hist, "description", ModificationType.ADD,
              "older value", 1, true);
   /*
    * Now simulate an add at an earlier date that the previous add.
    * The conflict resolution should detect that this add must be kept.
    * (a second time to make sure that historical information is kept...)
    */
   testModify(entry, hist, "description", ModificationType.ADD,
              "older value", 2, false);
   /*
    * Now simulate an add at a later date that the previous add.
    * conflict resolution should keep it
    */
   testModify(entry, hist, "description", ModificationType.ADD,
              "new value", 11, true);
 }
  /*
   * helper function.
   */
  private static void testModify(Entry entry,
      Historical hist, String attrName,
      ModificationType modType, String value,
      int date, boolean keepChangeResult)
  {
    InternalClientConnection connection = new InternalClientConnection();
    ChangeNumber t = new ChangeNumber(date, (short) 0, (short) 0);
    /* create AttributeType description that will be usedfor this test */
    AttributeType attrType =
      DirectoryServer.getAttributeType(attrName, true);
    LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>();
    if (value != null)
      values.add(new AttributeValue(attrType, value));
    Attribute attr = new Attribute(attrType, attrName, values);
    List<Modification> mods = new ArrayList<Modification>();
    Modification mod = new Modification(modType, attr);
    mods.add(mod);
    ModifyOperation modOp = new ModifyOperation(connection, 1, 1, null,
                                              entry.getDN(), mods);
    modOp.setAttachment(SYNCHRONIZATION, t);
    hist.replayOperation(modOp, entry);
    /*
     * The last older change should have been detected as conflicting
     * and should be removed by the conflict resolution code.
     */
    if (keepChangeResult)
    {
      assertTrue(mods.contains(mod));
      assertEquals(1, mods.size());
    }
    else
    {
      assertFalse(mods.contains(mod));
      assertEquals(0, mods.size());
    }
  }
}
opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/SynchronizationMsgTest.java
New file
@@ -0,0 +1,278 @@
/*
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  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
 *
 *
 *      Portions Copyright 2006 Sun Microsystems, Inc.
 */
package org.opends.server.synchronization;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
import org.opends.server.core.DeleteOperation;
import org.opends.server.core.DirectoryException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ModifyDNOperation;
import org.opends.server.core.ModifyOperation;
import org.opends.server.core.Operation;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.synchronization.ModifyMsg;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.DN;
import org.opends.server.types.Modification;
import org.opends.server.types.ModificationType;
import org.opends.server.types.RDN;
import org.opends.server.util.TimeThread;
import static org.opends.server.synchronization.SynchMessages.SYNCHRONIZATION;
/**
 * Test the contructors, encoders and decoders of the synchronization
 * ModifyMsg, ModifyDnMsg, AddMsg and Delete Msg
 */
public class SynchronizationMsgTest extends SynchronizationTestCase
{
  /**
   * Build some data for the ModifyMsg test below.
   */
  @DataProvider(name = "modifyEncodeDecode")
  public Object[][] createData() {
    ChangeNumber cn1 = new ChangeNumber(1, (short) 0, (short) 1);
    ChangeNumber cn2 = new ChangeNumber(TimeThread.getTime(),
                                       (short) 123, (short) 45);
    AttributeType type = DirectoryServer.getAttributeType("description");
    Attribute attr1 = new Attribute("description", "new value");
    Modification mod1 = new Modification(ModificationType.REPLACE, attr1);
    List<Modification> mods1 = new ArrayList<Modification>();
    mods1.add(mod1);
    Attribute attr2 =
      new Attribute(DirectoryServer.getAttributeType("description", true));
    Modification mod2 = new Modification(ModificationType.DELETE, attr2);
    List<Modification> mods2 = new ArrayList<Modification>();
    mods2.add(mod1);
    mods2.add(mod2);
    List<Modification> mods3 = new ArrayList<Modification>();
    LinkedHashSet<AttributeValue> values3 =
                      new LinkedHashSet<AttributeValue>();
    values3.add(new AttributeValue(type, "string"));
    values3.add(new AttributeValue(type, "value"));
    values3.add(new AttributeValue(type, "again"));
    Attribute attr3 = new Attribute(type, "description", values3);
    Modification mod3 = new Modification(ModificationType.ADD, attr3);
    mods3.add(mod3);
    List<Modification> mods4 = new ArrayList<Modification>();
    for (int i =0; i< 10; i++)
    {
      LinkedHashSet<AttributeValue> values =
                      new LinkedHashSet<AttributeValue>();
      values.add(new AttributeValue(type, "string" + String.valueOf(i)));
      Attribute attr = new Attribute(type, "description", values);
      Modification mod = new Modification(ModificationType.ADD, attr);
      mods4.add(mod);
    }
    return new Object[][] {
        { cn1, "dc=test", mods1},
        { cn2, "dc=cn2", mods1},
        { cn2, "dc=test with a much longer dn in case this would "
               + "make a difference", mods1},
        { cn2, "dc=test, cn=with a, o=more complex, ou=dn", mods1},
        { cn2, "cn=use\\, backslash", mods1},
        { cn2, "dc=test with several mod", mods2},
        { cn2, "dc=test with several values", mods3},
        { cn2, "dc=test with long mod", mods4},
        };
  }
  /**
   * Create a ModifyMsg from the data provided above.
   * The call getBytes() to test the encoding of the Msg and
   * create another ModifyMsg from the encoded byte array.
   * Finally test that both Msg matches.
   */
  @Test(dataProvider = "modifyEncodeDecode")
  public void modifyEncodeDecode(ChangeNumber changeNumber,
                               String rawdn, List<Modification> mods)
         throws Exception
  {
    DN dn = DN.decode(rawdn);
    InternalClientConnection connection = new InternalClientConnection();
    ModifyMsg msg = new ModifyMsg(changeNumber, dn, mods);
    ModifyMsg generatedMsg = new ModifyMsg(msg.getBytes());
    assertEquals(msg.changeNumber, generatedMsg.changeNumber);
    Operation op = msg.createOperation(connection);
    Operation generatedOperation = generatedMsg.createOperation(connection);
    assertEquals(op.getClass(), ModifyOperation.class);
    assertEquals(generatedOperation.getClass(), ModifyOperation.class);
    ModifyOperation mod1 = (ModifyOperation) op;
    ModifyOperation mod2 = (ModifyOperation) generatedOperation;
    assertEquals(mod1.getRawEntryDN(), mod2.getRawEntryDN());
    /*
     * TODO : test that the generated mod equals the original mod.
     */
  }
  /**
   * Build some data for the DeleteMsg test below.
   * @throws DirectoryException
   */
  @DataProvider(name = "deleteEncodeDecode")
  public Object[][] createDelData() {
    return new Object[][] {
        {"dc=com"},
        {"dc=delete,dc=an,dc=entry,dc=with,dc=a,dc=long dn"},
        };
  }
  /**
   * Create a Delete from the data provided above.
   * The call getBytes() to test the encoding of the Msg and
   * create another DeleteMsg from the encoded byte array.
   * Finally test that both Msg matches.
   */
  @Test(dataProvider = "deleteEncodeDecode")
  public void deleteEncodeDecode(String rawDN)
         throws Exception
  {
    InternalClientConnection connection = new InternalClientConnection();
    DeleteOperation op = new DeleteOperation(connection, 1, 1,null,
                                             DN.decode(rawDN));
    ChangeNumber cn = new ChangeNumber(TimeThread.getTime(),
      (short) 123, (short) 45);
    op.setAttachment(SYNCHRONIZATION, cn);
    DeleteMsg msg = new DeleteMsg(op);
    DeleteMsg generatedMsg = new DeleteMsg(msg.getBytes());
    assertEquals(msg.changeNumber, generatedMsg.changeNumber);
    Operation generatedOperation = generatedMsg.createOperation(connection);
    assertEquals(generatedOperation.getClass(), DeleteOperation.class);
    DeleteOperation mod2 = (DeleteOperation) generatedOperation;
    assertEquals(op.getRawEntryDN(), mod2.getRawEntryDN());
  }
  @DataProvider(name = "modifyDnEncodeDecode")
  public Object[][] createModifyDnData() {
    return new Object[][] {
        {"dc=test,dc=com", "dc=new", false, "dc=change"},
        {"dc=test,dc=com", "dc=new", true, "dc=change"},
        // testNG does not like null argument so use "" for the newSuperior
        // instead of null
        {"dc=test,dc=com", "dc=new", false, ""},
        {"dc=delete,dc=an,dc=entry,dc=with,dc=a,dc=long dn",
                   "dc=new",true, ""},
        };
  }
  @Test(dataProvider = "modifyDnEncodeDecode")
  public void modifyDnEncodeDecode(String rawDN, String newRdn,
                                   boolean deleteOldRdn, String newSuperior)
         throws Exception
  {
    InternalClientConnection connection = new InternalClientConnection();
    ModifyDNOperation op =
      new ModifyDNOperation(connection, 1, 1, null,
                  DN.decode(rawDN), RDN.decode(newRdn), deleteOldRdn,
                  (newSuperior.length() != 0 ? DN.decode(newSuperior) : null));
    ChangeNumber cn = new ChangeNumber(TimeThread.getTime(),
                                      (short) 123, (short) 45);
    op.setAttachment(SYNCHRONIZATION, cn);
    ModifyDNMsg msg = new ModifyDNMsg(op);
    ModifyDNMsg generatedMsg = new ModifyDNMsg(msg.getBytes());
    Operation generatedOperation = generatedMsg.createOperation(connection);
    ModifyDNOperation mod2 = (ModifyDNOperation) generatedOperation;
    assertEquals(msg.changeNumber, generatedMsg.changeNumber);
    assertEquals(op.getRawEntryDN(), mod2.getRawEntryDN());
    assertEquals(op.getRawNewRDN(), mod2.getRawNewRDN());
    assertEquals(op.deleteOldRDN(), mod2.deleteOldRDN());
    assertEquals(op.getRawNewSuperior(), mod2.getRawNewSuperior());
  }
  @DataProvider(name = "addEncodeDecode")
  public Object[][] createAddData() {
    return new Object[][] {
        {"dc=test,dc=com"},
        };
  }
  @Test(dataProvider = "addEncodeDecode")
  public void addEncodeDecode(String rawDN)
         throws Exception
  {
    LinkedHashSet<AttributeValue> ocValues =
      new LinkedHashSet<AttributeValue>();
    ocValues.add(
        new AttributeValue(DirectoryServer.getObjectClassAttributeType(),
        "organization"));
    Attribute objectClass =
      new Attribute(DirectoryServer.getObjectClassAttributeType(),
        "objectClass", ocValues);
    AttributeType org = DirectoryServer.getAttributeType("o", true);
    ArrayList<Attribute> userAttributes = new ArrayList<Attribute>(1);
    LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>();
    values.add(new AttributeValue(org, "com"));
    Attribute attr = new Attribute(org, "o", values);
    userAttributes.add(attr);
    ArrayList<Attribute> operationalAttributes = new ArrayList<Attribute>(1);
    org = DirectoryServer.getAttributeType("creatorsname", true);
    values = new LinkedHashSet<AttributeValue>();
    values.add(new AttributeValue(org, "dc=creator"));
    attr = new Attribute(org, "creatorsname", values);
    operationalAttributes.add(attr);
    ChangeNumber cn = new ChangeNumber(TimeThread.getTime(),
                                      (short) 123, (short) 45);
    AddMsg msg = new AddMsg(cn, rawDN, objectClass, userAttributes,
                            operationalAttributes);
    AddMsg generatedMsg = new AddMsg(msg.getBytes());
    assertEquals(msg.getBytes(), generatedMsg.getBytes());
    // TODO : should test that generated attributes match original attributes.
  }
}
opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/SynchronizationTestCase.java
New file
@@ -0,0 +1,37 @@
/*
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  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
 *
 *
 *      Portions Copyright 2006 Sun Microsystems, Inc.
 */
package org.opends.server.synchronization;
import org.opends.server.DirectoryServerTestCase;
import org.testng.annotations.Test;
/**
 * An abstract class that all sycnhronization unit test should extend.
 */
@Test(groups = { "precommit", "synchronization" })
public abstract class SynchronizationTestCase extends DirectoryServerTestCase
{}