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.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.measure.LiveMeasureDto;
35 import org.sonar.db.metric.MetricDto;
36 import org.sonar.db.project.ProjectDto;
37 import org.sonar.db.pushevent.PushEventDto;
38 import org.sonar.db.qualityprofile.ActiveRuleDto;
39 import org.sonar.db.qualityprofile.ActiveRuleParamDto;
40 import org.sonar.db.qualityprofile.QProfileDto;
41 import org.sonar.db.qualityprofile.QualityProfileTesting;
42 import org.sonar.db.rule.RuleDto;
43 import org.sonar.db.rule.RuleParamDto;
44 import org.sonar.server.qualityprofile.ActiveRuleChange;
45 import org.sonarqube.ws.Common;
47 import static java.util.List.of;
48 import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
49 import static org.apache.commons.lang.math.RandomUtils.nextInt;
50 import static org.assertj.core.api.Assertions.assertThat;
51 import static org.sonar.api.measures.CoreMetrics.NCLOC_LANGUAGE_DISTRIBUTION_KEY;
52 import static org.sonar.api.measures.Metric.ValueType.STRING;
53 import static org.sonar.db.rule.RuleTesting.newCustomRule;
54 import static org.sonar.db.rule.RuleTesting.newTemplateRule;
55 import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.ACTIVATED;
57 public class QualityProfileChangeEventServiceImplTest {
60 public DbTester db = DbTester.create();
62 public final QualityProfileChangeEventServiceImpl underTest = new QualityProfileChangeEventServiceImpl(db.getDbClient());
65 public void distributeRuleChangeEvent() {
66 QProfileDto qualityProfileDto = QualityProfileTesting.newQualityProfileDto();
68 RuleDto templateRule = newTemplateRule(RuleKey.of("xoo", "template-key"));
69 db.rules().insert(templateRule);
71 RuleDto rule1 = newCustomRule(templateRule, "<div>line1\nline2</div>")
73 .setRepositoryKey("repo")
74 .setRuleKey("ruleKey")
75 .setDescriptionFormat(RuleDto.Format.MARKDOWN);
76 db.rules().insert(rule1);
78 ActiveRuleChange activeRuleChange = changeActiveRule(qualityProfileDto, rule1, "paramChangeKey", "paramChangeValue");
80 Collection<QProfileDto> profiles = Collections.singleton(qualityProfileDto);
82 ProjectDto project = db.components().insertPrivateProjectDto();
83 db.qualityProfiles().associateWithProject(project, qualityProfileDto);
85 underTest.distributeRuleChangeEvent(profiles, of(activeRuleChange), "xoo");
87 Deque<PushEventDto> events = getProjectEvents(project.getUuid());
89 assertThat(events).isNotEmpty().hasSize(1);
90 assertThat(events.getFirst())
91 .extracting(PushEventDto::getName, PushEventDto::getLanguage)
92 .contains("RuleSetChanged", "xoo");
94 String ruleSetChangedEvent = new String(events.getFirst().getPayload(), StandardCharsets.UTF_8);
96 assertThat(ruleSetChangedEvent)
97 .contains("\"activatedRules\":[{\"key\":\"repo:ruleKey\"," +
98 "\"language\":\"xoo\"," +
99 "\"templateKey\":\"xoo:template-key\"," +
100 "\"params\":[{\"key\":\"paramChangeKey\",\"value\":\"paramChangeValue\"}]}]," +
101 "\"deactivatedRules\":[]");
105 public void distributeRuleChangeEvent_when_project_has_only_default_quality_profiles() {
106 String language = "xoo";
107 ComponentDto project = db.components().insertPrivateProject();
108 RuleDto templateRule = insertTemplateRule();
109 QProfileDto defaultQualityProfile = insertDefaultQualityProfile(language);
110 RuleDto rule = insertCustomRule(templateRule, language, "<div>line1\nline2</div>");
111 ActiveRuleChange activeRuleChange = changeActiveRule(defaultQualityProfile, rule, "paramChangeKey", "paramChangeValue");
112 insertQualityProfileLiveMeasure(project, language, NCLOC_LANGUAGE_DISTRIBUTION_KEY);
114 db.getSession().commit();
116 underTest.distributeRuleChangeEvent(List.of(defaultQualityProfile), of(activeRuleChange), language);
118 Deque<PushEventDto> events = getProjectEvents(project.projectUuid());
123 assertThat(events.getFirst())
124 .extracting(PushEventDto::getName, PushEventDto::getLanguage)
125 .contains("RuleSetChanged", "xoo");
127 String ruleSetChangedEvent = new String(events.getFirst().getPayload(), StandardCharsets.UTF_8);
129 assertThat(ruleSetChangedEvent)
130 .contains("\"activatedRules\":[{\"key\":\"repo:ruleKey\"," +
131 "\"language\":\"xoo\"," +
132 "\"templateKey\":\"xoo:template-key\"," +
133 "\"params\":[{\"key\":\"paramChangeKey\",\"value\":\"paramChangeValue\"}]}]," +
134 "\"deactivatedRules\":[]");
137 private Deque<PushEventDto> getProjectEvents(String projectUuid) {
138 return db.getDbClient()
140 .selectChunkByProjectUuids(db.getSession(), Set.of(projectUuid), 1L, null, 1);
143 private ActiveRuleChange changeActiveRule(QProfileDto defaultQualityProfile, RuleDto rule, String changeKey, String changeValue) {
144 ActiveRuleDto activeRuleDto = ActiveRuleDto.createFor(defaultQualityProfile, rule);
145 ActiveRuleChange activeRuleChange = new ActiveRuleChange(ACTIVATED, activeRuleDto, rule);
146 return activeRuleChange.setParameter(changeKey, changeValue);
149 private RuleDto insertCustomRule(RuleDto templateRule, String language, String description) {
150 RuleDto rule = newCustomRule(templateRule, description)
151 .setLanguage(language)
152 .setRepositoryKey("repo")
153 .setRuleKey("ruleKey")
154 .setDescriptionFormat(RuleDto.Format.MARKDOWN);
156 db.rules().insert(rule);
160 private QProfileDto insertDefaultQualityProfile(String language) {
161 Consumer<QProfileDto> configureQualityProfile = profile -> profile
163 .setLanguage(language);
165 QProfileDto defaultQualityProfile = db.qualityProfiles().insert(configureQualityProfile);
166 db.qualityProfiles().setAsDefault(defaultQualityProfile);
168 return defaultQualityProfile;
171 private RuleDto insertTemplateRule() {
172 RuleKey key = RuleKey.of("xoo", "template-key");
173 RuleDto templateRule = newTemplateRule(key);
174 db.rules().insert(templateRule);
179 public void publishRuleActivationToSonarLintClients() {
180 ProjectDto projectDao = new ProjectDto().setUuid("project-uuid");
181 QProfileDto activatedQualityProfile = QualityProfileTesting.newQualityProfileDto();
182 activatedQualityProfile.setLanguage("xoo");
183 db.qualityProfiles().insert(activatedQualityProfile);
184 RuleDto rule1 = db.rules().insert(r -> r.setLanguage("xoo").setRepositoryKey("repo").setRuleKey("ruleKey"));
185 RuleParamDto rule1Param = db.rules().insertRuleParam(rule1);
187 ActiveRuleDto activeRule1 = db.qualityProfiles().activateRule(activatedQualityProfile, rule1);
188 ActiveRuleParamDto activeRuleParam1 = ActiveRuleParamDto.createFor(rule1Param).setValue(randomAlphanumeric(20));
189 db.getDbClient().activeRuleDao().insertParam(db.getSession(), activeRule1, activeRuleParam1);
190 db.getSession().commit();
192 QProfileDto deactivatedQualityProfile = QualityProfileTesting.newQualityProfileDto();
193 db.qualityProfiles().insert(deactivatedQualityProfile);
194 RuleDto rule2 = db.rules().insert(r -> r.setLanguage("xoo").setRepositoryKey("repo2").setRuleKey("ruleKey2"));
195 RuleParamDto rule2Param = db.rules().insertRuleParam(rule2);
197 ActiveRuleDto activeRule2 = db.qualityProfiles().activateRule(deactivatedQualityProfile, rule2);
198 ActiveRuleParamDto activeRuleParam2 = ActiveRuleParamDto.createFor(rule2Param).setValue(randomAlphanumeric(20));
199 db.getDbClient().activeRuleDao().insertParam(db.getSession(), activeRule2, activeRuleParam2);
200 db.getSession().commit();
202 underTest.publishRuleActivationToSonarLintClients(projectDao, activatedQualityProfile, deactivatedQualityProfile);
204 Deque<PushEventDto> events = getProjectEvents(projectDao.getUuid());
206 assertThat(events).isNotEmpty().hasSize(1);
207 assertThat(events.getFirst())
208 .extracting(PushEventDto::getName, PushEventDto::getLanguage)
209 .contains("RuleSetChanged", "xoo");
211 String ruleSetChangedEvent = new String(events.getFirst().getPayload(), StandardCharsets.UTF_8);
213 assertThat(ruleSetChangedEvent)
214 .contains("\"activatedRules\":[{\"key\":\"repo:ruleKey\"," +
215 "\"language\":\"xoo\",\"severity\":\"" + Common.Severity.forNumber(rule1.getSeverity()).name() + "\"," +
216 "\"params\":[{\"key\":\"" + activeRuleParam1.getKey() + "\",\"value\":\"" + activeRuleParam1.getValue() + "\"}]}]," +
217 "\"deactivatedRules\":[\"repo2:ruleKey2\"]");
220 private void insertQualityProfileLiveMeasure(ComponentDto project, String language, String metricKey) {
221 MetricDto metric = insertMetric(metricKey);
223 Consumer<LiveMeasureDto> configureLiveMeasure = liveMeasure -> liveMeasure
224 .setMetricUuid(metric.getUuid())
225 .setComponentUuid(project.uuid())
226 .setProjectUuid(project.uuid())
227 .setData(language + "=" + nextInt(10));
229 db.measures().insertLiveMeasure(project, metric, configureLiveMeasure);
232 private MetricDto insertMetric(String metricKey) {
233 Consumer<MetricDto> configureMetric = metric -> metric
235 .setValueType(STRING.name())
238 return db.measures().insertMetric(configureMetric);