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.Collections;
24 import org.assertj.core.api.AbstractObjectAssert;
25 import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
26 import org.junit.Before;
27 import org.junit.Rule;
28 import org.junit.Test;
29 import org.mockito.ArgumentCaptor;
30 import org.sonar.api.impl.utils.AlwaysIncreasingSystem2;
31 import org.sonar.api.rule.Severity;
32 import org.sonar.api.utils.System2;
33 import org.sonar.core.util.SequenceUuidFactory;
34 import org.sonar.core.util.Uuids;
35 import org.sonar.db.DbSession;
36 import org.sonar.db.DbTester;
37 import org.sonar.db.project.ProjectDto;
38 import org.sonar.db.qualityprofile.ActiveRuleDto;
39 import org.sonar.db.qualityprofile.ActiveRuleParamDto;
40 import org.sonar.db.qualityprofile.OrgQProfileDto;
41 import org.sonar.db.qualityprofile.QProfileChangeDto;
42 import org.sonar.db.qualityprofile.QProfileDto;
43 import org.sonar.db.qualityprofile.RulesProfileDto;
44 import org.sonar.db.rule.RuleDefinitionDto;
45 import org.sonar.db.rule.RuleParamDto;
46 import org.sonar.db.user.GroupDto;
47 import org.sonar.db.user.UserDto;
48 import org.sonar.server.exceptions.BadRequestException;
49 import org.sonar.server.qualityprofile.builtin.QProfileName;
50 import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
52 import static java.util.Arrays.asList;
53 import static org.assertj.core.api.Assertions.assertThat;
54 import static org.assertj.core.api.Assertions.assertThatThrownBy;
55 import static org.mockito.ArgumentMatchers.any;
56 import static org.mockito.ArgumentMatchers.anyCollection;
57 import static org.mockito.Mockito.mock;
58 import static org.mockito.Mockito.never;
59 import static org.mockito.Mockito.verify;
60 import static org.mockito.Mockito.verifyZeroInteractions;
62 public class QProfileFactoryImplTest {
64 private System2 system2 = new AlwaysIncreasingSystem2();
67 public DbTester db = DbTester.create(system2);
69 private DbSession dbSession = db.getSession();
70 private ActiveRuleIndexer activeRuleIndexer = mock(ActiveRuleIndexer.class);
71 private QProfileFactory underTest = new QProfileFactoryImpl(db.getDbClient(), new SequenceUuidFactory(), system2, activeRuleIndexer);
72 private RuleDefinitionDto rule;
73 private RuleParamDto ruleParam;
77 rule = db.rules().insert();
78 ruleParam = db.rules().insertRuleParam(rule);
82 public void checkAndCreateCustom() {
83 QProfileDto profile = underTest.checkAndCreateCustom(dbSession, new QProfileName("xoo", "P1"));
85 assertThat(profile.getKee()).isNotEmpty();
86 assertThat(profile.getName()).isEqualTo("P1");
87 assertThat(profile.getLanguage()).isEqualTo("xoo");
88 assertThat(profile.getRulesProfileUuid()).isNotNull();
89 assertThat(profile.isBuiltIn()).isFalse();
91 QProfileDto reloaded = db.getDbClient().qualityProfileDao().selectByNameAndLanguage(dbSession, profile.getName(), profile.getLanguage());
92 assertEqual(profile, reloaded);
93 assertThat(db.getDbClient().qualityProfileDao().selectAll(dbSession)).extracting(QProfileDto::getKee).containsExactly(profile.getKee());
97 public void checkAndCreateCustom_throws_BadRequestException_if_name_null() {
98 QProfileName name = new QProfileName("xoo", null);
100 expectBadRequestException(() -> underTest.checkAndCreateCustom(dbSession, name), "quality_profiles.profile_name_cant_be_blank");
104 public void checkAndCreateCustom_throws_BadRequestException_if_name_empty() {
105 QProfileName name = new QProfileName("xoo", "");
107 expectBadRequestException(() -> underTest.checkAndCreateCustom(dbSession, name), "quality_profiles.profile_name_cant_be_blank");
111 public void checkAndCreateCustom_throws_BadRequestException_if_already_exists() {
112 QProfileName name = new QProfileName("xoo", "P1");
114 underTest.checkAndCreateCustom(dbSession, name);
117 expectBadRequestException(() -> underTest.checkAndCreateCustom(dbSession, name), "Quality profile already exists: xoo/P1");
121 public void delete_custom_profiles() {
122 QProfileDto profile1 = createCustomProfile();
123 QProfileDto profile2 = createCustomProfile();
124 QProfileDto profile3 = createCustomProfile();
126 underTest.delete(dbSession, asList(profile1, profile2));
128 verifyCallActiveRuleIndexerDelete(profile1.getKee(), profile2.getKee());
129 assertThatCustomProfileDoesNotExist(profile1);
130 assertThatCustomProfileDoesNotExist(profile2);
131 assertThatCustomProfileExists(profile3);
135 public void delete_removes_custom_profile_marked_as_default() {
136 QProfileDto profile = createCustomProfile();
137 db.qualityProfiles().setAsDefault(profile);
139 underTest.delete(dbSession, asList(profile));
141 assertThatCustomProfileDoesNotExist(profile);
145 public void delete_removes_custom_profile_from_project_associations() {
146 QProfileDto profile = createCustomProfile();
147 ProjectDto project = db.components().insertPrivateProjectDto();
148 db.qualityProfiles().associateWithProject(project, profile);
150 underTest.delete(dbSession, asList(profile));
152 assertThatCustomProfileDoesNotExist(profile);
156 public void delete_builtin_profile() {
157 RulesProfileDto builtInProfile = createBuiltInProfile();
158 QProfileDto profile = associateBuiltInProfile(builtInProfile);
160 underTest.delete(dbSession, asList(profile));
162 verifyNoCallsActiveRuleIndexerDelete();
164 // remove only from org_qprofiles
165 assertThat(db.getDbClient().qualityProfileDao().selectAll(dbSession)).isEmpty();
167 assertThatRulesProfileExists(builtInProfile);
171 public void delete_builtin_profile_associated_to_project() {
172 RulesProfileDto builtInProfile = createBuiltInProfile();
173 ProjectDto project = db.components().insertPrivateProjectDto();
174 QProfileDto profile = associateBuiltInProfile(builtInProfile);
175 db.qualityProfiles().associateWithProject(project, profile);
176 assertThat(db.getDbClient().qualityProfileDao().selectAssociatedToProjectAndLanguage(dbSession, project, profile.getLanguage())).isNotNull();
178 underTest.delete(dbSession, asList(profile));
180 verifyNoCallsActiveRuleIndexerDelete();
182 // remove only from org_qprofiles and project_qprofiles
183 assertThat(db.getDbClient().qualityProfileDao().selectAll(dbSession)).isEmpty();
184 assertThat(db.getDbClient().qualityProfileDao().selectAssociatedToProjectAndLanguage(dbSession, project, profile.getLanguage())).isNull();
185 assertThatRulesProfileExists(builtInProfile);
189 public void delete_builtin_profile_marked_as_default() {
190 RulesProfileDto builtInProfile = createBuiltInProfile();
191 QProfileDto profile = associateBuiltInProfile(builtInProfile);
192 db.qualityProfiles().setAsDefault(profile);
194 underTest.delete(dbSession, asList(profile));
196 verifyNoCallsActiveRuleIndexerDelete();
198 // remove only from org_qprofiles and default_qprofiles
199 assertThat(db.getDbClient().qualityProfileDao().selectAll(dbSession)).isEmpty();
200 assertThat(db.getDbClient().qualityProfileDao().selectDefaultProfile(dbSession, profile.getLanguage())).isNull();
201 assertThatRulesProfileExists(builtInProfile);
205 public void delete_accepts_empty_list_of_keys() {
206 QProfileDto profile = createCustomProfile();
208 underTest.delete(dbSession, Collections.emptyList());
210 verifyZeroInteractions(activeRuleIndexer);
211 assertQualityProfileFromDb(profile).isNotNull();
215 public void delete_removes_qprofile_edit_permissions() {
216 QProfileDto profile = db.qualityProfiles().insert();
217 UserDto user = db.users().insertUser();
218 db.qualityProfiles().addUserPermission(profile, user);
219 GroupDto group = db.users().insertGroup();
220 db.qualityProfiles().addGroupPermission(profile, group);
222 underTest.delete(dbSession, asList(profile));
224 assertThat(db.countRowsOfTable(dbSession, "qprofile_edit_users")).isZero();
225 assertThat(db.countRowsOfTable(dbSession, "qprofile_edit_groups")).isZero();
228 private QProfileDto createCustomProfile() {
229 QProfileDto profile = db.qualityProfiles().insert(p -> p.setLanguage("xoo").setIsBuiltIn(false));
230 ActiveRuleDto activeRuleDto = db.qualityProfiles().activateRule(profile, rule);
232 ActiveRuleParamDto activeRuleParam = new ActiveRuleParamDto()
233 .setRulesParameterUuid(ruleParam.getUuid())
236 db.getDbClient().activeRuleDao().insertParam(dbSession, activeRuleDto, activeRuleParam);
238 db.getDbClient().qProfileChangeDao().insert(dbSession, new QProfileChangeDto()
239 .setChangeType(ActiveRuleChange.Type.ACTIVATED.name())
240 .setRulesProfileUuid(profile.getRulesProfileUuid()));
245 private RulesProfileDto createBuiltInProfile() {
246 RulesProfileDto rulesProfileDto = new RulesProfileDto()
248 .setUuid(Uuids.createFast())
250 .setName("Sonar way");
251 db.getDbClient().qualityProfileDao().insert(dbSession, rulesProfileDto);
252 ActiveRuleDto activeRuleDto = new ActiveRuleDto()
253 .setProfileUuid(rulesProfileDto.getUuid())
254 .setRuleUuid(rule.getUuid())
255 .setSeverity(Severity.BLOCKER);
256 db.getDbClient().activeRuleDao().insert(dbSession, activeRuleDto);
258 ActiveRuleParamDto activeRuleParam = new ActiveRuleParamDto()
259 .setRulesParameterUuid(ruleParam.getUuid())
262 db.getDbClient().activeRuleDao().insertParam(dbSession, activeRuleDto, activeRuleParam);
264 db.getDbClient().qProfileChangeDao().insert(dbSession, new QProfileChangeDto()
265 .setChangeType(ActiveRuleChange.Type.ACTIVATED.name())
266 .setRulesProfileUuid(rulesProfileDto.getUuid()));
269 return rulesProfileDto;
272 private QProfileDto associateBuiltInProfile(RulesProfileDto rulesProfile) {
273 OrgQProfileDto orgQProfileDto = new OrgQProfileDto()
274 .setUuid(Uuids.createFast())
275 .setRulesProfileUuid(rulesProfile.getUuid());
277 db.getDbClient().qualityProfileDao().insert(dbSession, orgQProfileDto);
279 return QProfileDto.from(orgQProfileDto, rulesProfile);
282 private AbstractObjectAssert<?, QProfileDto> assertQualityProfileFromDb(QProfileDto profile) {
283 return assertThat(db.getDbClient().qualityProfileDao().selectByUuid(dbSession, profile.getKee()));
286 private void verifyNoCallsActiveRuleIndexerDelete() {
287 verify(activeRuleIndexer, never()).commitDeletionOfProfiles(any(DbSession.class), anyCollection());
290 private void verifyCallActiveRuleIndexerDelete(String... expectedRuleProfileUuids) {
291 Class<Set<QProfileDto>> setClass = (Class<Set<QProfileDto>>) (Class) Set.class;
292 ArgumentCaptor<Set<QProfileDto>> setCaptor = ArgumentCaptor.forClass(setClass);
293 verify(activeRuleIndexer).commitDeletionOfProfiles(any(DbSession.class), setCaptor.capture());
295 assertThat(setCaptor.getValue())
296 .extracting(QProfileDto::getKee)
297 .containsExactlyInAnyOrder(expectedRuleProfileUuids);
300 private void assertThatRulesProfileExists(RulesProfileDto rulesProfile) {
301 assertThat(db.getDbClient().qualityProfileDao().selectBuiltInRuleProfiles(dbSession))
302 .extracting(RulesProfileDto::getUuid)
303 .containsExactly(rulesProfile.getUuid());
304 assertThat(db.countRowsOfTable(dbSession, "active_rules")).isPositive();
305 assertThat(db.countRowsOfTable(dbSession, "active_rule_parameters")).isPositive();
306 assertThat(db.countRowsOfTable(dbSession, "qprofile_changes")).isPositive();
309 private void assertThatCustomProfileDoesNotExist(QProfileDto profile) {
310 assertThat(db.countSql(dbSession, "select count(*) from org_qprofiles where uuid = '" + profile.getKee() + "'")).isZero();
311 assertThat(db.countSql(dbSession, "select count(*) from project_qprofiles where profile_key = '" + profile.getKee() + "'")).isZero();
312 assertThat(db.countSql(dbSession, "select count(*) from default_qprofiles where qprofile_uuid = '" + profile.getKee() + "'")).isZero();
313 assertThat(db.countSql(dbSession, "select count(*) from rules_profiles where uuid = '" + profile.getRulesProfileUuid() + "'")).isZero();
314 assertThat(db.countSql(dbSession, "select count(*) from active_rules where profile_uuid = '" + profile.getRulesProfileUuid() + "'")).isZero();
315 assertThat(db.countSql(dbSession, "select count(*) from qprofile_changes where rules_profile_uuid = '" + profile.getRulesProfileUuid() + "'")).isZero();
316 // TODO active_rule_parameters
319 private void assertThatCustomProfileExists(QProfileDto profile) {
320 assertThat(db.countSql(dbSession, "select count(*) from org_qprofiles where uuid = '" + profile.getKee() + "'")).isPositive();
321 // assertThat(db.countSql(dbSession, "select count(*) from project_qprofiles where profile_key = '" + profile.getKee() +
322 // "'")).isPositive();
323 // assertThat(db.countSql(dbSession, "select count(*) from default_qprofiles where qprofile_uuid = '" + profile.getKee() +
324 // "'")).isPositive();
325 assertThat(db.countSql(dbSession, "select count(*) from rules_profiles where uuid = '" + profile.getRulesProfileUuid() + "'")).isOne();
326 assertThat(db.countSql(dbSession, "select count(*) from active_rules where profile_uuid = '" + profile.getRulesProfileUuid() + "'")).isPositive();
327 assertThat(db.countSql(dbSession, "select count(*) from qprofile_changes where rules_profile_uuid = '" + profile.getRulesProfileUuid() + "'")).isPositive();
328 // TODO active_rule_parameters
331 private static void assertEqual(QProfileDto p1, QProfileDto p2) {
332 assertThat(p2.getName()).isEqualTo(p1.getName());
333 assertThat(p2.getKee()).startsWith(p1.getKee());
334 assertThat(p2.getLanguage()).isEqualTo(p1.getLanguage());
335 assertThat(p2.getRulesProfileUuid()).isEqualTo(p1.getRulesProfileUuid());
336 assertThat(p2.getParentKee()).isEqualTo(p1.getParentKee());
339 private void expectBadRequestException(ThrowingCallable callback, String message) {
340 assertThatThrownBy(callback)
341 .isInstanceOf(BadRequestException.class)
342 .hasMessage(message);