From ff9756670f150cb2ce4803b72896f27407a51e51 Mon Sep 17 00:00:00 2001 From: Andreas Hufler Date: Tue, 27 Jan 2026 10:03:47 +0100 Subject: [PATCH 1/7] update versions --- pom.xml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 98e4f40..8f18ba7 100644 --- a/pom.xml +++ b/pom.xml @@ -1,11 +1,12 @@ - + 4.0.0 org.springframework.boot spring-boot-starter-parent - 4.0.1 + 4.0.2 @@ -25,7 +26,7 @@ it.aboutbits spring-boot-toolbox - 2.1.0-RC1 + 2.1.0 it.aboutbits From 48905774ecdc43bc27844cf409c497937abe8d46 Mon Sep 17 00:00:00 2001 From: Andreas Hufler Date: Tue, 27 Jan 2026 10:04:02 +0100 Subject: [PATCH 2/7] add validation tools for new toolbox --- .../validation/core/BaseRuleBuilder.java | 12 ++- .../validation/rule/RepeatedFieldRule.java | 46 +++++++++++ .../validation/rule/ValidDateRangeRule.java | 82 +++++++++++++++++++ .../rule/ValidNumericRangeRule.java | 82 +++++++++++++++++++ .../validation/rule/ValidPasswordRule.java | 22 +++++ .../source/LongerThanValueSource.java | 61 ++++++++++++++ .../source/ShorterThanValueSource.java | 64 +++++++++++++++ .../validation/ValidationAssertTest.java | 17 ++++ 8 files changed, 384 insertions(+), 2 deletions(-) create mode 100644 src/main/java/it/aboutbits/springboot/testing/validation/rule/RepeatedFieldRule.java create mode 100644 src/main/java/it/aboutbits/springboot/testing/validation/rule/ValidDateRangeRule.java create mode 100644 src/main/java/it/aboutbits/springboot/testing/validation/rule/ValidNumericRangeRule.java create mode 100644 src/main/java/it/aboutbits/springboot/testing/validation/rule/ValidPasswordRule.java create mode 100644 src/main/java/it/aboutbits/springboot/testing/validation/source/LongerThanValueSource.java create mode 100644 src/main/java/it/aboutbits/springboot/testing/validation/source/ShorterThanValueSource.java diff --git a/src/main/java/it/aboutbits/springboot/testing/validation/core/BaseRuleBuilder.java b/src/main/java/it/aboutbits/springboot/testing/validation/core/BaseRuleBuilder.java index adc4fc4..14e809f 100644 --- a/src/main/java/it/aboutbits/springboot/testing/validation/core/BaseRuleBuilder.java +++ b/src/main/java/it/aboutbits/springboot/testing/validation/core/BaseRuleBuilder.java @@ -14,8 +14,12 @@ import it.aboutbits.springboot.testing.validation.rule.PastRule; import it.aboutbits.springboot.testing.validation.rule.PositiveOrZeroRule; import it.aboutbits.springboot.testing.validation.rule.PositiveRule; +import it.aboutbits.springboot.testing.validation.rule.RepeatedFieldRule; import it.aboutbits.springboot.testing.validation.rule.SizeRule; import it.aboutbits.springboot.testing.validation.rule.ValidBeanRule; +import it.aboutbits.springboot.testing.validation.rule.ValidDateRangeRule; +import it.aboutbits.springboot.testing.validation.rule.ValidNumericRangeRule; +import it.aboutbits.springboot.testing.validation.rule.ValidPasswordRule; import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; @@ -39,13 +43,17 @@ public abstract class BaseRuleBuilder> implements NotBlankRule, NotEmptyRule, NotNullRule, + NotValidatedRule, NullableRule, PastRule, PositiveOrZeroRule, PositiveRule, - NotValidatedRule, + RepeatedFieldRule, SizeRule, - ValidBeanRule { + ValidBeanRule, + ValidDateRangeRule, + ValidNumericRangeRule, + ValidPasswordRule { @Getter(AccessLevel.PACKAGE) private final List rules = new ArrayList<>(); diff --git a/src/main/java/it/aboutbits/springboot/testing/validation/rule/RepeatedFieldRule.java b/src/main/java/it/aboutbits/springboot/testing/validation/rule/RepeatedFieldRule.java new file mode 100644 index 0000000..a2a069b --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/testing/validation/rule/RepeatedFieldRule.java @@ -0,0 +1,46 @@ +package it.aboutbits.springboot.testing.validation.rule; + +import it.aboutbits.springboot.testing.validation.core.BaseRuleBuilder; +import it.aboutbits.springboot.testing.validation.core.CustomValidationFunction; +import it.aboutbits.springboot.testing.validation.core.ValidationRulesData; +import it.aboutbits.springboot.toolbox.validation.annotation.RepeatedField; +import org.jspecify.annotations.NullMarked; + +import java.util.Arrays; + +@SuppressWarnings("unchecked") +@NullMarked +public interface RepeatedFieldRule> extends ValidationRulesData { + default V repeatedField(String originalField, String repeatedField) { + addValidationFunction( + (Object o) -> { + var isValid = false; + var currentClass = o.getClass(); + + while (currentClass != null) { + var annotations = currentClass.getDeclaredAnnotationsByType(RepeatedField.class); + + isValid = Arrays.stream(annotations).anyMatch( + a -> a.originalField().equals(originalField) && a.repeatedField() + .equals(repeatedField) + ); + + if (isValid) { + break; + } + currentClass = currentClass.getSuperclass(); + } + + return new CustomValidationFunction.Result( + isValid, + "@RepeatedField(originalField = \"%s\", repeatedField = \"%s\") is missing.".formatted( + originalField, + repeatedField + ) + ); + } + ); + + return (V) this; + } +} diff --git a/src/main/java/it/aboutbits/springboot/testing/validation/rule/ValidDateRangeRule.java b/src/main/java/it/aboutbits/springboot/testing/validation/rule/ValidDateRangeRule.java new file mode 100644 index 0000000..b26baf1 --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/testing/validation/rule/ValidDateRangeRule.java @@ -0,0 +1,82 @@ +package it.aboutbits.springboot.testing.validation.rule; + +import it.aboutbits.springboot.testing.validation.core.BaseRuleBuilder; +import it.aboutbits.springboot.testing.validation.core.CustomValidationFunction; +import it.aboutbits.springboot.testing.validation.core.ValidationRulesData; +import it.aboutbits.springboot.toolbox.validation.annotation.ValidDateRange; +import org.jspecify.annotations.NullMarked; + +import java.util.Arrays; + +@SuppressWarnings("unchecked") +@NullMarked +public interface ValidDateRangeRule> extends ValidationRulesData { + default V validDateRange(String fromDateField, String toDateField) { + addValidationFunction( + (Object o) -> { + var isValid = false; + var currentClass = o.getClass(); + + while (currentClass != null) { + var annotations = currentClass.getDeclaredAnnotationsByType(ValidDateRange.class); + + isValid = Arrays.stream(annotations).anyMatch( + a -> a.fromDateField().equals(fromDateField) && a.toDateField() + .equals(toDateField) + ); + + if (isValid) { + break; + } + currentClass = currentClass.getSuperclass(); + } + + return new CustomValidationFunction.Result( + isValid, + "@ValidDateRange(fromDateField = \"%s\", toDateField = \"%s\") is missing.".formatted( + fromDateField, + toDateField + ) + ); + } + ); + + return (V) this; + } + + default V validDateRange( + String fromDateField, + String toDateField, + boolean allowEmptyRange + ) { + addValidationFunction( + (Object o) -> { + var isValid = false; + var currentClass = o.getClass(); + + while (currentClass != null) { + var annotations = currentClass.getDeclaredAnnotationsByType(ValidDateRange.class); + + isValid = Arrays.stream(annotations).anyMatch( + a -> a.fromDateField().equals(fromDateField) + && a.toDateField().equals(toDateField) + && a.allowEmptyRange() == allowEmptyRange + ); + + if (isValid) { + break; + } + currentClass = currentClass.getSuperclass(); + } + + return new CustomValidationFunction.Result( + isValid, + "@ValidDateRange(fromDateField = \"%s\", toDateField = \"%s\", allowEmptyRange = %s) is missing." + .formatted(fromDateField, toDateField, allowEmptyRange) + ); + } + ); + + return (V) this; + } +} diff --git a/src/main/java/it/aboutbits/springboot/testing/validation/rule/ValidNumericRangeRule.java b/src/main/java/it/aboutbits/springboot/testing/validation/rule/ValidNumericRangeRule.java new file mode 100644 index 0000000..9ed5a52 --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/testing/validation/rule/ValidNumericRangeRule.java @@ -0,0 +1,82 @@ +package it.aboutbits.springboot.testing.validation.rule; + +import it.aboutbits.springboot.testing.validation.core.BaseRuleBuilder; +import it.aboutbits.springboot.testing.validation.core.CustomValidationFunction; +import it.aboutbits.springboot.testing.validation.core.ValidationRulesData; +import it.aboutbits.springboot.toolbox.validation.annotation.ValidNumericRange; +import org.jspecify.annotations.NullMarked; + +import java.util.Arrays; + +@SuppressWarnings("unchecked") +@NullMarked +public interface ValidNumericRangeRule> extends ValidationRulesData { + default V validNumericRange(String lowerBoundField, String upperBoundField) { + addValidationFunction( + (Object o) -> { + var isValid = false; + var currentClass = o.getClass(); + + while (currentClass != null) { + var annotations = currentClass.getDeclaredAnnotationsByType(ValidNumericRange.class); + + isValid = Arrays.stream(annotations).anyMatch( + a -> a.lowerBoundField().equals(lowerBoundField) && a.upperBoundField() + .equals(upperBoundField) + ); + + if (isValid) { + break; + } + currentClass = currentClass.getSuperclass(); + } + + return new CustomValidationFunction.Result( + isValid, + "@ValidNumericRange(lowerBoundField = \"%s\", upperBoundField = \"%s\") is missing.".formatted( + lowerBoundField, + upperBoundField + ) + ); + } + ); + + return (V) this; + } + + default V validNumericRange( + String lowerBoundField, + String upperBoundField, + boolean allowEqualValues + ) { + addValidationFunction( + (Object o) -> { + var isValid = false; + var currentClass = o.getClass(); + + while (currentClass != null) { + var annotations = currentClass.getDeclaredAnnotationsByType(ValidNumericRange.class); + + isValid = Arrays.stream(annotations).anyMatch( + a -> a.lowerBoundField().equals(lowerBoundField) + && a.upperBoundField().equals(upperBoundField) + && a.allowEqualValues() == allowEqualValues + ); + + if (isValid) { + break; + } + currentClass = currentClass.getSuperclass(); + } + + return new CustomValidationFunction.Result( + isValid, + "@ValidNumericRange(lowerBoundField = \"%s\", upperBoundField = \"%s\", allowEqualValues = %s) is missing." + .formatted(lowerBoundField, upperBoundField, allowEqualValues) + ); + } + ); + + return (V) this; + } +} diff --git a/src/main/java/it/aboutbits/springboot/testing/validation/rule/ValidPasswordRule.java b/src/main/java/it/aboutbits/springboot/testing/validation/rule/ValidPasswordRule.java new file mode 100644 index 0000000..aad78c7 --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/testing/validation/rule/ValidPasswordRule.java @@ -0,0 +1,22 @@ +package it.aboutbits.springboot.testing.validation.rule; + +import it.aboutbits.springboot.testing.validation.core.BaseRuleBuilder; +import it.aboutbits.springboot.testing.validation.core.Rule; +import it.aboutbits.springboot.testing.validation.core.ValidationRulesData; +import it.aboutbits.springboot.testing.validation.source.LongerThanValueSource; +import it.aboutbits.springboot.testing.validation.source.ShorterThanValueSource; +import org.jspecify.annotations.NullMarked; + +@SuppressWarnings("unchecked") +@NullMarked +public interface ValidPasswordRule> extends ValidationRulesData { + default V validPassword(String property, long minLength, long maxLength) { + addRule( + new Rule(property, LongerThanValueSource.class, maxLength) + ); + addRule( + new Rule(property, ShorterThanValueSource.class, minLength) + ); + return (V) this; + } +} diff --git a/src/main/java/it/aboutbits/springboot/testing/validation/source/LongerThanValueSource.java b/src/main/java/it/aboutbits/springboot/testing/validation/source/LongerThanValueSource.java new file mode 100644 index 0000000..d4239dc --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/testing/validation/source/LongerThanValueSource.java @@ -0,0 +1,61 @@ +package it.aboutbits.springboot.testing.validation.source; + +import it.aboutbits.springboot.testing.validation.core.ValueSource; +import org.jspecify.annotations.NullMarked; + +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import java.util.function.Function; +import java.util.stream.Stream; + +@NullMarked +public class LongerThanValueSource implements ValueSource { + private static final Map, Function>> TYPE_SOURCES = new HashMap<>(); + private static final Random RANDOM = new Random(); + + static { + TYPE_SOURCES.put(String.class, LongerThanValueSource::getStream); + } + + @SuppressWarnings("unused") + public static void registerType(Class type, Function> source) { + TYPE_SOURCES.put(type, source); + } + + @Override + @SuppressWarnings("unchecked") + public Stream values(Class propertyClass, Object... args) { + var sourceFunction = TYPE_SOURCES.get(propertyClass); + if (sourceFunction != null) { + return (Stream) sourceFunction.apply(args); + } + + throw new IllegalArgumentException("Property class not supported!"); + } + + private static Stream getStream(Object[] args) { + var length = Long.valueOf((long) args[0]).intValue(); + var minLength = length + 1; + + var maxLength = length + 1024; + + if (minLength == maxLength) { + return Stream.of(generateRandomString(minLength)); + } + + var randomLength = RANDOM.nextInt(minLength, maxLength); + + return Stream.of( + generateRandomString(minLength), + generateRandomString(maxLength), + generateRandomString(randomLength) + ).distinct(); + } + + private static String generateRandomString(int length) { + return RANDOM.ints(length, 32, 127) + .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append) + .toString(); + } +} diff --git a/src/main/java/it/aboutbits/springboot/testing/validation/source/ShorterThanValueSource.java b/src/main/java/it/aboutbits/springboot/testing/validation/source/ShorterThanValueSource.java new file mode 100644 index 0000000..2ef0a08 --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/testing/validation/source/ShorterThanValueSource.java @@ -0,0 +1,64 @@ +package it.aboutbits.springboot.testing.validation.source; + +import it.aboutbits.springboot.testing.validation.core.ValueSource; +import org.jspecify.annotations.NullMarked; + +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import java.util.function.Function; +import java.util.stream.Stream; + +@NullMarked +public class ShorterThanValueSource implements ValueSource { + private static final Map, Function>> TYPE_SOURCES = new HashMap<>(); + private static final Random RANDOM = new Random(); + + static { + TYPE_SOURCES.put(String.class, ShorterThanValueSource::getStream); + } + + @SuppressWarnings("unused") + public static void registerType(Class type, Function> source) { + TYPE_SOURCES.put(type, source); + } + + @Override + @SuppressWarnings("unchecked") + public Stream values(Class propertyClass, Object... args) { + var sourceFunction = TYPE_SOURCES.get(propertyClass); + if (sourceFunction != null) { + return (Stream) sourceFunction.apply(args); + } + + throw new IllegalArgumentException("Property class not supported!"); + } + + private static Stream getStream(Object[] args) { + var length = Long.valueOf((long) args[0]).intValue(); + if (length <= 0) { + return Stream.empty(); + } + + var minLength = 0; + var maxLength = length - 1; + + if (minLength == maxLength) { + return Stream.of(""); + } + + var randomLength = RANDOM.nextInt(minLength, maxLength); + + return Stream.of( + generateRandomString(minLength), + generateRandomString(maxLength), + generateRandomString(randomLength) + ).distinct(); + } + + private static String generateRandomString(int length) { + return RANDOM.ints(length, 32, 127) + .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append) + .toString(); + } +} diff --git a/src/test/java/it/aboutbits/springboot/testing/validation/ValidationAssertTest.java b/src/test/java/it/aboutbits/springboot/testing/validation/ValidationAssertTest.java index db4a08d..2883494 100644 --- a/src/test/java/it/aboutbits/springboot/testing/validation/ValidationAssertTest.java +++ b/src/test/java/it/aboutbits/springboot/testing/validation/ValidationAssertTest.java @@ -4,6 +4,7 @@ import it.aboutbits.springboot.testing.validation.core.BaseRuleBuilder; import it.aboutbits.springboot.testing.validation.core.BaseValidationAssert; import it.aboutbits.springboot.toolbox.type.ScaledBigDecimal; +import it.aboutbits.springboot.toolbox.validation.annotation.ValidPassword; import jakarta.validation.Valid; import jakarta.validation.constraints.Future; import jakarta.validation.constraints.Max; @@ -183,6 +184,10 @@ public record SomeValidParameter( // Nullable @Nullable Object nullable, + // + @ValidPassword + String password, + // Not validated Object notValidated ) { @@ -325,6 +330,9 @@ void testWithBeanValidation() { // Nullable .nullable("nullable") + // ValidPassword + .validPassword("password", 8, 50) + // Not validated .notValidated("notValidated") .isCompliant(); @@ -465,6 +473,9 @@ void invalidParameter_shouldFail() { // Nullable .nullable("nullable") + // ValidPassword + .validPassword("password", 8, 50) + // Not validated .notValidated("notValidated") .isCompliant()); @@ -606,6 +617,9 @@ void propertyMissingRule_shouldFail() { // Nullable .nullable("nullable") + // ValidPassword + .validPassword("password", 8, 50) + // Not validated .notValidated("notValidated") .isCompliant()); @@ -905,6 +919,9 @@ private static SomeValidParameter getSomeValidParameter() { // Nullable null, + // ValidPassword + "password123", + // Not validated null ); From 2116a2e432234fada9d41bb09ecbf3f92c0cef0b Mon Sep 17 00:00:00 2001 From: Andreas Hufler Date: Thu, 2 Apr 2026 09:03:14 +0200 Subject: [PATCH 3/7] remove redundant faker instance already present in superclass. prevent duplicate save call when no result mutator is set. --- .../base/ModifiableTestDataCreator.java | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/main/java/it/aboutbits/springboot/testing/testdata/base/ModifiableTestDataCreator.java b/src/main/java/it/aboutbits/springboot/testing/testdata/base/ModifiableTestDataCreator.java index 208ec51..732004b 100644 --- a/src/main/java/it/aboutbits/springboot/testing/testdata/base/ModifiableTestDataCreator.java +++ b/src/main/java/it/aboutbits/springboot/testing/testdata/base/ModifiableTestDataCreator.java @@ -2,6 +2,7 @@ import lombok.extern.slf4j.Slf4j; import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.util.ArrayList; import java.util.List; @@ -14,9 +15,6 @@ @Slf4j @NullMarked public abstract class ModifiableTestDataCreator, ITEM, PARAMETER> extends TestDataCreator { - @SuppressWarnings("unused") - protected static final FakerExtended FAKER = new FakerExtended(); - private boolean mutatorSet = false; private boolean mutatorCalled = false; @@ -25,8 +23,8 @@ public abstract class ModifiableTestDataCreator resultMutator = (_, _) -> { - }; + @Nullable + protected ObjIntConsumer resultMutator = null; protected ModifiableTestDataCreator(int count) { super(count); @@ -71,11 +69,15 @@ protected List create() { for (var index = 0; index < numberOfItems; index++) { var item = create(index); - resultMutator.accept(item, index); + if (resultMutator != null) { + resultMutator.accept(item, index); - result.add( - saveMutation(item) - ); + result.add( + saveMutation(item) + ); + } else { + result.add(item); + } } if (mutatorSet && !mutatorCalled) { From 09ea93b8fb790234b1b25c21225a5a59659e6a9a Mon Sep 17 00:00:00 2001 From: Andreas Hufler Date: Thu, 2 Apr 2026 09:03:28 +0200 Subject: [PATCH 4/7] move faker --- .../testing/testdata/FakerExtended.java | 113 ++++++++++++++++++ .../testing/testdata/base/FakerExtended.java | 4 + .../testdata/base/TestDataCreator.java | 1 + 3 files changed, 118 insertions(+) create mode 100644 src/main/java/it/aboutbits/springboot/testing/testdata/FakerExtended.java diff --git a/src/main/java/it/aboutbits/springboot/testing/testdata/FakerExtended.java b/src/main/java/it/aboutbits/springboot/testing/testdata/FakerExtended.java new file mode 100644 index 0000000..a6d3018 --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/testing/testdata/FakerExtended.java @@ -0,0 +1,113 @@ +package it.aboutbits.springboot.testing.testdata; + +import it.aboutbits.springboot.toolbox.type.ScaledBigDecimal; +import it.aboutbits.springboot.toolbox.type.identity.EntityId; +import net.datafaker.Faker; +import net.datafaker.service.FakeValuesService; +import net.datafaker.service.FakerContext; +import net.datafaker.service.RandomService; +import org.jspecify.annotations.NullMarked; + +import java.util.Locale; +import java.util.Random; +import java.util.function.Function; +import java.util.function.LongFunction; + +@NullMarked +public class FakerExtended extends Faker { + public FakerExtended() { + super(); + } + + public FakerExtended(Locale locale) { + super(locale); + } + + public FakerExtended(Random random) { + super(random); + } + + public FakerExtended(Locale locale, Random random) { + super(locale, random); + } + + public FakerExtended(Locale locale, RandomService randomService) { + super(locale, randomService); + } + + public FakerExtended(FakeValuesService fakeValuesService, FakerContext context) { + super(fakeValuesService, context); + } + + public > T randomEnumValue(Class enumClass) { + var values = enumClass.getEnumConstants(); + if (values.length == 0) { + throw new IllegalArgumentException("Enum class must have at least one value"); + } + return values[this.random().nextInt(values.length)]; + } + + public > T randomEntityId(LongFunction constructor) { + return constructor.apply( + super.random().nextInt(9999999) + ); + } + + public > T randomEntityId(Function constructor) { + return constructor.apply( + super.internet().uuid() + ); + } + + public String unique(String value) { + return value + "_" + super.random().nextInt(9999999); + } + + public RandomNumericRange numericRange() { + return new RandomNumericRange( + super.getFaker() + ); + } + + public static final class RandomNumericRange { + private final Faker parent; + + private RandomNumericRange(Faker parent) { + this.parent = parent; + } + + public NumericRange random() { + return random(-999999999, 999999999); + } + + public NumericRange positive() { + return random(1, 999999999); + } + + public NumericRange positiveOrZero() { + return random(0, 999999999); + } + + public NumericRange negative() { + return random(-999999999, -1); + } + + public NumericRange negativeOrZero() { + return random(-999999999, 0); + } + + public NumericRange random(double min, double max) { + var lower = parent.random().nextDouble(min, max - 1); + var upper = parent.random().nextDouble(lower, max); + + return new NumericRange( + ScaledBigDecimal.valueOf(lower), + ScaledBigDecimal.valueOf(upper) + ); + } + + public record NumericRange(ScaledBigDecimal lower, ScaledBigDecimal upper) { + + } + } +} diff --git a/src/main/java/it/aboutbits/springboot/testing/testdata/base/FakerExtended.java b/src/main/java/it/aboutbits/springboot/testing/testdata/base/FakerExtended.java index 3fbfb3c..d5a4315 100644 --- a/src/main/java/it/aboutbits/springboot/testing/testdata/base/FakerExtended.java +++ b/src/main/java/it/aboutbits/springboot/testing/testdata/base/FakerExtended.java @@ -13,6 +13,10 @@ import java.util.function.Function; import java.util.function.LongFunction; +/** + * @deprecated Moved. Use {@link it.aboutbits.springboot.testing.testdata.FakerExtended} instead. This class will be removed in a future release. + */ +@Deprecated(forRemoval = true) @NullMarked public class FakerExtended extends Faker { public FakerExtended() { diff --git a/src/main/java/it/aboutbits/springboot/testing/testdata/base/TestDataCreator.java b/src/main/java/it/aboutbits/springboot/testing/testdata/base/TestDataCreator.java index c5a810a..96287dd 100644 --- a/src/main/java/it/aboutbits/springboot/testing/testdata/base/TestDataCreator.java +++ b/src/main/java/it/aboutbits/springboot/testing/testdata/base/TestDataCreator.java @@ -1,5 +1,6 @@ package it.aboutbits.springboot.testing.testdata.base; +import it.aboutbits.springboot.testing.testdata.FakerExtended; import org.jspecify.annotations.NullMarked; import java.util.ArrayList; From cd4ac55f71f8a768c5430fdc4eadbb66e3ead456 Mon Sep 17 00:00:00 2001 From: Andreas Hufler Date: Thu, 2 Apr 2026 09:03:36 +0200 Subject: [PATCH 5/7] fix nullability --- .../springboot/testing/validation/core/ValueSource.java | 3 ++- .../testing/validation/source/BiggerThanValueSource.java | 5 +++-- .../testing/validation/source/BlankValueSource.java | 5 +++-- .../testing/validation/source/EmptyValueSource.java | 5 +++-- .../testing/validation/source/FutureValueSource.java | 5 +++-- .../testing/validation/source/InertValueSource.java | 3 ++- .../testing/validation/source/LessThanValueSource.java | 5 +++-- .../testing/validation/source/LongerThanValueSource.java | 5 +++-- .../testing/validation/source/NullValueSource.java | 5 +++-- .../testing/validation/source/PastValueSource.java | 5 +++-- .../testing/validation/source/ShorterThanValueSource.java | 5 +++-- .../validation/source/SizeGreaterThanValueSource.java | 7 ++++--- .../testing/validation/source/SizeLessThanValueSource.java | 7 ++++--- .../testing/validation/source/ZeroValueSource.java | 5 +++-- 14 files changed, 42 insertions(+), 28 deletions(-) diff --git a/src/main/java/it/aboutbits/springboot/testing/validation/core/ValueSource.java b/src/main/java/it/aboutbits/springboot/testing/validation/core/ValueSource.java index e9e7794..73fbf59 100644 --- a/src/main/java/it/aboutbits/springboot/testing/validation/core/ValueSource.java +++ b/src/main/java/it/aboutbits/springboot/testing/validation/core/ValueSource.java @@ -1,10 +1,11 @@ package it.aboutbits.springboot.testing.validation.core; import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.util.stream.Stream; @NullMarked public interface ValueSource { - Stream values(Class propertyClass, Object... args); + Stream<@Nullable T> values(Class propertyClass, Object... args); } diff --git a/src/main/java/it/aboutbits/springboot/testing/validation/source/BiggerThanValueSource.java b/src/main/java/it/aboutbits/springboot/testing/validation/source/BiggerThanValueSource.java index c025aba..a52a70c 100644 --- a/src/main/java/it/aboutbits/springboot/testing/validation/source/BiggerThanValueSource.java +++ b/src/main/java/it/aboutbits/springboot/testing/validation/source/BiggerThanValueSource.java @@ -3,6 +3,7 @@ import it.aboutbits.springboot.testing.validation.core.ValueSource; import it.aboutbits.springboot.toolbox.type.ScaledBigDecimal; import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.math.BigDecimal; import java.math.BigInteger; @@ -48,10 +49,10 @@ public static void registerType(Class type, Function> sou @Override @SuppressWarnings("unchecked") - public Stream values(Class propertyClass, Object... args) { + public Stream<@Nullable T> values(Class propertyClass, Object... args) { var sourceFunction = TYPE_SOURCES.get(propertyClass); if (sourceFunction != null) { - return (Stream) sourceFunction.apply(args); + return (Stream<@Nullable T>) sourceFunction.apply(args); } throw new IllegalArgumentException("Property class not supported!"); diff --git a/src/main/java/it/aboutbits/springboot/testing/validation/source/BlankValueSource.java b/src/main/java/it/aboutbits/springboot/testing/validation/source/BlankValueSource.java index 8e3b96d..df91057 100644 --- a/src/main/java/it/aboutbits/springboot/testing/validation/source/BlankValueSource.java +++ b/src/main/java/it/aboutbits/springboot/testing/validation/source/BlankValueSource.java @@ -2,6 +2,7 @@ import it.aboutbits.springboot.testing.validation.core.ValueSource; import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.util.HashMap; import java.util.Map; @@ -26,10 +27,10 @@ public static void registerType(Class type, Function> sou @Override @SuppressWarnings("unchecked") - public Stream values(Class propertyClass, Object... args) { + public Stream<@Nullable T> values(Class propertyClass, Object... args) { var sourceFunction = TYPE_SOURCES.get(propertyClass); if (sourceFunction != null) { - return (Stream) sourceFunction.apply(args); + return (Stream<@Nullable T>) sourceFunction.apply(args); } throw new IllegalArgumentException("Property class not supported!"); diff --git a/src/main/java/it/aboutbits/springboot/testing/validation/source/EmptyValueSource.java b/src/main/java/it/aboutbits/springboot/testing/validation/source/EmptyValueSource.java index 1567f0c..72d6618 100644 --- a/src/main/java/it/aboutbits/springboot/testing/validation/source/EmptyValueSource.java +++ b/src/main/java/it/aboutbits/springboot/testing/validation/source/EmptyValueSource.java @@ -2,6 +2,7 @@ import it.aboutbits.springboot.testing.validation.core.ValueSource; import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.util.ArrayList; import java.util.HashMap; @@ -38,10 +39,10 @@ public static void registerType(Class type, Function> sou @Override @SuppressWarnings("unchecked") - public Stream values(Class propertyClass, Object... args) { + public Stream<@Nullable T> values(Class propertyClass, Object... args) { var sourceFunction = TYPE_SOURCES.get(propertyClass); if (sourceFunction != null) { - return (Stream) sourceFunction.apply(args); + return (Stream<@Nullable T>) sourceFunction.apply(args); } throw new IllegalArgumentException("Property class not supported!"); diff --git a/src/main/java/it/aboutbits/springboot/testing/validation/source/FutureValueSource.java b/src/main/java/it/aboutbits/springboot/testing/validation/source/FutureValueSource.java index 82d5a72..9c1d716 100644 --- a/src/main/java/it/aboutbits/springboot/testing/validation/source/FutureValueSource.java +++ b/src/main/java/it/aboutbits/springboot/testing/validation/source/FutureValueSource.java @@ -2,6 +2,7 @@ import it.aboutbits.springboot.testing.validation.core.ValueSource; import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.time.Instant; import java.time.LocalDate; @@ -43,10 +44,10 @@ public static void registerType(Class type, Function> sou @Override @SuppressWarnings("unchecked") - public Stream values(Class propertyClass, Object... args) { + public Stream<@Nullable T> values(Class propertyClass, Object... args) { var sourceFunction = TYPE_SOURCES.get(propertyClass); if (sourceFunction != null) { - return (Stream) sourceFunction.apply(args); + return (Stream<@Nullable T>) sourceFunction.apply(args); } throw new IllegalArgumentException("Property class not supported!"); diff --git a/src/main/java/it/aboutbits/springboot/testing/validation/source/InertValueSource.java b/src/main/java/it/aboutbits/springboot/testing/validation/source/InertValueSource.java index f833467..bbbae90 100644 --- a/src/main/java/it/aboutbits/springboot/testing/validation/source/InertValueSource.java +++ b/src/main/java/it/aboutbits/springboot/testing/validation/source/InertValueSource.java @@ -2,13 +2,14 @@ import it.aboutbits.springboot.testing.validation.core.ValueSource; import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.util.stream.Stream; @NullMarked public class InertValueSource implements ValueSource { @Override - public Stream values(Class propertyClass, Object... args) { + public Stream<@Nullable T> values(Class propertyClass, Object... args) { return Stream.empty(); } } diff --git a/src/main/java/it/aboutbits/springboot/testing/validation/source/LessThanValueSource.java b/src/main/java/it/aboutbits/springboot/testing/validation/source/LessThanValueSource.java index 798f542..1ba951e 100644 --- a/src/main/java/it/aboutbits/springboot/testing/validation/source/LessThanValueSource.java +++ b/src/main/java/it/aboutbits/springboot/testing/validation/source/LessThanValueSource.java @@ -3,6 +3,7 @@ import it.aboutbits.springboot.testing.validation.core.ValueSource; import it.aboutbits.springboot.toolbox.type.ScaledBigDecimal; import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.math.BigDecimal; import java.math.BigInteger; @@ -49,10 +50,10 @@ public static void registerType(Class type, Function> sou @Override @SuppressWarnings("unchecked") - public Stream values(Class propertyClass, Object... args) { + public Stream<@Nullable T> values(Class propertyClass, Object... args) { var sourceFunction = TYPE_SOURCES.get(propertyClass); if (sourceFunction != null) { - return (Stream) sourceFunction.apply(args); + return (Stream<@Nullable T>) sourceFunction.apply(args); } throw new IllegalArgumentException("Property class not supported!"); diff --git a/src/main/java/it/aboutbits/springboot/testing/validation/source/LongerThanValueSource.java b/src/main/java/it/aboutbits/springboot/testing/validation/source/LongerThanValueSource.java index d4239dc..77ff0fa 100644 --- a/src/main/java/it/aboutbits/springboot/testing/validation/source/LongerThanValueSource.java +++ b/src/main/java/it/aboutbits/springboot/testing/validation/source/LongerThanValueSource.java @@ -2,6 +2,7 @@ import it.aboutbits.springboot.testing.validation.core.ValueSource; import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.util.HashMap; import java.util.Map; @@ -25,10 +26,10 @@ public static void registerType(Class type, Function> sou @Override @SuppressWarnings("unchecked") - public Stream values(Class propertyClass, Object... args) { + public Stream<@Nullable T> values(Class propertyClass, Object... args) { var sourceFunction = TYPE_SOURCES.get(propertyClass); if (sourceFunction != null) { - return (Stream) sourceFunction.apply(args); + return (Stream<@Nullable T>) sourceFunction.apply(args); } throw new IllegalArgumentException("Property class not supported!"); diff --git a/src/main/java/it/aboutbits/springboot/testing/validation/source/NullValueSource.java b/src/main/java/it/aboutbits/springboot/testing/validation/source/NullValueSource.java index 25cb35a..9a5fadc 100644 --- a/src/main/java/it/aboutbits/springboot/testing/validation/source/NullValueSource.java +++ b/src/main/java/it/aboutbits/springboot/testing/validation/source/NullValueSource.java @@ -2,6 +2,7 @@ import it.aboutbits.springboot.testing.validation.core.ValueSource; import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.util.HashMap; import java.util.Map; @@ -19,10 +20,10 @@ public static void registerType(Class type, Function> sou @Override @SuppressWarnings("unchecked") - public Stream values(Class propertyClass, Object... args) { + public Stream<@Nullable T> values(Class propertyClass, Object... args) { var sourceFunction = TYPE_SOURCES.get(propertyClass); if (sourceFunction != null) { - return (Stream) sourceFunction.apply(args); + return (Stream<@Nullable T>) sourceFunction.apply(args); } return Stream.of((T) null); diff --git a/src/main/java/it/aboutbits/springboot/testing/validation/source/PastValueSource.java b/src/main/java/it/aboutbits/springboot/testing/validation/source/PastValueSource.java index 2642f83..9893488 100644 --- a/src/main/java/it/aboutbits/springboot/testing/validation/source/PastValueSource.java +++ b/src/main/java/it/aboutbits/springboot/testing/validation/source/PastValueSource.java @@ -2,6 +2,7 @@ import it.aboutbits.springboot.testing.validation.core.ValueSource; import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.time.Instant; import java.time.LocalDate; @@ -43,10 +44,10 @@ public static void registerType(Class type, Function> sou @Override @SuppressWarnings("unchecked") - public Stream values(Class propertyClass, Object... args) { + public Stream<@Nullable T> values(Class propertyClass, Object... args) { var sourceFunction = TYPE_SOURCES.get(propertyClass); if (sourceFunction != null) { - return (Stream) sourceFunction.apply(args); + return (Stream<@Nullable T>) sourceFunction.apply(args); } throw new IllegalArgumentException("Property class not supported!"); diff --git a/src/main/java/it/aboutbits/springboot/testing/validation/source/ShorterThanValueSource.java b/src/main/java/it/aboutbits/springboot/testing/validation/source/ShorterThanValueSource.java index 2ef0a08..ff56179 100644 --- a/src/main/java/it/aboutbits/springboot/testing/validation/source/ShorterThanValueSource.java +++ b/src/main/java/it/aboutbits/springboot/testing/validation/source/ShorterThanValueSource.java @@ -2,6 +2,7 @@ import it.aboutbits.springboot.testing.validation.core.ValueSource; import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.util.HashMap; import java.util.Map; @@ -25,10 +26,10 @@ public static void registerType(Class type, Function> sou @Override @SuppressWarnings("unchecked") - public Stream values(Class propertyClass, Object... args) { + public Stream<@Nullable T> values(Class propertyClass, Object... args) { var sourceFunction = TYPE_SOURCES.get(propertyClass); if (sourceFunction != null) { - return (Stream) sourceFunction.apply(args); + return (Stream<@Nullable T>) sourceFunction.apply(args); } throw new IllegalArgumentException("Property class not supported!"); diff --git a/src/main/java/it/aboutbits/springboot/testing/validation/source/SizeGreaterThanValueSource.java b/src/main/java/it/aboutbits/springboot/testing/validation/source/SizeGreaterThanValueSource.java index 001a4ae..90cc4d6 100644 --- a/src/main/java/it/aboutbits/springboot/testing/validation/source/SizeGreaterThanValueSource.java +++ b/src/main/java/it/aboutbits/springboot/testing/validation/source/SizeGreaterThanValueSource.java @@ -2,6 +2,7 @@ import it.aboutbits.springboot.testing.validation.core.ValueSource; import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.lang.reflect.Array; import java.util.ArrayList; @@ -53,16 +54,16 @@ public static void registerType(Class type, LongFunction> source) { @Override @SuppressWarnings("unchecked") - public Stream values(Class propertyClass, Object... args) { + public Stream<@Nullable T> values(Class propertyClass, Object... args) { var value = (Long.valueOf((long) args[0])); if (propertyClass.isArray()) { - return (Stream) arrayFunction(propertyClass, value); + return (Stream<@Nullable T>) arrayFunction(propertyClass, value); } var sourceFunction = TYPE_SOURCES.get(propertyClass); if (sourceFunction != null) { - return (Stream) sourceFunction.apply(value); + return (Stream<@Nullable T>) sourceFunction.apply(value); } throw new IllegalArgumentException("Property class not supported!"); diff --git a/src/main/java/it/aboutbits/springboot/testing/validation/source/SizeLessThanValueSource.java b/src/main/java/it/aboutbits/springboot/testing/validation/source/SizeLessThanValueSource.java index 36c96fc..e74af4d 100644 --- a/src/main/java/it/aboutbits/springboot/testing/validation/source/SizeLessThanValueSource.java +++ b/src/main/java/it/aboutbits/springboot/testing/validation/source/SizeLessThanValueSource.java @@ -2,6 +2,7 @@ import it.aboutbits.springboot.testing.validation.core.ValueSource; import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.lang.reflect.Array; import java.util.ArrayList; @@ -53,7 +54,7 @@ public static void registerType(Class type, LongFunction> source) { @Override @SuppressWarnings("unchecked") - public Stream values(Class propertyClass, Object... args) { + public Stream<@Nullable T> values(Class propertyClass, Object... args) { var value = (Long.valueOf((long) args[0])); if (value == 0) { @@ -65,12 +66,12 @@ public Stream values(Class propertyClass, Object... args) { } if (propertyClass.isArray()) { - return (Stream) arrayFunction(propertyClass, value); + return (Stream<@Nullable T>) arrayFunction(propertyClass, value); } var sourceFunction = TYPE_SOURCES.get(propertyClass); if (sourceFunction != null) { - return (Stream) sourceFunction.apply(value); + return (Stream<@Nullable T>) sourceFunction.apply(value); } throw new IllegalArgumentException("Property class not supported!"); diff --git a/src/main/java/it/aboutbits/springboot/testing/validation/source/ZeroValueSource.java b/src/main/java/it/aboutbits/springboot/testing/validation/source/ZeroValueSource.java index 016cbf4..0f27ad3 100644 --- a/src/main/java/it/aboutbits/springboot/testing/validation/source/ZeroValueSource.java +++ b/src/main/java/it/aboutbits/springboot/testing/validation/source/ZeroValueSource.java @@ -3,6 +3,7 @@ import it.aboutbits.springboot.testing.validation.core.ValueSource; import it.aboutbits.springboot.toolbox.type.ScaledBigDecimal; import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.math.BigDecimal; import java.math.BigInteger; @@ -46,10 +47,10 @@ public static void registerType(Class type, Function> sou @Override @SuppressWarnings("unchecked") - public Stream values(Class propertyClass, Object... args) { + public Stream<@Nullable T> values(Class propertyClass, Object... args) { var sourceFunction = TYPE_SOURCES.get(propertyClass); if (sourceFunction != null) { - return (Stream) sourceFunction.apply(args); + return (Stream<@Nullable T>) sourceFunction.apply(args); } throw new IllegalArgumentException("Property class not supported!"); From 1e8d4bced044ff2d1a29ab3d6129c11a7455036b Mon Sep 17 00:00:00 2001 From: Andreas Hufler Date: Thu, 2 Apr 2026 09:08:56 +0200 Subject: [PATCH 6/7] update versions --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c71314a..c732eb2 100644 --- a/pom.xml +++ b/pom.xml @@ -26,12 +26,12 @@ it.aboutbits spring-boot-toolbox - 2.1.0 + 2.4.1 it.aboutbits archunit-toolbox - 1.0.0-RC1 + 1.1.0 From 113872fe6c3b0e188bb0bca9ce0f5835071400e1 Mon Sep 17 00:00:00 2001 From: Andreas Hufler Date: Thu, 2 Apr 2026 09:54:08 +0200 Subject: [PATCH 7/7] update versions --- pom.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index c732eb2..eb35304 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.springframework.boot spring-boot-starter-parent - 4.0.2 + 4.0.4 @@ -17,8 +17,8 @@ 25 - 2.46.0 - 0.12.15 + 2.48.0 + 0.13.1 @@ -78,7 +78,7 @@ net.datafaker datafaker - 2.5.3 + 2.5.4 @@ -107,7 +107,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.14.1 + 3.15.0 ${java.version} ${java.version} @@ -174,7 +174,7 @@ com.puppycrawl.tools checkstyle - 12.3.0 + 13.4.0 it.aboutbits