From 87cd922dc3635cc1457c767c98a723f06980551d Mon Sep 17 00:00:00 2001 From: Denis Stepanov Date: Thu, 1 Feb 2024 11:20:29 +0100 Subject: [PATCH] Add tests for nested serialization / deserialization (#746) --- .../BsonBinaryBasicSerdeCompileSpec.groovy | 6 +++ .../bson/BsonBinaryBasicSerdeSpec.groovy | 6 +++ .../serde/bson/BsonBinarySpec.groovy | 7 ++- serde-jackson/build.gradle.kts | 2 + .../serde/jackson/annotation/http/Api.java | 23 +++++++++ .../jackson/annotation/http/ApiClient.java | 7 +++ .../annotation/http/ApiController.java | 45 +++++++++++++++++ .../jackson/annotation/http/ApiSpec.groovy | 49 +++++++++++++++++++ .../OracleJdbcJsonBinaryBasicSerdeSpec.groovy | 7 +++ .../serde/AbstractBasicSerdeSpec.groovy | 16 ++++++ .../groovy/io/micronaut/serde/JsonSpec.groovy | 5 ++ .../java/io/micronaut/serde/ApiResponse.java | 22 +++++++++ .../main/java/io/micronaut/serde/Dummy.java | 22 +++++++++ 13 files changed, 216 insertions(+), 1 deletion(-) create mode 100644 serde-jackson/src/test/groovy/io/micronaut/serde/jackson/annotation/http/Api.java create mode 100644 serde-jackson/src/test/groovy/io/micronaut/serde/jackson/annotation/http/ApiClient.java create mode 100644 serde-jackson/src/test/groovy/io/micronaut/serde/jackson/annotation/http/ApiController.java create mode 100644 serde-jackson/src/test/groovy/io/micronaut/serde/jackson/annotation/http/ApiSpec.groovy create mode 100644 serde-tck/src/main/java/io/micronaut/serde/ApiResponse.java create mode 100644 serde-tck/src/main/java/io/micronaut/serde/Dummy.java diff --git a/serde-bson/src/test/groovy/io/micronaut/serde/bson/BsonBinaryBasicSerdeCompileSpec.groovy b/serde-bson/src/test/groovy/io/micronaut/serde/bson/BsonBinaryBasicSerdeCompileSpec.groovy index 6171e3181..4e739482a 100644 --- a/serde-bson/src/test/groovy/io/micronaut/serde/bson/BsonBinaryBasicSerdeCompileSpec.groovy +++ b/serde-bson/src/test/groovy/io/micronaut/serde/bson/BsonBinaryBasicSerdeCompileSpec.groovy @@ -1,5 +1,6 @@ package io.micronaut.serde.bson +import io.micronaut.core.type.Argument import io.micronaut.json.JsonMapper import io.micronaut.serde.AbstractBasicSerdeCompileSpec import io.micronaut.serde.AbstractBasicSerdeSpec @@ -24,6 +25,11 @@ class BsonBinaryBasicSerdeCompileSpec extends AbstractBasicSerdeCompileSpec impl return encodeAsBinaryDecodeJson(bean) } + @Override + String writeJson(JsonMapper jsonMapper, Argument argument, Object bean) { + return encodeAsBinaryDecodeJson(argument, bean) + } + @Override byte[] jsonAsBytes(String json) { def parse = BsonDocument.parse(json) diff --git a/serde-bson/src/test/groovy/io/micronaut/serde/bson/BsonBinaryBasicSerdeSpec.groovy b/serde-bson/src/test/groovy/io/micronaut/serde/bson/BsonBinaryBasicSerdeSpec.groovy index 5b25962cd..0e1d6e215 100644 --- a/serde-bson/src/test/groovy/io/micronaut/serde/bson/BsonBinaryBasicSerdeSpec.groovy +++ b/serde-bson/src/test/groovy/io/micronaut/serde/bson/BsonBinaryBasicSerdeSpec.groovy @@ -1,5 +1,6 @@ package io.micronaut.serde.bson +import io.micronaut.core.type.Argument import io.micronaut.json.JsonMapper import io.micronaut.serde.AbstractBasicSerdeSpec import io.micronaut.test.extensions.spock.annotation.MicronautTest @@ -22,6 +23,11 @@ class BsonBinaryBasicSerdeSpec extends AbstractBasicSerdeSpec implements BsonBin return encodeAsBinaryDecodeJson(bean) } + @Override + String writeJson(JsonMapper jsonMapper, Argument argument, Object bean) { + return encodeAsBinaryDecodeJson(argument, bean) + } + @Override byte[] jsonAsBytes(String json) { def parse = BsonDocument.parse(json) diff --git a/serde-bson/src/test/groovy/io/micronaut/serde/bson/BsonBinarySpec.groovy b/serde-bson/src/test/groovy/io/micronaut/serde/bson/BsonBinarySpec.groovy index ee6c43acd..155053c23 100644 --- a/serde-bson/src/test/groovy/io/micronaut/serde/bson/BsonBinarySpec.groovy +++ b/serde-bson/src/test/groovy/io/micronaut/serde/bson/BsonBinarySpec.groovy @@ -22,6 +22,11 @@ trait BsonBinarySpec { return readByteArrayAsJson(data) } + String encodeAsBinaryDecodeJson(Argument argument, Object obj) { + def data = getBsonBinaryMapper().writeValueAsBytes(argument, obj) + return readByteArrayAsJson(data) + } + def T encodeAsBinaryDecodeAsObject(BsonDocument ob, Class type) { def bytes = writeToByteArray(ob) return bsonBinaryMapper.readValue(bytes, Argument.of(type)) @@ -45,4 +50,4 @@ trait BsonBinarySpec { return document.toJson(JsonWriterSettings.builder().outputMode(JsonMode.RELAXED).indentCharacters("").build()) } -} \ No newline at end of file +} diff --git a/serde-jackson/build.gradle.kts b/serde-jackson/build.gradle.kts index 63fa12e5a..f97705824 100644 --- a/serde-jackson/build.gradle.kts +++ b/serde-jackson/build.gradle.kts @@ -25,4 +25,6 @@ dependencies { testImplementation(libs.aws.lambda.events) testImplementation(libs.micronaut.discovery) testImplementation(projects.micronautSerdeJacksonTck) + testImplementation(mn.micronaut.http.client) + testImplementation(mn.micronaut.http.server.netty) } diff --git a/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/annotation/http/Api.java b/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/annotation/http/Api.java new file mode 100644 index 000000000..ee0e723e8 --- /dev/null +++ b/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/annotation/http/Api.java @@ -0,0 +1,23 @@ +package io.micronaut.serde.jackson.annotation.http; + +import io.micronaut.http.annotation.Get; +import io.micronaut.serde.ApiResponse; +import io.micronaut.serde.Dummy; + +import java.util.List; + +public interface Api { + + @Get("/api/dummy/wrapped/list") + ApiResponse> wrappedList(); + + @Get("/api/dummy/wrapped/nested") + ApiResponse> wrappedNested(); + + @Get("/api/dummy/wrapped/simple") + ApiResponse wrappedSimple(); + + @Get("/api/dummy/raw") + List simpleList(); + +} diff --git a/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/annotation/http/ApiClient.java b/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/annotation/http/ApiClient.java new file mode 100644 index 000000000..49cde7e1f --- /dev/null +++ b/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/annotation/http/ApiClient.java @@ -0,0 +1,7 @@ +package io.micronaut.serde.jackson.annotation.http; + +import io.micronaut.http.client.annotation.Client; + +@Client("/") +public interface ApiClient extends Api { +} diff --git a/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/annotation/http/ApiController.java b/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/annotation/http/ApiController.java new file mode 100644 index 000000000..6535104a2 --- /dev/null +++ b/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/annotation/http/ApiController.java @@ -0,0 +1,45 @@ +package io.micronaut.serde.jackson.annotation.http; + +import io.micronaut.http.annotation.Controller; +import io.micronaut.serde.ApiResponse; +import io.micronaut.serde.Dummy; + +import java.util.List; + +@Controller +public class ApiController implements Api { + + @Override + public ApiResponse> wrappedList() { + return new ApiResponse<>( + List.of( + new Dummy("test-1"), + new Dummy("test-2") + ) + ); + } + + @Override + public ApiResponse> wrappedNested() { + return new ApiResponse<>( + new ApiResponse<>( + new Dummy("test-1") + ) + ); + } + + @Override + public ApiResponse wrappedSimple() { + return new ApiResponse<>( + new Dummy("test-1") + ); + } + + @Override + public List simpleList() { + return List.of( + new Dummy("test-1"), + new Dummy("test-2") + ); + } +} diff --git a/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/annotation/http/ApiSpec.groovy b/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/annotation/http/ApiSpec.groovy new file mode 100644 index 000000000..525531551 --- /dev/null +++ b/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/annotation/http/ApiSpec.groovy @@ -0,0 +1,49 @@ +package io.micronaut.serde.jackson.annotation.http + +import io.micronaut.context.ApplicationContext +import io.micronaut.http.client.annotation.Client +import io.micronaut.json.JsonMapper +import io.micronaut.serde.annotation.Serdeable +import io.micronaut.serde.jackson.JsonCompileSpec +import io.micronaut.test.extensions.spock.annotation.MicronautTest +import jakarta.inject.Inject +import spock.lang.Specification + +@MicronautTest +class ApiSpec extends Specification { + + @Inject + ApiClient apiClient + + void "test simpleList"() { + when: + var simpleList = apiClient.simpleList(); + then: + simpleList[0].name() == "test-1" + simpleList[1].name() == "test-2" + } + + void "test wrappedList"() { + when: + var wrappedList = apiClient.wrappedList(); + then: + wrappedList.content()[0].name() == "test-1" + wrappedList.content()[1].name() == "test-2" + } + + void "test wrappedNested"() { + when: + var wrappedNested = apiClient.wrappedNested(); + then: + wrappedNested.content().content().name() == "test-1" + } + + void "test wrappedSimple"() { + when: + var wrappedSimple = apiClient.wrappedSimple(); + then: + wrappedSimple.content().name() == "test-1" + } + +} + diff --git a/serde-oracle-jdbc-json/src/test/groovy/io/micronaut/serde/oracle/jdbc/json/OracleJdbcJsonBinaryBasicSerdeSpec.groovy b/serde-oracle-jdbc-json/src/test/groovy/io/micronaut/serde/oracle/jdbc/json/OracleJdbcJsonBinaryBasicSerdeSpec.groovy index c9548a65c..4d8e2f7cd 100644 --- a/serde-oracle-jdbc-json/src/test/groovy/io/micronaut/serde/oracle/jdbc/json/OracleJdbcJsonBinaryBasicSerdeSpec.groovy +++ b/serde-oracle-jdbc-json/src/test/groovy/io/micronaut/serde/oracle/jdbc/json/OracleJdbcJsonBinaryBasicSerdeSpec.groovy @@ -47,6 +47,13 @@ class OracleJdbcJsonBinaryBasicSerdeSpec extends AbstractBasicSerdeSpec { return new String(textJsonMapper.writeValueAsBytes(object), StandardCharsets.UTF_8) } + @Override + String writeJson(JsonMapper jsonMapper, Argument argument, Object bean) { + def bytes = jsonMapper.writeValueAsBytes(argument, bean) + def object = osonMapper.readValue(bytes, argument) + return new String(textJsonMapper.writeValueAsBytes(argument, object), StandardCharsets.UTF_8) + } + @Override byte[] jsonAsBytes(String json) { def object = textJsonMapper.readValue(json, OracleJsonObject) diff --git a/serde-tck/src/main/groovy/io/micronaut/serde/AbstractBasicSerdeSpec.groovy b/serde-tck/src/main/groovy/io/micronaut/serde/AbstractBasicSerdeSpec.groovy index f1172f56a..3abdc94f2 100644 --- a/serde-tck/src/main/groovy/io/micronaut/serde/AbstractBasicSerdeSpec.groovy +++ b/serde-tck/src/main/groovy/io/micronaut/serde/AbstractBasicSerdeSpec.groovy @@ -39,6 +39,22 @@ abstract class AbstractBasicSerdeSpec extends Specification implements JsonSpec, jsonMatches(result, '{"name":"Test"}') } + void "test nested"() { + when: + def bean = new ApiResponse(List.of(new Dummy("Xyz"))) + def argument = Argument.of(ApiResponse, Argument.listOf(Dummy)) + def result = writeJson(jsonMapper, argument, bean) + + then: + jsonMatches(result, '{"content":[{"name":"Xyz"}]}') + + when: + bean = jsonMapper.readValue(jsonAsBytes(result), argument) + + then: + bean.content[0].name == "Xyz" + } + void "test read/write constructor args"() { when: def bean = new ConstructorArgs("test", 100) diff --git a/serde-tck/src/main/groovy/io/micronaut/serde/JsonSpec.groovy b/serde-tck/src/main/groovy/io/micronaut/serde/JsonSpec.groovy index d445866cb..0506094d8 100644 --- a/serde-tck/src/main/groovy/io/micronaut/serde/JsonSpec.groovy +++ b/serde-tck/src/main/groovy/io/micronaut/serde/JsonSpec.groovy @@ -15,10 +15,15 @@ */ package io.micronaut.serde +import io.micronaut.core.type.Argument import io.micronaut.json.JsonMapper trait JsonSpec { String writeJson(JsonMapper jsonMapper, Object bean) { new String(jsonMapper.writeValueAsBytes(bean)) } + + String writeJson(JsonMapper jsonMapper, Argument argument, Object bean) { + new String(jsonMapper.writeValueAsBytes(argument, bean)) + } } diff --git a/serde-tck/src/main/java/io/micronaut/serde/ApiResponse.java b/serde-tck/src/main/java/io/micronaut/serde/ApiResponse.java new file mode 100644 index 000000000..892ef4b08 --- /dev/null +++ b/serde-tck/src/main/java/io/micronaut/serde/ApiResponse.java @@ -0,0 +1,22 @@ +/* + * Copyright 2017-2024 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.serde; + +import io.micronaut.serde.annotation.Serdeable; + +@Serdeable +public record ApiResponse(T content) { +} diff --git a/serde-tck/src/main/java/io/micronaut/serde/Dummy.java b/serde-tck/src/main/java/io/micronaut/serde/Dummy.java new file mode 100644 index 000000000..ab1614995 --- /dev/null +++ b/serde-tck/src/main/java/io/micronaut/serde/Dummy.java @@ -0,0 +1,22 @@ +/* + * Copyright 2017-2024 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.serde; + +import io.micronaut.serde.annotation.Serdeable; + +@Serdeable +public record Dummy(String name) { +}