]> source.dussan.org Git - sonarqube.git/blob
729ded73f01c588f9522b4118bfc3f459240c879
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2022 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
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.
10  *
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.
15  *
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.
19  */
20 package org.sonar.server.qualityprofile;
21
22 import com.google.common.collect.Multimap;
23 import java.security.SecureRandom;
24 import java.util.Arrays;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Random;
28 import java.util.function.Consumer;
29 import org.junit.Rule;
30 import org.junit.Test;
31 import org.mockito.ArgumentCaptor;
32 import org.sonar.api.rule.RuleKey;
33 import org.sonar.api.rule.Severity;
34 import org.sonar.api.rules.RulePriority;
35 import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition;
36 import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition.BuiltInActiveRule;
37 import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition.NewBuiltInQualityProfile;
38 import org.sonar.api.utils.System2;
39 import org.sonar.api.utils.log.LogTester;
40 import org.sonar.core.util.UuidFactoryFast;
41 import org.sonar.core.util.stream.MoreCollectors;
42 import org.sonar.db.DbClient;
43 import org.sonar.db.DbTester;
44 import org.sonar.db.qualityprofile.ActiveRuleDto;
45 import org.sonar.db.qualityprofile.QProfileDto;
46 import org.sonar.db.qualityprofile.RulesProfileDto;
47 import org.sonar.db.rule.RuleDto;
48 import org.sonar.server.pushapi.qualityprofile.QualityProfileChangeEventService;
49 import org.sonar.server.qualityprofile.builtin.BuiltInQProfile;
50 import org.sonar.server.qualityprofile.builtin.BuiltInQProfileInsert;
51 import org.sonar.server.qualityprofile.builtin.BuiltInQProfileInsertImpl;
52 import org.sonar.server.qualityprofile.builtin.BuiltInQProfileRepositoryRule;
53 import org.sonar.server.qualityprofile.builtin.BuiltInQProfileUpdate;
54 import org.sonar.server.qualityprofile.builtin.BuiltInQProfileUpdateImpl;
55 import org.sonar.server.qualityprofile.builtin.BuiltInQualityProfilesUpdateListener;
56 import org.sonar.server.qualityprofile.builtin.QProfileName;
57 import org.sonar.server.qualityprofile.builtin.RuleActivator;
58 import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
59 import org.sonar.server.rule.DefaultRuleFinder;
60 import org.sonar.server.rule.ServerRuleFinder;
61 import org.sonar.server.rule.index.RuleIndex;
62 import org.sonar.server.tester.UserSessionRule;
63 import org.sonar.server.util.TypeValidations;
64
65 import static com.google.common.base.Preconditions.checkState;
66 import static java.util.Collections.singleton;
67 import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
68 import static org.apache.commons.lang.math.RandomUtils.nextLong;
69 import static org.assertj.core.api.Assertions.assertThat;
70 import static org.assertj.core.api.Assertions.tuple;
71 import static org.mockito.ArgumentMatchers.any;
72 import static org.mockito.ArgumentMatchers.anyLong;
73 import static org.mockito.ArgumentMatchers.eq;
74 import static org.mockito.Mockito.mock;
75 import static org.mockito.Mockito.verify;
76 import static org.mockito.Mockito.verifyNoInteractions;
77 import static org.mockito.Mockito.when;
78 import static org.sonar.api.rules.RulePriority.MAJOR;
79 import static org.sonar.db.qualityprofile.QualityProfileTesting.newRuleProfileDto;
80 import static org.sonar.server.language.LanguageTesting.newLanguage;
81 import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.ACTIVATED;
82 import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.DEACTIVATED;
83 import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.UPDATED;
84
85 public class RegisterQualityProfilesNotificationTest {
86
87   private static final Random RANDOM = new SecureRandom();
88
89   private System2 system2 = mock(System2.class);
90   @Rule
91   public DbTester db = DbTester.create(system2);
92   @Rule
93   public UserSessionRule userSessionRule = UserSessionRule.standalone();
94   @Rule
95   public BuiltInQProfileRepositoryRule builtInQProfileRepositoryRule = new BuiltInQProfileRepositoryRule();
96   @Rule
97   public LogTester logTester = new LogTester();
98
99   private DbClient dbClient = db.getDbClient();
100   private TypeValidations typeValidations = mock(TypeValidations.class);
101   private ActiveRuleIndexer activeRuleIndexer = mock(ActiveRuleIndexer.class);
102   private QualityProfileChangeEventService qualityProfileChangeEventService = mock(QualityProfileChangeEventService.class);
103   private ServerRuleFinder ruleFinder = new DefaultRuleFinder(dbClient);
104   private BuiltInQProfileInsert builtInQProfileInsert = new BuiltInQProfileInsertImpl(dbClient, ruleFinder, system2, UuidFactoryFast.getInstance(),
105     typeValidations, activeRuleIndexer);
106   private RuleActivator ruleActivator = new RuleActivator(system2, dbClient, typeValidations, userSessionRule);
107   private QProfileRules qProfileRules = new QProfileRulesImpl(dbClient, ruleActivator, mock(RuleIndex.class), activeRuleIndexer, qualityProfileChangeEventService);
108   private BuiltInQProfileUpdate builtInQProfileUpdate = new BuiltInQProfileUpdateImpl(dbClient, ruleActivator, activeRuleIndexer, qualityProfileChangeEventService);
109   private BuiltInQualityProfilesUpdateListener builtInQualityProfilesNotification = mock(BuiltInQualityProfilesUpdateListener.class);
110   private RegisterQualityProfiles underTest = new RegisterQualityProfiles(builtInQProfileRepositoryRule, dbClient,
111     builtInQProfileInsert, builtInQProfileUpdate, builtInQualityProfilesNotification, system2);
112
113   @Test
114   public void do_not_send_notification_on_new_profile() {
115     String language = newLanguageKey();
116     builtInQProfileRepositoryRule.add(newLanguage(language), "Sonar way");
117     builtInQProfileRepositoryRule.initialize();
118
119     underTest.start();
120
121     verifyNoInteractions(builtInQualityProfilesNotification);
122   }
123
124   @Test
125   public void do_not_send_notification_when_profile_is_not_updated() {
126     String language = newLanguageKey();
127     RuleDto dbRule = db.rules().insert(r -> r.setLanguage(language));
128     RulesProfileDto dbProfile = insertBuiltInProfile(language);
129     activateRuleInDb(dbProfile, dbRule, MAJOR);
130     addPluginProfile(dbProfile, dbRule);
131     builtInQProfileRepositoryRule.initialize();
132
133     underTest.start();
134
135     verifyNoInteractions(builtInQualityProfilesNotification);
136   }
137
138   @Test
139   public void send_notification_when_a_new_rule_is_activated() {
140     String language = newLanguageKey();
141     RuleDto existingRule = db.rules().insert(r -> r.setLanguage(language));
142     RulesProfileDto dbProfile = insertBuiltInProfile(language);
143     activateRuleInDb(dbProfile, existingRule, MAJOR);
144     RuleDto newRule = db.rules().insert(r -> r.setLanguage(language));
145     addPluginProfile(dbProfile, existingRule, newRule);
146     builtInQProfileRepositoryRule.initialize();
147
148     underTest.start();
149
150     ArgumentCaptor<Multimap> captor = ArgumentCaptor.forClass(Multimap.class);
151     verify(builtInQualityProfilesNotification).onChange(captor.capture(), anyLong(), anyLong());
152     Multimap<QProfileName, ActiveRuleChange> updatedProfiles = captor.getValue();
153     assertThat(updatedProfiles.keySet())
154       .extracting(QProfileName::getName, QProfileName::getLanguage)
155       .containsExactlyInAnyOrder(tuple(dbProfile.getName(), dbProfile.getLanguage()));
156     assertThat(updatedProfiles.values())
157       .extracting(value -> value.getActiveRule().getRuleUuid(), ActiveRuleChange::getType)
158       .containsExactlyInAnyOrder(tuple(newRule.getUuid(), ACTIVATED));
159   }
160
161   @Test
162   public void send_notification_when_a_rule_is_deactivated() {
163     String language = newLanguageKey();
164     RuleDto existingRule = db.rules().insert(r -> r.setLanguage(language));
165     RulesProfileDto dbProfile = insertBuiltInProfile(language);
166     activateRuleInDb(dbProfile, existingRule, MAJOR);
167     addPluginProfile(dbProfile);
168     builtInQProfileRepositoryRule.initialize();
169
170     underTest.start();
171
172     ArgumentCaptor<Multimap> captor = ArgumentCaptor.forClass(Multimap.class);
173     verify(builtInQualityProfilesNotification).onChange(captor.capture(), anyLong(), anyLong());
174     Multimap<QProfileName, ActiveRuleChange> updatedProfiles = captor.getValue();
175     assertThat(updatedProfiles.keySet())
176       .extracting(QProfileName::getName, QProfileName::getLanguage)
177       .containsExactlyInAnyOrder(tuple(dbProfile.getName(), dbProfile.getLanguage()));
178     assertThat(updatedProfiles.values())
179       .extracting(value -> value.getActiveRule().getRuleUuid(), ActiveRuleChange::getType)
180       .containsExactlyInAnyOrder(tuple(existingRule.getUuid(), DEACTIVATED));
181   }
182
183   @Test
184   public void send_a_single_notification_when_multiple_rules_are_activated() {
185     String language = newLanguageKey();
186
187     RuleDto existingRule1 = db.rules().insert(r -> r.setLanguage(language));
188     RuleDto newRule1 = db.rules().insert(r -> r.setLanguage(language));
189     RulesProfileDto dbProfile1 = insertBuiltInProfile(language);
190     activateRuleInDb(dbProfile1, existingRule1, MAJOR);
191     addPluginProfile(dbProfile1, existingRule1, newRule1);
192
193     RuleDto existingRule2 = db.rules().insert(r -> r.setLanguage(language));
194     RuleDto newRule2 = db.rules().insert(r -> r.setLanguage(language));
195     RulesProfileDto dbProfile2 = insertBuiltInProfile(language);
196     activateRuleInDb(dbProfile2, existingRule2, MAJOR);
197     addPluginProfile(dbProfile2, existingRule2, newRule2);
198     builtInQProfileRepositoryRule.initialize();
199
200     underTest.start();
201
202     ArgumentCaptor<Multimap> captor = ArgumentCaptor.forClass(Multimap.class);
203     verify(builtInQualityProfilesNotification).onChange(captor.capture(), anyLong(), anyLong());
204     Multimap<QProfileName, ActiveRuleChange> updatedProfiles = captor.getValue();
205     assertThat(updatedProfiles.keySet())
206       .extracting(QProfileName::getName, QProfileName::getLanguage)
207       .containsExactlyInAnyOrder(
208         tuple(dbProfile1.getName(), dbProfile1.getLanguage()),
209         tuple(dbProfile2.getName(), dbProfile2.getLanguage()));
210     assertThat(updatedProfiles.values())
211       .extracting(value -> value.getActiveRule().getRuleUuid(), ActiveRuleChange::getType)
212       .containsExactlyInAnyOrder(
213         tuple(newRule1.getUuid(), ACTIVATED),
214         tuple(newRule2.getUuid(), ACTIVATED));
215   }
216
217   @Test
218   public void notification_does_not_include_inherited_profiles_when_rule_is_added() {
219     String language = newLanguageKey();
220     RuleDto newRule = db.rules().insert(r -> r.setLanguage(language));
221
222     QProfileDto builtInQProfileDto = insertProfile(orgQProfile -> orgQProfile.setIsBuiltIn(true).setLanguage(language));
223     QProfileDto childQProfileDto = insertProfile(orgQProfile -> orgQProfile.setIsBuiltIn(false).setLanguage(language).setParentKee(builtInQProfileDto.getKee()));
224     addPluginProfile(builtInQProfileDto, newRule);
225     builtInQProfileRepositoryRule.initialize();
226
227     underTest.start();
228
229     ArgumentCaptor<Multimap> captor = ArgumentCaptor.forClass(Multimap.class);
230     verify(builtInQualityProfilesNotification).onChange(captor.capture(), anyLong(), anyLong());
231     Multimap<QProfileName, ActiveRuleChange> updatedProfiles = captor.getValue();
232     assertThat(updatedProfiles.keySet())
233       .extracting(QProfileName::getName, QProfileName::getLanguage)
234       .containsExactlyInAnyOrder(tuple(builtInQProfileDto.getName(), builtInQProfileDto.getLanguage()));
235     assertThat(updatedProfiles.values())
236       .extracting(value -> value.getActiveRule().getRuleUuid(), ActiveRuleChange::getType)
237       .containsExactlyInAnyOrder(tuple(newRule.getUuid(), ACTIVATED));
238   }
239
240   @Test
241   public void notification_does_not_include_inherited_profiled_when_rule_is_changed() {
242     String language = newLanguageKey();
243     RuleDto rule = db.rules().insert(r -> r.setLanguage(language).setSeverity(Severity.MINOR));
244
245     QProfileDto builtInProfile = insertProfile(orgQProfile -> orgQProfile.setIsBuiltIn(true).setLanguage(language));
246     db.qualityProfiles().activateRule(builtInProfile, rule, ar -> ar.setSeverity(Severity.MINOR));
247     QProfileDto childProfile = insertProfile(orgQProfile -> orgQProfile.setIsBuiltIn(false).setLanguage(language).setParentKee(builtInProfile.getKee()));
248     db.qualityProfiles().activateRule(childProfile, rule, ar -> ar.setInheritance(ActiveRuleDto.INHERITED).setSeverity(Severity.MINOR));
249     addPluginProfile(builtInProfile, rule);
250     builtInQProfileRepositoryRule.initialize();
251     db.commit();
252
253     underTest.start();
254
255     ArgumentCaptor<Multimap> captor = ArgumentCaptor.forClass(Multimap.class);
256     verify(builtInQualityProfilesNotification).onChange(captor.capture(), anyLong(), anyLong());
257     Multimap<QProfileName, ActiveRuleChange> updatedProfiles = captor.getValue();
258     assertThat(updatedProfiles.keySet())
259       .extracting(QProfileName::getName, QProfileName::getLanguage)
260       .containsExactlyInAnyOrder(tuple(builtInProfile.getName(), builtInProfile.getLanguage()));
261     assertThat(updatedProfiles.values())
262       .extracting(value -> value.getActiveRule().getRuleUuid(), ActiveRuleChange::getType)
263       .containsExactlyInAnyOrder(tuple(rule.getUuid(), UPDATED));
264   }
265
266   @Test
267   public void notification_does_not_include_inherited_profiles_when_rule_is_deactivated() {
268     String language = newLanguageKey();
269     RuleDto rule = db.rules().insert(r -> r.setLanguage(language).setSeverity(Severity.MINOR));
270
271     QProfileDto builtInQProfileDto = insertProfile(orgQProfile -> orgQProfile.setIsBuiltIn(true).setLanguage(language));
272     db.qualityProfiles().activateRule(builtInQProfileDto, rule);
273     QProfileDto childQProfileDto = insertProfile(orgQProfile -> orgQProfile.setIsBuiltIn(false).setLanguage(language).setParentKee(builtInQProfileDto.getKee()));
274     qProfileRules.activateAndCommit(db.getSession(), childQProfileDto, singleton(RuleActivation.create(rule.getUuid())));
275     db.commit();
276
277     addPluginProfile(builtInQProfileDto);
278     builtInQProfileRepositoryRule.initialize();
279
280     underTest.start();
281
282     ArgumentCaptor<Multimap> captor = ArgumentCaptor.forClass(Multimap.class);
283     verify(builtInQualityProfilesNotification).onChange(captor.capture(), anyLong(), anyLong());
284     Multimap<QProfileName, ActiveRuleChange> updatedProfiles = captor.getValue();
285     assertThat(updatedProfiles.keySet())
286       .extracting(QProfileName::getName, QProfileName::getLanguage)
287       .containsExactlyInAnyOrder(tuple(builtInQProfileDto.getName(), builtInQProfileDto.getLanguage()));
288     assertThat(updatedProfiles.values())
289       .extracting(value -> value.getActiveRule().getRuleUuid(), ActiveRuleChange::getType)
290       .containsExactlyInAnyOrder(tuple(rule.getUuid(), DEACTIVATED));
291   }
292
293   @Test
294   public void notification_contains_send_start_and_end_date() {
295     String language = newLanguageKey();
296     RuleDto existingRule = db.rules().insert(r -> r.setLanguage(language));
297     RulesProfileDto dbProfile = insertBuiltInProfile(language);
298     activateRuleInDb(dbProfile, existingRule, MAJOR);
299     RuleDto newRule = db.rules().insert(r -> r.setLanguage(language));
300     addPluginProfile(dbProfile, existingRule, newRule);
301     builtInQProfileRepositoryRule.initialize();
302     long startDate = RANDOM.nextInt(5000);
303     long endDate = startDate + RANDOM.nextInt(5000);
304     when(system2.now()).thenReturn(startDate, endDate);
305
306     underTest.start();
307
308     verify(builtInQualityProfilesNotification).onChange(any(), eq(startDate), eq(endDate));
309   }
310
311   private void addPluginProfile(RulesProfileDto dbProfile, RuleDto... dbRules) {
312     BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context();
313     NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile(dbProfile.getName(), dbProfile.getLanguage());
314
315     Arrays.stream(dbRules).forEach(dbRule -> newQp.activateRule(dbRule.getRepositoryKey(), dbRule.getRuleKey()).overrideSeverity(Severity.MAJOR));
316     newQp.done();
317     List<BuiltInActiveRule> rules = context.profile(dbProfile.getLanguage(), dbProfile.getName()).rules();
318     BuiltInQProfile.ActiveRule[] activeRules = toActiveRules(rules, dbRules);
319     builtInQProfileRepositoryRule.add(newLanguage(dbProfile.getLanguage()), dbProfile.getName(), false, activeRules);
320   }
321
322   private static BuiltInQProfile.ActiveRule[] toActiveRules(List<BuiltInActiveRule> rules, RuleDto[] dbRules) {
323     Map<RuleKey, RuleDto> dbRulesByRuleKey = Arrays.stream(dbRules)
324       .collect(MoreCollectors.uniqueIndex(RuleDto::getKey));
325     return rules.stream()
326       .map(r -> {
327         RuleKey ruleKey = RuleKey.of(r.repoKey(), r.ruleKey());
328         RuleDto ruleDefinitionDto = dbRulesByRuleKey.get(ruleKey);
329         checkState(ruleDefinitionDto != null, "Rule '%s' not found", ruleKey);
330         return new BuiltInQProfile.ActiveRule(ruleDefinitionDto.getUuid(), r);
331       }).toArray(BuiltInQProfile.ActiveRule[]::new);
332   }
333
334   private void addPluginProfile(QProfileDto profile, RuleDto... dbRules) {
335     BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context();
336     NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile(profile.getName(), profile.getLanguage());
337
338     Arrays.stream(dbRules).forEach(dbRule -> newQp.activateRule(dbRule.getRepositoryKey(), dbRule.getRuleKey()).overrideSeverity(Severity.MAJOR));
339     newQp.done();
340     BuiltInQProfile.ActiveRule[] activeRules = toActiveRules(context.profile(profile.getLanguage(), profile.getName()).rules(), dbRules);
341     builtInQProfileRepositoryRule.add(newLanguage(profile.getLanguage()), profile.getName(), false, activeRules);
342   }
343
344   private RulesProfileDto insertBuiltInProfile(String language) {
345     RulesProfileDto ruleProfileDto = newRuleProfileDto(rp -> rp.setIsBuiltIn(true).setLanguage(language));
346     db.getDbClient().qualityProfileDao().insert(db.getSession(), ruleProfileDto);
347     db.commit();
348     return ruleProfileDto;
349   }
350
351   private void activateRuleInDb(RulesProfileDto profile, RuleDto rule, RulePriority severity) {
352     ActiveRuleDto dto = new ActiveRuleDto()
353       .setProfileUuid(profile.getUuid())
354       .setSeverity(severity.name())
355       .setRuleUuid(rule.getUuid())
356       .setCreatedAt(nextLong())
357       .setUpdatedAt(nextLong());
358     db.getDbClient().activeRuleDao().insert(db.getSession(), dto);
359     db.commit();
360   }
361
362   private QProfileDto insertProfile(Consumer<QProfileDto> consumer) {
363     QProfileDto builtInQProfileDto = db.qualityProfiles().insert(consumer);
364     db.commit();
365     return builtInQProfileDto;
366   }
367
368   private static String newLanguageKey() {
369     return randomAlphanumeric(20).toLowerCase();
370   }
371 }