blob: f8b88706be65e1bbeb56b894f64f99bc59768d8e [file] [log] [blame]
// Copyright (C) 2023 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.googlesource.gerrit.owners;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
import static com.google.gerrit.server.project.testing.TestLabels.codeReview;
import static com.google.gerrit.server.project.testing.TestLabels.labelBuilder;
import static com.google.gerrit.server.project.testing.TestLabels.value;
import static com.google.gerrit.server.project.testing.TestLabels.verified;
import static com.googlesource.gerrit.owners.OwnersSubmitRequirement.hasSufficientApproval;
import static com.googlesource.gerrit.owners.OwnersSubmitRequirement.isApprovalMissing;
import static com.googlesource.gerrit.owners.OwnersSubmitRequirement.isApprovedByOwner;
import static com.googlesource.gerrit.owners.OwnersSubmitRequirement.isLabelApproved;
import static com.googlesource.gerrit.owners.OwnersSubmitRequirement.resolveLabel;
import static org.mockito.Mockito.mock;
import com.google.gerrit.entities.Account;
import com.google.gerrit.entities.LabelFunction;
import com.google.gerrit.entities.LabelId;
import com.google.gerrit.entities.LabelType;
import com.google.gerrit.entities.LabelTypes;
import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.entities.PatchSetApproval;
import com.google.gerrit.entities.Project;
import java.sql.Timestamp;
import java.time.Instant;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.junit.Test;
public class OwnersSubmitRequirementTest {
private static String LABEL_ID = "foo";
private static int MAX_LABEL_VALUE = 1;
private static Project.NameKey PROJECT = Project.nameKey("project");
@Test
public void shouldResolveLabelToConfiguredOne() {
// when
String label = resolveLabel(null, Optional.of(LABEL_ID));
// then
assertThat(label).isEqualTo(LABEL_ID);
}
@Test
public void shouldResolveLabelToCodeReviewIfProjectHasCodeReviewLabelConfigured() {
// given
LabelTypes types =
new LabelTypes(
List.of(verified(), label().setFunction(LabelFunction.NO_BLOCK).build(), codeReview()));
// when
String label = resolveLabel(types, Optional.empty());
// then
assertThat(label).isEqualTo(LabelId.CODE_REVIEW);
}
@Test
public void shouldOwnersLabelContainOnlyConfiguredLabel() {
// when
LabelTypes result =
OwnersSubmitRequirement.ownersLabel(
new LabelTypes(List.of(label().build())), LABEL_ID, PROJECT);
// then
assertThat(result.getLabelTypes()).hasSize(1);
assertThat(result.byLabel(LABEL_ID)).isPresent();
}
@Test
public void shouldOwnersLabelBeEmptyIfNonExistingLabelIsConfigured() {
// when
LabelTypes result =
OwnersSubmitRequirement.ownersLabel(
new LabelTypes(List.of(codeReview())), LABEL_ID, PROJECT);
// then
assertThat(result.getLabelTypes()).isEmpty();
}
@Test
public void shouldApprovalBeMissingWhenSomeoneElseApproved() {
// given
Account.Id fileOwner = mock(Account.Id.class);
Account.Id uploader = mock(Account.Id.class);
LabelTypes labelTypes = maxNoBlockLabelFooTypes();
Map<Account.Id, List<PatchSetApproval>> uploaderApproval =
Map.of(uploader, List.of(approvedBy(uploader, LABEL_ID, MAX_LABEL_VALUE)));
// when
boolean isApprovalMissing =
isApprovalMissing(
Map.entry("path", Set.of(fileOwner)), uploader, uploaderApproval, labelTypes);
// then
assertThat(isApprovalMissing).isTrue();
}
@Test
public void shouldApprovalBeNotMissingWhenApprovedByFileOwner() {
// given
Account.Id fileOwner = mock(Account.Id.class);
Account.Id uploader = mock(Account.Id.class);
LabelTypes labelTypes = maxNoBlockLabelFooTypes();
Map<Account.Id, List<PatchSetApproval>> fileOwnerApproval =
Map.of(fileOwner, List.of(approvedBy(fileOwner, LABEL_ID, MAX_LABEL_VALUE)));
// when
boolean isApprovalMissing =
isApprovalMissing(
Map.entry("path", Set.of(fileOwner)), uploader, fileOwnerApproval, labelTypes);
// then
assertThat(isApprovalMissing).isFalse();
}
@Test
public void shouldApprovalBeNotMissingWhenApprovedByAtLeastOneOwner() {
// given
Account.Id fileOwnerA = mock(Account.Id.class);
Account.Id fileOwnerB = mock(Account.Id.class);
Account.Id uploader = mock(Account.Id.class);
LabelTypes labelTypes = maxNoBlockLabelFooTypes();
Map<Account.Id, List<PatchSetApproval>> fileOwnerApproval =
Map.of(fileOwnerA, List.of(approvedBy(fileOwnerA, LABEL_ID, MAX_LABEL_VALUE)));
// when
boolean isApprovalMissing =
isApprovalMissing(
Map.entry("path", Set.of(fileOwnerA, fileOwnerB)),
uploader,
fileOwnerApproval,
labelTypes);
// then
assertThat(isApprovalMissing).isFalse();
}
@Test
public void shouldNotBeApprovedByOwnerWhenSomeoneElseApproved() {
// given
Account.Id fileOwner = mock(Account.Id.class);
Account.Id uploader = mock(Account.Id.class);
LabelTypes labelTypes = maxNoBlockLabelFooTypes();
Map<Account.Id, List<PatchSetApproval>> uploaderApproval =
Map.of(uploader, List.of(approvedBy(uploader, LABEL_ID, MAX_LABEL_VALUE)));
// when
boolean approvedByOwner = isApprovedByOwner(fileOwner, fileOwner, uploaderApproval, labelTypes);
// then
assertThat(approvedByOwner).isFalse();
}
@Test
public void shouldNotBeApprovedWhenApprovalGivenForDifferentLabel() {
// given
Account.Id fileOwner = mock(Account.Id.class);
LabelTypes labelTypes =
new LabelTypes(
List.of(label().setName("bar").setFunction(LabelFunction.MAX_NO_BLOCK).build()));
Map<Account.Id, List<PatchSetApproval>> fileOwnerForDifferentLabelApproval =
Map.of(fileOwner, List.of(approvedBy(fileOwner, LABEL_ID, MAX_LABEL_VALUE)));
// when
boolean approvedByOwner =
isApprovedByOwner(fileOwner, fileOwner, fileOwnerForDifferentLabelApproval, labelTypes);
// then
assertThat(approvedByOwner).isFalse();
}
@Test
public void shouldBeApprovedByOwner() {
// given
Account.Id fileOwner = mock(Account.Id.class);
LabelTypes labelTypes = maxNoBlockLabelFooTypes();
Map<Account.Id, List<PatchSetApproval>> fileOwnerApproval =
Map.of(fileOwner, List.of(approvedBy(fileOwner, LABEL_ID, MAX_LABEL_VALUE)));
// when
boolean approvedByOwner =
isApprovedByOwner(fileOwner, fileOwner, fileOwnerApproval, labelTypes);
// then
assertThat(approvedByOwner).isTrue();
}
@Test
public void shouldHaveNotSufficientApprovalWhenLabelIsNotApproved() {
// given
LabelType maxValueRequired = label().setFunction(LabelFunction.MAX_NO_BLOCK).build();
Account.Id fileOwner = mock(Account.Id.class);
LabelTypes labelTypes = new LabelTypes(List.of(maxValueRequired));
// when
boolean hasSufficientApproval =
hasSufficientApproval(approvedBy(fileOwner, LABEL_ID, 0), labelTypes, fileOwner, fileOwner);
// then
assertThat(hasSufficientApproval).isFalse();
}
@Test
public void shouldHaveNotSufficientApprovalWhenLabelDoesntMatch() {
// given
Account.Id fileOwner = mock(Account.Id.class);
LabelTypes labelTypes = new LabelTypes(Collections.emptyList());
// when
boolean hasSufficientApproval =
hasSufficientApproval(approvedBy(fileOwner, LABEL_ID, 0), labelTypes, fileOwner, fileOwner);
// then
assertThat(hasSufficientApproval).isFalse();
}
@Test
public void shouldHaveSufficientApprovalWhenLabelIsApproved() {
// given
LabelType maxValueRequired = label().setFunction(LabelFunction.MAX_NO_BLOCK).build();
Account.Id fileOwner = mock(Account.Id.class);
LabelTypes labelTypes = new LabelTypes(List.of(maxValueRequired));
// when
boolean hasSufficientApproval =
hasSufficientApproval(
approvedBy(fileOwner, LABEL_ID, MAX_LABEL_VALUE), labelTypes, fileOwner, fileOwner);
// then
assertThat(hasSufficientApproval).isTrue();
}
@Test
public void labelShouldNotBeApprovedWhenSelfApprovalIsDisabledAndOwnerApproved() {
// given
LabelType ignoreSelfApproval = label().setIgnoreSelfApproval(true).build();
Account.Id fileOwner = mock(Account.Id.class);
// when
boolean approved =
isLabelApproved(
ignoreSelfApproval,
fileOwner,
fileOwner,
approvedBy(fileOwner, LABEL_ID, MAX_LABEL_VALUE));
// then
assertThat(approved).isFalse();
}
@Test
public void labelShouldNotBeApprovedWhenMaxValueIsRequiredButNotProvided() {
// given
LabelType maxValueRequired = label().setFunction(LabelFunction.MAX_NO_BLOCK).build();
Account.Id fileOwner = mock(Account.Id.class);
// when
boolean approved =
isLabelApproved(maxValueRequired, fileOwner, fileOwner, approvedBy(fileOwner, LABEL_ID, 0));
// then
assertThat(approved).isFalse();
}
@Test
public void labelShouldBeApprovedWhenMaxValueIsRequiredAndProvided() {
// given
LabelType maxValueRequired = label().setFunction(LabelFunction.MAX_NO_BLOCK).build();
Account.Id fileOwner = mock(Account.Id.class);
// when
boolean approved =
isLabelApproved(
maxValueRequired,
fileOwner,
fileOwner,
approvedBy(fileOwner, LABEL_ID, MAX_LABEL_VALUE));
// then
assertThat(approved).isTrue();
}
@Test
public void labelShouldNotBeApprovedWhenAnyValueWithBlockIsConfiguredAndMaxNegativeIsProvided() {
// given
LabelType anyWithBlock = label().setFunction(LabelFunction.ANY_WITH_BLOCK).build();
Account.Id fileOwner = mock(Account.Id.class);
// when
boolean approved =
isLabelApproved(anyWithBlock, fileOwner, fileOwner, approvedBy(fileOwner, LABEL_ID, -1));
// then
assertThat(approved).isFalse();
}
@Test
public void labelShouldBeApprovedWhenAnyValueWithBlockIsConfiguredAndPositiveValueIsProvided() {
// given
LabelType anyWithBlock =
label()
.setValues(
Arrays.asList(
value(2, "Approved"),
value(1, "OK"),
value(0, "No score"),
value(-1, "Blocked")))
.setFunction(LabelFunction.ANY_WITH_BLOCK)
.build();
Account.Id fileOwner = mock(Account.Id.class);
// when
boolean approved =
isLabelApproved(anyWithBlock, fileOwner, fileOwner, approvedBy(fileOwner, LABEL_ID, 1));
// then
assertThat(approved).isTrue();
}
@Test
public void labelShouldNotBeApprovedWhenAnyValueWithBlockIsConfiguredAndDefaultValueIsProvided() {
// given
LabelType anyWithBlock =
label()
.setValues(
Arrays.asList(
value(2, "Approved"),
value(1, "OK"),
value(0, "No score"),
value(-1, "Blocked")))
.setFunction(LabelFunction.ANY_WITH_BLOCK)
.build();
Account.Id fileOwner = mock(Account.Id.class);
// when
boolean approved =
isLabelApproved(anyWithBlock, fileOwner, fileOwner, approvedBy(fileOwner, LABEL_ID, 0));
// then
assertThat(approved).isFalse();
}
private static final LabelTypes maxNoBlockLabelFooTypes() {
LabelType maxValueRequired = label().setFunction(LabelFunction.MAX_NO_BLOCK).build();
return new LabelTypes(List.of(maxValueRequired));
}
private static final LabelType.Builder label() {
return labelBuilder(
LABEL_ID, value(MAX_LABEL_VALUE, "Approved"), value(0, "No score"), value(-1, "Blocked"));
}
private static final PatchSetApproval approvedBy(Account.Id approving, String label, int value) {
return PatchSetApproval.builder()
.key(PatchSetApproval.key(mock(PatchSet.Id.class), approving, LabelId.create(label)))
.granted(Timestamp.from(Instant.now()))
.realAccountId(approving)
.value(value)
.build();
}
}