| // Copyright (C) 2018 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.api.project; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| import static com.google.gerrit.acceptance.GitUtil.pushHead; |
| import static com.google.gerrit.testing.GerritJUnit.assertThrows; |
| import static java.util.stream.Collectors.toList; |
| import static java.util.stream.Collectors.toSet; |
| |
| import com.google.common.collect.Iterables; |
| import com.google.gerrit.acceptance.AbstractDaemonTest; |
| import com.google.gerrit.acceptance.PushOneCommit; |
| import com.google.gerrit.extensions.api.projects.CheckProjectInput; |
| import com.google.gerrit.extensions.api.projects.CheckProjectInput.AutoCloseableChangesCheckInput; |
| import com.google.gerrit.extensions.api.projects.CheckProjectResultInfo; |
| import com.google.gerrit.extensions.client.ChangeStatus; |
| import com.google.gerrit.extensions.client.InheritableBoolean; |
| import com.google.gerrit.extensions.common.ChangeInfo; |
| import com.google.gerrit.extensions.restapi.BadRequestException; |
| import com.google.gerrit.extensions.restapi.UnprocessableEntityException; |
| import com.google.gerrit.server.project.ProjectsConsistencyChecker; |
| import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository; |
| import org.eclipse.jgit.junit.TestRepository; |
| import org.eclipse.jgit.lib.PersonIdent; |
| import org.eclipse.jgit.revwalk.RevCommit; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| public class CheckProjectIT extends AbstractDaemonTest { |
| private TestRepository<InMemoryRepository> serverSideTestRepo; |
| |
| @Before |
| public void setUp() throws Exception { |
| serverSideTestRepo = |
| new TestRepository<>((InMemoryRepository) repoManager.openRepository(project)); |
| } |
| |
| @Test |
| public void noProblem() throws Exception { |
| PushOneCommit.Result r = createChange("refs/for/master"); |
| String branch = r.getChange().change().getDest().branch(); |
| |
| ChangeInfo info = change(r).info(); |
| assertThat(info.status).isEqualTo(ChangeStatus.NEW); |
| |
| CheckProjectResultInfo checkResult = |
| gApi.projects().name(project.get()).check(checkProjectInputForAutoCloseableCheck(branch)); |
| assertThat(checkResult.autoCloseableChangesCheckResult.autoCloseableChanges).isEmpty(); |
| |
| info = change(r).info(); |
| assertThat(info.status).isEqualTo(ChangeStatus.NEW); |
| } |
| |
| @Test |
| public void detectAutoCloseableChangeByCommit() throws Exception { |
| RevCommit commit = pushCommitWithoutChangeIdForReview(); |
| ChangeInfo change = |
| Iterables.getOnlyElement(gApi.changes().query("commit:" + commit.name()).get()); |
| |
| String branch = "refs/heads/master"; |
| serverSideTestRepo.branch(branch).update(testRepo.getRevWalk().parseCommit(commit)); |
| |
| ChangeInfo info = gApi.changes().id(change._number).info(); |
| assertThat(info.status).isEqualTo(ChangeStatus.NEW); |
| |
| CheckProjectResultInfo checkResult = |
| gApi.projects().name(project.get()).check(checkProjectInputForAutoCloseableCheck(branch)); |
| assertThat( |
| checkResult.autoCloseableChangesCheckResult.autoCloseableChanges.stream() |
| .map(i -> i._number) |
| .collect(toList())) |
| .containsExactly(change._number); |
| |
| info = gApi.changes().id(change._number).info(); |
| assertThat(info.status).isEqualTo(ChangeStatus.NEW); |
| } |
| |
| @Test |
| public void fixAutoCloseableChangeByCommit() throws Exception { |
| RevCommit commit = pushCommitWithoutChangeIdForReview(); |
| ChangeInfo change = |
| Iterables.getOnlyElement(gApi.changes().query("commit:" + commit.name()).get()); |
| |
| String branch = "refs/heads/master"; |
| serverSideTestRepo.branch(branch).update(commit); |
| |
| ChangeInfo info = gApi.changes().id(change._number).info(); |
| assertThat(info.status).isEqualTo(ChangeStatus.NEW); |
| |
| CheckProjectInput input = checkProjectInputForAutoCloseableCheck(branch); |
| input.autoCloseableChangesCheck.fix = true; |
| CheckProjectResultInfo checkResult = gApi.projects().name(project.get()).check(input); |
| assertThat( |
| checkResult.autoCloseableChangesCheckResult.autoCloseableChanges.stream() |
| .map(i -> i._number) |
| .collect(toSet())) |
| .containsExactly(change._number); |
| |
| info = gApi.changes().id(change._number).info(); |
| assertThat(info.status).isEqualTo(ChangeStatus.MERGED); |
| } |
| |
| @Test |
| public void detectAutoCloseableChangeByChangeId() throws Exception { |
| PushOneCommit.Result r = createChange("refs/for/master"); |
| String branch = r.getChange().change().getDest().branch(); |
| |
| RevCommit amendedCommit = serverSideTestRepo.amend(r.getCommit()).create(); |
| serverSideTestRepo.branch(branch).update(amendedCommit); |
| |
| ChangeInfo info = change(r).info(); |
| assertThat(info.status).isEqualTo(ChangeStatus.NEW); |
| |
| CheckProjectResultInfo checkResult = |
| gApi.projects().name(project.get()).check(checkProjectInputForAutoCloseableCheck(branch)); |
| assertThat( |
| checkResult.autoCloseableChangesCheckResult.autoCloseableChanges.stream() |
| .map(i -> i._number) |
| .collect(toSet())) |
| .containsExactly(r.getChange().getId().get()); |
| |
| info = change(r).info(); |
| assertThat(info.status).isEqualTo(ChangeStatus.NEW); |
| } |
| |
| @Test |
| public void fixAutoCloseableChangeByChangeId() throws Exception { |
| PushOneCommit.Result r = createChange("refs/for/master"); |
| String branch = r.getChange().change().getDest().branch(); |
| |
| RevCommit amendedCommit = serverSideTestRepo.amend(r.getCommit()).create(); |
| serverSideTestRepo.branch(branch).update(amendedCommit); |
| |
| ChangeInfo info = change(r).info(); |
| assertThat(info.status).isEqualTo(ChangeStatus.NEW); |
| |
| CheckProjectInput input = checkProjectInputForAutoCloseableCheck(branch); |
| input.autoCloseableChangesCheck.fix = true; |
| CheckProjectResultInfo checkResult = gApi.projects().name(project.get()).check(input); |
| assertThat( |
| checkResult.autoCloseableChangesCheckResult.autoCloseableChanges.stream() |
| .map(i -> i._number) |
| .collect(toSet())) |
| .containsExactly(r.getChange().getId().get()); |
| |
| info = change(r).info(); |
| assertThat(info.status).isEqualTo(ChangeStatus.MERGED); |
| } |
| |
| @Test |
| public void maxCommits() throws Exception { |
| PushOneCommit.Result r = createChange("refs/for/master"); |
| String branch = r.getChange().change().getDest().branch(); |
| |
| RevCommit amendedCommit = serverSideTestRepo.amend(r.getCommit()).create(); |
| serverSideTestRepo.branch(branch).update(amendedCommit); |
| |
| serverSideTestRepo.commit(amendedCommit); |
| |
| ChangeInfo info = change(r).info(); |
| assertThat(info.status).isEqualTo(ChangeStatus.NEW); |
| |
| CheckProjectInput input = checkProjectInputForAutoCloseableCheck(branch); |
| input.autoCloseableChangesCheck.fix = true; |
| input.autoCloseableChangesCheck.maxCommits = 1; |
| CheckProjectResultInfo checkResult = gApi.projects().name(project.get()).check(input); |
| assertThat(checkResult.autoCloseableChangesCheckResult.autoCloseableChanges).isEmpty(); |
| |
| info = change(r).info(); |
| assertThat(info.status).isEqualTo(ChangeStatus.NEW); |
| |
| input.autoCloseableChangesCheck.maxCommits = 2; |
| checkResult = gApi.projects().name(project.get()).check(input); |
| assertThat( |
| checkResult.autoCloseableChangesCheckResult.autoCloseableChanges.stream() |
| .map(i -> i._number) |
| .collect(toSet())) |
| .containsExactly(r.getChange().getId().get()); |
| |
| info = change(r).info(); |
| assertThat(info.status).isEqualTo(ChangeStatus.MERGED); |
| } |
| |
| @Test |
| public void skipCommits() throws Exception { |
| PushOneCommit.Result r = createChange("refs/for/master"); |
| String branch = r.getChange().change().getDest().branch(); |
| |
| RevCommit amendedCommit = serverSideTestRepo.amend(r.getCommit()).create(); |
| serverSideTestRepo.branch(branch).update(amendedCommit); |
| |
| serverSideTestRepo.commit(amendedCommit); |
| |
| ChangeInfo info = change(r).info(); |
| assertThat(info.status).isEqualTo(ChangeStatus.NEW); |
| |
| CheckProjectInput input = checkProjectInputForAutoCloseableCheck(branch); |
| input.autoCloseableChangesCheck.fix = true; |
| input.autoCloseableChangesCheck.maxCommits = 1; |
| CheckProjectResultInfo checkResult = gApi.projects().name(project.get()).check(input); |
| assertThat(checkResult.autoCloseableChangesCheckResult.autoCloseableChanges).isEmpty(); |
| |
| info = change(r).info(); |
| assertThat(info.status).isEqualTo(ChangeStatus.NEW); |
| |
| input.autoCloseableChangesCheck.skipCommits = 1; |
| checkResult = gApi.projects().name(project.get()).check(input); |
| assertThat( |
| checkResult.autoCloseableChangesCheckResult.autoCloseableChanges.stream() |
| .map(i -> i._number) |
| .collect(toSet())) |
| .containsExactly(r.getChange().getId().get()); |
| |
| info = change(r).info(); |
| assertThat(info.status).isEqualTo(ChangeStatus.MERGED); |
| } |
| |
| @Test |
| public void noBranch() throws Exception { |
| CheckProjectInput input = new CheckProjectInput(); |
| input.autoCloseableChangesCheck = new AutoCloseableChangesCheckInput(); |
| |
| BadRequestException thrown = |
| assertThrows( |
| BadRequestException.class, () -> gApi.projects().name(project.get()).check(input)); |
| assertThat(thrown).hasMessageThat().contains("branch is required"); |
| } |
| |
| @Test |
| public void nonExistingBranch() throws Exception { |
| CheckProjectInput input = checkProjectInputForAutoCloseableCheck("non-existing"); |
| |
| UnprocessableEntityException thrown = |
| assertThrows( |
| UnprocessableEntityException.class, |
| () -> gApi.projects().name(project.get()).check(input)); |
| assertThat(thrown).hasMessageThat().contains("branch 'non-existing' not found"); |
| } |
| |
| @Test |
| public void branchPrefixCanBeOmitted() throws Exception { |
| CheckProjectInput input = checkProjectInputForAutoCloseableCheck("master"); |
| |
| @SuppressWarnings("unused") |
| var unused = gApi.projects().name(project.get()).check(input); |
| } |
| |
| @Test |
| public void setLimitForMaxCommits() throws Exception { |
| CheckProjectInput input = checkProjectInputForAutoCloseableCheck("refs/heads/master"); |
| input.autoCloseableChangesCheck.maxCommits = |
| ProjectsConsistencyChecker.AUTO_CLOSE_MAX_COMMITS_LIMIT; |
| |
| @SuppressWarnings("unused") |
| var unused = gApi.projects().name(project.get()).check(input); |
| } |
| |
| @Test |
| public void tooLargeMaxCommits() throws Exception { |
| CheckProjectInput input = checkProjectInputForAutoCloseableCheck("refs/heads/master"); |
| input.autoCloseableChangesCheck.maxCommits = |
| ProjectsConsistencyChecker.AUTO_CLOSE_MAX_COMMITS_LIMIT + 1; |
| |
| BadRequestException thrown = |
| assertThrows( |
| BadRequestException.class, () -> gApi.projects().name(project.get()).check(input)); |
| assertThat(thrown) |
| .hasMessageThat() |
| .contains( |
| "max commits can at most be set to " |
| + ProjectsConsistencyChecker.AUTO_CLOSE_MAX_COMMITS_LIMIT); |
| } |
| |
| private RevCommit pushCommitWithoutChangeIdForReview() throws Exception { |
| setRequireChangeId(InheritableBoolean.FALSE); |
| RevCommit commit = |
| testRepo |
| .branch("HEAD") |
| .commit() |
| .message("A change") |
| .insertChangeId() |
| .author(admin.newIdent()) |
| .committer(new PersonIdent(admin.newIdent(), testRepo.getDate())) |
| .create(); |
| pushHead(testRepo, "refs/for/master"); |
| |
| return commit; |
| } |
| |
| private static CheckProjectInput checkProjectInputForAutoCloseableCheck(String branch) { |
| CheckProjectInput input = new CheckProjectInput(); |
| input.autoCloseableChangesCheck = new AutoCloseableChangesCheckInput(); |
| input.autoCloseableChangesCheck.branch = branch; |
| return input; |
| } |
| } |