Skip to content

Commit

Permalink
[fix] 주문 시스템 변경으로 인한 테스트 수정
Browse files Browse the repository at this point in the history
- 주문과 동시에 RDB 검증 주석 처리
  • Loading branch information
kimhyun5u committed Sep 19, 2024
1 parent 3264373 commit 37c92f0
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 106 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -185,11 +185,11 @@ void test2() {
// when
stockRequester.request(cartItems1);
assertThatThrownBy(() -> stockRequester.request(cartItems2)).isInstanceOf(NotEnoughStockException.class);

Menu findMenu1 = getStockCountFromDB(menu1.getId());
Menu findMenu2 = getStockCountFromDB(menu2.getId());
assertThat(findMenu1.getStockCount()).isEqualTo(1L);
assertThat(findMenu2.getStockCount()).isEqualTo(0L);
// DB 싱크 스케줄링 변경으로 인한 주석 처리
// Menu findMenu1 = getStockCountFromDB(menu1.getId());
// Menu findMenu2 = getStockCountFromDB(menu2.getId());
// assertThat(findMenu1.getStockCount()).isEqualTo(1L);
// assertThat(findMenu2.getStockCount()).isEqualTo(0L);
}

@Test
Expand Down Expand Up @@ -347,4 +347,4 @@ private Menu getStockCountFromDB(Long menuId) {
.setParameter("menuId", menuId)
.getSingleResult();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -138,92 +138,6 @@ void clean() {
redissonClient.getKeys().flushall();
}

@Nested
@DisplayName("카트 담기, 주문 동시성 통합 테스트")
class TestClass {

@Test
@DisplayName("N명이 동시에 주문했을 때 음식 상품 메뉴의 재고수가 N개 감소한다.")
void concurrentOrderTest() throws InterruptedException {
menus = List.of(saveMenu(store, menuCategory, "메뉴1", 5000L, 10L));

// given
int totalCustomerCount = customers.size(); // 고객 수만큼 멀티스레딩
ExecutorService executorService = Executors.newFixedThreadPool(totalCustomerCount);
CountDownLatch latch = new CountDownLatch(totalCustomerCount);

Menu targetMenu = menus.get(0); // 첫 번째 메뉴를 대상으로 테스트
Long initialStock = targetMenu.getStockCount();

// when
for (int customerCount = 0; customerCount < totalCustomerCount; customerCount++) {
Customer customer = customers.get(customerCount);
executorService.submit(() -> executeAddCartAndOrderCreation(latch, customer, targetMenu));
}
latch.await(10, TimeUnit.SECONDS);
executorService.shutdown();

// then: Redis Cache 정합성
assertThat(redissonClient.getAtomicLong(
RedisCacheConstants.MENU_STOCK_PREFIX + targetMenu.getId()).get())
.isEqualTo(initialStock - totalCustomerCount);

// then: verify RDB
Menu findMenu = getStockCountFromDB(targetMenu.getId());
assertThat(findMenu.getStockCount()).isEqualTo(initialStock - totalCustomerCount);
}

/**
* 시나리오:
* 고객1: [메뉴1:1개, 메뉴2:1개]
* 고객2: [메뉴1:1개, 메뉴2:1개]
* 음식 상품: [메뉴1:2개, 메뉴2:1개]
*/
@Test
@DisplayName("여러 고객이 동시에 주문할 때, 재고 부족 시 롤백되어야 한다.")
void concurrentOrderWithLimitedStockTest() throws InterruptedException {
// given
menus = List.of(
saveMenu(store, menuCategory, "메뉴1", 5000L, 2L),
saveMenu(store, menuCategory, "메뉴2", 5000L, 1L) // 여기서 터진다.
);

for (int index = 0; index < customers.size(); index++) {
Customer customer = customers.get(index);
for (Menu menu : menus) {// 각 메뉴를 1개씩 담음
cartService.addMenu(new AddCartCommand(customer.getId().toString(), menu.getId()));
}
}

int numberOfThreads = 2;
ExecutorService executorService = Executors.newFixedThreadPool(numberOfThreads);
CountDownLatch latch = new CountDownLatch(numberOfThreads);
Menu menu1 = menus.get(0);
Menu menu2 = menus.get(1);

// when
for (int personCount = 0; personCount < numberOfThreads; personCount++) {
final int index = personCount;
executorService.submit(() -> executeOrderCreation(latch, customers.get(index)));
}
latch.await(10, TimeUnit.MINUTES);
executorService.shutdown();

// then: Redis Cache 정합성
assertThat(redissonClient.getAtomicLong(
RedisCacheConstants.MENU_STOCK_PREFIX + menu1.getId()).get()).isEqualTo(1);
assertThat(redissonClient.getAtomicLong(
RedisCacheConstants.MENU_STOCK_PREFIX + menu2.getId()).get()).isEqualTo(0);

// then: verify RDB
Menu updatedMenu1 = getStockCountFromDB(menu1.getId());
Menu updatedMenu2 = getStockCountFromDB(menu2.getId());

assertThat(updatedMenu1.getStockCount()).isEqualTo(1);
assertThat(updatedMenu2.getStockCount()).isEqualTo(0);
}
}

/**
* Test Scenario: 계좌 잔액 부족으로 인한 롤백 시나리오
* 고객1: [메뉴1:3개, 메뉴2:3개, 메뉴3:3개] 잔액: 100000원
Expand Down Expand Up @@ -298,13 +212,14 @@ void insufficientBalanceExceptionThenRollback() throws Throwable {
executorService.shutdown();

// then: verify RDB
Menu updatedMenu1 = getStockCountFromDB(menu1.getId());
Menu updatedMenu2 = getStockCountFromDB(menu2.getId());
Menu updatedMenu3 = getStockCountFromDB(menu3.getId());

assertThat(updatedMenu1.getStockCount()).isEqualTo(menu1ExpectedCount);
assertThat(updatedMenu2.getStockCount()).isEqualTo(menu2ExpectedCount);
assertThat(updatedMenu3.getStockCount()).isEqualTo(menu3ExpectedCount);
// DB 싱크 스케줄링 변경으로 인한 주석 처리
// Menu updatedMenu1 = getStockCountFromDB(menu1.getId());
// Menu updatedMenu2 = getStockCountFromDB(menu2.getId());
// Menu updatedMenu3 = getStockCountFromDB(menu3.getId());
//
// assertThat(updatedMenu1.getStockCount()).isEqualTo(menu1ExpectedCount);
// assertThat(updatedMenu2.getStockCount()).isEqualTo(menu2ExpectedCount);
// assertThat(updatedMenu3.getStockCount()).isEqualTo(menu3ExpectedCount);

// then: verify Redis
assertThat(redissonClient.getAtomicLong(
Expand Down Expand Up @@ -399,13 +314,14 @@ void minOrderPriceExceptionThenRollback() throws Throwable {
executorService.shutdown();

// then: verify RDB
Menu updatedMenu1 = getStockCountFromDB(menu1.getId());
Menu updatedMenu2 = getStockCountFromDB(menu2.getId());
Menu updatedMenu3 = getStockCountFromDB(menu3.getId());

assertThat(updatedMenu1.getStockCount()).isEqualTo(menu1ExpectedCount);
assertThat(updatedMenu2.getStockCount()).isEqualTo(menu2ExpectedCount);
assertThat(updatedMenu3.getStockCount()).isEqualTo(menu3ExpectedCount);
// DB 싱크 스케줄링 변경으로 인한 주석 처리
// Menu updatedMenu1 = getStockCountFromDB(menu1.getId());
// Menu updatedMenu2 = getStockCountFromDB(menu2.getId());
// Menu updatedMenu3 = getStockCountFromDB(menu3.getId());
//
// assertThat(updatedMenu1.getStockCount()).isEqualTo(menu1ExpectedCount);
// assertThat(updatedMenu2.getStockCount()).isEqualTo(menu2ExpectedCount);
// assertThat(updatedMenu3.getStockCount()).isEqualTo(menu3ExpectedCount);

// then: verify Redis
assertThat(redissonClient.getAtomicLong(
Expand All @@ -416,6 +332,94 @@ void minOrderPriceExceptionThenRollback() throws Throwable {
RedisCacheConstants.MENU_STOCK_PREFIX + menu3.getId()).get()).isEqualTo(menu3ExpectedCount);
}

@Nested
@DisplayName("카트 담기, 주문 동시성 통합 테스트")
class TestClass {

@Test
@DisplayName("N명이 동시에 주문했을 때 음식 상품 메뉴의 재고수가 N개 감소한다.")
void concurrentOrderTest() throws InterruptedException {
menus = List.of(saveMenu(store, menuCategory, "메뉴1", 5000L, 10L));

// given
int totalCustomerCount = customers.size(); // 고객 수만큼 멀티스레딩
ExecutorService executorService = Executors.newFixedThreadPool(totalCustomerCount);
CountDownLatch latch = new CountDownLatch(totalCustomerCount);

Menu targetMenu = menus.get(0); // 첫 번째 메뉴를 대상으로 테스트
Long initialStock = targetMenu.getStockCount();

// when
for (int customerCount = 0; customerCount < totalCustomerCount; customerCount++) {
Customer customer = customers.get(customerCount);
executorService.submit(() -> executeAddCartAndOrderCreation(latch, customer, targetMenu));
}
latch.await(10, TimeUnit.SECONDS);
executorService.shutdown();

// then: Redis Cache 정합성
assertThat(redissonClient.getAtomicLong(
RedisCacheConstants.MENU_STOCK_PREFIX + targetMenu.getId()).get())
.isEqualTo(initialStock - totalCustomerCount);

// then: verify RDB
// DB 싱크 스케줄링 변경으로 인한 주석 처리
// Menu findMenu = getStockCountFromDB(targetMenu.getId());
// assertThat(findMenu.getStockCount()).isEqualTo(initialStock - totalCustomerCount);
}

/**
* 시나리오:
* 고객1: [메뉴1:1개, 메뉴2:1개]
* 고객2: [메뉴1:1개, 메뉴2:1개]
* 음식 상품: [메뉴1:2개, 메뉴2:1개]
*/
@Test
@DisplayName("여러 고객이 동시에 주문할 때, 재고 부족 시 롤백되어야 한다.")
void concurrentOrderWithLimitedStockTest() throws InterruptedException {
// given
menus = List.of(
saveMenu(store, menuCategory, "메뉴1", 5000L, 2L),
saveMenu(store, menuCategory, "메뉴2", 5000L, 1L) // 여기서 터진다.
);

for (int index = 0; index < customers.size(); index++) {
Customer customer = customers.get(index);
for (Menu menu : menus) {// 각 메뉴를 1개씩 담음
cartService.addMenu(new AddCartCommand(customer.getId().toString(), menu.getId()));
}
}

int numberOfThreads = 2;
ExecutorService executorService = Executors.newFixedThreadPool(numberOfThreads);
CountDownLatch latch = new CountDownLatch(numberOfThreads);
Menu menu1 = menus.get(0);
Menu menu2 = menus.get(1);

// when
for (int personCount = 0; personCount < numberOfThreads; personCount++) {
final int index = personCount;
executorService.submit(() -> executeOrderCreation(latch, customers.get(index)));
}
latch.await(10, TimeUnit.MINUTES);
executorService.shutdown();

// then: Redis Cache 정합성
assertThat(redissonClient.getAtomicLong(
RedisCacheConstants.MENU_STOCK_PREFIX + menu1.getId()).get()).isEqualTo(1);
assertThat(redissonClient.getAtomicLong(
RedisCacheConstants.MENU_STOCK_PREFIX + menu2.getId()).get()).isEqualTo(0);

// then: verify RDB
// DB 싱크 스케줄링 변경으로 인한 주석 처리
// Menu updatedMenu1 = getStockCountFromDB(menu1.getId());
// Menu updatedMenu2 = getStockCountFromDB(menu2.getId());
//
// assertThat(updatedMenu1.getStockCount()).isEqualTo(1);
// assertThat(updatedMenu2.getStockCount()).isEqualTo(0);
}
}

void setupMenuToCart(Customer customer, Menu menu, int addCount) {
for (int count = 0; count < addCount; count++) {
cartService.addMenu(new AddCartCommand(customer.getId().toString(), menu.getId()));
Expand Down

0 comments on commit 37c92f0

Please sign in to comment.