]> source.dussan.org Git - sonarqube.git/blob
4dfc4c57b0b647f72a8ca5287507ff9e3ff5f34d
[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.builtin;
21
22 import java.util.Collection;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Optional;
26 import javax.annotation.Nullable;
27 import org.assertj.core.groups.Tuple;
28 import org.junit.Before;
29 import org.junit.Rule;
30 import org.junit.Test;
31 import org.sonar.api.impl.utils.TestSystem2;
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.NewBuiltInQualityProfile;
37 import org.sonar.api.utils.System2;
38 import org.sonar.db.DbSession;
39 import org.sonar.db.DbTester;
40 import org.sonar.db.qualityprofile.ActiveRuleDto;
41 import org.sonar.db.qualityprofile.ActiveRuleKey;
42 import org.sonar.db.qualityprofile.ActiveRuleParamDto;
43 import org.sonar.db.qualityprofile.OrgActiveRuleDto;
44 import org.sonar.db.qualityprofile.QProfileDto;
45 import org.sonar.db.qualityprofile.RulesProfileDto;
46 import org.sonar.db.rule.RuleDefinitionDto;
47 import org.sonar.db.rule.RuleParamDto;
48 import org.sonar.server.pushapi.qualityprofile.QualityProfileChangeEventService;
49 import org.sonar.server.qualityprofile.ActiveRuleChange;
50 import org.sonar.server.qualityprofile.ActiveRuleInheritance;
51 import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
52 import org.sonar.server.tester.UserSessionRule;
53 import org.sonar.server.util.IntegerTypeValidation;
54 import org.sonar.server.util.StringTypeValidation;
55 import org.sonar.server.util.TypeValidations;
56
57 import static java.util.Arrays.asList;
58 import static java.util.Collections.emptyMap;
59 import static java.util.Collections.singletonList;
60 import static org.assertj.core.api.Assertions.assertThat;
61 import static org.assertj.core.groups.Tuple.tuple;
62 import static org.mockito.ArgumentMatchers.any;
63 import static org.mockito.ArgumentMatchers.eq;
64 import static org.mockito.Mockito.mock;
65 import static org.mockito.Mockito.verify;
66 import static org.mockito.Mockito.verifyNoInteractions;
67 import static org.mockito.Mockito.verifyNoMoreInteractions;
68 import static org.sonar.api.rules.RulePriority.BLOCKER;
69 import static org.sonar.api.rules.RulePriority.CRITICAL;
70 import static org.sonar.api.rules.RulePriority.MAJOR;
71 import static org.sonar.api.rules.RulePriority.MINOR;
72 import static org.sonar.db.qualityprofile.QualityProfileTesting.newRuleProfileDto;
73 import static org.sonar.server.qualityprofile.ActiveRuleInheritance.INHERITED;
74
75 public class BuiltInQProfileUpdateImplTest {
76
77   private static final long NOW = 1_000;
78   private static final long PAST = NOW - 100;
79
80   @Rule
81   public BuiltInQProfileRepositoryRule builtInProfileRepository = new BuiltInQProfileRepositoryRule();
82   @Rule
83   public DbTester db = DbTester.create();
84   @Rule
85   public UserSessionRule userSession = UserSessionRule.standalone();
86   private System2 system2 = new TestSystem2().setNow(NOW);
87   private ActiveRuleIndexer activeRuleIndexer = mock(ActiveRuleIndexer.class);
88   private TypeValidations typeValidations = new TypeValidations(asList(new StringTypeValidation(), new IntegerTypeValidation()));
89   private QualityProfileChangeEventService qualityProfileChangeEventService = mock(QualityProfileChangeEventService.class);
90   private RuleActivator ruleActivator = new RuleActivator(system2, db.getDbClient(), typeValidations, userSession);
91
92   private BuiltInQProfileUpdateImpl underTest = new BuiltInQProfileUpdateImpl(db.getDbClient(), ruleActivator, activeRuleIndexer,
93     qualityProfileChangeEventService);
94
95   private RulesProfileDto persistedProfile;
96
97   @Before
98   public void setUp() {
99     persistedProfile = newRuleProfileDto(rp -> rp
100       .setIsBuiltIn(true)
101       .setLanguage("xoo")
102       .setRulesUpdatedAt(null));
103     db.getDbClient().qualityProfileDao().insert(db.getSession(), persistedProfile);
104     db.commit();
105   }
106
107   @Test
108   public void activate_new_rules() {
109     RuleDefinitionDto rule1 = db.rules().insert(r -> r.setLanguage("xoo"));
110     RuleDefinitionDto rule2 = db.rules().insert(r -> r.setLanguage("xoo"));
111     BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context();
112     NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile("Sonar way", "xoo");
113     newQp.activateRule(rule1.getRepositoryKey(), rule1.getRuleKey()).overrideSeverity(Severity.CRITICAL);
114     newQp.activateRule(rule2.getRepositoryKey(), rule2.getRuleKey()).overrideSeverity(Severity.MAJOR);
115     newQp.done();
116     BuiltInQProfile builtIn = builtInProfileRepository.create(context.profile("xoo", "Sonar way"), rule1, rule2);
117
118     List<ActiveRuleChange> changes = underTest.update(db.getSession(), builtIn, persistedProfile);
119
120     List<ActiveRuleDto> activeRules = db.getDbClient().activeRuleDao().selectByRuleProfile(db.getSession(), persistedProfile);
121     assertThat(activeRules).hasSize(2);
122     assertThatRuleIsNewlyActivated(activeRules, rule1, CRITICAL);
123     assertThatRuleIsNewlyActivated(activeRules, rule2, MAJOR);
124     assertThatProfileIsMarkedAsUpdated(persistedProfile);
125     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage()));
126   }
127
128   @Test
129   public void already_activated_rule_is_updated_in_case_of_differences() {
130     RuleDefinitionDto rule = db.rules().insert(r -> r.setLanguage("xoo"));
131     BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context();
132     NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile("Sonar way", "xoo");
133     newQp.activateRule(rule.getRepositoryKey(), rule.getRuleKey()).overrideSeverity(Severity.CRITICAL);
134     newQp.done();
135     BuiltInQProfile builtIn = builtInProfileRepository.create(context.profile("xoo", "Sonar way"), rule);
136
137     activateRuleInDb(persistedProfile, rule, BLOCKER);
138
139     List<ActiveRuleChange> changes = underTest.update(db.getSession(), builtIn, persistedProfile);
140
141     List<ActiveRuleDto> activeRules = db.getDbClient().activeRuleDao().selectByRuleProfile(db.getSession(), persistedProfile);
142     assertThat(activeRules).hasSize(1);
143     assertThatRuleIsUpdated(activeRules, rule, CRITICAL);
144     assertThatProfileIsMarkedAsUpdated(persistedProfile);
145     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage()));
146   }
147
148   @Test
149   public void already_activated_rule_is_not_touched_if_no_differences() {
150     RuleDefinitionDto rule = db.rules().insert(r -> r.setLanguage("xoo"));
151     BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context();
152     NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile("Sonar way", "xoo");
153     newQp.activateRule(rule.getRepositoryKey(), rule.getRuleKey()).overrideSeverity(Severity.CRITICAL);
154     newQp.done();
155     BuiltInQProfile builtIn = builtInProfileRepository.create(context.profile("xoo", "Sonar way"), rule);
156
157     activateRuleInDb(persistedProfile, rule, CRITICAL);
158
159     underTest.update(db.getSession(), builtIn, persistedProfile);
160
161     List<ActiveRuleDto> activeRules = db.getDbClient().activeRuleDao().selectByRuleProfile(db.getSession(), persistedProfile);
162     assertThat(activeRules).hasSize(1);
163     assertThatRuleIsUntouched(activeRules, rule, CRITICAL);
164     assertThatProfileIsNotMarkedAsUpdated(persistedProfile);
165     verifyNoInteractions(qualityProfileChangeEventService);
166   }
167
168   @Test
169   public void deactivate_rule_that_is_not_in_built_in_definition_anymore() {
170     RuleDefinitionDto rule1 = db.rules().insert(r -> r.setLanguage("xoo"));
171     RuleDefinitionDto rule2 = db.rules().insert(r -> r.setLanguage("xoo"));
172     BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context();
173     NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile("Sonar way", "xoo");
174     newQp.activateRule(rule2.getRepositoryKey(), rule2.getRuleKey()).overrideSeverity(Severity.MAJOR);
175     newQp.done();
176     BuiltInQProfile builtIn = builtInProfileRepository.create(context.profile("xoo", "Sonar way"), rule1, rule2);
177
178     // built-in definition contains only rule2
179     // so rule1 must be deactivated
180     activateRuleInDb(persistedProfile, rule1, CRITICAL);
181
182     List<ActiveRuleChange> changes = underTest.update(db.getSession(), builtIn, persistedProfile);
183
184     List<ActiveRuleDto> activeRules = db.getDbClient().activeRuleDao().selectByRuleProfile(db.getSession(), persistedProfile);
185     assertThat(activeRules).hasSize(1);
186     assertThatRuleIsDeactivated(activeRules, rule1);
187     assertThatProfileIsMarkedAsUpdated(persistedProfile);
188     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage()));
189   }
190
191   @Test
192   public void activate_deactivate_and_update_three_rules_at_the_same_time() {
193     RuleDefinitionDto rule1 = db.rules().insert(r -> r.setLanguage("xoo"));
194     RuleDefinitionDto rule2 = db.rules().insert(r -> r.setLanguage("xoo"));
195     RuleDefinitionDto rule3 = db.rules().insert(r -> r.setLanguage("xoo"));
196
197     BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context();
198     NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile("Sonar way", "xoo");
199     newQp.activateRule(rule1.getRepositoryKey(), rule1.getRuleKey()).overrideSeverity(Severity.CRITICAL);
200     newQp.activateRule(rule2.getRepositoryKey(), rule2.getRuleKey()).overrideSeverity(Severity.MAJOR);
201     newQp.done();
202     BuiltInQProfile builtIn = builtInProfileRepository.create(context.profile("xoo", "Sonar way"), rule1, rule2);
203
204     // rule1 must be updated (blocker to critical)
205     // rule2 must be activated
206     // rule3 must be deactivated
207     activateRuleInDb(persistedProfile, rule1, BLOCKER);
208     activateRuleInDb(persistedProfile, rule3, BLOCKER);
209
210     List<ActiveRuleChange> changes = underTest.update(db.getSession(), builtIn, persistedProfile);
211
212     List<ActiveRuleDto> activeRules = db.getDbClient().activeRuleDao().selectByRuleProfile(db.getSession(), persistedProfile);
213     assertThat(activeRules).hasSize(2);
214     assertThatRuleIsUpdated(activeRules, rule1, CRITICAL);
215     assertThatRuleIsNewlyActivated(activeRules, rule2, MAJOR);
216     assertThatRuleIsDeactivated(activeRules, rule3);
217     assertThatProfileIsMarkedAsUpdated(persistedProfile);
218     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage()));
219   }
220
221   // SONAR-10473
222   @Test
223   public void activate_rule_on_built_in_profile_resets_severity_to_default_if_not_overridden() {
224     RuleDefinitionDto rule = db.rules().insert(r -> r.setSeverity(Severity.MAJOR).setLanguage("xoo"));
225
226     BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context();
227     NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile("Sonar way", "xoo");
228     newQp.activateRule(rule.getRepositoryKey(), rule.getRuleKey());
229     newQp.done();
230     BuiltInQProfile builtIn = builtInProfileRepository.create(context.profile("xoo", "Sonar way"), rule);
231     underTest.update(db.getSession(), builtIn, persistedProfile);
232
233     List<ActiveRuleDto> activeRules = db.getDbClient().activeRuleDao().selectByRuleProfile(db.getSession(), persistedProfile);
234     assertThatRuleIsNewlyActivated(activeRules, rule, MAJOR);
235
236     // emulate an upgrade of analyzer that changes the default severity of the rule
237     rule.setSeverity(Severity.MINOR);
238     db.rules().update(rule);
239
240     List<ActiveRuleChange> changes = underTest.update(db.getSession(), builtIn, persistedProfile);
241     activeRules = db.getDbClient().activeRuleDao().selectByRuleProfile(db.getSession(), persistedProfile);
242     assertThatRuleIsNewlyActivated(activeRules, rule, MINOR);
243     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage()));
244   }
245
246   @Test
247   public void activate_rule_on_built_in_profile_resets_params_to_default_if_not_overridden() {
248     RuleDefinitionDto rule = db.rules().insert(r -> r.setLanguage("xoo"));
249     RuleParamDto ruleParam = db.rules().insertRuleParam(rule, p -> p.setName("min").setDefaultValue("10"));
250
251     BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context();
252     NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile("Sonar way", rule.getLanguage());
253     newQp.activateRule(rule.getRepositoryKey(), rule.getRuleKey());
254     newQp.done();
255     BuiltInQProfile builtIn = builtInProfileRepository.create(context.profile(newQp.language(), newQp.name()), rule);
256     List<ActiveRuleChange> changes = underTest.update(db.getSession(), builtIn, persistedProfile);
257
258     List<ActiveRuleDto> activeRules = db.getDbClient().activeRuleDao().selectByRuleProfile(db.getSession(), persistedProfile);
259     assertThat(activeRules).hasSize(1);
260     assertThatRuleHasParams(db, activeRules.get(0), tuple("min", "10"));
261     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage()));
262
263     // emulate an upgrade of analyzer that changes the default value of parameter min
264     ruleParam.setDefaultValue("20");
265     db.getDbClient().ruleDao().updateRuleParam(db.getSession(), rule, ruleParam);
266
267     changes = underTest.update(db.getSession(), builtIn, persistedProfile);
268     activeRules = db.getDbClient().activeRuleDao().selectByRuleProfile(db.getSession(), persistedProfile);
269     assertThat(activeRules).hasSize(1);
270     assertThatRuleHasParams(db, activeRules.get(0), tuple("min", "20"));
271     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage()));
272   }
273
274   @Test
275   public void propagate_activation_to_descendant_profiles() {
276     RuleDefinitionDto rule = db.rules().insert(r -> r.setLanguage("xoo"));
277
278     QProfileDto profile = db.qualityProfiles().insert(p -> p.setLanguage(rule.getLanguage()).setIsBuiltIn(true));
279     QProfileDto childProfile = createChildProfile(profile);
280     QProfileDto grandchildProfile = createChildProfile(childProfile);
281
282     BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context();
283     NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile(profile.getName(), profile.getLanguage());
284     newQp.activateRule(rule.getRepositoryKey(), rule.getRuleKey());
285     newQp.done();
286     BuiltInQProfile builtIn = builtInProfileRepository.create(context.profile(profile.getLanguage(), profile.getName()), rule);
287     List<ActiveRuleChange> changes = underTest.update(db.getSession(), builtIn, RulesProfileDto.from(profile));
288
289     assertThat(changes).hasSize(3);
290     assertThatRuleIsActivated(profile, rule, changes, rule.getSeverityString(), null, emptyMap());
291     assertThatRuleIsActivated(childProfile, rule, changes, rule.getSeverityString(), INHERITED, emptyMap());
292     assertThatRuleIsActivated(grandchildProfile, rule, changes, rule.getSeverityString(), INHERITED, emptyMap());
293     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage()));
294   }
295
296   // SONAR-14559
297   @Test
298   public void propagate_rule_update_to_descendant_active_rule() {
299     RuleDefinitionDto rule = db.rules().insert(r -> r.setLanguage("xoo").setSeverity(Severity.BLOCKER));
300
301     QProfileDto parentProfile = db.qualityProfiles().insert(p -> p.setLanguage(rule.getLanguage()).setIsBuiltIn(true));
302     activateRuleInDb(RulesProfileDto.from(parentProfile), rule, RulePriority.valueOf(Severity.MINOR), null);
303
304     QProfileDto childProfile = createChildProfile(parentProfile);
305     activateRuleInDb(RulesProfileDto.from(childProfile), rule, RulePriority.valueOf(Severity.MINOR), INHERITED);
306
307     BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context();
308     NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile(parentProfile.getName(), parentProfile.getLanguage());
309     newQp.activateRule(rule.getRepositoryKey(), rule.getRuleKey());
310     newQp.done();
311     BuiltInQProfile builtIn = builtInProfileRepository.create(context.profile(parentProfile.getLanguage(), parentProfile.getName()), rule);
312     List<ActiveRuleChange> changes = underTest.update(db.getSession(), builtIn, RulesProfileDto.from(parentProfile));
313
314     assertThat(changes).hasSize(2);
315
316     List<ActiveRuleDto> parentActiveRules = db.getDbClient().activeRuleDao().selectByRuleProfile(db.getSession(), RulesProfileDto.from(parentProfile));
317     assertThatRuleIsUpdated(parentActiveRules, rule, RulePriority.BLOCKER, null);
318
319     List<ActiveRuleDto> childActiveRules = db.getDbClient().activeRuleDao().selectByRuleProfile(db.getSession(), RulesProfileDto.from(childProfile));
320     assertThatRuleIsUpdated(childActiveRules, rule, RulePriority.BLOCKER, INHERITED);
321     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage()));
322   }
323
324   @Test
325   public void propagate_rule_param_update_to_descendant_active_rule_params() {
326     RuleDefinitionDto rule = db.rules().insert(r -> r.setLanguage("xoo").setSeverity(Severity.BLOCKER));
327     RuleParamDto ruleParam = db.rules().insertRuleParam(rule, p -> p.setName("min").setDefaultValue("10"));
328
329     QProfileDto parentProfile = db.qualityProfiles().insert(p -> p.setLanguage(rule.getLanguage()).setIsBuiltIn(true));
330     ActiveRuleDto parentActiveRuleDto = activateRuleInDb(RulesProfileDto.from(parentProfile), rule,
331         RulePriority.valueOf(Severity.MINOR), null);
332     activateRuleParamInDb(parentActiveRuleDto, ruleParam, "20");
333
334     QProfileDto childProfile = createChildProfile(parentProfile);
335     ActiveRuleDto childActiveRuleDto = activateRuleInDb(RulesProfileDto.from(childProfile), rule,
336         RulePriority.valueOf(Severity.MINOR), INHERITED);
337     activateRuleParamInDb(childActiveRuleDto, ruleParam, "20");
338
339     BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context();
340     NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile(parentProfile.getName(), parentProfile.getLanguage());
341     newQp.activateRule(rule.getRepositoryKey(), rule.getRuleKey());
342     newQp.done();
343     BuiltInQProfile builtIn = builtInProfileRepository.create(context.profile(parentProfile.getLanguage(), parentProfile.getName()), rule);
344     List<ActiveRuleChange> changes = underTest.update(db.getSession(), builtIn, RulesProfileDto.from(parentProfile));
345
346     assertThat(changes).hasSize(2);
347
348     assertThatRuleHasParams(db, parentActiveRuleDto, tuple("min", "10"));
349     assertThatRuleHasParams(db, childActiveRuleDto, tuple("min", "10"));
350     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage()));
351   }
352
353   @Test
354   public void do_not_load_descendants_if_no_changes() {
355     RuleDefinitionDto rule = db.rules().insert(r -> r.setLanguage("xoo"));
356
357     QProfileDto profile = db.qualityProfiles().insert(p -> p.setLanguage(rule.getLanguage()).setIsBuiltIn(true));
358     QProfileDto childProfile = createChildProfile(profile);
359
360     BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context();
361     NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile(profile.getName(), profile.getLanguage());
362     newQp.activateRule(rule.getRepositoryKey(), rule.getRuleKey());
363     newQp.done();
364
365     // first run
366     BuiltInQProfile builtIn = builtInProfileRepository.create(context.profile(profile.getLanguage(), profile.getName()), rule);
367     List<ActiveRuleChange> changes = underTest.update(db.getSession(), builtIn, RulesProfileDto.from(profile));
368     assertThat(changes).hasSize(2).extracting(ActiveRuleChange::getType).containsOnly(ActiveRuleChange.Type.ACTIVATED);
369     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage()));
370
371     // second run, without any input changes
372     RuleActivator ruleActivatorWithoutDescendants = new RuleActivator(system2, db.getDbClient(), typeValidations, userSession) {
373       @Override
374       DescendantProfilesSupplier createDescendantProfilesSupplier(DbSession dbSession) {
375         return (profiles, ruleIds) -> {
376           throw new IllegalStateException("BOOM - descendants should not be loaded");
377         };
378       }
379     };
380     changes = new BuiltInQProfileUpdateImpl(db.getDbClient(), ruleActivatorWithoutDescendants, activeRuleIndexer, qualityProfileChangeEventService)
381       .update(db.getSession(), builtIn, RulesProfileDto.from(profile));
382     assertThat(changes).isEmpty();
383     verifyNoMoreInteractions(qualityProfileChangeEventService);
384   }
385
386   @Test
387   public void propagate_deactivation_to_descendant_profiles() {
388     RuleDefinitionDto rule = db.rules().insert(r -> r.setLanguage("xoo"));
389
390     QProfileDto profile = db.qualityProfiles().insert(p -> p.setLanguage(rule.getLanguage()).setIsBuiltIn(true));
391     QProfileDto childProfile = createChildProfile(profile);
392     QProfileDto grandChildProfile = createChildProfile(childProfile);
393
394     BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context();
395     NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile(profile.getName(), profile.getLanguage());
396     newQp.activateRule(rule.getRepositoryKey(), rule.getRuleKey());
397     newQp.done();
398
399     // first run to activate the rule
400     BuiltInQProfile builtIn = builtInProfileRepository.create(context.profile(profile.getLanguage(), profile.getName()), rule);
401     List<ActiveRuleChange> changes = underTest.update(db.getSession(), builtIn, RulesProfileDto.from(profile));
402     assertThat(changes).hasSize(3).extracting(ActiveRuleChange::getType).containsOnly(ActiveRuleChange.Type.ACTIVATED);
403     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage()));
404
405     // second run to deactivate the rule
406     context = new BuiltInQualityProfilesDefinition.Context();
407     NewBuiltInQualityProfile updatedQp = context.createBuiltInQualityProfile(profile.getName(), profile.getLanguage());
408     updatedQp.done();
409     builtIn = builtInProfileRepository.create(context.profile(profile.getLanguage(), profile.getName()), rule);
410     changes = underTest.update(db.getSession(), builtIn, RulesProfileDto.from(profile));
411     assertThat(changes).hasSize(3).extracting(ActiveRuleChange::getType).containsOnly(ActiveRuleChange.Type.DEACTIVATED);
412     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage()));
413
414     assertThatRuleIsDeactivated(profile, rule);
415     assertThatRuleIsDeactivated(childProfile, rule);
416     assertThatRuleIsDeactivated(grandChildProfile, rule);
417   }
418
419   private QProfileDto createChildProfile(QProfileDto parent) {
420     return db.qualityProfiles().insert(p -> p
421       .setLanguage(parent.getLanguage())
422       .setParentKee(parent.getKee())
423       .setName("Child of " + parent.getName()))
424       .setIsBuiltIn(false);
425   }
426
427   private void assertThatRuleIsActivated(QProfileDto profile, RuleDefinitionDto rule, @Nullable List<ActiveRuleChange> changes,
428     String expectedSeverity, @Nullable ActiveRuleInheritance expectedInheritance, Map<String, String> expectedParams) {
429     OrgActiveRuleDto activeRule = db.getDbClient().activeRuleDao().selectByProfile(db.getSession(), profile)
430       .stream()
431       .filter(ar -> ar.getRuleKey().equals(rule.getKey()))
432       .findFirst()
433       .orElseThrow(IllegalStateException::new);
434
435     assertThat(activeRule.getSeverityString()).isEqualTo(expectedSeverity);
436     assertThat(activeRule.getInheritance()).isEqualTo(expectedInheritance != null ? expectedInheritance.name() : null);
437
438     List<ActiveRuleParamDto> params = db.getDbClient().activeRuleDao().selectParamsByActiveRuleUuid(db.getSession(), activeRule.getUuid());
439     assertThat(params).hasSize(expectedParams.size());
440
441     if (changes != null) {
442       ActiveRuleChange change = changes.stream()
443         .filter(c -> c.getActiveRule().getUuid().equals(activeRule.getUuid()))
444         .findFirst().orElseThrow(IllegalStateException::new);
445       assertThat(change.getInheritance()).isEqualTo(expectedInheritance);
446       assertThat(change.getSeverity()).isEqualTo(expectedSeverity);
447       assertThat(change.getType()).isEqualTo(ActiveRuleChange.Type.ACTIVATED);
448     }
449   }
450
451   private static void assertThatRuleHasParams(DbTester db, ActiveRuleDto activeRule, Tuple... expectedParams) {
452     assertThat(db.getDbClient().activeRuleDao().selectParamsByActiveRuleUuid(db.getSession(), activeRule.getUuid()))
453       .extracting(ActiveRuleParamDto::getKey, ActiveRuleParamDto::getValue)
454       .containsExactlyInAnyOrder(expectedParams);
455   }
456
457   private static void assertThatRuleIsNewlyActivated(List<ActiveRuleDto> activeRules, RuleDefinitionDto rule, RulePriority severity) {
458     ActiveRuleDto activeRule = findRule(activeRules, rule).get();
459
460     assertThat(activeRule.getInheritance()).isNull();
461     assertThat(activeRule.getSeverityString()).isEqualTo(severity.name());
462     assertThat(activeRule.getCreatedAt()).isEqualTo(NOW);
463     assertThat(activeRule.getUpdatedAt()).isEqualTo(NOW);
464   }
465
466   private static void assertThatRuleIsUpdated(List<ActiveRuleDto> activeRules, RuleDefinitionDto rule, RulePriority severity, @Nullable ActiveRuleInheritance expectedInheritance) {
467     ActiveRuleDto activeRule = findRule(activeRules, rule).get();
468
469     if (expectedInheritance != null) {
470       assertThat(activeRule.getInheritance()).isEqualTo(expectedInheritance.name());
471     } else {
472       assertThat(activeRule.getInheritance()).isNull();
473     }
474     assertThat(activeRule.getSeverityString()).isEqualTo(severity.name());
475     assertThat(activeRule.getCreatedAt()).isEqualTo(PAST);
476     assertThat(activeRule.getUpdatedAt()).isEqualTo(NOW);
477   }
478
479   private static void assertThatRuleIsUpdated(List<ActiveRuleDto> activeRules, RuleDefinitionDto rule, RulePriority severity) {
480     assertThatRuleIsUpdated(activeRules, rule, severity, null);
481   }
482
483   private static void assertThatRuleIsUpdated(ActiveRuleDto activeRules, RuleDefinitionDto rule, RulePriority severity, @Nullable ActiveRuleInheritance expectedInheritance) {
484     assertThatRuleIsUpdated(singletonList(activeRules), rule, severity, expectedInheritance);
485   }
486
487   private static void assertThatRuleIsUntouched(List<ActiveRuleDto> activeRules, RuleDefinitionDto rule, RulePriority severity) {
488     ActiveRuleDto activeRule = findRule(activeRules, rule).get();
489
490     assertThat(activeRule.getInheritance()).isNull();
491     assertThat(activeRule.getSeverityString()).isEqualTo(severity.name());
492     assertThat(activeRule.getCreatedAt()).isEqualTo(PAST);
493     assertThat(activeRule.getUpdatedAt()).isEqualTo(PAST);
494   }
495
496   private void assertThatRuleIsDeactivated(QProfileDto profile, RuleDefinitionDto rule) {
497     Collection<ActiveRuleDto> activeRules = db.getDbClient().activeRuleDao().selectByRulesAndRuleProfileUuids(
498       db.getSession(), singletonList(rule.getUuid()), singletonList(profile.getRulesProfileUuid()));
499     assertThat(activeRules).isEmpty();
500   }
501
502   private static void assertThatRuleIsDeactivated(List<ActiveRuleDto> activeRules, RuleDefinitionDto rule) {
503     assertThat(findRule(activeRules, rule)).isEmpty();
504   }
505
506   private void assertThatProfileIsMarkedAsUpdated(RulesProfileDto dto) {
507     RulesProfileDto reloaded = db.getDbClient().qualityProfileDao().selectBuiltInRuleProfiles(db.getSession())
508       .stream()
509       .filter(p -> p.getUuid().equals(dto.getUuid()))
510       .findFirst()
511       .get();
512     assertThat(reloaded.getRulesUpdatedAt()).isNotEmpty();
513   }
514
515   private void assertThatProfileIsNotMarkedAsUpdated(RulesProfileDto dto) {
516     RulesProfileDto reloaded = db.getDbClient().qualityProfileDao().selectBuiltInRuleProfiles(db.getSession())
517       .stream()
518       .filter(p -> p.getUuid().equals(dto.getUuid()))
519       .findFirst()
520       .get();
521     assertThat(reloaded.getRulesUpdatedAt()).isNull();
522   }
523
524   private static Optional<ActiveRuleDto> findRule(List<ActiveRuleDto> activeRules, RuleDefinitionDto rule) {
525     return activeRules.stream()
526       .filter(ar -> ar.getRuleKey().equals(rule.getKey()))
527       .findFirst();
528   }
529
530   private ActiveRuleDto activateRuleInDb(RulesProfileDto profile, RuleDefinitionDto rule, RulePriority severity) {
531     return activateRuleInDb(profile, rule, severity, null);
532   }
533
534   private ActiveRuleDto activateRuleInDb(RulesProfileDto ruleProfile, RuleDefinitionDto rule, RulePriority severity, @Nullable ActiveRuleInheritance inheritance) {
535     ActiveRuleDto dto = new ActiveRuleDto()
536       .setKey(ActiveRuleKey.of(ruleProfile, RuleKey.of(rule.getRepositoryKey(), rule.getRuleKey())))
537       .setProfileUuid(ruleProfile.getUuid())
538       .setSeverity(severity.name())
539       .setRuleUuid(rule.getUuid())
540       .setInheritance(inheritance != null ? inheritance.name() : null)
541       .setCreatedAt(PAST)
542       .setUpdatedAt(PAST);
543     db.getDbClient().activeRuleDao().insert(db.getSession(), dto);
544     db.commit();
545     return dto;
546   }
547
548   private void activateRuleParamInDb(ActiveRuleDto activeRuleDto, RuleParamDto ruleParamDto, String value) {
549     ActiveRuleParamDto dto = new ActiveRuleParamDto()
550         .setActiveRuleUuid(activeRuleDto.getUuid())
551         .setRulesParameterUuid(ruleParamDto.getUuid())
552         .setKey(ruleParamDto.getName())
553         .setValue(value);
554     db.getDbClient().activeRuleDao().insertParam(db.getSession(), activeRuleDto, dto);
555     db.commit();
556   }
557 }