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

lutoff
08.42.2006 9e87cf54bdad6b73da706a9adc8e31a2b467d224
opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/ModifyConflictTest.java
@@ -31,18 +31,17 @@
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
import static org.opends.server.synchronization.OperationContext.*;
import org.opends.server.SchemaFixture;
import org.opends.server.core.AddOperation;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ModifyOperation;
import org.opends.server.protocols.asn1.ASN1OctetString;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
@@ -54,89 +53,206 @@
import org.opends.server.types.ObjectClass;
/*
 * 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
 * Test the conflict resolution for modify operations As a consequence,
 * this will also test the Historical.java Class 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
public class ModifyConflictTest
    extends SynchronizationTestCase
{
  /**
   * Test that conflict between a modify-replace and modify-add
   * for multi-valued attributes are handled correctly.
   * Test that conflict between a modify-replace and modify-add for
   * multi-valued attributes@DataProvider(name = "ackMsg") are handled
   * correctly.
   */
  @Test()
  public void replaceAndAdd()
         throws Exception
  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);
    // Construct a new random UUID. and add it into the entry
    UUID uuid = UUID.randomUUID();
    // Create the att values list of uuid
    LinkedHashSet<AttributeValue> valuesUuid = new LinkedHashSet<AttributeValue>(
        1);
    valuesUuid.add(new AttributeValue(Historical.entryuuidAttrType,
        new ASN1OctetString(uuid.toString())));
    ArrayList<Attribute> uuidList = new ArrayList<Attribute>(1);
    Attribute uuidAttr = new Attribute(Historical.entryuuidAttrType,
        "entryUUID", valuesUuid);
    uuidList.add(uuidAttr);
    /*
     * Add the uuid in the entry
     */
    Map<AttributeType, List<Attribute>> operationalAttributes = entry
        .getOperationalAttributes();
    operationalAttributes.put(Historical.entryuuidAttrType, uuidList);
    // Create the att values list of historicalAttr
    String stringVal =
      "ds-sync-hist:00000108b3a6cbb800000001:repl:00000108b3a6cbb800000002";
  AttributeValue val = new AttributeValue(Historical.historicalAttrType,
      stringVal);
    LinkedHashSet<AttributeValue> valuesHist =
      new LinkedHashSet<AttributeValue>(1);
    valuesHist.add(val);
    ArrayList<Attribute> histList = new ArrayList<Attribute>(1);
    Attribute histAttr = new Attribute(Historical.historicalAttrType,
        "ds-sync-hist", valuesHist);
    histList.add(histAttr);
    //Add the historical att in the entry
    operationalAttributes.put(Historical.historicalAttrType,histList) ;
    // load historical from the entry
    Historical hist = Historical.load(entry);
    /*
     * simulate a modify-replace done at time t10
     */
    testModify(entry, hist, "description", ModificationType.REPLACE,
               "init value", 10, true);
        "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);
        "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...)
     * conflict resolution should remove it. (a second time to make
     * sure...)
     */
    testModify(entry, hist, "description", ModificationType.ADD,
               "older value", 2, false);
        "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);
    testModify(entry, hist, "description", ModificationType.ADD, "new value",
        11, true);
  }
  /*
   * helper function.
   */
  private void testHistoricalAndFake(
      Historical hist, Entry entry)
  {
    // Get the historical uuid associated to the entry
    // (the one that needs to be tested)
    String uuid = hist.getEntryUuid(entry);
    // Get the Entry uuid in String format
    List<Attribute> uuidAttrs = entry
        .getOperationalAttribute(Historical.entryuuidAttrType);
    uuidAttrs.get(0).getValues().iterator().next().toString();
    if (uuidAttrs != null)
    {
      if (uuidAttrs.size() > 0)
      {
        Attribute att = uuidAttrs.get(0);
        String retrievedUuid = (att.getValues().iterator().next()).toString();
        assertTrue(retrievedUuid.equals(uuid));
      }
    }
    try
    {
      Historical dup = hist.duplicate();
      // TODO Check values
    }
    catch (RuntimeException e)
    {
      assertTrue(false) ;
    }
    // Test FakeOperation
    try
    {
      Iterable<FakeOperation> fks = Historical.generateFakeOperations(entry);
      if (fks.iterator().hasNext())
      {
        FakeOperation fk = fks.iterator().next();
        assertTrue(new FakeOperationComparator().compare(fk, fk) == 0);
        assertTrue(new FakeOperationComparator().compare(null , fk) < 0);
        SynchronizationMessage generatedMsg = fk.generateMessage() ;
        if (generatedMsg instanceof UpdateMessage)
        {
          UpdateMessage new_name = (UpdateMessage) generatedMsg;
          assertEquals(new_name.getUniqueId(),uuid);
        }
      }
    }
    catch (RuntimeException e)
    {
      assertTrue(false) ;
    }
  }
  /*
   * helper function.
   */
  private void testHistorical(
      Historical hist, AddOperation addOp)
  {
    // Get the historical uuid associated to the entry
    // (the one that needs to be tested)
    String uuid = hist.getEntryUuid(addOp);
    // Get the op uuid in String format
    List<Attribute> uuidAttrs = addOp.getOperationalAttributes().get(
        Historical.entryuuidAttrType);
    uuidAttrs.get(0).getValues().iterator().next().toString();
    if (uuidAttrs != null)
    {
      if (uuidAttrs.size() > 0)
      {
        Attribute att = uuidAttrs.get(0);
        String retrievedUuid = (att.getValues().iterator().next()).toString();
        assertTrue(retrievedUuid.equals(uuid));
      }
    }
  }
  /**
   * Test that conflict between a modify-delete-attribute and modify-add
   * for multi-valued attributes are handled correctly.
   */
  @Test()
  public void deleteAndAdd()
         throws Exception
  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.
     * 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>();
@@ -147,95 +263,174 @@
     * start with a new entry with an empty description
     */
    Entry entry = new Entry(dn, objectClasses, null, null);
    // Construct a new random UUID. and add it into the entry
    UUID uuid = UUID.randomUUID();
    // Create the att values list
    LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>(
        1);
    values.add(new AttributeValue(Historical.entryuuidAttrType,
        new ASN1OctetString(uuid.toString())));
    ArrayList<Attribute> uuidList = new ArrayList<Attribute>(1);
    Attribute uuidAttr = new Attribute(Historical.entryuuidAttrType,
        "entryUUID", values);
    uuidList.add(uuidAttr);
    /*
     * Add the uuid in the entry
     */
    Map<AttributeType, List<Attribute>> operationalAttributes = entry
        .getOperationalAttributes();
    operationalAttributes.put(Historical.entryuuidAttrType, uuidList);
    // Create the att values list of historicalAttr
    String stringVal =
      "ds-sync-hist:00000108b3a6cbb800000001:del:00000108b3a6cbb800000002";
  AttributeValue val = new AttributeValue(Historical.historicalAttrType,
      stringVal);
    LinkedHashSet<AttributeValue> valuesHist =
      new LinkedHashSet<AttributeValue>(1);
    valuesHist.add(val);
    ArrayList<Attribute> histList = new ArrayList<Attribute>(1);
    Attribute histAttr = new Attribute(Historical.historicalAttrType,
        "ds-sync-hist", valuesHist);
    //Add the historical att in the entry
    entry.putAttribute(Historical.historicalAttrType,histList) ;
    // load historical from the entry
    Historical hist = Historical.load(entry);
    /*
     * simulate a delete of the whole description attribute done at time t10
     * simulate a delete of the whole description attribute done at time
     * t10
     */
    testModify(entry, hist, "description", ModificationType.DELETE,
               null, 10, true);
    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...)
     * 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", 2, false);
        "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);
    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");
   * 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);
    /*
     * start with a new entry with an empty description
     */
    Entry entry = new Entry(dn, objectClasses, null, null);
   /*
    * simulate a add of the description attribute done at time t10
    */
   testModify(entry, hist, "description", ModificationType.ADD,
              "init value", 10, true);
    // Construct a new random UUID. and add it into the entry
    UUID uuid = UUID.randomUUID();
   /*
    * 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);
    // Create the att values list
    LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>(
        1);
    values.add(new AttributeValue(Historical.entryuuidAttrType,
        new ASN1OctetString(uuid.toString())));
    ArrayList<Attribute> uuidList = new ArrayList<Attribute>(1);
    Attribute uuidAttr = new Attribute(Historical.entryuuidAttrType,
        "entryUUID", values);
    uuidList.add(uuidAttr);
   /*
    * 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);
    /*
     * Add the uuid in the entry
     */
    Map<AttributeType, List<Attribute>> operationalAttributes = entry
        .getOperationalAttributes();
   /*
    * 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);
 }
    operationalAttributes.put(Historical.entryuuidAttrType, uuidList);
    // Create the att values list of historicalAttr
    String stringVal =
      "ds-sync-hist:00000108b3a6cbb800000001:add:00000108b3a6cbb800000002";
  AttributeValue val = new AttributeValue(Historical.historicalAttrType,
      stringVal);
    LinkedHashSet<AttributeValue> valuesHist =
      new LinkedHashSet<AttributeValue>(1);
    valuesHist.add(val);
    ArrayList<Attribute> histList = new ArrayList<Attribute>(1);
    Attribute histAttr = new Attribute(Historical.historicalAttrType,
        "ds-sync-hist", valuesHist);
    histList.add(histAttr);
    //Add the historycal att in the entry
    entry.putAttribute(Historical.historicalAttrType,histList) ;
    // load historical from the entry
    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,
  private void testModify(Entry entry,
      Historical hist, String attrName,
      ModificationType modType, String value,
      int date, boolean keepChangeResult)
@@ -254,17 +449,27 @@
    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);
        entry.getDN(), mods);
    ModifyContext ctx = new ModifyContext(t, "uniqueId");
    modOp.setAttachment(SYNCHROCONTEXT, ctx);
    hist.replayOperation(modOp, entry);
    if (modType.intValue() == ModificationType.ADD.intValue())
    {
      AddOperation addOp = new AddOperation(connection, 1, 1, null, entry
          .getDN(), entry.getObjectClasses(), entry.getUserAttributes(),
          entry.getOperationalAttributes());
      testHistorical(hist, addOp);
    }
    else
    {
      testHistoricalAndFake(hist, entry);
    }
    /*
     * The last older change should have been detected as conflicting
     * and should be removed by the conflict resolution code.
     * The last older change should have been detected as conflicting and
     * should be removed by the conflict resolution code.
     */
    if (keepChangeResult)
    {
@@ -277,27 +482,4 @@
      assertEquals(0, mods.size());
    }
  }
  /**
   * Set up the environment for performing the tests in this suite.
   *
   * @throws Exception
   *           If the environment could not be set up.
   */
  @BeforeClass
  public void setUp() throws Exception {
    // This test suite depends on having the schema available.
    SchemaFixture.FACTORY.setUp();
  }
  /**
   * Tears down the environment for performing the tests in this suite.
   *
   * @throws Exception
   *           If the environment could not be finalized.
   */
  @AfterClass
  public void tearDown() throws Exception {
    SchemaFixture.FACTORY.tearDown();
  }
}