Merge "Add unit tests for CodeOwnerApprovalCheckInput"
diff --git a/javatests/com/google/gerrit/plugins/codeowners/backend/CodeOwnerApprovalCheckInputTest.java b/javatests/com/google/gerrit/plugins/codeowners/backend/CodeOwnerApprovalCheckInputTest.java
new file mode 100644
index 0000000..aa1d939
--- /dev/null
+++ b/javatests/com/google/gerrit/plugins/codeowners/backend/CodeOwnerApprovalCheckInputTest.java
@@ -0,0 +1,279 @@
+// 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.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();
+ }
+
+ 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();
+ }
+}