Skip to content

Commit 5894045

Browse files
committed
add initial validation implementation
1 parent cdc5d90 commit 5894045

35 files changed

Lines changed: 1519 additions & 0 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,4 @@ build/
4646

4747
### Local Development ###
4848
application-local.yml
49+
/src/main/java/it/aboutbits/springboot/testing/demo_project/

.idea/encodings.xml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pom.xml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@
33
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
44
<modelVersion>4.0.0</modelVersion>
55

6+
<parent>
7+
<groupId>org.springframework.boot</groupId>
8+
<artifactId>spring-boot-starter-parent</artifactId>
9+
<version>3.3.2</version>
10+
<relativePath/> <!-- lookup parent from repository -->
11+
</parent>
12+
613
<groupId>it.aboutbits</groupId>
714
<artifactId>spring-boot-testing</artifactId>
815
<version>BUILD-SNAPSHOT</version>
@@ -13,7 +20,30 @@
1320
</properties>
1421

1522
<dependencies>
23+
<dependency>
24+
<groupId>org.springframework.boot</groupId>
25+
<artifactId>spring-boot-starter-validation</artifactId>
26+
</dependency>
27+
28+
<!-- Utilities -->
29+
<dependency>
30+
<groupId>org.projectlombok</groupId>
31+
<artifactId>lombok</artifactId>
32+
<optional>true</optional>
33+
</dependency>
34+
35+
<!-- https://mvnrepository.com/artifact/org.jetbrains/annotations -->
36+
<dependency>
37+
<groupId>org.jetbrains</groupId>
38+
<artifactId>annotations</artifactId>
39+
<version>24.1.0</version>
40+
</dependency>
1641

42+
<!-- Testing -->
43+
<dependency>
44+
<groupId>org.springframework.boot</groupId>
45+
<artifactId>spring-boot-starter-test</artifactId>
46+
</dependency>
1747
</dependencies>
1848

1949
<build>
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package it.aboutbits.springboot.testing.validation.core;
2+
3+
import it.aboutbits.springboot.testing.validation.rule.BetweenRule;
4+
import it.aboutbits.springboot.testing.validation.rule.FutureRule;
5+
import it.aboutbits.springboot.testing.validation.rule.MaxRule;
6+
import it.aboutbits.springboot.testing.validation.rule.MinRule;
7+
import it.aboutbits.springboot.testing.validation.rule.NegativeOrZeroRule;
8+
import it.aboutbits.springboot.testing.validation.rule.NegativeRule;
9+
import it.aboutbits.springboot.testing.validation.rule.NotBlankRule;
10+
import it.aboutbits.springboot.testing.validation.rule.NotEmptyRule;
11+
import it.aboutbits.springboot.testing.validation.rule.NotNullRule;
12+
import it.aboutbits.springboot.testing.validation.rule.NullableRule;
13+
import it.aboutbits.springboot.testing.validation.rule.PastRule;
14+
import it.aboutbits.springboot.testing.validation.rule.PositiveOrZeroRule;
15+
import it.aboutbits.springboot.testing.validation.rule.PositiveRule;
16+
import it.aboutbits.springboot.testing.validation.rule.UncheckedRule;
17+
import it.aboutbits.springboot.testing.validation.rule.ValidBeanRule;
18+
import lombok.AccessLevel;
19+
import lombok.Getter;
20+
import lombok.NonNull;
21+
import lombok.RequiredArgsConstructor;
22+
import lombok.Setter;
23+
24+
import java.util.ArrayList;
25+
import java.util.List;
26+
27+
@RequiredArgsConstructor
28+
public abstract class BaseRuleBuilder<R extends BaseRuleBuilder<?>> implements
29+
ValidationRulesData,
30+
BetweenRule<R>,
31+
FutureRule<R>,
32+
MaxRule<R>,
33+
MinRule<R>,
34+
NegativeOrZeroRule<R>,
35+
NegativeRule<R>,
36+
NotBlankRule<R>,
37+
NotEmptyRule<R>,
38+
NotNullRule<R>,
39+
NullableRule<R>,
40+
PastRule<R>,
41+
PositiveOrZeroRule<R>,
42+
PositiveRule<R>,
43+
UncheckedRule<R>,
44+
ValidBeanRule<R> {
45+
@Getter(AccessLevel.PACKAGE)
46+
private final List<Rule> rules = new ArrayList<>();
47+
48+
@Setter(AccessLevel.PACKAGE)
49+
private Runnable triggerValidation;
50+
51+
@Override
52+
public void addRule(@NonNull Rule rule) {
53+
rules.add(rule);
54+
}
55+
56+
public void isCompliant() {
57+
triggerValidation.run();
58+
}
59+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package it.aboutbits.springboot.testing.validation.core;
2+
3+
import lombok.AccessLevel;
4+
import lombok.Getter;
5+
import lombok.NonNull;
6+
import lombok.RequiredArgsConstructor;
7+
import lombok.Setter;
8+
9+
import java.util.HashSet;
10+
import java.util.Set;
11+
import java.util.function.BiConsumer;
12+
import java.util.function.Consumer;
13+
14+
import static org.assertj.core.api.Assertions.assertThat;
15+
16+
@RequiredArgsConstructor
17+
public abstract class BaseValidationAssert<R extends BaseRuleBuilder<?>> implements AutoCloseable {
18+
@Getter(AccessLevel.PROTECTED)
19+
private final R ruleBuilder;
20+
21+
protected static final Set<Class<?>> NON_BEAN_TYPES = new HashSet<>();
22+
23+
private Object parameterUnderTest;
24+
25+
@Setter(AccessLevel.PRIVATE)
26+
private Consumer<Object> functionToCallWithParameter = null;
27+
28+
protected boolean hasBeenCalled = false;
29+
30+
public static void registerNonBeanType(Class<?> type) {
31+
NON_BEAN_TYPES.add(type);
32+
}
33+
34+
public <P> CallBuilder<R, P> that(@NonNull P parameterUnderTest) {
35+
this.parameterUnderTest = parameterUnderTest;
36+
ruleBuilder.setTriggerValidation(this::assertValidation);
37+
return new CallBuilder<>(this, parameterUnderTest);
38+
}
39+
40+
@Override
41+
public void close() throws RuntimeException {
42+
assertThat(hasBeenCalled)
43+
.withFailMessage("Validation was never invoked by calling 'isCompliant()'.")
44+
.isTrue();
45+
}
46+
47+
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
48+
public static final class CallBuilder<R extends BaseRuleBuilder<?>, P> {
49+
private final BaseValidationAssert<R> parent;
50+
private final P parameterUnderTest;
51+
52+
public R calling(@NonNull Consumer<P> functionToCallWithParameter) {
53+
parent.setFunctionToCallWithParameter((Consumer<Object>) functionToCallWithParameter);
54+
return parent.ruleBuilder;
55+
}
56+
57+
public R usingBeanValidation() {
58+
return parent.ruleBuilder;
59+
}
60+
61+
public <ID> R calling(
62+
@NonNull BiConsumer<ID, P> functionToCallWithParameter,
63+
@NonNull ID id
64+
) {
65+
parent.setFunctionToCallWithParameter(
66+
p -> functionToCallWithParameter.accept(id, (P) p)
67+
);
68+
return parent.ruleBuilder;
69+
}
70+
}
71+
72+
private void assertValidation() {
73+
hasBeenCalled = true;
74+
new RuleValidator<>().assertValidation(
75+
new RuleValidator.AssertionParameter<>(
76+
parameterUnderTest,
77+
functionToCallWithParameter,
78+
ruleBuilder.getRules(),
79+
NON_BEAN_TYPES
80+
)
81+
);
82+
}
83+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package it.aboutbits.springboot.testing.validation.core;
2+
3+
import it.aboutbits.springboot.testing.validation.source.InertValueSource;
4+
import lombok.AccessLevel;
5+
import lombok.Getter;
6+
7+
public final class Rule {
8+
@Getter(AccessLevel.PACKAGE)
9+
private final String property;
10+
11+
@Getter(AccessLevel.PACKAGE)
12+
private final Class<? extends ValueSource> valueSource;
13+
14+
@Getter(AccessLevel.PACKAGE)
15+
private final Object[] args;
16+
17+
@Getter(AccessLevel.PACKAGE)
18+
private boolean requireValid = false;
19+
20+
@Getter(AccessLevel.PACKAGE)
21+
private boolean requireNullable = false;
22+
23+
public Rule(String property, Class<? extends ValueSource> valueSource, Object... args) {
24+
checkPropertyName(property);
25+
26+
this.property = property;
27+
this.valueSource = valueSource;
28+
this.args = args;
29+
}
30+
31+
public static Rule validAnnotated(String property) {
32+
checkPropertyName(property);
33+
34+
var rule = new Rule(property, InertValueSource.class);
35+
rule.requireValid = true;
36+
return rule;
37+
}
38+
39+
public static Rule nullableAnnotated(String property) {
40+
checkPropertyName(property);
41+
42+
var rule = new Rule(property, InertValueSource.class);
43+
rule.requireNullable = true;
44+
return rule;
45+
}
46+
47+
private static void checkPropertyName(String property) {
48+
if (property.contains(".")) {
49+
throw new IllegalArgumentException("Referencing sub-objects using dot notation is not supported.");
50+
}
51+
}
52+
}

0 commit comments

Comments
 (0)