From 3597ebc35a099f4c9a825759ded9680b00a3ca1c Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 12 Sep 2019 17:03:21 +0100 Subject: [PATCH] Allow responses with non-standard status codes to be documented Fixes gh-639 --- .../restdocs/operation/OperationResponse.java | 12 ++++- .../operation/OperationResponseFactory.java | 12 +++-- .../operation/StandardOperationResponse.java | 9 +++- .../UriModifyingOperationPreprocessor.java | 2 +- .../RestDocumentationGeneratorTests.java | 4 +- .../RestDocumentationConfigurerTests.java | 2 +- .../ContentTypeLinkExtractorTests.java | 6 +-- .../LinkExtractorsPayloadTests.java | 2 +- ...ntModifyingOperationPreprocessorTests.java | 2 +- ...derRemovingOperationPreprocessorTests.java | 2 +- ...riModifyingOperationPreprocessorTests.java | 4 +- .../restdocs/test/OperationBuilder.java | 4 +- .../mockmvc/MockMvcResponseConverter.java | 5 +- .../MockMvcResponseConverterTests.java | 11 +++- .../RestAssuredResponseConverter.java | 5 +- .../RestAssuredResponseConverterTests.java | 52 +++++++++++++++++++ .../WebTestClientResponseConverter.java | 2 +- 17 files changed, 107 insertions(+), 29 deletions(-) create mode 100644 spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured3/RestAssuredResponseConverterTests.java diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationResponse.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationResponse.java index 6e5e20bb..378160d4 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationResponse.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 the original author or authors. + * Copyright 2014-2019 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. @@ -30,10 +30,18 @@ public interface OperationResponse { /** * Returns the status of the response. - * @return the status + * @return the status or {@code null} if the status is unknown to {@link HttpStatus} */ HttpStatus getStatus(); + /** + * Returns the status code of the response. + * @return the status code + */ + default int getStatusCode() { + throw new UnsupportedOperationException(); + } + /** * Returns the headers in the response. * @return the headers diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationResponseFactory.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationResponseFactory.java index e865987e..2768451b 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationResponseFactory.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationResponseFactory.java @@ -34,8 +34,14 @@ public class OperationResponseFactory { * @param headers the request's headers * @param content the content of the request * @return the {@code OperationResponse} + * @deprecated since 2.0.4 in favor of {@link #create(int, HttpHeaders, byte[])} */ + @Deprecated public OperationResponse create(HttpStatus status, HttpHeaders headers, byte[] content) { + return this.create(status.value(), headers, content); + } + + public OperationResponse create(int status, HttpHeaders headers, byte[] content) { return new StandardOperationResponse(status, augmentHeaders(headers, content), content); } @@ -49,8 +55,8 @@ public class OperationResponseFactory { * @return the new response with the new content */ public OperationResponse createFrom(OperationResponse original, byte[] newContent) { - return new StandardOperationResponse(original.getStatus(), getUpdatedHeaders(original.getHeaders(), newContent), - newContent); + return new StandardOperationResponse(original.getStatusCode(), + getUpdatedHeaders(original.getHeaders(), newContent), newContent); } /** @@ -61,7 +67,7 @@ public class OperationResponseFactory { * @return the new response with the new headers */ public OperationResponse createFrom(OperationResponse original, HttpHeaders newHeaders) { - return new StandardOperationResponse(original.getStatus(), newHeaders, original.getContent()); + return new StandardOperationResponse(original.getStatusCode(), newHeaders, original.getContent()); } private HttpHeaders augmentHeaders(HttpHeaders originalHeaders, byte[] content) { diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/StandardOperationResponse.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/StandardOperationResponse.java index 6896245c..3a169365 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/StandardOperationResponse.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/StandardOperationResponse.java @@ -26,7 +26,7 @@ import org.springframework.http.HttpStatus; */ class StandardOperationResponse extends AbstractOperationMessage implements OperationResponse { - private final HttpStatus status; + private final int status; /** * Creates a new response with the given {@code status}, {@code headers}, and @@ -35,13 +35,18 @@ class StandardOperationResponse extends AbstractOperationMessage implements Oper * @param headers the headers of the response * @param content the content of the response */ - StandardOperationResponse(HttpStatus status, HttpHeaders headers, byte[] content) { + StandardOperationResponse(int status, HttpHeaders headers, byte[] content) { super(content, headers); this.status = status; } @Override public HttpStatus getStatus() { + return HttpStatus.resolve(this.status); + } + + @Override + public int getStatusCode() { return this.status; } diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/UriModifyingOperationPreprocessor.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/UriModifyingOperationPreprocessor.java index f1856dbd..42e0df1c 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/UriModifyingOperationPreprocessor.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/UriModifyingOperationPreprocessor.java @@ -141,7 +141,7 @@ public class UriModifyingOperationPreprocessor implements OperationPreprocessor @Override public OperationResponse preprocess(OperationResponse response) { - return this.contentModifyingDelegate.preprocess(new OperationResponseFactory().create(response.getStatus(), + return this.contentModifyingDelegate.preprocess(new OperationResponseFactory().create(response.getStatusCode(), modify(response.getHeaders()), response.getContent())); } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/RestDocumentationGeneratorTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/RestDocumentationGeneratorTests.java index c16c2727..c84ec96e 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/RestDocumentationGeneratorTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/RestDocumentationGeneratorTests.java @@ -69,7 +69,7 @@ public class RestDocumentationGeneratorTests { private final OperationRequest operationRequest = new OperationRequestFactory() .create(URI.create("http://localhost:8080"), null, null, new HttpHeaders(), null, null); - private final OperationResponse operationResponse = new OperationResponseFactory().create(null, null, null); + private final OperationResponse operationResponse = new OperationResponseFactory().create(0, null, null); private final Snippet snippet = mock(Snippet.class); @@ -197,7 +197,7 @@ public class RestDocumentationGeneratorTests { } private static OperationResponse createResponse() { - return new OperationResponseFactory().create(null, null, null); + return new OperationResponseFactory().create(0, null, null); } } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/config/RestDocumentationConfigurerTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/config/RestDocumentationConfigurerTests.java index 990a3e60..c8ade927 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/config/RestDocumentationConfigurerTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/config/RestDocumentationConfigurerTests.java @@ -217,7 +217,7 @@ public class RestDocumentationConfigurerTests { .get(RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_OPERATION_RESPONSE_PREPROCESSOR); HttpHeaders headers = new HttpHeaders(); headers.add("Foo", "value"); - OperationResponse response = new OperationResponseFactory().create(HttpStatus.OK, headers, null); + OperationResponse response = new OperationResponseFactory().create(HttpStatus.OK.value(), headers, null); assertThat(preprocessor.preprocess(response).getHeaders()).doesNotContainKey("Foo"); } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/ContentTypeLinkExtractorTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/ContentTypeLinkExtractorTests.java index cb88d4d2..e987a5af 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/ContentTypeLinkExtractorTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/ContentTypeLinkExtractorTests.java @@ -49,7 +49,7 @@ public class ContentTypeLinkExtractorTests { public void extractionFailsWithNullContentType() throws IOException { this.thrown.expect(IllegalStateException.class); new ContentTypeLinkExtractor() - .extractLinks(this.responseFactory.create(HttpStatus.OK, new HttpHeaders(), null)); + .extractLinks(this.responseFactory.create(HttpStatus.OK.value(), new HttpHeaders(), null)); } @Test @@ -59,7 +59,7 @@ public class ContentTypeLinkExtractorTests { extractors.put(MediaType.APPLICATION_JSON, extractor); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setContentType(MediaType.APPLICATION_JSON); - OperationResponse response = this.responseFactory.create(HttpStatus.OK, httpHeaders, null); + OperationResponse response = this.responseFactory.create(HttpStatus.OK.value(), httpHeaders, null); new ContentTypeLinkExtractor(extractors).extractLinks(response); verify(extractor).extractLinks(response); } @@ -71,7 +71,7 @@ public class ContentTypeLinkExtractorTests { extractors.put(MediaType.APPLICATION_JSON, extractor); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setContentType(MediaType.parseMediaType("application/json;foo=bar")); - OperationResponse response = this.responseFactory.create(HttpStatus.OK, httpHeaders, null); + OperationResponse response = this.responseFactory.create(HttpStatus.OK.value(), httpHeaders, null); new ContentTypeLinkExtractor(extractors).extractLinks(response); verify(extractor).extractLinks(response); } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/LinkExtractorsPayloadTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/LinkExtractorsPayloadTests.java index 55e4ccac..1eb99f19 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/LinkExtractorsPayloadTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/LinkExtractorsPayloadTests.java @@ -106,7 +106,7 @@ public class LinkExtractorsPayloadTests { } private OperationResponse createResponse(String contentName) throws IOException { - return this.responseFactory.create(HttpStatus.OK, null, + return this.responseFactory.create(HttpStatus.OK.value(), null, FileCopyUtils.copyToByteArray(getPayloadFile(contentName))); } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/ContentModifyingOperationPreprocessorTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/ContentModifyingOperationPreprocessorTests.java index 1835b856..57b54c98 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/ContentModifyingOperationPreprocessorTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/ContentModifyingOperationPreprocessorTests.java @@ -67,7 +67,7 @@ public class ContentModifyingOperationPreprocessorTests { @Test public void modifyResponseContent() { - OperationResponse response = this.responseFactory.create(HttpStatus.OK, new HttpHeaders(), + OperationResponse response = this.responseFactory.create(HttpStatus.OK.value(), new HttpHeaders(), "content".getBytes()); OperationResponse preprocessed = this.preprocessor.preprocess(response); assertThat(preprocessed.getContent()).isEqualTo("modified".getBytes()); diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/HeaderRemovingOperationPreprocessorTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/HeaderRemovingOperationPreprocessorTests.java index 79330122..87f5e8c0 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/HeaderRemovingOperationPreprocessorTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/HeaderRemovingOperationPreprocessorTests.java @@ -87,7 +87,7 @@ public class HeaderRemovingOperationPreprocessorTests { } private OperationResponse createResponse(String... extraHeaders) { - return this.responseFactory.create(HttpStatus.OK, getHttpHeaders(extraHeaders), new byte[0]); + return this.responseFactory.create(HttpStatus.OK.value(), getHttpHeaders(extraHeaders), new byte[0]); } private HttpHeaders getHttpHeaders(String... extraHeaders) { diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/UriModifyingOperationPreprocessorTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/UriModifyingOperationPreprocessorTests.java index 2880c4b0..2b9562bd 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/UriModifyingOperationPreprocessorTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/UriModifyingOperationPreprocessorTests.java @@ -321,13 +321,13 @@ public class UriModifyingOperationPreprocessorTests { } private OperationResponse createResponseWithContent(String content) { - return this.responseFactory.create(HttpStatus.OK, new HttpHeaders(), content.getBytes()); + return this.responseFactory.create(HttpStatus.OK.value(), new HttpHeaders(), content.getBytes()); } private OperationResponse createResponseWithHeader(String name, String value) { HttpHeaders headers = new HttpHeaders(); headers.add(name, value); - return this.responseFactory.create(HttpStatus.OK, headers, new byte[0]); + return this.responseFactory.create(HttpStatus.OK.value(), headers, new byte[0]); } } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/test/OperationBuilder.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/test/OperationBuilder.java index d4ec2520..fbe16a08 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/test/OperationBuilder.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/test/OperationBuilder.java @@ -261,7 +261,7 @@ public class OperationBuilder extends OperationTestRule { */ public final class OperationResponseBuilder { - private HttpStatus status = HttpStatus.OK; + private int status = HttpStatus.OK.value(); private HttpHeaders headers = new HttpHeaders(); @@ -272,7 +272,7 @@ public class OperationBuilder extends OperationTestRule { } public OperationResponseBuilder status(int status) { - this.status = HttpStatus.valueOf(status); + this.status = status; return this; } diff --git a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcResponseConverter.java b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcResponseConverter.java index 8e1e49aa..93e6b1f4 100644 --- a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcResponseConverter.java +++ b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcResponseConverter.java @@ -19,7 +19,6 @@ package org.springframework.restdocs.mockmvc; import javax.servlet.http.Cookie; import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.restdocs.operation.OperationResponse; import org.springframework.restdocs.operation.OperationResponseFactory; @@ -36,8 +35,8 @@ class MockMvcResponseConverter implements ResponseConverter { @Override public OperationResponse convert(Response response) { - return new OperationResponseFactory().create(HttpStatus.valueOf(response.getStatusCode()), - extractHeaders(response), extractContent(response)); + return new OperationResponseFactory().create(response.getStatusCode(), extractHeaders(response), + extractContent(response)); } private HttpHeaders extractHeaders(Response response) { diff --git a/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured3/RestAssuredResponseConverterTests.java b/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured3/RestAssuredResponseConverterTests.java new file mode 100644 index 00000000..927cf67b --- /dev/null +++ b/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured3/RestAssuredResponseConverterTests.java @@ -0,0 +1,52 @@ +/* + * Copyright 2014-2019 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.restdocs.restassured3; + +import io.restassured.http.Headers; +import io.restassured.response.Response; +import io.restassured.response.ResponseBody; +import org.junit.Test; + +import org.springframework.restdocs.operation.OperationResponse; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link RestAssuredResponseConverter}. + * + * @author Andy Wilkinson + */ +public class RestAssuredResponseConverterTests { + + private final RestAssuredResponseConverter converter = new RestAssuredResponseConverter(); + + @Test + public void responseWithCustomStatus() { + Response response = mock(Response.class); + given(response.getStatusCode()).willReturn(600); + given(response.getHeaders()).willReturn(new Headers()); + ResponseBody body = mock(ResponseBody.class); + given(response.getBody()).willReturn(body); + given(body.asByteArray()).willReturn(new byte[0]); + OperationResponse operationResponse = this.converter.convert(response); + assertThat(operationResponse.getStatus()).isNull(); + assertThat(operationResponse.getStatusCode()).isEqualTo(600); + } + +} diff --git a/spring-restdocs-webtestclient/src/main/java/org/springframework/restdocs/webtestclient/WebTestClientResponseConverter.java b/spring-restdocs-webtestclient/src/main/java/org/springframework/restdocs/webtestclient/WebTestClientResponseConverter.java index 293203df..df0a6084 100644 --- a/spring-restdocs-webtestclient/src/main/java/org/springframework/restdocs/webtestclient/WebTestClientResponseConverter.java +++ b/spring-restdocs-webtestclient/src/main/java/org/springframework/restdocs/webtestclient/WebTestClientResponseConverter.java @@ -36,7 +36,7 @@ class WebTestClientResponseConverter implements ResponseConverter