diff --git a/CHANGELOG.md b/CHANGELOG.md index 54061489..a135c7d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,14 @@ 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.3.6] - 2026-05-05 + +### Fixed + - External `$ref` tests no longer require outbound internet access. Fixtures + are now served by a local HTTP server (`ExternalRefHttpServer`) on + `http://localhost:18089`, started in `BaseCheckTest`. Affected tests: + OAR031 (v2/v3), OAR094, OAR068, OAR086. + ## [1.3.5] - 2026-04-08 ### Fixed diff --git a/pom.xml b/pom.xml index 1a24854e..c39e397a 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.apiaddicts.apitools.dosonarapi sonaropenapi-rules-community - 1.3.5 + 1.3.6 sonar-plugin SonarQube OpenAPI Community Rules diff --git a/src/test/java/apiaddicts/sonar/openapi/BaseCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/BaseCheckTest.java index c4d16112..f68c63ca 100644 --- a/src/test/java/apiaddicts/sonar/openapi/BaseCheckTest.java +++ b/src/test/java/apiaddicts/sonar/openapi/BaseCheckTest.java @@ -8,6 +8,8 @@ import org.apiaddicts.apitools.dosonarapi.api.OpenApiCheck; +import apiaddicts.sonar.openapi.utils.ExternalRefHttpServer; + import java.util.Arrays; import java.util.HashSet; import java.util.List; @@ -77,6 +79,7 @@ private void verify(String file, boolean isV2, boolean isV3, boolean isV31) { @BeforeClass public static void beforeClass() { + ExternalRefHttpServer.start(); I18nContext.setLang("en"); if (repository != null) return; OpenAPICustomRulesDefinition rulesDefinition = new OpenAPICustomRulesDefinition(); diff --git a/src/test/java/apiaddicts/sonar/openapi/utils/ExternalRefHttpServer.java b/src/test/java/apiaddicts/sonar/openapi/utils/ExternalRefHttpServer.java new file mode 100644 index 00000000..13f28492 --- /dev/null +++ b/src/test/java/apiaddicts/sonar/openapi/utils/ExternalRefHttpServer.java @@ -0,0 +1,59 @@ +package apiaddicts.sonar.openapi.utils; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpServer; + +public final class ExternalRefHttpServer { + + public static final int PORT = 18089; + public static final String BASE_URL = "http://localhost:" + PORT; + + private static final Path FIXTURES = Paths.get("src", "test", "resources", "externalRef").toAbsolutePath().normalize(); + + private static HttpServer server; + + private ExternalRefHttpServer() { + } + + public static synchronized void start() { + if (server != null) { + return; + } + try { + HttpServer s = HttpServer.create(new InetSocketAddress("127.0.0.1", PORT), 0); + s.createContext("/", ExternalRefHttpServer::handle); + s.setExecutor(null); + s.start(); + Runtime.getRuntime().addShutdownHook(new Thread(() -> s.stop(0))); + server = s; + } catch (IOException e) { + throw new IllegalStateException("Could not start external ref HTTP server on port " + PORT, e); + } + } + + private static void handle(HttpExchange exchange) throws IOException { + try { + String name = exchange.getRequestURI().getPath().replaceFirst("^/+", ""); + Path file = FIXTURES.resolve(name).normalize(); + if (!file.startsWith(FIXTURES) || !Files.isRegularFile(file)) { + exchange.sendResponseHeaders(404, -1); + return; + } + byte[] body = Files.readAllBytes(file); + exchange.getResponseHeaders().set("Content-Type", "application/yaml; charset=utf-8"); + exchange.sendResponseHeaders(200, body.length); + try (OutputStream os = exchange.getResponseBody()) { + os.write(body); + } + } finally { + exchange.close(); + } + } +} diff --git a/src/test/resources/checks/v2/examples/OAR031/externalref.yaml b/src/test/resources/checks/v2/examples/OAR031/externalref.yaml index fc5c3de3..8b2b3bc2 100644 --- a/src/test/resources/checks/v2/examples/OAR031/externalref.yaml +++ b/src/test/resources/checks/v2/examples/OAR031/externalref.yaml @@ -18,7 +18,7 @@ paths: $ref: '#/definitions/User' 400: $ref: >- # Noncompliant {{OAR031: Responses must have one or more examples defined}} - https://raw.githubusercontent.com/apiaddicts/sonaropenapi-rules/refs/heads/master/src/test/resources/externalRef/OAR031.yaml#/components/responses/server_error_response + http://localhost:18089/OAR031.yaml#/components/responses/server_error_response /users/{userId}: get: parameters: diff --git a/src/test/resources/checks/v3/examples/OAR031/externalref.yaml b/src/test/resources/checks/v3/examples/OAR031/externalref.yaml index 0b1ec46d..05e57ddb 100644 --- a/src/test/resources/checks/v3/examples/OAR031/externalref.yaml +++ b/src/test/resources/checks/v3/examples/OAR031/externalref.yaml @@ -24,7 +24,7 @@ paths: $ref: '#/components/schemas/User' '400': $ref: >- # Noncompliant {{OAR031: Responses must have one or more examples defined}} - https://raw.githubusercontent.com/apiaddicts/sonaropenapi-rules/refs/heads/master/src/test/resources/externalRef/OAR031.yaml#/components/responses/server_error_response + http://localhost:18089/OAR031.yaml#/components/responses/server_error_response /users/{userId}: get: summary: Get a user by ID @@ -48,7 +48,7 @@ paths: $ref: '#/components/schemas/User' '400': $ref: >- # Noncompliant {{OAR031: Responses must have one or more examples defined}} - https://raw.githubusercontent.com/apiaddicts/sonaropenapi-rules/refs/heads/master/src/test/resources/externalRef/OAR031.yaml#/components/responses/server_error_response + http://localhost:18089/OAR031.yaml#/components/responses/server_error_response components: schemas: User: diff --git a/src/test/resources/checks/v3/examples/OAR094/externalref.yaml b/src/test/resources/checks/v3/examples/OAR094/externalref.yaml index d750e57a..bb80ad35 100644 --- a/src/test/resources/checks/v3/examples/OAR094/externalref.yaml +++ b/src/test/resources/checks/v3/examples/OAR094/externalref.yaml @@ -36,7 +36,7 @@ paths: application/json: schema: $ref: >- # Noncompliant {{OAR094: It is recommended to use examples instead of example as some tools like microcks use this section of the definition}} - https://raw.githubusercontent.com/apiaddicts/sonaropenapi-rules/refs/heads/master/src/test/resources/externalRef/OAR094.yaml#/components/schemas/Pet + http://localhost:18089/OAR094.yaml#/components/schemas/Pet '400': description: Bad Request. headers: @@ -48,7 +48,7 @@ paths: application/json: schema: $ref: >- # Noncompliant {{OAR094: It is recommended to use examples instead of example as some tools like microcks use this section of the definition}} - https://raw.githubusercontent.com/apiaddicts/sonaropenapi-rules/refs/heads/master/src/test/resources/externalRef/OAR094.yaml#/components/schemas/ErrorMessage + http://localhost:18089/OAR094.yaml#/components/schemas/ErrorMessage example: # Noncompliant {{OAR094: It is recommended to use examples instead of example as some tools like microcks use this section of the definition}} error: status: '400' diff --git a/src/test/resources/checks/v3/format/OAR068/externalref.yaml b/src/test/resources/checks/v3/format/OAR068/externalref.yaml index 5035f52c..94a9abb5 100644 --- a/src/test/resources/checks/v3/format/OAR068/externalref.yaml +++ b/src/test/resources/checks/v3/format/OAR068/externalref.yaml @@ -36,7 +36,7 @@ paths: application/json: schema: # Noncompliant {{OAR068: RequestBody and Responses schema property names must be compliant with the PascalCase naming convention}} $ref: >- - https://raw.githubusercontent.com/apiaddicts/sonaropenapi-rules/refs/heads/master/src/test/resources/externalRef/OAR068.yaml#/components/schemas/datosUsuario + http://localhost:18089/OAR068.yaml#/components/schemas/datosUsuario '400': description: Bad Request. headers: @@ -48,7 +48,7 @@ paths: application/json: schema: # Noncompliant {{OAR068: RequestBody and Responses schema property names must be compliant with the PascalCase naming convention}} $ref: >- - https://raw.githubusercontent.com/apiaddicts/sonaropenapi-rules/refs/heads/master/src/test/resources/externalRef/OAR086.yaml#/components/schemas/ErrorMessage + http://localhost:18089/OAR086.yaml#/components/schemas/ErrorMessage example: error: status: '400' diff --git a/src/test/resources/checks/v3/format/OAR086/external-refexample.yaml b/src/test/resources/checks/v3/format/OAR086/external-refexample.yaml index 942a056b..6eca289b 100644 --- a/src/test/resources/checks/v3/format/OAR086/external-refexample.yaml +++ b/src/test/resources/checks/v3/format/OAR086/external-refexample.yaml @@ -36,7 +36,7 @@ paths: application/json: schema: $ref: >- # Noncompliant {{OAR086: Descriptions must begin with a capital letter, end with a period and not be empty}} - https://raw.githubusercontent.com/apiaddicts/sonaropenapi-rules/refs/heads/master/src/test/resources/externalRef/OAR086.yaml#/components/schemas/datosUsuario + http://localhost:18089/OAR086.yaml#/components/schemas/datosUsuario '400': description: Bad Request. headers: @@ -48,7 +48,7 @@ paths: application/json: schema: $ref: >- # Noncompliant {{OAR086: Descriptions must begin with a capital letter, end with a period and not be empty}} - https://raw.githubusercontent.com/apiaddicts/sonaropenapi-rules/refs/heads/master/src/test/resources/externalRef/OAR086.yaml#/components/schemas/ErrorMessage + http://localhost:18089/OAR086.yaml#/components/schemas/ErrorMessage example: error: status: '400'