]> source.dussan.org Git - sonarqube.git/blob
7cf8aec8d4bf4774b89b633f9f9d08e0a12ccdb6
[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 java.util.List;
23 import java.util.Map;
24 import java.util.Optional;
25 import javax.annotation.Nullable;
26 import org.junit.Rule;
27 import org.junit.Test;
28 import org.sonar.api.impl.utils.AlwaysIncreasingSystem2;
29 import org.sonar.api.rule.RuleStatus;
30 import org.sonar.api.rule.Severity;
31 import org.sonar.api.utils.System2;
32 import org.sonar.db.DbTester;
33 import org.sonar.db.qualityprofile.ActiveRuleParamDto;
34 import org.sonar.db.qualityprofile.OrgActiveRuleDto;
35 import org.sonar.db.qualityprofile.QProfileDto;
36 import org.sonar.db.rule.RuleDto;
37 import org.sonar.server.es.EsTester;
38 import org.sonar.server.exceptions.BadRequestException;
39 import org.sonar.server.pushapi.qualityprofile.QualityProfileChangeEventService;
40 import org.sonar.server.qualityprofile.builtin.RuleActivator;
41 import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
42 import org.sonar.server.tester.UserSessionRule;
43 import org.sonar.server.util.IntegerTypeValidation;
44 import org.sonar.server.util.StringTypeValidation;
45 import org.sonar.server.util.TypeValidations;
46
47 import static java.util.Arrays.asList;
48 import static java.util.Collections.emptyMap;
49 import static java.util.Collections.singleton;
50 import static org.assertj.core.api.Assertions.assertThat;
51 import static org.assertj.core.api.Assertions.assertThatThrownBy;
52 import static org.mockito.ArgumentMatchers.any;
53 import static org.mockito.ArgumentMatchers.anyList;
54 import static org.mockito.ArgumentMatchers.eq;
55 import static org.mockito.Mockito.mock;
56 import static org.mockito.Mockito.times;
57 import static org.mockito.Mockito.verify;
58 import static org.sonar.api.rule.Severity.BLOCKER;
59 import static org.sonar.server.qualityprofile.ActiveRuleInheritance.INHERITED;
60
61 public class QProfileTreeImplTest {
62
63   private System2 system2 = new AlwaysIncreasingSystem2();
64   @Rule
65   public DbTester db = DbTester.create(system2);
66   @Rule
67   public EsTester es = EsTester.create();
68   @Rule
69   public UserSessionRule userSession = UserSessionRule.standalone();
70   private ActiveRuleIndexer activeRuleIndexer = new ActiveRuleIndexer(db.getDbClient(), es.client());
71   private TypeValidations typeValidations = new TypeValidations(asList(new StringTypeValidation(), new IntegerTypeValidation()));
72   private QualityProfileChangeEventService qualityProfileChangeEventService = mock(QualityProfileChangeEventService.class);
73   private RuleActivator ruleActivator = new RuleActivator(system2, db.getDbClient(), typeValidations, userSession);
74   private QProfileRules qProfileRules = new QProfileRulesImpl(db.getDbClient(), ruleActivator, null, activeRuleIndexer, qualityProfileChangeEventService);
75   private QProfileTree underTest = new QProfileTreeImpl(db.getDbClient(), ruleActivator, System2.INSTANCE, activeRuleIndexer, mock(QualityProfileChangeEventService.class));
76
77   @Test
78   public void set_itself_as_parent_fails() {
79     RuleDto rule = createRule();
80     QProfileDto profile = createProfile(rule);
81
82     assertThatThrownBy(() -> underTest.setParentAndCommit(db.getSession(), profile, profile))
83       .isInstanceOf(BadRequestException.class)
84       .hasMessageContaining(" can not be selected as parent of ");
85   }
86
87   @Test
88   public void set_child_as_parent_fails() {
89     RuleDto rule = createRule();
90     QProfileDto parentProfile = createProfile(rule);
91     QProfileDto childProfile = createChildProfile(parentProfile);
92
93     assertThatThrownBy(() -> underTest.setParentAndCommit(db.getSession(), parentProfile, childProfile))
94       .isInstanceOf(BadRequestException.class)
95       .hasMessageContaining(" can not be selected as parent of ");
96   }
97
98   @Test
99   public void set_grandchild_as_parent_fails() {
100     RuleDto rule = createRule();
101     QProfileDto parentProfile = createProfile(rule);
102     QProfileDto childProfile = createChildProfile(parentProfile);
103     QProfileDto grandchildProfile = createChildProfile(childProfile);
104
105     assertThatThrownBy(() -> underTest.setParentAndCommit(db.getSession(), parentProfile, grandchildProfile))
106       .isInstanceOf(BadRequestException.class)
107       .hasMessageContaining(" can not be selected as parent of ");
108   }
109
110   @Test
111   public void cannot_set_parent_if_language_is_different() {
112     RuleDto rule1 = db.rules().insert(r -> r.setLanguage("foo"));
113     RuleDto rule2 = db.rules().insert(r -> r.setLanguage("bar"));
114
115     QProfileDto parentProfile = createProfile(rule1);
116     List<ActiveRuleChange> changes = activate(parentProfile, RuleActivation.create(rule1.getUuid()));
117     assertThat(changes).hasSize(1);
118
119     QProfileDto childProfile = createProfile(rule2);
120     changes = activate(childProfile, RuleActivation.create(rule2.getUuid()));
121     assertThat(changes).hasSize(1);
122
123     assertThatThrownBy(() -> underTest.setParentAndCommit(db.getSession(), childProfile, parentProfile))
124       .isInstanceOf(BadRequestException.class)
125       .hasMessageContaining("Cannot set the profile");
126   }
127
128   @Test
129   public void set_then_unset_parent() {
130     RuleDto rule1 = createJavaRule();
131     RuleDto rule2 = createJavaRule();
132
133     QProfileDto profile1 = createProfile(rule1);
134     List<ActiveRuleChange> changes = activate(profile1, RuleActivation.create(rule1.getUuid()));
135     assertThat(changes).hasSize(1);
136
137     QProfileDto profile2 = createProfile(rule2);
138     changes = activate(profile2, RuleActivation.create(rule2.getUuid()));
139     assertThat(changes).hasSize(1);
140
141     changes = underTest.setParentAndCommit(db.getSession(), profile2, profile1);
142     assertThat(changes).hasSize(1);
143     assertThatRuleIsActivated(profile2, rule1, changes, rule1.getSeverityString(), INHERITED, emptyMap());
144     assertThatRuleIsActivated(profile2, rule2, null, rule2.getSeverityString(), null, emptyMap());
145     verify(qualityProfileChangeEventService, times(2)).distributeRuleChangeEvent(any(), any(), eq(profile2.getLanguage()));
146
147     changes = underTest.removeParentAndCommit(db.getSession(), profile2);
148     assertThat(changes).hasSize(1);
149     assertThatRuleIsActivated(profile2, rule2, null, rule2.getSeverityString(), null, emptyMap());
150     assertThatRuleIsNotPresent(profile2, rule1);
151     verify(qualityProfileChangeEventService, times(2)).distributeRuleChangeEvent(any(), any(), eq(profile2.getLanguage()));
152   }
153
154   @Test
155   public void set_then_unset_parent_keep_overridden_rules() {
156     RuleDto rule1 = createJavaRule();
157     RuleDto rule2 = createJavaRule();
158     QProfileDto profile1 = createProfile(rule1);
159     List<ActiveRuleChange> changes = activate(profile1, RuleActivation.create(rule1.getUuid()));
160     assertThat(changes).hasSize(1);
161
162     QProfileDto profile2 = createProfile(rule2);
163     changes = activate(profile2, RuleActivation.create(rule2.getUuid()));
164     assertThat(changes).hasSize(1);
165
166     changes = underTest.setParentAndCommit(db.getSession(), profile2, profile1);
167     assertThat(changes).hasSize(1);
168     assertThatRuleIsActivated(profile2, rule1, changes, rule1.getSeverityString(), INHERITED, emptyMap());
169     assertThatRuleIsActivated(profile2, rule2, null, rule2.getSeverityString(), null, emptyMap());
170     verify(qualityProfileChangeEventService, times(2)).distributeRuleChangeEvent(any(), any(), eq(profile2.getLanguage()));
171
172     RuleActivation activation = RuleActivation.create(rule1.getUuid(), BLOCKER, null);
173     changes = activate(profile2, activation);
174     assertThat(changes).hasSize(1);
175     assertThatRuleIsUpdated(profile2, rule1, BLOCKER, ActiveRuleInheritance.OVERRIDES, emptyMap());
176     assertThatRuleIsActivated(profile2, rule2, null, rule2.getSeverityString(), null, emptyMap());
177
178     changes = underTest.removeParentAndCommit(db.getSession(), profile2);
179     assertThat(changes).hasSize(1);
180     // Not testing changes here since severity is not set in changelog
181     assertThatRuleIsActivated(profile2, rule1, null, BLOCKER, null, emptyMap());
182     assertThatRuleIsActivated(profile2, rule2, null, rule2.getSeverityString(), null, emptyMap());
183     verify(qualityProfileChangeEventService, times(3)).distributeRuleChangeEvent(anyList(), any(), eq(profile2.getLanguage()));
184   }
185
186   @Test
187   public void activation_errors_are_ignored_when_setting_a_parent() {
188     RuleDto rule1 = createJavaRule();
189     RuleDto rule2 = createJavaRule();
190     QProfileDto parentProfile = createProfile(rule1);
191     activate(parentProfile, RuleActivation.create(rule1.getUuid()));
192     activate(parentProfile, RuleActivation.create(rule2.getUuid()));
193
194     rule1.setStatus(RuleStatus.REMOVED);
195     db.rules().update(rule1);
196
197     QProfileDto childProfile = createProfile(rule1);
198     List<ActiveRuleChange> changes = underTest.setParentAndCommit(db.getSession(), childProfile, parentProfile);
199     verify(qualityProfileChangeEventService, times(2)).distributeRuleChangeEvent(any(), any(), eq(childProfile.getLanguage()));
200
201     assertThatRuleIsNotPresent(childProfile, rule1);
202     assertThatRuleIsActivated(childProfile, rule2, changes, rule2.getSeverityString(), INHERITED, emptyMap());
203   }
204
205   private List<ActiveRuleChange> activate(QProfileDto profile, RuleActivation activation) {
206     return qProfileRules.activateAndCommit(db.getSession(), profile, singleton(activation));
207   }
208
209   private QProfileDto createProfile(RuleDto rule) {
210     return db.qualityProfiles().insert(p -> p.setLanguage(rule.getLanguage()));
211   }
212
213   private QProfileDto createChildProfile(QProfileDto parent) {
214     return db.qualityProfiles().insert(p -> p
215       .setLanguage(parent.getLanguage())
216       .setParentKee(parent.getKee())
217       .setName("Child of " + parent.getName()));
218   }
219
220   private void assertThatRuleIsActivated(QProfileDto profile, RuleDto rule, @Nullable List<ActiveRuleChange> changes,
221     String expectedSeverity, @Nullable ActiveRuleInheritance expectedInheritance, Map<String, String> expectedParams) {
222     OrgActiveRuleDto activeRule = db.getDbClient().activeRuleDao().selectByProfile(db.getSession(), profile)
223       .stream()
224       .filter(ar -> ar.getRuleKey().equals(rule.getKey()))
225       .findFirst()
226       .orElseThrow(IllegalStateException::new);
227
228     assertThat(activeRule.getSeverityString()).isEqualTo(expectedSeverity);
229     assertThat(activeRule.getInheritance()).isEqualTo(expectedInheritance != null ? expectedInheritance.name() : null);
230
231     List<ActiveRuleParamDto> params = db.getDbClient().activeRuleDao().selectParamsByActiveRuleUuid(db.getSession(), activeRule.getUuid());
232     assertThat(params).hasSize(expectedParams.size());
233
234     if (changes != null) {
235       ActiveRuleChange change = changes.stream()
236         .filter(c -> c.getActiveRule().getUuid().equals(activeRule.getUuid()))
237         .findFirst().orElseThrow(IllegalStateException::new);
238       assertThat(change.getInheritance()).isEqualTo(expectedInheritance);
239       assertThat(change.getSeverity()).isEqualTo(expectedSeverity);
240       assertThat(change.getType()).isEqualTo(ActiveRuleChange.Type.ACTIVATED);
241     }
242   }
243
244   private void assertThatRuleIsNotPresent(QProfileDto profile, RuleDto rule) {
245     Optional<OrgActiveRuleDto> activeRule = db.getDbClient().activeRuleDao().selectByProfile(db.getSession(), profile)
246       .stream()
247       .filter(ar -> ar.getRuleKey().equals(rule.getKey()))
248       .findFirst();
249
250     assertThat(activeRule).isEmpty();
251   }
252
253   private void assertThatRuleIsUpdated(QProfileDto profile, RuleDto rule,
254     String expectedSeverity, @Nullable ActiveRuleInheritance expectedInheritance, Map<String, String> expectedParams) {
255     OrgActiveRuleDto activeRule = db.getDbClient().activeRuleDao().selectByProfile(db.getSession(), profile)
256       .stream()
257       .filter(ar -> ar.getRuleKey().equals(rule.getKey()))
258       .findFirst()
259       .orElseThrow(IllegalStateException::new);
260
261     assertThat(activeRule.getSeverityString()).isEqualTo(expectedSeverity);
262     assertThat(activeRule.getInheritance()).isEqualTo(expectedInheritance != null ? expectedInheritance.name() : null);
263
264     List<ActiveRuleParamDto> params = db.getDbClient().activeRuleDao().selectParamsByActiveRuleUuid(db.getSession(), activeRule.getUuid());
265     assertThat(params).hasSize(expectedParams.size());
266   }
267
268   private RuleDto createRule() {
269     return db.rules().insert(r -> r.setSeverity(Severity.MAJOR));
270   }
271
272   private RuleDto createJavaRule() {
273     return db.rules().insert(r -> r.setSeverity(Severity.MAJOR).setLanguage("java"));
274   }
275 }