Skip to content

Commit

Permalink
Add integration tests for all @each* constraints
Browse files Browse the repository at this point in the history
  • Loading branch information
jirutka committed Aug 1, 2015
1 parent b14c72d commit 2ed3b7e
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 23 deletions.
8 changes: 8 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,14 @@

<!--//// Test ////-->

<!-- Needed only for testing @EachSafeHtml -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.8.2</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* The MIT License
*
* Copyright 2013-2014 Jakub Jirutka <jakub@jirutka.cz>.
* Copyright 2013-2015 Jakub Jirutka <jakub@jirutka.cz>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
Expand Down Expand Up @@ -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


Expand Down Expand Up @@ -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'
Expand Down
27 changes: 23 additions & 4 deletions src/test/groovy/cz/jirutka/validator/collection/TestUtils.groovy
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* The MIT License
*
* Copyright 2013-2014 Jakub Jirutka <jakub@jirutka.cz>.
* Copyright 2013-2015 Jakub Jirutka <jakub@jirutka.cz>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
Expand All @@ -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) {
Expand All @@ -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)
}
}

Expand All @@ -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)
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* The MIT License
*
* Copyright 2013-2014 Jakub Jirutka <jakub@jirutka.cz>.
* Copyright 2013-2015 Jakub Jirutka <jakub@jirutka.cz>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
Expand All @@ -23,31 +23,46 @@
*/
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)
and:
def validateAs = constraint.getAnnotation(EachConstraint).validateAs()
constraint.simpleName == /Each${validateAs.simpleName}/
where:
constraint << CONSTRAINTS
constraint << eachConstraints
name = constraint.simpleName
expValidateAsName = name.replaceFirst('^Each', '') + '.class'
}
Expand All @@ -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 | [:] | ['<b>foo</b>'] | ['<x>WAT?</x>']
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)
}
}

0 comments on commit 2ed3b7e

Please sign in to comment.