*/
package org.sonar.scanner.externalissue;
+import java.util.HashMap;
import java.util.LinkedHashSet;
+import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
public void execute() {
if (report.rules != null) {
- importRules();
+ importNewFormat();
} else {
importDeprecatedFormat();
}
logStatistics(issueCount, StringUtils.EMPTY);
}
- private void importRules() {
+ private void importNewFormat() {
+ Map<String, Rule> rulesMap = new HashMap<>();
+
for (Rule rule : report.rules) {
+ rulesMap.put(rule.id, rule);
NewAdHocRule adHocRule = createAdHocRule(rule);
- int issueCount = 0;
- for (Issue issue : rule.issues) {
- if (importIssue(issue, rule)) {
- issueCount++;
- }
- }
- logStatistics(issueCount, String.format(" for ruleId '%s'", rule.ruleId));
adHocRule.save();
}
+
+ int issueCount = 0;
+ for (Issue issue : report.issues) {
+ if (importIssue(issue, rulesMap.get(issue.ruleId))) {
+ issueCount++;
+ }
+ }
+ logStatistics(issueCount, StringUtils.EMPTY);
}
private NewAdHocRule createAdHocRule(Rule rule) {
NewAdHocRule adHocRule = context.newAdHocRule();
- adHocRule.ruleId(rule.ruleId);
+ adHocRule.ruleId(rule.id);
adHocRule.name(rule.name);
adHocRule.description(rule.description);
adHocRule.engineId(rule.engineId);
private boolean importIssue(Issue issue, ExternalIssueReport.Rule rule) {
NewExternalIssue externalIssue = context.newExternalIssue()
.engineId(rule.engineId)
- .ruleId(rule.ruleId)
+ .ruleId(rule.id)
.severity(backmapSeverityFromImpact(rule))
.type(backmapTypeFromImpact(rule));
}
static class Rule {
- String ruleId;
+ String id;
String engineId;
String name;
@Nullable
String description;
String cleanCodeAttribute;
Impact[] impacts;
- Issue[] issues;
}
static class Impact {
package org.sonar.scanner.externalissue;
import java.nio.file.Path;
+import java.util.HashSet;
+import java.util.Set;
import javax.annotation.Nullable;
-import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.scanner.ScannerSide;
@ScannerSide
public class ExternalIssueReportValidator {
private static final Logger LOGGER = LoggerFactory.getLogger(ExternalIssueReportValidator.class);
- private static final String RULE_ID = "ruleId";
+ private static final String ISSUE_RULE_ID = "ruleId";
private static final String SEVERITY = "severity";
private static final String TYPE = "type";
private static final String DOCUMENTATION_SUFFIX = "/analyzing-source-code/importing-external-issues/generic-issue-import-format/";
this.documentationLinkGenerator = documentationLinkGenerator;
}
+ /**
+ * <p>Since we are supporting deprecated format, we decide which format it is in order by:
+ * <ul>
+ * <li>if both 'rules' and 'issues' fields are present, we assume it is CCT format</li>
+ * <li>if only 'issues' field is present, we assume it is deprecated format</li>
+ * <li>otherwise we throw exception as an invalid report was detected</li>
+ * </ul>
+ * </p>
+ */
public void validate(ExternalIssueReport report, Path reportPath) {
- if (report.rules != null) {
- checkNoField(report.issues, "issues", reportPath);
- validateRules(report.rules, reportPath);
- } else if (report.issues != null) {
+ if (report.rules != null && report.issues != null) {
+ Set<String> ruleIds = validateRules(report.rules, reportPath);
+ validateIssuesCctFormat(report.issues, ruleIds, reportPath);
+ } else if (report.rules == null && report.issues != null) {
String documentationLink = documentationLinkGenerator.getDocumentationLink(DOCUMENTATION_SUFFIX);
LOGGER.warn("External issues were imported with a deprecated format which will be removed soon. " +
"Please switch to the newest format to fully benefit from Clean Code: {}", documentationLink);
- validateIssues(report.issues, reportPath);
+ validateIssuesDeprecatedFormat(report.issues, reportPath);
} else {
- throw new IllegalStateException(String.format("Failed to parse report '%s': missing mandatory field 'rules'", reportPath));
+ throw new IllegalStateException(String.format("Failed to parse report '%s': invalid report detected.", reportPath));
}
}
- private static void validateIssues(ExternalIssueReport.Issue[] issues, Path reportPath) {
+ private static void validateIssuesCctFormat(ExternalIssueReport.Issue[] issues, Set<String> ruleIds, Path reportPath) {
for (ExternalIssueReport.Issue issue : issues) {
- mandatoryField(issue.ruleId, RULE_ID, reportPath);
+ mandatoryField(issue.ruleId, ISSUE_RULE_ID, reportPath);
+ checkRuleExistsInReport(ruleIds, issue, reportPath);
+ checkNoField(issue.severity, SEVERITY, reportPath);
+ checkNoField(issue.type, TYPE, reportPath);
+ validateAlwaysRequiredIssueFields(issue, reportPath);
+ }
+ }
+
+ private static void validateIssuesDeprecatedFormat(ExternalIssueReport.Issue[] issues, Path reportPath) {
+ for (ExternalIssueReport.Issue issue : issues) {
+ mandatoryField(issue.ruleId, ISSUE_RULE_ID, reportPath);
mandatoryField(issue.severity, SEVERITY, reportPath);
mandatoryField(issue.type, TYPE, reportPath);
mandatoryField(issue.engineId, "engineId", reportPath);
}
}
- private static void validateRules(ExternalIssueReport.Rule[] rules, Path reportPath) {
+ private static Set<String> validateRules(ExternalIssueReport.Rule[] rules, Path reportPath) {
+ Set<String> ruleIds = new HashSet<>();
for (ExternalIssueReport.Rule rule : rules) {
- mandatoryField(rule.ruleId, RULE_ID, reportPath);
+ mandatoryField(rule.id, "id", reportPath);
mandatoryField(rule.name, "name", reportPath);
mandatoryField(rule.engineId, "engineId", reportPath);
mandatoryField(rule.cleanCodeAttribute, "cleanCodeAttribute", reportPath);
- mandatoryArray(rule.impacts, "impacts", reportPath);
- validateIssuesAsPartOfARule(rule.issues, reportPath);
- }
- }
+ checkImpactsArray(rule.impacts, reportPath);
- private static void validateIssuesAsPartOfARule(ExternalIssueReport.Issue[] issues, Path reportPath) {
- for (ExternalIssueReport.Issue issue : issues) {
- validateAlwaysRequiredIssueFields(issue, reportPath);
- checkNoField(issue.severity, SEVERITY, reportPath);
- checkNoField(issue.type, TYPE, reportPath);
- checkNoField(issue.ruleId, RULE_ID, reportPath);
+ if (!ruleIds.add(rule.id)) {
+ throw new IllegalStateException(String.format("Failed to parse report '%s': found duplicate rule ID '%s'.", reportPath, rule.id));
+ }
}
+
+ return ruleIds;
}
private static void checkNoField(@Nullable Object value, String fieldName, Path reportPath) {
}
}
- private static void mandatoryArray(@Nullable Object[] value, String fieldName, Path reportPath) {
- mandatoryField(value, fieldName, reportPath);
+ private static void checkImpactsArray(@Nullable Object[] value, Path reportPath) {
+ mandatoryField(value, "impacts", reportPath);
if (value.length == 0) {
throw new IllegalStateException(String.format("Failed to parse report '%s': mandatory array '%s' not populated.", reportPath,
- fieldName));
+ "impacts"));
}
}
- private static void mandatoryField(@Nullable String value, String fieldName, Path reportPath) {
- if (StringUtils.isBlank(value)) {
- throw new IllegalStateException(String.format("Failed to parse report '%s': missing mandatory field '%s'.", reportPath, fieldName));
+ private static void checkRuleExistsInReport(Set<String> ruleIds, ExternalIssueReport.Issue issue, Path reportPath) {
+ if (!ruleIds.contains(issue.ruleId)) {
+ throw new IllegalStateException(String.format("Failed to parse report '%s': rule with '%s' not present.", reportPath, issue.ruleId));
}
}
}
public void execute_whenNewFormatWithZeroIssues() {
ExternalIssueReport report = new ExternalIssueReport();
ExternalIssueReport.Rule rule = createRule();
- rule.issues = new ExternalIssueReport.Issue[0];
+ report.issues = new ExternalIssueReport.Issue[0];
report.rules = new ExternalIssueReport.Rule[]{rule};
ExternalIssueImporter underTest = new ExternalIssueImporter(this.context, report);
assertThat(context.allExternalIssues()).isEmpty();
assertThat(context.allIssues()).isEmpty();
- assertThat(logs.logs(Level.INFO)).contains("Imported 0 issues in 0 files for ruleId 'some_rule_id'");
+ assertThat(logs.logs(Level.INFO)).contains("Imported 0 issues in 0 files");
}
@Test
assertThat(output.severity()).isEqualTo(Severity.CRITICAL); //backmapped
assertThat(output.type()).isEqualTo(RuleType.VULNERABILITY); //backmapped
assertThat(output.remediationEffort()).isNull();
- assertThat(logs.logs(Level.INFO)).contains("Imported 1 issue in 1 file for ruleId 'some_rule_id'");
+ assertThat(logs.logs(Level.INFO)).contains("Imported 1 issue in 1 file");
assertThat(context.allAdHocRules()).hasSize(1);
AdHocRule output1 = context.allAdHocRules().iterator().next();
public void execute_whenNewFormatContainsNonExistentCleanCodeAttribute_shouldThrowException() {
ExternalIssueReport report = new ExternalIssueReport();
ExternalIssueReport.Rule rule = createRule("not_existent_attribute", MAINTAINABILITY.name(), HIGH.name());
- rule.issues = new ExternalIssueReport.Issue[]{};
+ report.issues = new ExternalIssueReport.Issue[]{};
report.rules = new ExternalIssueReport.Rule[]{rule};
ExternalIssueImporter underTest = new ExternalIssueImporter(this.context, report);
public void execute_whenNewFormatContainsNonExistentSoftwareQuality_shouldThrowException() {
ExternalIssueReport report = new ExternalIssueReport();
ExternalIssueReport.Rule rule = createRule(CleanCodeAttribute.CONVENTIONAL.name(), "not_existent_software_quality", HIGH.name());
- rule.issues = new ExternalIssueReport.Issue[]{};
+ report.issues = new ExternalIssueReport.Issue[]{};
report.rules = new ExternalIssueReport.Rule[]{rule};
ExternalIssueImporter underTest = new ExternalIssueImporter(this.context, report);
ExternalIssueReport report = new ExternalIssueReport();
ExternalIssueReport.Rule rule = createRule(CleanCodeAttribute.CONVENTIONAL.name(), SoftwareQuality.RELIABILITY.name(),
"not_existent_impact_severity");
- rule.issues = new ExternalIssueReport.Issue[]{};
+ report.issues = new ExternalIssueReport.Issue[]{};
report.rules = new ExternalIssueReport.Rule[]{rule};
ExternalIssueImporter underTest = new ExternalIssueImporter(this.context, report);
private static ExternalIssueReport.Rule createRule(String cleanCodeAttribute, String softwareQuality, String impactSeverity) {
ExternalIssueReport.Rule rule = new ExternalIssueReport.Rule();
- rule.ruleId = RULE_ID;
+ rule.id = RULE_ID;
rule.name = RULE_NAME;
rule.engineId = RULE_ENGINE_ID;
rule.cleanCodeAttribute = cleanCodeAttribute;
private void runOn(ExternalIssueReport.Issue input) {
ExternalIssueReport report = new ExternalIssueReport();
ExternalIssueReport.Rule rule = createRule();
- rule.issues = new ExternalIssueReport.Issue[]{input};
+ input.ruleId = rule.id;
+ report.issues = new ExternalIssueReport.Issue[]{input};
report.rules = new ExternalIssueReport.Rule[]{rule};
ExternalIssueImporter underTest = new ExternalIssueImporter(this.context, report);
}
private static void assertCctReport(ExternalIssueReport report) {
- assertThat(report.issues).isNull();
assertThat(report.rules).hasSize(2);
ExternalIssueReport.Rule rule = report.rules[0];
- assertThat(rule.ruleId).isEqualTo("rule1");
+ assertThat(rule.id).isEqualTo("rule1");
assertThat(rule.engineId).isEqualTo("test");
assertThat(rule.name).isEqualTo("just_some_rule_name");
assertThat(rule.description).isEqualTo("just_some_description");
assertThat(rule.impacts[1].severity).isEqualTo("LOW");
assertThat(rule.impacts[1].softwareQuality).isEqualTo("SECURITY");
- assertThat(rule.issues).hasSize(4);
+ assertThat(report.issues).hasSize(8);
- ExternalIssueReport.Issue issue1 = rule.issues[0];
+ ExternalIssueReport.Issue issue1 = report.issues[0];
assertThat(issue1.engineId).isNull();
- assertThat(issue1.ruleId).isNull();
+ assertThat(issue1.ruleId).isEqualTo("rule1");
assertThat(issue1.severity).isNull();
assertThat(issue1.effortMinutes).isEqualTo(40);
assertThat(issue1.type).isNull();
assertThat(issue1.primaryLocation.textRange.endLine).isEqualTo(3);
assertThat(issue1.secondaryLocations).isNull();
- ExternalIssueReport.Issue issue2 = rule.issues[3];
+ ExternalIssueReport.Issue issue2 = report.issues[7];
assertThat(issue2.engineId).isNull();
- assertThat(issue2.ruleId).isNull();
+ assertThat(issue2.ruleId).isEqualTo("rule2");
assertThat(issue2.severity).isNull();
assertThat(issue2.effortMinutes).isNull();
assertThat(issue2.type).isNull();
private static final String TEST_URL = "test-url";
private final Gson gson = new Gson();
- private final Path reportPath = Paths.get("some-folder");
+ private final Path reportPath = Paths.get("report-path");
private final DocumentationLinkGenerator documentationLinkGenerator = mock(DocumentationLinkGenerator.class);
private final ExternalIssueReportValidator validator = new ExternalIssueReportValidator(documentationLinkGenerator);
when(documentationLinkGenerator.getDocumentationLink(URL)).thenReturn(TEST_URL);
}
+ @Test
+ public void validate_whenInvalidReport_shouldThrowException() throws IOException {
+ ExternalIssueReport report = readInvalidReport(DEPRECATED_REPORTS_LOCATION);
+ assertThatThrownBy(() -> validator.validate(report, reportPath))
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessage("Failed to parse report 'report-path': invalid report detected.");
+ }
+
@Test
public void validate_whenCorrect_shouldNotThrowException() throws IOException {
ExternalIssueReport report = read(REPORTS_LOCATION);
assertThatCode(() -> validator.validate(report, reportPath)).doesNotThrowAnyException();
}
+ @Test
+ public void validate_whenDuplicateRuleIdFound_shouldThrowException() throws IOException {
+ ExternalIssueReport report = read(REPORTS_LOCATION);
+ report.rules[0].id = "rule2";
+
+ assertThatThrownBy(() -> validator.validate(report, reportPath))
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessage("Failed to parse report 'report-path': found duplicate rule ID 'rule2'.");
+ }
+
@Test
public void validate_whenMissingMandatoryCleanCodeAttributeField_shouldThrowException() throws IOException {
ExternalIssueReport report = read(REPORTS_LOCATION);
assertThatThrownBy(() -> validator.validate(report, reportPath))
.isInstanceOf(IllegalStateException.class)
- .hasMessage("Failed to parse report 'some-folder': missing mandatory field 'cleanCodeAttribute'.");
+ .hasMessage("Failed to parse report 'report-path': missing mandatory field 'cleanCodeAttribute'.");
}
@Test
assertThatThrownBy(() -> validator.validate(report, reportPath))
.isInstanceOf(IllegalStateException.class)
- .hasMessage("Failed to parse report 'some-folder': missing mandatory field 'engineId'.");
+ .hasMessage("Failed to parse report 'report-path': missing mandatory field 'engineId'.");
}
@Test
public void validate_whenMissingFilepathFieldForPrimaryLocation_shouldThrowException() throws IOException {
ExternalIssueReport report = read(REPORTS_LOCATION);
- report.rules[0].issues[0].primaryLocation.filePath = null;
+ report.issues[0].primaryLocation.filePath = null;
assertThatThrownBy(() -> validator.validate(report, reportPath))
.isInstanceOf(IllegalStateException.class)
- .hasMessage("Failed to parse report 'some-folder': missing mandatory field 'filePath' in the primary location of the issue.");
+ .hasMessage("Failed to parse report 'report-path': missing mandatory field 'filePath' in the primary location of the issue.");
}
@Test
assertThatThrownBy(() -> validator.validate(report, reportPath))
.isInstanceOf(IllegalStateException.class)
- .hasMessage("Failed to parse report 'some-folder': missing mandatory field 'impacts'.");
+ .hasMessage("Failed to parse report 'report-path': missing mandatory field 'impacts'.");
}
@Test
public void validate_whenMissingMessageFieldForPrimaryLocation_shouldThrowException() throws IOException {
ExternalIssueReport report = read(REPORTS_LOCATION);
- report.rules[0].issues[0].primaryLocation.message = null;
+ report.issues[0].primaryLocation.message = null;
assertThatThrownBy(() -> validator.validate(report, reportPath))
.isInstanceOf(IllegalStateException.class)
- .hasMessage("Failed to parse report 'some-folder': missing mandatory field 'message' in the primary location of the issue.");
+ .hasMessage("Failed to parse report 'report-path': missing mandatory field 'message' in the primary location of the issue.");
}
@Test
public void validate_whenMissingStartLineFieldForPrimaryLocation_shouldThrowException() throws IOException {
ExternalIssueReport report = read(REPORTS_LOCATION);
- report.rules[0].issues[0].primaryLocation.textRange.startLine = null;
+ report.issues[0].primaryLocation.textRange.startLine = null;
assertThatThrownBy(() -> validator.validate(report, reportPath))
.isInstanceOf(IllegalStateException.class)
- .hasMessage("Failed to parse report 'some-folder': missing mandatory field 'startLine of the text range' in the primary location of the issue.");
+ .hasMessage("Failed to parse report 'report-path': missing mandatory field 'startLine of the text range' in the primary location of the issue.");
}
@Test
public void validate_whenReportMissingFilePathForSecondaryLocation_shouldThrowException() throws IOException {
ExternalIssueReport report = read(REPORTS_LOCATION);
- report.rules[0].issues[3].secondaryLocations[0].filePath = null;
+ report.issues[3].secondaryLocations[0].filePath = null;
assertThatThrownBy(() -> validator.validate(report, reportPath))
.isInstanceOf(IllegalStateException.class)
- .hasMessage("Failed to parse report 'some-folder': missing mandatory field 'filePath' in a secondary location of the issue.");
+ .hasMessage("Failed to parse report 'report-path': missing mandatory field 'filePath' in a secondary location of the issue.");
}
@Test
public void validate_whenReportMissingTextRangeForSecondaryLocation_shouldThrowException() throws IOException {
ExternalIssueReport report = read(REPORTS_LOCATION);
- report.rules[0].issues[3].secondaryLocations[0].textRange = null;
+ report.issues[3].secondaryLocations[0].textRange = null;
assertThatThrownBy(() -> validator.validate(report, reportPath))
.isInstanceOf(IllegalStateException.class)
- .hasMessage("Failed to parse report 'some-folder': missing mandatory field 'textRange' in a secondary location of the issue.");
+ .hasMessage("Failed to parse report 'report-path': missing mandatory field 'textRange' in a secondary location of the issue.");
}
@Test
public void validate_whenReportMissingTextRangeStartLineForSecondaryLocation_shouldThrowException() throws IOException {
ExternalIssueReport report = read(REPORTS_LOCATION);
- report.rules[0].issues[3].secondaryLocations[0].textRange.startLine = null;
+ report.issues[3].secondaryLocations[0].textRange.startLine = null;
assertThatThrownBy(() -> validator.validate(report, reportPath))
.isInstanceOf(IllegalStateException.class)
- .hasMessage("Failed to parse report 'some-folder': missing mandatory field 'startLine of the text range' in a secondary location of the issue.");
+ .hasMessage("Failed to parse report 'report-path': missing mandatory field 'startLine of the text range' in a secondary location of the issue.");
}
@Test
assertThatThrownBy(() -> validator.validate(report, reportPath))
.isInstanceOf(IllegalStateException.class)
- .hasMessage("Failed to parse report 'some-folder': missing mandatory field 'name'.");
+ .hasMessage("Failed to parse report 'report-path': missing mandatory field 'name'.");
}
@Test
public void validate_whenMissingPrimaryLocationField_shouldThrowException() throws IOException {
ExternalIssueReport report = read(REPORTS_LOCATION);
- report.rules[0].issues[0].primaryLocation = null;
+ report.issues[0].primaryLocation = null;
+
+ assertThatThrownBy(() -> validator.validate(report, reportPath))
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessage("Failed to parse report 'report-path': missing mandatory field 'primaryLocation'.");
+ }
+
+ @Test
+ public void validate_whenMissingOrEmptyRuleIdField_shouldThrowException() throws IOException {
+ String errorMessage = "Failed to parse report 'report-path': missing mandatory field 'id'.";
+
+ ExternalIssueReport report = read(REPORTS_LOCATION);
+ report.rules[0].id = null;
+ assertThatThrownBy(() -> validator.validate(report, reportPath))
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessage(errorMessage);
+ report.rules[0].id = "";
assertThatThrownBy(() -> validator.validate(report, reportPath))
.isInstanceOf(IllegalStateException.class)
- .hasMessage("Failed to parse report 'some-folder': missing mandatory field 'primaryLocation'.");
+ .hasMessage(errorMessage);
}
@Test
- public void validate_whenMissingRuleIdField_shouldThrowException() throws IOException {
+ public void validate_whenIssueContainsRuleIdNotPresentInReport_shouldThrowException() throws IOException {
ExternalIssueReport report = read(REPORTS_LOCATION);
- report.rules[0].ruleId = null;
+ report.issues[0].ruleId = "rule-id-not-present";
assertThatThrownBy(() -> validator.validate(report, reportPath))
.isInstanceOf(IllegalStateException.class)
- .hasMessage("Failed to parse report 'some-folder': missing mandatory field 'ruleId'.");
+ .hasMessage("Failed to parse report 'report-path': rule with 'rule-id-not-present' not present.");
}
@Test
- public void validate_whenContainsDeprecatedIssuesEntry_shouldThrowException() throws IOException {
+ public void validate_whenIssueRuleIdNotPresentInReport_shouldThrowException() throws IOException {
ExternalIssueReport report = read(REPORTS_LOCATION);
- report.issues = new ExternalIssueReport.Issue[] { new ExternalIssueReport.Issue() };
+ report.issues[0].ruleId = null;
assertThatThrownBy(() -> validator.validate(report, reportPath))
.isInstanceOf(IllegalStateException.class)
- .hasMessage("Deprecated 'issues' field found in the following report: 'some-folder'.");
+ .hasMessage("Failed to parse report 'report-path': missing mandatory field 'ruleId'.");
}
@Test
public void validate_whenContainsDeprecatedSeverityEntry_shouldThrowException() throws IOException {
ExternalIssueReport report = read(REPORTS_LOCATION);
- report.rules[0].issues[0].severity = "MAJOR";
+ report.issues[0].severity = "MAJOR";
assertThatThrownBy(() -> validator.validate(report, reportPath))
.isInstanceOf(IllegalStateException.class)
- .hasMessage("Deprecated 'severity' field found in the following report: 'some-folder'.");
+ .hasMessage("Deprecated 'severity' field found in the following report: 'report-path'.");
}
@Test
public void validate_whenContainsDeprecatedTypeEntry_shouldThrowException() throws IOException {
ExternalIssueReport report = read(REPORTS_LOCATION);
- report.rules[0].issues[0].type = "BUG";
+ report.issues[0].type = "BUG";
assertThatThrownBy(() -> validator.validate(report, reportPath))
.isInstanceOf(IllegalStateException.class)
- .hasMessage("Deprecated 'type' field found in the following report: 'some-folder'.");
+ .hasMessage("Deprecated 'type' field found in the following report: 'report-path'.");
}
@Test
assertThatThrownBy(() -> validator.validate(report, reportPath))
.isInstanceOf(IllegalStateException.class)
- .hasMessage("Failed to parse report 'some-folder': mandatory array 'impacts' not populated.");
+ .hasMessage("Failed to parse report 'report-path': mandatory array 'impacts' not populated.");
}
@Test
assertThatThrownBy(() -> validator.validate(report, reportPath))
.isInstanceOf(IllegalStateException.class)
- .hasMessage("Failed to parse report 'some-folder': missing mandatory field 'engineId'.");
+ .hasMessage("Failed to parse report 'report-path': missing mandatory field 'engineId'.");
assertWarningLog();
}
assertThatThrownBy(() -> validator.validate(report, reportPath))
.isInstanceOf(IllegalStateException.class)
- .hasMessage("Failed to parse report 'some-folder': missing mandatory field 'filePath' in the primary location of the issue.");
+ .hasMessage("Failed to parse report 'report-path': missing mandatory field 'filePath' in the primary location of the issue.");
assertWarningLog();
}
assertThatThrownBy(() -> validator.validate(report, reportPath))
.isInstanceOf(IllegalStateException.class)
- .hasMessage("Failed to parse report 'some-folder': missing mandatory field 'message' in the primary location of the issue.");
+ .hasMessage("Failed to parse report 'report-path': missing mandatory field 'message' in the primary location of the issue.");
assertWarningLog();
}
assertThatThrownBy(() -> validator.validate(report, reportPath))
.isInstanceOf(IllegalStateException.class)
- .hasMessage("Failed to parse report 'some-folder': missing mandatory field 'primaryLocation'.");
+ .hasMessage("Failed to parse report 'report-path': missing mandatory field 'primaryLocation'.");
assertWarningLog();
}
assertThatThrownBy(() -> validator.validate(report, reportPath))
.isInstanceOf(IllegalStateException.class)
- .hasMessage("Failed to parse report 'some-folder': missing mandatory field 'startLine of the text range' in the primary location of the issue.");
+ .hasMessage("Failed to parse report 'report-path': missing mandatory field 'startLine of the text range' in the primary location of the issue.");
assertWarningLog();
}
assertThatThrownBy(() -> validator.validate(report, reportPath))
.isInstanceOf(IllegalStateException.class)
- .hasMessage("Failed to parse report 'some-folder': missing mandatory field 'filePath' in a secondary location of the issue.");
+ .hasMessage("Failed to parse report 'report-path': missing mandatory field 'filePath' in a secondary location of the issue.");
assertWarningLog();
}
assertThatThrownBy(() -> validator.validate(report, reportPath))
.isInstanceOf(IllegalStateException.class)
- .hasMessage("Failed to parse report 'some-folder': missing mandatory field 'textRange' in a secondary location of the issue.");
+ .hasMessage("Failed to parse report 'report-path': missing mandatory field 'textRange' in a secondary location of the issue.");
assertWarningLog();
}
assertThatThrownBy(() -> validator.validate(report, reportPath))
.isInstanceOf(IllegalStateException.class)
- .hasMessage("Failed to parse report 'some-folder': missing mandatory field 'startLine of the text range' in a secondary location of the issue.");
+ .hasMessage("Failed to parse report 'report-path': missing mandatory field 'startLine of the text range' in a secondary location of the issue.");
assertWarningLog();
}
assertThatThrownBy(() -> validator.validate(report, reportPath))
.isInstanceOf(IllegalStateException.class)
- .hasMessage("Failed to parse report 'some-folder': missing mandatory field 'ruleId'.");
+ .hasMessage("Failed to parse report 'report-path': missing mandatory field 'ruleId'.");
assertWarningLog();
}
assertThatThrownBy(() -> validator.validate(report, reportPath))
.isInstanceOf(IllegalStateException.class)
- .hasMessage("Failed to parse report 'some-folder': missing mandatory field 'severity'.");
+ .hasMessage("Failed to parse report 'report-path': missing mandatory field 'severity'.");
assertWarningLog();
}
assertThatThrownBy(() -> validator.validate(report, reportPath))
.isInstanceOf(IllegalStateException.class)
- .hasMessage("Failed to parse report 'some-folder': missing mandatory field 'type'.");
+ .hasMessage("Failed to parse report 'report-path': missing mandatory field 'type'.");
assertWarningLog();
}
- @Test
- public void validate_whenEmptyStringForMandatoryField_shouldStillThrowException() throws IOException {
- ExternalIssueReport report = read(REPORTS_LOCATION);
- report.rules[0].ruleId = "";
-
- assertThatThrownBy(() -> validator.validate(report, reportPath))
- .isInstanceOf(IllegalStateException.class)
- .hasMessage("Failed to parse report 'some-folder': missing mandatory field 'ruleId'.");
- }
-
private void assertWarningLog() {
assertThat(logTester.getLogs(Level.WARN))
.extracting(LogAndArguments::getFormattedMsg)
return gson.fromJson(reader, ExternalIssueReport.class);
}
+ private ExternalIssueReport readInvalidReport(String location) throws IOException {
+ Reader reader = Files.newBufferedReader(Paths.get(location + "invalid_report.json"), StandardCharsets.UTF_8);
+ return gson.fromJson(reader, ExternalIssueReport.class);
+ }
+
}
+++ /dev/null
-{
- "some_other_field": "with_some_values"
-}
{
"rules": [
{
- "ruleId": "rule1",
+ "id": "rule1",
"name": "just_some_rule_name",
"description": "just_some_description",
"engineId": "test",
"softwareQuality": "SECURITY",
"severity": "LOW"
}
- ],
- "issues": [
- {
- "effortMinutes": 40,
- "primaryLocation": {
- "message": "fix the issue here",
- "filePath": "file1.js",
- "textRange": {
- "startLine": 1,
- "startColumn": 2,
- "endLine": 3,
- "endColumn": 4
- }
- }
- },
- {
- "primaryLocation": {
- "message": "fix the bug here",
- "filePath": "file2.js",
- "textRange": {
- "startLine": 3
- }
- }
- },
- {
- "primaryLocation": {
- "message": "fix the bug here",
- "filePath": "file3.js"
- }
- },
- {
- "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
- }
- }
- ]
- }
]
},
{
- "ruleId": "rule2",
+ "id": "rule2",
"name": "just_some_other_rule_name",
"description": "just_some_description",
"engineId": "test2",
"softwareQuality": "RELIABILITY",
"severity": "LOW"
}
- ],
- "issues": [
+ ]
+ }
+ ],
+ "issues": [
+ {
+ "ruleId": "rule1",
+ "effortMinutes": 40,
+ "primaryLocation": {
+ "message": "fix the issue here",
+ "filePath": "file1.js",
+ "textRange": {
+ "startLine": 1,
+ "startColumn": 2,
+ "endLine": 3,
+ "endColumn": 4
+ }
+ }
+ },
+ {
+ "ruleId": "rule1",
+ "primaryLocation": {
+ "message": "fix the bug here",
+ "filePath": "file2.js",
+ "textRange": {
+ "startLine": 3
+ }
+ }
+ },
+ {
+ "ruleId": "rule1",
+ "primaryLocation": {
+ "message": "fix the bug here",
+ "filePath": "file3.js"
+ }
+ },
+ {
+ "ruleId": "rule1",
+ "primaryLocation": {
+ "message": "fix the bug here",
+ "filePath": "file3.js"
+ },
+ "secondaryLocations": [
{
- "effortMinutes": 40,
- "primaryLocation": {
- "message": "fix the issue here",
- "filePath": "file1.js",
- "textRange": {
- "startLine": 1,
- "startColumn": 2,
- "endLine": 3,
- "endColumn": 4
- }
+ "message": "fix the bug here",
+ "filePath": "file1.js",
+ "textRange": {
+ "startLine": 1
}
},
{
- "primaryLocation": {
- "message": "fix the bug here",
- "filePath": "file2.js",
- "textRange": {
- "startLine": 3
- }
+ "filePath": "file2.js",
+ "textRange": {
+ "startLine": 2
}
- },
+ }
+ ]
+ },
+ {
+ "ruleId": "rule2",
+ "effortMinutes": 40,
+ "primaryLocation": {
+ "message": "fix the issue here",
+ "filePath": "file1.js",
+ "textRange": {
+ "startLine": 1,
+ "startColumn": 2,
+ "endLine": 3,
+ "endColumn": 4
+ }
+ }
+ },
+ {
+ "ruleId": "rule2",
+ "primaryLocation": {
+ "message": "fix the bug here",
+ "filePath": "file2.js",
+ "textRange": {
+ "startLine": 3
+ }
+ }
+ },
+ {
+ "ruleId": "rule2",
+ "primaryLocation": {
+ "message": "fix the bug here",
+ "filePath": "file3.js"
+ }
+ },
+ {
+ "ruleId": "rule2",
+ "primaryLocation": {
+ "message": "fix the bug here",
+ "filePath": "file3.js"
+ },
+ "secondaryLocations": [
{
- "primaryLocation": {
- "message": "fix the bug here",
- "filePath": "file3.js"
+ "message": "fix the bug here",
+ "filePath": "file1.js",
+ "textRange": {
+ "startLine": 1
}
},
{
- "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
- }
- }
- ]
+ "filePath": "file2.js",
+ "textRange": {
+ "startLine": 2
+ }
}
]
}
--- /dev/null
+{
+ "some_other_field": "with_some_values"
+}