blob: 9972afed03faaf55552b693a9b67bd98ed52c277 [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.restapi;
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.server.project.ProjectCache.illegalState;
import static com.google.gerrit.server.schema.AllProjectsInput.getDefaultCodeReviewLabel;
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSortedSet;
import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.entities.LabelType;
import com.google.gerrit.plugins.codeowners.acceptance.AbstractCodeOwnersTest;
import com.google.gerrit.plugins.codeowners.acceptance.testsuite.CodeOwnerConfigOperations;
import com.google.gerrit.plugins.codeowners.api.BackendInfo;
import com.google.gerrit.plugins.codeowners.api.CodeOwnerBranchConfigInfo;
import com.google.gerrit.plugins.codeowners.api.CodeOwnerProjectConfigInfo;
import com.google.gerrit.plugins.codeowners.api.CodeOwnersStatusInfo;
import com.google.gerrit.plugins.codeowners.api.RequiredApprovalInfo;
import com.google.gerrit.plugins.codeowners.backend.CodeOwnerBackendId;
import com.google.gerrit.plugins.codeowners.backend.FallbackCodeOwners;
import com.google.gerrit.plugins.codeowners.backend.config.CodeOwnersPluginConfiguration;
import com.google.gerrit.plugins.codeowners.backend.config.CodeOwnersPluginProjectConfigSnapshot;
import com.google.gerrit.plugins.codeowners.backend.config.RequiredApproval;
import com.google.gerrit.plugins.codeowners.backend.findowners.FindOwnersBackend;
import com.google.gerrit.plugins.codeowners.backend.proto.ProtoBackend;
import com.google.gerrit.plugins.codeowners.common.MergeCommitStrategy;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.project.BranchResource;
import com.google.gerrit.server.project.ProjectResource;
import com.google.gerrit.server.restapi.project.ListBranches;
import com.google.inject.Inject;
import com.google.inject.Key;
import com.google.inject.Provider;
import java.io.IOException;
import java.util.Optional;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.mockito.quality.Strictness;
/** Tests for {@link CodeOwnerProjectConfigJson}. */
public class CodeOwnerProjectConfigJsonTest extends AbstractCodeOwnersTest {
@Rule public final MockitoRule mockito = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
@Mock private CodeOwnersPluginConfiguration codeOwnersPluginConfiguration;
@Mock private CodeOwnersPluginProjectConfigSnapshot codeOwnersPluginConfigSnapshot;
@Inject private CurrentUser currentUser;
private CodeOwnerConfigOperations codeOwnerConfigOperations;
private CodeOwnerProjectConfigJson codeOwnerProjectConfigJson;
private FindOwnersBackend findOwnersBackend;
private ProtoBackend protoBackend;
@Before
public void setUpCodeOwnersPlugin() throws Exception {
codeOwnerConfigOperations =
plugin.getSysInjector().getInstance(CodeOwnerConfigOperations.class);
codeOwnerProjectConfigJson =
new CodeOwnerProjectConfigJson(
codeOwnersPluginConfiguration,
plugin.getSysInjector().getInstance(new Key<Provider<ListBranches>>() {}));
findOwnersBackend = plugin.getSysInjector().getInstance(FindOwnersBackend.class);
protoBackend = plugin.getSysInjector().getInstance(ProtoBackend.class);
}
@Test
public void formatRequiredApproval() throws Exception {
RequiredApproval requiredApproval =
RequiredApproval.create(LabelType.withDefaultValues("Code-Review"), (short) 2);
RequiredApprovalInfo requiredApprovalInfo =
CodeOwnerProjectConfigJson.formatRequiredApproval(requiredApproval);
assertThat(requiredApprovalInfo.label).isEqualTo("Code-Review");
assertThat(requiredApprovalInfo.value).isEqualTo(2);
}
@Test
public void cannotFormatNullRequiredApproval() throws Exception {
NullPointerException npe =
assertThrows(
NullPointerException.class,
() -> CodeOwnerProjectConfigJson.formatRequiredApproval(/* requiredApproval= */ null));
assertThat(npe).hasMessageThat().isEqualTo("requiredApproval");
}
@Test
public void formatBackendIds() throws Exception {
createBranch(BranchNameKey.create(project, "stable-2.10"));
when(codeOwnersPluginConfigSnapshot.getBackend()).thenReturn(findOwnersBackend);
when(codeOwnersPluginConfigSnapshot.getBackend("refs/heads/master"))
.thenReturn(findOwnersBackend);
when(codeOwnersPluginConfigSnapshot.getBackend("refs/meta/config"))
.thenReturn(findOwnersBackend);
when(codeOwnersPluginConfigSnapshot.getBackend("refs/heads/stable-2.10"))
.thenReturn(protoBackend);
when(codeOwnersPluginConfiguration.getProjectConfig(project))
.thenReturn(codeOwnersPluginConfigSnapshot);
BackendInfo backendInfo = codeOwnerProjectConfigJson.formatBackendInfo(createProjectResource());
assertThat(backendInfo.id).isEqualTo(CodeOwnerBackendId.FIND_OWNERS.getBackendId());
// This project has 3 branches ("master", "stable-2.10" and "refs/meta/config"). The
// backendInfo.idsByBranch field only contains those branches that use a backend that differs
// from the backend that is returned by the backendInfo.id field. This means since "master" and
// "refs/meta/config" do use the find-owners backend, the same backend that is returned in the
// backendInfo.id field, they are omitted in the backendInfo.idsByBranch field.
assertThat(backendInfo.idsByBranch)
.containsExactly("refs/heads/stable-2.10", CodeOwnerBackendId.PROTO.getBackendId());
}
@Test
public void idsPerBranchNotSetIfThereIsNoBranchSpecificBackendConfiguration() throws Exception {
when(codeOwnersPluginConfigSnapshot.getBackend()).thenReturn(findOwnersBackend);
when(codeOwnersPluginConfigSnapshot.getBackend("refs/heads/master"))
.thenReturn(findOwnersBackend);
when(codeOwnersPluginConfigSnapshot.getBackend("refs/meta/config"))
.thenReturn(findOwnersBackend);
when(codeOwnersPluginConfiguration.getProjectConfig(project))
.thenReturn(codeOwnersPluginConfigSnapshot);
BackendInfo backendInfo = codeOwnerProjectConfigJson.formatBackendInfo(createProjectResource());
assertThat(backendInfo.id).isEqualTo(CodeOwnerBackendId.FIND_OWNERS.getBackendId());
assertThat(backendInfo.idsByBranch).isNull();
}
@Test
public void formatCodeOwnerProjectConfig() throws Exception {
createOwnersOverrideLabel();
createBranch(BranchNameKey.create(project, "stable-2.10"));
when(codeOwnersPluginConfigSnapshot.getFileExtension()).thenReturn(Optional.of("foo"));
when(codeOwnersPluginConfigSnapshot.getMergeCommitStrategy())
.thenReturn(MergeCommitStrategy.FILES_WITH_CONFLICT_RESOLUTION);
when(codeOwnersPluginConfigSnapshot.getFallbackCodeOwners())
.thenReturn(FallbackCodeOwners.ALL_USERS);
when(codeOwnersPluginConfigSnapshot.getOverrideInfoUrl())
.thenReturn(Optional.of("http://foo.example.com"));
when(codeOwnersPluginConfigSnapshot.getInvalidCodeOwnerConfigInfoUrl())
.thenReturn(Optional.of("http://bar.example.com"));
when(codeOwnersPluginConfigSnapshot.isDisabled()).thenReturn(false);
when(codeOwnersPluginConfigSnapshot.isDisabled(any(String.class))).thenReturn(false);
when(codeOwnersPluginConfigSnapshot.getBackend()).thenReturn(findOwnersBackend);
when(codeOwnersPluginConfigSnapshot.getBackend("refs/heads/master"))
.thenReturn(findOwnersBackend);
when(codeOwnersPluginConfigSnapshot.getBackend("refs/meta/config"))
.thenReturn(findOwnersBackend);
when(codeOwnersPluginConfigSnapshot.getBackend("refs/heads/stable-2.10"))
.thenReturn(protoBackend);
when(codeOwnersPluginConfigSnapshot.areImplicitApprovalsEnabled()).thenReturn(true);
when(codeOwnersPluginConfigSnapshot.getRequiredApproval())
.thenReturn(RequiredApproval.create(getDefaultCodeReviewLabel(), (short) 2));
when(codeOwnersPluginConfigSnapshot.getOverrideApprovals())
.thenReturn(
ImmutableSortedSet.of(
RequiredApproval.create(
LabelType.withDefaultValues("Owners-Override"), (short) 1)));
when(codeOwnersPluginConfiguration.getProjectConfig(project))
.thenReturn(codeOwnersPluginConfigSnapshot);
CodeOwnerProjectConfigInfo codeOwnerProjectConfigInfo =
codeOwnerProjectConfigJson.format(createProjectResource());
assertThat(codeOwnerProjectConfigInfo.status.disabled).isNull();
assertThat(codeOwnerProjectConfigInfo.status.disabledBranches).isNull();
assertThat(codeOwnerProjectConfigInfo.general.fileExtension).isEqualTo("foo");
assertThat(codeOwnerProjectConfigInfo.general.overrideInfoUrl)
.isEqualTo("http://foo.example.com");
assertThat(codeOwnerProjectConfigInfo.general.invalidCodeOwnerConfigInfoUrl)
.isEqualTo("http://bar.example.com");
assertThat(codeOwnerProjectConfigInfo.general.mergeCommitStrategy)
.isEqualTo(MergeCommitStrategy.FILES_WITH_CONFLICT_RESOLUTION);
assertThat(codeOwnerProjectConfigInfo.general.fallbackCodeOwners)
.isEqualTo(FallbackCodeOwners.ALL_USERS);
assertThat(codeOwnerProjectConfigInfo.general.implicitApprovals).isTrue();
assertThat(codeOwnerProjectConfigInfo.backend.id)
.isEqualTo(CodeOwnerBackendId.FIND_OWNERS.getBackendId());
assertThat(codeOwnerProjectConfigInfo.backend.idsByBranch)
.containsExactly("refs/heads/stable-2.10", CodeOwnerBackendId.PROTO.getBackendId());
assertThat(codeOwnerProjectConfigInfo.requiredApproval.label).isEqualTo("Code-Review");
assertThat(codeOwnerProjectConfigInfo.requiredApproval.value).isEqualTo(2);
assertThat(codeOwnerProjectConfigInfo.overrideApproval).hasSize(1);
assertThat(codeOwnerProjectConfigInfo.overrideApproval.get(0).label)
.isEqualTo("Owners-Override");
assertThat(codeOwnerProjectConfigInfo.overrideApproval.get(0).value).isEqualTo(1);
}
@Test
public void disabledBranchesNotSetIfDisabledOnProjectLevel() throws Exception {
when(codeOwnersPluginConfigSnapshot.isDisabled()).thenReturn(true);
when(codeOwnersPluginConfiguration.getProjectConfig(project))
.thenReturn(codeOwnersPluginConfigSnapshot);
CodeOwnersStatusInfo statusInfo =
codeOwnerProjectConfigJson.formatStatusInfo(createProjectResource());
assertThat(statusInfo.disabled).isTrue();
assertThat(statusInfo.disabledBranches).isNull();
}
@Test
public void emptyStatus() throws Exception {
when(codeOwnersPluginConfigSnapshot.isDisabled()).thenReturn(false);
when(codeOwnersPluginConfigSnapshot.isDisabled("refs/heads/master")).thenReturn(false);
when(codeOwnersPluginConfigSnapshot.isDisabled("refs/meta/config")).thenReturn(false);
when(codeOwnersPluginConfiguration.getProjectConfig(project))
.thenReturn(codeOwnersPluginConfigSnapshot);
CodeOwnersStatusInfo statusInfo =
codeOwnerProjectConfigJson.formatStatusInfo(createProjectResource());
assertThat(statusInfo.disabled).isNull();
assertThat(statusInfo.disabledBranches).isNull();
}
@Test
public void withDisabledBranches() throws Exception {
when(codeOwnersPluginConfigSnapshot.isDisabled()).thenReturn(false);
when(codeOwnersPluginConfigSnapshot.isDisabled("refs/heads/master")).thenReturn(true);
when(codeOwnersPluginConfigSnapshot.isDisabled("refs/meta/config")).thenReturn(false);
when(codeOwnersPluginConfiguration.getProjectConfig(project))
.thenReturn(codeOwnersPluginConfigSnapshot);
CodeOwnersStatusInfo statusInfo =
codeOwnerProjectConfigJson.formatStatusInfo(createProjectResource());
assertThat(statusInfo.disabled).isNull();
assertThat(statusInfo.disabledBranches).containsExactly("refs/heads/master");
}
@Test
public void withMultipleOverrides() throws Exception {
createOwnersOverrideLabel();
when(codeOwnersPluginConfigSnapshot.getOverrideApprovals())
.thenReturn(
ImmutableSortedSet.of(
RequiredApproval.create(LabelType.withDefaultValues("Owners-Override"), (short) 1),
RequiredApproval.create(LabelType.withDefaultValues("Code-Review"), (short) 2)));
when(codeOwnersPluginConfiguration.getProjectConfig(project))
.thenReturn(codeOwnersPluginConfigSnapshot);
ImmutableList<RequiredApprovalInfo> requiredApprovalInfos =
codeOwnerProjectConfigJson.formatOverrideApprovalInfo(project);
assertThat(requiredApprovalInfos).hasSize(2);
assertThat(requiredApprovalInfos.get(0).label).isEqualTo("Code-Review");
assertThat(requiredApprovalInfos.get(0).value).isEqualTo(2);
assertThat(requiredApprovalInfos.get(1).label).isEqualTo("Owners-Override");
assertThat(requiredApprovalInfos.get(1).value).isEqualTo(1);
}
@Test
public void formatCodeOwnerBranchConfig() throws Exception {
createOwnersOverrideLabel();
codeOwnerConfigOperations
.newCodeOwnerConfig()
.project(project)
.branch("master")
.folderPath("/")
.addCodeOwnerEmail(admin.email())
.create();
when(codeOwnersPluginConfigSnapshot.getFileExtension()).thenReturn(Optional.of("foo"));
when(codeOwnersPluginConfigSnapshot.getMergeCommitStrategy())
.thenReturn(MergeCommitStrategy.FILES_WITH_CONFLICT_RESOLUTION);
when(codeOwnersPluginConfigSnapshot.getFallbackCodeOwners())
.thenReturn(FallbackCodeOwners.ALL_USERS);
when(codeOwnersPluginConfigSnapshot.getOverrideInfoUrl())
.thenReturn(Optional.of("http://foo.example.com"));
when(codeOwnersPluginConfigSnapshot.getInvalidCodeOwnerConfigInfoUrl())
.thenReturn(Optional.of("http://bar.example.com"));
when(codeOwnersPluginConfigSnapshot.isDisabled(any(String.class))).thenReturn(false);
when(codeOwnersPluginConfigSnapshot.getBackend("refs/heads/master"))
.thenReturn(findOwnersBackend);
when(codeOwnersPluginConfigSnapshot.areImplicitApprovalsEnabled()).thenReturn(true);
when(codeOwnersPluginConfigSnapshot.getRequiredApproval())
.thenReturn(RequiredApproval.create(getDefaultCodeReviewLabel(), (short) 2));
when(codeOwnersPluginConfigSnapshot.getOverrideApprovals())
.thenReturn(
ImmutableSortedSet.of(
RequiredApproval.create(
LabelType.withDefaultValues("Owners-Override"), (short) 1)));
when(codeOwnersPluginConfiguration.getProjectConfig(project))
.thenReturn(codeOwnersPluginConfigSnapshot);
CodeOwnerBranchConfigInfo codeOwnerBranchConfigInfo =
codeOwnerProjectConfigJson.format(createBranchResource("refs/heads/master"));
assertThat(codeOwnerBranchConfigInfo.disabled).isNull();
assertThat(codeOwnerBranchConfigInfo.general.fileExtension).isEqualTo("foo");
assertThat(codeOwnerBranchConfigInfo.general.overrideInfoUrl)
.isEqualTo("http://foo.example.com");
assertThat(codeOwnerBranchConfigInfo.general.invalidCodeOwnerConfigInfoUrl)
.isEqualTo("http://bar.example.com");
assertThat(codeOwnerBranchConfigInfo.general.mergeCommitStrategy)
.isEqualTo(MergeCommitStrategy.FILES_WITH_CONFLICT_RESOLUTION);
assertThat(codeOwnerBranchConfigInfo.general.fallbackCodeOwners)
.isEqualTo(FallbackCodeOwners.ALL_USERS);
assertThat(codeOwnerBranchConfigInfo.general.implicitApprovals).isTrue();
assertThat(codeOwnerBranchConfigInfo.backendId)
.isEqualTo(CodeOwnerBackendId.FIND_OWNERS.getBackendId());
assertThat(codeOwnerBranchConfigInfo.requiredApproval.label).isEqualTo("Code-Review");
assertThat(codeOwnerBranchConfigInfo.requiredApproval.value).isEqualTo(2);
assertThat(codeOwnerBranchConfigInfo.overrideApproval).hasSize(1);
assertThat(codeOwnerBranchConfigInfo.overrideApproval.get(0).label)
.isEqualTo("Owners-Override");
assertThat(codeOwnerBranchConfigInfo.overrideApproval.get(0).value).isEqualTo(1);
}
@Test
public void formatCodeOwnerBranchConfig_disabled() throws Exception {
when(codeOwnersPluginConfigSnapshot.isDisabled(any(String.class))).thenReturn(true);
when(codeOwnersPluginConfiguration.getProjectConfig(project))
.thenReturn(codeOwnersPluginConfigSnapshot);
CodeOwnerBranchConfigInfo codeOwnerBranchConfigInfo =
codeOwnerProjectConfigJson.format(createBranchResource("refs/heads/master"));
assertThat(codeOwnerBranchConfigInfo.disabled).isTrue();
assertThat(codeOwnerBranchConfigInfo.general).isNull();
assertThat(codeOwnerBranchConfigInfo.backendId).isNull();
assertThat(codeOwnerBranchConfigInfo.requiredApproval).isNull();
assertThat(codeOwnerBranchConfigInfo.overrideApproval).isNull();
}
private ProjectResource createProjectResource() {
return new ProjectResource(
projectCache.get(project).orElseThrow(illegalState(project)), currentUser);
}
private BranchResource createBranchResource(String branch) throws IOException {
try (Repository repository = repoManager.openRepository(project)) {
Ref ref = repository.exactRef(branch);
return new BranchResource(
projectCache.get(project).orElseThrow(illegalState(project)), currentUser, ref);
}
}
}