diff --git a/pom.xml b/pom.xml index ba25028..10eb0fa 100644 --- a/pom.xml +++ b/pom.xml @@ -107,6 +107,14 @@ + + + org.jsoup + jsoup + 1.8.2 + test + + org.spockframework spock-core diff --git a/src/test/groovy/cz/jirutka/validator/collection/CommonEachValidatorIT.groovy b/src/test/groovy/cz/jirutka/validator/collection/CommonEachValidatorIT.groovy index 4f9e6b7..3c49925 100644 --- a/src/test/groovy/cz/jirutka/validator/collection/CommonEachValidatorIT.groovy +++ b/src/test/groovy/cz/jirutka/validator/collection/CommonEachValidatorIT.groovy @@ -1,7 +1,7 @@ /* * The MIT License * - * Copyright 2013-2014 Jakub Jirutka . + * Copyright 2013-2015 Jakub Jirutka . * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,17 +29,14 @@ import spock.lang.Issue import spock.lang.Specification import spock.lang.Unroll -import javax.validation.Validation - import static cz.jirutka.validator.collection.TestUtils.evalClassWithConstraint +import static cz.jirutka.validator.collection.TestUtils.validate @Unroll class CommonEachValidatorIT extends Specification { static HV_VERSION = HibernateValidatorInfo.getVersion() - def validator = Validation.buildDefaultValidatorFactory().getValidator() - def constraint = null @@ -141,10 +138,6 @@ class CommonEachValidatorIT extends Specification { //////// Helpers //////// - def validate(entity) { - validator.validate(entity) - } - void assertViolations(Object value, boolean shouldBeValid, Integer invalidIndex, String expectedMessage) { def entity = evalClassWithConstraint(constraint, value) def propertyPath = HV_VERSION >= 5_0_0 ? "valuesList[${invalidIndex}]" : 'valuesList' diff --git a/src/test/groovy/cz/jirutka/validator/collection/TestUtils.groovy b/src/test/groovy/cz/jirutka/validator/collection/TestUtils.groovy index 9695eb8..331652f 100644 --- a/src/test/groovy/cz/jirutka/validator/collection/TestUtils.groovy +++ b/src/test/groovy/cz/jirutka/validator/collection/TestUtils.groovy @@ -1,7 +1,7 @@ /* * The MIT License * - * Copyright 2013-2014 Jakub Jirutka . + * Copyright 2013-2015 Jakub Jirutka . * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -26,6 +26,8 @@ package cz.jirutka.validator.collection import org.hibernate.validator.internal.util.annotationfactory.AnnotationDescriptor import org.hibernate.validator.internal.util.annotationfactory.AnnotationFactory +import javax.validation.Validation + class TestUtils { static evalClassWithConstraint(annotationLine, List values) { @@ -44,12 +46,20 @@ class TestUtils { new GroovyClassLoader().parseClass(template).newInstance() } + static evalClassWithConstraint(Class annotationType, Map attributes, List values) { + evalClassWithConstraint(createAnnotationString(annotationType, attributes), values) + } + static toLiteral(value) { switch (value) { case null : return null - case Number : // go to next - case Boolean : return String.valueOf(value) - default : return "'${value.toString()}'" + case String : return "'${value.toString()}'" + case Long : return "${value}L" + case List : return '[' + value.collect { toLiteral(it) }.join(', ') + ']' + case Map : return '[' + value.collect { k, v -> "${k}: ${ toLiteral(v) }" }.join(',') + ']' + case Enum : return "${value.declaringClass.name}.${value.name()}" + case Date : return "new Date(${toLiteral(value.time)})" + default : return String.valueOf(value) } } @@ -61,4 +71,13 @@ class TestUtils { def desc = AnnotationDescriptor.getInstance(annotationType, attributes) AnnotationFactory.create(desc) } + + static createAnnotationString(Class annotationType, Map attributes) { + def attrsLine = attributes.collect { k, v -> "${k}=${toLiteral(v)}" }.join(', ') + "@${annotationType.name}(${attrsLine})" + } + + static validate(entity) { + Validation.buildDefaultValidatorFactory().validator.validate(entity) + } } diff --git a/src/test/groovy/cz/jirutka/validator/collection/constraints/EachAnnotationTest.groovy b/src/test/groovy/cz/jirutka/validator/collection/constraints/EachAnnotationTest.groovy index 2b4019f..2f7fd5d 100644 --- a/src/test/groovy/cz/jirutka/validator/collection/constraints/EachAnnotationTest.groovy +++ b/src/test/groovy/cz/jirutka/validator/collection/constraints/EachAnnotationTest.groovy @@ -1,7 +1,7 @@ /* * The MIT License * - * Copyright 2013-2014 Jakub Jirutka . + * Copyright 2013-2015 Jakub Jirutka . * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,23 +23,38 @@ */ package cz.jirutka.validator.collection.constraints +import cz.jirutka.validator.collection.internal.HibernateValidatorInfo +import org.hibernate.validator.constraints.EAN.Type import spock.lang.Specification import spock.lang.Unroll +import static cz.jirutka.validator.collection.TestUtils.evalClassWithConstraint +import static cz.jirutka.validator.collection.TestUtils.validate + @Unroll class EachAnnotationTest extends Specification { - static final CONSTRAINTS = [ - // JSR 303/349 + static final HV_VERSION = HibernateValidatorInfo.getVersion() + + // List of @Each* annotations for constraints defined in JSR 303/349. + static final CONSTRAINTS_JSR = [ EachAssertFalse, EachAssertTrue, EachDecimalMax, EachDecimalMin, EachDigits, EachFuture, EachMax, EachMin, EachNotNull, EachPast, - EachPattern, EachSize, - // Hibernate - EachCreditCardNumber, EachEAN, EachEmail, EachLength, EachLuhnCheck, - EachMod10Check, EachMod11Check, EachNotBlank, EachNotEmpty, - EachRange, EachSafeHtml, EachScriptAssert, EachURL + EachPattern, EachSize + ] + + // List of @Each* annotations for Hibernate constraints in HV 4.3.0. + static final CONSTRAINTS_HV = [ + EachCreditCardNumber, EachEmail, EachLength, EachNotBlank, + EachNotEmpty, EachRange, EachScriptAssert, EachURL + ] + + // List of @Each* annotations for Hibernate constraints in HV 5.1.0 and newer. + static final CONSTRAINTS_5_1_0 = [ + EachEAN, EachLuhnCheck, EachMod10Check, EachMod11Check, EachSafeHtml ] + def 'verify that @#name is annotated with @EachConstraint(validateAs = #expValidateAsName)'() { expect: constraint.isAnnotationPresent(EachConstraint) @@ -47,7 +62,7 @@ class EachAnnotationTest extends Specification { def validateAs = constraint.getAnnotation(EachConstraint).validateAs() constraint.simpleName == /Each${validateAs.simpleName}/ where: - constraint << CONSTRAINTS + constraint << eachConstraints name = constraint.simpleName expValidateAsName = name.replaceFirst('^Each', '') + '.class' } @@ -58,13 +73,72 @@ class EachAnnotationTest extends Specification { expect: attributesTypesSet(constraint).containsAll attributesTypesSet(validateAs) where: - constraint << CONSTRAINTS + constraint << eachConstraints + } + + def 'verify that @#constraint.simpleName basically works'() { + setup: + // skip test for constraints that doesn't work in the current HV version + if (!eachConstraints.contains(constraint)) return + and: + def validEntity = evalClassWithConstraint(constraint, attributes, validValue) + def invalidEntity = evalClassWithConstraint(constraint, attributes, invalidValue) + expect: + validate(validEntity).empty + ! validate(invalidEntity).empty + where: + constraint | attributes | validValue | invalidValue + EachAssertFalse | [:] | [false, false] | [false, true] + EachAssertTrue | [:] | [true, true] | [true, false] + //EachCreditCardNumber | [:] | [4417123456789113] | [4417123456789112] FIXME! + EachDecimalMax | [value: '3'] | [1, 2, 3] | [2, 3, 4] + EachDecimalMax | [value: '3'] | ['1', '2', '3'] | ['2', '3', '4'] + EachDecimalMin | [value: '3'] | [3, 4, 5] | [2, 3, 4] + EachDecimalMin | [value: '3'] | ['3', '4', '5'] | ['2', '3', '4'] + EachDigits | [integer: 2, fraction: 1] | [42.1, 13.2] | [42.1, 3.14] + EachDigits | [integer: 2, fraction: 1] | ['42.1', '13.2'] | ['42.1', '3.14'] + EachEAN | [type: Type.EAN8] | ['12345670'] | ['12345670', '123'] + EachEmail | [:] | ['x@y.z', 'a@b.c'] | ['x@y.z', 'ab.c'] + EachFuture | [:] | [futureDate()] | [pastDate()] + EachLength | [min: 1, max: 3] | ['a', 'foo'] | ['a', 'allons-y!'] + EachLuhnCheck | [:] | ['79927398713'] | ['79927398714'] + EachMax | [value: 3L] | [1, 2, 3] | [2, 3, 4] + EachMax | [value: 3L] | ['1', '2', '3'] | ['2', '3', '4'] + EachMin | [value: 3L] | [3, 4, 5] | [1, 2, 3] + EachMin | [value: 3L] | ['3', '4', '5'] | ['1', '2', '3'] + EachMod10Check | [:] | ['123'] | ['123', '124'] + EachMod11Check | [:] | ['124'] | ['124', '125'] + EachNotBlank | [:] | ['foo', 'bar'] | ['foo', ''] + //EachNotEmpty | [:] | ['x', 'yz'] | ['x', ''] FIXME! + EachNotNull | [:] | ['foo', 'bar'] | ['foo', null] + EachPast | [:] | [pastDate()] | [futureDate()] + EachPattern | [regexp: '[A-Z]+'] | ['FOO', 'BAR'] | ['FOO', '123'] + //EachRange | [min: 3L, max: 6L] | [3, 4, 5] | [6, 7, 8] FIXME! + EachSafeHtml | [:] | ['foo'] | ['WAT?'] + EachSize | [min: 1, max: 2] | ['a', 'xy'] | ['a', 'foo'] + EachSize | [min: 1, max: 2] | [[1], [2, 3]] | [[1], [2, 3, 4]] + EachSize | [min: 1, max: 2] | [[a: 1], [b: 2]] | [[a: 1], [:]] + EachURL | [protocol: 'https'] | ['https://nic.cz'] | ['http://nic.cz'] } + //////// Helpers //////// + + static getEachConstraints() { + CONSTRAINTS_JSR + CONSTRAINTS_HV + (HV_VERSION >= 5_1_0 ? CONSTRAINTS_5_1_0 : []) + } + def attributesTypesSet(Class annotation) { annotation.declaredMethods.collect(new HashSet()) { m -> [m.name, m.returnType] } } + + def futureDate() { + new Date().plus(1) + } + + def pastDate() { + new Date().minus(1) + } }