BAEL-4769 Additions for 'Querying the Query Model in Axon' (#12578)

* Update to latest and include script to start axon server

* Add the two other types of queries

* Add unit and integration tests for the queries

* BAEL-4769 Formatting

Co-authored-by: bipster <openbip@gmail.com>
This commit is contained in:
Gerard Klijs
2022-09-06 20:04:07 +02:00
committed by GitHub
parent c1a759900e
commit 2f9967dcd3
17 changed files with 730 additions and 80 deletions
@@ -0,0 +1,171 @@
package com.baeldung.axon.querymodel;
import com.baeldung.axon.coreapi.events.OrderConfirmedEvent;
import com.baeldung.axon.coreapi.events.OrderCreatedEvent;
import com.baeldung.axon.coreapi.events.OrderShippedEvent;
import com.baeldung.axon.coreapi.events.ProductAddedEvent;
import com.baeldung.axon.coreapi.events.ProductCountDecrementedEvent;
import com.baeldung.axon.coreapi.events.ProductCountIncrementedEvent;
import com.baeldung.axon.coreapi.events.ProductRemovedEvent;
import com.baeldung.axon.coreapi.queries.FindAllOrderedProductsQuery;
import com.baeldung.axon.coreapi.queries.Order;
import com.baeldung.axon.coreapi.queries.OrderStatus;
import com.baeldung.axon.coreapi.queries.OrderUpdatesQuery;
import com.baeldung.axon.coreapi.queries.TotalProductsShippedQuery;
import org.axonframework.queryhandling.QueryUpdateEmitter;
import org.junit.jupiter.api.*;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
public abstract class AbstractOrdersEventHandlerUnitTest {
private static final String ORDER_ID_1 = UUID.randomUUID().toString();
private static final String ORDER_ID_2 = UUID.randomUUID().toString();
private static final String PRODUCT_ID_1 = UUID.randomUUID().toString();
private static final String PRODUCT_ID_2 = UUID.randomUUID().toString();
private OrdersEventHandler handler;
private static Order orderOne;
private static Order orderTwo;
QueryUpdateEmitter emitter = mock(QueryUpdateEmitter.class);
@BeforeAll
static void createOrders() {
orderOne = new Order(ORDER_ID_1);
orderOne.getProducts().put(PRODUCT_ID_1, 3);
orderOne.setOrderShipped();
orderTwo = new Order(ORDER_ID_2);
orderTwo.getProducts().put(PRODUCT_ID_1, 1);
orderTwo.getProducts().put(PRODUCT_ID_2, 1);
orderTwo.setOrderConfirmed();
}
@BeforeEach
void setUp() {
handler = getHandler();
}
protected abstract OrdersEventHandler getHandler();
@Test
void givenTwoOrdersPlacedOfWhichOneNotShipped_whenFindAllOrderedProductsQuery_thenCorrectOrdersAreReturned() {
resetWithTwoOrders();
List<Order> result = handler.handle(new FindAllOrderedProductsQuery());
assertNotNull(result);
assertEquals(2, result.size());
Order order_1 = result.stream().filter(o -> o.getOrderId().equals(ORDER_ID_1)).findFirst().orElse(null);
assertEquals(orderOne, order_1);
Order order_2 = result.stream().filter(o -> o.getOrderId().equals(ORDER_ID_2)).findFirst().orElse(null);
assertEquals(orderTwo, order_2);
}
@Test
void givenNoOrdersPlaced_whenTotalProductsShippedQuery_thenZeroReturned() {
assertEquals(0, handler.handle(new TotalProductsShippedQuery(PRODUCT_ID_1)));
}
@Test
void givenTwoOrdersPlacedOfWhichOneNotShipped_whenTotalProductsShippedQuery_thenOnlyCountProductsFirstOrder() {
resetWithTwoOrders();
assertEquals(3, handler.handle(new TotalProductsShippedQuery(PRODUCT_ID_1)));
assertEquals(0, handler.handle(new TotalProductsShippedQuery(PRODUCT_ID_2)));
}
@Test
void givenTwoOrdersPlacedAndShipped_whenTotalProductsShippedQuery_thenCountBothOrders() {
resetWithTwoOrders();
handler.on(new OrderShippedEvent(ORDER_ID_2));
assertEquals(4, handler.handle(new TotalProductsShippedQuery(PRODUCT_ID_1)));
assertEquals(1, handler.handle(new TotalProductsShippedQuery(PRODUCT_ID_2)));
}
@Test
void givenOrderExist_whenOrderUpdatesQuery_thenOrderReturned() {
resetWithTwoOrders();
Order result = handler.handle(new OrderUpdatesQuery(ORDER_ID_1));
assertNotNull(result);
assertEquals(ORDER_ID_1, result.getOrderId());
assertEquals(3, result.getProducts().get(PRODUCT_ID_1));
assertEquals(OrderStatus.SHIPPED, result.getOrderStatus());
}
@Test
void givenOrderExist_whenProductAddedEvent_thenUpdateEmittedOnce() {
handler.on(new OrderCreatedEvent(ORDER_ID_1));
handler.on(new ProductAddedEvent(ORDER_ID_1, PRODUCT_ID_1));
verify(emitter, times(1)).emit(eq(OrderUpdatesQuery.class), any(), any(Order.class));
}
@Test
void givenOrderWithProductExist_whenProductCountDecrementedEvent_thenUpdateEmittedOnce() {
handler.on(new OrderCreatedEvent(ORDER_ID_1));
handler.on(new ProductAddedEvent(ORDER_ID_1, PRODUCT_ID_1));
reset(emitter);
handler.on(new ProductCountDecrementedEvent(ORDER_ID_1, PRODUCT_ID_1));
verify(emitter, times(1)).emit(eq(OrderUpdatesQuery.class), any(), any(Order.class));
}
@Test
void givenOrderWithProductExist_whenProductRemovedEvent_thenUpdateEmittedOnce() {
handler.on(new OrderCreatedEvent(ORDER_ID_1));
handler.on(new ProductAddedEvent(ORDER_ID_1, PRODUCT_ID_1));
reset(emitter);
handler.on(new ProductRemovedEvent(ORDER_ID_1, PRODUCT_ID_1));
verify(emitter, times(1)).emit(eq(OrderUpdatesQuery.class), any(), any(Order.class));
}
@Test
void givenOrderWithProductExist_whenProductCountIncrementedEvent_thenUpdateEmittedOnce() {
handler.on(new OrderCreatedEvent(ORDER_ID_1));
handler.on(new ProductAddedEvent(ORDER_ID_1, PRODUCT_ID_1));
reset(emitter);
handler.on(new ProductCountIncrementedEvent(ORDER_ID_1, PRODUCT_ID_1));
verify(emitter, times(1)).emit(eq(OrderUpdatesQuery.class), any(), any(Order.class));
}
@Test
void givenOrderWithProductExist_whenOrderConfirmedEvent_thenUpdateEmittedOnce() {
handler.on(new OrderCreatedEvent(ORDER_ID_1));
handler.on(new ProductAddedEvent(ORDER_ID_1, PRODUCT_ID_1));
reset(emitter);
handler.on(new OrderConfirmedEvent(ORDER_ID_1));
verify(emitter, times(1)).emit(eq(OrderUpdatesQuery.class), any(), any(Order.class));
}
@Test
void givenOrderWithProductAndConfirmationExist_whenOrderShippedEvent_thenUpdateEmittedOnce() {
handler.on(new OrderCreatedEvent(ORDER_ID_1));
handler.on(new ProductAddedEvent(ORDER_ID_1, PRODUCT_ID_1));
reset(emitter);
handler.on(new OrderShippedEvent(ORDER_ID_1));
verify(emitter, times(1)).emit(eq(OrderUpdatesQuery.class), any(), any(Order.class));
}
private void resetWithTwoOrders() {
handler.reset(Arrays.asList(orderOne, orderTwo));
}
}
@@ -0,0 +1,9 @@
package com.baeldung.axon.querymodel;
public class InMemoryOrdersEventHandlerUnitTest extends AbstractOrdersEventHandlerUnitTest {
@Override
protected OrdersEventHandler getHandler() {
return new InMemoryOrdersEventHandler(emitter);
}
}
@@ -0,0 +1,129 @@
package com.baeldung.axon.querymodel;
import com.baeldung.axon.OrderApplication;
import com.baeldung.axon.coreapi.events.OrderConfirmedEvent;
import com.baeldung.axon.coreapi.events.OrderShippedEvent;
import com.baeldung.axon.coreapi.events.ProductAddedEvent;
import com.baeldung.axon.coreapi.events.ProductCountDecrementedEvent;
import com.baeldung.axon.coreapi.events.ProductCountIncrementedEvent;
import com.baeldung.axon.coreapi.queries.Order;
import org.axonframework.eventhandling.gateway.EventGateway;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import reactor.test.StepVerifier;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest(classes = OrderApplication.class)
class OrderQueryServiceIntegrationTest {
@Autowired
OrderQueryService queryService;
@Autowired
EventGateway eventGateway;
@Autowired
OrdersEventHandler handler;
private String orderId;
private final String productId = "Deluxe Chair";
@BeforeEach
void setUp() {
orderId = UUID.randomUUID()
.toString();
Order order = new Order(orderId);
handler.reset(Collections.singletonList(order));
}
@Test
void givenOrderCreatedEventSend_whenCallingAllOrders_thenOneCreatedOrderIsReturned() throws ExecutionException, InterruptedException {
List<OrderResponse> result = queryService.findAllOrders()
.get();
assertEquals(1, result.size());
OrderResponse response = result.get(0);
assertEquals(orderId, response.getOrderId());
assertEquals(OrderStatusResponse.CREATED, response.getOrderStatus());
assertTrue(response.getProducts()
.isEmpty());
}
@Test
void givenThreeDeluxeChairsShipped_whenCallingAllShippedChairs_then234PlusTreeIsReturned() {
Order order = new Order(orderId);
order.getProducts()
.put(productId, 3);
order.setOrderShipped();
handler.reset(Collections.singletonList(order));
assertEquals(237, queryService.totalShipped(productId));
}
@Test
void givenOrdersAreUpdated_whenCallingOrderUpdates_thenUpdatesReturned() {
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
executor.schedule(this::addIncrementDecrementConfirmAndShip, 100L, TimeUnit.MILLISECONDS);
try {
StepVerifier.create(queryService.orderUpdates(orderId))
.assertNext(order -> assertTrue(order.getProducts()
.isEmpty()))
.assertNext(order -> assertEquals(1, order.getProducts()
.get(productId)))
.assertNext(order -> assertEquals(2, order.getProducts()
.get(productId)))
.assertNext(order -> assertEquals(1, order.getProducts()
.get(productId)))
.assertNext(order -> assertEquals(OrderStatusResponse.CONFIRMED, order.getOrderStatus()))
.assertNext(order -> assertEquals(OrderStatusResponse.SHIPPED, order.getOrderStatus()))
.thenCancel()
.verify();
} finally {
executor.shutdown();
}
}
private void addIncrementDecrementConfirmAndShip() {
sendProductAddedEvent();
sendProductCountIncrementEvent();
sendProductCountDecrementEvent();
sendOrderConfirmedEvent();
sendOrderShippedEvent();
}
private void sendProductAddedEvent() {
ProductAddedEvent event = new ProductAddedEvent(orderId, productId);
eventGateway.publish(event);
}
private void sendProductCountIncrementEvent() {
ProductCountIncrementedEvent event = new ProductCountIncrementedEvent(orderId, productId);
eventGateway.publish(event);
}
private void sendProductCountDecrementEvent() {
ProductCountDecrementedEvent event = new ProductCountDecrementedEvent(orderId, productId);
eventGateway.publish(event);
}
private void sendOrderConfirmedEvent() {
OrderConfirmedEvent event = new OrderConfirmedEvent(orderId);
eventGateway.publish(event);
}
private void sendOrderShippedEvent() {
OrderShippedEvent event = new OrderShippedEvent(orderId);
eventGateway.publish(event);
}
}
@@ -0,0 +1 @@
axon.axonserver.enabled=false