From: Duarte Meneses Date: Mon, 23 Apr 2018 09:45:03 +0000 (+0200) Subject: SONAR-10544 Set effort for external issues X-Git-Tag: 7.5~1304 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=5958b88243b4d4db54cdb20fb91fc45fac34e9d3;p=sonarqube.git SONAR-10544 Set effort for external issues --- diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDto.java index 0213e3106e5..3f72a51740a 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDto.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDto.java @@ -733,7 +733,7 @@ public final class IssueDto implements Serializable { issue.setUpdateDate(longToDate(issueUpdateDate)); issue.setSelectedAt(selectedAt); issue.setLocations(parseLocations()); - issue.setFromExternalRuleEngine(isExternal); + issue.setIsFromExternalRuleEngine(isExternal); return issue; } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/DebtCalculator.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/DebtCalculator.java index 328447ed02a..c896d0de584 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/DebtCalculator.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/DebtCalculator.java @@ -40,19 +40,22 @@ public class DebtCalculator { @CheckForNull public Duration calculate(DefaultIssue issue) { + if (issue.isFromExternalRuleEngine()) { + return issue.effort(); + } Rule rule = ruleRepository.getByKey(issue.ruleKey()); DebtRemediationFunction fn = rule.getRemediationFunction(); if (fn != null) { verifyEffortToFix(issue, fn); Duration debt = Duration.create(0); - String gapMultiplier =fn.gapMultiplier(); + String gapMultiplier = fn.gapMultiplier(); if (fn.type().usesGapMultiplier() && !Strings.isNullOrEmpty(gapMultiplier)) { - int effortToFixValue = MoreObjects.firstNonNull(issue.effortToFix(), 1).intValue(); + int effortToFixValue = MoreObjects.firstNonNull(issue.gap(), 1).intValue(); // TODO convert to Duration directly in Rule#remediationFunction -> better performance + error handling debt = durations.decode(gapMultiplier).multiply(effortToFixValue); } - String baseEffort= fn.baseEffort(); + String baseEffort = fn.baseEffort(); if (fn.type().usesBaseEffort() && !Strings.isNullOrEmpty(baseEffort)) { // TODO convert to Duration directly in Rule#remediationFunction -> better performance + error handling debt = debt.add(durations.decode(baseEffort)); @@ -63,7 +66,7 @@ public class DebtCalculator { } private static void verifyEffortToFix(DefaultIssue issue, DebtRemediationFunction fn) { - if (Type.CONSTANT_ISSUE.equals(fn.type()) && issue.effortToFix() != null) { + if (Type.CONSTANT_ISSUE.equals(fn.type()) && issue.gap() != null) { throw new IllegalArgumentException("Rule '" + issue.getRuleKey() + "' can not use 'Constant/issue' remediation function " + "because this rule does not have a fixed remediation cost."); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/TrackerRawInputFactory.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/TrackerRawInputFactory.java index 8b134cf9d18..5344410c459 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/TrackerRawInputFactory.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/TrackerRawInputFactory.java @@ -167,7 +167,7 @@ public class TrackerRawInputFactory { dbLocationsBuilder.addFlow(dbFlowBuilder); } } - issue.setFromExternalRuleEngine(false); + issue.setIsFromExternalRuleEngine(false); issue.setLocations(dbLocationsBuilder.build()); return issue; } @@ -204,7 +204,7 @@ public class TrackerRawInputFactory { dbLocationsBuilder.addFlow(dbFlowBuilder); } } - issue.setFromExternalRuleEngine(true); + issue.setIsFromExternalRuleEngine(true); issue.setLocations(dbLocationsBuilder.build()); issue.setType(toRuleType(reportIssue.getType())); diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/commonrule/CommonRule.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/commonrule/CommonRule.java index 9f1a84bafaa..397f9305355 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/commonrule/CommonRule.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/commonrule/CommonRule.java @@ -55,7 +55,7 @@ public abstract class CommonRule { issue.setSeverity(activeRule.get().getSeverity()); issue.setLine(null); issue.setChecksum(""); - issue.setFromExternalRuleEngine(false); + issue.setIsFromExternalRuleEngine(false); } } return issue; diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/ExternalRuleCreator.java b/server/sonar-server/src/main/java/org/sonar/server/rule/ExternalRuleCreator.java index 1f96b689c94..30ee1919563 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/ExternalRuleCreator.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/ExternalRuleCreator.java @@ -61,7 +61,6 @@ public class ExternalRuleCreator { .setUpdatedAt(system2.now())); Rule newRule = new RuleImpl(dao.selectOrFailByKey(dbSession, external.getKey())); - // TODO write rule repository if needed ruleIndexer.commitAndIndex(dbSession, newRule.getId()); return newRule; } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/DebtCalculatorTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/DebtCalculatorTest.java index f7154ba9de7..67a9d2ebd11 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/DebtCalculatorTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/DebtCalculatorTest.java @@ -22,6 +22,7 @@ package org.sonar.server.computation.task.projectanalysis.issue; import org.junit.Test; import org.sonar.api.server.debt.DebtRemediationFunction; import org.sonar.api.server.debt.internal.DefaultDebtRemediationFunction; +import org.sonar.api.utils.Duration; import org.sonar.api.utils.Durations; import org.sonar.core.issue.DefaultIssue; import org.sonar.db.rule.RuleTesting; @@ -72,6 +73,16 @@ public class DebtCalculatorTest { assertThat(underTest.calculate(issue).toMinutes()).isEqualTo((int) (coefficient * effortToFix)); } + @Test + public void copy_effort_for_external_issues() { + issue.setGap(null); + issue.setIsFromExternalRuleEngine(true); + issue.setEffort(Duration.create(20l)); + rule.setFunction(null); + + assertThat(underTest.calculate(issue).toMinutes()).isEqualTo(20l); + } + @Test public void constant_function() { int constant = 2; diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/TransitionActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/TransitionActionTest.java index 6ada52d8ace..cc5c1b113a0 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/TransitionActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/TransitionActionTest.java @@ -117,7 +117,7 @@ public class TransitionActionTest { loginAndAddProjectPermission("john", ISSUE_ADMIN); context.issue() - .setFromExternalRuleEngine(true) + .setIsFromExternalRuleEngine(true) .setStatus(STATUS_CLOSED); action.execute(ImmutableMap.of("transition", "close"), context); diff --git a/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java b/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java index 0add84d5a82..f1339f27a69 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java @@ -245,8 +245,8 @@ public class DefaultIssue implements Issue, Trackable, org.sonar.api.ce.measure. return isFromExternalRuleEngine; } - public DefaultIssue setFromExternalRuleEngine(boolean fromExternalRuleEngine) { - isFromExternalRuleEngine = fromExternalRuleEngine; + public DefaultIssue setIsFromExternalRuleEngine(boolean isFromExternalRuleEngine) { + this.isFromExternalRuleEngine = isFromExternalRuleEngine; return this; } diff --git a/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssueBuilder.java b/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssueBuilder.java index e302c0bd3e3..7f5c4fdf806 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssueBuilder.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssueBuilder.java @@ -170,7 +170,7 @@ public class DefaultIssueBuilder implements Issuable.IssueBuilder { issue.setCopied(false); issue.setBeingClosed(false); issue.setOnDisabledRule(false); - issue.setFromExternalRuleEngine(isFromExternalRuleEngine); + issue.setIsFromExternalRuleEngine(isFromExternalRuleEngine); return issue; } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/ExternalIssueImporter.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/ExternalIssueImporter.java index 4a5e35e96f4..99100318b68 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/ExternalIssueImporter.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/ExternalIssueImporter.java @@ -107,9 +107,12 @@ public class ExternalIssueImporter { InputFile file = findFile(context, location.filePath); if (file != null) { newLocation - .message(location.message) .on(file); + if (location.message != null) { + newLocation.message(location.message); + } + if (location.textRange != null) { if (location.textRange.startColumn != null) { TextPointer start = file.newPointer(location.textRange.startLine, location.textRange.startColumn); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/externalissue/ReportParserTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/externalissue/ReportParserTest.java index e13e64d6b85..add4404b2a9 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/externalissue/ReportParserTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/externalissue/ReportParserTest.java @@ -39,7 +39,7 @@ public class ReportParserTest { System.out.println(Paths.get("org/sonar/scanner/externalissue/report.json").toAbsolutePath()); Report report = parser.parse(); - assertThat(report.issues).hasSize(3); + assertThat(report.issues).hasSize(4); assertThat(report.issues[0].engineId).isEqualTo("eslint"); assertThat(report.issues[0].ruleId).isEqualTo("rule1"); assertThat(report.issues[0].severity).isEqualTo("MAJOR"); @@ -52,6 +52,19 @@ public class ReportParserTest { assertThat(report.issues[0].primaryLocation.textRange.endColumn).isEqualTo(4); assertThat(report.issues[0].primaryLocation.textRange.endLine).isEqualTo(3); assertThat(report.issues[0].secondaryLocations).isNull(); + + assertThat(report.issues[3].engineId).isEqualTo("eslint"); + assertThat(report.issues[3].ruleId).isEqualTo("rule3"); + assertThat(report.issues[3].severity).isEqualTo("MAJOR"); + assertThat(report.issues[3].effortMinutes).isNull(); + assertThat(report.issues[3].type).isEqualTo("BUG"); + assertThat(report.issues[3].secondaryLocations).hasSize(2); + assertThat(report.issues[3].secondaryLocations[0].filePath).isEqualTo("file1.js"); + assertThat(report.issues[3].secondaryLocations[0].message).isEqualTo("fix the bug here"); + assertThat(report.issues[3].secondaryLocations[0].textRange.startLine).isEqualTo(1); + assertThat(report.issues[3].secondaryLocations[1].filePath).isEqualTo("file2.js"); + assertThat(report.issues[3].secondaryLocations[1].message).isNull(); + assertThat(report.issues[3].secondaryLocations[1].textRange.startLine).isEqualTo(2); } private Path path(String reportName) { @@ -121,4 +134,12 @@ public class ReportParserTest { exception.expectMessage("missing mandatory field 'filePath'"); parser.parse(); } + + @Test + public void fail_if_message_not_set_in_primaryLocation() { + ReportParser parser = new ReportParser(path("report_missing_message.json")); + exception.expect(IllegalStateException.class); + exception.expectMessage("missing mandatory field 'message'"); + parser.parse(); + } } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/issues/ExternalIssuesMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/issues/ExternalIssuesMediumTest.java index 6d406300c3b..92e01b6491d 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/issues/ExternalIssuesMediumTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/issues/ExternalIssuesMediumTest.java @@ -28,7 +28,6 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.sonar.api.utils.log.LogTester; -import org.sonar.api.utils.log.LoggersTest; import org.sonar.scanner.mediumtest.ScannerMediumTester; import org.sonar.scanner.mediumtest.TaskResult; import org.sonar.scanner.protocol.Constants.Severity; @@ -111,18 +110,23 @@ public class ExternalIssuesMediumTest { assertThat(issue.getTextRange().getStartOffset()).isEqualTo(0); assertThat(issue.getTextRange().getEndOffset()).isEqualTo(24); - // One file-level issue in helloscala + // One file-level issue in helloscala, with secondary location List externalIssues2 = result.externalIssuesFor(result.inputFile("xources/hello/helloscala.xoo")); assertThat(externalIssues2).hasSize(1); issue = externalIssues2.iterator().next(); - assertThat(issue.getFlowCount()).isZero(); + assertThat(issue.getFlowCount()).isEqualTo(2); assertThat(issue.getMsg()).isEqualTo("fix the bug here"); assertThat(issue.getRuleKey()).isEqualTo("rule3"); assertThat(issue.getSeverity()).isEqualTo(Severity.MAJOR); assertThat(issue.getType()).isEqualTo(IssueType.BUG); assertThat(issue.hasTextRange()).isFalse(); - + assertThat(issue.getFlow(0).getLocationCount()).isOne(); + assertThat(issue.getFlow(0).getLocation(0).getTextRange().getStartLine()).isOne(); + assertThat(issue.getFlow(1).getLocationCount()).isOne(); + assertThat(issue.getFlow(1).getLocation(0).getTextRange().getStartLine()).isEqualTo(3); + + // one issue is located in a non-existing file assertThat(logs.logs()).contains("External issues ignored for 1 unknown files, including: invalidFile"); diff --git a/sonar-scanner-engine/src/test/resources/mediumtest/xoo/sample/externalIssues.json b/sonar-scanner-engine/src/test/resources/mediumtest/xoo/sample/externalIssues.json index 0e50bedd8dc..539533af78f 100644 --- a/sonar-scanner-engine/src/test/resources/mediumtest/xoo/sample/externalIssues.json +++ b/sonar-scanner-engine/src/test/resources/mediumtest/xoo/sample/externalIssues.json @@ -50,7 +50,21 @@ "primaryLocation": { "message": "fix the bug here", "filePath": "xources/hello/helloscala.xoo" - } + }, + "secondaryLocations": [ + { + "filePath": "xources/hello/HelloJava.xoo", + "textRange": { + "startLine": 1 + } + }, + { + "filePath": "xources/hello/HelloJava.xoo", + "textRange": { + "startLine": 3 + } + } + ] } ] } \ No newline at end of file diff --git a/sonar-scanner-engine/src/test/resources/org/sonar/scanner/externalissue/report.json b/sonar-scanner-engine/src/test/resources/org/sonar/scanner/externalissue/report.json index 06627d7106b..b951311436b 100644 --- a/sonar-scanner-engine/src/test/resources/org/sonar/scanner/externalissue/report.json +++ b/sonar-scanner-engine/src/test/resources/org/sonar/scanner/externalissue/report.json @@ -39,6 +39,31 @@ "message": "fix the bug here", "filePath": "file3.js" } + }, + { + "engineId": "eslint", + "ruleId": "rule3", + "severity": "MAJOR", + "type": "BUG", + "primaryLocation": { + "message": "fix the bug here", + "filePath": "file3.js" + }, + "secondaryLocations": [ + { + "message": "fix the bug here", + "filePath": "file1.js", + "textRange": { + "startLine": 1 + } + }, + { + "filePath": "file2.js", + "textRange": { + "startLine": 2 + } + } + ] } ] } diff --git a/sonar-scanner-engine/src/test/resources/org/sonar/scanner/externalissue/report_missing_message.json b/sonar-scanner-engine/src/test/resources/org/sonar/scanner/externalissue/report_missing_message.json new file mode 100644 index 00000000000..c191559faf7 --- /dev/null +++ b/sonar-scanner-engine/src/test/resources/org/sonar/scanner/externalissue/report_missing_message.json @@ -0,0 +1,28 @@ +{ +"issues" : [ + { + "engineId": "eslint", + "ruleId": "rule1", + "severity": "MAJOR", + "type": "CODE_SMELL", + "primaryLocation": { + "message": "fix the issue here", + "filePath": "file1.js", + "textRange": { + "startLine": 1, + "endLine": 2 + } + } + }, + { + "engineId": "eslint", + "ruleId": "rule2", + "severity": "MAJOR", + "type": "BUG", + "primaryLocation": { + "filePath": "file1.js" + } + } +] +} + diff --git a/tests/projects/shared/xoo-sample/externalIssues.json b/tests/projects/shared/xoo-sample/externalIssues.json new file mode 100644 index 00000000000..9be94f4cae2 --- /dev/null +++ b/tests/projects/shared/xoo-sample/externalIssues.json @@ -0,0 +1,45 @@ +{ +"issues" : [ + { + "engineId": "externalXoo", + "ruleId": "rule1", + "severity": "MAJOR", + "type": "CODE_SMELL", + "effortMinutes": 50, + "primaryLocation": { + "message": "fix the issue here", + "filePath": "src/main/xoo/sample/Sample.xoo", + "textRange": { + "startLine": 5, + "startColumn": 2, + "endLine": 5, + "endColumn": 21 + } + } + }, + { + "engineId": "externalXoo", + "ruleId": "rule2", + "severity": "CRITICAL", + "type": "BUG", + "primaryLocation": { + "message": "fix the bug here", + "filePath": "src/main/xoo/sample/Sample.xoo" + }, + "secondaryLocations": [ + { + "filePath": "src/main/xoo/sample/Sample.xoo", + "textRange": { + "startLine": 1 + } + }, + { + "filePath": "unknown", + "textRange": { + "startLine": 3 + } + } + ] + } +] +} \ No newline at end of file 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 d8d4677560c..4a1f4b91a7b 100644 --- a/tests/src/test/java/org/sonarqube/tests/issue/ExternalIssueTest.java +++ b/tests/src/test/java/org/sonarqube/tests/issue/ExternalIssueTest.java @@ -19,10 +19,11 @@ */ package org.sonarqube.tests.issue; -import com.sonar.orchestrator.build.SonarScanner; +import com.sonar.orchestrator.Orchestrator; +import java.util.Collections; import java.util.List; -import java.util.stream.Collectors; import org.junit.Before; +import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.sonarqube.qa.util.Tester; @@ -35,9 +36,15 @@ import util.ItUtils; import static org.assertj.core.api.Assertions.assertThat; -public class ExternalIssueTest extends AbstractIssueTest { +public class ExternalIssueTest { private static final String PROJECT_KEY = "project"; + // This class uses its own instance of the server because it creates external rules in it + @ClassRule + public static final Orchestrator ORCHESTRATOR = ItUtils.newOrchestratorBuilder() + .addPlugin(ItUtils.xooPlugin()) + .build(); + @Rule public Tester tester = new Tester(ORCHESTRATOR); @@ -50,45 +57,95 @@ public class ExternalIssueTest extends AbstractIssueTest { @Test public void should_import_external_issues_and_create_external_rules() { - noExternalRuleAndNoIssues(); + noIssues(); + ruleDoesntExist("external_xoo:OneExternalIssuePerLine"); - SonarScanner sonarScanner = ItUtils.runProjectAnalysis(ORCHESTRATOR, "shared/xoo-sample", + ItUtils.runProjectAnalysis(ORCHESTRATOR, "shared/xoo-sample", "sonar.oneExternalIssuePerLine.activate", "true"); List issuesList = tester.wsClient().issues().search(new SearchRequest()).getIssuesList(); assertThat(issuesList).hasSize(17); assertThat(issuesList).allMatch(issue -> "external_xoo:OneExternalIssuePerLine".equals(issue.getRule())); assertThat(issuesList).allMatch(issue -> "This issue is generated on each line".equals(issue.getMessage())); - assertThat(issuesList).allMatch(issue -> "This issue is generated on each line".equals(issue.getMessage())); assertThat(issuesList).allMatch(issue -> Severity.MAJOR.equals(issue.getSeverity())); - assertThat(issuesList).allMatch(issue -> RuleType.CODE_SMELL.equals(issue.getType())); + 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")); - List rulesList = tester.wsClient().rules() - .search(new org.sonarqube.ws.client.rules.SearchRequest().setIsExternal(Boolean.toString(true))).getRulesList(); - List externalRules = rulesList.stream().filter(rule -> rule.getIsExternal()).collect(Collectors.toList()); - - assertThat(externalRules).hasSize(1); - assertThat(externalRules.get(0).getKey()).isEqualTo("external_xoo:OneExternalIssuePerLine"); - assertThat(externalRules.get(0).getIsTemplate()).isFalse(); - assertThat(externalRules.get(0).getIsExternal()).isTrue(); - assertThat(externalRules.get(0).getTags().getTagsCount()).isEqualTo(0); - assertThat(externalRules.get(0).getScope()).isEqualTo(RuleScope.ALL); + ruleExists("external_xoo:OneExternalIssuePerLine"); // second analysis, issue tracking should work - sonarScanner = ItUtils.runProjectAnalysis(ORCHESTRATOR, "shared/xoo-sample", + ItUtils.runProjectAnalysis(ORCHESTRATOR, "shared/xoo-sample", "sonar.oneExternalIssuePerLine.activate", "true"); issuesList = tester.wsClient().issues().search(new SearchRequest()).getIssuesList(); assertThat(issuesList).hasSize(17); } - private void noExternalRuleAndNoIssues() { + @Test + public void should_import_external_issues_from_json_report_and_create_external_rules() { + noIssues(); + ruleDoesntExist("external_externalXoo:rule1"); + ruleDoesntExist("external_externalXoo:rule2"); + + ItUtils.runProjectAnalysis(ORCHESTRATOR, "shared/xoo-sample", + "sonar.externalIssuesReportPaths", "externalIssues.json"); + + List issuesList = tester.wsClient().issues().search(new SearchRequest() + .setRules(Collections.singletonList("external_externalXoo:rule1"))).getIssuesList(); + assertThat(issuesList).hasSize(1); + + assertThat(issuesList.get(0).getRule()).isEqualTo("external_externalXoo:rule1"); + assertThat(issuesList.get(0).getMessage()).isEqualTo("fix the issue here"); + assertThat(issuesList.get(0).getSeverity()).isEqualTo(Severity.MAJOR); + assertThat(issuesList.get(0).getType()).isEqualTo(RuleType.CODE_SMELL); + assertThat(issuesList.get(0).getComponent()).isEqualTo("sample:src/main/xoo/sample/Sample.xoo"); + assertThat(issuesList.get(0).getStatus()).isEqualTo("OPEN"); + assertThat(issuesList.get(0).getEffort()).isEqualTo("20min"); + assertThat(issuesList.get(0).getExternalRuleEngine()).isEqualTo("externalXoo"); + + issuesList = tester.wsClient().issues().search(new SearchRequest() + .setRules(Collections.singletonList("external_externalXoo:rule2"))).getIssuesList(); + assertThat(issuesList).hasSize(1); + + assertThat(issuesList.get(0).getRule()).isEqualTo("external_externalXoo:rule2"); + assertThat(issuesList.get(0).getMessage()).isEqualTo("fix the bug here"); + assertThat(issuesList.get(0).getSeverity()).isEqualTo(Severity.CRITICAL); + assertThat(issuesList.get(0).getType()).isEqualTo(RuleType.BUG); + assertThat(issuesList.get(0).getComponent()).isEqualTo("sample:src/main/xoo/sample/Sample.xoo"); + assertThat(issuesList.get(0).getStatus()).isEqualTo("OPEN"); + assertThat(issuesList.get(0).getExternalRuleEngine()).isEqualTo("externalXoo"); + + ruleExists("external_externalXoo:rule1"); + ruleExists("external_externalXoo:rule2"); + } + + private void ruleDoesntExist(String key) { List rulesList = tester.wsClient().rules() - .search(new org.sonarqube.ws.client.rules.SearchRequest().setIsExternal(Boolean.toString(true))).getRulesList(); - assertThat(rulesList).noneMatch(rule -> rule.getIsExternal()); + .search(new org.sonarqube.ws.client.rules.SearchRequest() + .setRuleKey(key) + .setIsExternal(Boolean.toString(true))) + .getRulesList(); + assertThat(rulesList).isEmpty(); + + } + + private void ruleExists(String key) { + List rulesList = tester.wsClient().rules() + .search(new org.sonarqube.ws.client.rules.SearchRequest() + .setRuleKey(key) + .setIsExternal(Boolean.toString(true))) + .getRulesList(); + + assertThat(rulesList).hasSize(1); + assertThat(rulesList.get(0).getKey()).isEqualTo(key); + assertThat(rulesList.get(0).getIsTemplate()).isFalse(); + assertThat(rulesList.get(0).getIsExternal()).isTrue(); + assertThat(rulesList.get(0).getTags().getTagsCount()).isEqualTo(0); + assertThat(rulesList.get(0).getScope()).isEqualTo(RuleScope.ALL); + } + private void noIssues() { List issuesList = tester.wsClient().issues().search(new SearchRequest()).getIssuesList(); assertThat(issuesList).isEmpty(); }