From bf7624315f6680bc944c15c32d18119663ded2bc Mon Sep 17 00:00:00 2001 From: Tim Yates Date: Mon, 6 Feb 2023 12:35:01 +0000 Subject: [PATCH] test: add octet stream serialization to the TCK (#8712) Inspired by https://github.com/micronaut-projects/micronaut-aws/issues/1545 --- .../http/server/tck/AssertionUtils.java | 4 +- .../http/server/tck/BodyAssertion.java | 116 ++++++++++++++---- .../server/tck/HttpResponseAssertion.java | 10 +- .../http/server/tck/tests/MiscTest.java | 1 - .../http/server/tck/tests/OctetTest.java | 70 +++++++++++ 5 files changed, 172 insertions(+), 29 deletions(-) create mode 100644 http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/OctetTest.java 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 6dd2bb26073..873e083952e 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 @@ -98,9 +98,9 @@ public static void assertDoesNotThrow(@NonNull ServerUnderTest server, assertion.getResponseConsumer().ifPresent(httpResponseConsumer -> httpResponseConsumer.accept(response)); } - private static void assertBody(@NonNull HttpResponse response, @Nullable BodyAssertion bodyAssertion) { + private static void assertBody(@NonNull HttpResponse response, @Nullable BodyAssertion bodyAssertion) { if (bodyAssertion != null) { - Optional bodyOptional = response.getBody(String.class); + Optional bodyOptional = response.getBody(bodyAssertion.getBodyType()); assertTrue(bodyOptional.isPresent()); bodyOptional.ifPresent(bodyAssertion::evaluate); } diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/BodyAssertion.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/BodyAssertion.java index 71355557883..e37021054ad 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/BodyAssertion.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/BodyAssertion.java @@ -17,37 +17,69 @@ import io.micronaut.core.annotation.Experimental; -import java.util.function.BiFunction; +import java.util.Arrays; +import java.util.function.BiPredicate; import static org.junit.jupiter.api.Assertions.assertTrue; /** - * HTTP Reponse's body assertions. + * HTTP Response's body assertions. + * + * @param The body type */ @Experimental -public final class BodyAssertion { - private final String expected; - private final BiFunction evaluator; +public final class BodyAssertion { + + private final Class bodyType; + private final T expected; + private final BiPredicate evaluator; - private BodyAssertion(String expected, BiFunction evaluator) { + private BodyAssertion(Class bodyType, T expected, BiPredicate evaluator) { + this.bodyType = bodyType; this.expected = expected; this.evaluator = evaluator; } + /** + * @return a Builder; + */ + public static BodyAssertion.Builder builder() { + return new BodyAssertion.Builder(); + } + /** * Evaluates the HTTP Response Body. + * * @param body The HTTP Response Body */ - public void evaluate(String body) { - assertTrue(this.evaluator.apply(expected, body)); + @SuppressWarnings("java:S5960") // Assertion is the whole point of this method + public void evaluate(T body) { + assertTrue(this.evaluator.test(expected, body)); + } + + /** + * @return The expected body type + */ + public Class getBodyType() { + return bodyType; } /** + * The interface for typed BodyAssertion Builders. * - * @return a Builder; + * @param The body type */ - public static BodyAssertion.Builder builder() { - return new BodyAssertion.Builder(); + public interface AssertionBuilder { + + /** + * @return a body assertion which verifiers the HTTP Response's body contains the expected body + */ + BodyAssertion contains(); + + /** + * @return a body assertion which verifiers the HTTP Response's body is equals to the expected body + */ + BodyAssertion equals(); } /** @@ -55,32 +87,74 @@ public static BodyAssertion.Builder builder() { */ public static class Builder { - private String body; + /** + * @param expected Expected Body + * @return The Builder + */ + public AssertionBuilder body(String expected) { + return new StringBodyAssertionBuilder(expected); + } /** - * * @param expected Expected Body * @return The Builder */ - public Builder body(String expected) { + public AssertionBuilder body(byte[] expected) { + return new ByteArrayBodyAssertionBuilder(expected); + } + } + + /** + * String BodyAssertion Builder. + */ + public static class StringBodyAssertionBuilder extends BodyAssertion.Builder implements AssertionBuilder { + + private final String body; + + public StringBodyAssertionBuilder(String expected) { + this.body = expected; + } + + /** + * @return a body assertion which verifiers the HTTP Response's body contains the expected body + */ + public BodyAssertion contains() { + return new BodyAssertion<>(String.class, this.body, (required, received) -> received.contains(required)); + } + + /** + * @return a body assertion which verifiers the HTTP Response's body is equals to the expected body + */ + public BodyAssertion equals() { + return new BodyAssertion<>(String.class, this.body, (required, received) -> received.equals(required)); + } + } + + /** + * Byte Array BodyAssertion Builder. + */ + public static class ByteArrayBodyAssertionBuilder extends BodyAssertion.Builder implements BodyAssertion.AssertionBuilder { + + private final byte[] body; + + public ByteArrayBodyAssertionBuilder(byte[] expected) { this.body = expected; - return this; } /** - * * @return a body assertion which verifiers the HTTP Response's body contains the expected body */ - public BodyAssertion contains() { - return new BodyAssertion(this.body, (expected, body) -> body.contains(expected)); + public BodyAssertion contains() { + return new BodyAssertion<>(byte[].class, this.body, (required, received) -> { + throw new AssertionError("Not implemented yet!"); + }); } /** - * * @return a body assertion which verifiers the HTTP Response's body is equals to the expected body */ - public BodyAssertion equals() { - return new BodyAssertion(this.body, (expected, body) -> body.equals(expected)); + public BodyAssertion equals() { + return new BodyAssertion<>(byte[].class, this.body, (required, received) -> Arrays.equals(received, required)); } } } 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 0ec962345b8..1b39cdfb938 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 @@ -36,14 +36,14 @@ public final class HttpResponseAssertion { private final HttpStatus httpStatus; private final Map headers; - private final BodyAssertion bodyAssertion; + private final BodyAssertion bodyAssertion; @Nullable private final Consumer> responseConsumer; private HttpResponseAssertion(HttpStatus httpStatus, Map headers, - BodyAssertion bodyAssertion, + BodyAssertion bodyAssertion, @Nullable Consumer> responseConsumer) { this.httpStatus = httpStatus; this.headers = headers; @@ -77,7 +77,7 @@ public Map getHeaders() { * @return Expected HTTP Response body */ - public BodyAssertion getBody() { + public BodyAssertion getBody() { return bodyAssertion; } @@ -95,7 +95,7 @@ public static HttpResponseAssertion.Builder builder() { public static class Builder { private HttpStatus httpStatus; private Map headers; - private BodyAssertion bodyAssertion; + private BodyAssertion bodyAssertion; private Consumer> responseConsumer; @@ -148,7 +148,7 @@ public Builder body(String containsBody) { * @param bodyAssertion Response Body Assertion * @return HTTP Response Assertion Builder */ - public Builder body(BodyAssertion bodyAssertion) { + public Builder body(BodyAssertion bodyAssertion) { this.bodyAssertion = bodyAssertion; return this; } 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 5e665b41c0a..a7104d96e4f 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 @@ -38,7 +38,6 @@ import java.util.Collections; import java.util.Map; - @SuppressWarnings({ "java:S5960", // We're allowed assertions, as these are used in tests only "checkstyle:MissingJavadocType", diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/OctetTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/OctetTest.java new file mode 100644 index 00000000000..5778c8c1e42 --- /dev/null +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/OctetTest.java @@ -0,0 +1,70 @@ +/* + * 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.MediaType; +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.BodyAssertion; +import io.micronaut.http.server.tck.HttpResponseAssertion; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.stream.IntStream; + +import static io.micronaut.http.server.tck.TestScenario.asserts; + +@SuppressWarnings({ + "java:S5960", // We're allowed assertions, as these are used in tests only + "checkstyle:MissingJavadocType", + "checkstyle:DesignForExtension" +}) +public class OctetTest { + + public static final String SPEC_NAME = "OctetTest"; + + @Test + void canReadOctetEncodedData() throws IOException { + asserts(SPEC_NAME, + HttpRequest.GET("/octets"), + (server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.OK) + .body(BodyAssertion.builder().body(OctetController.BODY_BYTES).equals()) + .build())); + } + + @Controller("/octets") + @Requires(property = "spec.name", value = SPEC_NAME) + static class OctetController { + + static final byte[] BODY_BYTES = IntStream.iterate(1, i -> i + 1) + .limit(256) + .map(i -> (byte) i) + .collect(ByteArrayOutputStream::new, ByteArrayOutputStream::write, (a, b) -> a.write(b.toByteArray(), 0, b.size())) + .toByteArray(); + + @Get(produces = MediaType.APPLICATION_OCTET_STREAM) + HttpResponse byteArray() { + return HttpResponse.ok(BODY_BYTES); + } + } +}