blob: ef9c2b61e48f7b4c817cb795d15964a863697152 [file] [log] [blame]
// Copyright (C) 2020 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.plugins.codeowners.acceptance.api;
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.plugins.codeowners.testing.CodeOwnerStatusInfoSubject.assertThat;
import static com.google.gerrit.plugins.codeowners.testing.SubmitRequirementInfoSubject.assertThatCollection;
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.TestAccount;
import com.google.gerrit.acceptance.config.GerritConfig;
import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.projects.DeleteBranchesInput;
import com.google.gerrit.extensions.client.ChangeStatus;
import com.google.gerrit.extensions.client.ListChangesOption;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.plugins.codeowners.acceptance.AbstractCodeOwnersIT;
import com.google.gerrit.plugins.codeowners.api.CodeOwnerStatusInfo;
import com.google.gerrit.plugins.codeowners.common.CodeOwnerStatus;
import com.google.gerrit.plugins.codeowners.testing.SubmitRequirementInfoSubject;
import org.junit.Test;
/** Acceptance test for {@code com.google.gerrit.plugins.codeowners.backend.CodeOwnerSubmitRule}. */
public class CodeOwnerSubmitRuleIT extends AbstractCodeOwnersIT {
@Test
public void changeIsSubmittableIfCodeOwnersFuctionalityIsDisabled() throws Exception {
disableCodeOwnersForProject(project);
String changeId = createChange("Test Change", "foo/bar.baz", "file content").getChangeId();
// Approve by a non-code-owner.
approve(changeId);
// Check the submittable flag.
ChangeInfo changeInfo = gApi.changes().id(changeId).get(ListChangesOption.SUBMITTABLE);
assertThat(changeInfo.submittable).isTrue();
// Check that there is no submit requirement.
assertThat(changeInfo.requirements).isEmpty();
// Submit the change.
gApi.changes().id(changeId).current().submit();
assertThat(gApi.changes().id(changeId).get().status).isEqualTo(ChangeStatus.MERGED);
}
@Test
@GerritConfig(name = "plugin.code-owners.disabled", value = "true")
@GerritConfig(name = "plugin.code-owners.requiredApproval", value = "INVALID")
public void changeIsSubmittableIfCodeOwnersFuctionalityIsDisabled_invalidPluginConfig()
throws Exception {
String changeId = createChange("Test Change", "foo/bar.baz", "file content").getChangeId();
// Approve by a non-code-owner.
approve(changeId);
// Check the submittable flag.
ChangeInfo changeInfo = gApi.changes().id(changeId).get(ListChangesOption.SUBMITTABLE);
assertThat(changeInfo.submittable).isTrue();
// Check that there is no submit requirement.
assertThat(changeInfo.requirements).isEmpty();
// Submit the change.
gApi.changes().id(changeId).current().submit();
assertThat(gApi.changes().id(changeId).get().status).isEqualTo(ChangeStatus.MERGED);
}
@Test
public void changeWithInsufficentReviewersIsNotSubmittable() throws Exception {
String changeId = createChange("Test Change", "foo/bar.baz", "file content").getChangeId();
// Approve by a non-code-owner.
approve(changeId);
// Verify that the code owner status for the changed file is INSUFFICIENT_REVIEWERS.
CodeOwnerStatusInfo codeOwnerStatus =
changeCodeOwnersApiFactory.change(changeId).getCodeOwnerStatus();
assertThat(codeOwnerStatus)
.hasFileCodeOwnerStatusesThat()
.onlyElement()
.hasNewPathStatusThat()
.value()
.hasStatusThat()
.isEqualTo(CodeOwnerStatus.INSUFFICIENT_REVIEWERS);
// Check the submittable flag.
ChangeInfo changeInfo = gApi.changes().id(changeId).get(ListChangesOption.SUBMITTABLE);
assertThat(changeInfo.submittable).isFalse();
// Check the submit requirement.
SubmitRequirementInfoSubject submitRequirementInfoSubject =
assertThatCollection(changeInfo.requirements).onlyElement();
submitRequirementInfoSubject.hasStatusThat().isEqualTo("NOT_READY");
submitRequirementInfoSubject.hasFallbackTextThat().isEqualTo("Code Owners");
submitRequirementInfoSubject.hasTypeThat().isEqualTo("code-owners");
// Try to submit the change.
ResourceConflictException exception =
assertThrows(
ResourceConflictException.class, () -> gApi.changes().id(changeId).current().submit());
assertThat(exception)
.hasMessageThat()
.isEqualTo(
String.format(
"Failed to submit 1 change due to the following problems:\n"
+ "Change %d: Submit requirement not fulfilled: Code Owners",
changeInfo._number));
}
@Test
public void changeWithPendingCodeOwnerApprovalsIsNotSubmittable() throws Exception {
codeOwnerConfigOperations
.newCodeOwnerConfig()
.project(project)
.branch("master")
.folderPath("/foo/")
.addCodeOwnerEmail(user.email())
.create();
String changeId = createChange("Test Change", "foo/bar.baz", "file content").getChangeId();
// Add a reviewer that is a code owner.
gApi.changes().id(changeId).addReviewer(user.email());
// Approve by a non-code-owner.
approve(changeId);
// Verify that the code owner status for the changed file is PENDING.
CodeOwnerStatusInfo codeOwnerStatus =
changeCodeOwnersApiFactory.change(changeId).getCodeOwnerStatus();
assertThat(codeOwnerStatus)
.hasFileCodeOwnerStatusesThat()
.onlyElement()
.hasNewPathStatusThat()
.value()
.hasStatusThat()
.isEqualTo(CodeOwnerStatus.PENDING);
// Check the submittable flag.
ChangeInfo changeInfo = gApi.changes().id(changeId).get(ListChangesOption.SUBMITTABLE);
assertThat(changeInfo.submittable).isFalse();
// Check the submit requirement.
SubmitRequirementInfoSubject submitRequirementInfoSubject =
assertThatCollection(changeInfo.requirements).onlyElement();
submitRequirementInfoSubject.hasStatusThat().isEqualTo("NOT_READY");
submitRequirementInfoSubject.hasFallbackTextThat().isEqualTo("Code Owners");
submitRequirementInfoSubject.hasTypeThat().isEqualTo("code-owners");
// Try to submit the change.
ResourceConflictException exception =
assertThrows(
ResourceConflictException.class, () -> gApi.changes().id(changeId).current().submit());
assertThat(exception)
.hasMessageThat()
.isEqualTo(
String.format(
"Failed to submit 1 change due to the following problems:\n"
+ "Change %d: Submit requirement not fulfilled: Code Owners",
changeInfo._number));
}
@Test
public void changeWithCodeOwnerApprovalsIsSubmittable() throws Exception {
codeOwnerConfigOperations
.newCodeOwnerConfig()
.project(project)
.branch("master")
.folderPath("/foo/")
.addCodeOwnerEmail(admin.email())
.create();
String changeId = createChange("Test Change", "foo/bar.baz", "file content").getChangeId();
// Approve by a code-owner.
approve(changeId);
// Verify that the code owner status for the changed file is APPROVED.
CodeOwnerStatusInfo codeOwnerStatus =
changeCodeOwnersApiFactory.change(changeId).getCodeOwnerStatus();
assertThat(codeOwnerStatus)
.hasFileCodeOwnerStatusesThat()
.onlyElement()
.hasNewPathStatusThat()
.value()
.hasStatusThat()
.isEqualTo(CodeOwnerStatus.APPROVED);
// Check the submittable flag.
ChangeInfo changeInfo = gApi.changes().id(changeId).get(ListChangesOption.SUBMITTABLE);
assertThat(changeInfo.submittable).isTrue();
// Check the submit requirement.
SubmitRequirementInfoSubject submitRequirementInfoSubject =
assertThatCollection(changeInfo.requirements).onlyElement();
submitRequirementInfoSubject.hasStatusThat().isEqualTo("OK");
submitRequirementInfoSubject.hasFallbackTextThat().isEqualTo("Code Owners");
submitRequirementInfoSubject.hasTypeThat().isEqualTo("code-owners");
// Submit the change.
gApi.changes().id(changeId).current().submit();
assertThat(gApi.changes().id(changeId).get().status).isEqualTo(ChangeStatus.MERGED);
}
@Test
@GerritConfig(name = "plugin.code-owners.overrideApproval", value = "Owners-Override+1")
public void changeWithOverrideApprovalIsSubmittable() throws Exception {
createOwnersOverrideLabel();
String changeId = createChange("Test Change", "foo/bar.baz", "file content").getChangeId();
// Check that the change is not submittable.
assertThat(gApi.changes().id(changeId).get(ListChangesOption.SUBMITTABLE).submittable)
.isFalse();
// Add an override approval.
gApi.changes().id(changeId).current().review(new ReviewInput().label("Owners-Override", 1));
// Verify that the code owner status for the changed file is APPROVED.
CodeOwnerStatusInfo codeOwnerStatus =
changeCodeOwnersApiFactory.change(changeId).getCodeOwnerStatus();
assertThat(codeOwnerStatus)
.hasFileCodeOwnerStatusesThat()
.onlyElement()
.hasNewPathStatusThat()
.value()
.hasStatusThat()
.isEqualTo(CodeOwnerStatus.APPROVED);
// Approve by a non-code-owner to satisfy the Code-Review+2 requirement.
approve(changeId);
// Check the submit requirement.
SubmitRequirementInfoSubject submitRequirementInfoSubject =
assertThatCollection(gApi.changes().id(changeId).get().requirements).onlyElement();
submitRequirementInfoSubject.hasStatusThat().isEqualTo("OK");
submitRequirementInfoSubject.hasFallbackTextThat().isEqualTo("Code Owners");
submitRequirementInfoSubject.hasTypeThat().isEqualTo("code-owners");
// Submit the change.
gApi.changes().id(changeId).current().submit();
assertThat(gApi.changes().id(changeId).get().status).isEqualTo(ChangeStatus.MERGED);
}
@Test
public void changeIsNotSubmittableIfDestinationBranchWasDeleted() throws Exception {
codeOwnerConfigOperations
.newCodeOwnerConfig()
.project(project)
.branch("master")
.folderPath("/foo/")
.addCodeOwnerEmail(admin.email())
.create();
String branchName = "tempBranch";
createBranch(BranchNameKey.create(project, branchName));
String changeId = createChange("refs/for/" + branchName).getChangeId();
// Approve by a code-owner.
approve(changeId);
DeleteBranchesInput input = new DeleteBranchesInput();
input.branches = ImmutableList.of(branchName);
gApi.projects().name(project.get()).deleteBranches(input);
ChangeInfo changeInfo = gApi.changes().id(changeId).get(ListChangesOption.SUBMITTABLE);
assertThat(changeInfo.submittable).isFalse();
// Check that the submit requirement.
SubmitRequirementInfoSubject submitRequirementInfoSubject =
assertThatCollection(changeInfo.requirements).onlyElement();
submitRequirementInfoSubject.hasStatusThat().isEqualTo("NOT_READY");
submitRequirementInfoSubject.hasFallbackTextThat().isEqualTo("Code Owners");
submitRequirementInfoSubject.hasTypeThat().isEqualTo("code-owners");
// Try to submit the change.
ResourceConflictException exception =
assertThrows(
ResourceConflictException.class, () -> gApi.changes().id(changeId).current().submit());
assertThat(exception)
.hasMessageThat()
.isEqualTo(String.format("destination branch \"refs/heads/%s\" not found.", branchName));
}
@Test
@GerritConfig(name = "plugin.code-owners.exemptedUser", value = "exempted-user@example.com")
public void changeIsSubmittableIfUserIsExcempted() throws Exception {
TestAccount exemptedUser =
accountCreator.create(
"exemptedUser", "exempted-user@example.com", "Exempted User", /* displayName= */ null);
PushOneCommit.Result r = createChange(exemptedUser, "Some Change", "foo.txt", "some content");
String changeId = r.getChangeId();
// Apply Code-Review+2 by a non-code-owner to satisfy the MaxWithBlock function of the
// Code-Review label.
approve(changeId);
ChangeInfo changeInfo =
gApi.changes()
.id(changeId)
.get(
ListChangesOption.SUBMITTABLE,
ListChangesOption.ALL_REVISIONS,
ListChangesOption.CURRENT_ACTIONS);
assertThat(changeInfo.submittable).isTrue();
// Check that the submit button is enabled.
assertThat(changeInfo.revisions.get(r.getCommit().getName()).actions.get("submit").enabled)
.isTrue();
// Check the submit requirement.
SubmitRequirementInfoSubject submitRequirementInfoSubject =
assertThatCollection(changeInfo.requirements).onlyElement();
submitRequirementInfoSubject.hasStatusThat().isEqualTo("OK");
submitRequirementInfoSubject.hasFallbackTextThat().isEqualTo("Code Owners");
submitRequirementInfoSubject.hasTypeThat().isEqualTo("code-owners");
// Submit the change.
gApi.changes().id(changeId).current().submit();
assertThat(gApi.changes().id(changeId).get().status).isEqualTo(ChangeStatus.MERGED);
}
}