From 1015a2c82f3f4d30a956f951ef9edee5b9e1c19e Mon Sep 17 00:00:00 2001 From: Hector Espert Date: Mon, 29 May 2023 23:37:47 +0200 Subject: [PATCH] Test spring WebClient http connectors --- logbook-spring-webflux/pom.xml | 19 +++ .../LogbookExchangeFilterFunctionTest.java | 154 +++++++++++------- 2 files changed, 117 insertions(+), 56 deletions(-) diff --git a/logbook-spring-webflux/pom.xml b/logbook-spring-webflux/pom.xml index 41109b4b9..1137aba08 100644 --- a/logbook-spring-webflux/pom.xml +++ b/logbook-spring-webflux/pom.xml @@ -79,6 +79,25 @@ wiremock test + + org.eclipse.jetty + jetty-reactive-httpclient + 3.0.8 + test + + + org.apache.httpcomponents.client5 + httpclient5 + 5.2.1 + test + + + org.apache.httpcomponents.core5 + httpcore5-reactive + 5.2.1 + test + + diff --git a/logbook-spring-webflux/src/test/java/org/zalando/logbook/spring/webflux/LogbookExchangeFilterFunctionTest.java b/logbook-spring-webflux/src/test/java/org/zalando/logbook/spring/webflux/LogbookExchangeFilterFunctionTest.java index 83b655e99..bcc666f32 100644 --- a/logbook-spring-webflux/src/test/java/org/zalando/logbook/spring/webflux/LogbookExchangeFilterFunctionTest.java +++ b/logbook-spring-webflux/src/test/java/org/zalando/logbook/spring/webflux/LogbookExchangeFilterFunctionTest.java @@ -3,11 +3,14 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.github.tomakehurst.wiremock.WireMockServer; import com.github.tomakehurst.wiremock.core.Options; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP; +import org.eclipse.jetty.io.ClientConnector; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.junit.jupiter.api.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.ArgumentCaptor; -import org.springframework.http.client.reactive.ReactorClientHttpConnector; +import org.springframework.http.client.reactive.*; import org.springframework.http.codec.json.Jackson2JsonDecoder; import org.springframework.http.codec.json.Jackson2JsonEncoder; import org.springframework.web.reactive.function.client.WebClient; @@ -22,6 +25,8 @@ import reactor.netty.http.client.HttpClient; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.client.WireMock.get; @@ -53,21 +58,11 @@ class LogbookExchangeFilterFunctionTest { .sink(new DefaultSink(new DefaultHttpLogFormatter(), writer)) .build(); - private WebClient client; - @BeforeEach void setup() { server.start(); serverWithoutChunkEncoding.start(); when(writer.isActive()).thenReturn(true); - - client = WebClient.builder() - .clientConnector(new ReactorClientHttpConnector(HttpClient.create())) - .baseUrl(server.baseUrl()) - .filter(new LogbookExchangeFilterFunction(logbook)) - .codecs(it -> it.customCodecs().register(new Jackson2JsonEncoder(new ObjectMapper()))) - .codecs(it -> it.customCodecs().register(new Jackson2JsonDecoder(new ObjectMapper()))) - .build(); } @AfterEach @@ -76,11 +71,22 @@ void tearDown() { serverWithoutChunkEncoding.stop(); } - @Test - void shouldLogRequestWithoutBody() throws IOException { + private static List clientHttpConnectors() { + ArrayList clients = new ArrayList<>(); + clients.add(new ReactorClientHttpConnector(HttpClient.create())); + clients.add(jettyClientHttpConnector()); + clients.add(new HttpComponentsClientHttpConnector()); + clients.add(new JdkClientHttpConnector()); + return clients; + } + + + @ParameterizedTest + @MethodSource("clientHttpConnectors") + void shouldLogRequestWithoutBody(ClientHttpConnector clientHttpConnector) throws IOException { server.stubFor(post("/echo").willReturn(aResponse().withStatus(200))); - sendAndReceive(); + sendAndReceive(clientHttpConnector); final String message = captureRequest(); @@ -90,11 +96,12 @@ void shouldLogRequestWithoutBody() throws IOException { .doesNotContain("Hello, world!"); } - @Test - void shouldLogEmptyResponseWithTransferEncodingChunked() throws IOException { + @ParameterizedTest + @MethodSource("clientHttpConnectors") + void shouldLogEmptyResponseWithTransferEncodingChunked(ClientHttpConnector clientHttpConnector) throws IOException { server.stubFor(get("/empty-chunked").willReturn(aResponse().withStatus(400))); - assertThatThrownBy(() -> client + assertThatThrownBy(() -> buildClient(clientHttpConnector) .get() .uri(server.baseUrl() + "/empty-chunked") .retrieve() @@ -117,11 +124,13 @@ void shouldLogEmptyResponseWithTransferEncodingChunked() throws IOException { .contains("HTTP/1.1 400 Bad Request"); } - @Test - void shouldLogRequestWithBody() throws IOException { + @ParameterizedTest + @MethodSource("clientHttpConnectors") + void shouldLogRequestWithBody(ClientHttpConnector clientHttpConnector) throws IOException { server.stubFor(post("/discard").willReturn(aResponse().withStatus(200))); - client.post() + buildClient(clientHttpConnector) + .post() .uri("/discard") .bodyValue("Hello, world!") .retrieve() @@ -136,28 +145,24 @@ void shouldLogRequestWithBody() throws IOException { .contains("Hello, world!"); } - private String captureRequest() throws IOException { - final ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); - verify(writer).write(any(Precorrelation.class), captor.capture()); - return captor.getValue(); - } - - @Test - void shouldNotLogRequestIfInactive() throws IOException { + @ParameterizedTest + @MethodSource("clientHttpConnectors") + void shouldNotLogRequestIfInactive(ClientHttpConnector clientHttpConnector) throws IOException { server.stubFor(post("/echo").willReturn(aResponse().withStatus(200))); when(writer.isActive()).thenReturn(false); - sendAndReceive(); + sendAndReceive(clientHttpConnector); verify(writer, never()).write(any(Precorrelation.class), any()); } - @Test - void shouldLogResponseWithoutBody() throws IOException { + @ParameterizedTest + @MethodSource("clientHttpConnectors") + void shouldLogResponseWithoutBody(ClientHttpConnector clientHttpConnector) throws IOException { serverWithoutChunkEncoding.stubFor(post("/discard").willReturn(aResponse().withStatus(200))); - client + buildClient(clientHttpConnector) .post() .uri(serverWithoutChunkEncoding.baseUrl() + "/discard") .retrieve() @@ -173,11 +178,13 @@ void shouldLogResponseWithoutBody() throws IOException { .doesNotContain("Hello, world!"); } - @Test - void shouldLogResponseWithBody() throws IOException { + @ParameterizedTest + @MethodSource("clientHttpConnectors") + void shouldLogResponseWithBody(ClientHttpConnector clientHttpConnector) throws IOException { serverWithoutChunkEncoding.stubFor(post("/echo").willReturn(aResponse().withStatus(200).withBody("Hello, world!"))); - final String response = client.post() + final String response = buildClient(clientHttpConnector) + .post() .uri(serverWithoutChunkEncoding.baseUrl() + "/echo") .bodyValue("Hello, world!") .retrieve() @@ -195,28 +202,25 @@ void shouldLogResponseWithBody() throws IOException { .contains("Hello, world!"); } - private String captureResponse() throws IOException { - final ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); - verify(writer, timeout(1_000)).write(any(Correlation.class), captor.capture()); - return captor.getValue(); - } - - @Test - void shouldNotLogResponseIfInactive() throws IOException { + @ParameterizedTest + @MethodSource("clientHttpConnectors") + void shouldNotLogResponseIfInactive(ClientHttpConnector clientHttpConnector) throws IOException { server.stubFor(post("/echo").willReturn(aResponse().withStatus(200))); when(writer.isActive()).thenReturn(false); - sendAndReceive(); + sendAndReceive(clientHttpConnector); verify(writer, never()).write(any(Correlation.class), any()); } - @Test - void shouldLogChunkedResponseWithBody() throws IOException { + @ParameterizedTest + @MethodSource("clientHttpConnectors") + void shouldLogChunkedResponseWithBody(ClientHttpConnector clientHttpConnector) throws IOException { server.stubFor(get("/chunked").willReturn(aResponse().withStatus(200).withBody("Hello, world!"))); - final String response = client.get() + final String response = buildClient(clientHttpConnector) + .get() .uri("/chunked") .retrieve() .bodyToMono(String.class) @@ -233,11 +237,13 @@ void shouldLogChunkedResponseWithBody() throws IOException { .contains("Hello, world!"); } - @Test - void shouldIgnoreBodies() throws IOException { + @ParameterizedTest + @MethodSource("clientHttpConnectors") + void shouldIgnoreBodies(ClientHttpConnector clientHttpConnector) throws IOException { server.stubFor(post("/echo").willReturn(aResponse().withStatus(200).withBody("Hello, world!"))); - final String response = client.post() + final String response = buildClient(clientHttpConnector) + .post() .uri("/echo") .header("Ignore", "true") .bodyValue("Hello, world!") @@ -266,11 +272,47 @@ void shouldIgnoreBodies() throws IOException { } } - private String sendAndReceive() { - return sendAndReceive("/echo"); + private WebClient buildClient(ClientHttpConnector connector) { + return WebClient.builder() + .clientConnector(connector) + .baseUrl(server.baseUrl()) + .filter(new LogbookExchangeFilterFunction(logbook)) + .codecs(it -> it.customCodecs().register(new Jackson2JsonEncoder(new ObjectMapper()))) + .codecs(it -> it.customCodecs().register(new Jackson2JsonDecoder(new ObjectMapper()))) + .build(); + } + + private static JettyClientHttpConnector jettyClientHttpConnector() { + JettyResourceFactory jettyResourceFactory = new JettyResourceFactory(); + SslContextFactory.Client sslContextFactory = new SslContextFactory.Client(); + ClientConnector connector = new ClientConnector(); + connector.setSslContextFactory(sslContextFactory); + HttpClientTransportOverHTTP transport = new HttpClientTransportOverHTTP(connector); + org.eclipse.jetty.client.HttpClient httpClient = new org.eclipse.jetty.client.HttpClient(transport); + return new JettyClientHttpConnector(httpClient, jettyResourceFactory); + } + + private String captureRequest() throws IOException { + final ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); + verify(writer).write(any(Precorrelation.class), captor.capture()); + return captor.getValue(); + } + + private String captureResponse() throws IOException { + final ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); + verify(writer, timeout(1_000)).write(any(Correlation.class), captor.capture()); + return captor.getValue(); + } + + private void sendAndReceive(ClientHttpConnector clientHttpConnector) { + sendAndReceive(buildClient(clientHttpConnector)); + } + + private String sendAndReceive(final WebClient client) { + return sendAndReceive(client, "/echo"); } - private String sendAndReceive(final String uri) { + private String sendAndReceive(final WebClient client, final String uri) { return client .post() .uri(uri)