]> source.dussan.org Git - sonarqube.git/blob
da9b72c4f17c0e6453b0242660335e7bbaafa974
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2020 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.io.Resources;
23 import java.io.Reader;
24 import java.io.StringReader;
25 import java.io.StringWriter;
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.Collections;
29 import java.util.List;
30 import javax.annotation.Nullable;
31 import org.junit.Rule;
32 import org.junit.Test;
33 import org.junit.rules.ExpectedException;
34 import org.sonar.api.impl.utils.AlwaysIncreasingSystem2;
35 import org.sonar.api.rule.RuleKey;
36 import org.sonar.api.rule.RuleStatus;
37 import org.sonar.api.rules.RuleType;
38 import org.sonar.api.utils.System2;
39 import org.sonar.db.DbSession;
40 import org.sonar.db.DbTester;
41 import org.sonar.db.organization.OrganizationDto;
42 import org.sonar.db.qualityprofile.ActiveRuleDto;
43 import org.sonar.db.qualityprofile.ActiveRuleParamDto;
44 import org.sonar.db.qualityprofile.QProfileDto;
45 import org.sonar.db.qualityprofile.QualityProfileTesting;
46 import org.sonar.db.rule.RuleDefinitionDto;
47 import org.sonar.db.rule.RuleParamDto;
48 import org.sonar.server.rule.RuleCreator;
49
50 import static java.nio.charset.StandardCharsets.UTF_8;
51 import static org.assertj.core.api.Assertions.assertThat;
52 import static org.junit.Assert.fail;
53 import static org.junit.rules.ExpectedException.none;
54 import static org.mockito.ArgumentMatchers.any;
55 import static org.mockito.ArgumentMatchers.anyList;
56 import static org.mockito.Mockito.mock;
57 import static org.mockito.Mockito.when;
58
59 public class QProfileBackuperImplTest {
60
61   private static final String EMPTY_BACKUP = "<?xml version='1.0' encoding='UTF-8'?>" +
62     "<profile><name>foo</name>" +
63     "<language>js</language>" +
64     "<rules/>" +
65     "</profile>";
66
67   private System2 system2 = new AlwaysIncreasingSystem2();
68
69   @Rule
70   public ExpectedException expectedException = none();
71   @Rule
72   public DbTester db = DbTester.create(system2);
73
74   private DummyReset reset = new DummyReset();
75   private QProfileFactory profileFactory = new DummyProfileFactory();
76   private RuleCreator ruleCreator = mock(RuleCreator.class);
77
78   private QProfileBackuper underTest = new QProfileBackuperImpl(db.getDbClient(), reset, profileFactory, ruleCreator, new QProfileParser());
79
80   @Test
81   public void backup_generates_xml_file() {
82     RuleDefinitionDto rule = createRule();
83     QProfileDto profile = createProfile(rule.getLanguage());
84     ActiveRuleDto activeRule = activate(profile, rule);
85
86     StringWriter writer = new StringWriter();
87     underTest.backup(db.getSession(), profile, writer);
88
89     assertThat(writer.toString()).isEqualTo("<?xml version='1.0' encoding='UTF-8'?>" +
90       "<profile><name>" + profile.getName() + "</name>" +
91       "<language>" + profile.getLanguage() + "</language>" +
92       "<rules>" +
93       "<rule>" +
94       "<repositoryKey>" + rule.getRepositoryKey() + "</repositoryKey>" +
95       "<key>" + rule.getRuleKey() + "</key>" +
96       "<type>" + RuleType.valueOf(rule.getType()).name() + "</type>" +
97       "<priority>" + activeRule.getSeverityString() + "</priority>" +
98       "<parameters/>" +
99       "</rule>" +
100       "</rules>" +
101       "</profile>");
102   }
103
104   @Test
105   public void backup_rules_having_parameters() {
106     RuleDefinitionDto rule = createRule();
107     RuleParamDto param = db.rules().insertRuleParam(rule);
108     QProfileDto profile = createProfile(rule.getLanguage());
109     ActiveRuleDto activeRule = activate(profile, rule, param);
110
111     StringWriter writer = new StringWriter();
112     underTest.backup(db.getSession(), profile, writer);
113
114     assertThat(writer.toString()).contains(
115       "<rule>" +
116         "<repositoryKey>" + rule.getRepositoryKey() + "</repositoryKey>" +
117         "<key>" + rule.getRuleKey() + "</key>" +
118         "<type>" + RuleType.valueOf(rule.getType()).name() + "</type>" +
119         "<priority>" + activeRule.getSeverityString() + "</priority>" +
120         "<parameters><parameter>" +
121         "<key>" + param.getName() + "</key>" +
122         "<value>20</value>" +
123         "</parameter></parameters>" +
124         "</rule>");
125   }
126
127   @Test
128   public void backup_empty_profile() {
129     RuleDefinitionDto rule = createRule();
130     QProfileDto profile = createProfile(rule.getLanguage());
131
132     StringWriter writer = new StringWriter();
133     underTest.backup(db.getSession(), profile, writer);
134
135     assertThat(writer.toString()).isEqualTo("<?xml version='1.0' encoding='UTF-8'?>" +
136       "<profile><name>" + profile.getName() + "</name>" +
137       "<language>" + profile.getLanguage() + "</language>" +
138       "<rules/>" +
139       "</profile>");
140   }
141
142   @Test
143   public void backup_custom_rules_with_params() {
144     RuleDefinitionDto templateRule = db.rules().insert(ruleDefinitionDto -> ruleDefinitionDto
145       .setIsTemplate(true));
146     RuleDefinitionDto rule = db.rules().insert(ruleDefinitionDto -> ruleDefinitionDto
147       .setDescription("custom rule description")
148       .setName("custom rule name")
149       .setStatus(RuleStatus.READY)
150       .setTemplateUuid(templateRule.getUuid()));
151     RuleParamDto param = db.rules().insertRuleParam(rule);
152     QProfileDto profile = createProfile(rule.getLanguage());
153     ActiveRuleDto activeRule = activate(profile, rule, param);
154
155     StringWriter writer = new StringWriter();
156     underTest.backup(db.getSession(), profile, writer);
157
158     assertThat(writer.toString()).isEqualTo("<?xml version='1.0' encoding='UTF-8'?>" +
159       "<profile>" +
160       "<name>" + profile.getName() + "</name>" +
161       "<language>" + profile.getLanguage() + "</language>" +
162       "<rules><rule>" +
163       "<repositoryKey>" + rule.getRepositoryKey() + "</repositoryKey>" +
164       "<key>" + rule.getKey().rule() + "</key>" +
165       "<type>" + RuleType.valueOf(rule.getType()) + "</type>" +
166       "<priority>" + activeRule.getSeverityString() + "</priority>" +
167       "<name>" + rule.getName() + "</name>" +
168       "<templateKey>" + templateRule.getKey().rule() + "</templateKey>" +
169       "<description>" + rule.getDescription() + "</description>" +
170       "<parameters><parameter>" +
171       "<key>" + param.getName() + "</key>" +
172       "<value>20</value>" +
173       "</parameter></parameters>" +
174       "</rule></rules></profile>");
175   }
176
177   @Test
178   public void restore_backup_on_the_profile_specified_in_backup() {
179     Reader backup = new StringReader(EMPTY_BACKUP);
180
181     QProfileRestoreSummary summary = underTest.restore(db.getSession(), backup, (String) null);
182
183     assertThat(summary.getProfile().getName()).isEqualTo("foo");
184     assertThat(summary.getProfile().getLanguage()).isEqualTo("js");
185
186     assertThat(reset.calledProfile.getKee()).isEqualTo(summary.getProfile().getKee());
187     assertThat(reset.calledActivations).isEmpty();
188   }
189
190   @Test
191   public void restore_backup_on_profile_having_different_name() {
192     Reader backup = new StringReader(EMPTY_BACKUP);
193
194     QProfileRestoreSummary summary = underTest.restore(db.getSession(), backup, "bar");
195
196     assertThat(summary.getProfile().getName()).isEqualTo("bar");
197     assertThat(summary.getProfile().getLanguage()).isEqualTo("js");
198
199     assertThat(reset.calledProfile.getKee()).isEqualTo(summary.getProfile().getKee());
200     assertThat(reset.calledActivations).isEmpty();
201   }
202
203   @Test
204   public void restore_resets_the_activated_rules() {
205     String ruleUuid = db.rules().insert(RuleKey.of("sonarjs", "s001")).getUuid();
206     Reader backup = new StringReader("<?xml version='1.0' encoding='UTF-8'?>" +
207       "<profile><name>foo</name>" +
208       "<language>js</language>" +
209       "<rules>" +
210       "<rule>" +
211       "<repositoryKey>sonarjs</repositoryKey>" +
212       "<key>s001</key>" +
213       "<priority>BLOCKER</priority>" +
214       "<parameters>" +
215       "<parameter><key>bar</key><value>baz</value></parameter>" +
216       "</parameters>" +
217       "</rule>" +
218       "</rules>" +
219       "</profile>");
220
221     underTest.restore(db.getSession(), backup, (String) null);
222
223     assertThat(reset.calledActivations).hasSize(1);
224     RuleActivation activation = reset.calledActivations.get(0);
225     assertThat(activation.getSeverity()).isEqualTo("BLOCKER");
226     assertThat(activation.getRuleUuid()).isEqualTo(ruleUuid);
227     assertThat(activation.getParameter("bar")).isEqualTo("baz");
228   }
229
230   @Test
231   public void restore_custom_rule() {
232     when(ruleCreator.create(any(), anyList())).then(invocation -> Collections.singletonList(db.rules().insert(RuleKey.of("sonarjs", "s001")).getKey()));
233
234     Reader backup = new StringReader("<?xml version='1.0' encoding='UTF-8'?>" +
235       "<profile>" +
236       "<name>custom rule</name>" +
237       "<language>js</language>" +
238       "<rules><rule>" +
239       "<repositoryKey>sonarjs</repositoryKey>" +
240       "<key>s001</key>" +
241       "<type>CODE_SMELL</type>" +
242       "<priority>CRITICAL</priority>" +
243       "<name>custom rule name</name>" +
244       "<templateKey>rule_mc8</templateKey>" +
245       "<description>custom rule description</description>" +
246       "<parameters><parameter>" +
247       "<key>bar</key>" +
248       "<value>baz</value>" +
249       "</parameter>" +
250       "</parameters>" +
251       "</rule></rules></profile>");
252
253     underTest.restore(db.getSession(), backup, (String) null);
254
255     assertThat(reset.calledActivations).hasSize(1);
256     RuleActivation activation = reset.calledActivations.get(0);
257     assertThat(activation.getSeverity()).isEqualTo("CRITICAL");
258     assertThat(activation.getParameter("bar")).isEqualTo("baz");
259   }
260
261   @Test
262   public void restore_skips_rule_without_template_key_and_db_definition() {
263     String ruleUuid = db.rules().insert(RuleKey.of("sonarjs", "s001")).getUuid();
264     Reader backup = new StringReader("<?xml version='1.0' encoding='UTF-8'?>" +
265       "<profile><name>foo</name>" +
266       "<language>js</language>" +
267       "<rules>" +
268       "<rule>" +
269       "<repositoryKey>sonarjs</repositoryKey>" +
270       "<key>s001</key>" +
271       "<priority>BLOCKER</priority>" +
272       "<parameters>" +
273       "<parameter><key>bar</key><value>baz</value></parameter>" +
274       "</parameters>" +
275       "</rule>" +
276       "<rule>" +
277       "<repositoryKey>sonarjs</repositoryKey>" +
278       "<key>s002</key>" +
279       "<priority>MAJOR</priority>" +
280       "</rule>" +
281       "</rules>" +
282       "</profile>");
283
284     underTest.restore(db.getSession(), backup, (String) null);
285
286     assertThat(reset.calledActivations).hasSize(1);
287     RuleActivation activation = reset.calledActivations.get(0);
288     assertThat(activation.getRuleUuid()).isEqualTo(ruleUuid);
289     assertThat(activation.getSeverity()).isEqualTo("BLOCKER");
290     assertThat(activation.getParameter("bar")).isEqualTo("baz");
291   }
292
293   @Test
294   public void copy_profile() {
295     RuleDefinitionDto rule = createRule();
296     RuleParamDto param = db.rules().insertRuleParam(rule);
297     QProfileDto from = createProfile(rule.getLanguage());
298     ActiveRuleDto activeRule = activate(from, rule, param);
299
300     QProfileDto to = createProfile(rule.getLanguage());
301     underTest.copy(db.getSession(), from, to);
302
303     assertThat(reset.calledActivations).extracting(RuleActivation::getRuleUuid).containsOnly(activeRule.getRuleUuid());
304     assertThat(reset.calledActivations.get(0).getParameter(param.getName())).isEqualTo("20");
305     assertThat(reset.calledProfile).isEqualTo(to);
306   }
307
308   @Test
309   public void fail_to_restore_if_bad_xml_format() {
310     try {
311       underTest.restore(db.getSession(), new StringReader("<rules><rule></rules>"), (String) null);
312       fail();
313     } catch (IllegalArgumentException e) {
314       assertThat(e).hasMessage("Backup XML is not valid. Root element must be <profile>.");
315       assertThat(reset.calledProfile).isNull();
316     }
317   }
318
319   @Test
320   public void fail_to_restore_if_not_xml_backup() {
321     try {
322       underTest.restore(db.getSession(), new StringReader("foo"), (String) null);
323       fail();
324     } catch (IllegalArgumentException e) {
325       assertThat(reset.calledProfile).isNull();
326     }
327   }
328
329   @Test
330   public void fail_to_restore_if_xml_is_not_well_formed() {
331     expectedException.expect(IllegalArgumentException.class);
332     expectedException.expectMessage("Fail to restore Quality profile backup, XML document is not well formed");
333     String notWellFormedXml = "<?xml version='1.0' encoding='UTF-8'?><profile><name>\"profil\"</name><language>\"language\"</language><rules/></profile";
334
335     underTest.restore(db.getSession(), new StringReader(notWellFormedXml), (String) null);
336   }
337
338   @Test
339   public void fail_to_restore_if_duplicate_rule() throws Exception {
340     try {
341       String xml = Resources.toString(getClass().getResource("QProfileBackuperTest/duplicates-xml-backup.xml"), UTF_8);
342       underTest.restore(db.getSession(), new StringReader(xml), (String) null);
343       fail();
344     } catch (IllegalArgumentException e) {
345       assertThat(e).hasMessage("The quality profile cannot be restored as it contains duplicates for the following rules: xoo:x1, xoo:x2");
346       assertThat(reset.calledProfile).isNull();
347     }
348   }
349
350   @Test
351   public void fail_to_restore_external_rule() {
352     db.rules().insert(RuleKey.of("sonarjs", "s001"), r -> r.setIsExternal(true));
353     OrganizationDto organization = db.organizations().getDefaultOrganization();
354     Reader backup = new StringReader("<?xml version='1.0' encoding='UTF-8'?>" +
355       "<profile><name>foo</name>" +
356       "<language>js</language>" +
357       "<rules>" +
358       "<rule>" +
359       "<repositoryKey>sonarjs</repositoryKey>" +
360       "<key>s001</key>" +
361       "<priority>BLOCKER</priority>" +
362       "<parameters>" +
363       "<parameter><key>bar</key><value>baz</value></parameter>" +
364       "</parameters>" +
365       "</rule>" +
366       "</rules>" +
367       "</profile>");
368
369     expectedException.expect(IllegalArgumentException.class);
370     expectedException.expectMessage("The quality profile cannot be restored as it contains rules from external rule engines: sonarjs:s001");
371     underTest.restore(db.getSession(), backup, (String) null);
372   }
373
374   private RuleDefinitionDto createRule() {
375     return db.rules().insert();
376   }
377
378   private QProfileDto createProfile(String language) {
379     return db.qualityProfiles().insert(p -> p.setLanguage(language));
380   }
381
382   private ActiveRuleDto activate(QProfileDto profile, RuleDefinitionDto rule) {
383     return db.qualityProfiles().activateRule(profile, rule);
384   }
385
386   private ActiveRuleDto activate(QProfileDto profile, RuleDefinitionDto rule, RuleParamDto param) {
387     ActiveRuleDto activeRule = db.qualityProfiles().activateRule(profile, rule);
388     ActiveRuleParamDto dto = ActiveRuleParamDto.createFor(param)
389       .setValue("20")
390       .setActiveRuleUuid(activeRule.getUuid());
391     db.getDbClient().activeRuleDao().insertParam(db.getSession(), activeRule, dto);
392     return activeRule;
393   }
394
395   private static class DummyReset implements QProfileReset {
396     private QProfileDto calledProfile;
397     private List<RuleActivation> calledActivations;
398
399     @Override
400     public BulkChangeResult reset(DbSession dbSession, QProfileDto profile, Collection<RuleActivation> activations) {
401       this.calledProfile = profile;
402       this.calledActivations = new ArrayList<>(activations);
403       return new BulkChangeResult();
404     }
405   }
406
407   private static class DummyProfileFactory implements QProfileFactory {
408     @Override
409     public QProfileDto getOrCreateCustom(DbSession dbSession, QProfileName key) {
410       return QualityProfileTesting.newQualityProfileDto()
411         .setLanguage(key.getLanguage())
412         .setName(key.getName());
413     }
414
415     @Override
416     public QProfileDto checkAndCreateCustom(DbSession dbSession, QProfileName name) {
417       throw new UnsupportedOperationException();
418     }
419
420     @Override
421     public QProfileDto createCustom(DbSession dbSession, QProfileName name, @Nullable String parentKey) {
422       throw new UnsupportedOperationException();
423     }
424
425     @Override
426     public void delete(DbSession dbSession, Collection<QProfileDto> profiles) {
427       throw new UnsupportedOperationException();
428     }
429   }
430 }