| // Copyright (C) 2020 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.testsuite.change; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.permissionKey; |
| import static com.google.gerrit.extensions.common.testing.CommitInfoSubject.assertThat; |
| import static com.google.gerrit.extensions.common.testing.CommitInfoSubject.hasCommit; |
| import static com.google.gerrit.extensions.restapi.testing.BinaryResultSubject.assertThat; |
| import static com.google.gerrit.testing.GerritJUnit.assertThrows; |
| import static com.google.gerrit.truth.MapSubject.assertThatMap; |
| |
| import com.google.gerrit.acceptance.AbstractDaemonTest; |
| import com.google.gerrit.acceptance.testsuite.account.AccountOperations; |
| import com.google.gerrit.acceptance.testsuite.project.ProjectOperations; |
| import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations; |
| import com.google.gerrit.entities.Account; |
| import com.google.gerrit.entities.Change; |
| import com.google.gerrit.entities.PatchSet; |
| import com.google.gerrit.entities.Permission; |
| import com.google.gerrit.entities.Project; |
| import com.google.gerrit.extensions.client.ChangeKind; |
| import com.google.gerrit.extensions.common.ChangeInfo; |
| import com.google.gerrit.extensions.common.CommitInfo; |
| import com.google.gerrit.extensions.common.FileInfo; |
| import com.google.gerrit.extensions.common.RevisionInfo; |
| import com.google.gerrit.extensions.restapi.BinaryResult; |
| import com.google.gerrit.extensions.restapi.RestApiException; |
| import com.google.inject.Inject; |
| import java.util.Map; |
| import org.eclipse.jgit.lib.ObjectId; |
| import org.junit.Test; |
| |
| public class ChangeOperationsImplTest extends AbstractDaemonTest { |
| |
| @Inject private ChangeOperations changeOperations; |
| @Inject private ProjectOperations projectOperations; |
| @Inject private AccountOperations accountOperations; |
| @Inject private RequestScopeOperations requestScopeOperations; |
| |
| @Test |
| public void changeCanBeCreatedWithoutSpecifyingAnyParameters() throws Exception { |
| Change.Id numericChangeId = changeOperations.newChange().create(); |
| |
| ChangeInfo change = getChangeFromServer(numericChangeId); |
| assertThat(change._number).isEqualTo(numericChangeId.get()); |
| assertThat(change.changeId).isNotEmpty(); |
| } |
| |
| @Test |
| public void changeCanBeCreatedEvenWithRequestScopeOfArbitraryUser() throws Exception { |
| Account.Id user = accountOperations.newAccount().create(); |
| |
| requestScopeOperations.setApiUser(user); |
| Change.Id numericChangeId = changeOperations.newChange().create(); |
| |
| ChangeInfo change = getChangeFromServer(numericChangeId); |
| assertThat(change._number).isEqualTo(numericChangeId.get()); |
| } |
| |
| @Test |
| public void twoChangesWithoutAnyParametersDoNotClash() { |
| Change.Id changeId1 = changeOperations.newChange().create(); |
| Change.Id changeId2 = changeOperations.newChange().create(); |
| |
| TestChange change1 = changeOperations.change(changeId1).get(); |
| TestChange change2 = changeOperations.change(changeId2).get(); |
| assertThat(change1.numericChangeId()).isNotEqualTo(change2.numericChangeId()); |
| assertThat(change1.changeId()).isNotEqualTo(change2.changeId()); |
| } |
| |
| @Test |
| public void twoSubsequentlyCreatedChangesDoNotDependOnEachOther() throws Exception { |
| Change.Id changeId1 = changeOperations.newChange().create(); |
| Change.Id changeId2 = changeOperations.newChange().create(); |
| |
| ChangeInfo change1 = getChangeFromServer(changeId1); |
| ChangeInfo change2 = getChangeFromServer(changeId2); |
| CommitInfo currentPatchsetCommit1 = change1.revisions.get(change1.currentRevision).commit; |
| CommitInfo currentPatchsetCommit2 = change2.revisions.get(change2.currentRevision).commit; |
| assertThat(currentPatchsetCommit1) |
| .parents() |
| .comparingElementsUsing(hasCommit()) |
| .doesNotContain(currentPatchsetCommit2.commit); |
| assertThat(currentPatchsetCommit2) |
| .parents() |
| .comparingElementsUsing(hasCommit()) |
| .doesNotContain(currentPatchsetCommit1.commit); |
| } |
| |
| @Test |
| public void createdChangeHasAtLeastOnePatchset() throws Exception { |
| Change.Id changeId = changeOperations.newChange().create(); |
| |
| ChangeInfo change = getChangeFromServer(changeId); |
| assertThatMap(change.revisions).size().isAtLeast(1); |
| } |
| |
| @Test |
| public void createdChangeIsInSpecifiedProject() throws Exception { |
| Project.NameKey project = projectOperations.newProject().create(); |
| Change.Id changeId = changeOperations.newChange().project(project).create(); |
| |
| ChangeInfo change = getChangeFromServer(changeId); |
| assertThat(change.project).isEqualTo(project.get()); |
| } |
| |
| @Test |
| public void changeCanBeCreatedInEmptyRepository() throws Exception { |
| Project.NameKey project = projectOperations.newProject().noEmptyCommit().create(); |
| Change.Id changeId = changeOperations.newChange().project(project).create(); |
| |
| ChangeInfo change = getChangeFromServer(changeId); |
| assertThat(change.project).isEqualTo(project.get()); |
| } |
| |
| @Test |
| public void createdChangeHasSpecifiedTargetBranch() throws Exception { |
| Project.NameKey project = projectOperations.newProject().branches("test-branch").create(); |
| Change.Id changeId = |
| changeOperations.newChange().project(project).branch("test-branch").create(); |
| |
| ChangeInfo change = getChangeFromServer(changeId); |
| assertThat(change.branch).isEqualTo("test-branch"); |
| } |
| |
| @Test |
| public void createdChangeUsesTipOfTargetBranchAsParentByDefault() throws Exception { |
| Project.NameKey project = projectOperations.newProject().branches("test-branch").create(); |
| ObjectId parentCommitId = projectOperations.project(project).getHead("test-branch").getId(); |
| Change.Id changeId = |
| changeOperations.newChange().project(project).branch("test-branch").create(); |
| |
| ChangeInfo change = getChangeFromServer(changeId); |
| CommitInfo currentPatchsetCommit = change.revisions.get(change.currentRevision).commit; |
| assertThat(currentPatchsetCommit) |
| .parents() |
| .onlyElement() |
| .commit() |
| .isEqualTo(parentCommitId.getName()); |
| } |
| |
| @Test |
| public void createdChangeHasSpecifiedOwner() throws Exception { |
| Account.Id changeOwner = accountOperations.newAccount().create(); |
| Change.Id changeId = changeOperations.newChange().owner(changeOwner).create(); |
| |
| ChangeInfo change = getChangeFromServer(changeId); |
| assertThat(change.owner._accountId).isEqualTo(changeOwner.get()); |
| } |
| |
| @Test |
| public void changeOwnerDoesNotNeedAnyPermissionsForChangeCreation() throws Exception { |
| Account.Id changeOwner = accountOperations.newAccount().create(); |
| Project.NameKey project = projectOperations.newProject().branches("test-branch").create(); |
| // Remove any read and push permissions which might potentially exist. Without read, users |
| // shouldn't be able to do anything. The newly created project should only inherit from |
| // All-Projects. |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .remove(permissionKey(Permission.READ).ref("refs/heads/test-branch")) |
| .remove(permissionKey(Permission.PUSH).ref("refs/heads/test-branch")) |
| .update(); |
| projectOperations |
| .allProjectsForUpdate() |
| .remove(permissionKey(Permission.READ).ref("refs/heads/test-branch")) |
| .remove(permissionKey(Permission.PUSH).ref("refs/heads/test-branch")) |
| .update(); |
| |
| Change.Id changeId = |
| changeOperations |
| .newChange() |
| .owner(changeOwner) |
| .branch("test-branch") |
| .project(project) |
| .create(); |
| |
| ChangeInfo change = getChangeFromServer(changeId); |
| assertThat(change.owner._accountId).isEqualTo(changeOwner.get()); |
| } |
| |
| @Test |
| public void createdChangeHasSpecifiedCommitMessage() throws Exception { |
| Change.Id changeId = |
| changeOperations |
| .newChange() |
| .commitMessage("Summary line\n\nDetailed description.") |
| .create(); |
| |
| ChangeInfo change = getChangeFromServer(changeId); |
| CommitInfo currentPatchsetCommit = change.revisions.get(change.currentRevision).commit; |
| assertThat(currentPatchsetCommit).message().startsWith("Summary line\n\nDetailed description."); |
| } |
| |
| @Test |
| public void changeCannotBeCreatedWithoutCommitMessage() { |
| assertThrows( |
| IllegalStateException.class, () -> changeOperations.newChange().commitMessage("").create()); |
| } |
| |
| @Test |
| public void commitMessageOfCreatedChangeAutomaticallyGetsChangeId() throws Exception { |
| Change.Id changeId = |
| changeOperations |
| .newChange() |
| .commitMessage("Summary line\n\nDetailed description.") |
| .create(); |
| |
| ChangeInfo change = getChangeFromServer(changeId); |
| CommitInfo currentPatchsetCommit = change.revisions.get(change.currentRevision).commit; |
| assertThat(currentPatchsetCommit).message().contains("Change-Id:"); |
| } |
| |
| @Test |
| public void changeIdSpecifiedInCommitMessageIsKeptForCreatedChange() throws Exception { |
| Change.Id changeId = |
| changeOperations |
| .newChange() |
| .commitMessage("Summary line\n\nChange-Id: I0123456789012345678901234567890123456789") |
| .create(); |
| |
| ChangeInfo change = getChangeFromServer(changeId); |
| CommitInfo currentPatchsetCommit = change.revisions.get(change.currentRevision).commit; |
| assertThat(currentPatchsetCommit) |
| .message() |
| .contains("Change-Id: I0123456789012345678901234567890123456789"); |
| assertThat(change.changeId).isEqualTo("I0123456789012345678901234567890123456789"); |
| } |
| |
| @Test |
| public void createdChangeHasSpecifiedFiles() throws Exception { |
| Change.Id changeId = |
| changeOperations |
| .newChange() |
| .file("file1") |
| .content("Line 1") |
| .file("path/to/file2.txt") |
| .content("Line one") |
| .create(); |
| |
| ChangeInfo change = getChangeFromServer(changeId); |
| Map<String, FileInfo> files = change.revisions.get(change.currentRevision).files; |
| assertThatMap(files).keys().containsExactly("file1", "path/to/file2.txt"); |
| BinaryResult fileContent1 = gApi.changes().id(changeId.get()).current().file("file1").content(); |
| assertThat(fileContent1).asString().isEqualTo("Line 1"); |
| BinaryResult fileContent2 = |
| gApi.changes().id(changeId.get()).current().file("path/to/file2.txt").content(); |
| assertThat(fileContent2).asString().isEqualTo("Line one"); |
| } |
| |
| @Test |
| public void existingChangeCanBeCheckedForExistence() { |
| Change.Id changeId = changeOperations.newChange().create(); |
| |
| boolean exists = changeOperations.change(changeId).exists(); |
| |
| assertThat(exists).isTrue(); |
| } |
| |
| @Test |
| public void notExistingChangeCanBeCheckedForExistence() { |
| Change.Id changeId = Change.id(123456789); |
| |
| boolean exists = changeOperations.change(changeId).exists(); |
| |
| assertThat(exists).isFalse(); |
| } |
| |
| @Test |
| public void retrievingNotExistingChangeFails() { |
| Change.Id changeId = Change.id(123456789); |
| assertThrows(IllegalStateException.class, () -> changeOperations.change(changeId).get()); |
| } |
| |
| @Test |
| public void numericChangeIdOfExistingChangeCanBeRetrieved() { |
| Change.Id changeId = changeOperations.newChange().create(); |
| |
| TestChange change = changeOperations.change(changeId).get(); |
| assertThat(change.numericChangeId()).isEqualTo(changeId); |
| } |
| |
| @Test |
| public void changeIdOfExistingChangeCanBeRetrieved() { |
| Change.Id changeId = |
| changeOperations |
| .newChange() |
| .commitMessage("Summary line\n\nChange-Id: I0123456789012345678901234567890123456789") |
| .create(); |
| |
| TestChange change = changeOperations.change(changeId).get(); |
| assertThat(change.changeId()).isEqualTo("I0123456789012345678901234567890123456789"); |
| } |
| |
| @Test |
| public void currentPatchsetOfExistingChangeCanBeRetrieved() throws Exception { |
| Change.Id changeId = changeOperations.newChange().create(); |
| |
| TestPatchset patchset = changeOperations.change(changeId).currentPatchset().get(); |
| |
| ChangeInfo expectedChange = getChangeFromServer(changeId); |
| String expectedCommitId = expectedChange.currentRevision; |
| int expectedPatchsetNumber = expectedChange.revisions.get(expectedCommitId)._number; |
| assertThat(patchset.commitId()).isEqualTo(ObjectId.fromString(expectedCommitId)); |
| assertThat(patchset.patchsetId()).isEqualTo(PatchSet.id(changeId, expectedPatchsetNumber)); |
| } |
| |
| @Test |
| public void earlierPatchsetOfExistingChangeCanBeRetrieved() { |
| Change.Id changeId = changeOperations.newChange().create(); |
| PatchSet.Id earlierPatchsetId = |
| changeOperations.change(changeId).currentPatchset().get().patchsetId(); |
| PatchSet.Id currentPatchsetId = changeOperations.change(changeId).newPatchset().create(); |
| |
| TestPatchset earlierPatchset = |
| changeOperations.change(changeId).patchset(earlierPatchsetId).get(); |
| |
| assertThat(earlierPatchset.patchsetId()).isEqualTo(earlierPatchsetId); |
| assertThat(earlierPatchset.patchsetId()).isNotEqualTo(currentPatchsetId); |
| } |
| |
| @Test |
| public void newPatchsetCanBeCreatedWithoutSpecifyingAnyParameters() throws Exception { |
| Change.Id changeId = changeOperations.newChange().create(); |
| ChangeInfo unmodifiedChange = getChangeFromServer(changeId); |
| int originalPatchsetCount = unmodifiedChange.revisions.size(); |
| |
| PatchSet.Id patchsetId = changeOperations.change(changeId).newPatchset().create(); |
| |
| ChangeInfo change = getChangeFromServer(changeId); |
| assertThatMap(change.revisions).hasSize(originalPatchsetCount + 1); |
| RevisionInfo currentRevision = change.revisions.get(change.currentRevision); |
| assertThat(currentRevision._number).isEqualTo(patchsetId.get()); |
| } |
| |
| @Test |
| public void newPatchsetIsCopyOfPreviousPatchsetByDefault() throws Exception { |
| Change.Id changeId = changeOperations.newChange().create(); |
| |
| PatchSet.Id patchsetId = changeOperations.change(changeId).newPatchset().create(); |
| |
| ChangeInfo change = getChangeFromServer(changeId); |
| RevisionInfo patchsetRevision = getRevision(change, patchsetId); |
| assertThat(patchsetRevision.kind).isEqualTo(ChangeKind.NO_CHANGE); |
| } |
| |
| @Test |
| public void newPatchsetCanHaveReplacedFileContent() throws Exception { |
| Change.Id changeId = changeOperations.newChange().file("file1").content("Line 1").create(); |
| |
| PatchSet.Id patchsetId = |
| changeOperations |
| .change(changeId) |
| .newPatchset() |
| .file("file1") |
| .content("Different content") |
| .create(); |
| |
| ChangeInfo change = getChangeFromServer(changeId); |
| Map<String, FileInfo> files = change.revisions.get(change.currentRevision).files; |
| assertThatMap(files).keys().containsExactly("file1"); |
| BinaryResult fileContent = getFileContent(changeId, patchsetId, "file1"); |
| assertThat(fileContent).asString().isEqualTo("Different content"); |
| } |
| |
| @Test |
| public void newPatchsetCanHaveAdditionalFile() throws Exception { |
| Change.Id changeId = changeOperations.newChange().file("file1").content("Line 1").create(); |
| |
| PatchSet.Id patchsetId = |
| changeOperations |
| .change(changeId) |
| .newPatchset() |
| .file("file2") |
| .content("My file content") |
| .create(); |
| |
| ChangeInfo change = getChangeFromServer(changeId); |
| Map<String, FileInfo> files = change.revisions.get(change.currentRevision).files; |
| assertThatMap(files).keys().containsExactly("file1", "file2"); |
| BinaryResult fileContent = getFileContent(changeId, patchsetId, "file2"); |
| assertThat(fileContent).asString().isEqualTo("My file content"); |
| } |
| |
| @Test |
| public void newPatchsetCanHaveLessFiles() throws Exception { |
| Change.Id changeId = |
| changeOperations |
| .newChange() |
| .file("file1") |
| .content("Line 1") |
| .file("file2") |
| .content("Line one") |
| .create(); |
| |
| changeOperations.change(changeId).newPatchset().file("file2").delete().create(); |
| |
| ChangeInfo change = getChangeFromServer(changeId); |
| Map<String, FileInfo> files = change.revisions.get(change.currentRevision).files; |
| assertThatMap(files).keys().containsExactly("file1"); |
| } |
| |
| @Test |
| public void newPatchsetCanHaveRenamedFile() throws Exception { |
| Change.Id changeId = |
| changeOperations |
| .newChange() |
| .file("file1") |
| .content("Line 1") |
| .file("file2") |
| .content("Line one") |
| .create(); |
| |
| PatchSet.Id patchsetId = |
| changeOperations |
| .change(changeId) |
| .newPatchset() |
| .file("file2") |
| .renameTo("renamed file") |
| .create(); |
| |
| ChangeInfo change = getChangeFromServer(changeId); |
| Map<String, FileInfo> files = change.revisions.get(change.currentRevision).files; |
| assertThatMap(files).keys().containsExactly("file1", "renamed file"); |
| BinaryResult fileContent = getFileContent(changeId, patchsetId, "renamed file"); |
| assertThat(fileContent).asString().isEqualTo("Line one"); |
| } |
| |
| private ChangeInfo getChangeFromServer(Change.Id changeId) throws RestApiException { |
| return gApi.changes().id(changeId.get()).get(); |
| } |
| |
| private RevisionInfo getRevision(ChangeInfo change, PatchSet.Id patchsetId) { |
| return change.revisions.values().stream() |
| .filter(revision -> revision._number == patchsetId.get()) |
| .findAny() |
| .orElseThrow( |
| () -> |
| new IllegalStateException( |
| String.format( |
| "Change %d doesn't have specified patchset %d.", |
| change._number, patchsetId.get()))); |
| } |
| |
| private BinaryResult getFileContent(Change.Id changeId, PatchSet.Id patchsetId, String filePath) |
| throws RestApiException { |
| return gApi.changes().id(changeId.get()).revision(patchsetId.get()).file(filePath).content(); |
| } |
| } |