Add REST endpoint to update an existing label Bug: Issue 11522 Signed-off-by: Edwin Kempin <ekempin@google.com> Change-Id: Ic50e7fc1cf4daedfb8c7f0ae6458a807146ade16
diff --git a/Documentation/rest-api-projects.txt b/Documentation/rest-api-projects.txt index fd2e5b9..65996a2 100644 --- a/Documentation/rest-api-projects.txt +++ b/Documentation/rest-api-projects.txt
@@ -3139,6 +3139,61 @@ } ---- +[[set-label]] +=== Set Label +-- +'PUT /projects/link:#project-name[\{project-name\}]/labels/link:#label-name[\{label-name\}]' +-- + +Updates the definition of a label that is defined in this project. + +The calling user must have write access to the `refs/meta/config` branch of the +project. + +Properties which are not set in the input entity are not modified. + +.Request +---- + PUT /projects/All-Projects/labels/Code-Review HTTP/1.0 + Content-Type: application/json; charset=UTF-8 + + { + "commit_message": "Ignore self approvals for Code-Review label", + "ignore_self_approval": true + } +---- + +As response a link:#label-definition-info[LabelDefinitionInfo] entity is +returned that describes the updated label. + +.Response +---- + HTTP/1.1 200 OK + Content-Disposition: attachment + Content-Type: application/json; charset=UTF-8 + + )]}' + { + "name": "Code-Review", + "project": "All-Projects", + "function": "MaxWithBlock", + "values": { + " 0": "No score", + "-1": "I would prefer this is not merged as is", + "-2": "This shall not be merged", + "+1": "Looks good to me, but someone else must approve", + "+2": "Looks good to me, approved" + }, + "default_value": 0, + "can_override": true, + "copy_min_score": true, + "copy_all_scores_if_no_change": true, + "copy_all_scores_on_trivial_rebase": true, + "allow_post_submit": true, + "ignore_self_approval": true + } +---- + [[ids]] == IDs @@ -3735,6 +3790,66 @@ set on the label. |============================= +[[label-definition-input]] +=== LabelDefinitionInput +The `LabelTypeInput` entity describes a link:config-labels.html[ +review label]. + +[options="header",cols="1,^2,4"] +|============================= +|Field Name ||Description +|`commit_message`|optional| +Message that should be used to commit the change of the label in the +`project.config` file to the `refs/meta/config` branch. +|`name` |optional| +The new link:config-labels.html#label_name[name] of the label. +|`function` |optional| +The new link:config-labels.html#label_function[function] of the label (can be +`MaxWithBlock`, `AnyWithBlock`, `MaxNoBlock`, `NoBlock`, `NoOp` and `PatchSetLock`. +|`values` |optional| +The new link:config-labels.html#label_value[values] of the label as a map of +label value to value description. The label values are formatted strings, e.g. +"+1" instead of "1", " 0" instead of "0". +|`default_value` |optional| +The new link:config-labels.html#label_defaultValue[default value] of the label +(as integer). +|`branches` |optional| +The new branches for which the label applies as a list of +link:config-labels.html#label_branch[branches]. A branch can be a ref, a ref +pattern or a regular expression. If not set, the label applies for all +branches. +|`can_override` |optional| +Whether this label can be link:config-labels.html#label_canOverride[overridden] +by child projects. +|`copy_any_score`|optional| +Whether link:config-labels.html#label_copyAnyScore[copyAnyScore] is set on the +label. +|`copy_min_score`|optional| +Whether link:config-labels.html#label_copyMinScore[copyMinScore] is set on the +label. +|`copy_max_score`|optional| +Whether link:config-labels.html#label_copyMaxScore[copyMaxScore] is set on the +label. +|`copy_all_scores_if_no_change`|optional| +Whether link:config-labels.html#label_copyAllScoresIfNoChange[ +copyAllScoresIfNoChange] is set on the label. +|`copy_all_scores_if_no_code_change`|optional| +Whether link:config-labels.html#label_copyAllScoresIfNoCodeChange[ +copyAllScoresIfNoCodeChange] is set on the label. +|`copy_all_scores_on_trivial_rebase`|optional| +Whether link:config-labels.html#label_copyAllScoresOnTrivialRebase[ +copyAllScoresOnTrivialRebase] is set on the label. +|`copy_all_scores_on_merge_first_parent_update`|optional| +Whether link:config-labels.html#label_copyAllScoresOnMergeFirstParentUpdate[ +copyAllScoresOnMergeFirstParentUpdate] is set on the label. +|`allow_post_submit`|optional| +Whether link:config-labels.html#label_allowPostSubmit[allowPostSubmit] is set +on the label. +|`ignore_self_approval`|optional| +Whether link:config-labels.html#label_ignoreSelfApproval[ignoreSelfApproval] is +set on the label. +|============================= + [[label-type-info]] === LabelTypeInfo The `LabelTypeInfo` entity contains metadata about the labels that a
diff --git a/java/com/google/gerrit/common/data/LabelType.java b/java/com/google/gerrit/common/data/LabelType.java index 4be7251..14b8310 100644 --- a/java/com/google/gerrit/common/data/LabelType.java +++ b/java/com/google/gerrit/common/data/LabelType.java
@@ -157,6 +157,10 @@ return name; } + public void setName(String name) { + this.name = checkName(name); + } + public boolean matches(PatchSetApproval psa) { return psa.labelId().get().equalsIgnoreCase(name); } @@ -199,7 +203,7 @@ } public void setRefPatterns(List<String> refPatterns) { - if (refPatterns != null) { + if (refPatterns != null && !refPatterns.isEmpty()) { this.refPatterns = refPatterns.stream().collect(collectingAndThen(toList(), Collections::unmodifiableList)); } else { @@ -211,6 +215,10 @@ return values; } + public void setValues(List<LabelValue> values) { + this.values = sortValues(values); + } + public LabelValue getMin() { if (values.isEmpty()) { return null;
diff --git a/java/com/google/gerrit/extensions/api/projects/LabelApi.java b/java/com/google/gerrit/extensions/api/projects/LabelApi.java index 8a3ba08c..f11d394 100644 --- a/java/com/google/gerrit/extensions/api/projects/LabelApi.java +++ b/java/com/google/gerrit/extensions/api/projects/LabelApi.java
@@ -15,12 +15,15 @@ package com.google.gerrit.extensions.api.projects; import com.google.gerrit.extensions.common.LabelDefinitionInfo; +import com.google.gerrit.extensions.common.LabelDefinitionInput; import com.google.gerrit.extensions.restapi.NotImplementedException; import com.google.gerrit.extensions.restapi.RestApiException; public interface LabelApi { LabelDefinitionInfo get() throws RestApiException; + LabelDefinitionInfo update(LabelDefinitionInput input) throws RestApiException; + /** * A default implementation which allows source compatibility when adding new methods to the * interface. @@ -30,5 +33,10 @@ public LabelDefinitionInfo get() throws RestApiException { throw new NotImplementedException(); } + + @Override + public LabelDefinitionInfo update(LabelDefinitionInput input) throws RestApiException { + throw new NotImplementedException(); + } } }
diff --git a/java/com/google/gerrit/extensions/common/LabelDefinitionInput.java b/java/com/google/gerrit/extensions/common/LabelDefinitionInput.java new file mode 100644 index 0000000..6b088d3 --- /dev/null +++ b/java/com/google/gerrit/extensions/common/LabelDefinitionInput.java
@@ -0,0 +1,37 @@ +// 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.extensions.common; + +import java.util.List; +import java.util.Map; + +public class LabelDefinitionInput { + public String commitMessage; + public String name; + public String function; + public Map<String, String> values; + public Short defaultValue; + public List<String> branches; + public Boolean canOverride; + public Boolean copyAnyScore; + public Boolean copyMinScore; + public Boolean copyMaxScore; + public Boolean copyAllScoresIfNoChange; + public Boolean copyAllScoresIfNoCodeChange; + public Boolean copyAllScoresOnTrivialRebase; + public Boolean copyAllScoresOnMergeFirstParentUpdate; + public Boolean allowPostSubmit; + public Boolean ignoreSelfApproval; +}
diff --git a/java/com/google/gerrit/server/api/projects/LabelApiImpl.java b/java/com/google/gerrit/server/api/projects/LabelApiImpl.java index 1c7229e..4c8759e 100644 --- a/java/com/google/gerrit/server/api/projects/LabelApiImpl.java +++ b/java/com/google/gerrit/server/api/projects/LabelApiImpl.java
@@ -18,9 +18,11 @@ import com.google.gerrit.extensions.api.projects.LabelApi; import com.google.gerrit.extensions.common.LabelDefinitionInfo; +import com.google.gerrit.extensions.common.LabelDefinitionInput; import com.google.gerrit.extensions.restapi.RestApiException; import com.google.gerrit.server.project.LabelResource; import com.google.gerrit.server.restapi.project.GetLabel; +import com.google.gerrit.server.restapi.project.SetLabel; import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; @@ -30,11 +32,13 @@ } private final GetLabel getLabel; + private final SetLabel setLabel; private final LabelResource rsrc; @Inject - LabelApiImpl(GetLabel getLabel, @Assisted LabelResource rsrc) { + LabelApiImpl(GetLabel getLabel, SetLabel setLabel, @Assisted LabelResource rsrc) { this.getLabel = getLabel; + this.setLabel = setLabel; this.rsrc = rsrc; } @@ -46,4 +50,13 @@ throw asRestApiException("Cannot get label", e); } } + + @Override + public LabelDefinitionInfo update(LabelDefinitionInput input) throws RestApiException { + try { + return setLabel.apply(rsrc, input).value(); + } catch (Exception e) { + throw asRestApiException("Cannot update label", e); + } + } }
diff --git a/java/com/google/gerrit/server/project/ProjectConfig.java b/java/com/google/gerrit/server/project/ProjectConfig.java index 44d9d98..9604f28 100644 --- a/java/com/google/gerrit/server/project/ProjectConfig.java +++ b/java/com/google/gerrit/server/project/ProjectConfig.java
@@ -1488,6 +1488,8 @@ List<String> refPatterns = label.getRefPatterns(); if (refPatterns != null && !refPatterns.isEmpty()) { rc.setStringList(LABEL, name, KEY_BRANCH, refPatterns); + } else { + rc.unset(LABEL, name, KEY_BRANCH); } }
diff --git a/java/com/google/gerrit/server/restapi/project/Module.java b/java/com/google/gerrit/server/restapi/project/Module.java index 0f5982f..d8ea436 100644 --- a/java/com/google/gerrit/server/restapi/project/Module.java +++ b/java/com/google/gerrit/server/restapi/project/Module.java
@@ -69,6 +69,7 @@ child(PROJECT_KIND, "labels").to(LabelsCollection.class); get(LABEL_KIND).to(GetLabel.class); + put(LABEL_KIND).to(SetLabel.class); get(PROJECT_KIND, "HEAD").to(GetHead.class); put(PROJECT_KIND, "HEAD").to(SetHead.class);
diff --git a/java/com/google/gerrit/server/restapi/project/SetLabel.java b/java/com/google/gerrit/server/restapi/project/SetLabel.java new file mode 100644 index 0000000..b61a953 --- /dev/null +++ b/java/com/google/gerrit/server/restapi/project/SetLabel.java
@@ -0,0 +1,239 @@ +// 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.server.restapi.project; + +import com.google.common.base.Strings; +import com.google.common.primitives.Shorts; +import com.google.gerrit.common.data.LabelFunction; +import com.google.gerrit.common.data.LabelType; +import com.google.gerrit.common.data.LabelValue; +import com.google.gerrit.common.data.PermissionRule; +import com.google.gerrit.entities.RefNames; +import com.google.gerrit.exceptions.InvalidNameException; +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.gerrit.extensions.restapi.Response; +import com.google.gerrit.extensions.restapi.RestModifyView; +import com.google.gerrit.server.git.meta.MetaDataUpdate; +import com.google.gerrit.server.permissions.PermissionBackend; +import com.google.gerrit.server.permissions.PermissionBackendException; +import com.google.gerrit.server.permissions.ProjectPermission; +import com.google.gerrit.server.project.LabelDefinitionJson; +import com.google.gerrit.server.project.LabelResource; +import com.google.gerrit.server.project.ProjectCache; +import com.google.gerrit.server.project.ProjectConfig; +import com.google.gerrit.server.project.RefPattern; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map.Entry; +import java.util.Optional; +import org.eclipse.jgit.errors.ConfigInvalidException; + +@Singleton +public class SetLabel implements RestModifyView<LabelResource, LabelDefinitionInput> { + private final PermissionBackend permissionBackend; + private final MetaDataUpdate.User updateFactory; + private final ProjectConfig.Factory projectConfigFactory; + private final ProjectCache projectCache; + + @Inject + public SetLabel( + PermissionBackend permissionBackend, + MetaDataUpdate.User updateFactory, + ProjectConfig.Factory projectConfigFactory, + ProjectCache projectCache) { + this.permissionBackend = permissionBackend; + this.updateFactory = updateFactory; + this.projectConfigFactory = projectConfigFactory; + this.projectCache = projectCache; + } + + @Override + public Response<LabelDefinitionInfo> apply(LabelResource rsrc, LabelDefinitionInput input) + throws AuthException, BadRequestException, ResourceConflictException, + PermissionBackendException, IOException, ConfigInvalidException { + permissionBackend + .currentUser() + .project(rsrc.getProject().getNameKey()) + .check(ProjectPermission.WRITE_CONFIG); + + if (input == null) { + input = new LabelDefinitionInput(); + } + + LabelType labelType = rsrc.getLabelType(); + + try (MetaDataUpdate md = updateFactory.create(rsrc.getProject().getNameKey())) { + boolean dirty = false; + + ProjectConfig config = projectConfigFactory.read(md); + config.getLabelSections().remove(labelType.getName()); + + if (input.name != null) { + String newName = input.name.trim(); + if (newName.isEmpty()) { + throw new BadRequestException("name cannot be empty"); + } + if (!newName.equals(labelType.getName())) { + if (config.getLabelSections().containsKey(newName)) { + throw new ResourceConflictException("name " + newName + " already in use"); + } + try { + labelType.setName(newName); + } catch (IllegalArgumentException e) { + throw new BadRequestException("invalid name: " + input.name, e); + } + dirty = true; + } + } + + if (input.function != null) { + String newFunctionName = input.function.trim(); + if (newFunctionName.isEmpty()) { + throw new BadRequestException("function cannot be empty"); + } + Optional<LabelFunction> newFunction = LabelFunction.parse(newFunctionName); + if (!newFunction.isPresent()) { + throw new BadRequestException("unknown function: " + input.function); + } + labelType.setFunction(newFunction.get()); + dirty = true; + } + + if (input.values != null) { + if (input.values.isEmpty()) { + throw new BadRequestException("values cannot be empty"); + } + + List<LabelValue> newValues = new ArrayList<>(); + for (Entry<String, String> e : input.values.entrySet()) { + short value; + try { + value = Shorts.checkedCast(PermissionRule.parseInt(e.getKey().trim())); + } catch (NumberFormatException ex) { + throw new BadRequestException("invalid value: " + e.getKey(), ex); + } + String valueDescription = e.getValue().trim(); + if (valueDescription.isEmpty()) { + throw new BadRequestException( + "description for value '" + e.getKey() + "' cannot be empty"); + } + newValues.add(new LabelValue(value, valueDescription)); + } + labelType.setValues(newValues); + dirty = true; + } + + if (input.defaultValue != null) { + if (labelType.getValue(input.defaultValue) == null) { + throw new BadRequestException("invalid default value: " + input.defaultValue); + } + labelType.setDefaultValue(input.defaultValue); + dirty = true; + } + + if (input.branches != null) { + List<String> newBranches = new ArrayList<>(); + for (String branch : input.branches) { + String newBranch = branch.trim(); + if (newBranch.isEmpty()) { + continue; + } + if (!RefPattern.isRE(newBranch) && !newBranch.startsWith(RefNames.REFS)) { + newBranch = RefNames.REFS_HEADS + newBranch; + } + try { + RefPattern.validate(newBranch); + } catch (InvalidNameException e) { + throw new BadRequestException("invalid branch: " + branch, e); + } + newBranches.add(newBranch); + } + labelType.setRefPatterns(newBranches); + dirty = true; + } + + if (input.canOverride != null) { + labelType.setCanOverride(input.canOverride); + dirty = true; + } + + if (input.copyAnyScore != null) { + labelType.setCopyAnyScore(input.copyAnyScore); + dirty = true; + } + + if (input.copyMinScore != null) { + labelType.setCopyMinScore(input.copyMinScore); + dirty = true; + } + + if (input.copyMaxScore != null) { + labelType.setCopyMaxScore(input.copyMaxScore); + dirty = true; + } + + if (input.copyAllScoresIfNoChange != null) { + labelType.setCopyAllScoresIfNoChange(input.copyAllScoresIfNoChange); + } + + if (input.copyAllScoresIfNoCodeChange != null) { + labelType.setCopyAllScoresIfNoCodeChange(input.copyAllScoresIfNoCodeChange); + dirty = true; + } + + if (input.copyAllScoresOnTrivialRebase != null) { + labelType.setCopyAllScoresOnTrivialRebase(input.copyAllScoresOnTrivialRebase); + dirty = true; + } + + if (input.copyAllScoresOnMergeFirstParentUpdate != null) { + labelType.setCopyAllScoresOnMergeFirstParentUpdate( + input.copyAllScoresOnMergeFirstParentUpdate); + dirty = true; + } + + if (input.allowPostSubmit != null) { + labelType.setAllowPostSubmit(input.allowPostSubmit); + dirty = true; + } + + if (input.ignoreSelfApproval != null) { + labelType.setIgnoreSelfApproval(input.ignoreSelfApproval); + dirty = true; + } + + if (dirty) { + config.getLabelSections().put(labelType.getName(), labelType); + + if (input.commitMessage != null) { + md.setMessage(Strings.emptyToNull(input.commitMessage.trim())); + } else { + md.setMessage("Update label"); + } + + config.commit(md); + projectCache.evict(rsrc.getProject().getProjectState().getProject()); + } + } + return Response.ok(LabelDefinitionJson.format(rsrc.getProject().getNameKey(), labelType)); + } +}
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/SetLabelIT.java b/javatests/com/google/gerrit/acceptance/rest/project/SetLabelIT.java new file mode 100644 index 0000000..b4c7cdb --- /dev/null +++ b/javatests/com/google/gerrit/acceptance/rest/project/SetLabelIT.java
@@ -0,0 +1,849 @@ +// 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.common.data.LabelFunction; +import com.google.gerrit.common.data.LabelType; +import com.google.gerrit.common.data.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.gerrit.extensions.restapi.ResourceNotFoundException; +import com.google.inject.Inject; +import org.eclipse.jgit.revwalk.RevCommit; +import org.junit.Test; + +@NoHttpd +public class SetLabelIT extends AbstractDaemonTest { + @Inject private RequestScopeOperations requestScopeOperations; + @Inject private ProjectOperations projectOperations; + + @Test + public void notAllowed() throws Exception { + projectOperations + .project(allProjects) + .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(allProjects.get()) + .label("Code-Review") + .update(new LabelDefinitionInput())); + assertThat(thrown).hasMessageThat().contains("write refs/meta/config not permitted"); + } + + @Test + public void updateName() throws Exception { + LabelDefinitionInput input = new LabelDefinitionInput(); + input.name = "Foo-Review"; + + LabelDefinitionInfo updatedLabel = + gApi.projects().name(allProjects.get()).label("Code-Review").update(input); + assertThat(updatedLabel.name).isEqualTo(input.name); + + assertThat(gApi.projects().name(allProjects.get()).label("Foo-Review").get()).isNotNull(); + assertThrows( + ResourceNotFoundException.class, + () -> gApi.projects().name(project.get()).label("Code-Review").get()); + } + + @Test + public void nameIsTrimmed() throws Exception { + LabelDefinitionInput input = new LabelDefinitionInput(); + input.name = " Foo-Review "; + + LabelDefinitionInfo updatedLabel = + gApi.projects().name(allProjects.get()).label("Code-Review").update(input); + assertThat(updatedLabel.name).isEqualTo("Foo-Review"); + + assertThat(gApi.projects().name(allProjects.get()).label("Foo-Review").get()).isNotNull(); + assertThrows( + ResourceNotFoundException.class, + () -> gApi.projects().name(project.get()).label("Code-Review").get()); + } + + @Test + public void cannotSetEmptyName() throws Exception { + LabelDefinitionInput input = new LabelDefinitionInput(); + input.name = ""; + + BadRequestException thrown = + assertThrows( + BadRequestException.class, + () -> gApi.projects().name(allProjects.get()).label("Code-Review").update(input)); + assertThat(thrown).hasMessageThat().contains("name cannot be empty"); + } + + @Test + public void cannotSetInvalidName() throws Exception { + LabelDefinitionInput input = new LabelDefinitionInput(); + input.name = "INVALID_NAME"; + + BadRequestException thrown = + assertThrows( + BadRequestException.class, + () -> gApi.projects().name(allProjects.get()).label("Code-Review").update(input)); + assertThat(thrown).hasMessageThat().contains("invalid name: " + input.name); + } + + @Test + public void cannotSetNameIfNameClashes() throws Exception { + configLabel("Foo-Review", LabelFunction.NO_OP); + configLabel("Bar-Review", LabelFunction.NO_OP); + + LabelDefinitionInput input = new LabelDefinitionInput(); + input.name = "Bar-Review"; + + ResourceConflictException thrown = + assertThrows( + ResourceConflictException.class, + () -> gApi.projects().name(project.get()).label("Foo-Review").update(input)); + assertThat(thrown).hasMessageThat().contains("name " + input.name + " already in use"); + } + + @Test + public void updateFunction() throws Exception { + LabelDefinitionInput input = new LabelDefinitionInput(); + input.function = LabelFunction.NO_OP.getFunctionName(); + + LabelDefinitionInfo updatedLabel = + gApi.projects().name(allProjects.get()).label("Code-Review").update(input); + assertThat(updatedLabel.function).isEqualTo(input.function); + + assertThat(gApi.projects().name(allProjects.get()).label("Code-Review").get().function) + .isEqualTo(input.function); + } + + @Test + public void functionIsTrimmed() throws Exception { + LabelDefinitionInput input = new LabelDefinitionInput(); + input.function = " " + LabelFunction.NO_OP.getFunctionName() + " "; + + LabelDefinitionInfo updatedLabel = + gApi.projects().name(allProjects.get()).label("Code-Review").update(input); + assertThat(updatedLabel.function).isEqualTo(LabelFunction.NO_OP.getFunctionName()); + + assertThat(gApi.projects().name(allProjects.get()).label("Code-Review").get().function) + .isEqualTo(LabelFunction.NO_OP.getFunctionName()); + } + + @Test + public void cannotSetEmptyFunction() throws Exception { + LabelDefinitionInput input = new LabelDefinitionInput(); + input.function = ""; + + BadRequestException thrown = + assertThrows( + BadRequestException.class, + () -> gApi.projects().name(allProjects.get()).label("Code-Review").update(input)); + assertThat(thrown).hasMessageThat().contains("function cannot be empty"); + } + + @Test + public void cannotSetUnknownFunction() throws Exception { + LabelDefinitionInput input = new LabelDefinitionInput(); + input.function = "UnknownFunction"; + + BadRequestException thrown = + assertThrows( + BadRequestException.class, + () -> gApi.projects().name(allProjects.get()).label("Code-Review").update(input)); + assertThat(thrown).hasMessageThat().contains("unknown function: " + input.function); + } + + @Test + public void cannotSetEmptyValues() throws Exception { + LabelDefinitionInput input = new LabelDefinitionInput(); + input.values = ImmutableMap.of(); + + BadRequestException thrown = + assertThrows( + BadRequestException.class, + () -> gApi.projects().name(allProjects.get()).label("Code-Review").update(input)); + assertThat(thrown).hasMessageThat().contains("values cannot be empty"); + } + + @Test + public void updateValues() 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 updatedLabel = + gApi.projects().name(allProjects.get()).label("Code-Review").update(input); + assertThat(updatedLabel.values) + .containsExactly( + "+2", "Looks Very Good", + "+1", "Looks Good", + " 0", "Don't Know", + "-1", "Looks Bad", + "-2", "Looks Very Bad"); + + assertThat(gApi.projects().name(allProjects.get()).label("Code-Review").get().values) + .containsExactly( + "+2", "Looks Very Good", + "+1", "Looks Good", + " 0", "Don't Know", + "-1", "Looks Bad", + "-2", "Looks Very Bad"); + } + + @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 updatedLabel = + gApi.projects().name(allProjects.get()).label("Code-Review").update(input); + assertThat(updatedLabel.values) + .containsExactly( + "+2", "Looks Very Good", + "+1", "Looks Good", + " 0", "Don't Know", + "-1", "Looks Bad", + "-2", "Looks Very Bad"); + + assertThat(gApi.projects().name(allProjects.get()).label("Code-Review").get().values) + .containsExactly( + "+2", "Looks Very Good", + "+1", "Looks Good", + " 0", "Don't Know", + "-1", "Looks Bad", + "-2", "Looks Very Bad"); + } + + @Test + public void cannotSetInvalidValues() throws Exception { + LabelDefinitionInput input = new LabelDefinitionInput(); + input.values = ImmutableMap.of("invalidValue", "description"); + + BadRequestException thrown = + assertThrows( + BadRequestException.class, + () -> gApi.projects().name(allProjects.get()).label("Code-Review").update(input)); + assertThat(thrown).hasMessageThat().contains("invalid value: invalidValue"); + } + + @Test + public void cannotSetValueWithEmptyDescription() throws Exception { + LabelDefinitionInput input = new LabelDefinitionInput(); + input.values = ImmutableMap.of("+1", ""); + + BadRequestException thrown = + assertThrows( + BadRequestException.class, + () -> gApi.projects().name(allProjects.get()).label("Code-Review").update(input)); + assertThat(thrown).hasMessageThat().contains("description for value '+1' cannot be empty"); + } + + @Test + public void updateDefaultValue() throws Exception { + LabelDefinitionInput input = new LabelDefinitionInput(); + input.defaultValue = 1; + + LabelDefinitionInfo updatedLabel = + gApi.projects().name(allProjects.get()).label("Code-Review").update(input); + assertThat(updatedLabel.defaultValue).isEqualTo(input.defaultValue); + + assertThat(gApi.projects().name(allProjects.get()).label("Code-Review").get().defaultValue) + .isEqualTo(input.defaultValue); + } + + @Test + public void cannotSetInvalidDefaultValue() throws Exception { + LabelDefinitionInput input = new LabelDefinitionInput(); + input.defaultValue = 5; + + BadRequestException thrown = + assertThrows( + BadRequestException.class, + () -> gApi.projects().name(allProjects.get()).label("Code-Review").update(input)); + assertThat(thrown).hasMessageThat().contains("invalid default value: " + input.defaultValue); + } + + @Test + public void updateBranches() throws Exception { + LabelDefinitionInput input = new LabelDefinitionInput(); + // Branches can be full ref, ref pattern or regular expression. + input.branches = + ImmutableList.of("refs/heads/master", "refs/heads/foo/*", "^refs/heads/stable-.*"); + + LabelDefinitionInfo updatedLabel = + gApi.projects().name(allProjects.get()).label("Code-Review").update(input); + assertThat(updatedLabel.branches).containsExactlyElementsIn(input.branches); + + assertThat(gApi.projects().name(allProjects.get()).label("Code-Review").get().branches) + .containsExactlyElementsIn(input.branches); + } + + @Test + public void branchesAreTrimmed() throws Exception { + LabelDefinitionInput input = new LabelDefinitionInput(); + input.branches = + ImmutableList.of(" refs/heads/master ", " refs/heads/foo/* ", " ^refs/heads/stable-.* "); + + LabelDefinitionInfo updatedLabel = + gApi.projects().name(allProjects.get()).label("Code-Review").update(input); + assertThat(updatedLabel.branches) + .containsExactly("refs/heads/master", "refs/heads/foo/*", "^refs/heads/stable-.*"); + + assertThat(gApi.projects().name(allProjects.get()).label("Code-Review").get().branches) + .containsExactly("refs/heads/master", "refs/heads/foo/*", "^refs/heads/stable-.*"); + } + + @Test + public void emptyBranchesAreIgnored() throws Exception { + LabelDefinitionInput input = new LabelDefinitionInput(); + input.branches = ImmutableList.of("refs/heads/master", "", " "); + + LabelDefinitionInfo updatedLabel = + gApi.projects().name(allProjects.get()).label("Code-Review").update(input); + assertThat(updatedLabel.branches).containsExactly("refs/heads/master"); + + assertThat(gApi.projects().name(allProjects.get()).label("Code-Review").get().branches) + .containsExactly("refs/heads/master"); + } + + @Test + public void branchesCanBeUnset() throws Exception { + LabelDefinitionInput input = new LabelDefinitionInput(); + input.branches = ImmutableList.of("refs/heads/master"); + gApi.projects().name(allProjects.get()).label("Code-Review").update(input); + assertThat(gApi.projects().name(allProjects.get()).label("Code-Review").get().branches) + .isNotNull(); + + input.branches = ImmutableList.of(); + LabelDefinitionInfo updatedLabel = + gApi.projects().name(allProjects.get()).label("Code-Review").update(input); + assertThat(updatedLabel.branches).isNull(); + assertThat(gApi.projects().name(allProjects.get()).label("Code-Review").get().branches) + .isNull(); + } + + @Test + public void cannotSetInvalidBranch() throws Exception { + LabelDefinitionInput input = new LabelDefinitionInput(); + input.branches = ImmutableList.of("refs heads master"); + + BadRequestException thrown = + assertThrows( + BadRequestException.class, + () -> gApi.projects().name(allProjects.get()).label("Code-Review").update(input)); + assertThat(thrown).hasMessageThat().contains("invalid branch: refs heads master"); + } + + @Test + public void branchesAreAutomaticallyPrefixedWithRefsHeads() throws Exception { + LabelDefinitionInput input = new LabelDefinitionInput(); + input.branches = ImmutableList.of("master", "refs/meta/config"); + + LabelDefinitionInfo updatedLabel = + gApi.projects().name(allProjects.get()).label("Code-Review").update(input); + assertThat(updatedLabel.branches).containsExactly("refs/heads/master", "refs/meta/config"); + + assertThat(gApi.projects().name(allProjects.get()).label("Code-Review").get().branches) + .containsExactly("refs/heads/master", "refs/meta/config"); + } + + @Test + public void setCanOverride() throws Exception { + configLabel("foo", LabelFunction.NO_OP); + try (ProjectConfigUpdate u = updateProject(project)) { + LabelType labelType = u.getConfig().getLabelSections().get("foo"); + labelType.setCanOverride(false); + u.getConfig().getLabelSections().put(labelType.getName(), labelType); + u.save(); + } + assertThat(gApi.projects().name(project.get()).label("foo").get().canOverride).isNull(); + + LabelDefinitionInput input = new LabelDefinitionInput(); + input.canOverride = true; + + LabelDefinitionInfo updatedLabel = + gApi.projects().name(project.get()).label("foo").update(input); + assertThat(updatedLabel.canOverride).isTrue(); + + assertThat(gApi.projects().name(project.get()).label("foo").get().canOverride).isTrue(); + } + + @Test + public void unsetCanOverride() throws Exception { + configLabel("foo", LabelFunction.NO_OP); + assertThat(gApi.projects().name(project.get()).label("foo").get().canOverride).isTrue(); + + LabelDefinitionInput input = new LabelDefinitionInput(); + input.canOverride = false; + + LabelDefinitionInfo updatedLabel = + gApi.projects().name(project.get()).label("foo").update(input); + assertThat(updatedLabel.canOverride).isNull(); + + assertThat(gApi.projects().name(project.get()).label("foo").get().canOverride).isNull(); + } + + @Test + public void setCopyAnyScore() throws Exception { + configLabel("foo", LabelFunction.NO_OP); + assertThat(gApi.projects().name(project.get()).label("foo").get().copyAnyScore).isNull(); + + LabelDefinitionInput input = new LabelDefinitionInput(); + input.copyAnyScore = true; + + LabelDefinitionInfo updatedLabel = + gApi.projects().name(project.get()).label("foo").update(input); + assertThat(updatedLabel.copyAnyScore).isTrue(); + + assertThat(gApi.projects().name(project.get()).label("foo").get().copyAnyScore).isTrue(); + } + + @Test + public void unsetCopyAnyScore() throws Exception { + configLabel("foo", LabelFunction.NO_OP); + try (ProjectConfigUpdate u = updateProject(project)) { + LabelType labelType = u.getConfig().getLabelSections().get("foo"); + labelType.setCopyAnyScore(true); + u.getConfig().getLabelSections().put(labelType.getName(), labelType); + u.save(); + } + assertThat(gApi.projects().name(project.get()).label("foo").get().copyAnyScore).isTrue(); + + LabelDefinitionInput input = new LabelDefinitionInput(); + input.copyAnyScore = false; + + LabelDefinitionInfo updatedLabel = + gApi.projects().name(project.get()).label("foo").update(input); + assertThat(updatedLabel.copyAnyScore).isNull(); + + assertThat(gApi.projects().name(project.get()).label("foo").get().copyAnyScore).isNull(); + } + + @Test + public void setCopyMinScore() throws Exception { + configLabel("foo", LabelFunction.NO_OP); + assertThat(gApi.projects().name(project.get()).label("foo").get().copyMinScore).isNull(); + + LabelDefinitionInput input = new LabelDefinitionInput(); + input.copyMinScore = true; + + LabelDefinitionInfo updatedLabel = + gApi.projects().name(project.get()).label("foo").update(input); + assertThat(updatedLabel.copyMinScore).isTrue(); + + assertThat(gApi.projects().name(project.get()).label("foo").get().copyMinScore).isTrue(); + } + + @Test + public void unsetCopyMinScore() throws Exception { + configLabel("foo", LabelFunction.NO_OP); + try (ProjectConfigUpdate u = updateProject(project)) { + LabelType labelType = u.getConfig().getLabelSections().get("foo"); + labelType.setCopyMinScore(true); + u.getConfig().getLabelSections().put(labelType.getName(), labelType); + u.save(); + } + assertThat(gApi.projects().name(project.get()).label("foo").get().copyMinScore).isTrue(); + + LabelDefinitionInput input = new LabelDefinitionInput(); + input.copyMinScore = false; + + LabelDefinitionInfo updatedLabel = + gApi.projects().name(project.get()).label("foo").update(input); + assertThat(updatedLabel.copyMinScore).isNull(); + + assertThat(gApi.projects().name(project.get()).label("foo").get().copyMinScore).isNull(); + } + + @Test + public void setCopyMaxScore() throws Exception { + configLabel("foo", LabelFunction.NO_OP); + assertThat(gApi.projects().name(project.get()).label("foo").get().copyMaxScore).isNull(); + + LabelDefinitionInput input = new LabelDefinitionInput(); + input.copyMaxScore = true; + + LabelDefinitionInfo updatedLabel = + gApi.projects().name(project.get()).label("foo").update(input); + assertThat(updatedLabel.copyMaxScore).isTrue(); + + assertThat(gApi.projects().name(project.get()).label("foo").get().copyMaxScore).isTrue(); + } + + @Test + public void unsetCopyMaxScore() throws Exception { + configLabel("foo", LabelFunction.NO_OP); + try (ProjectConfigUpdate u = updateProject(project)) { + LabelType labelType = u.getConfig().getLabelSections().get("foo"); + labelType.setCopyMaxScore(true); + u.getConfig().getLabelSections().put(labelType.getName(), labelType); + u.save(); + } + assertThat(gApi.projects().name(project.get()).label("foo").get().copyMaxScore).isTrue(); + + LabelDefinitionInput input = new LabelDefinitionInput(); + input.copyMaxScore = false; + + LabelDefinitionInfo updatedLabel = + gApi.projects().name(project.get()).label("foo").update(input); + assertThat(updatedLabel.copyMaxScore).isNull(); + + assertThat(gApi.projects().name(project.get()).label("foo").get().copyMaxScore).isNull(); + } + + @Test + public void setCopyAllScoresIfNoChange() throws Exception { + configLabel("foo", LabelFunction.NO_OP); + try (ProjectConfigUpdate u = updateProject(project)) { + LabelType labelType = u.getConfig().getLabelSections().get("foo"); + labelType.setCopyAllScoresIfNoChange(false); + u.getConfig().getLabelSections().put(labelType.getName(), labelType); + u.save(); + } + assertThat(gApi.projects().name(project.get()).label("foo").get().copyAllScoresIfNoChange) + .isNull(); + + LabelDefinitionInput input = new LabelDefinitionInput(); + input.copyAllScoresIfNoChange = true; + + LabelDefinitionInfo updatedLabel = + gApi.projects().name(project.get()).label("foo").update(input); + assertThat(updatedLabel.copyAllScoresIfNoChange).isTrue(); + + assertThat(gApi.projects().name(project.get()).label("foo").get().copyAllScoresIfNoChange) + .isTrue(); + } + + @Test + public void unsetCopyAllScoresIfNoChange() throws Exception { + configLabel("foo", LabelFunction.NO_OP); + assertThat(gApi.projects().name(project.get()).label("foo").get().copyAllScoresIfNoChange) + .isTrue(); + + LabelDefinitionInput input = new LabelDefinitionInput(); + input.copyAllScoresIfNoChange = false; + + LabelDefinitionInfo updatedLabel = + gApi.projects().name(project.get()).label("foo").update(input); + assertThat(updatedLabel.copyAllScoresIfNoChange).isNull(); + + assertThat(gApi.projects().name(project.get()).label("foo").get().copyAllScoresIfNoChange) + .isNull(); + } + + @Test + public void setCopyAllScoresIfNoCodeChange() throws Exception { + configLabel("foo", LabelFunction.NO_OP); + assertThat(gApi.projects().name(project.get()).label("foo").get().copyAllScoresIfNoCodeChange) + .isNull(); + + LabelDefinitionInput input = new LabelDefinitionInput(); + input.copyAllScoresIfNoCodeChange = true; + + LabelDefinitionInfo updatedLabel = + gApi.projects().name(project.get()).label("foo").update(input); + assertThat(updatedLabel.copyAllScoresIfNoCodeChange).isTrue(); + + assertThat(gApi.projects().name(project.get()).label("foo").get().copyAllScoresIfNoCodeChange) + .isTrue(); + } + + @Test + public void unsetCopyAllScoresIfNoCodeChange() throws Exception { + configLabel("foo", LabelFunction.NO_OP); + try (ProjectConfigUpdate u = updateProject(project)) { + LabelType labelType = u.getConfig().getLabelSections().get("foo"); + labelType.setCopyAllScoresIfNoCodeChange(true); + u.getConfig().getLabelSections().put(labelType.getName(), labelType); + u.save(); + } + assertThat(gApi.projects().name(project.get()).label("foo").get().copyAllScoresIfNoCodeChange) + .isTrue(); + + LabelDefinitionInput input = new LabelDefinitionInput(); + input.copyAllScoresIfNoCodeChange = false; + + LabelDefinitionInfo updatedLabel = + gApi.projects().name(project.get()).label("foo").update(input); + assertThat(updatedLabel.copyAllScoresIfNoCodeChange).isNull(); + + assertThat(gApi.projects().name(project.get()).label("foo").get().copyAllScoresIfNoCodeChange) + .isNull(); + } + + @Test + public void setCopyAllScoresOnTrivialRebase() throws Exception { + configLabel("foo", LabelFunction.NO_OP); + assertThat(gApi.projects().name(project.get()).label("foo").get().copyAllScoresOnTrivialRebase) + .isNull(); + + LabelDefinitionInput input = new LabelDefinitionInput(); + input.copyAllScoresOnTrivialRebase = true; + + LabelDefinitionInfo updatedLabel = + gApi.projects().name(project.get()).label("foo").update(input); + assertThat(updatedLabel.copyAllScoresOnTrivialRebase).isTrue(); + + assertThat(gApi.projects().name(project.get()).label("foo").get().copyAllScoresOnTrivialRebase) + .isTrue(); + } + + @Test + public void unsetCopyAllScoresOnTrivialRebase() throws Exception { + configLabel("foo", LabelFunction.NO_OP); + try (ProjectConfigUpdate u = updateProject(project)) { + LabelType labelType = u.getConfig().getLabelSections().get("foo"); + labelType.setCopyAllScoresOnTrivialRebase(true); + u.getConfig().getLabelSections().put(labelType.getName(), labelType); + u.save(); + } + assertThat(gApi.projects().name(project.get()).label("foo").get().copyAllScoresOnTrivialRebase) + .isTrue(); + + LabelDefinitionInput input = new LabelDefinitionInput(); + input.copyAllScoresOnTrivialRebase = false; + + LabelDefinitionInfo updatedLabel = + gApi.projects().name(project.get()).label("foo").update(input); + assertThat(updatedLabel.copyAllScoresOnTrivialRebase).isNull(); + + assertThat(gApi.projects().name(project.get()).label("foo").get().copyAllScoresOnTrivialRebase) + .isNull(); + } + + @Test + public void setCopyAllScoresOnMergeFirstParentUpdate() throws Exception { + configLabel("foo", LabelFunction.NO_OP); + assertThat( + gApi.projects() + .name(project.get()) + .label("foo") + .get() + .copyAllScoresOnMergeFirstParentUpdate) + .isNull(); + + LabelDefinitionInput input = new LabelDefinitionInput(); + input.copyAllScoresOnMergeFirstParentUpdate = true; + + LabelDefinitionInfo updatedLabel = + gApi.projects().name(project.get()).label("foo").update(input); + assertThat(updatedLabel.copyAllScoresOnMergeFirstParentUpdate).isTrue(); + + assertThat( + gApi.projects() + .name(project.get()) + .label("foo") + .get() + .copyAllScoresOnMergeFirstParentUpdate) + .isTrue(); + } + + @Test + public void unsetCopyAllScoresOnMergeFirstParentUpdate() throws Exception { + configLabel("foo", LabelFunction.NO_OP); + try (ProjectConfigUpdate u = updateProject(project)) { + LabelType labelType = u.getConfig().getLabelSections().get("foo"); + labelType.setCopyAllScoresOnMergeFirstParentUpdate(true); + u.getConfig().getLabelSections().put(labelType.getName(), labelType); + u.save(); + } + assertThat( + gApi.projects() + .name(project.get()) + .label("foo") + .get() + .copyAllScoresOnMergeFirstParentUpdate) + .isTrue(); + + LabelDefinitionInput input = new LabelDefinitionInput(); + input.copyAllScoresOnMergeFirstParentUpdate = false; + + LabelDefinitionInfo updatedLabel = + gApi.projects().name(project.get()).label("foo").update(input); + assertThat(updatedLabel.copyAllScoresOnMergeFirstParentUpdate).isNull(); + + assertThat( + gApi.projects() + .name(project.get()) + .label("foo") + .get() + .copyAllScoresOnMergeFirstParentUpdate) + .isNull(); + } + + @Test + public void setAllowPostSubmit() throws Exception { + configLabel("foo", LabelFunction.NO_OP); + try (ProjectConfigUpdate u = updateProject(project)) { + LabelType labelType = u.getConfig().getLabelSections().get("foo"); + labelType.setAllowPostSubmit(false); + u.getConfig().getLabelSections().put(labelType.getName(), labelType); + u.save(); + } + assertThat(gApi.projects().name(project.get()).label("foo").get().allowPostSubmit).isNull(); + + LabelDefinitionInput input = new LabelDefinitionInput(); + input.allowPostSubmit = true; + + LabelDefinitionInfo updatedLabel = + gApi.projects().name(project.get()).label("foo").update(input); + assertThat(updatedLabel.allowPostSubmit).isTrue(); + + assertThat(gApi.projects().name(project.get()).label("foo").get().allowPostSubmit).isTrue(); + } + + @Test + public void unsetAllowPostSubmit() throws Exception { + configLabel("foo", LabelFunction.NO_OP); + assertThat(gApi.projects().name(project.get()).label("foo").get().allowPostSubmit).isTrue(); + + LabelDefinitionInput input = new LabelDefinitionInput(); + input.allowPostSubmit = false; + + LabelDefinitionInfo updatedLabel = + gApi.projects().name(project.get()).label("foo").update(input); + assertThat(updatedLabel.allowPostSubmit).isNull(); + + assertThat(gApi.projects().name(project.get()).label("foo").get().allowPostSubmit).isNull(); + } + + @Test + public void setIgnoreSelfApproval() throws Exception { + configLabel("foo", LabelFunction.NO_OP); + assertThat(gApi.projects().name(project.get()).label("foo").get().ignoreSelfApproval).isNull(); + + LabelDefinitionInput input = new LabelDefinitionInput(); + input.ignoreSelfApproval = true; + + LabelDefinitionInfo updatedLabel = + gApi.projects().name(project.get()).label("foo").update(input); + assertThat(updatedLabel.ignoreSelfApproval).isTrue(); + + assertThat(gApi.projects().name(project.get()).label("foo").get().ignoreSelfApproval).isTrue(); + } + + @Test + public void unsetIgnoreSelfApproval() throws Exception { + configLabel("foo", LabelFunction.NO_OP); + try (ProjectConfigUpdate u = updateProject(project)) { + LabelType labelType = u.getConfig().getLabelSections().get("foo"); + labelType.setIgnoreSelfApproval(true); + u.getConfig().getLabelSections().put(labelType.getName(), labelType); + u.save(); + } + assertThat(gApi.projects().name(project.get()).label("foo").get().ignoreSelfApproval).isTrue(); + + LabelDefinitionInput input = new LabelDefinitionInput(); + input.ignoreSelfApproval = false; + + LabelDefinitionInfo updatedLabel = + gApi.projects().name(project.get()).label("foo").update(input); + assertThat(updatedLabel.ignoreSelfApproval).isNull(); + + assertThat(gApi.projects().name(project.get()).label("foo").get().ignoreSelfApproval).isNull(); + } + + @Test + public void noOpUpdate() throws Exception { + RevCommit refsMetaConfigHead = + projectOperations.project(allProjects).getHead(RefNames.REFS_CONFIG); + + LabelDefinitionInfo updatedLabel = + gApi.projects() + .name(allProjects.get()) + .label("Code-Review") + .update(new LabelDefinitionInput()); + LabelAssert.assertCodeReviewLabel(updatedLabel); + + LabelAssert.assertCodeReviewLabel( + gApi.projects().name(allProjects.get()).label("Code-Review").get()); + + assertThat(projectOperations.project(allProjects).getHead(RefNames.REFS_CONFIG)) + .isEqualTo(refsMetaConfigHead); + } + + @Test + public void defaultCommitMessage() throws Exception { + LabelDefinitionInput input = new LabelDefinitionInput(); + input.function = LabelFunction.NO_OP.getFunctionName(); + gApi.projects().name(allProjects.get()).label("Code-Review").update(input); + assertThat( + projectOperations.project(allProjects).getHead(RefNames.REFS_CONFIG).getShortMessage()) + .isEqualTo("Update label"); + } + + @Test + public void withCommitMessage() throws Exception { + LabelDefinitionInput input = new LabelDefinitionInput(); + input.function = LabelFunction.NO_OP.getFunctionName(); + input.commitMessage = "Set NoOp function"; + gApi.projects().name(allProjects.get()).label("Code-Review").update(input); + assertThat( + projectOperations.project(allProjects).getHead(RefNames.REFS_CONFIG).getShortMessage()) + .isEqualTo(input.commitMessage); + } + + @Test + public void commitMessageIsTrimmed() throws Exception { + LabelDefinitionInput input = new LabelDefinitionInput(); + input.function = LabelFunction.NO_OP.getFunctionName(); + input.commitMessage = " Set NoOp function "; + gApi.projects().name(allProjects.get()).label("Code-Review").update(input); + assertThat( + projectOperations.project(allProjects).getHead(RefNames.REFS_CONFIG).getShortMessage()) + .isEqualTo("Set NoOp function"); + } +}