]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-10536 refactoring, no functional changes
authorSimon Brandhof <simon.brandhof@sonarsource.com>
Tue, 3 Apr 2018 11:54:14 +0000 (13:54 +0200)
committerSonarTech <sonartech@sonarsource.com>
Thu, 5 Apr 2018 18:20:48 +0000 (20:20 +0200)
server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentKeyUpdaterDao.java
server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentKeyUpdaterMapper.java
server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentKeyUpdaterMapper.xml
server/sonar-server/src/main/java/org/sonar/server/computation/queue/ReportSubmitter.java
server/sonar-server/src/main/java/org/sonar/server/project/ws/UpdateKeyAction.java
server/sonar-server/src/test/java/org/sonar/server/project/ws/UpdateKeyActionTest.java
tests/src/test/java/org/sonarqube/tests/component/ComponentSuite.java
tests/src/test/java/org/sonarqube/tests/component/ModuleTest.java [new file with mode: 0644]
tests/src/test/java/org/sonarqube/tests/project/ProjectKeyUpdateTest.java
tests/src/test/java/util/XooProjectBuilder.java [new file with mode: 0644]

index 6a0ea848516eeb3f2157682ca3d7ff25b27c0291..8bc0a216bb8fb79c968468b5bc235dac381e0877 100644 (file)
@@ -56,7 +56,7 @@ public class ComponentKeyUpdaterDao implements Dao {
     }
 
     // must SELECT first everything
-    ResourceDto project = mapper.selectProject(projectOrModuleUuid);
+    ResourceDto project = mapper.selectProjectByUuid(projectOrModuleUuid);
     String projectOldKey = project.getKey();
     List<ResourceDto> resources = mapper.selectProjectResources(projectOrModuleUuid);
     resources.add(project);
@@ -67,7 +67,7 @@ public class ComponentKeyUpdaterDao implements Dao {
       .filter(branch -> !projectOrModuleUuid.equals(branch.getUuid()))
       .forEach(branch -> {
         resources.addAll(mapper.selectProjectResources(branch.getUuid()));
-        resources.add(mapper.selectProject(branch.getUuid()));
+        resources.add(mapper.selectProjectByUuid(branch.getUuid()));
       });
 
     // and then proceed with the batch UPDATE at once
@@ -166,7 +166,7 @@ public class ComponentKeyUpdaterDao implements Dao {
   }
 
   private static Set<ResourceDto> collectAllModules(String projectUuid, String stringToReplace, ComponentKeyUpdaterMapper mapper, boolean includeDisabled) {
-    ResourceDto project = mapper.selectProject(projectUuid);
+    ResourceDto project = mapper.selectProjectByUuid(projectUuid);
     Set<ResourceDto> modules = new HashSet<>();
     if (project.getKey().contains(stringToReplace) && (project.isEnabled() || includeDisabled)) {
       modules.add(project);
index 75cfc798e7a5b301b66db568a74d6f31143c0605..364fe67eae35fb03f1f21b210a9737edc12d9b42 100644 (file)
@@ -22,14 +22,11 @@ package org.sonar.db.component;
 import java.util.List;
 import org.apache.ibatis.annotations.Param;
 
-/**
- * @since 3.2
- */
 public interface ComponentKeyUpdaterMapper {
 
   int countResourceByKey(String key);
 
-  ResourceDto selectProject(@Param("uuid") String uuid);
+  ResourceDto selectProjectByUuid(@Param("uuid") String uuid);
 
   List<ResourceDto> selectProjectResources(@Param("rootUuid") String rootUuid);
 
index 51f2114ab66c442a51e4cc68798dab4db28d1717..f0afa194b7598d4e95b41011872e07f2a121f18e 100644 (file)
@@ -19,7 +19,7 @@
     WHERE kee = #{key,jdbcType=VARCHAR}
   </select>
 
-  <select id="selectProject" parameterType="String" resultMap="resourceResultMap">
+  <select id="selectProjectByUuid" parameterType="String" resultMap="resourceResultMap">
     select * from projects
     where uuid = #{uuid,jdbcType=VARCHAR}
   </select>
index ee5c0b6487522245d6cc097e6d7898fa2ee2a4c6..22165c43d746905da7b2293e25637f97e3afb44b 100644 (file)
@@ -125,9 +125,9 @@ public class ReportSubmitter {
     if (!Qualifiers.PROJECT.equals(component.qualifier()) || !Scopes.PROJECT.equals(component.scope())) {
       errors.add(format("Component '%s' is not a project", rawProjectKey));
     }
-    if (!project.get().projectUuid().equals(project.get().uuid())) {
+    if (!component.projectUuid().equals(component.uuid())) {
       // Project key is already used as a module of another project
-      ComponentDto anotherBaseProject = dbClient.componentDao().selectOrFailByUuid(dbSession, project.get().projectUuid());
+      ComponentDto anotherBaseProject = dbClient.componentDao().selectOrFailByUuid(dbSession, component.projectUuid());
       errors.add(format("The project '%s' is already defined in SonarQube but as a module of project '%s'. "
         + "If you really want to stop directly analysing project '%s', please first delete it from SonarQube and then relaunch the analysis of project '%s'.",
         rawProjectKey, anotherBaseProject.getKey(), anotherBaseProject.getKey(), rawProjectKey));
index 2908be1dfbc94dfb66c4a395581e45568660033e..16bd63ab8376f5797be5cf558e90551d0d12b8a4 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.server.project.ws;
 
+import com.google.common.base.Optional;
 import org.sonar.api.server.ws.Change;
 import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.Response;
@@ -26,12 +27,8 @@ import org.sonar.api.server.ws.WebService;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.component.ComponentDto;
-import org.sonar.server.component.ComponentFinder;
-import org.sonar.server.component.ComponentFinder.ParamNames;
 import org.sonar.server.component.ComponentService;
-
-import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
+import org.sonar.server.exceptions.NotFoundException;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static org.sonar.core.util.Uuids.UUID_EXAMPLE_01;
@@ -42,12 +39,10 @@ import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_TO;
 
 public class UpdateKeyAction implements ProjectsWsAction {
   private final DbClient dbClient;
-  private final ComponentFinder componentFinder;
   private final ComponentService componentService;
 
-  public UpdateKeyAction(DbClient dbClient, ComponentFinder componentFinder, ComponentService componentService) {
+  public UpdateKeyAction(DbClient dbClient, ComponentService componentService) {
     this.dbClient = dbClient;
-    this.componentFinder = componentFinder;
     this.componentService = componentService;
   }
 
@@ -59,19 +54,20 @@ public class UpdateKeyAction implements ProjectsWsAction {
   public WebService.NewAction doDefine(WebService.NewController context) {
     WebService.NewAction action = context.createAction(ACTION_UPDATE_KEY)
       .setDescription("Update a project or module key and all its sub-components keys.<br>" +
-        "Either '%s' or '%s' must be provided.<br> " +
-        "Requires one of the following permissions: " +
-        "<ul>" +
-        "<li>'Administer System'</li>" +
-        "<li>'Administer' rights on the specified project</li>" +
-        "</ul>",
+          "Either '%s' or '%s' must be provided.<br> " +
+          "Requires one of the following permissions: " +
+          "<ul>" +
+          "<li>'Administer System'</li>" +
+          "<li>'Administer' rights on the specified project</li>" +
+          "</ul>",
         PARAM_FROM, PARAM_PROJECT_ID)
       .setSince("6.1")
       .setPost(true)
       .setHandler(this);
 
     action.setChangelog(
-      new Change("6.4", "Move from api/components/update_key to api/projects/update_key"));
+      new Change("6.4", "Move from api/components/update_key to api/projects/update_key"),
+      new Change("7.1", "Ability to update key of a disabled module"));
 
     action.createParam(PARAM_PROJECT_ID)
       .setDescription("Project or module id")
@@ -95,82 +91,24 @@ public class UpdateKeyAction implements ProjectsWsAction {
 
   @Override
   public void handle(Request request, Response response) throws Exception {
-    doHandle(toWsRequest(request));
-    response.noContent();
-  }
+    String uuid = request.param(PARAM_PROJECT_ID);
+    String key = request.param(PARAM_FROM);
+    String newKey = request.mandatoryParam(PARAM_TO);
+    checkArgument(uuid != null ^ key != null, "Either '%s' or '%s' must be provided", PARAM_PROJECT_ID, PARAM_FROM);
 
-  private void doHandle(UpdateKeyRequest request) {
     try (DbSession dbSession = dbClient.openSession(false)) {
-      ComponentDto projectOrModule = componentFinder.getByUuidOrKey(dbSession, request.getId(), request.getKey(), ParamNames.PROJECT_ID_AND_FROM);
-      componentService.updateKey(dbSession, projectOrModule, request.getNewKey());
-    }
-  }
-
-  private static UpdateKeyRequest toWsRequest(Request request) {
-    return UpdateKeyRequest.builder()
-      .setId(request.param(PARAM_PROJECT_ID))
-      .setKey(request.param(PARAM_FROM))
-      .setNewKey(request.mandatoryParam(PARAM_TO))
-      .build();
-  }
-
-  private static class UpdateKeyRequest {
-    private final String id;
-    private final String key;
-    private final String newKey;
-
-    public UpdateKeyRequest(Builder builder) {
-      this.id = builder.id;
-      this.key = builder.key;
-      this.newKey = builder.newKey;
-    }
-
-    @CheckForNull
-    public String getId() {
-      return id;
-    }
-
-    @CheckForNull
-    public String getKey() {
-      return key;
-    }
-
-    public String getNewKey() {
-      return newKey;
-    }
-
-    public static Builder builder() {
-      return new Builder();
-    }
-  }
-
-  private static class Builder {
-    private String id;
-    private String key;
-    private String newKey;
-
-    private Builder() {
-      // enforce method constructor
-    }
-
-    public Builder setId(@Nullable String id) {
-      this.id = id;
-      return this;
-    }
-
-    public Builder setKey(@Nullable String key) {
-      this.key = key;
-      return this;
-    }
-
-    public Builder setNewKey(String newKey) {
-      this.newKey = newKey;
-      return this;
-    }
-
-    public UpdateKeyRequest build() {
-      checkArgument(newKey != null && !newKey.isEmpty(), "The new key must not be empty");
-      return new UpdateKeyRequest(this);
+      Optional<ComponentDto> component;
+      if (uuid != null) {
+        component = dbClient.componentDao().selectByUuid(dbSession, uuid);
+      } else {
+        component = dbClient.componentDao().selectByKey(dbSession, key);
+      }
+      if (!component.isPresent() || component.get().getMainBranchProjectUuid() != null) {
+        throw new NotFoundException("Component not found");
+      }
+
+      componentService.updateKey(dbSession, component.get(), newKey);
     }
+    response.noContent();
   }
 }
index 91b7df17ec7dde0b593af2b95a437bee4b7676ec..bca7c9f01a1c8fd5036ef404d6959593754d94a8 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.server.project.ws;
 
+import com.google.common.base.Optional;
 import javax.annotation.Nullable;
 import org.junit.Rule;
 import org.junit.Test;
@@ -26,23 +27,21 @@ import org.junit.rules.ExpectedException;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.api.server.ws.WebService.Param;
 import org.sonar.api.utils.System2;
+import org.sonar.api.web.UserRole;
 import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
-import org.sonar.db.component.ComponentDbTester;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.ComponentTesting;
 import org.sonar.server.component.ComponentService;
-import org.sonar.server.component.TestComponentFinder;
+import org.sonar.server.es.ProjectIndexers;
+import org.sonar.server.es.ProjectIndexersImpl;
+import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.tester.UserSessionRule;
 import org.sonar.server.ws.TestRequest;
 import org.sonar.server.ws.WsActionTester;
 
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
 import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_FROM;
 import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_PROJECT_ID;
 import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_TO;
@@ -54,55 +53,104 @@ public class UpdateKeyActionTest {
   public ExpectedException expectedException = ExpectedException.none();
   @Rule
   public DbTester db = DbTester.create(System2.INSTANCE);
+  @Rule
+  public UserSessionRule userSessionRule = UserSessionRule.standalone();
+  private DbClient dbClient = db.getDbClient();
+  private ProjectIndexers projectIndexers = new ProjectIndexersImpl();
+  private ComponentService componentService = new ComponentService(dbClient, userSessionRule, projectIndexers);
+  private WsActionTester ws = new WsActionTester(new UpdateKeyAction(dbClient, componentService));
 
-  ComponentDbTester componentDb = new ComponentDbTester(db);
-  DbClient dbClient = db.getDbClient();
+  @Test
+  public void update_key_of_project_referenced_by_its_key() {
+    ComponentDto project = insertProject();
+    userSessionRule.addProjectPermission(UserRole.ADMIN, project);
 
-  ComponentService componentService = mock(ComponentService.class);
+    callByKey(project.getKey(), ANOTHER_KEY);
 
-  WsActionTester ws = new WsActionTester(new org.sonar.server.project.ws.UpdateKeyAction(dbClient, TestComponentFinder.from(db), componentService));
+    assertThat(selectByKey(project.getKey()).isPresent()).isFalse();
+    assertThat(selectByKey(ANOTHER_KEY).get().uuid()).isEqualTo(project.uuid());
+  }
 
   @Test
-  public void call_by_key() {
+  public void update_key_of_project_referenced_by_its_uuid() {
     ComponentDto project = insertProject();
+    userSessionRule.addProjectPermission(UserRole.ADMIN, project);
 
-    callByKey(project.getDbKey(), ANOTHER_KEY);
+    callByUuid(project.uuid(), ANOTHER_KEY);
 
-    assertCallComponentService(ANOTHER_KEY);
+    assertThat(selectByKey(project.getKey()).isPresent()).isFalse();
+    assertThat(selectByKey(ANOTHER_KEY).get().uuid()).isEqualTo(project.uuid());
   }
 
   @Test
-  public void call_by_uuid() {
+  public void update_key_of_module_referenced_by_its_uuid() {
     ComponentDto project = insertProject();
+    ComponentDto module = db.components().insertComponent(ComponentTesting.newModuleDto(project));
+    userSessionRule.addProjectPermission(UserRole.ADMIN, project);
 
-    callByUuid(project.uuid(), ANOTHER_KEY);
+    callByUuid(module.uuid(), ANOTHER_KEY);
 
-    assertCallComponentService(ANOTHER_KEY);
+    assertThat(selectByKey(project.getKey()).isPresent()).isTrue();
+    assertThat(selectByKey(module.getKey()).isPresent()).isFalse();
+    assertThat(selectByKey(ANOTHER_KEY).get().uuid()).isEqualTo(module.uuid());
   }
 
   @Test
-  public void fail_if_new_key_is_not_provided() {
-    expectedException.expect(IllegalArgumentException.class);
+  public void update_key_of_disabled_module() {
+    ComponentDto project = insertProject();
+    ComponentDto module = db.components().insertComponent(ComponentTesting.newModuleDto(project).setEnabled(false));
+    userSessionRule.addProjectPermission(UserRole.ADMIN, project);
+
+    callByKey(module.getKey(), ANOTHER_KEY);
+
+    assertThat(selectByKey(project.getKey()).isPresent()).isTrue();
+    assertThat(selectByKey(module.getKey()).isPresent()).isFalse();
+    ComponentDto loadedModule = selectByKey(ANOTHER_KEY).get();
+    assertThat(loadedModule.uuid()).isEqualTo(module.uuid());
+    assertThat(loadedModule.isEnabled()).isFalse();
+  }
+
+
+  @Test
+  public void fail_if_not_authorized() {
+    ComponentDto project = insertProject();
+    userSessionRule.addProjectPermission(UserRole.USER, project);
+
+    expectedException.expect(ForbiddenException.class);
+    expectedException.expectMessage("Insufficient privileges");
+
+    callByKey(project.getKey(), ANOTHER_KEY);
+  }
 
+  @Test
+  public void fail_if_new_key_is_not_provided() {
     ComponentDto project = insertProject();
+    userSessionRule.addProjectPermission(UserRole.ADMIN, project);
+
+    expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage("The 'to' parameter is missing");
 
-    callByKey(project.getDbKey(), null);
+    callByKey(project.getKey(), null);
   }
 
   @Test
   public void fail_if_uuid_nor_key_provided() {
     expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage("Either 'projectId' or 'from' must be provided");
 
     call(null, null, ANOTHER_KEY);
   }
 
   @Test
-  public void fail_if_uuid_and_key_provided() {
+  public void fail_if_both_uuid_and_key_provided() {
+    ComponentDto project = insertProject();
+    userSessionRule.addProjectPermission(UserRole.ADMIN, project);
+
     expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage("Either 'projectId' or 'from' must be provided");
 
-    ComponentDto project = insertProject();
 
-    call(project.uuid(), project.getDbKey(), ANOTHER_KEY);
+    call(project.uuid(), project.getKey(), ANOTHER_KEY);
   }
 
   @Test
@@ -116,9 +164,10 @@ public class UpdateKeyActionTest {
   public void fail_when_using_branch_db_key() throws Exception {
     ComponentDto project = db.components().insertMainBranch();
     ComponentDto branch = db.components().insertProjectBranch(project);
+    userSessionRule.addProjectPermission(UserRole.ADMIN, project);
 
     expectedException.expect(NotFoundException.class);
-    expectedException.expectMessage(String.format("Component key '%s' not found", branch.getDbKey()));
+    expectedException.expectMessage("Component not found");
 
     callByKey(branch.getDbKey(), ANOTHER_KEY);
   }
@@ -127,9 +176,10 @@ public class UpdateKeyActionTest {
   public void fail_when_using_branch_uuid() {
     ComponentDto project = db.components().insertMainBranch();
     ComponentDto branch = db.components().insertProjectBranch(project);
+    userSessionRule.addProjectPermission(UserRole.ADMIN, project);
 
     expectedException.expect(NotFoundException.class);
-    expectedException.expectMessage(String.format("Component id '%s' not found", branch.uuid()));
+    expectedException.expectMessage("Component not found");
 
     callByUuid(branch.uuid(), ANOTHER_KEY);
   }
@@ -141,26 +191,23 @@ public class UpdateKeyActionTest {
     assertThat(definition.since()).isEqualTo("6.1");
     assertThat(definition.isPost()).isTrue();
     assertThat(definition.key()).isEqualTo("update_key");
+    assertThat(definition.changelog()).hasSize(2);
     assertThat(definition.params())
       .hasSize(3)
       .extracting(Param::key)
       .containsOnlyOnce("projectId", "from", "to");
   }
 
-  private void assertCallComponentService(@Nullable String newKey) {
-    verify(componentService).updateKey(any(DbSession.class), any(ComponentDto.class), eq(newKey));
-  }
-
   private ComponentDto insertProject() {
-    return componentDb.insertComponent(ComponentTesting.newPrivateProjectDto(db.organizations().insert()));
+    return db.components().insertComponent(ComponentTesting.newPrivateProjectDto(db.organizations().insert()));
   }
 
-  private String callByUuid(@Nullable String uuid, @Nullable String newKey) {
-    return call(uuid, null, newKey);
+  private void callByUuid(@Nullable String uuid, @Nullable String newKey) {
+    call(uuid, null, newKey);
   }
 
-  private String callByKey(@Nullable String key, @Nullable String newKey) {
-    return call(null, key, newKey);
+  private void callByKey(@Nullable String key, @Nullable String newKey) {
+    call(null, key, newKey);
   }
 
   private String call(@Nullable String uuid, @Nullable String key, @Nullable String newKey) {
@@ -178,4 +225,8 @@ public class UpdateKeyActionTest {
 
     return request.execute().getInput();
   }
+
+  private Optional<ComponentDto> selectByKey(String key) {
+    return db.getDbClient().componentDao().selectByKey(db.getSession(), key);
+  }
 }
index 0384ce9562b2d0ac85b74a83431645aa6ff89ae4..9d08069bc36800eaa1a455a9cecf0e1c17f51c2f 100644 (file)
@@ -31,15 +31,14 @@ import static util.ItUtils.xooPlugin;
 @Suite.SuiteClasses({
   BranchTest.class,
   CodePageTest.class,
-  ComponentsWsTest.class
+  ComponentsWsTest.class,
+  ModuleTest.class
 })
 public class ComponentSuite {
 
   @ClassRule
   public static final Orchestrator ORCHESTRATOR = ItUtils.newOrchestratorBuilder()
-
     .addPlugin(xooPlugin())
-
     .build();
 
 }
diff --git a/tests/src/test/java/org/sonarqube/tests/component/ModuleTest.java b/tests/src/test/java/org/sonarqube/tests/component/ModuleTest.java
new file mode 100644 (file)
index 0000000..ffc3f4e
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonarqube.tests.component;
+
+import com.sonar.orchestrator.Orchestrator;
+import com.sonar.orchestrator.build.BuildFailureException;
+import com.sonar.orchestrator.build.SonarScanner;
+import java.io.File;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonarqube.qa.util.Tester;
+import org.sonarqube.ws.Organizations;
+import org.sonarqube.ws.client.components.ShowRequest;
+import org.sonarqube.ws.client.components.TreeRequest;
+import org.sonarqube.ws.client.projects.UpdateKeyRequest;
+import util.XooProjectBuilder;
+
+import static java.util.Arrays.asList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.fail;
+
+public class ModuleTest {
+
+  private static final String PROJECT_KEY = "sample";
+
+  @ClassRule
+  public static Orchestrator orchestrator = ComponentSuite.ORCHESTRATOR;
+
+  @Rule
+  public TemporaryFolder temp = new TemporaryFolder();
+  @Rule
+  public Tester tester = new Tester(orchestrator);
+
+  /**
+   * SONAR-10536
+   */
+  @Test
+  public void analyze_disabled_module_as_a_new_project() throws Exception {
+    Organizations.Organization organization = tester.organizations().generate();
+    String projectKey = PROJECT_KEY;
+    String moduleKey = projectKey + ":module_a";
+
+    // analyze project "sample" with module "sample:module_a"
+    File projectWithModule = new XooProjectBuilder(projectKey)
+      .addModules("module_a")
+      .build(temp.newFolder());
+    analyze(organization, projectWithModule);
+    assertThat(tester.projects().exists(moduleKey)).isTrue();
+    assertThat(countFilesInProject(projectKey)).isEqualTo(2 /* 1 file in project and 1 file in module */);
+
+    // analyze project "sample" without module "sample:module_a". The latter
+    // is considered as disabled
+    File projectWithoutModule = new XooProjectBuilder(projectKey)
+      .build(temp.newFolder());
+    analyze(organization, projectWithoutModule);
+    assertThat(tester.projects().exists(moduleKey)).isFalse();
+    assertThat(countFilesInProject(projectKey)).isEqualTo(1 /* 1 file in project */);
+
+    // analyze module_a as a project
+    File moduleAsProject = new XooProjectBuilder(moduleKey)
+      .build(temp.newFolder());
+    try {
+      analyze(organization, moduleAsProject);
+      fail();
+    } catch (BuildFailureException e) {
+      assertThat(e.getResult().getLogs()).contains("The project '" + moduleKey + "' is already defined in SonarQube but as a module of project '" + projectKey + "'");
+      assertThat(tester.projects().exists(moduleKey)).isFalse();
+    }
+
+    // the only workaround is to rename the key of the disabled module
+    updateModuleKey(moduleKey, moduleKey + "_old");
+
+    // module_a can now be analyzed as a project
+    analyze(organization, moduleAsProject);
+    assertThat(tester.projects().exists(moduleKey)).isTrue();
+    assertThat(countFilesInProject(moduleKey)).isEqualTo(1);
+    assertThat(tester.wsClient().components().show(new ShowRequest().setComponent(moduleKey)).getComponent().getQualifier()).isEqualTo("TRK");
+  }
+
+  private void analyze(Organizations.Organization organization, File projectDir) {
+    orchestrator.executeBuild(SonarScanner.create(projectDir,
+      "sonar.organization", organization.getKey(),
+      "sonar.login", "admin", "sonar.password", "admin"));
+  }
+
+  private int countFilesInProject(String projectKey) {
+    TreeRequest request = new TreeRequest().setComponent(projectKey).setQualifiers(asList("FIL"));
+    return tester.wsClient().components().tree(request).getComponentsCount();
+  }
+
+  private void updateModuleKey(String fromKey, String toKey) {
+    tester.wsClient().projects().updateKey(new UpdateKeyRequest()
+      .setFrom(fromKey)
+      .setTo(toKey));
+  }
+}
index d4f004dd9f14327c62ceece94eec89f1b613c051..b1486e5f5395e4ba18967085935b8f243347d981 100644 (file)
@@ -23,16 +23,11 @@ import com.sonar.orchestrator.Orchestrator;
 import com.sonar.orchestrator.build.SonarScanner;
 import java.io.File;
 import java.io.IOException;
-import java.io.OutputStream;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
-import java.util.Properties;
 import java.util.stream.Collectors;
 import javax.annotation.CheckForNull;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.lang.StringUtils;
 import org.junit.After;
 import org.junit.ClassRule;
 import org.junit.Rule;
@@ -54,6 +49,7 @@ import org.sonarqube.ws.client.components.TreeRequest;
 import org.sonarqube.ws.client.projects.BulkUpdateKeyRequest;
 import org.sonarqube.ws.client.projects.UpdateKeyRequest;
 import util.ItUtils;
+import util.XooProjectBuilder;
 
 import static java.util.Arrays.asList;
 import static org.assertj.core.api.Assertions.assertThat;
@@ -398,58 +394,4 @@ public class ProjectKeyUpdateTest {
       .collect(Collectors.toList());
   }
 
-  private static class XooProjectBuilder {
-    private final String key;
-    private final List<String> moduleKeys = new ArrayList<>();
-    private int filesPerModule = 1;
-
-    XooProjectBuilder(String projectKey) {
-      this.key = projectKey;
-    }
-
-    XooProjectBuilder addModules(String key, String... otherKeys) {
-      this.moduleKeys.add(key);
-      this.moduleKeys.addAll(asList(otherKeys));
-      return this;
-    }
-
-    XooProjectBuilder setFilesPerModule(int i) {
-      this.filesPerModule = i;
-      return this;
-    }
-
-    File build(File dir) {
-      for (String moduleKey : moduleKeys) {
-        generateModule(moduleKey, new File(dir, moduleKey), new Properties());
-      }
-      Properties additionalProps = new Properties();
-      additionalProps.setProperty("sonar.modules", StringUtils.join(moduleKeys, ","));
-      generateModule(key, dir, additionalProps);
-      return dir;
-    }
-
-    private void generateModule(String key, File dir, Properties additionalProps) {
-      try {
-        File sourceDir = new File(dir, "src");
-        FileUtils.forceMkdir(sourceDir);
-        for (int i = 0; i < filesPerModule; i++) {
-          File sourceFile = new File(sourceDir, "File" + i + ".xoo");
-          FileUtils.write(sourceFile, "content of " + sourceFile.getName());
-        }
-        Properties props = new Properties();
-        props.setProperty("sonar.projectKey", key);
-        props.setProperty("sonar.projectName", key);
-        props.setProperty("sonar.projectVersion", "1.0");
-        props.setProperty("sonar.sources", sourceDir.getName());
-        props.putAll(additionalProps);
-        File propsFile = new File(dir, "sonar-project.properties");
-        try (OutputStream output = FileUtils.openOutputStream(propsFile)) {
-          props.store(output, "generated");
-        }
-      } catch (IOException e) {
-        throw new IllegalStateException(e);
-      }
-    }
-  }
-
 }
diff --git a/tests/src/test/java/util/XooProjectBuilder.java b/tests/src/test/java/util/XooProjectBuilder.java
new file mode 100644 (file)
index 0000000..0eaf934
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package util;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang.StringUtils;
+
+import static java.util.Arrays.asList;
+
+public class XooProjectBuilder {
+  private final String key;
+  private final List<String> moduleKeys = new ArrayList<>();
+  private int filesPerModule = 1;
+
+  public XooProjectBuilder(String projectKey) {
+    this.key = projectKey;
+  }
+
+  public XooProjectBuilder addModules(String key, String... otherKeys) {
+    this.moduleKeys.add(key);
+    this.moduleKeys.addAll(asList(otherKeys));
+    return this;
+  }
+
+  public XooProjectBuilder setFilesPerModule(int i) {
+    this.filesPerModule = i;
+    return this;
+  }
+
+  public File build(File dir) {
+    for (String moduleKey : moduleKeys) {
+      generateModule(moduleKey, new File(dir, moduleKey), new Properties());
+    }
+    Properties additionalProps = new Properties();
+    additionalProps.setProperty("sonar.modules", StringUtils.join(moduleKeys, ","));
+    generateModule(key, dir, additionalProps);
+    return dir;
+  }
+
+  private void generateModule(String key, File dir, Properties additionalProps) {
+    try {
+      File sourceDir = new File(dir, "src");
+      FileUtils.forceMkdir(sourceDir);
+      for (int i = 0; i < filesPerModule; i++) {
+        File sourceFile = new File(sourceDir, "File" + i + ".xoo");
+        FileUtils.write(sourceFile, "content of " + sourceFile.getName());
+      }
+      Properties props = new Properties();
+      props.setProperty("sonar.projectKey", key);
+      props.setProperty("sonar.projectName", key);
+      props.setProperty("sonar.projectVersion", "1.0");
+      props.setProperty("sonar.sources", sourceDir.getName());
+      props.putAll(additionalProps);
+      File propsFile = new File(dir, "sonar-project.properties");
+      try (OutputStream output = FileUtils.openOutputStream(propsFile)) {
+        props.store(output, "generated");
+      }
+    } catch (IOException e) {
+      throw new IllegalStateException(e);
+    }
+  }
+}