/*
* Hibernate Validator, declare and validate application constraints
*
* License: Apache License, Version 2.0
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
*/
package org.hibernate.validator.test.internal.metadata.descriptor;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList;
import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet;
import static org.hibernate.validator.testutils.ValidatorUtil.getBeanDescriptor;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import javax.validation.ConstraintDeclarationException;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import javax.validation.metadata.BeanDescriptor;
import javax.validation.metadata.ConstraintDescriptor;
import javax.validation.metadata.ConstructorDescriptor;
import javax.validation.metadata.MethodDescriptor;
import javax.validation.metadata.MethodType;
import javax.validation.metadata.ParameterDescriptor;
import javax.validation.metadata.PropertyDescriptor;
import org.joda.time.DateMidnight;
import org.testng.annotations.Test;
import org.hibernate.validator.constraints.ScriptAssert;
import org.hibernate.validator.test.internal.metadata.Customer;
import org.hibernate.validator.test.internal.metadata.CustomerRepository;
import org.hibernate.validator.test.internal.metadata.CustomerRepositoryExt;
import org.hibernate.validator.test.internal.metadata.IllegalCustomerRepositoryExt;
import org.hibernate.validator.testutil.TestForIssue;
/**
* Unit test for {@link BeanDescriptor} and its creation.
*
* @author Gunnar Morling
* @author Hardy Ferentschik
*/
public class BeanDescriptorTest {
@Test
public void testGetElementClass() {
BeanDescriptor descriptor = getBeanDescriptor( CustomerRepository.class );
assertEquals( descriptor.getElementClass(), CustomerRepository.class );
}
@Test
public void testIsTypeConstrainedForUnconstrainedType() {
BeanDescriptor descriptor = getBeanDescriptor( UnconstrainedType.class );
assertFalse( descriptor.isBeanConstrained() );
}
@Test
public void testIsBeanConstrainedClassLevelConstraint() {
BeanDescriptor descriptor = getBeanDescriptor( ClassLevelConstrainedType.class );
assertTrue( descriptor.isBeanConstrained() );
}
@Test
public void testIsBeanConstrainedFieldConstraint() {
BeanDescriptor descriptor = getBeanDescriptor( FieldConstrainedType.class );
assertTrue( descriptor.isBeanConstrained() );
}
@Test
public void testIsBeanConstrainedGetterConstraint() {
BeanDescriptor descriptor = getBeanDescriptor( GetterConstrainedType.class );
assertTrue( descriptor.isBeanConstrained() );
assertTrue( descriptor.getConstrainedMethods( MethodType.NON_GETTER ).isEmpty() );
assertFalse( descriptor.getConstrainedMethods( MethodType.GETTER ).isEmpty() );
}
@Test
public void testIsTypeConstrainedForBeanConstrainedType() {
BeanDescriptor descriptor = getBeanDescriptor( CustomerRepository.class );
assertTrue( descriptor.isBeanConstrained() );
}
@Test
public void testIsTypeConstrainedForParameterConstrainedType() {
BeanDescriptor descriptor = getBeanDescriptor( ParameterConstrainedType.class );
assertFalse( descriptor.isBeanConstrained(), "The entity should have no bean constraints" );
assertFalse(
descriptor.getConstrainedMethods( MethodType.NON_GETTER ).isEmpty(),
"The entity should have constrained methods"
);
}
@Test
public void testIsTypeConstrainedForConstructorParameterConstrainedType() {
BeanDescriptor descriptor = getBeanDescriptor( ConstructorParameterConstrainedType.class );
assertFalse( descriptor.isBeanConstrained(), "The entity should have no bean constraints" );
assertTrue(
descriptor.getConstrainedMethods( MethodType.NON_GETTER, MethodType.GETTER ).isEmpty(),
"The entity should have no constrained methods"
);
assertTrue(
descriptor.getConstrainedConstructors().size() == 1,
"The entity should have a constrained constructor"
);
}
@Test
public void testIsTypeConstrainedForCascadingParameterType() {
BeanDescriptor descriptor = getBeanDescriptor( CascadingParameterType.class );
assertFalse( descriptor.isBeanConstrained(), "The entity should have no bean constraints" );
assertFalse(
descriptor.getConstrainedMethods( MethodType.NON_GETTER ).isEmpty(),
"The entity should have constrained methods"
);
}
@Test
public void testIsTypeConstrainedForConstructorCascadingParameterType() {
BeanDescriptor descriptor = getBeanDescriptor( ConstructorCascadingParameterType.class );
assertFalse( descriptor.isBeanConstrained(), "The entity should have no bean constraints" );
assertTrue(
descriptor.getConstrainedMethods( MethodType.NON_GETTER ).isEmpty(),
"The entity should have no constrained methods"
);
assertTrue(
descriptor.getConstrainedConstructors().size() == 1,
"The entity should have a constrained constructor"
);
}
@Test
public void testIsTypeConstrainedForReturnValueConstrainedType() {
BeanDescriptor descriptor = getBeanDescriptor( ReturnValueConstrainedType.class );
assertFalse( descriptor.isBeanConstrained(), "The entity should have no bean constraints" );
assertFalse(
descriptor.getConstrainedMethods( MethodType.NON_GETTER ).isEmpty(),
"The entity should have constrained methods"
);
}
@Test
public void testIsTypeConstrainedForConstructorReturnValueConstrainedType() {
BeanDescriptor descriptor = getBeanDescriptor( ConstructorReturnValueConstrainedType.class );
assertFalse( descriptor.isBeanConstrained(), "The entity should have no bean constraints" );
assertTrue(
descriptor.getConstrainedMethods( MethodType.NON_GETTER, MethodType.GETTER ).isEmpty(),
"The entity should have no constrained methods"
);
assertTrue(
descriptor.getConstrainedConstructors().size() == 1,
"The entity should have a constrained constructor"
);
}
@Test
public void testIsTypeConstrainedForCascadingReturnValueType() {
BeanDescriptor descriptor = getBeanDescriptor( CascadingReturnValueType.class );
assertFalse( descriptor.isBeanConstrained(), "The entity should have no bean constraints" );
assertFalse(
descriptor.getConstrainedMethods( MethodType.NON_GETTER ).isEmpty(),
"The entity should have constrained methods"
);
}
@Test
public void testIsTypeConstrainedForConstructorCascadingReturnValueType() {
BeanDescriptor descriptor = getBeanDescriptor( ConstructorCascadingReturnValueType.class );
assertFalse( descriptor.isBeanConstrained(), "The entity should have no bean constraints" );
assertTrue(
descriptor.getConstrainedMethods( MethodType.NON_GETTER, MethodType.GETTER ).isEmpty(),
"The entity should have no constrained methods"
);
assertTrue(
descriptor.getConstrainedConstructors().size() == 1,
"The entity should have a constrained constructor"
);
}
@Test
public void testIsTypeConstrainedForDerivedConstrainedType() {
BeanDescriptor descriptor = getBeanDescriptor( DerivedConstrainedType.class );
assertFalse( descriptor.isBeanConstrained(), "The entity should have no bean constraints" );
assertFalse(
descriptor.getConstrainedMethods( MethodType.NON_GETTER ).isEmpty(),
"The entity should have constrained methods"
);
}
@Test
public void testGetConstraintDescriptors() {
BeanDescriptor descriptor = getBeanDescriptor( CustomerRepository.class );
Set<ConstraintDescriptor<?>> constraintDescriptors = descriptor.getConstraintDescriptors();
assertEquals( constraintDescriptors.size(), 1 );
assertEquals(
constraintDescriptors.iterator().next().getAnnotation().annotationType(),
ScriptAssert.class
);
}
@Test
public void testGetBeanDescriptor() {
BeanDescriptor beanDescriptor = getBeanDescriptor( CustomerRepository.class );
assertNotNull( beanDescriptor );
assertEquals( beanDescriptor.getElementClass(), CustomerRepository.class );
}
@Test
public void testGetConstraintsForMethod() throws Exception {
BeanDescriptor descriptor = getBeanDescriptor( CustomerRepository.class );
MethodDescriptor methodDescriptor = descriptor.getConstraintsForMethod( "foo" );
assertNotNull( methodDescriptor );
}
// A method descriptor can be retrieved by specifying an overridden method
// from a base type.
@Test
public void testGetConstraintsForOverriddenMethod() throws Exception {
BeanDescriptor descriptor = getBeanDescriptor( CustomerRepositoryExt.class );
MethodDescriptor methodDescriptor = descriptor.getConstraintsForMethod( "foo" );
assertNotNull( methodDescriptor );
}
// A method descriptor can be retrieved by specifying a method from a base
// type (qax() is not defined on CustomerRepositoryExt, but only on
// CustomerRepository).
@Test
public void testGetConstraintsForMethodFromBaseType() throws Exception {
BeanDescriptor descriptor = getBeanDescriptor( CustomerRepositoryExt.class );
MethodDescriptor methodDescriptor = descriptor.getConstraintsForMethod(
"qax",
Integer.class
);
assertNotNull( methodDescriptor );
}
@Test
public void testGetConstraintsForUnknownMethod() throws Exception {
BeanDescriptor descriptor = getBeanDescriptor( CustomerRepository.class );
assertNull( descriptor.getConstraintsForMethod( "zap" ) );
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testGetConstraintsFailsForNullMethod() throws Exception {
BeanDescriptor descriptor = getBeanDescriptor( CustomerRepository.class );
descriptor.getConstraintsForMethod( null );
}
@Test
public void testGetConstrainedMethods() {
BeanDescriptor descriptor = getBeanDescriptor( CustomerRepository.class );
Set<MethodDescriptor> constrainedMethods = descriptor.getConstrainedMethods( MethodType.NON_GETTER );
assertThat( getMethodNames( constrainedMethods ) ).containsOnly(
"createCustomer",
"saveCustomer",
"foo",
"bar",
"baz",
"zap",
"qax",
"methodWithCrossParameterConstraint",
"methodWithParameterGroupConversion",
"methodWithReturnValueGroupConversion"
);
}
@Test
public void testGetConstrainedMethodsForDerivedType() {
BeanDescriptor descriptor = getBeanDescriptor( CustomerRepositoryExt.class );
Set<MethodDescriptor> constrainedMethods = descriptor.getConstrainedMethods( MethodType.NON_GETTER );
assertThat( getMethodNames( constrainedMethods ) ).containsOnly(
"createCustomer",
"saveCustomer",
"modifyCustomer",
"foo",
"bar",
"baz",
"zip",
"zap",
"qax",
"methodWithCrossParameterConstraint",
"methodWithParameterGroupConversion",
"methodWithReturnValueGroupConversion"
);
}
@Test(expectedExceptions = ConstraintDeclarationException.class, expectedExceptionsMessageRegExp = "HV000151.*")
@TestForIssue(jiraKey = "HV-683")
public void testGetConstrainedMethodsForTypeWithIllegalMethodCausesDeclarationException() {
getBeanDescriptor( IllegalCustomerRepositoryExt.class ).getConstrainedMethods( MethodType.NON_GETTER );
}
@Test
public void testGetConstrainedConstructors() {
BeanDescriptor descriptor = getBeanDescriptor( CustomerRepositoryExt.class );
Set<ConstructorDescriptor> constrainedConstructors = descriptor.getConstrainedConstructors();
assertThat( constrainedConstructors ).isNotNull();
assertThat( getSignatures( constrainedConstructors ) ).containsOnly(
Arrays.<Class<?>>asList( String.class ),
Arrays.<Class<?>>asList( String.class, Customer.class ),
Collections.<Class<?>>emptyList(),
Arrays.<Class<?>>asList( DateMidnight.class, DateMidnight.class )
);
}
@Test
public void testGetConstraintsForConstructor() {
BeanDescriptor descriptor = getBeanDescriptor( CustomerRepositoryExt.class );
ConstructorDescriptor constructorDescriptor = descriptor.getConstraintsForConstructor(
String.class,
Customer.class
);
assertThat( constructorDescriptor ).isNotNull();
}
@Test
public void testGetConstraintsForUnconstrainedConstructor() {
BeanDescriptor descriptor = getBeanDescriptor( CustomerRepositoryExt.class );
ConstructorDescriptor constructorDescriptor = descriptor.getConstraintsForConstructor( int.class );
assertThat( constructorDescriptor ).isNull();
}
@Test
@TestForIssue(jiraKey = "HV-660")
public void testGetConstrainedPropertiesForTypeWithClassLevelConstraint() {
BeanDescriptor descriptor = getBeanDescriptor( ClassLevelConstrainedType.class );
Set<PropertyDescriptor> constrainedProperties = descriptor.getConstrainedProperties();
assertThat( constrainedProperties ).isEmpty();
}
@Test
@TestForIssue(jiraKey = "HV-761")
public void testGetConstraintMethods() {
BeanDescriptor beanDescriptor = getBeanDescriptor( Mixed.class );
Set<MethodDescriptor> methodDescriptors = beanDescriptor.getConstrainedMethods( MethodType.GETTER );
assertEquals( methodDescriptors.size(), 1, "There should be only one getter" );
MethodDescriptor methodDescriptor = methodDescriptors.iterator().next();
assertEquals( methodDescriptor.getName(), "getFoo", "Unexpected method name" );
methodDescriptors = beanDescriptor.getConstrainedMethods( MethodType.NON_GETTER );
assertEquals( methodDescriptors.size(), 1, "There should be only one non-getter" );
methodDescriptor = methodDescriptors.iterator().next();
assertEquals( methodDescriptor.getName(), "foo", "Unexpected method name" );
methodDescriptors = beanDescriptor.getConstrainedMethods( MethodType.NON_GETTER, MethodType.GETTER );
assertEquals( methodDescriptors.size(), 2, "There should be two methods" );
// passing null as main argument
methodDescriptors = beanDescriptor.getConstrainedMethods( null );
assertEquals( methodDescriptors.size(), 0, "There should be no match" );
// passing null as vararg
methodDescriptors = beanDescriptor.getConstrainedMethods( MethodType.GETTER, null );
assertEquals( methodDescriptors.size(), 1, "There should be only one getter" );
}
private Set<String> getMethodNames(Set<MethodDescriptor> descriptors) {
Set<String> methodNames = newHashSet();
for ( MethodDescriptor methodDescriptor : descriptors ) {
methodNames.add( methodDescriptor.getName() );
}
return methodNames;
}
private Set<List<Class<?>>> getSignatures(Set<ConstructorDescriptor> descriptors) {
Set<List<Class<?>>> signatures = newHashSet();
for ( ConstructorDescriptor methodDescriptor : descriptors ) {
List<Class<?>> parameterTypes = newArrayList();
for ( ParameterDescriptor oneParameter : methodDescriptor.getParameterDescriptors() ) {
parameterTypes.add( oneParameter.getElementClass() );
}
signatures.add( parameterTypes );
}
return signatures;
}
private static class UnconstrainedType {
@SuppressWarnings("unused")
public void foo(String foo) {
}
}
private static class ParameterConstrainedType {
@SuppressWarnings("unused")
public void foo(@NotNull String foo) {
}
}
private static class ConstructorParameterConstrainedType {
@SuppressWarnings("unused")
public ConstructorParameterConstrainedType(@NotNull String foo) {
}
}
private static class CascadingParameterType {
@SuppressWarnings("unused")
public void foo(@Valid List<String> foo) {
}
}
private static class ConstructorCascadingParameterType {
@SuppressWarnings("unused")
public ConstructorCascadingParameterType(@Valid List<String> foo) {
}
}
private static class ReturnValueConstrainedType {
@NotNull
@SuppressWarnings("unused")
public String foo(String foo) {
return null;
}
}
private static class ConstructorReturnValueConstrainedType {
@NotNull
@SuppressWarnings("unused")
public ConstructorReturnValueConstrainedType(String foo) {
}
}
private static class CascadingReturnValueType {
@Valid
@SuppressWarnings("unused")
public List<String> foo(String foo) {
return null;
}
}
private static class ConstructorCascadingReturnValueType {
@Valid
@SuppressWarnings("unused")
public ConstructorCascadingReturnValueType(String foo) {
}
}
private static class DerivedConstrainedType extends ParameterConstrainedType {
@Override
public void foo(String foo) {
}
}
@ScriptAssert(lang = "", script = "")
private static class ClassLevelConstrainedType {
}
private static class FieldConstrainedType {
@NotNull
private String foo;
}
private static class GetterConstrainedType {
private String foo;
@NotNull
public String getFoo() {
return foo;
}
}
private static class Mixed {
@NotNull
String foo() {
return null;
}
@NotNull
String getFoo() {
return null;
}
}
}