diff --git a/documentation/saga.png b/documentation/saga.png new file mode 100644 index 00000000..4203c685 Binary files /dev/null and b/documentation/saga.png differ diff --git a/order-service/README.md b/order-service/README.md index 4db0ebe4..0dd24a19 100644 --- a/order-service/README.md +++ b/order-service/README.md @@ -31,6 +31,11 @@ curl --location 'http://localhost:9090/orders/create' \ ``` curl --location 'http://localhost:9090/orders/1652' ``` + +## Saga Orchestration +Saga orchestration is a design pattern in distributed systems where car-demo process is broken down into a series of smaller, independent transactions (sagas) that can be orchestrated to ensure consistency and reliability across the entire process. + +![saga.png](..%2Fdocumentation%2Fsaga.png) ## Contributing Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. diff --git a/order-service/pom.xml b/order-service/pom.xml index a90b324e..af6390d7 100644 --- a/order-service/pom.xml +++ b/order-service/pom.xml @@ -193,7 +193,7 @@ nashtech https://sonarcloud.io b47aeba29df2889126c736ee7012a5a490edc34a - ${sonarConfig} + ${sonarConfig} target/coverage-reports/jacoco.exec **/*config*/**, diff --git a/order-service/src/main/java/com.nashtech.order/OrderApplication.java b/order-service/src/main/java/com/nashtech/order/OrderApplication.java similarity index 88% rename from order-service/src/main/java/com.nashtech.order/OrderApplication.java rename to order-service/src/main/java/com/nashtech/order/OrderApplication.java index 4d2fd339..91ba4b23 100644 --- a/order-service/src/main/java/com.nashtech.order/OrderApplication.java +++ b/order-service/src/main/java/com/nashtech/order/OrderApplication.java @@ -1,7 +1,7 @@ package com.nashtech.order; -import com.nashtech.order.commands.CreateOrderCommandInterceptor; -import com.nashtech.order.exception.handler.OrderServiceEventsErrorHandler; +import com.nashtech.order.commands.handler.CreateOrderCommandInterceptor; +import com.nashtech.order.exception.OrderServiceEventsErrorHandler; import org.axonframework.commandhandling.CommandBus; import org.axonframework.config.EventProcessingConfigurer; import org.springframework.beans.factory.annotation.Autowired; diff --git a/order-service/src/main/java/com.nashtech.order/aggregate/OrderAggregate.java b/order-service/src/main/java/com/nashtech/order/aggregate/OrderAggregate.java similarity index 94% rename from order-service/src/main/java/com.nashtech.order/aggregate/OrderAggregate.java rename to order-service/src/main/java/com/nashtech/order/aggregate/OrderAggregate.java index 6a91516a..3ec07c47 100644 --- a/order-service/src/main/java/com.nashtech.order/aggregate/OrderAggregate.java +++ b/order-service/src/main/java/com/nashtech/order/aggregate/OrderAggregate.java @@ -20,13 +20,12 @@ public class OrderAggregate { @AggregateIdentifier private String orderId; - private String productId; - private String userId; - private String paymentId; - private String shipmentId; - private OrderStatus orderStatus; - - private String reasonToRejectedOrder; + String productId; + String userId; + String paymentId; + String shipmentId; + OrderStatus orderStatus; + String reasonToRejectedOrder; public OrderAggregate() { } diff --git a/order-service/src/main/java/com.nashtech.order/commands/ApproveOrderCommand.java b/order-service/src/main/java/com/nashtech/order/commands/ApproveOrderCommand.java similarity index 92% rename from order-service/src/main/java/com.nashtech.order/commands/ApproveOrderCommand.java rename to order-service/src/main/java/com/nashtech/order/commands/ApproveOrderCommand.java index 8fa0fc45..b6ec5180 100644 --- a/order-service/src/main/java/com.nashtech.order/commands/ApproveOrderCommand.java +++ b/order-service/src/main/java/com/nashtech/order/commands/ApproveOrderCommand.java @@ -2,11 +2,11 @@ import com.nashtech.common.utils.OrderStatus; import lombok.Builder; -import lombok.Value; +import lombok.Getter; import org.axonframework.modelling.command.TargetAggregateIdentifier; -@Value @Builder +@Getter public class ApproveOrderCommand { @TargetAggregateIdentifier String orderId; diff --git a/order-service/src/main/java/com.nashtech.order/commands/CreateOrderCommand.java b/order-service/src/main/java/com/nashtech/order/commands/CreateOrderCommand.java similarity index 90% rename from order-service/src/main/java/com.nashtech.order/commands/CreateOrderCommand.java rename to order-service/src/main/java/com/nashtech/order/commands/CreateOrderCommand.java index 2b3e4adf..1707d63a 100644 --- a/order-service/src/main/java/com.nashtech.order/commands/CreateOrderCommand.java +++ b/order-service/src/main/java/com/nashtech/order/commands/CreateOrderCommand.java @@ -1,11 +1,11 @@ package com.nashtech.order.commands; import lombok.Builder; -import lombok.Value; +import lombok.Getter; import org.axonframework.modelling.command.TargetAggregateIdentifier; -@Value @Builder +@Getter public class CreateOrderCommand { @TargetAggregateIdentifier String orderId; diff --git a/order-service/src/main/java/com.nashtech.order/commands/RejectOrderCommand.java b/order-service/src/main/java/com/nashtech/order/commands/RejectOrderCommand.java similarity index 83% rename from order-service/src/main/java/com.nashtech.order/commands/RejectOrderCommand.java rename to order-service/src/main/java/com/nashtech/order/commands/RejectOrderCommand.java index 3d00f1ed..cb2322f4 100644 --- a/order-service/src/main/java/com.nashtech.order/commands/RejectOrderCommand.java +++ b/order-service/src/main/java/com/nashtech/order/commands/RejectOrderCommand.java @@ -2,13 +2,13 @@ import com.nashtech.common.utils.OrderStatus; import lombok.Builder; -import lombok.Value; +import lombok.Getter; import org.axonframework.modelling.command.TargetAggregateIdentifier; import java.util.Date; -@Value @Builder +@Getter public class RejectOrderCommand { @TargetAggregateIdentifier String orderId; @@ -16,8 +16,6 @@ public class RejectOrderCommand { String productId; String paymentId; String shipmentId; - Date timestamp = new Date(); String reasonToFailed; - String errorMessage; OrderStatus orderStatus; } \ No newline at end of file diff --git a/order-service/src/main/java/com.nashtech.order/commands/CreateOrderCommandInterceptor.java b/order-service/src/main/java/com/nashtech/order/commands/handler/CreateOrderCommandInterceptor.java similarity index 94% rename from order-service/src/main/java/com.nashtech.order/commands/CreateOrderCommandInterceptor.java rename to order-service/src/main/java/com/nashtech/order/commands/handler/CreateOrderCommandInterceptor.java index 2632a3df..4cfed918 100644 --- a/order-service/src/main/java/com.nashtech.order/commands/CreateOrderCommandInterceptor.java +++ b/order-service/src/main/java/com/nashtech/order/commands/handler/CreateOrderCommandInterceptor.java @@ -1,5 +1,6 @@ -package com.nashtech.order.commands; +package com.nashtech.order.commands.handler; +import com.nashtech.order.commands.CreateOrderCommand; import com.nashtech.order.repository.OrderLookupRepository; import com.nashtech.order.repository.entity.OrderLookupEntity; import lombok.extern.slf4j.Slf4j; diff --git a/order-service/src/main/java/com.nashtech.order/config/AxonXStreamConfig.java b/order-service/src/main/java/com/nashtech/order/config/AxonXStreamConfig.java similarity index 100% rename from order-service/src/main/java/com.nashtech.order/config/AxonXStreamConfig.java rename to order-service/src/main/java/com/nashtech/order/config/AxonXStreamConfig.java diff --git a/order-service/src/main/java/com.nashtech.order/config/CorsConfig.java b/order-service/src/main/java/com/nashtech/order/config/CorsConfig.java similarity index 100% rename from order-service/src/main/java/com.nashtech.order/config/CorsConfig.java rename to order-service/src/main/java/com/nashtech/order/config/CorsConfig.java diff --git a/order-service/src/main/java/com.nashtech.order/events/OrderApprovedEvent.java b/order-service/src/main/java/com/nashtech/order/events/OrderApprovedEvent.java similarity index 89% rename from order-service/src/main/java/com.nashtech.order/events/OrderApprovedEvent.java rename to order-service/src/main/java/com/nashtech/order/events/OrderApprovedEvent.java index af3ebb66..fdd6cc58 100644 --- a/order-service/src/main/java/com.nashtech.order/events/OrderApprovedEvent.java +++ b/order-service/src/main/java/com/nashtech/order/events/OrderApprovedEvent.java @@ -2,9 +2,9 @@ import com.nashtech.common.utils.OrderStatus; import lombok.Builder; -import lombok.Value; +import lombok.Getter; -@Value +@Getter @Builder public class OrderApprovedEvent { String orderId; diff --git a/order-service/src/main/java/com.nashtech.order/events/OrderCancelledEvent.java b/order-service/src/main/java/com/nashtech/order/events/OrderCancelledEvent.java similarity index 91% rename from order-service/src/main/java/com.nashtech.order/events/OrderCancelledEvent.java rename to order-service/src/main/java/com/nashtech/order/events/OrderCancelledEvent.java index ef9b788a..57cbe6c4 100644 --- a/order-service/src/main/java/com.nashtech.order/events/OrderCancelledEvent.java +++ b/order-service/src/main/java/com/nashtech/order/events/OrderCancelledEvent.java @@ -2,10 +2,10 @@ import com.nashtech.common.utils.OrderStatus; import lombok.Builder; -import lombok.Value; +import lombok.Getter; -@Value @Builder +@Getter public class OrderCancelledEvent { String orderId; String productId; diff --git a/order-service/src/main/java/com.nashtech.order/events/OrderCreatedEvent.java b/order-service/src/main/java/com/nashtech/order/events/OrderCreatedEvent.java similarity index 89% rename from order-service/src/main/java/com.nashtech.order/events/OrderCreatedEvent.java rename to order-service/src/main/java/com/nashtech/order/events/OrderCreatedEvent.java index 9cf671f1..e8bbb80e 100644 --- a/order-service/src/main/java/com.nashtech.order/events/OrderCreatedEvent.java +++ b/order-service/src/main/java/com/nashtech/order/events/OrderCreatedEvent.java @@ -2,9 +2,9 @@ import com.nashtech.common.utils.OrderStatus; import lombok.Builder; -import lombok.Value; +import lombok.Getter; -@Value +@Getter @Builder public class OrderCreatedEvent { String orderId; diff --git a/order-service/src/main/java/com.nashtech.order/exception/CompensateOrder.java b/order-service/src/main/java/com/nashtech/order/exception/CompensateOrder.java similarity index 83% rename from order-service/src/main/java/com.nashtech.order/exception/CompensateOrder.java rename to order-service/src/main/java/com/nashtech/order/exception/CompensateOrder.java index fc2c5d97..174da09c 100644 --- a/order-service/src/main/java/com.nashtech.order/exception/CompensateOrder.java +++ b/order-service/src/main/java/com/nashtech/order/exception/CompensateOrder.java @@ -1,10 +1,12 @@ package com.nashtech.order.exception; -import lombok.Data; +import lombok.Getter; import lombok.NoArgsConstructor; +import lombok.Setter; -@Data @NoArgsConstructor +@Setter +@Getter public class CompensateOrder { private String orderId; private String userId; diff --git a/order-service/src/main/java/com.nashtech.order/exception/ErrorMessage.java b/order-service/src/main/java/com/nashtech/order/exception/ErrorMessage.java similarity index 69% rename from order-service/src/main/java/com.nashtech.order/exception/ErrorMessage.java rename to order-service/src/main/java/com/nashtech/order/exception/ErrorMessage.java index 824cbbd2..27467273 100644 --- a/order-service/src/main/java/com.nashtech.order/exception/ErrorMessage.java +++ b/order-service/src/main/java/com/nashtech/order/exception/ErrorMessage.java @@ -1,14 +1,14 @@ package com.nashtech.order.exception; import lombok.AllArgsConstructor; -import lombok.Data; +import lombok.Getter; import java.util.Date; -@Data @AllArgsConstructor +@Getter public class ErrorMessage { - private final Date timestamp; - private final String message; + private Date timestamp; + private String message; } \ No newline at end of file diff --git a/order-service/src/main/java/com.nashtech.order/exception/handler/OrderServiceErrorHandler.java b/order-service/src/main/java/com/nashtech/order/exception/OrderServiceErrorHandler.java similarity index 96% rename from order-service/src/main/java/com.nashtech.order/exception/handler/OrderServiceErrorHandler.java rename to order-service/src/main/java/com/nashtech/order/exception/OrderServiceErrorHandler.java index fa82cc00..5be769f6 100644 --- a/order-service/src/main/java/com.nashtech.order/exception/handler/OrderServiceErrorHandler.java +++ b/order-service/src/main/java/com/nashtech/order/exception/OrderServiceErrorHandler.java @@ -1,4 +1,4 @@ -package com.nashtech.order.exception.handler; +package com.nashtech.order.exception; import com.nashtech.order.exception.ErrorMessage; import org.springframework.http.HttpHeaders; diff --git a/order-service/src/main/java/com.nashtech.order/exception/handler/OrderServiceEventsErrorHandler.java b/order-service/src/main/java/com/nashtech/order/exception/OrderServiceEventsErrorHandler.java similarity index 93% rename from order-service/src/main/java/com.nashtech.order/exception/handler/OrderServiceEventsErrorHandler.java rename to order-service/src/main/java/com/nashtech/order/exception/OrderServiceEventsErrorHandler.java index 5d27bd70..277b1650 100644 --- a/order-service/src/main/java/com.nashtech.order/exception/handler/OrderServiceEventsErrorHandler.java +++ b/order-service/src/main/java/com/nashtech/order/exception/OrderServiceEventsErrorHandler.java @@ -1,4 +1,4 @@ -package com.nashtech.order.exception.handler; +package com.nashtech.order.exception; import lombok.extern.slf4j.Slf4j; import org.axonframework.eventhandling.EventMessage; diff --git a/order-service/src/main/java/com.nashtech.order/handler/OrderEventsHandler.java b/order-service/src/main/java/com/nashtech/order/handler/OrderEventsHandler.java similarity index 98% rename from order-service/src/main/java/com.nashtech.order/handler/OrderEventsHandler.java rename to order-service/src/main/java/com/nashtech/order/handler/OrderEventsHandler.java index 11d79e7d..1a59c9dc 100644 --- a/order-service/src/main/java/com.nashtech.order/handler/OrderEventsHandler.java +++ b/order-service/src/main/java/com/nashtech/order/handler/OrderEventsHandler.java @@ -37,6 +37,7 @@ public void on(OrderCreatedEvent event) { FailedOrderEntity failedOrder = new FailedOrderEntity(); failedOrder.setOrderId(event.getOrderId()); + failedOrder.setTimestamp(new Date()); failedOrderRepository.save(failedOrder); } diff --git a/order-service/src/main/java/com.nashtech.order/handler/OrderLookupEventsHandler.java b/order-service/src/main/java/com/nashtech/order/handler/OrderLookupEventsHandler.java similarity index 100% rename from order-service/src/main/java/com.nashtech.order/handler/OrderLookupEventsHandler.java rename to order-service/src/main/java/com/nashtech/order/handler/OrderLookupEventsHandler.java diff --git a/order-service/src/main/java/com.nashtech.order/query/FindOrderQuery.java b/order-service/src/main/java/com/nashtech/order/query/FindOrderQuery.java similarity index 88% rename from order-service/src/main/java/com.nashtech.order/query/FindOrderQuery.java rename to order-service/src/main/java/com/nashtech/order/query/FindOrderQuery.java index 65210fc0..6d0fa651 100644 --- a/order-service/src/main/java/com.nashtech.order/query/FindOrderQuery.java +++ b/order-service/src/main/java/com/nashtech/order/query/FindOrderQuery.java @@ -1,12 +1,10 @@ package com.nashtech.order.query; import lombok.AllArgsConstructor; -import lombok.Data; import lombok.NoArgsConstructor; @AllArgsConstructor @NoArgsConstructor -@Data public class FindOrderQuery { String orderId; } diff --git a/order-service/src/main/java/com.nashtech.order/query/FindOrdersByUserQuery.java b/order-service/src/main/java/com/nashtech/order/query/FindOrdersByUserQuery.java similarity index 65% rename from order-service/src/main/java/com.nashtech.order/query/FindOrdersByUserQuery.java rename to order-service/src/main/java/com/nashtech/order/query/FindOrdersByUserQuery.java index 7e11aa26..c52513d0 100644 --- a/order-service/src/main/java/com.nashtech.order/query/FindOrdersByUserQuery.java +++ b/order-service/src/main/java/com/nashtech/order/query/FindOrdersByUserQuery.java @@ -1,12 +1,10 @@ package com.nashtech.order.query; import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; +import lombok.Getter; @AllArgsConstructor -@NoArgsConstructor -@Data +@Getter public class FindOrdersByUserQuery { String userId; } \ No newline at end of file diff --git a/order-service/src/main/java/com.nashtech.order/query/OrderQueriesHandler.java b/order-service/src/main/java/com/nashtech/order/query/handler/OrderQueriesHandler.java similarity index 87% rename from order-service/src/main/java/com.nashtech.order/query/OrderQueriesHandler.java rename to order-service/src/main/java/com/nashtech/order/query/handler/OrderQueriesHandler.java index dd7c0703..931b20e7 100644 --- a/order-service/src/main/java/com.nashtech.order/query/OrderQueriesHandler.java +++ b/order-service/src/main/java/com/nashtech/order/query/handler/OrderQueriesHandler.java @@ -1,9 +1,11 @@ -package com.nashtech.order.query; +package com.nashtech.order.query.handler; +import com.nashtech.order.query.FindOrdersByUserQuery; import com.nashtech.order.repository.OrderRepository; import com.nashtech.order.repository.entity.OrderEntity; import org.axonframework.queryhandling.QueryHandler; import org.springframework.stereotype.Component; + import java.util.List; @Component diff --git a/order-service/src/main/java/com.nashtech.order/repository/FailedOrderRepository.java b/order-service/src/main/java/com/nashtech/order/repository/FailedOrderRepository.java similarity index 100% rename from order-service/src/main/java/com.nashtech.order/repository/FailedOrderRepository.java rename to order-service/src/main/java/com/nashtech/order/repository/FailedOrderRepository.java diff --git a/order-service/src/main/java/com.nashtech.order/repository/OrderLookupRepository.java b/order-service/src/main/java/com/nashtech/order/repository/OrderLookupRepository.java similarity index 100% rename from order-service/src/main/java/com.nashtech.order/repository/OrderLookupRepository.java rename to order-service/src/main/java/com/nashtech/order/repository/OrderLookupRepository.java diff --git a/order-service/src/main/java/com.nashtech.order/repository/OrderRepository.java b/order-service/src/main/java/com/nashtech/order/repository/OrderRepository.java similarity index 100% rename from order-service/src/main/java/com.nashtech.order/repository/OrderRepository.java rename to order-service/src/main/java/com/nashtech/order/repository/OrderRepository.java diff --git a/order-service/src/main/java/com.nashtech.order/repository/entity/FailedOrderEntity.java b/order-service/src/main/java/com/nashtech/order/repository/entity/FailedOrderEntity.java similarity index 88% rename from order-service/src/main/java/com.nashtech.order/repository/entity/FailedOrderEntity.java rename to order-service/src/main/java/com/nashtech/order/repository/entity/FailedOrderEntity.java index aa6e9717..a6e91939 100644 --- a/order-service/src/main/java/com.nashtech.order/repository/entity/FailedOrderEntity.java +++ b/order-service/src/main/java/com/nashtech/order/repository/entity/FailedOrderEntity.java @@ -3,12 +3,12 @@ import jakarta.persistence.Entity; import jakarta.persistence.Id; import lombok.AllArgsConstructor; -import lombok.Data; import lombok.NoArgsConstructor; +import lombok.Setter; import java.util.Date; -@Data +@Setter @NoArgsConstructor @AllArgsConstructor @Entity(name = "failed_orders") @@ -19,7 +19,7 @@ public class FailedOrderEntity { private String productId; private String paymentId; private String shipmentId; - private Date timestamp = new Date(); + private Date timestamp; private String reasonToFailed; private String orderStatus; } diff --git a/order-service/src/main/java/com.nashtech.order/repository/entity/OrderEntity.java b/order-service/src/main/java/com/nashtech/order/repository/entity/OrderEntity.java similarity index 92% rename from order-service/src/main/java/com.nashtech.order/repository/entity/OrderEntity.java rename to order-service/src/main/java/com/nashtech/order/repository/entity/OrderEntity.java index 35f98135..22e399bf 100644 --- a/order-service/src/main/java/com.nashtech.order/repository/entity/OrderEntity.java +++ b/order-service/src/main/java/com/nashtech/order/repository/entity/OrderEntity.java @@ -20,6 +20,6 @@ public class OrderEntity { private String productId; private String paymentId; private String shipmentId; - private Date timestamp = new Date(); + private Date timestamp; private String orderStatus; } \ No newline at end of file diff --git a/order-service/src/main/java/com.nashtech.order/repository/entity/OrderLookupEntity.java b/order-service/src/main/java/com/nashtech/order/repository/entity/OrderLookupEntity.java similarity index 93% rename from order-service/src/main/java/com.nashtech.order/repository/entity/OrderLookupEntity.java rename to order-service/src/main/java/com/nashtech/order/repository/entity/OrderLookupEntity.java index 65b725b1..4a3905c5 100644 --- a/order-service/src/main/java/com.nashtech.order/repository/entity/OrderLookupEntity.java +++ b/order-service/src/main/java/com/nashtech/order/repository/entity/OrderLookupEntity.java @@ -3,13 +3,11 @@ import jakarta.persistence.Entity; import jakarta.persistence.Id; import lombok.AllArgsConstructor; -import lombok.Data; import lombok.NoArgsConstructor; import java.util.Date; -@Data @NoArgsConstructor @AllArgsConstructor @Entity diff --git a/order-service/src/main/java/com.nashtech.order/restapi/OrdersCommandController.java b/order-service/src/main/java/com/nashtech/order/restapi/OrdersCommandController.java similarity index 87% rename from order-service/src/main/java/com.nashtech.order/restapi/OrdersCommandController.java rename to order-service/src/main/java/com/nashtech/order/restapi/OrdersCommandController.java index f5a6af09..ba8afc02 100644 --- a/order-service/src/main/java/com.nashtech.order/restapi/OrdersCommandController.java +++ b/order-service/src/main/java/com/nashtech/order/restapi/OrdersCommandController.java @@ -18,6 +18,7 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; + import javax.validation.Valid; import java.util.List; import java.util.UUID; @@ -46,13 +47,6 @@ public OrderSummary createOrder(@Valid @RequestBody OrderCreateRequest orderRequ .quantity(orderRequest.getQuantity()).orderId(orderId) .build(); -/* try (SubscriptionQueryResult queryResult = queryGateway.subscriptionQuery( - new FindOrderQuery(orderId), ResponseTypes.instanceOf(OrderSummary.class), - ResponseTypes.instanceOf(OrderSummary.class))) { - commandGateway.send(createOrderCommand); - return queryResult.updates().blockFirst(); - }*/ - commandGateway.send(createOrderCommand); return OrderSummary.builder() .orderId(orderId) diff --git a/order-service/src/main/java/com.nashtech.order/restapi/request/OrderCreateRequest.java b/order-service/src/main/java/com/nashtech/order/restapi/request/OrderCreateRequest.java similarity index 95% rename from order-service/src/main/java/com.nashtech.order/restapi/request/OrderCreateRequest.java rename to order-service/src/main/java/com/nashtech/order/restapi/request/OrderCreateRequest.java index 3df1c022..e263734b 100644 --- a/order-service/src/main/java/com.nashtech.order/restapi/request/OrderCreateRequest.java +++ b/order-service/src/main/java/com/nashtech/order/restapi/request/OrderCreateRequest.java @@ -2,13 +2,13 @@ import lombok.AllArgsConstructor; import lombok.Builder; -import lombok.Data; +import lombok.Getter; import lombok.NoArgsConstructor; import javax.validation.constraints.Min; import javax.validation.constraints.NotBlank; -@Data +@Getter @Builder @AllArgsConstructor @NoArgsConstructor diff --git a/order-service/src/main/java/com.nashtech.order/restapi/response/OrderSummary.java b/order-service/src/main/java/com/nashtech/order/restapi/response/OrderSummary.java similarity index 59% rename from order-service/src/main/java/com.nashtech.order/restapi/response/OrderSummary.java rename to order-service/src/main/java/com/nashtech/order/restapi/response/OrderSummary.java index 2c78a6d4..035cfd4f 100644 --- a/order-service/src/main/java/com.nashtech.order/restapi/response/OrderSummary.java +++ b/order-service/src/main/java/com/nashtech/order/restapi/response/OrderSummary.java @@ -1,10 +1,14 @@ package com.nashtech.order.restapi.response; +import lombok.AllArgsConstructor; import lombok.Builder; -import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; -@Data @Builder +@NoArgsConstructor +@AllArgsConstructor +@Getter public class OrderSummary { private String orderId; private String orderStatus; diff --git a/order-service/src/main/java/com.nashtech.order/saga/OrderSaga.java b/order-service/src/main/java/com/nashtech/order/saga/OrderSaga.java similarity index 90% rename from order-service/src/main/java/com.nashtech.order/saga/OrderSaga.java rename to order-service/src/main/java/com/nashtech/order/saga/OrderSaga.java index 63e55173..9daa8a2a 100644 --- a/order-service/src/main/java/com.nashtech.order/saga/OrderSaga.java +++ b/order-service/src/main/java/com/nashtech/order/saga/OrderSaga.java @@ -1,7 +1,16 @@ package com.nashtech.order.saga; -import com.nashtech.common.command.*; -import com.nashtech.common.event.*; +import com.nashtech.common.command.CancelPaymentCommand; +import com.nashtech.common.command.CancelProductReserveCommand; +import com.nashtech.common.command.CreateShipmentCommand; +import com.nashtech.common.command.ProcessPaymentCommand; +import com.nashtech.common.command.ReserveProductCommand; +import com.nashtech.common.event.OrderShippedEvent; +import com.nashtech.common.event.PaymentApprovedEvent; +import com.nashtech.common.event.PaymentCancelledEvent; +import com.nashtech.common.event.ProductReserveFailedEvent; +import com.nashtech.common.event.ProductReservedEvent; +import com.nashtech.common.event.ShipmentCancelledEvent; import com.nashtech.common.utils.OderFailure; import com.nashtech.common.utils.OrderStatus; import com.nashtech.order.commands.ApproveOrderCommand; @@ -18,7 +27,6 @@ import org.axonframework.modelling.saga.EndSaga; import org.axonframework.modelling.saga.SagaEventHandler; import org.axonframework.modelling.saga.StartSaga; -import org.axonframework.queryhandling.QueryGateway; import org.axonframework.queryhandling.QueryUpdateEmitter; import org.axonframework.spring.stereotype.Saga; import org.springframework.beans.factory.annotation.Autowired; @@ -30,18 +38,19 @@ @Slf4j public class OrderSaga { - @Autowired - private transient CommandGateway commandGateway; + private final CommandGateway commandGateway; - @Autowired - private transient QueryGateway queryGateway; + private final QueryUpdateEmitter queryUpdateEmitter; @Autowired - private transient QueryUpdateEmitter queryUpdateEmitter; + public OrderSaga(CommandGateway commandGateway,QueryUpdateEmitter queryUpdateEmitter) { + this.commandGateway = commandGateway; + this.queryUpdateEmitter = queryUpdateEmitter; + } @StartSaga @SagaEventHandler(associationProperty = "orderId") - private void handle(OrderCreatedEvent orderCreatedEvent) { + public void handle(OrderCreatedEvent orderCreatedEvent) { log.info("Order Saga started for Order Id : {}", orderCreatedEvent.getOrderId()); ReserveProductCommand reserveProductCommand = ReserveProductCommand.builder() @@ -110,7 +119,7 @@ public void handle(ProductReservedEvent productReservedEvent) { } @SagaEventHandler(associationProperty = "orderId") - private void handle(ProductReserveFailedEvent productReserveFailedEvent) { + public void handle(ProductReserveFailedEvent productReserveFailedEvent) { // Start the compensating transaction log.info("ProductReserveFailedEvent started for orderId : {}", productReserveFailedEvent.getOrderId()); CompensateOrder compensateOrder = new CompensateOrder(); @@ -122,7 +131,7 @@ private void handle(ProductReserveFailedEvent productReserveFailedEvent) { } @SagaEventHandler(associationProperty = "orderId") - private void handle(PaymentApprovedEvent paymentApprovedEvent) { + public void handle(PaymentApprovedEvent paymentApprovedEvent) { log.info("PaymentProcessedEvent started for paymentId : {}", paymentApprovedEvent.getPaymentId()); CreateShipmentCommand createShipmentCommand = CreateShipmentCommand.builder() @@ -160,7 +169,7 @@ private void handle(PaymentApprovedEvent paymentApprovedEvent) { } @SagaEventHandler(associationProperty = "orderId") - private void handle(PaymentCancelledEvent paymentCancelledEvent) { + public void handle(PaymentCancelledEvent paymentCancelledEvent) { log.info("Payment is not approved!"); // Start a compensating transaction CancelProductReserveCommand cancelProductReserveCommand = CancelProductReserveCommand.builder() @@ -195,7 +204,7 @@ public void handle(OrderShippedEvent orderShippedEvent) { } @SagaEventHandler(associationProperty = "orderId") - private void handle(ShipmentCancelledEvent shipmentCancelledEvent) { + public void handle(ShipmentCancelledEvent shipmentCancelledEvent) { log.info("Start a compensating transaction from Shipment"); // Start a compensating transaction CancelPaymentCommand cancelPaymentCommand = CancelPaymentCommand.builder() @@ -231,7 +240,7 @@ private void handle(ShipmentCancelledEvent shipmentCancelledEvent) { @SagaEventHandler(associationProperty = "orderId") @EndSaga - public void on(OrderApprovedEvent event) { + public void handle(OrderApprovedEvent event) { log.info("Order is approved. Order Saga is complete for orderId: " + event.getOrderId()); OrderSummary orderSummary = OrderSummary.builder() .orderId(event.getOrderId()) @@ -267,6 +276,7 @@ private void orderRejectedCommand(CompensateOrder compensateOrder) { commandGateway.send(rejectOrderCommand); } + @SuppressWarnings("java:S3740") private String simplifyErrorMessage(CommandResultMessage commandResultMessage, OderFailure errorMessage) { return commandResultMessage.exceptionResult().getMessage() != null && commandResultMessage.exceptionResult().getMessage().contains("No Handler for command:")? diff --git a/order-service/src/test/java/com/nashtech/order/OrderApplicationTest.java b/order-service/src/test/java/com/nashtech/order/OrderApplicationTest.java new file mode 100644 index 00000000..f6ec5f07 --- /dev/null +++ b/order-service/src/test/java/com/nashtech/order/OrderApplicationTest.java @@ -0,0 +1,35 @@ +package com.nashtech.order; + +import com.nashtech.order.commands.handler.CreateOrderCommandInterceptor; +import com.nashtech.order.repository.OrderLookupRepository; +import org.axonframework.commandhandling.CommandBus; +import org.axonframework.test.utils.RecordingCommandBus; +import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +import static org.mockito.Mockito.*; + +public class OrderApplicationTest { + /** + * Method under test: + * {@link OrderApplication#registerCreateProductCommandInterceptor(ApplicationContext, CommandBus)} + */ + @Test + public void testRegisterCreateProductCommandInterceptor() throws BeansException { + // Arrange + OrderApplication orderApplication = new OrderApplication(); + AnnotationConfigApplicationContext context = mock(AnnotationConfigApplicationContext.class); + when(context.getBean(Mockito.>any())) + .thenReturn(new CreateOrderCommandInterceptor(mock(OrderLookupRepository.class))); + + // Act + orderApplication.registerCreateProductCommandInterceptor(context, new RecordingCommandBus()); + + // Assert that nothing has changed + verify(context).getBean(Mockito.>any()); + } + +} diff --git a/order-service/src/test/java/com/nashtech/order/OrderEventsHandlerTest.java b/order-service/src/test/java/com/nashtech/order/OrderEventsHandlerTest.java deleted file mode 100644 index 07bc2305..00000000 --- a/order-service/src/test/java/com/nashtech/order/OrderEventsHandlerTest.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.nashtech.order; - -import com.nashtech.common.utils.OrderStatus; -import com.nashtech.order.events.OrderApprovedEvent; -import com.nashtech.order.events.OrderCancelledEvent; -import com.nashtech.order.events.OrderCreatedEvent; -import com.nashtech.order.handler.OrderEventsHandler; -import com.nashtech.order.repository.FailedOrderRepository; -import com.nashtech.order.repository.OrderRepository; -import com.nashtech.order.repository.entity.OrderEntity; -import org.junit.Before; -import org.junit.Test; - -import java.util.Date; -import java.util.UUID; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class OrderEventsHandlerTest { - private static final String ORDER_ID = UUID.randomUUID().toString(); - private static final String PRODUCT_ID = UUID.randomUUID().toString(); - private static final String PAYMENT_ID = UUID.randomUUID().toString(); - private static final String SHIPMENT_ID = UUID.randomUUID().toString(); - private static final String USER_ID = "1652"; - - - private OrderRepository orderRepository = mock(OrderRepository.class); - - private FailedOrderRepository failedOrderRepository = mock(FailedOrderRepository.class); - - // @Autowired - private OrderEventsHandler handler; - - @Before - public void setUp() { - handler = new OrderEventsHandler(orderRepository, failedOrderRepository); - } - - - @Test - public void testOrderCreatedEventHandler() { - OrderCreatedEvent orderCreatedEvent = OrderCreatedEvent.builder() - .orderId(ORDER_ID) - .productId(PRODUCT_ID) - .quantity(2) - .userId(USER_ID) - .orderStatus(OrderStatus.ORDER_PLACED) - .build(); - handler.on(orderCreatedEvent); - } - - @Test - public void testOrderApprovedEventHandler() { - OrderApprovedEvent orderApprovedEvent = OrderApprovedEvent.builder() - .orderId(ORDER_ID) - .paymentId(PAYMENT_ID) - .shipmentId(SHIPMENT_ID) - .orderStatus(OrderStatus.ORDER_PLACED) - .build(); - - OrderEntity order = new OrderEntity(orderApprovedEvent.getOrderId(), USER_ID, PRODUCT_ID, - null, null, new Date(), orderApprovedEvent.getOrderStatus().toString()); - - when(orderRepository.findByOrderId(ORDER_ID)).thenReturn(order); - - handler.on(orderApprovedEvent); - } - - @Test - public void testOrderCancelledEventHandler() { - OrderCancelledEvent orderCancelledEvent = OrderCancelledEvent.builder() - .orderId(ORDER_ID) - .userId(USER_ID) - .productId(PRODUCT_ID) - .reasonToFailed("Car quantity not sufficient in inventory") - .orderStatus(OrderStatus.ORDER_NOT_APPROVED) - .build(); - handler.on(orderCancelledEvent); - } -} diff --git a/order-service/src/test/java/com/nashtech/order/OrderQueriesHandlerTest.java b/order-service/src/test/java/com/nashtech/order/OrderQueriesHandlerTest.java deleted file mode 100644 index c5832dc6..00000000 --- a/order-service/src/test/java/com/nashtech/order/OrderQueriesHandlerTest.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.nashtech.order; - -import com.nashtech.common.utils.OrderStatus; -import com.nashtech.order.query.FindOrdersByUserQuery; -import com.nashtech.order.query.OrderQueriesHandler; -import com.nashtech.order.repository.OrderRepository; -import com.nashtech.order.repository.entity.OrderEntity; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.UUID; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class OrderQueriesHandlerTest { - - private static final String ORDER_ID1 = UUID.randomUUID().toString(); - private static final String ORDER_ID2 = UUID.randomUUID().toString(); - private static final String PRODUCT_ID = UUID.randomUUID().toString(); - private static final String USER_ID = "1652"; - - private final OrderRepository orderRepository = mock(OrderRepository.class); - - private OrderQueriesHandler queriesHandler; - - @Before - public void setUp() { - queriesHandler = new OrderQueriesHandler(orderRepository); - } - - @Test - public void testOrderCreatedEventHandler() { - FindOrdersByUserQuery findOrdersByUserQuery = new FindOrdersByUserQuery(USER_ID); - - List orderEntityList = new ArrayList<>(); - OrderEntity order1 = new OrderEntity(ORDER_ID1, USER_ID, PRODUCT_ID, - null, null, new Date(), OrderStatus.ORDER_APPROVED.toString()); - orderEntityList.add(order1); - OrderEntity order2= new OrderEntity(ORDER_ID2, USER_ID, PRODUCT_ID, - null, null, new Date(), OrderStatus.ORDER_APPROVED.toString()); - orderEntityList.add(order2); - when(orderRepository.findByUserId(USER_ID)).thenReturn(orderEntityList); - - List orders = queriesHandler.findOrders(findOrdersByUserQuery); - - Assert.assertEquals(2,orders.size()); - Assert.assertEquals(ORDER_ID1,orders.get(0).getOrderId()); - Assert.assertEquals(ORDER_ID2,orders.get(1).getOrderId()); - } -} diff --git a/order-service/src/test/java/com/nashtech/order/OrderSagaTest.java b/order-service/src/test/java/com/nashtech/order/OrderSagaTest.java deleted file mode 100644 index 4d489169..00000000 --- a/order-service/src/test/java/com/nashtech/order/OrderSagaTest.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.nashtech.order; - -import com.nashtech.common.event.ProductReservedEvent; -import com.nashtech.common.utils.OrderStatus; -import com.nashtech.order.events.OrderCreatedEvent; -import com.nashtech.order.saga.OrderSaga; -import org.axonframework.test.matchers.Matchers; -import org.axonframework.test.saga.FixtureConfiguration; -import org.axonframework.test.saga.SagaTestFixture; -import org.junit.Test; - -import java.time.Duration; -import java.util.UUID; - -public class OrderSagaTest { - private static final String ORDER_ID = UUID.randomUUID().toString(); - private static final String PRODUCT_ID = UUID.randomUUID().toString(); - private static final String PAYMENT_ID = UUID.randomUUID().toString(); - private static final String SHIPMENT_ID = UUID.randomUUID().toString(); - private static final String USER_ID = "1652"; - private static final FixtureConfiguration fixture = new SagaTestFixture<>(OrderSaga.class); - - @Test - public void testOrderCreatedEvent() { - OrderCreatedEvent orderCreatedEvent = OrderCreatedEvent.builder() - .orderId(ORDER_ID) - .productId(PRODUCT_ID) - .quantity(2) - .userId(USER_ID) - .orderStatus(OrderStatus.ORDER_PARTIALLY_APPROVED) - .build(); - - fixture.givenAggregate(ORDER_ID) - .published(orderCreatedEvent) - .whenTimeElapses(Duration.ofDays(31)) - .expectDispatchedCommandsMatching(Matchers.listWithAllOf()); - } - - @Test - public void testProductReservedEvent() { - ProductReservedEvent productReservedEvent = ProductReservedEvent.builder() - .orderId(ORDER_ID) - .productId(PRODUCT_ID) - .userId(USER_ID) - .quantity(2) - .brand("Tata") - .basePrice(30000.0) - .tax(0.03f) - .totalTax(0.06f) - .subTotal(60000.0) - .total(60000.06) - .model("model") - .mileage(12d) - .color("Red") - .year(2024) - .build(); - - fixture.givenAggregate(ORDER_ID) - .published(productReservedEvent) - .whenTimeElapses(Duration.ofDays(31)) - .expectDispatchedCommandsMatching(Matchers.listWithAllOf()); - } -} diff --git a/order-service/src/test/java/com/nashtech/order/OrderAggregateTest.java b/order-service/src/test/java/com/nashtech/order/aggregate/OrderAggregateTest.java similarity index 98% rename from order-service/src/test/java/com/nashtech/order/OrderAggregateTest.java rename to order-service/src/test/java/com/nashtech/order/aggregate/OrderAggregateTest.java index 244dcb00..1c28e44b 100644 --- a/order-service/src/test/java/com/nashtech/order/OrderAggregateTest.java +++ b/order-service/src/test/java/com/nashtech/order/aggregate/OrderAggregateTest.java @@ -1,7 +1,6 @@ -package com.nashtech.order; +package com.nashtech.order.aggregate; import com.nashtech.common.utils.OrderStatus; -import com.nashtech.order.aggregate.OrderAggregate; import com.nashtech.order.commands.ApproveOrderCommand; import com.nashtech.order.commands.CreateOrderCommand; import com.nashtech.order.commands.RejectOrderCommand; diff --git a/order-service/src/test/java/com/nashtech/order/commands/CreateOrderCommandInterceptorTest.java b/order-service/src/test/java/com/nashtech/order/commands/CreateOrderCommandInterceptorTest.java new file mode 100644 index 00000000..d79f5aaa --- /dev/null +++ b/order-service/src/test/java/com/nashtech/order/commands/CreateOrderCommandInterceptorTest.java @@ -0,0 +1,57 @@ +package com.nashtech.order.commands; + +import com.nashtech.order.commands.handler.CreateOrderCommandInterceptor; +import com.nashtech.order.repository.OrderLookupRepository; +import io.axoniq.axonserver.grpc.command.Command; +import org.axonframework.axonserver.connector.command.GrpcBackedCommandMessage; +import org.axonframework.axonserver.connector.event.axon.GrpcMetaDataAwareSerializer; +import org.axonframework.commandhandling.CommandMessage; +import org.axonframework.serialization.SerializedType; +import org.axonframework.serialization.Serializer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import java.util.ArrayList; +import java.util.List; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ContextConfiguration(classes = {CreateOrderCommandInterceptor.class}) +@ExtendWith(SpringExtension.class) +class CreateOrderCommandInterceptorTest { + @Autowired + private CreateOrderCommandInterceptor createOrderCommandInterceptor; + + @MockBean + private OrderLookupRepository orderLookupRepository; + + /** + * Method under test: {@link CreateOrderCommandInterceptor#handle(List)} + */ + @Test + void testCreateOrderCommandInterceptorHandle() { + // Arrange + Serializer delegate = mock(Serializer.class); + Class forNameResult = Object.class; + when(delegate.classForType(Mockito.any())).thenReturn(forNameResult); + GrpcMetaDataAwareSerializer serializer = new GrpcMetaDataAwareSerializer(delegate); + GrpcBackedCommandMessage grpcBackedCommandMessage = new GrpcBackedCommandMessage<>(Command.getDefaultInstance(), + serializer); + + ArrayList> messages = new ArrayList<>(); + messages.add(grpcBackedCommandMessage); + + // Act + createOrderCommandInterceptor.handle(messages); + + // Assert + verify(delegate).classForType(Mockito.any()); + } +} diff --git a/order-service/src/test/java/com/nashtech/order/config/AxonXStreamConfigTest.java b/order-service/src/test/java/com/nashtech/order/config/AxonXStreamConfigTest.java new file mode 100644 index 00000000..28a65a8a --- /dev/null +++ b/order-service/src/test/java/com/nashtech/order/config/AxonXStreamConfigTest.java @@ -0,0 +1,24 @@ +package com.nashtech.order.config; + +import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.converters.reflection.SunUnsafeReflectionProvider; +import com.thoughtworks.xstream.mapper.CachingMapper; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +class AxonXStreamConfigTest { + /** + * Method under test: {@link AxonXStreamConfig#xStream()} + */ + @Test + void testXStream() { + XStream actualXStreamResult = (new AxonXStreamConfig()).xStream(); + // Assert + assertInstanceOf(SunUnsafeReflectionProvider.class, actualXStreamResult.getReflectionProvider()); + assertInstanceOf(CachingMapper.class, actualXStreamResult.getMapper()); + assertNotNull(actualXStreamResult.getClassLoader()); + assertNotNull(actualXStreamResult.getClassLoaderReference().getReference()); + } +} diff --git a/order-service/src/test/java/com/nashtech/order/config/CorsConfigTest.java b/order-service/src/test/java/com/nashtech/order/config/CorsConfigTest.java new file mode 100644 index 00000000..1494b4c7 --- /dev/null +++ b/order-service/src/test/java/com/nashtech/order/config/CorsConfigTest.java @@ -0,0 +1,28 @@ +package com.nashtech.order.config; + +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.web.servlet.config.annotation.CorsRegistration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +class CorsConfigTest { + /** + * Method under test: {@link CorsConfig#addCorsMappings(CorsRegistry)} + */ + @Test + void testAddCorsMappings() { + CorsConfig corsConfig = new CorsConfig(); + CorsRegistry registry = mock(CorsRegistry.class); + when(registry.addMapping(Mockito.any())).thenReturn(new CorsRegistration("Path Pattern")); + + // Act + corsConfig.addCorsMappings(registry); + + // Assert that nothing has changed + verify(registry).addMapping(Mockito.any()); + } +} diff --git a/order-service/src/test/java/com/nashtech/order/exception/OrderServiceErrorHandlerTest.java b/order-service/src/test/java/com/nashtech/order/exception/OrderServiceErrorHandlerTest.java new file mode 100644 index 00000000..7a073d4f --- /dev/null +++ b/order-service/src/test/java/com/nashtech/order/exception/OrderServiceErrorHandlerTest.java @@ -0,0 +1,80 @@ +package com.nashtech.order.exception; + +import org.junit.Test; +import org.springframework.http.ResponseEntity; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.web.context.request.ServletWebRequest; +import org.springframework.web.context.request.WebRequest; + +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class OrderServiceErrorHandlerTest { + /** + * Method under test: + * {@link OrderServiceErrorHandler#handleIllegalStateException(IllegalStateException, WebRequest)} + */ + @Test + public void testHandleIllegalStateException() { + // Arrange + OrderServiceErrorHandler orderServiceErrorHandler = new OrderServiceErrorHandler(); + IllegalStateException ex = new IllegalStateException("foo"); + + // Act + ResponseEntity actualHandleIllegalStateExceptionResult = orderServiceErrorHandler + .handleIllegalStateException(ex, new ServletWebRequest(new MockHttpServletRequest())); + + // Assert + assertEquals("foo", ((ErrorMessage) actualHandleIllegalStateExceptionResult.getBody()).getMessage()); + + Date current = new Date(); + Date expectedDate = ((ErrorMessage) actualHandleIllegalStateExceptionResult.getBody()).getTimestamp(); + assertDate(current,expectedDate); + + assertEquals(500, actualHandleIllegalStateExceptionResult.getStatusCodeValue()); + assertTrue(actualHandleIllegalStateExceptionResult.hasBody()); + assertTrue(actualHandleIllegalStateExceptionResult.getHeaders().isEmpty()); + } + + /** + * Method under test: + * {@link OrderServiceErrorHandler#handleOtherExceptions(Exception, WebRequest)} + */ + @Test + public void testHandleOtherExceptions() { + // Arrange + OrderServiceErrorHandler orderServiceErrorHandler = new OrderServiceErrorHandler(); + Exception ex = new Exception("foo"); + + // Act + ResponseEntity actualHandleOtherExceptionsResult = orderServiceErrorHandler.handleOtherExceptions(ex, + new ServletWebRequest(new MockHttpServletRequest())); + + // Assert + assertEquals("foo", ((ErrorMessage) actualHandleOtherExceptionsResult.getBody()).getMessage()); + + Date current = new Date(); + Date expectedDate = ((ErrorMessage) actualHandleOtherExceptionsResult.getBody()).getTimestamp(); + assertDate(current,expectedDate); + + assertEquals(500, actualHandleOtherExceptionsResult.getStatusCodeValue()); + assertTrue(actualHandleOtherExceptionsResult.hasBody()); + assertTrue(actualHandleOtherExceptionsResult.getHeaders().isEmpty()); + } + + public void assertDate(Date currentDate, Date expectedDate) { + Calendar currentCal = new GregorianCalendar(); + currentCal.setTime(currentDate); + + Calendar expectedCal = new GregorianCalendar(); + currentCal.setTime(expectedDate); + + assertEquals("month", expectedCal.get(Calendar.MONTH), currentCal.get(Calendar.MONTH)); + assertEquals("day", expectedCal.get(Calendar.DATE), currentCal.get(Calendar.DATE)); + assertEquals("year", expectedCal.get(Calendar.YEAR), currentCal.get(Calendar.YEAR)); + } +} diff --git a/order-service/src/test/java/com/nashtech/order/handler/OrderEventsHandlerTest.java b/order-service/src/test/java/com/nashtech/order/handler/OrderEventsHandlerTest.java new file mode 100644 index 00000000..6a223df2 --- /dev/null +++ b/order-service/src/test/java/com/nashtech/order/handler/OrderEventsHandlerTest.java @@ -0,0 +1,163 @@ +package com.nashtech.order.handler; + +import com.nashtech.common.utils.OrderStatus; +import com.nashtech.order.events.OrderApprovedEvent; +import com.nashtech.order.events.OrderCancelledEvent; +import com.nashtech.order.events.OrderCreatedEvent; +import com.nashtech.order.repository.FailedOrderRepository; +import com.nashtech.order.repository.OrderRepository; +import com.nashtech.order.repository.entity.FailedOrderEntity; +import com.nashtech.order.repository.entity.OrderEntity; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import java.time.LocalDate; +import java.time.ZoneOffset; +import java.util.Date; +import java.util.Optional; + +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ContextConfiguration(classes = {OrderEventsHandler.class}) +@ExtendWith(SpringExtension.class) +class OrderEventsHandlerTest { + @MockBean + private FailedOrderRepository failedOrderRepository; + + @Autowired + private OrderEventsHandler orderEventsHandler; + + @MockBean + private OrderRepository orderRepository; + + /** + * Method under test: {@link OrderEventsHandler#on(OrderApprovedEvent)} + */ + @Test + void testOrderApprovedEventHandler() { + // Arrange + OrderEntity orderEntity = new OrderEntity(); + orderEntity.setOrderId("42"); + orderEntity.setOrderStatus("Order Status"); + orderEntity.setPaymentId("42"); + orderEntity.setProductId("42"); + orderEntity.setShipmentId("42"); + orderEntity.setTimestamp(Date.from(LocalDate.of(1970, 1, 1).atStartOfDay().atZone(ZoneOffset.UTC).toInstant())); + orderEntity.setUserId("42"); + + OrderEntity orderEntity2 = new OrderEntity(); + orderEntity2.setOrderId("42"); + orderEntity2.setOrderStatus("Order Status"); + orderEntity2.setPaymentId("42"); + orderEntity2.setProductId("42"); + orderEntity2.setShipmentId("42"); + orderEntity2.setTimestamp(Date.from(LocalDate.of(1970, 1, 1).atStartOfDay().atZone(ZoneOffset.UTC).toInstant())); + orderEntity2.setUserId("42"); + when(orderRepository.save(Mockito.any())).thenReturn(orderEntity2); + when(orderRepository.findByOrderId(Mockito.any())).thenReturn(orderEntity); + doNothing().when(failedOrderRepository).delete(Mockito.any()); + OrderApprovedEvent orderApprovedEvent = OrderApprovedEvent.builder() + .orderId("42") + .orderStatus(OrderStatus.ORDER_NOT_APPROVED) + .paymentId("42") + .shipmentId("42") + .build(); + + // Act + orderEventsHandler.on(orderApprovedEvent); + + // Assert + verify(orderRepository).findByOrderId(Mockito.any()); + verify(failedOrderRepository).delete(Mockito.any()); + verify(orderRepository).save(Mockito.any()); + } + + /** + * Method under test: {@link OrderEventsHandler#on(OrderCancelledEvent)} + */ + @Test + void testOrderCancelledEventHandler() { + // Arrange + Optional emptyResult = Optional.empty(); + when(failedOrderRepository.findById(Mockito.any())).thenReturn(emptyResult); + OrderCancelledEvent event = OrderCancelledEvent.builder() + .orderId("42") + .orderStatus(OrderStatus.ORDER_NOT_APPROVED) + .paymentId("42") + .productId("42") + .reasonToFailed("Just cause") + .shipmentId("42") + .userId("42") + .build(); + + // Act + orderEventsHandler.on(event); + + // Assert that nothing has changed + verify(failedOrderRepository).findById(Mockito.any()); + } + + /** + * Method under test: {@link OrderEventsHandler#on(OrderCreatedEvent)} + */ + @Test + void testOrderCreatedEventHandler() { + // Arrange + OrderEntity orderEntity = new OrderEntity(); + orderEntity.setOrderId("42"); + orderEntity.setOrderStatus("Order Status"); + orderEntity.setPaymentId("42"); + orderEntity.setProductId("42"); + orderEntity.setShipmentId("42"); + orderEntity.setTimestamp(Date.from(LocalDate.of(1970, 1, 1).atStartOfDay().atZone(ZoneOffset.UTC).toInstant())); + orderEntity.setUserId("42"); + when(orderRepository.save(Mockito.any())).thenReturn(orderEntity); + + FailedOrderEntity failedOrderEntity = new FailedOrderEntity(); + failedOrderEntity.setOrderId("42"); + failedOrderEntity.setOrderStatus("Order Status"); + failedOrderEntity.setPaymentId("42"); + failedOrderEntity.setProductId("42"); + failedOrderEntity.setReasonToFailed("Just cause"); + failedOrderEntity.setShipmentId("42"); + failedOrderEntity + .setTimestamp(Date.from(LocalDate.of(1970, 1, 1).atStartOfDay().atZone(ZoneOffset.UTC).toInstant())); + failedOrderEntity.setUserId("42"); + when(failedOrderRepository.save(Mockito.any())).thenReturn(failedOrderEntity); + OrderCreatedEvent event = OrderCreatedEvent.builder() + .orderId("42") + .orderStatus(OrderStatus.ORDER_NOT_APPROVED) + .productId("42") + .quantity(1) + .userId("42") + .build(); + + // Act + orderEventsHandler.on(event); + + // Assert + verify(failedOrderRepository).save(Mockito.any()); + verify(orderRepository).save(Mockito.any()); + } + + /** + * Method under test: {@link OrderEventsHandler#handle(Exception)} + */ + @Test + void testExceptionHandle() { + Exception exception = new Exception("foo"); + try { + Assertions.assertEquals("foo", exception.getMessage()); + orderEventsHandler.handle(exception); + }catch (Exception e){} + + } +} diff --git a/order-service/src/test/java/com/nashtech/order/handler/OrderLookupEventsHandlerTest.java b/order-service/src/test/java/com/nashtech/order/handler/OrderLookupEventsHandlerTest.java new file mode 100644 index 00000000..46bd8ef8 --- /dev/null +++ b/order-service/src/test/java/com/nashtech/order/handler/OrderLookupEventsHandlerTest.java @@ -0,0 +1,71 @@ +package com.nashtech.order.handler; + +import com.nashtech.common.utils.OrderStatus; +import com.nashtech.order.events.OrderCreatedEvent; +import com.nashtech.order.repository.OrderLookupRepository; +import com.nashtech.order.repository.entity.OrderLookupEntity; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import java.time.LocalDate; +import java.time.ZoneOffset; +import java.util.Date; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ContextConfiguration(classes = {OrderLookupEventsHandler.class}) +@ExtendWith(SpringExtension.class) +class OrderLookupEventsHandlerTest { + @Autowired + private OrderLookupEventsHandler orderLookupEventsHandler; + + @MockBean + private OrderLookupRepository orderLookupRepository; + + /** + * Method under test: {@link OrderLookupEventsHandler#on(OrderCreatedEvent)} + */ + @Test + void testOrderCreatedEventEventHandler() { + // Arrange + OrderLookupEntity orderLookupEntity = new OrderLookupEntity("42", + Date.from(LocalDate.of(1970, 1, 1).atStartOfDay().atZone(ZoneOffset.UTC).toInstant())); + + when(orderLookupRepository.save(Mockito.any())).thenReturn(orderLookupEntity); + OrderCreatedEvent event = OrderCreatedEvent.builder() + .orderId("42") + .orderStatus(OrderStatus.ORDER_NOT_APPROVED) + .productId("42") + .quantity(1) + .userId("42") + .build(); + + // Act + orderLookupEventsHandler.on(event); + + // Assert + verify(orderLookupRepository).save(Mockito.any()); + } + + /** + * Method under test: {@link OrderLookupEventsHandler#handle(Exception)} + */ + @Test + void testExceptionHandle() { + Exception exception = new Exception("foo"); + try { + Assertions.assertEquals("foo", exception.getMessage()); + orderLookupEventsHandler.handle(exception); + } catch (Exception e) { + e.printStackTrace(); + } + + } +} diff --git a/order-service/src/test/java/com/nashtech/order/query/FindOrderQueryTest.java b/order-service/src/test/java/com/nashtech/order/query/FindOrderQueryTest.java new file mode 100644 index 00000000..7965f1e0 --- /dev/null +++ b/order-service/src/test/java/com/nashtech/order/query/FindOrderQueryTest.java @@ -0,0 +1,18 @@ +package com.nashtech.order.query; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +class FindOrderQueryTest { + /** + * Method under test: {@link FindOrderQuery#FindOrderQuery()} + */ + @Test + void testNewFindOrderQuery() { + // Arrange, Act and Assert + assertNull((new FindOrderQuery()).orderId); + assertEquals("42", (new FindOrderQuery("42")).orderId); + } +} diff --git a/order-service/src/test/java/com/nashtech/order/query/OrderQueriesHandlerTest.java b/order-service/src/test/java/com/nashtech/order/query/OrderQueriesHandlerTest.java new file mode 100644 index 00000000..326faf43 --- /dev/null +++ b/order-service/src/test/java/com/nashtech/order/query/OrderQueriesHandlerTest.java @@ -0,0 +1,49 @@ +package com.nashtech.order.query; + +import com.nashtech.order.query.handler.OrderQueriesHandler; +import com.nashtech.order.repository.OrderRepository; +import com.nashtech.order.repository.entity.OrderEntity; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ContextConfiguration(classes = {OrderQueriesHandler.class}) +@RunWith(SpringJUnit4ClassRunner.class) +public class OrderQueriesHandlerTest { + @Autowired + private OrderQueriesHandler orderQueriesHandler; + + @MockBean + private OrderRepository orderRepository; + + /** + * Method under test: + * {@link OrderQueriesHandler#findOrders(FindOrdersByUserQuery)} + */ + @Test + public void testFindOrders() { + // Arrange + ArrayList orderEntityList = new ArrayList<>(); + when(orderRepository.findByUserId(Mockito.any())).thenReturn(orderEntityList); + + // Act + List actualFindOrdersResult = orderQueriesHandler.findOrders(new FindOrdersByUserQuery("42")); + + // Assert + verify(orderRepository).findByUserId(Mockito.any()); + assertTrue(actualFindOrdersResult.isEmpty()); + assertSame(orderEntityList, actualFindOrdersResult); + } +} diff --git a/order-service/src/test/java/com/nashtech/order/RestTest.java b/order-service/src/test/java/com/nashtech/order/restapi/RestTest.java similarity index 81% rename from order-service/src/test/java/com/nashtech/order/RestTest.java rename to order-service/src/test/java/com/nashtech/order/restapi/RestTest.java index 6a8f0586..b5a9d4a8 100644 --- a/order-service/src/test/java/com/nashtech/order/RestTest.java +++ b/order-service/src/test/java/com/nashtech/order/restapi/RestTest.java @@ -1,7 +1,8 @@ -package com.nashtech.order; +package com.nashtech.order.restapi; import com.fasterxml.jackson.databind.ObjectMapper; import com.nashtech.order.restapi.request.OrderCreateRequest; +import com.nashtech.order.restapi.response.OrderSummary; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -14,12 +15,13 @@ import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import java.util.Collection; import java.util.Objects; import static org.hamcrest.MatcherAssert.assertThat; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -34,10 +36,11 @@ public class RestTest { @Test public void testCreateOrder() throws Exception { + ObjectMapper objectMapper= new ObjectMapper(); OrderCreateRequest orderRequest = OrderCreateRequest.builder().build(); MvcResult result = mvc.perform(post("/orders/create") .contentType(MediaType.APPLICATION_JSON) - .content(new ObjectMapper().writeValueAsString(orderRequest)) + .content(objectMapper.writeValueAsString(orderRequest)) .characterEncoding("utf-8")) .andExpect(status().isOk()) .andDo(print()) @@ -49,6 +52,10 @@ public void testCreateOrder() throws Exception { Objects.requireNonNull(mockResponse.getContentType()).contains("application/json")); Assert.assertFalse(mockResponse.getContentAsString().isEmpty()); + OrderSummary orderSummary = objectMapper.readValue(mockResponse.getContentAsString(), OrderSummary.class); + + assertEquals("ORDER_PLACED", orderSummary.getOrderStatus()); + Collection responseHeaders = mockResponse.getHeaderNames(); Assert.assertNotNull(responseHeaders); Assert.assertFalse(responseHeaders.isEmpty()); @@ -58,13 +65,12 @@ public void testCreateOrder() throws Exception { public void testGetOrdersByUser() throws Exception { MvcResult result = mvc - .perform(get("/orders/1652")) + .perform(MockMvcRequestBuilders.get("/orders/{userId}", "1652")) .andExpect(status().isOk()) .andDo(print()) .andReturn(); MockHttpServletResponse mockResponse = result.getResponse(); - Assert.assertEquals(200, mockResponse.getStatus()); assertThat(mockResponse.getContentType(), Objects.requireNonNull(mockResponse.getContentType()).contains("application/json")); diff --git a/order-service/src/test/java/com/nashtech/order/saga/OrderSagaTest.java b/order-service/src/test/java/com/nashtech/order/saga/OrderSagaTest.java new file mode 100644 index 00000000..58cd7d60 --- /dev/null +++ b/order-service/src/test/java/com/nashtech/order/saga/OrderSagaTest.java @@ -0,0 +1,266 @@ +package com.nashtech.order.saga; + +import com.nashtech.common.event.*; +import com.nashtech.common.model.ShipmentStatus; +import com.nashtech.common.model.User; +import com.nashtech.common.utils.OrderStatus; +import com.nashtech.order.events.OrderApprovedEvent; +import com.nashtech.order.events.OrderCancelledEvent; +import com.nashtech.order.events.OrderCreatedEvent; +import org.axonframework.commandhandling.CommandCallback; +import org.axonframework.commandhandling.gateway.CommandGateway; +import org.axonframework.queryhandling.QueryUpdateEmitter; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.util.concurrent.CompletableFuture; +import java.util.function.Predicate; + +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ContextConfiguration(classes = {OrderSaga.class}) +@RunWith(SpringJUnit4ClassRunner.class) +public class OrderSagaTest { + @MockBean + private CommandGateway commandGateway; + + @Autowired + private OrderSaga orderSaga; + + + @MockBean + private QueryUpdateEmitter queryUpdateEmitter; + + /** + * Method under test: {@link OrderSaga#handle(OrderShippedEvent)} + */ + @Test + public void testOrderShippedEventHandlerSaga() { + // Arrange + when(commandGateway.send(Mockito.any())).thenReturn(new CompletableFuture<>()); + OrderShippedEvent orderShippedEvent = OrderShippedEvent.builder() + .orderId("42") + .paymentId("42") + .shipmentId("42") + .build(); + + // Act + orderSaga.handle(orderShippedEvent); + + // Assert + verify(commandGateway).send(Mockito.any()); + } + + /** + * Method under test: {@link OrderSaga#handle(PaymentApprovedEvent)} + */ + @Test + public void testPaymentApprovedEventHandlerSaga() { + // Arrange + doNothing().when(commandGateway).send(Mockito.any(), Mockito.>any()); + PaymentApprovedEvent.PaymentApprovedEventBuilder totalTaxResult = PaymentApprovedEvent.builder() + .basePrice(10.0d) + .brand("Brand") + .orderId("42") + .paymentId("42") + .productId("42") + .quantity(1) + .subTotal(10.0d) + .tax(10.0f) + .total(10.0d) + .totalTax(10.0f); + User user = User.builder().emailId("42").firstName("Jane").lastName("Doe").mobileNumber("42").userId("42").build(); + PaymentApprovedEvent paymentApprovedEvent = totalTaxResult.user(user).build(); + + // Act + orderSaga.handle(paymentApprovedEvent); + + // Assert + verify(commandGateway).send(Mockito.any(), Mockito.>any()); + } + + /** + * Method under test: {@link OrderSaga#handle(PaymentCancelledEvent)} + */ + @Test + public void testPaymentCancelledEventHandlerSaga() { + // Arrange + when(commandGateway.send(Mockito.any())).thenReturn(new CompletableFuture<>()); + PaymentCancelledEvent paymentCancelledEvent = PaymentCancelledEvent.builder() + .orderId("42") + .paymentId("42") + .productId("42") + .quantity(1) + .reasonToFailed("Just cause") + .userId("42") + .build(); + + // Act + orderSaga.handle(paymentCancelledEvent); + + // Assert + verify(commandGateway, atLeast(1)).send(Mockito.any()); + } + + /** + * Method under test: {@link OrderSaga#handle(ProductReserveFailedEvent)} + */ + @Test + public void testProductReserveFailedEventHandlerSaga() { + // Arrange + when(commandGateway.send(Mockito.any())).thenReturn(new CompletableFuture<>()); + ProductReserveFailedEvent productReserveFailedEvent = ProductReserveFailedEvent.builder() + .orderId("42") + .productId("42") + .reasonToFailed("Just cause") + .userId("42") + .build(); + + // Act + orderSaga.handle(productReserveFailedEvent); + + // Assert + verify(commandGateway).send(Mockito.any()); + } + + /** + * Method under test: {@link OrderSaga#handle(ProductReservedEvent)} + */ + @Test + public void testProductReservedEventHandlerSaga() { + // Arrange + doNothing().when(commandGateway).send(Mockito.any(), Mockito.>any()); + ProductReservedEvent productReservedEvent = ProductReservedEvent.builder() + .basePrice(10.0d) + .brand("Brand") + .color("Color") + .mileage(10.0d) + .model("Model") + .orderId("42") + .productId("42") + .quantity(1) + .subTotal(10.0d) + .tax(10.0f) + .total(10.0d) + .totalTax(10.0f) + .userId("42") + .year(1) + .build(); + + // Act + orderSaga.handle(productReservedEvent); + + // Assert + verify(commandGateway).send(Mockito.any(), Mockito.>any()); + } + + /** + * Method under test: {@link OrderSaga#handle(ShipmentCancelledEvent)} + */ + @Test + public void testShipmentCancelledEventHandlerSaga() { + // Arrange + when(commandGateway.send(Mockito.any())).thenReturn(new CompletableFuture<>()); + ShipmentCancelledEvent shipmentCancelledEvent = ShipmentCancelledEvent.builder() + .firstName("Jane") + .grandTotal(10.0d) + .lastName("Doe") + .orderId("42") + .paymentId("42") + .price(10.0d) + .productId("42") + .quantity(1) + .reasonToFailed("Just cause") + .shipmentId("42") + .shipmentStatus(ShipmentStatus.SHIPMENT_CREATED) + .subTotal(10.0d) + .tax(10.0f) + .userId("42") + .build(); + + // Act + orderSaga.handle(shipmentCancelledEvent); + + // Assert + verify(commandGateway, atLeast(1)).send(Mockito.any()); + } + + /** + * Method under test: {@link OrderSaga#handle(OrderApprovedEvent)} + */ + @Test + public void testOrderApprovedEventHandlerSaga() { + // Arrange + doNothing().when(queryUpdateEmitter) + .emit(Mockito.>any(), Mockito.>any(), Mockito.any()); + OrderApprovedEvent event = OrderApprovedEvent.builder() + .orderId("42") + .orderStatus(OrderStatus.ORDER_NOT_APPROVED) + .paymentId("42") + .shipmentId("42") + .build(); + + // Act + orderSaga.handle(event); + + // Assert + verify(queryUpdateEmitter).emit(Mockito.>any(), Mockito.>any(), + Mockito.any()); + } + + /** + * Method under test: {@link OrderSaga#handle(OrderCancelledEvent)} + */ + @Test + public void testOrderCancelledEventHandlerSaga() { + // Arrange + doNothing().when(queryUpdateEmitter) + .emit(Mockito.>any(), Mockito.>any(), Mockito.any()); + OrderCancelledEvent event = OrderCancelledEvent.builder() + .orderId("42") + .orderStatus(OrderStatus.ORDER_NOT_APPROVED) + .paymentId("42") + .productId("42") + .reasonToFailed("Just cause") + .shipmentId("42") + .userId("42") + .build(); + + // Act + orderSaga.handle(event); + + // Assert + verify(queryUpdateEmitter).emit(Mockito.>any(), Mockito.>any(), + Mockito.any()); + } + + /** + * Method under test: {@link OrderSaga#handle(OrderCreatedEvent)} + */ + @Test + public void testOrderCreatedEventHandlerSaga() { + // Arrange + doNothing().when(commandGateway).send(Mockito.any(), Mockito.>any()); + OrderCreatedEvent orderCreatedEvent = OrderCreatedEvent.builder() + .orderId("42") + .orderStatus(OrderStatus.ORDER_PLACED) + .productId("42") + .quantity(1) + .userId("42") + .build(); + + // Act + orderSaga.handle(orderCreatedEvent); + + // Assert + verify(commandGateway).send(Mockito.any(), Mockito.>any()); + } +}