Skip to content

Commit 8dab6f2

Browse files
yrodieregsmet
authored andcommitted
HV-1939 Test constructor validation on inner classes declared in methods
1 parent 58466ed commit 8dab6f2

File tree

2 files changed

+216
-0
lines changed

2 files changed

+216
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
/*
2+
* Hibernate Validator, declare and validate application constraints
3+
*
4+
* License: Apache License, Version 2.0
5+
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
6+
*/
7+
package org.hibernate.validator.test.internal.engine.methodvalidation;
8+
9+
import static java.lang.annotation.ElementType.CONSTRUCTOR;
10+
import static java.lang.annotation.ElementType.TYPE;
11+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
12+
import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat;
13+
import static org.hibernate.validator.testutil.ConstraintViolationAssert.pathWith;
14+
import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf;
15+
import static org.hibernate.validator.testutils.ValidatorUtil.getValidator;
16+
17+
import java.lang.annotation.Retention;
18+
import java.lang.annotation.Target;
19+
import java.lang.reflect.Constructor;
20+
import java.lang.reflect.InvocationTargetException;
21+
import java.util.Set;
22+
23+
import org.hibernate.validator.test.internal.engine.methodvalidation.model.Customer;
24+
import org.hibernate.validator.test.internal.engine.methodvalidation.service.CustomerRepositoryImpl;
25+
26+
import jakarta.validation.Constraint;
27+
import jakarta.validation.ConstraintValidator;
28+
import jakarta.validation.ConstraintValidatorContext;
29+
import jakarta.validation.ConstraintViolation;
30+
import jakarta.validation.Payload;
31+
import jakarta.validation.Valid;
32+
import jakarta.validation.constraints.NotNull;
33+
import jakarta.validation.executable.ExecutableValidator;
34+
import org.testng.annotations.BeforeMethod;
35+
import org.testng.annotations.Test;
36+
37+
public class MethodInnerClassConstructorValidationTest {
38+
39+
protected ExecutableValidator executableValidator;
40+
private final CustomerRepositoryImplMetamodel metamodel = new CustomerRepositoryImplMetamodel();
41+
42+
@BeforeMethod
43+
public void setUp() {
44+
this.executableValidator = getValidator().forExecutables();
45+
}
46+
47+
@Test
48+
public void constructorParameterValidationYieldsConstraintViolation() throws Exception {
49+
Set<? extends ConstraintViolation<?>> violations = executableValidator.validateConstructorParameters(
50+
metamodel.clazz().getConstructor( CustomerRepositoryImplMetamodel.class, String.class ),
51+
new Object[] { metamodel, null }
52+
);
53+
54+
assertThat( violations ).containsOnlyViolations(
55+
violationOf( NotNull.class )
56+
.withMessage( "must not be null" )
57+
.withInvalidValue( null )
58+
.withRootBeanClass( metamodel.clazz() )
59+
.withPropertyPath( pathWith()
60+
.constructor( metamodel.clazz() )
61+
.parameter( "id", 1 )
62+
)
63+
);
64+
}
65+
66+
@Test
67+
public void cascadedConstructorParameterValidationYieldsConstraintViolation() throws Exception {
68+
Set<? extends ConstraintViolation<?>> violations = executableValidator.validateConstructorParameters(
69+
metamodel.clazz().getConstructor( CustomerRepositoryImplMetamodel.class, Customer.class ),
70+
new Object[] { metamodel, new Customer( null ) }
71+
);
72+
73+
assertThat( violations ).containsOnlyViolations(
74+
violationOf( NotNull.class )
75+
.withMessage( "must not be null" )
76+
.withInvalidValue( null )
77+
.withRootBeanClass( metamodel.clazz() )
78+
.withPropertyPath( pathWith()
79+
.constructor( metamodel.clazz() )
80+
.parameter( "customer", 1 )
81+
.property( "name" )
82+
)
83+
);
84+
}
85+
86+
@Test
87+
public void constructorReturnValueValidationYieldsConstraintViolation() throws Exception {
88+
Constructor<?> constructor = metamodel.clazz().getDeclaredConstructor( CustomerRepositoryImplMetamodel.class );
89+
Object customerRepository = constructor.newInstance( metamodel );
90+
Set<? extends ConstraintViolation<?>> violations = executableValidator.validateConstructorReturnValue(
91+
constructor,
92+
customerRepository
93+
);
94+
95+
assertThat( violations ).containsOnlyViolations(
96+
violationOf( ValidB2BRepository.class )
97+
.withMessage( "{ValidB2BRepository.message}" )
98+
.withInvalidValue( customerRepository )
99+
.withRootBeanClass( metamodel.clazz() )
100+
.withPropertyPath( pathWith()
101+
.constructor( metamodel.clazz() )
102+
.returnValue()
103+
)
104+
);
105+
}
106+
107+
@Test
108+
public void cascadedConstructorReturnValueValidationYieldsConstraintViolation() throws Exception {
109+
Constructor<?> constructor = metamodel.clazz().getDeclaredConstructor(
110+
CustomerRepositoryImplMetamodel.class, String.class );
111+
Object customerRepository = constructor.newInstance( metamodel, null );
112+
Set<? extends ConstraintViolation<?>> violations = executableValidator.validateConstructorReturnValue(
113+
constructor,
114+
customerRepository
115+
);
116+
117+
assertThat( violations ).containsOnlyViolations(
118+
violationOf( NotNull.class )
119+
.withMessage( "must not be null" )
120+
.withInvalidValue( null )
121+
.withRootBeanClass( metamodel.clazz() )
122+
.withPropertyPath( pathWith()
123+
.constructor( metamodel.clazz() )
124+
.returnValue()
125+
.property( "customer" )
126+
)
127+
);
128+
}
129+
130+
private static class CustomerRepositoryImplMetamodel {
131+
public Class<?> clazz() {
132+
class CustomerRepositoryImpl {
133+
@NotNull
134+
private final Customer customer = null;
135+
136+
@ValidB2BRepository
137+
public CustomerRepositoryImpl() {
138+
}
139+
140+
@Valid
141+
public CustomerRepositoryImpl(@NotNull String id) {
142+
}
143+
144+
public CustomerRepositoryImpl(@Valid Customer customer) {
145+
}
146+
}
147+
return CustomerRepositoryImpl.class;
148+
}
149+
150+
public Object newInstance()
151+
throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
152+
return clazz().getDeclaredConstructor( CustomerRepositoryImplMetamodel.class ).newInstance( this );
153+
}
154+
}
155+
156+
@Constraint(validatedBy = { ValidB2BRepositoryValidator.class })
157+
@Target({ TYPE, CONSTRUCTOR })
158+
@Retention(RUNTIME)
159+
public @interface ValidB2BRepository {
160+
String message() default "{ValidB2BRepository.message}";
161+
162+
Class<?>[] groups() default { };
163+
164+
Class<? extends Payload>[] payload() default { };
165+
}
166+
167+
public static class ValidB2BRepositoryValidator
168+
implements ConstraintValidator<ValidB2BRepository, Object> {
169+
@Override
170+
public boolean isValid(Object repository, ConstraintValidatorContext context) {
171+
return false;
172+
}
173+
}
174+
}

engine/src/test/java/org/hibernate/validator/test/internal/metadata/descriptor/ConstructorDescriptorTest.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,15 @@
1212
import java.util.List;
1313
import java.util.Set;
1414

15+
import jakarta.validation.Valid;
16+
import jakarta.validation.constraints.NotNull;
1517
import jakarta.validation.metadata.ConstraintDescriptor;
1618
import jakarta.validation.metadata.ConstructorDescriptor;
1719
import jakarta.validation.metadata.ParameterDescriptor;
1820
import jakarta.validation.metadata.ReturnValueDescriptor;
1921

2022
import org.hibernate.validator.test.internal.metadata.Customer;
23+
import org.hibernate.validator.test.internal.metadata.CustomerRepository;
2124
import org.hibernate.validator.test.internal.metadata.CustomerRepositoryExt;
2225
import org.hibernate.validator.test.internal.metadata.CustomerRepositoryExt.ValidB2BRepository;
2326
import org.testng.annotations.Test;
@@ -81,4 +84,43 @@ public void testGetReturnValueDescriptor() {
8184
ConstraintDescriptor<?> descriptor = constraintDescriptors.iterator().next();
8285
assertThat( descriptor.getAnnotation().annotationType() ).isEqualTo( ValidB2BRepository.class );
8386
}
87+
88+
@Test
89+
public void testMethodInnerClassGetParameterDescriptors() {
90+
class MethodInnerClass extends CustomerRepository {
91+
public MethodInnerClass(@NotNull String foo, @Valid Customer customer) {
92+
}
93+
}
94+
95+
ConstructorDescriptor constructorDescriptor = getConstructorDescriptor(
96+
MethodInnerClass.class,
97+
ConstructorDescriptorTest.class,
98+
String.class,
99+
Customer.class
100+
);
101+
102+
List<ParameterDescriptor> parameterDescriptors = constructorDescriptor.getParameterDescriptors();
103+
assertThat( parameterDescriptors ).hasSize( 3 );
104+
105+
// This is the parameter representing the enclosing instance.
106+
ParameterDescriptor parameterDescriptor0 = parameterDescriptors.get( 0 );
107+
assertThat( parameterDescriptor0.getElementClass() ).isEqualTo( ConstructorDescriptorTest.class );
108+
assertThat( parameterDescriptor0.getIndex() ).isEqualTo( 0 );
109+
assertThat( parameterDescriptor0.hasConstraints() ).isFalse();
110+
assertThat( parameterDescriptor0.isCascaded() ).isFalse();
111+
112+
ParameterDescriptor parameterDescriptor1 = parameterDescriptors.get( 1 );
113+
assertThat( parameterDescriptor1.getElementClass() ).isEqualTo( String.class );
114+
assertThat( parameterDescriptor1.getIndex() ).isEqualTo( 1 );
115+
assertThat( parameterDescriptor1.getName() ).isEqualTo( "foo" );
116+
assertThat( parameterDescriptor1.hasConstraints() ).isTrue();
117+
assertThat( parameterDescriptor1.isCascaded() ).isFalse();
118+
119+
ParameterDescriptor parameterDescriptor2 = parameterDescriptors.get( 2 );
120+
assertThat( parameterDescriptor2.getElementClass() ).isEqualTo( Customer.class );
121+
assertThat( parameterDescriptor2.getIndex() ).isEqualTo( 2 );
122+
assertThat( parameterDescriptor2.getName() ).isEqualTo( "customer" );
123+
assertThat( parameterDescriptor2.hasConstraints() ).isFalse();
124+
assertThat( parameterDescriptor2.isCascaded() ).isTrue();
125+
}
84126
}

0 commit comments

Comments
 (0)