3 * Copyright (C) 2009-2024 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.pushapi.qualityprofile;
22 import java.nio.charset.StandardCharsets;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.Deque;
26 import java.util.List;
28 import java.util.function.Consumer;
29 import org.junit.Rule;
30 import org.junit.Test;
31 import org.sonar.api.rule.RuleKey;
32 import org.sonar.db.DbTester;
33 import org.sonar.db.component.ComponentDto;
34 import org.sonar.db.component.ProjectData;
35 import org.sonar.db.project.ProjectDto;
36 import org.sonar.db.pushevent.PushEventDto;
37 import org.sonar.db.qualityprofile.ActiveRuleDto;
38 import org.sonar.db.qualityprofile.ActiveRuleParamDto;
39 import org.sonar.db.qualityprofile.QProfileDto;
40 import org.sonar.db.qualityprofile.QualityProfileTesting;
41 import org.sonar.db.rule.RuleDto;
42 import org.sonar.db.rule.RuleParamDto;
43 import org.sonar.server.qualityprofile.ActiveRuleChange;
44 import org.sonarqube.ws.Common;
46 import static java.util.List.of;
47 import static org.apache.commons.lang3.RandomStringUtils.secure;
48 import static org.assertj.core.api.Assertions.assertThat;
49 import static org.sonar.api.measures.CoreMetrics.NCLOC_LANGUAGE_DISTRIBUTION_KEY;
50 import static org.sonar.db.rule.RuleTesting.newCustomRule;
51 import static org.sonar.db.rule.RuleTesting.newTemplateRule;
52 import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.ACTIVATED;
54 public class QualityProfileChangeEventServiceImplTest {
57 public DbTester db = DbTester.create();
59 public final QualityProfileChangeEventServiceImpl underTest = new QualityProfileChangeEventServiceImpl(db.getDbClient());
62 public void distributeRuleChangeEvent() {
63 QProfileDto qualityProfileDto = QualityProfileTesting.newQualityProfileDto();
65 RuleDto templateRule = newTemplateRule(RuleKey.of("xoo", "template-key"));
66 db.rules().insert(templateRule);
68 RuleDto rule1 = newCustomRule(templateRule, "<div>line1\nline2</div>")
70 .setRepositoryKey("repo")
71 .setRuleKey("ruleKey")
72 .setDescriptionFormat(RuleDto.Format.MARKDOWN);
73 db.rules().insert(rule1);
75 ActiveRuleChange activeRuleChange = changeActiveRule(qualityProfileDto, rule1, "paramChangeKey", "paramChangeValue");
77 Collection<QProfileDto> profiles = Collections.singleton(qualityProfileDto);
79 ProjectDto project = db.components().insertPrivateProject().getProjectDto();
80 db.qualityProfiles().associateWithProject(project, qualityProfileDto);
82 underTest.distributeRuleChangeEvent(profiles, of(activeRuleChange), "xoo");
84 Deque<PushEventDto> events = getProjectEvents(project);
86 assertThat(events).isNotEmpty().hasSize(1);
87 assertThat(events.getFirst())
88 .extracting(PushEventDto::getName, PushEventDto::getLanguage)
89 .contains("RuleSetChanged", "xoo");
91 String ruleSetChangedEvent = new String(events.getFirst().getPayload(), StandardCharsets.UTF_8);
93 assertThat(ruleSetChangedEvent)
94 .contains("\"activatedRules\":[{\"key\":\"repo:ruleKey\"," +
95 "\"language\":\"xoo\"," +
96 "\"templateKey\":\"xoo:template-key\"," +
97 "\"params\":[{\"key\":\"paramChangeKey\",\"value\":\"paramChangeValue\"}]}]," +
98 "\"deactivatedRules\":[]");
102 public void distributeRuleChangeEvent_when_project_has_only_default_quality_profiles() {
103 String language = "xoo";
104 ProjectData projectData = db.components().insertPrivateProject();
105 ComponentDto mainBranch = projectData.getMainBranchComponent();
106 RuleDto templateRule = insertTemplateRule();
107 QProfileDto defaultQualityProfile = insertDefaultQualityProfile(language);
108 RuleDto rule = insertCustomRule(templateRule, language, "<div>line1\nline2</div>");
109 ActiveRuleChange activeRuleChange = changeActiveRule(defaultQualityProfile, rule, "paramChangeKey", "paramChangeValue");
110 db.measures().insertMeasure(mainBranch, m -> m.addValue(NCLOC_LANGUAGE_DISTRIBUTION_KEY, language + "=100"));
112 db.getSession().commit();
114 underTest.distributeRuleChangeEvent(List.of(defaultQualityProfile), of(activeRuleChange), language);
116 Deque<PushEventDto> events = getProjectEvents(projectData.getProjectDto());
121 assertThat(events.getFirst())
122 .extracting(PushEventDto::getName, PushEventDto::getLanguage)
123 .contains("RuleSetChanged", "xoo");
125 String ruleSetChangedEvent = new String(events.getFirst().getPayload(), StandardCharsets.UTF_8);
127 assertThat(ruleSetChangedEvent)
128 .contains("\"activatedRules\":[{\"key\":\"repo:ruleKey\"," +
129 "\"language\":\"xoo\"," +
130 "\"templateKey\":\"xoo:template-key\"," +
131 "\"params\":[{\"key\":\"paramChangeKey\",\"value\":\"paramChangeValue\"}]}]," +
132 "\"deactivatedRules\":[]");
135 private Deque<PushEventDto> getProjectEvents(ProjectDto projectDto) {
136 return db.getDbClient()
138 .selectChunkByProjectUuids(db.getSession(), Set.of(projectDto.getUuid()), 1L, null, 1);
141 private ActiveRuleChange changeActiveRule(QProfileDto defaultQualityProfile, RuleDto rule, String changeKey, String changeValue) {
142 ActiveRuleDto activeRuleDto = ActiveRuleDto.createFor(defaultQualityProfile, rule);
143 ActiveRuleChange activeRuleChange = new ActiveRuleChange(ACTIVATED, activeRuleDto, rule);
144 return activeRuleChange.setParameter(changeKey, changeValue);
147 private RuleDto insertCustomRule(RuleDto templateRule, String language, String description) {
148 RuleDto rule = newCustomRule(templateRule, description)
149 .setLanguage(language)
150 .setRepositoryKey("repo")
151 .setRuleKey("ruleKey")
152 .setDescriptionFormat(RuleDto.Format.MARKDOWN);
154 db.rules().insert(rule);
158 private QProfileDto insertDefaultQualityProfile(String language) {
159 Consumer<QProfileDto> configureQualityProfile = profile -> profile
161 .setLanguage(language);
163 QProfileDto defaultQualityProfile = db.qualityProfiles().insert(configureQualityProfile);
164 db.qualityProfiles().setAsDefault(defaultQualityProfile);
166 return defaultQualityProfile;
169 private RuleDto insertTemplateRule() {
170 RuleKey key = RuleKey.of("xoo", "template-key");
171 RuleDto templateRule = newTemplateRule(key);
172 db.rules().insert(templateRule);
177 public void publishRuleActivationToSonarLintClients() {
178 ProjectDto projectDto = new ProjectDto().setUuid("project-uuid");
179 QProfileDto activatedQualityProfile = QualityProfileTesting.newQualityProfileDto();
180 activatedQualityProfile.setLanguage("xoo");
181 db.qualityProfiles().insert(activatedQualityProfile);
182 RuleDto rule1 = db.rules().insert(r -> r.setLanguage("xoo").setRepositoryKey("repo").setRuleKey("ruleKey"));
183 RuleParamDto rule1Param = db.rules().insertRuleParam(rule1);
185 ActiveRuleDto activeRule1 = db.qualityProfiles().activateRule(activatedQualityProfile, rule1);
186 ActiveRuleParamDto activeRuleParam1 = ActiveRuleParamDto.createFor(rule1Param).setValue(secure().nextAlphanumeric(20));
187 db.getDbClient().activeRuleDao().insertParam(db.getSession(), activeRule1, activeRuleParam1);
188 db.getSession().commit();
190 QProfileDto deactivatedQualityProfile = QualityProfileTesting.newQualityProfileDto();
191 db.qualityProfiles().insert(deactivatedQualityProfile);
192 RuleDto rule2 = db.rules().insert(r -> r.setLanguage("xoo").setRepositoryKey("repo2").setRuleKey("ruleKey2"));
193 RuleParamDto rule2Param = db.rules().insertRuleParam(rule2);
195 ActiveRuleDto activeRule2 = db.qualityProfiles().activateRule(deactivatedQualityProfile, rule2);
196 ActiveRuleParamDto activeRuleParam2 = ActiveRuleParamDto.createFor(rule2Param).setValue(secure().nextAlphanumeric(20));
197 db.getDbClient().activeRuleDao().insertParam(db.getSession(), activeRule2, activeRuleParam2);
198 db.getSession().commit();
200 underTest.publishRuleActivationToSonarLintClients(projectDto, activatedQualityProfile, deactivatedQualityProfile);
202 Deque<PushEventDto> events = getProjectEvents(projectDto);
204 assertThat(events).isNotEmpty().hasSize(1);
205 assertThat(events.getFirst())
206 .extracting(PushEventDto::getName, PushEventDto::getLanguage)
207 .contains("RuleSetChanged", "xoo");
209 String ruleSetChangedEvent = new String(events.getFirst().getPayload(), StandardCharsets.UTF_8);
211 assertThat(ruleSetChangedEvent)
212 .contains("\"activatedRules\":[{\"key\":\"repo:ruleKey\"," +
213 "\"language\":\"xoo\",\"severity\":\"" + Common.Severity.forNumber(rule1.getSeverity()).name() + "\"," +
214 "\"params\":[{\"key\":\"" + activeRuleParam1.getKey() + "\",\"value\":\"" + activeRuleParam1.getValue() + "\"}]}]," +
215 "\"deactivatedRules\":[\"repo2:ruleKey2\"]");