]> source.dussan.org Git - sonarqube.git/blob
f156feab153e5d016df1dd6dbff9600962fb6a57
[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;
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 java.util.function.Consumer;
31 import java.util.stream.Stream;
32 import javax.annotation.Nullable;
33 import org.junit.jupiter.api.Test;
34 import org.junit.jupiter.api.extension.RegisterExtension;
35 import org.junit.jupiter.params.ParameterizedTest;
36 import org.junit.jupiter.params.provider.Arguments;
37 import org.junit.jupiter.params.provider.MethodSource;
38 import org.sonar.api.impl.utils.AlwaysIncreasingSystem2;
39 import org.sonar.api.rule.RuleKey;
40 import org.sonar.api.rule.RuleStatus;
41 import org.sonar.api.rules.RuleType;
42 import org.sonar.api.utils.System2;
43 import org.sonar.core.util.UuidFactoryFast;
44 import org.sonar.db.DbSession;
45 import org.sonar.db.DbTester;
46 import org.sonar.db.qualityprofile.ActiveRuleDto;
47 import org.sonar.db.qualityprofile.ActiveRuleParamDto;
48 import org.sonar.db.qualityprofile.QProfileDto;
49 import org.sonar.db.qualityprofile.QualityProfileTesting;
50 import org.sonar.db.rule.RuleDto;
51 import org.sonar.db.rule.RuleParamDto;
52 import org.sonar.server.qualityprofile.builtin.QProfileName;
53 import org.sonar.server.common.rule.RuleCreator;
54
55 import static java.nio.charset.StandardCharsets.UTF_8;
56 import static org.assertj.core.api.Assertions.assertThat;
57 import static org.assertj.core.api.Assertions.assertThatThrownBy;
58 import static org.junit.Assert.assertThrows;
59 import static org.mockito.ArgumentMatchers.any;
60 import static org.mockito.ArgumentMatchers.anyList;
61 import static org.mockito.Mockito.mock;
62 import static org.mockito.Mockito.when;
63 import static org.sonar.db.rule.RuleDescriptionSectionDto.createDefaultRuleDescriptionSection;
64 import static org.sonar.db.rule.RuleTesting.newRule;
65 import static org.sonar.db.rule.RuleTesting.newRuleWithoutDescriptionSection;
66
67 class QProfileBackuperImplIT {
68
69   private static final String EMPTY_BACKUP = "<?xml version='1.0' encoding='UTF-8'?>" +
70     "<profile><name>foo</name>" +
71     "<language>js</language>" +
72     "<rules/>" +
73     "</profile>";
74
75   private final System2 system2 = new AlwaysIncreasingSystem2();
76
77   @RegisterExtension
78   private final DbTester db = DbTester.create(system2);
79
80   private final DummyReset reset = new DummyReset();
81   private final QProfileFactory profileFactory = new DummyProfileFactory();
82   private final RuleCreator ruleCreator = mock(RuleCreator.class);
83
84   private final QProfileBackuper underTest = new QProfileBackuperImpl(db.getDbClient(), reset, profileFactory, ruleCreator, new QProfileParser());
85
86   @Test
87   void backup_generates_xml_file() {
88     RuleDto rule = createRule();
89     QProfileDto profile = createProfile(rule.getLanguage());
90     ActiveRuleDto activeRule = activate(profile, rule, ar -> ar.setPrioritizedRule(false));
91
92     StringWriter writer = new StringWriter();
93     underTest.backup(db.getSession(), profile, writer);
94
95     assertThat(writer).hasToString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
96       "<profile><name>" + profile.getName() + "</name>" +
97       "<language>" + profile.getLanguage() + "</language>" +
98       "<rules>" +
99       "<rule>" +
100       "<repositoryKey>" + rule.getRepositoryKey() + "</repositoryKey>" +
101       "<key>" + rule.getRuleKey() + "</key>" +
102       "<type>" + RuleType.valueOf(rule.getType()).name() + "</type>" +
103       "<priority>" + activeRule.getSeverityString() + "</priority>" +
104       "<parameters></parameters>" +
105       "</rule>" +
106       "</rules>" +
107       "</profile>");
108   }
109
110   @Test
111   void backup_prioritized_rule() {
112     RuleDto rule = createRule();
113     QProfileDto profile = createProfile(rule.getLanguage());
114     ActiveRuleDto activeRule = activate(profile, rule, ar -> ar.setPrioritizedRule(true));
115
116     StringWriter writer = new StringWriter();
117     underTest.backup(db.getSession(), profile, writer);
118
119     assertThat(writer).hasToString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
120       "<profile><name>" + profile.getName() + "</name>" +
121       "<language>" + profile.getLanguage() + "</language>" +
122       "<rules>" +
123       "<rule>" +
124       "<repositoryKey>" + rule.getRepositoryKey() + "</repositoryKey>" +
125       "<key>" + rule.getRuleKey() + "</key>" +
126       "<type>" + RuleType.valueOf(rule.getType()).name() + "</type>" +
127       "<priority>" + activeRule.getSeverityString() + "</priority>" +
128       "<prioritizedRule>true</prioritizedRule>" +
129       "<parameters></parameters>" +
130       "</rule>" +
131       "</rules>" +
132       "</profile>");
133   }
134
135   @Test
136   void backup_rules_having_parameters() {
137     RuleDto rule = createRule();
138     RuleParamDto param = db.rules().insertRuleParam(rule);
139     QProfileDto profile = createProfile(rule.getLanguage());
140     ActiveRuleDto activeRule = activate(profile, rule, param);
141
142     StringWriter writer = new StringWriter();
143     underTest.backup(db.getSession(), profile, writer);
144
145     assertThat(writer.toString()).contains(
146       "<rule>" +
147         "<repositoryKey>" + rule.getRepositoryKey() + "</repositoryKey>" +
148         "<key>" + rule.getRuleKey() + "</key>" +
149         "<type>" + RuleType.valueOf(rule.getType()).name() + "</type>" +
150         "<priority>" + activeRule.getSeverityString() + "</priority>" +
151         "<parameters><parameter>" +
152         "<key>" + param.getName() + "</key>" +
153         "<value>20</value>" +
154         "</parameter></parameters>" +
155         "</rule>");
156   }
157
158   @Test
159   void backup_empty_profile() {
160     RuleDto rule = createRule();
161     QProfileDto profile = createProfile(rule.getLanguage());
162
163     StringWriter writer = new StringWriter();
164     underTest.backup(db.getSession(), profile, writer);
165
166     assertThat(writer).hasToString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
167       "<profile><name>" + profile.getName() + "</name>" +
168       "<language>" + profile.getLanguage() + "</language>" +
169       "<rules></rules>" +
170       "</profile>");
171   }
172
173   @Test
174   void backup_custom_rules_with_params() {
175     RuleDto templateRule = db.rules().insert(ruleDefinitionDto -> ruleDefinitionDto
176       .setIsTemplate(true));
177     RuleDto rule = db.rules().insert(
178       newRule(createDefaultRuleDescriptionSection(UuidFactoryFast.getInstance().create(), "custom rule description"))
179         .setName("custom rule name")
180         .setStatus(RuleStatus.READY)
181         .setTemplateUuid(templateRule.getUuid()));
182     RuleParamDto param = db.rules().insertRuleParam(rule);
183     QProfileDto profile = createProfile(rule.getLanguage());
184     ActiveRuleDto activeRule = activate(profile, rule, param);
185
186     StringWriter writer = new StringWriter();
187     underTest.backup(db.getSession(), profile, writer);
188
189     assertThat(writer).hasToString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
190       "<profile>" +
191       "<name>" + profile.getName() + "</name>" +
192       "<language>" + profile.getLanguage() + "</language>" +
193       "<rules><rule>" +
194       "<repositoryKey>" + rule.getRepositoryKey() + "</repositoryKey>" +
195       "<key>" + rule.getKey().rule() + "</key>" +
196       "<type>" + RuleType.valueOf(rule.getType()) + "</type>" +
197       "<priority>" + activeRule.getSeverityString() + "</priority>" +
198       "<name>" + rule.getName() + "</name>" +
199       "<templateKey>" + templateRule.getKey().rule() + "</templateKey>" +
200       "<description>" + rule.getDefaultRuleDescriptionSection().getContent() + "</description>" +
201       "<parameters><parameter>" +
202       "<key>" + param.getName() + "</key>" +
203       "<value>20</value>" +
204       "</parameter></parameters>" +
205       "</rule></rules></profile>");
206   }
207
208   @Test
209   void backup_custom_rules_without_description_section() {
210     var rule = newRuleWithoutDescriptionSection();
211     db.rules().insert(rule);
212     RuleParamDto param = db.rules().insertRuleParam(rule);
213     QProfileDto profile = createProfile(rule.getLanguage());
214     ActiveRuleDto activeRule = activate(profile, rule, param);
215
216     StringWriter writer = new StringWriter();
217     underTest.backup(db.getSession(), profile, writer);
218
219     assertThat(writer).hasToString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
220       "<profile>" +
221       "<name>" + profile.getName() + "</name>" +
222       "<language>" + profile.getLanguage() + "</language>" +
223       "<rules><rule>" +
224       "<repositoryKey>" + rule.getRepositoryKey() + "</repositoryKey>" +
225       "<key>" + rule.getKey().rule() + "</key>" +
226       "<type>" + RuleType.valueOf(rule.getType()) + "</type>" +
227       "<priority>" + activeRule.getSeverityString() + "</priority>" +
228       "<parameters><parameter>" +
229       "<key>" + param.getName() + "</key>" +
230       "<value>20</value>" +
231       "</parameter></parameters>" +
232       "</rule></rules></profile>");
233   }
234
235   @Test
236   void restore_backup_on_the_profile_specified_in_backup() {
237     Reader backup = new StringReader(EMPTY_BACKUP);
238
239     QProfileRestoreSummary summary = underTest.restore(db.getSession(), backup, (String) null);
240
241     assertThat(summary.profile().getName()).isEqualTo("foo");
242     assertThat(summary.profile().getLanguage()).isEqualTo("js");
243
244     assertThat(reset.calledProfile.getKee()).isEqualTo(summary.profile().getKee());
245     assertThat(reset.calledActivations).isEmpty();
246   }
247
248   @Test
249   void restore_detects_deprecated_rule_keys() {
250     String ruleUuid = db.rules().insert(RuleKey.of("sonarjs", "s001")).getUuid();
251     db.rules().insertDeprecatedKey(c -> c.setRuleUuid(ruleUuid).setOldRuleKey("oldkey").setOldRepositoryKey("oldrepo"));
252
253     Reader backup = new StringReader("<?xml version='1.0' encoding='UTF-8'?>" +
254       "<profile><name>foo</name>" +
255       "<language>js</language>" +
256       "<rules>" +
257       "<rule>" +
258       "<repositoryKey>oldrepo</repositoryKey>" +
259       "<key>oldkey</key>" +
260       "<priority>BLOCKER</priority>" +
261       "<parameters>" +
262       "<parameter><key>bar</key><value>baz</value></parameter>" +
263       "</parameters>" +
264       "</rule>" +
265       "</rules>" +
266       "</profile>");
267
268     underTest.restore(db.getSession(), backup, (String) null);
269
270     assertThat(reset.calledActivations).hasSize(1);
271     RuleActivation activation = reset.calledActivations.get(0);
272     assertThat(activation.getSeverity()).isEqualTo("BLOCKER");
273     assertThat(activation.getRuleUuid()).isEqualTo(ruleUuid);
274     assertThat(activation.getParameter("bar")).isEqualTo("baz");
275   }
276
277   @Test
278   void restore_ignores_deprecated_rule_keys_if_new_key_is_already_present() {
279     String ruleUuid = db.rules().insert(RuleKey.of("sonarjs", "s001")).getUuid();
280     db.rules().insertDeprecatedKey(c -> c.setRuleUuid(ruleUuid).setOldRuleKey("oldkey").setOldRepositoryKey("oldrepo"));
281
282     Reader backup = new StringReader("<?xml version='1.0' encoding='UTF-8'?>" +
283       "<profile><name>foo</name>" +
284       "<language>js</language>" +
285       "<rules>" +
286       "<rule>" +
287       "<repositoryKey>oldrepo</repositoryKey>" +
288       "<key>oldkey</key>" +
289       "<priority>MAJOR</priority>" +
290       "<parameters>" +
291       "<parameter><key>bar</key><value>baz</value></parameter>" +
292       "</parameters>" +
293       "</rule>" +
294       "<rule>" +
295       "<repositoryKey>sonarjs</repositoryKey>" +
296       "<key>s001</key>" +
297       "<priority>BLOCKER</priority>" +
298       "<parameters>" +
299       "<parameter><key>bar2</key><value>baz2</value></parameter>" +
300       "</parameters>" +
301       "</rule>" +
302       "</rules>" +
303       "</profile>");
304
305     underTest.restore(db.getSession(), backup, (String) null);
306
307     assertThat(reset.calledActivations).hasSize(1);
308     RuleActivation activation = reset.calledActivations.get(0);
309     assertThat(activation.getSeverity()).isEqualTo("BLOCKER");
310     assertThat(activation.getRuleUuid()).isEqualTo(ruleUuid);
311     assertThat(activation.getParameter("bar2")).isEqualTo("baz2");
312   }
313
314   @Test
315   void restore_backup_on_profile_having_different_name() {
316     Reader backup = new StringReader(EMPTY_BACKUP);
317
318     QProfileRestoreSummary summary = underTest.restore(db.getSession(), backup, "bar");
319
320     assertThat(summary.profile().getName()).isEqualTo("bar");
321     assertThat(summary.profile().getLanguage()).isEqualTo("js");
322
323     assertThat(reset.calledProfile.getKee()).isEqualTo(summary.profile().getKee());
324     assertThat(reset.calledActivations).isEmpty();
325   }
326
327   @Test
328   void restore_resets_the_activated_rules() {
329     String ruleUuid = db.rules().insert(RuleKey.of("sonarjs", "s001")).getUuid();
330     Reader backup = new StringReader("<?xml version='1.0' encoding='UTF-8'?>" +
331       "<profile><name>foo</name>" +
332       "<language>js</language>" +
333       "<rules>" +
334       "<rule>" +
335       "<repositoryKey>sonarjs</repositoryKey>" +
336       "<key>s001</key>" +
337       "<priority>BLOCKER</priority>" +
338       "<prioritizedRule>true</prioritizedRule>" +
339       "<parameters>" +
340       "<parameter><key>bar</key><value>baz</value></parameter>" +
341       "</parameters>" +
342       "</rule>" +
343       "</rules>" +
344       "</profile>");
345
346     underTest.restore(db.getSession(), backup, (String) null);
347
348     assertThat(reset.calledActivations).hasSize(1);
349     RuleActivation activation = reset.calledActivations.get(0);
350     assertThat(activation.getSeverity()).isEqualTo("BLOCKER");
351     assertThat(activation.isPrioritizedRule()).isTrue();
352     assertThat(activation.getRuleUuid()).isEqualTo(ruleUuid);
353     assertThat(activation.getParameter("bar")).isEqualTo("baz");
354   }
355
356   @ParameterizedTest
357   @MethodSource("prioritizedRules")
358   void restore_sets_correctly_the_prioritizedRule_flag(boolean prioritizedInProfile, Boolean prioritizedInBackup, boolean expected) {
359     RuleDto rule = createRule();
360     QProfileDto profile = createProfile(rule.getLanguage());
361     ActiveRuleDto activeRule = activate(profile, rule, ar -> ar.setPrioritizedRule(prioritizedInProfile));
362
363     Reader backup = new StringReader("<?xml version='1.0' encoding='UTF-8'?>" +
364       "<profile><name>" + profile.getName() + "</name>" +
365       "<language>" + profile.getLanguage() + "</language>" +
366       "<rules>" +
367       "<rule>" +
368       "<repositoryKey>" + rule.getRepositoryKey() + "</repositoryKey>" +
369       "<key>" + rule.getRuleKey() + "</key>" +
370       "<type>" + RuleType.valueOf(rule.getType()).name() + "</type>" +
371       "<priority>" + activeRule.getSeverityString() + "</priority>" +
372       (prioritizedInBackup == null ? "" : "<prioritizedRule>" + prioritizedInBackup + "</prioritizedRule>") +
373       "</rule>" +
374       "</rules>" +
375       "</profile>");
376
377     underTest.restore(db.getSession(), backup, (String) null);
378
379     assertThat(reset.calledActivations).hasSize(1);
380     RuleActivation activation = reset.calledActivations.get(0);
381     assertThat(activation.getSeverity()).isEqualTo(activeRule.getSeverityString());
382     assertThat(activation.isPrioritizedRule()).isEqualTo(expected);
383     assertThat(activation.getRuleUuid()).isEqualTo(rule.getUuid());
384   }
385
386   private static Stream<Arguments> prioritizedRules() {
387     return Stream.of(
388       Arguments.of(true, false, false),
389       Arguments.of(true, null, false),
390       Arguments.of(true, true, true),
391       Arguments.of(false, false, false),
392       Arguments.of(false, null, false),
393       Arguments.of(false, true, true)
394     );
395   }
396
397
398   @Test
399   void restore_custom_rule() {
400     when(ruleCreator.create(any(), anyList())).then(invocation -> Collections.singletonList(db.rules().insert(RuleKey.of("sonarjs", "s001"))));
401
402     Reader backup = new StringReader("<?xml version='1.0' encoding='UTF-8'?>" +
403       "<profile>" +
404       "<name>custom rule</name>" +
405       "<language>js</language>" +
406       "<rules><rule>" +
407       "<repositoryKey>sonarjs</repositoryKey>" +
408       "<key>s001</key>" +
409       "<type>CODE_SMELL</type>" +
410       "<priority>CRITICAL</priority>" +
411       "<name>custom rule name</name>" +
412       "<templateKey>rule_mc8</templateKey>" +
413       "<description>custom rule description</description>" +
414       "<parameters><parameter>" +
415       "<key>bar</key>" +
416       "<value>baz</value>" +
417       "</parameter>" +
418       "</parameters>" +
419       "</rule></rules></profile>");
420
421     underTest.restore(db.getSession(), backup, (String) null);
422
423     assertThat(reset.calledActivations).hasSize(1);
424     RuleActivation activation = reset.calledActivations.get(0);
425     assertThat(activation.getSeverity()).isEqualTo("CRITICAL");
426     assertThat(activation.getParameter("bar")).isEqualTo("baz");
427   }
428
429   @Test
430   void restore_skips_rule_without_template_key_and_db_definition() {
431     String ruleUuid = db.rules().insert(RuleKey.of("sonarjs", "s001")).getUuid();
432     Reader backup = new StringReader("<?xml version='1.0' encoding='UTF-8'?>" +
433       "<profile><name>foo</name>" +
434       "<language>js</language>" +
435       "<rules>" +
436       "<rule>" +
437       "<repositoryKey>sonarjs</repositoryKey>" +
438       "<key>s001</key>" +
439       "<priority>BLOCKER</priority>" +
440       "<parameters>" +
441       "<parameter><key>bar</key><value>baz</value></parameter>" +
442       "</parameters>" +
443       "</rule>" +
444       "<rule>" +
445       "<repositoryKey>sonarjs</repositoryKey>" +
446       "<key>s002</key>" +
447       "<priority>MAJOR</priority>" +
448       "</rule>" +
449       "</rules>" +
450       "</profile>");
451
452     underTest.restore(db.getSession(), backup, (String) null);
453
454     assertThat(reset.calledActivations).hasSize(1);
455     RuleActivation activation = reset.calledActivations.get(0);
456     assertThat(activation.getRuleUuid()).isEqualTo(ruleUuid);
457     assertThat(activation.getSeverity()).isEqualTo("BLOCKER");
458     assertThat(activation.getParameter("bar")).isEqualTo("baz");
459   }
460
461   @Test
462   void copy_profile() {
463     RuleDto rule = createRule();
464     RuleParamDto param = db.rules().insertRuleParam(rule);
465     QProfileDto from = createProfile(rule.getLanguage());
466     ActiveRuleDto activeRule = activate(from, rule, param);
467
468     QProfileDto to = createProfile(rule.getLanguage());
469     underTest.copy(db.getSession(), from, to);
470
471     assertThat(reset.calledActivations).extracting(RuleActivation::getRuleUuid).containsOnly(activeRule.getRuleUuid());
472     assertThat(reset.calledActivations.get(0).getParameter(param.getName())).isEqualTo("20");
473     assertThat(reset.calledProfile).isEqualTo(to);
474   }
475
476   @Test
477   void copy_profile_with_custom_rule() {
478     RuleDto templateRule = db.rules().insert(ruleDefinitionDto -> ruleDefinitionDto
479       .setIsTemplate(true));
480     RuleDto rule = db.rules().insert(
481       newRule(createDefaultRuleDescriptionSection(UuidFactoryFast.getInstance().create(), "custom rule description"))
482         .setName("custom rule name")
483         .setStatus(RuleStatus.READY)
484         .setTemplateUuid(templateRule.getUuid()));
485
486     RuleParamDto param = db.rules().insertRuleParam(rule);
487     QProfileDto from = createProfile(rule.getLanguage());
488     ActiveRuleDto activeRule = activate(from, rule, param);
489
490     QProfileDto to = createProfile(rule.getLanguage());
491     underTest.copy(db.getSession(), from, to);
492
493     assertThat(reset.calledActivations).extracting(RuleActivation::getRuleUuid).containsOnly(activeRule.getRuleUuid());
494     assertThat(reset.calledActivations.get(0).getParameter(param.getName())).isEqualTo("20");
495     assertThat(reset.calledProfile).isEqualTo(to);
496   }
497
498   @Test
499   void fail_to_restore_if_bad_xml_format() {
500     DbSession session = db.getSession();
501     StringReader backup = new StringReader("<rules><rule></rules>");
502     IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () -> underTest.restore(session, backup, (String) null));
503     assertThat(thrown).hasMessage("Backup XML is not valid. Root element must be <profile>.");
504     assertThat(reset.calledProfile).isNull();
505   }
506
507   @Test
508   void fail_to_restore_if_not_xml_backup() {
509     DbSession session = db.getSession();
510     StringReader backup = new StringReader("foo");
511     assertThrows(IllegalArgumentException.class, () -> underTest.restore(session, backup, (String) null));
512     assertThat(reset.calledProfile).isNull();
513   }
514
515   @Test
516   void fail_to_restore_if_xml_is_not_well_formed() {
517     assertThatThrownBy(() -> {
518       String notWellFormedXml = "<?xml version='1.0' encoding='UTF-8'?><profile><name>\"profil\"</name><language>\"language\"</language><rules/></profile";
519
520       underTest.restore(db.getSession(), new StringReader(notWellFormedXml), (String) null);
521     })
522       .isInstanceOf(IllegalArgumentException.class)
523       .hasMessage("Fail to restore Quality profile backup, XML document is not well formed");
524   }
525
526   @Test
527   void fail_to_restore_if_duplicate_rule() throws Exception {
528     DbSession session = db.getSession();
529     String xml = Resources.toString(getClass().getResource("QProfileBackuperIT/duplicates-xml-backup.xml"), UTF_8);
530     StringReader backup = new StringReader(xml);
531     IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () -> underTest.restore(session, backup, (String) null));
532     assertThat(thrown).hasMessage("The quality profile cannot be restored as it contains duplicates for the following rules: xoo:x1, xoo:x2");
533     assertThat(reset.calledProfile).isNull();
534   }
535
536   @Test
537   void fail_to_restore_external_rule() {
538     db.rules().insert(RuleKey.of("sonarjs", "s001"), r -> r.setIsExternal(true));
539     Reader backup = new StringReader("<?xml version='1.0' encoding='UTF-8'?>" +
540       "<profile><name>foo</name>" +
541       "<language>js</language>" +
542       "<rules>" +
543       "<rule>" +
544       "<repositoryKey>sonarjs</repositoryKey>" +
545       "<key>s001</key>" +
546       "<priority>BLOCKER</priority>" +
547       "<parameters>" +
548       "<parameter><key>bar</key><value>baz</value></parameter>" +
549       "</parameters>" +
550       "</rule>" +
551       "</rules>" +
552       "</profile>");
553
554     assertThatThrownBy(() -> {
555       underTest.restore(db.getSession(), backup, (String) null);
556     })
557       .isInstanceOf(IllegalArgumentException.class)
558       .hasMessage("The quality profile cannot be restored as it contains rules from external rule engines: sonarjs:s001");
559   }
560
561   private RuleDto createRule() {
562     return db.rules().insert();
563   }
564
565   private QProfileDto createProfile(String language) {
566     return db.qualityProfiles().insert(p -> p.setLanguage(language));
567   }
568
569   private ActiveRuleDto activate(QProfileDto profile, RuleDto rule) {
570     return db.qualityProfiles().activateRule(profile, rule);
571   }
572
573   private ActiveRuleDto activate(QProfileDto profile, RuleDto rule, Consumer<ActiveRuleDto> consumer) {
574     return db.qualityProfiles().activateRule(profile, rule, consumer);
575   }
576
577   private ActiveRuleDto activate(QProfileDto profile, RuleDto rule, RuleParamDto param) {
578     ActiveRuleDto activeRule = db.qualityProfiles().activateRule(profile, rule, ar -> ar.setPrioritizedRule(false));
579     ActiveRuleParamDto dto = ActiveRuleParamDto.createFor(param)
580       .setValue("20")
581       .setActiveRuleUuid(activeRule.getUuid());
582     db.getDbClient().activeRuleDao().insertParam(db.getSession(), activeRule, dto);
583     return activeRule;
584   }
585
586   private static class DummyReset implements QProfileReset {
587     private QProfileDto calledProfile;
588     private List<RuleActivation> calledActivations;
589
590     @Override
591     public BulkChangeResult reset(DbSession dbSession, QProfileDto profile, Collection<RuleActivation> activations) {
592       this.calledProfile = profile;
593       this.calledActivations = new ArrayList<>(activations);
594       return new BulkChangeResult();
595     }
596   }
597
598   private static class DummyProfileFactory implements QProfileFactory {
599     @Override
600     public QProfileDto getOrCreateCustom(DbSession dbSession, QProfileName key) {
601       return QualityProfileTesting.newQualityProfileDto()
602         .setLanguage(key.getLanguage())
603         .setName(key.getName());
604     }
605
606     @Override
607     public QProfileDto checkAndCreateCustom(DbSession dbSession, QProfileName name) {
608       throw new UnsupportedOperationException();
609     }
610
611     @Override
612     public QProfileDto createCustom(DbSession dbSession, QProfileName name, @Nullable String parentKey) {
613       throw new UnsupportedOperationException();
614     }
615
616     @Override
617     public void delete(DbSession dbSession, Collection<QProfileDto> profiles) {
618       throw new UnsupportedOperationException();
619     }
620   }
621 }