Merge "CreateBranch: Test LOCK_FAILURE when branch got created by concurrent request"
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/CreateBranchIT.java b/javatests/com/google/gerrit/acceptance/rest/project/CreateBranchIT.java
index af8565e..1541850 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/CreateBranchIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/CreateBranchIT.java
@@ -23,7 +23,9 @@
 import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
 import static com.google.gerrit.testing.GerritJUnit.assertThrows;
 
+import com.google.common.collect.ImmutableList;
 import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.ExtensionRegistry;
 import com.google.gerrit.acceptance.RestResponse;
 import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
 import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
@@ -40,7 +42,16 @@
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.server.events.RefReceivedEvent;
+import com.google.gerrit.server.git.validators.RefOperationValidationListener;
+import com.google.gerrit.server.git.validators.ValidationMessage;
+import com.google.gerrit.server.validators.ValidationException;
 import com.google.inject.Inject;
+import java.io.IOException;
+import java.util.List;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.junit.Before;
 import org.junit.Test;
@@ -48,6 +59,7 @@
 public class CreateBranchIT extends AbstractDaemonTest {
   @Inject private ProjectOperations projectOperations;
   @Inject private RequestScopeOperations requestScopeOperations;
+  @Inject private ExtensionRegistry extensionRegistry;
 
   private BranchNameKey testBranch;
 
@@ -83,7 +95,49 @@
   @Test
   public void branchAlreadyExists_Conflict() throws Exception {
     assertCreateSucceeds(testBranch);
-    assertCreateFails(testBranch, ResourceConflictException.class);
+    assertCreateFails(
+        testBranch,
+        ResourceConflictException.class,
+        "branch \"" + testBranch.branch() + "\" already exists");
+  }
+
+  @Test
+  public void createBranch_LockFailure() throws Exception {
+    // check that the branch doesn't exist yet
+    assertThrows(ResourceNotFoundException.class, () -> branch(testBranch).get());
+
+    // Register a validation listener that creates the branch to simulate a concurrent request that
+    // creates the same branch.
+    try (ExtensionRegistry.Registration registration =
+        extensionRegistry
+            .newRegistration()
+            .add(
+                new RefOperationValidationListener() {
+                  @Override
+                  public List<ValidationMessage> onRefOperation(RefReceivedEvent refEvent)
+                      throws ValidationException {
+                    try (Repository repo = repoManager.openRepository(project)) {
+                      RefUpdate u = repo.updateRef(testBranch.branch());
+                      u.setExpectedOldObjectId(ObjectId.zeroId());
+                      u.setNewObjectId(repo.exactRef("refs/heads/master").getObjectId());
+                      RefUpdate.Result result = u.update();
+                      if (result != RefUpdate.Result.NEW) {
+                        throw new ValidationException(
+                            "Concurrent creation of branch failed: " + result);
+                      }
+                      return ImmutableList.of();
+                    } catch (IOException e) {
+                      throw new ValidationException("Concurrent creation of branch failed.", e);
+                    }
+                  }
+                })) {
+      // Creating the branch is expected to fail, since it is created by the validation listener
+      // right before the ref update to create the new branch is done.
+      assertCreateFails(
+          testBranch,
+          ResourceConflictException.class,
+          "branch \"" + testBranch.branch() + "\" already exists");
+    }
   }
 
   @Test
@@ -324,9 +378,4 @@
       assertThat(thrown).hasMessageThat().contains(errMsg);
     }
   }
-
-  private void assertCreateFails(BranchNameKey branch, Class<? extends RestApiException> errType)
-      throws Exception {
-    assertCreateFails(branch, errType, null);
-  }
 }