From 6ba4ba37a27090edf9bdb1c4bdad844dbd6f450b Mon Sep 17 00:00:00 2001 From: JohannesRabauer Date: Fri, 20 Dec 2024 10:00:05 +0100 Subject: [PATCH 1/3] Added more contraint tests --- .../constraints/ConstraintDaoObject.java | 20 +- .../tests/constraints/ConstraintsTest.java | 438 +++++++++++++++++- 2 files changed, 441 insertions(+), 17 deletions(-) diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/constraints/ConstraintDaoObject.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/constraints/ConstraintDaoObject.java index 7d113e96..54c18e35 100644 --- a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/constraints/ConstraintDaoObject.java +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/constraints/ConstraintDaoObject.java @@ -65,7 +65,7 @@ public class ConstraintDaoObject @DecimalMin("5.00") BigDecimal discountMin5; - @DecimalMax("30.00") + @DecimalMax("20.00") BigDecimal discountMax20; @Email @@ -116,8 +116,8 @@ public class ConstraintDaoObject @PositiveOrZero int positiveOrZeroField; - @Size(min = 2, max = 240) - String messageMin2AndMax240; + @Size(min = 2, max = 10) + String messageMin2AndMax10; public ConstraintDaoObject() { @@ -141,7 +141,7 @@ public ConstraintDaoObject() this.phoneNumber = "(123)456-7890"; this.area = BigDecimal.valueOf(1); this.positiveOrZeroField = 1; - this.messageMin2AndMax240 = ".."; + this.messageMin2AndMax10 = ".."; } @AssertFalse @@ -186,12 +186,12 @@ public void setDiscountMin5(final @DecimalMin("5.00") BigDecimal discountMin5) this.discountMin5 = discountMin5; } - public @DecimalMax("30.00") BigDecimal getDiscountMax20() + public @DecimalMax("20.00") BigDecimal getDiscountMax20() { return this.discountMax20; } - public void setDiscountMax20(final @DecimalMax("30.00") BigDecimal discountMax20) + public void setDiscountMax20(final @DecimalMax("20.00") BigDecimal discountMax20) { this.discountMax20 = discountMax20; } @@ -361,13 +361,13 @@ public void setPositiveOrZeroField(@PositiveOrZero final int positiveOrZeroField this.positiveOrZeroField = positiveOrZeroField; } - public @Size(min = 2, max = 240) String getMessageMin2AndMax240() + public @Size(min = 2, max = 10) String getMessageMin2AndMax10() { - return this.messageMin2AndMax240; + return this.messageMin2AndMax10; } - public void setMessageMin2AndMax240(final @Size(min = 2, max = 240) String messageMin2AndMax240) + public void setMessageMin2AndMax10(final @Size(min = 2, max = 10) String messageMin2AndMax10) { - this.messageMin2AndMax240 = messageMin2AndMax240; + this.messageMin2AndMax10 = messageMin2AndMax10; } } diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/constraints/ConstraintsTest.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/constraints/ConstraintsTest.java index 5578cea9..ea8016a1 100644 --- a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/constraints/ConstraintsTest.java +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/constraints/ConstraintsTest.java @@ -15,11 +15,20 @@ */ package software.xdev.spring.data.eclipse.store.integration.isolated.tests.constraints; +import static org.springframework.test.annotation.DirtiesContext.ClassMode.BEFORE_CLASS; + +import java.math.BigDecimal; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Date; +import java.util.function.Consumer; + import jakarta.validation.ConstraintViolationException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import software.xdev.spring.data.eclipse.store.integration.isolated.IsolatedTestAnnotations; @@ -32,6 +41,8 @@ */ @IsolatedTestAnnotations @ContextConfiguration(classes = {ConstraintsTestConfiguration.class}) +@SuppressWarnings("checkstyle:MethodName") +@DirtiesContext(classMode = BEFORE_CLASS) class ConstraintsTest { @Autowired @@ -42,19 +53,432 @@ class ConstraintsTest @Test void assertFalseWithTrue() { - final ConstraintDaoObject constraintDaoObject = new ConstraintDaoObject(); - constraintDaoObject.setAlwaysFalse(true); - Assertions.assertThrows( - ConstraintViolationException.class, - () -> this.repository.save(constraintDaoObject) - ); + this.assertConstraintViolationChange(o -> o.setAlwaysFalse(true)); } @Test void assertFalseWithFalse() + { + this.assertGoodChange(o -> o.setAlwaysFalse(false)); + } + + @Test + void assertTrueWithTrue() + { + this.assertGoodChange(o -> o.setAlwaysTrue(true)); + } + + @Test + void assertTrueWithFalse() + { + this.assertConstraintViolationChange(o -> o.setAlwaysTrue(false)); + } + + @Test + void assertDigitsGood() + { + this.assertGoodChange(o -> o.setPrice(BigDecimal.valueOf(123456.78))); + } + + @Test + void assertDigitsTooMuchInteger() + { + this.assertConstraintViolationChange(o -> o.setPrice(BigDecimal.valueOf(1234567))); + } + + @Test + void assertDigitsTooMuchFraction() + { + this.assertConstraintViolationChange(o -> o.setPrice(BigDecimal.valueOf(0.123))); + } + + @Test + void assertDecimalMinGoodExact() + { + this.assertGoodChange(o -> o.setDiscountMin5(BigDecimal.valueOf(5))); + } + + @Test + void assertDecimalMinGoodGreater() + { + this.assertGoodChange(o -> o.setDiscountMin5(BigDecimal.valueOf(6))); + } + + @Test + void assertDecimalMinBadSmaller() + { + this.assertConstraintViolationChange(o -> o.setDiscountMin5(BigDecimal.valueOf(4))); + } + + @Test + void assertDecimalMinBadNegative() + { + this.assertConstraintViolationChange(o -> o.setDiscountMin5(BigDecimal.valueOf(-1))); + } + + @Test + void assertDecimalMaxGoodExact() + { + this.assertGoodChange(o -> o.setDiscountMax20(BigDecimal.valueOf(20))); + } + + @Test + void assertDecimalMaxGoodSmaller() + { + this.assertGoodChange(o -> o.setDiscountMax20(BigDecimal.ONE)); + } + + @Test + void assertDecimalMaxGoodZero() + { + this.assertGoodChange(o -> o.setDiscountMax20(BigDecimal.ZERO)); + } + + @Test + void assertDecimalMaxBadBigger() + { + this.assertConstraintViolationChange(o -> o.setDiscountMax20(BigDecimal.valueOf(21))); + } + + @Test + void assertDecimalMaxBadBiggest() + { + this.assertConstraintViolationChange(o -> o.setDiscountMax20(BigDecimal.valueOf(Double.MAX_VALUE))); + } + + @Test + void assertDecimalMaxGoodNegative() + { + this.assertGoodChange(o -> o.setDiscountMax20(BigDecimal.valueOf(-1))); + } + + @Test + void assertEmailGood() + { + this.assertGoodChange(o -> o.setEmailField("a@b.c")); + } + + @Test + void assertEmailBad_MissingAt() + { + this.assertConstraintViolationChange(o -> o.setEmailField("a.b")); + } + + @Test + void assertEmailGood_MissingDot() + { + this.assertGoodChange(o -> o.setEmailField("a@b")); + } + + @Test + void assertEmailBad_NoPrefixBeforeAt() + { + this.assertConstraintViolationChange(o -> o.setEmailField("@b.c")); + } + + @Test + void assertEmailGood_Empty() + { + this.assertGoodChange(o -> o.setEmailField("")); + } + + @Test + void assertEmailGood_Null() + { + this.assertGoodChange(o -> o.setEmailField(null)); + } + + @Test + void assertFutureFieldGood() + { + this.assertGoodChange(o -> o.setFutureField(Date.from(Instant.now().plus(1, ChronoUnit.DAYS)))); + } + + @Test + void assertFutureFieldBad_Now() + { + this.assertConstraintViolationChange(o -> o.setFutureField(Date.from(Instant.now()))); + } + + @Test + void assertFutureFieldBad_Yesterday() + { + this.assertConstraintViolationChange(o -> o.setFutureField(Date.from(Instant.now().minus(1, ChronoUnit.DAYS)))); + } + + @Test + void assertFutureOrPresentFieldGood() + { + this.assertGoodChange(o -> o.setFutureOrPresentField(Date.from(Instant.now().plus(1, ChronoUnit.DAYS)))); + } + + @Test + void assertFutureOrPresentFieldBad_Yesterday() + { + this.assertConstraintViolationChange(o -> o.setFutureOrPresentField(Date.from(Instant.now() + .minus(1, ChronoUnit.DAYS)))); + } + + @Test + void assertPastFieldBad() + { + this.assertConstraintViolationChange(o -> o.setPastField(Date.from(Instant.now().plus(1, ChronoUnit.DAYS)))); + } + + @Test + void assertPastFieldGood_Now() + { + this.assertGoodChange(o -> o.setPastField(Date.from(Instant.now()))); + } + + @Test + void assertPastFieldGood_Yesterday() + { + this.assertGoodChange(o -> o.setPastField(Date.from(Instant.now().minus(1, ChronoUnit.DAYS)))); + } + + @Test + void assertPastOrPresentFieldBad() + { + this.assertConstraintViolationChange(o -> o.setPastOrPresentField(Date.from(Instant.now() + .plus(1, ChronoUnit.DAYS)))); + } + + @Test + void assertPastOrPresentFieldGood_Now() + { + this.assertGoodChange(o -> o.setPastOrPresentField(Date.from(Instant.now()))); + } + + @Test + void assertPastOrPresentFieldGood_Yesterday() + { + this.assertGoodChange(o -> o.setPastOrPresentField(Date.from(Instant.now().minus(1, ChronoUnit.DAYS)))); + } + + @Test + void assertMaxGood() + { + this.assertGoodChange(o -> o.setQuantityMax10(10)); + } + + @Test + void assertMaxBad() + { + this.assertConstraintViolationChange(o -> o.setQuantityMax10(11)); + } + + @Test + void assertMinGood() + { + this.assertGoodChange(o -> o.setQuantityMin5(5)); + } + + @Test + void assertMinBad() + { + this.assertConstraintViolationChange(o -> o.setQuantityMin5(3)); + } + + @Test + void assertNegativeFieldGood() + { + this.assertGoodChange(o -> o.setNegativeField(-1)); + } + + @Test + void assertNegativeFieldBad() + { + this.assertConstraintViolationChange(o -> o.setNegativeField(1)); + } + + @Test + void assertNegativeFieldBad_Zero() + { + this.assertConstraintViolationChange(o -> o.setNegativeField(0)); + } + + @Test + void assertNegativeOrZeroFieldGood_Zero() + { + this.assertGoodChange(o -> o.setNegativeOrZeroField(0)); + } + + @Test + void assertNegativeOrZeroFieldGood() + { + this.assertGoodChange(o -> o.setNegativeOrZeroField(-1)); + } + + @Test + void assertNegativeOrZeroFieldBad() + { + this.assertConstraintViolationChange(o -> o.setNegativeOrZeroField(1)); + } + + @Test + void assertNotBlankGood() + { + this.assertGoodChange(o -> o.setMessageNotBlank("a")); + } + + @Test + void assertNotBlankBad_Null() + { + this.assertConstraintViolationChange(o -> o.setMessageNotBlank(null)); + } + + @Test + void assertNotBlankBad_Empty() + { + this.assertConstraintViolationChange(o -> o.setMessageNotBlank("")); + } + + @Test + void assertNotBlankBad_Space() + { + this.assertConstraintViolationChange(o -> o.setMessageNotBlank(" ")); + } + + @Test + void assertNotEmptyGood() + { + this.assertGoodChange(o -> o.setMessageNotEmpty("a")); + } + + @Test + void assertNotEmptyBad_Null() + { + this.assertConstraintViolationChange(o -> o.setMessageNotEmpty(null)); + } + + @Test + void assertNotEmptyBad_Empty() + { + this.assertConstraintViolationChange(o -> o.setMessageNotEmpty("")); + } + + @Test + void assertNotEmptyGood_Space() + { + this.assertGoodChange(o -> o.setUsername(" ")); + } + + @Test + void assertNotNullGood() + { + this.assertGoodChange(o -> o.setUsername("a")); + } + + @Test + void assertNotNullBad_Null() + { + this.assertConstraintViolationChange(o -> o.setUsername(null)); + } + + @Test + void assertNotNullGood_Empty() + { + this.assertGoodChange(o -> o.setUsername("")); + } + + @Test + void assertNotNullGood_Space() + { + this.assertGoodChange(o -> o.setUsername(" ")); + } + + @Test + void assertPatternGood() + { + this.assertGoodChange(o -> o.setPhoneNumber("(123)456-7890")); + } + + @Test + void assertPatternBad() + { + this.assertConstraintViolationChange(o -> o.setPhoneNumber("456-7890")); + } + + @Test + void assertPositiveGood() + { + this.assertGoodChange(o -> o.setArea(BigDecimal.ONE)); + } + + @Test + void assertPositiveBad() + { + this.assertConstraintViolationChange(o -> o.setArea(BigDecimal.valueOf(-1))); + } + + @Test + void assertPositiveBad_Zero() + { + this.assertConstraintViolationChange(o -> o.setArea(BigDecimal.ZERO)); + } + + @Test + void assertPositiveOrZeroGood() + { + this.assertGoodChange(o -> o.setPositiveOrZeroField(1)); + } + + @Test + void assertPositiveOrZeroBad() + { + this.assertConstraintViolationChange(o -> o.setPositiveOrZeroField(-1)); + } + + @Test + void assertPositiveOrZeroGood_Zero() + { + this.assertGoodChange(o -> o.setPositiveOrZeroField(0)); + } + + @Test + void assertStringMinAndMaxGood() + { + this.assertGoodChange(o -> o.setMessageMin2AndMax10("123")); + } + + @Test + void assertStringMinAndMaxBad_TooSmall() + { + this.assertConstraintViolationChange(o -> o.setMessageMin2AndMax10("1")); + } + + @Test + void assertStringMinAndMaxBad_TooBig() + { + this.assertConstraintViolationChange(o -> o.setMessageMin2AndMax10("1234567890-")); + } + + /// ////-------------Help Functions -------------////////////// + private void assertGoodChange(final Consumer change) { final ConstraintDaoObject constraintDaoObject = new ConstraintDaoObject(); - constraintDaoObject.setAlwaysFalse(false); + change.accept(constraintDaoObject); + this.assertGoodSave(constraintDaoObject); + } + + private void assertGoodSave(final ConstraintDaoObject constraintDaoObject) + { Assertions.assertDoesNotThrow(() -> this.repository.save(constraintDaoObject)); } + + private void assertConstraintViolationChange(final Consumer change) + { + final ConstraintDaoObject constraintDaoObject = new ConstraintDaoObject(); + change.accept(constraintDaoObject); + this.assertConstraintViolationSave(constraintDaoObject); + } + + private void assertConstraintViolationSave(final ConstraintDaoObject constraintDaoObject) + { + Assertions.assertThrows( + ConstraintViolationException.class, + () -> this.repository.save(constraintDaoObject) + ); + } } From 6499afdf911250a7aa8f0823a499c5edb2bce474 Mon Sep 17 00:00:00 2001 From: JohannesRabauer Date: Fri, 20 Dec 2024 11:03:06 +0100 Subject: [PATCH 2/3] Added documentation for validation-constraints --- CHANGELOG.md | 1 - docs/modules/ROOT/nav.adoc | 1 + .../modules/ROOT/pages/features/features.adoc | 1 + .../features/validation-constraints.adoc | 19 +++++++++++++++++++ 4 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 docs/modules/ROOT/pages/features/validation-constraints.adoc diff --git a/CHANGELOG.md b/CHANGELOG.md index 18e17ae2..480cfdd0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,6 @@ * Auto-Fix problems with adding ids to entities with existing data store. -~~~~ # 2.3.0 * Add support for shutting down the storage during application shutdown diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index ae44098a..c056d12a 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -9,5 +9,6 @@ ** xref:features/transactions.adoc[Transactions] ** xref:features/versions.adoc[Versions] ** xref:features/rest-api.adoc[REST Interface] +** xref:features/validation-constraints.adoc[Validation Constraints] * xref:migration.adoc[Migration from JPA] * xref:known-issues.adoc[Known issues] diff --git a/docs/modules/ROOT/pages/features/features.adoc b/docs/modules/ROOT/pages/features/features.adoc index de38b497..0692dfe8 100644 --- a/docs/modules/ROOT/pages/features/features.adoc +++ b/docs/modules/ROOT/pages/features/features.adoc @@ -6,3 +6,4 @@ * xref:features/transactions.adoc[Transactions] * xref:features/versions.adoc[Versions] * xref:features/rest-api.adoc[REST Interface] +* xref:features/validation-constraints.adoc[Validation Constraints] diff --git a/docs/modules/ROOT/pages/features/validation-constraints.adoc b/docs/modules/ROOT/pages/features/validation-constraints.adoc new file mode 100644 index 00000000..3dabb23c --- /dev/null +++ b/docs/modules/ROOT/pages/features/validation-constraints.adoc @@ -0,0 +1,19 @@ += Validation Constraints + +By using the https://jakarta.ee/learn/docs/jakartaee-tutorial/current/beanvalidation/bean-validation/bean-validation.html[Jakarta Bean Validation Constraints] developers with {product-name} can easily limit the allowed input of entities. +Here is a full list of supported validations: https://jakarta.ee/learn/docs/jakartaee-tutorial/current/beanvalidation/bean-validation/bean-validation.html#_using_jakarta_bean_validation_constraints[https://jakarta.ee/learn] + +[source,java,title="https://github.com/xdev-software/spring-data-eclipse-store/blob/develop/spring-data-eclipse-store-demo/src/main/java/software/xdev/spring/data/eclipse/store/demo/complex/model/Person.java[Example from complex demo]"] +---- +package software.xdev.spring.data.eclipse.store.demo.complex.model; + +import jakarta.validation.constraints.NotBlank; + +public class Person extends BaseEntity +{ + @NotBlank + private String firstName; + //... +---- + +The ``jakarta.validation.Validator`` is provided by the https://github.com/xdev-software/spring-data-eclipse-store/tree/develop/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/config/EclipseStoreClientConfiguration.java[``EclipseStoreClientConfiguration``] and can be changed in the project-specific configuration. From 78a67944f65997039c4334ad6be6894d7e5433a0 Mon Sep 17 00:00:00 2001 From: JohannesRabauer Date: Fri, 20 Dec 2024 11:03:14 +0100 Subject: [PATCH 3/3] Refactoring --- .../data/eclipse/store/demo/complex/model/Person.java | 4 ++++ .../repository/config/EclipseStoreClientConfiguration.java | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/spring-data-eclipse-store-demo/src/main/java/software/xdev/spring/data/eclipse/store/demo/complex/model/Person.java b/spring-data-eclipse-store-demo/src/main/java/software/xdev/spring/data/eclipse/store/demo/complex/model/Person.java index b1eb515e..b0ede23d 100644 --- a/spring-data-eclipse-store-demo/src/main/java/software/xdev/spring/data/eclipse/store/demo/complex/model/Person.java +++ b/spring-data-eclipse-store-demo/src/main/java/software/xdev/spring/data/eclipse/store/demo/complex/model/Person.java @@ -15,8 +15,12 @@ */ package software.xdev.spring.data.eclipse.store.demo.complex.model; +import jakarta.validation.constraints.NotBlank; + + public class Person extends BaseEntity { + @NotBlank private String firstName; private String lastName; diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/config/EclipseStoreClientConfiguration.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/config/EclipseStoreClientConfiguration.java index b75a22b4..c00eeb11 100644 --- a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/config/EclipseStoreClientConfiguration.java +++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/config/EclipseStoreClientConfiguration.java @@ -17,6 +17,7 @@ import jakarta.validation.Validation; import jakarta.validation.Validator; +import jakarta.validation.ValidatorFactory; import org.eclipse.serializer.reflect.ClassLoaderProvider; import org.eclipse.store.integrations.spring.boot.types.configuration.EclipseStoreProperties; @@ -206,6 +207,9 @@ public void shutdownStorageOnContextClosed(final ContextClosedEvent event) @Bean public Validator getValidator() { - return Validation.buildDefaultValidatorFactory().getValidator(); + try(ValidatorFactory factory = Validation.buildDefaultValidatorFactory()) + { + return factory.getValidator(); + } } }