Move MultipartAutoConfigurationTests to main code's module

This commit is contained in:
Andy Wilkinson
2025-06-05 12:24:28 +01:00
committed by Phillip Webb
parent 8efbebb9b3
commit baadf28fc3
3 changed files with 16 additions and 10 deletions

View File

@@ -18,8 +18,17 @@ dependencies {
optional("jakarta.servlet:jakarta.servlet-api")
optional("org.springframework.security:spring-security-config")
testImplementation(project(":spring-boot-project:spring-boot-jetty"))
testImplementation(project(":spring-boot-project:spring-boot-test"))
testImplementation(project(":spring-boot-project:spring-boot-tomcat"))
testImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support"))
testImplementation(project(":spring-boot-project:spring-boot-undertow"))
testImplementation("org.springframework:spring-webmvc")
testRuntimeOnly("ch.qos.logback:logback-classic")
testRuntimeOnly("org.apache.httpcomponents.client5:httpclient5")
}
tasks.named("test") {
jvmArgs += "--add-opens=java.base/java.net=ALL-UNNAMED"
}

View File

@@ -0,0 +1,404 @@
/*
* Copyright 2012-2025 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.servlet.autoconfigure;
import java.net.URI;
import java.util.stream.Stream;
import jakarta.servlet.MultipartConfigElement;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.jetty.servlet.JettyServletWebServerFactory;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.boot.testsupport.classpath.ForkedClassPath;
import org.springframework.boot.testsupport.web.servlet.DirtiesUrlFactories;
import org.springframework.boot.tomcat.autoconfigure.servlet.TomcatServletWebServerAutoConfiguration;
import org.springframework.boot.tomcat.servlet.TomcatServletWebServerFactory;
import org.springframework.boot.undertow.servlet.UndertowServletWebServerFactory;
import org.springframework.boot.web.server.autoconfigure.ServerProperties;
import org.springframework.boot.web.server.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link MultipartAutoConfiguration}. Tests an empty configuration, no
* multipart configuration, and a multipart configuration (with both Jetty and Tomcat).
*
* @author Greg Turnquist
* @author Dave Syer
* @author Josh Long
* @author Ivan Sopov
* @author Toshiaki Maki
* @author Yanming Zhou
*/
@DirtiesUrlFactories
class MultipartAutoConfigurationTests {
private AnnotationConfigServletWebServerApplicationContext context;
@AfterEach
void close() {
if (this.context != null) {
this.context.close();
}
}
@Test
void webServerWithNothing() throws Exception {
this.context = new AnnotationConfigServletWebServerApplicationContext(WebServerWithNothing.class,
BaseConfiguration.class);
DispatcherServlet servlet = this.context.getBean(DispatcherServlet.class);
verify404();
assertThat(servlet.getMultipartResolver()).isNotNull();
assertThat(this.context.getBeansOfType(StandardServletMultipartResolver.class)).hasSize(1);
assertThat(this.context.getBeansOfType(MultipartResolver.class)).hasSize(1);
}
@ParameterizedTest(name = "{0}")
@MethodSource("webServerWithNoMultipartConfigurationArguments")
@ForkedClassPath
void webServerWithNoMultipartConfiguration(String server, Class<?> configuration) {
this.context = new AnnotationConfigServletWebServerApplicationContext(configuration, BaseConfiguration.class);
assertThat(this.context.getBeansOfType(StandardServletMultipartResolver.class)).hasSize(1);
assertThat(this.context.getBeansOfType(MultipartResolver.class)).hasSize(1);
verifyServletWorks();
assertThat(this.context.getBean(StandardServletMultipartResolver.class))
.isSameAs(this.context.getBean(DispatcherServlet.class).getMultipartResolver());
}
static Stream<Arguments> webServerWithNoMultipartConfigurationArguments() {
return Stream.of(Arguments.of("Jetty", WebServerWithNoMultipartJetty.class),
Arguments.of("Tomcat", WebServerWithNoMultipartTomcat.class),
Arguments.of("Undertow", WebServerWithNoMultipartUndertow.class));
}
@ParameterizedTest(name = "{0}")
@MethodSource("webServerWithAutomatedMultipartConfigurationArguments")
@ForkedClassPath
void webServerWithAutomatedMultipartConfiguration(String server, Class<?> configuration) {
this.context = new AnnotationConfigServletWebServerApplicationContext(configuration, BaseConfiguration.class);
this.context.getBean(MultipartConfigElement.class);
verifyServletWorks();
assertThat(this.context.getBean(StandardServletMultipartResolver.class))
.isSameAs(this.context.getBean(DispatcherServlet.class).getMultipartResolver());
}
static Stream<Arguments> webServerWithAutomatedMultipartConfigurationArguments() {
return Stream.of(Arguments.of("Jetty", WebServerWithEverythingJetty.class),
Arguments.of("Tomcat", WebServerWithEverythingTomcat.class),
Arguments.of("Undertow", WebServerWithEverythingUndertow.class));
}
@Test
void webServerWithNonAbsoluteMultipartLocationUndertowConfiguration() {
this.context = new AnnotationConfigServletWebServerApplicationContext(
WebServerWithNonAbsolutePathUndertow.class, BaseConfiguration.class);
this.context.getBean(MultipartConfigElement.class);
verifyServletWorks();
assertThat(this.context.getBean(StandardServletMultipartResolver.class))
.isSameAs(this.context.getBean(DispatcherServlet.class).getMultipartResolver());
}
@Test
void webServerWithMultipartConfigDisabled() {
testWebServerWithCustomMultipartConfigEnabledSetting("false", 0);
}
@Test
void webServerWithMultipartConfigEnabled() {
testWebServerWithCustomMultipartConfigEnabledSetting("true", 1);
}
private void testWebServerWithCustomMultipartConfigEnabledSetting(final String propertyValue,
int expectedNumberOfMultipartConfigElementBeans) {
this.context = new AnnotationConfigServletWebServerApplicationContext();
TestPropertyValues.of("spring.servlet.multipart.enabled=" + propertyValue).applyTo(this.context);
this.context.register(WebServerWithNoMultipartTomcat.class, BaseConfiguration.class);
this.context.refresh();
this.context.getBean(MultipartProperties.class);
assertThat(this.context.getBeansOfType(MultipartConfigElement.class))
.hasSize(expectedNumberOfMultipartConfigElementBeans);
}
@Test
void webServerWithCustomMultipartResolver() {
this.context = new AnnotationConfigServletWebServerApplicationContext(
WebServerWithCustomMultipartResolver.class, BaseConfiguration.class);
MultipartResolver multipartResolver = this.context.getBean(MultipartResolver.class);
assertThat(multipartResolver).isNotInstanceOf(StandardServletMultipartResolver.class);
assertThat(this.context.getBeansOfType(MultipartConfigElement.class)).hasSize(1);
}
@Test
void configureResolveLazily() {
this.context = new AnnotationConfigServletWebServerApplicationContext();
TestPropertyValues.of("spring.servlet.multipart.resolve-lazily=true").applyTo(this.context);
this.context.register(WebServerWithNothing.class, BaseConfiguration.class);
this.context.refresh();
StandardServletMultipartResolver multipartResolver = this.context
.getBean(StandardServletMultipartResolver.class);
assertThat(multipartResolver).hasFieldOrPropertyWithValue("resolveLazily", true);
}
@Test
void configureStrictServletCompliance() {
this.context = new AnnotationConfigServletWebServerApplicationContext();
TestPropertyValues.of("spring.servlet.multipart.strict-servlet-compliance=true").applyTo(this.context);
this.context.register(WebServerWithNothing.class, BaseConfiguration.class);
this.context.refresh();
StandardServletMultipartResolver multipartResolver = this.context
.getBean(StandardServletMultipartResolver.class);
assertThat(multipartResolver).hasFieldOrPropertyWithValue("strictServletCompliance", true);
}
@Test
void configureMultipartProperties() {
this.context = new AnnotationConfigServletWebServerApplicationContext();
TestPropertyValues
.of("spring.servlet.multipart.max-file-size=2048KB", "spring.servlet.multipart.max-request-size=15MB")
.applyTo(this.context);
this.context.register(WebServerWithNothing.class, BaseConfiguration.class);
this.context.refresh();
MultipartConfigElement multipartConfigElement = this.context.getBean(MultipartConfigElement.class);
assertThat(multipartConfigElement.getMaxFileSize()).isEqualTo(2048 * 1024);
assertThat(multipartConfigElement.getMaxRequestSize()).isEqualTo(15 * 1024 * 1024);
}
@Test
void configureMultipartPropertiesWithRawLongValues() {
this.context = new AnnotationConfigServletWebServerApplicationContext();
TestPropertyValues
.of("spring.servlet.multipart.max-file-size=512", "spring.servlet.multipart.max-request-size=2048")
.applyTo(this.context);
this.context.register(WebServerWithNothing.class, BaseConfiguration.class);
this.context.refresh();
MultipartConfigElement multipartConfigElement = this.context.getBean(MultipartConfigElement.class);
assertThat(multipartConfigElement.getMaxFileSize()).isEqualTo(512);
assertThat(multipartConfigElement.getMaxRequestSize()).isEqualTo(2048);
}
private void verify404() throws Exception {
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
ClientHttpRequest request = requestFactory
.createRequest(new URI("http://localhost:" + this.context.getWebServer().getPort() + "/"), HttpMethod.GET);
try (ClientHttpResponse response = request.execute()) {
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
}
}
private void verifyServletWorks() {
RestTemplate restTemplate = new RestTemplate();
String url = "http://localhost:" + this.context.getWebServer().getPort() + "/";
assertThat(restTemplate.getForObject(url, String.class)).isEqualTo("Hello");
}
@Configuration(proxyBeanMethods = false)
static class WebServerWithNothing {
}
@Configuration(proxyBeanMethods = false)
static class WebServerWithNoMultipartJetty {
@Bean
JettyServletWebServerFactory webServerFactory() {
return new JettyServletWebServerFactory();
}
@Bean
WebController controller() {
return new WebController();
}
}
@Configuration(proxyBeanMethods = false)
static class WebServerWithNoMultipartUndertow {
@Bean
UndertowServletWebServerFactory webServerFactory() {
return new UndertowServletWebServerFactory();
}
@Bean
WebController controller() {
return new WebController();
}
}
@Configuration(proxyBeanMethods = false)
@Import({ TomcatServletWebServerAutoConfiguration.class, MultipartAutoConfiguration.class })
@EnableConfigurationProperties(MultipartProperties.class)
static class BaseConfiguration {
@Bean
ServerProperties serverProperties() {
ServerProperties properties = new ServerProperties();
properties.setPort(0);
return properties;
}
@Bean
DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
}
@Configuration(proxyBeanMethods = false)
static class WebServerWithNoMultipartTomcat {
@Bean
TomcatServletWebServerFactory webServerFactory() {
return new TomcatServletWebServerFactory();
}
@Bean
WebController controller() {
return new WebController();
}
}
@Configuration(proxyBeanMethods = false)
static class WebServerWithEverythingJetty {
@Bean
MultipartConfigElement multipartConfigElement() {
return new MultipartConfigElement("");
}
@Bean
JettyServletWebServerFactory webServerFactory() {
return new JettyServletWebServerFactory();
}
@Bean
WebController webController() {
return new WebController();
}
}
@Configuration(proxyBeanMethods = false)
@EnableWebMvc
static class WebServerWithEverythingTomcat {
@Bean
MultipartConfigElement multipartConfigElement() {
return new MultipartConfigElement("");
}
@Bean
TomcatServletWebServerFactory webServerFactory() {
return new TomcatServletWebServerFactory();
}
@Bean
WebController webController() {
return new WebController();
}
}
@Configuration(proxyBeanMethods = false)
@EnableWebMvc
static class WebServerWithEverythingUndertow {
@Bean
MultipartConfigElement multipartConfigElement() {
return new MultipartConfigElement("");
}
@Bean
UndertowServletWebServerFactory webServerFactory() {
return new UndertowServletWebServerFactory();
}
@Bean
WebController webController() {
return new WebController();
}
}
@Configuration(proxyBeanMethods = false)
@EnableWebMvc
static class WebServerWithNonAbsolutePathUndertow {
@Bean
MultipartConfigElement multipartConfigElement() {
return new MultipartConfigElement("test/not-absolute");
}
@Bean
UndertowServletWebServerFactory webServerFactory() {
return new UndertowServletWebServerFactory();
}
@Bean
WebController webController() {
return new WebController();
}
}
@Configuration(proxyBeanMethods = false)
static class WebServerWithCustomMultipartResolver {
@Bean
MultipartResolver multipartResolver() {
return mock(MultipartResolver.class);
}
}
@Controller
static class WebController {
@RequestMapping("/")
@ResponseBody
String index() {
return "Hello";
}
}
}