diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1c92dcd7..d2b54e91 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,7 +31,7 @@ jobs: - name: Build with Maven env: MAVEN_OPTS: -Dhttps.protocols=TLSv1.2 -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.requestSentEnabled=true -Dmaven.wagon.http.retryHandler.count=10 - run: mvn --batch-mode --errors --update-snapshots package --file pom.xml + run: mvn --batch-mode --errors --update-snapshots verify --file pom.xml - name: Upload artifacts uses: actions/upload-artifact@v7 with: diff --git a/openig-core/src/main/java/org/forgerock/openig/handler/router/OpenApiRouteBuilder.java b/openig-core/src/main/java/org/forgerock/openig/handler/router/OpenApiRouteBuilder.java index 31e9d3ff..706ccd0f 100644 --- a/openig-core/src/main/java/org/forgerock/openig/handler/router/OpenApiRouteBuilder.java +++ b/openig-core/src/main/java/org/forgerock/openig/handler/router/OpenApiRouteBuilder.java @@ -108,9 +108,15 @@ public JsonValue buildRouteJson(final OpenAPI spec, final File specFile, routeName, specFile.getName(), condition, baseUri != null ? baseUri : "", failOnResponseViolation, mockMode); + // Normalise the platform path separator to '/': this path is embedded in an EL + // read('...') string literal, where a Windows backslash (D:\...) is parsed as an + // (invalid) escape sequence. Forward slashes are accepted by read()/File on every OS. + final String absolutePath = specFile.getAbsolutePath(); + final String specPath = absolutePath == null ? null : absolutePath.replace(File.separatorChar, '/'); + // ----- heap: OpenApiValidationFilter entry ----- final Map validatorConfig = new LinkedHashMap<>(); - validatorConfig.put("spec", "${read('" + specFile.getAbsolutePath() + "')}"); + validatorConfig.put("spec", "${read('" + specPath + "')}"); validatorConfig.put("failOnResponseViolation", failOnResponseViolation); final Map validatorHeapObject = new LinkedHashMap<>(); @@ -125,7 +131,7 @@ public JsonValue buildRouteJson(final OpenAPI spec, final File specFile, if (mockMode) { // ----- heap: OpenApiMockResponseHandler entry ----- final Map mockConfig = new LinkedHashMap<>(); - mockConfig.put("spec", "${read('" + specFile.getAbsolutePath() + "')}"); + mockConfig.put("spec", "${read('" + specPath + "')}"); final Map mockHeapObject = new LinkedHashMap<>(); mockHeapObject.put("name", MOCK_HANDLER_HEAP_NAME); diff --git a/openig-core/src/test/java/org/forgerock/openig/handler/router/OpenApiRouteBuilderTest.java b/openig-core/src/test/java/org/forgerock/openig/handler/router/OpenApiRouteBuilderTest.java index 7b4d40cb..9a75fbee 100644 --- a/openig-core/src/test/java/org/forgerock/openig/handler/router/OpenApiRouteBuilderTest.java +++ b/openig-core/src/test/java/org/forgerock/openig/handler/router/OpenApiRouteBuilderTest.java @@ -239,7 +239,8 @@ public void buildRouteJson_validatorHeapObjectHasAbsoluteSpecFilePath() throws I .orElseThrow(() -> new AssertionError("No OpenApiValidationFilter in heap")); final java.util.Map config = (java.util.Map) validatorEntry.get("config"); - assertThat(config.get("spec").toString()).contains(spec.getAbsolutePath()); + // OpenApiRouteBuilder normalises the platform separator to '/' for the read() literal. + assertThat(config.get("spec").toString()).contains(spec.getAbsolutePath().replace(File.separatorChar, '/')); } @Test @@ -342,7 +343,8 @@ public void buildRouteJson_mockMode_mockHandlerConfigContainsSpecPath() throws I .orElseThrow(() -> new AssertionError("No OpenApiMockResponseHandler in heap")); final java.util.Map config = (java.util.Map) mockEntry.get("config"); - assertThat(config.get("spec").toString()).contains(spec.getAbsolutePath()); + // OpenApiRouteBuilder normalises the platform separator to '/' for the read() literal. + assertThat(config.get("spec").toString()).contains(spec.getAbsolutePath().replace(File.separatorChar, '/')); } diff --git a/openig-war/pom.xml b/openig-war/pom.xml index ae9c0bb0..36f8ce7f 100644 --- a/openig-war/pom.xml +++ b/openig-war/pom.xml @@ -205,8 +205,8 @@ integration-test + verify - integration-test integration-test diff --git a/openig-war/src/test/java/org/openidentityplatrform/openig/test/integration/IT_MockRoute.java b/openig-war/src/test/java/org/openidentityplatrform/openig/test/integration/IT_MockRoute.java index 6bb16368..a6aee558 100644 --- a/openig-war/src/test/java/org/openidentityplatrform/openig/test/integration/IT_MockRoute.java +++ b/openig-war/src/test/java/org/openidentityplatrform/openig/test/integration/IT_MockRoute.java @@ -83,11 +83,11 @@ public void testMockRoute_petCollection() throws IOException { .atMost(30, SECONDS) .until(() -> routeAvailable(ROUTE_ID)); - // GET /v2/pet/findByStatus?status=available → 200 JSON array + // GET /v2.1/pet/findByStatus?status=available → 200 JSON array final String body = RestAssured .given() .when() - .get("/v2/pet/findByStatus?status=available") + .get("/v2.1/pet/findByStatus?status=available") .then() .statusCode(200) .contentType("application/json") @@ -99,7 +99,7 @@ public void testMockRoute_petCollection() throws IOException { // Parse and assert array contents @SuppressWarnings("unchecked") final List> pets = - RestAssured.given().when().get("/v2/pet/findByStatus?status=available") + RestAssured.given().when().get("/v2.1/pet/findByStatus?status=available") .jsonPath().getList("$"); assertThat(pets).isNotEmpty(); @@ -131,7 +131,7 @@ public void testMockRoute_petCollection() throws IOException { .until(() -> !routeAvailable(ROUTE_ID)); RestAssured.given().when() - .get("/v2/pet/findByStatus?status=available") + .get("/v2.1/pet/findByStatus?status=available") .then() .statusCode(404); } @@ -161,10 +161,10 @@ public void testMockRoute_singlePet() throws IOException { .atMost(30, SECONDS) .until(() -> routeAvailable("petstore-mock-single")); - // GET /v2/pet/{petId} → single object + // GET /v2.1/pet/{petId} → single object @SuppressWarnings("unchecked") final Map pet = - RestAssured.given().when().get("/v2/pet/42") + RestAssured.given().when().get("/v2.1/pet/42") .then() .statusCode(200) .contentType("application/json") @@ -216,6 +216,8 @@ private static String getSpecFilePath() throws IOException { Files.copy(in, tmp, StandardCopyOption.REPLACE_EXISTING); } tmp.toFile().deleteOnExit(); - return tmp.toAbsolutePath().toString(); + // Use forward slashes: the path is substituted into the route JSON and an EL read('...') + // expression, where a Windows backslash path (C:\...) is an invalid JSON/EL escape. + return tmp.toAbsolutePath().toString().replace('\\', '/'); } } diff --git a/openig-war/src/test/java/org/openidentityplatrform/openig/test/integration/IT_SwaggerRoute.java b/openig-war/src/test/java/org/openidentityplatrform/openig/test/integration/IT_SwaggerRoute.java index fa263922..ae233287 100644 --- a/openig-war/src/test/java/org/openidentityplatrform/openig/test/integration/IT_SwaggerRoute.java +++ b/openig-war/src/test/java/org/openidentityplatrform/openig/test/integration/IT_SwaggerRoute.java @@ -87,7 +87,9 @@ public void testSwaggerRoute() throws IOException { @Test public void testCustomRoute() throws IOException { String testConfigPath = getTestConfigPath(); - String openApiSpec = this.getClass().getClassLoader().getResource("routes/petstore.yaml").getPath(); + // Use forward slashes: the path is substituted into the route JSON and an EL read('...') + // expression, where a Windows backslash path (D:\...) is an invalid JSON/EL escape. + String openApiSpec = this.getClass().getClassLoader().getResource("routes/petstore.yaml").getPath().replace('\\', '/'); String routeContents = IOUtils.resourceToString("routes/01-find-pet.json", StandardCharsets.UTF_8, this.getClass().getClassLoader()) .replace("$$SWAGGER_FILE$$", openApiSpec); Path destination = Path.of(testConfigPath, "config", "routes", "01-find-pet.json"); diff --git a/openig-war/src/test/resources/routes/01-find-pet.json b/openig-war/src/test/resources/routes/01-find-pet.json index 5c89b4bb..5d6cd768 100644 --- a/openig-war/src/test/resources/routes/01-find-pet.json +++ b/openig-war/src/test/resources/routes/01-find-pet.json @@ -1,7 +1,7 @@ { "name": "swagger-petstore-test", - "condition": "${(matches(request.uri.path, '^/v2/pet/findByStatus$') && matches(request.method, '^GET$'))}", - "baseURI": "http://localhost:8090/v2", + "condition": "${(matches(request.uri.path, '^/v2.1/pet/findByStatus$') && matches(request.method, '^GET$'))}", + "baseURI": "http://localhost:8090/v2.1", "heap": [ { "name": "OpenApiValidator", diff --git a/openig-war/src/test/resources/routes/petstore-mock.json b/openig-war/src/test/resources/routes/petstore-mock.json index 60dac964..b41b5276 100644 --- a/openig-war/src/test/resources/routes/petstore-mock.json +++ b/openig-war/src/test/resources/routes/petstore-mock.json @@ -1,6 +1,6 @@ { "name": "petstore-mock", - "condition": "${matches(request.uri.path, '^/v2/')}", + "condition": "${matches(request.uri.path, '^/v2.1/')}", "heap": [ { "name": "PetstoreMock", diff --git a/pom.xml b/pom.xml index 6033c767..794b603d 100644 --- a/pom.xml +++ b/pom.xml @@ -939,6 +939,9 @@ org.apache.maven.plugins maven-failsafe-plugin 3.0.0-M4 + + ${java.surefire.options} +