| // Copyright (C) 2014 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.common.truth.Truth8.assertThat; |
| import static com.google.gerrit.reviewdb.client.RefNames.REFS_HEADS; |
| import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS; |
| import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS; |
| |
| import com.google.gerrit.acceptance.AbstractDaemonTest; |
| import com.google.gerrit.acceptance.RestResponse; |
| import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations; |
| import com.google.gerrit.common.data.Permission; |
| import com.google.gerrit.extensions.api.projects.BranchApi; |
| import com.google.gerrit.extensions.api.projects.BranchInfo; |
| import com.google.gerrit.extensions.api.projects.BranchInput; |
| 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.RestApiException; |
| import com.google.gerrit.reviewdb.client.Account; |
| import com.google.gerrit.reviewdb.client.AccountGroup; |
| import com.google.gerrit.reviewdb.client.Branch; |
| import com.google.gerrit.reviewdb.client.RefNames; |
| import com.google.inject.Inject; |
| import org.eclipse.jgit.revwalk.RevCommit; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| public class CreateBranchIT extends AbstractDaemonTest { |
| @Inject private RequestScopeOperations requestScopeOperations; |
| |
| private Branch.NameKey testBranch; |
| |
| @Before |
| public void setUp() throws Exception { |
| testBranch = new Branch.NameKey(project, "test"); |
| } |
| |
| @Test |
| public void createBranchRestApi() throws Exception { |
| BranchInput input = new BranchInput(); |
| input.ref = "foo"; |
| assertThat(gApi.projects().name(project.get()).branches().get().stream().map(i -> i.ref)) |
| .doesNotContain(REFS_HEADS + input.ref); |
| RestResponse r = |
| adminRestSession.put("/projects/" + project.get() + "/branches/" + input.ref, input); |
| r.assertCreated(); |
| assertThat(gApi.projects().name(project.get()).branches().get().stream().map(i -> i.ref)) |
| .contains(REFS_HEADS + input.ref); |
| } |
| |
| @Test |
| public void createBranch_Forbidden() throws Exception { |
| requestScopeOperations.setApiUser(user.id()); |
| assertCreateFails(testBranch, AuthException.class, "not permitted: create on refs/heads/test"); |
| } |
| |
| @Test |
| public void createBranchByAdmin() throws Exception { |
| assertCreateSucceeds(testBranch); |
| } |
| |
| @Test |
| public void branchAlreadyExists_Conflict() throws Exception { |
| assertCreateSucceeds(testBranch); |
| assertCreateFails(testBranch, ResourceConflictException.class); |
| } |
| |
| @Test |
| public void createBranchByProjectOwner() throws Exception { |
| grantOwner(); |
| requestScopeOperations.setApiUser(user.id()); |
| assertCreateSucceeds(testBranch); |
| } |
| |
| @Test |
| public void createBranchByAdminCreateReferenceBlocked_Forbidden() throws Exception { |
| blockCreateReference(); |
| assertCreateFails(testBranch, AuthException.class, "not permitted: create on refs/heads/test"); |
| } |
| |
| @Test |
| public void createBranchByProjectOwnerCreateReferenceBlocked_Forbidden() throws Exception { |
| grantOwner(); |
| blockCreateReference(); |
| requestScopeOperations.setApiUser(user.id()); |
| assertCreateFails(testBranch, AuthException.class, "not permitted: create on refs/heads/test"); |
| } |
| |
| @Test |
| public void createMetaBranch() throws Exception { |
| String metaRef = RefNames.REFS_META + "foo"; |
| allow(metaRef, Permission.CREATE, REGISTERED_USERS); |
| allow(metaRef, Permission.PUSH, REGISTERED_USERS); |
| assertCreateSucceeds(new Branch.NameKey(project, metaRef)); |
| } |
| |
| @Test |
| public void createUserBranch_Conflict() throws Exception { |
| allow(allUsers, RefNames.REFS_USERS + "*", Permission.CREATE, REGISTERED_USERS); |
| allow(allUsers, RefNames.REFS_USERS + "*", Permission.PUSH, REGISTERED_USERS); |
| assertCreateFails( |
| new Branch.NameKey(allUsers, RefNames.refsUsers(new Account.Id(1))), |
| RefNames.refsUsers(admin.id()), |
| ResourceConflictException.class, |
| "Not allowed to create user branch."); |
| } |
| |
| @Test |
| public void createGroupBranch_Conflict() throws Exception { |
| allow(allUsers, RefNames.REFS_GROUPS + "*", Permission.CREATE, REGISTERED_USERS); |
| allow(allUsers, RefNames.REFS_GROUPS + "*", Permission.PUSH, REGISTERED_USERS); |
| assertCreateFails( |
| new Branch.NameKey(allUsers, RefNames.refsGroups(new AccountGroup.UUID("foo"))), |
| RefNames.refsGroups(adminGroupUuid()), |
| ResourceConflictException.class, |
| "Not allowed to create group branch."); |
| } |
| |
| @Test |
| public void createWithRevision() throws Exception { |
| RevCommit revision = getRemoteHead(project, "master"); |
| |
| // update master so that points to a different revision than the revision on which we create the |
| // new branch |
| pushTo("refs/heads/master"); |
| assertThat(getRemoteHead(project, "master")).isNotEqualTo(revision); |
| |
| BranchInput input = new BranchInput(); |
| input.revision = revision.name(); |
| BranchInfo created = branch(testBranch).create(input).get(); |
| assertThat(created.ref).isEqualTo(testBranch.get()); |
| assertThat(created.revision).isEqualTo(revision.name()); |
| assertThat(getRemoteHead(project, testBranch.getShortName())).isEqualTo(revision); |
| } |
| |
| @Test |
| public void createWithoutSpecifyingRevision() throws Exception { |
| // If revision is not specified, the branch is created based on HEAD, which points to master. |
| RevCommit expectedRevision = getRemoteHead(project, "master"); |
| |
| BranchInput input = new BranchInput(); |
| input.revision = null; |
| BranchInfo created = branch(testBranch).create(input).get(); |
| assertThat(created.ref).isEqualTo(testBranch.get()); |
| assertThat(created.revision).isEqualTo(expectedRevision.name()); |
| assertThat(getRemoteHead(project, testBranch.getShortName())).isEqualTo(expectedRevision); |
| } |
| |
| @Test |
| public void createWithEmptyRevision() throws Exception { |
| // If revision is not specified, the branch is created based on HEAD, which points to master. |
| RevCommit expectedRevision = getRemoteHead(project, "master"); |
| |
| BranchInput input = new BranchInput(); |
| input.revision = ""; |
| BranchInfo created = branch(testBranch).create(input).get(); |
| assertThat(created.ref).isEqualTo(testBranch.get()); |
| assertThat(created.revision).isEqualTo(expectedRevision.name()); |
| assertThat(getRemoteHead(project, testBranch.getShortName())).isEqualTo(expectedRevision); |
| } |
| |
| @Test |
| public void createRevisionIsTrimmed() throws Exception { |
| RevCommit revision = getRemoteHead(project, "master"); |
| |
| BranchInput input = new BranchInput(); |
| input.revision = "\t" + revision.name(); |
| BranchInfo created = branch(testBranch).create(input).get(); |
| assertThat(created.ref).isEqualTo(testBranch.get()); |
| assertThat(created.revision).isEqualTo(revision.name()); |
| assertThat(getRemoteHead(project, testBranch.getShortName())).isEqualTo(revision); |
| } |
| |
| @Test |
| public void createWithBranchNameAsRevision() throws Exception { |
| RevCommit expectedRevision = getRemoteHead(project, "master"); |
| |
| BranchInput input = new BranchInput(); |
| input.revision = "master"; |
| BranchInfo created = branch(testBranch).create(input).get(); |
| assertThat(created.ref).isEqualTo(testBranch.get()); |
| assertThat(created.revision).isEqualTo(expectedRevision.name()); |
| assertThat(getRemoteHead(project, testBranch.getShortName())).isEqualTo(expectedRevision); |
| } |
| |
| @Test |
| public void createWithFullBranchNameAsRevision() throws Exception { |
| RevCommit expectedRevision = getRemoteHead(project, "master"); |
| |
| BranchInput input = new BranchInput(); |
| input.revision = "refs/heads/master"; |
| BranchInfo created = branch(testBranch).create(input).get(); |
| assertThat(created.ref).isEqualTo(testBranch.get()); |
| assertThat(created.revision).isEqualTo(expectedRevision.name()); |
| assertThat(getRemoteHead(project, testBranch.getShortName())).isEqualTo(expectedRevision); |
| } |
| |
| @Test |
| public void cannotCreateWithNonExistingBranchNameAsRevision() throws Exception { |
| assertCreateFails( |
| testBranch, |
| "refs/heads/non-existing", |
| BadRequestException.class, |
| "invalid revision \"refs/heads/non-existing\""); |
| } |
| |
| @Test |
| public void cannotCreateWithNonExistingRevision() throws Exception { |
| assertCreateFails( |
| testBranch, |
| "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef", |
| BadRequestException.class, |
| "invalid revision \"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef\""); |
| } |
| |
| @Test |
| public void cannotCreateWithInvalidRevision() throws Exception { |
| assertCreateFails( |
| testBranch, |
| "invalid\trevision", |
| BadRequestException.class, |
| "invalid revision \"invalid\trevision\""); |
| } |
| |
| private void blockCreateReference() throws Exception { |
| block("refs/*", Permission.CREATE, ANONYMOUS_USERS); |
| } |
| |
| private void grantOwner() throws Exception { |
| allow("refs/*", Permission.OWNER, REGISTERED_USERS); |
| } |
| |
| private BranchApi branch(Branch.NameKey branch) throws Exception { |
| return gApi.projects().name(branch.getParentKey().get()).branch(branch.get()); |
| } |
| |
| private void assertCreateSucceeds(Branch.NameKey branch) throws Exception { |
| BranchInfo created = branch(branch).create(new BranchInput()).get(); |
| assertThat(created.ref).isEqualTo(branch.get()); |
| } |
| |
| private void assertCreateFails( |
| Branch.NameKey branch, Class<? extends RestApiException> errType, String errMsg) |
| throws Exception { |
| assertCreateFails(branch, null, errType, errMsg); |
| } |
| |
| private void assertCreateFails( |
| Branch.NameKey branch, |
| String revision, |
| Class<? extends RestApiException> errType, |
| String errMsg) |
| throws Exception { |
| BranchInput in = new BranchInput(); |
| in.revision = revision; |
| if (errMsg != null) { |
| exception.expectMessage(errMsg); |
| } |
| exception.expect(errType); |
| branch(branch).create(in); |
| } |
| |
| private void assertCreateFails(Branch.NameKey branch, Class<? extends RestApiException> errType) |
| throws Exception { |
| assertCreateFails(branch, errType, null); |
| } |
| } |