From 11b46eccf0741e6b344b27288c2f11d1970c9bb5 Mon Sep 17 00:00:00 2001 From: Aliona Budzevich Date: Mon, 5 Feb 2024 09:05:00 +0100 Subject: [PATCH] [plugin-rest-api] Post executed requests in a curl syntax in allure attachments --- .../bootstrap/3.4.1/fonts/clipboard.svg | 4 + ...tils.java => ContentTypeHeaderParser.java} | 28 +++- .../main/java/org/vividus/http/CurlUtils.java | 110 ++++++++++++++++ .../http/ExtendedHttpLoggingInterceptor.java | 4 +- .../http/PublishingAttachmentInterceptor.java | 31 ++++- .../vividus/http/attachment/api-message.ftl | 46 ++++++- ...java => ContentTypeHeaderParserTests.java} | 34 ++++- .../java/org/vividus/http/CurlUtilsTests.java | 120 ++++++++++++++++++ .../PublishingAttachmentInterceptorTests.java | 22 +++- 9 files changed, 374 insertions(+), 25 deletions(-) create mode 100644 vividus-allure-adaptor/src/main/resources/allure-customization/webjars/bootstrap/3.4.1/fonts/clipboard.svg rename vividus-plugin-rest-api/src/main/java/org/vividus/http/{MimeTypeUtils.java => ContentTypeHeaderParser.java} (64%) create mode 100644 vividus-plugin-rest-api/src/main/java/org/vividus/http/CurlUtils.java rename vividus-plugin-rest-api/src/test/java/org/vividus/http/{MimeTypeUtilsTests.java => ContentTypeHeaderParserTests.java} (61%) create mode 100644 vividus-plugin-rest-api/src/test/java/org/vividus/http/CurlUtilsTests.java diff --git a/vividus-allure-adaptor/src/main/resources/allure-customization/webjars/bootstrap/3.4.1/fonts/clipboard.svg b/vividus-allure-adaptor/src/main/resources/allure-customization/webjars/bootstrap/3.4.1/fonts/clipboard.svg new file mode 100644 index 0000000000..b92f42a5bc --- /dev/null +++ b/vividus-allure-adaptor/src/main/resources/allure-customization/webjars/bootstrap/3.4.1/fonts/clipboard.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/vividus-plugin-rest-api/src/main/java/org/vividus/http/MimeTypeUtils.java b/vividus-plugin-rest-api/src/main/java/org/vividus/http/ContentTypeHeaderParser.java similarity index 64% rename from vividus-plugin-rest-api/src/main/java/org/vividus/http/MimeTypeUtils.java rename to vividus-plugin-rest-api/src/main/java/org/vividus/http/ContentTypeHeaderParser.java index c5496a713c..5fde5f4693 100644 --- a/vividus-plugin-rest-api/src/main/java/org/vividus/http/MimeTypeUtils.java +++ b/vividus-plugin-rest-api/src/main/java/org/vividus/http/ContentTypeHeaderParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2023 the original author or authors. + * Copyright 2019-2024 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. @@ -16,6 +16,7 @@ package org.vividus.http; +import java.nio.charset.Charset; import java.util.Optional; import java.util.stream.Stream; @@ -24,11 +25,12 @@ import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElement; import org.apache.hc.core5.http.HttpHeaders; +import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.http.message.MessageSupport; -public final class MimeTypeUtils +public final class ContentTypeHeaderParser { - private MimeTypeUtils() + private ContentTypeHeaderParser() { } @@ -40,17 +42,29 @@ private MimeTypeUtils() */ public static String getMimeTypeFromHeadersWithDefault(Header... headers) { - return getMimeTypeFromHeaders(headers).orElseGet(ContentType.DEFAULT_TEXT::getMimeType); + return getMimeType(headers).orElseGet(ContentType.DEFAULT_TEXT::getMimeType); } - public static Optional getMimeTypeFromHeaders(Header... headers) + public static Optional getMimeType(Header... headers) + { + return getContentTypeHeader(headers).map(HeaderElement::getName); + } + + public static Optional getCharset(Header... headers) + { + return getContentTypeHeader(headers) + .map(h -> h.getParameterByName("charset")) + .map(NameValuePair::getValue) + .map(Charset::forName); + } + + private static Optional getContentTypeHeader(Header... headers) { return Stream.of(headers) .filter(h -> HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(h.getName()) && StringUtils.isNotBlank(h.getValue())) .findFirst() .map(MessageSupport::parse) - .map(elements -> elements[0]) - .map(HeaderElement::getName); + .map(elements -> elements[0]); } } diff --git a/vividus-plugin-rest-api/src/main/java/org/vividus/http/CurlUtils.java b/vividus-plugin-rest-api/src/main/java/org/vividus/http/CurlUtils.java new file mode 100644 index 0000000000..b40b029d94 --- /dev/null +++ b/vividus-plugin-rest-api/src/main/java/org/vividus/http/CurlUtils.java @@ -0,0 +1,110 @@ +/* + * Copyright 2019-2024 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.vividus.http; + +import java.net.URISyntaxException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +import org.apache.hc.core5.http.Header; +import org.apache.hc.core5.http.HttpRequest; + +public final class CurlUtils +{ + private static final Pattern NAME_FILE_NAME_PATTERN = Pattern.compile("name=\"(.+)\"; filename=\"(.+)\""); + private static final Pattern NAME_CONTENT_PATTERN = Pattern.compile("name=\"(.+)\"\r\nContent-Type:.+\r\n\r\n(.*)"); + private static final String SINGLE_QUOTE = "'"; + private static final String END_OF_LINE = " \\\n"; + private static final String DOUBLE_QUOTE = "\""; + + private CurlUtils() + { + } + + public static String buildCurlCommand(HttpRequest request, String mimeType, + byte[] body, boolean binary) throws URISyntaxException + { + StringBuilder curlCommand = new StringBuilder("curl "); + appendMethodAndUri(curlCommand, request); + appendHeaders(curlCommand, request.getHeaders()); + if (body != null) + { + Charset charset = ContentTypeHeaderParser.getCharset(request.getHeaders()).orElse(StandardCharsets.UTF_8); + appendBody(curlCommand, mimeType, charset, body, binary); + } + return curlCommand.toString(); + } + + private static void appendMethodAndUri(StringBuilder curlCommand, HttpRequest request) throws URISyntaxException + { + curlCommand.append("-X ").append(request.getMethod()).append(" '") + .append(request.getUri()).append(SINGLE_QUOTE); + } + + private static void appendHeaders(StringBuilder curlCommand, Header... headers) + { + Stream.of(headers).forEach(h -> curlCommand.append(END_OF_LINE) + .append("-H '").append(h.getName()).append(": ").append(h.getValue()).append(SINGLE_QUOTE)); + } + + private static void appendBody(StringBuilder curlCommand, String mimeType, Charset charset, + byte[] body, boolean binary) + { + String bodyAsString = new String(body, charset); + if (binary) + { + curlCommand.append(END_OF_LINE).append("--data-binary '@'"); + return; + } + if (mimeType.contains("multipart")) + { + appendMultipartData(curlCommand, bodyAsString); + return; + } + curlCommand.append(END_OF_LINE).append("-d '").append(bodyAsString).append(SINGLE_QUOTE); + } + + private static void appendMultipartData(StringBuilder curlCommand, String bodyAsString) + { + String regex = bodyAsString.split("\\R", 2)[0]; + String[] formDataArray = bodyAsString.split(regex + ".*"); + + Stream.of(formDataArray).forEach(e -> + { + Matcher matcher = NAME_FILE_NAME_PATTERN.matcher(e); + String formStringStart = "-F '"; + if (matcher.find()) + { + curlCommand.append(END_OF_LINE).append(formStringStart).append(matcher.group(1)) + .append("=@\"").append(matcher.group(2)) + .append(DOUBLE_QUOTE).append(SINGLE_QUOTE); + } + else + { + matcher = NAME_CONTENT_PATTERN.matcher(e); + if (matcher.find()) + { + curlCommand.append(END_OF_LINE).append(formStringStart).append(matcher.group(1)) + .append("=\"").append(matcher.group(2)).append(DOUBLE_QUOTE).append(SINGLE_QUOTE); + } + } + }); + } +} diff --git a/vividus-plugin-rest-api/src/main/java/org/vividus/http/ExtendedHttpLoggingInterceptor.java b/vividus-plugin-rest-api/src/main/java/org/vividus/http/ExtendedHttpLoggingInterceptor.java index 332ddeb5e1..ad1e51edf7 100644 --- a/vividus-plugin-rest-api/src/main/java/org/vividus/http/ExtendedHttpLoggingInterceptor.java +++ b/vividus-plugin-rest-api/src/main/java/org/vividus/http/ExtendedHttpLoggingInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2023 the original author or authors. + * Copyright 2019-2024 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. @@ -125,7 +125,7 @@ public void handle(HttpResponse httpResponse) if (httpResponse.getResponseBody() != null) { - String mimeType = MimeTypeUtils.getMimeTypeFromHeadersWithDefault(headers); + String mimeType = ContentTypeHeaderParser.getMimeTypeFromHeadersWithDefault(headers); if (mimeType.startsWith("text/") || LOGGED_CONTENT_TYPES.contains(mimeType)) { loggingEventBuilder = loggingEventBuilder.addArgument(httpResponse::getResponseBodyAsString); diff --git a/vividus-plugin-rest-api/src/main/java/org/vividus/http/PublishingAttachmentInterceptor.java b/vividus-plugin-rest-api/src/main/java/org/vividus/http/PublishingAttachmentInterceptor.java index 02373c8701..03d444fe98 100644 --- a/vividus-plugin-rest-api/src/main/java/org/vividus/http/PublishingAttachmentInterceptor.java +++ b/vividus-plugin-rest-api/src/main/java/org/vividus/http/PublishingAttachmentInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2023 the original author or authors. + * Copyright 2019-2024 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. @@ -18,11 +18,13 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; import java.util.Optional; +import org.apache.commons.text.StringEscapeUtils; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; @@ -30,6 +32,7 @@ import org.apache.hc.core5.http.HttpEntityContainer; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpRequestInterceptor; +import org.apache.hc.core5.http.io.entity.ByteArrayEntity; import org.apache.hc.core5.http.protocol.HttpContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -53,16 +56,18 @@ public void process(HttpRequest request, EntityDetails entityDetails, HttpContex { byte[] body = null; String mimeType = null; + boolean binaryContent = false; if (request instanceof HttpEntityContainer httpEntityContainer) { HttpEntity entity = httpEntityContainer.getEntity(); if (entity != null) { - mimeType = MimeTypeUtils.getMimeTypeFromHeaders(request.getHeaders()) + mimeType = ContentTypeHeaderParser.getMimeType(request.getHeaders()) .orElseGet(() -> Optional.ofNullable(ContentType.parseLenient(entity.getContentType())) .orElse(ContentType.DEFAULT_TEXT).getMimeType() ); + binaryContent = entity instanceof ByteArrayEntity; try (ByteArrayOutputStream baos = new ByteArrayOutputStream((int) entity.getContentLength())) { // https://github.com/apache/httpcomponents-client/commit/09cefc2b8970eea56d81b1a886d9bb769a48daf3 @@ -75,25 +80,37 @@ public void process(HttpRequest request, EntityDetails entityDetails, HttpContex } } } - attachApiMessage("Request: " + request, request.getHeaders(), body, mimeType, -1); + String curlCommand = null; + try + { + curlCommand = CurlUtils.buildCurlCommand(request, mimeType, body, binaryContent); + } + catch (URISyntaxException e) + { + LOGGER.error("Error is occurred on building cURL command", e); + } + attachApiMessage("Request: " + request, request.getHeaders(), body, mimeType, -1, curlCommand); } @Override - public void handle(HttpResponse response) throws IOException + public void handle(HttpResponse response) { Header[] headers = response.getResponseHeaders(); String attachmentTitle = String.format("Response: %s %s", response.getMethod(), response.getFrom()); - String mimeType = MimeTypeUtils.getMimeTypeFromHeadersWithDefault(headers); - attachApiMessage(attachmentTitle, headers, response.getResponseBody(), mimeType, response.getStatusCode()); + String mimeType = ContentTypeHeaderParser.getMimeTypeFromHeadersWithDefault(headers); + attachApiMessage(attachmentTitle, headers, response.getResponseBody(), mimeType, + response.getStatusCode(), null); } - private void attachApiMessage(String title, Header[] headers, byte[] body, String mimeType, int statusCode) + private void attachApiMessage(String title, Header[] headers, byte[] body, String mimeType, + int statusCode, String curlCommand) { Map dataMap = new HashMap<>(); dataMap.put("headers", headers); dataMap.put("body", body != null ? new String(body, StandardCharsets.UTF_8) : null); dataMap.put("bodyContentType", mimeType); dataMap.put("statusCode", statusCode); + dataMap.put("curlCommand", StringEscapeUtils.escapeHtml4(curlCommand)); attachmentPublisher.publishAttachment("/org/vividus/http/attachment/api-message.ftl", dataMap, title); } diff --git a/vividus-plugin-rest-api/src/main/resources/org/vividus/http/attachment/api-message.ftl b/vividus-plugin-rest-api/src/main/resources/org/vividus/http/attachment/api-message.ftl index 755e39dea2..77031e4cef 100644 --- a/vividus-plugin-rest-api/src/main/resources/org/vividus/http/attachment/api-message.ftl +++ b/vividus-plugin-rest-api/src/main/resources/org/vividus/http/attachment/api-message.ftl @@ -31,7 +31,23 @@ .panel-heading a.collapsed:after { content:"\F105"; } - + .container { + position: relative; + } + .copy-button { + position: absolute; + top: 0px; + right: 15px; + } + #copy-toast { + position: absolute; + display: none; + top: 35px; + right: 0px; + font-size: 13px; + background-color: #666362; + color: #fff; + }
@@ -94,6 +110,25 @@
+ + <#if curlCommand??> +
+
+

+ +

+
+
+
+
${curlCommand}
+ + Copied! +
+
+
+ @@ -110,6 +145,15 @@ hljs.highlightElement(e); }); }); + + function copyCurlCommand() { + var text = document.querySelector('#collapse-curl code').textContent; + navigator.clipboard.writeText(text); + document.getElementById("copy-toast").style.display = "inline"; + setTimeout( function() { + document.getElementById("copy-toast").style.display = "none"; + }, 1000); + } diff --git a/vividus-plugin-rest-api/src/test/java/org/vividus/http/MimeTypeUtilsTests.java b/vividus-plugin-rest-api/src/test/java/org/vividus/http/ContentTypeHeaderParserTests.java similarity index 61% rename from vividus-plugin-rest-api/src/test/java/org/vividus/http/MimeTypeUtilsTests.java rename to vividus-plugin-rest-api/src/test/java/org/vividus/http/ContentTypeHeaderParserTests.java index 8abd577dcb..702c454e4b 100644 --- a/vividus-plugin-rest-api/src/test/java/org/vividus/http/MimeTypeUtilsTests.java +++ b/vividus-plugin-rest-api/src/test/java/org/vividus/http/ContentTypeHeaderParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2023 the original author or authors. + * Copyright 2019-2024 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. @@ -20,6 +20,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.params.provider.Arguments.arguments; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.Optional; import java.util.stream.Stream; @@ -31,7 +33,7 @@ import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; -class MimeTypeUtilsTests +class ContentTypeHeaderParserTests { private static final String TEXT_TYPE = "text/plain"; @@ -41,12 +43,20 @@ static Stream headersWithoutContentType() arguments(new BasicHeader("stubHeader", "stubHeaderValue"))); } + static Stream headersWithContentType() + { + return Stream.of(arguments(new BasicHeader(CONTENT_TYPE, "application/x-www-form-urlencoded"), + Optional.empty()), + arguments(new BasicHeader(CONTENT_TYPE, "multipart/form-data; charset=ISO-8859-1"), + Optional.of(StandardCharsets.ISO_8859_1))); + } + @ParameterizedTest @ValueSource(strings = { CONTENT_TYPE, "content-type" }) void shouldGetMimeTypeFromHeaders(String contentType) { Header contentTypeHeader = new BasicHeader(contentType, TEXT_TYPE); - String actualMimeType = MimeTypeUtils.getMimeTypeFromHeaders(contentTypeHeader).get(); + String actualMimeType = ContentTypeHeaderParser.getMimeType(contentTypeHeader).get(); assertEquals(TEXT_TYPE, actualMimeType); } @@ -54,14 +64,28 @@ void shouldGetMimeTypeFromHeaders(String contentType) @MethodSource("headersWithoutContentType") void shouldNotGetMimeTypeFromHeadersWithoutContentType(Header header) { - Optional actualMimeType = MimeTypeUtils.getMimeTypeFromHeaders(header); + Optional actualMimeType = ContentTypeHeaderParser.getMimeType(header); assertEquals(Optional.empty(), actualMimeType); } @Test void shouldGetMimeTypeFromHeadersWithDefault() { - String actualMimeType = MimeTypeUtils.getMimeTypeFromHeadersWithDefault(); + String actualMimeType = ContentTypeHeaderParser.getMimeTypeFromHeadersWithDefault(); assertEquals(TEXT_TYPE, actualMimeType); } + + @ParameterizedTest + @MethodSource("headersWithoutContentType") + void testGetDefaultCharsetForCaseWithoutContentTypeHeaderValue(Header header) + { + assertEquals(Optional.empty(), ContentTypeHeaderParser.getCharset(header)); + } + + @ParameterizedTest + @MethodSource("headersWithContentType") + void testGetCharsetForCaseWithContentTypeHeaderValue(Header header, Optional charset) + { + assertEquals(charset, ContentTypeHeaderParser.getCharset(header)); + } } diff --git a/vividus-plugin-rest-api/src/test/java/org/vividus/http/CurlUtilsTests.java b/vividus-plugin-rest-api/src/test/java/org/vividus/http/CurlUtilsTests.java new file mode 100644 index 0000000000..54cd7ea394 --- /dev/null +++ b/vividus-plugin-rest-api/src/test/java/org/vividus/http/CurlUtilsTests.java @@ -0,0 +1,120 @@ +/* + * Copyright 2019-2024 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.vividus.http; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.vividus.http.HttpMethod.GET; +import static org.vividus.http.HttpMethod.POST; + +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + +import org.apache.hc.core5.http.Header; +import org.apache.hc.core5.http.message.BasicHeader; +import org.apache.hc.core5.http.message.BasicHttpRequest; +import org.junit.jupiter.api.Test; + +class CurlUtilsTests +{ + private static final String ANY = "any"; + private static final Header[] EMPTY_HEADERS = {}; + private static final Charset CHARSET = StandardCharsets.UTF_8; + + @Test + void testRequestWithHeadersAndWithoutBody() throws URISyntaxException + { + BasicHttpRequest request = mock(); + when(request.getMethod()).thenReturn(GET.name()); + when(request.getUri()).thenReturn(new URI("http://get.example.org/")); + Header[] requestHeaders = + { + new BasicHeader("Connection", "keep-alive"), + new BasicHeader("Host", "example.org") + }; + when(request.getHeaders()).thenReturn(requestHeaders); + + assertEquals(CurlUtils.buildCurlCommand(request, ANY, null, false), + """ + curl -X GET 'http://get.example.org/' \\ + -H 'Connection: keep-alive' \\ + -H 'Host: example.org'"""); + } + + @Test + void testRequestWithHeadersAndWithBody() throws URISyntaxException + { + BasicHttpRequest request = mock(); + when(request.getMethod()).thenReturn(POST.name()); + when(request.getUri()).thenReturn(new URI("http://post.example.org/")); + when(request.getHeaders()).thenReturn(EMPTY_HEADERS); + String postBody = """ + { + "string": "<,',:,\\" <, ', :, "", + "number": 42, + "boolean": true + }"""; + + assertEquals(CurlUtils.buildCurlCommand(request, ANY, postBody.getBytes(CHARSET), false), + """ + curl -X POST 'http://post.example.org/' \\ + -d '{ + "string": "<,',:,\\" <, ', :, "", + "number": 42, + "boolean": true + }'"""); + } + + @Test + void testRequestWithFormBody() throws URISyntaxException + { + BasicHttpRequest request = mock(); + when(request.getMethod()).thenReturn(POST.name()); + when(request.getUri()).thenReturn(new URI("http://post.form.data.example.org/")); + when(request.getHeaders()).thenReturn(EMPTY_HEADERS); + + String formDataBody = "--Bbg5_2qfo5RoGjrGzlpR2MFzlAqzj2ie49bp7\r\n" + + "Content-Disposition: form-data; name=\"file-key\"; filename=\"raw.txt\"\r\n" + + "Content-Type: text/plain\r\n\r\n--Bbg5_2qfo5RoGjrGzlpR2MFzlAqzj2ie49bp7\r\n" + + "Content-Disposition: form-data; name=\"string-key\"\r\nContent-Type: text/plain\r\n" + + "\r\nstring1\r\n--Bbg5_2qfo5RoGjrGzlpR2MFzlAqzj2ie49bp7--\r\n"; + + assertEquals(CurlUtils.buildCurlCommand(request, "multipart/form-data", + formDataBody.getBytes(CHARSET), false), + """ + curl -X POST 'http://post.form.data.example.org/' \\ + -F 'file-key=@"raw.txt"' \\ + -F 'string-key="string1"'"""); + } + + @Test + void testRequestWithBinaryContent() throws URISyntaxException + { + BasicHttpRequest request = mock(); + when(request.getMethod()).thenReturn(POST.name()); + when(request.getUri()).thenReturn(new URI("http://post.binary.example.org/")); + when(request.getHeaders()).thenReturn(EMPTY_HEADERS); + + assertEquals(CurlUtils.buildCurlCommand(request, ANY, ANY.getBytes(CHARSET), true), + """ + curl -X POST 'http://post.binary.example.org/' \\ + --data-binary '@'"""); + } +} diff --git a/vividus-plugin-rest-api/src/test/java/org/vividus/http/PublishingAttachmentInterceptorTests.java b/vividus-plugin-rest-api/src/test/java/org/vividus/http/PublishingAttachmentInterceptorTests.java index 570b2f9cf1..64994a724c 100644 --- a/vividus-plugin-rest-api/src/test/java/org/vividus/http/PublishingAttachmentInterceptorTests.java +++ b/vividus-plugin-rest-api/src/test/java/org/vividus/http/PublishingAttachmentInterceptorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2023 the original author or authors. + * Copyright 2019-2024 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. @@ -33,6 +33,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.URI; +import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.List; @@ -49,6 +50,7 @@ import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpStatus; +import org.apache.hc.core5.http.io.entity.ByteArrayEntity; import org.apache.hc.core5.http.message.BasicClassicHttpRequest; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.protocol.HttpContext; @@ -75,6 +77,7 @@ class PublishingAttachmentInterceptorTests private static final String CONTENT_TYPE = "Content-Type"; private static final String TEXT_PLAIN = "text/plain"; private static final byte[] DATA = "data".getBytes(StandardCharsets.UTF_8); + private static final String SPACE = " "; @Mock private IAttachmentPublisher attachmentPublisher; @InjectMocks private PublishingAttachmentInterceptor interceptor; @@ -104,7 +107,7 @@ void testHttpRequestWithNullBodyIsAttachedSuccessfully() void testHttpRequestIsAttachedSuccessfullyWhenContentTypeIsSet(String contentTypeHeaderName) throws IOException { Header contentTypeHeader = new BasicHeader(contentTypeHeaderName, TEXT_PLAIN); - var httpEntity = mock(HttpEntity.class); + var httpEntity = mock(ByteArrayEntity.class); testHttpRequestIsAttachedSuccessfully(contentTypeHeader, httpEntity); verify(httpEntity).getContentLength(); verify(httpEntity).writeTo(any(ByteArrayOutputStream.class)); @@ -135,7 +138,7 @@ void testNoHttpRequestBodyIsAttached() { HttpRequest httpRequest = mock(); when(httpRequest.getHeaders()).thenReturn(new Header[] {}); - when(httpRequest.toString()).thenReturn(METHOD + " " + ENDPOINT); + when(httpRequest.toString()).thenReturn(METHOD + SPACE + ENDPOINT); testNoHttpRequestBodyIsAttached(httpRequest, empty()); } @@ -177,6 +180,19 @@ void testNoHttpResponseBodyIsAttached() throws IOException verifyPublishAttachment(RESPONSE); } + @Test + void testCurlBuildIsFailed() throws URISyntaxException + { + HttpRequest httpRequest = mock(); + when(httpRequest.getHeaders()).thenReturn(new Header[] {}); + when(httpRequest.toString()).thenReturn(METHOD + SPACE + ENDPOINT); + String empty = ""; + var exception = new URISyntaxException(empty, empty); + when(httpRequest.getUri()).thenThrow(exception); + testNoHttpRequestBodyIsAttached(httpRequest, + equalTo(List.of(error(exception, "Error is occurred on building cURL command")))); + } + private void testHttpRequestIsAttachedSuccessfully(Header contentTypeHeader, HttpEntity httpEntity) { var httpRequest = createClassicHttpRequest(new Header[] { contentTypeHeader }, httpEntity);