Test skipping code owner config validation when reverting via API

Signed-off-by: Edwin Kempin <ekempin@google.com>
Change-Id: I1678f7ec4eebce1434240426e40e97a76ed5fb2f
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 5b7ab3d..0800de1 100644
--- a/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CodeOwnerConfigValidatorIT.java
+++ b/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CodeOwnerConfigValidatorIT.java
@@ -40,6 +40,7 @@
 import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.extensions.api.changes.CherryPickInput;
 import com.google.gerrit.extensions.api.changes.RebaseInput;
+import com.google.gerrit.extensions.api.changes.RevertInput;
 import com.google.gerrit.extensions.api.projects.BranchInput;
 import com.google.gerrit.extensions.api.projects.ConfigInput;
 import com.google.gerrit.extensions.client.ChangeStatus;
@@ -2465,6 +2466,113 @@
   }
 
   @Test
+  public void skipValidationForRevert() throws Exception {
+    // Make admin a code owner so that admin can code-owner approve changes.
+    setAsRootCodeOwners(admin);
+
+    // Create a code owner config file with a non-resolvable code owner.
+    CodeOwnerConfig.Key codeOwnerConfigKey = createCodeOwnerConfigKey("/foo/");
+    String unknownEmail = "non-existing-email@example.com";
+    PushOneCommit push =
+        pushFactory.create(
+            admin.newIdent(),
+            testRepo,
+            "Add code owner config file with non-resolvable code owner",
+            codeOwnerConfigOperations.codeOwnerConfig(codeOwnerConfigKey).getJGitFilePath(),
+            format(
+                CodeOwnerConfig.builder(codeOwnerConfigKey, TEST_REVISION)
+                    .addCodeOwnerSet(CodeOwnerSet.createWithoutPathExpressions(unknownEmail))
+                    .build()));
+    push.setPushOptions(
+        ImmutableList.of(
+            String.format("code-owners~%s", SkipCodeOwnerConfigValidationPushOption.NAME)));
+    PushOneCommit.Result r = push.to("refs/for/master");
+    assertOkWithHints(
+        r,
+        "skipping validation of code owner config files",
+        String.format(
+            "the validation is skipped due to the --code-owners~%s push option",
+            SkipCodeOwnerConfigValidationPushOption.NAME));
+    approve(r.getChangeId());
+    gApi.changes().id(r.getChangeId()).current().submit();
+    assertThat(gApi.changes().id(r.getChangeId()).get().status).isEqualTo(ChangeStatus.MERGED);
+
+    // Fix the code owner config file.
+    PushOneCommit.Result r2 =
+        createChange(
+            "Fix code owner config file",
+            codeOwnerConfigOperations.codeOwnerConfig(codeOwnerConfigKey).getJGitFilePath(),
+            format(
+                CodeOwnerConfig.builder(codeOwnerConfigKey, TEST_REVISION)
+                    .addCodeOwnerSet(CodeOwnerSet.createWithoutPathExpressions(admin.email()))
+                    .build()));
+    assertOkWithHints(r2, "code owner config files validated, no issues found");
+    approve(r2.getChangeId());
+    gApi.changes().id(r2.getChangeId()).current().submit();
+    assertThat(gApi.changes().id(r2.getChangeId()).get().status).isEqualTo(ChangeStatus.MERGED);
+
+    // Try reverting the fix, expect failure due to invalid code owner config file.
+    RevertInput revertInput = new RevertInput();
+    ResourceConflictException exception =
+        assertThrows(
+            ResourceConflictException.class,
+            () -> gApi.changes().id(r2.getChangeId()).revert(revertInput));
+    assertThat(exception)
+        .hasMessageThat()
+        .isEqualTo(
+            String.format(
+                "[code-owners] invalid code owner config files:\n"
+                    + "  [code-owners] code owner email '%s' in '%s' cannot be resolved for %s",
+                unknownEmail,
+                codeOwnerConfigOperations.codeOwnerConfig(codeOwnerConfigKey).getFilePath(),
+                admin.username()));
+
+    // Revert the fix and skip the validation
+    revertInput.validationOptions =
+        ImmutableMap.of(
+            String.format("code-owners~%s", SkipCodeOwnerConfigValidationPushOption.NAME), "true");
+    gApi.changes().id(r.getChangeId()).revert(revertInput);
+  }
+
+  @Test
+  public void userWithoutCapabilitySkipValidationCannotSkipValidationWithRevert() throws Exception {
+    // Make admin a code owner so that admin can code-owner approve changes.
+    setAsRootCodeOwners(admin);
+
+    // Create a code owner config without issues.
+    CodeOwnerConfig.Key codeOwnerConfigKey = createCodeOwnerConfigKey("/foo/");
+    PushOneCommit.Result r =
+        createChange(
+            "Add code owners",
+            codeOwnerConfigOperations.codeOwnerConfig(codeOwnerConfigKey).getJGitFilePath(),
+            format(
+                CodeOwnerConfig.builder(codeOwnerConfigKey, TEST_REVISION)
+                    .addCodeOwnerSet(CodeOwnerSet.createWithoutPathExpressions(admin.email()))
+                    .build()));
+    assertOkWithHints(r, "code owner config files validated, no issues found");
+    approve(r.getChangeId());
+    gApi.changes().id(r.getChangeId()).current().submit();
+
+    // Trying to use the skip validation option on revert is rejected because user has no permission
+    // to skip the validation.
+    requestScopeOperations.setApiUser(user.id());
+    RevertInput revertInput = new RevertInput();
+    revertInput.validationOptions =
+        ImmutableMap.of(
+            String.format("code-owners~%s", SkipCodeOwnerConfigValidationPushOption.NAME), "true");
+    ResourceConflictException resourceConflictException =
+        assertThrows(
+            ResourceConflictException.class,
+            () -> gApi.changes().id(r.getChangeId()).revert(revertInput));
+    assertThat(resourceConflictException)
+        .hasMessageThat()
+        .contains(
+            String.format(
+                "[code-owners] %s for plugin code-owners not permitted",
+                SkipCodeOwnerConfigValidationCapability.ID));
+  }
+
+  @Test
   @GerritConfig(name = "plugin.code-owners.enableValidationOnBranchCreation", value = "true")
   public void cannotCreateBranchWithInvalidCodeOwnerConfigFileViaRestApi() throws Exception {
     // Add a non code owner config file to verify that it is not validated as code owner config file