blob: 3c41f143dd30da12fe1107c3eb0ac13d0051279b [file] [log] [blame]
// Copyright (C) 2022 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.backend;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.acceptance.TestAccount;
import com.google.gerrit.acceptance.config.GerritConfig;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.entities.Account;
import com.google.gerrit.entities.Change;
import com.google.gerrit.entities.PatchSetApproval;
import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.changes.ChangeApi;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.common.LabelDefinitionInput;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.plugins.codeowners.acceptance.AbstractCodeOwnersTest;
import com.google.gerrit.plugins.codeowners.backend.config.CodeOwnersPluginConfiguration;
import com.google.gerrit.plugins.codeowners.backend.config.CodeOwnersPluginProjectConfigSnapshot;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.inject.Inject;
import org.junit.Before;
import org.junit.Test;
/** Tests for {@link CodeOwnerApprovalCheckInput}. */
public class CodeOwnerApprovalCheckInputTest extends AbstractCodeOwnersTest {
@Inject private ChangeNotes.Factory changeNotesFactory;
@Inject private RequestScopeOperations requestScopeOperations;
private CodeOwnerApprovalCheckInput.Loader.Factory inputLoaderFactory;
private CodeOwnerResolver codeOwnerResolver;
private CodeOwnersPluginProjectConfigSnapshot codeOwnersConfig;
private TestAccount user2;
private Change.Id changeId;
private Account.Id changeOwner;
@Before
public void setUpCodeOwnersPlugin() throws Exception {
inputLoaderFactory =
plugin.getSysInjector().getInstance(CodeOwnerApprovalCheckInput.Loader.Factory.class);
codeOwnerResolver = plugin.getSysInjector().getInstance(CodeOwnerResolver.class);
CodeOwnersPluginConfiguration codeOwnersPluginConfiguration =
plugin.getSysInjector().getInstance(CodeOwnersPluginConfiguration.class);
codeOwnersConfig = codeOwnersPluginConfiguration.getProjectConfig(project);
}
@Before
public void setUp() throws Exception {
user2 = accountCreator.user2();
changeId = createChange().getChange().getId();
changeOwner = admin.id();
}
@Test
public void noReviewers() {
assertThat(loadInput().reviewers()).isEmpty();
}
@Test
public void withReviewers() throws Exception {
changeApi().addReviewer(user.email());
changeApi().addReviewer(user2.email());
// make the change owner a reviewer:
// the change owner cannot be added as a reviewer, but the change owner becomes a reviewer when
// they vote on the change
requestScopeOperations.setApiUser(changeOwner);
recommend(changeId.toString());
assertThat(loadInput().reviewers()).containsExactly(user.id(), user2.id(), changeOwner);
}
@Test
public void withReviewers_selfApprovalsIgnored() throws Exception {
disableSelfCodeReviewApprovals();
changeApi().addReviewer(user.email());
changeApi().addReviewer(user2.email());
// make the change owner a reviewer:
// the change owner cannot be added as a reviewer, but the change owner becomes a reviewer when
// they vote on the change
requestScopeOperations.setApiUser(changeOwner);
recommend(changeId.toString());
assertThat(loadInput().reviewers()).containsExactly(user.id(), user2.id());
}
@Test
public void noApprovers() {
assertThat(loadInput().approvers()).isEmpty();
}
@Test
public void withApprovers() throws Exception {
// self approve
requestScopeOperations.setApiUser(changeOwner);
recommend(changeId.toString());
// approve as user
requestScopeOperations.setApiUser(user.id());
recommend(changeId.toString());
// approve as user2
requestScopeOperations.setApiUser(user2.id());
recommend(changeId.toString());
assertThat(loadInput().approvers()).containsExactly(changeOwner, user.id(), user2.id());
}
@Test
public void withApprovers_selfApprovalsIgnored() throws Exception {
disableSelfCodeReviewApprovals();
// self approve
requestScopeOperations.setApiUser(changeOwner);
recommend(changeId.toString());
// approve as user
requestScopeOperations.setApiUser(user.id());
recommend(changeId.toString());
// approve as user2
requestScopeOperations.setApiUser(user2.id());
recommend(changeId.toString());
assertThat(loadInput().approvers()).containsExactly(user.id(), user2.id());
}
@Test
public void noImplicitApprover() {
assertThat(loadInput().implicitApprover()).isEmpty();
}
@Test
@GerritConfig(name = "plugin.code-owners.enableImplicitApprovals", value = "true")
public void withImplicitApprover() {
// If implicit approvals are enabled, an implicit approval of the current uploader is assumed.
// Since the change has only 1 patch set the current uploader is the change owner.
assertThat(loadInput().implicitApprover()).hasValue(changeOwner);
}
@Test
public void noOverrides() {
assertThat(loadInput().overrides()).isEmpty();
}
@Test
@GerritConfig(name = "plugin.code-owners.overrideApproval", value = "Owners-Override+1")
public void withOverrides() throws Exception {
createOwnersOverrideLabel();
ReviewInput reviewInput = new ReviewInput().label("Owners-Override", 1);
// self override
requestScopeOperations.setApiUser(changeOwner);
gApi.changes().id(changeId.toString()).current().review(reviewInput);
// override as user
requestScopeOperations.setApiUser(user.id());
gApi.changes().id(changeId.toString()).current().review(reviewInput);
// override as user2
requestScopeOperations.setApiUser(user2.id());
gApi.changes().id(changeId.toString()).current().review(reviewInput);
assertThat(
loadInput().overrides().stream()
.map(PatchSetApproval::accountId)
.collect(toImmutableSet()))
.containsExactly(changeOwner, user.id(), user2.id());
}
@Test
@GerritConfig(name = "plugin.code-owners.overrideApproval", value = "Owners-Override+1")
public void withOverrides_selfApprovalsIgnored() throws Exception {
createOwnersOverrideLabel();
disableSelfOwnersOverrideApprovals();
ReviewInput reviewInput = new ReviewInput().label("Owners-Override", 1);
// self override
requestScopeOperations.setApiUser(changeOwner);
gApi.changes().id(changeId.toString()).current().review(reviewInput);
// override as user
requestScopeOperations.setApiUser(user.id());
gApi.changes().id(changeId.toString()).current().review(reviewInput);
// override as user2
requestScopeOperations.setApiUser(user2.id());
gApi.changes().id(changeId.toString()).current().review(reviewInput);
assertThat(
loadInput().overrides().stream()
.map(PatchSetApproval::accountId)
.collect(toImmutableSet()))
.containsExactly(user.id(), user2.id());
}
@Test
public void noGlobalCodeOwners() {
CodeOwnerResolverResult globalCodeOwners = loadInput().globalCodeOwners();
assertThat(globalCodeOwners.codeOwnersAccountIds()).isEmpty();
assertThat(globalCodeOwners.ownedByAllUsers()).isFalse();
}
@Test
@GerritConfig(
name = "plugin.code-owners.globalCodeOwner",
values = {"user1@example.com", "user2@example.com"})
public void withGlobalCodeOwners() {
CodeOwnerResolverResult globalCodeOwners = loadInput().globalCodeOwners();
assertThat(globalCodeOwners.codeOwnersAccountIds()).containsExactly(user.id(), user2.id());
assertThat(globalCodeOwners.ownedByAllUsers()).isFalse();
}
@Test
@GerritConfig(name = "plugin.code-owners.globalCodeOwner", value = "*")
public void withAllUsersAsGlobalCodeOwners() {
CodeOwnerResolverResult globalCodeOwners = loadInput().globalCodeOwners();
assertThat(globalCodeOwners.codeOwnersAccountIds()).isEmpty();
assertThat(globalCodeOwners.ownedByAllUsers()).isTrue();
}
@Test
public void withFallbackCodeOwnersNone() {
assertThat(loadInput().fallbackCodeOwners()).isEqualTo(FallbackCodeOwners.NONE);
}
@Test
@GerritConfig(name = "plugin.code-owners.fallbackCodeOwners", value = "ALL_USERS")
public void withFallbackCodeOwnersAllUsers() {
assertThat(loadInput().fallbackCodeOwners()).isEqualTo(FallbackCodeOwners.ALL_USERS);
}
@Test
public void checkAllOwnersIsFalse() {
assertThat(loadInput().checkAllOwners()).isFalse();
}
@Test
public void createInputForComputingOwnedPaths_noReviewers() throws Exception {
changeApi().addReviewer(user.email());
changeApi().addReviewer(user2.email());
// CodeOwnerApprovalCheckInput#createForComputingOwnedPaths never sets reviewers
assertThat(createInputForComputingOwnedPaths(ImmutableSet.of()).reviewers()).isEmpty();
}
@Test
public void createInputForComputingOwnedPaths_noApprovers() throws Exception {
// self approve
requestScopeOperations.setApiUser(changeOwner);
recommend(changeId.toString());
// approve as user
requestScopeOperations.setApiUser(user.id());
recommend(changeId.toString());
// approve as user2
requestScopeOperations.setApiUser(user2.id());
recommend(changeId.toString());
// CodeOwnerApprovalCheckInput#createForComputingOwnedPaths always sets approvers to the given
// accounts
assertThat(createInputForComputingOwnedPaths(ImmutableSet.of()).approvers()).isEmpty();
}
@Test
public void createInputForComputingOwnedPaths_withApprovers() {
// CodeOwnerApprovalCheckInput#createForComputingOwnedPaths always sets approvers to the given
// accounts
assertThat(
createInputForComputingOwnedPaths(ImmutableSet.of(admin.id(), user.id(), user2.id()))
.approvers())
.containsExactly(admin.id(), user.id(), user2.id());
}
@Test
@GerritConfig(name = "plugin.code-owners.enableImplicitApprovals", value = "true")
public void createInputForComputingOwnedPaths_noImplicitApprover() {
// CodeOwnerApprovalCheckInput#createForComputingOwnedPaths never sets an implicit approver
assertThat(createInputForComputingOwnedPaths(ImmutableSet.of()).implicitApprover()).isEmpty();
}
@Test
@GerritConfig(name = "plugin.code-owners.overrideApproval", value = "Owners-Override+1")
public void createInputForComputingOwnedPaths_noOverride() throws Exception {
createOwnersOverrideLabel();
ReviewInput reviewInput = new ReviewInput().label("Owners-Override", 1);
// self override
requestScopeOperations.setApiUser(changeOwner);
gApi.changes().id(changeId.toString()).current().review(reviewInput);
// override as user
requestScopeOperations.setApiUser(user.id());
gApi.changes().id(changeId.toString()).current().review(reviewInput);
// override as user2
requestScopeOperations.setApiUser(user2.id());
gApi.changes().id(changeId.toString()).current().review(reviewInput);
// CodeOwnerApprovalCheckInput#createForComputingOwnedPaths never sets overrides
assertThat(createInputForComputingOwnedPaths(ImmutableSet.of()).overrides()).isEmpty();
}
@Test
public void createInputForComputingOwnedPaths_noGlobalCodeOwners() {
CodeOwnerResolverResult globalCodeOwners =
createInputForComputingOwnedPaths(ImmutableSet.of()).globalCodeOwners();
assertThat(globalCodeOwners.codeOwnersAccountIds()).isEmpty();
assertThat(globalCodeOwners.ownedByAllUsers()).isFalse();
}
@Test
@GerritConfig(
name = "plugin.code-owners.globalCodeOwner",
values = {"user1@example.com", "user2@example.com"})
public void createInputForComputingOwnedPaths_withGlobalCodeOwners() {
CodeOwnerResolverResult globalCodeOwners =
createInputForComputingOwnedPaths(ImmutableSet.of()).globalCodeOwners();
assertThat(globalCodeOwners.codeOwnersAccountIds()).containsExactly(user.id(), user2.id());
assertThat(globalCodeOwners.ownedByAllUsers()).isFalse();
}
@Test
@GerritConfig(name = "plugin.code-owners.globalCodeOwner", value = "*")
public void createInputForComputingOwnedPaths_withAllUsersAsGlobalCodeOwners() {
CodeOwnerResolverResult globalCodeOwners =
createInputForComputingOwnedPaths(ImmutableSet.of()).globalCodeOwners();
assertThat(globalCodeOwners.codeOwnersAccountIds()).isEmpty();
assertThat(globalCodeOwners.ownedByAllUsers()).isTrue();
}
@Test
public void createInputForComputingOwnedPaths_withFallbackCodeOwnersNone() {
assertThat(createInputForComputingOwnedPaths(ImmutableSet.of()).fallbackCodeOwners())
.isEqualTo(FallbackCodeOwners.NONE);
}
@Test
@GerritConfig(name = "plugin.code-owners.fallbackCodeOwners", value = "ALL_USERS")
public void createInputForComputingOwnedPaths_withFallbackCodeOwnersAllUsers() {
assertThat(createInputForComputingOwnedPaths(ImmutableSet.of()).fallbackCodeOwners())
.isEqualTo(FallbackCodeOwners.ALL_USERS);
}
@Test
public void createInputForComputingOwnedPaths_checkAllOwnersIsTrue() {
assertThat(createInputForComputingOwnedPaths(ImmutableSet.of()).checkAllOwners()).isTrue();
}
private void disableSelfCodeReviewApprovals() throws Exception {
disableSelfApprovals(allProjects, "Code-Review");
}
private void disableSelfOwnersOverrideApprovals() throws Exception {
disableSelfApprovals(project, "Owners-Override");
}
private void disableSelfApprovals(Project.NameKey project, String labelName) throws Exception {
LabelDefinitionInput input = new LabelDefinitionInput();
input.ignoreSelfApproval = true;
gApi.projects().name(project.get()).label(labelName).update(input);
}
private ChangeApi changeApi() throws RestApiException {
return gApi.changes().id(changeId.get());
}
private CodeOwnerApprovalCheckInput loadInput() {
ChangeNotes changeNotes = changeNotesFactory.create(project, changeId);
return inputLoaderFactory.create(codeOwnersConfig, codeOwnerResolver, changeNotes).load();
}
private CodeOwnerApprovalCheckInput createInputForComputingOwnedPaths(
ImmutableSet<Account.Id> accounts) {
ChangeNotes changeNotes = changeNotesFactory.create(project, changeId);
return CodeOwnerApprovalCheckInput.createForComputingOwnedPaths(
codeOwnersConfig, codeOwnerResolver, changeNotes, accounts);
}
}