From f3076a0a7b60fa444dbaea5a69f40ab423562765 Mon Sep 17 00:00:00 2001
From: Valery Kharseko <vharseko@3a-systems.ru>
Date: Thu, 10 Sep 2026 13:23:09 +0000
Subject: [PATCH] [#955] Answer a ModifyDN whose entry is gone before the new superior is looked up (#965)

---
 opendj-server-legacy/src/test/java/org/opends/server/replication/plugin/NamingConflictTest.java |   40 ++++++++++++++++++++++++++++++++++++++++
 1 files changed, 40 insertions(+), 0 deletions(-)

diff --git a/opendj-server-legacy/src/test/java/org/opends/server/replication/plugin/NamingConflictTest.java b/opendj-server-legacy/src/test/java/org/opends/server/replication/plugin/NamingConflictTest.java
index 65c4498..cb74306 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/replication/plugin/NamingConflictTest.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/replication/plugin/NamingConflictTest.java
@@ -205,6 +205,46 @@
   }
 
   /**
+   * Test case for [Issue 955]: a ModifyDN whose entry and whose new superior are both
+   * gone from this replica is a conflict between a delete and this ModifyDN, and it is
+   * solved as such rather than left to be delivered again until this replica gives up on
+   * it.
+   * <p>
+   * Neither entryUUID the message carries resolves to a DN here, so conflict resolution
+   * has no new superior to move the entry under - and no entry to move either. The entry
+   * having been deleted settles what the ModifyDN was trying to do, which is what makes
+   * the change resolved: an entry which is not in the database can not be marked as
+   * conflicting, and marking it is what used to be attempted first, on the DN of an entry
+   * which is not there.
+   */
+  @Test
+  public void modifyDnOnAnEntryAndANewSuperiorWhichAreBothGone() throws Exception
+  {
+    final Entry entry = createAndAddEntry("modDnOnEntryAndNewSuperiorBothGone");
+    final String entryUUID = getEntryUUID(entry.getName());
+
+    final Entry newSuperior = TestCaseUtils.addEntry(
+        "dn: ou=newSuperiorBothGone," + TEST_ROOT_DN_STRING,
+        "objectClass: top",
+        "objectClass: organizationalUnit",
+        "ou: newSuperiorBothGone");
+    final String newSuperiorUUID = getEntryUUID(newSuperior.getName());
+
+    // Both entries are deleted on this replica while the ModifyDN is on its way.
+    TestCaseUtils.deleteEntry(newSuperior.getName());
+    TestCaseUtils.deleteEntry(entry.getName());
+
+    final CSN csn = gen.newCSN();
+    replayMsg(new ModifyDNMsg(entry.getName(), csn, entryUUID, newSuperiorUUID, false,
+        newSuperior.getName().toString(), entry.getName().rdn().toString()));
+
+    assertFalse(entryExists(entry.getName()),
+        "the deleted entry was brought back by the replayed ModifyDN");
+    assertTrue(domain.getServerState().cover(csn),
+        "a ModifyDN which the delete of its entry has settled must be recorded as replayed");
+  }
+
+  /**
    * Test that when a previous conflict is resolved because
    * a delete operation has removed one of the conflicting entries
    * the other conflicting entry is correctly renamed to its original name.

--
Gitblit v1.10.0