Restore support for REST Assured

Closes gh-784
This commit is contained in:
Andy Wilkinson
2022-01-21 15:33:21 +00:00
parent f5beaaacff
commit d38d79755b
53 changed files with 3403 additions and 11 deletions

View File

@@ -0,0 +1,295 @@
/*
* Copyright 2014-2022 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.restassured;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.net.URI;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import io.restassured.RestAssured;
import io.restassured.specification.FilterableRequestSpecification;
import io.restassured.specification.RequestSpecification;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.restdocs.operation.OperationRequest;
import org.springframework.restdocs.operation.OperationRequestPart;
import org.springframework.restdocs.operation.RequestCookie;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link RestAssuredRequestConverter}.
*
* @author Andy Wilkinson
*/
public class RestAssuredRequestConverterTests {
@ClassRule
public static TomcatServer tomcat = new TomcatServer();
@Rule
public final ExpectedException thrown = ExpectedException.none();
private final RestAssuredRequestConverter factory = new RestAssuredRequestConverter();
@Test
public void requestUri() {
RequestSpecification requestSpec = RestAssured.given().port(tomcat.getPort());
requestSpec.get("/foo/bar");
OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec);
assertThat(request.getUri()).isEqualTo(URI.create("http://localhost:" + tomcat.getPort() + "/foo/bar"));
}
@Test
public void requestMethod() {
RequestSpecification requestSpec = RestAssured.given().port(tomcat.getPort());
requestSpec.head("/foo/bar");
OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec);
assertThat(request.getMethod()).isEqualTo(HttpMethod.HEAD);
}
@Test
public void queryStringParameters() {
RequestSpecification requestSpec = RestAssured.given().port(tomcat.getPort()).queryParam("foo", "bar");
requestSpec.get("/");
OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec);
assertThat(request.getParameters()).hasSize(1);
assertThat(request.getParameters()).containsEntry("foo", Collections.singletonList("bar"));
}
@Test
public void queryStringFromUrlParameters() {
RequestSpecification requestSpec = RestAssured.given().port(tomcat.getPort());
requestSpec.get("/?foo=bar&foo=qix");
OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec);
assertThat(request.getParameters()).hasSize(1);
assertThat(request.getParameters()).containsEntry("foo", Arrays.asList("bar", "qix"));
}
@Test
public void formParameters() {
RequestSpecification requestSpec = RestAssured.given().port(tomcat.getPort()).formParam("foo", "bar");
requestSpec.get("/");
OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec);
assertThat(request.getParameters()).hasSize(1);
assertThat(request.getParameters()).containsEntry("foo", Collections.singletonList("bar"));
}
@Test
public void requestParameters() {
RequestSpecification requestSpec = RestAssured.given().port(tomcat.getPort()).param("foo", "bar");
requestSpec.get("/");
OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec);
assertThat(request.getParameters()).hasSize(1);
assertThat(request.getParameters()).containsEntry("foo", Collections.singletonList("bar"));
}
@Test
public void headers() {
RequestSpecification requestSpec = RestAssured.given().port(tomcat.getPort()).header("Foo", "bar");
requestSpec.get("/");
OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec);
assertThat(request.getHeaders()).hasSize(2);
assertThat(request.getHeaders()).containsEntry("Foo", Collections.singletonList("bar"));
assertThat(request.getHeaders()).containsEntry("Host",
Collections.singletonList("localhost:" + tomcat.getPort()));
}
@Test
public void headersWithCustomAccept() {
RequestSpecification requestSpec = RestAssured.given().port(tomcat.getPort()).header("Foo", "bar")
.accept("application/json");
requestSpec.get("/");
OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec);
assertThat(request.getHeaders()).hasSize(3);
assertThat(request.getHeaders()).containsEntry("Foo", Collections.singletonList("bar"));
assertThat(request.getHeaders()).containsEntry("Accept", Collections.singletonList("application/json"));
assertThat(request.getHeaders()).containsEntry("Host",
Collections.singletonList("localhost:" + tomcat.getPort()));
}
@Test
public void cookies() {
RequestSpecification requestSpec = RestAssured.given().port(tomcat.getPort()).cookie("cookie1", "cookieVal1")
.cookie("cookie2", "cookieVal2");
requestSpec.get("/");
OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec);
assertThat(request.getCookies().size()).isEqualTo(2);
Iterator<RequestCookie> cookieIterator = request.getCookies().iterator();
RequestCookie cookie1 = cookieIterator.next();
assertThat(cookie1.getName()).isEqualTo("cookie1");
assertThat(cookie1.getValue()).isEqualTo("cookieVal1");
RequestCookie cookie2 = cookieIterator.next();
assertThat(cookie2.getName()).isEqualTo("cookie2");
assertThat(cookie2.getValue()).isEqualTo("cookieVal2");
}
@Test
public void multipart() {
RequestSpecification requestSpec = RestAssured.given().port(tomcat.getPort())
.multiPart("a", "a.txt", "alpha", null).multiPart("b", new ObjectBody("bar"), "application/json");
requestSpec.post();
OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec);
Collection<OperationRequestPart> parts = request.getParts();
assertThat(parts).hasSize(2);
assertThat(parts).extracting("name").containsExactly("a", "b");
assertThat(parts).extracting("submittedFileName").containsExactly("a.txt", "file");
assertThat(parts).extracting("contentAsString").containsExactly("alpha", "{\"foo\":\"bar\"}");
assertThat(parts).extracting("headers").extracting(HttpHeaders.CONTENT_TYPE).containsExactly(
Collections.singletonList(MediaType.TEXT_PLAIN_VALUE),
Collections.singletonList(MediaType.APPLICATION_JSON_VALUE));
}
@Test
public void byteArrayBody() {
RequestSpecification requestSpec = RestAssured.given().body("body".getBytes()).port(tomcat.getPort());
requestSpec.post();
this.factory.convert((FilterableRequestSpecification) requestSpec);
}
@Test
public void stringBody() {
RequestSpecification requestSpec = RestAssured.given().body("body").port(tomcat.getPort());
requestSpec.post();
OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec);
assertThat(request.getContentAsString()).isEqualTo("body");
}
@Test
public void objectBody() {
RequestSpecification requestSpec = RestAssured.given().body(new ObjectBody("bar")).port(tomcat.getPort());
requestSpec.post();
OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec);
assertThat(request.getContentAsString()).isEqualTo("{\"foo\":\"bar\"}");
}
@Test
public void byteArrayInputStreamBody() {
RequestSpecification requestSpec = RestAssured.given().body(new ByteArrayInputStream(new byte[] { 1, 2, 3, 4 }))
.port(tomcat.getPort());
requestSpec.post();
OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec);
assertThat(request.getContent()).isEqualTo(new byte[] { 1, 2, 3, 4 });
}
@Test
public void fileBody() {
RequestSpecification requestSpec = RestAssured.given().body(new File("src/test/resources/body.txt"))
.port(tomcat.getPort());
requestSpec.post();
OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec);
assertThat(request.getContentAsString()).isEqualTo("file");
}
@Test
public void fileInputStreamBody() throws FileNotFoundException {
FileInputStream inputStream = new FileInputStream("src/test/resources/body.txt");
RequestSpecification requestSpec = RestAssured.given().body(inputStream).port(tomcat.getPort());
requestSpec.post();
this.thrown.expect(IllegalStateException.class);
this.thrown.expectMessage("Cannot read content from input stream " + inputStream + " due to reset() failure");
this.factory.convert((FilterableRequestSpecification) requestSpec);
}
@Test
public void multipartWithByteArrayInputStreamBody() {
RequestSpecification requestSpec = RestAssured.given().port(tomcat.getPort()).multiPart("foo", "foo.txt",
new ByteArrayInputStream("foo".getBytes()));
requestSpec.post();
OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec);
assertThat(request.getParts().iterator().next().getContentAsString()).isEqualTo("foo");
}
@Test
public void multipartWithStringBody() {
RequestSpecification requestSpec = RestAssured.given().port(tomcat.getPort()).multiPart("control", "foo");
requestSpec.post();
OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec);
assertThat(request.getParts().iterator().next().getContentAsString()).isEqualTo("foo");
}
@Test
public void multipartWithByteArrayBody() {
RequestSpecification requestSpec = RestAssured.given().port(tomcat.getPort()).multiPart("control", "file",
"foo".getBytes());
requestSpec.post();
OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec);
assertThat(request.getParts().iterator().next().getContentAsString()).isEqualTo("foo");
}
@Test
public void multipartWithFileBody() {
RequestSpecification requestSpec = RestAssured.given().port(tomcat.getPort())
.multiPart(new File("src/test/resources/body.txt"));
requestSpec.post();
OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec);
assertThat(request.getParts().iterator().next().getContentAsString()).isEqualTo("file");
}
@Test
public void multipartWithFileInputStreamBody() throws FileNotFoundException {
FileInputStream inputStream = new FileInputStream("src/test/resources/body.txt");
RequestSpecification requestSpec = RestAssured.given().port(tomcat.getPort()).multiPart("foo", "foo.txt",
inputStream);
requestSpec.post();
this.thrown.expect(IllegalStateException.class);
this.thrown.expectMessage("Cannot read content from input stream " + inputStream + " due to reset() failure");
this.factory.convert((FilterableRequestSpecification) requestSpec);
}
@Test
public void multipartWithObjectBody() {
RequestSpecification requestSpec = RestAssured.given().port(tomcat.getPort()).multiPart("control",
new ObjectBody("bar"));
requestSpec.post();
OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec);
assertThat(request.getParts().iterator().next().getContentAsString()).isEqualTo("{\"foo\":\"bar\"}");
}
/**
* Sample object body to verify JSON serialization.
*/
public static class ObjectBody {
private final String foo;
ObjectBody(String foo) {
this.foo = foo;
}
public String getFoo() {
return this.foo;
}
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2014-2022 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.restassured;
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);
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright 2014-2022 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.restassured;
import java.util.List;
import java.util.Map;
import io.restassured.filter.FilterContext;
import io.restassured.specification.FilterableRequestSpecification;
import io.restassured.specification.FilterableResponseSpecification;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.restdocs.JUnitRestDocumentation;
import org.springframework.restdocs.generate.RestDocumentationGenerator;
import org.springframework.restdocs.operation.preprocess.OperationRequestPreprocessor;
import org.springframework.restdocs.operation.preprocess.OperationResponsePreprocessor;
import org.springframework.restdocs.operation.preprocess.Preprocessors;
import org.springframework.restdocs.snippet.WriterResolver;
import org.springframework.restdocs.templates.TemplateEngine;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link RestAssuredRestDocumentationConfigurer}.
*
* @author Andy Wilkinson
* @author Filip Hrisafov
*/
public class RestAssuredRestDocumentationConfigurerTests {
@Rule
public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
private final FilterableRequestSpecification requestSpec = mock(FilterableRequestSpecification.class);
private final FilterableResponseSpecification responseSpec = mock(FilterableResponseSpecification.class);
private final FilterContext filterContext = mock(FilterContext.class);
private final RestAssuredRestDocumentationConfigurer configurer = new RestAssuredRestDocumentationConfigurer(
this.restDocumentation);
@Test
public void nextFilterIsCalled() {
this.configurer.filter(this.requestSpec, this.responseSpec, this.filterContext);
verify(this.filterContext).next(this.requestSpec, this.responseSpec);
}
@Test
public void configurationIsAddedToTheContext() {
this.configurer.operationPreprocessors().withRequestDefaults(Preprocessors.prettyPrint())
.withResponseDefaults(Preprocessors.removeHeaders("Foo"))
.filter(this.requestSpec, this.responseSpec, this.filterContext);
@SuppressWarnings("rawtypes")
ArgumentCaptor<Map> configurationCaptor = ArgumentCaptor.forClass(Map.class);
verify(this.filterContext).setValue(eq(RestDocumentationFilter.CONTEXT_KEY_CONFIGURATION),
configurationCaptor.capture());
@SuppressWarnings("unchecked")
Map<String, Object> configuration = configurationCaptor.getValue();
assertThat(configuration.get(TemplateEngine.class.getName())).isInstanceOf(TemplateEngine.class);
assertThat(configuration.get(WriterResolver.class.getName())).isInstanceOf(WriterResolver.class);
assertThat(configuration.get(RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_SNIPPETS))
.isInstanceOf(List.class);
assertThat(configuration.get(RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_OPERATION_REQUEST_PREPROCESSOR))
.isInstanceOf(OperationRequestPreprocessor.class);
assertThat(configuration.get(RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_OPERATION_RESPONSE_PREPROCESSOR))
.isInstanceOf(OperationResponsePreprocessor.class);
}
}

View File

@@ -0,0 +1,410 @@
/*
* Copyright 2014-2022 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.restassured;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
import java.util.regex.Pattern;
import io.restassured.builder.RequestSpecBuilder;
import io.restassured.specification.RequestSpecification;
import org.assertj.core.api.Condition;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.restdocs.JUnitRestDocumentation;
import org.springframework.restdocs.templates.TemplateFormat;
import org.springframework.restdocs.templates.TemplateFormats;
import org.springframework.restdocs.testfixtures.SnippetConditions;
import org.springframework.restdocs.testfixtures.SnippetConditions.CodeBlockCondition;
import org.springframework.restdocs.testfixtures.SnippetConditions.HttpRequestCondition;
import org.springframework.restdocs.testfixtures.SnippetConditions.HttpResponseCondition;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.RequestMethod;
import static io.restassured.RestAssured.given;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.fail;
import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName;
import static org.springframework.restdocs.headers.HeaderDocumentation.responseHeaders;
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel;
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.maskLinks;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.modifyUris;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.removeHeaders;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.replacePattern;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath;
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
import static org.springframework.restdocs.request.RequestDocumentation.partWithName;
import static org.springframework.restdocs.request.RequestDocumentation.pathParameters;
import static org.springframework.restdocs.request.RequestDocumentation.requestParameters;
import static org.springframework.restdocs.request.RequestDocumentation.requestParts;
import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.document;
import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.documentationConfiguration;
/**
* Integration tests for using Spring REST Docs with REST Assured.
*
* @author Andy Wilkinson
* @author Tomasz Kopczynski
* @author Filip Hrisafov
*/
public class RestAssuredRestDocumentationIntegrationTests {
@Rule
public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
@ClassRule
public static TomcatServer tomcat = new TomcatServer();
@Test
public void defaultSnippetGeneration() {
given().port(tomcat.getPort()).filter(documentationConfiguration(this.restDocumentation))
.filter(document("default")).get("/").then().statusCode(200);
assertExpectedSnippetFilesExist(new File("build/generated-snippets/default"), "http-request.adoc",
"http-response.adoc", "curl-request.adoc");
}
@Test
public void curlSnippetWithContent() throws Exception {
String contentType = "text/plain; charset=UTF-8";
given().port(tomcat.getPort()).filter(documentationConfiguration(this.restDocumentation))
.filter(document("curl-snippet-with-content")).accept("application/json").body("content")
.contentType(contentType).post("/").then().statusCode(200);
assertThat(new File("build/generated-snippets/curl-snippet-with-content/curl-request.adoc")).has(content(
codeBlock(TemplateFormats.asciidoctor(), "bash").withContent(String.format("$ curl 'http://localhost:"
+ tomcat.getPort() + "/' -i -X POST \\%n" + " -H 'Accept: application/json' \\%n"
+ " -H 'Content-Type: " + contentType + "' \\%n" + " -d 'content'"))));
}
@Test
public void curlSnippetWithCookies() throws Exception {
String contentType = "text/plain; charset=UTF-8";
given().port(tomcat.getPort()).filter(documentationConfiguration(this.restDocumentation))
.filter(document("curl-snippet-with-cookies")).accept("application/json").contentType(contentType)
.cookie("cookieName", "cookieVal").get("/").then().statusCode(200);
assertThat(new File("build/generated-snippets/curl-snippet-with-cookies/curl-request.adoc")).has(content(
codeBlock(TemplateFormats.asciidoctor(), "bash").withContent(String.format("$ curl 'http://localhost:"
+ tomcat.getPort() + "/' -i -X GET \\%n" + " -H 'Accept: application/json' \\%n"
+ " -H 'Content-Type: " + contentType + "' \\%n" + " --cookie 'cookieName=cookieVal'"))));
}
@Test
public void curlSnippetWithEmptyParameterQueryString() throws Exception {
given().port(tomcat.getPort()).filter(documentationConfiguration(this.restDocumentation))
.filter(document("curl-snippet-with-empty-parameter-query-string")).accept("application/json")
.param("a", "").get("/").then().statusCode(200);
assertThat(
new File("build/generated-snippets/curl-snippet-with-empty-parameter-query-string/curl-request.adoc"))
.has(content(codeBlock(TemplateFormats.asciidoctor(), "bash")
.withContent(String.format("$ curl 'http://localhost:" + tomcat.getPort()
+ "/?a=' -i -X GET \\%n -H 'Accept: application/json'"))));
}
@Test
public void curlSnippetWithQueryStringOnPost() throws Exception {
given().port(tomcat.getPort()).filter(documentationConfiguration(this.restDocumentation))
.filter(document("curl-snippet-with-query-string")).accept("application/json").param("foo", "bar")
.param("a", "alpha").post("/?foo=bar").then().statusCode(200);
String contentType = "application/x-www-form-urlencoded; charset=ISO-8859-1";
assertThat(new File("build/generated-snippets/curl-snippet-with-query-string/curl-request.adoc"))
.has(content(codeBlock(TemplateFormats.asciidoctor(), "bash")
.withContent(String.format("$ curl " + "'http://localhost:" + tomcat.getPort()
+ "/?foo=bar' -i -X POST \\%n" + " -H 'Accept: application/json' \\%n"
+ " -H 'Content-Type: " + contentType + "' \\%n" + " -d 'a=alpha'"))));
}
@Test
public void linksSnippet() throws Exception {
given().port(tomcat.getPort()).filter(documentationConfiguration(this.restDocumentation))
.filter(document("links", links(linkWithRel("rel").description("The description"))))
.accept("application/json").get("/").then().statusCode(200);
assertExpectedSnippetFilesExist(new File("build/generated-snippets/links"), "http-request.adoc",
"http-response.adoc", "curl-request.adoc", "links.adoc");
}
@Test
public void pathParametersSnippet() throws Exception {
given().port(tomcat.getPort()).filter(documentationConfiguration(this.restDocumentation))
.filter(document("path-parameters",
pathParameters(parameterWithName("foo").description("The description"))))
.accept("application/json").get("/{foo}", "").then().statusCode(200);
assertExpectedSnippetFilesExist(new File("build/generated-snippets/path-parameters"), "http-request.adoc",
"http-response.adoc", "curl-request.adoc", "path-parameters.adoc");
}
@Test
public void requestParametersSnippet() throws Exception {
given().port(tomcat.getPort()).filter(documentationConfiguration(this.restDocumentation))
.filter(document("request-parameters",
requestParameters(parameterWithName("foo").description("The description"))))
.accept("application/json").param("foo", "bar").get("/").then().statusCode(200);
assertExpectedSnippetFilesExist(new File("build/generated-snippets/request-parameters"), "http-request.adoc",
"http-response.adoc", "curl-request.adoc", "request-parameters.adoc");
}
@Test
public void requestFieldsSnippet() throws Exception {
given().port(tomcat.getPort()).filter(documentationConfiguration(this.restDocumentation))
.filter(document("request-fields", requestFields(fieldWithPath("a").description("The description"))))
.accept("application/json").body("{\"a\":\"alpha\"}").post("/").then().statusCode(200);
assertExpectedSnippetFilesExist(new File("build/generated-snippets/request-fields"), "http-request.adoc",
"http-response.adoc", "curl-request.adoc", "request-fields.adoc");
}
@Test
public void requestPartsSnippet() throws Exception {
given().port(tomcat.getPort()).filter(documentationConfiguration(this.restDocumentation))
.filter(document("request-parts", requestParts(partWithName("a").description("The description"))))
.multiPart("a", "foo").post("/upload").then().statusCode(200);
assertExpectedSnippetFilesExist(new File("build/generated-snippets/request-parts"), "http-request.adoc",
"http-response.adoc", "curl-request.adoc", "request-parts.adoc");
}
@Test
public void responseFieldsSnippet() throws Exception {
given().port(tomcat.getPort()).filter(documentationConfiguration(this.restDocumentation))
.filter(document("response-fields",
responseFields(fieldWithPath("a").description("The description"),
subsectionWithPath("links").description("Links to other resources"))))
.accept("application/json").get("/").then().statusCode(200);
assertExpectedSnippetFilesExist(new File("build/generated-snippets/response-fields"), "http-request.adoc",
"http-response.adoc", "curl-request.adoc", "response-fields.adoc");
}
@Test
public void parameterizedOutputDirectory() throws Exception {
given().port(tomcat.getPort()).filter(documentationConfiguration(this.restDocumentation))
.filter(document("{method-name}")).get("/").then().statusCode(200);
assertExpectedSnippetFilesExist(new File("build/generated-snippets/parameterized-output-directory"),
"http-request.adoc", "http-response.adoc", "curl-request.adoc");
}
@Test
public void multiStep() throws Exception {
RequestSpecification spec = new RequestSpecBuilder().setPort(tomcat.getPort())
.addFilter(documentationConfiguration(this.restDocumentation))
.addFilter(document("{method-name}-{step}")).build();
given(spec).get("/").then().statusCode(200);
assertExpectedSnippetFilesExist(new File("build/generated-snippets/multi-step-1/"), "http-request.adoc",
"http-response.adoc", "curl-request.adoc");
given(spec).get("/").then().statusCode(200);
assertExpectedSnippetFilesExist(new File("build/generated-snippets/multi-step-2/"), "http-request.adoc",
"http-response.adoc", "curl-request.adoc");
given(spec).get("/").then().statusCode(200);
assertExpectedSnippetFilesExist(new File("build/generated-snippets/multi-step-3/"), "http-request.adoc",
"http-response.adoc", "curl-request.adoc");
}
@Test
public void additionalSnippets() throws Exception {
RestDocumentationFilter documentation = document("{method-name}-{step}");
RequestSpecification spec = new RequestSpecBuilder().setPort(tomcat.getPort())
.addFilter(documentationConfiguration(this.restDocumentation)).addFilter(documentation).build();
given(spec).filter(documentation.document(
responseHeaders(headerWithName("a").description("one"), headerWithName("Foo").description("two"))))
.get("/").then().statusCode(200);
assertExpectedSnippetFilesExist(new File("build/generated-snippets/additional-snippets-1/"),
"http-request.adoc", "http-response.adoc", "curl-request.adoc", "response-headers.adoc");
}
@Test
public void responseWithCookie() {
given().port(tomcat.getPort()).filter(documentationConfiguration(this.restDocumentation))
.filter(document("set-cookie",
preprocessResponse(removeHeaders(HttpHeaders.DATE, HttpHeaders.CONTENT_TYPE))))
.get("/set-cookie").then().statusCode(200);
assertExpectedSnippetFilesExist(new File("build/generated-snippets/set-cookie"), "http-request.adoc",
"http-response.adoc", "curl-request.adoc");
assertThat(new File("build/generated-snippets/set-cookie/http-response.adoc"))
.has(content(httpResponse(TemplateFormats.asciidoctor(), HttpStatus.OK)
.header(HttpHeaders.SET_COOKIE, "name=value; Domain=localhost; HttpOnly")
.header("Keep-Alive", "timeout=60").header("Connection", "keep-alive")));
}
@Test
public void preprocessedRequest() throws Exception {
Pattern pattern = Pattern.compile("(\"alpha\")");
given().port(tomcat.getPort()).filter(documentationConfiguration(this.restDocumentation)).header("a", "alpha")
.header("b", "bravo").contentType("application/json").accept("application/json")
.body("{\"a\":\"alpha\"}").filter(document("original-request"))
.filter(document("preprocessed-request",
preprocessRequest(prettyPrint(), replacePattern(pattern, "\"<<beta>>\""),
modifyUris().removePort(), removeHeaders("a", HttpHeaders.CONTENT_LENGTH))))
.get("/").then().statusCode(200);
assertThat(new File("build/generated-snippets/original-request/http-request.adoc"))
.has(content(httpRequest(TemplateFormats.asciidoctor(), RequestMethod.GET, "/").header("a", "alpha")
.header("b", "bravo").header("Accept", MediaType.APPLICATION_JSON_VALUE)
.header("Content-Type", "application/json").header("Host", "localhost:" + tomcat.getPort())
.header("Content-Length", "13").content("{\"a\":\"alpha\"}")));
String prettyPrinted = String.format("{%n \"a\" : \"<<beta>>\"%n}");
assertThat(new File("build/generated-snippets/preprocessed-request/http-request.adoc"))
.has(content(httpRequest(TemplateFormats.asciidoctor(), RequestMethod.GET, "/").header("b", "bravo")
.header("Accept", MediaType.APPLICATION_JSON_VALUE).header("Content-Type", "application/json")
.header("Host", "localhost").content(prettyPrinted)));
}
@Test
public void defaultPreprocessedRequest() throws Exception {
Pattern pattern = Pattern.compile("(\"alpha\")");
given().port(tomcat.getPort())
.filter(documentationConfiguration(this.restDocumentation).operationPreprocessors().withRequestDefaults(
prettyPrint(), replacePattern(pattern, "\"<<beta>>\""), modifyUris().removePort(),
removeHeaders("a", HttpHeaders.CONTENT_LENGTH)))
.header("a", "alpha").header("b", "bravo").contentType("application/json").accept("application/json")
.body("{\"a\":\"alpha\"}").filter(document("default-preprocessed-request")).get("/").then()
.statusCode(200);
String prettyPrinted = String.format("{%n \"a\" : \"<<beta>>\"%n}");
assertThat(new File("build/generated-snippets/default-preprocessed-request/http-request.adoc"))
.has(content(httpRequest(TemplateFormats.asciidoctor(), RequestMethod.GET, "/").header("b", "bravo")
.header("Accept", MediaType.APPLICATION_JSON_VALUE).header("Content-Type", "application/json")
.header("Host", "localhost").content(prettyPrinted)));
}
@Test
public void preprocessedResponse() throws Exception {
Pattern pattern = Pattern.compile("(\"alpha\")");
given().port(tomcat.getPort()).filter(documentationConfiguration(this.restDocumentation))
.filter(document("original-response"))
.filter(document("preprocessed-response",
preprocessResponse(prettyPrint(), maskLinks(),
removeHeaders("a", "Transfer-Encoding", "Date", "Server"),
replacePattern(pattern, "\"<<beta>>\""),
modifyUris().scheme("https").host("api.example.com").removePort())))
.get("/").then().statusCode(200);
String prettyPrinted = String.format("{%n \"a\" : \"<<beta>>\",%n \"links\" : "
+ "[ {%n \"rel\" : \"rel\",%n \"href\" : \"...\"%n } ]%n}");
assertThat(new File("build/generated-snippets/preprocessed-response/http-response.adoc"))
.has(content(httpResponse(TemplateFormats.asciidoctor(), HttpStatus.OK)
.header("Foo", "https://api.example.com/foo/bar")
.header("Content-Type", "application/json;charset=UTF-8").header("Keep-Alive", "timeout=60")
.header("Connection", "keep-alive")
.header(HttpHeaders.CONTENT_LENGTH, prettyPrinted.getBytes().length).content(prettyPrinted)));
}
@Test
public void defaultPreprocessedResponse() throws Exception {
Pattern pattern = Pattern.compile("(\"alpha\")");
given().port(tomcat.getPort())
.filter(documentationConfiguration(this.restDocumentation).operationPreprocessors()
.withResponseDefaults(prettyPrint(), maskLinks(),
removeHeaders("a", "Transfer-Encoding", "Date", "Server"),
replacePattern(pattern, "\"<<beta>>\""),
modifyUris().scheme("https").host("api.example.com").removePort()))
.filter(document("default-preprocessed-response")).get("/").then().statusCode(200);
String prettyPrinted = String.format("{%n \"a\" : \"<<beta>>\",%n \"links\" : "
+ "[ {%n \"rel\" : \"rel\",%n \"href\" : \"...\"%n } ]%n}");
assertThat(new File("build/generated-snippets/default-preprocessed-response/http-response.adoc"))
.has(content(httpResponse(TemplateFormats.asciidoctor(), HttpStatus.OK)
.header("Foo", "https://api.example.com/foo/bar")
.header("Content-Type", "application/json;charset=UTF-8").header("Keep-Alive", "timeout=60")
.header("Connection", "keep-alive")
.header(HttpHeaders.CONTENT_LENGTH, prettyPrinted.getBytes().length).content(prettyPrinted)));
}
@Test
public void customSnippetTemplate() throws Exception {
ClassLoader classLoader = new URLClassLoader(
new URL[] { new File("src/test/resources/custom-snippet-templates").toURI().toURL() },
getClass().getClassLoader());
ClassLoader previous = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(classLoader);
try {
given().port(tomcat.getPort()).accept("application/json")
.filter(documentationConfiguration(this.restDocumentation))
.filter(document("custom-snippet-template")).get("/").then().statusCode(200);
}
finally {
Thread.currentThread().setContextClassLoader(previous);
}
assertThat(new File("build/generated-snippets/custom-snippet-template/curl-request.adoc"))
.hasContent("Custom curl request");
}
@Test
public void exceptionShouldBeThrownWhenCallDocumentRequestSpecificationNotConfigured() {
assertThatThrownBy(() -> given().port(tomcat.getPort()).filter(document("default")).get("/"))
.isInstanceOf(IllegalStateException.class)
.hasMessage("REST Docs configuration not found. Did you forget to add a "
+ "RestAssuredRestDocumentationConfigurer as a filter when building the RequestSpecification?");
}
@Test
public void exceptionShouldBeThrownWhenCallDocumentSnippetsRequestSpecificationNotConfigured() {
RestDocumentationFilter documentation = document("{method-name}-{step}");
assertThatThrownBy(() -> given().port(tomcat.getPort())
.filter(documentation.document(responseHeaders(headerWithName("a").description("one")))).get("/"))
.isInstanceOf(IllegalStateException.class)
.hasMessage("REST Docs configuration not found. Did you forget to add a "
+ "RestAssuredRestDocumentationConfigurer as a filter when building the "
+ "RequestSpecification?");
}
private void assertExpectedSnippetFilesExist(File directory, String... snippets) {
for (String snippet : snippets) {
assertThat(new File(directory, snippet)).isFile();
}
}
private Condition<File> content(final Condition<String> delegate) {
return new Condition<File>() {
@Override
public boolean matches(File value) {
try {
return delegate.matches(FileCopyUtils
.copyToString(new InputStreamReader(new FileInputStream(value), StandardCharsets.UTF_8)));
}
catch (IOException ex) {
fail("Failed to read '" + value + "'", ex);
return false;
}
}
};
}
private CodeBlockCondition<?> codeBlock(TemplateFormat format, String language) {
return SnippetConditions.codeBlock(format, language);
}
private HttpRequestCondition httpRequest(TemplateFormat format, RequestMethod requestMethod, String uri) {
return SnippetConditions.httpRequest(format, requestMethod, uri);
}
private HttpResponseCondition httpResponse(TemplateFormat format, HttpStatus status) {
return SnippetConditions.httpResponse(format, status);
}
}

View File

@@ -0,0 +1,125 @@
/*
* Copyright 2014-20212the 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.restassured;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.startup.Tomcat;
import org.junit.rules.ExternalResource;
/**
* {@link ExternalResource} that starts and stops a Tomcat server.
*
* @author Andy Wilkinson
*/
class TomcatServer extends ExternalResource {
private Tomcat tomcat;
private int port;
@Override
protected void before() throws LifecycleException {
this.tomcat = new Tomcat();
this.tomcat.getConnector().setPort(0);
Context context = this.tomcat.addContext("/", null);
this.tomcat.addServlet("/", "test", new TestServlet());
context.addServletMappingDecoded("/", "test");
this.tomcat.addServlet("/", "set-cookie", new CookiesServlet());
context.addServletMappingDecoded("/set-cookie", "set-cookie");
this.tomcat.start();
this.port = this.tomcat.getConnector().getLocalPort();
}
@Override
protected void after() {
try {
this.tomcat.stop();
}
catch (LifecycleException ex) {
throw new RuntimeException(ex);
}
}
int getPort() {
return this.port;
}
/**
* {@link HttpServlet} used to handle requests in the tests.
*/
private static final class TestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
respondWithJson(response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
respondWithJson(response);
}
private void respondWithJson(HttpServletResponse response) throws IOException, JsonProcessingException {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
Map<String, Object> content = new HashMap<>();
content.put("a", "alpha");
Map<String, String> link = new HashMap<>();
link.put("rel", "rel");
link.put("href", "href");
content.put("links", Arrays.asList(link));
response.getWriter().println(new ObjectMapper().writeValueAsString(content));
response.setHeader("a", "alpha");
response.setHeader("Foo", "http://localhost:12345/foo/bar");
response.flushBuffer();
}
}
/**
* {@link HttpServlet} used to handle cookies-related requests in the tests.
*/
private static final class CookiesServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Cookie cookie = new Cookie("name", "value");
cookie.setDomain("localhost");
cookie.setHttpOnly(true);
resp.addCookie(cookie);
}
}
}

View File

@@ -0,0 +1 @@
file