Add unit tests for CodeOwnerApprovalCheckInput
Now that the logic for computing the input for CodeOwnerApprovalCheck is
in a separate class we can easily unit test it.
We already have integration tests that cover this logic, but the unit
tests are easier to debug if anything gets broken.
This change doesn't add tests for CodeOwnerApprovalCheckInputs that are
created by the createForComputingOwnedPaths method yet, we may add those
later.
Signed-off-by: Edwin Kempin <ekempin@google.com>
Change-Id: I9e63ce035c0212454372435db9edf3c565069c8b
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();
+ }
+}