diff --git a/spring-test/src/main/java/org/springframework/mock/http/server/reactive/MockServerHttpRequest.java b/spring-test/src/main/java/org/springframework/mock/http/server/reactive/MockServerHttpRequest.java index 90eeb86054de..e35e3f15e49a 100644 --- a/spring-test/src/main/java/org/springframework/mock/http/server/reactive/MockServerHttpRequest.java +++ b/spring-test/src/main/java/org/springframework/mock/http/server/reactive/MockServerHttpRequest.java @@ -44,6 +44,7 @@ import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MimeType; import org.springframework.util.MultiValueMap; +import org.springframework.util.StringUtils; import org.springframework.web.util.UriComponentsBuilder; /** @@ -55,11 +56,10 @@ */ public final class MockServerHttpRequest extends AbstractServerHttpRequest { - @Nullable - private final HttpMethod httpMethod; - - @Nullable - private final String customHttpMethod; + /** + * String representation of one of {@link HttpMethod} or not empty custom method (e.g. CONNECT). + */ + private final String httpMethodValue; private final MultiValueMap cookies; @@ -74,16 +74,13 @@ public final class MockServerHttpRequest extends AbstractServerHttpRequest { private final Flux body; - - private MockServerHttpRequest(@Nullable HttpMethod httpMethod, @Nullable String customHttpMethod, + private MockServerHttpRequest(String httpMethodValue, URI uri, @Nullable String contextPath, HttpHeaders headers, MultiValueMap cookies, @Nullable InetSocketAddress remoteAddress, @Nullable InetSocketAddress localAddress, @Nullable SslInfo sslInfo, Publisher body) { super(uri, contextPath, headers); - Assert.isTrue(httpMethod != null || customHttpMethod != null, "HTTP method must not be null"); - this.httpMethod = httpMethod; - this.customHttpMethod = customHttpMethod; + this.httpMethodValue = httpMethodValue; this.cookies = cookies; this.remoteAddress = remoteAddress; this.localAddress = localAddress; @@ -93,14 +90,15 @@ private MockServerHttpRequest(@Nullable HttpMethod httpMethod, @Nullable String @Override + @Nullable public HttpMethod getMethod() { - return this.httpMethod; + return HttpMethod.resolve(httpMethodValue); } @Override @SuppressWarnings("ConstantConditions") public String getMethodValue() { - return (this.httpMethod != null ? this.httpMethod.name() : this.customHttpMethod); + return httpMethodValue; } @Override @@ -232,24 +230,23 @@ public static BodyBuilder method(HttpMethod method, URI url) { * @return the created builder */ public static BodyBuilder method(HttpMethod method, String urlTemplate, Object... vars) { - Assert.notNull(method, "HttpMethod is required. If testing a custom HTTP method, " + - "please use the variant that accepts a String based HTTP method."); URI url = UriComponentsBuilder.fromUriString(urlTemplate).buildAndExpand(vars).encode().toUri(); return new DefaultBodyBuilder(method, url); } /** - * Create a builder with a raw HTTP method value that is outside the range + * Create a builder with a raw HTTP methodValue value that is outside the range * of {@link HttpMethod} enum values. - * @param method the HTTP method value + * @param methodValue the HTTP methodValue value * @param urlTemplate the URL template * @param vars variables to expand into the template * @return the created builder + * @throws IllegalArgumentException if methodValue is null, empty String or contains only white characters * @since 5.2.7 */ - public static BodyBuilder method(String method, String urlTemplate, Object... vars) { + public static BodyBuilder method(String methodValue, String urlTemplate, Object... vars) { URI url = UriComponentsBuilder.fromUriString(urlTemplate).buildAndExpand(vars).encode().toUri(); - return new DefaultBodyBuilder(method, url); + return new DefaultBodyBuilder(methodValue, url); } @@ -431,12 +428,7 @@ private static class DefaultBodyBuilder implements BodyBuilder { private static final DataBufferFactory BUFFER_FACTORY = new DefaultDataBufferFactory(); - - @Nullable - private final HttpMethod method; - - @Nullable - private final String customMethod; + private final String methodValue; private final URI url; @@ -458,23 +450,18 @@ private static class DefaultBodyBuilder implements BodyBuilder { @Nullable private SslInfo sslInfo; - - DefaultBodyBuilder(HttpMethod method, URI url) { - this.method = method; - this.customMethod = null; + protected DefaultBodyBuilder(String methodValue, URI url) { + Assert.isTrue(StringUtils.hasLength(methodValue) && + StringUtils.hasLength(methodValue.trim()), "HttpMethod is required. " + + "Please initialize it to non empty value"); + this.methodValue = methodValue.trim(); this.url = url; } - DefaultBodyBuilder(String method, URI url) { - HttpMethod resolved = HttpMethod.resolve(method); - if (resolved != null) { - this.method = resolved; - this.customMethod = null; - } - else { - this.method = null; - this.customMethod = method; - } + protected DefaultBodyBuilder(HttpMethod method, URI url) { + Assert.notNull(method, "HttpMethod is required. If testing a custom HTTP method, " + + "please use the variant that accepts a String based HTTP method."); + this.methodValue = method.name(); this.url = url; } @@ -611,7 +598,7 @@ private Charset getCharset() { @Override public MockServerHttpRequest body(Publisher body) { applyCookiesIfNecessary(); - return new MockServerHttpRequest(this.method, this.customMethod, getUrlToUse(), this.contextPath, + return new MockServerHttpRequest(this.methodValue, getUrlToUse(), this.contextPath, this.headers, this.cookies, this.remoteAddress, this.localAddress, this.sslInfo, body); } diff --git a/spring-test/src/test/java/org/springframework/mock/http/server/reactive/MockServerHttpRequestTests.java b/spring-test/src/test/java/org/springframework/mock/http/server/reactive/MockServerHttpRequestTests.java index c3a44c23be67..52ebc906b1db 100644 --- a/spring-test/src/test/java/org/springframework/mock/http/server/reactive/MockServerHttpRequestTests.java +++ b/spring-test/src/test/java/org/springframework/mock/http/server/reactive/MockServerHttpRequestTests.java @@ -17,11 +17,18 @@ package org.springframework.mock.http.server.reactive; import java.util.Arrays; +import java.util.stream.Stream; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.function.Executable; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.springframework.http.HttpCookie; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.web.util.UriComponentsBuilder; import static org.assertj.core.api.Assertions.assertThat; @@ -56,4 +63,22 @@ void queryParams() throws Exception { assertThat(request.getURI().toString()).isEqualTo("/foo%20bar?a=b&name%20A=value%20A1&name%20A=value%20A2&name%20B=value%20B1"); } + @ParameterizedTest + @MethodSource("invalidMockServerHttpRequestBuilds") + void httpMethodNotNullOrEmpty(Executable executable) { + IllegalArgumentException expectedIllegalArgumentException = Assertions.assertThrows(IllegalArgumentException.class, + executable); + assertThat(expectedIllegalArgumentException.getMessage()).contains("HttpMethod is required."); + } + + static Stream invalidMockServerHttpRequestBuilds() { + String uriTemplate = "/foo bar?a=b"; + return Stream.of( + () -> MockServerHttpRequest.method(null, UriComponentsBuilder.fromUriString(uriTemplate).build("")).build(), + () -> MockServerHttpRequest.method((HttpMethod) null, uriTemplate).build(), + () -> MockServerHttpRequest.method((String) null, uriTemplate).build(), + () -> MockServerHttpRequest.method("", uriTemplate).build(), + () -> MockServerHttpRequest.method(" ", uriTemplate).build() + ); + } } diff --git a/spring-web/src/testFixtures/java/org/springframework/web/testfixture/http/server/reactive/MockServerHttpRequest.java b/spring-web/src/testFixtures/java/org/springframework/web/testfixture/http/server/reactive/MockServerHttpRequest.java index 9a468cb99372..69d98c3b8685 100644 --- a/spring-web/src/testFixtures/java/org/springframework/web/testfixture/http/server/reactive/MockServerHttpRequest.java +++ b/spring-web/src/testFixtures/java/org/springframework/web/testfixture/http/server/reactive/MockServerHttpRequest.java @@ -44,6 +44,7 @@ import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MimeType; import org.springframework.util.MultiValueMap; +import org.springframework.util.StringUtils; import org.springframework.web.util.UriComponentsBuilder; /** @@ -55,11 +56,10 @@ */ public final class MockServerHttpRequest extends AbstractServerHttpRequest { - @Nullable - private final HttpMethod httpMethod; - - @Nullable - private final String customHttpMethod; + /** + * String representation of one of {@link HttpMethod} or not empty custom method (e.g. CONNECT). + */ + private final String httpMethodValue; private final MultiValueMap cookies; @@ -74,16 +74,13 @@ public final class MockServerHttpRequest extends AbstractServerHttpRequest { private final Flux body; - - private MockServerHttpRequest(@Nullable HttpMethod httpMethod, @Nullable String customHttpMethod, - URI uri, @Nullable String contextPath, HttpHeaders headers, MultiValueMap cookies, - @Nullable InetSocketAddress remoteAddress, @Nullable InetSocketAddress localAddress, - @Nullable SslInfo sslInfo, Publisher body) { + private MockServerHttpRequest(String httpMethodValue, + URI uri, @Nullable String contextPath, HttpHeaders headers, MultiValueMap cookies, + @Nullable InetSocketAddress remoteAddress, @Nullable InetSocketAddress localAddress, + @Nullable SslInfo sslInfo, Publisher body) { super(uri, contextPath, headers); - Assert.isTrue(httpMethod != null || customHttpMethod != null, "HTTP method must not be null"); - this.httpMethod = httpMethod; - this.customHttpMethod = customHttpMethod; + this.httpMethodValue = httpMethodValue; this.cookies = cookies; this.remoteAddress = remoteAddress; this.localAddress = localAddress; @@ -93,14 +90,15 @@ private MockServerHttpRequest(@Nullable HttpMethod httpMethod, @Nullable String @Override + @Nullable public HttpMethod getMethod() { - return this.httpMethod; + return HttpMethod.resolve(httpMethodValue); } @Override @SuppressWarnings("ConstantConditions") public String getMethodValue() { - return (this.httpMethod != null ? this.httpMethod.name() : this.customHttpMethod); + return httpMethodValue; } @Override @@ -232,24 +230,23 @@ public static BodyBuilder method(HttpMethod method, URI url) { * @return the created builder */ public static BodyBuilder method(HttpMethod method, String urlTemplate, Object... vars) { - Assert.notNull(method, "HttpMethod is required. If testing a custom HTTP method, " + - "please use the variant that accepts a String based HTTP method."); URI url = UriComponentsBuilder.fromUriString(urlTemplate).buildAndExpand(vars).encode().toUri(); return new DefaultBodyBuilder(method, url); } /** - * Create a builder with a raw HTTP method value that is outside the range + * Create a builder with a raw HTTP methodValue value that is outside the range * of {@link HttpMethod} enum values. - * @param method the HTTP method value + * @param methodValue the HTTP methodValue value * @param urlTemplate the URL template * @param vars variables to expand into the template * @return the created builder + * @throws IllegalArgumentException if methodValue is null, empty String or contains only white characters * @since 5.2.7 */ - public static BodyBuilder method(String method, String urlTemplate, Object... vars) { + public static BodyBuilder method(String methodValue, String urlTemplate, Object... vars) { URI url = UriComponentsBuilder.fromUriString(urlTemplate).buildAndExpand(vars).encode().toUri(); - return new DefaultBodyBuilder(method, url); + return new DefaultBodyBuilder(methodValue, url); } @@ -431,12 +428,7 @@ private static class DefaultBodyBuilder implements BodyBuilder { private static final DataBufferFactory BUFFER_FACTORY = new DefaultDataBufferFactory(); - - @Nullable - private final HttpMethod method; - - @Nullable - private final String customMethod; + private final String methodValue; private final URI url; @@ -458,23 +450,18 @@ private static class DefaultBodyBuilder implements BodyBuilder { @Nullable private SslInfo sslInfo; - - DefaultBodyBuilder(HttpMethod method, URI url) { - this.method = method; - this.customMethod = null; + protected DefaultBodyBuilder(String methodValue, URI url) { + Assert.isTrue(StringUtils.hasLength(methodValue) && + StringUtils.hasLength(methodValue.trim()), "HttpMethod is required. " + + "Please initialize it to non empty value"); + this.methodValue = methodValue.trim(); this.url = url; } - DefaultBodyBuilder(String method, URI url) { - HttpMethod resolved = HttpMethod.resolve(method); - if (resolved != null) { - this.method = resolved; - this.customMethod = null; - } - else { - this.method = null; - this.customMethod = method; - } + protected DefaultBodyBuilder(HttpMethod method, URI url) { + Assert.notNull(method, "HttpMethod is required. If testing a custom HTTP method, " + + "please use the variant that accepts a String based HTTP method."); + this.methodValue = method.name(); this.url = url; } @@ -611,7 +598,7 @@ private Charset getCharset() { @Override public MockServerHttpRequest body(Publisher body) { applyCookiesIfNecessary(); - return new MockServerHttpRequest(this.method, this.customMethod, getUrlToUse(), this.contextPath, + return new MockServerHttpRequest(this.methodValue, getUrlToUse(), this.contextPath, this.headers, this.cookies, this.remoteAddress, this.localAddress, this.sslInfo, body); }