blob: 537c7d86eadb3f0cfb92084de0928047a68217d3 [file] [log] [blame]
// Copyright (C) 2021 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.server.query;
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.testsuite.change.ChangeKindCreator;
import com.google.gerrit.acceptance.testsuite.change.ChangeOperations;
import com.google.gerrit.entities.Account;
import com.google.gerrit.entities.Change;
import com.google.gerrit.entities.LabelId;
import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.entities.PatchSetApproval;
import com.google.gerrit.extensions.client.ChangeKind;
import com.google.gerrit.index.query.QueryParseException;
import com.google.gerrit.server.change.ChangeKindCache;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.query.approval.ApprovalContext;
import com.google.gerrit.server.query.approval.ApprovalQueryBuilder;
import com.google.inject.Inject;
import java.util.Date;
import org.junit.Test;
public class ApprovalQueryIT extends AbstractDaemonTest {
@Inject private ApprovalQueryBuilder queryBuilder;
@Inject private ChangeKindCreator changeKindCreator;
@Inject private ChangeNotes.Factory changeNotesFactory;
@Inject private ChangeKindCache changeKindCache;
@Inject private ChangeOperations changeOperations;
@Test
public void magicValuePredicate() throws Exception {
assertTrue(queryBuilder.parse("is:MAX").asMatchable().match(contextForCodeReviewLabel(2)));
assertTrue(queryBuilder.parse("is:mAx").asMatchable().match(contextForCodeReviewLabel(2)));
assertFalse(queryBuilder.parse("is:MAX").asMatchable().match(contextForCodeReviewLabel(-2)));
assertFalse(queryBuilder.parse("is:MAX").asMatchable().match(contextForCodeReviewLabel(1)));
assertFalse(queryBuilder.parse("is:MAX").asMatchable().match(contextForCodeReviewLabel(5000)));
assertTrue(queryBuilder.parse("is:MIN").asMatchable().match(contextForCodeReviewLabel(-2)));
assertTrue(queryBuilder.parse("is:mIn").asMatchable().match(contextForCodeReviewLabel(-2)));
assertFalse(queryBuilder.parse("is:MIN").asMatchable().match(contextForCodeReviewLabel(2)));
assertFalse(queryBuilder.parse("is:MIN").asMatchable().match(contextForCodeReviewLabel(-1)));
assertFalse(queryBuilder.parse("is:MIN").asMatchable().match(contextForCodeReviewLabel(5000)));
assertTrue(queryBuilder.parse("is:ANY").asMatchable().match(contextForCodeReviewLabel(-2)));
assertTrue(queryBuilder.parse("is:ANY").asMatchable().match(contextForCodeReviewLabel(2)));
assertTrue(queryBuilder.parse("is:aNy").asMatchable().match(contextForCodeReviewLabel(2)));
}
@Test
public void changeKindPredicate_noCodeChange() throws Exception {
String change = changeKindCreator.createChange(ChangeKind.NO_CODE_CHANGE, testRepo, admin);
changeKindCreator.updateChange(change, ChangeKind.NO_CODE_CHANGE, testRepo, admin, project);
PatchSet.Id ps1 = PatchSet.id(Change.id(gApi.changes().id(change).get()._number), /* id= */ 1);
assertTrue(
queryBuilder
.parse("changekind:no-code-change")
.asMatchable()
.match(contextForCodeReviewLabel(/* value= */ -2, ps1, admin.id())));
changeKindCreator.updateChange(change, ChangeKind.TRIVIAL_REBASE, testRepo, admin, project);
PatchSet.Id ps2 = PatchSet.id(Change.id(gApi.changes().id(change).get()._number), /* id= */ 2);
assertFalse(
queryBuilder
.parse("changekind:no-code-change")
.asMatchable()
.match(contextForCodeReviewLabel(/* value= */ -2, ps2, admin.id())));
}
@Test
public void changeKindPredicate_trivialRebase() throws Exception {
String change = changeKindCreator.createChange(ChangeKind.TRIVIAL_REBASE, testRepo, admin);
changeKindCreator.updateChange(change, ChangeKind.TRIVIAL_REBASE, testRepo, admin, project);
PatchSet.Id ps1 = PatchSet.id(Change.id(gApi.changes().id(change).get()._number), /* id= */ 1);
assertTrue(
queryBuilder
.parse("changekind:trivial-rebase")
.asMatchable()
.match(contextForCodeReviewLabel(/* value= */ -2, ps1, admin.id())));
changeKindCreator.updateChange(change, ChangeKind.REWORK, testRepo, admin, project);
PatchSet.Id ps2 = PatchSet.id(Change.id(gApi.changes().id(change).get()._number), /* id= */ 2);
assertFalse(
queryBuilder
.parse("changekind:trivial-rebase")
.asMatchable()
.match(contextForCodeReviewLabel(/* value= */ -2, ps2, admin.id())));
}
@Test
public void changeKindPredicate_reworkAndNotRework() throws Exception {
String change = changeKindCreator.createChange(ChangeKind.REWORK, testRepo, admin);
changeKindCreator.updateChange(change, ChangeKind.REWORK, testRepo, admin, project);
PatchSet.Id ps1 = PatchSet.id(Change.id(gApi.changes().id(change).get()._number), /* id= */ 1);
assertTrue(
queryBuilder
.parse("changekind:rework")
.asMatchable()
.match(contextForCodeReviewLabel(/* value= */ -2, ps1, admin.id())));
changeKindCreator.updateChange(change, ChangeKind.REWORK, testRepo, admin, project);
PatchSet.Id ps2 = PatchSet.id(Change.id(gApi.changes().id(change).get()._number), /* id= */ 2);
assertFalse(
queryBuilder
.parse("-changekind:rework")
.asMatchable()
.match(contextForCodeReviewLabel(/* value= */ -2, ps2, admin.id())));
}
@Test
public void uploaderInPredicate() throws Exception {
String administratorsUUID = gApi.groups().query("name:Administrators").get().get(0).id;
PushOneCommit.Result pushResult = createChange();
String changeCreatedByAdmin = pushResult.getChangeId();
approve(changeCreatedByAdmin);
// PS2 uploaded by admin
amendChange(changeCreatedByAdmin);
// PS3 uploaded by user
amendChangeWithUploader(pushResult, project, user).assertOkStatus();
// can copy approval from patchset 1 -> 2
assertTrue(
queryBuilder
.parse("uploaderin:" + administratorsUUID)
.asMatchable()
.match(
contextForCodeReviewLabel(
/* value= */ 2,
PatchSet.id(pushResult.getChange().getId(), /* id= */ 1),
admin.id())));
// can not copy approval from patchset 2 -> 3
assertFalse(
queryBuilder
.parse("uploaderin:" + administratorsUUID)
.asMatchable()
.match(
contextForCodeReviewLabel(
/* value= */ 2,
PatchSet.id(pushResult.getChange().getId(), /* id= */ 2),
admin.id())));
}
@Test
public void approverInPredicate() throws Exception {
String administratorsUUID = gApi.groups().query("name:Administrators").get().get(0).id;
PushOneCommit.Result pushResult = createChange();
amendChange(pushResult.getChangeId());
amendChange(pushResult.getChangeId());
// can copy approval from patchset 1 -> 2
assertTrue(
queryBuilder
.parse("approverin:" + administratorsUUID)
.asMatchable()
.match(
contextForCodeReviewLabel(
/* value= */ 2,
PatchSet.id(pushResult.getChange().getId(), /* id= */ 1),
admin.id())));
// can not copy approval from patchset 2 -> 3
assertFalse(
queryBuilder
.parse("approverin:" + administratorsUUID)
.asMatchable()
.match(
contextForCodeReviewLabel(
/* value= */ 2,
PatchSet.id(pushResult.getChange().getId(), /* id= */ 2),
user.id())));
}
@Test
public void userInPredicate_groupNotFound() {
QueryParseException thrown =
assertThrows(
QueryParseException.class,
() ->
queryBuilder
.parse("uploaderin:foobar")
.asMatchable()
.match(contextForCodeReviewLabel(/* value= */ 2)));
assertThat(thrown).hasMessageThat().contains("Group foobar not found");
}
@Test
public void hasChangedFilesPredicate() throws Exception {
Change.Id changeId =
changeOperations.newChange().project(project).file("file").content("content").create();
changeOperations.change(changeId).newPatchset().file("file").content("new content").create();
// can copy approval from patch-set 1 -> 2
assertTrue(
queryBuilder
.parse("has:unchanged-files")
.asMatchable()
.match(
contextForCodeReviewLabel(
/* value= */ 2, PatchSet.id(changeId, /* id= */ 1), admin.id())));
changeOperations.change(changeId).newPatchset().file("file").delete().create();
// can not copy approval from patch-set 2 -> 3
assertFalse(
queryBuilder
.parse("has:unchanged-files")
.asMatchable()
.match(
contextForCodeReviewLabel(
/* value= */ 2, PatchSet.id(changeId, /* id= */ 2), admin.id())));
}
@Test
public void hasChangedFilesPredicate_unsupportedOperator() {
QueryParseException thrown =
assertThrows(
QueryParseException.class,
() ->
queryBuilder
.parse("has:invalid")
.asMatchable()
.match(contextForCodeReviewLabel(/* value= */ 2)));
assertThat(thrown)
.hasMessageThat()
.contains(
"'invalid' is not a supported argument for has. only 'unchanged-files' is supported");
}
private ApprovalContext contextForCodeReviewLabel(int value) throws Exception {
PushOneCommit.Result result = createChange();
amendChange(result.getChangeId());
PatchSet.Id psId = PatchSet.id(result.getChange().getId(), 1);
return contextForCodeReviewLabel(value, psId, admin.id());
}
private ApprovalContext contextForCodeReviewLabel(
int value, PatchSet.Id psId, Account.Id approver) {
ChangeNotes changeNotes = changeNotesFactory.create(project, psId.changeId());
PatchSet.Id newPsId = PatchSet.id(psId.changeId(), psId.get() + 1);
ChangeKind changeKind =
changeKindCache.getChangeKind(
changeNotes.getChange(), changeNotes.getPatchSets().get(newPsId));
PatchSetApproval approval =
PatchSetApproval.builder()
.postSubmit(false)
.granted(new Date())
.key(PatchSetApproval.key(psId, approver, LabelId.create("Code-Review")))
.value(value)
.build();
return ApprovalContext.create(
changeNotes, approval, changeNotes.getPatchSets().get(newPsId), changeKind);
}
}