]> source.dussan.org Git - sonarqube.git/commitdiff
Merge commit 'a56e60d168802ff9dc23ffec978bb412fd501f97'
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Tue, 13 Sep 2016 14:40:42 +0000 (16:40 +0200)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Tue, 13 Sep 2016 14:40:42 +0000 (16:40 +0200)
19 files changed:
1  2 
server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ProjectAssociationParameters.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfilesWsModule.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/SearchAction.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/BackupActionTest.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ChangelogActionTest.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/DeleteActionTest.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ExportActionTest.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ProjectsActionTest.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfilesWsModuleTest.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfilesWsTest.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/RenameActionTest.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/SearchActionTest.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/SetDefaultActionTest.java
sonar-db/src/main/java/org/sonar/db/qualityprofile/QualityProfileDao.java
sonar-db/src/main/java/org/sonar/db/qualityprofile/QualityProfileMapper.java
sonar-db/src/main/resources/org/sonar/db/qualityprofile/QualityProfileMapper.xml
sonar-db/src/test/java/org/sonar/db/qualityprofile/QualityProfileDaoTest.java
sonar-db/src/test/java/org/sonar/db/qualityprofile/QualityProfileDbTester.java

index 156110ecde094731215a088e9a39af06e2a54df7,003b90789c4b4bcfe64791f837fd7783fa37ce1f..392a091ed085960afd15b97a094a5e32a39263a9
@@@ -390,6 -375,8 +349,7 @@@ public class PlatformLevel4 extends Pla
        QProfileCopier.class,
        QProfileBackuper.class,
        QProfileReset.class,
 -      RubyQProfileActivityService.class,
+       QProfilesWsModule.class,
  
        // rule
        RuleIndexDefinition.class,
index 0000000000000000000000000000000000000000,e78d91c1b32fc22a70f3042ae41b9b61208a1c5e..4f423cd14e11c85d17d9140594ea6968bda41b05
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,61 +1,61 @@@
 -import org.sonar.server.component.ws.LanguageParamUtils;
+ /*
+  * SonarQube
+  * Copyright (C) 2009-2016 SonarSource SA
+  * mailto:contact 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.sonar.server.qualityprofile.ws;
+ import org.sonar.api.resources.Languages;
+ import org.sonar.api.server.ws.WebService;
++import org.sonar.server.util.LanguageParamUtils;
+ import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
+ import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_LANGUAGE;
+ import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_PROFILE_KEY;
+ import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_PROFILE_NAME;
+ import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_PROJECT_KEY;
+ import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_PROJECT_UUID;
+ public class ProjectAssociationParameters {
+   private final Languages languages;
+   public ProjectAssociationParameters(Languages languages) {
+     this.languages = languages;
+   }
+   void addParameters(WebService.NewAction action) {
+     action.setPost(true);
+     action.createParam(PARAM_PROJECT_UUID)
+       .setDescription("A project UUID. Either this parameter, or projectKey must be set.")
+       .setExampleValue("69e57151-be0d-4157-adff-c06741d88879");
+     action.createParam(PARAM_PROJECT_KEY)
+       .setDescription("A project key. Either this parameter, or projectUuid must be set.")
+       .setExampleValue(KEY_PROJECT_EXAMPLE_001);
+     action.createParam(PARAM_PROFILE_KEY)
+       .setDescription("A quality profile key. Either this parameter, or a combination of profileName + language must be set.")
+       .setExampleValue("sonar-way-java-12345");
+     action.createParam(PARAM_PROFILE_NAME)
+       .setDescription("A quality profile name. If this parameter is set, profileKey must not be set and language must be set to disambiguate.")
+       .setExampleValue("Soanr way");
+     action.createParam(PARAM_LANGUAGE)
+       .setDescription("A quality profile language. If this parameter is set, profileKey must not be set and profileName must be set to disambiguate.")
+       .setPossibleValues(LanguageParamUtils.getLanguageKeys(languages))
+       .setExampleValue("js");
+   }
+ }
index 0000000000000000000000000000000000000000,0c85730db8cd51abb2d8aa1277873bdfabf6cdc4..d1a3bf7bbc22030f2debe34aaadea0cfd02d6550
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,54 +1,55 @@@
+ /*
+  * SonarQube
+  * Copyright (C) 2009-2016 SonarSource SA
+  * mailto:contact 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.sonar.server.qualityprofile.ws;
+ import org.sonar.core.platform.Module;
+ public class QProfilesWsModule extends Module {
+   @Override
+   protected void configureModule() {
+     add(
+       AddProjectAction.class,
+       BackupAction.class,
+       BulkRuleActivationActions.class,
+       CompareAction.class,
+       CopyAction.class,
+       ChangelogAction.class,
++      ChangelogLoader.class,
+       ChangeParentAction.class,
+       CreateAction.class,
+       DeleteAction.class,
+       ExportAction.class,
+       ExportersAction.class,
+       ImportersAction.class,
+       InheritanceAction.class,
+       QProfilesWs.class,
+       ProjectAssociationFinder.class,
+       ProjectAssociationParameters.class,
+       ProjectsAction.class,
+       RenameAction.class,
+       RemoveProjectAction.class,
+       RestoreAction.class,
+       RestoreBuiltInAction.class,
+       RuleActivationActions.class,
+       SearchAction.class,
+       SetDefaultAction.class
+       );
+   }
+ }
index 10732fc1dccfbf3d0feaa0d91f6e471e4e1b820e,4ce55995985039c058f37e753c6045e4592db509..57d1cd18caef244d62ba66944f61ea67d4f10d4a
@@@ -33,10 -35,14 +33,11 @@@ import org.sonarqube.ws.QualityProfiles
  import org.sonarqube.ws.QualityProfiles.SearchWsResponse.QualityProfile;
  import org.sonarqube.ws.client.qualityprofile.SearchWsRequest;
  
 -import static com.google.common.collect.Maps.uniqueIndex;
  import static java.lang.String.format;
 +import static java.util.function.Function.identity;
 +import static org.sonar.api.utils.DateUtils.formatDateTime;
  import static org.sonar.server.ws.WsUtils.writeProtobuf;
 -import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.ACTION_SEARCH;
 -import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_DEFAULTS;
 -import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_LANGUAGE;
 -import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_PROFILE_NAME;
 -import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_PROJECT_KEY;
++import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.*;
  
  public class SearchAction implements QProfileWsAction {
  
@@@ -76,7 -77,7 +72,7 @@@
      action
        .createParam(PARAM_DEFAULTS)
        .setDescription(format("Return the quality profile marked as default for each language. " +
--        "If provided, then the parameters '%s', '%s' must not be set.",
++          "If provided, then the parameters '%s', '%s' must not be set.",
          PARAM_LANGUAGE, PARAM_PROJECT_KEY))
        .setDefaultValue(false)
        .setBooleanPossibleValues();
index e5deda778c2db30ab4122816751326423618a397,8b90e445abb366f53d6008ec8cf006b7c0eeced6..a1a4a2199cb81a804138485b4a89130b46158265
@@@ -46,15 -47,24 +46,14 @@@ import static org.mockito.Mockito.when
  @RunWith(MockitoJUnitRunner.class)
  public class BackupActionTest {
  
 -  @Rule
 -  public DbTester db = DbTester.create(System2.INSTANCE);
 +  private static final String SOME_PROFILE_KEY = "polop-palap-xoo-12345";
  
 -  // TODO Replace with proper DbTester + EsTester medium test once DaoV2 is removed
 -  @Mock
 -  private QProfileBackuper backuper;
 -
 -  private WsTester tester;
 -
 -  @Before
 -  public void setUp() {
 -    DbClient dbClient = new DbClient(db.database(), db.myBatis());
 -
 -    tester = new WsTester(new QProfilesWs(
 -      mock(RuleActivationActions.class),
 -      mock(BulkRuleActivationActions.class),
 -      new BackupAction(backuper, dbClient, new QProfileFactory(dbClient), LanguageTesting.newLanguages("xoo"))));
 -  }
 +  private QProfileBackuper backuper = mock(QProfileBackuper.class);
 +  private QProfileFactory profileFactory = mock(QProfileFactory.class);
 +  private WsTester tester = new WsTester(new QProfilesWs(
 +    mock(RuleActivationActions.class),
 +    mock(BulkRuleActivationActions.class),
-     mock(ProjectAssociationActions.class),
 +    new BackupAction(mock(DbClient.class), backuper, profileFactory, LanguageTesting.newLanguages("xoo"))));
  
    @Test
    public void backup_profile() throws Exception {
index 2b0a82c5664fcab6264b0ba514b260f5825c0c37,67acd3ed3cd1bd0eff548a7b058f4a92287f3f23..9397b7313430f2beafda14b507dfc748d9e88801
@@@ -58,8 -71,27 +58,8 @@@ public class ChangelogActionTest 
  
    @Before
    public void before() {
-     wsTester = new WsTester(new QProfilesWs(mock(RuleActivationActions.class), mock(BulkRuleActivationActions.class), mock(ProjectAssociationActions.class),
 -    dbTester.truncateTables();
 -    esTester.truncateIndices();
 -
 -    System2 system = mock(System2.class);
 -
 -    // create pre-defined rules
 -    RuleDto xooRule1 = RuleTesting.newXooX1().setSeverity("MINOR");
 -    db.ruleDao().insert(dbSession, xooRule1);
 -
 -    // create pre-defined profiles P1 and P2
 -    db.qualityProfileDao().insert(dbSession, QProfileTesting.newXooP1(), QProfileTesting.newXooP2());
 -
 -    login = "david";
 -    UserDto user = new UserDto().setLogin(login).setName("David").setEmail("dav@id.com").setCreatedAt(System.currentTimeMillis()).setUpdatedAt(System.currentTimeMillis());
 -    db.userDao().insert(dbSession, user);
 -
 -    dbSession.commit();
 -    dbSession.clearCache();
 -
+     wsTester = new WsTester(new QProfilesWs(mock(RuleActivationActions.class), mock(BulkRuleActivationActions.class),
 -      new ChangelogAction(db, new ActivityIndex(esTester.client()), new QProfileFactory(db), LanguageTesting.newLanguages("xoo"))));
 +      new ChangelogAction(changelogLoader, profileFactory, new Languages(), dbTester.getDbClient())));
    }
  
    @Test
index a54473afbec58064c821e6e13f1e9440537d0f32,523ef703292d2025abae49e9c7e3c838b791c186..fbad86f85808674122a2723113418bfae541f97c
@@@ -28,17 -27,13 +27,13 @@@ 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.ComponentDao;
  import org.sonar.db.component.ComponentDto;
  import org.sonar.db.component.ComponentTesting;
- import org.sonar.db.qualityprofile.QualityProfileDao;
  import org.sonar.db.qualityprofile.QualityProfileDto;
- import org.sonar.db.user.AuthorizationDao;
  import org.sonar.db.user.GroupRoleDto;
  import org.sonar.db.user.RoleDao;
- import org.sonar.db.user.UserDao;
  import org.sonar.db.user.UserDto;
 -import org.sonar.db.user.UserRoleDto;
 +import org.sonar.db.user.UserPermissionDto;
  import org.sonar.server.exceptions.NotFoundException;
  import org.sonar.server.qualityprofile.QProfileTesting;
  import org.sonar.server.tester.UserSessionRule;
index 0000000000000000000000000000000000000000,81f381ea9483ee593f228151769d61e09d11764b..f543e068dee37a63582a1c74b3a9458e1b5e5466
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,34 +1,34 @@@
 -    assertThat(container.size()).isEqualTo(24 + 2);
+ /*
+  * SonarQube
+  * Copyright (C) 2009-2016 SonarSource SA
+  * mailto:contact 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.sonar.server.qualityprofile.ws;
+ import org.junit.Test;
+ import org.sonar.core.platform.ComponentContainer;
+ import static org.assertj.core.api.Assertions.assertThat;
+ public class QProfilesWsModuleTest {
+   @Test
+   public void verify_count_of_added_components() {
+     ComponentContainer container = new ComponentContainer();
+     new QProfilesWsModule().configure(container);
++    assertThat(container.size()).isEqualTo(25 + 2);
+   }
+ }
index dc27773870725a39aca794458a1e69e0e3b2454c,bba366584633871da5de01c71e4f69190b7abdd2..4acd2d1ad6028052cbb89425f0920853eb2cc5b2
@@@ -54,19 -52,20 +54,21 @@@ public class QProfilesWsTest 
    public void setUp() {
      QProfileService profileService = mock(QProfileService.class);
      I18n i18n = mock(I18n.class);
 +    DbClient dbClient = mock(DbClient.class);
  
      Languages languages = LanguageTesting.newLanguages(xoo1Key, xoo2Key);
+     ProjectAssociationParameters projectAssociationParameters = new ProjectAssociationParameters(languages);
  
      ProfileImporter[] importers = createImporters(languages);
  
      controller = new WsTester(new QProfilesWs(
        new RuleActivationActions(profileService),
        new BulkRuleActivationActions(profileService, null, i18n, userSessionRule),
-       new ProjectAssociationActions(null, null, null, languages, userSessionRule),
+       new AddProjectAction(projectAssociationParameters, null, null, null),
+       new RemoveProjectAction(projectAssociationParameters, null, null, null),
        new CreateAction(null, null, null, languages, importers, userSessionRule),
        new ImportersAction(importers),
 -      new RestoreBuiltInAction(null),
 +      new RestoreBuiltInAction(null, languages),
        new SearchAction(null, languages),
        new SetDefaultAction(languages, null, null, userSessionRule),
        new ProjectsAction(null, userSessionRule),
index 950a0afb610f03e672a48be6f25d8febb04ea340,9421eb153035991c0bd5d377d862ebebae2c7a7f..6a90536fd2c27ea0fc2f7dc118c95a74ad5fc474
@@@ -58,9 -58,9 +58,8 @@@ public class RenameActionTest 
  
    private DbSession session;
  
    @Before
    public void setUp() {
 -    dbTester.truncateTables();
      qualityProfileDao = new QualityProfileDao(dbTester.myBatis(), mock(System2.class));
      dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), qualityProfileDao);
      session = dbClient.openSession(false);
index 9b430fb0dbd63b65d2259f61a607285af9cb4707,76a4488149618160dd04d8699257c286adaee443..2c3dc3b541492c00d115596ae553182273d3db0e
@@@ -42,27 -39,18 +42,25 @@@ import org.sonar.db.qualityprofile.Qual
  import org.sonar.server.component.ComponentFinder;
  import org.sonar.server.language.LanguageTesting;
  import org.sonar.server.qualityprofile.QProfileFactory;
 -import org.sonar.server.qualityprofile.QProfileLoader;
  import org.sonar.server.qualityprofile.QProfileLookup;
 +import org.sonar.server.qualityprofile.index.ActiveRuleIndex;
 +import org.sonar.server.ws.TestRequest;
  import org.sonar.server.ws.WsActionTester;
 +import org.sonarqube.ws.MediaTypes;
 +import org.sonarqube.ws.QualityProfiles.SearchWsResponse;
 +import org.sonarqube.ws.QualityProfiles.SearchWsResponse.QualityProfile;
  
 +import static com.google.common.base.Throwables.propagate;
  import static org.assertj.core.api.Assertions.assertThat;
 +import static org.assertj.core.api.Assertions.tuple;
  import static org.mockito.Mockito.mock;
  import static org.mockito.Mockito.when;
 +import static org.sonar.api.utils.DateUtils.formatDateTime;
 +import static org.sonar.api.utils.DateUtils.parseDateTime;
  import static org.sonar.db.component.ComponentTesting.newProjectDto;
- import static org.sonar.server.qualityprofile.ws.SearchAction.PARAM_DEFAULTS;
- import static org.sonar.server.qualityprofile.ws.SearchAction.PARAM_PROFILE_NAME;
- import static org.sonar.server.qualityprofile.ws.SearchAction.PARAM_PROJECT_KEY;
 +import static org.sonar.db.qualityprofile.QualityProfileTesting.newQualityProfileDto;
  import static org.sonar.test.JsonAssert.assertJson;
 -import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_DEFAULTS;
 -import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_PROFILE_NAME;
 -import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_PROJECT_KEY;
++import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.*;
  
  public class SearchActionTest {
  
index da3bfd252d4a46cb2255cbd91aadc865868281cd,acbfc3772c1ed7848cf53188b0e783d03c58d75d..bd46312f1f9f90a05fbb7b16738a74c75a02671f
@@@ -30,17 -30,14 +30,18 @@@ 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 static com.google.common.collect.ImmutableList.of;
 +import static com.google.common.collect.Lists.newArrayList;
 +import static java.util.Collections.emptyList;
  import static java.util.Collections.singletonList;
  import static org.assertj.core.api.Assertions.assertThat;
 +import static org.assertj.core.api.Assertions.tuple;
  import static org.mockito.Mockito.mock;
  import static org.mockito.Mockito.when;
 -import static org.sonar.db.qualityprofile.QualityProfileDto.createFor;
 +import static org.sonar.db.component.ComponentTesting.newProjectDto;
 +import static org.sonar.db.qualityprofile.QualityProfileTesting.newQualityProfileDto;
  
  public class QualityProfileDaoTest {
  
      assertThat(underTest.selectByProjectAndLanguages(dbTester.getSession(), "unknown", singletonList("java"))).isEmpty();
    }
  
 -    ComponentDto project = componentDb.insertComponent(ComponentTesting.newProjectDto());
 +  @Test
 +  public void selectByKeys() {
 +    qualityProfileDb.insertQualityProfiles(newQualityProfileDto().setKey("qp-key-1"), newQualityProfileDto().setKee("qp-key-2"), newQualityProfileDto().setKee("qp-key-3"));
 +
 +    assertThat(underTest.selectOrFailByKey(dbSession, "qp-key-1")).isNotNull();
 +    assertThat(underTest.selectByKey(dbSession, "qp-key-1")).isNotNull();
 +    assertThat(underTest.selectByKey(dbSession, "qp-key-42")).isNull();
 +    assertThat(underTest.selectByKeys(dbSession, newArrayList("qp-key-1", "qp-key-3", "qp-key-42")))
 +      .hasSize(2)
 +      .extracting(QualityProfileDto::getKey).containsOnlyOnce("qp-key-1", "qp-key-3");
 +    assertThat(underTest.selectByKeys(dbSession, emptyList())).isEmpty();
 +  }
 +
 +  @Test
 +  public void select_selected_projects() throws Exception {
 +    ComponentDto project1 = componentDbTester.insertComponent(newProjectDto().setName("Project1 name"));
 +    ComponentDto project2 = componentDbTester.insertComponent(newProjectDto().setName("Project2 name"));
 +    ComponentDto project3 = componentDbTester.insertComponent(newProjectDto().setName("Project3 name"));
 +
 +    QualityProfileDto profile1 = newQualityProfileDto();
 +    qualityProfileDb.insertQualityProfiles(profile1);
 +    qualityProfileDb.associateProjectWithQualityProfile(project1, profile1);
 +    qualityProfileDb.associateProjectWithQualityProfile(project2, profile1);
 +
 +    QualityProfileDto profile2 = newQualityProfileDto();
 +    qualityProfileDb.insertQualityProfiles(profile2);
 +    qualityProfileDb.associateProjectWithQualityProfile(project3, profile2);
 +
 +    assertThat(underTest.selectSelectedProjects(profile1.getKey(), null, dbSession))
 +      .extracting("projectId", "projectUuid", "projectKey", "projectName", "profileKey")
 +      .containsOnly(
 +        tuple(project1.getId(), project1.uuid(), project1.key(), project1.name(), profile1.getKey()),
 +        tuple(project2.getId(), project2.uuid(), project2.key(), project2.name(), profile1.getKey())
 +      );
 +
 +    assertThat(underTest.selectSelectedProjects(profile1.getKey(), "ect1", dbSession)).hasSize(1);
 +    assertThat(underTest.selectSelectedProjects("unknown", null, dbSession)).isEmpty();
 +  }
 +
 +  @Test
 +  public void select_deselected_projects() throws Exception {
 +    ComponentDto project1 = componentDbTester.insertComponent(newProjectDto().setName("Project1 name"));
 +    ComponentDto project2 = componentDbTester.insertComponent(newProjectDto().setName("Project2 name"));
 +    ComponentDto project3 = componentDbTester.insertComponent(newProjectDto().setName("Project3 name"));
 +
 +    QualityProfileDto profile1 = newQualityProfileDto();
 +    qualityProfileDb.insertQualityProfiles(profile1);
 +    qualityProfileDb.associateProjectWithQualityProfile(project1, profile1);
 +
 +    QualityProfileDto profile2 = newQualityProfileDto();
 +    qualityProfileDb.insertQualityProfiles(profile2);
 +    qualityProfileDb.associateProjectWithQualityProfile(project2, profile2);
 +
 +    assertThat(underTest.selectDeselectedProjects(profile1.getKey(), null, dbSession))
 +      .extracting("projectId", "projectUuid", "projectKey", "projectName", "profileKey")
 +      .containsOnly(
 +        tuple(project2.getId(), project2.uuid(), project2.key(), project2.name(), null),
 +        tuple(project3.getId(), project3.uuid(), project3.key(), project3.name(), null)
 +      );
 +
 +    assertThat(underTest.selectDeselectedProjects(profile1.getKey(), "ect2", dbSession)).hasSize(1);
 +    assertThat(underTest.selectDeselectedProjects("unknown", null, dbSession)).hasSize(3);
 +  }
 +
 +  @Test
 +  public void select_project_associations() throws Exception {
 +    ComponentDto project1 = componentDbTester.insertComponent(newProjectDto().setName("Project1 name"));
 +    ComponentDto project2 = componentDbTester.insertComponent(newProjectDto().setName("Project2 name"));
 +    ComponentDto project3 = componentDbTester.insertComponent(newProjectDto().setName("Project3 name"));
 +
 +    QualityProfileDto profile1 = newQualityProfileDto();
 +    qualityProfileDb.insertQualityProfiles(profile1);
 +    qualityProfileDb.associateProjectWithQualityProfile(project1, profile1);
 +
 +    QualityProfileDto profile2 = newQualityProfileDto();
 +    qualityProfileDb.insertQualityProfiles(profile2);
 +    qualityProfileDb.associateProjectWithQualityProfile(project2, profile2);
 +
 +    assertThat(underTest.selectProjectAssociations(profile1.getKey(), null, dbSession))
 +      .extracting("projectId", "projectUuid", "projectKey", "projectName", "profileKey")
 +      .containsOnly(
 +        tuple(project1.getId(), project1.uuid(), project1.key(), project1.name(), profile1.getKey()),
 +        tuple(project2.getId(), project2.uuid(), project2.key(), project2.name(), null),
 +        tuple(project3.getId(), project3.uuid(), project3.key(), project3.name(), null)
 +      );
 +
 +    assertThat(underTest.selectProjectAssociations(profile1.getKey(), "ect2", dbSession)).hasSize(1);
 +    assertThat(underTest.selectProjectAssociations("unknown", null, dbSession)).hasSize(3);
 +  }
 +
+   @Test
+   public void update_project_profile_association() {
 -    qualityProfileDbTester.associateProjectWithQualityProfile(project, profile1Language1, profile2Language2);
++    ComponentDto project = componentDbTester.insertComponent(ComponentTesting.newProjectDto());
+     QualityProfileDto profile1Language1 = insertQualityProfileDto("profile1", "Profile 1", "xoo");
+     QualityProfileDto profile2Language2 = insertQualityProfileDto("profile2", "Profile 2", "xoo2");
+     QualityProfileDto profile3Language1 = insertQualityProfileDto("profile3", "Profile 3", "xoo");
++    qualityProfileDb.associateProjectWithQualityProfile(project, profile1Language1, profile2Language2);
+     underTest.updateProjectProfileAssociation(project.uuid(), profile3Language1.getKey(), profile1Language1.getKey(), dbSession);
+     assertThat(underTest.selectByProjectAndLanguage(dbSession, project.getKey(), "xoo").getKey()).isEqualTo(profile3Language1.getKey());
+     assertThat(underTest.selectByProjectAndLanguage(dbSession, project.getKey(), "xoo2").getKey()).isEqualTo(profile2Language2.getKey());
+   }
+   private QualityProfileDto insertQualityProfileDto(String key, String name, String language) {
+     QualityProfileDto dto = QualityProfileDto.createFor(key).setName(name).setLanguage(language);
+     underTest.insert(dbSession, dto);
+     return dto;
+   }
++
  }
index 53d67380329736d01f1c1db3e7870678bbf095c1,d6e0a12d31c913615bc009eb27babf84cfa553c0..33e123cd74e6c8278d07f12aa18477c9fd189b1e
@@@ -35,9 -35,13 +35,14 @@@ public class QualityProfileDbTester 
  
    public void insertQualityProfiles(QualityProfileDto qualityProfile, QualityProfileDto... qualityProfiles) {
      dbClient.qualityProfileDao().insert(dbSession, qualityProfile, qualityProfiles);
 +    dbSession.commit();
    }
  
+   public QualityProfileDto insertQualityProfile(QualityProfileDto qualityProfile) {
+     dbClient.qualityProfileDao().insert(dbSession, qualityProfile);
+     return qualityProfile;
+   }
    public void insertProjectWithQualityProfileAssociations(ComponentDto project, QualityProfileDto... qualityProfiles) {
      dbClient.componentDao().insert(dbSession, project);
      for (QualityProfileDto qualityProfile : qualityProfiles) {