From ea9ed842a0bc516a1baa0cbd5c8cd4cc901ac3c2 Mon Sep 17 00:00:00 2001 From: Sergio del Amo Date: Mon, 19 Dec 2022 15:29:22 +0100 Subject: [PATCH 01/13] feat: Add HTTP Server TCK Adds a new module `http-server-tck` This module contains multiple tests for a Micronaut HTTP Server. Those tests were ported from the AWS Module. It has an API `ServerUnderTest` and `ServerUnderTestProvider` which allows registering a module, implementing the TCK easily via a service loader. It contains a test module `test-suite-http-server-tck-netty` which runs the TCK against `micronaut-http-server-netty`. --- gradle/libs.versions.toml | 1 + http-server-tck/build.gradle.kts | 28 ++ .../http/server/tck/AssertionUtils.java | 117 ++++++ .../server/tck/EmbeddedServerUnderTest.java | 87 ++++ .../tck/EmbeddedServerUnderTestProvider.java | 33 ++ .../server/tck/HttpResponseAssertion.java | 118 ++++++ .../http/server/tck/HttpServerTestSuite.java | 57 +++ .../http/server/tck/ServerUnderTest.java | 76 ++++ .../server/tck/ServerUnderTestProvider.java | 64 +++ .../tck/ServerUnderTestProviderUtils.java | 47 +++ .../http/server/tck/TestScenario.java | 133 ++++++ .../server/tck/tests/BodyArgumentTest.java | 62 +++ .../http/server/tck/tests/BodyTest.java | 171 ++++++++ .../http/server/tck/tests/ConsumesTest.java | 72 ++++ .../http/server/tck/tests/CookiesTest.java | 102 +++++ .../tck/tests/DeleteWithoutBodyTest.java | 63 +++ .../server/tck/tests/ErrorHandlerTest.java | 380 ++++++++++++++++++ .../server/tck/tests/FilterErrorTest.java | 348 ++++++++++++++++ .../http/server/tck/tests/FiltersTest.java | 151 +++++++ .../http/server/tck/tests/FluxTest.java | 60 +++ .../http/server/tck/tests/HelloWorldTest.java | 62 +++ .../http/server/tck/tests/MiscTest.java | 284 +++++++++++++ .../http/server/tck/tests/ParameterTest.java | 61 +++ .../server/tck/tests/RemoteAddressTest.java | 89 ++++ .../server/tck/tests/ResponseStatusTest.java | 135 +++++++ .../http/server/tck/tests/StatusTest.java | 106 +++++ .../http/server/tck/tests/VersionTest.java | 69 ++++ settings.gradle | 7 +- .../build.gradle.kts | 8 + .../netty/tests/NettyHttpServerTestSuite.java | 6 + ...ut.http.server.tck.ServerUnderTestProvider | 1 + .../src/test/resources/logback.xml | 11 + test-suite-javax-inject/build.gradle | 1 - 33 files changed, 3008 insertions(+), 2 deletions(-) create mode 100644 http-server-tck/build.gradle.kts create mode 100644 http-server-tck/src/main/java/io/micronaut/http/server/tck/AssertionUtils.java create mode 100644 http-server-tck/src/main/java/io/micronaut/http/server/tck/EmbeddedServerUnderTest.java create mode 100644 http-server-tck/src/main/java/io/micronaut/http/server/tck/EmbeddedServerUnderTestProvider.java create mode 100644 http-server-tck/src/main/java/io/micronaut/http/server/tck/HttpResponseAssertion.java create mode 100644 http-server-tck/src/main/java/io/micronaut/http/server/tck/HttpServerTestSuite.java create mode 100644 http-server-tck/src/main/java/io/micronaut/http/server/tck/ServerUnderTest.java create mode 100644 http-server-tck/src/main/java/io/micronaut/http/server/tck/ServerUnderTestProvider.java create mode 100644 http-server-tck/src/main/java/io/micronaut/http/server/tck/ServerUnderTestProviderUtils.java create mode 100644 http-server-tck/src/main/java/io/micronaut/http/server/tck/TestScenario.java create mode 100644 http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/BodyArgumentTest.java create mode 100644 http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/BodyTest.java create mode 100644 http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ConsumesTest.java create mode 100644 http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/CookiesTest.java create mode 100644 http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/DeleteWithoutBodyTest.java create mode 100644 http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ErrorHandlerTest.java create mode 100644 http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FilterErrorTest.java create mode 100644 http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FiltersTest.java create mode 100644 http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FluxTest.java create mode 100644 http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/HelloWorldTest.java create mode 100644 http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/MiscTest.java create mode 100644 http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ParameterTest.java create mode 100644 http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/RemoteAddressTest.java create mode 100644 http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ResponseStatusTest.java create mode 100644 http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/StatusTest.java create mode 100644 http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/VersionTest.java create mode 100644 test-suite-http-server-tck-netty/build.gradle.kts create mode 100644 test-suite-http-server-tck-netty/src/test/java/io/micronaut/http/server/tck/netty/tests/NettyHttpServerTestSuite.java create mode 100644 test-suite-http-server-tck-netty/src/test/resources/META-INF/services/io.micronaut.http.server.tck.ServerUnderTestProvider create mode 100644 test-suite-http-server-tck-netty/src/test/resources/logback.xml diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index de2a09d00fd..a55abfd368c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -401,6 +401,7 @@ jsr107 = { module = "org.jsr107.ri:cache-ri-impl", version.ref = "jsr107" } junit-jupiter-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "managed-junit5" } junit-jupiter-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "managed-junit5" } +junit-jupiter-params = { module = "org.junit.jupiter:junit-jupiter-params", version.ref = "managed-junit5" } junit-vintage = { module = "org.junit.vintage:junit-vintage-engine", version.ref = "managed-junit5" } jetbrains-annotations = { module = "org.jetbrains:annotations", version.ref = "jetbrains-annotations" } diff --git a/http-server-tck/build.gradle.kts b/http-server-tck/build.gradle.kts new file mode 100644 index 00000000000..f8a0ff7a35b --- /dev/null +++ b/http-server-tck/build.gradle.kts @@ -0,0 +1,28 @@ +plugins { + id("io.micronaut.build.internal.convention-library") +} +repositories { + mavenCentral() +} + +dependencies { + annotationProcessor(projects.injectJava) + annotationProcessor(projects.validation) + implementation(projects.validation) + implementation(projects.runtime) + implementation(projects.inject) + api(projects.httpServer) + api(libs.junit.jupiter.api) + api(libs.junit.jupiter.params) + api(libs.managed.reactor) +} + +java { + sourceCompatibility = JavaVersion.toVersion("1.8") + targetCompatibility = JavaVersion.toVersion("1.8") +} +micronautBuild { + binaryCompatibility { + enabled.set(false) + } +} diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/AssertionUtils.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/AssertionUtils.java new file mode 100644 index 00000000000..63703937765 --- /dev/null +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/AssertionUtils.java @@ -0,0 +1,117 @@ +/* + * Copyright 2017-2022 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.http.server.tck; + +import io.micronaut.core.annotation.NonNull; +import io.micronaut.core.annotation.Nullable; +import io.micronaut.http.HttpHeaders; +import io.micronaut.http.HttpRequest; +import io.micronaut.http.HttpResponse; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.client.exceptions.HttpClientResponseException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.function.Executable; +import org.junit.jupiter.api.function.ThrowingSupplier; + +import java.util.Map; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Utility class used to perform assertions. + * @author Sergio del Amo + * @since 3.8.0 + */ +public final class AssertionUtils { + + private AssertionUtils() { + + } + + public static void assertThrows(@NonNull ServerUnderTest server, + @NonNull HttpRequest request, + @NonNull HttpResponseAssertion assertion) { + assertThrows(server, request, assertion.getHttpStatus(), assertion.getBody(), assertion.getHeaders()); + } + + public static void assertThrows(@NonNull ServerUnderTest server, + @NonNull HttpRequest request, + @NonNull HttpStatus expectedStatus, + @Nullable String expectedBody, + @Nullable Map expectedHeaders) { + Executable e = expectedBody != null ? + () -> server.exchange(request, String.class) : + () -> server.exchange(request); + HttpClientResponseException thrown = Assertions.assertThrows(HttpClientResponseException.class, e); + HttpResponse response = thrown.getResponse(); + assertEquals(expectedStatus, response.getStatus()); + assertHeaders(response, expectedHeaders); + assertBody(response, expectedBody); + } + + public static void assertDoesNotThrow(@NonNull ServerUnderTest server, + @NonNull HttpRequest request, + @NonNull HttpResponseAssertion assertion) { + assertDoesNotThrow(server, request, assertion.getHttpStatus(), assertion.getBody(), assertion.getHeaders()); + } + + public static void assertDoesNotThrow(@NonNull ServerUnderTest server, + @NonNull HttpRequest request, + @NonNull HttpStatus expectedStatus, + @Nullable String expectedBody, + @Nullable Map expectedHeaders) { + ThrowingSupplier> executable = expectedBody != null ? + () -> server.exchange(request, String.class) : + () -> server.exchange(request); + HttpResponse response = Assertions.assertDoesNotThrow(executable); + assertEquals(expectedStatus, response.getStatus()); + assertHeaders(response, expectedHeaders); + assertBody(response, expectedBody); + } + + private static void assertBody(@NonNull HttpResponse response, @Nullable String expectedBody) { + if (expectedBody != null) { + Optional bodyOptional = response.getBody(String.class); + assertTrue(bodyOptional.isPresent()); + assertTrue(bodyOptional.get().contains(expectedBody)); + } + } + + private static void assertHeaders(@NonNull HttpResponse response, @Nullable Map expectedHeaders) { + + if (expectedHeaders != null) { + for (String headerName : expectedHeaders.keySet()) { + Optional headerOptional = response.getHeaders().getFirst(headerName); + assertTrue(headerOptional.isPresent(), () -> "Header " + headerName + " not present"); + String headerValue = headerOptional.get(); + String expectedValue = expectedHeaders.get(headerName); + if (headerName.equals(HttpHeaders.CONTENT_TYPE)) { + if (headerValue.contains(";charset=")) { + assertTrue(headerValue.startsWith(expectedValue), () -> "header value " + headerValue + " does not start with " + expectedValue); + } else { + assertEquals(expectedValue, headerOptional.get()); + } + } else { + assertEquals(expectedValue, headerOptional.get()); + } + + } + } + } + +} diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/EmbeddedServerUnderTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/EmbeddedServerUnderTest.java new file mode 100644 index 00000000000..22d8fda9bde --- /dev/null +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/EmbeddedServerUnderTest.java @@ -0,0 +1,87 @@ +/* + * Copyright 2017-2022 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.http.server.tck; + +import io.micronaut.context.ApplicationContext; +import io.micronaut.core.annotation.NonNull; +import io.micronaut.core.type.Argument; +import io.micronaut.http.HttpRequest; +import io.micronaut.http.HttpResponse; +import io.micronaut.http.client.BlockingHttpClient; +import io.micronaut.http.client.HttpClient; +import io.micronaut.runtime.server.EmbeddedServer; + +import java.io.IOException; +import java.util.Map; +import java.util.Optional; + +/** + * {@link ServerUnderTest} implementation for {@link EmbeddedServer}. + * @author Sergio del Amo + * @since 3.0.0 + */ +public class EmbeddedServerUnderTest implements ServerUnderTest { + + private EmbeddedServer embeddedServer; + private HttpClient httpClient; + private BlockingHttpClient client; + + public EmbeddedServerUnderTest(@NonNull Map properties) { + this.embeddedServer = ApplicationContext.run(EmbeddedServer.class, properties); + } + + @Override + public HttpResponse exchange(HttpRequest request, Argument bodyType) { + return getBlockingHttpClient().exchange(request, bodyType); + } + + @Override + public ApplicationContext getApplicationContext() { + return embeddedServer.getApplicationContext(); + } + + @Override + public void close() throws IOException { + if (httpClient != null) { + httpClient.close(); + } + if (embeddedServer != null) { + embeddedServer.close(); + } + } + + @Override + @NonNull + public Optional getPort() { + return Optional.ofNullable(embeddedServer).map(EmbeddedServer::getPort); + } + + @NonNull + private HttpClient getHttpClient() { + if (httpClient == null) { + this.httpClient = getApplicationContext().createBean(HttpClient.class, embeddedServer.getURL()); + } + return httpClient; + } + + @NonNull + private BlockingHttpClient getBlockingHttpClient() { + if (client == null) { + this.client = getHttpClient().toBlocking(); + } + return client; + } +} diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/EmbeddedServerUnderTestProvider.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/EmbeddedServerUnderTestProvider.java new file mode 100644 index 00000000000..8f69830bc78 --- /dev/null +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/EmbeddedServerUnderTestProvider.java @@ -0,0 +1,33 @@ +/* + * Copyright 2017-2022 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.http.server.tck; + +import io.micronaut.core.annotation.NonNull; + +import java.util.Map; + +/** + * {@link ServerUnderTestProvider} implemntation which returns an instance of {@link EmbeddedServerUnderTest}. + * @author Sergio del Amo + * @since 3.8.0 + */ +public class EmbeddedServerUnderTestProvider implements ServerUnderTestProvider { + @Override + @NonNull + public ServerUnderTest getServer(@NonNull Map properties) { + return new EmbeddedServerUnderTest(properties); + } +} diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/HttpResponseAssertion.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/HttpResponseAssertion.java new file mode 100644 index 00000000000..88d213636a3 --- /dev/null +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/HttpResponseAssertion.java @@ -0,0 +1,118 @@ +/* + * Copyright 2017-2022 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.http.server.tck; + +import io.micronaut.http.HttpStatus; + +import java.util.Map; +import java.util.Objects; + +/** + * Utility class to verify assertions given an HTTP Response. + * @author Sergio del Amo + * @since 3.8.0 + */ +public final class HttpResponseAssertion { + + private final HttpStatus httpStatus; + private final Map headers; + private final String body; + + private HttpResponseAssertion(HttpStatus httpStatus, Map headers, String body) { + this.httpStatus = httpStatus; + this.headers = headers; + this.body = body; + } + + /** + * + * @return Expected HTTP Response Status + */ + public HttpStatus getHttpStatus() { + return httpStatus; + } + + /** + * + * @return Expected HTTP Response Headers + */ + public Map getHeaders() { + return headers; + } + + /** + * + * @return Expected HTTP Response body + */ + public String getBody() { + return body; + } + + /** + * + * @return Creates an instance of {@link HttpResponseAssertion.Builder}. + */ + public static HttpResponseAssertion.Builder builder() { + return new HttpResponseAssertion.Builder(); + } + + /** + * HTTP Response Assertion Builder. + */ + public static class Builder { + private HttpStatus httpStatus; + private Map headers; + private String body; + + /** + * + * @param headers HTTP Headers + * @return HTTP Response Assertion Builder + */ + public Builder headers(Map headers) { + this.headers = headers; + return this; + } + + /** + * + * @param body Response Body + * @return HTTP Response Assertion Builder + */ + public Builder body(String body) { + this.body = body; + return this; + } + + /** + * + * @param httpStatus Response's HTTP Status + * @return HTTP Response Assertion Builder + */ + public Builder status(HttpStatus httpStatus) { + this.httpStatus = httpStatus; + return this; + } + + /** + * + * @return HTTP Response Assertion + */ + public HttpResponseAssertion build() { + return new HttpResponseAssertion(Objects.requireNonNull(httpStatus), headers, body); + } + } +} diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/HttpServerTestSuite.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/HttpServerTestSuite.java new file mode 100644 index 00000000000..2cb35b88055 --- /dev/null +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/HttpServerTestSuite.java @@ -0,0 +1,57 @@ +/* + * Copyright 2017-2022 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.http.server.tck; + +import io.micronaut.http.server.tck.tests.BodyArgumentTest; +import io.micronaut.http.server.tck.tests.BodyTest; +import io.micronaut.http.server.tck.tests.ConsumesTest; +import io.micronaut.http.server.tck.tests.CookiesTest; +import io.micronaut.http.server.tck.tests.DeleteWithoutBodyTest; +import io.micronaut.http.server.tck.tests.ErrorHandlerTest; +import io.micronaut.http.server.tck.tests.FilterErrorTest; +import io.micronaut.http.server.tck.tests.FiltersTest; +import io.micronaut.http.server.tck.tests.FluxTest; +import io.micronaut.http.server.tck.tests.HelloWorldTest; +import io.micronaut.http.server.tck.tests.MiscTest; +import io.micronaut.http.server.tck.tests.ParameterTest; +import io.micronaut.http.server.tck.tests.RemoteAddressTest; +import io.micronaut.http.server.tck.tests.ResponseStatusTest; +import io.micronaut.http.server.tck.tests.StatusTest; +import io.micronaut.http.server.tck.tests.VersionTest; + +/** + * Http Server TCK Test Suite. + * @author Sergio del Amo + * @since 3.8.0 + */ +public interface HttpServerTestSuite extends + BodyArgumentTest, + ConsumesTest, + ParameterTest, + StatusTest, + ResponseStatusTest, + VersionTest, + FluxTest, + CookiesTest, + FiltersTest, + MiscTest, + DeleteWithoutBodyTest, + RemoteAddressTest, + ErrorHandlerTest, + FilterErrorTest, + BodyTest, + HelloWorldTest { +} diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/ServerUnderTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/ServerUnderTest.java new file mode 100644 index 00000000000..0712fc9633b --- /dev/null +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/ServerUnderTest.java @@ -0,0 +1,76 @@ +/* + * Copyright 2017-2022 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.http.server.tck; + +import io.micronaut.context.ApplicationContextProvider; +import io.micronaut.core.annotation.Experimental; +import io.micronaut.core.annotation.NonNull; +import io.micronaut.core.type.Argument; +import io.micronaut.http.HttpRequest; +import io.micronaut.http.HttpResponse; + +import java.io.Closeable; +import java.util.Optional; + +/** + * An API for a Micronaut HTTP Server under test. An implementation can be Netty or AWS Lambda Handler. + * @author Sergio del Amo + * @since 1.8.0 + */ +@Experimental +public interface ServerUnderTest extends ApplicationContextProvider, Closeable, AutoCloseable { + + /* + * Perform an HTTP request for the given request against the server under test and returns the the full HTTP response + * @param request The {@link HttpRequest} to execute + * @param The request body type + * @param The response body type + * @return The full {@link HttpResponse} object + * @throws HttpClientResponseException when an error status is returned + */ + default HttpResponse exchange(HttpRequest request) { + return exchange(request, (Argument) null); + } + + /* + * Perform an HTTP request for the given request against the server under test and returns the full HTTP response + * @param request The {@link HttpRequest} to execute + * @param bodyType The body type + * @param The request body type + * @param The response body type + * @return The full {@link HttpResponse} object + * @throws HttpClientResponseException when an error status is returned + */ + default HttpResponse exchange(HttpRequest request, Class bodyType) { + return exchange(request, Argument.of(bodyType)); + } + + /* + * Perform an HTTP request for the given request against the server under test and returns the full HTTP response + * @param request The {@link HttpRequest} to execute + * @param bodyType The body type + * @param The request body type + * @param The response body type + * @return The full {@link HttpResponse} object + * @throws HttpClientResponseException when an error status is returned + */ + HttpResponse exchange(HttpRequest request, Argument bodyType); + + @NonNull + default Optional getPort() { + return Optional.empty(); + } +} diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/ServerUnderTestProvider.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/ServerUnderTestProvider.java new file mode 100644 index 00000000000..df0b39b1a5e --- /dev/null +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/ServerUnderTestProvider.java @@ -0,0 +1,64 @@ +/* + * Copyright 2017-2022 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.http.server.tck; + +import io.micronaut.core.annotation.NonNull; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * Provides a server to test. + * @author Sergio del Amo + * @since 3.8.0 + */ +@FunctionalInterface +public interface ServerUnderTestProvider { + + /** + * + * @param properties Properties supplied to application context started. + * @return The server under test. + */ + @NonNull + ServerUnderTest getServer(Map properties); + + /** + * + * @param specName value of {@literal spec.name} property used to avoid bean pollution. + * @param properties Properties supplied to application context started. + * @return Server under test + */ + @NonNull + default ServerUnderTest getServer(String specName, Map properties) { + Map props = properties != null ? new HashMap<>(properties) : new HashMap<>(); + if (specName != null) { + props.put("spec.name", specName); + } + return getServer(props); + } + + /** + * + * @param specName value of {@literal spec.name} property used to avoid bean pollution. + * @return Server under test + */ + @NonNull + default ServerUnderTest getServer(String specName) { + return getServer(specName, Collections.emptyMap()); + } +} diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/ServerUnderTestProviderUtils.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/ServerUnderTestProviderUtils.java new file mode 100644 index 00000000000..4a0b25d9dab --- /dev/null +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/ServerUnderTestProviderUtils.java @@ -0,0 +1,47 @@ +/* + * Copyright 2017-2022 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.http.server.tck; + +import io.micronaut.context.exceptions.ConfigurationException; +import io.micronaut.core.annotation.NonNull; + +import java.util.Iterator; +import java.util.ServiceLoader; + +/** + * Utility class to retrieve a {@link ServerUnderTestProvider} via a Service Loader. + * @author Sergio del Amo + * @since 3.8.0 + */ +public final class ServerUnderTestProviderUtils { + + private ServerUnderTestProviderUtils() { + } + + /** + * + * @return The first {@link ServerUnderTestProvider} loaded via a Service loader. + * @throws ConfigurationException if it cannot load any {@link ServerUnderTestProvider}. + */ + @NonNull + public static ServerUnderTestProvider getServerUnderTestProvider() { + Iterator it = ServiceLoader.load(ServerUnderTestProvider.class).iterator(); + if (it.hasNext()) { + return it.next(); + } + throw new ConfigurationException("No ServiceUnderTestProvider present"); + } +} diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/TestScenario.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/TestScenario.java new file mode 100644 index 00000000000..10b8a92fc72 --- /dev/null +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/TestScenario.java @@ -0,0 +1,133 @@ +/* + * Copyright 2017-2022 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.http.server.tck; + +import io.micronaut.http.HttpRequest; +import java.io.IOException; +import java.util.Map; +import java.util.Objects; +import java.util.function.BiConsumer; + +/** + * Defines a HTTP Server Test Scenario. + * @author Sergio del Amo + * @since 3.8.0 + */ +public final class TestScenario { + private final String specName; + private final Map configuration; + + private final HttpRequest request; + private final BiConsumer> assertion; + + private TestScenario(String specName, + Map configuration, + HttpRequest request, + BiConsumer> assertion) { + this.specName = specName; + this.configuration = configuration; + this.request = request; + this.assertion = assertion; + } + + /** + * + * @return A Test Scenario builder. + */ + public static TestScenario.Builder builder() { + return new Builder(); + } + + private void run() throws IOException { + try (ServerUnderTest server = ServerUnderTestProviderUtils.getServerUnderTestProvider().getServer(specName, configuration)) { + if (assertion != null) { + assertion.accept(server, request); + } + } + } + + /** + * Test Scenario Builder. + */ + public static class Builder { + + private Map configuration; + + private String specName; + + private BiConsumer> assertion; + + private HttpRequest request; + + /** + * + * @param specName Value for {@literal spec.name} property. Used to avoid bean pollution. + * @return Test Scenario builder + */ + public Builder specName(String specName) { + this.specName = specName; + return this; + } + + /** + * + * @param request HTTP Request to be sent in the test scenario + * @return The Test Scneario Builder + */ + public Builder request(HttpRequest request) { + this.request = request; + return this; + } + + /** + * + * @param configuration Test Scenario configuration + * @return Test scenario builder + */ + public Builder configuration(Map configuration) { + this.configuration = configuration; + return this; + } + + /** + * + * @param assertion Assertion for a request and server. + * @return The Test Scenario Builder + */ + public Builder assertion(BiConsumer> assertion) { + this.assertion = assertion; + return this; + } + + /** + * + * @return Builds a Test scenario + */ + private TestScenario build() { + return new TestScenario(specName, configuration, + Objects.requireNonNull(request), + Objects.requireNonNull(assertion)); + } + + /** + * Runs the Test Scneario. + * @throws IOException Exception thrown while getting the server under test. + */ + public void run() throws IOException { + build().run(); + } + } +} diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/BodyArgumentTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/BodyArgumentTest.java new file mode 100644 index 00000000000..d2c82b41e0a --- /dev/null +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/BodyArgumentTest.java @@ -0,0 +1,62 @@ +/* + * Copyright 2017-2022 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.http.server.tck.tests; + +import io.micronaut.context.annotation.Requires; +import io.micronaut.http.HttpHeaders; +import io.micronaut.http.HttpRequest; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.MediaType; +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.Post; +import io.micronaut.http.annotation.Produces; +import io.micronaut.http.server.tck.AssertionUtils; +import io.micronaut.http.server.tck.HttpResponseAssertion; +import io.micronaut.http.server.tck.TestScenario; +import org.junit.jupiter.api.Test; +import java.io.IOException; + +@SuppressWarnings({ + "java:S5960", // We're allowed assertions, as these are used in tests only + "checkstyle:MissingJavadocType", +}) +public interface BodyArgumentTest { + /** + * @see micronaut-aws #1164 + */ + @Test + default void testBodyArguments() throws IOException { + TestScenario.builder() + .specName("BodyArgumentTest") + .request(HttpRequest.POST("/body-arguments-test/getA", "{\"a\":\"A\",\"b\":\"B\"}").header(HttpHeaders.ACCEPT, MediaType.TEXT_PLAIN)) + .assertion(((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.OK) + .body("A") + .build()))) + .run(); + } + + @Controller("/body-arguments-test") + @Requires(property = "spec.name", value = "BodyArgumentTest") + static class BodyController { + + @Post(uri = "/getA") + @Produces(MediaType.TEXT_PLAIN) + String getA(String a) { + return a; + } + } +} diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/BodyTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/BodyTest.java new file mode 100644 index 00000000000..69fb49129bc --- /dev/null +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/BodyTest.java @@ -0,0 +1,171 @@ +/* + * Copyright 2017-2022 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.http.server.tck.tests; + +import io.micronaut.context.annotation.Requires; +import io.micronaut.core.async.annotation.SingleResult; +import io.micronaut.http.HttpHeaders; +import io.micronaut.http.HttpRequest; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.MediaType; +import io.micronaut.http.annotation.Body; +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.Post; +import io.micronaut.http.annotation.Status; +import io.micronaut.http.server.tck.AssertionUtils; +import io.micronaut.http.server.tck.HttpResponseAssertion; +import io.micronaut.http.server.tck.TestScenario; +import org.junit.jupiter.api.Test; +import org.reactivestreams.Publisher; + +import java.io.IOException; +import java.util.Objects; + +@SuppressWarnings({ + "java:S5960", // We're allowed assertions, as these are used in tests only + "checkstyle:MissingJavadocType", +}) +public interface BodyTest { + + @Test + default void testCustomBodyPOJO() throws IOException { + TestScenario.builder() + .specName("BodyTest") + .request(HttpRequest.POST("/response-body/pojo", "{\"x\":10,\"y\":20}") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) + .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, + HttpResponseAssertion.builder() + .status(HttpStatus.CREATED) + .body("{\"x\":10,\"y\":20}") + .build())) + .run(); + } + + @Test + default void testCustomBodyPOJODefaultToJSON() throws IOException { + TestScenario.builder() + .specName("BodyTest") + .request(HttpRequest.POST("/response-body/pojo", "{\"x\":10,\"y\":20}")) + .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, + HttpResponseAssertion.builder() + .status(HttpStatus.CREATED) + .body("{\"x\":10,\"y\":20}") + .build())); + } + + @Test + default void testCustomBodyPOJOWithWholeRequest() throws IOException { + TestScenario.builder() + .specName("BodyTest") + .request(HttpRequest.POST("/response-body/pojo-and-request", "{\"x\":10,\"y\":20}") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) + .assertion((server, request) -> HttpResponseAssertion.builder() + .status(HttpStatus.CREATED) + .body("{\"x\":10,\"y\":20}") + .build()) + .run(); + } + + @Test + default void testCustomBodyPOJOReactiveTypes() throws IOException { + TestScenario.builder() + .specName("BodyTest") + .request(HttpRequest.POST("/response-body/pojo-reactive", "{\"x\":10,\"y\":20}") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) + .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, + HttpResponseAssertion.builder() + .status(HttpStatus.CREATED) + .body("{\"x\":10,\"y\":20}") + .build())) + .run(); + } + + @Controller("/response-body") + @Requires(property = "spec.name", value = "BodyTest") + class BodyController { + + @Post(uri = "/pojo") + @Status(HttpStatus.CREATED) + Point post(@Body Point data) { + return data; + } + + @Post(uri = "/pojo-and-request") + @Status(HttpStatus.CREATED) + Point postRequest(HttpRequest request) { + return request.getBody().orElse(null); + } + + @Post(uri = "/pojo-reactive") + @Status(HttpStatus.CREATED) + @SingleResult + Publisher post(@Body Publisher data) { + return data; + } + + @Post(uri = "/bytes", consumes = MediaType.TEXT_PLAIN) + @Status(HttpStatus.CREATED) + String postBytes(@Body byte[] bytes) { + return new String(bytes); + } + } + + class Point { + private Integer x; + private Integer y; + + public Integer getX() { + return x; + } + + public void setX(Integer x) { + this.x = x; + } + + public Integer getY() { + return y; + } + + public void setY(Integer y) { + this.y = y; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + Point point = (Point) o; + + if (!Objects.equals(x, point.x)) { + return false; + } + return Objects.equals(y, point.y); + } + + @Override + public int hashCode() { + int result = x != null ? x.hashCode() : 0; + result = 31 * result + (y != null ? y.hashCode() : 0); + return result; + } + } + +} diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ConsumesTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ConsumesTest.java new file mode 100644 index 00000000000..ec99867d939 --- /dev/null +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ConsumesTest.java @@ -0,0 +1,72 @@ +/* + * Copyright 2017-2022 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.http.server.tck.tests; + +import io.micronaut.context.annotation.Requires; +import io.micronaut.http.HttpHeaders; +import io.micronaut.http.HttpRequest; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.MediaType; +import io.micronaut.http.annotation.Body; +import io.micronaut.http.annotation.Consumes; +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.Post; +import io.micronaut.http.server.tck.AssertionUtils; +import io.micronaut.http.server.tck.HttpResponseAssertion; +import io.micronaut.http.server.tck.TestScenario; +import org.junit.jupiter.api.Test; +import java.io.IOException; + +@SuppressWarnings({ + "java:S5960", // We're allowed assertions, as these are used in tests only + "checkstyle:MissingJavadocType", +}) +public interface ConsumesTest { + @Test + default void testMultipleConsumesDefinition() throws IOException { + TestScenario.builder() + .specName("ConsumesTest") + .request(HttpRequest.POST("/consumes-test", "{\"name\":\"Fred\"}").header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) + .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.OK) + .body("{\"name\":\"Fred\"}") + .build())) + .run(); + } + + @Controller("/consumes-test") + @Requires(property = "spec.name", value = "ConsumesTest") + class ConsumesController { + + @Post("/") + @Consumes({MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_JSON}) + Pojo save(@Body Pojo pojo) { + return pojo; + } + } + + class Pojo { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } +} diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/CookiesTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/CookiesTest.java new file mode 100644 index 00000000000..9b119af8745 --- /dev/null +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/CookiesTest.java @@ -0,0 +1,102 @@ +/* + * Copyright 2017-2022 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.http.server.tck.tests; + +import io.micronaut.context.annotation.Requires; +import io.micronaut.core.util.CollectionUtils; +import io.micronaut.http.HttpRequest; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.CookieValue; +import io.micronaut.http.annotation.Get; +import io.micronaut.http.cookie.Cookie; +import io.micronaut.http.server.tck.AssertionUtils; +import io.micronaut.http.server.tck.HttpResponseAssertion; +import io.micronaut.http.server.tck.TestScenario; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +@SuppressWarnings({ + "java:S5960", // We're allowed assertions, as these are used in tests only + "checkstyle:MissingJavadocType", +}) +public interface CookiesTest { + + @Test + default void testCookieBind() throws IOException { + TestScenario.builder() + .specName("CookiesTest") + .request(HttpRequest.GET("/cookies-test/bind") + .cookie(Cookie.of("one", "foo")) + .cookie(Cookie.of("two", "bar"))) + .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.OK) + .body("{\"one\":\"foo\",\"two\":\"bar\"}") + .build())) + .run(); + } + + @Test + default void testGetCookiesMethod() throws IOException { + TestScenario.builder() + .specName("CookiesTest") + .request(HttpRequest.GET("/cookies-test/all") + .cookie(Cookie.of("one", "foo")) + .cookie(Cookie.of("two", "bar"))) + .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.OK) + .body("{\"one\":\"foo\",\"two\":\"bar\"}") + .build())) + .run(); + } + + @Test + default void testNoCookie() throws IOException { + TestScenario.builder() + .specName("CookiesTest") + .request(HttpRequest.GET("/cookies-test/all")) + .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.OK) + .body("{}") + .build())) + .run(); + } + + @Controller("/cookies-test") + @Requires(property = "spec.name", value = "CookiesTest") + class CookieController { + + @Get(uri = "/all") + Map all(HttpRequest request) { + Map map = new HashMap<>(); + for (String cookieName : request.getCookies().names()) { + map.put(cookieName, request.getCookies().get(cookieName).getValue()); + } + return map; + } + + @Get(uri = "/bind") + Map all(@CookieValue String one, @CookieValue String two) { + return CollectionUtils.mapOf( + "one", one, + "two", two + ); + } + } +} diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/DeleteWithoutBodyTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/DeleteWithoutBodyTest.java new file mode 100644 index 00000000000..0257735c2e5 --- /dev/null +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/DeleteWithoutBodyTest.java @@ -0,0 +1,63 @@ +/* + * Copyright 2017-2022 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.http.server.tck.tests; + +import io.micronaut.context.annotation.Requires; +import io.micronaut.http.HttpHeaderValues; +import io.micronaut.http.HttpHeaders; +import io.micronaut.http.HttpRequest; +import io.micronaut.http.HttpResponse; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.Delete; +import io.micronaut.http.annotation.Header; +import io.micronaut.http.annotation.PathVariable; +import io.micronaut.http.annotation.Status; +import io.micronaut.http.server.tck.TestScenario; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; + +@SuppressWarnings({ + "java:S5960", // We're allowed assertions, as these are used in tests only + "checkstyle:MissingJavadocType", +}) +public interface DeleteWithoutBodyTest { + @Test + default void verifiesItIsPossibleToExposesADeleteEndpointWhichIsInvokedWithoutABody() throws IOException { + TestScenario.builder() + .specName("DeleteWithoutBodyTest") + .request(HttpRequest.DELETE("/sessions/sergio").header(HttpHeaders.AUTHORIZATION, HttpHeaderValues.AUTHORIZATION_PREFIX_BEARER + " xxx")) + .assertion((server, request) -> { + HttpResponse response = assertDoesNotThrow(() -> server.exchange(request)); + assertEquals(HttpStatus.OK, response.getStatus()); + }) + .run(); + } + + @Requires(property = "spec.name", value = "DeleteWithoutBodyTest") + @Controller("/sessions") + class SessionsController { + @Status(HttpStatus.OK) + @Delete("/{username}") + void delete(@PathVariable String username, + @Header(HttpHeaders.AUTHORIZATION) String authorization) { + } + } +} diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ErrorHandlerTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ErrorHandlerTest.java new file mode 100644 index 00000000000..89255ed40e3 --- /dev/null +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ErrorHandlerTest.java @@ -0,0 +1,380 @@ +/* + * Copyright 2017-2022 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.http.server.tck.tests; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.micronaut.context.annotation.Requires; +import io.micronaut.core.annotation.Introspected; +import io.micronaut.core.util.CollectionUtils; +import io.micronaut.core.util.StringUtils; +import io.micronaut.http.HttpHeaders; +import io.micronaut.http.HttpRequest; +import io.micronaut.http.HttpResponse; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.MediaType; +import io.micronaut.http.annotation.Body; +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.Error; +import io.micronaut.http.annotation.Get; +import io.micronaut.http.annotation.Post; +import io.micronaut.http.annotation.Produces; +import io.micronaut.http.annotation.Status; +import io.micronaut.http.codec.CodecException; +import io.micronaut.http.hateoas.JsonError; +import io.micronaut.http.hateoas.Link; +import io.micronaut.http.server.exceptions.ExceptionHandler; +import io.micronaut.http.server.tck.AssertionUtils; +import io.micronaut.http.server.tck.HttpResponseAssertion; +import io.micronaut.http.server.tck.ServerUnderTest; +import io.micronaut.http.server.tck.ServerUnderTestProviderUtils; +import io.micronaut.http.server.tck.TestScenario; +import jakarta.inject.Singleton; +import org.junit.jupiter.api.Test; + +import javax.validation.Valid; +import javax.validation.constraints.Min; +import java.io.IOException; +import java.util.Collections; +import java.util.Map; + + +@SuppressWarnings({ + "java:S5960", // We're allowed assertions, as these are used in tests only + "checkstyle:MissingJavadocType", +}) + +public interface ErrorHandlerTest { + @Test + default void testCustomGlobalExceptionHandlersDeclaredInController() throws IOException { + TestScenario.builder() + .configuration(CollectionUtils.mapOf( + "micronaut.server.cors.configurations.web.allowedOrigins", Collections.singletonList("http://localhost:8080"), + "micronaut.server.cors.enabled", StringUtils.TRUE + )) + .specName("ErrorHandlerTest") + .request(HttpRequest.GET("/errors/global-ctrl") + .header(HttpHeaders.CONTENT_TYPE, io.micronaut.http.MediaType.APPLICATION_JSON)) + .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, + HttpStatus.OK, + "bad things happens globally", + Collections.singletonMap(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN))) + .run(); + } + + @Test + default void testCustomGlobalExceptionHandlers() throws IOException { + TestScenario.builder() + .configuration(CollectionUtils.mapOf( + "micronaut.server.cors.configurations.web.allowedOrigins", Collections.singletonList("http://localhost:8080"), + "micronaut.server.cors.enabled", StringUtils.TRUE + )) + .specName("ErrorHandlerTest") + .request(HttpRequest.GET("/errors/global") + .header(HttpHeaders.CONTENT_TYPE, io.micronaut.http.MediaType.APPLICATION_JSON)) + .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, + HttpStatus.OK, + "Exception Handled", + Collections.singletonMap(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN))) + .run(); + } + + @Test + default void testCustomGlobalExceptionHandlersForPOSTWithBody() throws IOException { + Map configuration = CollectionUtils.mapOf( + "micronaut.server.cors.configurations.web.allowedOrigins", Collections.singletonList("http://localhost:8080"), + "micronaut.server.cors.enabled", StringUtils.TRUE + ); + try (ServerUnderTest server = ServerUnderTestProviderUtils.getServerUnderTestProvider().getServer("ErrorHandlerTest", configuration)) { + ObjectMapper objectMapper = server.getApplicationContext().getBean(ObjectMapper.class); + HttpRequest request = HttpRequest.POST("/json/errors/global", objectMapper.writeValueAsString(new RequestObject(101))) + .header(HttpHeaders.CONTENT_TYPE, io.micronaut.http.MediaType.APPLICATION_JSON); + AssertionUtils.assertDoesNotThrow(server, request, + HttpStatus.OK, + "{\"message\":\"Error: bad things when post and body in request\",\"", + Collections.singletonMap(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)); + } + } + + @Test + default void testCustomGlobalStatusHandlersDeclaredInController() throws IOException { + TestScenario.builder() + .configuration(CollectionUtils.mapOf( + "micronaut.server.cors.configurations.web.allowedOrigins", Collections.singletonList("http://localhost:8080"), + "micronaut.server.cors.enabled", StringUtils.TRUE + )) + .specName("ErrorHandlerTest") + .request(HttpRequest.GET("/errors/global-status-ctrl")) + .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, + HttpStatus.OK, + "global status", + Collections.singletonMap(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN))) + .run(); + } + + @Test + default void testLocalExceptionHandlers() throws IOException { + TestScenario.builder() + .configuration(CollectionUtils.mapOf( + "micronaut.server.cors.configurations.web.allowedOrigins", Collections.singletonList("http://localhost:8080"), + "micronaut.server.cors.enabled", StringUtils.TRUE)) + .specName("ErrorHandlerTest") + .request(HttpRequest.GET("/errors/local")) + .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, + HttpStatus.OK, + "bad things", + Collections.singletonMap(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN))) + .run(); + } + + @Test + default void jsonMessageFormatErrorsReturn400() throws IOException { + TestScenario.builder() + .configuration(CollectionUtils.mapOf( + "micronaut.server.cors.configurations.web.allowedOrigins", Collections.singletonList("http://localhost:8080"), + "micronaut.server.cors.enabled", StringUtils.TRUE + )).specName("ErrorHandlerTest") + .request(HttpRequest.POST("/json/jsonBody", "{\"numberField\": \"textInsteadOfNumber\"}")) + .assertion((server, request) -> AssertionUtils.assertThrows(server, request, + HttpResponseAssertion.builder() + .status(HttpStatus.BAD_REQUEST) + .headers(Collections.singletonMap(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) + .build() + )).run(); + } + + @Test + default void corsHeadersArePresentAfterFailedDeserialisationWhenErrorHandlerIsUsed() throws IOException { + TestScenario.builder() + .configuration(CollectionUtils.mapOf( + "micronaut.server.cors.configurations.web.allowedOrigins", Collections.singletonList("http://localhost:8080"), + "micronaut.server.cors.enabled", StringUtils.TRUE + )).specName("ErrorHandlerTest") + .request(HttpRequest.POST("/json/errors/global", "{\"numberField\": \"string is not a number\"}") + .header(HttpHeaders.ORIGIN, "http://localhost:8080")) + .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.OK) + .headers(Collections.singletonMap(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "http://localhost:8080")) + .build())) + .run(); + } + + @Test + default void corsHeadersArePresentAfterFailedDeserialisation() throws IOException { + TestScenario.builder() + .configuration(CollectionUtils.mapOf( + "micronaut.server.cors.configurations.web.allowedOrigins", Collections.singletonList("http://localhost:8080"), + "micronaut.server.cors.enabled", StringUtils.TRUE + )) + .specName("ErrorHandlerTest") + .request(HttpRequest.POST("/json/jsonBody", "{\"numberField\": \"string is not a number\"}") + .header(HttpHeaders.ORIGIN, "http://localhost:8080")) + .assertion((server, request) -> AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.BAD_REQUEST) + .headers(Collections.singletonMap(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "http://localhost:8080")) + .build())) + .run(); + } + + @Test + default void corsHeadersArePresentAfterExceptions() throws IOException { + TestScenario.builder() + .configuration(CollectionUtils.mapOf( + "micronaut.server.cors.configurations.web.allowedOrigins", Collections.singletonList("http://localhost:8080"), + "micronaut.server.cors.enabled", StringUtils.TRUE + )) + .specName("ErrorHandlerTest") + .request(HttpRequest.GET("/errors/global") + .header(HttpHeaders.ORIGIN, "http://localhost:8080")) + .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.OK) + .headers(Collections.singletonMap(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "http://localhost:8080")) + .build())) + .run(); + } + + @Test + default void messageValidationErrorsReturn400() throws IOException { + TestScenario.builder() + .configuration(CollectionUtils.mapOf( + "micronaut.server.cors.configurations.web.allowedOrigins", Collections.singletonList("http://localhost:8080"), + "micronaut.server.cors.enabled", StringUtils.TRUE + )) + .specName("ErrorHandlerTest") + .request(HttpRequest.POST("/json/jsonBody", "{\"numberField\": 0}")) + .assertion((server, request) -> AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.BAD_REQUEST) + .headers(Collections.singletonMap(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) + .build())) + .run(); + } + + @Controller("/secret") + @Requires(property = "spec.name", value = "ErrorHandlerTest") + class SecretController { + @Get + @Produces(MediaType.TEXT_PLAIN) + String index() { + return "area 51 hosts an alien"; + } + } + + @Requires(property = "spec.name", value = "ErrorHandlerTest") + @Controller("/errors") + class ErrorController { + + @Get("/global") + String globalHandler() { + throw new MyException("bad things"); + } + + @Get("/global-ctrl") + String globalControllerHandler() throws GloballyHandledException { + throw new GloballyHandledException("bad things happens globally"); + } + + @Get("/global-status-ctrl") + @Status(HttpStatus.I_AM_A_TEAPOT) + String globalControllerHandlerForStatus() { + return "original global status"; + } + + @Get("/local") + String localHandler() { + throw new AnotherException("bad things"); + } + + @Error + @Produces(io.micronaut.http.MediaType.TEXT_PLAIN) + @Status(HttpStatus.OK) + String localHandler(AnotherException throwable) { + return throwable.getMessage(); + } + } + + @Controller(value = "/json/errors", produces = io.micronaut.http.MediaType.APPLICATION_JSON) + @Requires(property = "spec.name", value = "ErrorHandlerTest") + class JsonErrorController { + + @Post("/global") + String globalHandlerPost(@Body RequestObject object) { + throw new RuntimeException("bad things when post and body in request"); + } + + @Error + HttpResponse errorHandler(HttpRequest request, RuntimeException exception) { + JsonError error = new JsonError("Error: " + exception.getMessage()) + .link(Link.SELF, Link.of(request.getUri())); + + return HttpResponse.status(HttpStatus.OK) + .body(error); + } + } + + @Introspected + class RequestObject { + @Min(1L) + private Integer numberField; + + public RequestObject(Integer numberField) { + this.numberField = numberField; + } + + public Integer getNumberField() { + return numberField; + } + } + + @Controller("/json") + @Requires(property = "spec.name", value = "ErrorHandlerTest") + class JsonController { + @Post("/jsonBody") + String jsonBody(@Valid @Body RequestObject data) { + return "blah"; + } + } + + @Controller("/global-errors") + @Requires(property = "spec.name", value = "ErrorHandlerTest") + class GlobalErrorController { + + @Error(global = true, exception = GloballyHandledException.class) + @Produces(io.micronaut.http.MediaType.TEXT_PLAIN) + @Status(HttpStatus.OK) + String globallyHandledException(GloballyHandledException throwable) { + return throwable.getMessage(); + } + + @Error(global = true, status = HttpStatus.I_AM_A_TEAPOT) + @Produces(io.micronaut.http.MediaType.TEXT_PLAIN) + @Status(HttpStatus.OK) + String globalControllerHandlerForStatus() { + return "global status"; + } + + } + + @Singleton + @Requires(property = "spec.name", value = "ErrorHandlerTest") + class CodecExceptionExceptionHandler + implements ExceptionHandler { + + @Override + public HttpResponse handle(HttpRequest request, CodecException exception) { + return HttpResponse.badRequest("Invalid JSON: " + exception.getMessage()).contentType(MediaType.APPLICATION_JSON); + } + } + + @Singleton + @Requires(property = "spec.name", value = "ErrorHandlerTest") + class RuntimeErrorHandler implements ExceptionHandler { + + @Override + public HttpResponse handle(HttpRequest request, RuntimeException exception) { + return HttpResponse.serverError("Exception: " + exception.getMessage()) + .contentType(MediaType.TEXT_PLAIN); + } + } + + @Singleton + @Requires(property = "spec.name", value = "ErrorHandlerTest") + class MyErrorHandler implements ExceptionHandler { + + @Override + public HttpResponse handle(HttpRequest request, MyException exception) { + return HttpResponse.ok("Exception Handled") + .contentType(MediaType.TEXT_PLAIN); + } + } + + + class MyException extends RuntimeException { + public MyException(String badThings) { + super(badThings); + } + } + + class AnotherException extends RuntimeException { + public AnotherException(String badThings) { + super(badThings); + } + } + + class GloballyHandledException extends Exception { + public GloballyHandledException(String badThingsHappensGlobally) { + super(badThingsHappensGlobally); + } + } +} diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FilterErrorTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FilterErrorTest.java new file mode 100644 index 00000000000..bca6fb2e0d9 --- /dev/null +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FilterErrorTest.java @@ -0,0 +1,348 @@ +/* + * Copyright 2017-2022 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.http.server.tck.tests; + +import io.micronaut.context.annotation.Requires; +import io.micronaut.context.condition.Condition; +import io.micronaut.context.condition.ConditionContext; +import io.micronaut.core.async.publisher.Publishers; +import io.micronaut.core.util.StringUtils; +import io.micronaut.http.HttpAttributes; +import io.micronaut.http.HttpHeaders; +import io.micronaut.http.HttpRequest; +import io.micronaut.http.HttpResponse; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.MediaType; +import io.micronaut.http.MutableHttpResponse; +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.Error; +import io.micronaut.http.annotation.Filter; +import io.micronaut.http.annotation.Get; +import io.micronaut.http.annotation.Status; +import io.micronaut.http.filter.HttpServerFilter; +import io.micronaut.http.filter.ServerFilterChain; +import io.micronaut.http.server.exceptions.ExceptionHandler; +import io.micronaut.http.server.tck.AssertionUtils; +import io.micronaut.http.server.tck.HttpResponseAssertion; +import io.micronaut.http.server.tck.TestScenario; +import io.micronaut.web.router.MethodBasedRouteMatch; +import io.micronaut.web.router.RouteMatch; +import jakarta.inject.Singleton; +import org.junit.jupiter.api.Test; +import org.reactivestreams.Publisher; + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@SuppressWarnings({ + "java:S5960", // We're allowed assertions, as these are used in tests only + "checkstyle:MissingJavadocType", +}) +public interface FilterErrorTest { + @Test + default void testFilterThrowingExceptionHandledByExceptionHandlerThrowingException() throws IOException { + TestScenario.builder() + .specName("FilterErrorSpec3") + .request(HttpRequest.GET("/filter-error-spec-3") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) + .assertion((server, request) -> { + AssertionUtils.assertThrows(server, request, + HttpResponseAssertion.builder() + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .body("from exception handler") + .build()); + ExceptionException filter = server.getApplicationContext().getBean(ExceptionException.class); + assertEquals(1, filter.executedCount.get()); + assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, filter.responseStatus.getAndSet(null)); + }) + .run(); + } + + @Test + default void testTheErrorRouteIsTheRouteMatch() throws IOException { + TestScenario.builder() + .request(HttpRequest.GET("/filter-error-spec-4/status").header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) + .specName("FilterErrorSpec4") + .assertion((server, request) -> { + AssertionUtils.assertDoesNotThrow(server, request, + HttpResponseAssertion.builder() + .status(HttpStatus.OK) + .build()); + ExceptionRoute filter = server.getApplicationContext().getBean(ExceptionRoute.class); + RouteMatch match = filter.routeMatch.getAndSet(null); + assertTrue(match instanceof MethodBasedRouteMatch); + assertEquals("testStatus", ((MethodBasedRouteMatch) match).getName()); + }) + .run(); + } + + @Test + default void testNonOncePerRequestFilterThrowingErrorDoesNotLoop() throws IOException { + TestScenario.builder() + .request(HttpRequest.GET("/filter-error-spec") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) + .specName("FilterErrorSpec2") + .assertion((server, request) -> { + AssertionUtils.assertThrows(server, request, + HttpResponseAssertion.builder() + .status(HttpStatus.BAD_REQUEST) + .body("from filter exception handler") + .build()); + FirstEvery filter = server.getApplicationContext().getBean(FirstEvery.class); + assertEquals(1, filter.executedCount.get()); + }).run(); + } + + @Test + default void testErrorsEmittedFromSecondFilterInteractingWithExceptionHandlers() throws IOException { + TestScenario.builder() + .request(HttpRequest.GET("/filter-error-spec"). + header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON) + .header("X-Passthru", StringUtils.TRUE)) + .specName("FilterErrorSpec") + .assertion((server, request) -> { + AssertionUtils.assertThrows(server, request, + HttpResponseAssertion.builder() + .status(HttpStatus.BAD_REQUEST) + .body("from NEXT filter exception handle").build()); + + First first = server.getApplicationContext().getBean(First.class); + Next next = server.getApplicationContext().getBean(Next.class); + + assertEquals(1, first.executedCount.get()); + assertEquals(HttpStatus.BAD_REQUEST, first.responseStatus.getAndSet(null)); + assertEquals(1, next.executedCount.get()); + }).run(); + } + + @Test + default void testErrorsEmittedFromFiltersInteractingWithExceptionHandlers() throws IOException { + TestScenario.builder() + .specName("FilterErrorSpec") + .request(HttpRequest.GET("/filter-error-spec").header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) + .assertion((server, request) -> { + AssertionUtils.assertThrows(server, request, + HttpResponseAssertion.builder() + .status(HttpStatus.BAD_REQUEST) + .body("from filter exception handler").build()); + + First first = server.getApplicationContext().getBean(First.class); + Next next = server.getApplicationContext().getBean(Next.class); + + assertEquals(1, first.executedCount.get()); + assertNull(first.responseStatus.getAndSet(null)); + assertEquals(0, next.executedCount.get()); + }) + .run(); + } + + class FilterExceptionException extends RuntimeException { + } + + class FilterException extends RuntimeException { + } + + class NextFilterException extends RuntimeException { + } + + @Requires(property = "spec.name", value = "FilterErrorSpec") + @Filter(Filter.MATCH_ALL_PATTERN) + class First implements HttpServerFilter { + AtomicInteger executedCount = new AtomicInteger(0); + AtomicReference responseStatus = new AtomicReference<>(); + + private void setResponse(MutableHttpResponse r) { + responseStatus.set(r.status()); + } + + @Override + public Publisher> doFilter(HttpRequest request, ServerFilterChain chain) { + executedCount.incrementAndGet(); + if (StringUtils.isTrue(request.getHeaders().get("X-Passthru"))) { + return Publishers.then(chain.proceed(request), this::setResponse); + } + return Publishers.just(new FilterException()); + } + + @Override + public int getOrder() { + return 10; + } + } + + @Requires(property = "spec.name", value = "FilterErrorSpec") + @Filter(Filter.MATCH_ALL_PATTERN) + static class Next implements HttpServerFilter { + AtomicInteger executedCount = new AtomicInteger(0); + + @Override + public Publisher> doFilter(HttpRequest request, ServerFilterChain chain) { + executedCount.incrementAndGet(); + return Publishers.just(new NextFilterException()); + } + + @Override + public int getOrder() { + return 20; + } + } + + @Requires(property = "spec.name", value = "FilterErrorSpec2") + @Filter(Filter.MATCH_ALL_PATTERN) + static class FirstEvery implements HttpServerFilter { + AtomicInteger executedCount = new AtomicInteger(0); + + @Override + public Publisher> doFilter(HttpRequest request, ServerFilterChain chain) { + executedCount.incrementAndGet(); + return Publishers.just(new FilterException()); + } + + @Override + public int getOrder() { + return 10; + } + } + + @Requires(property = "spec.name", value = "FilterErrorSpec3") + @Filter(Filter.MATCH_ALL_PATTERN) + class ExceptionException implements HttpServerFilter { + AtomicInteger executedCount = new AtomicInteger(0); + AtomicReference responseStatus = new AtomicReference<>(); + + private void setResponse(MutableHttpResponse r) { + responseStatus.set(r.status()); + } + + @Override + public Publisher> doFilter(HttpRequest request, ServerFilterChain chain) { + executedCount.incrementAndGet(); + return Publishers.then(chain.proceed(request), + this::setResponse); + } + + @Override + public int getOrder() { + return 10; + } + } + + @Requires(property = "spec.name", value = "FilterErrorSpec4") + @Filter(Filter.MATCH_ALL_PATTERN) + class ExceptionRoute implements HttpServerFilter { + AtomicReference> routeMatch = new AtomicReference<>(); + + @Override + public Publisher> doFilter(HttpRequest request, ServerFilterChain chain) { + return Publishers.then(chain.proceed(request), + httpResponse -> routeMatch.set(httpResponse.getAttribute(HttpAttributes.ROUTE_MATCH, RouteMatch.class).get())); + } + + @Override + public int getOrder() { + return 10; + } + } + + @Requires(condition = FilterCondition.class) + @Controller("/filter-error-spec") + class NeverReachedController { + @Get + String get() { + return "OK"; + } + } + + @Requires(condition = FilterCondition.class) + @Controller("/filter-error-spec-3") + class HandledByHandlerController { + @Get + String get() { + throw new FilterExceptionException(); + } + } + + @Requires(condition = FilterCondition.class) + @Controller("/filter-error-spec-4") + class HandledByErrorRouteController { + @Get("/exception") + String getException() { + throw new FilterExceptionException(); + } + + @Get("/status") + HttpStatus getStatus() { + return HttpStatus.NOT_FOUND; + } + + @Error(exception = FilterExceptionException.class) + @Status(HttpStatus.OK) + void testException() { + + } + + @Error(status = HttpStatus.NOT_FOUND) + @Status(HttpStatus.OK) + void testStatus() { + + } + } + + class FilterCondition implements Condition { + + @Override + public boolean matches(ConditionContext context) { + return context.getProperty("spec.name", String.class) + .map(val -> val.equals("FilterErrorSpec4") || val.equals("FilterErrorSpec3") || val.equals("FilterErrorSpec2") || val.equals("FilterErrorSpec")) + .orElse(false); + } + } + + @Requires(condition = FilterCondition.class) + @Singleton + class FilterExceptionExceptionHandler implements ExceptionHandler> { + + @Override + public HttpResponse handle(HttpRequest request, FilterExceptionException exception) { + throw new RuntimeException("from exception handler"); + } + } + + @Requires(condition = FilterCondition.class) + @Singleton + class FilterExceptionHandler implements ExceptionHandler> { + + @Override + public HttpResponse handle(HttpRequest request, FilterException exception) { + return HttpResponse.badRequest("from filter exception handler"); + } + } + + @Requires(condition = FilterCondition.class) + @Singleton + class NextFilterExceptionHandler implements ExceptionHandler> { + + @Override + public HttpResponse handle(HttpRequest request, NextFilterException exception) { + return HttpResponse.badRequest("from NEXT filter exception handler"); + } + } +} diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FiltersTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FiltersTest.java new file mode 100644 index 00000000000..7653f23e7c6 --- /dev/null +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FiltersTest.java @@ -0,0 +1,151 @@ +/* + * Copyright 2017-2022 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.http.server.tck.tests; + +import io.micronaut.context.annotation.Requires; +import io.micronaut.core.async.publisher.Publishers; +import io.micronaut.core.util.CollectionUtils; +import io.micronaut.core.util.StringUtils; +import io.micronaut.http.HttpRequest; +import io.micronaut.http.HttpResponse; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.MutableHttpResponse; +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.Filter; +import io.micronaut.http.annotation.Get; +import io.micronaut.http.annotation.Produces; +import io.micronaut.http.filter.HttpServerFilter; +import io.micronaut.http.filter.ServerFilterChain; +import io.micronaut.http.server.exceptions.ExceptionHandler; +import io.micronaut.http.server.tck.AssertionUtils; +import io.micronaut.http.server.tck.HttpResponseAssertion; +import io.micronaut.http.server.tck.ServerUnderTest; +import io.micronaut.http.server.tck.ServerUnderTestProviderUtils; +import jakarta.inject.Singleton; +import org.junit.jupiter.api.Test; +import org.reactivestreams.Publisher; + +import java.io.IOException; +import java.util.Collections; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +@SuppressWarnings({ + "java:S5960", // We're allowed assertions, as these are used in tests only + "checkstyle:MissingJavadocType", +}) +public interface FiltersTest { + + @Test + default void testFiltersAreRunCorrectly() throws IOException { + Map configuration = CollectionUtils.mapOf( + "micronaut.server.cors.enabled", StringUtils.TRUE + ); + try (ServerUnderTest server = ServerUnderTestProviderUtils.getServerUnderTestProvider().getServer("FiltersTest", configuration)) { + assertTrue(true); + HttpRequest request = HttpRequest.GET("/filter-test/ok"); + AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.OK) + .body("OK") + .headers(Collections.singletonMap("X-Test-Filter", StringUtils.TRUE)) + .build()); + } + } + + @Test + default void filtersAreAppliedOnNonMatchingMethodsCorsFilterWorks() throws IOException { + Map configuration = CollectionUtils.mapOf( + "micronaut.server.cors.enabled", StringUtils.TRUE + ); + try (ServerUnderTest server = ServerUnderTestProviderUtils.getServerUnderTestProvider().getServer("FiltersTest", configuration)) { + HttpRequest request = HttpRequest.OPTIONS("/filter-test/ok").header("Origin", "https://micronaut.io") + .header("Access-Control-Request-Method", "GET"); + AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.OK) + .headers(Collections.singletonMap("Access-Control-Allow-Origin", "https://micronaut.io")) + .build()); + } + } + + @Test + default void filtersAreAppliedOnNonMatchingMethodsCorsFilterDisableIfNotPreflight() throws IOException { + Map configuration = CollectionUtils.mapOf( + "micronaut.server.cors.enabled", StringUtils.TRUE + ); + try (ServerUnderTest server = ServerUnderTestProviderUtils.getServerUnderTestProvider().getServer("FiltersTest", configuration)) { + HttpRequest request = HttpRequest.OPTIONS("/filter-test/ok"); + AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.METHOD_NOT_ALLOWED) + .build()); + } + } + + @Test + default void testFiltersAreRunCorrectlyWithCustomExceptionHandler() throws IOException { + Map configuration = CollectionUtils.mapOf( + "micronaut.server.cors.enabled", StringUtils.TRUE + ); + try (ServerUnderTest server = ServerUnderTestProviderUtils.getServerUnderTestProvider().getServer("FiltersTest", configuration)) { + HttpRequest request = HttpRequest.GET("/filter-test/exception"); + AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.OK) + .body("Exception Handled") + .headers(Collections.singletonMap("X-Test-Filter", StringUtils.TRUE)) + .build()); + } + } + + @Controller("/filter-test") + @Requires(property = "spec.name", value = "FiltersTest") + static class TestController { + @Get("/ok") + String ok() { + return "OK"; + } + + @Get("/exception") + void exception() { + throw new CustomException(); + } + } + + @Filter("/filter-test/**") + @Requires(property = "spec.name", value = "FiltersTest") + class TestFilter implements HttpServerFilter { + @Override + public Publisher> doFilter(HttpRequest request, ServerFilterChain chain) { + return Publishers.map(chain.proceed(request), httpResponse -> { + httpResponse.getHeaders().add("X-Test-Filter", "true"); + return httpResponse; + }); + } + } + + static class CustomException extends RuntimeException { + } + + @Produces + @Singleton + @Requires(property = "spec.name", value = "FiltersTest") + class CustomExceptionHandler implements ExceptionHandler> { + @Override + public HttpResponse handle(HttpRequest request, CustomException exception) { + return HttpResponse.ok("Exception Handled"); + } + + } +} diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FluxTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FluxTest.java new file mode 100644 index 00000000000..2aad393c0fd --- /dev/null +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FluxTest.java @@ -0,0 +1,60 @@ +/* + * Copyright 2017-2022 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.http.server.tck.tests; + +import io.micronaut.context.annotation.Requires; +import io.micronaut.http.HttpRequest; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.Get; +import io.micronaut.http.server.tck.AssertionUtils; +import io.micronaut.http.server.tck.HttpResponseAssertion; +import io.micronaut.http.server.tck.TestScenario; +import org.junit.jupiter.api.Test; +import reactor.core.publisher.Flux; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.Map; + +@SuppressWarnings({ + "java:S5960", // We're allowed assertions, as these are used in tests only + "checkstyle:MissingJavadocType", +}) +public interface FluxTest { + + @Test + default void testControllerReturningAFlux() throws IOException { + TestScenario.builder() + .specName("FluxTest") + .request(HttpRequest.GET("/users")) + .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.OK) + .body("[{\"name\":\"Joe\"},{\"name\":\"Lewis\"}]") + .build())) + .run(); + } + + @Controller("/users") + @Requires(property = "spec.name", value = "FluxTest") + class UserController { + @Get + Flux> getAll() { + return Flux.fromIterable(Arrays.asList(Collections.singletonMap("name", "Joe"), Collections.singletonMap("name", "Lewis"))); + } + } +} diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/HelloWorldTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/HelloWorldTest.java new file mode 100644 index 00000000000..7694b83e481 --- /dev/null +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/HelloWorldTest.java @@ -0,0 +1,62 @@ +/* + * Copyright 2017-2022 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.http.server.tck.tests; + +import io.micronaut.context.annotation.Requires; +import io.micronaut.http.HttpHeaders; +import io.micronaut.http.HttpRequest; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.MediaType; +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.Get; +import io.micronaut.http.annotation.Produces; +import io.micronaut.http.server.tck.AssertionUtils; +import io.micronaut.http.server.tck.TestScenario; +import io.micronaut.http.uri.UriBuilder; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.Collections; + + +@SuppressWarnings({ + "java:S5960", // We're allowed assertions, as these are used in tests only + "checkstyle:MissingJavadocType", +}) +public interface HelloWorldTest { + + @Test + default void helloWorld() throws IOException { + TestScenario.builder() + .specName("HelloWorldTest") + .request(HttpRequest.GET(UriBuilder.of("/hello").path("world").build()).accept(MediaType.TEXT_PLAIN)) + .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, + HttpStatus.OK, + "Hello World", + Collections.singletonMap(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN))) + .run(); + } + + @Requires(property = "spec.name", value = "HelloWorldTest") + @Controller("/hello") + class HelloWorldController { + @Produces(MediaType.TEXT_PLAIN) + @Get("/world") + String hello() { + return "Hello World"; + } + } +} diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/MiscTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/MiscTest.java new file mode 100644 index 00000000000..2049c16d128 --- /dev/null +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/MiscTest.java @@ -0,0 +1,284 @@ +/* + * Copyright 2017-2022 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.http.server.tck.tests; + +import io.micronaut.context.annotation.Requires; +import io.micronaut.core.annotation.Introspected; +import io.micronaut.core.annotation.NonNull; +import io.micronaut.http.HttpHeaders; +import io.micronaut.http.HttpRequest; +import io.micronaut.http.HttpResponse; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.MediaType; +import io.micronaut.http.annotation.Body; +import io.micronaut.http.annotation.Consumes; +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.Get; +import io.micronaut.http.annotation.Post; +import io.micronaut.http.server.tck.AssertionUtils; +import io.micronaut.http.server.tck.HttpResponseAssertion; +import io.micronaut.http.server.tck.TestScenario; +import org.junit.jupiter.api.Test; + +import javax.validation.constraints.NotBlank; +import java.io.IOException; +import java.util.Collections; +import java.util.Map; + + +@SuppressWarnings({ + "java:S5960", // We're allowed assertions, as these are used in tests only + "checkstyle:MissingJavadocType", +}) +public interface MiscTest { + /** + * + * @see micronaut-aws #868 + */ + @Test + default void testSelectedRouteReflectsAcceptHeader() throws IOException { + TestScenario.builder() + .specName("MiscTest") + .request(HttpRequest.GET("/bar/ok").header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON)) + .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.OK) + .body("{\"status\":\"ok\"}") + .build())) + .run(); + + TestScenario.builder() + .specName("MiscTest") + .request(HttpRequest.GET("/bar/ok").header(HttpHeaders.ACCEPT, MediaType.TEXT_HTML)) + .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, + HttpResponseAssertion.builder() + .status(HttpStatus.OK) + .body("
ok
") + .build())) + .run(); + } + + @Test + default void testBehaviourOf404() throws IOException { + TestScenario.builder() + .specName("MiscTest") + .request(HttpRequest.GET("/does-not-exist").header("Accept", MediaType.APPLICATION_JSON)) + .assertion((server, request) -> AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.NOT_FOUND) + .build())) + .run(); + } + + @Test + default void postFormUrlEncodedBodyBindingToPojoWorks() throws IOException { + TestScenario.builder() + .specName("MiscTest") + .request(HttpRequest.POST("/form", "message=World") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED)) + .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.OK) + .body("{\"message\":\"Hello World\"}") + .build())) + .run(); + } + + @Test + default void postFormUrlEncodedBodyBindingToPojoWorksIfYouDontSpecifyBodyAnnotation() throws IOException { + TestScenario.builder() + .specName("MiscTest") + .request(HttpRequest.POST("/form/without-body-annotation", "message=World") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED)) + .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.OK) + .body("{\"message\":\"Hello World\"}") + .build())) + .run(); + } + + @Test + default void formUrlEncodedWithBodyAnnotationAndANestedAttribute() throws IOException { + TestScenario.builder() + .specName("MiscTest") + .request(HttpRequest.POST("/form/nested-attribute", "message=World") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED)) + .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.OK) + .body("{\"message\":\"Hello World\"}") + .build())) + .run(); + } + + /** + * + * @see micronaut-aws #1410 + */ + @Test + default void applicationJsonWithBodyAnnotationAndANestedAttribute() throws IOException { + TestScenario.builder() + .specName("MiscTest") + .request(HttpRequest.POST("/form/json-nested-attribute", "{\"message\":\"World\"}") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) + .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.OK) + .body("{\"message\":\"Hello World\"}") + .build())) + .run(); + } + + @Test + default void applicationJsonWithoutBodyAnnotation() throws IOException { + TestScenario.builder() + .specName("MiscTest") + .request(HttpRequest.POST("/form/json-without-body-annotation", "{\"message\":\"World\"}") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) + .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.OK) + .body("{\"message\":\"Hello World\"}") + .build())) + .run(); + } + + @Test + default void applicationJsonWithBodyAnnotationAndANestedAttributeAndMapReturnRenderedAsJSON() throws IOException { + TestScenario.builder() + .specName("MiscTest") + .request(HttpRequest.POST("/form/json-nested-attribute-with-map-return", "{\"message\":\"World\"}") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) + .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.OK) + .body("{\"message\":\"Hello World\"}") + .build())) + .run(); + } + + @Test + default void applicationJsonWithBodyAnnotationAndObjectReturnRenderedAsJson() throws IOException { + TestScenario.builder() + .specName("MiscTest") + .request(HttpRequest.POST("/form/json-with-body-annotation-and-with-object-return", "{\"message\":\"World\"}") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) + .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.OK) + .body("{\"greeting\":\"Hello World\"}") + .build())) + .run(); + } + + @Controller + @Requires(property = "spec.name", value = "MiscTest") + class SimpleController { + @Get(uri = "/foo") + HttpResponse getParamValue(HttpRequest request) { + return HttpResponse.ok() + .body(request.getParameters().get("param")) + .header("foo", "bar"); + } + } + + @Controller("/bar") + @Requires(property = "spec.name", value = "MiscTest") + class ProduceController { + @Get(value = "/ok", produces = MediaType.APPLICATION_JSON) + String getOkAsJson() { + return "{\"status\":\"ok\"}"; + } + + @Get(value = "/ok", produces = MediaType.TEXT_HTML) + String getOkAsHtml() { + return "
ok
"; + } + } + + @Introspected + class MessageCreate { + + @NonNull + @NotBlank + private final String message; + + MessageCreate(@NonNull String message) { + this.message = message; + } + + @NonNull + String getMessage() { + return message; + } + } + + @Introspected + class MyResponse { + + @NonNull + @NotBlank + private final String greeting; + + public MyResponse(@NonNull String greeting) { + this.greeting = greeting; + } + + @NonNull + public String getGreeting() { + return greeting; + } + } + + @Controller("/form") + @Requires(property = "spec.name", value = "MiscTest") + class FormController { + + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + @Post("/without-body-annotation") + String withoutBodyAnnotation(MessageCreate messageCreate) { + return "{\"message\":\"Hello " + messageCreate.getMessage() + "\"}"; + } + + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + @Post + String save(@Body MessageCreate messageCreate) { + return "{\"message\":\"Hello " + messageCreate.getMessage() + "\"}"; + } + + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + @Post("/nested-attribute") + String save(@Body("message") String value) { + return "{\"message\":\"Hello " + value + "\"}"; + } + + @Consumes(MediaType.APPLICATION_JSON) + @Post("/json-without-body-annotation") + String jsonWithoutBody(MessageCreate messageCreate) { + return "{\"message\":\"Hello " + messageCreate.getMessage() + "\"}"; + } + + @Consumes(MediaType.APPLICATION_JSON) + @Post("/json-nested-attribute") + String jsonNestedAttribute(@Body("message") String value) { + return "{\"message\":\"Hello " + value + "\"}"; + } + + @Consumes(MediaType.APPLICATION_JSON) + @Post("/json-nested-attribute-with-map-return") + Map jsonNestedAttributeWithMapReturn(@Body("message") String value) { + return Collections.singletonMap("message", "Hello " + value); + } + + @Consumes(MediaType.APPLICATION_JSON) + @Post("/json-with-body-annotation-and-with-object-return") + MyResponse jsonNestedAttributeWithObjectReturn(@Body MessageCreate messageCreate) { + return new MyResponse("Hello " + messageCreate.getMessage()); + } + } +} diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ParameterTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ParameterTest.java new file mode 100644 index 00000000000..8b68c3e89e7 --- /dev/null +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ParameterTest.java @@ -0,0 +1,61 @@ +/* + * Copyright 2017-2022 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.http.server.tck.tests; + +import io.micronaut.context.annotation.Requires; +import io.micronaut.http.HttpRequest; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.Get; +import io.micronaut.http.server.tck.AssertionUtils; +import io.micronaut.http.server.tck.HttpResponseAssertion; +import io.micronaut.http.server.tck.TestScenario; +import io.micronaut.http.uri.UriBuilder; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.List; + + +@SuppressWarnings({ + "java:S5960", // We're allowed assertions, as these are used in tests only + "checkstyle:MissingJavadocType", +}) +public interface ParameterTest { + @Test + default void testGetAllMethod() throws IOException { + TestScenario.builder() + .specName("ParameterTest") + .request(HttpRequest.GET(UriBuilder.of("/parameters-test").path("all") + .queryParam("test", "one", "two", "three+four") + .build())) + .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.OK) + .body("[\"one\",\"two\",\"three+four\"]") + .build())) + .run(); + } + + @Controller("/parameters-test") + @Requires(property = "spec.name", value = "ParameterTest") + static class BodyController { + + @Get(uri = "/all") + List all(HttpRequest request) { + return request.getParameters().getAll("test"); + } + } +} diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/RemoteAddressTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/RemoteAddressTest.java new file mode 100644 index 00000000000..bbf06289af4 --- /dev/null +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/RemoteAddressTest.java @@ -0,0 +1,89 @@ +/* + * Copyright 2017-2022 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.http.server.tck.tests; + +import io.micronaut.context.annotation.Requires; +import io.micronaut.core.async.publisher.Publishers; +import io.micronaut.http.HttpRequest; +import io.micronaut.http.HttpResponse; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.MutableHttpResponse; +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.Filter; +import io.micronaut.http.annotation.Get; +import io.micronaut.http.annotation.Produces; +import io.micronaut.http.filter.HttpServerFilter; +import io.micronaut.http.filter.ServerFilterChain; +import io.micronaut.http.server.exceptions.ExceptionHandler; +import io.micronaut.http.server.tck.AssertionUtils; +import io.micronaut.http.server.tck.HttpResponseAssertion; +import io.micronaut.http.server.tck.TestScenario; +import jakarta.inject.Singleton; +import org.junit.jupiter.api.Test; +import org.reactivestreams.Publisher; + +import java.io.IOException; +import java.util.Collections; + + +@SuppressWarnings({ + "java:S5960", // We're allowed assertions, as these are used in tests only + "checkstyle:MissingJavadocType", +}) +public interface RemoteAddressTest { + + @Test + default void testRemoteAddressComesFromIdentitySourceIp() throws IOException { + TestScenario.builder() + .specName("RemoteAddressTest") + .request(HttpRequest.GET("/remoteAddress/fromSourceIp")) + .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.OK) + .headers(Collections.singletonMap("X-Captured-Remote-Address", "127.0.0.1")) + .build())) + .run(); + } + + @Requires(property = "spec.name", value = "RemoteAddressTest") + @Controller("/remoteAddress") + class TestController { + @Get("fromSourceIp") + void sourceIp() { + } + } + + @Requires(property = "spec.name", value = "RemoteAddressTest") + @Filter("/remoteAddress/**") + class CaptureRemoteAddressFiter implements HttpServerFilter { + @Override + public Publisher> doFilter(HttpRequest request, ServerFilterChain chain) { + return Publishers.map(chain.proceed(request), httpResponse -> { + httpResponse.getHeaders().add("X-Captured-Remote-Address", request.getRemoteAddress().getAddress().getHostAddress()); + return httpResponse; + }); + } + } + + @Requires(property = "spec.name", value = "RemoteAddressTest") + @Produces + @Singleton + class CustomExceptionHandler implements ExceptionHandler { + @Override + public HttpResponse handle(HttpRequest request, Exception exception) { + return HttpResponse.serverError(exception.toString()); + } + } +} diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ResponseStatusTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ResponseStatusTest.java new file mode 100644 index 00000000000..9a056a3a56b --- /dev/null +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ResponseStatusTest.java @@ -0,0 +1,135 @@ +/* + * Copyright 2017-2022 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.http.server.tck.tests; + +import io.micronaut.context.annotation.Requires; +import io.micronaut.http.HttpHeaders; +import io.micronaut.http.HttpRequest; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.MediaType; +import io.micronaut.http.annotation.Body; +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.Delete; +import io.micronaut.http.annotation.Get; +import io.micronaut.http.annotation.Post; +import io.micronaut.http.annotation.Status; +import io.micronaut.http.server.tck.AssertionUtils; +import io.micronaut.http.server.tck.HttpResponseAssertion; +import io.micronaut.http.server.tck.TestScenario; +import org.junit.jupiter.api.Test; + +import javax.validation.ConstraintViolationException; +import java.io.IOException; +import java.util.Collections; +import java.util.Optional; + + +@SuppressWarnings({ + "java:S5960", // We're allowed assertions, as these are used in tests only + "checkstyle:MissingJavadocType", +}) +public interface ResponseStatusTest { + + @Test + default void testConstraintViolationCauses400() throws IOException { + TestScenario.builder() + .specName("ResponseStatusSpec") + .request(HttpRequest.POST("/response-status/constraint-violation", Collections.emptyMap()).header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)) + .assertion((server, request) -> AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.BAD_REQUEST) + .build())) + .run(); + } + + @Test + default void testVoidMethodsDoesNotCause404() throws IOException { + TestScenario.builder() + .specName("ResponseStatusSpec") + .request(HttpRequest.DELETE("/response-status/delete-something") + .header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)) + .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.NO_CONTENT) + .build())) + .run(); + } + + @Test + default void testNullCauses404() throws IOException { + TestScenario.builder() + .request(HttpRequest.GET("/response-status/null").header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)) + .specName("ResponseStatusSpec") + .assertion((server, request) -> AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.NOT_FOUND) + .build())) + .run(); + } + + @Test + default void testOptionalCauses404() throws IOException { + TestScenario.builder() + .specName("ResponseStatusSpec") + .request(HttpRequest.GET("/response-status/optional").header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)) + .assertion((server, request) -> AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.NOT_FOUND) + .build())) + .run(); + } + + @Test + default void testCustomResponseStatus() throws IOException { + TestScenario.builder() + .specName("ResponseStatusSpec") + .request(HttpRequest.POST("/response-status", "foo") + .header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)) + .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.CREATED) + .body("foo") + .build())) + .run(); + } + + @Controller("/response-status") + @Requires(property = "spec.name", value = "ResponseStatusSpec") + class StatusController { + + @Post(uri = "/", processes = MediaType.TEXT_PLAIN) + @Status(HttpStatus.CREATED) + String post(@Body String data) { + return data; + } + + @Get(uri = "/optional", processes = MediaType.TEXT_PLAIN) + Optional optional() { + return Optional.empty(); + } + + @Get(uri = "/null", processes = MediaType.TEXT_PLAIN) + String returnNull() { + return null; + } + + @Post(uri = "/constraint-violation", processes = MediaType.TEXT_PLAIN) + String constraintViolation() { + throw new ConstraintViolationException("Failed", Collections.emptySet()); + } + + @Status(HttpStatus.NO_CONTENT) + @Delete(uri = "/delete-something", processes = MediaType.TEXT_PLAIN) + void deleteSomething() { + // do nothing + } + } +} diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/StatusTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/StatusTest.java new file mode 100644 index 00000000000..c7f5db780f1 --- /dev/null +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/StatusTest.java @@ -0,0 +1,106 @@ +/* + * Copyright 2017-2022 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.http.server.tck.tests; + +import io.micronaut.context.annotation.Requires; +import io.micronaut.http.HttpRequest; +import io.micronaut.http.HttpResponse; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.Get; +import io.micronaut.http.annotation.Produces; +import io.micronaut.http.server.exceptions.ExceptionHandler; +import io.micronaut.http.server.exceptions.response.ErrorContext; +import io.micronaut.http.server.exceptions.response.ErrorResponseProcessor; +import io.micronaut.http.server.tck.AssertionUtils; +import io.micronaut.http.server.tck.HttpResponseAssertion; +import io.micronaut.http.server.tck.TestScenario; +import jakarta.inject.Singleton; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.io.IOException; + +@SuppressWarnings({ + "java:S5960", // We're allowed assertions, as these are used in tests only + "checkstyle:MissingJavadocType", +}) +public interface StatusTest { + /** + * @see micronaut-aws #1387 + * @param path Request Path + */ + @ParameterizedTest + @ValueSource(strings = {"/http-status", "/http-response-status", "/http-exception"}) + default void testControllerReturningHttpStatus(String path) throws IOException { + TestScenario.builder() + .specName("StatusSpec") + .request(HttpRequest.GET(path)) + .assertion((server, request) -> AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.I_AM_A_TEAPOT) + .build())) + .run(); + } + + @Requires(property = "spec.name", value = "StatusSpec") + @Controller("/http-status") + class HttpStatusController { + @Get + HttpStatus index() { + return HttpStatus.I_AM_A_TEAPOT; + } + } + + @Requires(property = "spec.name", value = "StatusSpec") + @Controller("/http-response-status") + class HttpResponseStatusController { + + @Get + HttpResponse index() { + return HttpResponse.status(HttpStatus.I_AM_A_TEAPOT); + } + } + + @Requires(property = "spec.name", value = "StatusSpec") + @Controller("/http-exception") + class HttpResponseErrorController { + + @Get + HttpResponse index() { + throw new TeapotException(); + } + } + + class TeapotException extends RuntimeException { + } + + @Produces + @Singleton + class TeapotExceptionHandler implements ExceptionHandler> { + private final ErrorResponseProcessor errorResponseProcessor; + + TeapotExceptionHandler(ErrorResponseProcessor errorResponseProcessor) { + this.errorResponseProcessor = errorResponseProcessor; + } + + @Override + public HttpResponse handle(HttpRequest request, TeapotException e) { + return errorResponseProcessor.processResponse(ErrorContext.builder(request) + .cause(e) + .build(), HttpResponse.status(HttpStatus.I_AM_A_TEAPOT)); + } + } +} diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/VersionTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/VersionTest.java new file mode 100644 index 00000000000..254da5ab494 --- /dev/null +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/VersionTest.java @@ -0,0 +1,69 @@ +/* + * Copyright 2017-2022 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.http.server.tck.tests; + +import io.micronaut.context.annotation.Requires; +import io.micronaut.core.util.CollectionUtils; +import io.micronaut.core.util.StringUtils; +import io.micronaut.core.version.annotation.Version; +import io.micronaut.http.HttpRequest; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.Get; +import io.micronaut.http.server.tck.AssertionUtils; +import io.micronaut.http.server.tck.HttpResponseAssertion; +import io.micronaut.http.server.tck.TestScenario; +import org.junit.jupiter.api.Test; +import java.io.IOException; + + +@SuppressWarnings({ + "java:S5960", // We're allowed assertions, as these are used in tests only + "checkstyle:MissingJavadocType", +}) +public interface VersionTest { + + @Test + default void testControllerMethodWithVersion2() throws IOException { + TestScenario.builder() + .configuration(CollectionUtils.mapOf( + "micronaut.router.versioning.enabled", StringUtils.TRUE, + "micronaut.router.versioning.header.enabled", StringUtils.TRUE + )).specName("VersionSpec") + .request(HttpRequest.GET("/version/ping").header("X-API-VERSION", "2")) + .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.OK) + .body("pong v2") + .build())) + .run(); + } + + @Controller("/version") + @Requires(property = "spec.name", value = "VersionSpec") + class ConsumesController { + + @Get("/ping") + String pingV1() { + return "pong v1"; + } + + @Version("2") + @Get("/ping") + String pingV2() { + return "pong v2"; + } + } +} diff --git a/settings.gradle b/settings.gradle index d15dc4124d5..c38cbc4b103 100644 --- a/settings.gradle +++ b/settings.gradle @@ -14,8 +14,10 @@ pluginManagement { } plugins { - id 'io.micronaut.build.shared.settings' version '5.3.14' + id 'io.micronaut.build.shared.settings' version '5.3.15' } +enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") + rootProject.name = 'micronaut' @@ -35,6 +37,7 @@ include "http-client-core" include "http-client" include "http-netty" include "http-server" +include "http-server-tck" include "http-server-netty" include "http-validation" include "inject" @@ -61,9 +64,11 @@ include "test-suite" include "test-suite-helper" include "test-suite-javax-inject" include "test-suite-jakarta-inject-bean-import" +include "test-suite-http-server-tck-netty" include "test-suite-kotlin" include "test-suite-graal" include "test-suite-groovy" +include "test-suite-groovy" include "test-utils" // benchmarks diff --git a/test-suite-http-server-tck-netty/build.gradle.kts b/test-suite-http-server-tck-netty/build.gradle.kts new file mode 100644 index 00000000000..ffc1abf9655 --- /dev/null +++ b/test-suite-http-server-tck-netty/build.gradle.kts @@ -0,0 +1,8 @@ +plugins { + id("io.micronaut.build.internal.convention-test-library") +} +dependencies { + testImplementation(projects.httpServerNetty) + testImplementation(projects.httpClient) + testImplementation(projects.httpServerTck) +} diff --git a/test-suite-http-server-tck-netty/src/test/java/io/micronaut/http/server/tck/netty/tests/NettyHttpServerTestSuite.java b/test-suite-http-server-tck-netty/src/test/java/io/micronaut/http/server/tck/netty/tests/NettyHttpServerTestSuite.java new file mode 100644 index 00000000000..949049a025f --- /dev/null +++ b/test-suite-http-server-tck-netty/src/test/java/io/micronaut/http/server/tck/netty/tests/NettyHttpServerTestSuite.java @@ -0,0 +1,6 @@ +package io.micronaut.http.server.tck.netty.tests; + +import io.micronaut.http.server.tck.HttpServerTestSuite; + +public class NettyHttpServerTestSuite implements HttpServerTestSuite { +} diff --git a/test-suite-http-server-tck-netty/src/test/resources/META-INF/services/io.micronaut.http.server.tck.ServerUnderTestProvider b/test-suite-http-server-tck-netty/src/test/resources/META-INF/services/io.micronaut.http.server.tck.ServerUnderTestProvider new file mode 100644 index 00000000000..adf15625293 --- /dev/null +++ b/test-suite-http-server-tck-netty/src/test/resources/META-INF/services/io.micronaut.http.server.tck.ServerUnderTestProvider @@ -0,0 +1 @@ +io.micronaut.http.server.tck.EmbeddedServerUnderTestProvider diff --git a/test-suite-http-server-tck-netty/src/test/resources/logback.xml b/test-suite-http-server-tck-netty/src/test/resources/logback.xml new file mode 100644 index 00000000000..6a62bd94d36 --- /dev/null +++ b/test-suite-http-server-tck-netty/src/test/resources/logback.xml @@ -0,0 +1,11 @@ + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + diff --git a/test-suite-javax-inject/build.gradle b/test-suite-javax-inject/build.gradle index e39503e43b5..ce67ef463c4 100644 --- a/test-suite-javax-inject/build.gradle +++ b/test-suite-javax-inject/build.gradle @@ -1,7 +1,6 @@ plugins { id "io.micronaut.build.internal.convention-test-library" } - dependencies { testAnnotationProcessor project(":inject-java") testCompileOnly project(":inject-groovy") From 9dd45deac8f0e0a6fc0c633a5a80a476fe83b920 Mon Sep 17 00:00:00 2001 From: Sergio del Amo Date: Tue, 20 Dec 2022 01:55:07 +0100 Subject: [PATCH 02/13] use @Suite --- gradle/libs.versions.toml | 3 + .../http/server/tck/HttpServerTestSuite.java | 57 ------------------- .../server/tck/tests/BodyArgumentTest.java | 5 +- .../http/server/tck/tests/BodyTest.java | 15 ++--- .../http/server/tck/tests/ConsumesTest.java | 9 +-- .../http/server/tck/tests/CookiesTest.java | 11 ++-- .../tck/tests/DeleteWithoutBodyTest.java | 7 ++- .../server/tck/tests/ErrorHandlerTest.java | 47 +++++++-------- .../server/tck/tests/FilterErrorTest.java | 39 ++++++------- .../http/server/tck/tests/FiltersTest.java | 15 ++--- .../http/server/tck/tests/FluxTest.java | 7 ++- .../http/server/tck/tests/HelloWorldTest.java | 7 ++- .../http/server/tck/tests/MiscTest.java | 31 +++++----- .../http/server/tck/tests/ParameterTest.java | 5 +- .../server/tck/tests/RemoteAddressTest.java | 11 ++-- .../server/tck/tests/ResponseStatusTest.java | 15 ++--- .../http/server/tck/tests/StatusTest.java | 15 ++--- .../http/server/tck/tests/VersionTest.java | 8 +-- .../build.gradle.kts | 1 + .../netty/tests/NettyHttpServerTestSuite.java | 9 ++- 20 files changed, 142 insertions(+), 175 deletions(-) delete mode 100644 http-server-tck/src/main/java/io/micronaut/http/server/tck/HttpServerTestSuite.java diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a55abfd368c..be2fa851be9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -36,6 +36,7 @@ managed-dekorate = "1.0.3" managed-elasticsearch = "7.16.3" managed-ignite = "2.13.0" managed-junit5 = "5.9.1" +managed-junit-platform="1.9.1" managed-kotlin = "1.6.21" managed-kotlin-coroutines = "1.5.1" managed-google-function-framework = "1.0.4" @@ -402,6 +403,8 @@ jsr107 = { module = "org.jsr107.ri:cache-ri-impl", version.ref = "jsr107" } junit-jupiter-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "managed-junit5" } junit-jupiter-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "managed-junit5" } junit-jupiter-params = { module = "org.junit.jupiter:junit-jupiter-params", version.ref = "managed-junit5" } +junit-platform-engine = { module = "org.junit.platform:junit-platform-suite-engine", version.ref = "managed-junit-platform" } + junit-vintage = { module = "org.junit.vintage:junit-vintage-engine", version.ref = "managed-junit5" } jetbrains-annotations = { module = "org.jetbrains:annotations", version.ref = "jetbrains-annotations" } diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/HttpServerTestSuite.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/HttpServerTestSuite.java deleted file mode 100644 index 2cb35b88055..00000000000 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/HttpServerTestSuite.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2017-2022 original authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.micronaut.http.server.tck; - -import io.micronaut.http.server.tck.tests.BodyArgumentTest; -import io.micronaut.http.server.tck.tests.BodyTest; -import io.micronaut.http.server.tck.tests.ConsumesTest; -import io.micronaut.http.server.tck.tests.CookiesTest; -import io.micronaut.http.server.tck.tests.DeleteWithoutBodyTest; -import io.micronaut.http.server.tck.tests.ErrorHandlerTest; -import io.micronaut.http.server.tck.tests.FilterErrorTest; -import io.micronaut.http.server.tck.tests.FiltersTest; -import io.micronaut.http.server.tck.tests.FluxTest; -import io.micronaut.http.server.tck.tests.HelloWorldTest; -import io.micronaut.http.server.tck.tests.MiscTest; -import io.micronaut.http.server.tck.tests.ParameterTest; -import io.micronaut.http.server.tck.tests.RemoteAddressTest; -import io.micronaut.http.server.tck.tests.ResponseStatusTest; -import io.micronaut.http.server.tck.tests.StatusTest; -import io.micronaut.http.server.tck.tests.VersionTest; - -/** - * Http Server TCK Test Suite. - * @author Sergio del Amo - * @since 3.8.0 - */ -public interface HttpServerTestSuite extends - BodyArgumentTest, - ConsumesTest, - ParameterTest, - StatusTest, - ResponseStatusTest, - VersionTest, - FluxTest, - CookiesTest, - FiltersTest, - MiscTest, - DeleteWithoutBodyTest, - RemoteAddressTest, - ErrorHandlerTest, - FilterErrorTest, - BodyTest, - HelloWorldTest { -} diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/BodyArgumentTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/BodyArgumentTest.java index d2c82b41e0a..572b89e04af 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/BodyArgumentTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/BodyArgumentTest.java @@ -32,13 +32,14 @@ @SuppressWarnings({ "java:S5960", // We're allowed assertions, as these are used in tests only "checkstyle:MissingJavadocType", + "checkstyle:DesignForExtension" }) -public interface BodyArgumentTest { +public class BodyArgumentTest { /** * @see micronaut-aws #1164 */ @Test - default void testBodyArguments() throws IOException { + void testBodyArguments() throws IOException { TestScenario.builder() .specName("BodyArgumentTest") .request(HttpRequest.POST("/body-arguments-test/getA", "{\"a\":\"A\",\"b\":\"B\"}").header(HttpHeaders.ACCEPT, MediaType.TEXT_PLAIN)) diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/BodyTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/BodyTest.java index 69fb49129bc..e0d45f05824 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/BodyTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/BodyTest.java @@ -37,11 +37,12 @@ @SuppressWarnings({ "java:S5960", // We're allowed assertions, as these are used in tests only "checkstyle:MissingJavadocType", + "checkstyle:DesignForExtension" }) -public interface BodyTest { +public class BodyTest { @Test - default void testCustomBodyPOJO() throws IOException { + void testCustomBodyPOJO() throws IOException { TestScenario.builder() .specName("BodyTest") .request(HttpRequest.POST("/response-body/pojo", "{\"x\":10,\"y\":20}") @@ -55,7 +56,7 @@ default void testCustomBodyPOJO() throws IOException { } @Test - default void testCustomBodyPOJODefaultToJSON() throws IOException { + void testCustomBodyPOJODefaultToJSON() throws IOException { TestScenario.builder() .specName("BodyTest") .request(HttpRequest.POST("/response-body/pojo", "{\"x\":10,\"y\":20}")) @@ -67,7 +68,7 @@ default void testCustomBodyPOJODefaultToJSON() throws IOException { } @Test - default void testCustomBodyPOJOWithWholeRequest() throws IOException { + void testCustomBodyPOJOWithWholeRequest() throws IOException { TestScenario.builder() .specName("BodyTest") .request(HttpRequest.POST("/response-body/pojo-and-request", "{\"x\":10,\"y\":20}") @@ -80,7 +81,7 @@ default void testCustomBodyPOJOWithWholeRequest() throws IOException { } @Test - default void testCustomBodyPOJOReactiveTypes() throws IOException { + void testCustomBodyPOJOReactiveTypes() throws IOException { TestScenario.builder() .specName("BodyTest") .request(HttpRequest.POST("/response-body/pojo-reactive", "{\"x\":10,\"y\":20}") @@ -95,7 +96,7 @@ default void testCustomBodyPOJOReactiveTypes() throws IOException { @Controller("/response-body") @Requires(property = "spec.name", value = "BodyTest") - class BodyController { + static class BodyController { @Post(uri = "/pojo") @Status(HttpStatus.CREATED) @@ -123,7 +124,7 @@ String postBytes(@Body byte[] bytes) { } } - class Point { + static class Point { private Integer x; private Integer y; diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ConsumesTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ConsumesTest.java index ec99867d939..0735fe2efb7 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ConsumesTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ConsumesTest.java @@ -33,10 +33,11 @@ @SuppressWarnings({ "java:S5960", // We're allowed assertions, as these are used in tests only "checkstyle:MissingJavadocType", + "checkstyle:DesignForExtension" }) -public interface ConsumesTest { +public class ConsumesTest { @Test - default void testMultipleConsumesDefinition() throws IOException { + void testMultipleConsumesDefinition() throws IOException { TestScenario.builder() .specName("ConsumesTest") .request(HttpRequest.POST("/consumes-test", "{\"name\":\"Fred\"}").header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) @@ -49,7 +50,7 @@ default void testMultipleConsumesDefinition() throws IOException { @Controller("/consumes-test") @Requires(property = "spec.name", value = "ConsumesTest") - class ConsumesController { + static class ConsumesController { @Post("/") @Consumes({MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_JSON}) @@ -58,7 +59,7 @@ Pojo save(@Body Pojo pojo) { } } - class Pojo { + static class Pojo { private String name; public String getName() { diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/CookiesTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/CookiesTest.java index 9b119af8745..d45437a875f 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/CookiesTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/CookiesTest.java @@ -35,11 +35,12 @@ @SuppressWarnings({ "java:S5960", // We're allowed assertions, as these are used in tests only "checkstyle:MissingJavadocType", + "checkstyle:DesignForExtension" }) -public interface CookiesTest { +public class CookiesTest { @Test - default void testCookieBind() throws IOException { + void testCookieBind() throws IOException { TestScenario.builder() .specName("CookiesTest") .request(HttpRequest.GET("/cookies-test/bind") @@ -53,7 +54,7 @@ default void testCookieBind() throws IOException { } @Test - default void testGetCookiesMethod() throws IOException { + void testGetCookiesMethod() throws IOException { TestScenario.builder() .specName("CookiesTest") .request(HttpRequest.GET("/cookies-test/all") @@ -67,7 +68,7 @@ default void testGetCookiesMethod() throws IOException { } @Test - default void testNoCookie() throws IOException { + void testNoCookie() throws IOException { TestScenario.builder() .specName("CookiesTest") .request(HttpRequest.GET("/cookies-test/all")) @@ -80,7 +81,7 @@ default void testNoCookie() throws IOException { @Controller("/cookies-test") @Requires(property = "spec.name", value = "CookiesTest") - class CookieController { + static class CookieController { @Get(uri = "/all") Map all(HttpRequest request) { diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/DeleteWithoutBodyTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/DeleteWithoutBodyTest.java index 0257735c2e5..1c5da8a08f7 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/DeleteWithoutBodyTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/DeleteWithoutBodyTest.java @@ -37,10 +37,11 @@ @SuppressWarnings({ "java:S5960", // We're allowed assertions, as these are used in tests only "checkstyle:MissingJavadocType", + "checkstyle:DesignForExtension" }) -public interface DeleteWithoutBodyTest { +public class DeleteWithoutBodyTest { @Test - default void verifiesItIsPossibleToExposesADeleteEndpointWhichIsInvokedWithoutABody() throws IOException { + void verifiesItIsPossibleToExposesADeleteEndpointWhichIsInvokedWithoutABody() throws IOException { TestScenario.builder() .specName("DeleteWithoutBodyTest") .request(HttpRequest.DELETE("/sessions/sergio").header(HttpHeaders.AUTHORIZATION, HttpHeaderValues.AUTHORIZATION_PREFIX_BEARER + " xxx")) @@ -53,7 +54,7 @@ default void verifiesItIsPossibleToExposesADeleteEndpointWhichIsInvokedWithoutAB @Requires(property = "spec.name", value = "DeleteWithoutBodyTest") @Controller("/sessions") - class SessionsController { + static class SessionsController { @Status(HttpStatus.OK) @Delete("/{username}") void delete(@PathVariable String username, diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ErrorHandlerTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ErrorHandlerTest.java index 89255ed40e3..25ed31fa603 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ErrorHandlerTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ErrorHandlerTest.java @@ -54,11 +54,12 @@ @SuppressWarnings({ "java:S5960", // We're allowed assertions, as these are used in tests only "checkstyle:MissingJavadocType", + "checkstyle:DesignForExtension" }) -public interface ErrorHandlerTest { +public class ErrorHandlerTest { @Test - default void testCustomGlobalExceptionHandlersDeclaredInController() throws IOException { + void testCustomGlobalExceptionHandlersDeclaredInController() throws IOException { TestScenario.builder() .configuration(CollectionUtils.mapOf( "micronaut.server.cors.configurations.web.allowedOrigins", Collections.singletonList("http://localhost:8080"), @@ -75,7 +76,7 @@ default void testCustomGlobalExceptionHandlersDeclaredInController() throws IOEx } @Test - default void testCustomGlobalExceptionHandlers() throws IOException { + void testCustomGlobalExceptionHandlers() throws IOException { TestScenario.builder() .configuration(CollectionUtils.mapOf( "micronaut.server.cors.configurations.web.allowedOrigins", Collections.singletonList("http://localhost:8080"), @@ -92,7 +93,7 @@ default void testCustomGlobalExceptionHandlers() throws IOException { } @Test - default void testCustomGlobalExceptionHandlersForPOSTWithBody() throws IOException { + void testCustomGlobalExceptionHandlersForPOSTWithBody() throws IOException { Map configuration = CollectionUtils.mapOf( "micronaut.server.cors.configurations.web.allowedOrigins", Collections.singletonList("http://localhost:8080"), "micronaut.server.cors.enabled", StringUtils.TRUE @@ -109,7 +110,7 @@ default void testCustomGlobalExceptionHandlersForPOSTWithBody() throws IOExcepti } @Test - default void testCustomGlobalStatusHandlersDeclaredInController() throws IOException { + void testCustomGlobalStatusHandlersDeclaredInController() throws IOException { TestScenario.builder() .configuration(CollectionUtils.mapOf( "micronaut.server.cors.configurations.web.allowedOrigins", Collections.singletonList("http://localhost:8080"), @@ -125,7 +126,7 @@ default void testCustomGlobalStatusHandlersDeclaredInController() throws IOExcep } @Test - default void testLocalExceptionHandlers() throws IOException { + void testLocalExceptionHandlers() throws IOException { TestScenario.builder() .configuration(CollectionUtils.mapOf( "micronaut.server.cors.configurations.web.allowedOrigins", Collections.singletonList("http://localhost:8080"), @@ -140,7 +141,7 @@ default void testLocalExceptionHandlers() throws IOException { } @Test - default void jsonMessageFormatErrorsReturn400() throws IOException { + void jsonMessageFormatErrorsReturn400() throws IOException { TestScenario.builder() .configuration(CollectionUtils.mapOf( "micronaut.server.cors.configurations.web.allowedOrigins", Collections.singletonList("http://localhost:8080"), @@ -156,7 +157,7 @@ default void jsonMessageFormatErrorsReturn400() throws IOException { } @Test - default void corsHeadersArePresentAfterFailedDeserialisationWhenErrorHandlerIsUsed() throws IOException { + void corsHeadersArePresentAfterFailedDeserialisationWhenErrorHandlerIsUsed() throws IOException { TestScenario.builder() .configuration(CollectionUtils.mapOf( "micronaut.server.cors.configurations.web.allowedOrigins", Collections.singletonList("http://localhost:8080"), @@ -172,7 +173,7 @@ default void corsHeadersArePresentAfterFailedDeserialisationWhenErrorHandlerIsUs } @Test - default void corsHeadersArePresentAfterFailedDeserialisation() throws IOException { + void corsHeadersArePresentAfterFailedDeserialisation() throws IOException { TestScenario.builder() .configuration(CollectionUtils.mapOf( "micronaut.server.cors.configurations.web.allowedOrigins", Collections.singletonList("http://localhost:8080"), @@ -189,7 +190,7 @@ default void corsHeadersArePresentAfterFailedDeserialisation() throws IOExceptio } @Test - default void corsHeadersArePresentAfterExceptions() throws IOException { + void corsHeadersArePresentAfterExceptions() throws IOException { TestScenario.builder() .configuration(CollectionUtils.mapOf( "micronaut.server.cors.configurations.web.allowedOrigins", Collections.singletonList("http://localhost:8080"), @@ -206,7 +207,7 @@ default void corsHeadersArePresentAfterExceptions() throws IOException { } @Test - default void messageValidationErrorsReturn400() throws IOException { + void messageValidationErrorsReturn400() throws IOException { TestScenario.builder() .configuration(CollectionUtils.mapOf( "micronaut.server.cors.configurations.web.allowedOrigins", Collections.singletonList("http://localhost:8080"), @@ -223,7 +224,7 @@ default void messageValidationErrorsReturn400() throws IOException { @Controller("/secret") @Requires(property = "spec.name", value = "ErrorHandlerTest") - class SecretController { + static class SecretController { @Get @Produces(MediaType.TEXT_PLAIN) String index() { @@ -233,7 +234,7 @@ String index() { @Requires(property = "spec.name", value = "ErrorHandlerTest") @Controller("/errors") - class ErrorController { + static class ErrorController { @Get("/global") String globalHandler() { @@ -266,7 +267,7 @@ String localHandler(AnotherException throwable) { @Controller(value = "/json/errors", produces = io.micronaut.http.MediaType.APPLICATION_JSON) @Requires(property = "spec.name", value = "ErrorHandlerTest") - class JsonErrorController { + static class JsonErrorController { @Post("/global") String globalHandlerPost(@Body RequestObject object) { @@ -284,7 +285,7 @@ HttpResponse errorHandler(HttpRequest request, RuntimeException excep } @Introspected - class RequestObject { + static class RequestObject { @Min(1L) private Integer numberField; @@ -299,7 +300,7 @@ public Integer getNumberField() { @Controller("/json") @Requires(property = "spec.name", value = "ErrorHandlerTest") - class JsonController { + static class JsonController { @Post("/jsonBody") String jsonBody(@Valid @Body RequestObject data) { return "blah"; @@ -308,7 +309,7 @@ String jsonBody(@Valid @Body RequestObject data) { @Controller("/global-errors") @Requires(property = "spec.name", value = "ErrorHandlerTest") - class GlobalErrorController { + static class GlobalErrorController { @Error(global = true, exception = GloballyHandledException.class) @Produces(io.micronaut.http.MediaType.TEXT_PLAIN) @@ -328,7 +329,7 @@ String globalControllerHandlerForStatus() { @Singleton @Requires(property = "spec.name", value = "ErrorHandlerTest") - class CodecExceptionExceptionHandler + static class CodecExceptionExceptionHandler implements ExceptionHandler { @Override @@ -339,7 +340,7 @@ public HttpResponse handle(HttpRequest request, CodecException exception) { @Singleton @Requires(property = "spec.name", value = "ErrorHandlerTest") - class RuntimeErrorHandler implements ExceptionHandler { + static class RuntimeErrorHandler implements ExceptionHandler { @Override public HttpResponse handle(HttpRequest request, RuntimeException exception) { @@ -350,7 +351,7 @@ public HttpResponse handle(HttpRequest request, RuntimeException exception) { @Singleton @Requires(property = "spec.name", value = "ErrorHandlerTest") - class MyErrorHandler implements ExceptionHandler { + static class MyErrorHandler implements ExceptionHandler { @Override public HttpResponse handle(HttpRequest request, MyException exception) { @@ -360,19 +361,19 @@ public HttpResponse handle(HttpRequest request, MyException exception) { } - class MyException extends RuntimeException { + static class MyException extends RuntimeException { public MyException(String badThings) { super(badThings); } } - class AnotherException extends RuntimeException { + static class AnotherException extends RuntimeException { public AnotherException(String badThings) { super(badThings); } } - class GloballyHandledException extends Exception { + static class GloballyHandledException extends Exception { public GloballyHandledException(String badThingsHappensGlobally) { super(badThingsHappensGlobally); } diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FilterErrorTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FilterErrorTest.java index bca6fb2e0d9..f748db55dfa 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FilterErrorTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FilterErrorTest.java @@ -55,10 +55,11 @@ @SuppressWarnings({ "java:S5960", // We're allowed assertions, as these are used in tests only "checkstyle:MissingJavadocType", + "checkstyle:DesignForExtension" }) -public interface FilterErrorTest { +public class FilterErrorTest { @Test - default void testFilterThrowingExceptionHandledByExceptionHandlerThrowingException() throws IOException { + void testFilterThrowingExceptionHandledByExceptionHandlerThrowingException() throws IOException { TestScenario.builder() .specName("FilterErrorSpec3") .request(HttpRequest.GET("/filter-error-spec-3") @@ -77,7 +78,7 @@ default void testFilterThrowingExceptionHandledByExceptionHandlerThrowingExcepti } @Test - default void testTheErrorRouteIsTheRouteMatch() throws IOException { + void testTheErrorRouteIsTheRouteMatch() throws IOException { TestScenario.builder() .request(HttpRequest.GET("/filter-error-spec-4/status").header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) .specName("FilterErrorSpec4") @@ -95,7 +96,7 @@ default void testTheErrorRouteIsTheRouteMatch() throws IOException { } @Test - default void testNonOncePerRequestFilterThrowingErrorDoesNotLoop() throws IOException { + void testNonOncePerRequestFilterThrowingErrorDoesNotLoop() throws IOException { TestScenario.builder() .request(HttpRequest.GET("/filter-error-spec") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) @@ -112,7 +113,7 @@ default void testNonOncePerRequestFilterThrowingErrorDoesNotLoop() throws IOExce } @Test - default void testErrorsEmittedFromSecondFilterInteractingWithExceptionHandlers() throws IOException { + void testErrorsEmittedFromSecondFilterInteractingWithExceptionHandlers() throws IOException { TestScenario.builder() .request(HttpRequest.GET("/filter-error-spec"). header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON) @@ -134,7 +135,7 @@ default void testErrorsEmittedFromSecondFilterInteractingWithExceptionHandlers() } @Test - default void testErrorsEmittedFromFiltersInteractingWithExceptionHandlers() throws IOException { + void testErrorsEmittedFromFiltersInteractingWithExceptionHandlers() throws IOException { TestScenario.builder() .specName("FilterErrorSpec") .request(HttpRequest.GET("/filter-error-spec").header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) @@ -154,18 +155,18 @@ default void testErrorsEmittedFromFiltersInteractingWithExceptionHandlers() thro .run(); } - class FilterExceptionException extends RuntimeException { + static class FilterExceptionException extends RuntimeException { } - class FilterException extends RuntimeException { + static class FilterException extends RuntimeException { } - class NextFilterException extends RuntimeException { + static class NextFilterException extends RuntimeException { } @Requires(property = "spec.name", value = "FilterErrorSpec") @Filter(Filter.MATCH_ALL_PATTERN) - class First implements HttpServerFilter { + static class First implements HttpServerFilter { AtomicInteger executedCount = new AtomicInteger(0); AtomicReference responseStatus = new AtomicReference<>(); @@ -224,7 +225,7 @@ public int getOrder() { @Requires(property = "spec.name", value = "FilterErrorSpec3") @Filter(Filter.MATCH_ALL_PATTERN) - class ExceptionException implements HttpServerFilter { + static class ExceptionException implements HttpServerFilter { AtomicInteger executedCount = new AtomicInteger(0); AtomicReference responseStatus = new AtomicReference<>(); @@ -247,7 +248,7 @@ public int getOrder() { @Requires(property = "spec.name", value = "FilterErrorSpec4") @Filter(Filter.MATCH_ALL_PATTERN) - class ExceptionRoute implements HttpServerFilter { + static class ExceptionRoute implements HttpServerFilter { AtomicReference> routeMatch = new AtomicReference<>(); @Override @@ -264,7 +265,7 @@ public int getOrder() { @Requires(condition = FilterCondition.class) @Controller("/filter-error-spec") - class NeverReachedController { + static class NeverReachedController { @Get String get() { return "OK"; @@ -273,7 +274,7 @@ String get() { @Requires(condition = FilterCondition.class) @Controller("/filter-error-spec-3") - class HandledByHandlerController { + static class HandledByHandlerController { @Get String get() { throw new FilterExceptionException(); @@ -282,7 +283,7 @@ String get() { @Requires(condition = FilterCondition.class) @Controller("/filter-error-spec-4") - class HandledByErrorRouteController { + static class HandledByErrorRouteController { @Get("/exception") String getException() { throw new FilterExceptionException(); @@ -306,7 +307,7 @@ void testStatus() { } } - class FilterCondition implements Condition { + static class FilterCondition implements Condition { @Override public boolean matches(ConditionContext context) { @@ -318,7 +319,7 @@ public boolean matches(ConditionContext context) { @Requires(condition = FilterCondition.class) @Singleton - class FilterExceptionExceptionHandler implements ExceptionHandler> { + static class FilterExceptionExceptionHandler implements ExceptionHandler> { @Override public HttpResponse handle(HttpRequest request, FilterExceptionException exception) { @@ -328,7 +329,7 @@ public HttpResponse handle(HttpRequest request, FilterExceptionException exce @Requires(condition = FilterCondition.class) @Singleton - class FilterExceptionHandler implements ExceptionHandler> { + static class FilterExceptionHandler implements ExceptionHandler> { @Override public HttpResponse handle(HttpRequest request, FilterException exception) { @@ -338,7 +339,7 @@ public HttpResponse handle(HttpRequest request, FilterException exception) { @Requires(condition = FilterCondition.class) @Singleton - class NextFilterExceptionHandler implements ExceptionHandler> { + static class NextFilterExceptionHandler implements ExceptionHandler> { @Override public HttpResponse handle(HttpRequest request, NextFilterException exception) { diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FiltersTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FiltersTest.java index 7653f23e7c6..160b0824ee1 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FiltersTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FiltersTest.java @@ -47,11 +47,12 @@ @SuppressWarnings({ "java:S5960", // We're allowed assertions, as these are used in tests only "checkstyle:MissingJavadocType", + "checkstyle:DesignForExtension" }) -public interface FiltersTest { +public class FiltersTest { @Test - default void testFiltersAreRunCorrectly() throws IOException { + void testFiltersAreRunCorrectly() throws IOException { Map configuration = CollectionUtils.mapOf( "micronaut.server.cors.enabled", StringUtils.TRUE ); @@ -67,7 +68,7 @@ default void testFiltersAreRunCorrectly() throws IOException { } @Test - default void filtersAreAppliedOnNonMatchingMethodsCorsFilterWorks() throws IOException { + void filtersAreAppliedOnNonMatchingMethodsCorsFilterWorks() throws IOException { Map configuration = CollectionUtils.mapOf( "micronaut.server.cors.enabled", StringUtils.TRUE ); @@ -82,7 +83,7 @@ default void filtersAreAppliedOnNonMatchingMethodsCorsFilterWorks() throws IOExc } @Test - default void filtersAreAppliedOnNonMatchingMethodsCorsFilterDisableIfNotPreflight() throws IOException { + void filtersAreAppliedOnNonMatchingMethodsCorsFilterDisableIfNotPreflight() throws IOException { Map configuration = CollectionUtils.mapOf( "micronaut.server.cors.enabled", StringUtils.TRUE ); @@ -95,7 +96,7 @@ default void filtersAreAppliedOnNonMatchingMethodsCorsFilterDisableIfNotPrefligh } @Test - default void testFiltersAreRunCorrectlyWithCustomExceptionHandler() throws IOException { + void testFiltersAreRunCorrectlyWithCustomExceptionHandler() throws IOException { Map configuration = CollectionUtils.mapOf( "micronaut.server.cors.enabled", StringUtils.TRUE ); @@ -125,7 +126,7 @@ void exception() { @Filter("/filter-test/**") @Requires(property = "spec.name", value = "FiltersTest") - class TestFilter implements HttpServerFilter { + static class TestFilter implements HttpServerFilter { @Override public Publisher> doFilter(HttpRequest request, ServerFilterChain chain) { return Publishers.map(chain.proceed(request), httpResponse -> { @@ -141,7 +142,7 @@ static class CustomException extends RuntimeException { @Produces @Singleton @Requires(property = "spec.name", value = "FiltersTest") - class CustomExceptionHandler implements ExceptionHandler> { + static class CustomExceptionHandler implements ExceptionHandler> { @Override public HttpResponse handle(HttpRequest request, CustomException exception) { return HttpResponse.ok("Exception Handled"); diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FluxTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FluxTest.java index 2aad393c0fd..dc75bf6714f 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FluxTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FluxTest.java @@ -34,11 +34,12 @@ @SuppressWarnings({ "java:S5960", // We're allowed assertions, as these are used in tests only "checkstyle:MissingJavadocType", + "checkstyle:DesignForExtension" }) -public interface FluxTest { +public class FluxTest { @Test - default void testControllerReturningAFlux() throws IOException { + void testControllerReturningAFlux() throws IOException { TestScenario.builder() .specName("FluxTest") .request(HttpRequest.GET("/users")) @@ -51,7 +52,7 @@ default void testControllerReturningAFlux() throws IOException { @Controller("/users") @Requires(property = "spec.name", value = "FluxTest") - class UserController { + static class UserController { @Get Flux> getAll() { return Flux.fromIterable(Arrays.asList(Collections.singletonMap("name", "Joe"), Collections.singletonMap("name", "Lewis"))); diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/HelloWorldTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/HelloWorldTest.java index 7694b83e481..dee6f2dfe7f 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/HelloWorldTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/HelloWorldTest.java @@ -35,11 +35,12 @@ @SuppressWarnings({ "java:S5960", // We're allowed assertions, as these are used in tests only "checkstyle:MissingJavadocType", + "checkstyle:DesignForExtension" }) -public interface HelloWorldTest { +public class HelloWorldTest { @Test - default void helloWorld() throws IOException { + void helloWorld() throws IOException { TestScenario.builder() .specName("HelloWorldTest") .request(HttpRequest.GET(UriBuilder.of("/hello").path("world").build()).accept(MediaType.TEXT_PLAIN)) @@ -52,7 +53,7 @@ default void helloWorld() throws IOException { @Requires(property = "spec.name", value = "HelloWorldTest") @Controller("/hello") - class HelloWorldController { + static class HelloWorldController { @Produces(MediaType.TEXT_PLAIN) @Get("/world") String hello() { diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/MiscTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/MiscTest.java index 2049c16d128..1a0ed46a9ee 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/MiscTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/MiscTest.java @@ -42,14 +42,15 @@ @SuppressWarnings({ "java:S5960", // We're allowed assertions, as these are used in tests only "checkstyle:MissingJavadocType", + "checkstyle:DesignForExtension" }) -public interface MiscTest { +public class MiscTest { /** * * @see micronaut-aws #868 */ @Test - default void testSelectedRouteReflectsAcceptHeader() throws IOException { + void testSelectedRouteReflectsAcceptHeader() throws IOException { TestScenario.builder() .specName("MiscTest") .request(HttpRequest.GET("/bar/ok").header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON)) @@ -71,7 +72,7 @@ default void testSelectedRouteReflectsAcceptHeader() throws IOException { } @Test - default void testBehaviourOf404() throws IOException { + void testBehaviourOf404() throws IOException { TestScenario.builder() .specName("MiscTest") .request(HttpRequest.GET("/does-not-exist").header("Accept", MediaType.APPLICATION_JSON)) @@ -82,7 +83,7 @@ default void testBehaviourOf404() throws IOException { } @Test - default void postFormUrlEncodedBodyBindingToPojoWorks() throws IOException { + void postFormUrlEncodedBodyBindingToPojoWorks() throws IOException { TestScenario.builder() .specName("MiscTest") .request(HttpRequest.POST("/form", "message=World") @@ -95,7 +96,7 @@ default void postFormUrlEncodedBodyBindingToPojoWorks() throws IOException { } @Test - default void postFormUrlEncodedBodyBindingToPojoWorksIfYouDontSpecifyBodyAnnotation() throws IOException { + void postFormUrlEncodedBodyBindingToPojoWorksIfYouDontSpecifyBodyAnnotation() throws IOException { TestScenario.builder() .specName("MiscTest") .request(HttpRequest.POST("/form/without-body-annotation", "message=World") @@ -108,7 +109,7 @@ default void postFormUrlEncodedBodyBindingToPojoWorksIfYouDontSpecifyBodyAnnotat } @Test - default void formUrlEncodedWithBodyAnnotationAndANestedAttribute() throws IOException { + void formUrlEncodedWithBodyAnnotationAndANestedAttribute() throws IOException { TestScenario.builder() .specName("MiscTest") .request(HttpRequest.POST("/form/nested-attribute", "message=World") @@ -125,7 +126,7 @@ default void formUrlEncodedWithBodyAnnotationAndANestedAttribute() throws IOExce * @see micronaut-aws #1410 */ @Test - default void applicationJsonWithBodyAnnotationAndANestedAttribute() throws IOException { + void applicationJsonWithBodyAnnotationAndANestedAttribute() throws IOException { TestScenario.builder() .specName("MiscTest") .request(HttpRequest.POST("/form/json-nested-attribute", "{\"message\":\"World\"}") @@ -138,7 +139,7 @@ default void applicationJsonWithBodyAnnotationAndANestedAttribute() throws IOExc } @Test - default void applicationJsonWithoutBodyAnnotation() throws IOException { + void applicationJsonWithoutBodyAnnotation() throws IOException { TestScenario.builder() .specName("MiscTest") .request(HttpRequest.POST("/form/json-without-body-annotation", "{\"message\":\"World\"}") @@ -151,7 +152,7 @@ default void applicationJsonWithoutBodyAnnotation() throws IOException { } @Test - default void applicationJsonWithBodyAnnotationAndANestedAttributeAndMapReturnRenderedAsJSON() throws IOException { + void applicationJsonWithBodyAnnotationAndANestedAttributeAndMapReturnRenderedAsJSON() throws IOException { TestScenario.builder() .specName("MiscTest") .request(HttpRequest.POST("/form/json-nested-attribute-with-map-return", "{\"message\":\"World\"}") @@ -164,7 +165,7 @@ default void applicationJsonWithBodyAnnotationAndANestedAttributeAndMapReturnRen } @Test - default void applicationJsonWithBodyAnnotationAndObjectReturnRenderedAsJson() throws IOException { + void applicationJsonWithBodyAnnotationAndObjectReturnRenderedAsJson() throws IOException { TestScenario.builder() .specName("MiscTest") .request(HttpRequest.POST("/form/json-with-body-annotation-and-with-object-return", "{\"message\":\"World\"}") @@ -178,7 +179,7 @@ default void applicationJsonWithBodyAnnotationAndObjectReturnRenderedAsJson() th @Controller @Requires(property = "spec.name", value = "MiscTest") - class SimpleController { + static class SimpleController { @Get(uri = "/foo") HttpResponse getParamValue(HttpRequest request) { return HttpResponse.ok() @@ -189,7 +190,7 @@ HttpResponse getParamValue(HttpRequest request) { @Controller("/bar") @Requires(property = "spec.name", value = "MiscTest") - class ProduceController { + static class ProduceController { @Get(value = "/ok", produces = MediaType.APPLICATION_JSON) String getOkAsJson() { return "{\"status\":\"ok\"}"; @@ -202,7 +203,7 @@ String getOkAsHtml() { } @Introspected - class MessageCreate { + static class MessageCreate { @NonNull @NotBlank @@ -219,7 +220,7 @@ String getMessage() { } @Introspected - class MyResponse { + static class MyResponse { @NonNull @NotBlank @@ -237,7 +238,7 @@ public String getGreeting() { @Controller("/form") @Requires(property = "spec.name", value = "MiscTest") - class FormController { + static class FormController { @Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Post("/without-body-annotation") diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ParameterTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ParameterTest.java index 8b68c3e89e7..34067050a6f 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ParameterTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ParameterTest.java @@ -33,10 +33,11 @@ @SuppressWarnings({ "java:S5960", // We're allowed assertions, as these are used in tests only "checkstyle:MissingJavadocType", + "checkstyle:DesignForExtension" }) -public interface ParameterTest { +public class ParameterTest { @Test - default void testGetAllMethod() throws IOException { + void testGetAllMethod() throws IOException { TestScenario.builder() .specName("ParameterTest") .request(HttpRequest.GET(UriBuilder.of("/parameters-test").path("all") diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/RemoteAddressTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/RemoteAddressTest.java index bbf06289af4..faf7627eb65 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/RemoteAddressTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/RemoteAddressTest.java @@ -42,11 +42,12 @@ @SuppressWarnings({ "java:S5960", // We're allowed assertions, as these are used in tests only "checkstyle:MissingJavadocType", + "checkstyle:DesignForExtension" }) -public interface RemoteAddressTest { +public class RemoteAddressTest { @Test - default void testRemoteAddressComesFromIdentitySourceIp() throws IOException { + void testRemoteAddressComesFromIdentitySourceIp() throws IOException { TestScenario.builder() .specName("RemoteAddressTest") .request(HttpRequest.GET("/remoteAddress/fromSourceIp")) @@ -59,7 +60,7 @@ default void testRemoteAddressComesFromIdentitySourceIp() throws IOException { @Requires(property = "spec.name", value = "RemoteAddressTest") @Controller("/remoteAddress") - class TestController { + static class TestController { @Get("fromSourceIp") void sourceIp() { } @@ -67,7 +68,7 @@ void sourceIp() { @Requires(property = "spec.name", value = "RemoteAddressTest") @Filter("/remoteAddress/**") - class CaptureRemoteAddressFiter implements HttpServerFilter { + static class CaptureRemoteAddressFiter implements HttpServerFilter { @Override public Publisher> doFilter(HttpRequest request, ServerFilterChain chain) { return Publishers.map(chain.proceed(request), httpResponse -> { @@ -80,7 +81,7 @@ public Publisher> doFilter(HttpRequest request, Server @Requires(property = "spec.name", value = "RemoteAddressTest") @Produces @Singleton - class CustomExceptionHandler implements ExceptionHandler { + static class CustomExceptionHandler implements ExceptionHandler { @Override public HttpResponse handle(HttpRequest request, Exception exception) { return HttpResponse.serverError(exception.toString()); diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ResponseStatusTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ResponseStatusTest.java index 9a056a3a56b..853e1106569 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ResponseStatusTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ResponseStatusTest.java @@ -40,11 +40,12 @@ @SuppressWarnings({ "java:S5960", // We're allowed assertions, as these are used in tests only "checkstyle:MissingJavadocType", + "checkstyle:DesignForExtension" }) -public interface ResponseStatusTest { +public class ResponseStatusTest { @Test - default void testConstraintViolationCauses400() throws IOException { + void testConstraintViolationCauses400() throws IOException { TestScenario.builder() .specName("ResponseStatusSpec") .request(HttpRequest.POST("/response-status/constraint-violation", Collections.emptyMap()).header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)) @@ -55,7 +56,7 @@ default void testConstraintViolationCauses400() throws IOException { } @Test - default void testVoidMethodsDoesNotCause404() throws IOException { + void testVoidMethodsDoesNotCause404() throws IOException { TestScenario.builder() .specName("ResponseStatusSpec") .request(HttpRequest.DELETE("/response-status/delete-something") @@ -67,7 +68,7 @@ default void testVoidMethodsDoesNotCause404() throws IOException { } @Test - default void testNullCauses404() throws IOException { + void testNullCauses404() throws IOException { TestScenario.builder() .request(HttpRequest.GET("/response-status/null").header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)) .specName("ResponseStatusSpec") @@ -78,7 +79,7 @@ default void testNullCauses404() throws IOException { } @Test - default void testOptionalCauses404() throws IOException { + void testOptionalCauses404() throws IOException { TestScenario.builder() .specName("ResponseStatusSpec") .request(HttpRequest.GET("/response-status/optional").header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)) @@ -89,7 +90,7 @@ default void testOptionalCauses404() throws IOException { } @Test - default void testCustomResponseStatus() throws IOException { + void testCustomResponseStatus() throws IOException { TestScenario.builder() .specName("ResponseStatusSpec") .request(HttpRequest.POST("/response-status", "foo") @@ -103,7 +104,7 @@ default void testCustomResponseStatus() throws IOException { @Controller("/response-status") @Requires(property = "spec.name", value = "ResponseStatusSpec") - class StatusController { + static class StatusController { @Post(uri = "/", processes = MediaType.TEXT_PLAIN) @Status(HttpStatus.CREATED) diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/StatusTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/StatusTest.java index c7f5db780f1..22b37f1f95b 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/StatusTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/StatusTest.java @@ -37,15 +37,16 @@ @SuppressWarnings({ "java:S5960", // We're allowed assertions, as these are used in tests only "checkstyle:MissingJavadocType", + "checkstyle:DesignForExtension" }) -public interface StatusTest { +public class StatusTest { /** * @see micronaut-aws #1387 * @param path Request Path */ @ParameterizedTest @ValueSource(strings = {"/http-status", "/http-response-status", "/http-exception"}) - default void testControllerReturningHttpStatus(String path) throws IOException { + void testControllerReturningHttpStatus(String path) throws IOException { TestScenario.builder() .specName("StatusSpec") .request(HttpRequest.GET(path)) @@ -57,7 +58,7 @@ default void testControllerReturningHttpStatus(String path) throws IOException { @Requires(property = "spec.name", value = "StatusSpec") @Controller("/http-status") - class HttpStatusController { + static class HttpStatusController { @Get HttpStatus index() { return HttpStatus.I_AM_A_TEAPOT; @@ -66,7 +67,7 @@ HttpStatus index() { @Requires(property = "spec.name", value = "StatusSpec") @Controller("/http-response-status") - class HttpResponseStatusController { + static class HttpResponseStatusController { @Get HttpResponse index() { @@ -76,7 +77,7 @@ HttpResponse index() { @Requires(property = "spec.name", value = "StatusSpec") @Controller("/http-exception") - class HttpResponseErrorController { + static class HttpResponseErrorController { @Get HttpResponse index() { @@ -84,12 +85,12 @@ HttpResponse index() { } } - class TeapotException extends RuntimeException { + static class TeapotException extends RuntimeException { } @Produces @Singleton - class TeapotExceptionHandler implements ExceptionHandler> { + static class TeapotExceptionHandler implements ExceptionHandler> { private final ErrorResponseProcessor errorResponseProcessor; TeapotExceptionHandler(ErrorResponseProcessor errorResponseProcessor) { diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/VersionTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/VersionTest.java index 254da5ab494..686d3f2ebb0 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/VersionTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/VersionTest.java @@ -29,15 +29,15 @@ import org.junit.jupiter.api.Test; import java.io.IOException; - @SuppressWarnings({ "java:S5960", // We're allowed assertions, as these are used in tests only "checkstyle:MissingJavadocType", + "checkstyle:DesignForExtension" }) -public interface VersionTest { +public class VersionTest { @Test - default void testControllerMethodWithVersion2() throws IOException { + void testControllerMethodWithVersion2() throws IOException { TestScenario.builder() .configuration(CollectionUtils.mapOf( "micronaut.router.versioning.enabled", StringUtils.TRUE, @@ -53,7 +53,7 @@ default void testControllerMethodWithVersion2() throws IOException { @Controller("/version") @Requires(property = "spec.name", value = "VersionSpec") - class ConsumesController { + static class ConsumesController { @Get("/ping") String pingV1() { diff --git a/test-suite-http-server-tck-netty/build.gradle.kts b/test-suite-http-server-tck-netty/build.gradle.kts index ffc1abf9655..1e36c4d3594 100644 --- a/test-suite-http-server-tck-netty/build.gradle.kts +++ b/test-suite-http-server-tck-netty/build.gradle.kts @@ -5,4 +5,5 @@ dependencies { testImplementation(projects.httpServerNetty) testImplementation(projects.httpClient) testImplementation(projects.httpServerTck) + testImplementation(libs.junit.platform.engine) } diff --git a/test-suite-http-server-tck-netty/src/test/java/io/micronaut/http/server/tck/netty/tests/NettyHttpServerTestSuite.java b/test-suite-http-server-tck-netty/src/test/java/io/micronaut/http/server/tck/netty/tests/NettyHttpServerTestSuite.java index 949049a025f..74d27fb1ce1 100644 --- a/test-suite-http-server-tck-netty/src/test/java/io/micronaut/http/server/tck/netty/tests/NettyHttpServerTestSuite.java +++ b/test-suite-http-server-tck-netty/src/test/java/io/micronaut/http/server/tck/netty/tests/NettyHttpServerTestSuite.java @@ -1,6 +1,11 @@ package io.micronaut.http.server.tck.netty.tests; -import io.micronaut.http.server.tck.HttpServerTestSuite; +import org.junit.platform.suite.api.SelectPackages; +import org.junit.platform.suite.api.Suite; +import org.junit.platform.suite.api.SuiteDisplayName; -public class NettyHttpServerTestSuite implements HttpServerTestSuite { +@Suite +@SelectPackages("io.micronaut.http.server.tck.tests") +@SuiteDisplayName("HTTP Server TCK for Netty") +public class NettyHttpServerTestSuite { } From 8be62322ff4ff3c833492771b2cd883a1fcca08a Mon Sep 17 00:00:00 2001 From: Sergio del Amo Date: Tue, 20 Dec 2022 01:59:59 +0100 Subject: [PATCH 03/13] Update build.gradle --- test-suite-javax-inject/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/test-suite-javax-inject/build.gradle b/test-suite-javax-inject/build.gradle index ce67ef463c4..e39503e43b5 100644 --- a/test-suite-javax-inject/build.gradle +++ b/test-suite-javax-inject/build.gradle @@ -1,6 +1,7 @@ plugins { id "io.micronaut.build.internal.convention-test-library" } + dependencies { testAnnotationProcessor project(":inject-java") testCompileOnly project(":inject-groovy") From 7eff7d23f089059e768237ff582a179b6b1fe8f8 Mon Sep 17 00:00:00 2001 From: Sergio del Amo Date: Tue, 20 Dec 2022 07:20:55 +0100 Subject: [PATCH 04/13] allow assertions --- .../main/java/io/micronaut/http/server/tck/AssertionUtils.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/AssertionUtils.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/AssertionUtils.java index 63703937765..5b473f412a2 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/AssertionUtils.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/AssertionUtils.java @@ -37,6 +37,9 @@ * @author Sergio del Amo * @since 3.8.0 */ +@SuppressWarnings({ + "java:S5960", // We're allowed assertions, as these are used in tests only +}) public final class AssertionUtils { private AssertionUtils() { From 71632d91def88f923c40de89c8ade45db4d9e293 Mon Sep 17 00:00:00 2001 From: Sergio del Amo Date: Tue, 20 Dec 2022 07:22:18 +0100 Subject: [PATCH 05/13] annotate with @Experimiental --- .../main/java/io/micronaut/http/server/tck/AssertionUtils.java | 2 ++ .../io/micronaut/http/server/tck/EmbeddedServerUnderTest.java | 2 ++ .../http/server/tck/EmbeddedServerUnderTestProvider.java | 2 ++ .../io/micronaut/http/server/tck/HttpResponseAssertion.java | 2 ++ .../io/micronaut/http/server/tck/ServerUnderTestProvider.java | 2 ++ .../micronaut/http/server/tck/ServerUnderTestProviderUtils.java | 2 ++ .../main/java/io/micronaut/http/server/tck/TestScenario.java | 2 ++ 7 files changed, 14 insertions(+) diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/AssertionUtils.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/AssertionUtils.java index 5b473f412a2..a3f1f0710e5 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/AssertionUtils.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/AssertionUtils.java @@ -15,6 +15,7 @@ */ package io.micronaut.http.server.tck; +import io.micronaut.core.annotation.Experimental; import io.micronaut.core.annotation.NonNull; import io.micronaut.core.annotation.Nullable; import io.micronaut.http.HttpHeaders; @@ -40,6 +41,7 @@ @SuppressWarnings({ "java:S5960", // We're allowed assertions, as these are used in tests only }) +@Experimental public final class AssertionUtils { private AssertionUtils() { diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/EmbeddedServerUnderTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/EmbeddedServerUnderTest.java index 22d8fda9bde..115feccf611 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/EmbeddedServerUnderTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/EmbeddedServerUnderTest.java @@ -16,6 +16,7 @@ package io.micronaut.http.server.tck; import io.micronaut.context.ApplicationContext; +import io.micronaut.core.annotation.Experimental; import io.micronaut.core.annotation.NonNull; import io.micronaut.core.type.Argument; import io.micronaut.http.HttpRequest; @@ -33,6 +34,7 @@ * @author Sergio del Amo * @since 3.0.0 */ +@Experimental public class EmbeddedServerUnderTest implements ServerUnderTest { private EmbeddedServer embeddedServer; diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/EmbeddedServerUnderTestProvider.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/EmbeddedServerUnderTestProvider.java index 8f69830bc78..f6708f64620 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/EmbeddedServerUnderTestProvider.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/EmbeddedServerUnderTestProvider.java @@ -15,6 +15,7 @@ */ package io.micronaut.http.server.tck; +import io.micronaut.core.annotation.Experimental; import io.micronaut.core.annotation.NonNull; import java.util.Map; @@ -24,6 +25,7 @@ * @author Sergio del Amo * @since 3.8.0 */ +@Experimental public class EmbeddedServerUnderTestProvider implements ServerUnderTestProvider { @Override @NonNull diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/HttpResponseAssertion.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/HttpResponseAssertion.java index 88d213636a3..690a43c7dd0 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/HttpResponseAssertion.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/HttpResponseAssertion.java @@ -15,6 +15,7 @@ */ package io.micronaut.http.server.tck; +import io.micronaut.core.annotation.Experimental; import io.micronaut.http.HttpStatus; import java.util.Map; @@ -25,6 +26,7 @@ * @author Sergio del Amo * @since 3.8.0 */ +@Experimental public final class HttpResponseAssertion { private final HttpStatus httpStatus; diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/ServerUnderTestProvider.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/ServerUnderTestProvider.java index df0b39b1a5e..2ea500fb918 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/ServerUnderTestProvider.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/ServerUnderTestProvider.java @@ -15,6 +15,7 @@ */ package io.micronaut.http.server.tck; +import io.micronaut.core.annotation.Experimental; import io.micronaut.core.annotation.NonNull; import java.util.Collections; @@ -26,6 +27,7 @@ * @author Sergio del Amo * @since 3.8.0 */ +@Experimental @FunctionalInterface public interface ServerUnderTestProvider { diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/ServerUnderTestProviderUtils.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/ServerUnderTestProviderUtils.java index 4a0b25d9dab..ff9bdede0a8 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/ServerUnderTestProviderUtils.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/ServerUnderTestProviderUtils.java @@ -16,6 +16,7 @@ package io.micronaut.http.server.tck; import io.micronaut.context.exceptions.ConfigurationException; +import io.micronaut.core.annotation.Experimental; import io.micronaut.core.annotation.NonNull; import java.util.Iterator; @@ -26,6 +27,7 @@ * @author Sergio del Amo * @since 3.8.0 */ +@Experimental public final class ServerUnderTestProviderUtils { private ServerUnderTestProviderUtils() { diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/TestScenario.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/TestScenario.java index 10b8a92fc72..be76baa49d1 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/TestScenario.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/TestScenario.java @@ -15,6 +15,7 @@ */ package io.micronaut.http.server.tck; +import io.micronaut.core.annotation.Experimental; import io.micronaut.http.HttpRequest; import java.io.IOException; import java.util.Map; @@ -26,6 +27,7 @@ * @author Sergio del Amo * @since 3.8.0 */ +@Experimental public final class TestScenario { private final String specName; private final Map configuration; From ebf86e99a8e8e3e94a7b33d7abe789e775cef21b Mon Sep 17 00:00:00 2001 From: Sergio del Amo Date: Tue, 20 Dec 2022 07:32:36 +0100 Subject: [PATCH 06/13] define SPEC_NAME constant --- .../server/tck/tests/BodyArgumentTest.java | 6 +- .../http/server/tck/tests/BodyTest.java | 9 +- .../http/server/tck/tests/ConsumesTest.java | 6 +- .../http/server/tck/tests/CookiesTest.java | 9 +- .../tck/tests/DeleteWithoutBodyTest.java | 4 +- .../server/tck/tests/ErrorHandlerTest.java | 85 ++++++++++--------- .../server/tck/tests/FilterErrorTest.java | 12 +-- .../http/server/tck/tests/FiltersTest.java | 15 ++-- .../http/server/tck/tests/FluxTest.java | 5 +- .../http/server/tck/tests/HelloWorldTest.java | 5 +- .../http/server/tck/tests/MiscTest.java | 28 +++--- .../http/server/tck/tests/ParameterTest.java | 6 +- .../server/tck/tests/RemoteAddressTest.java | 9 +- .../server/tck/tests/ResponseStatusTest.java | 13 +-- .../http/server/tck/tests/StatusTest.java | 10 ++- .../http/server/tck/tests/VersionTest.java | 5 +- 16 files changed, 127 insertions(+), 100 deletions(-) diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/BodyArgumentTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/BodyArgumentTest.java index 572b89e04af..66351ec9a01 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/BodyArgumentTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/BodyArgumentTest.java @@ -35,13 +35,15 @@ "checkstyle:DesignForExtension" }) public class BodyArgumentTest { + public static final String SPEC_NAME = "BodyArgumentTest"; + /** * @see micronaut-aws #1164 */ @Test void testBodyArguments() throws IOException { TestScenario.builder() - .specName("BodyArgumentTest") + .specName(SPEC_NAME) .request(HttpRequest.POST("/body-arguments-test/getA", "{\"a\":\"A\",\"b\":\"B\"}").header(HttpHeaders.ACCEPT, MediaType.TEXT_PLAIN)) .assertion(((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.OK) @@ -51,7 +53,7 @@ void testBodyArguments() throws IOException { } @Controller("/body-arguments-test") - @Requires(property = "spec.name", value = "BodyArgumentTest") + @Requires(property = "spec.name", value = SPEC_NAME) static class BodyController { @Post(uri = "/getA") diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/BodyTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/BodyTest.java index e0d45f05824..02ecad164cd 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/BodyTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/BodyTest.java @@ -40,6 +40,7 @@ "checkstyle:DesignForExtension" }) public class BodyTest { + public static final String SPEC_NAME = "BodyTest"; @Test void testCustomBodyPOJO() throws IOException { @@ -58,7 +59,7 @@ void testCustomBodyPOJO() throws IOException { @Test void testCustomBodyPOJODefaultToJSON() throws IOException { TestScenario.builder() - .specName("BodyTest") + .specName(SPEC_NAME) .request(HttpRequest.POST("/response-body/pojo", "{\"x\":10,\"y\":20}")) .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() @@ -70,7 +71,7 @@ void testCustomBodyPOJODefaultToJSON() throws IOException { @Test void testCustomBodyPOJOWithWholeRequest() throws IOException { TestScenario.builder() - .specName("BodyTest") + .specName(SPEC_NAME) .request(HttpRequest.POST("/response-body/pojo-and-request", "{\"x\":10,\"y\":20}") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) .assertion((server, request) -> HttpResponseAssertion.builder() @@ -83,7 +84,7 @@ void testCustomBodyPOJOWithWholeRequest() throws IOException { @Test void testCustomBodyPOJOReactiveTypes() throws IOException { TestScenario.builder() - .specName("BodyTest") + .specName(SPEC_NAME) .request(HttpRequest.POST("/response-body/pojo-reactive", "{\"x\":10,\"y\":20}") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, @@ -95,7 +96,7 @@ void testCustomBodyPOJOReactiveTypes() throws IOException { } @Controller("/response-body") - @Requires(property = "spec.name", value = "BodyTest") + @Requires(property = "spec.name", value = SPEC_NAME) static class BodyController { @Post(uri = "/pojo") diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ConsumesTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ConsumesTest.java index 0735fe2efb7..25f07c7f110 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ConsumesTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ConsumesTest.java @@ -36,10 +36,12 @@ "checkstyle:DesignForExtension" }) public class ConsumesTest { + public static final String SPEC_NAME = "ConsumesTest"; + @Test void testMultipleConsumesDefinition() throws IOException { TestScenario.builder() - .specName("ConsumesTest") + .specName(SPEC_NAME) .request(HttpRequest.POST("/consumes-test", "{\"name\":\"Fred\"}").header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.OK) @@ -49,7 +51,7 @@ void testMultipleConsumesDefinition() throws IOException { } @Controller("/consumes-test") - @Requires(property = "spec.name", value = "ConsumesTest") + @Requires(property = "spec.name", value = SPEC_NAME) static class ConsumesController { @Post("/") diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/CookiesTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/CookiesTest.java index d45437a875f..1aa60ef3de6 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/CookiesTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/CookiesTest.java @@ -38,11 +38,12 @@ "checkstyle:DesignForExtension" }) public class CookiesTest { + public static final String SPEC_NAME = "CookiesTest"; @Test void testCookieBind() throws IOException { TestScenario.builder() - .specName("CookiesTest") + .specName(SPEC_NAME) .request(HttpRequest.GET("/cookies-test/bind") .cookie(Cookie.of("one", "foo")) .cookie(Cookie.of("two", "bar"))) @@ -56,7 +57,7 @@ void testCookieBind() throws IOException { @Test void testGetCookiesMethod() throws IOException { TestScenario.builder() - .specName("CookiesTest") + .specName(SPEC_NAME) .request(HttpRequest.GET("/cookies-test/all") .cookie(Cookie.of("one", "foo")) .cookie(Cookie.of("two", "bar"))) @@ -70,7 +71,7 @@ void testGetCookiesMethod() throws IOException { @Test void testNoCookie() throws IOException { TestScenario.builder() - .specName("CookiesTest") + .specName(SPEC_NAME) .request(HttpRequest.GET("/cookies-test/all")) .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.OK) @@ -80,7 +81,7 @@ void testNoCookie() throws IOException { } @Controller("/cookies-test") - @Requires(property = "spec.name", value = "CookiesTest") + @Requires(property = "spec.name", value = SPEC_NAME) static class CookieController { @Get(uri = "/all") diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/DeleteWithoutBodyTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/DeleteWithoutBodyTest.java index 1c5da8a08f7..202c5904606 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/DeleteWithoutBodyTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/DeleteWithoutBodyTest.java @@ -40,6 +40,8 @@ "checkstyle:DesignForExtension" }) public class DeleteWithoutBodyTest { + public static final String SPEC_NAME = "DeleteWithoutBodyTest"; + @Test void verifiesItIsPossibleToExposesADeleteEndpointWhichIsInvokedWithoutABody() throws IOException { TestScenario.builder() @@ -52,7 +54,7 @@ void verifiesItIsPossibleToExposesADeleteEndpointWhichIsInvokedWithoutABody() th .run(); } - @Requires(property = "spec.name", value = "DeleteWithoutBodyTest") + @Requires(property = "spec.name", value = SPEC_NAME) @Controller("/sessions") static class SessionsController { @Status(HttpStatus.OK) diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ErrorHandlerTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ErrorHandlerTest.java index 25ed31fa603..6172f5deba9 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ErrorHandlerTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ErrorHandlerTest.java @@ -58,14 +58,19 @@ }) public class ErrorHandlerTest { + public static final String SPEC_NAME = "ErrorHandlerTest"; + public static final String PROPERTY_MICRONAUT_SERVER_CORS_CONFIGURATIONS_WEB_ALLOWED_ORIGINS = "micronaut.server.cors.configurations.web.allowed-origins"; + public static final String PROPERTY_MICRONAUT_SERVER_CORS_ENABLED = "micronaut.server.cors.enabled"; + public static final String LOCALHOST = "http://localhost:8080"; + @Test void testCustomGlobalExceptionHandlersDeclaredInController() throws IOException { TestScenario.builder() .configuration(CollectionUtils.mapOf( - "micronaut.server.cors.configurations.web.allowedOrigins", Collections.singletonList("http://localhost:8080"), - "micronaut.server.cors.enabled", StringUtils.TRUE + PROPERTY_MICRONAUT_SERVER_CORS_CONFIGURATIONS_WEB_ALLOWED_ORIGINS, Collections.singletonList("http://localhost:8080"), + PROPERTY_MICRONAUT_SERVER_CORS_ENABLED, StringUtils.TRUE )) - .specName("ErrorHandlerTest") + .specName(SPEC_NAME) .request(HttpRequest.GET("/errors/global-ctrl") .header(HttpHeaders.CONTENT_TYPE, io.micronaut.http.MediaType.APPLICATION_JSON)) .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, @@ -79,10 +84,10 @@ void testCustomGlobalExceptionHandlersDeclaredInController() throws IOException void testCustomGlobalExceptionHandlers() throws IOException { TestScenario.builder() .configuration(CollectionUtils.mapOf( - "micronaut.server.cors.configurations.web.allowedOrigins", Collections.singletonList("http://localhost:8080"), - "micronaut.server.cors.enabled", StringUtils.TRUE + PROPERTY_MICRONAUT_SERVER_CORS_CONFIGURATIONS_WEB_ALLOWED_ORIGINS, Collections.singletonList("http://localhost:8080"), + PROPERTY_MICRONAUT_SERVER_CORS_ENABLED, StringUtils.TRUE )) - .specName("ErrorHandlerTest") + .specName(SPEC_NAME) .request(HttpRequest.GET("/errors/global") .header(HttpHeaders.CONTENT_TYPE, io.micronaut.http.MediaType.APPLICATION_JSON)) .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, @@ -95,10 +100,10 @@ void testCustomGlobalExceptionHandlers() throws IOException { @Test void testCustomGlobalExceptionHandlersForPOSTWithBody() throws IOException { Map configuration = CollectionUtils.mapOf( - "micronaut.server.cors.configurations.web.allowedOrigins", Collections.singletonList("http://localhost:8080"), - "micronaut.server.cors.enabled", StringUtils.TRUE + PROPERTY_MICRONAUT_SERVER_CORS_CONFIGURATIONS_WEB_ALLOWED_ORIGINS, Collections.singletonList("http://localhost:8080"), + PROPERTY_MICRONAUT_SERVER_CORS_ENABLED, StringUtils.TRUE ); - try (ServerUnderTest server = ServerUnderTestProviderUtils.getServerUnderTestProvider().getServer("ErrorHandlerTest", configuration)) { + try (ServerUnderTest server = ServerUnderTestProviderUtils.getServerUnderTestProvider().getServer(SPEC_NAME, configuration)) { ObjectMapper objectMapper = server.getApplicationContext().getBean(ObjectMapper.class); HttpRequest request = HttpRequest.POST("/json/errors/global", objectMapper.writeValueAsString(new RequestObject(101))) .header(HttpHeaders.CONTENT_TYPE, io.micronaut.http.MediaType.APPLICATION_JSON); @@ -113,10 +118,10 @@ void testCustomGlobalExceptionHandlersForPOSTWithBody() throws IOException { void testCustomGlobalStatusHandlersDeclaredInController() throws IOException { TestScenario.builder() .configuration(CollectionUtils.mapOf( - "micronaut.server.cors.configurations.web.allowedOrigins", Collections.singletonList("http://localhost:8080"), - "micronaut.server.cors.enabled", StringUtils.TRUE + PROPERTY_MICRONAUT_SERVER_CORS_CONFIGURATIONS_WEB_ALLOWED_ORIGINS, Collections.singletonList("http://localhost:8080"), + PROPERTY_MICRONAUT_SERVER_CORS_ENABLED, StringUtils.TRUE )) - .specName("ErrorHandlerTest") + .specName(SPEC_NAME) .request(HttpRequest.GET("/errors/global-status-ctrl")) .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpStatus.OK, @@ -129,9 +134,9 @@ void testCustomGlobalStatusHandlersDeclaredInController() throws IOException { void testLocalExceptionHandlers() throws IOException { TestScenario.builder() .configuration(CollectionUtils.mapOf( - "micronaut.server.cors.configurations.web.allowedOrigins", Collections.singletonList("http://localhost:8080"), - "micronaut.server.cors.enabled", StringUtils.TRUE)) - .specName("ErrorHandlerTest") + PROPERTY_MICRONAUT_SERVER_CORS_CONFIGURATIONS_WEB_ALLOWED_ORIGINS, Collections.singletonList("http://localhost:8080"), + PROPERTY_MICRONAUT_SERVER_CORS_ENABLED, StringUtils.TRUE)) + .specName(SPEC_NAME) .request(HttpRequest.GET("/errors/local")) .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpStatus.OK, @@ -144,9 +149,9 @@ void testLocalExceptionHandlers() throws IOException { void jsonMessageFormatErrorsReturn400() throws IOException { TestScenario.builder() .configuration(CollectionUtils.mapOf( - "micronaut.server.cors.configurations.web.allowedOrigins", Collections.singletonList("http://localhost:8080"), - "micronaut.server.cors.enabled", StringUtils.TRUE - )).specName("ErrorHandlerTest") + PROPERTY_MICRONAUT_SERVER_CORS_CONFIGURATIONS_WEB_ALLOWED_ORIGINS, Collections.singletonList("http://localhost:8080"), + PROPERTY_MICRONAUT_SERVER_CORS_ENABLED, StringUtils.TRUE + )).specName(SPEC_NAME) .request(HttpRequest.POST("/json/jsonBody", "{\"numberField\": \"textInsteadOfNumber\"}")) .assertion((server, request) -> AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() @@ -160,14 +165,14 @@ void jsonMessageFormatErrorsReturn400() throws IOException { void corsHeadersArePresentAfterFailedDeserialisationWhenErrorHandlerIsUsed() throws IOException { TestScenario.builder() .configuration(CollectionUtils.mapOf( - "micronaut.server.cors.configurations.web.allowedOrigins", Collections.singletonList("http://localhost:8080"), - "micronaut.server.cors.enabled", StringUtils.TRUE - )).specName("ErrorHandlerTest") + PROPERTY_MICRONAUT_SERVER_CORS_CONFIGURATIONS_WEB_ALLOWED_ORIGINS, Collections.singletonList("http://localhost:8080"), + PROPERTY_MICRONAUT_SERVER_CORS_ENABLED, StringUtils.TRUE + )).specName(SPEC_NAME) .request(HttpRequest.POST("/json/errors/global", "{\"numberField\": \"string is not a number\"}") - .header(HttpHeaders.ORIGIN, "http://localhost:8080")) + .header(HttpHeaders.ORIGIN, LOCALHOST)) .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.OK) - .headers(Collections.singletonMap(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "http://localhost:8080")) + .headers(Collections.singletonMap(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, LOCALHOST)) .build())) .run(); } @@ -176,15 +181,15 @@ void corsHeadersArePresentAfterFailedDeserialisationWhenErrorHandlerIsUsed() thr void corsHeadersArePresentAfterFailedDeserialisation() throws IOException { TestScenario.builder() .configuration(CollectionUtils.mapOf( - "micronaut.server.cors.configurations.web.allowedOrigins", Collections.singletonList("http://localhost:8080"), - "micronaut.server.cors.enabled", StringUtils.TRUE + PROPERTY_MICRONAUT_SERVER_CORS_CONFIGURATIONS_WEB_ALLOWED_ORIGINS, Collections.singletonList(LOCALHOST), + PROPERTY_MICRONAUT_SERVER_CORS_ENABLED, StringUtils.TRUE )) - .specName("ErrorHandlerTest") + .specName(SPEC_NAME) .request(HttpRequest.POST("/json/jsonBody", "{\"numberField\": \"string is not a number\"}") - .header(HttpHeaders.ORIGIN, "http://localhost:8080")) + .header(HttpHeaders.ORIGIN, LOCALHOST)) .assertion((server, request) -> AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() .status(HttpStatus.BAD_REQUEST) - .headers(Collections.singletonMap(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "http://localhost:8080")) + .headers(Collections.singletonMap(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, LOCALHOST)) .build())) .run(); } @@ -193,15 +198,15 @@ void corsHeadersArePresentAfterFailedDeserialisation() throws IOException { void corsHeadersArePresentAfterExceptions() throws IOException { TestScenario.builder() .configuration(CollectionUtils.mapOf( - "micronaut.server.cors.configurations.web.allowedOrigins", Collections.singletonList("http://localhost:8080"), - "micronaut.server.cors.enabled", StringUtils.TRUE + PROPERTY_MICRONAUT_SERVER_CORS_CONFIGURATIONS_WEB_ALLOWED_ORIGINS, Collections.singletonList(LOCALHOST), + PROPERTY_MICRONAUT_SERVER_CORS_ENABLED, StringUtils.TRUE )) - .specName("ErrorHandlerTest") + .specName(SPEC_NAME) .request(HttpRequest.GET("/errors/global") - .header(HttpHeaders.ORIGIN, "http://localhost:8080")) + .header(HttpHeaders.ORIGIN, LOCALHOST)) .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.OK) - .headers(Collections.singletonMap(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "http://localhost:8080")) + .headers(Collections.singletonMap(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, LOCALHOST)) .build())) .run(); } @@ -210,10 +215,10 @@ void corsHeadersArePresentAfterExceptions() throws IOException { void messageValidationErrorsReturn400() throws IOException { TestScenario.builder() .configuration(CollectionUtils.mapOf( - "micronaut.server.cors.configurations.web.allowedOrigins", Collections.singletonList("http://localhost:8080"), - "micronaut.server.cors.enabled", StringUtils.TRUE + PROPERTY_MICRONAUT_SERVER_CORS_CONFIGURATIONS_WEB_ALLOWED_ORIGINS, Collections.singletonList("http://localhost:8080"), + PROPERTY_MICRONAUT_SERVER_CORS_ENABLED, StringUtils.TRUE )) - .specName("ErrorHandlerTest") + .specName(SPEC_NAME) .request(HttpRequest.POST("/json/jsonBody", "{\"numberField\": 0}")) .assertion((server, request) -> AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() .status(HttpStatus.BAD_REQUEST) @@ -223,7 +228,7 @@ void messageValidationErrorsReturn400() throws IOException { } @Controller("/secret") - @Requires(property = "spec.name", value = "ErrorHandlerTest") + @Requires(property = "spec.name", value = SPEC_NAME) static class SecretController { @Get @Produces(MediaType.TEXT_PLAIN) @@ -232,7 +237,7 @@ String index() { } } - @Requires(property = "spec.name", value = "ErrorHandlerTest") + @Requires(property = "spec.name", value = SPEC_NAME) @Controller("/errors") static class ErrorController { @@ -266,7 +271,7 @@ String localHandler(AnotherException throwable) { } @Controller(value = "/json/errors", produces = io.micronaut.http.MediaType.APPLICATION_JSON) - @Requires(property = "spec.name", value = "ErrorHandlerTest") + @Requires(property = "spec.name", value = SPEC_NAME) static class JsonErrorController { @Post("/global") @@ -299,7 +304,7 @@ public Integer getNumberField() { } @Controller("/json") - @Requires(property = "spec.name", value = "ErrorHandlerTest") + @Requires(property = "spec.name", value = SPEC_NAME) static class JsonController { @Post("/jsonBody") String jsonBody(@Valid @Body RequestObject data) { diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FilterErrorTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FilterErrorTest.java index f748db55dfa..10078b4b216 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FilterErrorTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FilterErrorTest.java @@ -58,6 +58,8 @@ "checkstyle:DesignForExtension" }) public class FilterErrorTest { + public static final String SPEC_NAME = "FilterErrorTest"; + @Test void testFilterThrowingExceptionHandledByExceptionHandlerThrowingException() throws IOException { TestScenario.builder() @@ -118,7 +120,7 @@ void testErrorsEmittedFromSecondFilterInteractingWithExceptionHandlers() throws .request(HttpRequest.GET("/filter-error-spec"). header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON) .header("X-Passthru", StringUtils.TRUE)) - .specName("FilterErrorSpec") + .specName(SPEC_NAME) .assertion((server, request) -> { AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() @@ -137,7 +139,7 @@ void testErrorsEmittedFromSecondFilterInteractingWithExceptionHandlers() throws @Test void testErrorsEmittedFromFiltersInteractingWithExceptionHandlers() throws IOException { TestScenario.builder() - .specName("FilterErrorSpec") + .specName(SPEC_NAME) .request(HttpRequest.GET("/filter-error-spec").header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) .assertion((server, request) -> { AssertionUtils.assertThrows(server, request, @@ -164,7 +166,7 @@ static class FilterException extends RuntimeException { static class NextFilterException extends RuntimeException { } - @Requires(property = "spec.name", value = "FilterErrorSpec") + @Requires(property = "spec.name", value = SPEC_NAME) @Filter(Filter.MATCH_ALL_PATTERN) static class First implements HttpServerFilter { AtomicInteger executedCount = new AtomicInteger(0); @@ -189,7 +191,7 @@ public int getOrder() { } } - @Requires(property = "spec.name", value = "FilterErrorSpec") + @Requires(property = "spec.name", value = SPEC_NAME) @Filter(Filter.MATCH_ALL_PATTERN) static class Next implements HttpServerFilter { AtomicInteger executedCount = new AtomicInteger(0); @@ -312,7 +314,7 @@ static class FilterCondition implements Condition { @Override public boolean matches(ConditionContext context) { return context.getProperty("spec.name", String.class) - .map(val -> val.equals("FilterErrorSpec4") || val.equals("FilterErrorSpec3") || val.equals("FilterErrorSpec2") || val.equals("FilterErrorSpec")) + .map(val -> val.equals("FilterErrorSpec4") || val.equals("FilterErrorSpec3") || val.equals("FilterErrorSpec2") || val.equals(SPEC_NAME)) .orElse(false); } } diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FiltersTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FiltersTest.java index 160b0824ee1..1a196af0d00 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FiltersTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FiltersTest.java @@ -50,13 +50,14 @@ "checkstyle:DesignForExtension" }) public class FiltersTest { + public static final String SPEC_NAME = "FiltersTest"; @Test void testFiltersAreRunCorrectly() throws IOException { Map configuration = CollectionUtils.mapOf( "micronaut.server.cors.enabled", StringUtils.TRUE ); - try (ServerUnderTest server = ServerUnderTestProviderUtils.getServerUnderTestProvider().getServer("FiltersTest", configuration)) { + try (ServerUnderTest server = ServerUnderTestProviderUtils.getServerUnderTestProvider().getServer(SPEC_NAME, configuration)) { assertTrue(true); HttpRequest request = HttpRequest.GET("/filter-test/ok"); AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() @@ -72,7 +73,7 @@ void filtersAreAppliedOnNonMatchingMethodsCorsFilterWorks() throws IOException { Map configuration = CollectionUtils.mapOf( "micronaut.server.cors.enabled", StringUtils.TRUE ); - try (ServerUnderTest server = ServerUnderTestProviderUtils.getServerUnderTestProvider().getServer("FiltersTest", configuration)) { + try (ServerUnderTest server = ServerUnderTestProviderUtils.getServerUnderTestProvider().getServer(SPEC_NAME, configuration)) { HttpRequest request = HttpRequest.OPTIONS("/filter-test/ok").header("Origin", "https://micronaut.io") .header("Access-Control-Request-Method", "GET"); AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() @@ -87,7 +88,7 @@ void filtersAreAppliedOnNonMatchingMethodsCorsFilterDisableIfNotPreflight() thro Map configuration = CollectionUtils.mapOf( "micronaut.server.cors.enabled", StringUtils.TRUE ); - try (ServerUnderTest server = ServerUnderTestProviderUtils.getServerUnderTestProvider().getServer("FiltersTest", configuration)) { + try (ServerUnderTest server = ServerUnderTestProviderUtils.getServerUnderTestProvider().getServer(SPEC_NAME, configuration)) { HttpRequest request = HttpRequest.OPTIONS("/filter-test/ok"); AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() .status(HttpStatus.METHOD_NOT_ALLOWED) @@ -100,7 +101,7 @@ void testFiltersAreRunCorrectlyWithCustomExceptionHandler() throws IOException { Map configuration = CollectionUtils.mapOf( "micronaut.server.cors.enabled", StringUtils.TRUE ); - try (ServerUnderTest server = ServerUnderTestProviderUtils.getServerUnderTestProvider().getServer("FiltersTest", configuration)) { + try (ServerUnderTest server = ServerUnderTestProviderUtils.getServerUnderTestProvider().getServer(SPEC_NAME, configuration)) { HttpRequest request = HttpRequest.GET("/filter-test/exception"); AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.OK) @@ -111,7 +112,7 @@ void testFiltersAreRunCorrectlyWithCustomExceptionHandler() throws IOException { } @Controller("/filter-test") - @Requires(property = "spec.name", value = "FiltersTest") + @Requires(property = "spec.name", value = SPEC_NAME) static class TestController { @Get("/ok") String ok() { @@ -125,7 +126,7 @@ void exception() { } @Filter("/filter-test/**") - @Requires(property = "spec.name", value = "FiltersTest") + @Requires(property = "spec.name", value = SPEC_NAME) static class TestFilter implements HttpServerFilter { @Override public Publisher> doFilter(HttpRequest request, ServerFilterChain chain) { @@ -141,7 +142,7 @@ static class CustomException extends RuntimeException { @Produces @Singleton - @Requires(property = "spec.name", value = "FiltersTest") + @Requires(property = "spec.name", value = SPEC_NAME) static class CustomExceptionHandler implements ExceptionHandler> { @Override public HttpResponse handle(HttpRequest request, CustomException exception) { diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FluxTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FluxTest.java index dc75bf6714f..8394a788725 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FluxTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FluxTest.java @@ -37,11 +37,12 @@ "checkstyle:DesignForExtension" }) public class FluxTest { + public static final String SPEC_NAME = "FluxTest"; @Test void testControllerReturningAFlux() throws IOException { TestScenario.builder() - .specName("FluxTest") + .specName(SPEC_NAME) .request(HttpRequest.GET("/users")) .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.OK) @@ -51,7 +52,7 @@ void testControllerReturningAFlux() throws IOException { } @Controller("/users") - @Requires(property = "spec.name", value = "FluxTest") + @Requires(property = "spec.name", value = SPEC_NAME) static class UserController { @Get Flux> getAll() { diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/HelloWorldTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/HelloWorldTest.java index dee6f2dfe7f..a3d332f13da 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/HelloWorldTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/HelloWorldTest.java @@ -38,11 +38,12 @@ "checkstyle:DesignForExtension" }) public class HelloWorldTest { + public static final String SPEC_NAME = "HelloWorldTest"; @Test void helloWorld() throws IOException { TestScenario.builder() - .specName("HelloWorldTest") + .specName(SPEC_NAME) .request(HttpRequest.GET(UriBuilder.of("/hello").path("world").build()).accept(MediaType.TEXT_PLAIN)) .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpStatus.OK, @@ -51,7 +52,7 @@ void helloWorld() throws IOException { .run(); } - @Requires(property = "spec.name", value = "HelloWorldTest") + @Requires(property = "spec.name", value = SPEC_NAME) @Controller("/hello") static class HelloWorldController { @Produces(MediaType.TEXT_PLAIN) diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/MiscTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/MiscTest.java index 1a0ed46a9ee..1a490b450ab 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/MiscTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/MiscTest.java @@ -45,6 +45,8 @@ "checkstyle:DesignForExtension" }) public class MiscTest { + public static final String SPEC_NAME = "MiscTest"; + /** * * @see micronaut-aws #868 @@ -52,7 +54,7 @@ public class MiscTest { @Test void testSelectedRouteReflectsAcceptHeader() throws IOException { TestScenario.builder() - .specName("MiscTest") + .specName(SPEC_NAME) .request(HttpRequest.GET("/bar/ok").header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON)) .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.OK) @@ -61,7 +63,7 @@ void testSelectedRouteReflectsAcceptHeader() throws IOException { .run(); TestScenario.builder() - .specName("MiscTest") + .specName(SPEC_NAME) .request(HttpRequest.GET("/bar/ok").header(HttpHeaders.ACCEPT, MediaType.TEXT_HTML)) .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() @@ -74,7 +76,7 @@ void testSelectedRouteReflectsAcceptHeader() throws IOException { @Test void testBehaviourOf404() throws IOException { TestScenario.builder() - .specName("MiscTest") + .specName(SPEC_NAME) .request(HttpRequest.GET("/does-not-exist").header("Accept", MediaType.APPLICATION_JSON)) .assertion((server, request) -> AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() .status(HttpStatus.NOT_FOUND) @@ -85,7 +87,7 @@ void testBehaviourOf404() throws IOException { @Test void postFormUrlEncodedBodyBindingToPojoWorks() throws IOException { TestScenario.builder() - .specName("MiscTest") + .specName(SPEC_NAME) .request(HttpRequest.POST("/form", "message=World") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED)) .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() @@ -98,7 +100,7 @@ void postFormUrlEncodedBodyBindingToPojoWorks() throws IOException { @Test void postFormUrlEncodedBodyBindingToPojoWorksIfYouDontSpecifyBodyAnnotation() throws IOException { TestScenario.builder() - .specName("MiscTest") + .specName(SPEC_NAME) .request(HttpRequest.POST("/form/without-body-annotation", "message=World") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED)) .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() @@ -111,7 +113,7 @@ void postFormUrlEncodedBodyBindingToPojoWorksIfYouDontSpecifyBodyAnnotation() th @Test void formUrlEncodedWithBodyAnnotationAndANestedAttribute() throws IOException { TestScenario.builder() - .specName("MiscTest") + .specName(SPEC_NAME) .request(HttpRequest.POST("/form/nested-attribute", "message=World") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED)) .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() @@ -128,7 +130,7 @@ void formUrlEncodedWithBodyAnnotationAndANestedAttribute() throws IOException { @Test void applicationJsonWithBodyAnnotationAndANestedAttribute() throws IOException { TestScenario.builder() - .specName("MiscTest") + .specName(SPEC_NAME) .request(HttpRequest.POST("/form/json-nested-attribute", "{\"message\":\"World\"}") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() @@ -141,7 +143,7 @@ void applicationJsonWithBodyAnnotationAndANestedAttribute() throws IOException { @Test void applicationJsonWithoutBodyAnnotation() throws IOException { TestScenario.builder() - .specName("MiscTest") + .specName(SPEC_NAME) .request(HttpRequest.POST("/form/json-without-body-annotation", "{\"message\":\"World\"}") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() @@ -154,7 +156,7 @@ void applicationJsonWithoutBodyAnnotation() throws IOException { @Test void applicationJsonWithBodyAnnotationAndANestedAttributeAndMapReturnRenderedAsJSON() throws IOException { TestScenario.builder() - .specName("MiscTest") + .specName(SPEC_NAME) .request(HttpRequest.POST("/form/json-nested-attribute-with-map-return", "{\"message\":\"World\"}") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() @@ -167,7 +169,7 @@ void applicationJsonWithBodyAnnotationAndANestedAttributeAndMapReturnRenderedAsJ @Test void applicationJsonWithBodyAnnotationAndObjectReturnRenderedAsJson() throws IOException { TestScenario.builder() - .specName("MiscTest") + .specName(SPEC_NAME) .request(HttpRequest.POST("/form/json-with-body-annotation-and-with-object-return", "{\"message\":\"World\"}") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() @@ -178,7 +180,7 @@ void applicationJsonWithBodyAnnotationAndObjectReturnRenderedAsJson() throws IOE } @Controller - @Requires(property = "spec.name", value = "MiscTest") + @Requires(property = "spec.name", value = SPEC_NAME) static class SimpleController { @Get(uri = "/foo") HttpResponse getParamValue(HttpRequest request) { @@ -189,7 +191,7 @@ HttpResponse getParamValue(HttpRequest request) { } @Controller("/bar") - @Requires(property = "spec.name", value = "MiscTest") + @Requires(property = "spec.name", value = SPEC_NAME) static class ProduceController { @Get(value = "/ok", produces = MediaType.APPLICATION_JSON) String getOkAsJson() { @@ -237,7 +239,7 @@ public String getGreeting() { } @Controller("/form") - @Requires(property = "spec.name", value = "MiscTest") + @Requires(property = "spec.name", value = SPEC_NAME) static class FormController { @Consumes(MediaType.APPLICATION_FORM_URLENCODED) diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ParameterTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ParameterTest.java index 34067050a6f..543fb2120be 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ParameterTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ParameterTest.java @@ -36,10 +36,12 @@ "checkstyle:DesignForExtension" }) public class ParameterTest { + public static final String SPEC_NAME = "ParameterTest"; + @Test void testGetAllMethod() throws IOException { TestScenario.builder() - .specName("ParameterTest") + .specName(SPEC_NAME) .request(HttpRequest.GET(UriBuilder.of("/parameters-test").path("all") .queryParam("test", "one", "two", "three+four") .build())) @@ -51,7 +53,7 @@ void testGetAllMethod() throws IOException { } @Controller("/parameters-test") - @Requires(property = "spec.name", value = "ParameterTest") + @Requires(property = "spec.name", value = SPEC_NAME) static class BodyController { @Get(uri = "/all") diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/RemoteAddressTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/RemoteAddressTest.java index faf7627eb65..af47e832a93 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/RemoteAddressTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/RemoteAddressTest.java @@ -45,11 +45,12 @@ "checkstyle:DesignForExtension" }) public class RemoteAddressTest { + public static final String SPEC_NAME = "RemoteAddressTest"; @Test void testRemoteAddressComesFromIdentitySourceIp() throws IOException { TestScenario.builder() - .specName("RemoteAddressTest") + .specName(SPEC_NAME) .request(HttpRequest.GET("/remoteAddress/fromSourceIp")) .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.OK) @@ -58,7 +59,7 @@ void testRemoteAddressComesFromIdentitySourceIp() throws IOException { .run(); } - @Requires(property = "spec.name", value = "RemoteAddressTest") + @Requires(property = "spec.name", value = SPEC_NAME) @Controller("/remoteAddress") static class TestController { @Get("fromSourceIp") @@ -66,7 +67,7 @@ void sourceIp() { } } - @Requires(property = "spec.name", value = "RemoteAddressTest") + @Requires(property = "spec.name", value = SPEC_NAME) @Filter("/remoteAddress/**") static class CaptureRemoteAddressFiter implements HttpServerFilter { @Override @@ -78,7 +79,7 @@ public Publisher> doFilter(HttpRequest request, Server } } - @Requires(property = "spec.name", value = "RemoteAddressTest") + @Requires(property = "spec.name", value = SPEC_NAME) @Produces @Singleton static class CustomExceptionHandler implements ExceptionHandler { diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ResponseStatusTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ResponseStatusTest.java index 853e1106569..53c1d626ed0 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ResponseStatusTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ResponseStatusTest.java @@ -43,11 +43,12 @@ "checkstyle:DesignForExtension" }) public class ResponseStatusTest { + public static final String SPEC_NAME = "ResponseStatusTest"; @Test void testConstraintViolationCauses400() throws IOException { TestScenario.builder() - .specName("ResponseStatusSpec") + .specName(SPEC_NAME) .request(HttpRequest.POST("/response-status/constraint-violation", Collections.emptyMap()).header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)) .assertion((server, request) -> AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() .status(HttpStatus.BAD_REQUEST) @@ -58,7 +59,7 @@ void testConstraintViolationCauses400() throws IOException { @Test void testVoidMethodsDoesNotCause404() throws IOException { TestScenario.builder() - .specName("ResponseStatusSpec") + .specName(SPEC_NAME) .request(HttpRequest.DELETE("/response-status/delete-something") .header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)) .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() @@ -71,7 +72,7 @@ void testVoidMethodsDoesNotCause404() throws IOException { void testNullCauses404() throws IOException { TestScenario.builder() .request(HttpRequest.GET("/response-status/null").header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)) - .specName("ResponseStatusSpec") + .specName(SPEC_NAME) .assertion((server, request) -> AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() .status(HttpStatus.NOT_FOUND) .build())) @@ -81,7 +82,7 @@ void testNullCauses404() throws IOException { @Test void testOptionalCauses404() throws IOException { TestScenario.builder() - .specName("ResponseStatusSpec") + .specName(SPEC_NAME) .request(HttpRequest.GET("/response-status/optional").header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)) .assertion((server, request) -> AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() .status(HttpStatus.NOT_FOUND) @@ -92,7 +93,7 @@ void testOptionalCauses404() throws IOException { @Test void testCustomResponseStatus() throws IOException { TestScenario.builder() - .specName("ResponseStatusSpec") + .specName(SPEC_NAME) .request(HttpRequest.POST("/response-status", "foo") .header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)) .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() @@ -103,7 +104,7 @@ void testCustomResponseStatus() throws IOException { } @Controller("/response-status") - @Requires(property = "spec.name", value = "ResponseStatusSpec") + @Requires(property = "spec.name", value = SPEC_NAME) static class StatusController { @Post(uri = "/", processes = MediaType.TEXT_PLAIN) diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/StatusTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/StatusTest.java index 22b37f1f95b..1e54c2f2eb5 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/StatusTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/StatusTest.java @@ -40,6 +40,8 @@ "checkstyle:DesignForExtension" }) public class StatusTest { + public static final String SPEC_NAME = "StatusTest"; + /** * @see micronaut-aws #1387 * @param path Request Path @@ -48,7 +50,7 @@ public class StatusTest { @ValueSource(strings = {"/http-status", "/http-response-status", "/http-exception"}) void testControllerReturningHttpStatus(String path) throws IOException { TestScenario.builder() - .specName("StatusSpec") + .specName(SPEC_NAME) .request(HttpRequest.GET(path)) .assertion((server, request) -> AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() .status(HttpStatus.I_AM_A_TEAPOT) @@ -56,7 +58,7 @@ void testControllerReturningHttpStatus(String path) throws IOException { .run(); } - @Requires(property = "spec.name", value = "StatusSpec") + @Requires(property = "spec.name", value = SPEC_NAME) @Controller("/http-status") static class HttpStatusController { @Get @@ -65,7 +67,7 @@ HttpStatus index() { } } - @Requires(property = "spec.name", value = "StatusSpec") + @Requires(property = "spec.name", value = SPEC_NAME) @Controller("/http-response-status") static class HttpResponseStatusController { @@ -75,7 +77,7 @@ HttpResponse index() { } } - @Requires(property = "spec.name", value = "StatusSpec") + @Requires(property = "spec.name", value = SPEC_NAME) @Controller("/http-exception") static class HttpResponseErrorController { diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/VersionTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/VersionTest.java index 686d3f2ebb0..f20f5631b13 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/VersionTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/VersionTest.java @@ -35,6 +35,7 @@ "checkstyle:DesignForExtension" }) public class VersionTest { + public static final String SPEC_NAME = "VersionTest"; @Test void testControllerMethodWithVersion2() throws IOException { @@ -42,7 +43,7 @@ void testControllerMethodWithVersion2() throws IOException { .configuration(CollectionUtils.mapOf( "micronaut.router.versioning.enabled", StringUtils.TRUE, "micronaut.router.versioning.header.enabled", StringUtils.TRUE - )).specName("VersionSpec") + )).specName(SPEC_NAME) .request(HttpRequest.GET("/version/ping").header("X-API-VERSION", "2")) .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.OK) @@ -52,7 +53,7 @@ void testControllerMethodWithVersion2() throws IOException { } @Controller("/version") - @Requires(property = "spec.name", value = "VersionSpec") + @Requires(property = "spec.name", value = SPEC_NAME) static class ConsumesController { @Get("/ping") From 28462ad10ad23ef902662c84ebd29b87ea70c3e7 Mon Sep 17 00:00:00 2001 From: Sergio del Amo Date: Tue, 20 Dec 2022 11:04:17 +0100 Subject: [PATCH 07/13] log: remove logger --- test-suite-http-server-tck-netty/src/test/resources/logback.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/test-suite-http-server-tck-netty/src/test/resources/logback.xml b/test-suite-http-server-tck-netty/src/test/resources/logback.xml index 6a62bd94d36..8eb8c3a8170 100644 --- a/test-suite-http-server-tck-netty/src/test/resources/logback.xml +++ b/test-suite-http-server-tck-netty/src/test/resources/logback.xml @@ -7,5 +7,4 @@ - From ee76cb825f887501d6935de58107c3ef6a5eb0b4 Mon Sep 17 00:00:00 2001 From: Sergio del Amo Date: Tue, 20 Dec 2022 11:05:37 +0100 Subject: [PATCH 08/13] use constant --- .../micronaut/http/server/tck/tests/ErrorHandlerTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ErrorHandlerTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ErrorHandlerTest.java index 6172f5deba9..c94260a90ce 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ErrorHandlerTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ErrorHandlerTest.java @@ -313,7 +313,7 @@ String jsonBody(@Valid @Body RequestObject data) { } @Controller("/global-errors") - @Requires(property = "spec.name", value = "ErrorHandlerTest") + @Requires(property = "spec.name", value = SPEC_NAME) static class GlobalErrorController { @Error(global = true, exception = GloballyHandledException.class) @@ -333,7 +333,7 @@ String globalControllerHandlerForStatus() { } @Singleton - @Requires(property = "spec.name", value = "ErrorHandlerTest") + @Requires(property = "spec.name", value = SPEC_NAME) static class CodecExceptionExceptionHandler implements ExceptionHandler { @@ -344,7 +344,7 @@ public HttpResponse handle(HttpRequest request, CodecException exception) { } @Singleton - @Requires(property = "spec.name", value = "ErrorHandlerTest") + @Requires(property = "spec.name", value = SPEC_NAME) static class RuntimeErrorHandler implements ExceptionHandler { @Override @@ -355,7 +355,7 @@ public HttpResponse handle(HttpRequest request, RuntimeException exception) { } @Singleton - @Requires(property = "spec.name", value = "ErrorHandlerTest") + @Requires(property = "spec.name", value = SPEC_NAME) static class MyErrorHandler implements ExceptionHandler { @Override From 4a1e672812b53940aab729bce9503a1022436d35 Mon Sep 17 00:00:00 2001 From: Sergio del Amo Date: Tue, 20 Dec 2022 11:08:18 +0100 Subject: [PATCH 09/13] extract constants --- .../http/server/tck/tests/FilterErrorTest.java | 14 +++++++------- .../http/server/tck/tests/FiltersTest.java | 15 ++++++++------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FilterErrorTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FilterErrorTest.java index 10078b4b216..3952493853b 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FilterErrorTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FilterErrorTest.java @@ -63,7 +63,7 @@ public class FilterErrorTest { @Test void testFilterThrowingExceptionHandledByExceptionHandlerThrowingException() throws IOException { TestScenario.builder() - .specName("FilterErrorSpec3") + .specName(SPEC_NAME + "3") .request(HttpRequest.GET("/filter-error-spec-3") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) .assertion((server, request) -> { @@ -83,7 +83,7 @@ void testFilterThrowingExceptionHandledByExceptionHandlerThrowingException() thr void testTheErrorRouteIsTheRouteMatch() throws IOException { TestScenario.builder() .request(HttpRequest.GET("/filter-error-spec-4/status").header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) - .specName("FilterErrorSpec4") + .specName(SPEC_NAME + "4") .assertion((server, request) -> { AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() @@ -102,7 +102,7 @@ void testNonOncePerRequestFilterThrowingErrorDoesNotLoop() throws IOException { TestScenario.builder() .request(HttpRequest.GET("/filter-error-spec") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) - .specName("FilterErrorSpec2") + .specName(SPEC_NAME + "2") .assertion((server, request) -> { AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() @@ -208,7 +208,7 @@ public int getOrder() { } } - @Requires(property = "spec.name", value = "FilterErrorSpec2") + @Requires(property = "spec.name", value = SPEC_NAME + "2") @Filter(Filter.MATCH_ALL_PATTERN) static class FirstEvery implements HttpServerFilter { AtomicInteger executedCount = new AtomicInteger(0); @@ -225,7 +225,7 @@ public int getOrder() { } } - @Requires(property = "spec.name", value = "FilterErrorSpec3") + @Requires(property = "spec.name", value = SPEC_NAME + "3") @Filter(Filter.MATCH_ALL_PATTERN) static class ExceptionException implements HttpServerFilter { AtomicInteger executedCount = new AtomicInteger(0); @@ -248,7 +248,7 @@ public int getOrder() { } } - @Requires(property = "spec.name", value = "FilterErrorSpec4") + @Requires(property = "spec.name", value = SPEC_NAME + "4") @Filter(Filter.MATCH_ALL_PATTERN) static class ExceptionRoute implements HttpServerFilter { AtomicReference> routeMatch = new AtomicReference<>(); @@ -314,7 +314,7 @@ static class FilterCondition implements Condition { @Override public boolean matches(ConditionContext context) { return context.getProperty("spec.name", String.class) - .map(val -> val.equals("FilterErrorSpec4") || val.equals("FilterErrorSpec3") || val.equals("FilterErrorSpec2") || val.equals(SPEC_NAME)) + .map(val -> val.equals(SPEC_NAME + "4") || val.equals(SPEC_NAME + "3") || val.equals(SPEC_NAME + "2") || val.equals(SPEC_NAME)) .orElse(false); } } diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FiltersTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FiltersTest.java index 1a196af0d00..fb33ae1d01a 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FiltersTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FiltersTest.java @@ -19,6 +19,7 @@ import io.micronaut.core.async.publisher.Publishers; import io.micronaut.core.util.CollectionUtils; import io.micronaut.core.util.StringUtils; +import io.micronaut.http.HttpHeaders; import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; import io.micronaut.http.HttpStatus; @@ -51,14 +52,14 @@ }) public class FiltersTest { public static final String SPEC_NAME = "FiltersTest"; + public static final String PROP_MICRONAUT_SERVER_CORS_ENABLED = "micronaut.server.cors.enabled"; @Test void testFiltersAreRunCorrectly() throws IOException { Map configuration = CollectionUtils.mapOf( - "micronaut.server.cors.enabled", StringUtils.TRUE + PROP_MICRONAUT_SERVER_CORS_ENABLED, StringUtils.TRUE ); try (ServerUnderTest server = ServerUnderTestProviderUtils.getServerUnderTestProvider().getServer(SPEC_NAME, configuration)) { - assertTrue(true); HttpRequest request = HttpRequest.GET("/filter-test/ok"); AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.OK) @@ -71,14 +72,14 @@ void testFiltersAreRunCorrectly() throws IOException { @Test void filtersAreAppliedOnNonMatchingMethodsCorsFilterWorks() throws IOException { Map configuration = CollectionUtils.mapOf( - "micronaut.server.cors.enabled", StringUtils.TRUE + PROP_MICRONAUT_SERVER_CORS_ENABLED, StringUtils.TRUE ); try (ServerUnderTest server = ServerUnderTestProviderUtils.getServerUnderTestProvider().getServer(SPEC_NAME, configuration)) { HttpRequest request = HttpRequest.OPTIONS("/filter-test/ok").header("Origin", "https://micronaut.io") - .header("Access-Control-Request-Method", "GET"); + .header(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET"); AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.OK) - .headers(Collections.singletonMap("Access-Control-Allow-Origin", "https://micronaut.io")) + .headers(Collections.singletonMap(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "https://micronaut.io")) .build()); } } @@ -86,7 +87,7 @@ void filtersAreAppliedOnNonMatchingMethodsCorsFilterWorks() throws IOException { @Test void filtersAreAppliedOnNonMatchingMethodsCorsFilterDisableIfNotPreflight() throws IOException { Map configuration = CollectionUtils.mapOf( - "micronaut.server.cors.enabled", StringUtils.TRUE + PROP_MICRONAUT_SERVER_CORS_ENABLED, StringUtils.TRUE ); try (ServerUnderTest server = ServerUnderTestProviderUtils.getServerUnderTestProvider().getServer(SPEC_NAME, configuration)) { HttpRequest request = HttpRequest.OPTIONS("/filter-test/ok"); @@ -99,7 +100,7 @@ void filtersAreAppliedOnNonMatchingMethodsCorsFilterDisableIfNotPreflight() thro @Test void testFiltersAreRunCorrectlyWithCustomExceptionHandler() throws IOException { Map configuration = CollectionUtils.mapOf( - "micronaut.server.cors.enabled", StringUtils.TRUE + PROP_MICRONAUT_SERVER_CORS_ENABLED, StringUtils.TRUE ); try (ServerUnderTest server = ServerUnderTestProviderUtils.getServerUnderTestProvider().getServer(SPEC_NAME, configuration)) { HttpRequest request = HttpRequest.GET("/filter-test/exception"); From 68dc6e2050c454f1306bad65dbcce12447cceb71 Mon Sep 17 00:00:00 2001 From: Sergio del Amo Date: Tue, 20 Dec 2022 11:10:45 +0100 Subject: [PATCH 10/13] use consant --- .../main/java/io/micronaut/http/server/tck/tests/BodyTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/BodyTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/BodyTest.java index 02ecad164cd..20ad3936e28 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/BodyTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/BodyTest.java @@ -45,7 +45,7 @@ public class BodyTest { @Test void testCustomBodyPOJO() throws IOException { TestScenario.builder() - .specName("BodyTest") + .specName(SPEC_NAME) .request(HttpRequest.POST("/response-body/pojo", "{\"x\":10,\"y\":20}") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, From 1f50932832c6ce88376603f23676584046e618e4 Mon Sep 17 00:00:00 2001 From: Sergio del Amo Date: Tue, 20 Dec 2022 11:36:16 +0100 Subject: [PATCH 11/13] use static methods to reduce repetition --- .../http/server/tck/TestScenario.java | 16 +++ .../server/tck/tests/BodyArgumentTest.java | 12 +- .../http/server/tck/tests/BodyTest.java | 45 +++--- .../http/server/tck/tests/ConsumesTest.java | 12 +- .../http/server/tck/tests/CookiesTest.java | 36 ++--- .../tck/tests/DeleteWithoutBodyTest.java | 13 +- .../server/tck/tests/ErrorHandlerTest.java | 128 +++++++----------- .../server/tck/tests/FilterErrorTest.java | 55 +++----- .../http/server/tck/tests/FluxTest.java | 12 +- .../http/server/tck/tests/HelloWorldTest.java | 12 +- .../http/server/tck/tests/MiscTest.java | 115 +++++++--------- .../http/server/tck/tests/ParameterTest.java | 14 +- .../server/tck/tests/RemoteAddressTest.java | 12 +- .../server/tck/tests/ResponseStatusTest.java | 54 +++----- .../http/server/tck/tests/StatusTest.java | 12 +- .../http/server/tck/tests/VersionTest.java | 14 +- 16 files changed, 239 insertions(+), 323 deletions(-) diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/TestScenario.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/TestScenario.java index be76baa49d1..8ce14621d4d 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/TestScenario.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/TestScenario.java @@ -45,6 +45,22 @@ private TestScenario(String specName, this.assertion = assertion; } + public static void asserts(String specName, Map configuration, HttpRequest request, BiConsumer> assertion) throws IOException { + TestScenario.builder() + .specName(specName) + .configuration(configuration) + .request(request) + .assertion(assertion) + .run(); + } + public static void asserts(String specName, HttpRequest request, BiConsumer> assertion) throws IOException { + TestScenario.builder() + .specName(specName) + .request(request) + .assertion(assertion) + .run(); + } + /** * * @return A Test Scenario builder. diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/BodyArgumentTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/BodyArgumentTest.java index 66351ec9a01..1923451f202 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/BodyArgumentTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/BodyArgumentTest.java @@ -25,9 +25,9 @@ import io.micronaut.http.annotation.Produces; import io.micronaut.http.server.tck.AssertionUtils; import io.micronaut.http.server.tck.HttpResponseAssertion; -import io.micronaut.http.server.tck.TestScenario; import org.junit.jupiter.api.Test; import java.io.IOException; +import static io.micronaut.http.server.tck.TestScenario.asserts; @SuppressWarnings({ "java:S5960", // We're allowed assertions, as these are used in tests only @@ -42,14 +42,12 @@ public class BodyArgumentTest { */ @Test void testBodyArguments() throws IOException { - TestScenario.builder() - .specName(SPEC_NAME) - .request(HttpRequest.POST("/body-arguments-test/getA", "{\"a\":\"A\",\"b\":\"B\"}").header(HttpHeaders.ACCEPT, MediaType.TEXT_PLAIN)) - .assertion(((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + asserts(SPEC_NAME, + HttpRequest.POST("/body-arguments-test/getA", "{\"a\":\"A\",\"b\":\"B\"}").header(HttpHeaders.ACCEPT, MediaType.TEXT_PLAIN), + (server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.OK) .body("A") - .build()))) - .run(); + .build())); } @Controller("/body-arguments-test") diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/BodyTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/BodyTest.java index 20ad3936e28..27c28cde8cf 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/BodyTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/BodyTest.java @@ -27,7 +27,7 @@ import io.micronaut.http.annotation.Status; import io.micronaut.http.server.tck.AssertionUtils; import io.micronaut.http.server.tck.HttpResponseAssertion; -import io.micronaut.http.server.tck.TestScenario; +import static io.micronaut.http.server.tck.TestScenario.asserts; import org.junit.jupiter.api.Test; import org.reactivestreams.Publisher; @@ -44,24 +44,21 @@ public class BodyTest { @Test void testCustomBodyPOJO() throws IOException { - TestScenario.builder() - .specName(SPEC_NAME) - .request(HttpRequest.POST("/response-body/pojo", "{\"x\":10,\"y\":20}") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) - .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, + asserts(SPEC_NAME, + HttpRequest.POST("/response-body/pojo", "{\"x\":10,\"y\":20}") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON), + (server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.CREATED) .body("{\"x\":10,\"y\":20}") - .build())) - .run(); + .build())); } @Test void testCustomBodyPOJODefaultToJSON() throws IOException { - TestScenario.builder() - .specName(SPEC_NAME) - .request(HttpRequest.POST("/response-body/pojo", "{\"x\":10,\"y\":20}")) - .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, + asserts(SPEC_NAME, + HttpRequest.POST("/response-body/pojo", "{\"x\":10,\"y\":20}"), + (server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.CREATED) .body("{\"x\":10,\"y\":20}") @@ -70,29 +67,25 @@ void testCustomBodyPOJODefaultToJSON() throws IOException { @Test void testCustomBodyPOJOWithWholeRequest() throws IOException { - TestScenario.builder() - .specName(SPEC_NAME) - .request(HttpRequest.POST("/response-body/pojo-and-request", "{\"x\":10,\"y\":20}") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) - .assertion((server, request) -> HttpResponseAssertion.builder() + asserts(SPEC_NAME, + HttpRequest.POST("/response-body/pojo-and-request", "{\"x\":10,\"y\":20}") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON), + (server, request) -> HttpResponseAssertion.builder() .status(HttpStatus.CREATED) .body("{\"x\":10,\"y\":20}") - .build()) - .run(); + .build()); } @Test void testCustomBodyPOJOReactiveTypes() throws IOException { - TestScenario.builder() - .specName(SPEC_NAME) - .request(HttpRequest.POST("/response-body/pojo-reactive", "{\"x\":10,\"y\":20}") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) - .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, + asserts(SPEC_NAME, + HttpRequest.POST("/response-body/pojo-reactive", "{\"x\":10,\"y\":20}") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON), + (server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.CREATED) .body("{\"x\":10,\"y\":20}") - .build())) - .run(); + .build())); } @Controller("/response-body") diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ConsumesTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ConsumesTest.java index 25f07c7f110..b3218f5c305 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ConsumesTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ConsumesTest.java @@ -26,7 +26,7 @@ import io.micronaut.http.annotation.Post; import io.micronaut.http.server.tck.AssertionUtils; import io.micronaut.http.server.tck.HttpResponseAssertion; -import io.micronaut.http.server.tck.TestScenario; +import static io.micronaut.http.server.tck.TestScenario.asserts; import org.junit.jupiter.api.Test; import java.io.IOException; @@ -40,14 +40,12 @@ public class ConsumesTest { @Test void testMultipleConsumesDefinition() throws IOException { - TestScenario.builder() - .specName(SPEC_NAME) - .request(HttpRequest.POST("/consumes-test", "{\"name\":\"Fred\"}").header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) - .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + asserts(SPEC_NAME, + HttpRequest.POST("/consumes-test", "{\"name\":\"Fred\"}").header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON), + (server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.OK) .body("{\"name\":\"Fred\"}") - .build())) - .run(); + .build())); } @Controller("/consumes-test") diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/CookiesTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/CookiesTest.java index 1aa60ef3de6..c11013a46d5 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/CookiesTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/CookiesTest.java @@ -25,7 +25,7 @@ import io.micronaut.http.cookie.Cookie; import io.micronaut.http.server.tck.AssertionUtils; import io.micronaut.http.server.tck.HttpResponseAssertion; -import io.micronaut.http.server.tck.TestScenario; +import static io.micronaut.http.server.tck.TestScenario.asserts; import org.junit.jupiter.api.Test; import java.io.IOException; @@ -42,42 +42,36 @@ public class CookiesTest { @Test void testCookieBind() throws IOException { - TestScenario.builder() - .specName(SPEC_NAME) - .request(HttpRequest.GET("/cookies-test/bind") + asserts(SPEC_NAME, + HttpRequest.GET("/cookies-test/bind") .cookie(Cookie.of("one", "foo")) - .cookie(Cookie.of("two", "bar"))) - .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + .cookie(Cookie.of("two", "bar")), + (server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.OK) .body("{\"one\":\"foo\",\"two\":\"bar\"}") - .build())) - .run(); + .build())); } @Test void testGetCookiesMethod() throws IOException { - TestScenario.builder() - .specName(SPEC_NAME) - .request(HttpRequest.GET("/cookies-test/all") + asserts(SPEC_NAME, + HttpRequest.GET("/cookies-test/all") .cookie(Cookie.of("one", "foo")) - .cookie(Cookie.of("two", "bar"))) - .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + .cookie(Cookie.of("two", "bar")), + (server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.OK) .body("{\"one\":\"foo\",\"two\":\"bar\"}") - .build())) - .run(); + .build())); } @Test void testNoCookie() throws IOException { - TestScenario.builder() - .specName(SPEC_NAME) - .request(HttpRequest.GET("/cookies-test/all")) - .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + asserts(SPEC_NAME, + HttpRequest.GET("/cookies-test/all"), + (server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.OK) .body("{}") - .build())) - .run(); + .build())); } @Controller("/cookies-test") diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/DeleteWithoutBodyTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/DeleteWithoutBodyTest.java index 202c5904606..4094294d3aa 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/DeleteWithoutBodyTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/DeleteWithoutBodyTest.java @@ -26,7 +26,7 @@ import io.micronaut.http.annotation.Header; import io.micronaut.http.annotation.PathVariable; import io.micronaut.http.annotation.Status; -import io.micronaut.http.server.tck.TestScenario; +import static io.micronaut.http.server.tck.TestScenario.asserts; import org.junit.jupiter.api.Test; import java.io.IOException; @@ -41,17 +41,14 @@ }) public class DeleteWithoutBodyTest { public static final String SPEC_NAME = "DeleteWithoutBodyTest"; - @Test void verifiesItIsPossibleToExposesADeleteEndpointWhichIsInvokedWithoutABody() throws IOException { - TestScenario.builder() - .specName("DeleteWithoutBodyTest") - .request(HttpRequest.DELETE("/sessions/sergio").header(HttpHeaders.AUTHORIZATION, HttpHeaderValues.AUTHORIZATION_PREFIX_BEARER + " xxx")) - .assertion((server, request) -> { + asserts(SPEC_NAME, + HttpRequest.DELETE("/sessions/sergio").header(HttpHeaders.AUTHORIZATION, HttpHeaderValues.AUTHORIZATION_PREFIX_BEARER + " xxx"), + (server, request) -> { HttpResponse response = assertDoesNotThrow(() -> server.exchange(request)); assertEquals(HttpStatus.OK, response.getStatus()); - }) - .run(); + }); } @Requires(property = "spec.name", value = SPEC_NAME) diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ErrorHandlerTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ErrorHandlerTest.java index c94260a90ce..05d55dded1c 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ErrorHandlerTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ErrorHandlerTest.java @@ -40,7 +40,7 @@ import io.micronaut.http.server.tck.HttpResponseAssertion; import io.micronaut.http.server.tck.ServerUnderTest; import io.micronaut.http.server.tck.ServerUnderTestProviderUtils; -import io.micronaut.http.server.tck.TestScenario; +import static io.micronaut.http.server.tck.TestScenario.asserts; import jakarta.inject.Singleton; import org.junit.jupiter.api.Test; @@ -65,36 +65,30 @@ public class ErrorHandlerTest { @Test void testCustomGlobalExceptionHandlersDeclaredInController() throws IOException { - TestScenario.builder() - .configuration(CollectionUtils.mapOf( + asserts(SPEC_NAME, + CollectionUtils.mapOf( PROPERTY_MICRONAUT_SERVER_CORS_CONFIGURATIONS_WEB_ALLOWED_ORIGINS, Collections.singletonList("http://localhost:8080"), PROPERTY_MICRONAUT_SERVER_CORS_ENABLED, StringUtils.TRUE - )) - .specName(SPEC_NAME) - .request(HttpRequest.GET("/errors/global-ctrl") - .header(HttpHeaders.CONTENT_TYPE, io.micronaut.http.MediaType.APPLICATION_JSON)) - .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, + ), + HttpRequest.GET("/errors/global-ctrl").header(HttpHeaders.CONTENT_TYPE, io.micronaut.http.MediaType.APPLICATION_JSON), + (server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpStatus.OK, "bad things happens globally", - Collections.singletonMap(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN))) - .run(); + Collections.singletonMap(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN))); } @Test void testCustomGlobalExceptionHandlers() throws IOException { - TestScenario.builder() - .configuration(CollectionUtils.mapOf( + asserts(SPEC_NAME, + CollectionUtils.mapOf( PROPERTY_MICRONAUT_SERVER_CORS_CONFIGURATIONS_WEB_ALLOWED_ORIGINS, Collections.singletonList("http://localhost:8080"), PROPERTY_MICRONAUT_SERVER_CORS_ENABLED, StringUtils.TRUE - )) - .specName(SPEC_NAME) - .request(HttpRequest.GET("/errors/global") - .header(HttpHeaders.CONTENT_TYPE, io.micronaut.http.MediaType.APPLICATION_JSON)) - .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, + ), HttpRequest.GET("/errors/global") + .header(HttpHeaders.CONTENT_TYPE, io.micronaut.http.MediaType.APPLICATION_JSON), + (server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpStatus.OK, "Exception Handled", - Collections.singletonMap(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN))) - .run(); + Collections.singletonMap(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN))); } @Test @@ -116,115 +110,97 @@ void testCustomGlobalExceptionHandlersForPOSTWithBody() throws IOException { @Test void testCustomGlobalStatusHandlersDeclaredInController() throws IOException { - TestScenario.builder() - .configuration(CollectionUtils.mapOf( + asserts(SPEC_NAME, + CollectionUtils.mapOf( PROPERTY_MICRONAUT_SERVER_CORS_CONFIGURATIONS_WEB_ALLOWED_ORIGINS, Collections.singletonList("http://localhost:8080"), PROPERTY_MICRONAUT_SERVER_CORS_ENABLED, StringUtils.TRUE - )) - .specName(SPEC_NAME) - .request(HttpRequest.GET("/errors/global-status-ctrl")) - .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, + ), HttpRequest.GET("/errors/global-status-ctrl"), + (server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpStatus.OK, "global status", - Collections.singletonMap(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN))) - .run(); + Collections.singletonMap(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN))); } @Test void testLocalExceptionHandlers() throws IOException { - TestScenario.builder() - .configuration(CollectionUtils.mapOf( + asserts(SPEC_NAME, + CollectionUtils.mapOf( PROPERTY_MICRONAUT_SERVER_CORS_CONFIGURATIONS_WEB_ALLOWED_ORIGINS, Collections.singletonList("http://localhost:8080"), - PROPERTY_MICRONAUT_SERVER_CORS_ENABLED, StringUtils.TRUE)) - .specName(SPEC_NAME) - .request(HttpRequest.GET("/errors/local")) - .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, + PROPERTY_MICRONAUT_SERVER_CORS_ENABLED, StringUtils.TRUE), + HttpRequest.GET("/errors/local"), + (server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpStatus.OK, "bad things", - Collections.singletonMap(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN))) - .run(); + Collections.singletonMap(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN))); } @Test void jsonMessageFormatErrorsReturn400() throws IOException { - TestScenario.builder() - .configuration(CollectionUtils.mapOf( + asserts(SPEC_NAME, + CollectionUtils.mapOf( PROPERTY_MICRONAUT_SERVER_CORS_CONFIGURATIONS_WEB_ALLOWED_ORIGINS, Collections.singletonList("http://localhost:8080"), - PROPERTY_MICRONAUT_SERVER_CORS_ENABLED, StringUtils.TRUE - )).specName(SPEC_NAME) - .request(HttpRequest.POST("/json/jsonBody", "{\"numberField\": \"textInsteadOfNumber\"}")) - .assertion((server, request) -> AssertionUtils.assertThrows(server, request, + PROPERTY_MICRONAUT_SERVER_CORS_ENABLED, StringUtils.TRUE), + HttpRequest.POST("/json/jsonBody", "{\"numberField\": \"textInsteadOfNumber\"}"), + (server, request) -> AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() .status(HttpStatus.BAD_REQUEST) .headers(Collections.singletonMap(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) .build() - )).run(); + )); } @Test void corsHeadersArePresentAfterFailedDeserialisationWhenErrorHandlerIsUsed() throws IOException { - TestScenario.builder() - .configuration(CollectionUtils.mapOf( + asserts(SPEC_NAME, + CollectionUtils.mapOf( PROPERTY_MICRONAUT_SERVER_CORS_CONFIGURATIONS_WEB_ALLOWED_ORIGINS, Collections.singletonList("http://localhost:8080"), PROPERTY_MICRONAUT_SERVER_CORS_ENABLED, StringUtils.TRUE - )).specName(SPEC_NAME) - .request(HttpRequest.POST("/json/errors/global", "{\"numberField\": \"string is not a number\"}") - .header(HttpHeaders.ORIGIN, LOCALHOST)) - .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + ), HttpRequest.POST("/json/errors/global", "{\"numberField\": \"string is not a number\"}") + .header(HttpHeaders.ORIGIN, LOCALHOST), + (server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.OK) .headers(Collections.singletonMap(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, LOCALHOST)) - .build())) - .run(); + .build())); } @Test void corsHeadersArePresentAfterFailedDeserialisation() throws IOException { - TestScenario.builder() - .configuration(CollectionUtils.mapOf( + asserts(SPEC_NAME, + CollectionUtils.mapOf( PROPERTY_MICRONAUT_SERVER_CORS_CONFIGURATIONS_WEB_ALLOWED_ORIGINS, Collections.singletonList(LOCALHOST), PROPERTY_MICRONAUT_SERVER_CORS_ENABLED, StringUtils.TRUE - )) - .specName(SPEC_NAME) - .request(HttpRequest.POST("/json/jsonBody", "{\"numberField\": \"string is not a number\"}") - .header(HttpHeaders.ORIGIN, LOCALHOST)) - .assertion((server, request) -> AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() + ), HttpRequest.POST("/json/jsonBody", "{\"numberField\": \"string is not a number\"}") + .header(HttpHeaders.ORIGIN, LOCALHOST), + (server, request) -> AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() .status(HttpStatus.BAD_REQUEST) .headers(Collections.singletonMap(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, LOCALHOST)) - .build())) - .run(); + .build())); } @Test void corsHeadersArePresentAfterExceptions() throws IOException { - TestScenario.builder() - .configuration(CollectionUtils.mapOf( + asserts(SPEC_NAME, + CollectionUtils.mapOf( PROPERTY_MICRONAUT_SERVER_CORS_CONFIGURATIONS_WEB_ALLOWED_ORIGINS, Collections.singletonList(LOCALHOST), PROPERTY_MICRONAUT_SERVER_CORS_ENABLED, StringUtils.TRUE - )) - .specName(SPEC_NAME) - .request(HttpRequest.GET("/errors/global") - .header(HttpHeaders.ORIGIN, LOCALHOST)) - .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + ), HttpRequest.GET("/errors/global").header(HttpHeaders.ORIGIN, LOCALHOST), + (server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.OK) .headers(Collections.singletonMap(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, LOCALHOST)) - .build())) - .run(); + .build())); } @Test void messageValidationErrorsReturn400() throws IOException { - TestScenario.builder() - .configuration(CollectionUtils.mapOf( + asserts(SPEC_NAME, + CollectionUtils.mapOf( PROPERTY_MICRONAUT_SERVER_CORS_CONFIGURATIONS_WEB_ALLOWED_ORIGINS, Collections.singletonList("http://localhost:8080"), PROPERTY_MICRONAUT_SERVER_CORS_ENABLED, StringUtils.TRUE - )) - .specName(SPEC_NAME) - .request(HttpRequest.POST("/json/jsonBody", "{\"numberField\": 0}")) - .assertion((server, request) -> AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() + ), HttpRequest.POST("/json/jsonBody", "{\"numberField\": 0}"), + (server, request) -> AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() .status(HttpStatus.BAD_REQUEST) .headers(Collections.singletonMap(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) - .build())) - .run(); + .build())); } @Controller("/secret") diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FilterErrorTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FilterErrorTest.java index 3952493853b..a4ba93082eb 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FilterErrorTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FilterErrorTest.java @@ -37,7 +37,7 @@ import io.micronaut.http.server.exceptions.ExceptionHandler; import io.micronaut.http.server.tck.AssertionUtils; import io.micronaut.http.server.tck.HttpResponseAssertion; -import io.micronaut.http.server.tck.TestScenario; +import static io.micronaut.http.server.tck.TestScenario.asserts; import io.micronaut.web.router.MethodBasedRouteMatch; import io.micronaut.web.router.RouteMatch; import jakarta.inject.Singleton; @@ -62,11 +62,10 @@ public class FilterErrorTest { @Test void testFilterThrowingExceptionHandledByExceptionHandlerThrowingException() throws IOException { - TestScenario.builder() - .specName(SPEC_NAME + "3") - .request(HttpRequest.GET("/filter-error-spec-3") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) - .assertion((server, request) -> { + asserts(SPEC_NAME + "3", + HttpRequest.GET("/filter-error-spec-3") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON), + (server, request) -> { AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() .status(HttpStatus.INTERNAL_SERVER_ERROR) @@ -75,16 +74,14 @@ void testFilterThrowingExceptionHandledByExceptionHandlerThrowingException() thr ExceptionException filter = server.getApplicationContext().getBean(ExceptionException.class); assertEquals(1, filter.executedCount.get()); assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, filter.responseStatus.getAndSet(null)); - }) - .run(); + }); } @Test void testTheErrorRouteIsTheRouteMatch() throws IOException { - TestScenario.builder() - .request(HttpRequest.GET("/filter-error-spec-4/status").header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) - .specName(SPEC_NAME + "4") - .assertion((server, request) -> { + asserts(SPEC_NAME + "4", + HttpRequest.GET("/filter-error-spec-4/status").header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON), + (server, request) -> { AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.OK) @@ -93,17 +90,14 @@ void testTheErrorRouteIsTheRouteMatch() throws IOException { RouteMatch match = filter.routeMatch.getAndSet(null); assertTrue(match instanceof MethodBasedRouteMatch); assertEquals("testStatus", ((MethodBasedRouteMatch) match).getName()); - }) - .run(); + }); } @Test void testNonOncePerRequestFilterThrowingErrorDoesNotLoop() throws IOException { - TestScenario.builder() - .request(HttpRequest.GET("/filter-error-spec") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) - .specName(SPEC_NAME + "2") - .assertion((server, request) -> { + asserts(SPEC_NAME + "2", + HttpRequest.GET("/filter-error-spec").header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON), + (server, request) -> { AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() .status(HttpStatus.BAD_REQUEST) @@ -111,17 +105,14 @@ void testNonOncePerRequestFilterThrowingErrorDoesNotLoop() throws IOException { .build()); FirstEvery filter = server.getApplicationContext().getBean(FirstEvery.class); assertEquals(1, filter.executedCount.get()); - }).run(); + }); } @Test void testErrorsEmittedFromSecondFilterInteractingWithExceptionHandlers() throws IOException { - TestScenario.builder() - .request(HttpRequest.GET("/filter-error-spec"). - header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON) - .header("X-Passthru", StringUtils.TRUE)) - .specName(SPEC_NAME) - .assertion((server, request) -> { + asserts(SPEC_NAME, + HttpRequest.GET("/filter-error-spec").header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON).header("X-Passthru", StringUtils.TRUE), + (server, request) -> { AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() .status(HttpStatus.BAD_REQUEST) @@ -133,15 +124,14 @@ void testErrorsEmittedFromSecondFilterInteractingWithExceptionHandlers() throws assertEquals(1, first.executedCount.get()); assertEquals(HttpStatus.BAD_REQUEST, first.responseStatus.getAndSet(null)); assertEquals(1, next.executedCount.get()); - }).run(); + }); } @Test void testErrorsEmittedFromFiltersInteractingWithExceptionHandlers() throws IOException { - TestScenario.builder() - .specName(SPEC_NAME) - .request(HttpRequest.GET("/filter-error-spec").header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) - .assertion((server, request) -> { + asserts(SPEC_NAME, + HttpRequest.GET("/filter-error-spec").header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON), + (server, request) -> { AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() .status(HttpStatus.BAD_REQUEST) @@ -153,8 +143,7 @@ void testErrorsEmittedFromFiltersInteractingWithExceptionHandlers() throws IOExc assertEquals(1, first.executedCount.get()); assertNull(first.responseStatus.getAndSet(null)); assertEquals(0, next.executedCount.get()); - }) - .run(); + }); } static class FilterExceptionException extends RuntimeException { diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FluxTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FluxTest.java index 8394a788725..887caeebcb2 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FluxTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FluxTest.java @@ -22,7 +22,7 @@ import io.micronaut.http.annotation.Get; import io.micronaut.http.server.tck.AssertionUtils; import io.micronaut.http.server.tck.HttpResponseAssertion; -import io.micronaut.http.server.tck.TestScenario; +import static io.micronaut.http.server.tck.TestScenario.asserts; import org.junit.jupiter.api.Test; import reactor.core.publisher.Flux; @@ -41,14 +41,12 @@ public class FluxTest { @Test void testControllerReturningAFlux() throws IOException { - TestScenario.builder() - .specName(SPEC_NAME) - .request(HttpRequest.GET("/users")) - .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + asserts(SPEC_NAME, + HttpRequest.GET("/users"), + (server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.OK) .body("[{\"name\":\"Joe\"},{\"name\":\"Lewis\"}]") - .build())) - .run(); + .build())); } @Controller("/users") diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/HelloWorldTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/HelloWorldTest.java index a3d332f13da..86089cb5d28 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/HelloWorldTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/HelloWorldTest.java @@ -24,7 +24,7 @@ import io.micronaut.http.annotation.Get; import io.micronaut.http.annotation.Produces; import io.micronaut.http.server.tck.AssertionUtils; -import io.micronaut.http.server.tck.TestScenario; +import static io.micronaut.http.server.tck.TestScenario.asserts; import io.micronaut.http.uri.UriBuilder; import org.junit.jupiter.api.Test; @@ -42,14 +42,12 @@ public class HelloWorldTest { @Test void helloWorld() throws IOException { - TestScenario.builder() - .specName(SPEC_NAME) - .request(HttpRequest.GET(UriBuilder.of("/hello").path("world").build()).accept(MediaType.TEXT_PLAIN)) - .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, + asserts(SPEC_NAME, + HttpRequest.GET(UriBuilder.of("/hello").path("world").build()).accept(MediaType.TEXT_PLAIN), + (server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpStatus.OK, "Hello World", - Collections.singletonMap(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN))) - .run(); + Collections.singletonMap(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN))); } @Requires(property = "spec.name", value = SPEC_NAME) diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/MiscTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/MiscTest.java index 1a490b450ab..5e665b41c0a 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/MiscTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/MiscTest.java @@ -30,7 +30,7 @@ import io.micronaut.http.annotation.Post; import io.micronaut.http.server.tck.AssertionUtils; import io.micronaut.http.server.tck.HttpResponseAssertion; -import io.micronaut.http.server.tck.TestScenario; +import static io.micronaut.http.server.tck.TestScenario.asserts; import org.junit.jupiter.api.Test; import javax.validation.constraints.NotBlank; @@ -53,74 +53,61 @@ public class MiscTest { */ @Test void testSelectedRouteReflectsAcceptHeader() throws IOException { - TestScenario.builder() - .specName(SPEC_NAME) - .request(HttpRequest.GET("/bar/ok").header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON)) - .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + asserts(SPEC_NAME, + HttpRequest.GET("/bar/ok").header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON), + (server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.OK) .body("{\"status\":\"ok\"}") - .build())) - .run(); + .build())); - TestScenario.builder() - .specName(SPEC_NAME) - .request(HttpRequest.GET("/bar/ok").header(HttpHeaders.ACCEPT, MediaType.TEXT_HTML)) - .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, + asserts(SPEC_NAME, + HttpRequest.GET("/bar/ok").header(HttpHeaders.ACCEPT, MediaType.TEXT_HTML), + (server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.OK) .body("
ok
") - .build())) - .run(); + .build())); } @Test void testBehaviourOf404() throws IOException { - TestScenario.builder() - .specName(SPEC_NAME) - .request(HttpRequest.GET("/does-not-exist").header("Accept", MediaType.APPLICATION_JSON)) - .assertion((server, request) -> AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() + asserts(SPEC_NAME, + HttpRequest.GET("/does-not-exist").header("Accept", MediaType.APPLICATION_JSON), + (server, request) -> AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() .status(HttpStatus.NOT_FOUND) - .build())) - .run(); + .build())); } @Test void postFormUrlEncodedBodyBindingToPojoWorks() throws IOException { - TestScenario.builder() - .specName(SPEC_NAME) - .request(HttpRequest.POST("/form", "message=World") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED)) - .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + asserts(SPEC_NAME, + HttpRequest.POST("/form", "message=World").header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED), + (server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.OK) .body("{\"message\":\"Hello World\"}") - .build())) - .run(); + .build())); } @Test void postFormUrlEncodedBodyBindingToPojoWorksIfYouDontSpecifyBodyAnnotation() throws IOException { - TestScenario.builder() - .specName(SPEC_NAME) - .request(HttpRequest.POST("/form/without-body-annotation", "message=World") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED)) - .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + asserts(SPEC_NAME, + HttpRequest.POST("/form/without-body-annotation", "message=World") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED), + (server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.OK) .body("{\"message\":\"Hello World\"}") - .build())) - .run(); + .build())); } @Test void formUrlEncodedWithBodyAnnotationAndANestedAttribute() throws IOException { - TestScenario.builder() - .specName(SPEC_NAME) - .request(HttpRequest.POST("/form/nested-attribute", "message=World") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED)) - .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + asserts(SPEC_NAME, + HttpRequest.POST("/form/nested-attribute", "message=World") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED), + (server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.OK) .body("{\"message\":\"Hello World\"}") - .build())) - .run(); + .build())); } /** @@ -129,54 +116,46 @@ void formUrlEncodedWithBodyAnnotationAndANestedAttribute() throws IOException { */ @Test void applicationJsonWithBodyAnnotationAndANestedAttribute() throws IOException { - TestScenario.builder() - .specName(SPEC_NAME) - .request(HttpRequest.POST("/form/json-nested-attribute", "{\"message\":\"World\"}") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) - .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + asserts(SPEC_NAME, + HttpRequest.POST("/form/json-nested-attribute", "{\"message\":\"World\"}") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON), + (server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.OK) .body("{\"message\":\"Hello World\"}") - .build())) - .run(); + .build())); } @Test void applicationJsonWithoutBodyAnnotation() throws IOException { - TestScenario.builder() - .specName(SPEC_NAME) - .request(HttpRequest.POST("/form/json-without-body-annotation", "{\"message\":\"World\"}") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) - .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + asserts(SPEC_NAME, + HttpRequest.POST("/form/json-without-body-annotation", "{\"message\":\"World\"}") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON), + (server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.OK) .body("{\"message\":\"Hello World\"}") - .build())) - .run(); + .build())); } @Test void applicationJsonWithBodyAnnotationAndANestedAttributeAndMapReturnRenderedAsJSON() throws IOException { - TestScenario.builder() - .specName(SPEC_NAME) - .request(HttpRequest.POST("/form/json-nested-attribute-with-map-return", "{\"message\":\"World\"}") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) - .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + asserts(SPEC_NAME, + HttpRequest.POST("/form/json-nested-attribute-with-map-return", "{\"message\":\"World\"}") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON), + (server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.OK) .body("{\"message\":\"Hello World\"}") - .build())) - .run(); + .build())); } @Test void applicationJsonWithBodyAnnotationAndObjectReturnRenderedAsJson() throws IOException { - TestScenario.builder() - .specName(SPEC_NAME) - .request(HttpRequest.POST("/form/json-with-body-annotation-and-with-object-return", "{\"message\":\"World\"}") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)) - .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + asserts(SPEC_NAME, + HttpRequest.POST("/form/json-with-body-annotation-and-with-object-return", "{\"message\":\"World\"}") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON), + (server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.OK) .body("{\"greeting\":\"Hello World\"}") - .build())) - .run(); + .build())); } @Controller diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ParameterTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ParameterTest.java index 543fb2120be..4ad42b4659f 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ParameterTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ParameterTest.java @@ -22,7 +22,7 @@ import io.micronaut.http.annotation.Get; import io.micronaut.http.server.tck.AssertionUtils; import io.micronaut.http.server.tck.HttpResponseAssertion; -import io.micronaut.http.server.tck.TestScenario; +import static io.micronaut.http.server.tck.TestScenario.asserts; import io.micronaut.http.uri.UriBuilder; import org.junit.jupiter.api.Test; @@ -40,16 +40,14 @@ public class ParameterTest { @Test void testGetAllMethod() throws IOException { - TestScenario.builder() - .specName(SPEC_NAME) - .request(HttpRequest.GET(UriBuilder.of("/parameters-test").path("all") + asserts(SPEC_NAME, + HttpRequest.GET(UriBuilder.of("/parameters-test").path("all") .queryParam("test", "one", "two", "three+four") - .build())) - .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + .build()), + (server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.OK) .body("[\"one\",\"two\",\"three+four\"]") - .build())) - .run(); + .build())); } @Controller("/parameters-test") diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/RemoteAddressTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/RemoteAddressTest.java index af47e832a93..49b94b05ba4 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/RemoteAddressTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/RemoteAddressTest.java @@ -30,7 +30,7 @@ import io.micronaut.http.server.exceptions.ExceptionHandler; import io.micronaut.http.server.tck.AssertionUtils; import io.micronaut.http.server.tck.HttpResponseAssertion; -import io.micronaut.http.server.tck.TestScenario; +import static io.micronaut.http.server.tck.TestScenario.asserts; import jakarta.inject.Singleton; import org.junit.jupiter.api.Test; import org.reactivestreams.Publisher; @@ -49,14 +49,12 @@ public class RemoteAddressTest { @Test void testRemoteAddressComesFromIdentitySourceIp() throws IOException { - TestScenario.builder() - .specName(SPEC_NAME) - .request(HttpRequest.GET("/remoteAddress/fromSourceIp")) - .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + asserts(SPEC_NAME, + HttpRequest.GET("/remoteAddress/fromSourceIp"), + (server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.OK) .headers(Collections.singletonMap("X-Captured-Remote-Address", "127.0.0.1")) - .build())) - .run(); + .build())); } @Requires(property = "spec.name", value = SPEC_NAME) diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ResponseStatusTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ResponseStatusTest.java index 53c1d626ed0..69959731609 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ResponseStatusTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/ResponseStatusTest.java @@ -28,7 +28,7 @@ import io.micronaut.http.annotation.Status; import io.micronaut.http.server.tck.AssertionUtils; import io.micronaut.http.server.tck.HttpResponseAssertion; -import io.micronaut.http.server.tck.TestScenario; +import static io.micronaut.http.server.tck.TestScenario.asserts; import org.junit.jupiter.api.Test; import javax.validation.ConstraintViolationException; @@ -47,60 +47,48 @@ public class ResponseStatusTest { @Test void testConstraintViolationCauses400() throws IOException { - TestScenario.builder() - .specName(SPEC_NAME) - .request(HttpRequest.POST("/response-status/constraint-violation", Collections.emptyMap()).header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)) - .assertion((server, request) -> AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() + asserts(SPEC_NAME, + HttpRequest.POST("/response-status/constraint-violation", Collections.emptyMap()).header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN), + (server, request) -> AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() .status(HttpStatus.BAD_REQUEST) - .build())) - .run(); + .build())); } @Test void testVoidMethodsDoesNotCause404() throws IOException { - TestScenario.builder() - .specName(SPEC_NAME) - .request(HttpRequest.DELETE("/response-status/delete-something") - .header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)) - .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + asserts(SPEC_NAME, + HttpRequest.DELETE("/response-status/delete-something").header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN), + (server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.NO_CONTENT) - .build())) - .run(); + .build())); } @Test void testNullCauses404() throws IOException { - TestScenario.builder() - .request(HttpRequest.GET("/response-status/null").header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)) - .specName(SPEC_NAME) - .assertion((server, request) -> AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() + asserts(SPEC_NAME, + HttpRequest.GET("/response-status/null").header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN), + (server, request) -> AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() .status(HttpStatus.NOT_FOUND) - .build())) - .run(); + .build())); } @Test void testOptionalCauses404() throws IOException { - TestScenario.builder() - .specName(SPEC_NAME) - .request(HttpRequest.GET("/response-status/optional").header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)) - .assertion((server, request) -> AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() + asserts(SPEC_NAME, + HttpRequest.GET("/response-status/optional").header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN), + (server, request) -> AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() .status(HttpStatus.NOT_FOUND) - .build())) - .run(); + .build())); } @Test void testCustomResponseStatus() throws IOException { - TestScenario.builder() - .specName(SPEC_NAME) - .request(HttpRequest.POST("/response-status", "foo") - .header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)) - .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + asserts(SPEC_NAME, + HttpRequest.POST("/response-status", "foo").header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN), + (server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.CREATED) .body("foo") - .build())) - .run(); + .build())); } @Controller("/response-status") diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/StatusTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/StatusTest.java index 1e54c2f2eb5..4f4d0a0f6a5 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/StatusTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/StatusTest.java @@ -27,7 +27,7 @@ import io.micronaut.http.server.exceptions.response.ErrorResponseProcessor; import io.micronaut.http.server.tck.AssertionUtils; import io.micronaut.http.server.tck.HttpResponseAssertion; -import io.micronaut.http.server.tck.TestScenario; +import static io.micronaut.http.server.tck.TestScenario.asserts; import jakarta.inject.Singleton; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -49,13 +49,11 @@ public class StatusTest { @ParameterizedTest @ValueSource(strings = {"/http-status", "/http-response-status", "/http-exception"}) void testControllerReturningHttpStatus(String path) throws IOException { - TestScenario.builder() - .specName(SPEC_NAME) - .request(HttpRequest.GET(path)) - .assertion((server, request) -> AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() + asserts(SPEC_NAME, + HttpRequest.GET(path), + (server, request) -> AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() .status(HttpStatus.I_AM_A_TEAPOT) - .build())) - .run(); + .build())); } @Requires(property = "spec.name", value = SPEC_NAME) diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/VersionTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/VersionTest.java index f20f5631b13..7665853c62f 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/VersionTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/VersionTest.java @@ -25,7 +25,7 @@ import io.micronaut.http.annotation.Get; import io.micronaut.http.server.tck.AssertionUtils; import io.micronaut.http.server.tck.HttpResponseAssertion; -import io.micronaut.http.server.tck.TestScenario; +import static io.micronaut.http.server.tck.TestScenario.asserts; import org.junit.jupiter.api.Test; import java.io.IOException; @@ -39,17 +39,15 @@ public class VersionTest { @Test void testControllerMethodWithVersion2() throws IOException { - TestScenario.builder() - .configuration(CollectionUtils.mapOf( + asserts(SPEC_NAME, + CollectionUtils.mapOf( "micronaut.router.versioning.enabled", StringUtils.TRUE, "micronaut.router.versioning.header.enabled", StringUtils.TRUE - )).specName(SPEC_NAME) - .request(HttpRequest.GET("/version/ping").header("X-API-VERSION", "2")) - .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + ), HttpRequest.GET("/version/ping").header("X-API-VERSION", "2"), + (server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() .status(HttpStatus.OK) .body("pong v2") - .build())) - .run(); + .build())); } @Controller("/version") From 1256cb7a588cca63c973f049a43e3dfb64829888 Mon Sep 17 00:00:00 2001 From: Sergio del Amo Date: Tue, 20 Dec 2022 13:31:48 +0100 Subject: [PATCH 12/13] fix chekcstyle and javadoc --- .../http/server/tck/TestScenario.java | 25 +++++++++++++++++-- .../tck/tests/DeleteWithoutBodyTest.java | 1 + .../http/server/tck/tests/FiltersTest.java | 3 --- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/TestScenario.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/TestScenario.java index 8ce14621d4d..dc2d4ccafe6 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/TestScenario.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/TestScenario.java @@ -45,7 +45,18 @@ private TestScenario(String specName, this.assertion = assertion; } - public static void asserts(String specName, Map configuration, HttpRequest request, BiConsumer> assertion) throws IOException { + /** + * + * @param specName Value for {@literal spec.name} property. Used to avoid bean pollution. + * @param configuration Test Scenario configuration + * @param request HTTP Request to be sent in the test scenario + * @param assertion Assertion for a request and server. + * @throws IOException Exception thrown while getting the server under test. + */ + public static void asserts(String specName, + Map configuration, + HttpRequest request, + BiConsumer> assertion) throws IOException { TestScenario.builder() .specName(specName) .configuration(configuration) @@ -53,7 +64,17 @@ public static void asserts(String specName, Map configuration, H .assertion(assertion) .run(); } - public static void asserts(String specName, HttpRequest request, BiConsumer> assertion) throws IOException { + + /** + * + * @param specName Value for {@literal spec.name} property. Used to avoid bean pollution. + * @param request HTTP Request to be sent in the test scenario + * @param assertion Assertion for a request and server. + * @throws IOException Exception thrown while getting the server under test. + */ + public static void asserts(String specName, + HttpRequest request, + BiConsumer> assertion) throws IOException { TestScenario.builder() .specName(specName) .request(request) diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/DeleteWithoutBodyTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/DeleteWithoutBodyTest.java index 4094294d3aa..c48393b448e 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/DeleteWithoutBodyTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/DeleteWithoutBodyTest.java @@ -41,6 +41,7 @@ }) public class DeleteWithoutBodyTest { public static final String SPEC_NAME = "DeleteWithoutBodyTest"; + @Test void verifiesItIsPossibleToExposesADeleteEndpointWhichIsInvokedWithoutABody() throws IOException { asserts(SPEC_NAME, diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FiltersTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FiltersTest.java index fb33ae1d01a..64a52d674bf 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FiltersTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FiltersTest.java @@ -42,9 +42,6 @@ import java.io.IOException; import java.util.Collections; import java.util.Map; - -import static org.junit.jupiter.api.Assertions.assertTrue; - @SuppressWarnings({ "java:S5960", // We're allowed assertions, as these are used in tests only "checkstyle:MissingJavadocType", From 861002d6ecd536a85f1f466e00093f7e0e2e8fa3 Mon Sep 17 00:00:00 2001 From: Sergio del Amo Date: Tue, 20 Dec 2022 13:36:03 +0100 Subject: [PATCH 13/13] sonar: use ifPresent and entrySet --- .../http/server/tck/AssertionUtils.java | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/AssertionUtils.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/AssertionUtils.java index a3f1f0710e5..e3a46348a01 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/AssertionUtils.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/AssertionUtils.java @@ -30,8 +30,7 @@ import java.util.Map; import java.util.Optional; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; /** * Utility class used to perform assertions. @@ -93,29 +92,31 @@ private static void assertBody(@NonNull HttpResponse response, @Nullable Str if (expectedBody != null) { Optional bodyOptional = response.getBody(String.class); assertTrue(bodyOptional.isPresent()); - assertTrue(bodyOptional.get().contains(expectedBody)); + bodyOptional.ifPresent(body -> assertTrue(body.contains(expectedBody))); } } private static void assertHeaders(@NonNull HttpResponse response, @Nullable Map expectedHeaders) { if (expectedHeaders != null) { - for (String headerName : expectedHeaders.keySet()) { + for (Map.Entry expectedHeadersEntrySet : expectedHeaders.entrySet()) { + String headerName = expectedHeadersEntrySet.getKey(); Optional headerOptional = response.getHeaders().getFirst(headerName); assertTrue(headerOptional.isPresent(), () -> "Header " + headerName + " not present"); - String headerValue = headerOptional.get(); - String expectedValue = expectedHeaders.get(headerName); - if (headerName.equals(HttpHeaders.CONTENT_TYPE)) { - if (headerValue.contains(";charset=")) { - assertTrue(headerValue.startsWith(expectedValue), () -> "header value " + headerValue + " does not start with " + expectedValue); + headerOptional.ifPresent(headerValue -> { + String expectedValue = expectedHeadersEntrySet.getValue(); + if (headerName.equals(HttpHeaders.CONTENT_TYPE)) { + if (headerValue.contains(";charset=")) { + assertTrue(headerValue.startsWith(expectedValue), () -> "header value " + headerValue + " does not start with " + expectedValue); + } else { + assertEquals(expectedValue, headerOptional.get()); + } } else { assertEquals(expectedValue, headerOptional.get()); } - } else { - assertEquals(expectedValue, headerOptional.get()); - } - + }); } + } }