From 77d37889d8e3b08055a7639db5b3c01465ab2750 Mon Sep 17 00:00:00 2001
From: Jean-Noel Rouvignac <jean-noel.rouvignac@forgerock.com>
Date: Wed, 19 Aug 2015 14:10:08 +0000
Subject: [PATCH] OPENDJ-1192 Modify request replay failures
---
opendj-server-legacy/src/test/java/org/opends/server/replication/plugin/AttrHistoricalMultipleTest.java | 436 +++++++++++++++++++++++++++++
/dev/null | 121 --------
opendj-server-legacy/src/test/java/org/opends/server/replication/plugin/AttrHistoricalSingleTest.java | 327 +++++++++++++++++++++
3 files changed, 763 insertions(+), 121 deletions(-)
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
new file mode 100644
index 0000000..01916ce
--- /dev/null
+++ b/opendj-server-legacy/src/test/java/org/opends/server/replication/plugin/AttrHistoricalMultipleTest.java
@@ -0,0 +1,436 @@
+/*
+ * 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 2006-2010 Sun Microsystems, Inc.
+ * Portions Copyright 2013-2015 ForgeRock AS.
+ */
+package org.opends.server.replication.plugin;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.forgerock.opendj.ldap.ModificationType.*;
+import static org.mockito.Mockito.*;
+import static org.testng.Assert.*;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.forgerock.opendj.ldap.ByteString;
+import org.forgerock.opendj.ldap.ModificationType;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.replication.ReplicationTestCase;
+import org.opends.server.replication.common.CSN;
+import org.opends.server.replication.common.CSNGenerator;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.Attributes;
+import org.opends.server.types.Entry;
+import org.opends.server.types.Modification;
+import org.opends.server.util.TimeThread;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+/** Test AttrHistoricalMultiple. */
+@SuppressWarnings("javadoc")
+public class AttrHistoricalMultipleTest extends ReplicationTestCase
+{
+ private static enum E
+ {
+ CONFLICT(true), CONFLICT_BUT_SHOULD_NOT_BE(true), NO_CONFLICT(false);
+
+ private final boolean expectedConflictStatus;
+
+ private E(boolean expectedResultForReplay)
+ {
+ this.expectedConflictStatus = expectedResultForReplay;
+ }
+
+ private boolean getExpectedResult()
+ {
+ return this.expectedConflictStatus;
+ }
+ }
+
+ private CSNGenerator csnGen = new CSNGenerator(1025, System.currentTimeMillis());
+ private AttrHistoricalMultiple attrHist;
+ private CSN csn;
+ private Entry entry;
+ /** Avoids declaring the variable in the tests. */
+ private Modification mod;
+
+ @BeforeMethod
+ public void localSetUp() throws Exception
+ {
+ attrHist = new AttrHistoricalMultiple();
+ csn = csnGen.newCSN();
+ entry = new Entry(null, null, null, null);
+ }
+
+ @AfterMethod
+ public void localTearDown() throws Exception
+ {
+ attrHist = null;
+ csn = null;
+ }
+
+ /** Build some data for the AttrInfo test below. */
+ @DataProvider(name = "attrInfo")
+ public Object[][] createData()
+ {
+ ByteString att1 = ByteString.valueOf("string");
+ ByteString att2 = ByteString.valueOf("value");
+ ByteString att3 = ByteString.valueOf("again");
+
+ CSN del1 = new CSN(1, 0, 1);
+ CSN del2 = new CSN(1, 1, 1);
+ CSN del3 = new CSN(1, 0, 2);
+
+ CSN upd1 = new CSN(TimeThread.getTime(), 123, 45);
+ CSN upd2 = new CSN(TimeThread.getTime() + 1000, 123, 45);
+ CSN upd3 = new CSN(TimeThread.getTime(), 321, 54);
+
+ return new Object[][]
+ {
+ { att1, del1, upd1 },
+ { att2, del2, upd2 },
+ { att3, del3, upd3 },
+ { att3, upd3, upd3 } };
+ }
+
+ /** Create a AttrInfo and check the methods. */
+ @Test(dataProvider = "attrInfo")
+ public void attrInfo(ByteString att, CSN deleteTime, CSN updateTime) throws Exception
+ {
+ // 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));
+
+ // Check constructor with parameter
+ AttrValueHistorical valueInfo2 = new AttrValueHistorical(att, updateTime, deleteTime);
+ AttrHistoricalMultiple attrInfo2 = new AttrHistoricalMultiple(
+ deleteTime, updateTime, Collections.singletonMap(valueInfo2, 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));
+
+ // Check duplicate
+ AttrHistoricalMultiple attrInfo4 = attrInfo3.duplicate();
+ Set<AttrValueHistorical> values4 = attrInfo4.getValuesHistorical();
+ assertEquals(attrInfo4.getDeleteTime().compareTo(attrInfo3.getDeleteTime()), 0);
+ assertEquals(values4.size(), values3.size());
+
+ // Check
+ attrInfo4.delete(att, updateTime);
+ assertEquals(attrInfo4.getValuesHistorical().size(), 1);
+
+ // Check
+ AttributeType type = DirectoryServer.getAttributeType("description");
+ attrInfo3.delete(Attributes.create(type, att), updateTime) ;
+ assertEquals(attrInfo3.getValuesHistorical().size(), 1);
+
+ // Check
+ attrInfo2.delete(updateTime);
+ assertEquals(attrInfo2.getValuesHistorical().size(), 0);
+ }
+
+ @Test
+ public void replay_addDeleteSameTime() throws Exception
+ {
+ mod = newModification(ADD, "X");
+ replayOperation(csn, entry, mod, E.CONFLICT_BUT_SHOULD_NOT_BE);
+
+ mod = newModification(DELETE, "X");
+ replayOperation(csn, entry, mod, E.CONFLICT_BUT_SHOULD_NOT_BE);
+ }
+
+ @Test
+ public void replay_addThenAddThenOlderDelete() throws Exception
+ {
+ CSN[] t = newCSNs(3);
+
+ mod = newModification(ADD, "X");
+ replayOperation(t[0], entry, mod, E.CONFLICT_BUT_SHOULD_NOT_BE);
+
+ mod = newModification(ADD, "Y");
+ replayOperation(t[2], entry, mod, E.CONFLICT_BUT_SHOULD_NOT_BE);
+
+ mod = newModification(DELETE, "Y");
+ replayOperationSuppressMod(t[1], entry, mod, E.CONFLICT_BUT_SHOULD_NOT_BE);
+ }
+
+ @Test
+ public void replay_addThenDeleteNoValueThenOlderAdd() throws Exception
+ {
+ CSN[] t = newCSNs(3);
+
+ replay_addDeleteNoValue(t[0], t[2]);
+
+ mod = newModification(ADD, "Y");
+ replayOperationSuppressMod(t[1], entry, mod, E.CONFLICT_BUT_SHOULD_NOT_BE);
+ }
+
+ @Test
+ public void replay_addThenDeleteWithValueThenOlderAdd() throws Exception
+ {
+ CSN[] t = newCSNs(3);
+
+ mod = newModification(ADD, "X");
+ replayOperation(t[0], entry, mod, E.CONFLICT_BUT_SHOULD_NOT_BE);
+
+ mod = newModification(DELETE, "X");
+ replayOperation(t[2], entry, mod, E.CONFLICT_BUT_SHOULD_NOT_BE);
+
+ mod = newModification(ADD, "X");
+ replayOperationSuppressMod(t[1], entry, mod, E.CONFLICT_BUT_SHOULD_NOT_BE);
+ }
+
+ @Test
+ public void replay_addThenAdd() throws Exception
+ {
+ CSN[] t = newCSNs(2);
+
+ mod = newModification(ADD, "X");
+ replayOperation(t[0], entry, mod, E.CONFLICT_BUT_SHOULD_NOT_BE);
+
+ mod = newModification(ADD, "X");
+ replayOperationSuppressMod(t[1], entry, mod, E.CONFLICT);
+ }
+
+ @Test
+ public void replay_addThenDeleteThenAdd() throws Exception
+ {
+ CSN[] t = newCSNs(3);
+
+ mod = newModification(ADD, "X");
+ replayOperation(t[0], entry, mod, E.CONFLICT_BUT_SHOULD_NOT_BE);
+
+ mod = newModification(DELETE, "X");
+ replayOperation(t[1], entry, mod, E.CONFLICT_BUT_SHOULD_NOT_BE);
+
+ mod = newModification(ADD, "X");
+ replayOperation(t[1], entry, mod, E.CONFLICT);
+ }
+
+ @Test
+ public void replay_deleteNoPreviousHistory() throws Exception
+ {
+ mod = newModification(DELETE, "Y");
+ replayOperationSuppressMod(csn, entry, mod, E.CONFLICT);
+ }
+
+ @Test
+ public void replay_addThenDelete() throws Exception
+ {
+ CSN[] t = newCSNs(2);
+
+ mod = newModification(ADD, "X");
+ replayOperation(t[0], entry, mod, E.CONFLICT_BUT_SHOULD_NOT_BE);
+
+ mod = newModification(DELETE, "X");
+ replayOperation(t[1], entry, mod, E.CONFLICT_BUT_SHOULD_NOT_BE);
+ }
+
+ @Test
+ public void replay_addThenDeleteThenOlderDelete() throws Exception
+ {
+ CSN[] t = newCSNs(3);
+
+ replay_addDeleteNoValue(t[0], t[2]);
+
+ mod = newModification(DELETE, "X");
+ replayOperationSuppressMod(t[1], entry, mod, E.CONFLICT_BUT_SHOULD_NOT_BE);
+ }
+
+ @Test
+ public void replay_addDeleteNoValueSameTimeNotConflict() throws Exception
+ {
+ replay_addDeleteNoValue(csn, csn);
+ }
+
+ @Test
+ public void replay_addThenDeleteNoValue() throws Exception
+ {
+ CSN[] t = newCSNs(2);
+ replay_addDeleteNoValue(t[0], t[1]);
+ }
+
+ private void replay_addDeleteNoValue(CSN tAdd, CSN tDel) throws Exception
+ {
+ mod = newModification(ADD, "X");
+ replayOperation(tAdd, entry, mod, E.CONFLICT_BUT_SHOULD_NOT_BE);
+
+ mod = newModification(DELETE);
+ replayOperation(tDel, entry, mod, E.CONFLICT_BUT_SHOULD_NOT_BE);
+ }
+
+ @Test
+ public void replay_replace() throws Exception
+ {
+ mod = newModification(REPLACE, "X");
+ replayOperation(csn, entry, mod, E.NO_CONFLICT);
+ }
+
+ @Test
+ public void replay_addThenOlderReplace() throws Exception
+ {
+ CSN[] t = newCSNs(2);
+
+ mod = newModification(ADD, "X");
+ replayOperation(t[1], entry, mod, E.CONFLICT_BUT_SHOULD_NOT_BE);
+
+ mod = newModification(REPLACE, "Y");
+ replayOperation(t[0], entry, mod, E.CONFLICT_BUT_SHOULD_NOT_BE);
+ }
+
+ @Test
+ public void replay_addThenDeleteThenOlderReplace() throws Exception
+ {
+ CSN[] t = newCSNs(3);
+
+ replay_addDeleteNoValue(t[0], t[2]);
+
+ mod = newModification(REPLACE, "Y");
+ replayOperationSuppressMod(t[1], entry, mod, E.CONFLICT_BUT_SHOULD_NOT_BE);
+ }
+
+ private CSN[] newCSNs(int nb)
+ {
+ CSN[] results = new CSN[nb];
+ for (int i = 0; i < nb; i++)
+ {
+ results[i] = csnGen.newCSN();
+ }
+ return results;
+ }
+
+ private Modification newModification(ModificationType modType, String attrValue)
+ {
+ return new Modification(modType, Attributes.create("description", attrValue));
+ }
+
+ private Modification newModification(ModificationType modType)
+ {
+ return new Modification(modType, Attributes.empty("display"));
+ }
+
+ private void replayOperationSuppressMod(CSN csn, Entry entry, Modification mod, E conflictStatus)
+ throws Exception
+ {
+ Iterator<Modification> itMod = mock(Iterator.class);
+ replayOperation(itMod, csn, entry, mod, conflictStatus);
+ verifyModNotReplayed(itMod);
+ }
+
+ private void replayOperation(CSN csn, Entry entry, Modification mod, E conflictStatus) throws Exception
+ {
+ replayOperation(null, csn, entry, mod, conflictStatus);
+ }
+
+ private void replayOperation(Iterator<Modification> modsIterator, CSN csn, Entry entry, Modification mod,
+ E conflictStatus) throws Exception
+ {
+ boolean result = attrHist.replayOperation(modsIterator, csn, entry, mod);
+ assertEquals(result, conflictStatus.getExpectedResult(),
+ "Expected " + (conflictStatus == E.CONFLICT ? "a" : "no") + " conflict when applying " + mod + " to " + entry);
+ if (entry != null && conflictStatus != E.CONFLICT)
+ {
+ entry.applyModification(mod);
+ assertAttributeValues(entry, mod);
+ }
+ }
+
+ private void assertAttributeValues(Entry entry, Modification mod)
+ {
+ List<ByteString> actualValues = getValues(entry, mod);
+ List<ByteString> expectedValues = getValues(mod.getAttribute());
+ switch (mod.getModificationType().asEnum())
+ {
+ case ADD:
+ assertThat(actualValues).containsAll(expectedValues);
+ return;
+
+ case REPLACE:
+ assertThat(actualValues).isEqualTo(expectedValues);
+ return;
+
+ case DELETE:
+ if (expectedValues.isEmpty())
+ {
+ assertThat(actualValues).isEmpty();
+ }
+ else
+ {
+ assertThat(actualValues).doesNotContainAnyElementsOf(expectedValues);
+ }
+ return;
+
+ case INCREMENT:
+ return;
+ }
+ }
+
+ private List<ByteString> getValues(Entry entry, Modification mod)
+ {
+ List<Attribute> attributes = entry.getAttribute(mod.getAttribute().getAttributeType());
+ if (attributes != null)
+ {
+ assertThat(attributes).hasSize(1);
+ return getValues(attributes.get(0));
+ }
+ return Collections.emptyList();
+ }
+
+ private List<ByteString> getValues(Attribute attribute)
+ {
+ List<ByteString> results = new ArrayList<>();
+ for (ByteString value : attribute)
+ {
+ results.add(value);
+ }
+ return results;
+ }
+
+ private void verifyModNotReplayed(Iterator<Modification> it)
+ {
+ verify(it, times(1)).remove();
+ verify(it, only()).remove();
+ }
+}
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
new file mode 100644
index 0000000..113c588
--- /dev/null
+++ b/opendj-server-legacy/src/test/java/org/opends/server/replication/plugin/AttrHistoricalSingleTest.java
@@ -0,0 +1,327 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
+ * or http://forgerock.org/license/CDDLv1.0.html.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at legal-notices/CDDLv1_0.txt.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ * Copyright 2015 ForgeRock AS
+ */
+package org.opends.server.replication.plugin;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.forgerock.opendj.ldap.ModificationType.*;
+import static org.mockito.Mockito.*;
+import static org.testng.Assert.*;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.forgerock.opendj.ldap.ByteString;
+import org.forgerock.opendj.ldap.ModificationType;
+import org.opends.server.replication.ReplicationTestCase;
+import org.opends.server.replication.common.CSN;
+import org.opends.server.replication.common.CSNGenerator;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.Attributes;
+import org.opends.server.types.Entry;
+import org.opends.server.types.Modification;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+@SuppressWarnings("javadoc")
+public class AttrHistoricalSingleTest extends ReplicationTestCase
+{
+ private static final boolean CONFLICT = true;
+ private static final boolean NO_CONFLICT = false;
+ private CSNGenerator csnGen = new CSNGenerator(1025, System.currentTimeMillis());
+ private AttrHistoricalSingle attrHist;
+ private CSN csn;
+ private Entry entry;
+ /** Avoids declaring the variable in the tests */
+ private Modification mod;
+
+ @BeforeMethod
+ public void localSetUp() throws Exception
+ {
+ attrHist = new AttrHistoricalSingle();
+ csn = csnGen.newCSN();
+ entry = new Entry(null, null, null, null);
+ }
+
+ @AfterMethod
+ public void localTearDown() throws Exception
+ {
+ attrHist = null;
+ csn = null;
+ }
+
+ @Test
+ public void replay_addDeleteSameTime() throws Exception
+ {
+ mod = newModification(ADD, "X");
+ replayOperationSuccess(csn, entry, mod, NO_CONFLICT);
+
+ mod = newModification(DELETE, "X");
+ replayOperationSuccess(csn, entry, mod, NO_CONFLICT);
+ }
+
+ @Test
+ public void replay_addThenDeleteThenOlderAdd() throws Exception
+ {
+ CSN[] t = newCSNs(3);
+
+ mod = newModification(ADD, "X");
+ replayOperationSuccess(t[0], entry, mod, NO_CONFLICT);
+
+ mod = newModification(DELETE, "X");
+ replayOperationSuccess(t[2], entry, mod, NO_CONFLICT);
+
+ mod = newModification(ADD, "X");
+ replayOperationFailure(t[1], entry, mod, CONFLICT);
+ }
+
+ @Test
+ public void replay_addThenDeleteThenAddThenOlderAdd() throws Exception
+ {
+ CSN[] t = newCSNs(4);
+
+ mod = newModification(ADD, "X");
+ replayOperationSuccess(t[0], entry, mod, NO_CONFLICT);
+
+ mod = newModification(DELETE, "X");
+ replayOperationSuccess(t[1], entry, mod, NO_CONFLICT);
+
+ mod = newModification(ADD, "X");
+ replayOperationSuccess(t[3], entry, mod, NO_CONFLICT);
+
+ mod = newModification(ADD, "Y");
+ replayOperationSuccess(t[2], entry, mod, CONFLICT);
+ }
+
+ @Test
+ public void replay_addThenDeleteThenDelete() throws Exception
+ {
+ CSN[] t = newCSNs(3);
+
+ mod = newModification(ADD, "X");
+ replayOperationSuccess(t[0], entry, mod, NO_CONFLICT);
+
+ mod = newModification(DELETE, "X");
+ replayOperationSuccess(t[1], entry, mod, NO_CONFLICT);
+
+ mod = newModification(DELETE, "X");
+ replayOperationFailure(t[2], entry, mod, CONFLICT);
+ }
+
+ @Test
+ public void replay_addThenOlderDelete() throws Exception
+ {
+ CSN[] t = newCSNs(2);
+
+ mod = newModification(ADD, "X");
+ replayOperationSuccess(t[1], null, mod, NO_CONFLICT);
+
+ mod = newModification(DELETE, "X");
+ replayOperationFailure(t[0], entry, mod, CONFLICT);
+ }
+
+ @Test
+ public void replay_deleteMissingAttribute() throws Exception
+ {
+ mod = newModification(DELETE, "X");
+ replayOperationFailure(csn, entry, mod, CONFLICT);
+ }
+
+ @Test
+ public void replay_deleteMissingAttributeValue() throws Exception
+ {
+ CSN[] t = newCSNs(2);
+
+ mod = newModification(ADD, "X");
+ replayOperationSuccess(t[0], entry, mod, NO_CONFLICT);
+
+ Iterator<Modification> it = mock(Iterator.class);
+ mod = newModification(DELETE, "Y");
+ replayOperationFailure(t[1], entry, mod, CONFLICT);
+
+ verifyModNotReplayed(it);
+ }
+
+ /**
+ * TODO JNR this looks dubious, is it ever possible in the server?
+ * <p>
+ * Could multi-threading make this scenario possible?
+ * <p>
+ * Or is it due to {@link AttrHistoricalSingle#assign(HistAttrModificationKey, ByteString, CSN)} ?
+ */
+ @Test
+ public void replay_deleteValueThatDoesNotExistOnEntry() throws Exception
+ {
+ CSN[] t = newCSNs(2);
+
+ mod = newModification(ADD, "X");
+ entry.applyModification(mod);
+
+ mod = newModification(DELETE, "Y");
+ replayOperationFailure(t[1], entry, mod, CONFLICT);
+ }
+
+ /**
+ * TODO JNR this looks dubious, is it ever possible in the server?
+ * <p>
+ * Could multi-threading make this scenario possible?
+ * <p>
+ * Or is it due to {@link AttrHistoricalSingle#assign(HistAttrModificationKey, ByteString, CSN)} ?
+ */
+ @Test
+ public void replay_deleteDubious() throws Exception
+ {
+ HistoricalAttributeValue histAttrVal = new HistoricalAttributeValue("display:" + csn + ":add:X");
+ attrHist.assign(histAttrVal.getHistKey(), histAttrVal.getAttributeValue(), csn);
+ mod = newModification(ADD, "X");
+ entry.applyModification(mod);
+
+ mod = newModification(DELETE, "Y");
+ replayOperationFailure(csn, entry, mod, CONFLICT);
+ }
+
+ @Test
+ public void replay_replaceWithValue() throws Exception
+ {
+ mod = newModification(REPLACE, "X");
+ replayOperationSuccess(csn, null, mod, NO_CONFLICT);
+ }
+
+ @Test
+ public void replay_replaceNoValue() throws Exception
+ {
+ mod = newModification(REPLACE);
+ replayOperationSuccess(csn, null, mod, NO_CONFLICT);
+ }
+
+ @Test
+ public void replay_addThenDeleteThenOlderReplace() throws Exception
+ {
+ CSN[] t = newCSNs(3);
+
+ mod = newModification(ADD, "X");
+ replayOperationSuccess(t[0], entry, mod, NO_CONFLICT);
+
+ mod = newModification(DELETE);
+ replayOperationSuccess(t[2], entry, mod, NO_CONFLICT);
+
+ mod = newModification(REPLACE);
+ replayOperationFailure(t[1], entry, mod, CONFLICT);
+ }
+
+ @Test
+ public void replay_increment() throws Exception
+ {
+ mod = newModification(INCREMENT, "X");
+ replayOperationSuccess(csn, null, mod, NO_CONFLICT);
+ }
+
+
+ private CSN[] newCSNs(int nb)
+ {
+ CSN[] results = new CSN[nb];
+ for (int i = 0; i < nb; i++)
+ {
+ results[i] = csnGen.newCSN();
+ }
+ return results;
+ }
+
+ private Modification newModification(ModificationType modType, String attrValue)
+ {
+ return new Modification(modType, Attributes.create("display", attrValue));
+ }
+
+ private Modification newModification(ModificationType modType)
+ {
+ return new Modification(modType, Attributes.empty("display"));
+ }
+
+ private void replayOperationSuccess(CSN csn, Entry entry, Modification mod, boolean shouldConflict) throws Exception
+ {
+ replayOperation(null, csn, entry, mod, shouldConflict);
+ }
+
+ private void replayOperationFailure(CSN csn, Entry entry, Modification mod, boolean shouldConflict) throws Exception
+ {
+ Iterator<Modification> itMod = mock(Iterator.class);
+ replayOperation(itMod, csn, entry, mod, shouldConflict);
+ verifyModNotReplayed(itMod);
+ }
+
+ private void replayOperation(Iterator<Modification> modsIterator, CSN csn, Entry entry, Modification mod,
+ boolean shouldConflict) throws Exception
+ {
+ boolean result = attrHist.replayOperation(modsIterator, csn, entry, mod);
+ assertEquals(result, shouldConflict,
+ "Expected " + (shouldConflict ? "a" : "no") + " conflict when applying " + mod + " to " + entry);
+ if (entry != null && !shouldConflict)
+ {
+ entry.applyModification(mod);
+ assertAttributeValue(entry, mod);
+ }
+ }
+
+ private void assertAttributeValue(Entry entry, Modification mod)
+ {
+ ByteString actualValue = getActualValue(entry, mod);
+ switch (mod.getModificationType().asEnum())
+ {
+ case ADD:
+ case REPLACE:
+ ByteString expectedValue = uniqueValue(mod.getAttribute());
+ assertEquals(actualValue, expectedValue);
+ return;
+
+ case DELETE:
+ assertNull(actualValue);
+ return;
+ }
+ }
+
+ private ByteString getActualValue(Entry entry, Modification mod)
+ {
+ List<Attribute> attributes = entry.getAttribute(mod.getAttribute().getAttributeType());
+ if (attributes != null)
+ {
+ assertThat(attributes).hasSize(1);
+ return uniqueValue(attributes.get(0));
+ }
+ return null;
+ }
+
+ private ByteString uniqueValue(Attribute attribute)
+ {
+ assertThat(attribute).hasSize(1);
+ ByteString attrValue = attribute.iterator().next();
+ return attrValue;
+ }
+
+ private void verifyModNotReplayed(Iterator<Modification> it)
+ {
+ verify(it, times(1)).remove();
+ verify(it, only()).remove();
+ }
+}
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/replication/plugin/AttrInfoTest.java b/opendj-server-legacy/src/test/java/org/opends/server/replication/plugin/AttrInfoTest.java
deleted file mode 100644
index 98aae65..0000000
--- a/opendj-server-legacy/src/test/java/org/opends/server/replication/plugin/AttrInfoTest.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * 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 2006-2010 Sun Microsystems, Inc.
- * Portions Copyright 2013-2015 ForgeRock AS.
- */
-package org.opends.server.replication.plugin;
-
-import java.util.Collections;
-import java.util.Set;
-
-import org.forgerock.opendj.ldap.ByteString;
-import org.opends.server.core.DirectoryServer;
-import org.opends.server.replication.ReplicationTestCase;
-import org.opends.server.replication.common.CSN;
-import org.opends.server.types.AttributeType;
-import org.opends.server.types.Attributes;
-import org.opends.server.util.TimeThread;
-import org.testng.annotations.DataProvider;
-import org.testng.annotations.Test;
-
-import static org.testng.Assert.*;
-
-/** Test AttrInfo and AttrInfoWithOptions. */
-@SuppressWarnings("javadoc")
-public class AttrInfoTest extends ReplicationTestCase
-{
- /** Build some data for the AttrInfo test below. */
- @DataProvider(name = "attrInfo")
- public Object[][] createData()
- {
- ByteString att1 = ByteString.valueOf("string");
- ByteString att2 = ByteString.valueOf("value");
- ByteString att3 = ByteString.valueOf("again");
-
- CSN del1 = new CSN(1, 0, 1);
- CSN del2 = new CSN(1, 1, 1);
- CSN del3 = new CSN(1, 0, 2);
-
- CSN upd1 = new CSN(TimeThread.getTime(), 123, 45);
- CSN upd2 = new CSN(TimeThread.getTime() + 1000, 123, 45);
- CSN upd3 = new CSN(TimeThread.getTime(), 321, 54);
-
- return new Object[][]
- {
- { att1, del1, upd1 },
- { att2, del2, upd2 },
- { att3, del3, upd3 },
- { att3, upd3, upd3 } };
- }
-
- /** Create a AttrInfo and check the methods. */
- @Test(dataProvider = "attrInfo")
- public void attrInfo(ByteString att, CSN deleteTime, CSN updateTime) throws Exception
- {
- // 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));
-
- // Check constructor with parameter
- AttrValueHistorical valueInfo2 = new AttrValueHistorical(att, updateTime, deleteTime);
- AttrHistoricalMultiple attrInfo2 = new AttrHistoricalMultiple(
- deleteTime, updateTime, Collections.singletonMap(valueInfo2, 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));
-
- // Check duplicate
- AttrHistoricalMultiple attrInfo4 = attrInfo3.duplicate();
- Set<AttrValueHistorical> values4 = attrInfo4.getValuesHistorical();
- assertEquals(attrInfo4.getDeleteTime().compareTo(attrInfo3.getDeleteTime()), 0);
- assertEquals(values4.size(), values3.size());
-
- // Check
- attrInfo4.delete(att, updateTime);
- assertEquals(attrInfo4.getValuesHistorical().size(), 1);
-
- // Check
- AttributeType type = DirectoryServer.getAttributeType("description");
- attrInfo3.delete(Attributes.create(type, att), updateTime) ;
- assertEquals(attrInfo3.getValuesHistorical().size(), 1);
-
- // Check
- attrInfo2.delete(updateTime);
- assertEquals(attrInfo2.getValuesHistorical().size(), 0);
- }
-}
--
Gitblit v1.10.0