blob: 8afc37356e3780924ae7bdb9c270c855e0679978 [file] [log] [blame]
// 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.plugins.checks.acceptance;
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.acceptance.GitUtil.deleteRef;
import static com.google.gerrit.acceptance.GitUtil.fetch;
import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowLabel;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.SkipProjectClone;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.entities.Change;
import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.client.ChangeStatus;
import com.google.gerrit.extensions.common.ChangeInput;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.plugins.checks.CheckerRef;
import com.google.gerrit.plugins.checks.CheckerUuid;
import com.google.gerrit.server.change.ChangeInserter;
import com.google.gerrit.server.notedb.Sequences;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.PushResult;
import org.eclipse.jgit.transport.RemoteRefUpdate;
import org.junit.Test;
@SkipProjectClone
public class CheckerRefsIT extends AbstractCheckersTest {
@Inject private ProjectOperations projectOperations;
@Inject private Sequences seq;
@Inject private ChangeInserter.Factory changeInserterFactory;
@Inject private BatchUpdate.Factory updateFactory;
@Test
public void cannotCreateCheckerRef() throws Exception {
projectOperations
.project(allProjects)
.forUpdate()
.add(allow(Permission.CREATE).ref(CheckerRef.REFS_CHECKERS + "*").group(adminGroupUuid()))
.add(allow(Permission.PUSH).ref(CheckerRef.REFS_CHECKERS + "*").group(adminGroupUuid()))
.update();
String checkerRef = CheckerUuid.parse("test:my-checker").toRefName();
TestRepository<InMemoryRepository> testRepo = cloneProject(allProjects);
PushOneCommit.Result r = pushFactory.create(admin.newIdent(), testRepo).to(checkerRef);
r.assertErrorStatus();
assertThat(r.getMessage()).contains("Not allowed to create checker ref.");
try (Repository repo = repoManager.openRepository(allProjects)) {
assertThat(repo.exactRef(checkerRef)).isNull();
}
}
@Test
public void canCreateCheckerLikeRef() throws Exception {
projectOperations
.project(project)
.forUpdate()
.add(allow(Permission.CREATE).ref(CheckerRef.REFS_CHECKERS + "*").group(adminGroupUuid()))
.add(allow(Permission.PUSH).ref(CheckerRef.REFS_CHECKERS + "*").group(adminGroupUuid()))
.update();
String checkerRef = CheckerUuid.parse("test:my-checker").toRefName();
// checker ref can be created in any project except All-Projects
TestRepository<InMemoryRepository> testRepo = cloneProject(project);
PushOneCommit.Result r = pushFactory.create(admin.newIdent(), testRepo).to(checkerRef);
r.assertOkStatus();
try (Repository repo = repoManager.openRepository(project)) {
assertThat(repo.exactRef(checkerRef)).isNotNull();
}
}
@Test
public void cannotDeleteCheckerRef() throws Exception {
projectOperations
.project(allProjects)
.forUpdate()
.add(
allow(Permission.DELETE)
.ref(CheckerRef.REFS_CHECKERS + "*")
.group(REGISTERED_USERS)
.force(true))
.update();
CheckerUuid checkerUuid = checkerOperations.newChecker().create();
String checkerRef = checkerUuid.toRefName();
TestRepository<InMemoryRepository> testRepo = cloneProject(allProjects);
PushResult r = deleteRef(testRepo, checkerRef);
RemoteRefUpdate refUpdate = r.getRemoteUpdate(checkerRef);
assertThat(refUpdate.getStatus()).isEqualTo(RemoteRefUpdate.Status.REJECTED_OTHER_REASON);
assertThat(refUpdate.getMessage()).contains("Not allowed to delete checker ref.");
try (Repository repo = repoManager.openRepository(allProjects)) {
assertThat(repo.exactRef(checkerRef)).isNotNull();
}
}
@Test
public void canDeleteCheckerLikeRef() throws Exception {
String checkerRef = CheckerUuid.parse("foo:bar").toRefName();
projectOperations
.project(project)
.forUpdate()
.add(
allow(Permission.DELETE)
.ref(CheckerRef.REFS_CHECKERS + "*")
.group(REGISTERED_USERS)
.force(true))
.add(allow(Permission.CREATE).ref(checkerRef).group(adminGroupUuid()))
.update();
createBranch(BranchNameKey.create(project, checkerRef));
// checker ref can be deleted in any project except All-Projects
TestRepository<InMemoryRepository> testRepo = cloneProject(project);
PushResult r = deleteRef(testRepo, checkerRef);
RemoteRefUpdate refUpdate = r.getRemoteUpdate(checkerRef);
assertThat(refUpdate.getStatus()).isEqualTo(RemoteRefUpdate.Status.OK);
try (Repository repo = repoManager.openRepository(project)) {
assertThat(repo.exactRef(checkerRef)).isNull();
}
}
@Test
public void updateCheckerRefsByPushIsDisabled() throws Exception {
CheckerUuid checkerUuid = checkerOperations.newChecker().create();
String checkerRef = checkerUuid.toRefName();
TestRepository<InMemoryRepository> repo = cloneProject(allProjects, admin);
fetch(repo, checkerRef + ":checkerRef");
repo.reset("checkerRef");
projectOperations
.project(allProjects)
.forUpdate()
.add(allow(Permission.PUSH).ref(CheckerRef.REFS_CHECKERS + "*").group(adminGroupUuid()))
.update();
PushOneCommit.Result r = pushFactory.create(admin.newIdent(), repo).to(checkerRef);
r.assertErrorStatus();
r.assertMessage("direct update of checker ref not allowed");
}
@Test
public void updateCheckerLikeRefByPush() throws Exception {
String checkerRef = CheckerUuid.parse("foo:bar").toRefName();
projectOperations
.project(project)
.forUpdate()
.add(allow(Permission.CREATE).ref(checkerRef).group(adminGroupUuid()))
.update();
createBranch(BranchNameKey.create(project, checkerRef));
TestRepository<InMemoryRepository> repo = cloneProject(project, admin);
fetch(repo, checkerRef + ":checkerRef");
repo.reset("checkerRef");
projectOperations
.project(project)
.forUpdate()
.add(allow(Permission.PUSH).ref(CheckerRef.REFS_CHECKERS + "*").group(adminGroupUuid()))
.update();
PushOneCommit.Result r = pushFactory.create(admin.newIdent(), repo).to(checkerRef);
r.assertOkStatus();
}
@Test
public void submitToCheckerRefsIsDisabled() throws Exception {
CheckerUuid checkerUuid = checkerOperations.newChecker().disable().create();
String checkerRef = checkerUuid.toRefName();
String changeId = createChangeWithoutCommitValidation(allProjects, checkerRef);
projectOperations
.project(allProjects)
.forUpdate()
.add(
allowLabel("Code-Review")
.ref(CheckerRef.REFS_CHECKERS + "*")
.group(adminGroupUuid())
.range(-2, 2))
.update();
approve(changeId);
projectOperations
.project(allProjects)
.forUpdate()
.add(allow(Permission.SUBMIT).ref(CheckerRef.REFS_CHECKERS + "*").group(adminGroupUuid()))
.update();
ResourceConflictException thrown =
assertThrows(
ResourceConflictException.class, () -> gApi.changes().id(changeId).current().submit());
assertThat(thrown).hasMessageThat().contains("submit to checker ref not allowed");
}
@Test
public void submitToCheckerLikeRef() throws Exception {
String checkerRef = CheckerUuid.parse("foo:bar").toRefName();
projectOperations
.project(project)
.forUpdate()
.add(allow(Permission.CREATE).ref(checkerRef).group(adminGroupUuid()))
.update();
createBranch(BranchNameKey.create(project, checkerRef));
String changeId = createChangeWithoutCommitValidation(project, checkerRef);
projectOperations
.project(project)
.forUpdate()
.add(
allowLabel("Code-Review")
.ref(CheckerRef.REFS_CHECKERS + "*")
.group(adminGroupUuid())
.range(-2, 2))
.update();
approve(changeId);
projectOperations
.project(project)
.forUpdate()
.add(allow(Permission.SUBMIT).ref(CheckerRef.REFS_CHECKERS + "*").group(adminGroupUuid()))
.update();
// submitting to a checker ref should work in any project except All-Projects
gApi.changes().id(changeId).current().submit();
assertThat(gApi.changes().id(changeId).get().status).isEqualTo(ChangeStatus.MERGED);
}
@Test
public void createChangeForCheckerRefsByPushIsDisabled() throws Exception {
CheckerUuid checkerUuid = checkerOperations.newChecker().create();
String checkerRef = checkerUuid.toRefName();
TestRepository<InMemoryRepository> repo = cloneProject(allProjects, admin);
fetch(repo, checkerRef + ":checkerRef");
repo.reset("checkerRef");
projectOperations
.project(allProjects)
.forUpdate()
.add(allow(Permission.PUSH).ref(CheckerRef.REFS_CHECKERS + "*").group(adminGroupUuid()))
.update();
PushOneCommit.Result r =
pushFactory.create(admin.newIdent(), repo).to("refs/for/" + checkerRef);
r.assertErrorStatus();
r.assertMessage("creating change for checker ref not allowed");
}
@Test
public void createChangeForCheckerLikeRefByPush() throws Exception {
String checkerRef = CheckerUuid.parse("foo:bar").toRefName();
projectOperations
.project(project)
.forUpdate()
.add(allow(Permission.CREATE).ref(checkerRef).group(adminGroupUuid()))
.update();
createBranch(BranchNameKey.create(project, checkerRef));
TestRepository<InMemoryRepository> repo = cloneProject(project, admin);
fetch(repo, checkerRef + ":checkerRef");
repo.reset("checkerRef");
// creating a change on a checker ref by push should work in any project except All-Projects
projectOperations
.project(project)
.forUpdate()
.add(allow(Permission.PUSH).ref(CheckerRef.REFS_CHECKERS + "*").group(adminGroupUuid()))
.update();
PushOneCommit.Result r =
pushFactory.create(admin.newIdent(), repo).to("refs/for/" + checkerRef);
r.assertOkStatus();
}
@Test
public void createChangeForCheckerRefsViaApiIsDisabled() throws Exception {
CheckerUuid checkerUuid = checkerOperations.newChecker().create();
String checkerRef = checkerUuid.toRefName();
TestRepository<InMemoryRepository> repo = cloneProject(allProjects, admin);
fetch(repo, checkerRef + ":checkerRef");
repo.reset("checkerRef");
RevCommit head = getHead(repo.getRepository(), "HEAD");
ChangeInput input = new ChangeInput();
input.project = allProjects.get();
input.branch = checkerRef;
input.baseCommit = head.name();
input.subject = "A change.";
ResourceConflictException thrown =
assertThrows(ResourceConflictException.class, () -> gApi.changes().create(input));
assertThat(thrown).hasMessageThat().contains("creating change for checker ref not allowed");
}
@Test
public void createChangeForCheckerLikeRefViaApi() throws Exception {
String checkerRef = CheckerUuid.parse("foo:bar").toRefName();
projectOperations
.project(project)
.forUpdate()
.add(allow(Permission.CREATE).ref(checkerRef).group(adminGroupUuid()))
.update();
createBranch(BranchNameKey.create(project, checkerRef));
TestRepository<InMemoryRepository> repo = cloneProject(project, admin);
fetch(repo, checkerRef + ":checkerRef");
repo.reset("checkerRef");
RevCommit head = getHead(repo.getRepository(), "HEAD");
// creating a change on a checker ref via API should work in any project except All-Projects
ChangeInput input = new ChangeInput();
input.project = project.get();
input.branch = checkerRef;
input.baseCommit = head.name();
input.subject = "A change.";
assertThat(gApi.changes().create(input).get()).isNotNull();
}
private String createChangeWithoutCommitValidation(Project.NameKey project, String targetRef)
throws Exception {
try (Repository git = repoManager.openRepository(project);
ObjectInserter oi = git.newObjectInserter();
ObjectReader reader = oi.newReader();
RevWalk rw = new RevWalk(reader);
TestRepository<Repository> testRepo = new TestRepository<>(git)) {
RevCommit head = rw.parseCommit(git.exactRef(targetRef).getObjectId());
RevCommit commit =
testRepo
.commit()
.author(admin.newIdent())
.message("A change.")
.insertChangeId()
.parent(head)
.create();
Change.Id changeId = Change.id(seq.nextChangeId());
ChangeInserter ins = changeInserterFactory.create(changeId, commit, targetRef);
ins.setValidate(false);
ins.setMessage(String.format("Uploaded patch set %s.", ins.getPatchSetId().get()));
try (BatchUpdate bu =
updateFactory.create(
project, identifiedUserFactory.create(admin.id()), TimeUtil.nowTs())) {
bu.setRepository(git, rw, oi);
bu.insertChange(ins);
bu.execute();
}
return changeId.toString();
}
}
}