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