/* * 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; } } }