summaryrefslogtreecommitdiffstats
path: root/javatests/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java
diff options
context:
space:
mode:
Diffstat (limited to 'javatests/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java')
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java486
1 files changed, 486 insertions, 0 deletions
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java b/javatests/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java
new file mode 100644
index 0000000000..97558c4a3a
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java
@@ -0,0 +1,486 @@
+// Copyright (C) 2013 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.Truth.assertWithMessage;
+import static com.google.common.truth.Truth8.assertThat;
+import static com.google.gerrit.acceptance.rest.project.ProjectAssert.assertProjectInfo;
+import static com.google.gerrit.acceptance.rest.project.ProjectAssert.assertProjectOwners;
+import static com.google.gerrit.server.project.ProjectConfig.PROJECT_CONFIG;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.google.common.net.HttpHeaders;
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.GerritConfig;
+import com.google.gerrit.acceptance.RestResponse;
+import com.google.gerrit.acceptance.UseLocalDisk;
+import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.extensions.api.projects.ConfigInfo;
+import com.google.gerrit.extensions.api.projects.ConfigInput;
+import com.google.gerrit.extensions.api.projects.ProjectInput;
+import com.google.gerrit.extensions.client.InheritableBoolean;
+import com.google.gerrit.extensions.client.SubmitType;
+import com.google.gerrit.extensions.common.ProjectInfo;
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
+import com.google.gerrit.extensions.restapi.Url;
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.reviewdb.client.BooleanProjectConfig;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.server.group.SystemGroupBackend;
+import com.google.gerrit.server.project.ProjectState;
+import java.util.Collections;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import org.apache.http.HttpStatus;
+import org.apache.http.message.BasicHeader;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.junit.Test;
+
+public class CreateProjectIT extends AbstractDaemonTest {
+ @Test
+ public void createProjectHttp() throws Exception {
+ String newProjectName = name("newProject");
+ RestResponse r = adminRestSession.put("/projects/" + newProjectName);
+ r.assertCreated();
+ ProjectInfo p = newGson().fromJson(r.getReader(), ProjectInfo.class);
+ assertThat(p.name).isEqualTo(newProjectName);
+
+ // Check that we populate the label data in the HTTP path. See GetProjectIT#getProject
+ // for more extensive coverage of the LabelTypeInfo.
+ assertThat(p.labels).hasSize(1);
+
+ ProjectState projectState = projectCache.get(new Project.NameKey(newProjectName));
+ assertThat(projectState).isNotNull();
+ assertProjectInfo(projectState.getProject(), p);
+ assertHead(newProjectName, "refs/heads/master");
+ }
+
+ @Test
+ public void createProjectHttpWhenProjectAlreadyExists_Conflict() throws Exception {
+ adminRestSession.put("/projects/" + allProjects.get()).assertConflict();
+ }
+
+ @Test
+ public void createProjectHttpWhenProjectAlreadyExists_PreconditionFailed() throws Exception {
+ adminRestSession
+ .putWithHeader(
+ "/projects/" + allProjects.get(), new BasicHeader(HttpHeaders.IF_NONE_MATCH, "*"))
+ .assertPreconditionFailed();
+ }
+
+ @Test
+ public void createSameProjectFromTwoConcurrentRequests() throws Exception {
+ ExecutorService executor = Executors.newFixedThreadPool(2);
+ try {
+ for (int i = 0; i < 10; i++) {
+ String newProjectName = name("foo" + i);
+ CyclicBarrier sync = new CyclicBarrier(2);
+ Callable<RestResponse> createProjectFoo =
+ () -> {
+ sync.await();
+ return adminRestSession.put("/projects/" + newProjectName);
+ };
+
+ Future<RestResponse> r1 = executor.submit(createProjectFoo);
+ Future<RestResponse> r2 = executor.submit(createProjectFoo);
+ assertThat(ImmutableList.of(r1.get().getStatusCode(), r2.get().getStatusCode()))
+ .containsAllOf(HttpStatus.SC_CREATED, HttpStatus.SC_CONFLICT);
+ }
+ } finally {
+ executor.shutdown();
+ executor.awaitTermination(5, TimeUnit.SECONDS);
+ }
+ }
+
+ @Test
+ @UseLocalDisk
+ public void createProjectHttpWithUnreasonableName_BadRequest() throws Exception {
+ ImmutableList<String> forbiddenStrings =
+ ImmutableList.of(
+ "/../", "/./", "//", ".git/", "?", "%", "*", ":", "<", ">", "|", "$", "/+", "~");
+ for (String s : forbiddenStrings) {
+ String projectName = name("invalid" + s + "name");
+ assertWithMessage("Expected status code for " + projectName + " to be 400.")
+ .that(adminRestSession.put("/projects/" + Url.encode(projectName)).getStatusCode())
+ .isEqualTo(HttpStatus.SC_BAD_REQUEST);
+ }
+ }
+
+ @Test
+ public void createProjectHttpWithNameMismatch_BadRequest() throws Exception {
+ ProjectInput in = new ProjectInput();
+ in.name = name("otherName");
+ adminRestSession.put("/projects/" + name("someName"), in).assertBadRequest();
+ }
+
+ @Test
+ public void createProjectHttpWithInvalidRefName_BadRequest() throws Exception {
+ ProjectInput in = new ProjectInput();
+ in.branches = Collections.singletonList(name("invalid ref name"));
+ adminRestSession.put("/projects/" + name("newProject"), in).assertBadRequest();
+ }
+
+ @Test
+ public void createProject() throws Exception {
+ String newProjectName = name("newProject");
+ ProjectInfo p = gApi.projects().create(newProjectName).get();
+ assertThat(p.name).isEqualTo(newProjectName);
+ ProjectState projectState = projectCache.get(new Project.NameKey(newProjectName));
+ assertThat(projectState).isNotNull();
+ assertProjectInfo(projectState.getProject(), p);
+ assertHead(newProjectName, "refs/heads/master");
+ assertThat(readProjectConfig(newProjectName))
+ .hasValue("[access]\n\tinheritFrom = All-Projects\n[submit]\n\taction = inherit\n");
+ }
+
+ @Test
+ public void createProjectWithGitSuffix() throws Exception {
+ String newProjectName = name("newProject");
+ ProjectInfo p = gApi.projects().create(newProjectName + ".git").get();
+ assertThat(p.name).isEqualTo(newProjectName);
+ ProjectState projectState = projectCache.get(new Project.NameKey(newProjectName));
+ assertThat(projectState).isNotNull();
+ assertProjectInfo(projectState.getProject(), p);
+ assertHead(newProjectName, "refs/heads/master");
+ }
+
+ @Test
+ public void createProjectThatEndsWithSlash() throws Exception {
+ String newProjectName = name("newProject");
+ ProjectInfo p = gApi.projects().create(newProjectName + "/").get();
+ assertThat(p.name).isEqualTo(newProjectName);
+ ProjectState projectState = projectCache.get(new Project.NameKey(newProjectName));
+ assertThat(projectState).isNotNull();
+ assertProjectInfo(projectState.getProject(), p);
+ assertHead(newProjectName, "refs/heads/master");
+ }
+
+ @Test
+ public void createProjectThatContainsSlash() throws Exception {
+ String newProjectName = name("newProject/newProject");
+ ProjectInfo p = gApi.projects().create(newProjectName).get();
+ assertThat(p.name).isEqualTo(newProjectName);
+ ProjectState projectState = projectCache.get(new Project.NameKey(newProjectName));
+ assertThat(projectState).isNotNull();
+ assertProjectInfo(projectState.getProject(), p);
+ assertHead(newProjectName, "refs/heads/master");
+ }
+
+ @Test
+ public void createProjectWithProperties() throws Exception {
+ String newProjectName = name("newProject");
+ ProjectInput in = new ProjectInput();
+ in.name = newProjectName;
+ in.description = "Test description";
+ in.submitType = SubmitType.CHERRY_PICK;
+ in.useContributorAgreements = InheritableBoolean.TRUE;
+ in.useSignedOffBy = InheritableBoolean.TRUE;
+ in.useContentMerge = InheritableBoolean.TRUE;
+ in.requireChangeId = InheritableBoolean.TRUE;
+ ProjectInfo p = gApi.projects().create(in).get();
+ assertThat(p.name).isEqualTo(newProjectName);
+ Project project = projectCache.get(new Project.NameKey(newProjectName)).getProject();
+ assertProjectInfo(project, p);
+ assertThat(project.getDescription()).isEqualTo(in.description);
+ assertThat(project.getConfiguredSubmitType()).isEqualTo(in.submitType);
+ assertThat(project.getBooleanConfig(BooleanProjectConfig.USE_CONTRIBUTOR_AGREEMENTS))
+ .isEqualTo(in.useContributorAgreements);
+ assertThat(project.getBooleanConfig(BooleanProjectConfig.USE_SIGNED_OFF_BY))
+ .isEqualTo(in.useSignedOffBy);
+ assertThat(project.getBooleanConfig(BooleanProjectConfig.USE_CONTENT_MERGE))
+ .isEqualTo(in.useContentMerge);
+ assertThat(project.getBooleanConfig(BooleanProjectConfig.REQUIRE_CHANGE_ID))
+ .isEqualTo(in.requireChangeId);
+ }
+
+ @Test
+ public void createChildProject() throws Exception {
+ String parentName = name("parent");
+ ProjectInput in = new ProjectInput();
+ in.name = parentName;
+ gApi.projects().create(in);
+
+ String childName = name("child");
+ in = new ProjectInput();
+ in.name = childName;
+ in.parent = parentName;
+ gApi.projects().create(in);
+ Project project = projectCache.get(new Project.NameKey(childName)).getProject();
+ assertThat(project.getParentName()).isEqualTo(in.parent);
+ }
+
+ @Test
+ public void createChildProjectUnderNonExistingParent_UnprocessableEntity() throws Exception {
+ ProjectInput in = new ProjectInput();
+ in.name = name("newProjectName");
+ in.parent = "non-existing-project";
+ assertCreateFails(in, UnprocessableEntityException.class);
+ }
+
+ @Test
+ public void createProjectWithOwner() throws Exception {
+ String newProjectName = name("newProject");
+ ProjectInput in = new ProjectInput();
+ in.name = newProjectName;
+ in.owners = Lists.newArrayListWithCapacity(3);
+ in.owners.add("Anonymous Users"); // by name
+ in.owners.add(SystemGroupBackend.REGISTERED_USERS.get()); // by UUID
+ in.owners.add(
+ Integer.toString(
+ groupCache
+ .get(new AccountGroup.NameKey("Administrators"))
+ .orElse(null)
+ .getId()
+ .get())); // by ID
+ gApi.projects().create(in);
+ ProjectState projectState = projectCache.get(new Project.NameKey(newProjectName));
+ Set<AccountGroup.UUID> expectedOwnerIds = Sets.newHashSetWithExpectedSize(3);
+ expectedOwnerIds.add(SystemGroupBackend.ANONYMOUS_USERS);
+ expectedOwnerIds.add(SystemGroupBackend.REGISTERED_USERS);
+ expectedOwnerIds.add(groupUuid("Administrators"));
+ assertProjectOwners(expectedOwnerIds, projectState);
+ }
+
+ @Test
+ public void createProjectWithNonExistingOwner_UnprocessableEntity() throws Exception {
+ ProjectInput in = new ProjectInput();
+ in.name = name("newProjectName");
+ in.owners = Collections.singletonList("non-existing-group");
+ assertCreateFails(in, UnprocessableEntityException.class);
+ }
+
+ @Test
+ public void createPermissionOnlyProject() throws Exception {
+ String newProjectName = name("newProject");
+ ProjectInput in = new ProjectInput();
+ in.name = newProjectName;
+ in.permissionsOnly = true;
+ gApi.projects().create(in);
+ assertHead(newProjectName, RefNames.REFS_CONFIG);
+ }
+
+ @Test
+ public void createProjectWithEmptyCommit() throws Exception {
+ String newProjectName = name("newProject");
+ ProjectInput in = new ProjectInput();
+ in.name = newProjectName;
+ in.createEmptyCommit = true;
+ gApi.projects().create(in);
+ assertEmptyCommit(newProjectName, "refs/heads/master");
+ }
+
+ @Test
+ public void createProjectWithBranches() throws Exception {
+ String newProjectName = name("newProject");
+ ProjectInput in = new ProjectInput();
+ in.name = newProjectName;
+ in.createEmptyCommit = true;
+ in.branches = Lists.newArrayListWithCapacity(3);
+ in.branches.add("refs/heads/test");
+ in.branches.add("refs/heads/master");
+ in.branches.add("release"); // without 'refs/heads' prefix
+ gApi.projects().create(in);
+ assertHead(newProjectName, "refs/heads/test");
+ assertEmptyCommit(newProjectName, "refs/heads/test", "refs/heads/master", "refs/heads/release");
+ }
+
+ @Test
+ public void createProjectWithCapability() throws Exception {
+ allowGlobalCapabilities(SystemGroupBackend.REGISTERED_USERS, GlobalCapability.CREATE_PROJECT);
+ try {
+ setApiUser(user);
+ ProjectInput in = new ProjectInput();
+ in.name = name("newProject");
+ ProjectInfo p = gApi.projects().create(in).get();
+ assertThat(p.name).isEqualTo(in.name);
+ } finally {
+ removeGlobalCapabilities(
+ SystemGroupBackend.REGISTERED_USERS, GlobalCapability.CREATE_PROJECT);
+ }
+ }
+
+ @Test
+ public void createProjectWithoutCapability_Forbidden() throws Exception {
+ setApiUser(user);
+ ProjectInput in = new ProjectInput();
+ in.name = name("newProject");
+ assertCreateFails(in, AuthException.class);
+ }
+
+ @Test
+ public void createProjectWhenProjectAlreadyExists_Conflict() throws Exception {
+ ProjectInput in = new ProjectInput();
+ in.name = allProjects.get();
+ assertCreateFails(in, ResourceConflictException.class);
+ }
+
+ @Test
+ public void createProjectWithCreateProjectCapabilityAndParentNotVisible() throws Exception {
+ Project parent = projectCache.get(allProjects).getProject();
+ parent.setState(com.google.gerrit.extensions.client.ProjectState.HIDDEN);
+ allowGlobalCapabilities(SystemGroupBackend.REGISTERED_USERS, GlobalCapability.CREATE_PROJECT);
+ try {
+ setApiUser(user);
+ ProjectInput in = new ProjectInput();
+ in.name = name("newProject");
+ ProjectInfo p = gApi.projects().create(in).get();
+ assertThat(p.name).isEqualTo(in.name);
+ } finally {
+ parent.setState(com.google.gerrit.extensions.client.ProjectState.ACTIVE);
+ removeGlobalCapabilities(
+ SystemGroupBackend.REGISTERED_USERS, GlobalCapability.CREATE_PROJECT);
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ @Test
+ public void createProjectWithDefaultInheritedSubmitType() throws Exception {
+ String parent = name("parent");
+ ProjectInput pin = new ProjectInput();
+ pin.name = parent;
+ ConfigInfo cfg = gApi.projects().create(pin).config();
+ assertThat(cfg.submitType).isEqualTo(SubmitType.MERGE_IF_NECESSARY);
+ assertThat(cfg.defaultSubmitType.value).isEqualTo(SubmitType.MERGE_IF_NECESSARY);
+ assertThat(cfg.defaultSubmitType.configuredValue).isEqualTo(SubmitType.INHERIT);
+ assertThat(cfg.defaultSubmitType.inheritedValue).isEqualTo(SubmitType.MERGE_IF_NECESSARY);
+
+ ConfigInput cin = new ConfigInput();
+ cin.submitType = SubmitType.CHERRY_PICK;
+ gApi.projects().name(parent).config(cin);
+ cfg = gApi.projects().name(parent).config();
+ assertThat(cfg.submitType).isEqualTo(SubmitType.CHERRY_PICK);
+ assertThat(cfg.defaultSubmitType.value).isEqualTo(SubmitType.CHERRY_PICK);
+ assertThat(cfg.defaultSubmitType.configuredValue).isEqualTo(SubmitType.CHERRY_PICK);
+ assertThat(cfg.defaultSubmitType.inheritedValue).isEqualTo(SubmitType.MERGE_IF_NECESSARY);
+
+ String child = name("child");
+ pin = new ProjectInput();
+ pin.submitType = SubmitType.INHERIT;
+ pin.parent = parent;
+ pin.name = child;
+ cfg = gApi.projects().create(pin).config();
+ assertThat(cfg.submitType).isEqualTo(SubmitType.CHERRY_PICK);
+ assertThat(cfg.defaultSubmitType.value).isEqualTo(SubmitType.CHERRY_PICK);
+ assertThat(cfg.defaultSubmitType.configuredValue).isEqualTo(SubmitType.INHERIT);
+ assertThat(cfg.defaultSubmitType.inheritedValue).isEqualTo(SubmitType.CHERRY_PICK);
+
+ cin = new ConfigInput();
+ cin.submitType = SubmitType.REBASE_IF_NECESSARY;
+ gApi.projects().name(parent).config(cin);
+ cfg = gApi.projects().name(parent).config();
+ assertThat(cfg.submitType).isEqualTo(SubmitType.REBASE_IF_NECESSARY);
+ assertThat(cfg.defaultSubmitType.value).isEqualTo(SubmitType.REBASE_IF_NECESSARY);
+ assertThat(cfg.defaultSubmitType.configuredValue).isEqualTo(SubmitType.REBASE_IF_NECESSARY);
+ assertThat(cfg.defaultSubmitType.inheritedValue).isEqualTo(SubmitType.MERGE_IF_NECESSARY);
+
+ cfg = gApi.projects().name(child).config();
+ assertThat(cfg.submitType).isEqualTo(SubmitType.REBASE_IF_NECESSARY);
+ assertThat(cfg.defaultSubmitType.value).isEqualTo(SubmitType.REBASE_IF_NECESSARY);
+ assertThat(cfg.defaultSubmitType.configuredValue).isEqualTo(SubmitType.INHERIT);
+ assertThat(cfg.defaultSubmitType.inheritedValue).isEqualTo(SubmitType.REBASE_IF_NECESSARY);
+ }
+
+ @SuppressWarnings("deprecation")
+ @Test
+ @GerritConfig(
+ name = "repository.testinheritedsubmittype/*.defaultSubmitType",
+ value = "CHERRY_PICK")
+ public void repositoryConfigTakesPrecedenceOverInheritedSubmitType() throws Exception {
+ // Can't use name() since we need to specify this project name in gerrit.config prior to
+ // startup. Pick something reasonably unique instead.
+ String parent = "testinheritedsubmittype";
+ ProjectInput pin = new ProjectInput();
+ pin.name = parent;
+ pin.submitType = SubmitType.MERGE_ALWAYS;
+ ConfigInfo cfg = gApi.projects().create(pin).config();
+ assertThat(cfg.submitType).isEqualTo(SubmitType.MERGE_ALWAYS);
+ assertThat(cfg.defaultSubmitType.value).isEqualTo(SubmitType.MERGE_ALWAYS);
+ assertThat(cfg.defaultSubmitType.configuredValue).isEqualTo(SubmitType.MERGE_ALWAYS);
+ assertThat(cfg.defaultSubmitType.inheritedValue).isEqualTo(SubmitType.MERGE_IF_NECESSARY);
+
+ String child = parent + "/child";
+ pin = new ProjectInput();
+ pin.parent = parent;
+ pin.name = child;
+ cfg = gApi.projects().create(pin).config();
+ assertThat(cfg.submitType).isEqualTo(SubmitType.CHERRY_PICK);
+ assertThat(cfg.defaultSubmitType.value).isEqualTo(SubmitType.CHERRY_PICK);
+ assertThat(cfg.defaultSubmitType.configuredValue).isEqualTo(SubmitType.CHERRY_PICK);
+ assertThat(cfg.defaultSubmitType.inheritedValue).isEqualTo(SubmitType.MERGE_ALWAYS);
+ }
+
+ private void assertHead(String projectName, String expectedRef) throws Exception {
+ try (Repository repo = repoManager.openRepository(new Project.NameKey(projectName))) {
+ assertThat(repo.exactRef(Constants.HEAD).getTarget().getName()).isEqualTo(expectedRef);
+ }
+ }
+
+ private void assertEmptyCommit(String projectName, String... refs) throws Exception {
+ Project.NameKey projectKey = new Project.NameKey(projectName);
+ try (Repository repo = repoManager.openRepository(projectKey);
+ RevWalk rw = new RevWalk(repo);
+ TreeWalk tw = new TreeWalk(rw.getObjectReader())) {
+ for (String ref : refs) {
+ RevCommit commit = rw.lookupCommit(repo.exactRef(ref).getObjectId());
+ rw.parseBody(commit);
+ tw.addTree(commit.getTree());
+ assertThat(tw.next()).isFalse();
+ tw.reset();
+ }
+ }
+ }
+
+ private void assertCreateFails(ProjectInput in, Class<? extends RestApiException> errType)
+ throws Exception {
+ exception.expect(errType);
+ gApi.projects().create(in);
+ }
+
+ private Optional<String> readProjectConfig(String projectName) throws Exception {
+ try (Repository repo = repoManager.openRepository(new Project.NameKey(projectName))) {
+ TestRepository<?> tr = new TestRepository<>(repo);
+ RevWalk rw = tr.getRevWalk();
+ Ref ref = repo.exactRef(RefNames.REFS_CONFIG);
+ if (ref == null) {
+ return Optional.empty();
+ }
+ ObjectLoader obj =
+ rw.getObjectReader()
+ .open(tr.get(rw.parseTree(ref.getObjectId()), PROJECT_CONFIG), Constants.OBJ_BLOB);
+ return Optional.of(new String(obj.getCachedBytes(Integer.MAX_VALUE), UTF_8));
+ }
+ }
+}