/* * 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.cfg; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertCorrectConstraintViolationMessages; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertCorrectPropertyPaths; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; import static org.hibernate.validator.testutil.ConstraintViolationAssert.pathWith; import static org.hibernate.validator.testutils.ValidatorUtil.getConfiguration; import static org.hibernate.validator.testutils.ValidatorUtil.getValidatingProxy; import static org.testng.Assert.assertNull; import static org.testng.Assert.fail; import java.lang.annotation.ElementType; import java.util.Set; import javax.validation.ConstraintDeclarationException; import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException; import javax.validation.ValidationException; import javax.validation.Validator; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import javax.validation.groups.Default; import org.hibernate.validator.HibernateValidator; import org.hibernate.validator.HibernateValidatorConfiguration; import org.hibernate.validator.cfg.ConstraintMapping; import org.hibernate.validator.cfg.GenericConstraintDef; import org.hibernate.validator.cfg.defs.NotNullDef; import org.hibernate.validator.cfg.defs.SizeDef; import org.hibernate.validator.testutil.TestForIssue; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Tests the definition of method constraints with the programmatic API. * * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI */ public class MethodConstraintMappingTest { private HibernateValidatorConfiguration config; private GreetingService wrappedObject; @BeforeClass public void setUp() { wrappedObject = new GreetingServiceImpl(); } @BeforeMethod public void setUpTest() { config = getConfiguration( HibernateValidator.class ); } @Test public void testCascadingMethodReturnDefinition() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( GreetingService.class ) .method( "greet", User.class ) .returnValue() .valid(); config.addMapping( mapping ); GreetingService service = getValidatingProxy( wrappedObject, config.buildValidatorFactory().getValidator() ); try { service.greet( new User( "foo" ) ); fail( "Expected exception wasn't thrown." ); } catch (ConstraintViolationException e) { assertCorrectConstraintViolationMessages( e, "may not be null" ); assertCorrectPropertyPaths( e, "greet.<return value>.message" ); } } @Test public void testCascadingMethodReturnDefinitionWithGroupConversion() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( GreetingService.class ) .method( "greet", User.class ) .returnValue() .valid() .convertGroup( Default.class ).to( TestGroup.class ) .type( Message.class ) .property( "message", ElementType.FIELD ) .constraint( new NotNullDef() .message( "message must not be null" ) .groups( TestGroup.class ) ); config.addMapping( mapping ); GreetingService service = getValidatingProxy( wrappedObject, config.buildValidatorFactory().getValidator() ); try { service.greet( new User( "foo" ) ); fail( "Expected exception wasn't thrown." ); } catch (ConstraintViolationException e) { assertCorrectConstraintViolationMessages( e, "message must not be null" ); assertCorrectPropertyPaths( e, "greet.<return value>.message" ); } } @Test public void testCascadingMethodParameterDefinition() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( GreetingService.class ) .method( "greet", User.class ) .parameter( 0 ) .valid(); config.addMapping( mapping ); GreetingService service = getValidatingProxy( wrappedObject, config.buildValidatorFactory().getValidator() ); try { service.greet( new User( null ) ); fail( "Expected exception wasn't thrown." ); } catch (ConstraintViolationException e) { assertCorrectConstraintViolationMessages( e, "may not be null" ); assertCorrectPropertyPaths( e, "greet.user.name" ); } } @Test( expectedExceptions = ValidationException.class, expectedExceptionsMessageRegExp = "HV000135.*" ) public void testCascadingDefinitionOnMissingMethod() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( GreetingService.class ) .method( "greet" ) .returnValue() .valid(); config.buildValidatorFactory().getValidator(); } @Test( expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "HV000056.*" ) public void testCascadingDefinitionOnInvalidMethodParameter() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( GreetingService.class ) .method( "greet", User.class ) .parameter( 1 ) .valid(); config.buildValidatorFactory().getValidator(); } @Test public void testOverridingMethodMayDefineSameConstraintsAsOverriddenMethod() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( GreetingService.class ) .method( "greet", String.class ) .parameter( 0 ) .constraint( new SizeDef().min( 5 ).max( 10 ) ) .type( GreetingServiceImpl.class ) .method( "greet", String.class ) .parameter( 0 ) .constraint( new SizeDef().min( 5 ).max( 10 ) ); config.addMapping( mapping ); GreetingService service = getValidatingProxy( wrappedObject, config.buildValidatorFactory().getValidator() ); try { service.greet( "Hi" ); } catch (ConstraintViolationException e) { assertCorrectConstraintViolationMessages( e, "size must be between 5 and 10" ); assertCorrectPropertyPaths( e, "greet.string" ); } } @Test public void testParameterCanMarkedAsCascadedSeveralTimesInTheHierarchy() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( GreetingService.class ) .method( "greet", User.class ) .parameter( 0 ) .valid() .type( GreetingServiceImpl.class ) .method( "greet", User.class ) .parameter( 0 ) .valid(); config.addMapping( mapping ); GreetingService service = getValidatingProxy( wrappedObject, config.buildValidatorFactory().getValidator() ); try { service.greet( new User( null ) ); } catch (ConstraintViolationException e) { assertCorrectConstraintViolationMessages( e, "may not be null" ); assertCorrectPropertyPaths( e, "greet.user.name" ); } } @Test(expectedExceptions = ConstraintDeclarationException.class, expectedExceptionsMessageRegExp = "HV000151.*") public void testCascadingMethodParameterDefinedOnlyOnSubType() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( GreetingServiceImpl.class ) .method( "greet", User.class ) .parameter( 0 ) .valid(); config.addMapping( mapping ); GreetingService service = getValidatingProxy( wrappedObject, config.buildValidatorFactory().getValidator() ); service.greet( new User( null ) ); } @Test public void testParameterConstraint() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( GreetingService.class ) .method( "greet", User.class ) .parameter( 0 ) .constraint( new NotNullDef() ); config.addMapping( mapping ); try { GreetingService service = getValidatingProxy( wrappedObject, config.buildValidatorFactory().getValidator() ); service.greet( (User) null ); fail( "Expected exception wasn't thrown." ); } catch (ConstraintViolationException e) { assertCorrectConstraintViolationMessages( e, "may not be null" ); assertCorrectPropertyPaths( e, "greet.user" ); } } @Test public void testGenericParameterConstraint() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( GreetingService.class ) .method( "greet", String.class ) .parameter( 0 ) .constraint( new GenericConstraintDef<>( Size.class ).param( "min", 1 ).param( "max", 10 ) ); config.addMapping( mapping ); try { GreetingService service = getValidatingProxy( wrappedObject, config.buildValidatorFactory().getValidator() ); service.greet( "" ); fail( "Expected exception wasn't thrown." ); } catch (ConstraintViolationException e) { assertCorrectConstraintViolationMessages( e, "size must be between 1 and 10" ); assertCorrectPropertyPaths( e, "greet.string" ); } } @Test public void testMultipleParameterConstraintsAtSameParameter() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( GreetingService.class ) .method( "greet", String.class ) .parameter( 0 ) .constraint( new SizeDef().min( 1 ).max( 10 ) ) .constraint( new SizeDef().min( 2 ).max( 10 ) ); config.addMapping( mapping ); try { GreetingService service = getValidatingProxy( wrappedObject, config.buildValidatorFactory().getValidator() ); service.greet( "" ); fail( "Expected exception wasn't thrown." ); } catch (ConstraintViolationException e) { assertCorrectConstraintViolationMessages( e, "size must be between 1 and 10", "size must be between 2 and 10" ); assertCorrectPropertyPaths( e, "greet.string", "greet.string" ); } } @Test public void testMultipleParameterConstraintsAtDifferentParameters() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( GreetingService.class ) .method( "greet", String.class, String.class ) .parameter( 0 ) .constraint( new SizeDef().min( 1 ).max( 10 ) ) .parameter( 1 ) .constraint( new SizeDef().min( 1 ).max( 10 ) ); config.addMapping( mapping ); try { GreetingService service = getValidatingProxy( wrappedObject, config.buildValidatorFactory().getValidator() ); service.greet( "", "" ); fail( "Expected exception wasn't thrown." ); } catch (ConstraintViolationException e) { assertCorrectConstraintViolationMessages( e, "size must be between 1 and 10", "size must be between 1 and 10" ); assertCorrectPropertyPaths( e, "greet.string1", "greet.string2" ); } } @Test public void testProgrammaticAndAnnotationParameterConstraintsAddUp() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( GreetingService.class ) .method( "sayHello", String.class ) .parameter( 0 ) .constraint( new SizeDef().min( 2 ).max( 10 ) ); config.addMapping( mapping ); try { GreetingService service = getValidatingProxy( wrappedObject, config.buildValidatorFactory().getValidator() ); service.sayHello( "" ); fail( "Expected exception wasn't thrown." ); } catch (ConstraintViolationException e) { assertCorrectConstraintViolationMessages( e, "size must be between 1 and 10", "size must be between 2 and 10" ); assertCorrectPropertyPaths( e, "sayHello.name", "sayHello.name" ); } } @Test public void testConstraintAtCascadedParameter() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( GreetingService.class ) .method( "greet", User.class ) .parameter( 0 ) .constraint( new NotNullDef() ) .valid(); config.addMapping( mapping ); GreetingService service = getValidatingProxy( wrappedObject, config.buildValidatorFactory().getValidator() ); try { service.greet( (User) null ); fail( "Expected exception wasn't thrown." ); } catch (ConstraintViolationException e) { assertCorrectConstraintViolationMessages( e, "may not be null" ); assertCorrectPropertyPaths( e, "greet.user" ); } try { service.greet( new User( null ) ); fail( "Expected exception wasn't thrown." ); } catch (ConstraintViolationException e) { assertCorrectConstraintViolationMessages( e, "may not be null" ); assertCorrectPropertyPaths( e, "greet.user.name" ); } } @Test public void testReturnValueConstraint() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( GreetingService.class ) .method( "greet", String.class ) .returnValue() .constraint( new SizeDef().min( 1 ).max( 10 ) ); config.addMapping( mapping ); try { GreetingService service = getValidatingProxy( wrappedObject, config.buildValidatorFactory().getValidator() ); service.greet( "Hello" ); fail( "Expected exception wasn't thrown." ); } catch (ConstraintViolationException e) { assertCorrectConstraintViolationMessages( e, "size must be between 1 and 10" ); assertCorrectPropertyPaths( e, "greet.<return value>" ); } } @Test public void testMultipleReturnValueConstraints() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( GreetingService.class ) .method( "greet", String.class ) .returnValue() .constraint( new SizeDef().min( 1 ).max( 10 ) ) .constraint( new SizeDef().min( 2 ).max( 10 ) ); config.addMapping( mapping ); try { GreetingService service = getValidatingProxy( wrappedObject, config.buildValidatorFactory().getValidator() ); service.greet( "Hello" ); fail( "Expected exception wasn't thrown." ); } catch (ConstraintViolationException e) { assertCorrectConstraintViolationMessages( e, "size must be between 1 and 10", "size must be between 2 and 10" ); assertCorrectPropertyPaths( e, "greet.<return value>", "greet.<return value>" ); } } @Test public void testGenericReturnValueConstraint() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( GreetingService.class ) .method( "greet", String.class ) .returnValue() .constraint( new GenericConstraintDef<>( Size.class ).param( "min", 1 ).param( "max", 10 ) ); config.addMapping( mapping ); try { GreetingService service = getValidatingProxy( wrappedObject, config.buildValidatorFactory().getValidator() ); service.greet( "" ); fail( "Expected exception wasn't thrown." ); } catch (ConstraintViolationException e) { assertCorrectConstraintViolationMessages( e, "size must be between 1 and 10" ); assertCorrectPropertyPaths( e, "greet.<return value>" ); } } @Test public void testProgrammaticAndAnnotationReturnValueConstraintsAddUp() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( GreetingService.class ) .method( "greet", String.class, String.class ) .returnValue() .constraint( new SizeDef().min( 2 ).max( 10 ) ); config.addMapping( mapping ); try { GreetingService service = getValidatingProxy( wrappedObject, config.buildValidatorFactory().getValidator() ); service.greet( "Hello", "World" ); fail( "Expected exception wasn't thrown." ); } catch (ConstraintViolationException e) { assertCorrectConstraintViolationMessages( e, "size must be between 1 and 10", "size must be between 2 and 10" ); assertCorrectPropertyPaths( e, "greet.<return value>", "greet.<return value>" ); } } @Test public void constraintConfiguredOnPropertyIsEvaluatedByMethodValidation() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( GreetingService.class ) .property( "hello", ElementType.METHOD ) .constraint( new NotNullDef() ); config.addMapping( mapping ); try { GreetingService service = getValidatingProxy( wrappedObject, config.buildValidatorFactory().getValidator() ); service.getHello(); fail( "Expected exception wasn't thrown." ); } catch (ConstraintViolationException e) { assertCorrectConstraintViolationMessages( e, "may not be null" ); assertCorrectPropertyPaths( e, "getHello.<return value>" ); } } @Test public void cascadeConfiguredOnPropertyIsEvaluatedByMethodValidation() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( GreetingService.class ) .property( "user", ElementType.METHOD ) .valid(); config.addMapping( mapping ); try { GreetingService service = getValidatingProxy( wrappedObject, config.buildValidatorFactory().getValidator() ); service.getUser(); fail( "Expected exception wasn't thrown." ); } catch (ConstraintViolationException e) { assertCorrectConstraintViolationMessages( e, "may not be null" ); assertCorrectPropertyPaths( e, "getUser.<return value>.name" ); } } @Test public void constraintConfiguredOnFieldIsNotEvaluatedByMethodValidation() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( GreetingServiceImpl.class ) .property( "hello", ElementType.FIELD ) .constraint( new NotNullDef() ); config.addMapping( mapping ); GreetingService service = getValidatingProxy( wrappedObject, config.buildValidatorFactory().getValidator() ); assertNull( service.getHello() ); } @Test public void cascadeConfiguredOnFieldIsNotEvaluatedByMethodValidation() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( GreetingServiceImpl.class ) .property( "user", ElementType.FIELD ) .valid(); config.addMapping( mapping ); GreetingService service = getValidatingProxy( wrappedObject, config.buildValidatorFactory().getValidator() ); assertNull( service.getUser().getName() ); } @Test public void constraintConfiguredOnMethodIsEvaluatedByPropertyValidation() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( GreetingService.class ) .method( "getHello" ) .returnValue() .constraint( new NotNullDef() ); config.addMapping( mapping ); Validator validator = config.buildValidatorFactory().getValidator(); Set<ConstraintViolation<GreetingServiceImpl>> violations = validator.validateProperty( new GreetingServiceImpl(), "hello" ); assertCorrectConstraintViolationMessages( violations, "may not be null" ); assertCorrectPropertyPaths( violations, "hello" ); } @Test public void cascadeConfiguredOnMethodIsEvaluatedByPropertyValidation() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( GreetingService.class ) .method( "getUser" ) .returnValue() .valid(); config.addMapping( mapping ); Validator validator = config.buildValidatorFactory().getValidator(); Set<ConstraintViolation<GreetingServiceImpl>> violations = validator.validate( new GreetingServiceImpl() ); assertCorrectConstraintViolationMessages( violations, "may not be null" ); assertCorrectPropertyPaths( violations, "user.name" ); } @Test @TestForIssue(jiraKey = "HV-769") public void shouldDetermineConstraintTargetForReturnValueConstraint() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( GreetingService.class ) .method( "greet", String.class, String.class ) .returnValue() .constraint( new GenericConstraintDef<>( GenericAndCrossParameterConstraint.class ) ); config.addMapping( mapping ); Validator validator = config.buildValidatorFactory().getValidator(); GreetingService service = getValidatingProxy( wrappedObject, validator ); try { service.greet( null, null ); fail( "Expected exception wasn't thrown" ); } catch (ConstraintViolationException cve) { assertThat( cve.getConstraintViolations() ).containsOnlyPaths( pathWith().method( "greet" ).returnValue() ); } } @Test @TestForIssue(jiraKey = "HV-642") public void crossParameterConstraint() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( GreetingService.class ) .method( "greet", String.class, String.class ) .crossParameter() .constraint( new GenericConstraintDef<>( GenericAndCrossParameterConstraint.class ) ); config.addMapping( mapping ); try { GreetingService service = getValidatingProxy( wrappedObject, config.buildValidatorFactory().getValidator() ); service.greet( "", "" ); fail( "Expected exception wasn't thrown." ); } catch (ConstraintViolationException e) { assertCorrectConstraintViolationMessages( e, "default message" ); assertCorrectPropertyPaths( e, "greet.<cross-parameter>" ); } } @Test @TestForIssue(jiraKey = "HV-1220") public void crossParameterConstraintOnMethodReturningVoid() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( GreetingService.class ) .method( "sayNothing", String.class ) .crossParameter() .constraint( new GenericConstraintDef<GenericAndCrossParameterConstraint>( GenericAndCrossParameterConstraint.class ) ); config.addMapping( mapping ); try { GreetingService service = getValidatingProxy( wrappedObject, config.buildValidatorFactory().getValidator() ); service.sayNothing( "" ); fail( "Expected exception wasn't thrown." ); } catch (ConstraintViolationException e) { assertCorrectConstraintViolationMessages( e, "default message" ); assertCorrectPropertyPaths( e, "sayNothing.<cross-parameter>" ); } } private interface TestGroup { } public class User { @NotNull private final String name; public User(String name) { this.name = name; } public String getName() { return name; } } public class Message { @NotNull private final String message; public Message(String message) { this.message = message; } } public interface GreetingService { Message greet(User user); String greet(String string); @Size(min = 1, max = 10) String greet(String string1, String string2); Message sayHello(@Size(min = 1, max = 10) String name); Message getHello(); User getUser(); void sayNothing(String string1); } public class GreetingServiceImpl implements GreetingService { @SuppressWarnings("unused") private Message hello; @SuppressWarnings("unused") private User user; @Override public Message greet(User user) { return new Message( null ); } @Override public String greet(String string) { return ""; } @Override public String greet(String string1, String string2) { return ""; } @Override public Message sayHello(String name) { return null; } @Override public Message getHello() { return null; } @Override public User getUser() { return new User( null ); } @Override public void sayNothing(String string1) { // Nothing to do } } }