diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000000..8b9993ca3c --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,576 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ + +import groovy.transform.Field + +/* + * See https://github.com/hibernate/hibernate-jenkins-pipeline-helpers + */ +@Library('hibernate-jenkins-pipeline-helpers@1.3') +import org.hibernate.jenkins.pipeline.helpers.job.JobHelper +import org.hibernate.jenkins.pipeline.helpers.alternative.AlternativeMultiMap +import org.hibernate.jenkins.pipeline.helpers.version.Version + +/* + * WARNING: DO NOT IMPORT LOCAL LIBRARIES HERE. + * + * By local, I mean libraries whose files are in the same Git repository. + * + * The Jenkinsfile is protected and will not be executed if modified in pull requests from external users, + * but other local library files are not protected. + * A user could potentially craft a malicious PR by modifying a local library. + * + * See https://blog.grdryn.me/blog/jenkins-pipeline-trust.html for a full explanation, + * and a potential solution if we really need local libraries. + * Alternatively we might be able to host libraries in a separate GitHub repo and configure + * them in the GUI: see https://ci.hibernate.org/job/hibernate-validator/configure, "Pipeline Libraries". + */ + +/* + * See https://github.com/hibernate/hibernate-jenkins-pipeline-helpers for the documentation + * of the helpers library used in this Jenkinsfile, + * and for help writing Jenkinsfiles. + * + * ### Jenkins configuration + * + * #### Jenkins plugins + * + * This file requires the following plugins in particular: + * + * - everything required by the helpers library (see the org.hibernate.(...) imports for a link to its documentation) + * - https://plugins.jenkins.io/pipeline-github for the trigger on pull request comments + * + * #### Script approval + * + * If not already done, you will need to allow the following calls in /scriptApproval/: + * + * - everything required by the helpers library (see the org.hibernate.(...) imports for a link to its documentation) + * + * ### Integrations + * + * #### Nexus deployment + * + * This job includes two deployment modes: + * + * - A deployment of snapshot artifacts for every non-PR build on "primary" branches (master and maintenance branches). + * - A full release when starting the job with specific parameters. + * + * In the first case, the name of a Maven settings file must be provided in the job configuration file + * (see below). + * + * #### Gitter (optional) + * + * You need to enable the Jenkins integration in your Gitter room first: + * see https://gitlab.com/gitlab-org/gitter/webapp/blob/master/docs/integrations.md + * + * Then you will also need to configure *global* secret text credentials containing the Gitter webhook URL, + * and list the ID of these credentials in the job configuration file + * (see https://github.com/hibernate/hibernate-jenkins-pipeline-helpers#job-configuration-file). + * + * ### Job configuration + * + * This Jenkinsfile gets its configuration from four sources: + * branch name, environment variables, a configuration file, and credentials. + * All configuration is optional for the default build (and it should stay that way), + * but some features require some configuration. + * + * #### Branch name + * + * See the org.hibernate.(...) imports for a link to the helpers library documentation, + * which explains the basics. + * + * #### Environment variables + * + * No particular environment variables is necessary. + * + * #### Job configuration file + * + * See the org.hibernate.(...) imports for a link to the helpers library documentation, + * which explains the basic structure of this file and how to set it up. + * + * Below is the additional structure specific to this Jenkinsfile: + * + * deployment: + * maven: + * # String containing the ID of a Maven settings file registered using the config-file-provider Jenkins plugin. + * # The settings must provide credentials to the servers with ID + * # 'jboss-releases-repository' and 'jboss-snapshots-repository'. + * settingsId: ... + */ + +@Field final String MAVEN_TOOL = 'Apache Maven 3.6' + +// Default node pattern, to be used for resource-intensive stages. +// Should not include the master node. +@Field final String NODE_PATTERN_BASE = 'Slave' +// Quick-use node pattern, to be used for very light, quick, and environment-independent stages, +// such as sending a notification. May include the master node in particular. +@Field final String QUICK_USE_NODE_PATTERN = 'Master||Slave' + +@Field AlternativeMultiMap environments +@Field JobHelper helper + +@Field boolean enableDefaultBuild = false +@Field boolean enableDefaultBuildIT = false +@Field boolean performRelease = false +@Field boolean deploySnapshot = false + +@Field Version releaseVersion +@Field Version afterReleaseDevelopmentVersion + +this.helper = new JobHelper(this) + +helper.runWithNotification { + +stage('Configure') { + this.environments = AlternativeMultiMap.create([ + jdk: [ + // This should not include every JDK; in particular let's not care too much about EOL'd JDKs like version 9 + // See http://www.oracle.com/technetwork/java/javase/eol-135779.html + new JdkBuildEnvironment(version: '8', buildJdkTool: 'OracleJDK8 Latest', + condition: TestCondition.BEFORE_MERGE, + isDefault: true), + new JdkBuildEnvironment(version: '11', buildJdkTool: 'OpenJDK 11 Latest', + condition: TestCondition.AFTER_MERGE), + new JdkBuildEnvironment(version: '14', buildJdkTool: 'OpenJDK 14 Latest', + condition: TestCondition.AFTER_MERGE), + // Disabled because of https://bugs.openjdk.java.net/browse/JDK-8253566 + new JdkBuildEnvironment(version: '15', buildJdkTool: 'OpenJDK 15 Latest', + condition: TestCondition.ON_DEMAND), + new JdkBuildEnvironment(version: '16', buildJdkTool: 'OpenJDK 16 Latest', + condition: TestCondition.AFTER_MERGE) + ], + wildflyTck: [ + new WildFlyTckBuildEnvironment(javaVersion: '8', buildJdkTool: 'OracleJDK8 Latest', + condition: TestCondition.AFTER_MERGE), + new WildFlyTckBuildEnvironment(javaVersion: '11', buildJdkTool: 'OpenJDK 11 Latest', + condition: TestCondition.AFTER_MERGE) + ] + ]) + + helper.configure { + configurationNodePattern QUICK_USE_NODE_PATTERN + file 'job-configuration.yaml' + jdk { + defaultTool environments.content.jdk.default.buildJdkTool + } + maven { + defaultTool MAVEN_TOOL + producedArtifactPattern "org/hibernate/validator/*" + // Relocation artifacts + producedArtifactPattern "org/hibernate/hibernate-validator*" + } + } + + properties([ + buildDiscarder( + logRotator(daysToKeepStr: '90') + ), + pipelineTriggers( + // HSEARCH-3417: do not add snapshotDependencies() here, this was known to cause problems. + [ + issueCommentTrigger('.*test this please.*') + ] + + helper.generateUpstreamTriggers() + ), + helper.generateNotificationProperty(), + parameters([ + choice( + name: 'ENVIRONMENT_SET', + choices: """AUTOMATIC +DEFAULT +SUPPORTED +ALL""", + description: """A set of environments that must be checked. +'AUTOMATIC' picks a different set of environments based on the branch name and whether a release is being performed. +'DEFAULT' means a single build with the default environment expected by the Maven configuration, +while other options will trigger multiple Maven executions in different environments.""" + ), + string( + name: 'ENVIRONMENT_FILTER', + defaultValue: '', + trim: true, + description: """A regex filter to apply to the environments that must be checked. +If this parameter is non-empty, ENVIRONMENT_SET will be ignored and environments whose tag matches the given regex will be checked. +Some useful filters: 'default', 'jdk', 'jdk-10', 'eclipse'. +""" + ), + string( + name: 'RELEASE_VERSION', + defaultValue: '', + description: 'The version to be released, e.g. 5.10.0.Final. Setting this triggers a release.', + trim: true + ), + string( + name: 'RELEASE_DEVELOPMENT_VERSION', + defaultValue: '', + description: 'The next version to be used after the release, e.g. 5.10.0-SNAPSHOT.', + trim: true + ), + booleanParam( + name: 'RELEASE_DRY_RUN', + defaultValue: false, + description: 'If true, just simulate the release, without pushing any commits or tags, and without uploading any artifacts or documentation.' + ) + ]) + ]) + + performRelease = (params.RELEASE_VERSION ? true : false) + + if (!performRelease && helper.scmSource.branch.primary && !helper.scmSource.pullRequest) { + if (helper.configuration.file?.deployment?.maven?.settingsId) { + deploySnapshot = true + } + else { + echo "Missing deployment configuration in job configuration file - snapshot deployment will be skipped." + } + } + + if (params.ENVIRONMENT_FILTER) { + keepOnlyEnvironmentsMatchingFilter(params.ENVIRONMENT_FILTER) + } + else { + keepOnlyEnvironmentsFromSet(params.ENVIRONMENT_SET) + } + + // Determine whether ITs need to be run in the default build + enableDefaultBuildIT = environments.content.any { key, envSet -> + return envSet.enabled.contains(envSet.default) + } + // No need to re-test default environments separately, they will be tested as part of the default build if needed + environments.content.each { key, envSet -> + envSet.enabled.remove(envSet.default) + } + + if ( enableDefaultBuildIT && params.LEGACY_IT ) { + echo "Enabling legacy integration tests in default environment due to explicit request" + enableDefaultBuildLegacyIT = true + } + + enableDefaultBuild = + enableDefaultBuildIT || + environments.content.any { key, envSet -> envSet.enabled.any { buildEnv -> buildEnv.requiresDefaultBuildArtifacts() } } || + deploySnapshot + + echo """Branch: ${helper.scmSource.branch.name} +PR: ${helper.scmSource.pullRequest?.id} +params.ENVIRONMENT_SET: ${params.ENVIRONMENT_SET} +params.ENVIRONMENT_FILTER: ${params.ENVIRONMENT_FILTER} + +Resulting execution plan: + enableDefaultBuild=$enableDefaultBuild + enableDefaultBuildIT=$enableDefaultBuildIT + environments=${environments.enabledAsString} + performRelease=$performRelease + deploySnapshot=$deploySnapshot +""" + + if (performRelease) { + releaseVersion = Version.parseReleaseVersion(params.RELEASE_VERSION) + echo "Inferred version family for the release to '$releaseVersion.family'" + + // Check that all the necessary parameters are set + if (!params.RELEASE_DEVELOPMENT_VERSION) { + throw new IllegalArgumentException( + "Missing value for parameter RELEASE_DEVELOPMENT_VERSION." + + " This parameter must be set when RELEASE_VERSION is set." + ) + } + if (!params.RELEASE_DRY_RUN && !helper.configuration.file?.deployment?.maven?.settingsId) { + throw new IllegalArgumentException( + "Missing deployment configuration in job configuration file." + + " Cannot deploy artifacts during the release." + ) + } + } + + if (params.RELEASE_DEVELOPMENT_VERSION) { + afterReleaseDevelopmentVersion = Version.parseDevelopmentVersion(params.RELEASE_DEVELOPMENT_VERSION) + } +} + +stage('Default build') { + if (!enableDefaultBuild) { + echo 'Skipping default build and integration tests in the default environment' + helper.markStageSkipped() + return + } + runBuildOnNode { + helper.withMavenWorkspace(mavenSettingsConfig: deploySnapshot ? helper.configuration.file.deployment.maven.settingsId : null) { + sh """ \ + mvn clean \ + --fail-at-end \ + ${deploySnapshot ? "\ + deploy -DdeployAtEnd=true \ + " : "\ + install \ + "} \ + -Pdist \ + -Psigtest \ + -Pjqassistant \ + ${enableDefaultBuildIT ? '' : '-DskipITs'} \ + ${toTestJdkArg(environments.content.jdk.default)} \ + """ + + dir(helper.configuration.maven.localRepositoryPath) { + stash name:'default-build-result', includes:"org/hibernate/validator/**" + } + } + } +} + +stage('Non-default environments') { + Map parameters = [:] + + // Test with multiple JDKs + environments.content.jdk.enabled.each { JdkBuildEnvironment buildEnv -> + parameters.put(buildEnv.tag, { + runBuildOnNode { + helper.withMavenWorkspace(jdk: buildEnv.buildJdkTool) { + mavenNonDefaultBuild buildEnv, """ \ + clean install \ + """ + } + } + }) + } + + // Run the TCK with WildFly in multiple environments + environments.content.wildflyTck.enabled.each { WildFlyTckBuildEnvironment buildEnv -> + parameters.put(buildEnv.tag, { + runBuildOnNode { + helper.withMavenWorkspace(jdk: buildEnv.buildJdkTool) { + mavenNonDefaultBuild buildEnv, """ \ + clean install \ + -pl tck-runner \ + -Dincontainer \ + """ + } + } + }) + } + + if (parameters.isEmpty()) { + echo 'Skipping builds in non-default environments' + helper.markStageSkipped() + } + else { + parameters.put('failFast', false) + parallel(parameters) + } +} + +stage('Deploy') { + if (deploySnapshot) { + // TODO delay the release to this stage? This would require to use staging repositories for snapshots, not sure it's possible. + echo "Already deployed snapshot as part of the 'Default build' stage." + } + else if (performRelease) { + echo "Performing full release for version ${releaseVersion.toString()}" + runBuildOnNode { + helper.withMavenWorkspace(mavenSettingsConfig: params.RELEASE_DRY_RUN ? null : helper.configuration.file.deployment.maven.settingsId) { + sh "git clone https://github.com/hibernate/hibernate-noorm-release-scripts.git" + sh "bash -xe hibernate-noorm-release-scripts/prepare-release.sh validator ${releaseVersion.toString()}" + + String deployCommand = "bash -xe hibernate-noorm-release-scripts/deploy.sh validator" + if (!params.RELEASE_DRY_RUN) { + sh deployCommand + } else { + echo "WARNING: Not deploying. Would have executed:" + echo deployCommand + } + + String uploadDistributionCommand = "bash -xe hibernate-noorm-release-scripts/upload-distribution.sh validator ${releaseVersion.toString()}" + String uploadDocumentationCommand = "bash -xe hibernate-noorm-release-scripts/upload-documentation.sh validator ${releaseVersion.toString()} ${releaseVersion.family}" + if (!params.RELEASE_DRY_RUN) { + sh uploadDistributionCommand + sh uploadDocumentationCommand + } + else { + echo "WARNING: Not uploading anything. Would have executed:" + echo uploadDistributionCommand + echo uploadDocumentationCommand + } + + sh "bash -xe hibernate-noorm-release-scripts/update-version.sh validator ${afterReleaseDevelopmentVersion.toString()}" + sh "bash -xe hibernate-noorm-release-scripts/push-upstream.sh validator ${releaseVersion.toString()} ${helper.scmSource.branch.name} ${!params.RELEASE_DRY_RUN}" + } + } + } + else { + echo "Skipping deployment" + helper.markStageSkipped() + return + } +} + +} // End of helper.runWithNotification + +// Job-specific helpers + +enum TestCondition { + // For environments that are expected to work correctly + // before merging into master or maintenance branches. + // Tested on master and maintenance branches, on feature branches, and for PRs. + BEFORE_MERGE, + // For environments that are expected to work correctly, + // but are considered too resource-intensive to test them on pull requests. + // Tested on master and maintenance branches only. + // Not tested on feature branches or PRs. + AFTER_MERGE, + // For environments that may not work correctly. + // Only tested when explicitly requested through job parameters. + ON_DEMAND; + + // Work around JENKINS-33023 + // See https://issues.jenkins-ci.org/browse/JENKINS-33023?focusedCommentId=325738&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-325738 + public TestCondition() {} +} + +abstract class BuildEnvironment { + boolean isDefault = false + TestCondition condition + String toString() { getTag() } + abstract String getTag() + boolean isDefault() { isDefault } + boolean requiresDefaultBuildArtifacts() { true } +} + +class JdkBuildEnvironment extends BuildEnvironment { + String version + String buildJdkTool + String testJdkTool + @Override + String getTag() { "jdk-$version" } + @Override + boolean requiresDefaultBuildArtifacts() { false } +} + +class WildFlyTckBuildEnvironment extends BuildEnvironment { + String javaVersion + String buildJdkTool + @Override + String getTag() { "wildfly-tck-jdk$javaVersion" } + @Override + boolean requiresDefaultBuildArtifacts() { true } +} + +void keepOnlyEnvironmentsMatchingFilter(String regex) { + def pattern = /$regex/ + + boolean enableDefault = ('default' =~ pattern) + + environments.content.each { key, envSet -> + envSet.enabled.removeAll { buildEnv -> + !(buildEnv.tag =~ pattern) && !(envSet.default == buildEnv && enableDefault) + } + } +} + +void keepOnlyEnvironmentsFromSet(String environmentSetName) { + boolean enableDefaultEnv = false + boolean enableBeforeMergeEnvs = false + boolean enableAfterMergeEnvs = false + boolean enableOnDemandEnvs = false + switch (environmentSetName) { + case 'DEFAULT': + enableDefaultEnv = true + break + case 'SUPPORTED': + enableDefaultEnv = true + enableBeforeMergeEnvs = true + enableAfterMergeEnvs = true + break + case 'ALL': + enableDefaultEnv = true + enableBeforeMergeEnvs = true + enableAfterMergeEnvs = true + enableOptional = true + break + case 'AUTOMATIC': + if (params.RELEASE_VERSION) { + echo "Releasing version '$params.RELEASE_VERSION'." + } else if (helper.scmSource.pullRequest) { + echo "Building pull request '$helper.scmSource.pullRequest.id'" + enableDefaultEnv = true + enableBeforeMergeEnvs = true + } else if (helper.scmSource.branch.primary) { + echo "Building primary branch '$helper.scmSource.branch.name'" + enableDefaultEnv = true + enableBeforeMergeEnvs = true + enableAfterMergeEnvs = true + echo "Legacy integration tests are enabled for the default build environment." + enableDefaultBuildLegacyIT = true + } else { + echo "Building feature branch '$helper.scmSource.branch.name'" + enableDefaultEnv = true + enableBeforeMergeEnvs = true + } + break + default: + throw new IllegalArgumentException( + "Unknown value for param 'ENVIRONMENT_SET': '$environmentSetName'." + ) + } + + // Filter environments + + environments.content.each { key, envSet -> + envSet.enabled.removeAll { buildEnv -> ! ( + enableDefaultEnv && buildEnv.isDefault || + enableBeforeMergeEnvs && buildEnv.condition == TestCondition.BEFORE_MERGE || + enableAfterMergeEnvs && buildEnv.condition == TestCondition.AFTER_MERGE || + enableOnDemandEnvs && buildEnv.condition == TestCondition.ON_DEMAND ) } + } +} + +void runBuildOnNode(Closure body) { + runBuildOnNode( NODE_PATTERN_BASE, body ) +} + +void runBuildOnNode(String label, Closure body) { + node( label ) { + timeout( [time: 1, unit: 'HOURS'], body ) + } +} + +void mavenNonDefaultBuild(BuildEnvironment buildEnv, String args, String projectPath = '.') { + if ( buildEnv.requiresDefaultBuildArtifacts() ) { + dir(helper.configuration.maven.localRepositoryPath) { + unstash name:'default-build-result' + } + } + + // Add a suffix to tests to distinguish between different executions + // of the same test in different environments in reports + def testSuffix = buildEnv.tag.replaceAll('[^a-zA-Z0-9_\\-+]+', '_') + + dir(projectPath) { + sh """ \ + mvn -Dsurefire.environment=$testSuffix \ + ${toTestJdkArg(buildEnv)} \ + --fail-at-end \ + $args \ + """ + } +} + +String toTestJdkArg(BuildEnvironment buildEnv) { + String args = '' + + if ( ! (buildEnv instanceof JdkBuildEnvironment) ) { + return args; + } + + String testJdkTool = buildEnv.testJdkTool + if ( testJdkTool ) { + def testJdkToolPath = tool(name: testJdkTool, type: 'jdk') + args += " -Dsurefire.jvm.java_executable=$testJdkToolPath/bin/java" + } + + return args +} diff --git a/README.md b/README.md index ab0cabbb14..51cc66a76f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Hibernate Validator -*Version: 6.1.2.Final - 31-01-2020* +*Version: 6.2.0.Final - 23-12-2020* ## What is it? @@ -35,7 +35,7 @@ Logging will delegate any log requests to that provider. org.hibernate.validator hibernate-validator - 6.1.2.Final + 6.2.0.Final You also need an API and implementation of the Unified Expression Language. These dependencies must be explicitly added in an SE environment. @@ -54,7 +54,7 @@ extension by adding the following dependency: org.hibernate.validator hibernate-validator-cdi - 6.1.2.Final + 6.2.0.Final * _hibernate-validator-annotation-processor-<version>.jar_ is an optional jar which can be integrated with your build diff --git a/annotation-processor/pom.xml b/annotation-processor/pom.xml index 09b51bfb95..208e815806 100644 --- a/annotation-processor/pom.xml +++ b/annotation-processor/pom.xml @@ -11,7 +11,7 @@ org.hibernate.validator hibernate-validator-parent - 6.1.3-SNAPSHOT + 6.2.1-SNAPSHOT ../pom.xml diff --git a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/ConstraintHelper.java b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/ConstraintHelper.java index 9b5090f802..e0c6548fa9 100644 --- a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/ConstraintHelper.java +++ b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/ConstraintHelper.java @@ -302,9 +302,10 @@ public ConstraintHelper(Types typeUtils, AnnotationApiHelper annotationApiHelper registerAllowedTypesForBuiltInConstraint( HibernateValidatorTypes.REGON_CHECK, CharSequence.class ); registerAllowedTypesForBuiltInConstraint( HibernateValidatorTypes.NIP_CHECK, CharSequence.class ); registerAllowedTypesForBuiltInConstraint( HibernateValidatorTypes.PESEL_CHECK, CharSequence.class ); + registerAllowedTypesForBuiltInConstraint( HibernateValidatorTypes.INN_CHECK, CharSequence.class ); registerAllowedTypesForBuiltInConstraint( HibernateValidatorTypes.NOT_BLANK, CharSequence.class ); registerAllowedTypesForBuiltInConstraint( HibernateValidatorTypes.NOT_EMPTY, TYPES_SUPPORTED_BY_SIZE_AND_NOT_EMPTY_ANNOTATIONS ); - registerAllowedTypesForBuiltInConstraint( HibernateValidatorTypes.SAFE_HTML, CharSequence.class ); + registerAllowedTypesForBuiltInConstraint( HibernateValidatorTypes.NORMALIZED, CharSequence.class ); registerAllowedTypesForBuiltInConstraint( HibernateValidatorTypes.SCRIPT_ASSERT, Object.class ); registerAllowedTypesForBuiltInConstraint( HibernateValidatorTypes.UNIQUE_ELEMENTS, Collection.class ); registerAllowedTypesForBuiltInConstraint( HibernateValidatorTypes.URL, CharSequence.class ); diff --git a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/TypeNames.java b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/TypeNames.java index b2a1ef1c9c..94fc1a4cb4 100644 --- a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/TypeNames.java +++ b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/TypeNames.java @@ -78,9 +78,10 @@ public static class HibernateValidatorTypes { public static final String REGON_CHECK = ORG_HIBERNATE_VALIDATOR_CONSTRAINTS + ".pl.REGON"; public static final String NIP_CHECK = ORG_HIBERNATE_VALIDATOR_CONSTRAINTS + ".pl.NIP"; public static final String PESEL_CHECK = ORG_HIBERNATE_VALIDATOR_CONSTRAINTS + ".pl.PESEL"; + public static final String INN_CHECK = ORG_HIBERNATE_VALIDATOR_CONSTRAINTS + ".ru.INN"; + public static final String NORMALIZED = ORG_HIBERNATE_VALIDATOR_CONSTRAINTS + ".Normalized"; public static final String NOT_BLANK = ORG_HIBERNATE_VALIDATOR_CONSTRAINTS + ".NotBlank"; public static final String NOT_EMPTY = ORG_HIBERNATE_VALIDATOR_CONSTRAINTS + ".NotEmpty"; - public static final String SAFE_HTML = ORG_HIBERNATE_VALIDATOR_CONSTRAINTS + ".SafeHtml"; public static final String SCRIPT_ASSERT = ORG_HIBERNATE_VALIDATOR_CONSTRAINTS + ".ScriptAssert"; public static final String UNIQUE_ELEMENTS = ORG_HIBERNATE_VALIDATOR_CONSTRAINTS + ".UniqueElements"; public static final String URL = ORG_HIBERNATE_VALIDATOR_CONSTRAINTS + ".URL"; diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/ConstraintValidationProcessorTest.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/ConstraintValidationProcessorTest.java index bf6f60e705..8fdad4b55f 100644 --- a/annotation-processor/src/test/java/org/hibernate/validator/ap/ConstraintValidationProcessorTest.java +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/ConstraintValidationProcessorTest.java @@ -26,6 +26,7 @@ import org.hibernate.validator.ap.testmodel.ModelWithJava8DateTime; import org.hibernate.validator.ap.testmodel.ModelWithJavaMoneyTypes; import org.hibernate.validator.ap.testmodel.ModelWithJodaTypes; +import org.hibernate.validator.ap.testmodel.ModelWithNormalizedConstraints; import org.hibernate.validator.ap.testmodel.ModelWithUniqueElementsConstraints; import org.hibernate.validator.ap.testmodel.ModelWithoutConstraints; import org.hibernate.validator.ap.testmodel.MultipleConstraintsOfSameType; @@ -154,6 +155,8 @@ public void hibernateValidatorProvidedCustomConstraints() { assertFalse( compilationResult ); assertThatDiagnosticsMatch( diagnostics, + new DiagnosticExpectation( Kind.ERROR, 66 ), + new DiagnosticExpectation( Kind.ERROR, 67 ), new DiagnosticExpectation( Kind.ERROR, 68 ), new DiagnosticExpectation( Kind.ERROR, 69 ), new DiagnosticExpectation( Kind.ERROR, 70 ), @@ -170,10 +173,7 @@ public void hibernateValidatorProvidedCustomConstraints() { new DiagnosticExpectation( Kind.ERROR, 81 ), new DiagnosticExpectation( Kind.ERROR, 82 ), new DiagnosticExpectation( Kind.ERROR, 83 ), - new DiagnosticExpectation( Kind.ERROR, 84 ), - new DiagnosticExpectation( Kind.ERROR, 85 ), - new DiagnosticExpectation( Kind.ERROR, 86 ), - new DiagnosticExpectation( Kind.ERROR, 87 ) + new DiagnosticExpectation( Kind.ERROR, 84 ) ); } @@ -724,6 +724,25 @@ public void codePointLengthConstraints() { ); } + @Test + @TestForIssue(jiraKey = "HV-1780") + public void normalizedConstraints() { + File[] sourceFiles = new File[] { + compilerHelper.getSourceFile( ModelWithNormalizedConstraints.class ) + }; + + boolean compilationResult = + compilerHelper.compile( new ConstraintValidationProcessor(), diagnostics, false, true, sourceFiles ); + + assertFalse( compilationResult ); + assertThatDiagnosticsMatch( + diagnostics, + new DiagnosticExpectation( Kind.ERROR, 17 ), + new DiagnosticExpectation( Kind.ERROR, 20 ), + new DiagnosticExpectation( Kind.ERROR, 23 ) + ); + } + @Test public void isbnConstraints() { File[] sourceFiles = new File[] { diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/ModelWithNormalizedConstraints.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/ModelWithNormalizedConstraints.java new file mode 100644 index 0000000000..2551e5189d --- /dev/null +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/ModelWithNormalizedConstraints.java @@ -0,0 +1,28 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.ap.testmodel; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import org.hibernate.validator.constraints.Normalized; + +public class ModelWithNormalizedConstraints { + + @Normalized + public Collection collection; + + @Normalized + public List list; + + @Normalized + public Set set; + + @Normalized + public String string; +} diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/customconstraints/HibernateValidatorProvidedCustomConstraints.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/customconstraints/HibernateValidatorProvidedCustomConstraints.java index 587fecec97..75d3706624 100644 --- a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/customconstraints/HibernateValidatorProvidedCustomConstraints.java +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/customconstraints/HibernateValidatorProvidedCustomConstraints.java @@ -19,7 +19,6 @@ import org.hibernate.validator.constraints.NotBlank; import org.hibernate.validator.constraints.NotEmpty; import org.hibernate.validator.constraints.Range; -import org.hibernate.validator.constraints.SafeHtml; import org.hibernate.validator.constraints.ScriptAssert; import org.hibernate.validator.constraints.URL; import org.hibernate.validator.constraints.br.CNPJ; @@ -48,7 +47,6 @@ public class HibernateValidatorProvidedCustomConstraints { @NotBlank @NotEmpty @Range - @SafeHtml @URL @CNPJ @CPF @@ -75,7 +73,6 @@ public class HibernateValidatorProvidedCustomConstraints { @NotBlank @NotEmpty @Range - @SafeHtml @URL @CNPJ @CPF diff --git a/build-config/pom.xml b/build-config/pom.xml index e5bd46024b..a864860553 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -11,7 +11,7 @@ org.hibernate.validator hibernate-validator-parent - 6.1.3-SNAPSHOT + 6.2.1-SNAPSHOT ../pom.xml diff --git a/cdi/pom.xml b/cdi/pom.xml index 43e9a6e463..29f000ed0e 100644 --- a/cdi/pom.xml +++ b/cdi/pom.xml @@ -11,7 +11,7 @@ org.hibernate.validator hibernate-validator-parent - 6.1.3-SNAPSHOT + 6.2.1-SNAPSHOT ../pom.xml @@ -67,8 +67,8 @@ test - log4j - log4j + org.apache.logging.log4j + log4j-core test diff --git a/changelog.txt b/changelog.txt index c9510ff45d..7d24c8fdd3 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,6 +1,89 @@ Hibernate Validator Changelog ============================= +6.2.0.Final (23-12-2020) +------------------------- + +** Bug + * HV-1821 - engine - HV-1755 introduces NPE in org.hibernate.validator.internal.engine.ValidatorFactoryImpl constructor + +** New Feature + * HV-1822 - validators - Add Russian specific validator for russian taxpayer identification number + +6.2.0.CR1 (07-12-2020) +------------------------- + +** Improvement + * HV-1812 - engine - Avoid reflection-based metadata extraction for built-in value extractors + +** New Feature + * HV-1816 - engine - Disable Expression Language by default for custom constraint violations + +** Remove Feature + * HV-1790 - engine, validators - Remove the SafeHtml constraint + +** Task + * HV-1820 - engine - Upgrade JBoss Logging to 3.4.1.Final + * HV-1819 - engine - Upgrade Classmate to 1.5.1 + * HV-1817 - build - Upgrade to checkstyle 8.38 + +6.1.6.Final (30-09-2020) +------------------------- + +** Bug + * HV-1804 - translations - Fix Dutch translation for @Size constraint + * HV-1797 - validators - Validation on classes with a bidirectional relationship cause stack overflow on 6.1.x + * HV-1761 - engine - Interpolation of primitive arrays causes a ClassCastException + +** Improvement + * HV-1782 - translations - Remove trailing dot from @Max constraint German translation + +** New Feature + * HV-1780 - validators - Add @Normalized contraint that validates if text is normalized in a given form + +** Task + * HV-1803 - tests - Move the tests to log4j2 + * HV-1802 - tests - Update ByteBuddy test dependency to 1.10.16 + * HV-1795 - build - Remove link to JavaMoney javadoc + +6.1.5.Final (06-05-2020) +------------------------- + +** Bug + * HV-1774 - engine - Invalid parsing of EL expression can lead to invalid EL expressions considered valid + * HV-1772 - engine - Building multiple ValidatorFactory instances from a single Configuration violates specification for MessageInterpolator + * HV-1771 - translations - Fix DecimalMin message German translation + +** Improvement + * HV-1773 - documentation - Be more explicit about issues with EL injection and how to avoid them + +6.1.4.Final (17-04-2020) +------------------------- + +** Bug + * HV-1760 - validators - @Negative*/@Positive* do not support CharSequence as documented + +** Improvement + * HV-1770 - engine - Relax constraint consistency checking for built-in constraints + * HV-1769 - engine - Only create the default TraversableResolver if none has been specified + * HV-1767 - engine - Reduce the overhead of ConstraintHelper initialization in the predefined scope case + +6.1.3.Final (10-04-2020) +------------------------- + +** Bug + * HV-1758 - translations - Extra dollar sign in validation messages for ModCheck + +** Improvement + * HV-1763 - engine - Improve performances of ExecutableHelper#getSignature + +** New Feature + * HV-1755 - engine - Introduce the notion of BeanMetaDataClassNormalizer in the standard ValidatorFactory + +** Task + * HV-1765 - integration - Upgrade WildFly versions to 18.0.1.Final and 19.0.0.Final + * HV-1764 - tests - Upgrade Jackson test dependencies to 2.10.3 + 6.1.2.Final (31-01-2020) ------------------------- diff --git a/distribution/pom.xml b/distribution/pom.xml index abccaa3efc..bfbacce1c5 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -10,7 +10,7 @@ org.hibernate.validator hibernate-validator-parent - 6.1.3-SNAPSHOT + 6.2.1-SNAPSHOT ../pom.xml @@ -50,8 +50,8 @@ - log4j - log4j + org.apache.logging.log4j + log4j-core jakarta.persistence @@ -65,10 +65,6 @@ javax.money money-api - - org.jsoup - jsoup - com.thoughtworks.paranamer paranamer @@ -119,7 +115,7 @@ ${java.api-docs.base-url} ${javaee.api-docs.base-url} ${bv.api-docs.base-url} - ${javamoney.api-docs.base-url} + diff --git a/distribution/src/main/assembly/dist.xml b/distribution/src/main/assembly/dist.xml index 08abb1d44f..0f1fb92a9e 100644 --- a/distribution/src/main/assembly/dist.xml +++ b/distribution/src/main/assembly/dist.xml @@ -42,10 +42,9 @@ dist/lib/optional - log4j:log4j + org.apache.logging.log4j:log4j-core joda-time:joda-time jakarta.persistence:jakarta.persistence-api - org.jsoup:jsoup com.thoughtworks.paranamer:paranamer diff --git a/documentation/pom.xml b/documentation/pom.xml index 82469be62b..2352d7be6c 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -11,7 +11,7 @@ org.hibernate.validator hibernate-validator-parent - 6.1.3-SNAPSHOT + 6.2.1-SNAPSHOT ../pom.xml diff --git a/documentation/src/main/asciidoc/ch02.asciidoc b/documentation/src/main/asciidoc/ch02.asciidoc index 391d464e48..69d386bdf8 100644 --- a/documentation/src/main/asciidoc/ch02.asciidoc +++ b/documentation/src/main/asciidoc/ch02.asciidoc @@ -707,15 +707,14 @@ With one exception also these constraints apply to the field/property level, onl Supported data types::: `CharSequence` Hibernate metadata impact::: None +`@Normalized(form=)`:: Validates that the annotated character sequence is normalized according to the given `form`. + Supported data types::: `CharSequence` + Hibernate metadata impact::: None + `@Range(min=, max=)`:: Checks whether the annotated value lies between (inclusive) the specified minimum and maximum Supported data types::: `BigDecimal`, `BigInteger`, `CharSequence`, `byte`, `short`, `int`, `long` and the respective wrappers of the primitive types Hibernate metadata impact::: None -`@SafeHtml(whitelistType= , additionalTags=, additionalTagsWithAttributes=, baseURI=)`:: Checks whether the annotated value contains potentially malicious fragments such as `" ); - Set> violations = validator.validate( foo ); - assertThat( violations ).containsOnlyViolations( - violationOf( SafeHtml.class ) - ); - } - - private static class Foo { - - @SafeHtml - private final String html; - - public Foo(String html) { - this.html = html; - } - } -} diff --git a/engine/src/test/java/org/hibernate/validator/test/constraints/annotations/hv/ru/INNValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/constraints/annotations/hv/ru/INNValidatorTest.java new file mode 100644 index 0000000000..4d51429b3a --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/constraints/annotations/hv/ru/INNValidatorTest.java @@ -0,0 +1,53 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.constraints.annotations.hv.ru; + +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNoViolations; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; + +import java.util.Set; + +import org.hibernate.validator.constraints.ru.INN; +import org.hibernate.validator.test.constraints.annotations.AbstractConstrainedTest; + +import javax.validation.ConstraintViolation; +import org.testng.annotations.Test; + +/** + * Test to make sure that elements annotated with {@link INN} are validated. + * + * @author Artem Boiarshinov + */ +public class INNValidatorTest extends AbstractConstrainedTest { + + @Test + public void testINN() { + final Person person = new Person( "245885856020" ); + final Set> violations = validator.validate( person ); + assertNoViolations( violations ); + } + + @Test + public void testINNInvalid() { + final Person person = new Person( "0123456789" ); + final Set> violations = validator.validate( person ); + assertThat( violations ).containsOnlyViolations( + violationOf( INN.class ).withMessage( "invalid Russian taxpayer identification number (INN)" ) + ); + } + + private static class Person { + + @INN + private final String inn; + + public Person(String inn) { + this.inn = inn; + } + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/el/ConstraintExpressionLanguageFeatureLevelTest.java b/engine/src/test/java/org/hibernate/validator/test/el/ConstraintExpressionLanguageFeatureLevelTest.java new file mode 100644 index 0000000000..6370b842d8 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/el/ConstraintExpressionLanguageFeatureLevelTest.java @@ -0,0 +1,257 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.el; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.validation.Constraint; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import javax.validation.Payload; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; + +import org.hibernate.validator.HibernateValidator; +import org.hibernate.validator.messageinterpolation.ExpressionLanguageFeatureLevel; +import org.hibernate.validator.testutil.TestForIssue; +import org.hibernate.validator.testutil.ValidationXmlTestHelper; +import org.hibernate.validator.testutils.ValidatorUtil; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +/** + * @author Guillaume Smet + */ +@TestForIssue(jiraKey = "HV-1816") +public class ConstraintExpressionLanguageFeatureLevelTest { + + private static ValidationXmlTestHelper validationXmlTestHelper; + + @BeforeClass + public static void setupValidationXmlTestHelper() { + validationXmlTestHelper = new ValidationXmlTestHelper( ConstraintExpressionLanguageFeatureLevelTest.class ); + } + + @Test + public void default_expression_language_feature_level() { + ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .buildValidatorFactory(); + + Validator validator = validatorFactory.getValidator(); + + assertThat( validator.validate( new VariablesBean( "value" ) ) ) + .containsOnlyViolations( violationOf( VariablesConstraint.class ).withMessage( "Variable: value" ) ); + assertThat( validator.validate( new BeanPropertiesBean( "value" ) ) ) + .containsOnlyViolations( violationOf( BeanPropertiesConstraint.class ).withMessage( "Bean property: 118" ) ); + assertThat( validator.validate( new BeanMethodsBean() ) ) + .containsOnlyViolations( violationOf( BeanMethodsConstraint.class ).withMessage( "Method execution: ${'aaaa'.substring(0, 1)}" ) ); + } + + @Test + public void none_expression_language_feature_level() { + ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .constraintExpressionLanguageFeatureLevel( ExpressionLanguageFeatureLevel.NONE ) + .buildValidatorFactory(); + + Validator validator = validatorFactory.getValidator(); + + assertThat( validator.validate( new VariablesBean( "value" ) ) ) + .containsOnlyViolations( violationOf( VariablesConstraint.class ).withMessage( "Variable: ${validatedValue}" ) ); + assertThat( validator.validate( new BeanPropertiesBean( "value" ) ) ) + .containsOnlyViolations( violationOf( BeanPropertiesConstraint.class ).withMessage( "Bean property: ${validatedValue.bytes[0]}" ) ); + assertThat( validator.validate( new BeanMethodsBean() ) ) + .containsOnlyViolations( violationOf( BeanMethodsConstraint.class ).withMessage( "Method execution: ${'aaaa'.substring(0, 1)}" ) ); + } + + @Test + public void variables_expression_language_feature_level() { + ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .constraintExpressionLanguageFeatureLevel( ExpressionLanguageFeatureLevel.VARIABLES ) + .buildValidatorFactory(); + + Validator validator = validatorFactory.getValidator(); + + assertThat( validator.validate( new VariablesBean( "value" ) ) ) + .containsOnlyViolations( violationOf( VariablesConstraint.class ).withMessage( "Variable: value" ) ); + assertThat( validator.validate( new BeanPropertiesBean( "value" ) ) ) + .containsOnlyViolations( violationOf( BeanPropertiesConstraint.class ).withMessage( "Bean property: ${validatedValue.bytes[0]}" ) ); + assertThat( validator.validate( new BeanMethodsBean() ) ) + .containsOnlyViolations( violationOf( BeanMethodsConstraint.class ).withMessage( "Method execution: ${'aaaa'.substring(0, 1)}" ) ); + } + + @Test + public void bean_properties_expression_language_feature_level() { + ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .constraintExpressionLanguageFeatureLevel( ExpressionLanguageFeatureLevel.BEAN_PROPERTIES ) + .buildValidatorFactory(); + + Validator validator = validatorFactory.getValidator(); + + assertThat( validator.validate( new VariablesBean( "value" ) ) ) + .containsOnlyViolations( violationOf( VariablesConstraint.class ).withMessage( "Variable: value" ) ); + assertThat( validator.validate( new BeanPropertiesBean( "value" ) ) ) + .containsOnlyViolations( violationOf( BeanPropertiesConstraint.class ).withMessage( "Bean property: 118" ) ); + assertThat( validator.validate( new BeanMethodsBean() ) ) + .containsOnlyViolations( violationOf( BeanMethodsConstraint.class ).withMessage( "Method execution: ${'aaaa'.substring(0, 1)}" ) ); + } + + @Test + public void bean_methods_expression_language_feature_level() { + ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .constraintExpressionLanguageFeatureLevel( ExpressionLanguageFeatureLevel.BEAN_METHODS ) + .buildValidatorFactory(); + + Validator validator = validatorFactory.getValidator(); + + assertThat( validator.validate( new VariablesBean( "value" ) ) ) + .containsOnlyViolations( violationOf( VariablesConstraint.class ).withMessage( "Variable: value" ) ); + assertThat( validator.validate( new BeanPropertiesBean( "value" ) ) ) + .containsOnlyViolations( violationOf( BeanPropertiesConstraint.class ).withMessage( "Bean property: 118" ) ); + assertThat( validator.validate( new BeanMethodsBean() ) ) + .containsOnlyViolations( violationOf( BeanMethodsConstraint.class ).withMessage( "Method execution: a" ) ); + } + + @Test + public void property_default_value() { + validationXmlTestHelper.runWithCustomValidationXml( + "validation-constraints-default.xml", new Runnable() { + + @Override + public void run() { + Validator validator = ValidatorUtil.getValidator(); + + assertThat( validator.validate( new VariablesBean( "value" ) ) ) + .containsOnlyViolations( violationOf( VariablesConstraint.class ).withMessage( "Variable: value" ) ); + assertThat( validator.validate( new BeanPropertiesBean( "value" ) ) ) + .containsOnlyViolations( violationOf( BeanPropertiesConstraint.class ).withMessage( "Bean property: 118" ) ); + assertThat( validator.validate( new BeanMethodsBean() ) ) + .containsOnlyViolations( violationOf( BeanMethodsConstraint.class ).withMessage( "Method execution: ${'aaaa'.substring(0, 1)}" ) ); + } + } ); + } + + @Test + public void property_bean_methods() { + validationXmlTestHelper.runWithCustomValidationXml( + "validation-constraints-bean-methods.xml", new Runnable() { + + @Override + public void run() { + Validator validator = ValidatorUtil.getValidator(); + + assertThat( validator.validate( new VariablesBean( "value" ) ) ) + .containsOnlyViolations( violationOf( VariablesConstraint.class ).withMessage( "Variable: value" ) ); + assertThat( validator.validate( new BeanPropertiesBean( "value" ) ) ) + .containsOnlyViolations( violationOf( BeanPropertiesConstraint.class ).withMessage( "Bean property: 118" ) ); + assertThat( validator.validate( new BeanMethodsBean() ) ) + .containsOnlyViolations( violationOf( BeanMethodsConstraint.class ).withMessage( "Method execution: a" ) ); + } + } ); + } + + @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE }) + @Retention(RUNTIME) + @Documented + @Constraint(validatedBy = { VariablesStringValidator.class }) + private @interface VariablesConstraint { + String message() default "Variable: ${validatedValue}"; + + Class[] groups() default { }; + + Class[] payload() default { }; + } + + public static class VariablesStringValidator implements ConstraintValidator { + + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + return false; + } + } + + public static class VariablesBean { + + public VariablesBean(String value) { + this.value = value; + } + + @VariablesConstraint + public String value; + } + + @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE }) + @Retention(RUNTIME) + @Documented + @Constraint(validatedBy = { BeanPropertiesConstraintStringValidator.class }) + private @interface BeanPropertiesConstraint { + String message() default "Bean property: ${validatedValue.bytes[0]}"; + + Class[] groups() default { }; + + Class[] payload() default { }; + } + + public static class BeanPropertiesConstraintStringValidator implements ConstraintValidator { + + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + return false; + } + } + + public static class BeanPropertiesBean { + + public BeanPropertiesBean(String value) { + this.value = value; + } + + @BeanPropertiesConstraint + public String value; + } + + @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE }) + @Retention(RUNTIME) + @Documented + @Constraint(validatedBy = { BeanMethodsConstraintStringValidator.class }) + private @interface BeanMethodsConstraint { + String message() default "Method execution: ${'aaaa'.substring(0, 1)}"; + + Class[] groups() default { }; + + Class[] payload() default { }; + } + + public static class BeanMethodsConstraintStringValidator implements ConstraintValidator { + + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + return false; + } + } + + public static class BeanMethodsBean { + + @BeanMethodsConstraint + public String value; + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/el/CustomViolationExpressionLanguageFeatureLevelTest.java b/engine/src/test/java/org/hibernate/validator/test/el/CustomViolationExpressionLanguageFeatureLevelTest.java new file mode 100644 index 0000000000..01a49f89ac --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/el/CustomViolationExpressionLanguageFeatureLevelTest.java @@ -0,0 +1,388 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.el; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; +import static org.testng.Assert.assertTrue; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.validation.Constraint; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import javax.validation.Payload; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.core.Logger; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.test.appender.ListAppender; +import org.hibernate.validator.HibernateValidator; +import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorContext; +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorContextImpl; +import org.hibernate.validator.messageinterpolation.ExpressionLanguageFeatureLevel; +import org.hibernate.validator.testutil.TestForIssue; +import org.hibernate.validator.testutil.ValidationXmlTestHelper; +import org.hibernate.validator.testutils.ValidatorUtil; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +/** + * @author Guillaume Smet + */ +@TestForIssue(jiraKey = "HV-1816") +public class CustomViolationExpressionLanguageFeatureLevelTest { + + private static ValidationXmlTestHelper validationXmlTestHelper; + + private ListAppender constraintValidatorContextImplLoglistAppender; + + + @BeforeClass + public static void setupValidationXmlTestHelper() { + validationXmlTestHelper = new ValidationXmlTestHelper( ConstraintExpressionLanguageFeatureLevelTest.class ); + } + + @BeforeTest + public void setUp() { + LoggerContext context = LoggerContext.getContext( false ); + Logger logger = context.getLogger( ConstraintValidatorContextImpl.class.getName() ); + constraintValidatorContextImplLoglistAppender = (ListAppender) logger.getAppenders().get( "List" ); + constraintValidatorContextImplLoglistAppender.clear(); + } + + @AfterTest + public void tearDown() { + constraintValidatorContextImplLoglistAppender.clear(); + } + + @Test + public void default_behavior() { + ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .buildValidatorFactory(); + + Validator validator = validatorFactory.getValidator(); + + assertThat( validator.validate( new DefaultLevelBean() ) ) + .containsOnlyViolations( violationOf( DefaultLevelConstraint.class ).withMessage( "Variable: ${validatedValue}" ), + violationOf( DefaultLevelConstraint.class ).withMessage( "Bean property: ${validatedValue.bytes[0]}" ), + violationOf( DefaultLevelConstraint.class ).withMessage( "Method execution: ${'aaaa'.substring(0, 1)}" ) ); + } + + @Test + public void enable_el() { + ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .buildValidatorFactory(); + + Validator validator = validatorFactory.getValidator(); + + assertThat( validator.validate( new EnableELBean( "value" ) ) ) + .containsOnlyViolations( violationOf( EnableELConstraint.class ).withMessage( "Variable: value" ), + violationOf( EnableELConstraint.class ).withMessage( "Bean property: ${validatedValue.bytes[0]}" ), + violationOf( EnableELConstraint.class ).withMessage( "Method execution: ${'aaaa'.substring(0, 1)}" ) ); + } + + @Test + public void enable_el_bean_properties() { + ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .buildValidatorFactory(); + + Validator validator = validatorFactory.getValidator(); + + assertThat( validator.validate( new EnableELBeanPropertiesBean( "value" ) ) ) + .containsOnlyViolations( violationOf( EnableELBeanPropertiesConstraint.class ).withMessage( "Variable: value" ), + violationOf( EnableELBeanPropertiesConstraint.class ).withMessage( "Bean property: 118" ), + violationOf( EnableELBeanPropertiesConstraint.class ).withMessage( "Method execution: ${'aaaa'.substring(0, 1)}" ) ); + } + + @Test + public void enable_el_bean_methods() { + ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .buildValidatorFactory(); + + Validator validator = validatorFactory.getValidator(); + + assertThat( validator.validate( new EnableELBeanMethodsBean( "value" ) ) ) + .containsOnlyViolations( violationOf( EnableELBeanMethodsConstraint.class ).withMessage( "Variable: value" ), + violationOf( EnableELBeanMethodsConstraint.class ).withMessage( "Bean property: 118" ), + violationOf( EnableELBeanMethodsConstraint.class ).withMessage( "Method execution: a" ) ); + } + + @Test + public void warn_when_default_behavior_and_expression_variables() { + ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .buildValidatorFactory(); + + Validator validator = validatorFactory.getValidator(); + + assertThat( validator.validate( new DefaultLevelWithExpressionVariablesBean() ) ) + .containsOnlyViolations( violationOf( DefaultLevelWithExpressionVariablesConstraint.class ).withMessage( "Variable: ${myVariable}" ) ); + + assertTrue( constraintValidatorContextImplLoglistAppender.getEvents().stream() + .filter( event -> event.getLevel().equals( Level.WARN ) ) + .map( event -> event.getMessage().getFormattedMessage() ) + .anyMatch( m -> m.startsWith( "HV000257" ) ) ); + } + + @Test + public void property_default_value() { + validationXmlTestHelper.runWithCustomValidationXml( + "validation-custom-violations-default.xml", new Runnable() { + + @Override + public void run() { + Validator validator = ValidatorUtil.getValidator(); + + assertThat( validator.validate( new EnableELBean( "value" ) ) ) + .containsOnlyViolations( violationOf( EnableELConstraint.class ).withMessage( "Variable: value" ), + violationOf( EnableELConstraint.class ).withMessage( "Bean property: ${validatedValue.bytes[0]}" ), + violationOf( EnableELConstraint.class ).withMessage( "Method execution: ${'aaaa'.substring(0, 1)}" ) ); + } + } ); + } + + @Test + public void property_bean_methods() { + validationXmlTestHelper.runWithCustomValidationXml( + "validation-custom-violations-bean-methods.xml", new Runnable() { + + @Override + public void run() { + Validator validator = ValidatorUtil.getValidator(); + + assertThat( validator.validate( new EnableELBeanMethodsBean( "value" ) ) ) + .containsOnlyViolations( violationOf( EnableELBeanMethodsConstraint.class ).withMessage( "Variable: value" ), + violationOf( EnableELBeanMethodsConstraint.class ).withMessage( "Bean property: 118" ), + violationOf( EnableELBeanMethodsConstraint.class ).withMessage( "Method execution: a" ) ); + } + } ); + } + + @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE }) + @Retention(RUNTIME) + @Documented + @Constraint(validatedBy = { DefaultLevelStringValidator.class }) + private @interface DefaultLevelConstraint { + + String message() default "-"; + + Class[] groups() default {}; + + Class[] payload() default {}; + } + + public static class DefaultLevelStringValidator implements ConstraintValidator { + + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + HibernateConstraintValidatorContext hibernateContext = (HibernateConstraintValidatorContext) context; + + hibernateContext.disableDefaultConstraintViolation(); + + hibernateContext.buildConstraintViolationWithTemplate( "Variable: ${validatedValue}" ).addConstraintViolation(); + hibernateContext.buildConstraintViolationWithTemplate( "Bean property: ${validatedValue.bytes[0]}" ).addConstraintViolation(); + hibernateContext.buildConstraintViolationWithTemplate( "Method execution: ${'aaaa'.substring(0, 1)}" ).addConstraintViolation(); + + return false; + } + } + + public static class DefaultLevelBean { + + @DefaultLevelConstraint + public String value; + } + + @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE }) + @Retention(RUNTIME) + @Documented + @Constraint(validatedBy = { EnableELStringValidator.class }) + private @interface EnableELConstraint { + + String message() default "-"; + + Class[] groups() default {}; + + Class[] payload() default {}; + } + + public static class EnableELStringValidator implements ConstraintValidator { + + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + HibernateConstraintValidatorContext hibernateContext = (HibernateConstraintValidatorContext) context; + + hibernateContext.disableDefaultConstraintViolation(); + + hibernateContext.buildConstraintViolationWithTemplate( "Variable: ${validatedValue}" ) + .enableExpressionLanguage() + .addConstraintViolation(); + hibernateContext.buildConstraintViolationWithTemplate( "Bean property: ${validatedValue.bytes[0]}" ) + .enableExpressionLanguage() + .addConstraintViolation(); + hibernateContext.buildConstraintViolationWithTemplate( "Method execution: ${'aaaa'.substring(0, 1)}" ) + .enableExpressionLanguage() + .addConstraintViolation(); + + return false; + } + } + + public static class EnableELBean { + + public EnableELBean(String value) { + this.value = value; + } + + @EnableELConstraint + public String value; + } + + @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE }) + @Retention(RUNTIME) + @Documented + @Constraint(validatedBy = { EnableELBeanPropertiesStringValidator.class }) + private @interface EnableELBeanPropertiesConstraint { + + String message() default "-"; + + Class[] groups() default {}; + + Class[] payload() default {}; + } + + public static class EnableELBeanPropertiesStringValidator implements ConstraintValidator { + + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + HibernateConstraintValidatorContext hibernateContext = (HibernateConstraintValidatorContext) context; + + hibernateContext.disableDefaultConstraintViolation(); + + hibernateContext.buildConstraintViolationWithTemplate( "Variable: ${validatedValue}" ) + .enableExpressionLanguage( ExpressionLanguageFeatureLevel.BEAN_PROPERTIES ) + .addConstraintViolation(); + hibernateContext.buildConstraintViolationWithTemplate( "Bean property: ${validatedValue.bytes[0]}" ) + .enableExpressionLanguage( ExpressionLanguageFeatureLevel.BEAN_PROPERTIES ) + .addConstraintViolation(); + hibernateContext.buildConstraintViolationWithTemplate( "Method execution: ${'aaaa'.substring(0, 1)}" ) + .enableExpressionLanguage( ExpressionLanguageFeatureLevel.BEAN_PROPERTIES ) + .addConstraintViolation(); + + return false; + } + } + + public static class EnableELBeanPropertiesBean { + + public EnableELBeanPropertiesBean(String value) { + this.value = value; + } + + @EnableELBeanPropertiesConstraint + public String value; + } + + @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE }) + @Retention(RUNTIME) + @Documented + @Constraint(validatedBy = { EnableELBeanMethodsStringValidator.class }) + private @interface EnableELBeanMethodsConstraint { + + String message() default "-"; + + Class[] groups() default {}; + + Class[] payload() default {}; + } + + public static class EnableELBeanMethodsStringValidator implements ConstraintValidator { + + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + HibernateConstraintValidatorContext hibernateContext = (HibernateConstraintValidatorContext) context; + + hibernateContext.disableDefaultConstraintViolation(); + + hibernateContext.buildConstraintViolationWithTemplate( "Variable: ${validatedValue}" ) + .enableExpressionLanguage( ExpressionLanguageFeatureLevel.BEAN_METHODS ) + .addConstraintViolation(); + hibernateContext.buildConstraintViolationWithTemplate( "Bean property: ${validatedValue.bytes[0]}" ) + .enableExpressionLanguage( ExpressionLanguageFeatureLevel.BEAN_METHODS ) + .addConstraintViolation(); + hibernateContext.buildConstraintViolationWithTemplate( "Method execution: ${'aaaa'.substring(0, 1)}" ) + .enableExpressionLanguage( ExpressionLanguageFeatureLevel.BEAN_METHODS ) + .addConstraintViolation(); + + return false; + } + } + + public static class EnableELBeanMethodsBean { + + public EnableELBeanMethodsBean(String value) { + this.value = value; + } + + @EnableELBeanMethodsConstraint + public String value; + } + + @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE }) + @Retention(RUNTIME) + @Documented + @Constraint(validatedBy = { DefaultLevelWithExpressionVariablesStringValidator.class }) + private @interface DefaultLevelWithExpressionVariablesConstraint { + + String message() default "-"; + + Class[] groups() default {}; + + Class[] payload() default {}; + } + + public static class DefaultLevelWithExpressionVariablesStringValidator + implements ConstraintValidator { + + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + HibernateConstraintValidatorContext hibernateContext = (HibernateConstraintValidatorContext) context; + + hibernateContext.disableDefaultConstraintViolation(); + + hibernateContext + .addExpressionVariable( "myVariable", "value" ) + .buildConstraintViolationWithTemplate( "Variable: ${myVariable}" ) + .addConstraintViolation(); + + return false; + } + } + + public static class DefaultLevelWithExpressionVariablesBean { + + @DefaultLevelWithExpressionVariablesConstraint + public String value; + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/bootstrap/ConfigurationReuseHibernateValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/bootstrap/ConfigurationReuseHibernateValidatorTest.java new file mode 100644 index 0000000000..16b9b4ca2d --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/bootstrap/ConfigurationReuseHibernateValidatorTest.java @@ -0,0 +1,59 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.bootstrap; + +import static org.testng.Assert.assertSame; + +import java.util.Locale; + +import javax.validation.Configuration; +import javax.validation.MessageInterpolator; +import javax.validation.Validation; +import javax.validation.ValidatorFactory; + +import org.testng.annotations.Test; + +/** + * @author Steven Walters + * @author Guillaume Smet + */ +public class ConfigurationReuseHibernateValidatorTest { + + public static class MessageInterpolatorImpl implements MessageInterpolator { + + private final String prefix; + + public MessageInterpolatorImpl(String prefix) { + this.prefix = prefix; + } + + @Override + public String interpolate(String messageTemplate, Context context) { + return prefix + ": " + messageTemplate; + } + + @Override + public String interpolate(String messageTemplate, Context context, Locale locale) { + return prefix + ": " + messageTemplate + locale.toLanguageTag(); + } + + public String toString() { + return getClass().getSimpleName() + prefix; + } + } + + @Test + public void testMessageInterpolatorChange() { + Configuration config = Validation.byDefaultProvider().configure(); + MessageInterpolator interpolator1 = new MessageInterpolatorImpl( "One" ); + MessageInterpolator interpolator2 = new MessageInterpolatorImpl( "Two" ); + ValidatorFactory factory1 = config.messageInterpolator( interpolator1 ).buildValidatorFactory(); + ValidatorFactory factory2 = config.messageInterpolator( interpolator2 ).buildValidatorFactory(); + assertSame( factory1.getMessageInterpolator(), interpolator1 ); + assertSame( factory2.getMessageInterpolator(), interpolator2 ); + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/MessagePropertiesTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/MessagePropertiesTest.java index ad05550768..dc28661d56 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/MessagePropertiesTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/MessagePropertiesTest.java @@ -56,9 +56,9 @@ import org.hibernate.validator.constraints.Mod10Check; import org.hibernate.validator.constraints.Mod11Check; import org.hibernate.validator.constraints.ModCheck; +import org.hibernate.validator.constraints.Normalized; import org.hibernate.validator.constraints.ParameterScriptAssert; import org.hibernate.validator.constraints.Range; -import org.hibernate.validator.constraints.SafeHtml; import org.hibernate.validator.constraints.ScriptAssert; import org.hibernate.validator.constraints.URL; import org.hibernate.validator.constraints.UniqueElements; @@ -68,6 +68,7 @@ import org.hibernate.validator.constraints.pl.NIP; import org.hibernate.validator.constraints.pl.PESEL; import org.hibernate.validator.constraints.pl.REGON; +import org.hibernate.validator.constraints.ru.INN; import org.hibernate.validator.constraints.time.DurationMax; import org.hibernate.validator.constraints.time.DurationMin; import org.hibernate.validator.testutil.ConstraintViolationAssert; @@ -157,10 +158,10 @@ public void testMessageProperties() throws NoSuchMethodException, SecurityExcept violationOf( Mod10Check.class ), violationOf( Mod11Check.class ), violationOf( ModCheck.class ), + violationOf( Normalized.class ), violationOf( org.hibernate.validator.constraints.NotBlank.class ), violationOf( org.hibernate.validator.constraints.NotEmpty.class ), violationOf( Range.class ), - violationOf( SafeHtml.class ), violationOf( UniqueElements.class ), violationOf( URL.class ), violationOf( CNPJ.class ), @@ -169,6 +170,7 @@ public void testMessageProperties() throws NoSuchMethodException, SecurityExcept violationOf( REGON.class ), violationOf( NIP.class ), violationOf( PESEL.class ), + violationOf( INN.class ), violationOf( DurationMax.class ), violationOf( DurationMin.class ), violationOf( ScriptAssert.class ) @@ -308,6 +310,9 @@ private static class Bean { @ModCheck(multiplier = 2, modType = ModCheck.ModType.MOD10) private String modCheck = "4"; + @Normalized(form = java.text.Normalizer.Form.NFKC) + private String normalized = "\uFE64script\uFE65"; + @org.hibernate.validator.constraints.NotBlank private String hvNotBlank = ""; @@ -317,9 +322,6 @@ private static class Bean { @Range(min = 2, max = 4) private int range = 6; - @SafeHtml - private String safeHtml = ""; - @UniqueElements private List uniqueElements = Arrays.asList( "a", "a" ); @@ -344,6 +346,9 @@ private static class Bean { @PESEL private String pesel = "invalid"; + @INN + private String inn = "invalid"; + @DurationMax(days = 4, hours = 4, minutes = 4, millis = 4, nanos = 4) private Duration durationMax = Duration.ofDays( 8 ); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/PredefinedScopeAllConstraintsTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/PredefinedScopeAllConstraintsTest.java new file mode 100644 index 0000000000..8826217afc --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/PredefinedScopeAllConstraintsTest.java @@ -0,0 +1,466 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.constraintvalidators; + +import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; + +import java.lang.annotation.Annotation; +import java.math.BigDecimal; +import java.time.Duration; +import java.time.LocalDate; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import javax.money.MonetaryAmount; +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.constraints.AssertFalse; +import javax.validation.constraints.AssertTrue; +import javax.validation.constraints.DecimalMax; +import javax.validation.constraints.DecimalMin; +import javax.validation.constraints.Digits; +import javax.validation.constraints.Email; +import javax.validation.constraints.Future; +import javax.validation.constraints.FutureOrPresent; +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.Negative; +import javax.validation.constraints.NegativeOrZero; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Null; +import javax.validation.constraints.Past; +import javax.validation.constraints.PastOrPresent; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Positive; +import javax.validation.constraints.PositiveOrZero; +import javax.validation.constraints.Size; + +import org.hibernate.validator.PredefinedScopeHibernateValidator; +import org.hibernate.validator.constraints.CodePointLength; +import org.hibernate.validator.constraints.CreditCardNumber; +import org.hibernate.validator.constraints.Currency; +import org.hibernate.validator.constraints.EAN; +import org.hibernate.validator.constraints.ISBN; +import org.hibernate.validator.constraints.Length; +import org.hibernate.validator.constraints.LuhnCheck; +import org.hibernate.validator.constraints.Mod10Check; +import org.hibernate.validator.constraints.Mod11Check; +import org.hibernate.validator.constraints.ModCheck; +import org.hibernate.validator.constraints.Normalized; +import org.hibernate.validator.constraints.ParameterScriptAssert; +import org.hibernate.validator.constraints.Range; +import org.hibernate.validator.constraints.ScriptAssert; +import org.hibernate.validator.constraints.URL; +import org.hibernate.validator.constraints.UniqueElements; +import org.hibernate.validator.constraints.br.CNPJ; +import org.hibernate.validator.constraints.br.CPF; +import org.hibernate.validator.constraints.br.TituloEleitoral; +import org.hibernate.validator.constraints.pl.NIP; +import org.hibernate.validator.constraints.pl.PESEL; +import org.hibernate.validator.constraints.pl.REGON; +import org.hibernate.validator.constraints.ru.INN; +import org.hibernate.validator.constraints.time.DurationMax; +import org.hibernate.validator.constraints.time.DurationMin; +import org.hibernate.validator.testutil.ConstraintViolationAssert; +import org.javamoney.moneta.Money; +import org.testng.annotations.Test; + +/** + * Test that all the messages of all the constraints are properly interpolated for all the supported locales. + * + * @author Guillaume Smet + */ +@SuppressWarnings("deprecation") +public class PredefinedScopeAllConstraintsTest { + + @Test + public void testConstraints() throws NoSuchMethodException, SecurityException { + testConstraint( AssertFalse.class, new AssertFalseBean() ); + testConstraint( AssertTrue.class, new AssertTrueBean() ); + testConstraint( DecimalMax.class, new DecimalMaxBean() ); + testConstraint( DecimalMin.class, new DecimalMinBean() ); + testConstraint( Digits.class, new DigitsBean() ); + testConstraint( Email.class, new EmailBean() ); + testConstraint( Future.class, new FutureBean() ); + testConstraint( FutureOrPresent.class, new FutureOrPresentBean() ); + testConstraint( Max.class, new MaxBean() ); + testConstraint( Min.class, new MinBean() ); + testConstraint( Negative.class, new NegativeBean() ); + testConstraint( NegativeOrZero.class, new NegativeOrZeroBean() ); + testConstraint( NotBlank.class, new NotBlankBean() ); + testConstraint( NotEmpty.class, new NotEmptyBean() ); + testConstraint( NotNull.class, new NotNullBean() ); + testConstraint( Null.class, new NullBean() ); + testConstraint( Past.class, new PastBean() ); + testConstraint( PastOrPresent.class, new PastOrPresentBean() ); + testConstraint( Pattern.class, new PatternBean() ); + testConstraint( Positive.class, new PositiveBean() ); + testConstraint( PositiveOrZero.class, new PositiveOrZeroBean() ); + testConstraint( Size.class, new SizeBean() ); + testConstraint( CreditCardNumber.class, new CreditCardNumberBean() ); + testConstraint( Currency.class, new CurrencyBean() ); + testConstraint( EAN.class, new EANBean() ); + testConstraint( org.hibernate.validator.constraints.Email.class, new HvEmailBean() ); + testConstraint( ISBN.class, new ISBNBean() ); + testConstraint( Length.class, new LengthBean() ); + testConstraint( CodePointLength.class, new CodePointLengthBean() ); + testConstraint( LuhnCheck.class, new LuhnCheckBean() ); + testConstraint( Mod10Check.class, new Mod10CheckBean() ); + testConstraint( Mod11Check.class, new Mod11CheckBean() ); + testConstraint( ModCheck.class, new ModCheckBean() ); + testConstraint( Normalized.class, new NormalizedBean() ); + testConstraint( org.hibernate.validator.constraints.NotBlank.class, new HvNotBlankBean() ); + testConstraint( org.hibernate.validator.constraints.NotEmpty.class, new HvNotEmptyBean() ); + testConstraint( Range.class, new RangeBean() ); + testConstraint( UniqueElements.class, new UniqueElementsBean() ); + testConstraint( URL.class, new URLBean() ); + testConstraint( CNPJ.class, new CNPJBean() ); + testConstraint( CPF.class, new CPFBean() ); + testConstraint( TituloEleitoral.class, new TituloEleitoralBean() ); + testConstraint( REGON.class, new REGONBean() ); + testConstraint( NIP.class, new NIPBean() ); + testConstraint( PESEL.class, new PESELBean() ); + testConstraint( INN.class, new INNBean() ); + testConstraint( DurationMax.class, new DurationMaxBean() ); + testConstraint( DurationMin.class, new DurationMinBean() ); + testConstraint( ScriptAssert.class, new ScriptAssertBean() ); + + Set> parameterScriptAssertBeanViolations = getValidator( ParameterScriptAssert.class, + ParameterScriptAssertBean.class ).forExecutables().validateParameters( + new ParameterScriptAssertBean(), ParameterScriptAssertBean.class.getDeclaredMethod( "doTest", boolean.class ), new Object[]{ false } ); + + ConstraintViolationAssert.assertThat( parameterScriptAssertBeanViolations ) + .containsOnlyViolations( + violationOf( ParameterScriptAssert.class ) ); + } + + private void testConstraint(Class constraint, T bean) { + Set> violations = getValidator( constraint, bean.getClass() ) + .validate( bean ); + ConstraintViolationAssert.assertThat( violations ) + .containsOnlyViolations( + violationOf( constraint ) ); + } + + private static Validator getValidator(Class constraint, Class beanClass) { + return Validation.byProvider( PredefinedScopeHibernateValidator.class ) + .configure() + .builtinConstraints( Collections.singleton( constraint.getName() ) ) + .initializeBeanMetaData( Collections.singleton( beanClass ) ) + .buildValidatorFactory() + .getValidator(); + } + + private static class AssertFalseBean { + + @AssertFalse + private boolean assertFalse = true; + } + + private static class AssertTrueBean { + + @AssertTrue + private boolean assertTrue = false; + } + + private static class DecimalMaxBean { + + @DecimalMax("3") + private double decimalMax = 4; + } + + private static class DecimalMinBean { + + @DecimalMin("3") + private double decimalMin = 2; + } + + private static class DigitsBean { + + @Digits(integer = 1, fraction = 3) + private BigDecimal digits = BigDecimal.valueOf( 13333.3333f ); + } + + private static class EmailBean { + + @Email + private String email = "invalid"; + } + + private static class FutureBean { + + @Future + private LocalDate future = LocalDate.of( 2010, 10, 4 ); + } + + private static class FutureOrPresentBean { + + @FutureOrPresent + private LocalDate futureOrPresent = LocalDate.of( 2010, 10, 4 ); + } + + private static class MaxBean { + + @Max(4) + private int max = 6; + } + + private static class MinBean { + + @Min(4) + private int min = 2; + } + + private static class NegativeBean { + + @Negative + private int negative = 4; + } + + private static class NegativeOrZeroBean { + + @NegativeOrZero + private int negativeOrZero = 4; + } + + private static class NotBlankBean { + + @NotBlank + private String notBlank = ""; + } + + private static class NotEmptyBean { + + @NotEmpty + private List notEmpty = Collections.emptyList(); + } + + private static class NotNullBean { + + @NotNull + private String notNull = null; + } + + private static class NullBean { + + @Null + private String nullConstraint = "not null"; + } + + private static class PastBean { + + @Past + private LocalDate past = LocalDate.of( 2890, 10, 4 ); + } + + private static class PastOrPresentBean { + + @PastOrPresent + private LocalDate pastOrPresent = LocalDate.of( 2890, 10, 4 ); + } + + private static class PatternBean { + + @Pattern(regexp = "[0-9]+") + private String pattern = "invalid"; + } + + private static class PositiveBean { + + @Positive + private int positive = -4; + } + + private static class PositiveOrZeroBean { + + @PositiveOrZero + private int positiveOrZero = -4; + } + + private static class SizeBean { + + @Size(min = 2, max = 4) + private String size = "666666"; + } + + private static class CreditCardNumberBean { + + @CreditCardNumber + private String creditCardNumber = "invalid"; + } + + private static class CurrencyBean { + + @Currency("EUR") + private MonetaryAmount currency = Money.of( 1000f, "USD" ); + } + + private static class EANBean { + + @EAN + private String ean = "invalid"; + } + + private static class HvEmailBean { + + @org.hibernate.validator.constraints.Email + private String hvEmail = "invalid"; + } + + private static class ISBNBean { + + @ISBN + private String isbn = "invalid"; + } + + private static class LengthBean { + + @Length(min = 2, max = 4) + private String length = "666666"; + } + + private static class CodePointLengthBean { + + @CodePointLength(min = 2, max = 4) + private String codePointLength = "666666"; + } + + private static class LuhnCheckBean { + + @LuhnCheck + private String luhnCheck = "4"; + } + + private static class Mod10CheckBean { + + @Mod10Check + private String mod10Check = "4"; + } + + private static class Mod11CheckBean { + + @Mod11Check + private String mod11Check = "4"; + } + + private static class ModCheckBean { + + @ModCheck(multiplier = 2, modType = ModCheck.ModType.MOD10) + private String modCheck = "4"; + } + + private static class NormalizedBean { + + @Normalized(form = java.text.Normalizer.Form.NFKC) + private String normalized = "\uFE64script\uFE65"; + + } + + private static class HvNotBlankBean { + + @org.hibernate.validator.constraints.NotBlank + private String hvNotBlank = ""; + } + + private static class HvNotEmptyBean { + + @org.hibernate.validator.constraints.NotEmpty + private List hvNotEmpty = Collections.emptyList(); + } + + private static class RangeBean { + + @Range(min = 2, max = 4) + private int range = 6; + } + + private static class UniqueElementsBean { + + @UniqueElements + private List uniqueElements = Arrays.asList( "a", "a" ); + } + + private static class URLBean { + + @URL + private String url = "invalid"; + } + + private static class CNPJBean { + + @CNPJ + private String cnpj = "invalid"; + } + + private static class CPFBean { + + @CPF + private String cpf = "invalid"; + } + + private static class TituloEleitoralBean { + + @TituloEleitoral + private String tituloEleitoral = "invalid"; + } + + private static class REGONBean { + + @REGON + private String regon = "invalid"; + } + + private static class NIPBean { + + @NIP + private String nip = "invalid"; + } + + private static class PESELBean { + + @PESEL + private String pesel = "invalid"; + } + + private static class INNBean { + + @INN + private String inn = "invalid"; + } + + private static class DurationMaxBean { + + @DurationMax(days = 4, hours = 4, minutes = 4, millis = 4, nanos = 4) + private Duration durationMax = Duration.ofDays( 8 ); + } + + private static class DurationMinBean { + + @DurationMin(days = 4, hours = 4, minutes = 4, millis = 4, nanos = 4) + private Duration durationMin = Duration.ofDays( 2 ); + } + + @ScriptAssert(lang = "groovy", script = "_this.scriptAssert") + private static class ScriptAssertBean { + + @SuppressWarnings("unused") + private boolean scriptAssert = false; + } + + private static class ParameterScriptAssertBean { + + @ParameterScriptAssert(lang = "groovy", script = "test") + public boolean doTest(boolean test) { + return test; + } + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/MaxValidatorForStringTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/MaxValidatorForStringTest.java index f81b2edb50..6a24171fae 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/MaxValidatorForStringTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/MaxValidatorForStringTest.java @@ -14,8 +14,8 @@ import javax.validation.constraints.DecimalMax; import javax.validation.constraints.Max; -import org.hibernate.validator.internal.constraintvalidators.bv.DecimalMaxValidatorForCharSequence; -import org.hibernate.validator.internal.constraintvalidators.bv.MaxValidatorForCharSequence; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MaxValidatorForCharSequence; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMaxValidatorForCharSequence; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMaxValidatorForNumber; import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; import org.hibernate.validator.testutil.MyCustomStringImpl; diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/MinValidatorForStringTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/MinValidatorForStringTest.java index 4701cd58e1..5a06df9e86 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/MinValidatorForStringTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/MinValidatorForStringTest.java @@ -14,8 +14,8 @@ import javax.validation.constraints.DecimalMin; import javax.validation.constraints.Min; -import org.hibernate.validator.internal.constraintvalidators.bv.DecimalMinValidatorForCharSequence; -import org.hibernate.validator.internal.constraintvalidators.bv.MinValidatorForCharSequence; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MinValidatorForCharSequence; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMinValidatorForCharSequence; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMinValidatorForNumber; import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; import org.hibernate.validator.testutil.MyCustomStringImpl; diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/NegativePositiveValidatorForStringTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/NegativePositiveValidatorForStringTest.java new file mode 100644 index 0000000000..f702729915 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/NegativePositiveValidatorForStringTest.java @@ -0,0 +1,108 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.constraintvalidators.bv; + +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +import javax.validation.constraints.Negative; +import javax.validation.constraints.NegativeOrZero; +import javax.validation.constraints.Positive; +import javax.validation.constraints.PositiveOrZero; + +import org.hibernate.validator.internal.constraintvalidators.bv.number.sign.NegativeOrZeroValidatorForCharSequence; +import org.hibernate.validator.internal.constraintvalidators.bv.number.sign.NegativeValidatorForCharSequence; +import org.hibernate.validator.internal.constraintvalidators.bv.number.sign.PositiveOrZeroValidatorForCharSequence; +import org.hibernate.validator.internal.constraintvalidators.bv.number.sign.PositiveValidatorForCharSequence; +import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; +import org.testng.annotations.Test; + +/** + * @author Guillaume Smet + */ +public class NegativePositiveValidatorForStringTest { + + @Test + public void testIsValidPositiveValidator() { + ConstraintAnnotationDescriptor.Builder descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( Positive.class ); + descriptorBuilder.setMessage( "{validator.positive}" ); + Positive m = descriptorBuilder.build().getAnnotation(); + + PositiveValidatorForCharSequence constraint = new PositiveValidatorForCharSequence(); + constraint.initialize( m ); + + assertTrue( constraint.isValid( null, null ) ); + assertTrue( constraint.isValid( "15", null ) ); + assertTrue( constraint.isValid( "15.0", null ) ); + assertFalse( constraint.isValid( "0", null ) ); + assertFalse( constraint.isValid( "-10", null ) ); + assertFalse( constraint.isValid( "-14.99", null ) ); + + // number format exception + assertFalse( constraint.isValid( "15l", null ) ); + } + + @Test + public void testIsValidPositiveOrZeroValidator() { + ConstraintAnnotationDescriptor.Builder descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( PositiveOrZero.class ); + descriptorBuilder.setMessage( "{validator.positiveOrZero}" ); + PositiveOrZero m = descriptorBuilder.build().getAnnotation(); + + PositiveOrZeroValidatorForCharSequence constraint = new PositiveOrZeroValidatorForCharSequence(); + constraint.initialize( m ); + + assertTrue( constraint.isValid( null, null ) ); + assertTrue( constraint.isValid( "15", null ) ); + assertTrue( constraint.isValid( "15.0", null ) ); + assertTrue( constraint.isValid( "0", null ) ); + assertFalse( constraint.isValid( "-10", null ) ); + assertFalse( constraint.isValid( "-14.99", null ) ); + + // number format exception + assertFalse( constraint.isValid( "15l", null ) ); + } + + @Test + public void testIsValidNegativeValidator() { + ConstraintAnnotationDescriptor.Builder descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( Negative.class ); + descriptorBuilder.setMessage( "{validator.negative}" ); + Negative m = descriptorBuilder.build().getAnnotation(); + + NegativeValidatorForCharSequence constraint = new NegativeValidatorForCharSequence(); + constraint.initialize( m ); + + assertTrue( constraint.isValid( null, null ) ); + assertFalse( constraint.isValid( "15", null ) ); + assertFalse( constraint.isValid( "15.0", null ) ); + assertFalse( constraint.isValid( "0", null ) ); + assertTrue( constraint.isValid( "-10", null ) ); + assertTrue( constraint.isValid( "-14.99", null ) ); + + // number format exception + assertFalse( constraint.isValid( "15l", null ) ); + } + + @Test + public void testIsValidNegativeOrZeroValidator() { + ConstraintAnnotationDescriptor.Builder descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( NegativeOrZero.class ); + descriptorBuilder.setMessage( "{validator.negativeOrZero}" ); + NegativeOrZero m = descriptorBuilder.build().getAnnotation(); + + NegativeOrZeroValidatorForCharSequence constraint = new NegativeOrZeroValidatorForCharSequence(); + constraint.initialize( m ); + + assertTrue( constraint.isValid( null, null ) ); + assertFalse( constraint.isValid( "15", null ) ); + assertFalse( constraint.isValid( "15.0", null ) ); + assertTrue( constraint.isValid( "0", null ) ); + assertTrue( constraint.isValid( "-10", null ) ); + assertTrue( constraint.isValid( "-14.99", null ) ); + + // number format exception + assertFalse( constraint.isValid( "15l", null ) ); + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/NormalizedValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/NormalizedValidatorTest.java new file mode 100644 index 0000000000..50681e6d81 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/NormalizedValidatorTest.java @@ -0,0 +1,96 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.constraintvalidators.hv; + +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +import org.hibernate.validator.constraints.Normalized; +import org.hibernate.validator.internal.constraintvalidators.hv.NormalizedValidator; +import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; +import org.hibernate.validator.testutil.MyCustomStringImpl; + +import java.text.Normalizer; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +/** + * Tests the {@link Normalized} constraint. + * + * @author Kazuki Shimizu + */ +public class NormalizedValidatorTest { + + private ConstraintAnnotationDescriptor.Builder descriptorBuilder; + + @BeforeMethod + public void setUp() throws Exception { + descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( Normalized.class ); + } + + @Test + public void testIsValid() { + descriptorBuilder.setMessage( "{validator.Normalized}" ); + Normalized l = descriptorBuilder.build().getAnnotation(); + NormalizedValidator constraint = new NormalizedValidator(); + constraint.initialize( l ); + assertTrue( constraint.isValid( null, null ) ); + assertTrue( constraint.isValid( "", null ) ); + assertTrue( constraint.isValid( "foobar", null ) ); + assertTrue( constraint.isValid( "\uFE64script\uFE65", null ) ); + } + + @Test + public void testIsValidCharSequence() { + Normalized l = descriptorBuilder.build().getAnnotation(); + NormalizedValidator constraint = new NormalizedValidator(); + constraint.initialize( l ); + assertTrue( constraint.isValid( new MyCustomStringImpl( "foobar" ), null ) ); + assertTrue( constraint.isValid( new MyCustomStringImpl( "\uFE64script\uFE65" ), null ) ); + } + + @Test + public void testIsValidNormalizationStrategyIsNfc() { + descriptorBuilder.setAttribute( "form", Normalizer.Form.NFC ); + Normalized l = descriptorBuilder.build().getAnnotation(); + NormalizedValidator constraint = new NormalizedValidator(); + constraint.initialize( l ); + assertTrue( constraint.isValid( "foobar", null ) ); + assertTrue( constraint.isValid( "\uFE64script\uFE65", null ) ); + } + + @Test + public void testIsValidNormalizationStrategyIsNfkc() { + descriptorBuilder.setAttribute( "form", Normalizer.Form.NFKC ); + Normalized l = descriptorBuilder.build().getAnnotation(); + NormalizedValidator constraint = new NormalizedValidator(); + constraint.initialize( l ); + assertTrue( constraint.isValid( "foobar", null ) ); + assertFalse( constraint.isValid( "\uFE64script\uFE65", null ) ); + } + + @Test + public void testIsValidNormalizationStrategyIsNfd() { + descriptorBuilder.setAttribute( "form", Normalizer.Form.NFD ); + Normalized l = descriptorBuilder.build().getAnnotation(); + NormalizedValidator constraint = new NormalizedValidator(); + constraint.initialize( l ); + assertTrue( constraint.isValid( "foobar", null ) ); + assertTrue( constraint.isValid( "\uFE64script\uFE65", null ) ); + } + + @Test + public void testIsValidNormalizationStrategyIsNfkd() { + descriptorBuilder.setAttribute( "form", Normalizer.Form.NFKD ); + Normalized l = descriptorBuilder.build().getAnnotation(); + NormalizedValidator constraint = new NormalizedValidator(); + constraint.initialize( l ); + assertTrue( constraint.isValid( "foobar", null ) ); + assertFalse( constraint.isValid( "\uFE64script\uFE65", null ) ); + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/SafeHtmlValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/SafeHtmlValidatorTest.java deleted file mode 100644 index 45e6f031da..0000000000 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/SafeHtmlValidatorTest.java +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Hibernate Validator, declare and validate application constraints - * - * License: Apache License, Version 2.0 - * See the license.txt file in the root directory or . - */ -package org.hibernate.validator.test.internal.constraintvalidators.hv; - -import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNoViolations; -import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; -import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; -import static org.hibernate.validator.testutils.ValidatorUtil.getValidator; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; - -import java.util.Set; - -import javax.validation.ConstraintViolation; -import javax.validation.Validator; - -import org.hibernate.validator.constraints.SafeHtml; -import org.hibernate.validator.constraints.SafeHtml.WhiteListType; -import org.hibernate.validator.internal.constraintvalidators.hv.SafeHtmlValidator; -import org.hibernate.validator.internal.util.annotation.AnnotationDescriptor; -import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; -import org.hibernate.validator.testutil.TestForIssue; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -/** - * Unit test for {@link SafeHtmlValidator}. - * - * @author George Gastaldi - * @author Hardy Ferentschik - * @author Marko Bekhta - */ -public class SafeHtmlValidatorTest { - - private ConstraintAnnotationDescriptor.Builder descriptorBuilder; - - @BeforeMethod - public void setUp() { - descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( SafeHtml.class ); - } - - @Test - public void testNullValue() throws Exception { - descriptorBuilder.setAttribute( "whitelistType", WhiteListType.BASIC ); - - assertTrue( getSafeHtmlValidator().isValid( null, null ) ); - } - - @Test - public void testInvalidScriptTagIncluded() throws Exception { - descriptorBuilder.setAttribute( "whitelistType", WhiteListType.BASIC ); - - assertFalse( getSafeHtmlValidator().isValid( "HelloWorld !", null ) ); - } - - @Test - // A "downlevel revealed" conditional 'comment' is not an (X)HTML comment at all, - // despite the misleading name, it is default Microsoft syntax. - // The tag is unrecognized by therefore executed - public void testDownlevelRevealedConditionalComment() throws Exception { - descriptorBuilder.setAttribute( "whitelistType", WhiteListType.BASIC ); - - assertFalse( getSafeHtmlValidator().isValid( "\n\n", null ) ); - } - - @Test - public void testDownlevelHiddenConditionalComment() throws Exception { - descriptorBuilder.setAttribute( "whitelistType", WhiteListType.BASIC ); - - assertFalse( getSafeHtmlValidator().isValid( "", null ) ); - } - - @Test - public void testSimpleComment() throws Exception { - descriptorBuilder.setAttribute( "whitelistType", WhiteListType.BASIC ); - - assertFalse( getSafeHtmlValidator().isValid( "", null ) ); - } - - @Test - public void testServerSideIncludesSSI() throws Exception { - descriptorBuilder.setAttribute( "whitelistType", WhiteListType.BASIC ); - - assertFalse( getSafeHtmlValidator().isValid( "alert{\"XSS\"}'}; ?>", null ) ); - } - - @Test - public void testPHPScript() throws Exception { - descriptorBuilder.setAttribute( "whitelistType", WhiteListType.BASIC ); - - assertFalse( getSafeHtmlValidator().isValid( "alert{\"XSS\"}'}; ?>", null ) ); - } - - @Test - public void testInvalidIncompleteImgTagWithScriptIncluded() { - descriptorBuilder.setAttribute( "whitelistType", WhiteListType.BASIC ); - - assertFalse( getSafeHtmlValidator().isValid( "Link

", null ) ); - } - - @Test - public void testAdditionalTags() throws Exception { - descriptorBuilder.setAttribute( "additionalTags", new String[] { "script" } ); - - assertTrue( getSafeHtmlValidator().isValid( "HelloWorld !", null ) ); - } - - @Test - @TestForIssue(jiraKey = "HV-817") - public void testDivNotAllowedInBasicWhiteList() throws Exception { - descriptorBuilder.setAttribute( "whitelistType", WhiteListType.BASIC ); - - SafeHtmlValidator validator = getSafeHtmlValidator(); - assertFalse( validator.isValid( "
test
", null ) ); - } - - @Test - @TestForIssue(jiraKey = "HV-817") - public void testDivAllowedInRelaxedWhiteList() throws Exception { - descriptorBuilder.setAttribute( "whitelistType", WhiteListType.RELAXED ); - - assertTrue( getSafeHtmlValidator().isValid( "
test
", null ) ); - } - - @Test - @TestForIssue(jiraKey = "HV-817") - public void testDivWithWhiteListedClassAttribute() throws Exception { - descriptorBuilder.setAttribute( "whitelistType", WhiteListType.RELAXED ); - - AnnotationDescriptor.Builder tagDescriptorBuilder = new AnnotationDescriptor.Builder<>( SafeHtml.Tag.class ); - tagDescriptorBuilder.setAttribute( "name", "div" ); - tagDescriptorBuilder.setAttribute( "attributes", new String[] { "class" } ); - SafeHtml.Tag tag = tagDescriptorBuilder.build().getAnnotation(); - descriptorBuilder.setAttribute( "additionalTagsWithAttributes", new SafeHtml.Tag[] { tag } ); - - assertTrue( - getSafeHtmlValidator().isValid( "
test
", null ), - "class attribute should be white listed" - ); - assertFalse( - getSafeHtmlValidator().isValid( "
test
", null ), - "style attribute is not white listed" - ); - } - - @Test - @TestForIssue(jiraKey = "HV-817") - public void testDivWithWhiteListedStyleAttribute() throws Exception { - Validator validator = getValidator(); - Set> constraintViolations = validator.validate( new Foo( "
test
" ) ); - assertNoViolations( constraintViolations ); - - // the attributes are optional - allowing
also allows just
- constraintViolations = validator.validate( new Foo( "
test
" ) ); - assertNoViolations( constraintViolations ); - - constraintViolations = validator.validate( new Foo( "
test
" ) ); - assertThat( constraintViolations ).containsOnlyViolations( - violationOf( SafeHtml.class ) - ); - } - - @Test - @TestForIssue(jiraKey = "HV-873") - public void testValidationOfInvalidFragment() throws Exception { - descriptorBuilder.setAttribute( "whitelistType", WhiteListType.NONE ); - - assertFalse( getSafeHtmlValidator().isValid( "1234qwer", null ) ); - } - - @Test - @TestForIssue(jiraKey = "HV-873") - public void testValidationOfValidFragment() throws Exception { - descriptorBuilder.setAttribute( "whitelistType", WhiteListType.RELAXED ); - - assertTrue( getSafeHtmlValidator().isValid( "1234qwer", null ) ); - } - - @Test - @TestForIssue(jiraKey = "HV-873") - public void testValidationOfTextFragment() throws Exception { - descriptorBuilder.setAttribute( "whitelistType", WhiteListType.NONE ); - - assertTrue( getSafeHtmlValidator().isValid( "Foobar", null ) ); - } - - @Test - @TestForIssue(jiraKey = "HV-1302") - public void testAdditionalProtocols() { - Validator validator = getValidator(); - - assertNoViolations( validator.validate( new Bar( "" ) ) ); - assertNoViolations( validator.validate( new Bar( "" ) ) ); - assertThat( validator.validate( new Bar( "" ) ) ) - .containsOnlyViolations( - violationOf( SafeHtml.class ) - ); - assertThat( validator.validate( new Bar( "" ) ) ) - .containsOnlyViolations( - violationOf( SafeHtml.class ) - ); - assertThat( validator.validate( new Bar( "
" ) ) ) - .containsOnlyViolations( - violationOf( SafeHtml.class ) - ); - assertThat( validator.validate( new Bar( "
" ) ) ) - .containsOnlyViolations( - violationOf( SafeHtml.class ) - ); - assertNoViolations( validator.validate( new Bar( - "" + - " " + - " " + - " " + - " " + - "
" + - " " + - "" ) ) ); - assertThat( validator.validate( new Bar( - "
" + - "" + - "" + - "/
" - ) ) ).containsOnlyViolations( - violationOf( SafeHtml.class ) - ); - } - - @Test - @TestForIssue(jiraKey = "HV-1303") - public void testPreserveRelativeLinks() throws Exception { - descriptorBuilder.setAttribute( "whitelistType", WhiteListType.RELAXED ); - descriptorBuilder.setAttribute( "baseURI", "http://127.0.0.1" ); - - assertTrue( getSafeHtmlValidator().isValid( "", null ) ); - - descriptorBuilder.setAttribute( "whitelistType", WhiteListType.RELAXED ); - descriptorBuilder.setAttribute( "baseURI", "" ); - - assertFalse( getSafeHtmlValidator().isValid( "", null ) ); - } - - private SafeHtmlValidator getSafeHtmlValidator() { - SafeHtml p = descriptorBuilder.build().getAnnotation(); - SafeHtmlValidator validator = new SafeHtmlValidator(); - validator.initialize( p ); - return validator; - } - - public static class Foo { - @SafeHtml( - whitelistType = WhiteListType.BASIC, - additionalTagsWithAttributes = @SafeHtml.Tag(name = "div", attributes = { "style" }) - ) - String source; - - public Foo(String source) { - this.source = source; - } - } - - public static class Bar { - @SafeHtml( - whitelistType = WhiteListType.BASIC, - additionalTagsWithAttributes = { - @SafeHtml.Tag(name = "img", attributesWithProtocols = @SafeHtml.Attribute(name = "src", protocols = { "data" })), - @SafeHtml.Tag(name = "custom", attributesWithProtocols = { - @SafeHtml.Attribute(name = "attr1", protocols = { "dataprotocol", "strange_protocol" }), - @SafeHtml.Attribute(name = "attr2", protocols = { "dataprotocol", "strange_protocol" }), - @SafeHtml.Attribute(name = "attr3", protocols = "some_protocol") - }), - @SafeHtml.Tag(name = "section", attributes = { "attr", "id" }) - } - ) - String source; - - public Bar(String source) { - this.source = source; - } - } -} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/ScriptAssertValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/ScriptAssertValidatorTest.java index 20d698abc8..95efc32d4c 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/ScriptAssertValidatorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/ScriptAssertValidatorTest.java @@ -43,14 +43,16 @@ public class ScriptAssertValidatorTest extends AbstractConstrainedTest { @Test public void scriptEvaluatesToTrue() throws Exception { - @ScriptAssert(lang = "groovy", script = "true") class TmpType { } + @ScriptAssert(lang = "groovy", script = "true") class TmpType { + } assertNoViolations( validator.validate( new TmpType() ) ); } @Test public void scriptEvaluatesToFalse() throws Exception { - @ScriptAssert(lang = "groovy", script = "false") class TmpType { } + @ScriptAssert(lang = "groovy", script = "false") class TmpType { + } assertThat( validator.validate( new TmpType() ) ).containsOnlyViolations( violationOf( ScriptAssert.class ) ); @@ -107,28 +109,32 @@ public void emptyAliasRaisesException() throws Exception { @Test(expectedExceptions = ConstraintDeclarationException.class) public void unknownLanguageNameRaisesException() throws Exception { - @ScriptAssert(lang = "foo", script = "script") class TmpType { } + @ScriptAssert(lang = "foo", script = "script") class TmpType { + } assertNoViolations( validator.validate( new TmpType() ) ); } @Test(expectedExceptions = ConstraintDeclarationException.class) public void illegalScriptExpressionRaisesException() throws Exception { - @ScriptAssert(lang = "groovy", script = "foo") class TmpType { } + @ScriptAssert(lang = "groovy", script = "foo") class TmpType { + } assertNoViolations( validator.validate( new TmpType() ) ); } @Test(expectedExceptions = ConstraintDeclarationException.class) public void scriptExpressionReturningNullRaisesException() throws Exception { - @ScriptAssert(lang = "groovy", script = "null") class TmpType { } + @ScriptAssert(lang = "groovy", script = "null") class TmpType { + } assertNoViolations( validator.validate( new TmpType() ) ); } @Test(expectedExceptions = ConstraintDeclarationException.class) public void scriptExpressionReturningNoBooleanRaisesException() throws Exception { - @ScriptAssert(lang = "groovy", script = "new java.util.Date()") class TmpType { } + @ScriptAssert(lang = "groovy", script = "new java.util.Date()") class TmpType { + } assertNoViolations( validator.validate( new TmpType() ) ); } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/ru/INNValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/ru/INNValidatorTest.java new file mode 100644 index 0000000000..24ff8691ed --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/ru/INNValidatorTest.java @@ -0,0 +1,132 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.constraintvalidators.hv.ru; + +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +import org.hibernate.validator.constraints.ru.INN; +import org.hibernate.validator.internal.constraintvalidators.hv.ru.INNValidator; +import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +/** + * A set of tests for {@link INN} constraint validator ({@link INNValidator}), + * which make sure that validation is performed correctly. + * + * @author Artem Boiarshinov + * @see fake INN generator + */ +public class INNValidatorTest { + + private INNValidator validator; + + @BeforeMethod + public void setUp() { + validator = new INNValidator(); + } + + @Test + public void validIndividualTypeINN() { + validator.initialize( initializeAnnotation( INN.Type.INDIVIDUAL ) ); + + assertValidINN( null ); + assertValidINN( "246964567008" ); + assertValidINN( "356393289962" ); + assertValidINN( "279837166431" ); + assertValidINN( "827175083460" ); + assertValidINN( "789429596404" ); + assertValidINN( "929603416330" ); + assertValidINN( "086229647992" ); + } + + @Test + public void invalidIndividualTypeINN() { + validator.initialize( initializeAnnotation( INN.Type.INDIVIDUAL ) ); + + //invalid checksum + assertInvalidINN( "012345678912" ); + assertInvalidINN( "246964567009" ); + + //invalid symbols + assertInvalidINN( "a46964567008" ); + + //invalid length + assertInvalidINN( "" ); + assertInvalidINN( "90660563173" ); + assertInvalidINN( "9066056317378" ); + + //invalid type + assertInvalidINN( "4546366155" ); + } + + @Test + public void validJuridicalTypeINN() { + validator.initialize( initializeAnnotation( INN.Type.JURIDICAL ) ); + + assertValidINN( null ); + assertValidINN( "0305773929" ); + assertValidINN( "5496344268" ); + assertValidINN( "0314580754" ); + assertValidINN( "8652697156" ); + assertValidINN( "3527694367" ); + assertValidINN( "8771236130" ); + assertValidINN( "9254906927" ); + } + + @Test + public void invalidJuridicalTypeINN() { + validator.initialize( initializeAnnotation( INN.Type.JURIDICAL ) ); + + //invalid checksum + assertInvalidINN( "0123456789" ); + assertInvalidINN( "0305773928" ); + + //invalid symbols + assertInvalidINN( "a305773929" ); + + //invalid length + assertInvalidINN( "" ); + assertInvalidINN( "906605631" ); + assertInvalidINN( "90660563173" ); + + //invalid type + assertInvalidINN( "246964567008" ); + } + + @Test + public void testAnyTypeINN() { + validator.initialize( initializeAnnotation( INN.Type.ANY ) ); + + final String personalValidINN = "246964567008"; + final String juridicalValidINN = "5496344268"; + final String personalInvalidINN = "246964567009"; + final String juridicalInvalidINN = "0305773928"; + + assertValidINN( null ); + assertValidINN( personalValidINN ); + assertValidINN( juridicalValidINN ); + assertInvalidINN( personalInvalidINN ); + assertInvalidINN( juridicalInvalidINN ); + } + + private void assertValidINN(String inn) { + assertTrue( validator.isValid( inn, null ), inn + " should be a valid INN" ); + } + + private void assertInvalidINN(String inn) { + assertFalse( validator.isValid( inn, null ), inn + " should be a invalid INN" ); + } + + private INN initializeAnnotation(INN.Type type) { + ConstraintAnnotationDescriptor.Builder descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( INN.class ); + descriptorBuilder.setAttribute( "type", type ); + return descriptorBuilder.build().getAnnotation(); + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/constraintvalidation/HibernateConstraintValidatorContextTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/constraintvalidation/HibernateConstraintValidatorContextTest.java index fa9c670e7e..3501d164c6 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/constraintvalidation/HibernateConstraintValidatorContextTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/constraintvalidation/HibernateConstraintValidatorContextTest.java @@ -45,6 +45,7 @@ public class HibernateConstraintValidatorContextTest { private static final String QUESTION_2 = "What is 1+1 and what is the answer to life?"; private static final String QUESTION_3 = "This is a trick question"; private static final String QUESTION_4 = "What keywords are not allowed?"; + private static final String QUESTION_5 = "What is 1+1 and what is the answer to life? But I won't get the right answer as Expression Language is disabled"; private static final List INVALID_KEYWORDS = Lists.newArrayList( "foo", "bar", "baz" ); @@ -130,7 +131,7 @@ public void testSettingInvalidCustomExpressionVariable() { @Test @TestForIssue(jiraKey = "HV-701") - public void testCreatingMultipleConstraintViolationWithExpressionVariables() { + public void testCreatingMultipleConstraintViolationWithExpressionVariablesWithExpressionLanguageEnabled() { Validator validator = getValidator(); Set> constraintViolations = validator.validate( new ExpressionVariableFoo( QUESTION_2 ) ); @@ -223,6 +224,18 @@ public void testNullIsReturnedIfPayloadIsNull() { Assert.assertNull( hibernateConstraintViolation.getDynamicPayload( Object.class ) ); } + @Test + @TestForIssue(jiraKey = "HV-1816") + public void testCreatingMultipleConstraintViolationWithExpressionVariables() { + Validator validator = getValidator(); + Set> constraintViolations = validator.validate( new ExpressionVariableFoo( QUESTION_5 ) ); + + assertThat( constraintViolations ).containsOnlyViolations( + violationOf( ExpressionVariableOracleConstraint.class ).withMessage( "answer 1: ${answer}" ), + violationOf( ExpressionVariableOracleConstraint.class ).withMessage( "answer 2: ${answer}" ) + ); + } + public class MessageParameterFoo { @MessageParameterOracleConstraint private final String question; @@ -323,7 +336,7 @@ public boolean isValid(String question, ConstraintValidatorContext context) { createSingleConstraintViolation( hibernateContext ); } else if ( question.equals( QUESTION_2 ) ) { - createMultipleConstraintViolationsUpdatingExpressionVariableValues( hibernateContext ); + createMultipleConstraintViolationsUpdatingExpressionVariableValuesWithExpressionLanguageEnabled( hibernateContext ); } else if ( question.equals( QUESTION_3 ) ) { hibernateContext.addExpressionVariable( "answer", "${foo}" ); @@ -331,6 +344,9 @@ else if ( question.equals( QUESTION_3 ) ) { else if ( question.equals( QUESTION_4 ) ) { hibernateContext.withDynamicPayload( INVALID_KEYWORDS ); } + else if ( question.equals( QUESTION_5 ) ) { + createMultipleConstraintViolationsUpdatingExpressionVariableValues( hibernateContext ); + } else { tryingToIllegallyUseNullExpressionVariableName( hibernateContext ); } @@ -343,6 +359,22 @@ private void tryingToIllegallyUseNullExpressionVariableName(HibernateConstraintV hibernateContext.addMessageParameter( null, "foo" ); } + private void createMultipleConstraintViolationsUpdatingExpressionVariableValuesWithExpressionLanguageEnabled( + HibernateConstraintValidatorContext hibernateContext) { + hibernateContext.disableDefaultConstraintViolation(); + + hibernateContext.addExpressionVariable( "answer", 2 ); + hibernateContext.buildConstraintViolationWithTemplate( "answer 1: ${answer}" ) + .enableExpressionLanguage() + .addConstraintViolation(); + + // resetting the expression variables + hibernateContext.addExpressionVariable( "answer", 42 ); + hibernateContext.buildConstraintViolationWithTemplate( "answer 2: ${answer}" ) + .enableExpressionLanguage() + .addConstraintViolation(); + } + private void createMultipleConstraintViolationsUpdatingExpressionVariableValues(HibernateConstraintValidatorContext hibernateContext) { hibernateContext.disableDefaultConstraintViolation(); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/AbstractTokenCollectorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/AbstractTokenCollectorTest.java new file mode 100644 index 0000000000..004d5c9bd2 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/AbstractTokenCollectorTest.java @@ -0,0 +1,186 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.messageinterpolation; + +import org.hibernate.validator.internal.engine.messageinterpolation.InterpolationTermType; +import org.hibernate.validator.internal.engine.messageinterpolation.parser.MessageDescriptorFormatException; +import org.hibernate.validator.internal.engine.messageinterpolation.parser.Token; +import org.hibernate.validator.internal.engine.messageinterpolation.parser.TokenCollector; + +import org.assertj.core.api.Assertions; +import org.testng.annotations.Test; + +/** + * Abstract base for {@code TokenCollector} tests. + * + * @author Hardy Ferentschik + */ +public abstract class AbstractTokenCollectorTest { + + protected abstract InterpolationTermType getInterpolationTermType(); + + @Test + public void testLiteral() { + Assertions.assertThat( + new TokenCollector( "foo bar", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 1 ) + .element( 0 ) + .returns( "foo bar", Token::getTokenValue ) + .returns( false, Token::isParameter ); + } + + @Test(expectedExceptions = MessageDescriptorFormatException.class, expectedExceptionsMessageRegExp = "HV000169.*") + public void testNestedParametersThrowException() { + new TokenCollector( "#{foo {}", getInterpolationTermType() ); + } + + @Test(expectedExceptions = MessageDescriptorFormatException.class, expectedExceptionsMessageRegExp = "HV000168.*") + public void testClosingBraceWithoutOpeningBraceThrowsException() { + new TokenCollector( "foo}", getInterpolationTermType() ); + } + + @Test(expectedExceptions = MessageDescriptorFormatException.class, expectedExceptionsMessageRegExp = "HV000168.*") + public void testOpeningBraceWithoutClosingBraceThrowsException() { + new TokenCollector( "{foo", getInterpolationTermType() ); + } + + @Test + public void testBackslashEscapesNonMetaCharacter() { + Assertions.assertThat( + new TokenCollector( "foo \\bar", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 1 ) + .element( 0 ) + // Backslashes are removed later, in AbstractMessageInterpolator.replaceEscapedLiterals + .returns( "foo \\bar", Token::getTokenValue ) + .returns( false, Token::isParameter ); + } + + @Test + public void testBackslashEscapesDollar() { + Assertions.assertThat( + new TokenCollector( "foo \\$ bar", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 1 ) + .element( 0 ) + // Backslashes are removed later, in AbstractMessageInterpolator.replaceEscapedLiterals + .returns( "foo \\$ bar", Token::getTokenValue ) + .returns( false, Token::isParameter ); + } + + @Test + public void testBackslashEscapesOpeningBrace() { + Assertions.assertThat( + new TokenCollector( "foo \\{ bar", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 1 ) + .element( 0 ) + // Backslashes are removed later, in AbstractMessageInterpolator.replaceEscapedLiterals + .returns( "foo \\{ bar", Token::getTokenValue ) + .returns( false, Token::isParameter ); + } + + @Test + public void testBackslashEscapesClosingBrace() { + Assertions.assertThat( + new TokenCollector( "foo \\} bar", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 1 ) + .element( 0 ) + // Backslashes are removed later, in AbstractMessageInterpolator.replaceEscapedLiterals + .returns( "foo \\} bar", Token::getTokenValue ) + .returns( false, Token::isParameter ); + } + + @Test + public void testBackslashEscapesBackslash() { + Assertions.assertThat( + new TokenCollector( "foo \\\\ bar", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 1 ) + .element( 0 ) + // Backslashes are removed later, in AbstractMessageInterpolator.replaceEscapedLiterals + .returns( "foo \\\\ bar", Token::getTokenValue ) + .returns( false, Token::isParameter ); + } + + @Test + public void testBackslashEscapesEL() { + Assertions.assertThat( + new TokenCollector( "foo \\$\\{bar\\}", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 1 ) + .element( 0 ) + // Backslashes are removed later, in AbstractMessageInterpolator.replaceEscapedLiterals + .returns( "foo \\$\\{bar\\}", Token::getTokenValue ) + // What's important is that we did NOT detect the expression + .returns( false, Token::isParameter ); + } + + @Test + public void testBackslashEscapesParameter() { + Assertions.assertThat( + new TokenCollector( "foo \\{bar\\}", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 1 ) + .element( 0 ) + // Backslashes are removed later, in AbstractMessageInterpolator.replaceEscapedLiterals + .returns( "foo \\{bar\\}", Token::getTokenValue ) + // What's important is that we did NOT detect the parameter + .returns( false, Token::isParameter ); + } + + @Test(expectedExceptions = MessageDescriptorFormatException.class, expectedExceptionsMessageRegExp = "HV000168.*") + public void testTrailingClosingBraceThrowsException() { + new TokenCollector( "this message contains a invalid parameter start token {", getInterpolationTermType() ); + } + + @Test + public void testDollarThenNonMetaCharacterInterpretedAsLiteral() { + Assertions.assertThat( + new TokenCollector( "$a", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 1 ) + .element( 0 ) + .returns( "$a", Token::getTokenValue ) + .returns( false, Token::isParameter ); + } + + @Test + public void testTrailingDollarInterpretedAsLiteral() { + Assertions.assertThat( + new TokenCollector( "foo $", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 1 ) + .element( 0 ) + .returns( "foo $", Token::getTokenValue ) + .returns( false, Token::isParameter ); + } + + @Test + public void testTrailingBackslashInterpretedAsLiteral() { + Assertions.assertThat( + new TokenCollector( "foo \\", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 1 ) + .element( 0 ) + .returns( "foo \\", Token::getTokenValue ) + .returns( false, Token::isParameter ); + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ExpressionLanguageMessageInterpolationTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ExpressionLanguageMessageInterpolationTest.java index b80ccd14f1..6f75a91b19 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ExpressionLanguageMessageInterpolationTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ExpressionLanguageMessageInterpolationTest.java @@ -20,9 +20,9 @@ import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind; import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; +import org.hibernate.validator.messageinterpolation.ExpressionLanguageFeatureLevel; import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator; import org.hibernate.validator.testutil.TestForIssue; - import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; @@ -42,7 +42,7 @@ public void setUp() { // Create some annotations for testing using AnnotationProxies ConstraintAnnotationDescriptor.Builder notNullAnnotationDescriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( NotNull.class ); notNullDescriptor = new ConstraintDescriptorImpl<>( - new ConstraintHelper(), + ConstraintHelper.forAllBuiltinConstraints(), null, notNullAnnotationDescriptorBuilder.build(), ConstraintLocationKind.FIELD @@ -50,7 +50,7 @@ public void setUp() { ConstraintAnnotationDescriptor.Builder sizeAnnotationDescriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( Size.class ); sizeDescriptor = new ConstraintDescriptorImpl<>( - new ConstraintHelper(), + ConstraintHelper.forAllBuiltinConstraints(), null, sizeAnnotationDescriptorBuilder.build(), ConstraintLocationKind.FIELD @@ -69,13 +69,53 @@ public void testExpressionLanguageGraphNavigation() { null, null, Collections.emptyMap(), - Collections.emptyMap() ); + Collections.emptyMap(), + ExpressionLanguageFeatureLevel.BEAN_METHODS, + false ); + + String expected = "18"; + String actual = interpolatorUnderTest.interpolate( "${validatedValue.age}", context ); + assertEquals( actual, expected, "Wrong substitution" ); + } + + @Test + public void testExpressionLanguageGraphNavigationBeanProperties() { + User user = new User(); + user.setAge( 18 ); + MessageInterpolator.Context context = new MessageInterpolatorContext( + notNullDescriptor, + user, + null, + null, + Collections.emptyMap(), + Collections.emptyMap(), + ExpressionLanguageFeatureLevel.BEAN_PROPERTIES, + false ); String expected = "18"; String actual = interpolatorUnderTest.interpolate( "${validatedValue.age}", context ); assertEquals( actual, expected, "Wrong substitution" ); } + @Test + public void testExpressionLanguageGraphNavigationVariables() { + User user = new User(); + user.setAge( 18 ); + MessageInterpolator.Context context = new MessageInterpolatorContext( + notNullDescriptor, + user, + null, + null, + Collections.emptyMap(), + Collections.emptyMap(), + ExpressionLanguageFeatureLevel.VARIABLES, + false ); + + String expected = "${validatedValue.age}"; + String actual = interpolatorUnderTest.interpolate( "${validatedValue.age}", context ); + assertEquals( actual, expected, "Wrong substitution" ); + } + @Test public void testUnknownPropertyInExpressionLanguageGraphNavigation() { MessageInterpolator.Context context = new MessageInterpolatorContext( @@ -84,7 +124,9 @@ public void testUnknownPropertyInExpressionLanguageGraphNavigation() { null, null, Collections.emptyMap(), - Collections.emptyMap() ); + Collections.emptyMap(), + ExpressionLanguageFeatureLevel.BEAN_METHODS, + false ); String expected = "${validatedValue.foo}"; String actual = interpolatorUnderTest.interpolate( "${validatedValue.foo}", context ); @@ -93,7 +135,7 @@ public void testUnknownPropertyInExpressionLanguageGraphNavigation() { @Test public void testNullValidatedValue() { - MessageInterpolator.Context context = createMessageInterpolatorContext( notNullDescriptor ); + MessageInterpolator.Context context = createMessageInterpolatorContextELBeanMethods( notNullDescriptor ); String expected = "Validated value was null"; String actual = interpolatorUnderTest.interpolate( @@ -105,7 +147,7 @@ public void testNullValidatedValue() { @Test public void testExpressionAndParameterInterpolationInSameMessageDescriptor() { - MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor ); + MessageInterpolator.Context context = createMessageInterpolatorContextELBeanMethods( sizeDescriptor ); String expected = "2 0 2147483647"; String actual = interpolatorUnderTest.interpolate( "${1+1} {min} {max}", context ); @@ -114,7 +156,7 @@ public void testExpressionAndParameterInterpolationInSameMessageDescriptor() { @Test public void testEscapedExpressionLanguage() { - MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor ); + MessageInterpolator.Context context = createMessageInterpolatorContextELBeanMethods( sizeDescriptor ); String expected = "${1+1}"; String actual = interpolatorUnderTest.interpolate( "\\${1+1}", context ); @@ -123,7 +165,7 @@ public void testEscapedExpressionLanguage() { @Test public void testTernaryExpressionLanguageOperator() { - MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor ); + MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor, ExpressionLanguageFeatureLevel.VARIABLES ); String expected = "foo"; String actual = interpolatorUnderTest.interpolate( "${min == 0 ? 'foo' : 'bar'}", context ); @@ -132,7 +174,7 @@ public void testTernaryExpressionLanguageOperator() { @Test public void testParameterFormatting() { - MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor ); + MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor, ExpressionLanguageFeatureLevel.VARIABLES ); String expected = "Max 2147483647, min 0"; String actual = interpolatorUnderTest.interpolate( "${formatter.format('Max %s, min %s', max, min)}", context ); @@ -141,7 +183,7 @@ public void testParameterFormatting() { @Test public void testLiteralStaysUnchanged() { - MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor ); + MessageInterpolator.Context context = createMessageInterpolatorContextELBeanMethods( sizeDescriptor ); String expected = "foo"; String actual = interpolatorUnderTest.interpolate( "foo", context ); @@ -150,7 +192,7 @@ public void testLiteralStaysUnchanged() { @Test public void testLiteralBackslash() { - MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor ); + MessageInterpolator.Context context = createMessageInterpolatorContextELBeanMethods( sizeDescriptor ); String expected = "\\foo"; String actual = interpolatorUnderTest.interpolate( "\\foo", context ); @@ -159,7 +201,7 @@ public void testLiteralBackslash() { @Test public void testPrecedenceOfParameterInterpolation() { - MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor ); + MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor, ExpressionLanguageFeatureLevel.VARIABLES ); String expected = "$0"; String actual = interpolatorUnderTest.interpolate( "${min}", context ); @@ -174,7 +216,9 @@ public void testLocaleBasedFormatting() { null, null, Collections.emptyMap(), - Collections.emptyMap() ); + Collections.emptyMap(), + ExpressionLanguageFeatureLevel.VARIABLES, + false ); // german locale String expected = "42,00"; @@ -197,7 +241,7 @@ public void testLocaleBasedFormatting() { @Test public void testMissingFormatArgument() { - MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor ); + MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor, ExpressionLanguageFeatureLevel.VARIABLES ); String expected = "${formatter.format('%1$s')}"; String actual = interpolatorUnderTest.interpolate( "${formatter.format('%1$s')}", context ); @@ -210,7 +254,7 @@ public void testMissingFormatArgument() { @Test public void testNoParametersToFormatter() { - MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor ); + MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor, ExpressionLanguageFeatureLevel.VARIABLES ); String expected = "${formatter.format()}"; String actual = interpolatorUnderTest.interpolate( "${formatter.format()}", context ); @@ -219,13 +263,31 @@ public void testNoParametersToFormatter() { @Test public void testNonFormatterFunction() { - MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor ); + MessageInterpolator.Context context = createMessageInterpolatorContextELBeanMethods( sizeDescriptor ); String expected = "foo"; String actual = interpolatorUnderTest.interpolate( "${'foobar'.substring(0,3)}", context ); assertEquals( actual, expected, "Calling of String#substring should work" ); } + @Test + public void testNonFormatterFunctionVariables() { + MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor, ExpressionLanguageFeatureLevel.VARIABLES ); + + String expected = "${'foobar'.substring(0,3)}"; + String actual = interpolatorUnderTest.interpolate( "${'foobar'.substring(0,3)}", context ); + assertEquals( actual, expected, "Calling of String#substring should work" ); + } + + @Test + public void testNonFormatterFunctionBeanProperties() { + MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor, ExpressionLanguageFeatureLevel.BEAN_PROPERTIES ); + + String expected = "${'foobar'.substring(0,3)}"; + String actual = interpolatorUnderTest.interpolate( "${'foobar'.substring(0,3)}", context ); + assertEquals( actual, expected, "Calling of String#substring should work" ); + } + @Test public void testCallingWrongFormatterMethod() { MessageInterpolator.Context context = new MessageInterpolatorContext( @@ -234,7 +296,9 @@ public void testCallingWrongFormatterMethod() { null, null, Collections.emptyMap(), - Collections.emptyMap() ); + Collections.emptyMap(), + ExpressionLanguageFeatureLevel.BEAN_METHODS, + false ); String expected = "${formatter.foo('%1$.2f', validatedValue)}"; String actual = interpolatorUnderTest.interpolate( @@ -252,7 +316,7 @@ public void testCallingWrongFormatterMethod() { @Test @TestForIssue(jiraKey = "HV-834") public void testOpeningCurlyBraceInELExpression() { - MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor ); + MessageInterpolator.Context context = createMessageInterpolatorContextELBeanMethods( sizeDescriptor ); String expected = "{"; String actual = interpolatorUnderTest.interpolate( "${1 > 0 ? '\\{' : '\\}'}", context ); @@ -262,7 +326,7 @@ public void testOpeningCurlyBraceInELExpression() { @Test @TestForIssue(jiraKey = "HV-834") public void testClosingCurlyBraceInELExpression() { - MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor ); + MessageInterpolator.Context context = createMessageInterpolatorContextELBeanMethods( sizeDescriptor ); String expected = "}"; String actual = interpolatorUnderTest.interpolate( "${1 < 0 ? '\\{' : '\\}'}", context ); @@ -272,7 +336,7 @@ public void testClosingCurlyBraceInELExpression() { @Test @TestForIssue(jiraKey = "HV-834") public void testCurlyBracesInELExpression() { - MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor ); + MessageInterpolator.Context context = createMessageInterpolatorContextELBeanMethods( sizeDescriptor ); String expected = "a{b}d"; String actual = interpolatorUnderTest.interpolate( "${1 < 0 ? 'foo' : 'a\\{b\\}d'}", context ); @@ -282,7 +346,7 @@ public void testCurlyBracesInELExpression() { @Test @TestForIssue(jiraKey = "HV-834") public void testEscapedQuoteInELExpression() { - MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor ); + MessageInterpolator.Context context = createMessageInterpolatorContextELBeanMethods( sizeDescriptor ); String expected = "\""; String actual = interpolatorUnderTest.interpolate( "${ true ? \"\\\"\" : \"foo\"}", context ); @@ -292,7 +356,7 @@ public void testEscapedQuoteInELExpression() { @Test @TestForIssue(jiraKey = "HV-834") public void testSingleEscapedQuoteInELExpression() { - MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor ); + MessageInterpolator.Context context = createMessageInterpolatorContextELBeanMethods( sizeDescriptor ); String expected = "'"; String actual = interpolatorUnderTest.interpolate( "${ false ? 'foo' : '\\''}", context ); @@ -303,13 +367,20 @@ public void testSingleEscapedQuoteInELExpression() { ); } - private MessageInterpolatorContext createMessageInterpolatorContext(ConstraintDescriptorImpl descriptor) { + private MessageInterpolatorContext createMessageInterpolatorContextELBeanMethods(ConstraintDescriptorImpl descriptor) { + return createMessageInterpolatorContext( descriptor, ExpressionLanguageFeatureLevel.BEAN_METHODS ); + } + + private MessageInterpolatorContext createMessageInterpolatorContext(ConstraintDescriptorImpl descriptor, + ExpressionLanguageFeatureLevel expressionLanguageFeatureLevel) { return new MessageInterpolatorContext( descriptor, null, null, null, Collections.emptyMap(), - Collections.emptyMap() ); + Collections.emptyMap(), + expressionLanguageFeatureLevel, + false ); } } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/MessageInterpolatorContextTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/MessageInterpolatorContextTest.java index c989b6b3ad..ab24a4675d 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/MessageInterpolatorContextTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/MessageInterpolatorContextTest.java @@ -8,6 +8,7 @@ package org.hibernate.validator.test.internal.engine.messageinterpolation; import org.hibernate.validator.internal.engine.MessageInterpolatorContext; +import org.hibernate.validator.messageinterpolation.ExpressionLanguageFeatureLevel; import org.hibernate.validator.messageinterpolation.HibernateMessageInterpolatorContext; import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator; import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator; @@ -92,7 +93,9 @@ public void testContextWithRightDescriptorAndValueAndRootBeanClassIsPassedToMess TestBean.class, null, Collections.emptyMap(), - Collections.emptyMap() ) + Collections.emptyMap(), + ExpressionLanguageFeatureLevel.BEAN_METHODS, + false ) ) ) .andReturn( "invalid" ); @@ -109,13 +112,15 @@ public void testContextWithRightDescriptorAndValueAndRootBeanClassIsPassedToMess @Test(expectedExceptions = ValidationException.class) public void testUnwrapToImplementationCausesValidationException() { - Context context = new MessageInterpolatorContext( null, null, null, null, Collections.emptyMap(), Collections.emptyMap() ); + Context context = new MessageInterpolatorContext( null, null, null, null, Collections.emptyMap(), + Collections.emptyMap(), ExpressionLanguageFeatureLevel.BEAN_METHODS, false ); context.unwrap( MessageInterpolatorContext.class ); } @Test public void testUnwrapToInterfaceTypesSucceeds() { - Context context = new MessageInterpolatorContext( null, null, null, null, Collections.emptyMap(), Collections.emptyMap() ); + Context context = new MessageInterpolatorContext( null, null, null, null, Collections.emptyMap(), + Collections.emptyMap(), ExpressionLanguageFeatureLevel.BEAN_METHODS, false ); MessageInterpolator.Context asMessageInterpolatorContext = context.unwrap( MessageInterpolator.Context.class ); assertSame( asMessageInterpolatorContext, context ); @@ -138,7 +143,9 @@ public void testGetRootBeanType() { rootBeanType, null, Collections.emptyMap(), - Collections.emptyMap() ); + Collections.emptyMap(), + ExpressionLanguageFeatureLevel.BEAN_METHODS, + false ); assertSame( context.unwrap( HibernateMessageInterpolatorContext.class ).getRootBeanType(), rootBeanType ); } @@ -153,7 +160,9 @@ public void testGetPropertyPath() { null, pathMock, Collections.emptyMap(), - Collections.emptyMap() ); + Collections.emptyMap(), + ExpressionLanguageFeatureLevel.BEAN_METHODS, + false ); assertSame( context.unwrap( HibernateMessageInterpolatorContext.class ).getPropertyPath(), pathMock ); } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ParameterMessageInterpolatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ParameterMessageInterpolatorTest.java index 210fe9867b..9557f87236 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ParameterMessageInterpolatorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ParameterMessageInterpolatorTest.java @@ -9,6 +9,7 @@ import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; import static org.hibernate.validator.testutils.ValidatorUtil.getConfiguration; +import static org.testng.Assert.assertTrue; import java.util.Set; @@ -16,11 +17,13 @@ import javax.validation.Validator; import javax.validation.constraints.Size; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.core.Logger; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.test.appender.ListAppender; import org.hibernate.validator.messageinterpolation.ParameterMessageInterpolator; -import org.hibernate.validator.testutil.MessageLoggedAssertionLogger; import org.hibernate.validator.testutil.TestForIssue; - -import org.apache.log4j.Logger; +import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; @@ -34,14 +37,26 @@ public class ParameterMessageInterpolatorTest { Validator validator; + ListAppender listAppender; + @BeforeTest public void setUp() { + LoggerContext context = LoggerContext.getContext( false ); + Logger logger = context.getLogger( ParameterMessageInterpolator.class.getName() ); + listAppender = (ListAppender) logger.getAppenders().get( "List" ); + listAppender.clear(); + validator = getConfiguration() .messageInterpolator( new ParameterMessageInterpolator() ) .buildValidatorFactory() .getValidator(); } + @AfterTest + public void tearDown() { + listAppender.clear(); + } + @Test public void testParameterMessageInterpolatorInterpolatesParameters() { Foo foo = new Foo(); @@ -55,20 +70,17 @@ public void testParameterMessageInterpolatorInterpolatesParameters() { @Test public void testParameterMessageInterpolatorIgnoresELExpressions() { - Logger log4jRootLogger = Logger.getRootLogger(); - MessageLoggedAssertionLogger assertingLogger = new MessageLoggedAssertionLogger( "HV000185" ); - log4jRootLogger.addAppender( assertingLogger ); - Foo foo = new Foo(); Set> constraintViolations = validator.validateProperty( foo, "bar" ); assertThat( constraintViolations ).containsOnlyViolations( violationOf( Size.class ) .withProperty( "bar" ) - .withMessage( "${validatedValue}" ) - ); + .withMessage( "${validatedValue}" ) ); - assertingLogger.assertMessageLogged(); - log4jRootLogger.removeAppender( assertingLogger ); + assertTrue( listAppender.getEvents().stream() + .filter( event -> event.getLevel().equals( Level.WARN ) ) + .map( event -> event.getMessage().getFormattedMessage() ) + .anyMatch( m -> m.startsWith( "HV000185" ) ) ); } public static class Foo { diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ParameterTermResolverTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ParameterTermResolverTest.java new file mode 100644 index 0000000000..8f6feb5f42 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ParameterTermResolverTest.java @@ -0,0 +1,160 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.messageinterpolation; + +import static org.testng.Assert.assertEquals; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import javax.validation.MessageInterpolator; +import javax.validation.metadata.ConstraintDescriptor; + +import org.hibernate.validator.internal.engine.messageinterpolation.ParameterTermResolver; +import org.hibernate.validator.messageinterpolation.HibernateMessageInterpolatorContext; +import org.hibernate.validator.testutil.TestForIssue; + +import org.easymock.EasyMock; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test for {@link org.hibernate.validator.internal.engine.messageinterpolation.ParameterTermResolver} + * + * @author Alexander Gatsenko + */ +public class ParameterTermResolverTest { + + private final ParameterTermResolver resolver = new ParameterTermResolver(); + + @DataProvider(name = "interpolateByNotArrayValueArgs") + public static Object[][] interpolateByNotArrayValueArgs() { + // lines of (String variableName, Object variableValue, String expectedResolvedExpression) + return new Object[][] { + { "value", null, "{value}" }, + { "value", true, "true" }, + { "value", false, "false" }, + { "value", 'a', "a" }, + { "value", (byte) 10, "10" }, + { "value", (short) 10, "10" }, + { "value", 10, "10" }, + { "value", 10L, "10" }, + { "value", 10.1, "10.1" }, + { "value", 10.1f, "10.1" }, + { "value", "string value", "string value" }, + }; + } + + @DataProvider(name = "interpolateByArrayValueArgs") + public static Object[][] interpolateByArrayValueArgs() { + // lines of (String variableName, variableValueArray, String expectedResolvedExpression) + return new Object[][] { + { "value", new boolean[] { true, false }, Arrays.toString( new boolean[] { true, false } ) }, + { "value", new char[] { 'a', 'b' }, Arrays.toString( new char[] { 'a', 'b' } ) }, + { "value", new byte[] { 1, 2 }, Arrays.toString( new byte[] { 1, 2 } ) }, + { "value", new short[] { 1, 2 }, Arrays.toString( new short[] { 1, 2 } ) }, + { "value", new int[] { 1, 2 }, Arrays.toString( new int[] { 1, 2 } ) }, + { "value", new long[] { 1, 2 }, Arrays.toString( new long[] { 1, 2 } ) }, + { "value", new double[] { 1.2, 3.4 }, Arrays.toString( new double[] { 1.2, 3.4 } ) }, + { "value", new float[] { 1.2F, 3.4F }, Arrays.toString( new float[] { 1.2F, 3.4F } ) }, + { "value", new String[] { "one", "two" }, Arrays.toString( new String[] { "one", "two" } ) }, + }; + } + + @Test(dataProvider = "interpolateByNotArrayValueArgs") + public void testInterpolateShouldResolveExpressionByNotArrayValue( + String variableName, + Object variableValue, + String expectedResolvedExpression) { + final MessageInterpolator.Context context = createHibernateContextWithConstraintDescriptorAttr( + variableName, + variableValue + ); + final String srcExpression = createVariableExpression( variableName ); + + final String actualResolvedExpression = resolver.interpolate( context, srcExpression ); + assertEquals( actualResolvedExpression, expectedResolvedExpression ); + } + + @Test(dataProvider = "interpolateByArrayValueArgs") + @TestForIssue(jiraKey = "HV-1761") + public void testInterpolateShouldResolveExpressionByArrayValue( + String variableName, + Object variableValueArray, + String expectedResolvedExpression) { + final MessageInterpolator.Context context = createHibernateContextWithConstraintDescriptorAttr( + variableName, + variableValueArray + ); + final String srcExpression = createVariableExpression( variableName ); + + final String actualResolvedExpression = resolver.interpolate( context, srcExpression ); + assertEquals( actualResolvedExpression, expectedResolvedExpression ); + } + + @Test(dataProvider = "interpolateByNotArrayValueArgs") + public void testInterpolateShouldAllowUseNotHibernateContext( + String variableName, + Object variableValue, + String expectedResolvedExpression) { + final MessageInterpolator.Context context = createNotHibernateContextWithConstraintDescriptorAttr( + variableName, + variableValue + ); + final String srcExpression = createVariableExpression( variableName ); + + final String actualResolvedExpression = resolver.interpolate( context, srcExpression ); + assertEquals( actualResolvedExpression, expectedResolvedExpression ); + } + + private static String createVariableExpression(String variableName) { + return String.format( "{%s}", variableName ); + } + + private static Map createConstraintDescriptorAttr(String attrName, Object attrValue) { + Map map = new HashMap<>( 1 ); + map.put( attrName, attrValue ); + return map; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private static MessageInterpolator.Context createNotHibernateContextWithConstraintDescriptorAttr( + String attrName, + Object attrValue) { + final Map attrs = createConstraintDescriptorAttr( attrName, attrValue ); + + final MessageInterpolator.Context context = EasyMock.mock( MessageInterpolator.Context.class ); + final ConstraintDescriptor constraintDescriptor = EasyMock.mock( ConstraintDescriptor.class ); + + EasyMock.expect( context.getConstraintDescriptor() ).andStubReturn( constraintDescriptor ); + EasyMock.expect( constraintDescriptor.getAttributes() ).andStubReturn( attrs ); + + EasyMock.replay( context ); + EasyMock.replay( constraintDescriptor ); + + return context; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private static MessageInterpolator.Context createHibernateContextWithConstraintDescriptorAttr( + String attrName, + Object attrValue) { + final Map attrs = createConstraintDescriptorAttr( attrName, attrValue ); + + final HibernateMessageInterpolatorContext context = EasyMock.mock( HibernateMessageInterpolatorContext.class ); + final ConstraintDescriptor constraintDescriptor = EasyMock.mock( ConstraintDescriptor.class ); + + EasyMock.expect( context.getMessageParameters() ).andStubReturn( attrs ); + EasyMock.expect( context.getConstraintDescriptor() ).andStubReturn( constraintDescriptor ); + EasyMock.expect( constraintDescriptor.getAttributes() ).andStubReturn( attrs ); + + EasyMock.replay( context ); + EasyMock.replay( constraintDescriptor ); + + return context; + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ResourceBundleMessageInterpolatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ResourceBundleMessageInterpolatorTest.java index 5636bafda4..68678d1ba1 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ResourceBundleMessageInterpolatorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ResourceBundleMessageInterpolatorTest.java @@ -29,6 +29,7 @@ import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind; import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; +import org.hibernate.validator.messageinterpolation.ExpressionLanguageFeatureLevel; import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator; import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator; import org.hibernate.validator.testutil.TestForIssue; @@ -52,7 +53,7 @@ public void setUp() { // Create some annotations for testing using AnnotationProxies ConstraintAnnotationDescriptor.Builder descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( NotNull.class ); notNullDescriptor = new ConstraintDescriptorImpl<>( - new ConstraintHelper(), + ConstraintHelper.forAllBuiltinConstraints(), null, descriptorBuilder.build(), ConstraintLocationKind.FIELD @@ -60,7 +61,7 @@ public void setUp() { ConstraintAnnotationDescriptor.Builder sizeAnnotationDescriptorBuilder = new ConstraintAnnotationDescriptor.Builder( Size.class ); sizeDescriptor = new ConstraintDescriptorImpl<>( - new ConstraintHelper(), + ConstraintHelper.forAllBuiltinConstraints(), null, sizeAnnotationDescriptorBuilder.build(), ConstraintLocationKind.FIELD @@ -214,7 +215,7 @@ public void testRecursiveMessageInterpolation() { ConstraintAnnotationDescriptor descriptor = descriptorBuilder.build(); ConstraintDescriptorImpl constraintDescriptor = new ConstraintDescriptorImpl( - new ConstraintHelper(), + ConstraintHelper.forAllBuiltinConstraints(), null, descriptorBuilder.build(), ConstraintLocationKind.FIELD @@ -243,7 +244,7 @@ public void testCorrectMessageInterpolationIfParameterCannotBeReplaced() { ConstraintAnnotationDescriptor maxDescriptor = descriptorBuilder.build(); ConstraintDescriptorImpl constraintDescriptor = new ConstraintDescriptorImpl( - new ConstraintHelper(), + ConstraintHelper.forAllBuiltinConstraints(), null, maxDescriptor, ConstraintLocationKind.FIELD @@ -281,7 +282,9 @@ private MessageInterpolatorContext createMessageInterpolatorContext(ConstraintDe null, null, Collections.emptyMap(), - Collections.emptyMap() ); + Collections.emptyMap(), + ExpressionLanguageFeatureLevel.BEAN_METHODS, + false ); } private void runInterpolation(boolean cachingEnabled) { diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/TokenCollectorMessageExpressionTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/TokenCollectorMessageExpressionTest.java new file mode 100644 index 0000000000..229e34174e --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/TokenCollectorMessageExpressionTest.java @@ -0,0 +1,110 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.messageinterpolation; + +import org.hibernate.validator.internal.engine.messageinterpolation.InterpolationTermType; +import org.hibernate.validator.internal.engine.messageinterpolation.parser.MessageDescriptorFormatException; +import org.hibernate.validator.internal.engine.messageinterpolation.parser.Token; +import org.hibernate.validator.internal.engine.messageinterpolation.parser.TokenCollector; + +import org.assertj.core.api.Assertions; +import org.assertj.core.api.ListAssert; +import org.testng.annotations.Test; + +/** + * Tests for {@code TokenCollector} in message expression mode. + * + * @author Hardy Ferentschik + */ +public class TokenCollectorMessageExpressionTest extends AbstractTokenCollectorTest { + @Override + protected InterpolationTermType getInterpolationTermType() { + return InterpolationTermType.EL; + } + + // Several tests inherited from the abstract class + + @Test + public void testMessageParameter() { + ListAssert assertion = Assertions.assertThat( + new TokenCollector( "foo {bar}", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 2 ); + assertion.element( 0 ) + .returns( "foo ", Token::getTokenValue ) + .returns( false, Token::isParameter ); + assertion.element( 1 ) + .returns( "{bar}", Token::getTokenValue ) + .returns( false, Token::isParameter ); + } + + @Test + public void testMessageExpression() { + ListAssert assertion = Assertions.assertThat( + new TokenCollector( "foo ${bar}", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 2 ); + assertion.element( 0 ) + .returns( "foo ", Token::getTokenValue ) + .returns( false, Token::isParameter ); + assertion.element( 1 ) + .returns( "${bar}", Token::getTokenValue ) + .returns( true, Token::isParameter ); + } + + @Test + public void testDollarThenDollarThenParameterInterpretedAsLiterals() { + ListAssert assertion = Assertions.assertThat( + new TokenCollector( "$${1+1}", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 2 ); + assertion.element( 0 ) + .returns( "$$", Token::getTokenValue ) + .returns( false, Token::isParameter ); + assertion.element( 1 ) + .returns( "{1+1}", Token::getTokenValue ) + .returns( false, Token::isParameter ); + } + + @Test + public void testDollarThenDollarThenLiteralsInterpretedAsLiterals() { + ListAssert assertion = Assertions.assertThat( + new TokenCollector( "$$foo", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 2 ); + assertion.element( 0 ) + .returns( "$$", Token::getTokenValue ) + .returns( false, Token::isParameter ); + assertion.element( 1 ) + .returns( "foo", Token::getTokenValue ) + .returns( false, Token::isParameter ); + } + + @Test(expectedExceptions = MessageDescriptorFormatException.class, expectedExceptionsMessageRegExp = "HV000168.*") + public void testDollarThenClosingBraceThrowsException() { + new TokenCollector( "$}", getInterpolationTermType() ); + } + + @Test + public void testDollarThenEscapeInterpretedAsLiterals() { + ListAssert assertion = Assertions.assertThat( + new TokenCollector( "$\\A{1+1}", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 2 ); + assertion.element( 0 ) + .returns( "$\\A", Token::getTokenValue ) + .returns( false, Token::isParameter ); + assertion.element( 1 ) + .returns( "{1+1}", Token::getTokenValue ) + .returns( false, Token::isParameter ); + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/TokenCollectorMessageParameterTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/TokenCollectorMessageParameterTest.java new file mode 100644 index 0000000000..9189f496b3 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/TokenCollectorMessageParameterTest.java @@ -0,0 +1,115 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.messageinterpolation; + +import org.hibernate.validator.internal.engine.messageinterpolation.InterpolationTermType; +import org.hibernate.validator.internal.engine.messageinterpolation.parser.MessageDescriptorFormatException; +import org.hibernate.validator.internal.engine.messageinterpolation.parser.Token; +import org.hibernate.validator.internal.engine.messageinterpolation.parser.TokenCollector; + +import org.assertj.core.api.Assertions; +import org.assertj.core.api.ListAssert; +import org.testng.annotations.Test; + +/** + * Tests for {@code TokenCollector} in message parameter mode. + * + * @author Hardy Ferentschik + */ +public class TokenCollectorMessageParameterTest extends AbstractTokenCollectorTest { + @Override + protected InterpolationTermType getInterpolationTermType() { + return InterpolationTermType.PARAMETER; + } + + // Several tests inherited from the abstract class + + @Test + public void testMessageParameter() { + ListAssert assertion = Assertions.assertThat( + new TokenCollector( "foo {bar}", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 2 ); + assertion.element( 0 ) + .returns( "foo ", Token::getTokenValue ) + .returns( false, Token::isParameter ); + assertion.element( 1 ) + .returns( "{bar}", Token::getTokenValue ) + .returns( true, Token::isParameter ); + } + + @Test + public void testMessageExpression() { + ListAssert assertion = Assertions.assertThat( + new TokenCollector( "foo ${bar}", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 2 ); + /* + * 6.3.1.1: + * Parameter interpolation has precedence over message expressions. + * For example for the message descriptor ${value}, + * trying to evaluate {value} as message parameter has precedence + * over evaluating ${value} as message expression. + */ + assertion.element( 0 ) + .returns( "foo $", Token::getTokenValue ) + .returns( false, Token::isParameter ); + assertion.element( 1 ) + .returns( "{bar}", Token::getTokenValue ) + .returns( true, Token::isParameter ); + } + + @Test + public void testDollarThenDollarThenParameterInterpretedAsLiteralAndParameter() { + ListAssert assertion = Assertions.assertThat( + new TokenCollector( "$${1+1}", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 2 ); + assertion.element( 0 ) + .returns( "$$", Token::getTokenValue ) + .returns( false, Token::isParameter ); + assertion.element( 1 ) + .returns( "{1+1}", Token::getTokenValue ) + .returns( true, Token::isParameter ); + } + + @Test + public void testDollarThenDollarThenLiteralsInterpretedAsLiterals() { + ListAssert assertion = Assertions.assertThat( + new TokenCollector( "$$foo", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 1 ); + assertion.element( 0 ) + .returns( "$$foo", Token::getTokenValue ) + .returns( false, Token::isParameter ); + } + + @Test(expectedExceptions = MessageDescriptorFormatException.class, expectedExceptionsMessageRegExp = "HV000168.*") + public void testDollarThenClosingBraceThrowsException() { + // Fails because of the dangling closing brace; the dollar sign is irrelevant + new TokenCollector( "$}", getInterpolationTermType() ); + } + + @Test + public void testDollarThenEscapeInterpretedAsLiterals() { + ListAssert assertion = Assertions.assertThat( + new TokenCollector( "$\\A{1+1}", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 2 ); + assertion.element( 0 ) + .returns( "$\\A", Token::getTokenValue ) + .returns( false, Token::isParameter ); + assertion.element( 1 ) + .returns( "{1+1}", Token::getTokenValue ) + .returns( true, Token::isParameter ); + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/TokenCollectorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/TokenCollectorTest.java deleted file mode 100644 index 972a9e0513..0000000000 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/TokenCollectorTest.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Hibernate Validator, declare and validate application constraints - * - * License: Apache License, Version 2.0 - * See the license.txt file in the root directory or . - */ -package org.hibernate.validator.test.internal.engine.messageinterpolation; - -import org.hibernate.validator.internal.engine.messageinterpolation.InterpolationTermType; -import org.hibernate.validator.internal.engine.messageinterpolation.parser.MessageDescriptorFormatException; -import org.hibernate.validator.internal.engine.messageinterpolation.parser.TokenCollector; -import org.testng.annotations.Test; - -/** - * Tests for {@code TokenCollector}. - * - * @author Hardy Ferentschik - */ -public class TokenCollectorTest { - - @Test(expectedExceptions = MessageDescriptorFormatException.class, expectedExceptionsMessageRegExp = "HV000169.*") - public void testNestedParametersThrowException() throws Exception { - new TokenCollector( "#{foo {}", InterpolationTermType.PARAMETER ); - } - - @Test(expectedExceptions = MessageDescriptorFormatException.class, expectedExceptionsMessageRegExp = "HV000168.*") - public void testParameterWithoutOpeningBraceThrowsException() throws Exception { - new TokenCollector( "foo}", InterpolationTermType.PARAMETER ); - } - - @Test(expectedExceptions = MessageDescriptorFormatException.class, expectedExceptionsMessageRegExp = "HV000168.*") - public void testELExpressionWithoutOpeningBraceThrowsException() throws Exception { - new TokenCollector( "$}", InterpolationTermType.EL ); - } - - @Test(expectedExceptions = MessageDescriptorFormatException.class, expectedExceptionsMessageRegExp = "HV000168.*") - public void testTermWithoutClosingBraceThrowsException() throws Exception { - new TokenCollector( "{foo", InterpolationTermType.PARAMETER ); - } - - @Test(expectedExceptions = MessageDescriptorFormatException.class, expectedExceptionsMessageRegExp = "HV000168.*") - public void testSingleClosingBraceThrowsException() throws Exception { - new TokenCollector( "this message contains a invalid parameter start token {", InterpolationTermType.EL ); - } -} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/AbstractMethodValidationTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/AbstractMethodValidationTest.java index b0b5a21750..5ab2de4dd5 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/AbstractMethodValidationTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/AbstractMethodValidationTest.java @@ -52,7 +52,8 @@ */ @Test public abstract class AbstractMethodValidationTest { - protected CustomerRepository customerRepository; + protected CustomerRepository customerRepositoryOriginalBean; + protected CustomerRepository customerRepositoryValidatingProxy; protected RepositoryBase repositoryBase; protected Validator validator; @@ -61,16 +62,17 @@ public abstract class AbstractMethodValidationTest { protected abstract String messagePrefix(); protected void createProxy(Class... groups) { - customerRepository = getValidatingProxy( - new CustomerRepositoryImpl(), validator, groups + customerRepositoryOriginalBean = new CustomerRepositoryImpl(); + customerRepositoryValidatingProxy = getValidatingProxy( + customerRepositoryOriginalBean, validator, groups ); - repositoryBase = customerRepository; + repositoryBase = customerRepositoryValidatingProxy; } @Test public void methodValidationYieldsConstraintViolation() { try { - customerRepository.findCustomerByName( null ); + customerRepositoryValidatingProxy.findCustomerByName( null ); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -92,13 +94,13 @@ public void methodValidationYieldsConstraintViolation() { assertMethod( constraintViolation, "findCustomerByName", String.class ); assertParameterIndex( constraintViolation, 0 ); assertMethodValidationType( constraintViolation, ElementKind.PARAMETER ); - assertEquals( constraintViolation.getRootBean(), customerRepository ); + assertEquals( constraintViolation.getRootBean(), customerRepositoryOriginalBean ); assertEquals( constraintViolation.getRootBeanClass(), CustomerRepositoryImpl.class ); assertEquals( constraintViolation.getPropertyPath().toString(), "findCustomerByName.name" ); - assertEquals( constraintViolation.getLeafBean(), customerRepository ); + assertEquals( constraintViolation.getLeafBean(), customerRepositoryOriginalBean ); assertEquals( constraintViolation.getInvalidValue(), null ); assertEquals( constraintViolation.getExecutableParameters(), new Object[] { null } ); assertEquals( constraintViolation.getExecutableReturnValue(), null ); @@ -108,7 +110,7 @@ public void methodValidationYieldsConstraintViolation() { @Test public void validationOfMethodWithMultipleParameters() { try { - customerRepository.findCustomerByAgeAndName( 30, null ); + customerRepositoryValidatingProxy.findCustomerByAgeAndName( 30, null ); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -141,7 +143,7 @@ public void validationOfMethodWithMultipleParameters() { @Test public void constraintViolationsAtMultipleParameters() { try { - customerRepository.findCustomerByAgeAndName( 1, null ); + customerRepositoryValidatingProxy.findCustomerByAgeAndName( 1, null ); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -170,7 +172,7 @@ public void constraintViolationsAtMultipleParameters() { public void methodValidationWithCascadingParameter() { Customer customer = new Customer( null, null ); try { - customerRepository.persistCustomer( customer ); + customerRepositoryValidatingProxy.persistCustomer( customer ); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -195,7 +197,7 @@ public void methodValidationWithCascadingParameter() { constraintViolation.getPropertyPath().toString(), "persistCustomer.customer.name" ); assertEquals( constraintViolation.getRootBeanClass(), CustomerRepositoryImpl.class ); - assertEquals( constraintViolation.getRootBean(), customerRepository ); + assertEquals( constraintViolation.getRootBean(), customerRepositoryOriginalBean ); assertEquals( constraintViolation.getLeafBean(), customer ); assertEquals( constraintViolation.getInvalidValue(), null ); assertEquals( constraintViolation.getExecutableParameters(), new Object[] { customer } ); @@ -209,7 +211,7 @@ public void methodValidationWithCascadingParameterAndCascadingConstraint() { Customer customer = new Customer( "Bob", address ); try { - customerRepository.persistCustomer( customer ); + customerRepositoryValidatingProxy.persistCustomer( customer ); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -236,7 +238,7 @@ public void methodValidationWithCascadingParameterAndCascadingConstraint() { "persistCustomer.customer.address.city" ); assertEquals( constraintViolation.getRootBeanClass(), CustomerRepositoryImpl.class ); - assertEquals( constraintViolation.getRootBean(), customerRepository ); + assertEquals( constraintViolation.getRootBean(), customerRepositoryOriginalBean ); assertEquals( constraintViolation.getLeafBean(), address ); assertEquals( constraintViolation.getInvalidValue(), null ); assertEquals( constraintViolation.getExecutableParameters(), new Object[] { customer } ); @@ -251,7 +253,7 @@ public void cascadingMapParameter() { customers.put( "Bob", bob ); try { - customerRepository.cascadingMapParameter( customers ); + customerRepositoryValidatingProxy.cascadingMapParameter( customers ); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -277,7 +279,7 @@ public void cascadingMapParameter() { "cascadingMapParameter.customer[Bob].name" ); assertEquals( constraintViolation.getRootBeanClass(), CustomerRepositoryImpl.class ); - assertEquals( constraintViolation.getRootBean(), customerRepository ); + assertEquals( constraintViolation.getRootBean(), customerRepositoryOriginalBean ); assertEquals( constraintViolation.getLeafBean(), bob ); assertEquals( constraintViolation.getInvalidValue(), null ); assertEquals( constraintViolation.getExecutableParameters(), new Object[] { customers } ); @@ -291,7 +293,7 @@ public void cascadingIterableParameter() { List customers = Arrays.asList( null, customer ); try { - customerRepository.cascadingIterableParameter( customers ); + customerRepositoryValidatingProxy.cascadingIterableParameter( customers ); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -317,7 +319,7 @@ public void cascadingIterableParameter() { "cascadingIterableParameter.customer[1].name" ); assertEquals( constraintViolation.getRootBeanClass(), CustomerRepositoryImpl.class ); - assertEquals( constraintViolation.getRootBean(), customerRepository ); + assertEquals( constraintViolation.getRootBean(), customerRepositoryOriginalBean ); assertEquals( constraintViolation.getLeafBean(), customer ); assertEquals( constraintViolation.getInvalidValue(), null ); assertEquals( constraintViolation.getExecutableParameters(), new Object[] { customers } ); @@ -331,7 +333,7 @@ public void cascadingArrayParameter() { Customer customer = new Customer( null ); try { - customerRepository.cascadingArrayParameter( null, customer ); + customerRepositoryValidatingProxy.cascadingArrayParameter( null, customer ); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -357,7 +359,7 @@ public void cascadingArrayParameter() { "cascadingArrayParameter.customer[1].name" ); assertEquals( constraintViolation.getRootBeanClass(), CustomerRepositoryImpl.class ); - assertEquals( constraintViolation.getRootBean(), customerRepository ); + assertEquals( constraintViolation.getRootBean(), customerRepositoryOriginalBean ); assertEquals( constraintViolation.getLeafBean(), customer ); assertEquals( constraintViolation.getInvalidValue(), null ); assertEquals( @@ -371,7 +373,7 @@ public void cascadingArrayParameter() { @Test public void constraintsAtMethodFromBaseClassAreEvaluated() { try { - customerRepository.findById( null ); + customerRepositoryValidatingProxy.findById( null ); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -398,7 +400,7 @@ public void constraintsAtMethodFromBaseClassAreEvaluated() { @Test public void constraintsAtOverriddenMethodAreEvaluated() { try { - customerRepository.foo( null ); + customerRepositoryValidatingProxy.foo( null ); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -425,7 +427,7 @@ public void constraintsAtOverriddenMethodAreEvaluated() { @Test public void validFromOverriddenMethodIsEvaluated() { try { - customerRepository.bar( new Customer( null, null ) ); + customerRepositoryValidatingProxy.bar( new Customer( null, null ) ); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -453,13 +455,13 @@ public void validFromOverriddenMethodIsEvaluated() { @Test public void parameterValidationOfParameterlessMethod() { - customerRepository.boz(); + customerRepositoryValidatingProxy.boz(); } @Test public void returnValueValidationYieldsConstraintViolation() { try { - customerRepository.baz(); + customerRepositoryValidatingProxy.baz(); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -478,10 +480,10 @@ public void returnValueValidationYieldsConstraintViolation() { assertEquals( constraintViolation.getMessage(), messagePrefix() + "must be greater than or equal to 10" ); assertMethod( constraintViolation, "baz" ); assertMethodValidationType( constraintViolation, ElementKind.RETURN_VALUE ); - assertEquals( constraintViolation.getRootBean(), customerRepository ); + assertEquals( constraintViolation.getRootBean(), customerRepositoryOriginalBean ); assertEquals( constraintViolation.getRootBeanClass(), CustomerRepositoryImpl.class ); assertEquals( constraintViolation.getPropertyPath().toString(), "baz." ); - assertEquals( constraintViolation.getLeafBean(), customerRepository ); + assertEquals( constraintViolation.getLeafBean(), customerRepositoryOriginalBean ); assertEquals( constraintViolation.getInvalidValue(), 9 ); assertEquals( constraintViolation.getExecutableParameters(), null ); assertEquals( constraintViolation.getExecutableReturnValue(), 9 ); @@ -491,7 +493,7 @@ public void returnValueValidationYieldsConstraintViolation() { @Test public void cascadingReturnValue() { try { - customerRepository.cascadingReturnValue(); + customerRepositoryValidatingProxy.cascadingReturnValue(); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -510,7 +512,7 @@ public void cascadingReturnValue() { assertEquals( constraintViolation.getMessage(), messagePrefix() + "must not be null" ); assertMethod( constraintViolation, "cascadingReturnValue" ); assertMethodValidationType( constraintViolation, ElementKind.RETURN_VALUE ); - assertEquals( constraintViolation.getRootBean(), customerRepository ); + assertEquals( constraintViolation.getRootBean(), customerRepositoryOriginalBean ); assertEquals( constraintViolation.getRootBeanClass(), CustomerRepositoryImpl.class ); assertEquals( constraintViolation.getPropertyPath().toString(), @@ -526,7 +528,7 @@ public void cascadingReturnValue() { @Test public void cascadingReturnValueFromSuperType() { try { - customerRepository.overriddenMethodWithCascadingReturnValue(); + customerRepositoryValidatingProxy.overriddenMethodWithCascadingReturnValue(); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -546,7 +548,7 @@ public void cascadingReturnValueFromSuperType() { assertMethod( constraintViolation, "overriddenMethodWithCascadingReturnValue" ); assertMethodValidationType( constraintViolation, ElementKind.RETURN_VALUE ); - assertEquals( constraintViolation.getRootBean(), customerRepository ); + assertEquals( constraintViolation.getRootBean(), customerRepositoryOriginalBean ); assertEquals( constraintViolation.getRootBeanClass(), CustomerRepositoryImpl.class ); assertEquals( constraintViolation.getPropertyPath().toString(), @@ -562,7 +564,7 @@ public void cascadingReturnValueFromSuperType() { @Test public void cascadingIterableReturnValue() { try { - customerRepository.cascadingIterableReturnValue(); + customerRepositoryValidatingProxy.cascadingIterableReturnValue(); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -587,7 +589,7 @@ public void cascadingIterableReturnValue() { "cascadingIterableReturnValue.[1].name" ); assertEquals( constraintViolation.getRootBeanClass(), CustomerRepositoryImpl.class ); - assertEquals( constraintViolation.getRootBean(), customerRepository ); + assertEquals( constraintViolation.getRootBean(), customerRepositoryOriginalBean ); assertEquals( constraintViolation.getLeafBean(), new Customer( null ) ); assertEquals( constraintViolation.getInvalidValue(), null ); assertEquals( constraintViolation.getExecutableParameters(), null ); @@ -598,7 +600,7 @@ public void cascadingIterableReturnValue() { @Test public void cascadingMapReturnValue() { try { - customerRepository.cascadingMapReturnValue(); + customerRepositoryValidatingProxy.cascadingMapReturnValue(); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -627,7 +629,7 @@ public void cascadingMapReturnValue() { "cascadingMapReturnValue.[Bob].name" ); assertEquals( constraintViolation.getRootBeanClass(), CustomerRepositoryImpl.class ); - assertEquals( constraintViolation.getRootBean(), customerRepository ); + assertEquals( constraintViolation.getRootBean(), customerRepositoryOriginalBean ); assertEquals( constraintViolation.getLeafBean(), customer ); assertEquals( constraintViolation.getInvalidValue(), null ); assertEquals( constraintViolation.getExecutableParameters(), null ); @@ -638,7 +640,7 @@ public void cascadingMapReturnValue() { @Test public void cascadingArrayReturnValue() { try { - customerRepository.cascadingArrayReturnValue(); + customerRepositoryValidatingProxy.cascadingArrayReturnValue(); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -663,7 +665,7 @@ public void cascadingArrayReturnValue() { "cascadingArrayReturnValue.[1].name" ); assertEquals( constraintViolation.getRootBeanClass(), CustomerRepositoryImpl.class ); - assertEquals( constraintViolation.getRootBean(), customerRepository ); + assertEquals( constraintViolation.getRootBean(), customerRepositoryOriginalBean ); assertEquals( constraintViolation.getLeafBean(), new Customer( null ) ); assertEquals( constraintViolation.getInvalidValue(), null ); assertEquals( constraintViolation.getExecutableParameters(), null ); @@ -674,7 +676,7 @@ public void cascadingArrayReturnValue() { @Test public void overridingMethodStrengthensReturnValueConstraint() { try { - customerRepository.overriddenMethodWithReturnValueConstraint(); + customerRepositoryValidatingProxy.overriddenMethodWithReturnValueConstraint(); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -729,13 +731,13 @@ public void runtimeTypeDefinesConstraintsToApply() { @Test public void methodValidationSucceedsAsNoConstraintOfValidatedGroupAreViolated() { - customerRepository.parameterConstraintInGroup( null ); + customerRepositoryValidatingProxy.parameterConstraintInGroup( null ); } @Test(expectedExceptions = ConstraintViolationException.class) public void methodValidationFailsAsConstraintOfValidatedGroupIsViolated() { createProxy( CustomerRepository.ValidationGroup.class ); - customerRepository.parameterConstraintInGroup( null ); + customerRepositoryValidatingProxy.parameterConstraintInGroup( null ); } @Test(expectedExceptions = ConstraintDeclarationException.class, expectedExceptionsMessageRegExp = "HV000132.*") @@ -750,7 +752,7 @@ public void voidMethodWithReturnValueConstraintCausesConstraintDeclarationExcept @TestForIssue(jiraKey = "HV-601") @Test(expectedExceptions = ConstraintViolationException.class) public void shouldValidateGetterLikeNamedMethodWithParameter() { - customerRepository.getFoo( "" ); + customerRepositoryValidatingProxy.getFoo( "" ); } @Test @@ -761,7 +763,7 @@ public void validationOfCrossParameterConstraint() { try { //when - customerRepository.methodWithCrossParameterConstraint( startDate, endDate ); + customerRepositoryValidatingProxy.methodWithCrossParameterConstraint( startDate, endDate ); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -778,8 +780,8 @@ public void validationOfCrossParameterConstraint() { ConstraintViolation constraintViolation = e.getConstraintViolations().iterator().next(); assertEquals( constraintViolation.getConstraintDescriptor().getAnnotation().annotationType(), ConsistentDateParameters.class ); assertEquals( constraintViolation.getInvalidValue(), new Object[] { startDate, endDate } ); - assertEquals( constraintViolation.getLeafBean(), customerRepository ); - assertEquals( constraintViolation.getRootBean(), customerRepository ); + assertEquals( constraintViolation.getLeafBean(), customerRepositoryOriginalBean ); + assertEquals( constraintViolation.getRootBean(), customerRepositoryOriginalBean ); assertEquals( constraintViolation.getRootBeanClass(), CustomerRepositoryImpl.class ); assertEquals( constraintViolation.getExecutableParameters(), new Object[] { startDate, endDate } ); assertEquals( constraintViolation.getExecutableReturnValue(), null ); @@ -795,7 +797,7 @@ public void validationOfCrossParameterConstraint() { @Test public void methodValidationSucceeds() { - customerRepository.findCustomerByName( "Bob" ); + customerRepositoryValidatingProxy.findCustomerByName( "Bob" ); } protected void assertMethod(ConstraintViolation constraintViolation, String methodName, Class... parameterTypes) { diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/AnnotationBasedMethodValidationTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/AnnotationBasedMethodValidationTest.java index 4e4575e3d1..a700db52d6 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/AnnotationBasedMethodValidationTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/AnnotationBasedMethodValidationTest.java @@ -46,7 +46,7 @@ public void iterableParameterWithCascadingTypeParameter() { List customers = Arrays.asList( null, customer ); try { - customerRepository.iterableParameterWithCascadingTypeParameter( customers ); + customerRepositoryValidatingProxy.iterableParameterWithCascadingTypeParameter( customers ); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -62,7 +62,7 @@ public void iterableParameterWithCascadingTypeParameter() { "iterableParameterWithCascadingTypeParameter.customer[1].name" ); assertEquals( constraintViolation.getRootBeanClass(), CustomerRepositoryImpl.class ); - assertEquals( constraintViolation.getRootBean(), customerRepository ); + assertEquals( constraintViolation.getRootBean(), customerRepositoryOriginalBean ); assertEquals( constraintViolation.getLeafBean(), customer ); assertEquals( constraintViolation.getInvalidValue(), null ); assertEquals( constraintViolation.getExecutableParameters(), new Object[] { customers } ); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/ParameterValidationCycleTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/ParameterValidationCycleTest.java new file mode 100644 index 0000000000..5f19c684c8 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/ParameterValidationCycleTest.java @@ -0,0 +1,108 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.methodvalidation; + +import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; + +import java.util.HashSet; +import java.util.Set; + +import javax.validation.ConstraintViolation; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import javax.validation.executable.ExecutableValidator; + +import org.hibernate.validator.testutil.ConstraintViolationAssert; +import org.hibernate.validator.testutil.TestForIssue; +import org.hibernate.validator.testutils.ValidatorUtil; +import org.testng.annotations.Test; + +/** + * @author Guillaume Smet + * @author Chris Westmorland + */ +public class ParameterValidationCycleTest { + + @Test + @TestForIssue(jiraKey = "HV-1797") + public void testParameterValidationCycle() throws NoSuchMethodException, SecurityException { + final Parent parent = new Parent(); + parent.setId( 1L ); + + final Child child = new Child(); + child.setId( null ); + child.setParent( parent ); + + parent.getChildren().add( child ); + + ExecutableValidator executableValidator = ValidatorUtil.getValidator().forExecutables(); + Set> violations = executableValidator.validateParameters( new ExecutableHolder(), + ExecutableHolder.class.getDeclaredMethod( "post", Parent.class ), + new Object[]{ parent } ); + ConstraintViolationAssert.assertThat( violations ).containsOnlyViolations( violationOf( NotNull.class ) ); + } + + private static class ExecutableHolder { + + @SuppressWarnings("unused") + public void post(@Valid @NotNull Parent parent) { + } + } + + @SuppressWarnings("unused") + private static class Parent { + + @NotNull + private Long id; + + @Valid + private Set children = new HashSet<>(); + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Set getChildren() { + return children; + } + + public void setChildren(Set children) { + this.children = children; + } + } + + @SuppressWarnings("unused") + private static class Child { + + @NotNull + private Long id; + + @NotNull + @Valid + private Parent parent; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Parent getParent() { + return parent; + } + + public void setParent(Parent parent) { + this.parent = parent; + } + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles1Test.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles1Test.java new file mode 100644 index 0000000000..8a6e512bb3 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles1Test.java @@ -0,0 +1,114 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.tracking; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import javax.validation.ConstraintViolation; +import javax.validation.Valid; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.constraints.NotNull; + +import org.hibernate.validator.PredefinedScopeHibernateValidator; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertTrue; + +/** + * This is not a real test, just an illustration. + *

+ * This is the most simple example. + * + * @author Guillaume Smet + */ +public class ProcessedBeansTrackingCycles1Test { + + @Test + public void testValidNull() throws Exception { + final Parent parent = new Parent( "parent property" ); + Set> violations = getValidator().validate( parent ); + assertTrue( violations.isEmpty() ); + } + + @Test + public void testValidNotNull() throws Exception { + final Parent parent = new Parent( "parent property" ); + parent.child = new Child( "child property" ); + + Set> violations = getValidator().validate( parent ); + //Set> violations = ValidatorUtil.getValidator().validate( parent ); + assertTrue( violations.isEmpty() ); + } + + @Test + public void testValidNotNullNonCyclic() throws Exception { + final Parent parent = new Parent( "parent property" ); + parent.child = new Child( "child property" ); + parent.child.parent = new Parent( "other parent property" ); + + Set> violations = getValidator().validate( parent ); + assertTrue( violations.isEmpty() ); + } + + @Test + public void testValidNotNullCyclic() throws Exception { + final Parent parent = new Parent( "parent property" ); + parent.child = new Child( "child property" ); + parent.child.parent = parent; + + Set> violations = getValidator().validate( parent ); + assertTrue( violations.isEmpty() ); + } + + private Validator getValidator() { + return Validation.byProvider( PredefinedScopeHibernateValidator.class ) + .configure() + .builtinConstraints( new HashSet<>( Arrays.asList( NotNull.class.getName() ) ) ) + .initializeBeanMetaData( new HashSet<>( Arrays.asList( Parent.class, Child.class, Other.class ) ) ) + .buildValidatorFactory() + .getValidator(); + } + private static class Parent { + + Parent(String property) { + this.property = property; + } + + @NotNull + private String property; + + @Valid + private Child child; + } + + private static class Child { + + Child(String property) { + this.property = property; + } + + @NotNull + private String property; + + @Valid + private Parent parent; + + @Valid + private Other other; + } + + private static class Other { + Other(String property) { + this.property = property; + } + + @NotNull + private String property; + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles2Test.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles2Test.java new file mode 100644 index 0000000000..5fa8226919 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles2Test.java @@ -0,0 +1,50 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.tracking; + +import java.util.List; + +import javax.validation.Valid; +import javax.validation.Validator; +import javax.validation.constraints.NotNull; + +import org.hibernate.validator.testutils.ValidatorUtil; +import org.testng.annotations.Test; + +/** + * This is not a real test, just an illustration. + *

+ * Simple enough but this time the cascading annotation is in the container element. + * + * @author Guillaume Smet + */ +public class ProcessedBeansTrackingCycles2Test { + + @Test + public void testSerializeHibernateEmail() throws Exception { + Validator validator = ValidatorUtil.getValidator(); + + validator.validate( new Parent() ); + } + + private static class Parent { + + @NotNull + private String property; + + private List<@Valid Child> children; + } + + private static class Child { + + @NotNull + private String property; + + @Valid + private Parent parent; + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles3Test.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles3Test.java new file mode 100644 index 0000000000..2d136405e5 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles3Test.java @@ -0,0 +1,51 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.tracking; + +import java.util.List; +import java.util.Map; + +import javax.validation.Valid; +import javax.validation.Validator; +import javax.validation.constraints.NotNull; + +import org.hibernate.validator.testutils.ValidatorUtil; +import org.testng.annotations.Test; + +/** + * This is not a real test, just an illustration. + *

+ * Simple enough but this time the cascading annotation is deep in a container element. + * + * @author Guillaume Smet + */ +public class ProcessedBeansTrackingCycles3Test { + + @Test + public void testSerializeHibernateEmail() throws Exception { + Validator validator = ValidatorUtil.getValidator(); + + validator.validate( new Parent() ); + } + + private static class Parent { + + @NotNull + private String property; + + private Map> children; + } + + private static class Child { + + @NotNull + private String property; + + @Valid + private Parent parent; + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles4Test.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles4Test.java new file mode 100644 index 0000000000..75cfbf50b6 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles4Test.java @@ -0,0 +1,51 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.tracking; + +import java.util.List; +import java.util.Map; + +import javax.validation.Valid; +import javax.validation.Validator; +import javax.validation.constraints.NotNull; + +import org.hibernate.validator.testutils.ValidatorUtil; +import org.testng.annotations.Test; + +/** + * This is not a real test, just an illustration. + *

+ * Simple enough but this time the cascading annotation is deep in a container element with a bound. + * + * @author Guillaume Smet + */ +public class ProcessedBeansTrackingCycles4Test { + + @Test + public void testSerializeHibernateEmail() throws Exception { + Validator validator = ValidatorUtil.getValidator(); + + validator.validate( new Parent() ); + } + + private static class Parent { + + @NotNull + private String property; + + private Map> children; + } + + private static class Child { + + @NotNull + private String property; + + @Valid + private Parent parent; + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles5Test.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles5Test.java new file mode 100644 index 0000000000..9b5457638a --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles5Test.java @@ -0,0 +1,58 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.tracking; + +import java.util.List; + +import javax.validation.Valid; +import javax.validation.Validator; +import javax.validation.constraints.NotNull; + +import org.hibernate.validator.testutils.ValidatorUtil; +import org.testng.annotations.Test; + +/** + * This is not a real test, just an illustration. + *

+ * This one is a bit more tricky: during the validation, when cascading, we take into account the runtime type to get + * the metadata, not the declared type. + *

+ * So even if you couldn't have a cycle with the declared type, when trying to find the cycles, we need to take into + * consideration all the subclasses too. The good news is that we are in a closed world so we have them all passed + * to our PredefinedScopedValidatorFactoryImpl! + * + * @author Guillaume Smet + */ +public class ProcessedBeansTrackingCycles5Test { + + @Test + public void testSerializeHibernateEmail() throws Exception { + Validator validator = ValidatorUtil.getValidator(); + + validator.validate( new Parent() ); + } + + private static class Parent { + + @NotNull + private String property; + + private List<@Valid ChildWithNoCycles> children; + } + + private static class ChildWithNoCycles { + + @NotNull + private String property; + } + + private static class Child extends ChildWithNoCycles { + + @Valid + private Parent parent; + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCyclesNoCyclesTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCyclesNoCyclesTest.java new file mode 100644 index 0000000000..2387dce609 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCyclesNoCyclesTest.java @@ -0,0 +1,207 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.tracking; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import javax.validation.ConstraintViolation; +import javax.validation.Valid; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.constraints.NotNull; + +import org.hibernate.validator.PredefinedScopeHibernateValidator; +import org.hibernate.validator.PredefinedScopeHibernateValidatorFactory; +import org.hibernate.validator.internal.engine.PredefinedScopeValidatorFactoryImpl; +import org.hibernate.validator.internal.engine.ValidatorFactoryScopedContext; +import org.hibernate.validator.internal.engine.tracking.ProcessedBeansTrackingStrategy; + +import org.testng.annotations.Test; + +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +/** + * An example of beans with cascading constraints, some cycle and others do not. + * + * A -> B ---> C ------> F -> G <- + * | ^ | ^ ^ | + * | | | | | | + * | -- D <-- | | | + * --------------------> E ------- + * + * A, B, C, D, E, F, and G are beans that get validated. + * + * An arrow, ->, indicates a cascading constraint. + * + * The following are the properties with cascading constraints: + * A.b + * .e + * B.c + * C.d + * .f + * D.b + * E.f + * .g + * F.g + * + * @author Gail Badner + * + */ + +public class ProcessedBeansTrackingCyclesNoCyclesTest { + + @Test + public void testTrackingEnabled() { + + final ValidatorFactoryScopedContext validatorFactoryScopedContext = getValidatorFactoryScopedContext(); + final ProcessedBeansTrackingStrategy processedBeansTrackingStrategy = + validatorFactoryScopedContext.getProcessedBeansTrackingStrategy(); + assertTrue( processedBeansTrackingStrategy.isEnabledForBean( + A.class, + true + ) ); + assertTrue( processedBeansTrackingStrategy.isEnabledForBean( + B.class, + true + ) ); + assertTrue( processedBeansTrackingStrategy.isEnabledForBean( + C.class, + true + ) ); + assertTrue( processedBeansTrackingStrategy.isEnabledForBean( + D.class, + true + ) ); + assertFalse( processedBeansTrackingStrategy.isEnabledForBean( + E.class, + true + ) ); + assertFalse( processedBeansTrackingStrategy.isEnabledForBean( + F.class, + true + ) ); + assertFalse( processedBeansTrackingStrategy.isEnabledForBean( + G.class, + false + ) ); + } + + @Test + public void testValidate() { + final A a = new A(); + final B b = new B(); + final C c = new C(); + final D d = new D(); + final E e = new E(); + final F f = new F(); + final G g = new G(); + + a.b = b; + a.e = e; + b.c = c; + c.d = d; + d.b = b; + e.f = f; + e.g = g; + e.gAnother = g; + f.g = g; + + final Validator validator = getValidator(); + final Set> violationsA = validator.validate( a ); + final Set> violationsB = validator.validate( b ); + final Set> violationsC = validator.validate( c ); + final Set> violationsD = validator.validate( d ); + final Set> violationsE = validator.validate( e ); + final Set> violationsF = validator.validate( f ); + final Set> violationsG = validator.validate( g ); + } + + private Validator getValidator() { + return getValidatorFactory().getValidator(); + } + + private PredefinedScopeHibernateValidatorFactory getValidatorFactory() { + return Validation.byProvider( PredefinedScopeHibernateValidator.class ) + .configure() + .builtinConstraints( new HashSet<>( Arrays.asList( NotNull.class.getName() ) ) ) + .initializeBeanMetaData( new HashSet<>( Arrays.asList( + A.class, B.class, C.class, D.class, E.class, F.class, G.class + ) ) ) + .buildValidatorFactory().unwrap( PredefinedScopeHibernateValidatorFactory.class ); + } + + private ValidatorFactoryScopedContext getValidatorFactoryScopedContext() { + return ( (PredefinedScopeValidatorFactoryImpl) getValidatorFactory() ).getValidatorFactoryScopedContext(); + } + + private static class A { + + private String description; + + @Valid + private B b; + + @Valid + private E e; + } + + private static class B { + @Valid + private String description; + + @Valid + private C c; + } + + private static class C { + + private String description; + + @Valid + private D d; + + @Valid + private F f; + } + + private static class D { + + private String description; + + @Valid + private B b; + } + + private static class E { + + private String description; + + @Valid + private F f; + + @Valid + private G g; + + @Valid + private G gAnother; + } + + private static class F { + + private String description; + + @Valid + private G g; + } + + private static class G { + + private String description; + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingNoCycles1Test.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingNoCycles1Test.java new file mode 100644 index 0000000000..8bb1d30b74 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingNoCycles1Test.java @@ -0,0 +1,44 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.tracking; + +import javax.validation.Valid; +import javax.validation.Validator; +import javax.validation.constraints.NotNull; + +import org.hibernate.validator.testutils.ValidatorUtil; +import org.testng.annotations.Test; + +/** + * This is not a real test, just an illustration. + * + * @author Guillaume Smet + */ +public class ProcessedBeansTrackingNoCycles1Test { + + @Test + public void testSerializeHibernateEmail() throws Exception { + Validator validator = ValidatorUtil.getValidator(); + + validator.validate( new Parent() ); + } + + private static class Parent { + + @NotNull + private String property; + + @Valid + private Child child; + } + + private static class Child { + + @NotNull + private String property; + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingNoCycles2Test.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingNoCycles2Test.java new file mode 100644 index 0000000000..90a3e0e7f6 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingNoCycles2Test.java @@ -0,0 +1,53 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.tracking; + +import java.util.ArrayList; +import java.util.List; + +import javax.validation.Valid; +import javax.validation.Validator; +import javax.validation.constraints.NotNull; + +import org.hibernate.validator.testutils.ValidatorUtil; +import org.testng.annotations.Test; + +/** + * This is not a real test, just an illustration. + * + * @author Guillaume Smet + */ +public class ProcessedBeansTrackingNoCycles2Test { + + @Test + public void testSerializeHibernateEmail() throws Exception { + Validator validator = ValidatorUtil.getValidator(); + + final Parent parent = new Parent(); + parent.property = "parent property"; + final Child child = new Child(); + child.property = "child property"; + parent.children = new ArrayList<>(); + parent.children.add( child ); + parent.children.add( child ); + validator.validate( parent ); + } + + private static class Parent { + + @NotNull + private String property; + + private List<@Valid Child> children; + } + + private static class Child { + + @NotNull + private String property; + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingNoCycles3Test.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingNoCycles3Test.java new file mode 100644 index 0000000000..10d4c6b239 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingNoCycles3Test.java @@ -0,0 +1,48 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.tracking; + +import java.util.List; + +import javax.validation.Valid; +import javax.validation.Validator; +import javax.validation.constraints.NotNull; + +import org.hibernate.validator.testutils.ValidatorUtil; +import org.testng.annotations.Test; + +/** + * This is not a real test, just an illustration. + *

+ * In this case, given we will have all the subclasses of Child in the metadata, we should be able to know there are no + * cycles. + * + * @author Guillaume Smet + */ +public class ProcessedBeansTrackingNoCycles3Test { + + @Test + public void testSerializeHibernateEmail() throws Exception { + Validator validator = ValidatorUtil.getValidator(); + + validator.validate( new Parent() ); + } + + private static class Parent { + + @NotNull + private String property; + + private List<@Valid ? extends Child> children; + } + + private static class Child { + + @NotNull + private String property; + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ExecutableMetaDataTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ExecutableMetaDataTest.java index f8a24308ed..caf2eb5849 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ExecutableMetaDataTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ExecutableMetaDataTest.java @@ -35,6 +35,7 @@ import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; import org.hibernate.validator.internal.metadata.provider.MetaDataProvider; import org.hibernate.validator.internal.properties.DefaultGetterPropertySelectionStrategy; +import org.hibernate.validator.internal.properties.Signature; import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; import org.hibernate.validator.internal.util.ExecutableHelper; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; @@ -152,7 +153,7 @@ public void getIdentifierForMethod() throws Exception { ExecutableMetaData methodMetaData = beanMetaData.getMetaDataFor( method ).get(); assertThat( methodMetaData.getSignatures() ).containsOnly( - "createCustomer(java.lang.CharSequence,java.lang.String)" + new Signature( "createCustomer", CharSequence.class, String.class ) ); } @@ -161,7 +162,7 @@ public void getIdentifierForParameterlessMethod() throws Exception { Method method = CustomerRepositoryExt.class.getMethod( "foo" ); ExecutableMetaData methodMetaData = beanMetaData.getMetaDataFor( method ).get(); - assertThat( methodMetaData.getSignatures() ).containsOnly( "foo()" ); + assertThat( methodMetaData.getSignatures() ).containsOnly( new Signature( "foo" ) ); } @Test @@ -169,7 +170,7 @@ public void getIdentifierForConstructor() throws Exception { Constructor constructor = CustomerRepositoryExt.class.getConstructor( String.class ); ExecutableMetaData constructorMetaData = beanMetaData.getMetaDataFor( constructor ).get(); - assertThat( constructorMetaData.getSignatures() ).containsOnly( "CustomerRepositoryExt(java.lang.String)" ); + assertThat( constructorMetaData.getSignatures() ).containsOnly( new Signature( "CustomerRepositoryExt", String.class ) ); } @Test @@ -180,8 +181,8 @@ public void getIdentifierForOverridingGenericMethod() throws Exception { ExecutableMetaData methodMetaData = beanMetaData.getMetaDataFor( method ).get(); assertThat( methodMetaData.getSignatures() ) - .describedAs( "Expecting super-type and sub-type method signatures" ) - .containsOnly( "createJob(java.lang.Object)", "createJob(java.util.UUID)" ); + .describedAs( "Expecting super-type and sub-type method signatures" ) + .containsOnly( new Signature( "createJob", Object.class ), new Signature( "createJob", UUID.class ) ); method = JobRepository.class.getMethod( "createJob", Object.class ); beanMetaData = beanMetaDataManager.getBeanMetaData( JobRepository.class ); @@ -189,7 +190,7 @@ public void getIdentifierForOverridingGenericMethod() throws Exception { assertThat( methodMetaData.getSignatures() ) .describedAs( "Expecting only super-type method signature" ) - .containsOnly( "createJob(java.lang.Object)" ); + .containsOnly( new Signature( "createJob", Object.class ) ); method = SpecialJobRepositoryImpl.class.getMethod( "createJob", Object.class ); beanMetaData = beanMetaDataManager.getBeanMetaData( SpecialJobRepositoryImpl.class ); @@ -197,7 +198,7 @@ public void getIdentifierForOverridingGenericMethod() throws Exception { assertThat( methodMetaData.getSignatures() ) .describedAs( "Expecting method signatures from super-types" ) - .containsOnly( "createJob(java.lang.Object)", "createJob(java.util.UUID)" ); + .containsOnly( new Signature( "createJob", Object.class ), new Signature( "createJob", UUID.class ) ); } @Test @@ -209,7 +210,7 @@ public void getIdentifierForOverloadedMethod() throws Exception { assertThat( methodMetaData.getSignatures() ) .describedAs( "Expecting sub-type method signature which overloads but not overrides super-type methods" ) - .containsOnly( "createJob(java.lang.String)" ); + .containsOnly( new Signature( "createJob", String.class ) ); } @Test diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/core/ConstraintHelperTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/core/ConstraintHelperTest.java index 2eb7604455..4708fae57b 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/core/ConstraintHelperTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/core/ConstraintHelperTest.java @@ -31,7 +31,7 @@ public class ConstraintHelperTest { @BeforeClass public static void init() { - constraintHelper = new ConstraintHelper(); + constraintHelper = ConstraintHelper.forAllBuiltinConstraints(); } @Test diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/core/MetaConstraintTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/core/MetaConstraintTest.java index cb8a4a04b2..11f91dc1b5 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/core/MetaConstraintTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/core/MetaConstraintTest.java @@ -45,7 +45,7 @@ public class MetaConstraintTest { @BeforeClass public void setUp() throws Exception { - constraintHelper = new ConstraintHelper(); + constraintHelper = ConstraintHelper.forAllBuiltinConstraints(); typeResolutionHelper = new TypeResolutionHelper(); valueExtractorManager = new ValueExtractorManager( Collections.emptySet() ); constraintValidatorManager = new ConstraintValidatorManagerImpl( new ConstraintValidatorFactoryImpl(), getDummyConstraintValidatorInitializationContext() ); diff --git a/engine/src/test/java/org/hibernate/validator/test/predefinedscope/PredefinedScopeValidatorFactoryTest.java b/engine/src/test/java/org/hibernate/validator/test/predefinedscope/PredefinedScopeValidatorFactoryTest.java index 2e94a98448..d92a8362d5 100644 --- a/engine/src/test/java/org/hibernate/validator/test/predefinedscope/PredefinedScopeValidatorFactoryTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/predefinedscope/PredefinedScopeValidatorFactoryTest.java @@ -14,6 +14,7 @@ import java.lang.annotation.ElementType; import java.util.AbstractCollection; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -245,6 +246,7 @@ public void testBeanMetaDataClassNormalizer() { ValidatorFactory validatorFactory = Validation.byProvider( PredefinedScopeHibernateValidator.class ) .configure() + .builtinConstraints( new HashSet<>( Arrays.asList( Email.class.getName(), NotNull.class.getName() ) ) ) .initializeBeanMetaData( beanMetaDataToInitialize ) .locales( Collections.singleton( Locale.getDefault() ) ) .beanMetaDataClassNormalizer( new MyProxyInterfaceBeanMetaDataClassNormalizer() ) @@ -266,6 +268,7 @@ public void validatorSpecificTraversableResolver() { ValidatorFactory validatorFactory = Validation.byProvider( PredefinedScopeHibernateValidator.class ) .configure() + .builtinConstraints( new HashSet<>( Arrays.asList( Email.class.getName(), NotNull.class.getName() ) ) ) .initializeBeanMetaData( beanMetaDataToInitialize ) .buildValidatorFactory(); @@ -290,6 +293,7 @@ public void variousObjectTypes() { ValidatorFactory validatorFactory = Validation.byProvider( PredefinedScopeHibernateValidator.class ) .configure() + .builtinConstraints( new HashSet<>( Arrays.asList( Email.class.getName(), NotNull.class.getName() ) ) ) .initializeBeanMetaData( beanMetaDataToInitialize ) .buildValidatorFactory(); @@ -330,6 +334,7 @@ private static ValidatorFactory getValidatorFactory() { return Validation.byProvider( PredefinedScopeHibernateValidator.class ) .configure() + .builtinConstraints( new HashSet<>( Arrays.asList( Email.class.getName(), NotNull.class.getName() ) ) ) .initializeBeanMetaData( beanMetaDataToInitialize ) .buildValidatorFactory(); } @@ -344,6 +349,7 @@ private static ValidatorFactory getValidatorFactoryWithInitializedLocale(Locale return Validation.byProvider( PredefinedScopeHibernateValidator.class ) .configure() + .builtinConstraints( new HashSet<>( Arrays.asList( Email.class.getName(), NotNull.class.getName() ) ) ) .initializeBeanMetaData( beanMetaDataToInitialize ) .locales( Collections.singleton( locale ) ) .buildValidatorFactory(); diff --git a/engine/src/test/java/org/hibernate/validator/testutils/ConstraintValidatorInitializationHelper.java b/engine/src/test/java/org/hibernate/validator/testutils/ConstraintValidatorInitializationHelper.java index 1978dc0c6b..86ab72ad86 100644 --- a/engine/src/test/java/org/hibernate/validator/testutils/ConstraintValidatorInitializationHelper.java +++ b/engine/src/test/java/org/hibernate/validator/testutils/ConstraintValidatorInitializationHelper.java @@ -34,7 +34,7 @@ */ public class ConstraintValidatorInitializationHelper { - private static final ConstraintHelper CONSTRAINT_HELPER = new ConstraintHelper(); + private static final ConstraintHelper CONSTRAINT_HELPER = ConstraintHelper.forAllBuiltinConstraints(); private static final HibernateConstraintValidatorInitializationContext DUMMY_CONSTRAINT_VALIDATOR_INITIALIZATION_CONTEXT = getConstraintValidatorInitializationContext( new DefaultScriptEvaluatorFactory( null ), DefaultClockProvider.INSTANCE, Duration.ZERO ); @@ -73,7 +73,7 @@ public static HibernateConstraintValidatorInitializationContext getDummyConstrai } public static ConstraintCreationContext getDummyConstraintCreationContext() { - return new ConstraintCreationContext( new ConstraintHelper(), + return new ConstraintCreationContext( ConstraintHelper.forAllBuiltinConstraints(), new ConstraintValidatorManagerImpl( new ConstraintValidatorFactoryImpl(), getDummyConstraintValidatorInitializationContext() ), new TypeResolutionHelper(), new ValueExtractorManager( Collections.emptySet() ) ); diff --git a/engine/src/test/java/org/hibernate/validator/testutils/ValidatorUtil.java b/engine/src/test/java/org/hibernate/validator/testutils/ValidatorUtil.java index 061272b05f..b91f10aad5 100644 --- a/engine/src/test/java/org/hibernate/validator/testutils/ValidatorUtil.java +++ b/engine/src/test/java/org/hibernate/validator/testutils/ValidatorUtil.java @@ -28,6 +28,7 @@ import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorContext; import org.hibernate.validator.internal.engine.DefaultClockProvider; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorContextImpl; +import org.hibernate.validator.messageinterpolation.ExpressionLanguageFeatureLevel; import org.hibernate.validator.testutil.DummyTraversableResolver; import org.hibernate.validator.testutil.ValidationInvocationHandler; @@ -235,6 +236,7 @@ public static T getValidatingProxy(I implementor, Validator exe } public static HibernateConstraintValidatorContext getConstraintValidatorContext() { - return new ConstraintValidatorContextImpl( DefaultClockProvider.INSTANCE, null, null, null ); + return new ConstraintValidatorContextImpl( DefaultClockProvider.INSTANCE, null, null, null, ExpressionLanguageFeatureLevel.BEAN_PROPERTIES, + ExpressionLanguageFeatureLevel.NONE ); } } diff --git a/engine/src/test/resources/log4j.properties b/engine/src/test/resources/log4j.properties deleted file mode 100644 index 78c71c612a..0000000000 --- a/engine/src/test/resources/log4j.properties +++ /dev/null @@ -1,26 +0,0 @@ -### direct log messages to stdout ### -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.Target=System.out -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n - -### direct messages to file hibernate.log ### -log4j.appender.file=org.apache.log4j.FileAppender -log4j.appender.file.File=hibernate.log -log4j.appender.file.layout=org.apache.log4j.PatternLayout -log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n - -### direct messages to socket - chainsaw ### -log4j.appender.socket=org.apache.log4j.net.SocketAppender -log4j.appender.socket.remoteHost=localhost -log4j.appender.socket.port=4560 -log4j.appender.socket.locationInfo=true - - -### set log levels - for more verbose logging change 'info' to 'debug' ### -log4j.rootLogger=info, stdout - -#log4j.logger.org.hibernate.validator.internal.engine.ValidatorImpl=trace -#log4j.logger.org.hibernate.validator.internal.engine.resolver.JPATraversableResolver=trace -#log4j.logger.org.hibernate.validatorengine.ConstraintTree=trace -#log4j.logger.org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator=info diff --git a/engine/src/test/resources/log4j2.properties b/engine/src/test/resources/log4j2.properties new file mode 100644 index 0000000000..6289842165 --- /dev/null +++ b/engine/src/test/resources/log4j2.properties @@ -0,0 +1,26 @@ +# License: Apache License, Version 2.0 +# See the LICENSE file in the root directory or . + +status = error + +# Direct log messages to stdout +appender.console.type = Console +appender.console.name = console +appender.console.layout.type = PatternLayout +appender.console.layout.pattern = %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n + +appender.list.type = List +appender.list.name = List + +# Root logger option +rootLogger.level = info +rootLogger.appenderRef.console.ref = console + +# Specific loggers options +logger.parametermessageinterpolator.name = org.hibernate.validator.messageinterpolation.ParameterMessageInterpolator +logger.parametermessageinterpolator.level = info +logger.parametermessageinterpolator.appenderRef.list.ref = List + +logger.constraintvalidatorcontextimpl.name = org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorContextImpl +logger.constraintvalidatorcontextimpl.level = info +logger.constraintvalidatorcontextimpl.appenderRef.list.ref = List \ No newline at end of file diff --git a/engine/src/test/resources/org/hibernate/validator/test/el/validation-constraints-bean-methods.xml b/engine/src/test/resources/org/hibernate/validator/test/el/validation-constraints-bean-methods.xml new file mode 100644 index 0000000000..671eed5699 --- /dev/null +++ b/engine/src/test/resources/org/hibernate/validator/test/el/validation-constraints-bean-methods.xml @@ -0,0 +1,16 @@ + + + + + bean-methods + diff --git a/engine/src/test/resources/org/hibernate/validator/test/el/validation-constraints-default.xml b/engine/src/test/resources/org/hibernate/validator/test/el/validation-constraints-default.xml new file mode 100644 index 0000000000..cc05e3311c --- /dev/null +++ b/engine/src/test/resources/org/hibernate/validator/test/el/validation-constraints-default.xml @@ -0,0 +1,16 @@ + + + + + default + diff --git a/engine/src/test/resources/org/hibernate/validator/test/el/validation-custom-violations-bean-methods.xml b/engine/src/test/resources/org/hibernate/validator/test/el/validation-custom-violations-bean-methods.xml new file mode 100644 index 0000000000..ecd0155803 --- /dev/null +++ b/engine/src/test/resources/org/hibernate/validator/test/el/validation-custom-violations-bean-methods.xml @@ -0,0 +1,16 @@ + + + + + bean-methods + diff --git a/engine/src/test/resources/org/hibernate/validator/test/el/validation-custom-violations-default.xml b/engine/src/test/resources/org/hibernate/validator/test/el/validation-custom-violations-default.xml new file mode 100644 index 0000000000..62a07bde5a --- /dev/null +++ b/engine/src/test/resources/org/hibernate/validator/test/el/validation-custom-violations-default.xml @@ -0,0 +1,16 @@ + + + + + default + diff --git a/integration/pom.xml b/integration/pom.xml index 5053e46480..551abf0f22 100644 --- a/integration/pom.xml +++ b/integration/pom.xml @@ -11,7 +11,7 @@ org.hibernate.validator hibernate-validator-parent - 6.1.3-SNAPSHOT + 6.2.1-SNAPSHOT ../pom.xml @@ -47,11 +47,6 @@ rest-assured test - - log4j - log4j - test - ${project.groupId} hibernate-validator-test-utils @@ -67,11 +62,6 @@ joda-time test - - org.jsoup - jsoup - test - javax.money money-api diff --git a/integration/src/test/java/org/hibernate/validator/integration/wildfly/CustomValidationProviderInDeploymentUnitIT.java b/integration/src/test/java/org/hibernate/validator/integration/wildfly/CustomValidationProviderInDeploymentUnitIT.java index d71ed060d5..833d2021a0 100644 --- a/integration/src/test/java/org/hibernate/validator/integration/wildfly/CustomValidationProviderInDeploymentUnitIT.java +++ b/integration/src/test/java/org/hibernate/validator/integration/wildfly/CustomValidationProviderInDeploymentUnitIT.java @@ -12,7 +12,6 @@ import javax.validation.Validator; import javax.validation.ValidatorFactory; -import org.apache.log4j.Logger; import org.hibernate.validator.integration.AbstractArquillianIT; import org.hibernate.validator.integration.util.IntegrationTestUtil; import org.hibernate.validator.integration.util.MyValidator; @@ -31,7 +30,6 @@ public class CustomValidationProviderInDeploymentUnitIT extends AbstractArquillianIT { private static final String WAR_FILE_NAME = CustomValidationProviderInDeploymentUnitIT.class.getSimpleName() + ".war"; - private static final Logger log = Logger.getLogger( CustomValidationProviderInDeploymentUnitIT.class ); @Deployment public static Archive createTestArchive() { @@ -49,8 +47,6 @@ public static Archive createTestArchive() { @Test public void testValidatorFactoryFromCustomValidationProvider() throws Exception { - log.debug( "Running testValidatorFactoryFromCustomValidationProvider..." ); - Validator validator = validatorFactory.getValidator(); // Asserting the validator type as the VF is the wrapper type used within WildFly (LazyValidatorFactory) @@ -58,7 +54,5 @@ public void testValidatorFactoryFromCustomValidationProvider() throws Exception .as( "The custom validator implementation as retrieved from the default provider configured in META-INF/validation.xml should be used but actually " + validator + " is used" ) .isInstanceOf( MyValidator.class ); - - log.debug( "testValidatorFactoryFromCustomValidationProvider completed" ); } } diff --git a/integration/src/test/java/org/hibernate/validator/integration/wildfly/JndiLookupOfValidatorFactoryIT.java b/integration/src/test/java/org/hibernate/validator/integration/wildfly/JndiLookupOfValidatorFactoryIT.java index 785bc28747..d0881e6af4 100644 --- a/integration/src/test/java/org/hibernate/validator/integration/wildfly/JndiLookupOfValidatorFactoryIT.java +++ b/integration/src/test/java/org/hibernate/validator/integration/wildfly/JndiLookupOfValidatorFactoryIT.java @@ -14,7 +14,6 @@ import javax.naming.NamingException; import javax.validation.ValidatorFactory; -import org.apache.log4j.Logger; import org.hibernate.validator.integration.AbstractArquillianIT; import org.hibernate.validator.internal.engine.ValidatorImpl; import org.jboss.arquillian.container.test.api.Deployment; @@ -28,7 +27,6 @@ */ public class JndiLookupOfValidatorFactoryIT extends AbstractArquillianIT { private static final String WAR_FILE_NAME = JndiLookupOfValidatorFactoryIT.class.getSimpleName() + ".war"; - private static final Logger log = Logger.getLogger( JndiLookupOfValidatorFactoryIT.class ); private static final String DEFAULT_JNDI_NAME_OF_VALIDATOR_FACTORY = "java:comp/ValidatorFactory"; @Deployment @@ -38,7 +36,6 @@ public static Archive createTestArchive() { @Test public void testDefaultValidatorFactoryLookup() throws Exception { - log.debug( "Running testDefaultValidatorFactoryLookup..." ); try { Context ctx = new InitialContext(); Object obj = ctx.lookup( DEFAULT_JNDI_NAME_OF_VALIDATOR_FACTORY ); @@ -51,6 +48,5 @@ public void testDefaultValidatorFactoryLookup() throws Exception { catch (NamingException e) { fail( "The default validator factory should be bound" ); } - log.debug( "testDefaultValidatorFactoryLookup completed" ); } } diff --git a/integration/src/test/java/org/hibernate/validator/integration/wildfly/OptionalConstraintsIT.java b/integration/src/test/java/org/hibernate/validator/integration/wildfly/OptionalConstraintsIT.java index 52c4f6218e..ab6d7246d5 100644 --- a/integration/src/test/java/org/hibernate/validator/integration/wildfly/OptionalConstraintsIT.java +++ b/integration/src/test/java/org/hibernate/validator/integration/wildfly/OptionalConstraintsIT.java @@ -18,7 +18,6 @@ import javax.validation.constraints.Future; import org.hibernate.validator.constraints.Currency; -import org.hibernate.validator.constraints.SafeHtml; import org.hibernate.validator.integration.AbstractArquillianIT; import org.javamoney.moneta.Money; import org.jboss.arquillian.container.test.api.Deployment; @@ -28,7 +27,7 @@ import org.testng.annotations.Test; /** - * Asserts that the constraints based on the JodaTime and JSoup server modules can be used. + * Asserts that the constraints based on the JodaTime and Javax Money server modules can be used. * * @author Gunnar Morling * @author Guillaume Smet @@ -57,14 +56,6 @@ public void canUseJodaTimeConstraintValidator() { assertThat( violations.iterator().next().getConstraintDescriptor().getAnnotation().annotationType() ).isEqualTo( Future.class ); } - @Test - public void canUseJsoupBasedConstraint() { - Set> violations = validator.validate( new Item() ); - - assertThat( violations.size() ).isEqualTo( 1 ); - assertThat( violations.iterator().next().getConstraintDescriptor().getAnnotation().annotationType() ).isEqualTo( SafeHtml.class ); - } - @Test public void canUseJavaMoneyBasedConstraint() { Set> violations = validator.validate( new Order( Money.of( 1200.0, "EUR" ) ) ); @@ -84,12 +75,6 @@ private static class Shipment { public DateTime deliveryDate = new DateTime( 2014, 10, 21, 0, 0, 0, 0 ); } - private static class Item { - - @SafeHtml - public String descriptionHtml = "