diff options
author | OrlovAlexander <alexander.orlov@sonarsource.com> | 2024-10-14 09:03:02 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2024-10-16 20:03:02 +0000 |
commit | c63283fce767013e11ca6bf1c26170a11c0d48da (patch) | |
tree | cd01b86e456639d5149ab9854b4ef65a363a6cf0 /server/sonar-webserver-webapi | |
parent | a49dcbfad564db95c05d538d7aff110a33db12a1 (diff) | |
download | sonarqube-c63283fce767013e11ca6bf1c26170a11c0d48da.tar.gz sonarqube-c63283fce767013e11ca6bf1c26170a11c0d48da.zip |
SONAR-23250 Support impacts in QP export-import
Diffstat (limited to 'server/sonar-webserver-webapi')
7 files changed, 259 insertions, 4 deletions
diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualityprofile/QProfileBackuperImplIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualityprofile/QProfileBackuperImplIT.java index 71f271bd7ae..286ca058acb 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualityprofile/QProfileBackuperImplIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualityprofile/QProfileBackuperImplIT.java @@ -27,6 +27,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.function.Consumer; import java.util.stream.Stream; import javax.annotation.Nullable; @@ -49,8 +50,8 @@ import org.sonar.db.qualityprofile.QProfileDto; import org.sonar.db.qualityprofile.QualityProfileTesting; import org.sonar.db.rule.RuleDto; import org.sonar.db.rule.RuleParamDto; -import org.sonar.server.qualityprofile.builtin.QProfileName; import org.sonar.server.common.rule.RuleCreator; +import org.sonar.server.qualityprofile.builtin.QProfileName; import static java.nio.charset.StandardCharsets.UTF_8; import static org.assertj.core.api.Assertions.assertThat; @@ -60,6 +61,10 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static org.sonar.api.issue.impact.Severity.BLOCKER; +import static org.sonar.api.issue.impact.Severity.INFO; +import static org.sonar.api.issue.impact.SoftwareQuality.MAINTAINABILITY; +import static org.sonar.api.issue.impact.SoftwareQuality.SECURITY; import static org.sonar.db.rule.RuleDescriptionSectionDto.createDefaultRuleDescriptionSection; import static org.sonar.db.rule.RuleTesting.newRule; import static org.sonar.db.rule.RuleTesting.newRuleWithoutDescriptionSection; @@ -101,6 +106,12 @@ class QProfileBackuperImplIT { "<key>" + rule.getRuleKey() + "</key>" + "<type>" + RuleType.valueOf(rule.getType()).name() + "</type>" + "<priority>" + activeRule.getSeverityString() + "</priority>" + + "<impacts>" + + "<impact>" + + "<softwareQuality>MAINTAINABILITY</softwareQuality>" + + "<severity>HIGH</severity>" + + "</impact>" + + "</impacts>" + "<parameters></parameters>" + "</rule>" + "</rules>" + @@ -125,6 +136,12 @@ class QProfileBackuperImplIT { "<key>" + rule.getRuleKey() + "</key>" + "<type>" + RuleType.valueOf(rule.getType()).name() + "</type>" + "<priority>" + activeRule.getSeverityString() + "</priority>" + + "<impacts>" + + "<impact>" + + "<softwareQuality>MAINTAINABILITY</softwareQuality>" + + "<severity>HIGH</severity>" + + "</impact>" + + "</impacts>" + "<prioritizedRule>true</prioritizedRule>" + "<parameters></parameters>" + "</rule>" + @@ -148,6 +165,12 @@ class QProfileBackuperImplIT { "<key>" + rule.getRuleKey() + "</key>" + "<type>" + RuleType.valueOf(rule.getType()).name() + "</type>" + "<priority>" + activeRule.getSeverityString() + "</priority>" + + "<impacts>" + + "<impact>" + + "<softwareQuality>MAINTAINABILITY</softwareQuality>" + + "<severity>HIGH</severity>" + + "</impact>" + + "</impacts>" + "<parameters><parameter>" + "<key>" + param.getName() + "</key>" + "<value>20</value>" + @@ -195,6 +218,12 @@ class QProfileBackuperImplIT { "<key>" + rule.getKey().rule() + "</key>" + "<type>" + RuleType.valueOf(rule.getType()) + "</type>" + "<priority>" + activeRule.getSeverityString() + "</priority>" + + "<impacts>" + + "<impact>" + + "<softwareQuality>MAINTAINABILITY</softwareQuality>" + + "<severity>HIGH</severity>" + + "</impact>" + + "</impacts>" + "<name>" + rule.getName() + "</name>" + "<templateKey>" + templateRule.getKey().rule() + "</templateKey>" + "<description>" + rule.getDefaultRuleDescriptionSection().getContent() + "</description>" + @@ -225,6 +254,12 @@ class QProfileBackuperImplIT { "<key>" + rule.getKey().rule() + "</key>" + "<type>" + RuleType.valueOf(rule.getType()) + "</type>" + "<priority>" + activeRule.getSeverityString() + "</priority>" + + "<impacts>" + + "<impact>" + + "<softwareQuality>MAINTAINABILITY</softwareQuality>" + + "<severity>HIGH</severity>" + + "</impact>" + + "</impacts>" + "<parameters><parameter>" + "<key>" + param.getName() + "</key>" + "<value>20</value>" + @@ -459,6 +494,48 @@ class QProfileBackuperImplIT { } @Test + void restore_should_override_impacts(){ + String ruleUuid = db.rules().insert(RuleKey.of("sonarjs", "s001")).getUuid(); + + Reader backup = new StringReader("<?xml version='1.0' encoding='UTF-8'?>" + + "<profile><name>foo</name>" + + "<language>js</language>" + + "<rules>" + + "<rule>" + + "<repositoryKey>sonarjs</repositoryKey>" + + "<key>s001</key>" + + "<priority>BLOCKER</priority>" + + "<impacts>" + + "<impact>" + + "<softwareQuality>MAINTAINABILITY</softwareQuality>" + + "<severity>BLOCKER</severity>" + + "</impact>" + + "<impact>" + + "<softwareQuality>SECURITY</softwareQuality>" + + "<severity>INFO</severity>" + + "</impact>" + + "</impacts>" + + "<parameters>" + + "<parameter><key>bar</key><value>baz</value></parameter>" + + "</parameters>" + + "</rule>" + + "</rules>" + + "</profile>"); + + underTest.restore(db.getSession(), backup, (String) null); + + assertThat(reset.calledActivations).hasSize(1); + RuleActivation activation = reset.calledActivations.get(0); + assertThat(activation.getSeverity()).isEqualTo("BLOCKER"); + assertThat(activation.getRuleUuid()).isEqualTo(ruleUuid); + assertThat(activation.getImpactSeverities()).isEqualTo(Map.of(MAINTAINABILITY, BLOCKER, SECURITY, INFO)); + db.getDbClient().activeRuleDao().selectByRuleUuid(db.getSession(), ruleUuid).forEach(ar -> { + assertThat(ar.getSeverityString()).isEqualTo("BLOCKER"); + assertThat(ar.getImpactsString()).isEqualTo("{\"MAINTAINABILITY\":\"BLOCKER\",\"SECURITY\":\"INFO\"}"); + }); + } + + @Test void copy_profile() { RuleDto rule = createRule(); RuleParamDto param = db.rules().insertRuleParam(rule); diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ImportedRule.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ImportedRule.java index 410fe724eaf..544a41022a4 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ImportedRule.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ImportedRule.java @@ -20,7 +20,7 @@ package org.sonar.server.qualityprofile; import java.util.Map; - +import org.sonar.api.issue.impact.SoftwareQuality; import org.sonar.api.rule.RuleKey; class ImportedRule { @@ -32,9 +32,11 @@ class ImportedRule { private String name = null; private String type = null; private String severity = null; + private Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> impacts = Map.of(); private Boolean prioritizedRule = false; private String description = null; private Map<String, String> parameters = null; + public Map<String, String> getParameters() { return parameters; } @@ -59,6 +61,10 @@ class ImportedRule { return severity; } + public Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> getImpacts() { + return impacts; + } + public Boolean getPrioritizedRule() { return prioritizedRule; } @@ -77,6 +83,11 @@ class ImportedRule { return this; } + ImportedRule setImpacts(Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> impacts) { + this.impacts = impacts; + return this; + } + public ImportedRule setPrioritizedRule(Boolean prioritizedRule) { this.prioritizedRule = prioritizedRule; return this; diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileBackuperImpl.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileBackuperImpl.java index 4c709b0a285..e6f8b9b78e4 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileBackuperImpl.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileBackuperImpl.java @@ -50,6 +50,7 @@ import org.sonar.server.qualityprofile.builtin.QProfileName; import static com.google.common.base.Preconditions.checkArgument; import static java.util.function.Function.identity; import static java.util.stream.Collectors.toSet; +import static org.sonar.server.qualityprofile.QProfileUtils.parseImpactsToMap; @ServerSide public class QProfileBackuperImpl implements QProfileBackuper { @@ -95,6 +96,7 @@ public class QProfileBackuperImpl implements QProfileBackuper { importedRule.setRepository(ruleKey.repository()); importedRule.setKey(ruleKey.rule()); importedRule.setSeverity(exportRuleDto.getSeverityString()); + importedRule.setImpacts(exportRuleDto.getImpacts() != null ? parseImpactsToMap(exportRuleDto.getImpacts()) : Map.of()); if (exportRuleDto.isCustomRule()) { importedRule.setTemplate(exportRuleDto.getTemplateRuleKey().rule()); importedRule.setDescription(exportRuleDto.getDescriptionOrThrow()); @@ -213,6 +215,7 @@ public class QProfileBackuperImpl implements QProfileBackuper { return NewCustomRule.createForCustomRule(r.getRuleKey(), r.getTemplateKey()) .setName(r.getName()) .setSeverity(r.getSeverity()) + .setImpacts(r.getImpacts().entrySet().stream().map(i -> new NewCustomRule.Impact(i.getKey(), i.getValue())).toList()) .setStatus(RuleStatus.READY) .setPreventReactivation(true) .setType(RuleType.valueOf(r.getType())) @@ -228,7 +231,12 @@ public class QProfileBackuperImpl implements QProfileBackuper { if (ruleDto == null) { continue; } - activatedRule.add(RuleActivation.create(ruleDto.getUuid(), r.getSeverity(), r.getPrioritizedRule(), r.getParameters())); + activatedRule.add(RuleActivation.create( + ruleDto.getUuid(), + r.getSeverity(), + r.getImpacts(), + r.getPrioritizedRule(), + r.getParameters())); } return activatedRule; } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileParser.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileParser.java index f0c1d16411a..e29e02bf6ca 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileParser.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileParser.java @@ -22,6 +22,7 @@ package org.sonar.server.qualityprofile; import java.io.Reader; import java.io.Writer; import java.util.ArrayList; +import java.util.EnumMap; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -36,6 +37,8 @@ import org.apache.commons.lang3.StringUtils; import org.codehaus.staxmate.SMInputFactory; import org.codehaus.staxmate.in.SMHierarchicCursor; import org.codehaus.staxmate.in.SMInputCursor; +import org.sonar.api.issue.impact.Severity; +import org.sonar.api.issue.impact.SoftwareQuality; import org.sonar.api.rule.RuleKey; import org.sonar.api.server.ServerSide; import org.sonar.api.utils.text.XmlWriter; @@ -43,6 +46,8 @@ import org.sonar.db.qualityprofile.ExportRuleDto; import org.sonar.db.qualityprofile.ExportRuleParamDto; import org.sonar.db.qualityprofile.QProfileDto; +import static org.sonar.server.qualityprofile.QProfileUtils.parseImpactsToMap; + @ServerSide public class QProfileParser { private static final String ATTRIBUTE_PROFILE = "profile"; @@ -53,6 +58,10 @@ public class QProfileParser { private static final String ATTRIBUTE_REPOSITORY_KEY = "repositoryKey"; private static final String ATTRIBUTE_KEY = "key"; private static final String ATTRIBUTE_PRIORITY = "priority"; + private static final String ATTRIBUTE_IMPACTS = "impacts"; + private static final String ATTRIBUTE_IMPACT = "impact"; + private static final String ATTRIBUTE_SEVERITY = "severity"; + private static final String ATTRIBUTE_SOFTWARE_QUALITY = "softwareQuality"; private static final String ATTRIBUTE_PRIORITIZED_RULE = "prioritizedRule"; private static final String ATTRIBUTE_TEMPLATE_KEY = "templateKey"; private static final String ATTRIBUTE_TYPE = "type"; @@ -76,6 +85,16 @@ public class QProfileParser { xml.prop(ATTRIBUTE_KEY, ruleToExport.getRuleKey().rule()); xml.prop(ATTRIBUTE_TYPE, ruleToExport.getRuleType().name()); xml.prop(ATTRIBUTE_PRIORITY, ruleToExport.getSeverityString()); + if (StringUtils.isNotEmpty(ruleToExport.getImpacts())) { + xml.begin(ATTRIBUTE_IMPACTS); + parseImpactsToMap(ruleToExport.getImpacts()).forEach((quality, severity) -> { + xml.begin(ATTRIBUTE_IMPACT); + xml.prop(ATTRIBUTE_SOFTWARE_QUALITY, quality.name()); + xml.prop(ATTRIBUTE_SEVERITY, severity.name()); + xml.end(); + }); + xml.end(); + } if (Boolean.TRUE.equals(ruleToExport.getPrioritizedRule())) { xml.prop(ATTRIBUTE_PRIORITIZED_RULE, ruleToExport.getPrioritizedRule()); } @@ -181,6 +200,11 @@ public class QProfileParser { rule.setDescription(StringUtils.trim(ruleCursor.collectDescendantText(false))); } else if (StringUtils.equals(ATTRIBUTE_PRIORITY, nodeName)) { rule.setSeverity(StringUtils.trim(ruleCursor.collectDescendantText(false))); + } else if (StringUtils.equals(ATTRIBUTE_IMPACTS, nodeName)) { + SMInputCursor impactsCursor = ruleCursor.childElementCursor(ATTRIBUTE_IMPACT); + Map<SoftwareQuality, Severity> impacts = new EnumMap<>(SoftwareQuality.class); + readImpacts(impactsCursor, impacts); + rule.setImpacts(impacts); } else if (StringUtils.equals(ATTRIBUTE_PRIORITIZED_RULE, nodeName)) { rule.setPrioritizedRule(Boolean.valueOf(StringUtils.trim(ruleCursor.collectDescendantText(false)))); } else if (StringUtils.equals(ATTRIBUTE_PARAMETERS, nodeName)) { @@ -209,4 +233,23 @@ public class QProfileParser { } } } + + private static void readImpacts(SMInputCursor impactsCursor, Map<SoftwareQuality, Severity> impacts) throws XMLStreamException { + while (impactsCursor.getNext() != null) { + SMInputCursor impactCursor = impactsCursor.childElementCursor(); + SoftwareQuality softwareQuality = null; + Severity severity = null; + while (impactCursor.getNext() != null) { + String nodeName = impactCursor.getLocalName(); + if (StringUtils.equals(ATTRIBUTE_SOFTWARE_QUALITY, nodeName)) { + softwareQuality = SoftwareQuality.valueOf(StringUtils.trim(impactCursor.collectDescendantText(false))); + } else if (StringUtils.equals(ATTRIBUTE_SEVERITY, nodeName)) { + severity = Severity.valueOf(StringUtils.trim(impactCursor.collectDescendantText(false))); + } + } + if (softwareQuality != null && severity != null) { + impacts.put(softwareQuality, severity); + } + } + } } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileUtils.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileUtils.java new file mode 100644 index 00000000000..a78bc0cc9bc --- /dev/null +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileUtils.java @@ -0,0 +1,46 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.qualityprofile; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.EnumMap; +import java.util.Map; +import org.sonar.api.issue.impact.Severity; +import org.sonar.api.issue.impact.SoftwareQuality; + +public class QProfileUtils { + + private QProfileUtils() { + } + + public static Map<SoftwareQuality, Severity> parseImpactsToMap(String impacts) { + ObjectMapper mapper = new ObjectMapper(); + Map<SoftwareQuality, Severity> parsedMap = new EnumMap<>(SoftwareQuality.class); + try { + Map<String, String> stringMap = mapper.readValue(impacts, Map.class); + for (Map.Entry<String, String> entry : stringMap.entrySet()) { + parsedMap.put(SoftwareQuality.valueOf(entry.getKey()), Severity.valueOf(entry.getValue())); + } + } catch (Exception e) { + throw new IllegalArgumentException("The quality profile cannot be restored as it contains invalid impacts: " + impacts); + } + return parsedMap; + } +} diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/builtin/RuleActivator.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/builtin/RuleActivator.java index 98d6bf350b9..146b0d5c69c 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/builtin/RuleActivator.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/builtin/RuleActivator.java @@ -259,11 +259,15 @@ public class RuleActivator { @Nullable ActiveRuleWrapper parentActiveRule) { String requestSeverity = request.getSeverity(); if (requestSeverity != null) { + Map<SoftwareQuality, Severity> impactSeverities = request.getImpactSeverities(); + if (impactSeverities != null && !impactSeverities.isEmpty()) { + return new SeverityConfiguration(requestSeverity, impactSeverities); + } // When standard severity is requested to be overridden, we translate it to the impact to override return new SeverityConfiguration(requestSeverity, QProfileImpactSeverityMapper.mapImpactSeverities(requestSeverity, ruleDto.getDefaultImpactsMap(), ruleDto.getEnumType())); } else if (!request.getImpactSeverities().isEmpty()) { - // If an impact is request to be overridden, we translat it to the standard severity + // If an impact is request to be overridden, we translate it to the standard severity return new SeverityConfiguration( QProfileImpactSeverityMapper.mapSeverity(request.getImpactSeverities(), ruleDto.getEnumType(), ruleDto.getSeverityString()), request.getImpactSeverities()); diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileParserTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileParserTest.java index 084bed6dad2..accbcfa7e12 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileParserTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileParserTest.java @@ -21,9 +21,15 @@ package org.sonar.server.qualityprofile; import java.io.Reader; import java.io.StringReader; +import java.util.Map; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; +import static org.sonar.api.issue.impact.Severity.BLOCKER; +import static org.sonar.api.issue.impact.Severity.INFO; +import static org.sonar.api.issue.impact.SoftwareQuality.MAINTAINABILITY; +import static org.sonar.api.issue.impact.SoftwareQuality.SECURITY; class QProfileParserTest { @@ -40,6 +46,16 @@ class QProfileParserTest { <key>s001</key> <type>CODE_SMELL</type> <priority>CRITICAL</priority> + <impacts> + <impact> + <softwareQuality>MAINTAINABILITY</softwareQuality> + <severity>BLOCKER</severity> + </impact> + <impact> + <softwareQuality>SECURITY</softwareQuality> + <severity>INFO</severity> + </impact> + </impacts> <name>custom rule name</name> <templateKey>rule_mc8</templateKey> <description>custom rule description</description> @@ -72,6 +88,7 @@ class QProfileParserTest { <type>CODE_SMELL</type> <priority>CRITICAL</priority> <prioritizedRule>false</prioritizedRule> + <impacts></impacts> <name>custom rule name</name> <templateKey>rule_mc8</templateKey> <description>custom rule description</description> @@ -90,9 +107,58 @@ class QProfileParserTest { var importedRule = importedQProfile.getRules().get(0); assertThat(importedRule.getDescription()).isEqualTo("custom rule description"); assertThat(importedRule.getPrioritizedRule()).isFalse(); + assertThat(importedRule.getImpacts()).isEqualTo(Map.of(MAINTAINABILITY, BLOCKER, SECURITY, INFO)); importedRule = importedQProfile.getRules().get(1); assertThat(importedRule.getPrioritizedRule()).isTrue(); + assertThat(importedRule.getImpacts()).isEmpty(); importedRule = importedQProfile.getRules().get(2); assertThat(importedRule.getPrioritizedRule()).isFalse(); + assertThat(importedRule.getImpacts()).isEmpty(); + } + + @Test + //test that exception is thrown if impacts has incorrect arguments + void readXml_whenImpactsIncorrectArguments_shouldThrow() { + Reader backup = new StringReader(""" + <?xml version='1.0' encoding='UTF-8'?> + <profile> + <name>custom rule</name> + <language>js</language> + <rules> + <rule> + <repositoryKey>sonarjs</repositoryKey> + <key>s001</key> + <type>CODE_SMELL</type> + <priority>CRITICAL</priority> + <impacts> + <impact> + <softwareQuality>MAINTAINABILITY</softwareQuality> + <severity>BLOCKER</severity> + </impact> + <impact> + <softwareQuality>SECURITY</softwareQuality> + <severity>INFO</severity> + </impact> + <impact> + <softwareQuality>RELIABILITY</softwareQuality> + <severity>MAJOR</severity> + <impact> + </impacts> + <name>custom rule name</name> + <templateKey>rule_mc8</templateKey> + <description>custom rule description</description> + <parameters> + <parameter> + <key>bar</key> + <value>baz</value> + </parameter> + </parameters> + </rule> + </rules> + </profile>"""); + var parser = new QProfileParser(); + assertThatThrownBy(() -> parser.readXml(backup)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("No enum constant org.sonar.api.issue.impact.Severity.MAJOR"); } } |