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

Valery Kharseko
8 hours ago f85d2d244205d32a39ff5d5f52e90ce2e10fc24a
[#910] Test that a ModifyDN conflict is still solved while server-error-result-code is one of the conflict codes (#938)
3 files modified
124 ■■■■ changed files
opendj-server-legacy/src/test/java/org/opends/server/replication/ReplicationTestCase.java 22 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/replication/UpdateOperationTest.java 17 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/replication/plugin/NamingConflictTest.java 85 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/replication/ReplicationTestCase.java
@@ -491,6 +491,28 @@
    addConfigEntry(synchroServerEntry, "Unable to add the synchronized server");
  }
  /**
   * Sets the result code this server puts on an internal error, the way an administrator
   * would, and asserts that the change was applied.
   * <p>
   * The setting is server-wide, so a test which changes it owns putting it back - after
   * a failure of its own as well, which is why the caller's tearDown() is the place for
   * that rather than a finally in the test body: an assertion failure raised while
   * restoring would otherwise replace the failure the test was reporting.
   *
   * @param resultCode the numeric result code
   * @throws Exception if the modification could not be run at all
   */
  protected static void setServerErrorResultCode(int resultCode) throws Exception
  {
    assertEquals(TestCaseUtils.applyModifications(true,
        "dn: cn=config",
        "changetype: modify",
        "replace: ds-cfg-server-error-result-code",
        "ds-cfg-server-error-result-code: " + resultCode), 0,
        "the server error result code could not be changed");
  }
  private void addConfigEntry(Entry configEntry, String errorMessage) throws Exception
  {
    if (configEntry != null)
opendj-server-legacy/src/test/java/org/opends/server/replication/UpdateOperationTest.java
@@ -2087,23 +2087,6 @@
  }
  /**
   * Sets the result code this server puts on an internal error, the way an administrator
   * would.
   *
   * @param resultCode the numeric result code
   * @throws Exception if the configuration could not be changed
   */
  private void setServerErrorResultCode(int resultCode) throws Exception
  {
    assertEquals(TestCaseUtils.applyModifications(true,
        "dn: cn=config",
        "changetype: modify",
        "replace: ds-cfg-server-error-result-code",
        "ds-cfg-server-error-result-code: " + resultCode), 0,
        "the server error result code could not be changed");
  }
  /**
   * Enable or disable the receive status of a synchronization provider.
   *
   * @param syncConfigDN The DN of the synchronization provider configuration
opendj-server-legacy/src/test/java/org/opends/server/replication/plugin/NamingConflictTest.java
@@ -13,6 +13,7 @@
 *
 * Copyright 2009-2010 Sun Microsystems, Inc.
 * Portions Copyright 2013-2016 ForgeRock AS.
 * Portions Copyright 2026 3A Systems, LLC.
 */
package org.opends.server.replication.plugin;
@@ -57,6 +58,17 @@
  private TestSynchronousReplayQueue queue;
  /**
   * The result code to put back in {@code ds-cfg-server-error-result-code}, or
   * {@code null} when this test did not change it.
   * <p>
   * The setting is server-wide, so it is put back by {@link #tearDown()} rather than by
   * the test which changed it: a method the harness kills on its timeout, or interrupts
   * inside a replay, would otherwise leave every later method of this class replaying
   * against a result code it never asked for.
   */
  private Integer serverErrorResultCodeToRestore;
  @BeforeMethod
  public void setUpLocal() throws Exception
  {
@@ -77,8 +89,20 @@
  @AfterMethod
  public void tearDown() throws Exception
  {
    try
    {
    MultimasterReplication.deleteDomain(baseDN);
  }
    finally
    {
      if (serverErrorResultCodeToRestore != null)
      {
        final int resultCode = serverErrorResultCodeToRestore;
        serverErrorResultCodeToRestore = null;
        setServerErrorResultCode(resultCode);
      }
    }
  }
  /**
   * Test for issue 3402 : test, that a modrdn that is older than an other
@@ -120,6 +144,67 @@
  }
  /**
   * Test case for [Issue 910]: a naming conflict which
   * {@code solveNamingConflict(ModifyDNOperation)} solves must still be solved while
   * {@code ds-cfg-server-error-result-code} is set to one of the result codes conflict
   * resolution owns.
   * <p>
   * That setting is a plain integer which is not validated as a result code, so it can be
   * one of them. Here it is {@code UNWILLING_TO_PERFORM}, which is what a ModifyDN whose
   * new superior is - on this replica - a subordinate of the entry being moved comes back
   * with, and only conflict resolution can turn such a change into an operation which
   * applies: it resolves both DNs again from the entryUUIDs the message carries. Reading
   * the code as a failure of the server would take the change away from it - the message
   * would never be rewritten, so no attempt would apply any better than the first - and
   * the change would be retried in place, delivered again and finally given up on, with
   * the entry left where it was.
   * <p>
   * {@code UpdateOperationTest.changeConflictResolutionCanNotSolveOnTheServerErrorCodeIsRetried}
   * covers the other half: a change which fails with that same code and which conflict
   * resolution can not solve is retried as the failure of the server it is.
   */
  @Test
  public void modifyDnConflictIsSolvedWhileTheServerErrorCodeIsOneOfTheConflictCodes() throws Exception
  {
    final Entry entry = createAndAddEntry("modDnOnConflictingServerErrorCode");
    final String entryUUID = getEntryUUID(entry.getName());
    final Entry newParent = TestCaseUtils.addEntry(
        "dn: ou=newParent," + TEST_ROOT_DN_STRING,
        "objectClass: top",
        "objectClass: organizationalUnit",
        "ou: newParent");
    final String newParentUUID = getEntryUUID(newParent.getName());
    // Remembered for tearDown() rather than restored here: the code which is in force,
    // which is the defined default unless the suite configured another one, and never a
    // value hardcoded by this test.
    serverErrorResultCodeToRestore =
        getServerContext().getCoreConfigManager().getServerErrorResultCode().intValue();
    setServerErrorResultCode(ResultCode.UNWILLING_TO_PERFORM.intValue());
    /*
     * The new superior as the master knew it: a DN which, here, is a subordinate of the
     * entry being moved - as it would be after that parent was renamed on this replica.
     * The operation reports UNWILLING_TO_PERFORM for as long as the message carries that
     * DN - ERR_MODDN_NEW_SUPERIOR_IN_SUBTREE, and the memory backend answers the same on
     * a new superior which is not in it - so the change is applied only if conflict
     * resolution gets to rewrite the message with the DNs the entryUUIDs resolve to here.
     */
    final String staleNewSuperior = "ou=newParent," + entry.getName();
    final CSN csn = gen.newCSN();
    replayMsg(new ModifyDNMsg(entry.getName(), csn, entryUUID, newParentUUID, false,
        staleNewSuperior, entry.getName().rdn().toString()));
    final DN resolvedDN = newParent.getName().child(entry.getName().rdn());
    assertTrue(entryExists(resolvedDN),
        "the naming conflict was not solved: no entry at " + resolvedDN);
    assertFalse(entryExists(entry.getName()), "the entry was not moved by the replayed ModifyDN");
    assertTrue(domain.getServerState().cover(csn),
        "a change which was applied 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.