From 251f9586274ce5f121b0bb596fbb21e1ecd39442 Mon Sep 17 00:00:00 2001
From: Jean-Noël Rouvignac <jean-noel.rouvignac@forgerock.com>
Date: Fri, 09 Sep 2016 12:52:10 +0000
Subject: [PATCH] OPENDJ-3281 Modify operations may not be replayed if case is mixed on attribute values

---
 opendj-server-legacy/src/test/java/org/opends/server/replication/plugin/AttrHistoricalMultipleTest.java |   53 +++++-----
 opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/AttrHistorical.java             |   11 +-
 opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/AttrHistoricalSingle.java       |   27 ++++-
 opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/AttrHistoricalMultiple.java     |   47 +++++---
 opendj-server-legacy/src/test/java/org/opends/server/replication/plugin/AttrValueHistoricalTest.java    |   56 +++++++---
 opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/AttrValueHistorical.java        |   41 +++++++-
 opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/EntryHistorical.java            |    2 
 opendj-server-legacy/src/test/java/org/opends/server/replication/plugin/AttrHistoricalSingleTest.java   |   10 +
 8 files changed, 164 insertions(+), 83 deletions(-)

diff --git a/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/AttrHistorical.java b/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/AttrHistorical.java
index 60276aa..7b83f30 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/AttrHistorical.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/AttrHistorical.java
@@ -20,8 +20,8 @@
 import java.util.Set;
 
 import org.forgerock.opendj.ldap.ByteString;
-import org.opends.server.replication.common.CSN;
 import org.forgerock.opendj.ldap.schema.AttributeType;
+import org.opends.server.replication.common.CSN;
 import org.opends.server.types.Entry;
 import org.opends.server.types.Modification;
 
@@ -59,12 +59,12 @@
   /**
    * Create a new object from a provided attribute type. Historical is empty.
    *
-   * @param type the provided attribute type.
+   * @param attrType the provided attribute type.
    * @return a new AttributeInfo object.
    */
-  public static AttrHistorical createAttributeHistorical(AttributeType type)
+  public static AttrHistorical createAttributeHistorical(AttributeType attrType)
   {
-    return type.isSingleValue() ? new AttrHistoricalSingle() : new AttrHistoricalMultiple();
+    return attrType.isSingleValue() ? new AttrHistoricalSingle(attrType) : new AttrHistoricalMultiple();
   }
 
   /**
@@ -85,8 +85,9 @@
    * Assign the provided information to this object.
    *
    * @param histKey the key to assign.
+   * @param attrType the associated attribute type.
    * @param value   the associated value or null if there is no value;
    * @param csn     the associated CSN.
    */
-  public abstract void assign(HistAttrModificationKey histKey, ByteString value, CSN csn);
+  public abstract void assign(HistAttrModificationKey histKey, AttributeType attrType, ByteString value, CSN csn);
 }
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/AttrHistoricalMultiple.java b/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/AttrHistoricalMultiple.java
index cabbce4..463a382 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/AttrHistoricalMultiple.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/AttrHistoricalMultiple.java
@@ -147,21 +147,25 @@
    */
   void delete(Attribute attr, CSN csn)
   {
+    AttributeType attrType = attr.getAttributeDescription().getAttributeType();
     for (ByteString val : attr)
     {
-      delete(val, csn);
+      delete(val, attrType, csn);
     }
   }
 
    /**
-    * Update the historical of this attribute after a delete value.
-    *
-    * @param val value that was deleted
-    * @param csn time when the delete was done
-    */
-   void delete(ByteString val, CSN csn)
+   * Update the historical of this attribute after a delete value.
+   *
+   * @param val
+   *          value that was deleted
+   * @param attrType
+   * @param csn
+   *          time when the delete was done
+   */
+  void delete(ByteString val, AttributeType attrType, CSN csn)
    {
-     update(csn, new AttrValueHistorical(val, null, csn));
+     update(csn, new AttrValueHistorical(val, attrType, null, csn));
    }
 
   /**
@@ -174,9 +178,10 @@
    */
   private void add(Attribute attr, CSN csn)
   {
+    AttributeType attrType = attr.getAttributeDescription().getAttributeType();
     for (ByteString val : attr)
     {
-      add(val, csn);
+      add(val, attrType, csn);
     }
   }
 
@@ -185,12 +190,13 @@
      *
      * @param addedValue
      *          values that was added
+     * @param attrType
      * @param csn
      *          time when the value was added
      */
-   void add(ByteString addedValue, CSN csn)
+   void add(ByteString addedValue, AttributeType attrType, CSN csn)
    {
-     update(csn, new AttrValueHistorical(addedValue, csn, null));
+     update(csn, new AttrValueHistorical(addedValue, attrType, csn, null));
    }
 
   private void update(CSN csn, AttrValueHistorical valInfo)
@@ -425,13 +431,14 @@
       // we are processing DELETE of some attribute values
       AttributeBuilder builder = new AttributeBuilder(modAttr);
 
+      AttributeType attrType = modAttr.getAttributeDescription().getAttributeType();
       for (ByteString val : modAttr)
       {
         boolean deleteIt = true;  // true if the delete must be done
         boolean addedInCurrentOp = false;
 
         // update historical information
-        AttrValueHistorical valInfo = new AttrValueHistorical(val, null, csn);
+        AttrValueHistorical valInfo = new AttrValueHistorical(val, attrType, null, csn);
         AttrValueHistorical oldValInfo = valuesHist.get(valInfo);
         if (oldValInfo != null)
         {
@@ -516,10 +523,12 @@
       return false;
     }
 
-    AttributeBuilder builder = new AttributeBuilder(m.getAttribute());
-    for (ByteString addVal : m.getAttribute())
+    Attribute attribute = m.getAttribute();
+    AttributeBuilder builder = new AttributeBuilder(attribute);
+    AttributeType attrType = attribute.getAttributeDescription().getAttributeType();
+    for (ByteString addVal : attribute)
     {
-      AttrValueHistorical valInfo = new AttrValueHistorical(addVal, csn, null);
+      AttrValueHistorical valInfo = new AttrValueHistorical(addVal, attrType, csn, null);
       AttrValueHistorical oldValInfo = valuesHist.get(valInfo);
       if (oldValInfo == null)
       {
@@ -582,21 +591,21 @@
   }
 
   @Override
-  public void assign(HistAttrModificationKey histKey, ByteString value, CSN csn)
+  public void assign(HistAttrModificationKey histKey, AttributeType attrType, ByteString value, CSN csn)
   {
     switch (histKey)
     {
     case ADD:
       if (value != null)
       {
-        add(value, csn);
+        add(value, attrType, csn);
       }
       break;
 
     case DEL:
       if (value != null)
       {
-        delete(value, csn);
+        delete(value, attrType, csn);
       }
       break;
 
@@ -604,7 +613,7 @@
       delete(csn);
       if (value != null)
       {
-        add(value, csn);
+        add(value, attrType, csn);
       }
       break;
 
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/AttrHistoricalSingle.java b/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/AttrHistoricalSingle.java
index 6fd69bc..9543edb 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/AttrHistoricalSingle.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/AttrHistoricalSingle.java
@@ -24,9 +24,9 @@
 
 import org.forgerock.opendj.ldap.ByteString;
 import org.forgerock.opendj.ldap.ModificationType;
+import org.forgerock.opendj.ldap.schema.AttributeType;
 import org.opends.server.replication.common.CSN;
 import org.opends.server.types.Attribute;
-import org.forgerock.opendj.ldap.schema.AttributeType;
 import org.opends.server.types.Entry;
 import org.opends.server.types.Modification;
 
@@ -38,18 +38,31 @@
  */
 public class AttrHistoricalSingle extends AttrHistorical
 {
-  /** Last time when the attribute was deleted. */
-  private CSN deleteTime;
-  /** Last time when a value was added. */
-  private CSN addTime;
   /** Last added value. */
   private ByteString value;
+  /** Attribute type for this historical value */
+  private AttributeType attributeType;
+  /** Last time when a value was added. */
+  private CSN addTime;
+  /** Last time when the attribute was deleted. */
+  private CSN deleteTime;
   /**
    * Last operation applied. This is only used for multiple mods on the same
    * single valued attribute in the same modification.
    */
   private HistAttrModificationKey lastMod;
 
+  /**
+   * Builds an {@link AttrHistoricalSingle} object.
+   *
+   * @param attributeType
+   *          the attribute type for this historical value
+   */
+  public AttrHistoricalSingle(AttributeType attributeType)
+  {
+    this.attributeType = attributeType;
+  }
+
   @Override
   public CSN getDeleteTime()
   {
@@ -61,7 +74,7 @@
   {
     if (addTime != null)
     {
-      return Collections.singleton(new AttrValueHistorical(value, addTime, null));
+      return Collections.singleton(new AttrValueHistorical(value, attributeType, addTime, null));
     }
     return Collections.emptySet();
   }
@@ -262,7 +275,7 @@
   }
 
   @Override
-  public void assign(HistAttrModificationKey histKey, ByteString value, CSN csn)
+  public void assign(HistAttrModificationKey histKey, AttributeType attrType, ByteString value, CSN csn)
   {
     switch (histKey)
     {
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/AttrValueHistorical.java b/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/AttrValueHistorical.java
index 4422c20..463e2fb 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/AttrValueHistorical.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/AttrValueHistorical.java
@@ -12,30 +12,38 @@
  * information: "Portions Copyright [year] [name of copyright owner]".
  *
  * Copyright 2006-2010 Sun Microsystems, Inc.
- * Portions Copyright 2013-2015 ForgeRock AS.
+ * Portions Copyright 2013-2016 ForgeRock AS.
  */
 package org.opends.server.replication.plugin;
 
+import static org.forgerock.util.Reject.*;
+
 import org.forgerock.opendj.ldap.ByteString;
+import org.forgerock.opendj.ldap.DecodeException;
+import org.forgerock.opendj.ldap.schema.AttributeType;
 import org.opends.server.replication.common.CSN;
 
 /** AttrValueHistorical is the historical information of the modification of one attribute value. */
 public class AttrValueHistorical
 {
+  private AttributeType attributeType;
   private ByteString value;
+  private ByteString normalizedValue;
   private CSN valueDeleteTime;
   private CSN valueUpdateTime;
 
   /**
    * Build an AttrValueHistorical for a provided attribute value, providing
    * the last time the provided value is either updated or deleted.
-   * @param value    the provided attributeValue
+   * @param value    the provided attribute value
+   * @param attributeType the provided attribute type
    * @param csnUpdate last time when this value was updated
    * @param csnDelete last time when this value for deleted
    */
-  public AttrValueHistorical(ByteString value, CSN csnUpdate, CSN csnDelete)
+  public AttrValueHistorical(ByteString value, AttributeType attributeType, CSN csnUpdate, CSN csnDelete)
   {
     this.value = value;
+    this.attributeType = checkNotNull(attributeType);
     this.valueUpdateTime = csnUpdate;
     this.valueDeleteTime = csnDelete;
   }
@@ -46,7 +54,14 @@
     if (obj instanceof AttrValueHistorical)
     {
       AttrValueHistorical objVal = (AttrValueHistorical) obj;
-      return value.equals(objVal.getAttributeValue());
+      try
+      {
+        return getNormalizedValue().equals(objVal.getNormalizedValue());
+      }
+      catch (DecodeException e)
+      {
+        return value.equals(objVal.getAttributeValue());
+      }
     }
     return false;
   }
@@ -54,7 +69,23 @@
   @Override
   public int hashCode()
   {
-    return value.hashCode();
+    try
+    {
+      return getNormalizedValue().hashCode();
+    }
+    catch (DecodeException e)
+    {
+      return value.hashCode();
+    }
+  }
+
+  private ByteString getNormalizedValue() throws DecodeException
+  {
+    if (normalizedValue == null)
+    {
+      normalizedValue = attributeType.getEqualityMatchingRule().normalizeAttributeValue(value);
+    }
+    return normalizedValue;
   }
 
   /**
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/EntryHistorical.java b/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/EntryHistorical.java
index 0cb9c59..59d0ab3 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/EntryHistorical.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/EntryHistorical.java
@@ -581,7 +581,7 @@
               attrInfo = AttrHistorical.createAttributeHistorical(attrDesc.getAttributeType());
               newHistorical.attributesHistorical.put(attrDesc, attrInfo);
             }
-            attrInfo.assign(histVal.getHistKey(), histVal.getAttributeValue(), csn);
+            attrInfo.assign(histVal.getHistKey(), attrDesc.getAttributeType(), histVal.getAttributeValue(), csn);
           }
         }
       }
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/replication/plugin/AttrHistoricalMultipleTest.java b/opendj-server-legacy/src/test/java/org/opends/server/replication/plugin/AttrHistoricalMultipleTest.java
index 31b211f..1e13c7f 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/replication/plugin/AttrHistoricalMultipleTest.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/replication/plugin/AttrHistoricalMultipleTest.java
@@ -25,12 +25,12 @@
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Set;
 
 import org.forgerock.i18n.LocalizableMessageBuilder;
 import org.forgerock.opendj.ldap.ByteString;
 import org.forgerock.opendj.ldap.ModificationType;
 import org.forgerock.opendj.ldap.schema.AttributeType;
+import org.forgerock.opendj.ldap.schema.CoreSchema;
 import org.opends.server.TestCaseUtils;
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.replication.ReplicationTestCase;
@@ -113,52 +113,54 @@
 
   /** Create a AttrInfo and check the methods. */
   @Test(dataProvider = "attrInfo")
-  public void attrInfo(ByteString att, CSN deleteTime, CSN updateTime) throws Exception
+  public void attrInfo(ByteString attrValue, CSN deleteTime, CSN updateTime) throws Exception
   {
+    AttributeType attrType = CoreSchema.getDescriptionAttributeType();
     // Create an empty AttrInfo
     AttrHistoricalMultiple attrInfo1 = new AttrHistoricalMultiple();
 
     // Check
-    attrInfo1.add(att, updateTime);
-    Set<AttrValueHistorical> values1 = attrInfo1.getValuesHistorical();
-    assertEquals(values1.size(), 1);
-    AttrValueHistorical valueInfo1 = new AttrValueHistorical(att, updateTime, null);
-    assertTrue(values1.contains(valueInfo1));
+    attrInfo1.add(attrValue, attrType, updateTime);
+    assertThat(attrInfo1.getValuesHistorical())
+        .containsOnly(new AttrValueHistorical(attrValue, attrType, updateTime, null));
 
     // Check constructor with parameter
-    AttrValueHistorical valueInfo2 = new AttrValueHistorical(att, updateTime, deleteTime);
+    AttrValueHistorical valueInfo2 = new AttrValueHistorical(attrValue, attrType, updateTime, deleteTime);
     AttrHistoricalMultiple attrInfo2 = new AttrHistoricalMultiple(
-        deleteTime, updateTime, Collections.singletonMap(valueInfo2, valueInfo2));
+        deleteTime, updateTime, Collections.singleton(valueInfo2));
 
     // Check equality
     //assertTrue(attrInfo1.getDeleteTime().compareTo(attrInfo2.getDeleteTime())==0);
 
     //  Check constructor with time parameter and not Value
     AttrHistoricalMultiple attrInfo3 = new AttrHistoricalMultiple(deleteTime, updateTime, null);
-    attrInfo3.add(att, updateTime);
-    Set<AttrValueHistorical> values3 = attrInfo3.getValuesHistorical();
-    assertEquals(values3.size(), 1);
-    valueInfo1 = new AttrValueHistorical(att, updateTime, null);
-    assertTrue(values3.contains(valueInfo1));
+    attrInfo3.add(attrValue, attrType, updateTime);
+    assertThat(attrInfo3.getValuesHistorical())
+        .containsOnly(new AttrValueHistorical(attrValue, attrType, updateTime, null));
 
     // Check duplicate
-    AttrHistoricalMultiple attrInfo4 = attrInfo3.duplicate();
-    Set<AttrValueHistorical> values4 = attrInfo4.getValuesHistorical();
+    AttrHistoricalMultiple attrInfo4 = duplicate(attrInfo3);
     assertEquals(attrInfo4.getDeleteTime().compareTo(attrInfo3.getDeleteTime()), 0);
-    assertEquals(values4.size(), values3.size());
+    assertThat(attrInfo4.getValuesHistorical()).isEqualTo(attrInfo3.getValuesHistorical());
 
     // Check
-    attrInfo4.delete(att, updateTime);
+    attrInfo4.delete(attrValue, attrType, updateTime);
     assertEquals(attrInfo4.getValuesHistorical().size(), 1);
 
     // Check
     AttributeType type = DirectoryServer.getSchema().getAttributeType(ATTRIBUTE_NAME);
-    attrInfo3.delete(Attributes.create(type, att), updateTime) ;
+    attrInfo3.delete(Attributes.create(type, attrValue), updateTime);
     assertEquals(attrInfo3.getValuesHistorical().size(), 1);
 
     // Check
     attrInfo2.delete(updateTime);
-    assertEquals(attrInfo2.getValuesHistorical().size(), 0);
+    assertThat(attrInfo2.getValuesHistorical()).isEmpty();
+  }
+
+  private AttrHistoricalMultiple duplicate(AttrHistoricalMultiple attrMul)
+  {
+    return new AttrHistoricalMultiple(
+        attrMul.getDeleteTime(), attrMul.getLastUpdateTime(), attrMul.getValuesHistorical());
   }
 
   @Test
@@ -253,22 +255,23 @@
     assertAttributeValues(entry, "X");
   }
 
+  /** Use case: user changes the case of a family name for example. */
   @Test
   public void replay_addThenDeleteThenAdd_differentCaseWithCaseIgnoreAttributeType() throws Exception
   {
     CSN[] t = newCSNs(3);
 
-    mod = newModification(ADD, "X");
+    mod = newModification(ADD, "x");
     replayOperation(t[0], entry, mod, SUCCESS);
-    assertAttributeValues(entry, "X");
+    assertAttributeValues(entry, "x");
 
-    mod = newModification(DELETE, "x");
+    mod = newModification(DELETE, "X");
     replayOperation(t[1], entry, mod, SUCCESS);
     assertNoAttributeValue(entry);
 
     mod = newModification(ADD, "X");
-    replayOperationSuppressMod(t[2], entry, mod, CONFLICT);
-    assertAttributeValues(entry);
+    replayOperation(t[2], entry, mod, SUCCESS);
+    assertAttributeValues(entry, "X");
   }
 
   @Test
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/replication/plugin/AttrHistoricalSingleTest.java b/opendj-server-legacy/src/test/java/org/opends/server/replication/plugin/AttrHistoricalSingleTest.java
index 625a9d7..2afb988 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/replication/plugin/AttrHistoricalSingleTest.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/replication/plugin/AttrHistoricalSingleTest.java
@@ -25,6 +25,8 @@
 import org.forgerock.i18n.LocalizableMessageBuilder;
 import org.forgerock.opendj.ldap.ByteString;
 import org.forgerock.opendj.ldap.ModificationType;
+import org.forgerock.opendj.ldap.schema.AttributeType;
+import org.forgerock.opendj.ldap.schema.Schema;
 import org.opends.server.TestCaseUtils;
 import org.opends.server.replication.ReplicationTestCase;
 import org.opends.server.replication.common.CSN;
@@ -54,7 +56,8 @@
   @BeforeMethod
   public void localSetUp() throws Exception
   {
-    attrHist = new AttrHistoricalSingle();
+    AttributeType attrType = Schema.getDefaultSchema().getAttributeType(ATTRIBUTE_NAME);
+    attrHist = new AttrHistoricalSingle(attrType);
     csn = csnGen.newCSN();
     entry = TestCaseUtils.makeEntry(
         "dn: uid=test.user",
@@ -213,8 +216,9 @@
   @Test
   public void replay_deleteDubious() throws Exception
   {
-    HistoricalAttributeValue histAttrVal = new HistoricalAttributeValue("display:" + csn + ":add:X");
-    attrHist.assign(histAttrVal.getHistKey(), histAttrVal.getAttributeValue(), csn);
+    AttributeType attrType = Schema.getDefaultSchema().getAttributeType(ATTRIBUTE_NAME);
+    HistoricalAttributeValue histAttrVal = new HistoricalAttributeValue(ATTRIBUTE_NAME + ":" + csn + ":add:X");
+    attrHist.assign(histAttrVal.getHistKey(), attrType, histAttrVal.getAttributeValue(), csn);
     mod = newModification(ADD, "X");
     entry.applyModification(mod);
     assertAttributeValue(entry, "X");
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/replication/plugin/ValueInfoTest.java b/opendj-server-legacy/src/test/java/org/opends/server/replication/plugin/AttrValueHistoricalTest.java
similarity index 65%
rename from opendj-server-legacy/src/test/java/org/opends/server/replication/plugin/ValueInfoTest.java
rename to opendj-server-legacy/src/test/java/org/opends/server/replication/plugin/AttrValueHistoricalTest.java
index 1042bc2..1d3a85f 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/replication/plugin/ValueInfoTest.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/replication/plugin/AttrValueHistoricalTest.java
@@ -12,29 +12,27 @@
  * information: "Portions Copyright [year] [name of copyright owner]".
  *
  * Copyright 2006-2010 Sun Microsystems, Inc.
- * Portions Copyright 2013-2015 ForgeRock AS.
+ * Portions Copyright 2013-2016 ForgeRock AS.
  */
 package org.opends.server.replication.plugin;
 
+import static org.assertj.core.api.Assertions.*;
+import static org.testng.Assert.*;
+
+import org.forgerock.opendj.ldap.ByteString;
+import org.forgerock.opendj.ldap.schema.AttributeType;
+import org.forgerock.opendj.ldap.schema.CoreSchema;
 import org.opends.server.replication.ReplicationTestCase;
 import org.opends.server.replication.common.CSN;
-import org.forgerock.opendj.ldap.ByteString;
 import org.opends.server.util.TimeThread;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
-import static org.testng.Assert.*;
-
-/**
- * Test of ValueInfo.
- */
+/** Test of AttrValueHistorical. */
 @SuppressWarnings("javadoc")
-public class ValueInfoTest extends ReplicationTestCase
+public class AttrValueHistoricalTest extends ReplicationTestCase
 {
-
-  /**
-   * Build some data for the ValueInfo test below.
-   */
+  /** Build some data for the ValueInfo test below. */
   @DataProvider(name = "valueInfo")
   public Object[][] createData() {
     ByteString att1 = ByteString.valueOfUtf8("string");
@@ -56,16 +54,15 @@
         {att3,upd3,del3}};
   }
 
-  /**
-   * Create a ValueInfo and check the methods.
-   */
+  /** Create a ValueInfo and check the methods. */
   @Test(dataProvider = "valueInfo")
   public void valueInfo(ByteString value, CSN csnUpdate, CSN csnDelete) throws Exception
   {
-    AttrValueHistorical valInfo1 = new AttrValueHistorical(value, csnUpdate, csnDelete);
-    AttrValueHistorical valInfo2 = new AttrValueHistorical(value, csnUpdate, csnUpdate);
+    AttributeType attrType = CoreSchema.getDescriptionAttributeType();
+    AttrValueHistorical valInfo1 = new AttrValueHistorical(value, attrType, csnUpdate, csnDelete);
+    AttrValueHistorical valInfo2 = new AttrValueHistorical(value, attrType, csnUpdate, csnUpdate);
     AttrValueHistorical valInfo3 = new AttrValueHistorical(ByteString.valueOfUtf8("Test"),
-            csnUpdate, csnUpdate);
+            attrType, csnUpdate, csnUpdate);
 
     // Check equals
     assertFalse(valInfo1.equals(new Object()));
@@ -87,4 +84,27 @@
     assertEquals(valInfo1.getAttributeValue(), value);
     assertEquals(valInfo1.isUpdate(), csnUpdate != null);
   }
+
+  @Test
+  public void testEqualsHashCode()
+  {
+    final AttrValueHistorical histVal1 = newAttrValueHistorical("description");
+    final AttrValueHistorical histVal2 = newAttrValueHistorical("Description");
+    final AttrValueHistorical histVal3 = newAttrValueHistorical("another description");
+
+    assertThat(histVal1).isEqualTo(histVal1);
+    assertThat(histVal1).isEqualTo(histVal2);
+    assertThat(histVal2).isEqualTo(histVal1);
+
+    assertThat(histVal1).isNotEqualTo(histVal3);
+
+    assertThat(histVal1.hashCode()).isEqualTo(histVal2.hashCode());
+    assertThat(histVal1.hashCode()).isNotEqualTo(histVal3.hashCode());
+  }
+
+  private AttrValueHistorical newAttrValueHistorical(String pp)
+  {
+    AttributeType attrType = CoreSchema.getDescriptionAttributeType();
+    return new AttrValueHistorical(ByteString.valueOfUtf8(pp), attrType, null, null);
+  }
 }

--
Gitblit v1.10.0