From 5450954be8e5930eaf8a22c2d60c0ece2ac23d3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20M=C3=BChlemann?= Date: Tue, 2 Jan 2024 07:51:46 +0100 Subject: [PATCH] Add enum discriminator tests Issue: #219 --- example-jakarta-3/build.gradle | 2 +- example/build.gradle | 7 +- .../openapi-oneof-enum-discriminator.yml | 69 ++++++++++++++ example/src/main/resources/openapi-oneof.yml | 10 ++ ...scriminatorMappingDeserialisationTest.java | 78 +++++++++++++++ ...DiscriminatorMappingSerialisationTest.java | 50 ++++++++++ .../DiscriminatorMappingValidationTest.java | 94 +++++++++++++++++++ .../__snapshots__/GeneratedClassesTest.snap | 6 ++ .../DeserialisationTest.java | 82 ++++++++++++++++ .../GeneratedClassesTest.java | 22 +++++ .../SerialisationTest.java | 48 ++++++++++ .../ValidationTest.java | 90 ++++++++++++++++++ .../__snapshots__/GeneratedClassesTest.snap | 13 +++ settings.gradle | 1 + spring-example/build.gradle | 2 +- 15 files changed, 571 insertions(+), 3 deletions(-) create mode 100644 example/src/main/resources/openapi-oneof-enum-discriminator.yml create mode 100644 example/src/test/java/com/github/muehmar/gradle/openapi/oneof/DiscriminatorMappingDeserialisationTest.java create mode 100644 example/src/test/java/com/github/muehmar/gradle/openapi/oneof/DiscriminatorMappingSerialisationTest.java create mode 100644 example/src/test/java/com/github/muehmar/gradle/openapi/oneof/DiscriminatorMappingValidationTest.java create mode 100644 example/src/test/java/com/github/muehmar/gradle/openapi/oneofenumdiscriminator/DeserialisationTest.java create mode 100644 example/src/test/java/com/github/muehmar/gradle/openapi/oneofenumdiscriminator/GeneratedClassesTest.java create mode 100644 example/src/test/java/com/github/muehmar/gradle/openapi/oneofenumdiscriminator/SerialisationTest.java create mode 100644 example/src/test/java/com/github/muehmar/gradle/openapi/oneofenumdiscriminator/ValidationTest.java create mode 100644 example/src/test/java/com/github/muehmar/gradle/openapi/oneofenumdiscriminator/__snapshots__/GeneratedClassesTest.snap diff --git a/example-jakarta-3/build.gradle b/example-jakarta-3/build.gradle index 5b69e0cde..409f83012 100644 --- a/example-jakarta-3/build.gradle +++ b/example-jakarta-3/build.gradle @@ -1,5 +1,5 @@ plugins { - id "com.github.muehmar.openapischema" version "2.4.1" + id "com.github.muehmar.openapischema" version "2.5.0-SNAPSHOT" id "com.diffplug.spotless" id 'openapischema.java' } diff --git a/example/build.gradle b/example/build.gradle index f94ee52df..1929fe6f1 100644 --- a/example/build.gradle +++ b/example/build.gradle @@ -1,5 +1,5 @@ plugins { - id "com.github.muehmar.openapischema" version "2.4.1" + id "com.github.muehmar.openapischema" version "2.5.0-SNAPSHOT" id "com.diffplug.spotless" id 'openapischema.java' id 'java-test-fixtures' @@ -59,6 +59,7 @@ def specifications = [name: 'freeform', file: 'openapi-freeform.yml'], [name: 'validation', file: 'openapi-validation.yml'], [name: 'oneof', file: 'openapi-oneof.yml'], + [name: 'oneofenumdiscriminator', file: 'openapi-oneof-enum-discriminator.yml'], [name: 'anyof', file: 'openapi-anyof.yml'], [name: 'allof', file: 'openapi-allof.yml'], [name: 'identifiers', file: 'openapi-identifiers.yml'], @@ -245,6 +246,10 @@ openApiGenerator { builderMethodPrefix = "" } + oneOfEnumDiscriminator { + inputSpec = "$projectDir/src/main/resources/openapi-oneof-enum-discriminator.yml" + packageName = "com.github.muehmar.gradle.openapi.oneofenumdiscriminator" + } ['71', '182', '185', '190', '191', '192', '193', '195', '209'].forEach {issueNumber -> { "${issueNumber}" { inputSpec = "$projectDir/src/main/resources/issues/openapi-issue-${issueNumber}.yml" diff --git a/example/src/main/resources/openapi-oneof-enum-discriminator.yml b/example/src/main/resources/openapi-oneof-enum-discriminator.yml new file mode 100644 index 000000000..e53a8b1a4 --- /dev/null +++ b/example/src/main/resources/openapi-oneof-enum-discriminator.yml @@ -0,0 +1,69 @@ +openapi: "3.0.0" +info: { } + +paths: { } + +components: + schemas: + User: + required: + - id + - username + allOf: + - $ref: '#/components/schemas/BaseUser' + properties: + id: + type: string + minLength: 5 + username: + type: string + age: + type: integer + format: int32 + minimum: 18 + maximum: 199 + writeOnly: true + email: + type: string + nullable: true + Admin: + required: + - id + - adminname + allOf: + - $ref: '#/components/schemas/BaseUser' + properties: + id: + type: string + minLength: 5 + adminname: + type: string + level: + type: integer + format: int64 + minimum: 1 + + BaseUser: + required: + - type + properties: + type: + type: string + enum: + - user + - admin + + AdminOrUser: + oneOf: + - $ref: '#/components/schemas/Admin' + - $ref: '#/components/schemas/User' + discriminator: + propertyName: type + mapping: + admin: '#/components/schemas/Admin' + user: '#/components/schemas/User' + + + + + diff --git a/example/src/main/resources/openapi-oneof.yml b/example/src/main/resources/openapi-oneof.yml index f9285aa1a..4adcb802a 100644 --- a/example/src/main/resources/openapi-oneof.yml +++ b/example/src/main/resources/openapi-oneof.yml @@ -57,6 +57,16 @@ components: discriminator: propertyName: type + AdminOrUserDiscriminatorMapping: + oneOf: + - $ref: '#/components/schemas/Admin' + - $ref: '#/components/schemas/User' + discriminator: + propertyName: type + mapping: + adm: '#/components/schemas/Admin' + usr: '#/components/schemas/User' + OneOfMemberReference: oneOf: - $ref: '#/components/schemas/BaseOneOfMemberReference' diff --git a/example/src/test/java/com/github/muehmar/gradle/openapi/oneof/DiscriminatorMappingDeserialisationTest.java b/example/src/test/java/com/github/muehmar/gradle/openapi/oneof/DiscriminatorMappingDeserialisationTest.java new file mode 100644 index 000000000..0672f4ba6 --- /dev/null +++ b/example/src/test/java/com/github/muehmar/gradle/openapi/oneof/DiscriminatorMappingDeserialisationTest.java @@ -0,0 +1,78 @@ +package com.github.muehmar.gradle.openapi.oneof; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.muehmar.gradle.openapi.util.MapperFactory; +import com.github.muehmar.openapi.util.Tristate; +import org.junit.jupiter.api.Test; + +class DiscriminatorMappingDeserialisationTest { + private static final ObjectMapper MAPPER = MapperFactory.mapper(); + + @Test + void fold_when_matchesAdmin_then_adminDtoReturned() throws JsonProcessingException { + final AdminOrUserDiscriminatorMappingDto adminOrUserDto = + MAPPER.readValue( + "{\"id\":\"admin-id\",\"type\":\"adm\",\"adminname\":\"admin-name\",\"level\":5.5}", + AdminOrUserDiscriminatorMappingDto.class); + + final Object obj = adminOrUserDto.foldOneOf(admin -> admin, user -> user); + + final AdminDto adminDto = + AdminDto.builder() + .setId("admin-id") + .setType("adm") + .setAdminname("admin-name") + .andAllOptionals() + .setLevel(5L) + .build(); + assertEquals(adminDto, obj); + } + + @Test + void fold_when_matchesUser_then_userDtoReturned() throws JsonProcessingException { + final AdminOrUserDiscriminatorMappingDto adminOrUserDto = + MAPPER.readValue( + "{\"id\":\"user-id\",\"type\":\"usr\",\"username\":\"user-name\",\"age\":25,\"email\":null}", + AdminOrUserDiscriminatorMappingDto.class); + + final Object obj = adminOrUserDto.foldOneOf(admin -> admin, user -> user); + + final UserDto userDto = + UserDto.builder() + .setId("user-id") + .setType("usr") + .setUsername("user-name") + .andAllOptionals() + .setAge(25) + .setEmail(Tristate.ofNull()) + .build(); + assertEquals(userDto, obj); + } + + @Test + void fold_when_invalidTypeWithoutOnInvalid_then_exceptionThrown() throws JsonProcessingException { + final AdminOrUserDiscriminatorMappingDto adminOrUserDto = + MAPPER.readValue( + "{\"id\":\"admin-id\",\"type\":\"Admin\",\"adminname\":\"admin-name\",\"level\":5.5}", + AdminOrUserDiscriminatorMappingDto.class); + + assertThrows( + IllegalStateException.class, () -> adminOrUserDto.foldOneOf(admin -> admin, user -> user)); + } + + @Test + void fold_when_invalidTypeWithOnInvalid_then_onInvalidReturned() throws JsonProcessingException { + final AdminOrUserDiscriminatorMappingDto adminOrUserDto = + MAPPER.readValue( + "{\"id\":\"admin-id\",\"type\":\"Admin\",\"adminname\":\"admin-name\",\"level\":5.5}", + AdminOrUserDiscriminatorMappingDto.class); + + final Object obj = adminOrUserDto.foldOneOf(admin -> admin, user -> user, () -> "invalid"); + + assertEquals("invalid", obj); + } +} diff --git a/example/src/test/java/com/github/muehmar/gradle/openapi/oneof/DiscriminatorMappingSerialisationTest.java b/example/src/test/java/com/github/muehmar/gradle/openapi/oneof/DiscriminatorMappingSerialisationTest.java new file mode 100644 index 000000000..3394802fd --- /dev/null +++ b/example/src/test/java/com/github/muehmar/gradle/openapi/oneof/DiscriminatorMappingSerialisationTest.java @@ -0,0 +1,50 @@ +package com.github.muehmar.gradle.openapi.oneof; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.muehmar.gradle.openapi.util.MapperFactory; +import com.github.muehmar.openapi.util.Tristate; +import org.junit.jupiter.api.Test; + +class DiscriminatorMappingSerialisationTest { + private static final ObjectMapper MAPPER = MapperFactory.mapper(); + + @Test + void writeValueAsString_when_adminDto_then_correctJson() throws JsonProcessingException { + final AdminDto adminDto = + AdminDto.builder() + .setId("admin-id") + .setType("type") + .setAdminname("admin-name") + .andAllOptionals() + .setLevel(5L) + .build(); + final AdminOrUserDiscriminatorMappingDto dto = + AdminOrUserDiscriminatorMappingDto.builder().setAdminDto(adminDto).build(); + + assertEquals( + "{\"adminname\":\"admin-name\",\"id\":\"admin-id\",\"level\":5,\"type\":\"adm\"}", + MAPPER.writeValueAsString(dto)); + } + + @Test + void writeValueAsString_when_userDto_then_correctJson() throws JsonProcessingException { + final UserDto userDto = + UserDto.builder() + .setId("user-id") + .setType("type") + .setUsername("user-name") + .andAllOptionals() + .setAge(25) + .setEmail(Tristate.ofNull()) + .build(); + final AdminOrUserDiscriminatorMappingDto dto = + AdminOrUserDiscriminatorMappingDto.builder().setUserDto(userDto).build(); + + assertEquals( + "{\"age\":25,\"email\":null,\"id\":\"user-id\",\"type\":\"usr\",\"username\":\"user-name\"}", + MAPPER.writeValueAsString(dto)); + } +} diff --git a/example/src/test/java/com/github/muehmar/gradle/openapi/oneof/DiscriminatorMappingValidationTest.java b/example/src/test/java/com/github/muehmar/gradle/openapi/oneof/DiscriminatorMappingValidationTest.java new file mode 100644 index 000000000..bfe3c813f --- /dev/null +++ b/example/src/test/java/com/github/muehmar/gradle/openapi/oneof/DiscriminatorMappingValidationTest.java @@ -0,0 +1,94 @@ +package com.github.muehmar.gradle.openapi.oneof; + +import static com.github.muehmar.gradle.openapi.util.ValidationUtil.validate; +import static com.github.muehmar.gradle.openapi.util.ViolationFormatter.formatViolations; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.muehmar.gradle.openapi.util.MapperFactory; +import java.util.Arrays; +import java.util.Set; +import javax.validation.ConstraintViolation; +import org.junit.jupiter.api.Test; + +class DiscriminatorMappingValidationTest { + + private static final ObjectMapper MAPPER = MapperFactory.mapper(); + + @Test + void validate_when_matchesUserSchema_then_noViolation() throws JsonProcessingException { + final AdminOrUserDiscriminatorMappingDto adminOrUserDto = + MAPPER.readValue( + "{\"id\":\"user-id\",\"type\":\"usr\",\"username\":\"user-name\",\"age\":25,\"email\":null}", + AdminOrUserDiscriminatorMappingDto.class); + + final Set> violations = + validate(adminOrUserDto); + + assertEquals(0, violations.size()); + assertTrue(adminOrUserDto.isValid()); + } + + @Test + void validate_when_matchesUserSchemaButInvalidAge_then_violation() + throws JsonProcessingException { + final AdminOrUserDiscriminatorMappingDto adminOrUserDto = + MAPPER.readValue( + "{\"id\":\"user-id\",\"type\":\"usr\",\"username\":\"user-name\",\"age\":200,\"email\":null}", + AdminOrUserDiscriminatorMappingDto.class); + + final Set> violations = + validate(adminOrUserDto); + + assertEquals( + Arrays.asList( + "invalidOneOf[User].ageRaw -> must be less than or equal to 199", + "validAgainstNoOneOfSchema -> Is not valid against one of the schemas [Admin, User]", + "validAgainstTheCorrectSchema -> Not valid against the schema described by the discriminator"), + formatViolations(violations), + String.join("\n", formatViolations(violations))); + assertFalse(adminOrUserDto.isValid()); + } + + @Test + void validate_when_matchesNoSchema_then_violations() throws JsonProcessingException { + final AdminOrUserDiscriminatorMappingDto adminOrUserDto = + MAPPER.readValue("{}", AdminOrUserDiscriminatorMappingDto.class); + + final Set> violations = + validate(adminOrUserDto); + + assertEquals( + Arrays.asList( + "invalidOneOf[Admin].adminname -> must not be null", + "invalidOneOf[Admin].id -> must not be null", + "invalidOneOf[Admin].type -> must not be null", + "invalidOneOf[User].id -> must not be null", + "invalidOneOf[User].type -> must not be null", + "invalidOneOf[User].username -> must not be null", + "validAgainstNoOneOfSchema -> Is not valid against one of the schemas [Admin, User]", + "validAgainstTheCorrectSchema -> Not valid against the schema described by the discriminator"), + formatViolations(violations)); + assertFalse(adminOrUserDto.isValid()); + } + + @Test + void validate_when_doesMatchBothSchemas_then_violation() throws JsonProcessingException { + final AdminOrUserDiscriminatorMappingDto adminOrUserDto = + MAPPER.readValue( + "{\"id\":\"id-123\",\"type\":\"usr\",\"username\":\"user-name\",\"adminname\":\"admin-name\",\"age\":25,\"email\":null}", + AdminOrUserDiscriminatorMappingDto.class); + + final Set> violations = + validate(adminOrUserDto); + + assertEquals( + Arrays.asList( + "validAgainstMoreThanOneSchema -> Is valid against more than one of the schemas [Admin, User]"), + formatViolations(violations)); + assertFalse(adminOrUserDto.isValid()); + } +} diff --git a/example/src/test/java/com/github/muehmar/gradle/openapi/oneof/__snapshots__/GeneratedClassesTest.snap b/example/src/test/java/com/github/muehmar/gradle/openapi/oneof/__snapshots__/GeneratedClassesTest.snap index ed16808cb..a08badb7b 100644 --- a/example/src/test/java/com/github/muehmar/gradle/openapi/oneof/__snapshots__/GeneratedClassesTest.snap +++ b/example/src/test/java/com/github/muehmar/gradle/openapi/oneof/__snapshots__/GeneratedClassesTest.snap @@ -1,6 +1,12 @@ allGeneratedClasses=[ AdminDto AdminOrUserDiscriminatorDto +AdminOrUserDiscriminatorMappingDto +AdminOrUserDiscriminatorMappingOneOfContainerDto +AdminOrUserDiscriminatorMappingOneOfContainerRequestDto +AdminOrUserDiscriminatorMappingOneOfContainerResponseDto +AdminOrUserDiscriminatorMappingRequestDto +AdminOrUserDiscriminatorMappingResponseDto AdminOrUserDiscriminatorOneOfContainerDto AdminOrUserDiscriminatorOneOfContainerRequestDto AdminOrUserDiscriminatorOneOfContainerResponseDto diff --git a/example/src/test/java/com/github/muehmar/gradle/openapi/oneofenumdiscriminator/DeserialisationTest.java b/example/src/test/java/com/github/muehmar/gradle/openapi/oneofenumdiscriminator/DeserialisationTest.java new file mode 100644 index 000000000..69bda1408 --- /dev/null +++ b/example/src/test/java/com/github/muehmar/gradle/openapi/oneofenumdiscriminator/DeserialisationTest.java @@ -0,0 +1,82 @@ +package com.github.muehmar.gradle.openapi.oneofenumdiscriminator; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.muehmar.gradle.openapi.oneof.AdminOrUserDiscriminatorMappingDto; +import com.github.muehmar.gradle.openapi.util.MapperFactory; +import com.github.muehmar.openapi.util.Tristate; +import java.util.Optional; +import org.junit.jupiter.api.Test; + +class DeserialisationTest { + private static final ObjectMapper MAPPER = MapperFactory.mapper(); + + @Test + void fold_when_matchesAdmin_then_adminDtoReturned() throws JsonProcessingException { + final AdminOrUserDto adminOrUserDto = + MAPPER.readValue( + "{\"id\":\"admin-id\",\"type\":\"admin\",\"adminname\":\"admin-name\",\"level\":5.5}", + AdminOrUserDto.class); + + final Object obj = adminOrUserDto.foldOneOf(admin -> admin, user -> user); + + final AdminDto adminDto = + AdminDto.builder() + .setType(BaseUserDto.TypeEnum.ADMIN) + .setId("admin-id") + .setAdminname("admin-name") + .andAllOptionals() + .setLevel(5L) + .build(); + assertEquals(adminDto, obj); + assertEquals(Optional.empty(), adminOrUserDto.getUserDto()); + assertEquals(Optional.of(adminDto), adminOrUserDto.getAdminDto()); + } + + @Test + void fold_when_matchesUser_then_userDtoReturned() throws JsonProcessingException { + final AdminOrUserDto adminOrUserDto = + MAPPER.readValue( + "{\"id\":\"user-id\",\"type\":\"user\",\"username\":\"user-name\",\"age\":25,\"email\":null}", + AdminOrUserDto.class); + + final Object obj = adminOrUserDto.foldOneOf(admin -> admin, user -> user); + + final UserDto userDto = + UserDto.builder() + .setType(BaseUserDto.TypeEnum.USER) + .setId("user-id") + .setUsername("user-name") + .andAllOptionals() + .setAge(25) + .setEmail(Tristate.ofNull()) + .build(); + assertEquals(userDto, obj); + } + + @Test + void fold_when_invalidTypeWithoutOnInvalid_then_exceptionThrown() throws JsonProcessingException { + final AdminOrUserDiscriminatorMappingDto adminOrUserDto = + MAPPER.readValue( + "{\"id\":\"admin-id\",\"type\":\"Admin\",\"adminname\":\"admin-name\",\"level\":5.5}", + AdminOrUserDiscriminatorMappingDto.class); + + assertThrows( + IllegalStateException.class, () -> adminOrUserDto.foldOneOf(admin -> admin, user -> user)); + } + + @Test + void fold_when_invalidTypeWithOnInvalid_then_onInvalidReturned() throws JsonProcessingException { + final AdminOrUserDiscriminatorMappingDto adminOrUserDto = + MAPPER.readValue( + "{\"id\":\"admin-id\",\"type\":\"Admin\",\"adminname\":\"admin-name\",\"level\":5.5}", + AdminOrUserDiscriminatorMappingDto.class); + + final Object obj = adminOrUserDto.foldOneOf(admin -> admin, user -> user, () -> "invalid"); + + assertEquals("invalid", obj); + } +} diff --git a/example/src/test/java/com/github/muehmar/gradle/openapi/oneofenumdiscriminator/GeneratedClassesTest.java b/example/src/test/java/com/github/muehmar/gradle/openapi/oneofenumdiscriminator/GeneratedClassesTest.java new file mode 100644 index 000000000..7c571086e --- /dev/null +++ b/example/src/test/java/com/github/muehmar/gradle/openapi/oneofenumdiscriminator/GeneratedClassesTest.java @@ -0,0 +1,22 @@ +package com.github.muehmar.gradle.openapi.oneofenumdiscriminator; + +import au.com.origin.snapshots.Expect; +import au.com.origin.snapshots.annotations.SnapshotName; +import com.github.muehmar.gradle.openapi.snapshot.SnapshotTest; +import com.github.muehmar.gradle.openapi.util.ClassFinder; +import java.util.List; +import org.junit.jupiter.api.Test; + +@SnapshotTest +class GeneratedClassesTest { + private Expect expect; + + @Test + @SnapshotName("allGeneratedClasses") + void findNonTestClassesInPackage_when_modelPackage_then_allClasses() { + final List classNames = + ClassFinder.findNonTestClassesInPackage(getClass().getPackage().getName()); + + expect.toMatchSnapshot(String.join("\n", classNames)); + } +} diff --git a/example/src/test/java/com/github/muehmar/gradle/openapi/oneofenumdiscriminator/SerialisationTest.java b/example/src/test/java/com/github/muehmar/gradle/openapi/oneofenumdiscriminator/SerialisationTest.java new file mode 100644 index 000000000..9a9da5caf --- /dev/null +++ b/example/src/test/java/com/github/muehmar/gradle/openapi/oneofenumdiscriminator/SerialisationTest.java @@ -0,0 +1,48 @@ +package com.github.muehmar.gradle.openapi.oneofenumdiscriminator; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.muehmar.gradle.openapi.util.MapperFactory; +import com.github.muehmar.openapi.util.Tristate; +import org.junit.jupiter.api.Test; + +class SerialisationTest { + private static final ObjectMapper MAPPER = MapperFactory.mapper(); + + @Test + void writeValueAsString_when_adminDto_then_correctJson() throws JsonProcessingException { + final AdminDto adminDto = + AdminDto.builder() + .setType(BaseUserDto.TypeEnum.USER) + .setId("admin-id") + .setAdminname("admin-name") + .andAllOptionals() + .setLevel(5L) + .build(); + final AdminOrUserDto dto = AdminOrUserDto.builder().setAdminDto(adminDto).build(); + + assertEquals( + "{\"adminname\":\"admin-name\",\"id\":\"admin-id\",\"level\":5,\"type\":\"admin\"}", + MAPPER.writeValueAsString(dto)); + } + + @Test + void writeValueAsString_when_userDto_then_correctJson() throws JsonProcessingException { + final UserDto userDto = + UserDto.builder() + .setType(BaseUserDto.TypeEnum.ADMIN) + .setId("user-id") + .setUsername("user-name") + .andAllOptionals() + .setAge(25) + .setEmail(Tristate.ofNull()) + .build(); + final AdminOrUserDto dto = AdminOrUserDto.builder().setUserDto(userDto).build(); + + assertEquals( + "{\"age\":25,\"email\":null,\"id\":\"user-id\",\"type\":\"user\",\"username\":\"user-name\"}", + MAPPER.writeValueAsString(dto)); + } +} diff --git a/example/src/test/java/com/github/muehmar/gradle/openapi/oneofenumdiscriminator/ValidationTest.java b/example/src/test/java/com/github/muehmar/gradle/openapi/oneofenumdiscriminator/ValidationTest.java new file mode 100644 index 000000000..bd6f2ce49 --- /dev/null +++ b/example/src/test/java/com/github/muehmar/gradle/openapi/oneofenumdiscriminator/ValidationTest.java @@ -0,0 +1,90 @@ +package com.github.muehmar.gradle.openapi.oneofenumdiscriminator; + +import static com.github.muehmar.gradle.openapi.util.ValidationUtil.validate; +import static com.github.muehmar.gradle.openapi.util.ViolationFormatter.formatViolations; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.muehmar.gradle.openapi.util.MapperFactory; +import java.util.Arrays; +import java.util.Collections; +import java.util.Set; +import javax.validation.ConstraintViolation; +import org.junit.jupiter.api.Test; + +class ValidationTest { + + private static final ObjectMapper MAPPER = MapperFactory.mapper(); + + @Test + void validate_when_matchesUserSchema_then_noViolation() throws JsonProcessingException { + final AdminOrUserDto adminOrUserDto = + MAPPER.readValue( + "{\"id\":\"user-id\",\"type\":\"user\",\"username\":\"user-name\",\"age\":25,\"email\":null}", + AdminOrUserDto.class); + + final Set> violations = validate(adminOrUserDto); + + assertEquals(0, violations.size()); + assertTrue(adminOrUserDto.isValid()); + } + + @Test + void validate_when_matchesUserSchemaButInvalidAge_then_violation() + throws JsonProcessingException { + final AdminOrUserDto adminOrUserDto = + MAPPER.readValue( + "{\"id\":\"user-id\",\"type\":\"user\",\"username\":\"user-name\",\"age\":200,\"email\":null}", + AdminOrUserDto.class); + + final Set> violations = validate(adminOrUserDto); + + assertEquals( + Arrays.asList( + "invalidOneOf[User].ageRaw -> must be less than or equal to 199", + "validAgainstNoOneOfSchema -> Is not valid against one of the schemas [Admin, User]", + "validAgainstTheCorrectSchema -> Not valid against the schema described by the discriminator"), + formatViolations(violations), + String.join("\n", formatViolations(violations))); + assertFalse(adminOrUserDto.isValid()); + } + + @Test + void validate_when_matchesNoSchema_then_violations() throws JsonProcessingException { + final AdminOrUserDto adminOrUserDto = MAPPER.readValue("{}", AdminOrUserDto.class); + + final Set> violations = validate(adminOrUserDto); + + assertEquals( + Arrays.asList( + "invalidOneOf[Admin].adminname -> must not be null", + "invalidOneOf[Admin].baseUserDto.type -> must not be null", + "invalidOneOf[Admin].id -> must not be null", + "invalidOneOf[User].baseUserDto.type -> must not be null", + "invalidOneOf[User].id -> must not be null", + "invalidOneOf[User].username -> must not be null", + "validAgainstNoOneOfSchema -> Is not valid against one of the schemas [Admin, User]", + "validAgainstTheCorrectSchema -> Not valid against the schema described by the discriminator"), + formatViolations(violations)); + assertFalse(adminOrUserDto.isValid()); + } + + @Test + void validate_when_doesMatchBothSchemas_then_violation() throws JsonProcessingException { + final AdminOrUserDto adminOrUserDto = + MAPPER.readValue( + "{\"id\":\"id-123\",\"type\":\"user\",\"username\":\"user-name\",\"adminname\":\"admin-name\",\"age\":25,\"email\":null}", + AdminOrUserDto.class); + + final Set> violations = validate(adminOrUserDto); + + assertEquals( + Collections.singletonList( + "validAgainstMoreThanOneSchema -> Is valid against more than one of the schemas [Admin, User]"), + formatViolations(violations)); + assertFalse(adminOrUserDto.isValid()); + } +} diff --git a/example/src/test/java/com/github/muehmar/gradle/openapi/oneofenumdiscriminator/__snapshots__/GeneratedClassesTest.snap b/example/src/test/java/com/github/muehmar/gradle/openapi/oneofenumdiscriminator/__snapshots__/GeneratedClassesTest.snap new file mode 100644 index 000000000..cf8c5fbfc --- /dev/null +++ b/example/src/test/java/com/github/muehmar/gradle/openapi/oneofenumdiscriminator/__snapshots__/GeneratedClassesTest.snap @@ -0,0 +1,13 @@ +allGeneratedClasses=[ +AdminDto +AdminOrUserDto +AdminOrUserOneOfContainerDto +AdminOrUserOneOfContainerRequestDto +AdminOrUserOneOfContainerResponseDto +AdminOrUserRequestDto +AdminOrUserResponseDto +BaseUserDto +UserDto +UserRequestDto +UserResponseDto +] \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 03d1765dd..13b33d0ad 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,6 +1,7 @@ pluginManagement { repositories { gradlePluginPortal() + mavenLocal() } } diff --git a/spring-example/build.gradle b/spring-example/build.gradle index 392b9f4bb..06e15b253 100644 --- a/spring-example/build.gradle +++ b/spring-example/build.gradle @@ -1,5 +1,5 @@ plugins { - id "com.github.muehmar.openapischema" version "2.4.1" + id "com.github.muehmar.openapischema" version "2.5.0-SNAPSHOT" id "com.diffplug.spotless" id 'openapischema.java' id 'idea'