diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4ade06e..4e703d7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [1.2.1] - 2024-05-14
+
+## Fixed
+
+- Resolve language suffix conflict between the plugin's custom YAML/JSON support and SonarQube's built-in language detection.
+
## [1.2.0] - 2024-05-05
## Added
diff --git a/README.md b/README.md
index b951732..852982a 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
-# 🛠️ Sonar OpenApi (plugin)    [](https://www.gnu.org/licenses/lgpl-3.0)
+# 🛠️ Sonar OpenApi (plugin)    [](https://www.gnu.org/licenses/lgpl-3.0)
Sonar OpenApi (plugin) is a code analyzer for OpenAPI specifications, is the spiritual successor of [SonarOpenApi](https://github.com/societe-generale/sonar-openapi), carrying on from the point where it left off with support of Apiaddicts community.
diff --git a/its/pom.xml b/its/pom.xml
index c07aa94..49f6740 100644
--- a/its/pom.xml
+++ b/its/pom.xml
@@ -5,7 +5,7 @@
org.apiaddicts.apitools.dosonarapi
dosonarapi
- 1.2.0
+ 1.2.1
../pom.xml
4.0.0
diff --git a/openapi-checks/pom.xml b/openapi-checks/pom.xml
index e13b2c4..4ae3c61 100644
--- a/openapi-checks/pom.xml
+++ b/openapi-checks/pom.xml
@@ -5,7 +5,7 @@
org.apiaddicts.apitools.dosonarapi
dosonarapi
- 1.2.0
+ 1.2.1
../pom.xml
diff --git a/openapi-checks/src/main/java/org/apiaddicts/apitools/dosonarapi/checks/CheckList.java b/openapi-checks/src/main/java/org/apiaddicts/apitools/dosonarapi/checks/CheckList.java
index 83e30d6..2933c5f 100644
--- a/openapi-checks/src/main/java/org/apiaddicts/apitools/dosonarapi/checks/CheckList.java
+++ b/openapi-checks/src/main/java/org/apiaddicts/apitools/dosonarapi/checks/CheckList.java
@@ -23,7 +23,10 @@
import java.util.List;
public final class CheckList {
- public static final String REPOSITORY_KEY = "openapi";
+ public static final String YAML_REPOSITORY_KEY = "openapi-yaml";
+ public static final String JSON_REPOSITORY_KEY = "openapi-json";
+ public static final String YAML_LANGUAGE = "yaml";
+ public static final String JSON_LANGUAGE = "json";
private CheckList() {
}
diff --git a/openapi-checks/src/test/java/org/apiaddicts/apitools/dosonarapi/checks/CheckListTest.java b/openapi-checks/src/test/java/org/apiaddicts/apitools/dosonarapi/checks/CheckListTest.java
new file mode 100644
index 0000000..8a31509
--- /dev/null
+++ b/openapi-checks/src/test/java/org/apiaddicts/apitools/dosonarapi/checks/CheckListTest.java
@@ -0,0 +1,56 @@
+/*
+ * doSonarAPI: SonarQube OpenAPI Plugin
+ * Copyright (C) 2021-2022 Apiaddicts
+ * contacta AT apiaddicts DOT org
+ *
+ * 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.apiaddicts.apitools.dosonarapi.checks;
+
+import java.util.List;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class CheckListTest {
+
+ @Test
+ public void returns_all_check_classes() {
+ List> checks = CheckList.getChecks();
+ assertThat(checks).isNotEmpty().contains(
+ PathMaskeradingCheck.class,
+ MediaTypeCheck.class,
+ ParsingErrorCheck.class,
+ DefaultResponseCheck.class,
+ DefinedResponseCheck.class,
+ DeclaredTagCheck.class,
+ DocumentedTagCheck.class,
+ AtMostOneBodyParameterCheck.class,
+ NoUnusedDefinitionCheck.class,
+ NoContentIn204Check.class,
+ ProvideOpSummaryCheck.class,
+ ContactValidEmailCheck.class,
+ DescriptionDiffersSummaryCheck.class
+ );
+ }
+
+ @Test
+ public void constants_have_expected_values() {
+ assertThat(CheckList.YAML_REPOSITORY_KEY).isEqualTo("openapi-yaml");
+ assertThat(CheckList.JSON_REPOSITORY_KEY).isEqualTo("openapi-json");
+ assertThat(CheckList.YAML_LANGUAGE).isEqualTo("yaml");
+ assertThat(CheckList.JSON_LANGUAGE).isEqualTo("json");
+ }
+}
diff --git a/openapi-checks/src/test/java/org/apiaddicts/apitools/dosonarapi/checks/DocumentedTagCheckTest.java b/openapi-checks/src/test/java/org/apiaddicts/apitools/dosonarapi/checks/DocumentedTagCheckTest.java
index acbb4ae..80fc0b5 100644
--- a/openapi-checks/src/test/java/org/apiaddicts/apitools/dosonarapi/checks/DocumentedTagCheckTest.java
+++ b/openapi-checks/src/test/java/org/apiaddicts/apitools/dosonarapi/checks/DocumentedTagCheckTest.java
@@ -24,12 +24,12 @@
public class DocumentedTagCheckTest {
@Test
- public void verify_media_type_in_v2() {
- OpenApiCheckVerifier.verify("src/test/resources/checks/v2/declared-tag.yaml", new DeclaredTagCheck(), true, false, false);
+ public void verify_documented_tag_in_v2() {
+ OpenApiCheckVerifier.verify("src/test/resources/checks/v2/documented-tag.yaml", new DocumentedTagCheck(), true, false, false);
}
@Test
- public void verify_media_type_in_v3() {
- OpenApiCheckVerifier.verify("src/test/resources/checks/v3/declared-tag.yaml", new DeclaredTagCheck(), false, true, false);
+ public void verify_documented_tag_in_v3() {
+ OpenApiCheckVerifier.verify("src/test/resources/checks/v3/documented-tag.yaml", new DocumentedTagCheck(), false, true, false);
}
}
diff --git a/openapi-checks/src/test/java/org/apiaddicts/apitools/dosonarapi/checks/ParsingErrorCheckTest.java b/openapi-checks/src/test/java/org/apiaddicts/apitools/dosonarapi/checks/ParsingErrorCheckTest.java
index 0aa785d..7e07428 100644
--- a/openapi-checks/src/test/java/org/apiaddicts/apitools/dosonarapi/checks/ParsingErrorCheckTest.java
+++ b/openapi-checks/src/test/java/org/apiaddicts/apitools/dosonarapi/checks/ParsingErrorCheckTest.java
@@ -20,11 +20,16 @@
package org.apiaddicts.apitools.dosonarapi.checks;
import com.sonar.sslr.api.RecognitionException;
+import java.nio.charset.StandardCharsets;
import java.util.List;
import org.junit.Test;
import org.apiaddicts.apitools.dosonarapi.api.OpenApiFile;
import org.apiaddicts.apitools.dosonarapi.api.OpenApiVisitorContext;
import org.apiaddicts.apitools.dosonarapi.api.PreciseIssue;
+import org.apiaddicts.apitools.dosonarapi.openapi.OpenApiConfiguration;
+import org.apiaddicts.apitools.dosonarapi.openapi.parser.OpenApiParser;
+import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.ValidationException;
+import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.YamlParser;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.tuple;
@@ -43,6 +48,33 @@ public void reports_parsing_errors() {
.contains(tuple(3, "Parsing exception message"));
}
+ @Test
+ public void reports_validation_exception_causes_as_issues() {
+ String invalidYaml =
+ "openapi: \"3.0.0\"\n" +
+ "info:\n" +
+ " title: Test API\n" +
+ "paths:\n" +
+ " /pets:\n" +
+ " post:\n" +
+ " description: missing responses\n";
+
+ OpenApiConfiguration config = new OpenApiConfiguration(StandardCharsets.UTF_8, true);
+ YamlParser parser = OpenApiParser.createV3(config);
+ OpenApiVisitorContext context;
+ try {
+ parser.parse(invalidYaml);
+ context = new OpenApiVisitorContext(new TestFile(), (RecognitionException) null);
+ } catch (ValidationException e) {
+ context = new OpenApiVisitorContext(new TestFile(), e);
+ }
+
+ ParsingErrorCheck check = new ParsingErrorCheck();
+ List issues = check.scanFileForIssues(context);
+
+ assertThat(issues).isNotEmpty();
+ }
+
private static class TestFile implements OpenApiFile {
@Override
diff --git a/openapi-front-end/pom.xml b/openapi-front-end/pom.xml
index 73b4941..9c28b76 100644
--- a/openapi-front-end/pom.xml
+++ b/openapi-front-end/pom.xml
@@ -5,7 +5,7 @@
org.apiaddicts.apitools.dosonarapi
dosonarapi
- 1.2.0
+ 1.2.1
../pom.xml
diff --git a/openapi-front-end/src/main/java/org/apiaddicts/apitools/dosonarapi/api/v31/OpenApi31Grammar.java b/openapi-front-end/src/main/java/org/apiaddicts/apitools/dosonarapi/api/v31/OpenApi31Grammar.java
index a5ea9f2..b359e92 100644
--- a/openapi-front-end/src/main/java/org/apiaddicts/apitools/dosonarapi/api/v31/OpenApi31Grammar.java
+++ b/openapi-front-end/src/main/java/org/apiaddicts/apitools/dosonarapi/api/v31/OpenApi31Grammar.java
@@ -263,6 +263,7 @@ private static void buildServer(YamlGrammarBuilder b) {
private static void buildInfo(YamlGrammarBuilder b) {
b.rule(INFO).is(b.object(
b.mandatoryProperty("title", b.string()),
+ b.property("summary", b.string()),
b.property("description", DESCRIPTION),
b.property("termsOfService", b.string()),
b.property("contact", CONTACT),
diff --git a/openapi-front-end/src/test/java/org/apiaddicts/apitools/dosonarapi/api/IssueLocationTest.java b/openapi-front-end/src/test/java/org/apiaddicts/apitools/dosonarapi/api/IssueLocationTest.java
index 6ab9038..a91e9d9 100644
--- a/openapi-front-end/src/test/java/org/apiaddicts/apitools/dosonarapi/api/IssueLocationTest.java
+++ b/openapi-front-end/src/test/java/org/apiaddicts/apitools/dosonarapi/api/IssueLocationTest.java
@@ -175,5 +175,44 @@ public void compare_precise_location_differents_objects(){
assertThat(issueLocation1.equals(issueLocation2)).isFalse();
}
+ @Test
+ public void both_null_messages_are_equal() {
+ IssueLocation loc1 = IssueLocation.atLineLevel(null, 10);
+ IssueLocation loc2 = IssueLocation.atLineLevel(null, 10);
+ assertThat(loc1.equals(loc2)).isTrue();
+ assertThat(loc1).hasSameHashCodeAs(loc2);
+ }
+
+ @Test
+ public void null_message_not_equal_to_non_null_message() {
+ IssueLocation loc1 = IssueLocation.atLineLevel(null, 10);
+ IssueLocation loc2 = IssueLocation.atLineLevel(MESSAGE, 10);
+ assertThat(loc1.equals(loc2)).isFalse();
+ assertThat(loc2.equals(loc1)).isFalse();
+ }
+
+ @Test
+ public void hashcode_with_null_message() {
+ IssueLocation loc = IssueLocation.atLineLevel(null, 5);
+ int hash = loc.hashCode();
+ assertThat(hash).isEqualTo(IssueLocation.atLineLevel(null, 5).hashCode());
+ }
+
+ @Test
+ public void precise_location_with_multiline_value() {
+ JsonNode root = parser.parse("swagger: \"2.0\"\n" +
+ "info:\n" +
+ " version: 1.0.0\n" +
+ " title: T\n" +
+ " description: |\n" +
+ " line one\n" +
+ " line two\n" +
+ "paths:\n" +
+ " /pets: {}");
+ JsonNode descNode = root.at("/info/description").value();
+ IssueLocation loc = IssueLocation.preciseLocation(MESSAGE, descNode);
+ assertThat(loc.startLine()).isGreaterThan(0);
+ assertThat(loc.endLine()).isGreaterThanOrEqualTo(loc.startLine());
+ }
}
diff --git a/openapi-front-end/src/test/java/org/apiaddicts/apitools/dosonarapi/api/OpenApiCheckTest.java b/openapi-front-end/src/test/java/org/apiaddicts/apitools/dosonarapi/api/OpenApiCheckTest.java
index bd46711..6eadf0d 100644
--- a/openapi-front-end/src/test/java/org/apiaddicts/apitools/dosonarapi/api/OpenApiCheckTest.java
+++ b/openapi-front-end/src/test/java/org/apiaddicts/apitools/dosonarapi/api/OpenApiCheckTest.java
@@ -22,13 +22,19 @@
import com.google.common.collect.Sets;
import com.sonar.sslr.api.AstNodeType;
import java.io.File;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import org.junit.Test;
import org.sonar.check.Rule;
import org.apiaddicts.apitools.dosonarapi.api.v3.OpenApi3Grammar;
+import org.apiaddicts.apitools.dosonarapi.openapi.OpenApiConfiguration;
+import org.apiaddicts.apitools.dosonarapi.openapi.parser.OpenApiParser;
import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode;
+import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.YamlParser;
import static org.assertj.core.api.Assertions.assertThat;
@@ -76,4 +82,40 @@ public void skips_rules_on_x_nosonar() {
assertThat(rule2.visitedNodes).isEmpty();
assertThat(rule3.visitedNodes).containsOnly("/paths/~1pets/get", "/paths/~1pets/get/parameters/0");
}
+
+ private static class LineIssueCheck extends OpenApiCheck {
+ @Override
+ public Set subscribedKinds() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ protected void visitFile(JsonNode root) {
+ addLineIssue("line problem", 3);
+ }
+ }
+
+ private static class NoAnnotationCheck extends OpenApiCheck {}
+
+ @Test
+ public void add_line_issue_creates_issue_on_given_line() {
+ OpenApiConfiguration config = new OpenApiConfiguration(StandardCharsets.UTF_8, false);
+ YamlParser parser = OpenApiParser.createV3(config);
+ JsonNode root = parser.parse("openapi: \"3.0.0\"\ninfo:\n title: T\n version: 1.0\npaths: {}");
+ OpenApiFile file = new OpenApiFile() {
+ @Override public String content() { return ""; }
+ @Override public String fileName() { return "test.yaml"; }
+ };
+ OpenApiVisitorContext ctx = new OpenApiVisitorContext(root, parser.getIssues(), file);
+
+ LineIssueCheck check = new LineIssueCheck();
+ List issues = check.scanFileForIssues(ctx);
+ assertThat(issues).hasSize(1);
+ assertThat(issues.get(0).primaryLocation().startLine()).isEqualTo(3);
+ }
+
+ @Test
+ public void no_rule_annotation_returns_empty_rule_id() {
+ assertThat(new NoAnnotationCheck().getRuleId()).isEmpty();
+ }
}
diff --git a/openapi-front-end/src/test/java/org/apiaddicts/apitools/dosonarapi/api/PreciseIssueTest.java b/openapi-front-end/src/test/java/org/apiaddicts/apitools/dosonarapi/api/PreciseIssueTest.java
index 668c04e..720ec0a 100644
--- a/openapi-front-end/src/test/java/org/apiaddicts/apitools/dosonarapi/api/PreciseIssueTest.java
+++ b/openapi-front-end/src/test/java/org/apiaddicts/apitools/dosonarapi/api/PreciseIssueTest.java
@@ -19,22 +19,92 @@
*/
package org.apiaddicts.apitools.dosonarapi.api;
-import org.apiaddicts.apitools.dosonarapi.api.IssueLocation;
-import org.apiaddicts.apitools.dosonarapi.api.PreciseIssue;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatObject;
public class PreciseIssueTest {
- private static final String MESSAGE = "Test Message";
-
@Test
- public void compare_equals_objects(){
+ public void compare_equals_objects() {
PreciseIssue preciseIssue1 = new PreciseIssue(IssueLocation.atLineLevel(null, 42000)).withCost(5);
PreciseIssue preciseIssue2 = new PreciseIssue(IssueLocation.atLineLevel(null, 42000)).withCost(5);
assertThat(preciseIssue1.equals(preciseIssue2)).isTrue();
assertThat(preciseIssue1.hashCode() == preciseIssue2.hashCode()).isTrue();
}
+ @Test
+ public void not_equal_to_non_precise_issue() {
+ PreciseIssue issue = new PreciseIssue(IssueLocation.atLineLevel("msg", 1));
+ assertThatObject(issue).isNotEqualTo("not an issue");
+ assertThatObject(issue).isNotEqualTo(null);
+ }
+
+ @Test
+ public void not_equal_when_costs_differ() {
+ PreciseIssue issue1 = new PreciseIssue(IssueLocation.atLineLevel("msg", 1)).withCost(1);
+ PreciseIssue issue2 = new PreciseIssue(IssueLocation.atLineLevel("msg", 1)).withCost(2);
+ assertThat(issue1.equals(issue2)).isFalse();
+ }
+
+ @Test
+ public void not_equal_when_primary_location_differs() {
+ PreciseIssue issue1 = new PreciseIssue(IssueLocation.atLineLevel("msg", 1));
+ PreciseIssue issue2 = new PreciseIssue(IssueLocation.atLineLevel("msg", 2));
+ assertThat(issue1.equals(issue2)).isFalse();
+ }
+
+ @Test
+ public void secondary_location_via_issue_location() {
+ IssueLocation loc = IssueLocation.atLineLevel("secondary", 5);
+ PreciseIssue issue = new PreciseIssue(IssueLocation.atLineLevel("primary", 1));
+ issue.secondary(loc);
+ assertThat(issue.secondaryLocations()).hasSize(1);
+ assertThat(issue.secondaryLocations().get(0)).isEqualTo(loc);
+ }
+
+ @Test
+ public void cost_is_null_by_default() {
+ PreciseIssue issue = new PreciseIssue(IssueLocation.atLineLevel("msg", 1));
+ assertThat(issue.cost()).isNull();
+ }
+
+ @Test
+ public void hashcode_without_cost() {
+ PreciseIssue issue1 = new PreciseIssue(IssueLocation.atLineLevel("msg", 1));
+ PreciseIssue issue2 = new PreciseIssue(IssueLocation.atLineLevel("msg", 1));
+ assertThat(issue1).hasSameHashCodeAs(issue2);
+ }
+
+ @Test
+ public void equals_with_null_primary_location() {
+ PreciseIssue issue1 = new PreciseIssue(null);
+ PreciseIssue issue2 = new PreciseIssue(null);
+ assertThat(issue1.equals(issue2)).isTrue();
+ assertThat(issue1).hasSameHashCodeAs(issue2);
+ }
+
+ @Test
+ public void null_primary_location_not_equal_to_non_null() {
+ PreciseIssue issue1 = new PreciseIssue(null);
+ PreciseIssue issue2 = new PreciseIssue(IssueLocation.atLineLevel("msg", 1));
+ assertThat(issue1.equals(issue2)).isFalse();
+ assertThat(issue2.equals(issue1)).isFalse();
+ }
+
+ @Test
+ public void secondary_location_via_node() {
+ java.nio.charset.Charset utf8 = java.nio.charset.StandardCharsets.UTF_8;
+ org.apiaddicts.apitools.dosonarapi.openapi.OpenApiConfiguration config =
+ new org.apiaddicts.apitools.dosonarapi.openapi.OpenApiConfiguration(utf8, true);
+ org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.YamlParser parser =
+ org.apiaddicts.apitools.dosonarapi.openapi.parser.OpenApiParser.createV2(config);
+ org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode root =
+ parser.parse("swagger: \"2.0\"\ninfo:\n version: 1.0.0\n title: T\npaths:\n /pets: {}");
+ org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode node = root.at("/paths/~1pets").value();
+ PreciseIssue issue = new PreciseIssue(IssueLocation.atLineLevel("primary", 1));
+ issue.secondary(node, "secondary message");
+ assertThat(issue.secondaryLocations()).hasSize(1);
+ }
}
diff --git a/openapi-front-end/src/test/java/org/apiaddicts/apitools/dosonarapi/api/ResourceCheckTest.java b/openapi-front-end/src/test/java/org/apiaddicts/apitools/dosonarapi/api/ResourceCheckTest.java
new file mode 100644
index 0000000..11bf77c
--- /dev/null
+++ b/openapi-front-end/src/test/java/org/apiaddicts/apitools/dosonarapi/api/ResourceCheckTest.java
@@ -0,0 +1,179 @@
+/*
+ * doSonarAPI: SonarQube OpenAPI Plugin
+ * Copyright (C) 2021-2022 Apiaddicts
+ * contacta AT apiaddicts DOT org
+ *
+ * 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.apiaddicts.apitools.dosonarapi.api;
+
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.experimental.theories.DataPoints;
+import org.junit.experimental.theories.Theories;
+import org.junit.experimental.theories.Theory;
+import org.junit.runner.RunWith;
+import org.apiaddicts.apitools.dosonarapi.openapi.OpenApiConfiguration;
+import org.apiaddicts.apitools.dosonarapi.openapi.parser.OpenApiParser;
+import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode;
+import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.YamlParser;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(Theories.class)
+public class ResourceCheckTest {
+
+ private static class RecordingResourceCheck extends ResourceCheck {
+ final List visitedPaths = new ArrayList<>();
+
+ @Override
+ protected void visitResource(JsonNode node) {
+ visitedPaths.add(node.key().getTokenValue());
+ }
+ }
+
+ private static OpenApiVisitorContext createV2Context(String yaml) {
+ OpenApiConfiguration config = new OpenApiConfiguration(StandardCharsets.UTF_8, true);
+ YamlParser parser = OpenApiParser.createV2(config);
+ JsonNode root = parser.parse(yaml);
+ OpenApiFile file = new OpenApiFile() {
+ @Override public String content() { return yaml; }
+ @Override public String fileName() { return "test.yaml"; }
+ };
+ return new OpenApiVisitorContext(root, parser.getIssues(), file);
+ }
+
+ @DataPoints
+ public static String[] yamlsWithNoResourcePaths() {
+ return new String[] {
+ "swagger: \"2.0\"\n" +
+ "info:\n" +
+ " version: 1.0.0\n" +
+ " title: Test\n" +
+ "paths:\n" +
+ " /pets/{petId}:\n" +
+ " get:\n" +
+ " responses:\n" +
+ " '200':\n" +
+ " description: ok\n",
+
+ "swagger: \"2.0\"\n" +
+ "info:\n" +
+ " version: 1.0.0\n" +
+ " title: Test\n" +
+ "paths:\n" +
+ " /pets/:\n" +
+ " get:\n" +
+ " responses:\n" +
+ " '200':\n" +
+ " description: ok\n",
+
+ "swagger: \"2.0\"\n" +
+ "info:\n" +
+ " version: 1.0.0\n" +
+ " title: Test\n" +
+ "paths:\n" +
+ " /{entity}:\n" +
+ " get:\n" +
+ " responses:\n" +
+ " '200':\n" +
+ " description: ok\n"
+ };
+ }
+
+ @Theory
+ public void does_not_visit_non_resource_paths(String yaml) {
+ RecordingResourceCheck check = new RecordingResourceCheck();
+ check.scanFileForIssues(createV2Context(yaml));
+ assertThat(check.visitedPaths).isEmpty();
+ }
+
+ @Test
+ public void visits_resource_paths_only() {
+ String yaml =
+ "swagger: \"2.0\"\n" +
+ "info:\n" +
+ " version: 1.0.0\n" +
+ " title: Test\n" +
+ "paths:\n" +
+ " /pets:\n" +
+ " get:\n" +
+ " responses:\n" +
+ " '200':\n" +
+ " description: ok\n" +
+ " /pets/{petId}:\n" +
+ " get:\n" +
+ " responses:\n" +
+ " '200':\n" +
+ " description: ok\n";
+
+ RecordingResourceCheck check = new RecordingResourceCheck();
+ check.scanFileForIssues(createV2Context(yaml));
+
+ assertThat(check.visitedPaths).contains("/pets").doesNotContain("/pets/{petId}");
+ }
+
+ @Test
+ public void visits_sub_resource_paths() {
+ String yaml =
+ "swagger: \"2.0\"\n" +
+ "info:\n" +
+ " version: 1.0.0\n" +
+ " title: Test\n" +
+ "paths:\n" +
+ " /pets/{petId}/tags:\n" +
+ " get:\n" +
+ " responses:\n" +
+ " '200':\n" +
+ " description: ok\n" +
+ " /pets/{petId}/tags/{tagId}:\n" +
+ " get:\n" +
+ " responses:\n" +
+ " '200':\n" +
+ " description: ok\n";
+
+ RecordingResourceCheck check = new RecordingResourceCheck();
+ check.scanFileForIssues(createV2Context(yaml));
+
+ assertThat(check.visitedPaths).contains("/pets/{petId}/tags");
+ }
+
+ @Test
+ public void path_is_last_without_following_variable_child_is_not_resource() {
+ String yaml =
+ "swagger: \"2.0\"\n" +
+ "info:\n" +
+ " version: 1.0.0\n" +
+ " title: Test\n" +
+ "paths:\n" +
+ " /pets:\n" +
+ " get:\n" +
+ " responses:\n" +
+ " '200':\n" +
+ " description: ok\n" +
+ " /other:\n" +
+ " get:\n" +
+ " responses:\n" +
+ " '200':\n" +
+ " description: ok\n";
+
+ RecordingResourceCheck check = new RecordingResourceCheck();
+ check.scanFileForIssues(createV2Context(yaml));
+
+ assertThat(check.visitedPaths).doesNotContain("/pets", "/other");
+ }
+}
diff --git a/openapi-front-end/src/test/java/org/apiaddicts/apitools/dosonarapi/api/TestOpenApiVisitorRunnerTest.java b/openapi-front-end/src/test/java/org/apiaddicts/apitools/dosonarapi/api/TestOpenApiVisitorRunnerTest.java
new file mode 100644
index 0000000..16f3a00
--- /dev/null
+++ b/openapi-front-end/src/test/java/org/apiaddicts/apitools/dosonarapi/api/TestOpenApiVisitorRunnerTest.java
@@ -0,0 +1,86 @@
+/*
+ * doSonarAPI: SonarQube OpenAPI Plugin
+ * Copyright (C) 2021-2022 Apiaddicts
+ * contacta AT apiaddicts DOT org
+ *
+ * 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.apiaddicts.apitools.dosonarapi.api;
+
+import java.io.File;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
+
+public class TestOpenApiVisitorRunnerTest {
+
+ private static final File V3_FILE = new File(
+ TestOpenApiVisitorRunnerTest.class.getResource("/petstore.yaml").getFile());
+ private static final File V2_FILE = new File(
+ TestOpenApiVisitorRunnerTest.class.getResource("/models/v2/pet-store.yaml").getFile());
+ private static final File V31_FILE = new File(
+ TestOpenApiVisitorRunnerTest.class.getResource("/petstore-v31.yaml").getFile());
+ private static final File V32_FILE = new File(
+ TestOpenApiVisitorRunnerTest.class.getResource("/petstore-v32.yaml").getFile());
+
+ @Test
+ public void create_context_with_default_args_uses_v3_parser() {
+ OpenApiVisitorContext ctx = TestOpenApiVisitorRunner.createContext(V3_FILE);
+ assertThat(ctx.rootTree()).isNotNull();
+ }
+
+ @Test
+ public void create_context_with_v2_flag_uses_v2_parser() {
+ OpenApiVisitorContext ctx = TestOpenApiVisitorRunner.createContext(V2_FILE, true);
+ assertThat(ctx.rootTree()).isNotNull();
+ }
+
+ @Test
+ public void create_context_with_three_flags_false_uses_v3_parser() {
+ OpenApiVisitorContext ctx = TestOpenApiVisitorRunner.createContext(V3_FILE, false, false, false);
+ assertThat(ctx.rootTree()).isNotNull();
+ }
+
+ @Test
+ public void create_context_with_v31_flag_uses_v31_parser() {
+ OpenApiVisitorContext ctx = TestOpenApiVisitorRunner.createContext(V31_FILE, false, false, true);
+ assertThat(ctx.rootTree()).isNotNull();
+ }
+
+ @Test
+ public void create_context_with_v32_flag_uses_v32_parser() {
+ OpenApiVisitorContext ctx = TestOpenApiVisitorRunner.createContext(V32_FILE, false, false, false, true);
+ assertThat(ctx.rootTree()).isNotNull();
+ }
+
+ @Test
+ public void scan_file_for_comments_with_three_flags() {
+ assertThatCode(() -> TestOpenApiVisitorRunner.scanFileForComments(V3_FILE, false, false, false))
+ .doesNotThrowAnyException();
+ }
+
+ @Test
+ public void scan_file_for_comments_with_v32_flag() {
+ assertThatCode(() -> TestOpenApiVisitorRunner.scanFileForComments(V32_FILE, false, false, false, true))
+ .doesNotThrowAnyException();
+ }
+
+ @Test
+ public void scan_file_with_visitors() {
+ assertThatCode(() -> TestOpenApiVisitorRunner.scanFile(V3_FILE, new OpenApiVisitor()))
+ .doesNotThrowAnyException();
+ }
+}
diff --git a/openapi-front-end/src/test/java/org/apiaddicts/apitools/dosonarapi/api/v31/InfoTest.java b/openapi-front-end/src/test/java/org/apiaddicts/apitools/dosonarapi/api/v31/InfoTest.java
new file mode 100644
index 0000000..356dfec
--- /dev/null
+++ b/openapi-front-end/src/test/java/org/apiaddicts/apitools/dosonarapi/api/v31/InfoTest.java
@@ -0,0 +1,47 @@
+/*
+ * doSonarAPI: SonarQube OpenAPI Plugin
+ * Copyright (C) 2021-2022 Apiaddicts
+ * contacta AT apiaddicts DOT org
+ *
+ * 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.apiaddicts.apitools.dosonarapi.api.v31;
+
+import org.apiaddicts.apitools.dosonarapi.openapi.BaseNodeTest;
+import org.junit.Test;
+import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode;
+
+
+public class InfoTest extends BaseNodeTest {
+
+ @Test
+ public void can_parse_info_with_summary() {
+ JsonNode node = parseResource(OpenApi31Grammar.INFO, "/models/v31/info_with_summary.yaml");
+
+ assertEquals("My API", node, "/title");
+ assertEquals("A short summary of the API", node, "/summary");
+ assertEquals("1.0.0", node, "/version");
+ assertMissing(node.at("/description"));
+ }
+
+ @Test
+ public void can_parse_info_without_summary() {
+ JsonNode node = parseResource(OpenApi31Grammar.INFO, "/models/shared/info/minimal.yaml");
+
+ assertMissing(node.at("/summary"));
+ assertEquals("simple model", node, "/title");
+ assertEquals("1.0.0", node, "/version");
+ }
+}
diff --git a/openapi-front-end/src/test/java/org/apiaddicts/apitools/dosonarapi/openapi/OpenApiConfigurationTest.java b/openapi-front-end/src/test/java/org/apiaddicts/apitools/dosonarapi/openapi/OpenApiConfigurationTest.java
new file mode 100644
index 0000000..591d5ac
--- /dev/null
+++ b/openapi-front-end/src/test/java/org/apiaddicts/apitools/dosonarapi/openapi/OpenApiConfigurationTest.java
@@ -0,0 +1,42 @@
+/*
+ * doSonarAPI: SonarQube OpenAPI Plugin
+ * Copyright (C) 2021-2022 Apiaddicts
+ * contacta AT apiaddicts DOT org
+ *
+ * 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.apiaddicts.apitools.dosonarapi.openapi;
+
+import java.nio.charset.StandardCharsets;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class OpenApiConfigurationTest {
+
+ @Test
+ public void stores_charset_and_strict_flag() {
+ OpenApiConfiguration config = new OpenApiConfiguration(StandardCharsets.UTF_8, true);
+ assertThat(config.getCharset()).isEqualTo(StandardCharsets.UTF_8);
+ assertThat(config.isStrict()).isTrue();
+ }
+
+ @Test
+ public void strict_false() {
+ OpenApiConfiguration config = new OpenApiConfiguration(StandardCharsets.ISO_8859_1, false);
+ assertThat(config.getCharset()).isEqualTo(StandardCharsets.ISO_8859_1);
+ assertThat(config.isStrict()).isFalse();
+ }
+}
diff --git a/openapi-front-end/src/test/java/org/apiaddicts/apitools/dosonarapi/openapi/metrics/FileMetricsTest.java b/openapi-front-end/src/test/java/org/apiaddicts/apitools/dosonarapi/openapi/metrics/FileMetricsTest.java
index 463dc37..36025af 100644
--- a/openapi-front-end/src/test/java/org/apiaddicts/apitools/dosonarapi/openapi/metrics/FileMetricsTest.java
+++ b/openapi-front-end/src/test/java/org/apiaddicts/apitools/dosonarapi/openapi/metrics/FileMetricsTest.java
@@ -19,10 +19,12 @@
*/
package org.apiaddicts.apitools.dosonarapi.openapi.metrics;
+import com.sonar.sslr.api.RecognitionException;
import java.io.File;
-import org.apiaddicts.apitools.dosonarapi.openapi.metrics.FileMetrics;
+import org.apiaddicts.apitools.dosonarapi.api.OpenApiFile;
import org.junit.Test;
+import org.apiaddicts.apitools.dosonarapi.api.OpenApiVisitorContext;
import org.apiaddicts.apitools.dosonarapi.api.TestOpenApiVisitorRunner;
import static org.assertj.core.api.Assertions.assertThat;
@@ -49,6 +51,21 @@ public void complexity() {
assertThat(metrics("complexity.yaml").complexity()).isEqualTo(7);
}
+ @Test
+ public void null_root_tree_yields_zero_counts() {
+ OpenApiFile file = new OpenApiFile() {
+ @Override public String content() { return ""; }
+ @Override public String fileName() { return "dummy.yaml"; }
+ };
+ OpenApiVisitorContext context = new OpenApiVisitorContext(file, new RecognitionException(0, "parse error"));
+ FileMetrics fileMetrics = new FileMetrics(context);
+
+ assertThat(fileMetrics.numberOfOperations()).isZero();
+ assertThat(fileMetrics.numberOfPaths()).isZero();
+ assertThat(fileMetrics.numberOfSchemas()).isZero();
+ assertThat(fileMetrics.complexity()).isZero();
+ }
+
private FileMetrics metrics(String fileName) {
File baseDir = new File("src/test/resources/metrics/");
File file = new File(baseDir, fileName);
diff --git a/openapi-front-end/src/test/java/org/apiaddicts/apitools/dosonarapi/openapi/metrics/OpenApiMetricsTest.java b/openapi-front-end/src/test/java/org/apiaddicts/apitools/dosonarapi/openapi/metrics/OpenApiMetricsTest.java
new file mode 100644
index 0000000..dab744e
--- /dev/null
+++ b/openapi-front-end/src/test/java/org/apiaddicts/apitools/dosonarapi/openapi/metrics/OpenApiMetricsTest.java
@@ -0,0 +1,54 @@
+/*
+ * doSonarAPI: SonarQube OpenAPI Plugin
+ * Copyright (C) 2021-2022 Apiaddicts
+ * contacta AT apiaddicts DOT org
+ *
+ * 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.apiaddicts.apitools.dosonarapi.openapi.metrics;
+
+import java.util.List;
+import org.junit.Test;
+import org.sonar.api.measures.Metric;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class OpenApiMetricsTest {
+
+ @Test
+ public void provides_three_metrics() {
+ OpenApiMetrics openApiMetrics = new OpenApiMetrics();
+ List metrics = openApiMetrics.getMetrics();
+ assertThat(metrics).hasSize(3).contains(OpenApiMetrics.OPERATIONS_COUNT, OpenApiMetrics.PATHS_COUNT, OpenApiMetrics.SCHEMAS_COUNT);
+ }
+
+ @Test
+ public void operations_count_metric_has_correct_key() {
+ assertThat(OpenApiMetrics.OPERATIONS_COUNT.getKey()).isEqualTo("operations_count");
+ assertThat(OpenApiMetrics.OPERATIONS_COUNT.getName()).isEqualTo("Operations Count");
+ }
+
+ @Test
+ public void paths_count_metric_has_correct_key() {
+ assertThat(OpenApiMetrics.PATHS_COUNT.getKey()).isEqualTo("paths_count");
+ assertThat(OpenApiMetrics.PATHS_COUNT.getName()).isEqualTo("Paths Count");
+ }
+
+ @Test
+ public void schemas_count_metric_has_correct_key() {
+ assertThat(OpenApiMetrics.SCHEMAS_COUNT.getKey()).isEqualTo("schemas_count");
+ assertThat(OpenApiMetrics.SCHEMAS_COUNT.getName()).isEqualTo("Schemas Count");
+ }
+}
diff --git a/openapi-front-end/src/test/java/org/apiaddicts/apitools/dosonarapi/openapi/parser/OpenApiParserTest.java b/openapi-front-end/src/test/java/org/apiaddicts/apitools/dosonarapi/openapi/parser/OpenApiParserTest.java
new file mode 100644
index 0000000..643f3b3
--- /dev/null
+++ b/openapi-front-end/src/test/java/org/apiaddicts/apitools/dosonarapi/openapi/parser/OpenApiParserTest.java
@@ -0,0 +1,96 @@
+/*
+ * doSonarAPI: SonarQube OpenAPI Plugin
+ * Copyright (C) 2021-2022 Apiaddicts
+ * contacta AT apiaddicts DOT org
+ *
+ * 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.apiaddicts.apitools.dosonarapi.openapi.parser;
+
+import java.nio.charset.StandardCharsets;
+import org.junit.Test;
+import org.apiaddicts.apitools.dosonarapi.openapi.OpenApiConfiguration;
+import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode;
+import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.YamlParser;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class OpenApiParserTest {
+
+ private static final OpenApiConfiguration CONFIG = new OpenApiConfiguration(StandardCharsets.UTF_8, false);
+
+ private static final String MINIMAL_V2 =
+ "swagger: \"2.0\"\n" +
+ "info:\n" +
+ " title: Test\n" +
+ " version: 1.0.0\n" +
+ "paths: {}";
+
+ private static final String MINIMAL_V3 =
+ "openapi: \"3.0.0\"\n" +
+ "info:\n" +
+ " title: Test\n" +
+ " version: 1.0.0\n" +
+ "paths: {}";
+
+ @Test
+ public void create_v2_parser_parses_swagger_doc() {
+ YamlParser parser = OpenApiParser.createV2(CONFIG);
+ assertThat(parser).isNotNull();
+ JsonNode root = parser.parse(MINIMAL_V2);
+ assertThat(root).isNotNull();
+ assertThat(root.at("/swagger").getTokenValue()).isEqualTo("2.0");
+ }
+
+ @Test
+ public void create_v3_parser_parses_openapi_doc() {
+ YamlParser parser = OpenApiParser.createV3(CONFIG);
+ assertThat(parser).isNotNull();
+ JsonNode root = parser.parse(MINIMAL_V3);
+ assertThat(root).isNotNull();
+ assertThat(root.at("/openapi").getTokenValue()).isEqualTo("3.0.0");
+ }
+
+ @Test
+ public void create_v31_parser_returns_non_null() {
+ YamlParser parser = OpenApiParser.createV31(CONFIG);
+ assertThat(parser).isNotNull();
+ JsonNode root = parser.parse(MINIMAL_V3);
+ assertThat(root).isNotNull();
+ }
+
+ @Test
+ public void create_v32_parser_returns_non_null() {
+ YamlParser parser = OpenApiParser.createV32(CONFIG);
+ assertThat(parser).isNotNull();
+ JsonNode root = parser.parse(MINIMAL_V3);
+ assertThat(root).isNotNull();
+ }
+
+ @Test
+ public void create_generic_parser_returns_non_null() {
+ YamlParser parser = OpenApiParser.createGeneric(CONFIG);
+ assertThat(parser).isNotNull();
+ JsonNode root = parser.parse("key: value");
+ assertThat(root).isNotNull();
+ }
+
+ @Test
+ public void create_generic_with_non_strict_config() {
+ OpenApiConfiguration nonStrict = new OpenApiConfiguration(StandardCharsets.UTF_8, false);
+ YamlParser parser = OpenApiParser.createGeneric(nonStrict);
+ assertThat(parser).isNotNull();
+ }
+}
diff --git a/openapi-front-end/src/test/resources/models/v31/info_with_summary.yaml b/openapi-front-end/src/test/resources/models/v31/info_with_summary.yaml
new file mode 100644
index 0000000..194cf4d
--- /dev/null
+++ b/openapi-front-end/src/test/resources/models/v31/info_with_summary.yaml
@@ -0,0 +1,3 @@
+title: My API
+summary: A short summary of the API
+version: 1.0.0
diff --git a/openapi-front-end/src/test/resources/petstore-v31.yaml b/openapi-front-end/src/test/resources/petstore-v31.yaml
new file mode 100644
index 0000000..d7ee194
--- /dev/null
+++ b/openapi-front-end/src/test/resources/petstore-v31.yaml
@@ -0,0 +1,10 @@
+openapi: "3.1.0"
+info:
+ version: 1.0.0
+ title: Petstore V31
+paths:
+ /pets:
+ get:
+ responses:
+ '200':
+ description: ok
diff --git a/openapi-front-end/src/test/resources/petstore-v32.yaml b/openapi-front-end/src/test/resources/petstore-v32.yaml
new file mode 100644
index 0000000..dc04299
--- /dev/null
+++ b/openapi-front-end/src/test/resources/petstore-v32.yaml
@@ -0,0 +1,10 @@
+openapi: "3.2.0"
+info:
+ version: 1.0.0
+ title: Petstore V32
+paths:
+ /pets:
+ get:
+ responses:
+ '200':
+ description: ok
diff --git a/openapi-test-tools/pom.xml b/openapi-test-tools/pom.xml
index 2524ac1..cf684f6 100644
--- a/openapi-test-tools/pom.xml
+++ b/openapi-test-tools/pom.xml
@@ -5,7 +5,7 @@
org.apiaddicts.apitools.dosonarapi
dosonarapi
- 1.2.0
+ 1.2.1
../pom.xml
@@ -19,6 +19,11 @@
openapi-front-end
${project.version}
+
+ org.sonarsource.sonarqube
+ sonar-plugin-api
+ test
+
junit
junit
diff --git a/openapi-test-tools/src/test/java/org/apiaddicts/apitools/dosonarapi/OpenApiCheckVerifierTest.java b/openapi-test-tools/src/test/java/org/apiaddicts/apitools/dosonarapi/OpenApiCheckVerifierTest.java
index 6ff6377..0ce113f 100644
--- a/openapi-test-tools/src/test/java/org/apiaddicts/apitools/dosonarapi/OpenApiCheckVerifierTest.java
+++ b/openapi-test-tools/src/test/java/org/apiaddicts/apitools/dosonarapi/OpenApiCheckVerifierTest.java
@@ -28,6 +28,7 @@
import static com.sonar.sslr.api.GenericTokenType.COMMENT;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
public class OpenApiCheckVerifierTest {
@@ -76,6 +77,122 @@ public void adjusts_issue_column_based_on_issue_next_line() {
assertThat(issue.endColumn()).isEqualTo(6);
}
+ @Test
+ public void noncompliant_without_line_offset_uses_comment_line() {
+ Trivia trivia = makeComment("Noncompliant {{message}}", COMMENT_LINE, COMMENT_COLUMN);
+ OpenApiCheckVerifier verifier = new OpenApiCheckVerifier();
+ verifier.collectExpectedIssue(trivia);
+
+ assertThat(verifier.getCollectedIssues()).hasSize(1);
+ assertThat(verifier.getCollectedIssues().get(0).line()).isEqualTo(COMMENT_LINE);
+ assertThat(verifier.getCollectedIssues().get(0).message()).isEqualTo("message");
+ }
+
+ @Test
+ public void noncompliant_with_absolute_line_number() {
+ Trivia trivia = makeComment("Noncompliant @10", COMMENT_LINE, COMMENT_COLUMN);
+ OpenApiCheckVerifier verifier = new OpenApiCheckVerifier();
+ verifier.collectExpectedIssue(trivia);
+
+ assertThat(verifier.getCollectedIssues()).hasSize(1);
+ assertThat(verifier.getCollectedIssues().get(0).line()).isEqualTo(10);
+ }
+
+ @Test
+ public void noncompliant_with_negative_line_offset() {
+ Trivia trivia = makeComment("Noncompliant @-1", COMMENT_LINE, COMMENT_COLUMN);
+ OpenApiCheckVerifier verifier = new OpenApiCheckVerifier();
+ verifier.collectExpectedIssue(trivia);
+
+ assertThat(verifier.getCollectedIssues()).hasSize(1);
+ assertThat(verifier.getCollectedIssues().get(0).line()).isEqualTo(COMMENT_LINE - 1);
+ }
+
+ @Test
+ public void empty_secondary_lines_list() {
+ Trivia trivia = makeComment("Noncompliant [[secondary=]]", COMMENT_LINE, COMMENT_COLUMN);
+ OpenApiCheckVerifier verifier = new OpenApiCheckVerifier();
+ verifier.collectExpectedIssue(trivia);
+
+ assertThat(verifier.getCollectedIssues()).hasSize(1);
+ assertThat(verifier.getCollectedIssues().get(0).secondaryLines()).isEmpty();
+ }
+
+ @Test
+ public void throws_on_invalid_param_without_equals() {
+ OpenApiCheckVerifier verifier = new OpenApiCheckVerifier();
+ Trivia invalidParam = makeComment("Noncompliant [[invalidparam]]", COMMENT_LINE, COMMENT_COLUMN);
+ assertThatThrownBy(() -> verifier.collectExpectedIssue(invalidParam))
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessageContaining("Invalid param");
+ }
+
+ @Test
+ public void throws_on_unknown_param_name() {
+ OpenApiCheckVerifier verifier = new OpenApiCheckVerifier();
+ Trivia unknownParam = makeComment("Noncompliant [[unknownParam=5]]", COMMENT_LINE, COMMENT_COLUMN);
+ assertThatThrownBy(() -> verifier.collectExpectedIssue(unknownParam))
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessageContaining("Invalid param");
+ }
+
+ @Test
+ public void precise_location_throws_when_no_preceding_issue() {
+ OpenApiCheckVerifier verifier = new OpenApiCheckVerifier();
+ Trivia preciseLocation = makeComment("^^^", COMMENT_LINE, 0);
+ assertThatThrownBy(() -> verifier.collectExpectedIssue(preciseLocation))
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessageContaining("Invalid test file");
+ }
+
+ @Test
+ public void precise_location_throws_when_not_on_next_line() {
+ OpenApiCheckVerifier verifier = new OpenApiCheckVerifier();
+ verifier.collectExpectedIssue(makeComment("Noncompliant", COMMENT_LINE, COMMENT_COLUMN));
+ Trivia notNextLine = makeComment("^^^", COMMENT_LINE + 3, 0);
+ assertThatThrownBy(() -> verifier.collectExpectedIssue(notNextLine))
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessageContaining("Invalid test file");
+ }
+
+ @Test
+ public void precise_location_throws_when_column_not_zero() {
+ OpenApiCheckVerifier verifier = new OpenApiCheckVerifier();
+ verifier.collectExpectedIssue(makeComment("Noncompliant", COMMENT_LINE, COMMENT_COLUMN));
+ Trivia wrongColumn = makeComment("^^^", COMMENT_LINE + 1, 2);
+ assertThatThrownBy(() -> verifier.collectExpectedIssue(wrongColumn))
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessageContaining("column 1");
+ }
+
+ @Test
+ public void verify_passes_when_no_issues_expected_and_none_reported() {
+ OpenApiCheckVerifier.verify("src/test/resources/test-verify.yaml", new TestNoOpCheck(), true, false, false);
+ }
+
+ @Test
+ public void verify_passes_when_issue_matches_annotation() {
+ OpenApiCheckVerifier.verify("src/test/resources/test-verify-noncompliant.yaml", new TestPathsReportCheck(), true, false, false);
+ }
+
+ @Test
+ public void verify_fails_when_expected_issue_not_reported() {
+ TestNoOpCheck noOpCheck = new TestNoOpCheck();
+ assertThatThrownBy(() ->
+ OpenApiCheckVerifier.verify("src/test/resources/test-verify-noncompliant.yaml", noOpCheck, true, false, false))
+ .isInstanceOf(AssertionError.class)
+ .hasMessageContaining("Missing issue");
+ }
+
+ @Test
+ public void verify_fails_when_unexpected_issue_reported() {
+ TestPathsReportCheck pathsCheck = new TestPathsReportCheck();
+ assertThatThrownBy(() ->
+ OpenApiCheckVerifier.verify("src/test/resources/test-verify.yaml", pathsCheck, true, false, false))
+ .isInstanceOf(AssertionError.class)
+ .hasMessageContaining("Unexpected issue");
+ }
+
private Trivia makeComment(String comment, int line, int column) {
try {
return Trivia.createComment(
diff --git a/openapi-test-tools/src/test/java/org/apiaddicts/apitools/dosonarapi/TestNoOpCheck.java b/openapi-test-tools/src/test/java/org/apiaddicts/apitools/dosonarapi/TestNoOpCheck.java
new file mode 100644
index 0000000..a1074b2
--- /dev/null
+++ b/openapi-test-tools/src/test/java/org/apiaddicts/apitools/dosonarapi/TestNoOpCheck.java
@@ -0,0 +1,32 @@
+/*
+ * doSonarAPI: SonarQube OpenAPI Plugin
+ * Copyright (C) 2021-2022 Apiaddicts
+ * contacta AT apiaddicts DOT org
+ *
+ * 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.apiaddicts.apitools.dosonarapi;
+
+import com.sonar.sslr.api.AstNodeType;
+import java.util.Collections;
+import java.util.Set;
+import org.apiaddicts.apitools.dosonarapi.api.OpenApiCheck;
+
+public class TestNoOpCheck extends OpenApiCheck {
+ @Override
+ public Set subscribedKinds() {
+ return Collections.emptySet();
+ }
+}
diff --git a/openapi-test-tools/src/test/java/org/apiaddicts/apitools/dosonarapi/TestPathsReportCheck.java b/openapi-test-tools/src/test/java/org/apiaddicts/apitools/dosonarapi/TestPathsReportCheck.java
new file mode 100644
index 0000000..e2b7b2a
--- /dev/null
+++ b/openapi-test-tools/src/test/java/org/apiaddicts/apitools/dosonarapi/TestPathsReportCheck.java
@@ -0,0 +1,39 @@
+/*
+ * doSonarAPI: SonarQube OpenAPI Plugin
+ * Copyright (C) 2021-2022 Apiaddicts
+ * contacta AT apiaddicts DOT org
+ *
+ * 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.apiaddicts.apitools.dosonarapi;
+
+import com.google.common.collect.Sets;
+import com.sonar.sslr.api.AstNodeType;
+import java.util.Set;
+import org.apiaddicts.apitools.dosonarapi.api.OpenApiCheck;
+import org.apiaddicts.apitools.dosonarapi.api.v2.OpenApi2Grammar;
+import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode;
+
+public class TestPathsReportCheck extends OpenApiCheck {
+ @Override
+ public Set subscribedKinds() {
+ return Sets.newHashSet(OpenApi2Grammar.PATHS);
+ }
+
+ @Override
+ protected void visitNode(JsonNode node) {
+ addIssue("paths issue", node);
+ }
+}
diff --git a/openapi-test-tools/src/test/resources/test-verify-noncompliant.yaml b/openapi-test-tools/src/test/resources/test-verify-noncompliant.yaml
new file mode 100644
index 0000000..646fd59
--- /dev/null
+++ b/openapi-test-tools/src/test/resources/test-verify-noncompliant.yaml
@@ -0,0 +1,11 @@
+swagger: "2.0"
+info:
+ version: 1.0.0
+ title: Test API
+paths:
+# Noncompliant@+1 {{paths issue}}
+ /pets:
+ get:
+ responses:
+ '200':
+ description: ok
diff --git a/openapi-test-tools/src/test/resources/test-verify.yaml b/openapi-test-tools/src/test/resources/test-verify.yaml
new file mode 100644
index 0000000..32ed33a
--- /dev/null
+++ b/openapi-test-tools/src/test/resources/test-verify.yaml
@@ -0,0 +1,11 @@
+swagger: "2.0"
+info:
+ version: 1.0.0
+ title: Test API
+paths:
+ # just a comment, not noncompliant
+ /pets:
+ get:
+ responses:
+ '200':
+ description: ok
diff --git a/pom.xml b/pom.xml
index baf72e1..991d94c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -9,7 +9,7 @@
org.apiaddicts.apitools.dosonarapi
dosonarapi
- 1.2.0
+ 1.2.1
pom
SonarOpenAPI
diff --git a/sonar-openapi-plugin/pom.xml b/sonar-openapi-plugin/pom.xml
index 323e377..957db01 100644
--- a/sonar-openapi-plugin/pom.xml
+++ b/sonar-openapi-plugin/pom.xml
@@ -5,7 +5,7 @@
org.apiaddicts.apitools.dosonarapi
dosonarapi
- 1.2.0
+ 1.2.1
../pom.xml
diff --git a/sonar-openapi-plugin/src/main/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApi.java b/sonar-openapi-plugin/src/main/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApi.java
index 723749e..83dca61 100644
--- a/sonar-openapi-plugin/src/main/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApi.java
+++ b/sonar-openapi-plugin/src/main/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApi.java
@@ -19,40 +19,10 @@
*/
package org.apiaddicts.apitools.dosonarapi.plugin;
-import java.util.ArrayList;
-import java.util.List;
-import org.apache.commons.lang.StringUtils;
-import org.sonar.api.config.Configuration;
-import org.sonar.api.resources.AbstractLanguage;
-
-@java.lang.SuppressWarnings("squid:S2160") // purposely not redefining equals() to ignore Configuration differences
-public class OpenApi extends AbstractLanguage {
+public final class OpenApi {
public static final String KEY = "openapi";
- private static final String[] DEFAULT_FILE_SUFFIXES = {"yaml"};
-
- private Configuration settings;
-
- public OpenApi(Configuration settings) {
- super(KEY, "OpenAPI");
- this.settings = settings;
- }
-
- private static String[] filterEmptyStrings(String[] stringArray) {
- List nonEmptyStrings = new ArrayList<>();
- for (String string : stringArray) {
- if (StringUtils.isNotBlank(string.trim())) {
- nonEmptyStrings.add(string.trim());
- }
- }
- return nonEmptyStrings.toArray(new String[0]);
- }
-
- @Override
- public String[] getFileSuffixes() {
- String[] suffixes = filterEmptyStrings(settings.getStringArray(OpenApiPlugin.FILE_SUFFIXES_KEY));
- return suffixes.length == 0 ? OpenApi.DEFAULT_FILE_SUFFIXES : suffixes;
- }
+ private OpenApi() {}
}
diff --git a/sonar-openapi-plugin/src/main/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiAnalyzer.java b/sonar-openapi-plugin/src/main/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiAnalyzer.java
index 46bbd4d..8ed5257 100644
--- a/sonar-openapi-plugin/src/main/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiAnalyzer.java
+++ b/sonar-openapi-plugin/src/main/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiAnalyzer.java
@@ -155,6 +155,11 @@ private void scanFile(InputFile inputFile) {
visitorContext = new OpenApiVisitorContext(openApiFile, re);
LOG.error("Unable to parse file in i/o: " + inputFile.filename() + "\"\n" + ex.getMessage());
dumpException(re, inputFile);
+ } catch (RuntimeException ex) {
+ RecognitionException re = new RecognitionException(0, ex.getMessage());
+ visitorContext = new OpenApiVisitorContext(openApiFile, re);
+ LOG.error("Unexpected error parsing file: " + inputFile.filename() + "\"\n" + ex.getMessage());
+ dumpException(re, inputFile);
}
for (OpenApiCheck check : checks.all()) {
diff --git a/sonar-openapi-plugin/src/main/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiChecks.java b/sonar-openapi-plugin/src/main/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiChecks.java
index f3c1843..ae2c35e 100644
--- a/sonar-openapi-plugin/src/main/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiChecks.java
+++ b/sonar-openapi-plugin/src/main/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiChecks.java
@@ -59,7 +59,8 @@ public OpenApiChecks addCustomChecks(@Nullable OpenApiCustomRuleRepository[] cus
if (customRuleRepositories != null) {
for (OpenApiCustomRuleRepository ruleRepository : customRuleRepositories) {
- if (!ruleRepository.repositoryKey().equals(CheckList.REPOSITORY_KEY)) {
+ if (!ruleRepository.repositoryKey().equals(CheckList.YAML_REPOSITORY_KEY) &&
+ !ruleRepository.repositoryKey().equals(CheckList.JSON_REPOSITORY_KEY)) {
addChecks(ruleRepository.repositoryKey(), new ArrayList<>(ruleRepository.checkClasses()));
}
}
@@ -68,6 +69,34 @@ public OpenApiChecks addCustomChecks(@Nullable OpenApiCustomRuleRepository[] cus
return this;
}
+ public OpenApiChecks addCustomYamlChecks(@Nullable OpenApiCustomRuleRepository[] customRuleRepositories) {
+ if (customRuleRepositories != null) {
+ for (OpenApiCustomRuleRepository ruleRepository : customRuleRepositories) {
+ String key = ruleRepository.repositoryKey();
+ if (!key.equals(CheckList.YAML_REPOSITORY_KEY) &&
+ !key.equals(CheckList.JSON_REPOSITORY_KEY) &&
+ !key.endsWith("-json")) {
+ addChecks(key, new ArrayList<>(ruleRepository.checkClasses()));
+ }
+ }
+ }
+ return this;
+ }
+
+ public OpenApiChecks addCustomJsonChecks(@Nullable OpenApiCustomRuleRepository[] customRuleRepositories) {
+ if (customRuleRepositories != null) {
+ for (OpenApiCustomRuleRepository ruleRepository : customRuleRepositories) {
+ String key = ruleRepository.repositoryKey();
+ if (!key.equals(CheckList.YAML_REPOSITORY_KEY) &&
+ !key.equals(CheckList.JSON_REPOSITORY_KEY) &&
+ key.endsWith("-json")) {
+ addChecks(key, new ArrayList<>(ruleRepository.checkClasses()));
+ }
+ }
+ }
+ return this;
+ }
+
public List all() {
List allVisitors = new ArrayList<>();
diff --git a/sonar-openapi-plugin/src/main/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiPlugin.java b/sonar-openapi-plugin/src/main/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiPlugin.java
index ca5ec80..07f3b38 100644
--- a/sonar-openapi-plugin/src/main/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiPlugin.java
+++ b/sonar-openapi-plugin/src/main/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiPlugin.java
@@ -20,53 +20,13 @@
package org.apiaddicts.apitools.dosonarapi.plugin;
import org.sonar.api.Plugin;
-import org.sonar.api.config.PropertyDefinition;
-import org.sonar.api.resources.Qualifiers;
import org.apiaddicts.apitools.dosonarapi.openapi.metrics.OpenApiMetrics;
public class OpenApiPlugin implements Plugin {
- public static final String FILE_SUFFIXES_KEY = "sonar.openapi.file.suffixes";
- public static final String OPENAPI_CATEGORY = "OpenApi";
- // Subcategories
- private static final String GENERAL = "General";
-
@Override
public void define(Context context) {
-
context.addExtensions(
- PropertyDefinition.builder(FILE_SUFFIXES_KEY)
- .index(10)
- .name("File Suffixes")
- .description("A list of suffixes of OpenAPI files to analyze.")
- .category(OPENAPI_CATEGORY)
- .subCategory(GENERAL)
- .onQualifiers(Qualifiers.PROJECT)
- .multiValues(true)
- .defaultValue("yaml,json")
- .build(),
-/* PropertyDefinition.builder(OpenApiProperties.V2_PATH_KEY)
- .index(11)
- .name("Paths to OpenAPI v2 contract(s)")
- .description("Path to OpenAPI v2 contracts. Ant patterns are accepted for relative path. The contracts can be in JSON or in YAML.")
- .category(OPENAPI_CATEGORY)
- .subCategory(GENERAL)
- .onQualifiers(Qualifiers.PROJECT)
- .multiValues(true)
- .defaultValue(OpenApiProperties.DEFAULT_V2_PATH)
- .build(),
- PropertyDefinition.builder(OpenApiProperties.V3_PATH_KEY)
- .index(11)
- .name("Paths to OpenAPI v3 contract(s)")
- .description("Path to OpenAPI v3 contracts. Ant patterns are accepted for relative path. The contracts can be in JSON or in YAML.")
- .category(OPENAPI_CATEGORY)
- .subCategory(GENERAL)
- .onQualifiers(Qualifiers.PROJECT)
- .multiValues(true)
- .defaultValue(OpenApiProperties.DEFAULT_V3_PATH)
- .build(),*/
- OpenApi.class,
- OpenApiProfileDefinition.class,
OpenApiScannerSensor.class,
OpenApiRulesDefinition.class,
OpenApiMetrics.class);
diff --git a/sonar-openapi-plugin/src/main/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiRulesDefinition.java b/sonar-openapi-plugin/src/main/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiRulesDefinition.java
index a296e34..4794f9a 100644
--- a/sonar-openapi-plugin/src/main/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiRulesDefinition.java
+++ b/sonar-openapi-plugin/src/main/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiRulesDefinition.java
@@ -39,8 +39,13 @@ private static RuleMetadataLoader getRuleMetadataLoader() {
@Override
public void define(Context context) {
+ createRepository(context, repositoryKey(), CheckList.YAML_LANGUAGE);
+ createRepository(context, CheckList.JSON_REPOSITORY_KEY, CheckList.JSON_LANGUAGE);
+ }
+
+ private void createRepository(Context context, String key, String language) {
NewRepository repository = context
- .createRepository(repositoryKey(), OpenApi.KEY)
+ .createRepository(key, language)
.setName(REPOSITORY_NAME);
getRuleMetadataLoader().addRulesByAnnotatedClass(repository, checkClasses());
@@ -54,7 +59,7 @@ public void define(Context context) {
@Override
public String repositoryKey() {
- return CheckList.REPOSITORY_KEY;
+ return CheckList.YAML_REPOSITORY_KEY;
}
@Override
diff --git a/sonar-openapi-plugin/src/main/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiScannerSensor.java b/sonar-openapi-plugin/src/main/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiScannerSensor.java
index a6b29f3..fd47dbe 100644
--- a/sonar-openapi-plugin/src/main/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiScannerSensor.java
+++ b/sonar-openapi-plugin/src/main/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiScannerSensor.java
@@ -38,7 +38,8 @@
public class OpenApiScannerSensor implements Sensor {
private static final Logger LOGGER = Loggers.get(OpenApiScannerSensor.class);
- private final OpenApiChecks checks;
+ private final OpenApiChecks yamlChecks;
+ private final OpenApiChecks jsonChecks;
private FileLinesContextFactory fileLinesContextFactory;
private final NoSonarFilter noSonarFilter;
@@ -47,9 +48,12 @@ public OpenApiScannerSensor(CheckFactory checkFactory, FileLinesContextFactory f
}
public OpenApiScannerSensor(CheckFactory checkFactory, FileLinesContextFactory fileLinesContextFactory, NoSonarFilter noSonarFilter, @Nullable OpenApiCustomRuleRepository[] customRuleRepositories) {
- this.checks = OpenApiChecks.createOpenApiCheck(checkFactory)
- .addChecks(CheckList.REPOSITORY_KEY, CheckList.getChecks())
- .addCustomChecks(customRuleRepositories);
+ this.yamlChecks = OpenApiChecks.createOpenApiCheck(checkFactory)
+ .addChecks(CheckList.YAML_REPOSITORY_KEY, CheckList.getChecks())
+ .addCustomYamlChecks(customRuleRepositories);
+ this.jsonChecks = OpenApiChecks.createOpenApiCheck(checkFactory)
+ .addChecks(CheckList.JSON_REPOSITORY_KEY, CheckList.getChecks())
+ .addCustomJsonChecks(customRuleRepositories);
this.fileLinesContextFactory = fileLinesContextFactory;
this.noSonarFilter = noSonarFilter;
}
@@ -58,7 +62,7 @@ public OpenApiScannerSensor(CheckFactory checkFactory, FileLinesContextFactory f
public void describe(SensorDescriptor descriptor) {
descriptor.name("OpenAPI Scanner Sensor")
.onlyOnFileType(InputFile.Type.MAIN)
- .onlyOnLanguage(OpenApi.KEY);
+ .onlyOnLanguages(CheckList.YAML_LANGUAGE, CheckList.JSON_LANGUAGE);
}
@Override
@@ -68,18 +72,23 @@ public void execute(SensorContext context) {
}
public void scanFiles(SensorContext context, FilePredicates p) {
- Iterable it = context.fileSystem().inputFiles(
- p.and(p.hasType(InputFile.Type.MAIN),
- p.hasLanguage(OpenApi.KEY)
- ));
- List list = new ArrayList<>();
- it.forEach(list::add);
- List inputFiles = Collections.unmodifiableList(list);
+ List yamlFiles = new ArrayList<>();
+ context.fileSystem().inputFiles(
+ p.and(p.hasType(InputFile.Type.MAIN), p.hasLanguage(CheckList.YAML_LANGUAGE))
+ ).forEach(yamlFiles::add);
- if (!inputFiles.isEmpty()) {
- OpenApiAnalyzer scanner = new OpenApiAnalyzer(context, checks, fileLinesContextFactory, noSonarFilter, inputFiles/*, isV2*/);
- LOGGER.info("OpenAPI Scanner called for the following files: {}.", inputFiles);
- scanner.scanFiles();
+ List jsonFiles = new ArrayList<>();
+ context.fileSystem().inputFiles(
+ p.and(p.hasType(InputFile.Type.MAIN), p.hasLanguage(CheckList.JSON_LANGUAGE))
+ ).forEach(jsonFiles::add);
+
+ if (!yamlFiles.isEmpty()) {
+ LOGGER.info("OpenAPI Scanner called for yaml files: {}.", yamlFiles);
+ new OpenApiAnalyzer(context, yamlChecks, fileLinesContextFactory, noSonarFilter, Collections.unmodifiableList(yamlFiles)).scanFiles();
+ }
+ if (!jsonFiles.isEmpty()) {
+ LOGGER.info("OpenAPI Scanner called for json files: {}.", jsonFiles);
+ new OpenApiAnalyzer(context, jsonChecks, fileLinesContextFactory, noSonarFilter, Collections.unmodifiableList(jsonFiles)).scanFiles();
}
}
}
diff --git a/sonar-openapi-plugin/src/main/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiProfileDefinition.java b/sonar-openapi-plugin/src/test/java/org/apiaddicts/apitools/dosonarapi/plugin/MissingPropertyExceptionTest.java
similarity index 53%
rename from sonar-openapi-plugin/src/main/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiProfileDefinition.java
rename to sonar-openapi-plugin/src/test/java/org/apiaddicts/apitools/dosonarapi/plugin/MissingPropertyExceptionTest.java
index 9ea793b..1d339a3 100644
--- a/sonar-openapi-plugin/src/main/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiProfileDefinition.java
+++ b/sonar-openapi-plugin/src/test/java/org/apiaddicts/apitools/dosonarapi/plugin/MissingPropertyExceptionTest.java
@@ -19,22 +19,22 @@
*/
package org.apiaddicts.apitools.dosonarapi.plugin;
-import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition;
-import org.sonar.api.utils.AnnotationUtils;
-import org.sonar.check.Rule;
-import org.apiaddicts.apitools.dosonarapi.checks.CheckList;
+import org.junit.Test;
-public class OpenApiProfileDefinition implements BuiltInQualityProfilesDefinition {
- public static final String SONAR_WAY_PROFILE = "Sonar way";
+import static org.assertj.core.api.Assertions.assertThat;
- @Override
- public void define(BuiltInQualityProfilesDefinition.Context context) {
- NewBuiltInQualityProfile profile = context.createBuiltInQualityProfile(SONAR_WAY_PROFILE, OpenApi.KEY);
- profile.setDefault(true);
- for (Class> check : CheckList.getChecks()) {
- Rule annotation = AnnotationUtils.getAnnotation(check, Rule.class);
- profile.activateRule(CheckList.REPOSITORY_KEY, annotation.key());
- }
- profile.done();
+public class MissingPropertyExceptionTest {
+
+ @Test
+ public void stores_property_name_and_message() {
+ MissingPropertyException ex = new MissingPropertyException("my.property");
+ assertThat(ex.getPropertyName()).isEqualTo("my.property");
+ assertThat(ex.getMessage()).isEqualTo("Property my.property is not defined!");
+ }
+
+ @Test
+ public void is_runtime_exception() {
+ MissingPropertyException ex = new MissingPropertyException("foo");
+ assertThat(ex).isInstanceOf(RuntimeException.class);
}
}
diff --git a/sonar-openapi-plugin/src/test/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiChecksTest.java b/sonar-openapi-plugin/src/test/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiChecksTest.java
new file mode 100644
index 0000000..5b059e0
--- /dev/null
+++ b/sonar-openapi-plugin/src/test/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiChecksTest.java
@@ -0,0 +1,147 @@
+/*
+ * doSonarAPI: SonarQube OpenAPI Plugin
+ * Copyright (C) 2021-2022 Apiaddicts
+ * contacta AT apiaddicts DOT org
+ *
+ * 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.apiaddicts.apitools.dosonarapi.plugin;
+
+import java.util.List;
+import org.junit.Test;
+import org.sonar.api.batch.rule.ActiveRules;
+import org.sonar.api.batch.rule.CheckFactory;
+import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
+import org.sonar.api.rule.RuleKey;
+import org.apiaddicts.apitools.dosonarapi.api.OpenApiCustomRuleRepository;
+import org.apiaddicts.apitools.dosonarapi.checks.CheckList;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class OpenApiChecksTest {
+
+ private OpenApiCustomRuleRepository repoWithKey(String key) {
+ return new OpenApiCustomRuleRepository() {
+ @Override public String repositoryKey() { return key; }
+ @Override public List> checkClasses() { return CheckList.getChecks(); }
+ };
+ }
+
+ private CheckFactory factoryWithRule(String repoKey, String ruleKey) {
+ ActiveRules rules = new ActiveRulesBuilder()
+ .create(RuleKey.of(repoKey, ruleKey)).activate()
+ .build();
+ return new CheckFactory(rules);
+ }
+
+ @Test
+ public void addCustomYamlChecks_adds_non_json_repo() {
+ CheckFactory factory = factoryWithRule("my-custom-yaml", "PathMaskerading");
+ OpenApiChecks checks = OpenApiChecks.createOpenApiCheck(factory)
+ .addCustomYamlChecks(new OpenApiCustomRuleRepository[]{repoWithKey("my-custom-yaml")});
+ assertThat(checks.all()).isNotEmpty();
+ }
+
+ @Test
+ public void addCustomYamlChecks_skips_json_repo() {
+ CheckFactory factory = factoryWithRule("my-custom-json", "PathMaskerading");
+ OpenApiChecks checks = OpenApiChecks.createOpenApiCheck(factory)
+ .addCustomYamlChecks(new OpenApiCustomRuleRepository[]{repoWithKey("my-custom-json")});
+ assertThat(checks.all()).isEmpty();
+ }
+
+ @Test
+ public void addCustomYamlChecks_skips_base_yaml_repo() {
+ CheckFactory factory = factoryWithRule(CheckList.YAML_REPOSITORY_KEY, "PathMaskerading");
+ OpenApiChecks checks = OpenApiChecks.createOpenApiCheck(factory)
+ .addCustomYamlChecks(new OpenApiCustomRuleRepository[]{repoWithKey(CheckList.YAML_REPOSITORY_KEY)});
+ assertThat(checks.all()).isEmpty();
+ }
+
+ @Test
+ public void addCustomJsonChecks_adds_json_repo() {
+ CheckFactory factory = factoryWithRule("my-custom-json", "PathMaskerading");
+ OpenApiChecks checks = OpenApiChecks.createOpenApiCheck(factory)
+ .addCustomJsonChecks(new OpenApiCustomRuleRepository[]{repoWithKey("my-custom-json")});
+ assertThat(checks.all()).isNotEmpty();
+ }
+
+ @Test
+ public void addCustomJsonChecks_skips_non_json_repo() {
+ CheckFactory factory = factoryWithRule("my-custom-yaml", "PathMaskerading");
+ OpenApiChecks checks = OpenApiChecks.createOpenApiCheck(factory)
+ .addCustomJsonChecks(new OpenApiCustomRuleRepository[]{repoWithKey("my-custom-yaml")});
+ assertThat(checks.all()).isEmpty();
+ }
+
+ @Test
+ public void addCustomJsonChecks_skips_base_json_repo() {
+ CheckFactory factory = factoryWithRule(CheckList.JSON_REPOSITORY_KEY, "PathMaskerading");
+ OpenApiChecks checks = OpenApiChecks.createOpenApiCheck(factory)
+ .addCustomJsonChecks(new OpenApiCustomRuleRepository[]{repoWithKey(CheckList.JSON_REPOSITORY_KEY)});
+ assertThat(checks.all()).isEmpty();
+ }
+
+ @Test
+ public void addCustomChecks_handles_null() {
+ ActiveRules rules = new ActiveRulesBuilder().build();
+ CheckFactory factory = new CheckFactory(rules);
+ OpenApiChecks checks = OpenApiChecks.createOpenApiCheck(factory)
+ .addCustomYamlChecks(null)
+ .addCustomJsonChecks(null);
+ assertThat(checks.all()).isEmpty();
+ }
+
+ @Test
+ public void addCustomChecks_adds_non_built_in_repo() {
+ CheckFactory factory = factoryWithRule("my-custom-repo", "PathMaskerading");
+ OpenApiChecks checks = OpenApiChecks.createOpenApiCheck(factory)
+ .addCustomChecks(new OpenApiCustomRuleRepository[]{repoWithKey("my-custom-repo")});
+ assertThat(checks.all()).isNotEmpty();
+ }
+
+ @Test
+ public void addCustomChecks_skips_yaml_built_in_repo() {
+ CheckFactory factory = factoryWithRule(CheckList.YAML_REPOSITORY_KEY, "PathMaskerading");
+ OpenApiChecks checks = OpenApiChecks.createOpenApiCheck(factory)
+ .addCustomChecks(new OpenApiCustomRuleRepository[]{repoWithKey(CheckList.YAML_REPOSITORY_KEY)});
+ assertThat(checks.all()).isEmpty();
+ }
+
+ @Test
+ public void addCustomChecks_skips_json_built_in_repo() {
+ CheckFactory factory = factoryWithRule(CheckList.JSON_REPOSITORY_KEY, "PathMaskerading");
+ OpenApiChecks checks = OpenApiChecks.createOpenApiCheck(factory)
+ .addCustomChecks(new OpenApiCustomRuleRepository[]{repoWithKey(CheckList.JSON_REPOSITORY_KEY)});
+ assertThat(checks.all()).isEmpty();
+ }
+
+ @Test
+ public void addCustomChecks_handles_null_input() {
+ ActiveRules rules = new ActiveRulesBuilder().build();
+ CheckFactory factory = new CheckFactory(rules);
+ OpenApiChecks checks = OpenApiChecks.createOpenApiCheck(factory)
+ .addCustomChecks(null);
+ assertThat(checks.all()).isEmpty();
+ }
+
+ @Test
+ public void ruleKeyFor_returns_null_when_not_found() {
+ ActiveRules rules = new ActiveRulesBuilder().build();
+ CheckFactory factory = new CheckFactory(rules);
+ OpenApiChecks checks = OpenApiChecks.createOpenApiCheck(factory);
+ assertThat(checks.ruleKeyFor(new org.apiaddicts.apitools.dosonarapi.checks.PathMaskeradingCheck())).isNull();
+ }
+}
diff --git a/sonar-openapi-plugin/src/test/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiPluginTest.java b/sonar-openapi-plugin/src/test/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiPluginTest.java
new file mode 100644
index 0000000..bc68fe8
--- /dev/null
+++ b/sonar-openapi-plugin/src/test/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiPluginTest.java
@@ -0,0 +1,41 @@
+/*
+ * doSonarAPI: SonarQube OpenAPI Plugin
+ * Copyright (C) 2021-2022 Apiaddicts
+ * contacta AT apiaddicts DOT org
+ *
+ * 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.apiaddicts.apitools.dosonarapi.plugin;
+
+import org.junit.Test;
+import org.sonar.api.Plugin;
+import org.sonar.api.SonarQubeSide;
+import org.sonar.api.internal.SonarRuntimeImpl;
+import org.sonar.api.utils.Version;
+import org.apiaddicts.apitools.dosonarapi.openapi.metrics.OpenApiMetrics;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class OpenApiPluginTest {
+
+ @Test
+ public void defines_expected_extensions() {
+ Plugin.Context context = new Plugin.Context(SonarRuntimeImpl.forSonarQube(Version.create(6, 7), SonarQubeSide.SERVER));
+ new OpenApiPlugin().define(context);
+
+ assertThat(context.getExtensions())
+ .contains(OpenApiScannerSensor.class, OpenApiRulesDefinition.class, OpenApiMetrics.class);
+ }
+}
diff --git a/sonar-openapi-plugin/src/test/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiProfileDefinitionTest.java b/sonar-openapi-plugin/src/test/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiProfileDefinitionTest.java
deleted file mode 100644
index b1f29f5..0000000
--- a/sonar-openapi-plugin/src/test/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiProfileDefinitionTest.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * doSonarAPI: SonarQube OpenAPI Plugin
- * Copyright (C) 2021-2022 Apiaddicts
- * contacta AT apiaddicts DOT org
- *
- * 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.apiaddicts.apitools.dosonarapi.plugin;
-
-import org.junit.Test;
-import org.mockito.Mockito;
-import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition.Context;
-import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition.NewBuiltInQualityProfile;
-import org.apiaddicts.apitools.dosonarapi.checks.CheckList;
-
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.apiaddicts.apitools.dosonarapi.plugin.OpenApiProfileDefinition.SONAR_WAY_PROFILE;
-
-public class OpenApiProfileDefinitionTest {
- private static Context context(NewBuiltInQualityProfile profile) {
- Context context = mock(Context.class);
- when(context.createBuiltInQualityProfile(anyString(), anyString())).thenReturn(profile);
- return context;
- }
-
- @Test
- public void should_create_sonar_way_profile() {
- OpenApiProfileDefinition definition = new OpenApiProfileDefinition();
- NewBuiltInQualityProfile profile = mock(NewBuiltInQualityProfile.class);
- Context context = context(profile);
-
- definition.define(context);
- ;
-
- verify(context).createBuiltInQualityProfile(SONAR_WAY_PROFILE, OpenApi.KEY);
- verify(profile).setDefault(true);
- verify(profile, Mockito.atLeast(2)).activateRule(eq(CheckList.REPOSITORY_KEY), anyString());
- }
-}
diff --git a/sonar-openapi-plugin/src/test/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiRulesDefinitionTest.java b/sonar-openapi-plugin/src/test/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiRulesDefinitionTest.java
new file mode 100644
index 0000000..817814b
--- /dev/null
+++ b/sonar-openapi-plugin/src/test/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiRulesDefinitionTest.java
@@ -0,0 +1,70 @@
+/*
+ * doSonarAPI: SonarQube OpenAPI Plugin
+ * Copyright (C) 2021-2022 Apiaddicts
+ * contacta AT apiaddicts DOT org
+ *
+ * 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.apiaddicts.apitools.dosonarapi.plugin;
+
+import org.junit.Test;
+import org.sonar.api.server.rule.RulesDefinition;
+import org.apiaddicts.apitools.dosonarapi.checks.CheckList;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class OpenApiRulesDefinitionTest {
+
+ @Test
+ public void defines_yaml_and_json_repositories() {
+ OpenApiRulesDefinition definition = new OpenApiRulesDefinition();
+ RulesDefinition.Context context = new RulesDefinition.Context();
+
+ definition.define(context);
+
+ RulesDefinition.Repository yamlRepo = context.repository(CheckList.YAML_REPOSITORY_KEY);
+ assertThat(yamlRepo).isNotNull();
+ assertThat(yamlRepo.language()).isEqualTo(CheckList.YAML_LANGUAGE);
+ assertThat(yamlRepo.rules()).hasSizeGreaterThanOrEqualTo(1);
+
+ RulesDefinition.Repository jsonRepo = context.repository(CheckList.JSON_REPOSITORY_KEY);
+ assertThat(jsonRepo).isNotNull();
+ assertThat(jsonRepo.language()).isEqualTo(CheckList.JSON_LANGUAGE);
+ assertThat(jsonRepo.rules()).hasSizeGreaterThanOrEqualTo(1);
+ }
+
+ @Test
+ public void repository_key_returns_yaml_key() {
+ OpenApiRulesDefinition definition = new OpenApiRulesDefinition();
+ assertThat(definition.repositoryKey()).isEqualTo(CheckList.YAML_REPOSITORY_KEY);
+ }
+
+ @Test
+ public void check_classes_returns_all_checks() {
+ OpenApiRulesDefinition definition = new OpenApiRulesDefinition();
+ assertThat(definition.checkClasses()).isEqualTo(CheckList.getChecks());
+ }
+
+ @Test
+ public void yaml_and_json_repos_have_same_rules() {
+ OpenApiRulesDefinition definition = new OpenApiRulesDefinition();
+ RulesDefinition.Context context = new RulesDefinition.Context();
+ definition.define(context);
+
+ RulesDefinition.Repository yamlRepo = context.repository(CheckList.YAML_REPOSITORY_KEY);
+ RulesDefinition.Repository jsonRepo = context.repository(CheckList.JSON_REPOSITORY_KEY);
+ assertThat(yamlRepo.rules()).hasSameSizeAs(jsonRepo.rules());
+ }
+}
diff --git a/sonar-openapi-plugin/src/test/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiScannerSensorTest.java b/sonar-openapi-plugin/src/test/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiScannerSensorTest.java
index ac817e4..2d6b20b 100644
--- a/sonar-openapi-plugin/src/test/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiScannerSensorTest.java
+++ b/sonar-openapi-plugin/src/test/java/org/apiaddicts/apitools/dosonarapi/plugin/OpenApiScannerSensorTest.java
@@ -77,14 +77,14 @@ public void sensor_descriptor() {
sensor().describe(descriptor);
assertThat(descriptor.name()).isEqualTo("OpenAPI Scanner Sensor");
- assertThat(descriptor.languages()).containsOnly("openapi");
+ assertThat(descriptor.languages()).containsOnly("yaml", "json");
assertThat(descriptor.type()).isEqualTo(InputFile.Type.MAIN);
}
@Test
public void test_issues() {
activeRules = (new ActiveRulesBuilder())
- .create(RuleKey.of(CheckList.REPOSITORY_KEY, "PathMaskerading"))
+ .create(RuleKey.of(CheckList.YAML_REPOSITORY_KEY, "PathMaskerading"))
.activate()
.build();
@@ -115,11 +115,40 @@ public void test_issues() {
assertThat(context.allAnalysisErrors()).isEmpty();
}
+ @Test
+ public void test_json_issues_single_per_violation() {
+ activeRules = (new ActiveRulesBuilder())
+ .create(RuleKey.of(CheckList.JSON_REPOSITORY_KEY, "PathMaskerading"))
+ .activate()
+ .build();
+
+ inputFile("file1.json");
+ sensor().execute(context);
+
+ assertThat(context.allIssues()).hasSize(1);
+ Issue issue = Iterables.get(context.allIssues(), 0);
+ assertThat(issue.ruleKey().repository()).isEqualTo(CheckList.JSON_REPOSITORY_KEY);
+ assertThat(context.allAnalysisErrors()).isEmpty();
+ }
+
+ @Test
+ public void test_yaml_rules_do_not_fire_on_json_files() {
+ activeRules = (new ActiveRulesBuilder())
+ .create(RuleKey.of(CheckList.YAML_REPOSITORY_KEY, "PathMaskerading"))
+ .activate()
+ .build();
+
+ inputFile("file1.json");
+ sensor().execute(context);
+
+ assertThat(context.allIssues()).isEmpty();
+ }
+
@Test
public void parse_error() {
inputFile("parse-error.yaml");
activeRules = (new ActiveRulesBuilder())
- .create(RuleKey.of(CheckList.REPOSITORY_KEY, ParsingErrorCheck.CHECK_KEY))
+ .create(RuleKey.of(CheckList.YAML_REPOSITORY_KEY, ParsingErrorCheck.CHECK_KEY))
.activate()
.build();
sensor().execute(context);
@@ -136,7 +165,7 @@ public void parse_error() {
public void parse_openapi3_headers_ref() {
inputFile("headers_ref.yaml");
activeRules = (new ActiveRulesBuilder())
- .create(RuleKey.of(CheckList.REPOSITORY_KEY, ParsingErrorCheck.CHECK_KEY))
+ .create(RuleKey.of(CheckList.YAML_REPOSITORY_KEY, ParsingErrorCheck.CHECK_KEY))
.activate()
.build();
sensor().execute(context);
@@ -146,7 +175,7 @@ public void parse_openapi3_headers_ref() {
public void parse_yaml_break_comment_ok() {
inputFile("parse-yaml.yaml");
activeRules = (new ActiveRulesBuilder())
- .create(RuleKey.of(CheckList.REPOSITORY_KEY, ParsingErrorCheck.CHECK_KEY))
+ .create(RuleKey.of(CheckList.YAML_REPOSITORY_KEY, ParsingErrorCheck.CHECK_KEY))
.activate()
.build();
sensor().execute(context);
@@ -158,7 +187,7 @@ public void parse_yaml_break_comment_ok() {
public void parse_yaml_slash_ok() {
inputFile("parse-error-slash.json");
activeRules = (new ActiveRulesBuilder())
- .create(RuleKey.of(CheckList.REPOSITORY_KEY, ParsingErrorCheck.CHECK_KEY))
+ .create(RuleKey.of(CheckList.YAML_REPOSITORY_KEY, ParsingErrorCheck.CHECK_KEY))
.activate()
.build();
sensor().execute(context);
@@ -170,7 +199,7 @@ public void parse_yaml_slash_ok() {
public void parse_yaml_tabs_ok() {
inputFile("parse-error-tabs.json");
activeRules = (new ActiveRulesBuilder())
- .create(RuleKey.of(CheckList.REPOSITORY_KEY, ParsingErrorCheck.CHECK_KEY))
+ .create(RuleKey.of(CheckList.YAML_REPOSITORY_KEY, ParsingErrorCheck.CHECK_KEY))
.activate()
.build();
sensor().execute(context);
@@ -182,7 +211,7 @@ public void parse_yaml_tabs_ok() {
public void parse_yaml_tabs_ok_31() {
inputFile("file2.yaml");
activeRules = (new ActiveRulesBuilder())
- .create(RuleKey.of(CheckList.REPOSITORY_KEY, ParsingErrorCheck.CHECK_KEY))
+ .create(RuleKey.of(CheckList.YAML_REPOSITORY_KEY, ParsingErrorCheck.CHECK_KEY))
.activate()
.build();
sensor().execute(context);
@@ -197,7 +226,7 @@ public void test_folder() {
for (String file: files) {
context = SensorContextTester.create(baseDir);
inputFile(file);
- activeRules = (new ActiveRulesBuilder()).create(RuleKey.of(CheckList.REPOSITORY_KEY, ParsingErrorCheck.CHECK_KEY))
+ activeRules = (new ActiveRulesBuilder()).create(RuleKey.of(CheckList.YAML_REPOSITORY_KEY, ParsingErrorCheck.CHECK_KEY))
.activate().build();
sensor().execute(context);
if (!context.allIssues().isEmpty() || !context.allAnalysisErrors().isEmpty()) errorFiles.add(file);
@@ -223,11 +252,12 @@ private OpenApiScannerSensor sensor() {
}
private InputFile inputFile(String name) {
+ String language = name.endsWith(".json") ? CheckList.JSON_LANGUAGE : CheckList.YAML_LANGUAGE;
DefaultInputFile inputFile = TestInputFileBuilder.create("moduleKey", name)
.setModuleBaseDir(baseDir)
.setCharset(StandardCharsets.UTF_8)
.setType(InputFile.Type.MAIN)
- .setLanguage(OpenApi.KEY)
+ .setLanguage(language)
.initMetadata(TestUtils.fileContent(new File(baseDir.toFile(), name), StandardCharsets.UTF_8))
.build();
context.fileSystem().add(inputFile);
diff --git a/sonar-openapi-plugin/src/test/java/org/apiaddicts/apitools/dosonarapi/plugin/SonarQubeOpenApiFileTest.java b/sonar-openapi-plugin/src/test/java/org/apiaddicts/apitools/dosonarapi/plugin/SonarQubeOpenApiFileTest.java
new file mode 100644
index 0000000..3f409a0
--- /dev/null
+++ b/sonar-openapi-plugin/src/test/java/org/apiaddicts/apitools/dosonarapi/plugin/SonarQubeOpenApiFileTest.java
@@ -0,0 +1,80 @@
+/*
+ * doSonarAPI: SonarQube OpenAPI Plugin
+ * Copyright (C) 2021-2022 Apiaddicts
+ * contacta AT apiaddicts DOT org
+ *
+ * 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.apiaddicts.apitools.dosonarapi.plugin;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Paths;
+import org.junit.Test;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
+import org.apiaddicts.apitools.dosonarapi.api.OpenApiFile;
+import org.apiaddicts.apitools.dosonarapi.checks.CheckList;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class SonarQubeOpenApiFileTest {
+
+ private static final File BASE_DIR = Paths.get("src/test/resources/sensor").toAbsolutePath().toFile();
+
+ @Test
+ public void returns_filename() {
+ InputFile inputFile = TestInputFileBuilder.create("moduleKey", "file1.yaml")
+ .setModuleBaseDir(BASE_DIR.toPath())
+ .setCharset(StandardCharsets.UTF_8)
+ .setType(InputFile.Type.MAIN)
+ .setLanguage(CheckList.YAML_LANGUAGE)
+ .initMetadata(TestUtils.fileContent(new File(BASE_DIR, "file1.yaml"), StandardCharsets.UTF_8))
+ .build();
+
+ OpenApiFile openApiFile = SonarQubeOpenApiFile.create(inputFile);
+ assertThat(openApiFile.fileName()).isEqualTo("file1.yaml");
+ }
+
+ @Test
+ public void returns_content() {
+ InputFile inputFile = TestInputFileBuilder.create("moduleKey", "file1.yaml")
+ .setModuleBaseDir(BASE_DIR.toPath())
+ .setCharset(StandardCharsets.UTF_8)
+ .setType(InputFile.Type.MAIN)
+ .setLanguage(CheckList.YAML_LANGUAGE)
+ .initMetadata(TestUtils.fileContent(new File(BASE_DIR, "file1.yaml"), StandardCharsets.UTF_8))
+ .build();
+
+ OpenApiFile openApiFile = SonarQubeOpenApiFile.create(inputFile);
+ assertThat(openApiFile.content()).isNotEmpty();
+ }
+
+ @Test
+ public void throws_illegal_state_on_io_exception() throws IOException {
+ InputFile inputFile = mock(InputFile.class);
+ when(inputFile.filename()).thenReturn("test.yaml");
+ when(inputFile.contents()).thenThrow(new IOException("read error"));
+
+ OpenApiFile openApiFile = SonarQubeOpenApiFile.create(inputFile);
+ assertThatThrownBy(openApiFile::content)
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessageContaining("Could not read content");
+ }
+}
diff --git a/sonar-openapi-plugin/src/test/java/org/apiaddicts/apitools/dosonarapi/plugin/cpd/OpenApiCpdAnalyzerTest.java b/sonar-openapi-plugin/src/test/java/org/apiaddicts/apitools/dosonarapi/plugin/cpd/OpenApiCpdAnalyzerTest.java
index 1f4aa36..049e8bc 100644
--- a/sonar-openapi-plugin/src/test/java/org/apiaddicts/apitools/dosonarapi/plugin/cpd/OpenApiCpdAnalyzerTest.java
+++ b/sonar-openapi-plugin/src/test/java/org/apiaddicts/apitools/dosonarapi/plugin/cpd/OpenApiCpdAnalyzerTest.java
@@ -25,7 +25,7 @@
import java.util.List;
import java.util.stream.Collectors;
-import org.apiaddicts.apitools.dosonarapi.plugin.OpenApi;
+import org.apiaddicts.apitools.dosonarapi.checks.CheckList;
import org.apiaddicts.apitools.dosonarapi.plugin.TestUtils;
import org.junit.Test;
import org.sonar.api.batch.fs.InputFile;
@@ -75,7 +75,7 @@ private DefaultInputFile inputFile(String fileName) {
.setModuleBaseDir(Paths.get(BASE_DIR))
.setCharset(UTF_8)
.setType(InputFile.Type.MAIN)
- .setLanguage(OpenApi.KEY)
+ .setLanguage(CheckList.YAML_LANGUAGE)
.initMetadata(TestUtils.fileContent(file, Charsets.UTF_8))
.build();
diff --git a/sonar-openapi-plugin/src/test/resources/sensor/file1.json b/sonar-openapi-plugin/src/test/resources/sensor/file1.json
new file mode 100644
index 0000000..5178058
--- /dev/null
+++ b/sonar-openapi-plugin/src/test/resources/sensor/file1.json
@@ -0,0 +1,51 @@
+{
+ "openapi": "3.0.1",
+ "info": {
+ "version": "1.0.0",
+ "title": "Swagger Petstore"
+ },
+ "paths": {
+ "/pets/{petId}": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Pet"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/pets/1234": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Pet"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "Pet": {
+ "type": "object"
+ }
+ }
+ }
+}