/* * 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.provider; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; import static org.assertj.core.api.Assertions.assertThat; import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.Target; import java.util.Collections; import java.util.Map; import javax.validation.Constraint; import javax.validation.ConstraintDeclarationException; import javax.validation.Payload; import javax.validation.Valid; import javax.validation.constraints.NotNull; import javax.validation.groups.ConvertGroup; import javax.validation.groups.Default; import javax.validation.metadata.ConstraintDescriptor; import org.hibernate.validator.constraints.ScriptAssert; import org.hibernate.validator.internal.engine.cascading.ValueExtractorManager; import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.core.MetaConstraint; import org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider; import org.hibernate.validator.internal.metadata.raw.BeanConfiguration; import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; import org.hibernate.validator.internal.metadata.raw.ConstrainedElement.ConstrainedElementKind; import org.hibernate.validator.internal.metadata.raw.ConstrainedExecutable; import org.hibernate.validator.internal.metadata.raw.ConstrainedField; import org.hibernate.validator.internal.metadata.raw.ConstrainedType; import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.testutil.TestForIssue; import org.joda.time.DateMidnight; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Unit test for {@link AnnotationMetaDataProvider}. * * @author Gunnar Morling */ public class AnnotationMetaDataProviderTest extends AnnotationMetaDataProviderTestBase { private AnnotationMetaDataProvider provider; @BeforeMethod public void setUpProvider() { provider = new AnnotationMetaDataProvider( new ConstraintHelper(), new TypeResolutionHelper(), new ValueExtractorManager( Collections.emptySet() ), new AnnotationProcessingOptionsImpl() ); } @Test public void testGetConstructorMetaData() throws Exception { BeanConfiguration<Foo> beanConfiguration = provider.getBeanConfiguration( Foo.class ); ConstrainedExecutable constructor = findConstrainedConstructor( beanConfiguration, Foo.class, String.class ); assertThat( constructor.getKind() ).isEqualTo( ConstrainedElementKind.CONSTRUCTOR ); assertThat( constructor.isConstrained() ).isTrue(); assertThat( constructor.getCascadingMetaData().isCascading() ).isFalse(); assertThat( constructor.getConstraints() ).hasSize( 1 ); MetaConstraint<?> constraint = constructor.getConstraints().iterator().next(); assertThat( constraint.getDescriptor().getAnnotation().annotationType() ).isEqualTo( NotNull.class ); assertThat( constraint.getElementType() ).isEqualTo( ElementType.CONSTRUCTOR ); } @Test public void testGetCrossParameterMetaData() throws Exception { //when BeanConfiguration<Calendar> beanConfiguration = provider.getBeanConfiguration( Calendar.class ); ConstrainedExecutable createEvent = findConstrainedMethod( beanConfiguration, Calendar.class, "createEvent", DateMidnight.class, DateMidnight.class ); //then assertThat( createEvent.isConstrained() ).isTrue(); assertThat( createEvent.getCascadingMetaData().isCascading() ).isFalse(); assertThat( createEvent.getKind() ).isEqualTo( ConstrainedElementKind.METHOD ); assertThat( createEvent.getConstraints() ).as( "No return value constraints expected" ).isEmpty(); assertThat( createEvent.getCrossParameterConstraints() ).hasSize( 1 ); assertThat( createEvent.getExecutable() ).isEqualTo( Calendar.class.getMethod( "createEvent", DateMidnight.class, DateMidnight.class ) ); MetaConstraint<?> constraint = createEvent.getCrossParameterConstraints().iterator().next(); assertThat( constraint.getDescriptor() .getAnnotation() .annotationType() ).isEqualTo( ConsistentDateParameters.class ); assertThat( constraint.getLocation().getTypeForValidatorResolution() ).isEqualTo( Object[].class ); } @Test public void configurationsHaveAnnotationSource() { BeanConfiguration<User> beanConfiguration = provider.getBeanConfiguration( User.class ); assertThat( beanConfiguration.getSource() ).isEqualTo( ConfigurationSource.ANNOTATION ); } @Test public void noGroupConversionOnField() throws Exception { //when BeanConfiguration<User> beanConfiguration = provider.getBeanConfiguration( User.class ); ConstrainedField field = findConstrainedField( beanConfiguration, User.class, "mail" ); //then assertThat( field.getCascadingMetaData().getGroupConversions() ).isEmpty(); } @Test public void singleGroupConversionOnField() throws Exception { //when BeanConfiguration<User> beanConfiguration = provider.getBeanConfiguration( User.class ); ConstrainedField field = findConstrainedField( beanConfiguration, User.class, "phone" ); //then Map<Class<?>, Class<?>> expected = newHashMap(); expected.put( Default.class, BasicNumber.class ); assertThat( field.getCascadingMetaData().getGroupConversions() ).isEqualTo( expected ); } @Test public void multipleGroupConversionsOnField() throws Exception { //when BeanConfiguration<User> beanConfiguration = provider.getBeanConfiguration( User.class ); ConstrainedField field = findConstrainedField( beanConfiguration, User.class, "address" ); //then Map<Class<?>, Class<?>> expected = newHashMap(); expected.put( Default.class, BasicPostal.class ); expected.put( Complete.class, FullPostal.class ); assertThat( field.getCascadingMetaData().getGroupConversions() ).isEqualTo( expected ); } @Test(expectedExceptions = ConstraintDeclarationException.class, expectedExceptionsMessageRegExp = "HV000124.*") public void multipleGroupConversionsOnFieldWithSameFromCauseException() { provider.getBeanConfiguration( User2.class ); } @Test public void noGroupConversionOnMethod() throws Exception { //when BeanConfiguration<User> beanConfiguration = provider.getBeanConfiguration( User.class ); ConstrainedExecutable method = findConstrainedMethod( beanConfiguration, User.class, "getMail1" ); //then assertThat( method.getCascadingMetaData().getGroupConversions() ).isEmpty(); } @Test public void singleGroupConversionOnMethod() throws Exception { //when BeanConfiguration<User> beanConfiguration = provider.getBeanConfiguration( User.class ); ConstrainedExecutable method = findConstrainedMethod( beanConfiguration, User.class, "getPhone1" ); //then Map<Class<?>, Class<?>> expected = newHashMap(); expected.put( Default.class, BasicNumber.class ); assertThat( method.getCascadingMetaData().getGroupConversions() ).isEqualTo( expected ); } @Test public void multipleGroupConversionsOnMethod() throws Exception { //when BeanConfiguration<User> beanConfiguration = provider.getBeanConfiguration( User.class ); ConstrainedExecutable method = findConstrainedMethod( beanConfiguration, User.class, "getAddress1" ); //then Map<Class<?>, Class<?>> expected = newHashMap(); expected.put( Default.class, BasicPostal.class ); expected.put( Complete.class, FullPostal.class ); assertThat( method.getCascadingMetaData().getGroupConversions() ).isEqualTo( expected ); } @Test public void noGroupConversionOnParameter() throws Exception { //when BeanConfiguration<User> beanConfiguration = provider.getBeanConfiguration( User.class ); ConstrainedExecutable method = findConstrainedMethod( beanConfiguration, User.class, "setMail1", String.class ); //then assertThat( method.getParameterMetaData( 0 ).getCascadingMetaData().getGroupConversions() ).isEmpty(); } @Test public void singleGroupConversionOnParameter() throws Exception { //when BeanConfiguration<User> beanConfiguration = provider.getBeanConfiguration( User.class ); ConstrainedExecutable method = findConstrainedMethod( beanConfiguration, User.class, "setPhone1", PhoneNumber.class ); //then Map<Class<?>, Class<?>> expected = newHashMap(); expected.put( Default.class, BasicNumber.class ); assertThat( method.getParameterMetaData( 0 ).getCascadingMetaData().getGroupConversions() ).isEqualTo( expected ); } @Test public void multipleGroupConversionsOnParameter() throws Exception { //when BeanConfiguration<User> beanConfiguration = provider.getBeanConfiguration( User.class ); ConstrainedExecutable method = findConstrainedMethod( beanConfiguration, User.class, "setAddress1", Address.class ); //then Map<Class<?>, Class<?>> expected = newHashMap(); expected.put( Default.class, BasicPostal.class ); expected.put( Complete.class, FullPostal.class ); assertThat( method.getParameterMetaData( 0 ).getCascadingMetaData().getGroupConversions() ).isEqualTo( expected ); } @Test(expectedExceptions = ConstraintDeclarationException.class, expectedExceptionsMessageRegExp = "HV000124.*") public void multipleGroupConversionsOnParameterWithSameFromCauseException() { provider.getBeanConfiguration( User4.class ); } @Test public void singleGroupConversionOnConstructor() throws Exception { //when BeanConfiguration<User> beanConfiguration = provider.getBeanConfiguration( User.class ); ConstrainedExecutable constructor = findConstrainedConstructor( beanConfiguration, User.class ); //then Map<Class<?>, Class<?>> expected = newHashMap(); expected.put( Default.class, BasicNumber.class ); assertThat( constructor.getCascadingMetaData().getGroupConversions() ).isEqualTo( expected ); } @Test public void multipleGroupConversionsOnConstructorParameter() throws Exception { //when BeanConfiguration<User> beanConfiguration = provider.getBeanConfiguration( User.class ); ConstrainedExecutable constructor = findConstrainedConstructor( beanConfiguration, User.class, Address.class ); //then Map<Class<?>, Class<?>> expected = newHashMap(); expected.put( Default.class, BasicPostal.class ); expected.put( Complete.class, FullPostal.class ); assertThat( constructor.getParameterMetaData( 0 ).getCascadingMetaData().getGroupConversions() ).isEqualTo( expected ); } @Test @TestForIssue(jiraKey = "HV-626") public void onlyLocallyDefinedConstraintsAreConsidered() { BeanConfiguration<Person> beanConfiguration = provider.getBeanConfiguration( Person.class ); ConstrainedType personType = findConstrainedType( beanConfiguration, Person.class ); assertThat( personType.getConstraints() ).hasSize( 1 ); ConstraintDescriptor<?> constraintInSubType = personType.getConstraints() .iterator() .next() .getDescriptor(); assertThat( constraintInSubType.getAnnotation().annotationType() ).isEqualTo( ScriptAssert.class ); } @Test(expectedExceptions = ConstraintDeclarationException.class, expectedExceptionsMessageRegExp = "HV000124.*") public void groupConversionWithSameFromInSingleAndListAnnotationCauseException() { provider.getBeanConfiguration( User3.class ); } private static class Foo { @NotNull public Foo(@NotNull String foo) { } } private static class Calendar { @ConsistentDateParameters public void createEvent(DateMidnight start, DateMidnight end) { } } public interface Complete extends Default { } public interface BasicPostal { } public interface FullPostal extends BasicPostal { } private interface BasicNumber { } private static class Address { } private static class PhoneNumber { } @SuppressWarnings("unused") private static class User { private final String mail = null; @Valid @ConvertGroup(from = Default.class, to = BasicNumber.class) private final PhoneNumber phone = null; @Valid @ConvertGroup.List({ @ConvertGroup(from = Default.class, to = BasicPostal.class), @ConvertGroup(from = Complete.class, to = FullPostal.class) }) private final Address address = null; @Valid @ConvertGroup(from = Default.class, to = BasicNumber.class) public User() { } public User( @Valid @ConvertGroup.List({ @ConvertGroup(from = Default.class, to = BasicPostal.class), @ConvertGroup(from = Complete.class, to = FullPostal.class) }) Address address) { } public String getMail1() { return null; } public void setMail1(String mail) { } @Valid @ConvertGroup(from = Default.class, to = BasicNumber.class) public PhoneNumber getPhone1() { return null; } public void setPhone1(@Valid @ConvertGroup(from = Default.class, to = BasicNumber.class) PhoneNumber phone) { } @Valid @ConvertGroup.List({ @ConvertGroup(from = Default.class, to = BasicPostal.class), @ConvertGroup(from = Complete.class, to = FullPostal.class) }) public Address getAddress1() { return null; } public void setAddress1( @Valid @ConvertGroup.List({ @ConvertGroup(from = Default.class, to = BasicPostal.class), @ConvertGroup(from = Complete.class, to = FullPostal.class) }) Address address) { } } private static class User2 { @Valid @ConvertGroup.List({ @ConvertGroup(from = Default.class, to = BasicPostal.class), @ConvertGroup(from = Default.class, to = FullPostal.class) }) private final Address address = null; } private static class User3 { @Valid @ConvertGroup(from = Default.class, to = BasicPostal.class) @ConvertGroup.List(@ConvertGroup(from = Default.class, to = FullPostal.class)) private final Address address = null; } private static class User4 { @SuppressWarnings("unused") public void setAddress( @Valid @ConvertGroup.List({ @ConvertGroup(from = Default.class, to = BasicPostal.class), @ConvertGroup(from = Default.class, to = FullPostal.class) }) Address address) { } } @ClassLevelConstraint("some script") private static class PersonBase { } @ScriptAssert(lang = "javascript", script = "some script") private static class Person extends PersonBase { } @Target({ TYPE }) @Retention(RUNTIME) @Constraint(validatedBy = { }) @Documented @Inherited public @interface ClassLevelConstraint { String message() default "{ClassLevelConstraint.message}"; Class<?>[] groups() default { }; Class<? extends Payload>[] payload() default { }; String value(); } }