3 * Copyright (C) 2009-2022 SonarSource SA
4 * mailto:info AT sonarsource DOT com
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 3 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 package org.sonar.server.qualityprofile;
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.List;
25 import java.util.Optional;
26 import org.junit.Rule;
27 import org.junit.Test;
28 import org.sonar.api.impl.utils.AlwaysIncreasingSystem2;
29 import org.sonar.api.resources.Language;
30 import org.sonar.api.utils.System2;
31 import org.sonar.api.utils.log.LogTester;
32 import org.sonar.api.utils.log.LoggerLevel;
33 import org.sonar.db.DbClient;
34 import org.sonar.db.DbSession;
35 import org.sonar.db.DbTester;
36 import org.sonar.db.qualityprofile.QProfileDto;
37 import org.sonar.db.qualityprofile.RulesProfileDto;
38 import org.sonar.db.rule.RuleDefinitionDto;
39 import org.sonar.server.language.LanguageTesting;
40 import org.sonar.server.qualityprofile.builtin.BuiltInQProfile;
41 import org.sonar.server.qualityprofile.builtin.BuiltInQProfileInsert;
42 import org.sonar.server.qualityprofile.builtin.BuiltInQProfileRepositoryRule;
43 import org.sonar.server.qualityprofile.builtin.BuiltInQProfileUpdate;
44 import org.sonar.server.qualityprofile.builtin.BuiltInQualityProfilesUpdateListener;
45 import org.sonar.server.tester.UserSessionRule;
47 import static java.lang.String.format;
48 import static org.assertj.core.api.Assertions.assertThat;
49 import static org.assertj.core.api.Assertions.assertThatThrownBy;
50 import static org.mockito.Mockito.mock;
51 import static org.sonar.db.qualityprofile.QualityProfileTesting.newQualityProfileDto;
52 import static org.sonar.db.qualityprofile.QualityProfileTesting.newRuleProfileDto;
54 public class RegisterQualityProfilesTest {
55 private static final Language FOO_LANGUAGE = LanguageTesting.newLanguage("foo");
56 private static final Language BAR_LANGUAGE = LanguageTesting.newLanguage("bar");
58 private System2 system2 = new AlwaysIncreasingSystem2();
60 public DbTester db = DbTester.create(system2);
62 public UserSessionRule userSessionRule = UserSessionRule.standalone();
64 public BuiltInQProfileRepositoryRule builtInQProfileRepositoryRule = new BuiltInQProfileRepositoryRule();
66 public LogTester logTester = new LogTester();
68 private DbClient dbClient = db.getDbClient();
69 private DummyBuiltInQProfileInsert insert = new DummyBuiltInQProfileInsert();
70 private DummyBuiltInQProfileUpdate update = new DummyBuiltInQProfileUpdate();
71 private RegisterQualityProfiles underTest = new RegisterQualityProfiles(builtInQProfileRepositoryRule, dbClient, insert, update,
72 mock(BuiltInQualityProfilesUpdateListener.class), system2);
75 public void start_fails_if_BuiltInQProfileRepository_has_not_been_initialized() {
76 assertThatThrownBy(() -> underTest.start())
77 .isInstanceOf(IllegalStateException.class)
78 .hasMessage("initialize must be called first");
82 public void persist_built_in_profiles_that_are_not_persisted_yet() {
83 BuiltInQProfile builtInQProfile = builtInQProfileRepositoryRule.add(FOO_LANGUAGE, "Sonar way");
84 builtInQProfileRepositoryRule.initialize();
88 assertThat(insert.callLogs).containsExactly(builtInQProfile);
89 assertThat(update.callLogs).isEmpty();
90 assertThat(logTester.logs(LoggerLevel.INFO)).contains("Register profile foo/Sonar way");
94 public void dont_persist_built_in_profiles_that_are_already_persisted() {
97 BuiltInQProfile persistedBuiltIn = builtInQProfileRepositoryRule.add(FOO_LANGUAGE, name, true);
98 BuiltInQProfile nonPersistedBuiltIn = builtInQProfileRepositoryRule.add(BAR_LANGUAGE, name, true);
99 builtInQProfileRepositoryRule.initialize();
100 insertRulesProfile(persistedBuiltIn);
104 assertThat(insert.callLogs).containsExactly(nonPersistedBuiltIn);
105 assertThat(update.callLogs).containsExactly(persistedBuiltIn);
109 public void rename_custom_outdated_profiles_if_same_name_than_built_in_profile() {
110 QProfileDto outdatedProfile = db.qualityProfiles().insert(p -> p.setIsBuiltIn(false)
111 .setLanguage(FOO_LANGUAGE.getKey()).setName("Sonar way"));
112 builtInQProfileRepositoryRule.add(FOO_LANGUAGE, "Sonar way", false);
113 builtInQProfileRepositoryRule.initialize();
117 assertThat(selectPersistedName(outdatedProfile)).isEqualTo("Sonar way (outdated copy)");
118 assertThat(logTester.logs(LoggerLevel.INFO)).contains("Rename Quality profiles [foo/Sonar way] to [Sonar way (outdated copy)]");
122 public void update_built_in_profile_if_it_already_exists() {
123 RulesProfileDto ruleProfile = newRuleProfileDto(rp -> rp.setIsBuiltIn(true).setName("Sonar way").setLanguage(FOO_LANGUAGE.getKey()));
124 db.getDbClient().qualityProfileDao().insert(db.getSession(), ruleProfile);
127 BuiltInQProfile builtIn = builtInQProfileRepositoryRule.add(FOO_LANGUAGE, ruleProfile.getName(), false);
128 builtInQProfileRepositoryRule.initialize();
132 assertThat(insert.callLogs).isEmpty();
133 assertThat(update.callLogs).containsExactly(builtIn);
134 assertThat(logTester.logs(LoggerLevel.INFO)).contains("Update profile foo/Sonar way");
138 public void update_default_built_in_quality_profile() {
139 RulesProfileDto ruleProfileWithoutRule = newRuleProfileDto(rp -> rp.setIsBuiltIn(true).setName("Sonar way").setLanguage(FOO_LANGUAGE.getKey()));
140 RulesProfileDto ruleProfileWithOneRule = newRuleProfileDto(rp -> rp.setIsBuiltIn(true).setName("Sonar way 2").setLanguage(FOO_LANGUAGE.getKey()));
142 QProfileDto qProfileWithoutRule = newQualityProfileDto()
144 .setLanguage(FOO_LANGUAGE.getKey())
145 .setRulesProfileUuid(ruleProfileWithoutRule.getUuid());
146 QProfileDto qProfileWithOneRule = newQualityProfileDto()
148 .setLanguage(FOO_LANGUAGE.getKey())
149 .setRulesProfileUuid(ruleProfileWithOneRule.getUuid());
151 db.qualityProfiles().insert(qProfileWithoutRule, qProfileWithOneRule);
152 db.qualityProfiles().setAsDefault(qProfileWithoutRule);
154 RuleDefinitionDto ruleDefinition = db.rules().insert();
155 db.qualityProfiles().activateRule(qProfileWithOneRule, ruleDefinition);
158 builtInQProfileRepositoryRule.add(FOO_LANGUAGE, ruleProfileWithoutRule.getName(), true);
159 builtInQProfileRepositoryRule.add(FOO_LANGUAGE, ruleProfileWithOneRule.getName(), false);
160 builtInQProfileRepositoryRule.initialize();
164 logTester.logs(LoggerLevel.INFO).contains(
165 format("Default built-in quality profile for language [foo] has been updated from [%s] to [%s] since previous default does not have active rules.",
166 qProfileWithoutRule.getName(), qProfileWithOneRule.getName()));
168 assertThat(selectUuidOfDefaultProfile(FOO_LANGUAGE.getKey()))
170 .isEqualTo(qProfileWithOneRule.getKee());
173 private Optional<String> selectUuidOfDefaultProfile(String language) {
174 return db.select("select qprofile_uuid as \"profileUuid\" " +
175 " from default_qprofiles " +
176 " where language='" + language + "'")
179 .map(m -> (String) m.get("profileUuid"));
182 private String selectPersistedName(QProfileDto profile) {
183 return db.qualityProfiles().selectByUuid(profile.getKee()).get().getName();
186 private void insertRulesProfile(BuiltInQProfile builtIn) {
187 RulesProfileDto dto = newRuleProfileDto(rp -> rp
189 .setLanguage(builtIn.getLanguage())
190 .setName(builtIn.getName()));
191 dbClient.qualityProfileDao().insert(db.getSession(), dto);
195 private static class DummyBuiltInQProfileInsert implements BuiltInQProfileInsert {
196 private final List<BuiltInQProfile> callLogs = new ArrayList<>();
199 public void create(DbSession batchDbSession, BuiltInQProfile builtIn) {
200 callLogs.add(builtIn);
204 private static class DummyBuiltInQProfileUpdate implements BuiltInQProfileUpdate {
205 private final List<BuiltInQProfile> callLogs = new ArrayList<>();
208 public List<ActiveRuleChange> update(DbSession dbSession, BuiltInQProfile builtIn, RulesProfileDto ruleProfile) {
209 callLogs.add(builtIn);
210 return Collections.emptyList();