diff --git a/testing-modules/mockserver/pom.xml b/testing-modules/mockserver/pom.xml index 3495ddb09d9d..896e5ced7b4b 100644 --- a/testing-modules/mockserver/pom.xml +++ b/testing-modules/mockserver/pom.xml @@ -15,6 +15,27 @@ + + org.springframework.boot + spring-boot-starter-web + ${spring-boot.version} + + + org.springframework + spring-webflux + ${spring.webflux.version} + + + org.springframework.boot + spring-boot-test + ${spring-boot.version} + test + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + org.mock-server mockserver-netty @@ -35,11 +56,33 @@ httpcore ${apche-http-version} + + org.springframework.boot + spring-boot-test-autoconfigure + ${spring-boot.version} + test + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 17 + 17 + + + + + 3.10.8 4.4.1 + 3.3.2 + 6.1.13 + 2.18.0 \ No newline at end of file diff --git a/testing-modules/mockserver/src/main/java/com/baeldung/mock/server/multiplerequests/Config.java b/testing-modules/mockserver/src/main/java/com/baeldung/mock/server/multiplerequests/Config.java new file mode 100644 index 000000000000..72ae6f68a835 --- /dev/null +++ b/testing-modules/mockserver/src/main/java/com/baeldung/mock/server/multiplerequests/Config.java @@ -0,0 +1,15 @@ +package com.baeldung.mock.server.multiplerequests; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.reactive.function.client.WebClient; + +@Configuration +public class Config { + + @Bean + public WebClient webClient() { + return WebClient.create(); + } + +} diff --git a/testing-modules/mockserver/src/main/java/com/baeldung/mock/server/multiplerequests/PaymentController.java b/testing-modules/mockserver/src/main/java/com/baeldung/mock/server/multiplerequests/PaymentController.java new file mode 100644 index 000000000000..463ba7d4b3cd --- /dev/null +++ b/testing-modules/mockserver/src/main/java/com/baeldung/mock/server/multiplerequests/PaymentController.java @@ -0,0 +1,52 @@ +package com.baeldung.mock.server.multiplerequests; + +import java.util.UUID; + +import org.json.JSONException; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.reactive.function.BodyInserters; +import org.springframework.web.reactive.function.client.WebClient; + +@RestController +@RequestMapping("/api/") +public class PaymentController { + + private static final Logger logger = LoggerFactory.getLogger(PaymentController.class); + private final WebClient webClient; + + public PaymentController(WebClient webClient) { + this.webClient = webClient; + } + + @PostMapping("payment/process") + public ResponseEntity submitPayment(@RequestBody PaymentGatewayRequest paymentGatewayRequest) throws JSONException { + + String paymentSubmissionResponse = webClient.post() + .uri("http://localhost:9090/payment/submit") + .body(BodyInserters.fromValue(paymentGatewayRequest)) + .retrieve() + .bodyToMono(String.class) + .block(); + + UUID paymentId = UUID.fromString(new JSONObject(paymentSubmissionResponse).getString("paymentId")); + PaymentGatewayResponse.PaymentStatus paymentStatus = PaymentGatewayResponse.PaymentStatus.PENDING; + while (paymentStatus.equals(PaymentGatewayResponse.PaymentStatus.PENDING)) { + String paymentStatusResponse = webClient.get() + .uri("http://localhost:9090/payment/status/%s".formatted(paymentId)) + .retrieve() + .bodyToMono(String.class) + .block(); + paymentStatus = PaymentGatewayResponse.PaymentStatus.valueOf(new JSONObject(paymentStatusResponse).getString("paymentStatus")); + logger.info("Payment Status {}", paymentStatus); + } + return new ResponseEntity<>(new PaymentGatewayResponse(paymentId, paymentStatus), HttpStatus.OK); + } +} diff --git a/testing-modules/mockserver/src/main/java/com/baeldung/mock/server/multiplerequests/PaymentGatewayRequest.java b/testing-modules/mockserver/src/main/java/com/baeldung/mock/server/multiplerequests/PaymentGatewayRequest.java new file mode 100644 index 000000000000..1fa1edc3a1ff --- /dev/null +++ b/testing-modules/mockserver/src/main/java/com/baeldung/mock/server/multiplerequests/PaymentGatewayRequest.java @@ -0,0 +1,5 @@ +package com.baeldung.mock.server.multiplerequests; + +public record PaymentGatewayRequest(String cardNumber, String expiryMonth, String expiryYear, String currency, int amount, String cvv) { + +} diff --git a/testing-modules/mockserver/src/main/java/com/baeldung/mock/server/multiplerequests/PaymentGatewayResponse.java b/testing-modules/mockserver/src/main/java/com/baeldung/mock/server/multiplerequests/PaymentGatewayResponse.java new file mode 100644 index 000000000000..658cfb07cd33 --- /dev/null +++ b/testing-modules/mockserver/src/main/java/com/baeldung/mock/server/multiplerequests/PaymentGatewayResponse.java @@ -0,0 +1,16 @@ +package com.baeldung.mock.server.multiplerequests; + +import java.util.UUID; + +import com.fasterxml.jackson.annotation.JsonInclude; + +@JsonInclude(JsonInclude.Include.NON_NULL) +public record PaymentGatewayResponse(UUID id, PaymentStatus status) { + + public enum PaymentStatus { + PENDING, + AUTHORIZED, + DECLINED, + REJECTED + } +} diff --git a/testing-modules/mockserver/src/main/java/com/baeldung/mock/server/multiplerequests/SpringApplication.java b/testing-modules/mockserver/src/main/java/com/baeldung/mock/server/multiplerequests/SpringApplication.java new file mode 100644 index 000000000000..b02dabf802d5 --- /dev/null +++ b/testing-modules/mockserver/src/main/java/com/baeldung/mock/server/multiplerequests/SpringApplication.java @@ -0,0 +1,12 @@ +package com.baeldung.mock.server.multiplerequests; + +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringApplication { + + public static void main(String[] args) { + org.springframework.boot.SpringApplication.run(SpringApplication.class, args); + } + +} diff --git a/testing-modules/mockserver/src/test/java/com/baeldung/mock/server/multiplerequests/PaymentControllerTest.java b/testing-modules/mockserver/src/test/java/com/baeldung/mock/server/multiplerequests/PaymentControllerTest.java new file mode 100644 index 000000000000..9761cd6ca319 --- /dev/null +++ b/testing-modules/mockserver/src/test/java/com/baeldung/mock/server/multiplerequests/PaymentControllerTest.java @@ -0,0 +1,81 @@ +package com.baeldung.mock.server.multiplerequests; + +import static org.mockserver.integration.ClientAndServer.startClientAndServer; +import static org.mockserver.model.HttpRequest.request; +import static org.mockserver.model.HttpResponse.response; + +import java.util.UUID; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockserver.client.server.MockServerClient; +import org.mockserver.integration.ClientAndServer; +import org.mockserver.matchers.Times; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.test.web.reactive.server.WebTestClient; + +@SpringBootTest +@AutoConfigureMockMvc +class PaymentControllerTest { + + @Autowired + private WebTestClient webTestClient; + + private ClientAndServer clientAndServer; + + private final MockServerClient mockServerClient = new MockServerClient("localhost", 9090); + + @BeforeEach + void setup() { + clientAndServer = startClientAndServer(9090); + } + + @Test + void givenPaymentRequest_whenPaymentStatusChangesAfterFewSeconds_thenPaymentShouldBeProcessedSuccessfully() { + + String paymentId = UUID.randomUUID() + .toString(); + // When + mockServerClient.when(request().withMethod("POST") + .withPath("/payment/submit")) + .respond(response().withStatusCode(200) + .withBody("{\"paymentId\": \"%s\"}".formatted(paymentId)) + .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)); + + mockServerClient.when(request().withMethod("GET") + .withPath("/payment/status/%s".formatted(paymentId)), Times.exactly(4)) + .respond(response().withStatusCode(200) + .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .withBody("{\"paymentStatus\": \"%s\"}".formatted(PaymentGatewayResponse.PaymentStatus.PENDING.toString()))); + + mockServerClient.when(request().withMethod("GET") + .withPath("/payment/status/%s".formatted(paymentId))) + .respond(response().withStatusCode(200) + .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .withBody("{\"paymentStatus\": \"%s\"}".formatted(PaymentGatewayResponse.PaymentStatus.AUTHORIZED.toString()))); + + webTestClient.post() + .uri("http://localhost:9000/api/payment/process") + .bodyValue(new PaymentGatewayRequest("4111111111111111", "12", "2025", "USD", 10000, "123")) + .exchange() + .expectStatus() + .isOk() + .expectBody(PaymentGatewayResponse.class) + .value(response -> { + Assertions.assertNotNull(response); + Assertions.assertEquals(PaymentGatewayResponse.PaymentStatus.AUTHORIZED, response.status()); + }); + } + + @AfterEach + void tearDown() { + clientAndServer.stop(); + } + +} \ No newline at end of file