Improve access to raw content in WebTestClient

If the content has not been consumed, cause it to be produced, and
wait for a certain amount of time before giving up, so the raw content
can be made available. This can occur when:

1) In a mock server scenario the Flux representing the client request
content is passed directly to the mock server request, but is never
consumed because of an error before the body is read.

2) Test obtains FluxExchangeResult (e.g. for streaming) but instead of
consuming the Flux, it calls getResponseBodyContent() instead.

Issue: SPR-17363
This commit is contained in:
Rossen Stoyanchev
2018-10-11 19:10:11 -04:00
parent c567e65eea
commit 8df0bc88d2
9 changed files with 153 additions and 97 deletions

View File

@@ -17,6 +17,7 @@
package org.springframework.test.web.reactive.server;
import java.net.URI;
import java.time.Duration;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.concurrent.TimeUnit;
@@ -257,7 +258,7 @@ public class HeaderAssertionTests {
MonoProcessor<byte[]> emptyContent = MonoProcessor.create();
emptyContent.onComplete();
ExchangeResult result = new ExchangeResult(request, response, emptyContent, emptyContent, null);
ExchangeResult result = new ExchangeResult(request, response, emptyContent, emptyContent, Duration.ZERO, null);
return new HeaderAssertions(result, mock(WebTestClient.ResponseSpec.class));
}

View File

@@ -18,17 +18,19 @@ package org.springframework.test.web.reactive.server;
import java.util.Arrays;
import org.junit.Test;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseCookie;
import org.springframework.http.server.reactive.ServerHttpResponse;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import static java.nio.charset.StandardCharsets.*;
import static org.junit.Assert.*;
/**
* Test scenarios involving a mock server.
@@ -38,7 +40,7 @@ public class MockServerTests {
@Test // SPR-15674 (in comments)
public void mutateDoesNotCreateNewSession() throws Exception {
public void mutateDoesNotCreateNewSession() {
WebTestClient client = WebTestClient
.bindToWebHandler(exchange -> {
@@ -51,8 +53,7 @@ public class MockServerTests {
return exchange.getSession()
.map(session -> session.getAttributeOrDefault("foo", "none"))
.flatMap(value -> {
byte[] bytes = value.getBytes(UTF_8);
DataBuffer buffer = new DefaultDataBufferFactory().wrap(bytes);
DataBuffer buffer = toDataBuffer(value);
return exchange.getResponse().writeWith(Mono.just(buffer));
});
}
@@ -74,7 +75,7 @@ public class MockServerTests {
}
@Test // SPR-16059
public void mutateDoesCopy() throws Exception {
public void mutateDoesCopy() {
WebTestClient.Builder builder = WebTestClient
.bindToWebHandler(exchange -> exchange.getResponse().setComplete())
@@ -111,7 +112,7 @@ public class MockServerTests {
}
@Test // SPR-16124
public void exchangeResultHasCookieHeaders() throws Exception {
public void exchangeResultHasCookieHeaders() {
ExchangeResult result = WebTestClient
.bindToWebHandler(exchange -> {
@@ -136,4 +137,32 @@ public class MockServerTests {
result.getRequestHeaders().get(HttpHeaders.COOKIE));
}
@Test
public void responseBodyContentWithFluxExchangeResult() {
FluxExchangeResult<String> result = WebTestClient
.bindToWebHandler(exchange -> {
ServerHttpResponse response = exchange.getResponse();
response.getHeaders().setContentType(MediaType.TEXT_PLAIN);
return response.writeWith(Flux.just(toDataBuffer("body")));
})
.build()
.get().uri("/")
.exchange()
.expectStatus().isOk()
.returnResult(String.class);
// Get the raw content without consuming the response body flux..
byte[] bytes = result.getResponseBodyContent();
assertNotNull(bytes);
assertEquals("body", new String(bytes, UTF_8));
}
private DataBuffer toDataBuffer(String value) {
byte[] bytes = value.getBytes(UTF_8);
return new DefaultDataBufferFactory().wrap(bytes);
}
}

View File

@@ -17,6 +17,7 @@
package org.springframework.test.web.reactive.server;
import java.net.URI;
import java.time.Duration;
import org.junit.Test;
import reactor.core.publisher.MonoProcessor;
@@ -182,7 +183,7 @@ public class StatusAssertionTests {
MonoProcessor<byte[]> emptyContent = MonoProcessor.create();
emptyContent.onComplete();
ExchangeResult result = new ExchangeResult(request, response, emptyContent, emptyContent, null);
ExchangeResult result = new ExchangeResult(request, response, emptyContent, emptyContent, Duration.ZERO, null);
return new StatusAssertions(result, mock(WebTestClient.ResponseSpec.class));
}

View File

@@ -17,6 +17,7 @@
package org.springframework.test.web.reactive.server;
import java.net.URI;
import java.time.Duration;
import org.junit.Test;
import reactor.core.publisher.Mono;
@@ -57,7 +58,7 @@ public class WiretapConnectorTests {
function.exchange(clientRequest).block(ofMillis(0));
WiretapConnector.Info actual = wiretapConnector.claimRequest("1");
ExchangeResult result = actual.createExchangeResult(null);
ExchangeResult result = actual.createExchangeResult(Duration.ZERO, null);
assertEquals(HttpMethod.GET, result.getMethod());
assertEquals("/test", result.getUrl().toString());
}

View File

@@ -16,13 +16,21 @@
package org.springframework.test.web.reactive.server.samples;
import java.nio.charset.StandardCharsets;
import org.junit.Test;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.test.web.reactive.server.EntityExchangeResult;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import static org.junit.Assert.*;
/**
* Tests with error status codes or error conditions.
*
@@ -35,7 +43,7 @@ public class ErrorTests {
@Test
public void notFound() throws Exception {
public void notFound(){
this.client.get().uri("/invalid")
.exchange()
.expectStatus().isNotFound()
@@ -43,13 +51,28 @@ public class ErrorTests {
}
@Test
public void serverException() throws Exception {
public void serverException() {
this.client.get().uri("/server-error")
.exchange()
.expectStatus().isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR)
.expectBody(Void.class);
}
@Test // SPR-17363
public void badRequestBeforeRequestBodyConsumed() {
EntityExchangeResult<Void> result = this.client.post()
.uri("/post")
.contentType(MediaType.APPLICATION_JSON_UTF8)
.syncBody(new Person("Dan"))
.exchange()
.expectStatus().isBadRequest()
.expectBody().isEmpty();
byte[] content = result.getRequestBodyContent();
assertNotNull(content);
assertEquals("{\"name\":\"Dan\"}", new String(content, StandardCharsets.UTF_8));
}
@RestController
static class TestController {
@@ -58,6 +81,10 @@ public class ErrorTests {
void handleAndThrowException() {
throw new IllegalStateException("server error");
}
@PostMapping(path = "/post", params = "p")
void handlePost(@RequestBody Person person) {
}
}
}