| // Copyright (C) 2019 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.acceptance.rest.project; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow; |
| import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS; |
| import static com.google.gerrit.testing.GerritJUnit.assertThrows; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.gerrit.acceptance.AbstractDaemonTest; |
| import com.google.gerrit.acceptance.NoHttpd; |
| import com.google.gerrit.acceptance.testsuite.project.ProjectOperations; |
| import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations; |
| import com.google.gerrit.entities.LabelFunction; |
| import com.google.gerrit.entities.LabelId; |
| import com.google.gerrit.entities.Permission; |
| import com.google.gerrit.entities.RefNames; |
| import com.google.gerrit.extensions.common.LabelDefinitionInfo; |
| import com.google.gerrit.extensions.common.LabelDefinitionInput; |
| import com.google.gerrit.extensions.restapi.AuthException; |
| import com.google.gerrit.extensions.restapi.BadRequestException; |
| import com.google.gerrit.extensions.restapi.ResourceConflictException; |
| import com.google.inject.Inject; |
| import org.junit.Test; |
| |
| @NoHttpd |
| public class CreateLabelIT extends AbstractDaemonTest { |
| @Inject private RequestScopeOperations requestScopeOperations; |
| @Inject private ProjectOperations projectOperations; |
| |
| @Test |
| public void anonymous() throws Exception { |
| requestScopeOperations.setApiUserAnonymous(); |
| AuthException thrown = |
| assertThrows( |
| AuthException.class, |
| () -> |
| gApi.projects() |
| .name(project.get()) |
| .label("Foo-Review") |
| .create(new LabelDefinitionInput())); |
| assertThat(thrown).hasMessageThat().contains("Authentication required"); |
| } |
| |
| @Test |
| public void notAllowed() throws Exception { |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.READ).ref(RefNames.REFS_CONFIG).group(REGISTERED_USERS)) |
| .update(); |
| |
| requestScopeOperations.setApiUser(user.id()); |
| AuthException thrown = |
| assertThrows( |
| AuthException.class, |
| () -> |
| gApi.projects() |
| .name(project.get()) |
| .label("Foo-Review") |
| .create(new LabelDefinitionInput())); |
| assertThat(thrown).hasMessageThat().contains("write refs/meta/config not permitted"); |
| } |
| |
| @Test |
| public void cannotCreateLabelIfNameDoesntMatch() throws Exception { |
| LabelDefinitionInput input = new LabelDefinitionInput(); |
| input.name = "Foo"; |
| |
| BadRequestException thrown = |
| assertThrows( |
| BadRequestException.class, |
| () -> gApi.projects().name(project.get()).label("Bar").create(input)); |
| assertThat(thrown).hasMessageThat().contains("name in input must match name in URL"); |
| } |
| |
| @Test |
| public void cannotCreateLabelWithNameThatIsAlreadyInUse() throws Exception { |
| ResourceConflictException thrown = |
| assertThrows( |
| ResourceConflictException.class, |
| () -> |
| gApi.projects() |
| .name(allProjects.get()) |
| .label(LabelId.CODE_REVIEW) |
| .create(new LabelDefinitionInput())); |
| assertThat(thrown).hasMessageThat().contains("label Code-Review already exists"); |
| } |
| |
| @Test |
| public void cannotCreateLabelWithNameThatConflicts() throws Exception { |
| ResourceConflictException thrown = |
| assertThrows( |
| ResourceConflictException.class, |
| () -> |
| gApi.projects() |
| .name(allProjects.get()) |
| .label("code-review") |
| .create(new LabelDefinitionInput())); |
| assertThat(thrown) |
| .hasMessageThat() |
| .contains("label code-review conflicts with existing label Code-Review"); |
| } |
| |
| @Test |
| public void cannotCreateLabelWithInvalidName() throws Exception { |
| LabelDefinitionInput input = new LabelDefinitionInput(); |
| input.values = ImmutableMap.of("+1", "Looks Good", "0", "Don't Know", "-1", "Looks Bad"); |
| |
| BadRequestException thrown = |
| assertThrows( |
| BadRequestException.class, |
| () -> gApi.projects().name(project.get()).label("INVALID_NAME").create(input)); |
| assertThat(thrown).hasMessageThat().contains("invalid name: INVALID_NAME"); |
| } |
| |
| @Test |
| public void cannotCreateLabelWithoutValues() throws Exception { |
| LabelDefinitionInput input = new LabelDefinitionInput(); |
| BadRequestException thrown = |
| assertThrows( |
| BadRequestException.class, |
| () -> gApi.projects().name(project.get()).label("Foo").create(input)); |
| assertThat(thrown).hasMessageThat().contains("values are required"); |
| |
| input.values = ImmutableMap.of(); |
| thrown = |
| assertThrows( |
| BadRequestException.class, |
| () -> gApi.projects().name(project.get()).label("Foo").create(input)); |
| assertThat(thrown).hasMessageThat().contains("values are required"); |
| } |
| |
| @Test |
| public void cannotCreateLabelWithInvalidValues() throws Exception { |
| LabelDefinitionInput input = new LabelDefinitionInput(); |
| input.values = ImmutableMap.of("invalidValue", "description"); |
| |
| BadRequestException thrown = |
| assertThrows( |
| BadRequestException.class, |
| () -> gApi.projects().name(project.get()).label("Foo").create(input)); |
| assertThat(thrown).hasMessageThat().contains("invalid value: invalidValue"); |
| } |
| |
| @Test |
| public void cannotCreateLabelWithValuesThatHaveEmptyDescription() throws Exception { |
| LabelDefinitionInput input = new LabelDefinitionInput(); |
| input.values = ImmutableMap.of("+1", ""); |
| |
| BadRequestException thrown = |
| assertThrows( |
| BadRequestException.class, |
| () -> gApi.projects().name(project.get()).label("Foo").create(input)); |
| assertThat(thrown).hasMessageThat().contains("description for value '+1' cannot be empty"); |
| } |
| |
| @Test |
| public void cannotCreateLabelWithDuplicateValues() throws Exception { |
| LabelDefinitionInput input = new LabelDefinitionInput(); |
| // Positive values can be specified as '<value>' or '+<value>'. |
| input.values = |
| ImmutableMap.of( |
| "+1", "Looks Good", "1", "Looks Good", "0", "Don't Know", "-1", "Looks Bad"); |
| |
| BadRequestException thrown = |
| assertThrows( |
| BadRequestException.class, |
| () -> gApi.projects().name(allProjects.get()).label("Foo").create(input)); |
| assertThat(thrown).hasMessageThat().contains("duplicate value: 1"); |
| } |
| |
| @Test |
| public void cannotCreateLabelWithInvalidDefaultValue() throws Exception { |
| LabelDefinitionInput input = new LabelDefinitionInput(); |
| input.values = ImmutableMap.of("+1", "Looks Good", "0", "Don't Know", "-1", "Looks Bad"); |
| input.defaultValue = 5; |
| |
| BadRequestException thrown = |
| assertThrows( |
| BadRequestException.class, |
| () -> gApi.projects().name(project.get()).label("Foo").create(input)); |
| assertThat(thrown).hasMessageThat().contains("invalid default value: " + input.defaultValue); |
| } |
| |
| @Test |
| public void cannotCreateLabelWithUnknownFunction() throws Exception { |
| LabelDefinitionInput input = new LabelDefinitionInput(); |
| input.values = ImmutableMap.of("+1", "Looks Good", "0", "Don't Know", "-1", "Looks Bad"); |
| input.function = "UnknownFuction"; |
| |
| BadRequestException thrown = |
| assertThrows( |
| BadRequestException.class, |
| () -> gApi.projects().name(project.get()).label("Foo").create(input)); |
| assertThat(thrown).hasMessageThat().contains("unknown function: " + input.function); |
| } |
| |
| @Test |
| public void cannotCreateLabelWithInvalidBranch() throws Exception { |
| LabelDefinitionInput input = new LabelDefinitionInput(); |
| input.values = ImmutableMap.of("+1", "Looks Good", "0", "Don't Know", "-1", "Looks Bad"); |
| input.branches = ImmutableList.of("refs heads master"); |
| |
| BadRequestException thrown = |
| assertThrows( |
| BadRequestException.class, |
| () -> gApi.projects().name(project.get()).label("Foo").create(input)); |
| assertThat(thrown).hasMessageThat().contains("invalid branch: refs heads master"); |
| } |
| |
| @Test |
| public void createWithNameAndDescription() throws Exception { |
| LabelDefinitionInput input = new LabelDefinitionInput(); |
| input.values = ImmutableMap.of("+1", "Looks Good", " 0", "Don't Know", "-1", "Looks Bad"); |
| input.description = "Foo label description"; |
| |
| LabelDefinitionInfo createdLabel = |
| gApi.projects().name(project.get()).label("Foo").create(input).get(); |
| |
| assertThat(createdLabel.name).isEqualTo("Foo"); |
| assertThat(createdLabel.description).isEqualTo("Foo label description"); |
| } |
| |
| @Test |
| public void createWithNameAndValuesOnly() throws Exception { |
| LabelDefinitionInput input = new LabelDefinitionInput(); |
| input.values = ImmutableMap.of("+1", "Looks Good", " 0", "Don't Know", "-1", "Looks Bad"); |
| |
| LabelDefinitionInfo createdLabel = |
| gApi.projects().name(project.get()).label("Foo").create(input).get(); |
| |
| assertThat(createdLabel.name).isEqualTo("Foo"); |
| assertThat(createdLabel.projectName).isEqualTo(project.get()); |
| assertThat(createdLabel.function).isEqualTo(LabelFunction.MAX_WITH_BLOCK.getFunctionName()); |
| assertThat(createdLabel.values).containsExactlyEntriesIn(input.values); |
| assertThat(createdLabel.defaultValue).isEqualTo(0); |
| assertThat(createdLabel.branches).isNull(); |
| assertThat(createdLabel.canOverride).isTrue(); |
| assertThat(createdLabel.allowPostSubmit).isTrue(); |
| assertThat(createdLabel.ignoreSelfApproval).isNull(); |
| } |
| |
| @Test |
| public void createWithFunction() throws Exception { |
| LabelDefinitionInput input = new LabelDefinitionInput(); |
| input.values = ImmutableMap.of("+1", "Looks Good", " 0", "Don't Know", "-1", "Looks Bad"); |
| input.function = LabelFunction.NO_OP.getFunctionName(); |
| |
| LabelDefinitionInfo createdLabel = |
| gApi.projects().name(project.get()).label("Foo").create(input).get(); |
| |
| assertThat(createdLabel.function).isEqualTo(LabelFunction.NO_OP.getFunctionName()); |
| } |
| |
| @Test |
| public void functionEmptyAfterTrim() throws Exception { |
| LabelDefinitionInput input = new LabelDefinitionInput(); |
| input.values = ImmutableMap.of("+1", "Looks Good", " 0", "Don't Know", "-1", "Looks Bad"); |
| input.function = " "; |
| |
| LabelDefinitionInfo createdLabel = |
| gApi.projects().name(project.get()).label("Foo").create(input).get(); |
| |
| assertThat(createdLabel.function).isEqualTo(LabelFunction.MAX_WITH_BLOCK.getFunctionName()); |
| } |
| |
| @Test |
| public void valuesAndDescriptionsAreTrimmed() throws Exception { |
| LabelDefinitionInput input = new LabelDefinitionInput(); |
| // Positive values can be specified as '<value>' or '+<value>'. |
| input.values = |
| ImmutableMap.of( |
| " 2 ", |
| " Looks Very Good ", |
| " +1 ", |
| " Looks Good ", |
| " 0 ", |
| " Don't Know ", |
| " -1 ", |
| " Looks Bad ", |
| " -2 ", |
| " Looks Very Bad "); |
| |
| LabelDefinitionInfo createdLabel = |
| gApi.projects().name(project.get()).label("Foo").create(input).get(); |
| assertThat(createdLabel.values) |
| .containsExactly( |
| "+2", "Looks Very Good", |
| "+1", "Looks Good", |
| " 0", "Don't Know", |
| "-1", "Looks Bad", |
| "-2", "Looks Very Bad"); |
| } |
| |
| @Test |
| public void createWithDefaultValue() throws Exception { |
| LabelDefinitionInput input = new LabelDefinitionInput(); |
| input.values = ImmutableMap.of("+1", "Looks Good", " 0", "Don't Know", "-1", "Looks Bad"); |
| input.defaultValue = 1; |
| |
| LabelDefinitionInfo createdLabel = |
| gApi.projects().name(project.get()).label("Foo").create(input).get(); |
| |
| assertThat(createdLabel.defaultValue).isEqualTo(input.defaultValue); |
| } |
| |
| @Test |
| public void createWithBranches() throws Exception { |
| LabelDefinitionInput input = new LabelDefinitionInput(); |
| input.values = ImmutableMap.of("+1", "Looks Good", " 0", "Don't Know", "-1", "Looks Bad"); |
| // Branches can be full ref, ref pattern or regular expression. |
| input.branches = |
| ImmutableList.of("refs/heads/master", "refs/heads/foo/*", "^refs/heads/stable-.*"); |
| |
| LabelDefinitionInfo createdLabel = |
| gApi.projects().name(project.get()).label("Foo").create(input).get(); |
| assertThat(createdLabel.branches).containsExactlyElementsIn(input.branches); |
| } |
| |
| @Test |
| public void branchesAreTrimmed() throws Exception { |
| LabelDefinitionInput input = new LabelDefinitionInput(); |
| input.values = ImmutableMap.of("+1", "Looks Good", " 0", "Don't Know", "-1", "Looks Bad"); |
| input.branches = |
| ImmutableList.of(" refs/heads/master ", " refs/heads/foo/* ", " ^refs/heads/stable-.* "); |
| |
| LabelDefinitionInfo createdLabel = |
| gApi.projects().name(project.get()).label("Foo").create(input).get(); |
| assertThat(createdLabel.branches) |
| .containsExactly("refs/heads/master", "refs/heads/foo/*", "^refs/heads/stable-.*"); |
| } |
| |
| @Test |
| public void emptyBranchesAreIgnored() throws Exception { |
| LabelDefinitionInput input = new LabelDefinitionInput(); |
| input.values = ImmutableMap.of("+1", "Looks Good", " 0", "Don't Know", "-1", "Looks Bad"); |
| input.branches = ImmutableList.of("refs/heads/master", "", " "); |
| |
| LabelDefinitionInfo createdLabel = |
| gApi.projects().name(project.get()).label("Foo").create(input).get(); |
| assertThat(createdLabel.branches).containsExactly("refs/heads/master"); |
| } |
| |
| @Test |
| public void branchesAreAutomaticallyPrefixedWithRefsHeads() throws Exception { |
| LabelDefinitionInput input = new LabelDefinitionInput(); |
| input.values = ImmutableMap.of("+1", "Looks Good", " 0", "Don't Know", "-1", "Looks Bad"); |
| input.branches = ImmutableList.of("master", "refs/meta/config"); |
| |
| LabelDefinitionInfo createdLabel = |
| gApi.projects().name(project.get()).label("Foo").create(input).get(); |
| assertThat(createdLabel.branches).containsExactly("refs/heads/master", "refs/meta/config"); |
| } |
| |
| @Test |
| public void createWithCanOverride() throws Exception { |
| LabelDefinitionInput input = new LabelDefinitionInput(); |
| input.values = ImmutableMap.of("+1", "Looks Good", " 0", "Don't Know", "-1", "Looks Bad"); |
| input.canOverride = true; |
| |
| LabelDefinitionInfo createdLabel = |
| gApi.projects().name(project.get()).label("foo").create(input).get(); |
| assertThat(createdLabel.canOverride).isTrue(); |
| } |
| |
| @Test |
| public void createWithoutCanOverride() throws Exception { |
| LabelDefinitionInput input = new LabelDefinitionInput(); |
| input.values = ImmutableMap.of("+1", "Looks Good", " 0", "Don't Know", "-1", "Looks Bad"); |
| input.canOverride = false; |
| |
| LabelDefinitionInfo createdLabel = |
| gApi.projects().name(project.get()).label("foo").create(input).get(); |
| assertThat(createdLabel.canOverride).isNull(); |
| } |
| |
| @Test |
| public void createWithCopyCondition() throws Exception { |
| LabelDefinitionInput input = new LabelDefinitionInput(); |
| input.values = ImmutableMap.of("+1", "Looks Good", " 0", "Don't Know", "-1", "Looks Bad"); |
| input.copyCondition = "is:MAX"; |
| |
| LabelDefinitionInfo createdLabel = |
| gApi.projects().name(project.get()).label("foo").create(input).get(); |
| assertThat(createdLabel.copyCondition).isEqualTo("is:MAX"); |
| } |
| |
| @Test |
| public void createCopyConditionPerformsGroupVisibilityCheckWhenUserInPredicateIsUsed() |
| throws Exception { |
| String administratorsUUID = gApi.groups().query("name:Administrators").get().get(0).id; |
| LabelDefinitionInput input = new LabelDefinitionInput(); |
| input.values = ImmutableMap.of("+1", "Looks Good", " 0", "Don't Know", "-1", "Looks Bad"); |
| input.copyCondition = "uploaderin:" + administratorsUUID; |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.OWNER).ref("refs/*").group(REGISTERED_USERS)) |
| .update(); |
| // User can't see admin group |
| requestScopeOperations.setApiUser(user.id()); |
| BadRequestException thrown = |
| assertThrows( |
| BadRequestException.class, |
| () -> gApi.projects().name(project.get()).label("foo").create(input)); |
| assertThat(thrown).hasMessageThat().contains("Group " + administratorsUUID + " not found"); |
| |
| // Admin can see admin group |
| requestScopeOperations.setApiUser(admin.id()); |
| LabelDefinitionInfo updatedLabel = |
| gApi.projects().name(project.get()).label("foo").create(input).get(); |
| assertThat(updatedLabel.copyCondition).isEqualTo(input.copyCondition); |
| } |
| |
| @Test |
| public void createWithInvalidCopyCondition() throws Exception { |
| LabelDefinitionInput input = new LabelDefinitionInput(); |
| input.values = ImmutableMap.of("+1", "Looks Good", " 0", "Don't Know", "-1", "Looks Bad"); |
| input.copyCondition = "blarg::asd"; |
| |
| BadRequestException thrown = |
| assertThrows( |
| BadRequestException.class, |
| () -> gApi.projects().name(project.get()).label("Bar").create(input)); |
| assertThat(thrown).hasMessageThat().contains("unable to parse copy condition"); |
| } |
| |
| @Test |
| public void createWithAllowPostSubmit() throws Exception { |
| LabelDefinitionInput input = new LabelDefinitionInput(); |
| input.values = ImmutableMap.of("+1", "Looks Good", " 0", "Don't Know", "-1", "Looks Bad"); |
| input.allowPostSubmit = true; |
| |
| LabelDefinitionInfo createdLabel = |
| gApi.projects().name(project.get()).label("foo").create(input).get(); |
| assertThat(createdLabel.allowPostSubmit).isTrue(); |
| } |
| |
| @Test |
| public void createWithoutAllowPostSubmit() throws Exception { |
| LabelDefinitionInput input = new LabelDefinitionInput(); |
| input.values = ImmutableMap.of("+1", "Looks Good", " 0", "Don't Know", "-1", "Looks Bad"); |
| input.allowPostSubmit = false; |
| |
| LabelDefinitionInfo createdLabel = |
| gApi.projects().name(project.get()).label("foo").create(input).get(); |
| assertThat(createdLabel.allowPostSubmit).isNull(); |
| } |
| |
| @Test |
| public void createWithIgnoreSelfApproval() throws Exception { |
| LabelDefinitionInput input = new LabelDefinitionInput(); |
| input.values = ImmutableMap.of("+1", "Looks Good", " 0", "Don't Know", "-1", "Looks Bad"); |
| input.ignoreSelfApproval = true; |
| |
| LabelDefinitionInfo createdLabel = |
| gApi.projects().name(project.get()).label("foo").create(input).get(); |
| assertThat(createdLabel.ignoreSelfApproval).isTrue(); |
| } |
| |
| @Test |
| public void createWithoutIgnoreSelfApproval() throws Exception { |
| LabelDefinitionInput input = new LabelDefinitionInput(); |
| input.values = ImmutableMap.of("+1", "Looks Good", " 0", "Don't Know", "-1", "Looks Bad"); |
| input.ignoreSelfApproval = false; |
| |
| LabelDefinitionInfo createdLabel = |
| gApi.projects().name(project.get()).label("foo").create(input).get(); |
| assertThat(createdLabel.ignoreSelfApproval).isNull(); |
| } |
| |
| @Test |
| public void defaultCommitMessage() throws Exception { |
| LabelDefinitionInput input = new LabelDefinitionInput(); |
| input.values = ImmutableMap.of("+1", "Looks Good", " 0", "Don't Know", "-1", "Looks Bad"); |
| gApi.projects().name(project.get()).label("Foo").create(input); |
| assertThat(projectOperations.project(project).getHead(RefNames.REFS_CONFIG).getShortMessage()) |
| .isEqualTo("Update label"); |
| } |
| |
| @Test |
| public void withCommitMessage() throws Exception { |
| LabelDefinitionInput input = new LabelDefinitionInput(); |
| input.values = ImmutableMap.of("+1", "Looks Good", " 0", "Don't Know", "-1", "Looks Bad"); |
| input.commitMessage = "Add Foo Label"; |
| gApi.projects().name(project.get()).label("Foo").create(input); |
| assertThat(projectOperations.project(project).getHead(RefNames.REFS_CONFIG).getShortMessage()) |
| .isEqualTo(input.commitMessage); |
| } |
| |
| @Test |
| public void commitMessageIsTrimmed() throws Exception { |
| LabelDefinitionInput input = new LabelDefinitionInput(); |
| input.values = ImmutableMap.of("+1", "Looks Good", " 0", "Don't Know", "-1", "Looks Bad"); |
| input.commitMessage = " Add Foo Label "; |
| gApi.projects().name(project.get()).label("Foo").create(input); |
| assertThat(projectOperations.project(project).getHead(RefNames.REFS_CONFIG).getShortMessage()) |
| .isEqualTo("Add Foo Label"); |
| } |
| } |