Update javaxval/src/test/java/org/baeldung/javaxval/messageinterpolator/ParameterMessageInterpolaterIntegrationTest.java

update to use the givenX_whenY_thenZ naming convention for tests

Co-Authored-By: KevinGilmore <kpg102@gmail.com>
This commit is contained in:
Yavuz Tas
2019-10-29 10:02:27 +01:00
committed by GitHub
parent db85c8f275
commit e28fd3e7c9
20479 changed files with 1642089 additions and 0 deletions
+12
View File
@@ -0,0 +1,12 @@
#folders#
.idea
/target
/neoDb*
/data
/src/main/webapp/WEB-INF/classes
*/META-INF/*
# Packaged files #
*.jar
*.war
*.ear
+11
View File
@@ -0,0 +1,11 @@
## Spring REST Example Project
This module contains articles about reactive Spring 5 WebClient
### The Course
The "REST With Spring" Classes: http://bit.ly/restwithspring
### Relevant Articles
- [Logging Spring WebClient Calls](https://www.baeldung.com/spring-log-webclient-calls)
- [Simultaneous Spring WebClient Calls](https://www.baeldung.com/spring-webclient-simultaneous-calls)
+175
View File
@@ -0,0 +1,175 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-5-reactive-client</artifactId>
<name>spring-5-reactive-client</name>
<packaging>jar</packaging>
<description>spring 5 sample project about new features</description>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>parent-boot-2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../parent-boot-2</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.projectreactor</groupId>
<artifactId>reactor-spring</artifactId>
<version>${reactor-spring.version}</version>
</dependency>
<dependency>
<groupId>javax.json.bind</groupId>
<artifactId>javax.json.bind-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.geronimo.specs</groupId>
<artifactId>geronimo-json_1.1_spec</artifactId>
<version>${geronimo-json_1.1_spec.version}</version>
</dependency>
<dependency>
<groupId>org.apache.johnzon</groupId>
<artifactId>johnzon-jsonb</artifactId>
</dependency>
<!-- utils -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- okhttp -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>mockwebserver</artifactId>
<version>4.0.1</version>
<scope>test</scope>
</dependency>
<!-- runtime and test scoped -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock</artifactId>
<version>2.24.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>${commons-collections4.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>2.23.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<version>3.2.10.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-reactive-httpclient</artifactId>
<version>${jetty-reactive-httpclient.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.baeldung.Spring5Application</mainClass>
<layout>JAR</layout>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19.1</version>
<dependencies>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-surefire-provider</artifactId>
<version>1.0.1</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
<properties>
<reactor-spring.version>1.0.1.RELEASE</reactor-spring.version>
<johnzon.version>1.1.3</johnzon.version>
<jsonb-api.version>1.0</jsonb-api.version>
<geronimo-json_1.1_spec.version>1.0</geronimo-json_1.1_spec.version>
<commons-collections4.version>4.1</commons-collections4.version>
<jetty-reactive-httpclient.version>1.0.3</jetty-reactive-httpclient.version>
</properties>
</project>
@@ -0,0 +1,5 @@
package com.baeldung.reactive.enums;
public enum Role {
ENGINEER, SENIOR_ENGINEER, LEAD_ENGINEER
}
@@ -0,0 +1,64 @@
package com.baeldung.reactive.model;
import com.baeldung.reactive.enums.Role;
public class Employee {
private Integer employeeId;
private String firstName;
private String lastName;
private Integer age;
private Role role;
public Employee() {
}
public Employee(Integer employeeId, String firstName, String lastName, Integer age, Role role) {
this.employeeId = employeeId;
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
this.role = role;
}
public Integer getEmployeeId() {
return employeeId;
}
public void setEmployeeId(Integer employeeId) {
this.employeeId = employeeId;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Role getRole() {
return role;
}
public void setRole(Role role) {
this.role = role;
}
}
@@ -0,0 +1,13 @@
package com.baeldung.reactive.model;
import lombok.AllArgsConstructor;
import lombok.Data;
@AllArgsConstructor
@Data
public class Foo {
private long id;
private String name;
}
@@ -0,0 +1,55 @@
package com.baeldung.reactive.service;
import com.baeldung.reactive.model.Employee;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
public class EmployeeService {
private WebClient webClient;
public static String PATH_PARAM_BY_ID = "/employee/{id}";
public static String ADD_EMPLOYEE = "/employee";
public EmployeeService(WebClient webClient) {
this.webClient = webClient;
}
public EmployeeService(String baseUrl) {
this.webClient = WebClient.create(baseUrl);
}
public Mono<Employee> getEmployeeById(Integer employeeId) {
return webClient
.get()
.uri(PATH_PARAM_BY_ID, employeeId)
.retrieve()
.bodyToMono(Employee.class);
}
public Mono<Employee> addNewEmployee(Employee newEmployee) {
return webClient
.post()
.uri(ADD_EMPLOYEE)
.syncBody(newEmployee)
.retrieve().
bodyToMono(Employee.class);
}
public Mono<Employee> updateEmployee(Integer employeeId, Employee updateEmployee) {
return webClient
.put()
.uri(PATH_PARAM_BY_ID,employeeId)
.syncBody(updateEmployee)
.retrieve()
.bodyToMono(Employee.class);
}
public Mono<String> deleteEmployeeById(Integer employeeId) {
return webClient
.delete()
.uri(PATH_PARAM_BY_ID,employeeId)
.retrieve()
.bodyToMono(String.class);
}
}
@@ -0,0 +1,65 @@
package com.baeldung.reactive.webclient.simultaneous;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import reactor.core.publisher.Flux;
import reactor.core.scheduler.Schedulers;
import java.util.List;
import java.util.logging.Logger;
public class Client {
private static final Logger LOG = Logger.getLogger(Client.class.getName());
private WebClient webClient;
public Client(String uri) {
this.webClient = WebClient.create(uri);
}
public Mono<User> getUser(int id) {
LOG.info(String.format("Calling getUser(%d)", id));
return webClient.get()
.uri("/user/{id}", id)
.retrieve()
.bodyToMono(User.class);
}
public Mono<Item> getItem(int id) {
return webClient.get()
.uri("/item/{id}", id)
.retrieve()
.bodyToMono(Item.class);
}
public Mono<User> getOtherUser(int id) {
return webClient.get()
.uri("/otheruser/{id}", id)
.retrieve()
.bodyToMono(User.class);
}
public Flux<User> fetchUsers(List<Integer> userIds) {
return Flux.fromIterable(userIds)
.parallel()
.runOn(Schedulers.elastic())
.flatMap(this::getUser)
.ordered((u1, u2) -> u2.id() - u1.id());
}
public Flux<User> fetchUserAndOtherUser(int id) {
return Flux.merge(getUser(id), getOtherUser(id))
.parallel()
.runOn(Schedulers.elastic())
.ordered((u1, u2) -> u2.id() - u1.id());
}
public Mono<UserWithItem> fetchUserAndItem(int userId, int itemId) {
Mono<User> user = getUser(userId).subscribeOn(Schedulers.elastic());
Mono<Item> item = getItem(itemId).subscribeOn(Schedulers.elastic());
return Mono.zip(user, item, UserWithItem::new);
}
}
@@ -0,0 +1,17 @@
package com.baeldung.reactive.webclient.simultaneous;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
public class Item {
private int id;
@JsonCreator
public Item(@JsonProperty("id") int id) {
this.id = id;
}
public int id() {
return id;
}
}
@@ -0,0 +1,17 @@
package com.baeldung.reactive.webclient.simultaneous;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
public class User {
private int id;
@JsonCreator
public User(@JsonProperty("id") int id) {
this.id = id;
}
public int id() {
return id;
}
}
@@ -0,0 +1,19 @@
package com.baeldung.reactive.webclient.simultaneous;
public class UserWithItem {
private User user;
private Item item;
public UserWithItem(User user, Item item) {
this.user = user;
this.item = item;
}
public User user() {
return user;
}
public Item item() {
return item;
}
}
@@ -0,0 +1,3 @@
logging.level.root=INFO
server.port=8081
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
# Pattern of log message for console appender
<Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern>
</layout>
</appender>
<logger name="org.springframework" level="INFO" />
<root level="INFO">
<appender-ref ref="stdout" />
</root>
</configuration>
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<display-name>Spring Functional Application</display-name>
<servlet>
<servlet-name>functional</servlet-name>
<servlet-class>com.baeldung.functional.RootServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>functional</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
@@ -0,0 +1,42 @@
package com.baeldung.reactive;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;
import com.baeldung.reactive.model.Foo;
import reactor.core.publisher.Mono;
@SpringBootTest
public class ReactiveIntegrationTest {
private WebClient client;
@BeforeEach
public void before() {
client = WebClient.create("http://localhost:8080");
}
//
@Test
public void whenMonoReactiveEndpointIsConsumed_thenCorrectOutput() {
final Mono<ClientResponse> fooMono = client.get().uri("/foos/123").exchange().log();
System.out.println(fooMono.subscribe());
}
@Test
public void whenFluxReactiveEndpointIsConsumed_thenCorrectOutput() throws InterruptedException {
client.get().uri("/foos")
.retrieve()
.bodyToFlux(Foo.class).log()
.subscribe(System.out::println);
System.out.println();
}
}
@@ -0,0 +1,35 @@
package com.baeldung.reactive;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.reactive.function.client.WebClient;
import com.baeldung.reactive.model.Foo;
@SpringBootApplication
public class Spring5ReactiveTestApplication {
@Bean
public WebClient client() {
return WebClient.create("http://localhost:8080");
}
@Bean
CommandLineRunner cmd(WebClient client) {
return args -> {
client.get().uri("/foos2")
.retrieve()
.bodyToFlux(Foo.class).log()
.subscribe(System.out::println);
};
}
//
public static void main(String[] args) {
SpringApplication.run(Spring5ReactiveTestApplication.class, args);
}
}
@@ -0,0 +1,154 @@
package com.baeldung.reactive.logging;
import ch.qos.logback.classic.spi.LoggingEvent;
import ch.qos.logback.core.Appender;
import com.baeldung.reactive.logging.filters.LogFilters;
import com.baeldung.reactive.logging.netty.CustomLogger;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.URI;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.slf4j.LoggerFactory;
import org.springframework.http.client.reactive.JettyClientHttpConnector;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.netty.channel.BootstrapHandlers;
import reactor.netty.http.client.HttpClient;
import static com.baeldung.reactive.logging.jetty.RequestLogEnhancer.enhance;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class WebClientLoggingIntegrationTest {
@AllArgsConstructor
@Data
class Post {
private String title;
private String body;
private int userId;
}
private Appender jettyAppender;
private Appender nettyAppender;
private Appender mockAppender;
private String sampleUrl = "https://jsonplaceholder.typicode.com/posts";
private Post post;
private String sampleResponseBody;
@BeforeEach
private void setup() throws Exception {
post = new Post("Learn WebClient logging with Baeldung!", "", 1);
sampleResponseBody = new ObjectMapper().writeValueAsString(post);
ch.qos.logback.classic.Logger jetty = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("com.baeldung.reactive.logging.jetty");
jettyAppender = mock(Appender.class);
when(jettyAppender.getName()).thenReturn("com.baeldung.reactive.logging.jetty");
jetty.addAppender(jettyAppender);
ch.qos.logback.classic.Logger netty = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("reactor.netty.http.client");
nettyAppender = mock(Appender.class);
when(nettyAppender.getName()).thenReturn("reactor.netty.http.client");
netty.addAppender(nettyAppender);
ch.qos.logback.classic.Logger test = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("com.baeldung.reactive");
mockAppender = mock(Appender.class);
when(mockAppender.getName()).thenReturn("com.baeldung.reactive");
test.addAppender(mockAppender);
}
@Test
public void givenJettyHttpClient_whenEndpointIsConsumed_thenRequestAndResponseBodyLogged() {
SslContextFactory.Client sslContextFactory = new SslContextFactory.Client();
org.eclipse.jetty.client.HttpClient httpClient = new org.eclipse.jetty.client.HttpClient(sslContextFactory) {
@Override
public Request newRequest(URI uri) {
Request request = super.newRequest(uri);
return enhance(request);
}
};
WebClient
.builder()
.clientConnector(new JettyClientHttpConnector(httpClient))
.build()
.post()
.uri(sampleUrl)
.body(BodyInserters.fromObject(post))
.retrieve()
.bodyToMono(String.class)
.block();
verify(jettyAppender).doAppend(argThat(argument -> (((LoggingEvent) argument).getFormattedMessage()).contains(sampleResponseBody)));
}
@Test
public void givenNettyHttpClientWithWiretap_whenEndpointIsConsumed_thenRequestAndResponseBodyLogged() {
reactor.netty.http.client.HttpClient httpClient = HttpClient
.create()
.wiretap(true);
WebClient
.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build()
.post()
.uri(sampleUrl)
.body(BodyInserters.fromObject(post))
.exchange()
.block();
verify(nettyAppender).doAppend(argThat(argument -> (((LoggingEvent) argument).getFormattedMessage()).contains("00000300")));
}
@Test
public void givenNettyHttpClientWithCustomLogger_whenEndpointIsConsumed_thenRequestAndResponseBodyLogged() {
reactor.netty.http.client.HttpClient httpClient = HttpClient
.create()
.tcpConfiguration(
tc -> tc.bootstrap(
b -> BootstrapHandlers.updateLogSupport(b, new CustomLogger(HttpClient.class))));
WebClient
.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build()
.post()
.uri(sampleUrl)
.body(BodyInserters.fromObject(post))
.exchange()
.block();
verify(nettyAppender).doAppend(argThat(argument -> (((LoggingEvent) argument).getFormattedMessage()).contains(sampleResponseBody)));
}
@Test
public void givenDefaultHttpClientWithFilter_whenEndpointIsConsumed_thenRequestAndResponseLogged() {
WebClient
.builder()
.filters(exchangeFilterFunctions -> {
exchangeFilterFunctions.addAll(LogFilters.prepareFilters());
})
.build()
.post()
.uri(sampleUrl)
.body(BodyInserters.fromObject(post))
.exchange()
.block();
verify(mockAppender).doAppend(argThat(argument -> (((LoggingEvent) argument).getFormattedMessage()).contains("domain=.typicode.com;")));
}
}
@@ -0,0 +1,54 @@
package com.baeldung.reactive.logging.filters;
import java.util.Arrays;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
import reactor.core.publisher.Mono;
@Slf4j
public class LogFilters {
public static List<ExchangeFilterFunction> prepareFilters() {
return Arrays.asList(logRequest(), logResponse());
}
private static ExchangeFilterFunction logRequest() {
return ExchangeFilterFunction.ofRequestProcessor(clientRequest -> {
if (log.isDebugEnabled()) {
StringBuilder sb = new StringBuilder("Request: \n")
.append(clientRequest.method())
.append(" ")
.append(clientRequest.url());
clientRequest
.headers()
.forEach((name, values) -> values.forEach(value -> sb
.append("\n")
.append(name)
.append(":")
.append(value)));
log.debug(sb.toString());
}
return Mono.just(clientRequest);
});
}
private static ExchangeFilterFunction logResponse() {
return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
if (log.isDebugEnabled()) {
StringBuilder sb = new StringBuilder("Response: \n")
.append("Status: ")
.append(clientResponse.rawStatusCode());
clientResponse
.headers()
.asHttpHeaders()
.forEach((key, value1) -> value1.forEach(value -> sb
.append("\n")
.append(key)
.append(":")
.append(value)));
log.debug(sb.toString());
}
return Mono.just(clientResponse);
});
}
}
@@ -0,0 +1,93 @@
package com.baeldung.reactive.logging.jetty;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
@Slf4j
public class RequestLogEnhancer {
public static Request enhance(Request request) {
StringBuilder group = new StringBuilder();
request.onRequestBegin(theRequest -> group
.append("Request ")
.append(theRequest.getMethod())
.append(" ")
.append(theRequest.getURI())
.append("\n"));
request.onRequestHeaders(theRequest -> {
for (HttpField header : theRequest.getHeaders())
group
.append(header)
.append("\n");
});
request.onRequestContent((theRequest, content) -> {
group.append(toString(content, getCharset(theRequest.getHeaders())));
});
request.onRequestSuccess(theRequest -> {
log.debug(group.toString());
group.delete(0, group.length());
});
group.append("\n");
request.onResponseBegin(theResponse -> {
group
.append("Response \n")
.append(theResponse.getVersion())
.append(" ")
.append(theResponse.getStatus());
if (theResponse.getReason() != null) {
group
.append(" ")
.append(theResponse.getReason());
}
group.append("\n");
});
request.onResponseHeaders(theResponse -> {
for (HttpField header : theResponse.getHeaders())
group
.append(header)
.append("\n");
});
request.onResponseContent((theResponse, content) -> {
group.append(toString(content, getCharset(theResponse.getHeaders())));
});
request.onResponseSuccess(theResponse -> {
log.debug(group.toString());
});
return request;
}
private static String toString(ByteBuffer buffer, Charset charset) {
byte[] bytes;
if (buffer.hasArray()) {
bytes = new byte[buffer.capacity()];
System.arraycopy(buffer.array(), 0, bytes, 0, buffer.capacity());
} else {
bytes = new byte[buffer.remaining()];
buffer.get(bytes, 0, bytes.length);
}
return new String(bytes, charset);
}
private static Charset getCharset(HttpFields headers) {
String contentType = headers.get(HttpHeader.CONTENT_TYPE);
if (contentType != null) {
String[] tokens = contentType
.toLowerCase(Locale.US)
.split("charset=");
if (tokens.length == 2) {
String encoding = tokens[1].replaceAll("[;\"]", "");
return Charset.forName(encoding);
}
}
return StandardCharsets.UTF_8;
}
}
@@ -0,0 +1,42 @@
package com.baeldung.reactive.logging.netty;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.logging.LoggingHandler;
import java.nio.charset.Charset;
import static io.netty.util.internal.PlatformDependent.allocateUninitializedArray;
import static java.lang.Math.max;
import static java.nio.charset.Charset.defaultCharset;
public class CustomLogger extends LoggingHandler {
public CustomLogger(Class<?> clazz) {
super(clazz);
}
@Override
protected String format(ChannelHandlerContext ctx, String event, Object arg) {
if (arg instanceof ByteBuf) {
ByteBuf msg = (ByteBuf) arg;
return decode(msg, msg.readerIndex(), msg.readableBytes(), defaultCharset());
}
return super.format(ctx, event, arg);
}
private String decode(ByteBuf src, int readerIndex, int len, Charset charset) {
if (len != 0) {
byte[] array;
int offset;
if (src.hasArray()) {
array = src.array();
offset = src.arrayOffset() + readerIndex;
} else {
array = allocateUninitializedArray(max(len, 1024));
offset = 0;
src.getBytes(readerIndex, array, 0, len);
}
return new String(array, offset, len, charset);
}
return "";
}
}
@@ -0,0 +1,121 @@
package com.baeldung.reactive.service;
import com.baeldung.reactive.model.Employee;
import com.baeldung.reactive.enums.Role;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;
import org.junit.Rule;
import org.junit.jupiter.api.*;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import java.io.IOException;
import static org.junit.jupiter.api.Assertions.*;
class EmployeeServiceIntegrationTest {
public static MockWebServer mockBackEnd;
private EmployeeService employeeService;
private ObjectMapper MAPPER = new ObjectMapper();
@BeforeAll
static void setUp() throws IOException {
mockBackEnd = new MockWebServer();
mockBackEnd.start();
}
@AfterAll
static void tearDown() throws IOException {
mockBackEnd.shutdown();
}
@BeforeEach
void initialize() {
String baseUrl = String.format("http://localhost:%s", mockBackEnd.getPort());
employeeService = new EmployeeService(baseUrl);
}
@Test
void getEmployeeById() throws Exception {
Employee mockEmployee = new Employee(100, "Adam", "Sandler", 32, Role.LEAD_ENGINEER);
mockBackEnd.enqueue(new MockResponse().setBody(MAPPER.writeValueAsString(mockEmployee))
.addHeader("Content-Type", "application/json"));
Mono<Employee> employeeMono = employeeService.getEmployeeById(100);
StepVerifier.create(employeeMono)
.expectNextMatches(employee -> employee.getRole().equals(Role.LEAD_ENGINEER))
.verifyComplete();
RecordedRequest recordedRequest = mockBackEnd.takeRequest();
assertEquals("GET", recordedRequest.getMethod());
assertEquals("/employee/100", recordedRequest.getPath());
}
@Test
void addNewEmployee() throws Exception {
Employee newEmployee = new Employee(null, "Adam", "Sandler", 32, Role.LEAD_ENGINEER);
Employee webClientResponse = new Employee(100, "Adam", "Sandler", 32, Role.LEAD_ENGINEER);
mockBackEnd.enqueue(new MockResponse().setBody(MAPPER.writeValueAsString(webClientResponse))
.addHeader("Content-Type", "application/json"));
Mono<Employee> employeeMono = employeeService.addNewEmployee(newEmployee);
StepVerifier.create(employeeMono)
.expectNextMatches(employee -> employee.getEmployeeId().equals(100))
.verifyComplete();
RecordedRequest recordedRequest = mockBackEnd.takeRequest();
assertEquals("POST", recordedRequest.getMethod());
assertEquals("/employee", recordedRequest.getPath());
}
@Test
void updateEmployee() throws Exception {
Integer newAge = 33;
String newLastName = "Sandler New";
Employee updateEmployee = new Employee(100, "Adam", newLastName, newAge, Role.LEAD_ENGINEER);
mockBackEnd.enqueue(new MockResponse().setBody(MAPPER.writeValueAsString(updateEmployee))
.addHeader("Content-Type", "application/json"));
Mono<Employee> updatedEmploye = employeeService.updateEmployee(100, updateEmployee);
StepVerifier.create(updatedEmploye)
.expectNextMatches(employee -> employee.getLastName().equals(newLastName) && employee.getAge() == newAge)
.verifyComplete();
RecordedRequest recordedRequest = mockBackEnd.takeRequest();
assertEquals("PUT", recordedRequest.getMethod());
assertEquals("/employee/100", recordedRequest.getPath());
}
@Test
void deleteEmployee() throws Exception {
String responseMessage = "Employee Deleted SuccessFully";
Integer employeeId = 100;
mockBackEnd.enqueue(new MockResponse().setBody(MAPPER.writeValueAsString(responseMessage))
.addHeader("Content-Type", "application/json"));
Mono<String> deletedEmployee = employeeService.deleteEmployeeById(employeeId);
StepVerifier.create(deletedEmployee)
.expectNext("\"Employee Deleted SuccessFully\"")
.verifyComplete();
RecordedRequest recordedRequest = mockBackEnd.takeRequest();
assertEquals("DELETE", recordedRequest.getMethod());
assertEquals("/employee/100", recordedRequest.getPath());
}
}
@@ -0,0 +1,114 @@
package com.baeldung.reactive.service;
import com.baeldung.reactive.model.Employee;
import com.baeldung.reactive.enums.Role;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.exceptions.base.MockitoException;
import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class EmployeeServiceUnitTest {
EmployeeService employeeService;
@Mock
private WebClient webClientMock;
@Mock
private WebClient.RequestHeadersSpec requestHeadersMock;
@Mock
private WebClient.RequestHeadersUriSpec requestHeadersUriMock;
@Mock
private WebClient.RequestBodySpec requestBodyMock;
@Mock
private WebClient.RequestBodyUriSpec requestBodyUriMock;
@Mock
private WebClient.ResponseSpec responseMock;
@BeforeEach
void setUp() {
employeeService = new EmployeeService(webClientMock);
}
@Test
void givenEmployeeId_whenGetEmployeeById_thenReturnEmployee() {
Integer employeeId = 100;
Employee mockEmployee = new Employee(100, "Adam", "Sandler", 32, Role.LEAD_ENGINEER);
when(webClientMock.get()).thenReturn(requestHeadersUriMock);
when(requestHeadersUriMock.uri("/employee/{id}", employeeId)).thenReturn(requestHeadersMock);
when(requestHeadersMock.retrieve()).thenReturn(responseMock);
when(responseMock.bodyToMono(Employee.class)).thenReturn(Mono.just(mockEmployee));
Mono<Employee> employeeMono = employeeService.getEmployeeById(employeeId);
StepVerifier.create(employeeMono)
.expectNextMatches(employee -> employee.getRole().equals(Role.LEAD_ENGINEER))
.verifyComplete();
}
@Test
void givenEmployee_whenAddEmployee_thenAddNewEmployee() {
Employee newEmployee = new Employee(null, "Adam", "Sandler", 32, Role.LEAD_ENGINEER);
Employee webClientResponse = new Employee(100, "Adam", "Sandler", 32, Role.LEAD_ENGINEER);
when(webClientMock.post()).thenReturn(requestBodyUriMock);
when(requestBodyUriMock.uri(EmployeeService.ADD_EMPLOYEE)).thenReturn(requestBodyMock);
when(requestBodyMock.syncBody(newEmployee)).thenReturn(requestHeadersMock);
when(requestHeadersMock.retrieve()).thenReturn(responseMock);
when(responseMock.bodyToMono(Employee.class)).thenReturn(Mono.just(webClientResponse));
Mono<Employee> employeeMono = employeeService.addNewEmployee(newEmployee);
StepVerifier.create(employeeMono)
.expectNextMatches(employee -> employee.getEmployeeId().equals(100))
.verifyComplete();
}
@Test
void givenEmployee_whenupdateEmployee_thenUpdatedEmployee() {
Integer newAge = 33;
String newLastName = "Sandler New";
Employee updateEmployee = new Employee(100, "Adam", newLastName, newAge, Role.LEAD_ENGINEER);
when(webClientMock.put()).thenReturn(requestBodyUriMock);
when(requestBodyUriMock.uri(EmployeeService.PATH_PARAM_BY_ID, 100)).thenReturn(requestBodyMock);
when(requestBodyMock.syncBody(updateEmployee)).thenReturn(requestHeadersMock);
when(requestHeadersMock.retrieve()).thenReturn(responseMock);
when(responseMock.bodyToMono(Employee.class)).thenReturn(Mono.just(updateEmployee));
Mono<Employee> updatedEmployee = employeeService.updateEmployee(100, updateEmployee);
StepVerifier.create(updatedEmployee)
.expectNextMatches(employee -> employee.getLastName().equals(newLastName) && employee.getAge() == newAge)
.verifyComplete();
}
@Test
void givenEmployee_whenDeleteEmployeeById_thenDeleteSuccessful() {
String responseMessage = "Employee Deleted SuccessFully";
when(webClientMock.delete()).thenReturn(requestHeadersUriMock);
when(requestHeadersUriMock.uri(EmployeeService.PATH_PARAM_BY_ID, 100)).thenReturn(requestHeadersMock);
when(requestHeadersMock.retrieve()).thenReturn(responseMock);
when(responseMock.bodyToMono(String.class)).thenReturn(Mono.just(responseMessage));
Mono<String> deletedEmployee = employeeService.deleteEmployeeById(100);
StepVerifier.create(deletedEmployee)
.expectNext(responseMessage)
.verifyComplete();
}
}
@@ -0,0 +1,70 @@
package com.baeldung.reactive.webclient.simultaneous;
import org.junit.Test;
import org.junit.Before;
import org.junit.After;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import com.github.tomakehurst.wiremock.WireMockServer;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
@RunWith(SpringRunner.class)
@SpringBootTest
public class ClientIntegrationTest {
private WireMockServer wireMockServer;
@Before
public void setup() {
wireMockServer = new WireMockServer(wireMockConfig().port(8089));
wireMockServer.start();
configureFor("localhost", wireMockServer.port());
}
@After
public void tearDown() {
wireMockServer.stop();
}
@Test
public void givenClient_whenFetchingUsers_thenExecutionTimeIsLessThanDouble() {
// Arrange
int requestsNumber = 5;
int singleRequestTime = 1000;
for (int i = 1; i <= requestsNumber; i++) {
stubFor(get(urlEqualTo("/user/" + i)).willReturn(aResponse().withFixedDelay(singleRequestTime)
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody(String.format("{ \"id\": %d }", i))));
}
List<Integer> userIds = IntStream.rangeClosed(1, requestsNumber)
.boxed()
.collect(Collectors.toList());
Client client = new Client("http://localhost:8089");
// Act
long start = System.currentTimeMillis();
List<User> users = client.fetchUsers(userIds)
.collectList()
.block();
long end = System.currentTimeMillis();
// Assert
long totalExecutionTime = end - start;
assertEquals("Unexpected number of users", requestsNumber, users.size());
assertTrue("Execution time is too big", 2 * singleRequestTime > totalExecutionTime);
}
}
@@ -0,0 +1,17 @@
package org.baeldung;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import com.baeldung.reactive.Spring5ReactiveTestApplication;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Spring5ReactiveTestApplication.class)
public class SpringContextIntegrationTest {
@Test
public void whenSpringContextIsBootstrapped_thenNoExceptions() {
}
}
@@ -0,0 +1,17 @@
package org.baeldung;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import com.baeldung.reactive.Spring5ReactiveTestApplication;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Spring5ReactiveTestApplication.class)
public class SpringContextTest {
@Test
public void whenSpringContextIsBootstrapped_thenNoExceptions() {
}
}
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
# Pattern of log message for console appender
<Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern>
</layout>
</appender>
<logger name="org.springframework" level="INFO" />
<logger name="com.baeldung.reactive.logging.jetty" level="DEBUG" />
<logger name="reactor.netty.http.client.HttpClient" level="DEBUG" />
<logger name="com.baeldung.reactive.logging" level="DEBUG" />
<root level="INFO">
<appender-ref ref="stdout" />
</root>
</configuration>