package com.github.dzieciou.testing.curl; import static io.restassured.RestAssured.config; import static io.restassured.RestAssured.given; import static io.restassured.config.HttpClientConfig.httpClientConfig; import static io.restassured.config.MultiPartConfig.multiPartConfig; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockserver.integration.ClientAndServer.startClientAndServer; import static org.mockserver.model.HttpRequest.request; import static org.mockserver.model.HttpResponse.response; import io.restassured.config.HttpClientConfig; import io.restassured.config.RestAssuredConfig; import io.restassured.http.ContentType; import io.restassured.http.Cookie; import io.restassured.http.Cookies; import java.io.File; import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; import org.apache.commons.io.FileUtils; import org.apache.http.HttpException; import org.apache.http.HttpRequest; import org.apache.http.HttpRequestInterceptor; import org.apache.http.client.HttpClient; import org.apache.http.impl.client.AbstractHttpClient; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.protocol.HttpContext; import org.mockserver.client.MockServerClient; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @SuppressWarnings("unchecked") public class UsingWithRestAssuredTest { private static final int MOCK_PORT = 9999; private static final String MOCK_HOST = "localhost"; private static final String MOCK_BASE_URI = "http://" + MOCK_HOST; private MockServerClient mockServer; private TemporaryFolder tempFolder; private RestAssuredConfig getRestAssuredConfig(Consumer<String> curlConsumer) { return config() .httpClient(httpClientConfig() .reuseHttpClientInstance().httpClientFactory(new MyHttpClientFactory(curlConsumer))); } @BeforeClass public void setupMock() throws IOException { tempFolder = new TemporaryFolder(); mockServer = startClientAndServer(MOCK_PORT); mockServer.when(request()).respond(response()); } @Test(groups = "end-to-end-samples") public void cookiesTest() { Consumer<String> curlConsumer = mock(Consumer.class); //@formatter:off given() .redirects().follow(false) .baseUri(MOCK_BASE_URI) .port(MOCK_PORT) .cookie("token", "tokenValue") .cookie("context", "contextValue") .config(config() .httpClient(httpClientConfig() .reuseHttpClientInstance() .httpClientFactory(new MyHttpClientFactory(curlConsumer)))) .when() .get("/access") .then() .statusCode(200); //@formatter:on verify(curlConsumer).accept("curl 'http://localhost:" + MOCK_PORT + "/access' -b 'token=tokenValue; context=contextValue' -H 'Accept: */*' --compressed -k -v"); } @Test(groups = "end-to-end-samples") public void cookieWithSpecialCharactersTest() { Consumer<String> curlConsumer = mock(Consumer.class); //@formatter:off given() .redirects().follow(false) .baseUri(MOCK_BASE_URI) .port(MOCK_PORT) .cookie("token1", "1-XQLTiKxwRNyUpJYkr+IV2g==-+nLy/6GiMDj7SW/jN107UGmpf4hsM7IXsXdN9z/+7dyljV5N+0Pqpg/da0XIGOgSt2mMIIStakcjGyPlEq30Wx2gvYmVadkmH7gmcSGcaBupjlcKM2Fio96AbzJVjxUUsE5jvjBI8YlyX8fMiesQ8Gbt8XhEGbJKJe4/ogMDn7Qv687DQraxGewISOu5VIQuhgztTDqa2OUCgObG94wtAo3lSo+7HSbxcbM0LNKbbqA=-5GVOIPO4SZ7m8E0DtLS1E76h0LOmzWN00iiIeWZz360=") .cookie("token2", "2-XQLTiKxwRNyUpJYkr+IV2g==-+nLy/6GiMDj7SW/jN107UGmpf4hsM7IXsXdN9z/+7dyljV5N+0Pqpg/da0XIGOgSt2mMIIStakcjGyPlEq30Wx2gvYmVadkmH7gmcSGcaBupjlcKM2Fio96AbzJVjxUUsE5jvjBI8YlyX8fMiesQ8Gbt8XhEGbJKJe4/ogMDn7Qv687DQraxGewISOu5VIQuhgztTDqa2OUCgObG94wtAo3lSo+7HSbxcbM0LNKbbqA=-5GVOIPO4SZ7m8E0DtLS1E76h0LOmzWN00iiIeWZz360=") .config(config() .httpClient(httpClientConfig() .reuseHttpClientInstance() .httpClientFactory(new MyHttpClientFactory(curlConsumer)))) .when() .get("/access") .then() .statusCode(200); //@formatter:on verify(curlConsumer).accept("curl 'http://localhost:" + MOCK_PORT + "/access' " + "-b '" + "token1=1-XQLTiKxwRNyUpJYkr+IV2g==-+nLy/6GiMDj7SW/jN107UGmpf4hsM7IXsXdN9z/+7dyljV5N+0Pqpg/da0XIGOgSt2mMIIStakcjGyPlEq30Wx2gvYmVadkmH7gmcSGcaBupjlcKM2Fio96AbzJVjxUUsE5jvjBI8YlyX8fMiesQ8Gbt8XhEGbJKJe4/ogMDn7Qv687DQraxGewISOu5VIQuhgztTDqa2OUCgObG94wtAo3lSo+7HSbxcbM0LNKbbqA=-5GVOIPO4SZ7m8E0DtLS1E76h0LOmzWN00iiIeWZz360=; " + "token2=2-XQLTiKxwRNyUpJYkr+IV2g==-+nLy/6GiMDj7SW/jN107UGmpf4hsM7IXsXdN9z/+7dyljV5N+0Pqpg/da0XIGOgSt2mMIIStakcjGyPlEq30Wx2gvYmVadkmH7gmcSGcaBupjlcKM2Fio96AbzJVjxUUsE5jvjBI8YlyX8fMiesQ8Gbt8XhEGbJKJe4/ogMDn7Qv687DQraxGewISOu5VIQuhgztTDqa2OUCgObG94wtAo3lSo+7HSbxcbM0LNKbbqA=-5GVOIPO4SZ7m8E0DtLS1E76h0LOmzWN00iiIeWZz360=" + "' " + "-H 'Accept: */*' --compressed -k -v"); } @Test(groups = "end-to-end-samples") public void customizedCookie() { Consumer<String> curlConsumer = mock(Consumer.class); List<Cookie> cookies = new ArrayList<>(); cookies.add( new Cookie.Builder("token", "tokenValue").setDomain("testing.com").setPath("/access") .build()); //@formatter:off given() .redirects().follow(false) .baseUri(MOCK_BASE_URI) .port(MOCK_PORT) .cookies(new Cookies(cookies)) .config(config() .httpClient(httpClientConfig() .reuseHttpClientInstance() .httpClientFactory(new MyHttpClientFactory(curlConsumer)))) .when() .get("/access") .then() .statusCode(200); //@formatter:on verify(curlConsumer).accept("curl 'http://localhost:" + MOCK_PORT + "/access' -b 'token=tokenValue' -H 'Accept: */*' --compressed -k -v"); } @Test(groups = "end-to-end-samples") public void basicIntegrationTest() { Consumer<String> curlConsumer = mock(Consumer.class); //@formatter:off given() .redirects().follow(false) .baseUri(MOCK_BASE_URI) .port(MOCK_PORT) .config(getRestAssuredConfig(curlConsumer)) .when() .get("/") .then() .statusCode(200); //@formatter:on verify(curlConsumer).accept( "curl 'http://localhost:" + MOCK_PORT + "/' -H 'Accept: */*' --compressed -k -v"); } @Test(groups = "end-to-end-samples") public void shouldPrintPostRequestWithMultipartDataProperly() { Consumer<String> curlConsumer = mock(Consumer.class); //@formatter:off given() .baseUri(MOCK_BASE_URI) .port(MOCK_PORT) .config(getRestAssuredConfig(curlConsumer)) .multiPart(new File("README.md")) .formParam("parameterX", "parameterXValue") .when().post("/"); //@formatter:on verify(curlConsumer).accept( "curl 'http://localhost:" + MOCK_PORT + "/' -X POST -H 'Accept: */*' -F '[email protected];type=application/octet-stream' -F 'parameterX=parameterXValue;type=text/plain; charset=US-ASCII' --compressed -k -v"); } @Test(groups = "end-to-end-samples") public void shouldPrintBody() { Consumer<String> curlConsumer = mock(Consumer.class); //@formatter:off given() .baseUri(MOCK_BASE_URI) .port(MOCK_PORT) .config(getRestAssuredConfig(curlConsumer)) .contentType(ContentType.JSON) .body("name=Administração") .when().post("/"); //@formatter:on verify(curlConsumer).accept( "curl 'http://localhost:" + MOCK_PORT + "/' -H 'Accept: */*' -H 'Content-Type: application/json; charset=UTF-8' --data-binary 'name=Administração' --compressed -k -v"); } @Test(groups = "end-to-end-samples") public void shouldPrintBodyWithEncoding() { Consumer<String> curlConsumer = mock(Consumer.class); //@formatter:off given() .baseUri(MOCK_BASE_URI) .port(MOCK_PORT) .config(getRestAssuredConfig(curlConsumer)) .contentType(ContentType.JSON) .body("{\n'name':\"CKB2\",'salary':'123','age':'23'\n}") .when().post("/"); //@formatter:on verify(curlConsumer).accept( "curl 'http://localhost:" + MOCK_PORT + "/' -H 'Accept: */*' -H 'Content-Type: application/json; charset=UTF-8' --data-binary " + "$'{\\n\\'name\\':\"CKB2\",\\'salary\\':\\'123\\',\\'age\\':\\'23\\'\\n}' --compressed -k -v"); } @Test(groups = "end-to-end-samples") public void shouldPrintMultipartWithContentTypesForTypes() { Consumer<String> curlConsumer = mock(Consumer.class); //@formatter:off given() .baseUri(MOCK_BASE_URI) .port(MOCK_PORT) .config(getRestAssuredConfig(curlConsumer)) .multiPart("message", "{content:\"interesting\"}", "application/json") .when().post("/"); //@formatter:on verify(curlConsumer).accept( "curl 'http://localhost:" + MOCK_PORT + "/' -X POST -H 'Accept: */*' -F 'message={content:\"interesting\"};type=application/json; charset=US-ASCII' --compressed -k -v"); } @Test public void shouldPrintMultipartWithMixedType() { Consumer<String> curlConsumer = mock(Consumer.class); //@formatter:off given() .baseUri(MOCK_BASE_URI) .port(MOCK_PORT) .config(getRestAssuredConfig(curlConsumer) .multiPartConfig(multiPartConfig().defaultSubtype("mixed"))) .multiPart("myfile", new File("README.md"), "application/json") .when().post("/"); //@formatter:on verify(curlConsumer).accept( "curl 'http://localhost:" + MOCK_PORT + "/' -X POST -H 'Accept: */*' -H 'Content-Type: multipart/mixed' -F '[email protected];type=application/json' --compressed -k -v"); } @Test public void shouldPrintFileAsBinary() throws IOException { Consumer<String> curlConsumer = mock(Consumer.class); File tempFile = tempFolder.createFile().toFile(); FileUtils.writeStringToFile(tempFile, "{ 'message' : 'hello world'}", Charset.defaultCharset()); //@formatter:off given() .baseUri(MOCK_BASE_URI) .port(MOCK_PORT) .config(getRestAssuredConfig(curlConsumer)) .body(tempFile) .when().post("/"); //@formatter:on verify(curlConsumer).accept( "curl 'http://localhost:" + MOCK_PORT + "/' -H 'Accept: */*' -H 'Content-Type: text/plain; charset=ISO-8859-1' --data-binary $'{ \\'message\\' : \\'hello world\\'}' --compressed -k -v"); } @Test public void shouldPrintForm() { Consumer<String> curlConsumer = mock(Consumer.class); //@formatter:off given() .baseUri(MOCK_BASE_URI) .port(MOCK_PORT) .config(getRestAssuredConfig(curlConsumer)) .formParam("birthyear","1905") .formParam("invitation","I am Daniel") .when().post("/"); //@formatter:on verify(curlConsumer).accept( "curl 'http://localhost:" + MOCK_PORT + "/' -H 'Accept: */*' -H 'Content-Type: application/x-www-form-urlencoded; charset=ISO-8859-1' --data-binary 'birthyear=1905&invitation=I%20am%20Daniel' --compressed -k -v"); } @Test(groups = "end-to-end-samples") public void shouldPrintPut() { Consumer<String> curlConsumer = mock(Consumer.class); //@formatter:off given() .redirects().follow(false) .baseUri(MOCK_BASE_URI) .port(MOCK_PORT) .config(getRestAssuredConfig(curlConsumer)) .when() .put("/") .then() .statusCode(200); //@formatter:on verify(curlConsumer).accept( "curl 'http://localhost:" + MOCK_PORT + "/' -X PUT -H 'Accept: */*' -H 'Content-Length: 0' --compressed -k -v"); } @AfterClass public void closeMock() { mockServer.stop(); tempFolder.deleteAll(); } private class MyHttpClientFactory implements HttpClientConfig.HttpClientFactory { public final Consumer<String> curlConsumer; private MyHttpClientFactory(Consumer<String> curlConsumer) { this.curlConsumer = curlConsumer; } @SuppressWarnings("deprecation") @Override public HttpClient createHttpClient() { AbstractHttpClient client = new DefaultHttpClient(); client.addRequestInterceptor(new CurlTestingInterceptor(curlConsumer)); return client; } } private static class CurlTestingInterceptor implements HttpRequestInterceptor { public final Consumer<String> curlConsumer; public CurlTestingInterceptor(Consumer<String> curlConsumer) { this.curlConsumer = curlConsumer; } @Override public void process(HttpRequest request, HttpContext context) throws HttpException, IOException { Options options = Options.builder() .printSingleliner() .targetPlatform(Platform.UNIX) .useShortForm() .updateCurl(curl -> curl .removeHeader("Host") .removeHeader("User-Agent") .removeHeader("Connection")) .build(); try { curlConsumer.accept(new Http2Curl(options).generateCurl(request)); } catch (Exception e) { throw new RuntimeException(e); } } } }