From c72965048f5cf06bdf3414846a347402f2b8dddf Mon Sep 17 00:00:00 2001 From: Duarte Meneses Date: Wed, 30 May 2018 10:45:00 +0200 Subject: [PATCH] SONAR-10809 Analyzers need to declare whether a rule is external --- .../main/java/org/sonar/xoo/XooPlugin.java | 2 + .../rule/OneExternalIssuePerLineSensor.java | 4 +- ...ExternalIssueWithDetailsPerLineSensor.java | 83 +++++++++++++++++++ .../sonar/xoo/rule/XooRulesDefinition.java | 15 ++++ .../java/org/sonar/xoo/XooPluginTest.java | 2 +- .../xoo/rule/XooRulesDefinitionTest.java | 9 ++ .../org/sonar/server/rule/RegisterRules.java | 3 +- .../sonar/server/rule/RegisterRulesTest.java | 50 ++++++++++- .../api/server/rule/RulesDefinition.java | 43 ++++++++-- .../api/server/rule/RulesDefinitionTest.java | 46 ++++++++++ .../tests/issue/ExternalIssueTest.java | 46 +++++++++- 11 files changed, 289 insertions(+), 14 deletions(-) create mode 100644 plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneExternalIssueWithDetailsPerLineSensor.java diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java index 3b626b8ed9f..639d51c2172 100644 --- a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java @@ -51,6 +51,7 @@ import org.sonar.xoo.rule.OneBlockerIssuePerFileSensor; import org.sonar.xoo.rule.OneBugIssuePerLineSensor; import org.sonar.xoo.rule.OneDayDebtPerFileSensor; import org.sonar.xoo.rule.OneExternalIssuePerLineSensor; +import org.sonar.xoo.rule.OneExternalIssueWithDetailsPerLineSensor; import org.sonar.xoo.rule.OneIssueOnDirPerFileSensor; import org.sonar.xoo.rule.OneIssuePerDirectorySensor; import org.sonar.xoo.rule.OneIssuePerFileSensor; @@ -176,6 +177,7 @@ public class XooPlugin implements Plugin { if (context.getSonarQubeVersion().isGreaterThanOrEqual(Version.create(7, 2))) { context.addExtensions( OneExternalIssuePerLineSensor.class, + OneExternalIssueWithDetailsPerLineSensor.class, SignificantCodeSensor.class); } } diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneExternalIssuePerLineSensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneExternalIssuePerLineSensor.java index d9fe4b59e82..c8d09704371 100644 --- a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneExternalIssuePerLineSensor.java +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneExternalIssuePerLineSensor.java @@ -38,7 +38,7 @@ public class OneExternalIssuePerLineSensor implements Sensor { public static final String ENGINE_KEY = "XooEngine"; public static final String SEVERITY = "MAJOR"; public static final Long EFFORT = 10l; - public static final RuleType type = RuleType.BUG; + public static final RuleType TYPE = RuleType.BUG; public static final String ACTIVATE_EXTERNAL_ISSUES = "sonar.oneExternalIssuePerLine.activate"; private static final String NAME = "One External Issue Per Line"; @@ -76,7 +76,7 @@ public class OneExternalIssuePerLineSensor implements Sensor { .message("This issue is generated on each line")) .severity(Severity.valueOf(SEVERITY)) .remediationEffortMinutes(EFFORT) - .type(type) + .type(TYPE) .save(); } } diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneExternalIssueWithDetailsPerLineSensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneExternalIssueWithDetailsPerLineSensor.java new file mode 100644 index 00000000000..0598b7a8d40 --- /dev/null +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneExternalIssueWithDetailsPerLineSensor.java @@ -0,0 +1,83 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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.xoo.rule; + +import org.sonar.api.batch.fs.FilePredicates; +import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.InputFile.Type; +import org.sonar.api.batch.rule.Severity; +import org.sonar.api.batch.sensor.Sensor; +import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.batch.sensor.SensorDescriptor; +import org.sonar.api.batch.sensor.issue.NewExternalIssue; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rules.RuleType; +import org.sonar.xoo.Xoo; +import org.sonar.xoo.Xoo2; + +public class OneExternalIssueWithDetailsPerLineSensor implements Sensor { + public static final String RULE_KEY = "OneExternalIssueWithDetailsPerLine"; + public static final String ENGINE_KEY = "XooEngine"; + public static final String SEVERITY = "MAJOR"; + public static final Long EFFORT = 10l; + public static final RuleType TYPE = RuleType.BUG; + public static final String ACTIVATE_EXTERNAL_ISSUES = "sonar.oneExternalIssueWithDetailsPerLine.activate"; + private static final String NAME = "One External Issue Per Line"; + + @Override + public void describe(SensorDescriptor descriptor) { + descriptor + .name(NAME) + .onlyOnLanguages(Xoo.KEY, Xoo2.KEY) + .onlyWhenConfiguration(c -> c.getBoolean(ACTIVATE_EXTERNAL_ISSUES).orElse(false)); + } + + @Override + public void execute(SensorContext context) { + analyse(context, Xoo.KEY, XooRulesDefinition.XOO_REPOSITORY); + analyse(context, Xoo2.KEY, XooRulesDefinition.XOO2_REPOSITORY); + } + + private void analyse(SensorContext context, String language, String repo) { + FileSystem fs = context.fileSystem(); + FilePredicates p = fs.predicates(); + for (InputFile file : fs.inputFiles(p.and(p.hasLanguages(language), p.hasType(Type.MAIN)))) { + createIssues(file, context, repo); + } + } + + private void createIssues(InputFile file, SensorContext context, String repo) { + RuleKey ruleKey = RuleKey.of(repo, RULE_KEY); + for (int line = 1; line <= file.lines(); line++) { + NewExternalIssue newIssue = context.newExternalIssue(); + newIssue + .forRule(ruleKey) + .at(newIssue.newLocation() + .on(file) + .at(file.selectLine(line)) + .message("This issue is generated on each line and the rule contains details")) + .severity(Severity.valueOf(SEVERITY)) + .remediationEffortMinutes(EFFORT) + .type(TYPE) + .save(); + } + } +} diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooRulesDefinition.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooRulesDefinition.java index 13c238cae7a..4f119f9ebe0 100644 --- a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooRulesDefinition.java +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooRulesDefinition.java @@ -35,6 +35,7 @@ public class XooRulesDefinition implements RulesDefinition { public static final String XOO_REPOSITORY = "xoo"; public static final String XOO2_REPOSITORY = "xoo2"; + public static final String XOO_EXTERNAL_REPOSITORY = "xoo"; private static final String TEN_MIN = "10min"; @@ -42,6 +43,7 @@ public class XooRulesDefinition implements RulesDefinition { public void define(Context context) { defineRulesXoo(context); defineRulesXoo2(context); + defineRulesXooExternal(context); } private static void defineRulesXoo2(Context context) { @@ -156,4 +158,17 @@ public class XooRulesDefinition implements RulesDefinition { } + private static void defineRulesXooExternal(Context context) { + NewRepository repo = context.createExternalRepository(XOO_EXTERNAL_REPOSITORY, Xoo.KEY).setName("XooExternal"); + + repo.createRule(OneExternalIssueWithDetailsPerLineSensor.RULE_KEY) + .setSeverity(OneExternalIssueWithDetailsPerLineSensor.SEVERITY) + .setType(OneExternalIssueWithDetailsPerLineSensor.TYPE) + .setScope(RuleScope.ALL) + .setHtmlDescription("Generates one external issue in each line") + .setName("One external issue per line"); + + repo.done(); + } + } diff --git a/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java index dacd5b3f575..e66d56e45c8 100644 --- a/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java +++ b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java @@ -62,6 +62,6 @@ public class XooPluginTest { SonarRuntime runtime = SonarRuntimeImpl.forSonarQube(Version.parse("7.2"), SonarQubeSide.SCANNER); Plugin.Context context = new PluginContextImpl.Builder().setSonarRuntime(runtime).build(); new XooPlugin().define(context); - assertThat(context.getExtensions()).hasSize(54).contains(OneExternalIssuePerLineSensor.class); + assertThat(context.getExtensions()).hasSize(55).contains(OneExternalIssuePerLineSensor.class); } } diff --git a/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/rule/XooRulesDefinitionTest.java b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/rule/XooRulesDefinitionTest.java index 7de2430f0c1..9658ab8e3a4 100644 --- a/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/rule/XooRulesDefinitionTest.java +++ b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/rule/XooRulesDefinitionTest.java @@ -51,6 +51,15 @@ public class XooRulesDefinitionTest { assertThat(rule.debtRemediationFunction().baseEffort()).isNull(); assertThat(rule.gapDescription()).isNotEmpty(); } + + @Test + public void define_xooExternal_rules() { + RulesDefinition.Repository repo = context.repository("external_xoo"); + assertThat(repo).isNotNull(); + assertThat(repo.name()).isEqualTo("XooExternal"); + assertThat(repo.language()).isEqualTo("xoo"); + assertThat(repo.rules()).hasSize(1); + } @Test public void define_xoo2_rules() { diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java b/server/sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java index ba7e348ea02..51c5549ba3d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java @@ -392,7 +392,7 @@ public class RegisterRules implements Startable { .setSystemTags(ruleDef.tags()) .setType(RuleType.valueOf(ruleDef.type().name())) .setScope(toDtoScope(ruleDef.scope())) - .setIsExternal(false) + .setIsExternal(ruleDef.repository().isExternal()) .setCreatedAt(system2.now()) .setUpdatedAt(system2.now()); if (ruleDef.htmlDescription() != null) { @@ -746,7 +746,6 @@ public class RegisterRules implements Startable { dbClient.ruleDao().update(session, rule); } - private static void verifyRuleKeyConsistency(List repositories, RegisterRulesContext registerRulesContext) { List definedRules = repositories.stream() .flatMap(r -> r.rules().stream()) diff --git a/server/sonar-server/src/test/java/org/sonar/server/rule/RegisterRulesTest.java b/server/sonar-server/src/test/java/org/sonar/server/rule/RegisterRulesTest.java index 580e22dc528..2d70a76fa5e 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/rule/RegisterRulesTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/rule/RegisterRulesTest.java @@ -92,6 +92,8 @@ public class RegisterRulesTest { private static final Date DATE2 = DateUtils.parseDateTime("2014-02-01T12:10:03+0100"); private static final Date DATE3 = DateUtils.parseDateTime("2014-03-01T12:10:03+0100"); + private static final RuleKey EXTERNAL_RULE_KEY1 = RuleKey.of("external_eslint", "rule1"); + private static final RuleKey RULE_KEY1 = RuleKey.of("fake", "rule1"); private static final RuleKey RULE_KEY2 = RuleKey.of("fake", "rule2"); private static final RuleKey RULE_KEY3 = RuleKey.of("fake", "rule3"); @@ -148,6 +150,7 @@ public class RegisterRulesTest { assertThat(rule1.getDefRemediationBaseEffort()).isEqualTo("10h"); assertThat(rule1.getType()).isEqualTo(RuleType.CODE_SMELL.getDbConstant()); assertThat(rule1.getPluginKey()).isEqualTo(FAKE_PLUGIN_KEY); + assertThat(rule1.isExternal()).isFalse(); List params = dbClient.ruleDao().selectRuleParamsByRuleKey(dbTester.getSession(), RULE_KEY1); assertThat(params).hasSize(2); @@ -163,6 +166,31 @@ public class RegisterRulesTest { assertThat(dbClient.ruleRepositoryDao().selectAll(dbTester.getSession())).extracting(RuleRepositoryDto::getKey).containsOnly("fake"); } + @Test + public void insert_new_external_rule() { + execute(new ExternalRuleRepository()); + + // verify db + assertThat(dbClient.ruleDao().selectAllDefinitions(dbTester.getSession())).hasSize(1); + RuleDto rule1 = dbClient.ruleDao().selectOrFailByKey(dbTester.getSession(), dbTester.getDefaultOrganization(), EXTERNAL_RULE_KEY1); + assertThat(rule1.getName()).isEqualTo("One"); + assertThat(rule1.getDescription()).isEqualTo("Description of One"); + assertThat(rule1.getSeverityString()).isEqualTo(BLOCKER); + assertThat(rule1.getTags()).isEmpty(); + assertThat(rule1.getSystemTags()).containsOnly("tag1", "tag2", "tag3"); + assertThat(rule1.getConfigKey()).isEqualTo("config1"); + assertThat(rule1.getStatus()).isEqualTo(RuleStatus.BETA); + assertThat(rule1.getCreatedAt()).isEqualTo(DATE1.getTime()); + assertThat(rule1.getScope()).isEqualTo(Scope.ALL); + assertThat(rule1.getUpdatedAt()).isEqualTo(DATE1.getTime()); + assertThat(rule1.getDefRemediationFunction()).isNull(); + assertThat(rule1.getDefRemediationGapMultiplier()).isNull(); + assertThat(rule1.getDefRemediationBaseEffort()).isNull(); + assertThat(rule1.getType()).isEqualTo(RuleType.CODE_SMELL.getDbConstant()); + assertThat(rule1.getPluginKey()).isEqualTo(FAKE_PLUGIN_KEY); + assertThat(rule1.isExternal()).isTrue(); + } + @Test public void insert_then_remove_rule() { String ruleKey = randomAlphanumeric(5); @@ -496,7 +524,7 @@ public class RegisterRulesTest { @DataProvider public static Object[][] allRenamingCases() { - return new Object[][]{ + return new Object[][] { {"repo1", "rule1", "repo1", "rule2"}, {"repo1", "rule1", "repo2", "rule1"}, {"repo1", "rule1", "repo2", "rule2"}, @@ -857,8 +885,7 @@ public class RegisterRulesTest { expectedException.expect(IllegalStateException.class); expectedException.expectMessage("An incorrect state of deprecated rule keys has been detected.\n " + - "The deprecated rule key [javascript:linelength] was previously deprecated by [javascript:s103]. [javascript:s103] should be a deprecated key of [sonarjs:s103]," - ); + "The deprecated rule key [javascript:linelength] was previously deprecated by [javascript:s103]. [javascript:s103] should be a deprecated key of [sonarjs:s103],"); // This rule should have been moved to another repository execute(context -> createRule(context, "javascript", "sonarjs", "s103", @@ -992,6 +1019,23 @@ public class RegisterRulesTest { } } + static class ExternalRuleRepository implements RulesDefinition { + @Override + public void define(Context context) { + NewRepository repo = context.createExternalRepository("eslint", "js"); + repo.createRule(RULE_KEY1.rule()) + .setName("One") + .setHtmlDescription("Description of One") + .setSeverity(BLOCKER) + .setInternalKey("config1") + .setTags("tag1", "tag2", "tag3") + .setScope(RuleScope.ALL) + .setType(RuleType.CODE_SMELL) + .setStatus(RuleStatus.BETA); + repo.done(); + } + } + static class BigRepository implements RulesDefinition { static final int SIZE = 500; diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RulesDefinition.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RulesDefinition.java index cb1ce8ceac3..8c30bd2e82b 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RulesDefinition.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RulesDefinition.java @@ -397,7 +397,17 @@ public interface RulesDefinition { * to execute {@link org.sonar.api.server.rule.RulesDefinition.NewRepository#setName(String)} */ public NewRepository createRepository(String key, String language) { - return new NewRepositoryImpl(this, key, language); + return new NewRepositoryImpl(this, key, language, false); + } + + /** + * Creates a repository of rules from external rule engines. + * The key will always be prefixed with "external_". + * + * @since 7.2 + */ + public NewRepository createExternalRepository(String key, String language) { + return new NewRepositoryImpl(this, key, language, true); } /** @@ -472,19 +482,32 @@ public interface RulesDefinition { interface NewRepository extends NewExtendedRepository { NewRepository setName(String s); + + /** + * @since 7.2 + */ + boolean isExternal(); } class NewRepositoryImpl implements NewRepository { private final Context context; private final String key; + private final boolean isExternal; private String language; private String name; private final Map newRules = new HashMap<>(); - private NewRepositoryImpl(Context context, String key, String language) { + private NewRepositoryImpl(Context context, String key, String language, boolean isExternal) { this.context = context; - this.key = this.name = key; + this.key = isExternal ? (RuleKey.EXTERNAL_RULE_REPO_PREFIX + key) : key; + this.name = key; this.language = language; + this.isExternal = isExternal; + } + + @Override + public boolean isExternal() { + return isExternal; } @Override @@ -550,6 +573,11 @@ public interface RulesDefinition { interface Repository extends ExtendedRepository { String name(); + + /** + * @since 7.2 + */ + boolean isExternal(); } @Immutable @@ -557,12 +585,13 @@ public interface RulesDefinition { private final String key; private final String language; private final String name; + private final boolean isExternal; private final Map rulesByKey; private RepositoryImpl(NewRepositoryImpl newRepository, @Nullable Repository mergeInto) { this.key = newRepository.key; this.language = newRepository.language; - + this.isExternal = newRepository.isExternal; Map ruleBuilder = new HashMap<>(); if (mergeInto != null) { if (!StringUtils.equals(newRepository.language, mergeInto.language()) || !StringUtils.equals(newRepository.key, mergeInto.key())) { @@ -600,6 +629,11 @@ public interface RulesDefinition { return name; } + @Override + public boolean isExternal() { + return isExternal; + } + @Override @CheckForNull public Rule rule(String ruleKey) { @@ -1036,7 +1070,6 @@ public interface RulesDefinition { /** * @since 7.1 - * @return */ public RuleScope scope() { return scope; diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionTest.java index 8398f50a2ae..0eefbb801ae 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionTest.java @@ -103,6 +103,7 @@ public class RulesDefinitionTest { RulesDefinition.Repository repo = context.repository("findbugs"); assertThat(repo.rules()).hasSize(2); + assertThat(repo.isExternal()).isFalse(); RulesDefinition.Rule rule = repo.rule("NPE"); assertThat(rule.scope()).isEqualTo(RuleScope.ALL); @@ -165,6 +166,51 @@ public class RulesDefinitionTest { assertThat(rule.debtRemediationFunction()).isNull(); } + @Test + public void define_external_rules() { + RulesDefinition.NewRepository newRepo = context.createExternalRepository("eslint", "js"); + newRepo.createRule("NPE") + .setName("Detect NPE") + .setHtmlDescription("Detect java.lang.NullPointerException") + .setSeverity(Severity.BLOCKER) + .setInternalKey("/something") + .setStatus(RuleStatus.BETA) + .setTags("one", "two") + .setScope(RuleScope.ALL) + .addTags("two", "three", "four"); + + newRepo.createRule("ABC").setName("ABC").setMarkdownDescription("ABC"); + newRepo.done(); + + assertThat(context.repository("eslint")).isNull(); + RulesDefinition.Repository repo = context.repository("external_eslint"); + assertThat(repo.rules()).hasSize(2); + assertThat(repo.isExternal()).isTrue(); + + RulesDefinition.Rule rule = repo.rule("NPE"); + assertThat(rule.scope()).isEqualTo(RuleScope.ALL); + assertThat(rule.key()).isEqualTo("NPE"); + assertThat(rule.name()).isEqualTo("Detect NPE"); + assertThat(rule.severity()).isEqualTo(Severity.BLOCKER); + assertThat(rule.htmlDescription()).isEqualTo("Detect java.lang.NullPointerException"); + assertThat(rule.markdownDescription()).isNull(); + assertThat(rule.tags()).containsOnly("one", "two", "three", "four"); + assertThat(rule.params()).isEmpty(); + assertThat(rule.internalKey()).isEqualTo("/something"); + assertThat(rule.template()).isFalse(); + assertThat(rule.status()).isEqualTo(RuleStatus.BETA); + assertThat(rule.toString()).isEqualTo("[repository=external_eslint, key=NPE]"); + assertThat(rule.repository()).isSameAs(repo); + + RulesDefinition.Rule otherRule = repo.rule("ABC"); + assertThat(otherRule.htmlDescription()).isNull(); + assertThat(otherRule.markdownDescription()).isEqualTo("ABC"); + + // test equals() and hashCode() + assertThat(rule).isEqualTo(rule).isNotEqualTo(otherRule).isNotEqualTo("NPE").isNotEqualTo(null); + assertThat(rule.hashCode()).isEqualTo(rule.hashCode()); + } + @Test public void define_rule_parameters() { RulesDefinition.NewRepository newFindbugs = context.createRepository("findbugs", "java"); diff --git a/tests/src/test/java/org/sonarqube/tests/issue/ExternalIssueTest.java b/tests/src/test/java/org/sonarqube/tests/issue/ExternalIssueTest.java index 5aa33193a6b..c71120b7ade 100644 --- a/tests/src/test/java/org/sonarqube/tests/issue/ExternalIssueTest.java +++ b/tests/src/test/java/org/sonarqube/tests/issue/ExternalIssueTest.java @@ -79,6 +79,33 @@ public class ExternalIssueTest { assertThat(issuesList).hasSize(17); } + @Test + public void should_import_external_issues_with_details_provided_by_code_analyzers() { + noIssues(); + ruleExistsWithDetails("external_xoo:OneExternalIssueWithDetailsPerLine"); + + ItUtils.runProjectAnalysis(orchestrator, "shared/xoo-sample", + "sonar.oneExternalIssueWithDetailsPerLine.activate", "true"); + List issuesList = tester.wsClient().issues().search(new SearchRequest()).getIssuesList(); + assertThat(issuesList).hasSize(17); + + assertThat(issuesList).allMatch(issue -> "external_xoo:OneExternalIssueWithDetailsPerLine".equals(issue.getRule())); + assertThat(issuesList).allMatch(issue -> "This issue is generated on each line and the rule contains details".equals(issue.getMessage())); + assertThat(issuesList).allMatch(issue -> Severity.MAJOR.equals(issue.getSeverity())); + assertThat(issuesList).allMatch(issue -> RuleType.BUG.equals(issue.getType())); + assertThat(issuesList).allMatch(issue -> "sample:src/main/xoo/sample/Sample.xoo".equals(issue.getComponent())); + assertThat(issuesList).allMatch(issue -> "OPEN".equals(issue.getStatus())); + assertThat(issuesList).allMatch(issue -> issue.getExternalRuleEngine().equals("xoo")); + + ruleExistsWithDetails("external_xoo:OneExternalIssueWithDetailsPerLine"); + + // second analysis, issue tracking should work + ItUtils.runProjectAnalysis(orchestrator, "shared/xoo-sample", + "sonar.oneExternalIssueWithDetailsPerLine.activate", "true"); + issuesList = tester.wsClient().issues().search(new SearchRequest()).getIssuesList(); + assertThat(issuesList).hasSize(17); + } + @Test public void should_import_external_issues_from_json_report_and_create_external_rules() { noIssues(); @@ -127,7 +154,7 @@ public class ExternalIssueTest { } - private void ruleExists(String key) { + private org.sonarqube.ws.Rules.Rule ruleExists(String key) { List rulesList = tester.wsClient().rules() .search(new org.sonarqube.ws.client.rules.SearchRequest() .setRuleKey(key) @@ -140,6 +167,23 @@ public class ExternalIssueTest { assertThat(rulesList.get(0).getIsExternal()).isTrue(); assertThat(rulesList.get(0).getTags().getTagsCount()).isEqualTo(0); assertThat(rulesList.get(0).getScope()).isEqualTo(RuleScope.ALL); + + // if flag to include external is not given, shouldn't be able to find it + List rulesListWithoutExternal = tester.wsClient().rules() + .search(new org.sonarqube.ws.client.rules.SearchRequest() + .setRuleKey(key)) + .getRulesList(); + assertThat(rulesListWithoutExternal).isEmpty(); + + return rulesList.get(0); + } + + private void ruleExistsWithDetails(String key) { + org.sonarqube.ws.Rules.Rule rule = ruleExists(key); + assertThat(rule.getHtmlDesc()).isEqualTo("Generates one external issue in each line"); + assertThat(rule.getSeverity()).isEqualTo("MAJOR"); + assertThat(rule.getType()).isEqualTo(RuleType.BUG); + assertThat(rule.getName()).isEqualTo("One external issue per line"); } private void noIssues() { -- 2.39.5