|
|
|
@ -1,5 +1,5 @@
|
|
|
|
|
/*
|
|
|
|
|
* Copyright 2012-2020 the original author or authors.
|
|
|
|
|
* Copyright 2012-2021 the original author or authors.
|
|
|
|
|
*
|
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
@ -24,12 +24,14 @@ import javax.validation.Validator;
|
|
|
|
|
import javax.validation.constraints.Min;
|
|
|
|
|
import javax.validation.constraints.Size;
|
|
|
|
|
|
|
|
|
|
import org.junit.jupiter.api.AfterEach;
|
|
|
|
|
import org.junit.jupiter.api.Test;
|
|
|
|
|
|
|
|
|
|
import org.springframework.beans.factory.config.BeanPostProcessor;
|
|
|
|
|
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
|
|
|
|
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
|
|
|
|
import org.springframework.boot.autoconfigure.validation.ValidationAutoConfigurationTests.CustomValidatorConfiguration.TestBeanPostProcessor;
|
|
|
|
|
import org.springframework.boot.test.util.TestPropertyValues;
|
|
|
|
|
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
|
|
|
|
|
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
|
|
|
|
import org.springframework.boot.validation.beanvalidation.MethodValidationExcludeFilter;
|
|
|
|
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
|
|
|
|
import org.springframework.context.annotation.Bean;
|
|
|
|
@ -55,171 +57,143 @@ import static org.mockito.Mockito.mock;
|
|
|
|
|
*/
|
|
|
|
|
class ValidationAutoConfigurationTests {
|
|
|
|
|
|
|
|
|
|
private AnnotationConfigApplicationContext context;
|
|
|
|
|
|
|
|
|
|
@AfterEach
|
|
|
|
|
void close() {
|
|
|
|
|
if (this.context != null) {
|
|
|
|
|
this.context.close();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner(
|
|
|
|
|
AnnotationConfigApplicationContext::new)
|
|
|
|
|
.withConfiguration(AutoConfigurations.of(ValidationAutoConfiguration.class));
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void validationAutoConfigurationShouldConfigureDefaultValidator() {
|
|
|
|
|
load(Config.class);
|
|
|
|
|
String[] jsrValidatorNames = this.context.getBeanNamesForType(Validator.class);
|
|
|
|
|
String[] springValidatorNames = this.context
|
|
|
|
|
.getBeanNamesForType(org.springframework.validation.Validator.class);
|
|
|
|
|
assertThat(jsrValidatorNames).containsExactly("defaultValidator");
|
|
|
|
|
assertThat(springValidatorNames).containsExactly("defaultValidator");
|
|
|
|
|
Validator jsrValidator = this.context.getBean(Validator.class);
|
|
|
|
|
org.springframework.validation.Validator springValidator = this.context
|
|
|
|
|
.getBean(org.springframework.validation.Validator.class);
|
|
|
|
|
assertThat(jsrValidator).isInstanceOf(LocalValidatorFactoryBean.class);
|
|
|
|
|
assertThat(jsrValidator).isEqualTo(springValidator);
|
|
|
|
|
assertThat(isPrimaryBean("defaultValidator")).isTrue();
|
|
|
|
|
this.contextRunner.run((context) -> {
|
|
|
|
|
assertThat(context.getBeanNamesForType(Validator.class)).containsExactly("defaultValidator");
|
|
|
|
|
assertThat(context.getBeanNamesForType(org.springframework.validation.Validator.class))
|
|
|
|
|
.containsExactly("defaultValidator");
|
|
|
|
|
assertThat(context.getBean(Validator.class)).isInstanceOf(LocalValidatorFactoryBean.class)
|
|
|
|
|
.isEqualTo(context.getBean(org.springframework.validation.Validator.class));
|
|
|
|
|
assertThat(isPrimaryBean(context, "defaultValidator")).isTrue();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void validationAutoConfigurationWhenUserProvidesValidatorShouldBackOff() {
|
|
|
|
|
load(UserDefinedValidatorConfig.class);
|
|
|
|
|
String[] jsrValidatorNames = this.context.getBeanNamesForType(Validator.class);
|
|
|
|
|
String[] springValidatorNames = this.context
|
|
|
|
|
.getBeanNamesForType(org.springframework.validation.Validator.class);
|
|
|
|
|
assertThat(jsrValidatorNames).containsExactly("customValidator");
|
|
|
|
|
assertThat(springValidatorNames).containsExactly("customValidator");
|
|
|
|
|
org.springframework.validation.Validator springValidator = this.context
|
|
|
|
|
.getBean(org.springframework.validation.Validator.class);
|
|
|
|
|
Validator jsrValidator = this.context.getBean(Validator.class);
|
|
|
|
|
assertThat(jsrValidator).isInstanceOf(OptionalValidatorFactoryBean.class);
|
|
|
|
|
assertThat(jsrValidator).isEqualTo(springValidator);
|
|
|
|
|
assertThat(isPrimaryBean("customValidator")).isFalse();
|
|
|
|
|
this.contextRunner.withUserConfiguration(UserDefinedValidatorConfig.class).run((context) -> {
|
|
|
|
|
assertThat(context.getBeanNamesForType(Validator.class)).containsExactly("customValidator");
|
|
|
|
|
assertThat(context.getBeanNamesForType(org.springframework.validation.Validator.class))
|
|
|
|
|
.containsExactly("customValidator");
|
|
|
|
|
assertThat(context.getBean(Validator.class)).isInstanceOf(OptionalValidatorFactoryBean.class)
|
|
|
|
|
.isEqualTo(context.getBean(org.springframework.validation.Validator.class));
|
|
|
|
|
assertThat(isPrimaryBean(context, "customValidator")).isFalse();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void validationAutoConfigurationWhenUserProvidesDefaultValidatorShouldNotEnablePrimary() {
|
|
|
|
|
load(UserDefinedDefaultValidatorConfig.class);
|
|
|
|
|
String[] jsrValidatorNames = this.context.getBeanNamesForType(Validator.class);
|
|
|
|
|
String[] springValidatorNames = this.context
|
|
|
|
|
.getBeanNamesForType(org.springframework.validation.Validator.class);
|
|
|
|
|
assertThat(jsrValidatorNames).containsExactly("defaultValidator");
|
|
|
|
|
assertThat(springValidatorNames).containsExactly("defaultValidator");
|
|
|
|
|
assertThat(isPrimaryBean("defaultValidator")).isFalse();
|
|
|
|
|
this.contextRunner.withUserConfiguration(UserDefinedDefaultValidatorConfig.class).run((context) -> {
|
|
|
|
|
assertThat(context.getBeanNamesForType(Validator.class)).containsExactly("defaultValidator");
|
|
|
|
|
assertThat(context.getBeanNamesForType(org.springframework.validation.Validator.class))
|
|
|
|
|
.containsExactly("defaultValidator");
|
|
|
|
|
assertThat(isPrimaryBean(context, "defaultValidator")).isFalse();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void validationAutoConfigurationWhenUserProvidesJsrValidatorShouldBackOff() {
|
|
|
|
|
load(UserDefinedJsrValidatorConfig.class);
|
|
|
|
|
String[] jsrValidatorNames = this.context.getBeanNamesForType(Validator.class);
|
|
|
|
|
String[] springValidatorNames = this.context
|
|
|
|
|
.getBeanNamesForType(org.springframework.validation.Validator.class);
|
|
|
|
|
assertThat(jsrValidatorNames).containsExactly("customValidator");
|
|
|
|
|
assertThat(springValidatorNames).isEmpty();
|
|
|
|
|
assertThat(isPrimaryBean("customValidator")).isFalse();
|
|
|
|
|
this.contextRunner.withUserConfiguration(UserDefinedJsrValidatorConfig.class).run((context) -> {
|
|
|
|
|
assertThat(context.getBeanNamesForType(Validator.class)).containsExactly("customValidator");
|
|
|
|
|
assertThat(context.getBeanNamesForType(org.springframework.validation.Validator.class)).isEmpty();
|
|
|
|
|
assertThat(isPrimaryBean(context, "customValidator")).isFalse();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void validationAutoConfigurationWhenUserProvidesSpringValidatorShouldCreateJsrValidator() {
|
|
|
|
|
load(UserDefinedSpringValidatorConfig.class);
|
|
|
|
|
String[] jsrValidatorNames = this.context.getBeanNamesForType(Validator.class);
|
|
|
|
|
String[] springValidatorNames = this.context
|
|
|
|
|
.getBeanNamesForType(org.springframework.validation.Validator.class);
|
|
|
|
|
assertThat(jsrValidatorNames).containsExactly("defaultValidator");
|
|
|
|
|
assertThat(springValidatorNames).containsExactly("customValidator", "anotherCustomValidator",
|
|
|
|
|
"defaultValidator");
|
|
|
|
|
Validator jsrValidator = this.context.getBean(Validator.class);
|
|
|
|
|
org.springframework.validation.Validator springValidator = this.context
|
|
|
|
|
.getBean(org.springframework.validation.Validator.class);
|
|
|
|
|
assertThat(jsrValidator).isInstanceOf(LocalValidatorFactoryBean.class);
|
|
|
|
|
assertThat(jsrValidator).isEqualTo(springValidator);
|
|
|
|
|
assertThat(isPrimaryBean("defaultValidator")).isTrue();
|
|
|
|
|
this.contextRunner.withUserConfiguration(UserDefinedSpringValidatorConfig.class).run((context) -> {
|
|
|
|
|
assertThat(context.getBeanNamesForType(Validator.class)).containsExactly("defaultValidator");
|
|
|
|
|
assertThat(context.getBeanNamesForType(org.springframework.validation.Validator.class))
|
|
|
|
|
.containsExactly("customValidator", "anotherCustomValidator", "defaultValidator");
|
|
|
|
|
assertThat(context.getBean(Validator.class)).isInstanceOf(LocalValidatorFactoryBean.class)
|
|
|
|
|
.isEqualTo(context.getBean(org.springframework.validation.Validator.class));
|
|
|
|
|
assertThat(isPrimaryBean(context, "defaultValidator")).isTrue();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void validationAutoConfigurationWhenUserProvidesPrimarySpringValidatorShouldRemovePrimaryFlag() {
|
|
|
|
|
load(UserDefinedPrimarySpringValidatorConfig.class);
|
|
|
|
|
String[] jsrValidatorNames = this.context.getBeanNamesForType(Validator.class);
|
|
|
|
|
String[] springValidatorNames = this.context
|
|
|
|
|
.getBeanNamesForType(org.springframework.validation.Validator.class);
|
|
|
|
|
assertThat(jsrValidatorNames).containsExactly("defaultValidator");
|
|
|
|
|
assertThat(springValidatorNames).containsExactly("customValidator", "anotherCustomValidator",
|
|
|
|
|
"defaultValidator");
|
|
|
|
|
Validator jsrValidator = this.context.getBean(Validator.class);
|
|
|
|
|
org.springframework.validation.Validator springValidator = this.context
|
|
|
|
|
.getBean(org.springframework.validation.Validator.class);
|
|
|
|
|
assertThat(jsrValidator).isInstanceOf(LocalValidatorFactoryBean.class);
|
|
|
|
|
assertThat(springValidator).isEqualTo(this.context.getBean("anotherCustomValidator"));
|
|
|
|
|
assertThat(isPrimaryBean("defaultValidator")).isFalse();
|
|
|
|
|
this.contextRunner.withUserConfiguration(UserDefinedPrimarySpringValidatorConfig.class).run((context) -> {
|
|
|
|
|
assertThat(context.getBeanNamesForType(Validator.class)).containsExactly("defaultValidator");
|
|
|
|
|
assertThat(context.getBeanNamesForType(org.springframework.validation.Validator.class))
|
|
|
|
|
.containsExactly("customValidator", "anotherCustomValidator", "defaultValidator");
|
|
|
|
|
assertThat(context.getBean(Validator.class)).isInstanceOf(LocalValidatorFactoryBean.class);
|
|
|
|
|
assertThat(context.getBean(org.springframework.validation.Validator.class))
|
|
|
|
|
.isEqualTo(context.getBean("anotherCustomValidator"));
|
|
|
|
|
assertThat(isPrimaryBean(context, "defaultValidator")).isFalse();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void validationIsEnabled() {
|
|
|
|
|
load(SampleService.class);
|
|
|
|
|
assertThat(this.context.getBeansOfType(Validator.class)).hasSize(1);
|
|
|
|
|
SampleService service = this.context.getBean(SampleService.class);
|
|
|
|
|
service.doSomething("Valid");
|
|
|
|
|
assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(() -> service.doSomething("KO"));
|
|
|
|
|
this.contextRunner.withUserConfiguration(SampleService.class).run((context) -> {
|
|
|
|
|
assertThat(context.getBeansOfType(Validator.class)).hasSize(1);
|
|
|
|
|
SampleService service = context.getBean(SampleService.class);
|
|
|
|
|
service.doSomething("Valid");
|
|
|
|
|
assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(() -> service.doSomething("KO"));
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void classCanBeExcludedFromValidation() {
|
|
|
|
|
load(ExcludedServiceConfiguration.class);
|
|
|
|
|
assertThat(this.context.getBeansOfType(Validator.class)).hasSize(1);
|
|
|
|
|
ExcludedService service = this.context.getBean(ExcludedService.class);
|
|
|
|
|
service.doSomething("Valid");
|
|
|
|
|
assertThatNoException().isThrownBy(() -> service.doSomething("KO"));
|
|
|
|
|
this.contextRunner.withUserConfiguration(ExcludedServiceConfiguration.class).run((context) -> {
|
|
|
|
|
assertThat(context.getBeansOfType(Validator.class)).hasSize(1);
|
|
|
|
|
ExcludedService service = context.getBean(ExcludedService.class);
|
|
|
|
|
service.doSomething("Valid");
|
|
|
|
|
assertThatNoException().isThrownBy(() -> service.doSomething("KO"));
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void validationUsesCglibProxy() {
|
|
|
|
|
load(DefaultAnotherSampleService.class);
|
|
|
|
|
assertThat(this.context.getBeansOfType(Validator.class)).hasSize(1);
|
|
|
|
|
DefaultAnotherSampleService service = this.context.getBean(DefaultAnotherSampleService.class);
|
|
|
|
|
service.doSomething(42);
|
|
|
|
|
assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(() -> service.doSomething(2));
|
|
|
|
|
this.contextRunner.withUserConfiguration(DefaultAnotherSampleService.class).run((context) -> {
|
|
|
|
|
assertThat(context.getBeansOfType(Validator.class)).hasSize(1);
|
|
|
|
|
DefaultAnotherSampleService service = context.getBean(DefaultAnotherSampleService.class);
|
|
|
|
|
service.doSomething(42);
|
|
|
|
|
assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(() -> service.doSomething(2));
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void validationCanBeConfiguredToUseJdkProxy() {
|
|
|
|
|
load(AnotherSampleServiceConfiguration.class, "spring.aop.proxy-target-class=false");
|
|
|
|
|
assertThat(this.context.getBeansOfType(Validator.class)).hasSize(1);
|
|
|
|
|
assertThat(this.context.getBeansOfType(DefaultAnotherSampleService.class)).isEmpty();
|
|
|
|
|
AnotherSampleService service = this.context.getBean(AnotherSampleService.class);
|
|
|
|
|
service.doSomething(42);
|
|
|
|
|
assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(() -> service.doSomething(2));
|
|
|
|
|
this.contextRunner.withUserConfiguration(AnotherSampleServiceConfiguration.class)
|
|
|
|
|
.withPropertyValues("spring.aop.proxy-target-class=false").run((context) -> {
|
|
|
|
|
assertThat(context.getBeansOfType(Validator.class)).hasSize(1);
|
|
|
|
|
assertThat(context.getBeansOfType(DefaultAnotherSampleService.class)).isEmpty();
|
|
|
|
|
AnotherSampleService service = context.getBean(AnotherSampleService.class);
|
|
|
|
|
service.doSomething(42);
|
|
|
|
|
assertThatExceptionOfType(ConstraintViolationException.class)
|
|
|
|
|
.isThrownBy(() -> service.doSomething(2));
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void userDefinedMethodValidationPostProcessorTakesPrecedence() {
|
|
|
|
|
load(SampleConfiguration.class);
|
|
|
|
|
assertThat(this.context.getBeansOfType(Validator.class)).hasSize(1);
|
|
|
|
|
Object userMethodValidationPostProcessor = this.context.getBean("testMethodValidationPostProcessor");
|
|
|
|
|
assertThat(this.context.getBean(MethodValidationPostProcessor.class))
|
|
|
|
|
.isSameAs(userMethodValidationPostProcessor);
|
|
|
|
|
assertThat(this.context.getBeansOfType(MethodValidationPostProcessor.class)).hasSize(1);
|
|
|
|
|
assertThat(this.context.getBean(Validator.class))
|
|
|
|
|
.isNotSameAs(ReflectionTestUtils.getField(userMethodValidationPostProcessor, "validator"));
|
|
|
|
|
this.contextRunner.withUserConfiguration(SampleConfiguration.class).run((context) -> {
|
|
|
|
|
assertThat(context.getBeansOfType(Validator.class)).hasSize(1);
|
|
|
|
|
Object userMethodValidationPostProcessor = context.getBean("testMethodValidationPostProcessor");
|
|
|
|
|
assertThat(context.getBean(MethodValidationPostProcessor.class))
|
|
|
|
|
.isSameAs(userMethodValidationPostProcessor);
|
|
|
|
|
assertThat(context.getBeansOfType(MethodValidationPostProcessor.class)).hasSize(1);
|
|
|
|
|
assertThat(context.getBean(Validator.class))
|
|
|
|
|
.isNotSameAs(ReflectionTestUtils.getField(userMethodValidationPostProcessor, "validator"));
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void methodValidationPostProcessorValidatorDependencyDoesNotTriggerEarlyInitialization() {
|
|
|
|
|
load(CustomValidatorConfiguration.class);
|
|
|
|
|
assertThat(this.context.getBean(TestBeanPostProcessor.class).postProcessed).contains("someService");
|
|
|
|
|
this.contextRunner.withUserConfiguration(CustomValidatorConfiguration.class)
|
|
|
|
|
.run((context) -> assertThat(context.getBean(TestBeanPostProcessor.class).postProcessed)
|
|
|
|
|
.contains("someService"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private boolean isPrimaryBean(String beanName) {
|
|
|
|
|
return this.context.getBeanDefinition(beanName).isPrimary();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void load(Class<?> config, String... environment) {
|
|
|
|
|
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
|
|
|
|
TestPropertyValues.of(environment).applyTo(ctx);
|
|
|
|
|
if (config != null) {
|
|
|
|
|
ctx.register(config);
|
|
|
|
|
}
|
|
|
|
|
ctx.register(ValidationAutoConfiguration.class);
|
|
|
|
|
ctx.refresh();
|
|
|
|
|
this.context = ctx;
|
|
|
|
|
private boolean isPrimaryBean(AssertableApplicationContext context, String beanName) {
|
|
|
|
|
return ((BeanDefinitionRegistry) context.getSourceApplicationContext()).getBeanDefinition(beanName).isPrimary();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Configuration(proxyBeanMethods = false)
|
|
|
|
|