Test exception handling and dry run mode of CodeOwnerConfigValidator

This increases the test coverage of CodeOwnerConfigValidator from 89.9%
to 93.5%.

Signed-off-by: Edwin Kempin <ekempin@google.com>
Change-Id: I7a2f5f307a0616e00c5f359a29cf277d790e9a72
diff --git a/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CodeOwnerConfigValidatorIT.java b/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CodeOwnerConfigValidatorIT.java
index c9f7916..d402a43 100644
--- a/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CodeOwnerConfigValidatorIT.java
+++ b/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CodeOwnerConfigValidatorIT.java
@@ -30,11 +30,15 @@
 import com.google.gerrit.entities.BranchNameKey;
 import com.google.gerrit.entities.Permission;
 import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.Project.NameKey;
 import com.google.gerrit.extensions.api.projects.ConfigInput;
 import com.google.gerrit.extensions.client.ChangeStatus;
 import com.google.gerrit.extensions.client.ProjectState;
 import com.google.gerrit.extensions.common.ChangeInput;
 import com.google.gerrit.extensions.common.MergeInput;
+import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.registration.PrivateInternals_DynamicMapImpl;
+import com.google.gerrit.extensions.registration.RegistrationHandle;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.git.ObjectIds;
 import com.google.gerrit.plugins.codeowners.acceptance.AbstractCodeOwnersIT;
@@ -43,16 +47,24 @@
 import com.google.gerrit.plugins.codeowners.backend.CodeOwnerConfigImportMode;
 import com.google.gerrit.plugins.codeowners.backend.CodeOwnerConfigImportType;
 import com.google.gerrit.plugins.codeowners.backend.CodeOwnerConfigReference;
+import com.google.gerrit.plugins.codeowners.backend.CodeOwnerConfigUpdate;
 import com.google.gerrit.plugins.codeowners.backend.CodeOwnerResolver;
 import com.google.gerrit.plugins.codeowners.backend.CodeOwnerSet;
+import com.google.gerrit.plugins.codeowners.backend.PathExpressionMatcher;
 import com.google.gerrit.plugins.codeowners.backend.config.BackendConfig;
 import com.google.gerrit.plugins.codeowners.backend.findowners.FindOwnersBackend;
 import com.google.gerrit.plugins.codeowners.backend.findowners.FindOwnersCodeOwnerConfigParser;
 import com.google.gerrit.plugins.codeowners.backend.proto.ProtoBackend;
 import com.google.gerrit.plugins.codeowners.backend.proto.ProtoCodeOwnerConfigParser;
+import com.google.gerrit.server.IdentifiedUser;
 import com.google.inject.Inject;
+import com.google.inject.Key;
+import com.google.inject.util.Providers;
+import java.nio.file.Path;
+import java.util.Optional;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.revwalk.RevWalk;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -67,6 +79,7 @@
   private BackendConfig backendConfig;
   private FindOwnersCodeOwnerConfigParser findOwnersCodeOwnerConfigParser;
   private ProtoCodeOwnerConfigParser protoCodeOwnerConfigParser;
+  private DynamicMap<CodeOwnerBackend> codeOwnerBackends;
 
   @Before
   public void setUpCodeOwnersPlugin() throws Exception {
@@ -75,6 +88,8 @@
         plugin.getSysInjector().getInstance(FindOwnersCodeOwnerConfigParser.class);
     protoCodeOwnerConfigParser =
         plugin.getSysInjector().getInstance(ProtoCodeOwnerConfigParser.class);
+    codeOwnerBackends =
+        plugin.getSysInjector().getInstance(new Key<DynamicMap<CodeOwnerBackend>>() {});
   }
 
   @Test
@@ -1844,6 +1859,57 @@
     assertThat(gApi.changes().id(r.getChangeId()).get().status).isEqualTo(ChangeStatus.MERGED);
   }
 
+  @Test
+  @GerritConfig(name = "plugin.code-owners.backend", value = FailingCodeOwnerBackend.ID)
+  public void pushFailsOnInternalError() throws Exception {
+    try (AutoCloseable registration = registerTestBackend(new FailingCodeOwnerBackend())) {
+      PushOneCommit.Result r = createChange("Add code owners", "OWNERS", "content");
+      r.assertErrorStatus("internal error");
+    }
+  }
+
+  @Test
+  @GerritConfig(name = "plugin.code-owners.backend", value = FailingCodeOwnerBackend.ID)
+  @GerritConfig(name = "plugin.code-owners.enableValidationOnCommitReceived", value = "DRY_RUN")
+  public void pushSucceedsOnInternalErrorIfValidationIsDoneAsDryRun() throws Exception {
+    try (AutoCloseable registration = registerTestBackend(new FailingCodeOwnerBackend())) {
+      PushOneCommit.Result r = createChange("Add code owners", "OWNERS", "content");
+      r.assertOkStatus();
+    }
+  }
+
+  @Test
+  @GerritConfig(name = "plugin.code-owners.backend", value = FailingCodeOwnerBackend.ID)
+  public void submitFailsOnInternalError() throws Exception {
+    try (AutoCloseable registration = registerTestBackend(new FailingCodeOwnerBackend())) {
+      disableCodeOwnersForProject(project);
+      PushOneCommit.Result r = createChange("Add code owners", "OWNERS", "content");
+      r.assertOkStatus();
+      enableCodeOwnersForProject(project);
+      approve(r.getChangeId());
+      IllegalStateException exception =
+          assertThrows(
+              IllegalStateException.class,
+              () -> gApi.changes().id(r.getChangeId()).current().submit());
+      assertThat(exception).hasMessageThat().isEqualTo(FailingCodeOwnerBackend.EXCEPTION_MESSAGE);
+    }
+  }
+
+  @Test
+  @GerritConfig(name = "plugin.code-owners.backend", value = FailingCodeOwnerBackend.ID)
+  @GerritConfig(name = "plugin.code-owners.enableValidationOnSubmit", value = "DRY_RUN")
+  public void submitSucceedsOnInternalErrorIfValidationIsDoneAsDryRun() throws Exception {
+    try (AutoCloseable registration = registerTestBackend(new FailingCodeOwnerBackend())) {
+      disableCodeOwnersForProject(project);
+      PushOneCommit.Result r = createChange("Add code owners", "OWNERS", "content");
+      r.assertOkStatus();
+      enableCodeOwnersForProject(project);
+      approve(r.getChangeId());
+      gApi.changes().id(r.getChangeId()).current().submit();
+      assertThat(gApi.changes().id(r.getChangeId()).get().status).isEqualTo(ChangeStatus.MERGED);
+    }
+  }
+
   private CodeOwnerConfig createCodeOwnerConfigWithImport(
       CodeOwnerConfig.Key keyOfImportingCodeOwnerConfig,
       CodeOwnerConfigImportType importType,
@@ -1993,4 +2059,45 @@
     pushResult.assertNotMessage("warning");
     pushResult.assertNotMessage("hint");
   }
+
+  private AutoCloseable registerTestBackend(CodeOwnerBackend codeOwnerBackend) {
+    RegistrationHandle registrationHandle =
+        ((PrivateInternals_DynamicMapImpl<CodeOwnerBackend>) codeOwnerBackends)
+            .put("gerrit", FailingCodeOwnerBackend.ID, Providers.of(codeOwnerBackend));
+    return registrationHandle::remove;
+  }
+
+  private static class FailingCodeOwnerBackend implements CodeOwnerBackend {
+    static final String ID = "test-backend";
+    static final String EXCEPTION_MESSAGE = "failure from test";
+
+    @Override
+    public boolean isCodeOwnerConfigFile(NameKey project, String fileName) {
+      throw new IllegalStateException(EXCEPTION_MESSAGE);
+    }
+
+    @Override
+    public Optional<CodeOwnerConfig> getCodeOwnerConfig(
+        CodeOwnerConfig.Key codeOwnerConfigKey, RevWalk revWalk, ObjectId revision) {
+      return Optional.empty();
+    }
+
+    @Override
+    public Path getFilePath(CodeOwnerConfig.Key codeOwnerConfigKey) {
+      return codeOwnerConfigKey.filePath("OWNERS");
+    }
+
+    @Override
+    public Optional<CodeOwnerConfig> upsertCodeOwnerConfig(
+        CodeOwnerConfig.Key codeOwnerConfigKey,
+        CodeOwnerConfigUpdate codeOwnerConfigUpdate,
+        IdentifiedUser currentUser) {
+      return Optional.empty();
+    }
+
+    @Override
+    public Optional<PathExpressionMatcher> getPathExpressionMatcher() {
+      return Optional.empty();
+    }
+  }
 }