/*
* 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.engine.typeannotationconstraint;
import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap;
import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertCorrectConstraintTypes;
import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertCorrectConstraintViolationMessages;
import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertCorrectPropertyPaths;
import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNumberOfViolations;
import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat;
import static org.hibernate.validator.testutil.ConstraintViolationAssert.pathWith;
import static org.hibernate.validator.testutils.ValidatorUtil.getValidator;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javax.validation.ConstraintDeclarationException;
import javax.validation.ConstraintViolation;
import javax.validation.Valid;
import javax.validation.Validator;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.apache.log4j.Logger;
import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.internal.engine.path.NodeImpl;
import org.hibernate.validator.internal.util.CollectionHelper;
import org.hibernate.validator.testutil.MessageLoggedAssertionLogger;
import org.hibernate.validator.testutil.TestForIssue;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
/**
* Tests Java 8 type use annotations.
*
* @author Khalid Alqinyah
* @author Hardy Ferentschik
* @author Guillaume Smet
*/
public class TypeAnnotationConstraintTest {
private Validator validator;
@BeforeClass
public void setup() {
validator = getValidator();
}
// Validate value extractors are not called on null values
@Test
public void value_extractor_not_called_on_null_values() {
Set<ConstraintViolation<ModelWithNullValue>> constraintViolations = validator.validate( new ModelWithNullValue() );
assertCorrectPropertyPaths( constraintViolations, "nullValue" );
assertCorrectConstraintViolationMessages( constraintViolations, "container" );
assertCorrectConstraintTypes( constraintViolations, NotNull.class );
}
// List
@Test
public void field_constraint_provided_on_type_parameter_of_a_list_gets_validated() {
TypeWithList1 l = new TypeWithList1();
l.names = Arrays.asList( "First", "", null );
Set<ConstraintViolation<TypeWithList1>> constraintViolations = validator.validate( l );
assertCorrectPropertyPaths( constraintViolations, "names[1].<list element>", "names[2].<list element>", "names[2].<list element>" );
assertCorrectConstraintTypes(
constraintViolations,
NotBlank.class,
NotBlank.class,
NotNull.class
);
}
@Test
public void constraint_provided_on_custom_bean_used_as_list_parameter_gets_validated() {
TypeWithList3 l = new TypeWithList3();
l.bars = Arrays.asList( new Bar( 2 ), null );
Set<ConstraintViolation<TypeWithList3>> constraintViolations = validator.validate( l );
assertNumberOfViolations( constraintViolations, 2 );
assertCorrectPropertyPaths( constraintViolations, "bars[1].<list element>", "bars[0].number" );
assertCorrectConstraintTypes( constraintViolations, Min.class, NotNull.class );
}
@Test
@TestForIssue(jiraKey = "HV-1165")
public void constraints_specified_on_list_and_on_type_parameter_of_list_get_validated() {
TypeWithList4 l = new TypeWithList4();
l.names = Arrays.asList( "First", "", null );
Set<ConstraintViolation<TypeWithList4>> constraintViolations = validator.validate( l );
assertNumberOfViolations( constraintViolations, 2 );
assertCorrectPropertyPaths( constraintViolations, "names[1].<list element>", "names[2].<list element>" );
assertCorrectConstraintTypes( constraintViolations, NotBlank.class, NotBlank.class );
l = new TypeWithList4();
l.names = new ArrayList<>();
constraintViolations = validator.validate( l );
assertNumberOfViolations( constraintViolations, 1 );
assertCorrectPropertyPaths( constraintViolations, "names" );
assertCorrectConstraintTypes( constraintViolations, Size.class );
}
@Test
public void getter_constraint_provided_on_type_parameter_of_a_list_gets_validated() {
TypeWithList5 l = new TypeWithList5();
l.strings = Arrays.asList( "", "First", null );
Set<ConstraintViolation<TypeWithList5>> constraintViolations = validator.validate( l );
assertNumberOfViolations( constraintViolations, 3 );
assertCorrectPropertyPaths( constraintViolations, "strings[0].<list element>", "strings[2].<list element>", "strings[2].<list element>" );
assertCorrectConstraintTypes(
constraintViolations,
NotBlank.class,
NotBlank.class,
NotNull.class
);
}
@Test
public void return_value_constraint_provided_on_type_parameter_of_a_list_gets_validated() throws Exception {
Method method = TypeWithList6.class.getDeclaredMethod( "returnStrings" );
Set<ConstraintViolation<TypeWithList6>> constraintViolations = validator.forExecutables().validateReturnValue(
new TypeWithList6(),
method,
Arrays.asList( "First", "", null )
);
assertNumberOfViolations( constraintViolations, 3 );
assertCorrectPropertyPaths(
constraintViolations,
"returnStrings.<return value>[1].<list element>",
"returnStrings.<return value>[2].<list element>",
"returnStrings.<return value>[2].<list element>"
);
assertCorrectConstraintTypes(
constraintViolations,
NotBlank.class,
NotBlank.class,
NotNull.class
);
}
@Test
@TestForIssue(jiraKey = "HV-1121")
public void property_path_contains_index_information_for_list() {
TypeWithList1 l = new TypeWithList1();
l.names = Arrays.asList( "" );
Set<ConstraintViolation<TypeWithList1>> constraintViolations = validator.validate( l );
assertNumberOfViolations( constraintViolations, 1 );
assertThat( constraintViolations ).containsOnlyPaths(
pathWith()
.property( "names" )
.containerElement( NodeImpl.LIST_ELEMENT_NODE_NAME, true, null, 0, List.class, 0 )
);
}
@Test
public void method_parameter_constraint_provided_as_type_parameter_of_a_list_gets_validated() throws Exception {
Method method = TypeWithList7.class.getDeclaredMethod( "setValues", List.class );
Object[] values = new Object[] { Arrays.asList( "", "First", null ) };
Set<ConstraintViolation<TypeWithList7>> constraintViolations = validator.forExecutables().validateParameters(
new TypeWithList7(),
method,
values
);
assertNumberOfViolations( constraintViolations, 3 );
assertCorrectPropertyPaths(
constraintViolations,
"setValues.listParameter[0].<list element>",
"setValues.listParameter[2].<list element>",
"setValues.listParameter[2].<list element>"
);
assertCorrectConstraintTypes(
constraintViolations,
NotBlank.class,
NotBlank.class,
NotNull.class
);
}
@Test
public void constructor_parameter_constraint_provided_on_type_parameter_of_a_list_gets_validated() throws Exception {
Constructor<TypeWithList8> constructor = TypeWithList8.class.getDeclaredConstructor( List.class );
Object[] values = new Object[] { Arrays.asList( "", "First", null ) };
Set<ConstraintViolation<TypeWithList8>> constraintViolations = validator.forExecutables().validateConstructorParameters(
constructor,
values
);
assertNumberOfViolations( constraintViolations, 3 );
assertCorrectPropertyPaths(
constraintViolations,
"TypeWithList8.listParameter[0].<list element>",
"TypeWithList8.listParameter[2].<list element>",
"TypeWithList8.listParameter[2].<list element>"
);
assertCorrectConstraintTypes(
constraintViolations,
NotBlank.class,
NotBlank.class,
NotNull.class
);
}
// Set
@Test
@TestForIssue(jiraKey = "HV-1165")
public void field_constraint_provided_on_type_parameter_of_a_set_gets_validated() {
TypeWithSet1 s = new TypeWithSet1();
s.names = CollectionHelper.asSet( "First", "", null );
Set<ConstraintViolation<TypeWithSet1>> constraintViolations = validator.validate( s );
assertNumberOfViolations( constraintViolations, 3 );
assertCorrectPropertyPaths( constraintViolations, "names[].<iterable element>", "names[].<iterable element>", "names[].<iterable element>" );
assertCorrectConstraintTypes(
constraintViolations,
NotBlank.class,
NotBlank.class,
NotNull.class
);
}
@Test
@TestForIssue(jiraKey = "HV-1165")
public void constraint_provided_on_custom_bean_used_as_set_parameter_gets_validated() {
TypeWithSet3 s = new TypeWithSet3();
s.bars = CollectionHelper.asSet( new Bar( 2 ), null );
Set<ConstraintViolation<TypeWithSet3>> constraintViolations = validator.validate( s );
assertCorrectPropertyPaths( constraintViolations, "bars[].<iterable element>", "bars[].number" );
assertCorrectConstraintTypes( constraintViolations, Min.class, NotNull.class );
}
@Test
@TestForIssue(jiraKey = "HV-1165")
public void constraints_specified_on_set_and_on_type_parameter_of_set_get_validated() {
TypeWithSet4 s = new TypeWithSet4();
s.names = CollectionHelper.asSet( "First", "", null );
Set<ConstraintViolation<TypeWithSet4>> constraintViolations = validator.validate( s );
assertNumberOfViolations( constraintViolations, 2 );
assertCorrectPropertyPaths( constraintViolations, "names[].<iterable element>", "names[].<iterable element>" );
assertCorrectConstraintTypes( constraintViolations, NotBlank.class, NotBlank.class );
s = new TypeWithSet4();
s.names = new HashSet<>();
constraintViolations = validator.validate( s );
assertNumberOfViolations( constraintViolations, 1 );
assertCorrectPropertyPaths( constraintViolations, "names" );
assertCorrectConstraintTypes( constraintViolations, Size.class );
}
@Test
@TestForIssue(jiraKey = "HV-1165")
public void getter_constraint_provided_on_type_parameter_of_a_set_gets_validated() {
TypeWithSet5 s = new TypeWithSet5();
s.strings = new HashSet<>();
s.strings.add( "First" );
s.strings.add( "" );
s.strings.add( null );
Set<ConstraintViolation<TypeWithSet5>> constraintViolations = validator.validate( s );
assertNumberOfViolations( constraintViolations, 3 );
assertCorrectPropertyPaths( constraintViolations, "strings[].<iterable element>", "strings[].<iterable element>", "strings[].<iterable element>" );
assertCorrectConstraintTypes(
constraintViolations,
NotBlank.class,
NotBlank.class,
NotNull.class
);
}
@Test
@TestForIssue(jiraKey = "HV-1165")
public void return_value_constraint_provided_on_type_parameter_of_a_set_gets_validated() throws Exception {
Method method = TypeWithSet6.class.getDeclaredMethod( "returnStrings" );
Set<ConstraintViolation<TypeWithSet6>> constraintViolations = validator.forExecutables().validateReturnValue(
new TypeWithSet6(),
method,
CollectionHelper.asSet( "First", "", null )
);
assertNumberOfViolations( constraintViolations, 3 );
assertCorrectPropertyPaths(
constraintViolations,
"returnStrings.<return value>[].<iterable element>",
"returnStrings.<return value>[].<iterable element>",
"returnStrings.<return value>[].<iterable element>"
);
assertCorrectConstraintTypes(
constraintViolations,
NotBlank.class,
NotBlank.class,
NotNull.class
);
}
@Test
public void method_parameter_constraint_provided_as_type_parameter_of_a_set_gets_validated() throws Exception {
Method method = TypeWithSet7.class.getDeclaredMethod( "setValues", Set.class );
Object[] values = new Object[] { CollectionHelper.asSet( "", "First", null ) };
Set<ConstraintViolation<TypeWithSet7>> constraintViolations = validator.forExecutables().validateParameters(
new TypeWithSet7(),
method,
values
);
assertNumberOfViolations( constraintViolations, 3 );
assertCorrectPropertyPaths(
constraintViolations,
"setValues.setParameter[].<iterable element>",
"setValues.setParameter[].<iterable element>",
"setValues.setParameter[].<iterable element>"
);
assertCorrectConstraintTypes(
constraintViolations,
NotBlank.class,
NotBlank.class,
NotNull.class
);
}
@Test
public void constructor_parameter_constraint_provided_on_type_parameter_of_a_set_gets_validated() throws Exception {
Constructor<TypeWithSet8> constructor = TypeWithSet8.class.getDeclaredConstructor( Set.class );
Object[] values = new Object[] { CollectionHelper.asSet( "", "First", null ) };
Set<ConstraintViolation<TypeWithSet8>> constraintViolations = validator.forExecutables().validateConstructorParameters(
constructor,
values
);
assertNumberOfViolations( constraintViolations, 3 );
assertCorrectPropertyPaths(
constraintViolations,
"TypeWithSet8.setParameter[].<iterable element>",
"TypeWithSet8.setParameter[].<iterable element>",
"TypeWithSet8.setParameter[].<iterable element>"
);
assertCorrectConstraintTypes(
constraintViolations,
NotBlank.class,
NotBlank.class,
NotNull.class
);
}
// Map
@Test
public void constraint_specified_on_value_type_of_map_gets_validated() {
TypeWithMap1 m = new TypeWithMap1();
m.nameMap = newHashMap();
m.nameMap.put( "first", "Name 1" );
m.nameMap.put( "second", "" );
m.nameMap.put( "third", "Name 3" );
Set<ConstraintViolation<TypeWithMap1>> constraintViolations = validator.validate( m );
assertNumberOfViolations( constraintViolations, 1 );
assertCorrectPropertyPaths( constraintViolations, "nameMap[second].<map value>" );
assertCorrectConstraintTypes( constraintViolations, NotBlank.class );
}
@Test
public void constraint_provided_on_custom_bean_used_as_map_parameter_gets_validated() {
TypeWithMap3 m = new TypeWithMap3();
m.barMap = newHashMap();
m.barMap.put( "bar", new Bar( 2 ) );
m.barMap.put( "foo", null );
Set<ConstraintViolation<TypeWithMap3>> constraintViolations = validator.validate( m );
assertNumberOfViolations( constraintViolations, 2 );
assertCorrectPropertyPaths( constraintViolations, "barMap[foo].<map value>", "barMap[bar].number" );
assertCorrectConstraintTypes( constraintViolations, Min.class, NotNull.class );
}
@Test
@TestForIssue(jiraKey = "HV-1062")
public void constraints_specified_on_map_and_on_value_type_of_map_get_validated() {
TypeWithMap4 m = new TypeWithMap4();
m.nameMap = newHashMap();
m.nameMap.put( "first", "Name 1" );
m.nameMap.put( "second", "" );
m.nameMap.put( "third", "Name 3" );
Set<ConstraintViolation<TypeWithMap4>> constraintViolations = validator.validate( m );
assertNumberOfViolations( constraintViolations, 1 );
assertCorrectPropertyPaths( constraintViolations, "nameMap[second].<map value>" );
assertCorrectConstraintTypes( constraintViolations, NotBlank.class );
m = new TypeWithMap4();
constraintViolations = validator.validate( m );
assertNumberOfViolations( constraintViolations, 1 );
assertCorrectPropertyPaths( constraintViolations, "nameMap" );
assertCorrectConstraintTypes( constraintViolations, NotNull.class );
}
@Test
public void getter_constraint_provided_on_type_parameter_of_a_map_gets_validated() {
TypeWithMap5 m = new TypeWithMap5();
m.stringMap = newHashMap();
m.stringMap.put( "first", "" );
m.stringMap.put( "second", "Second" );
m.stringMap.put( "third", null );
Set<ConstraintViolation<TypeWithMap5>> constraintViolations = validator.validate( m );
assertNumberOfViolations( constraintViolations, 3 );
assertCorrectPropertyPaths( constraintViolations, "stringMap[first].<map value>", "stringMap[third].<map value>",
"stringMap[third].<map value>" );
assertCorrectConstraintTypes(
constraintViolations,
NotBlank.class,
NotBlank.class,
NotNull.class
);
}
@Test
public void return_value_constraint_provided_on_type_parameter_of_a_map_gets_validated() throws Exception {
Method method = TypeWithMap6.class.getDeclaredMethod( "returnStringMap" );
Map<String, String> parameter = newHashMap();
parameter.put( "first", "First" );
parameter.put( "second", "" );
parameter.put( "third", null );
Set<ConstraintViolation<TypeWithMap6>> constraintViolations = validator.forExecutables().validateReturnValue(
new TypeWithMap6(),
method,
parameter
);
assertNumberOfViolations( constraintViolations, 3 );
assertCorrectPropertyPaths(
constraintViolations,
"returnStringMap.<return value>[second].<map value>",
"returnStringMap.<return value>[third].<map value>",
"returnStringMap.<return value>[third].<map value>"
);
assertCorrectConstraintTypes(
constraintViolations,
NotBlank.class,
NotBlank.class,
NotNull.class
);
}
@Test
public void property_path_contains_index_information_for_map() {
TypeWithMap1 m = new TypeWithMap1();
m.nameMap = newHashMap();
m.nameMap.put( "first", "" );
Set<ConstraintViolation<TypeWithMap1>> constraintViolations = validator.validate( m );
assertThat( constraintViolations ).containsOnlyPaths(
pathWith()
.property( "nameMap" )
.containerElement( NodeImpl.MAP_VALUE_NODE_NAME, true, "first", null, Map.class, 1 )
);
}
@Test
public void method_parameter_constraint_provided_as_type_parameter_of_a_map_gets_validated() throws Exception {
Method method = TypeWithMap7.class.getDeclaredMethod( "setValues", Map.class );
Map<String, String> parameter = newHashMap();
parameter.put( "first", "First" );
parameter.put( "second", "" );
parameter.put( "third", null );
Object[] values = new Object[] { parameter };
Set<ConstraintViolation<TypeWithMap7>> constraintViolations = validator.forExecutables().validateParameters(
new TypeWithMap7(),
method,
values
);
assertNumberOfViolations( constraintViolations, 3 );
assertCorrectPropertyPaths(
constraintViolations,
"setValues.mapParameter[second].<map value>",
"setValues.mapParameter[third].<map value>",
"setValues.mapParameter[third].<map value>"
);
assertCorrectConstraintTypes(
constraintViolations,
NotBlank.class,
NotBlank.class,
NotNull.class
);
}
@Test
public void constructor_parameter_constraint_provided_on_type_parameter_of_a_map_gets_validated() throws Exception {
Constructor<TypeWithMap8> constructor = TypeWithMap8.class.getDeclaredConstructor( Map.class );
Map<String, String> parameter = newHashMap();
parameter.put( "first", "First" );
parameter.put( "second", "" );
parameter.put( "third", null );
Object[] values = new Object[] { parameter };
Set<ConstraintViolation<TypeWithMap8>> constraintViolations = validator.forExecutables().validateConstructorParameters(
constructor,
values
);
assertNumberOfViolations( constraintViolations, 3 );
assertCorrectPropertyPaths(
constraintViolations,
"TypeWithMap8.mapParameter[second].<map value>",
"TypeWithMap8.mapParameter[third].<map value>",
"TypeWithMap8.mapParameter[third].<map value>"
);
assertCorrectConstraintTypes(
constraintViolations,
NotBlank.class,
NotBlank.class,
NotNull.class
);
}
// Array
@Test
@TestForIssue(jiraKey = "HV-1175")
public void field_constraint_provided_on_type_parameter_of_an_array_gets_validated() {
TypeWithArray1 a = new TypeWithArray1();
a.names = new String[]{ "First", "", null };
Set<ConstraintViolation<TypeWithArray1>> constraintViolations = validator.validate( a );
assertNumberOfViolations( constraintViolations, 3 );
assertCorrectPropertyPaths( constraintViolations, "names[1].<iterable element>", "names[2].<iterable element>", "names[2].<iterable element>" );
assertCorrectConstraintTypes(
constraintViolations,
NotBlank.class,
NotBlank.class,
NotNull.class
);
}
@Test
@TestForIssue(jiraKey = "HV-1175")
public void constraint_provided_on_custom_bean_used_as_array_parameter_gets_validated() {
TypeWithArray3 a = new TypeWithArray3();
a.bars = new Bar[]{ new Bar( 2 ), null };
Set<ConstraintViolation<TypeWithArray3>> constraintViolations = validator.validate( a );
assertCorrectPropertyPaths( constraintViolations, "bars[1].<iterable element>", "bars[0].number" );
assertCorrectConstraintTypes( constraintViolations, Min.class, NotNull.class );
}
@Test
@TestForIssue(jiraKey = "HV-1175")
public void constraints_specified_on_array_and_on_type_parameter_of_array_get_validated() {
TypeWithArray4 a = new TypeWithArray4();
a.names = new String[]{ "First", "", null };
Set<ConstraintViolation<TypeWithArray4>> constraintViolations = validator.validate( a );
assertNumberOfViolations( constraintViolations, 2 );
assertCorrectPropertyPaths( constraintViolations, "names[1].<iterable element>", "names[2].<iterable element>" );
assertCorrectConstraintTypes( constraintViolations, NotBlank.class, NotBlank.class );
a = new TypeWithArray4();
a.names = new String[0];
constraintViolations = validator.validate( a );
assertNumberOfViolations( constraintViolations, 1 );
assertCorrectPropertyPaths( constraintViolations, "names" );
assertCorrectConstraintTypes( constraintViolations, Size.class );
}
@Test
@TestForIssue(jiraKey = "HV-1175")
public void getter_constraint_provided_on_type_parameter_of_an_array_gets_validated() {
TypeWithArray5 a = new TypeWithArray5();
a.strings = new String[]{ "", "First", null };
Set<ConstraintViolation<TypeWithArray5>> constraintViolations = validator.validate( a );
assertNumberOfViolations( constraintViolations, 3 );
assertCorrectPropertyPaths( constraintViolations, "strings[0].<iterable element>", "strings[2].<iterable element>", "strings[2].<iterable element>" );
assertCorrectConstraintTypes(
constraintViolations,
NotBlank.class,
NotBlank.class,
NotNull.class
);
}
@Test
@TestForIssue(jiraKey = "HV-1175")
public void return_value_constraint_provided_on_type_parameter_of_an_array_gets_validated() throws Exception {
Method method = TypeWithArray6.class.getDeclaredMethod( "returnStrings" );
Set<ConstraintViolation<TypeWithArray6>> constraintViolations = validator.forExecutables().validateReturnValue(
new TypeWithArray6(),
method,
new String[]{ "First", "", null }
);
assertNumberOfViolations( constraintViolations, 3 );
assertCorrectPropertyPaths(
constraintViolations,
"returnStrings.<return value>[1].<iterable element>",
"returnStrings.<return value>[2].<iterable element>",
"returnStrings.<return value>[2].<iterable element>"
);
assertCorrectConstraintTypes(
constraintViolations,
NotBlank.class,
NotBlank.class,
NotNull.class
);
}
@Test
@TestForIssue(jiraKey = "HV-1175")
public void property_path_contains_index_information_for_array() {
TypeWithArray1 a = new TypeWithArray1();
a.names = new String[]{ "" };
Set<ConstraintViolation<TypeWithArray1>> constraintViolations = validator.validate( a );
assertThat( constraintViolations ).containsOnlyPaths(
pathWith()
.property( "names" )
.containerElement( NodeImpl.ITERABLE_ELEMENT_NODE_NAME, true, null, 0, Object[].class, null )
);
}
@Test
@TestForIssue(jiraKey = "HV-1175")
public void method_parameter_constraint_provided_as_type_parameter_of_an_array_gets_validated() throws Exception {
Method method = TypeWithArray7.class.getDeclaredMethod( "setValues", String[].class );
Object[] values = new Object[] { new String[]{ "", "First", null } };
Set<ConstraintViolation<TypeWithArray7>> constraintViolations = validator.forExecutables().validateParameters(
new TypeWithArray7(),
method,
values
);
assertNumberOfViolations( constraintViolations, 3 );
assertCorrectPropertyPaths(
constraintViolations,
"setValues.arrayParameter[0].<iterable element>",
"setValues.arrayParameter[2].<iterable element>",
"setValues.arrayParameter[2].<iterable element>"
);
assertCorrectConstraintTypes(
constraintViolations,
NotBlank.class,
NotBlank.class,
NotNull.class
);
}
@Test
@TestForIssue(jiraKey = "HV-1175")
public void constructor_parameter_constraint_provided_on_type_parameter_of_an_array_gets_validated() throws Exception {
Constructor<TypeWithArray8> constructor = TypeWithArray8.class.getDeclaredConstructor( String[].class );
Object[] values = new Object[] { new String[]{ "", "First", null } };
Set<ConstraintViolation<TypeWithArray8>> constraintViolations = validator.forExecutables().validateConstructorParameters(
constructor,
values
);
assertNumberOfViolations( constraintViolations, 3 );
assertCorrectPropertyPaths(
constraintViolations,
"TypeWithArray8.arrayParameter[0].<iterable element>",
"TypeWithArray8.arrayParameter[2].<iterable element>",
"TypeWithArray8.arrayParameter[2].<iterable element>"
);
assertCorrectConstraintTypes(
constraintViolations,
NotBlank.class,
NotBlank.class,
NotNull.class
);
}
// Array of primitives
@Test
@TestForIssue(jiraKey = "HV-1175")
public void field_constraint_provided_on_type_parameter_of_an_array_of_primitives_gets_validated() {
TypeWithArrayOfPrimitives1 a = new TypeWithArrayOfPrimitives1();
a.ints = new int[]{ 6, 1 };
Set<ConstraintViolation<TypeWithArrayOfPrimitives1>> constraintViolations = validator.validate( a );
assertNumberOfViolations( constraintViolations, 1 );
assertCorrectPropertyPaths( constraintViolations, "ints[1].<iterable element>" );
assertCorrectConstraintTypes(
constraintViolations,
Min.class
);
}
// case 3 does not make sense here so we skip it
@Test
@TestForIssue(jiraKey = "HV-1175")
public void constraints_specified_on_array_and_on_type_parameter_of_array_of_primitives_get_validated() {
TypeWithArrayOfPrimitives4 a = new TypeWithArrayOfPrimitives4();
a.ints = new int[]{ 6, 1 };
Set<ConstraintViolation<TypeWithArrayOfPrimitives4>> constraintViolations = validator.validate( a );
assertNumberOfViolations( constraintViolations, 1 );
assertCorrectPropertyPaths( constraintViolations, "ints[1].<iterable element>" );
assertCorrectConstraintTypes( constraintViolations, Min.class );
a = new TypeWithArrayOfPrimitives4();
a.ints = new int[0];
constraintViolations = validator.validate( a );
assertNumberOfViolations( constraintViolations, 1 );
assertCorrectPropertyPaths( constraintViolations, "ints" );
assertCorrectConstraintTypes( constraintViolations, Size.class );
}
@Test
@TestForIssue(jiraKey = "HV-1175")
public void getter_constraint_provided_on_type_parameter_of_an_array_of_primitives_gets_validated() {
TypeWithArrayOfPrimitives5 a = new TypeWithArrayOfPrimitives5();
a.ints = new int[]{ 6, 1 };
Set<ConstraintViolation<TypeWithArrayOfPrimitives5>> constraintViolations = validator.validate( a );
assertNumberOfViolations( constraintViolations, 1 );
assertCorrectPropertyPaths( constraintViolations, "ints[1].<iterable element>" );
assertCorrectConstraintTypes(
constraintViolations,
Min.class
);
}
@Test
@TestForIssue(jiraKey = "HV-1175")
public void return_value_constraint_provided_on_type_parameter_of_an_array_of_primitives_gets_validated() throws Exception {
Method method = TypeWithArrayOfPrimitives6.class.getDeclaredMethod( "returnInts" );
Set<ConstraintViolation<TypeWithArrayOfPrimitives6>> constraintViolations = validator.forExecutables().validateReturnValue(
new TypeWithArrayOfPrimitives6(),
method,
new int[]{ 6, 1 }
);
assertNumberOfViolations( constraintViolations, 1 );
assertCorrectPropertyPaths(
constraintViolations,
"returnInts.<return value>[1].<iterable element>"
);
assertCorrectConstraintTypes(
constraintViolations,
Min.class
);
}
@Test
@TestForIssue(jiraKey = "HV-1175")
public void property_path_contains_index_information_for_array_of_primitives() {
TypeWithArrayOfPrimitives1 a = new TypeWithArrayOfPrimitives1();
a.ints = new int[]{ 1 };
Set<ConstraintViolation<TypeWithArrayOfPrimitives1>> constraintViolations = validator.validate( a );
assertThat( constraintViolations ).containsOnlyPaths(
pathWith()
.property( "ints" )
.containerElement( NodeImpl.ITERABLE_ELEMENT_NODE_NAME, true, null, 0, int[].class, null )
);
}
@Test
@TestForIssue(jiraKey = "HV-1175")
public void method_parameter_constraint_provided_as_type_parameter_of_an_array_of_primitives_gets_validated() throws Exception {
Method method = TypeWithArrayOfPrimitives7.class.getDeclaredMethod( "setValues", int[].class );
Object[] values = new Object[] { new int[]{ 6, 1 } };
Set<ConstraintViolation<TypeWithArrayOfPrimitives7>> constraintViolations = validator.forExecutables().validateParameters(
new TypeWithArrayOfPrimitives7(),
method,
values
);
assertNumberOfViolations( constraintViolations, 1 );
assertCorrectPropertyPaths(
constraintViolations,
"setValues.arrayParameter[1].<iterable element>"
);
assertCorrectConstraintTypes(
constraintViolations,
Min.class
);
}
@Test
@TestForIssue(jiraKey = "HV-1175")
public void constructor_parameter_constraint_provided_on_type_parameter_of_an_array_of_primitives_gets_validated() throws Exception {
Constructor<TypeWithArrayOfPrimitives8> constructor = TypeWithArrayOfPrimitives8.class.getDeclaredConstructor( int[].class );
Object[] values = new Object[] { new int[]{ 6, 1 } };
Set<ConstraintViolation<TypeWithArrayOfPrimitives8>> constraintViolations = validator.forExecutables().validateConstructorParameters(
constructor,
values
);
assertNumberOfViolations( constraintViolations, 1 );
assertCorrectPropertyPaths(
constraintViolations,
"TypeWithArrayOfPrimitives8.arrayParameter[1].<iterable element>"
);
assertCorrectConstraintTypes(
constraintViolations,
Min.class
);
}
// Optional
@Test
public void constraint_specified_on_type_parameter_of_optional_gets_validated() {
TypeWithOptional1 o = new TypeWithOptional1();
o.stringOptional = Optional.of( "" );
Set<ConstraintViolation<TypeWithOptional1>> constraintViolations = validator.validate( o );
assertNumberOfViolations( constraintViolations, 1 );
assertCorrectPropertyPaths( constraintViolations, "stringOptional" );
assertCorrectConstraintTypes( constraintViolations, NotBlank.class );
}
// Case 2 does not make sense here so we skip it
@Test
public void constraint_provided_on_custom_bean_used_as_optional_parameter_gets_validated() {
TypeWithOptional3 o = new TypeWithOptional3();
o.bar = Optional.empty();
Set<ConstraintViolation<TypeWithOptional3>> constraintViolations = validator.validate( o );
assertNumberOfViolations( constraintViolations, 1 );
assertCorrectPropertyPaths( constraintViolations, "bar" );
assertCorrectConstraintTypes( constraintViolations, NotNull.class );
}
@Test
public void constraints_specified_on_optional_and_on_type_parameter_of_optional_get_validated() {
TypeWithOptional4 o = new TypeWithOptional4();
o.stringOptional = Optional.of( "" );
Set<ConstraintViolation<TypeWithOptional4>> constraintViolations = validator.validate( o );
assertNumberOfViolations( constraintViolations, 1 );
assertCorrectPropertyPaths( constraintViolations, "stringOptional" );
assertCorrectConstraintTypes( constraintViolations, NotBlank.class );
o = new TypeWithOptional4();
o.stringOptional = null;
constraintViolations = validator.validate( o );
assertNumberOfViolations( constraintViolations, 1 );
assertCorrectPropertyPaths( constraintViolations, "stringOptional" );
assertCorrectConstraintTypes( constraintViolations, NotNull.class );
}
@Test
public void getter_constraint_provided_on_type_parameter_of_an_optional_gets_validated() {
TypeWithOptional5 o = new TypeWithOptional5();
o.stringOptional = Optional.of( "" );
Set<ConstraintViolation<TypeWithOptional5>> constraintViolations = validator.validate( o );
assertNumberOfViolations( constraintViolations, 1 );
assertCorrectPropertyPaths( constraintViolations, "stringOptional" );
assertCorrectConstraintTypes(
constraintViolations,
NotBlank.class
);
}
@Test
public void return_value_constraint_provided_on_type_parameter_of_an_optional_gets_validated() throws Exception {
Method method = TypeWithOptional6.class.getDeclaredMethod( "returnStringOptional" );
Set<ConstraintViolation<TypeWithOptional6>> constraintViolations = validator.forExecutables().validateReturnValue(
new TypeWithOptional6(),
method,
Optional.of( "" )
);
assertNumberOfViolations( constraintViolations, 1 );
assertCorrectPropertyPaths(
constraintViolations,
"returnStringOptional.<return value>"
);
assertCorrectConstraintTypes(
constraintViolations,
NotBlank.class
);
}
@Test
public void method_parameter_constraint_provided_as_type_parameter_of_an_optional_gets_validated()
throws Exception {
Method method = TypeWithOptional7.class.getDeclaredMethod( "setValues", Optional.class );
Object[] values = new Object[] { Optional.of( "" ) };
Set<ConstraintViolation<TypeWithOptional7>> constraintViolations = validator.forExecutables().validateParameters(
new TypeWithOptional7(),
method,
values
);
assertNumberOfViolations( constraintViolations, 1 );
assertCorrectPropertyPaths(
constraintViolations,
"setValues.optionalParameter"
);
assertCorrectConstraintTypes(
constraintViolations,
NotBlank.class
);
}
@Test
public void constructor_parameter_constraint_provided_on_type_parameter_of_an_optional_gets_validated()
throws Exception {
Constructor<TypeWithOptional8> constructor = TypeWithOptional8.class.getDeclaredConstructor( Optional.class );
Object[] values = new Object[] { Optional.of( "" ) };
Set<ConstraintViolation<TypeWithOptional8>> constraintViolations = validator.forExecutables().validateConstructorParameters(
constructor,
values
);
assertNumberOfViolations( constraintViolations, 1 );
assertCorrectPropertyPaths(
constraintViolations,
"TypeWithOptional8.optionalParameter"
);
assertCorrectConstraintTypes(
constraintViolations,
NotBlank.class
);
}
// No unwrapper available
@Test(expectedExceptions = ConstraintDeclarationException.class, expectedExceptionsMessageRegExp = "HV000197.*")
public void custom_generic_type_with_type_annotation_constraint_but_no_unwrapper_throws_exception() {
// No unwrapper is registered for Baz
BazHolder bazHolder = new BazHolder();
bazHolder.baz = null;
validator.validate( bazHolder );
}
@Test(enabled = false)
// TODO decide what to do with this one; an error seems reasonable now, as there is no unwrapper/value extractor
public void unsupported_use_of_type_constraints_logs_warning() {
Logger log4jRootLogger = Logger.getRootLogger();
MessageLoggedAssertionLogger assertingLogger = new MessageLoggedAssertionLogger( "HV000188" );
log4jRootLogger.addAppender( assertingLogger );
// No unwrapper exception shouldn't be thrown, type use constraints are ignored
FooHolder fooHolder = new FooHolder();
fooHolder.foo = null;
validator.validate( fooHolder );
assertingLogger.assertMessageLogged();
log4jRootLogger.removeAppender( assertingLogger );
}
// Validate value extractors are not called on null values
static class ModelWithNullValue {
private @NotNull(message = "container") Optional<@NotNull(message = "type") String> nullValue;
}
// List
static class TypeWithList1 {
List<@NotNull @NotBlank String> names;
}
static class TypeWithList2 {
List<@NotNull @NotBlank String> names;
}
static class TypeWithList3 {
@Valid
List<@NotNull Bar> bars;
}
static class TypeWithList4 {
@Valid
@Size(min = 1)
List<@NotBlank String> names;
}
static class TypeWithList5 {
List<String> strings;
@Valid
public List<@NotNull @NotBlank String> getStrings() {
return strings;
}
}
static class TypeWithList6 {
List<String> strings;
@Valid
public List<@NotNull @NotBlank String> returnStrings() {
return strings;
}
}
static class TypeWithList7 {
public void setValues(@Valid List<@NotNull @NotBlank String> listParameter) {
}
}
static class TypeWithList8 {
public TypeWithList8(@Valid List<@NotNull @NotBlank String> listParameter) {
}
}
// Set
static class TypeWithSet1 {
@Valid
Set<@NotNull @NotBlank String> names;
}
static class TypeWithSet2 {
Set<@NotNull @NotBlank String> names;
}
static class TypeWithSet3 {
@Valid
Set<@NotNull Bar> bars;
}
static class TypeWithSet4 {
@Valid
@Size(min = 1)
Set<@NotBlank String> names;
}
static class TypeWithSet5 {
Set<String> strings;
@Valid
public Set<@NotNull @NotBlank String> getStrings() {
return strings;
}
}
static class TypeWithSet6 {
Set<String> strings;
@Valid
public Set<@NotNull @NotBlank String> returnStrings() {
return strings;
}
}
static class TypeWithSet7 {
public void setValues(@Valid Set<@NotNull @NotBlank String> setParameter) {
}
}
static class TypeWithSet8 {
public TypeWithSet8(@Valid Set<@NotNull @NotBlank String> setParameter) {
}
}
// Map
static class TypeWithMap1 {
@Valid
Map<String, @NotBlank String> nameMap;
}
static class TypeWithMap2 {
Map<String, @NotNull @NotBlank String> nameMap;
}
static class TypeWithMap3 {
@Valid
Map<String, @NotNull Bar> barMap;
}
static class TypeWithMap4 {
@Valid
@NotNull
Map<String, @NotBlank String> nameMap;
}
static class TypeWithMap5 {
Map<String, String> stringMap;
@Valid
public Map<String, @NotNull @NotBlank String> getStringMap() {
return stringMap;
}
}
static class TypeWithMap6 {
Map<String, String> stringMap;
@Valid
public Map<String, @NotNull @NotBlank String> returnStringMap() {
return stringMap;
}
}
static class TypeWithMap7 {
public void setValues(@Valid Map<String, @NotNull @NotBlank String> mapParameter) {
}
}
static class TypeWithMap8 {
public TypeWithMap8(@Valid Map<String, @NotNull @NotBlank String> mapParameter) {
}
}
// Array of objects
static class TypeWithArray1 {
@Valid
String @NotNull @NotBlank [] names;
}
static class TypeWithArray2 {
String @NotNull @NotBlank [] names;
}
static class TypeWithArray3 {
@Valid
Bar @NotNull [] bars;
}
static class TypeWithArray4 {
@Valid
@Size(min = 1)
String @NotBlank [] names;
}
static class TypeWithArray5 {
String[] strings;
@Valid
public String @NotNull @NotBlank [] getStrings() {
return strings;
}
}
static class TypeWithArray6 {
String[] strings;
@Valid
public String @NotNull @NotBlank [] returnStrings() {
return strings;
}
}
static class TypeWithArray7 {
public void setValues(@Valid String @NotNull @NotBlank [] arrayParameter) {
}
}
static class TypeWithArray8 {
public TypeWithArray8(@Valid String @NotNull @NotBlank [] arrayParameter) {
}
}
// Array of primitives
static class TypeWithArrayOfPrimitives1 {
@Valid
int @Min(4) [] ints;
}
static class TypeWithArrayOfPrimitives2 {
int @Min(4) [] ints;
}
// case 3 does not make sense here so we skip it
static class TypeWithArrayOfPrimitives4 {
@Valid
@Size(min = 2)
int @Min(4) [] ints;
}
static class TypeWithArrayOfPrimitives5 {
int[] ints;
@Valid
public int @Min(4) [] getInts() {
return ints;
}
}
static class TypeWithArrayOfPrimitives6 {
int[] ints;
@Valid
public int @Min(4) [] returnInts() {
return ints;
}
}
static class TypeWithArrayOfPrimitives7 {
public void setValues(@Valid int @Min(4) [] arrayParameter) {
}
}
static class TypeWithArrayOfPrimitives8 {
public TypeWithArrayOfPrimitives8(@Valid int @Min(4) [] arrayParameter) {
}
}
// Optional
static class TypeWithOptional1 {
Optional<@NotBlank String> stringOptional;
}
static class TypeWithOptional3 {
Optional<@NotNull Bar> bar;
}
static class TypeWithOptional4 {
@NotNull
Optional<@NotBlank String> stringOptional;
}
static class TypeWithOptional5 {
Optional<String> stringOptional;
public Optional<@NotNull @NotBlank String> getStringOptional() {
return stringOptional;
}
}
static class TypeWithOptional6 {
Optional<String> stringOptional;
public Optional<@NotNull @NotBlank String> returnStringOptional() {
return stringOptional;
}
}
static class TypeWithOptional7 {
public void setValues(Optional<@NotBlank String> optionalParameter) {
}
}
static class TypeWithOptional8 {
public TypeWithOptional8(Optional<@NotBlank String> optionalParameter) {
}
}
// No wrapper available
static class Bar {
@Min(4)
Integer number;
public Bar(Integer number) {
this.number = number;
}
}
static class BazHolder {
Baz<@NotNull String> baz;
}
class Baz<T> {
}
class FooHolder {
Foo<@NotNull Integer, @NotBlank String> foo;
}
class Foo<T, V> {
}
}