From 3414520a0ba68c367ff1e965ff718552db99aa94 Mon Sep 17 00:00:00 2001 From: kimhyun5u <22kimhyun5u@gmail.com> Date: Sun, 18 Aug 2024 15:38:37 +0900 Subject: [PATCH 01/21] =?UTF-8?q?[feat]=20MenuRepository=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20-=20MenuRepository.findByIdForUpdate:=20=EB=B9=84?= =?UTF-8?q?=EA=B4=80=EC=A0=81=20=EB=9D=BD=EC=9D=84=20=EC=A0=81=EC=9A=A9?= =?UTF-8?q?=ED=95=9C=20findById=20-=20MenuRepository.updateStock:=20?= =?UTF-8?q?=EB=A9=94=EB=89=B4=20=EC=88=98=EB=9F=89=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../woowak/lab/menu/repository/MenuRepository.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/main/java/camp/woowak/lab/menu/repository/MenuRepository.java b/src/main/java/camp/woowak/lab/menu/repository/MenuRepository.java index a1de78e7..44c794c6 100644 --- a/src/main/java/camp/woowak/lab/menu/repository/MenuRepository.java +++ b/src/main/java/camp/woowak/lab/menu/repository/MenuRepository.java @@ -5,6 +5,7 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Lock; +import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; @@ -18,4 +19,17 @@ public interface MenuRepository extends JpaRepository { @Lock(LockModeType.PESSIMISTIC_WRITE) @Query("SELECT m FROM Menu m where m.id in :ids") List findAllByIdForUpdate(List ids); + + @Lock(LockModeType.PESSIMISTIC_WRITE) + Optional findByIdForUpdate(Long id); + + /** + * + * 메뉴의 재고를 변경합니다. + * TODO: [논의] @Transactional을 Respository 단에 안둬도되는가? + * Repository 에서 직접 접근할 때 사용자가 실수해서 @Transactional 을 빼먹을 수도 있다. + */ + @Modifying + @Query("UPDATE Menu m SET m.stockCount = :stock WHERE m.id = :id") + int updateStock(@Param("id") Long id, @Param("stock") int stock); } From 8e0cc9e245b46bb127ef1b3d56cff872102a91ca Mon Sep 17 00:00:00 2001 From: kimhyun5u <22kimhyun5u@gmail.com> Date: Sun, 18 Aug 2024 15:43:35 +0900 Subject: [PATCH 02/21] =?UTF-8?q?[test]=20UpdateMenuStockServiceTest=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20-=20testUpdateMenuStock:=20=EB=A9=94?= =?UTF-8?q?=EB=89=B4=20=EC=88=98=EB=9F=89=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?-=20testUpdateMenuStockNotFound:=20=EB=A9=94=EB=89=B4=EB=A5=BC?= =?UTF-8?q?=20=EC=B0=BE=EC=9D=84=20=EC=88=98=20=EC=97=86=EB=8A=94=20?= =?UTF-8?q?=EA=B2=BD=EC=9A=B0=20-=20testUpdateMenuStockNotEqualsOwner:=20?= =?UTF-8?q?=EB=A9=94=EB=89=B4=EB=A5=BC=20=EC=86=8C=EC=9C=A0=ED=95=9C=20?= =?UTF-8?q?=EA=B0=80=EA=B2=8C=EC=9D=98=20=EC=A3=BC=EC=9D=B8=EC=9D=B4=20?= =?UTF-8?q?=EC=95=84=EB=8B=8C=20=EA=B2=BD=EC=9A=B0=20-=20testUpdateMenuSto?= =?UTF-8?q?ckStoreNotOpen:=20=EB=A7=A4=EC=9E=A5=EC=9D=B4=20=EC=97=B4?= =?UTF-8?q?=EB=A0=A4=EC=9E=88=EC=A7=80=20=EC=95=8A=EC=9D=80=20=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0=20-=20testUpdateMenuStockInvalid:=20=EB=A9=94?= =?UTF-8?q?=EB=89=B4=EC=9D=98=20=EC=9E=AC=EA=B3=A0=EB=A5=BC=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=ED=95=A0=20=EC=88=98=20=EC=97=86=EB=8A=94=20=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/UpdateMenuStockServiceTest.java | 162 ++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 src/test/java/camp/woowak/lab/menu/service/UpdateMenuStockServiceTest.java diff --git a/src/test/java/camp/woowak/lab/menu/service/UpdateMenuStockServiceTest.java b/src/test/java/camp/woowak/lab/menu/service/UpdateMenuStockServiceTest.java new file mode 100644 index 00000000..dd120b71 --- /dev/null +++ b/src/test/java/camp/woowak/lab/menu/service/UpdateMenuStockServiceTest.java @@ -0,0 +1,162 @@ +package camp.woowak.lab.menu.service; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.BDDMockito.*; + +import java.util.Optional; +import java.util.UUID; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import camp.woowak.lab.cart.exception.MenuNotFoundException; +import camp.woowak.lab.menu.domain.Menu; +import camp.woowak.lab.menu.exception.InvalidMenuStockUpdateException; +import camp.woowak.lab.menu.exception.NotEqualsOwnerException; +import camp.woowak.lab.menu.exception.StoreNotOpenException; +import camp.woowak.lab.menu.repository.MenuRepository; +import camp.woowak.lab.menu.service.command.UpdateMenuStockCommand; +import camp.woowak.lab.store.domain.Store; + +@ExtendWith(MockitoExtension.class) +class UpdateMenuStockServiceTest { + @InjectMocks + private UpdateMenuStockService updateMenuStockService; + + @Mock + private MenuRepository menuRepository; + + @Test + @DisplayName("메뉴 재고 업데이트 테스트 - 성공") + void testUpdateMenuStock() { + // given + Long menuId = 1L; + int stock = 10; + UUID vendorId = UUID.randomUUID(); + Menu fakeMenu = Mockito.mock(Menu.class); + Store fakeStore = Mockito.mock(Store.class); + + given(fakeMenu.getStore()).willReturn(fakeStore); + given(fakeStore.isOwnedBy(any(UUID.class))).willReturn(true); + given(menuRepository.findByIdForUpdate(anyLong())).willReturn(Optional.of(fakeMenu)); + given(fakeStore.isOpen()).willReturn(true); + given(menuRepository.updateStock(anyLong(), anyInt())).willReturn(1); + + UpdateMenuStockCommand cmd = new UpdateMenuStockCommand(menuId, stock, vendorId); + + // when + updateMenuStockService.updateMenuStock(cmd); + + // then + verify(menuRepository, times(1)).findByIdForUpdate(anyLong()); + verify(fakeMenu, times(2)).getStore(); + verify(fakeStore, times(1)).isOwnedBy(any(UUID.class)); + verify(fakeStore, times(1)).isOpen(); + verify(menuRepository, times(1)).updateStock(anyLong(), anyInt()); + } + + @Test + @DisplayName("메뉴 재고 업데이트 테스트 - 메뉴를 찾을 수 없는 경우") + void testUpdateMenuStockNotFound() { + // given + Long menuId = 1L; + int stock = 10; + UUID vendorId = UUID.randomUUID(); + + given(menuRepository.findByIdForUpdate(anyLong())).willReturn(Optional.empty()); + + UpdateMenuStockCommand cmd = new UpdateMenuStockCommand(menuId, stock, vendorId); + + // when + // then + assertThrows(MenuNotFoundException.class, () -> updateMenuStockService.updateMenuStock(cmd)); + verify(menuRepository, times(1)).findByIdForUpdate(anyLong()); + verify(menuRepository, never()).updateStock(anyLong(), anyInt()); + } + + @Test + @DisplayName("메뉴 재고 업데이트 테스트 - 메뉴를 소유한 가게의 주인이 아닌 경우") + void testUpdateMenuStockNotEqualsOwner() { + // given + Long menuId = 1L; + int stock = 10; + UUID vendorId = UUID.randomUUID(); + Menu fakeMenu = Mockito.mock(Menu.class); + Store fakeStore = Mockito.mock(Store.class); + + given(fakeMenu.getStore()).willReturn(fakeStore); + given(fakeStore.isOwnedBy(any(UUID.class))).willReturn(false); + given(menuRepository.findByIdForUpdate(anyLong())).willReturn(Optional.of(fakeMenu)); + + UpdateMenuStockCommand cmd = new UpdateMenuStockCommand(menuId, stock, vendorId); + + // when + // then + assertThrows(NotEqualsOwnerException.class, () -> updateMenuStockService.updateMenuStock(cmd)); + verify(menuRepository, times(1)).findByIdForUpdate(anyLong()); + verify(fakeMenu, times(1)).getStore(); + verify(fakeStore, times(1)).isOwnedBy(any(UUID.class)); + verify(menuRepository, never()).updateStock(anyLong(), anyInt()); + } + + @Test + @DisplayName("메뉴 재고 업데이트 테스트 - 매장이 열려있지 않은 경우") + void testUpdateMenuStockStoreNotOpen() { + // given + Long menuId = 1L; + int stock = 10; + UUID vendorId = UUID.randomUUID(); + Menu fakeMenu = Mockito.mock(Menu.class); + Store fakeStore = Mockito.mock(Store.class); + + given(fakeMenu.getStore()).willReturn(fakeStore); + given(fakeStore.isOwnedBy(any(UUID.class))).willReturn(true); + given(fakeStore.isOpen()).willReturn(false); + given(menuRepository.findByIdForUpdate(anyLong())).willReturn(Optional.of(fakeMenu)); + + UpdateMenuStockCommand cmd = new UpdateMenuStockCommand(menuId, stock, vendorId); + + // when + // then + assertThrows(StoreNotOpenException.class, () -> updateMenuStockService.updateMenuStock(cmd)); + verify(menuRepository, times(1)).findByIdForUpdate(anyLong()); + verify(fakeMenu, times(2)).getStore(); + verify(fakeStore, times(1)).isOwnedBy(any(UUID.class)); + verify(fakeStore, times(1)).isOpen(); + verify(menuRepository, never()).updateStock(anyLong(), anyInt()); + } + + @Test + @DisplayName("메뉴 재고 업데이트 테스트 - 메뉴의 재고를 변경할 수 없는 경우") + void testUpdateMenuStockInvalid() { + // given + Long menuId = 1L; + int stock = 10; + UUID vendorId = UUID.randomUUID(); + Menu fakeMenu = Mockito.mock(Menu.class); + Store fakeStore = Mockito.mock(Store.class); + + given(fakeMenu.getStore()).willReturn(fakeStore); + given(fakeStore.isOwnedBy(any(UUID.class))).willReturn(true); + given(fakeStore.isOpen()).willReturn(true); + given(menuRepository.findByIdForUpdate(anyLong())).willReturn(Optional.of(fakeMenu)); + given(menuRepository.updateStock(anyLong(), anyInt())).willReturn(0); + + UpdateMenuStockCommand cmd = new UpdateMenuStockCommand(menuId, stock, vendorId); + + // when + // then + assertThrows(InvalidMenuStockUpdateException.class, () -> updateMenuStockService.updateMenuStock(cmd)); + verify(menuRepository, times(1)).findByIdForUpdate(anyLong()); + verify(fakeMenu, times(2)).getStore(); + verify(fakeStore, times(1)).isOwnedBy(any(UUID.class)); + verify(fakeStore, times(1)).isOpen(); + + verify(menuRepository, times(1)).updateStock(anyLong(), anyInt()); + } +} From 40507a9e45cef64b4bddc0ac60b781b541ca208d Mon Sep 17 00:00:00 2001 From: kimhyun5u <22kimhyun5u@gmail.com> Date: Sun, 18 Aug 2024 15:44:21 +0900 Subject: [PATCH 03/21] =?UTF-8?q?[feat]=20UpdateMenuStockService=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20-=20updateMenuStock:=20=EB=A9=94=EB=89=B4?= =?UTF-8?q?=EC=9D=98=20=EC=88=98=EB=9F=89=EC=9D=84=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EB=A9=94=EC=86=8C=EB=93=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../menu/service/UpdateMenuStockService.java | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 src/main/java/camp/woowak/lab/menu/service/UpdateMenuStockService.java diff --git a/src/main/java/camp/woowak/lab/menu/service/UpdateMenuStockService.java b/src/main/java/camp/woowak/lab/menu/service/UpdateMenuStockService.java new file mode 100644 index 00000000..bbdf2ad0 --- /dev/null +++ b/src/main/java/camp/woowak/lab/menu/service/UpdateMenuStockService.java @@ -0,0 +1,56 @@ +package camp.woowak.lab.menu.service; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import camp.woowak.lab.cart.exception.MenuNotFoundException; +import camp.woowak.lab.menu.domain.Menu; +import camp.woowak.lab.menu.exception.InvalidMenuStockUpdateException; +import camp.woowak.lab.menu.exception.NotEqualsOwnerException; +import camp.woowak.lab.menu.exception.StoreNotOpenException; +import camp.woowak.lab.menu.repository.MenuRepository; +import camp.woowak.lab.menu.service.command.UpdateMenuStockCommand; + +@Service +public class UpdateMenuStockService { + private final MenuRepository menuRepository; + + public UpdateMenuStockService(MenuRepository menuRepository) { + this.menuRepository = menuRepository; + } + + /** + * + * @throws MenuNotFoundException 메뉴를 찾을 수 없는 경우 발생한다. + * @throws NotEqualsOwnerException 메뉴를 소유한 가게의 주인이 아닌 경우 발생한다. + * @throws StoreNotOpenException 가게가 열려있지 않은 경우 발생한다. + * @throws InvalidMenuStockUpdateException 메뉴의 재고를 변경할 수 없는 경우 발생한다. + */ + @Transactional + public Long updateMenuStock(UpdateMenuStockCommand cmd) { + // 수량을 변경하려는 메뉴를 조회한다. + Menu targetMenu = findMenuByIdForUpdateOrThrow(cmd.menuId()); + + // 메뉴를 소유한 가게를 조회한다. + if (!targetMenu.getStore().isOwnedBy(cmd.vendorId())) { + throw new NotEqualsOwnerException("메뉴를 소유한 가게의 주인이 아닙니다."); + } + + // 가게가 열려있는지 확인한다. + if (!targetMenu.getStore().isOpen()) { + throw new StoreNotOpenException("가게가 열려있지 않습니다."); + } + + // 메뉴의 재고를 변경한다. + int modifiedRow = menuRepository.updateStock(cmd.menuId(), cmd.stock()); + if (modifiedRow != 1) { // 변경된 메뉴의 개수가 1이 아닌 경우 예외를 발생시킨다. + throw new InvalidMenuStockUpdateException("변경의 영향을 받은 메뉴의 개수가 1이 아닙니다."); + } + + return targetMenu.getId(); + } + + private Menu findMenuByIdForUpdateOrThrow(Long menuId) { + return menuRepository.findByIdForUpdate(menuId).orElseThrow(() -> new MenuNotFoundException("메뉴를 찾을 수 없습니다.")); + } +} From 6d9ce65fd7d79d8aede11d49f721c0b339c5d58e Mon Sep 17 00:00:00 2001 From: kimhyun5u <22kimhyun5u@gmail.com> Date: Sun, 18 Aug 2024 15:44:34 +0900 Subject: [PATCH 04/21] =?UTF-8?q?[feat]=20UpdateMenuStockCommand=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lab/menu/service/command/UpdateMenuStockCommand.java | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/main/java/camp/woowak/lab/menu/service/command/UpdateMenuStockCommand.java diff --git a/src/main/java/camp/woowak/lab/menu/service/command/UpdateMenuStockCommand.java b/src/main/java/camp/woowak/lab/menu/service/command/UpdateMenuStockCommand.java new file mode 100644 index 00000000..dc2a9bed --- /dev/null +++ b/src/main/java/camp/woowak/lab/menu/service/command/UpdateMenuStockCommand.java @@ -0,0 +1,6 @@ +package camp.woowak.lab.menu.service.command; + +import java.util.UUID; + +public record UpdateMenuStockCommand(Long menuId, int stock, UUID vendorId) { +} From 9fa5676e2e710169b5e610dbf08b98c4afebf375 Mon Sep 17 00:00:00 2001 From: kimhyun5u <22kimhyun5u@gmail.com> Date: Sun, 18 Aug 2024 15:44:43 +0900 Subject: [PATCH 05/21] =?UTF-8?q?[feat]=20StoreNotOpenException=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../woowak/lab/menu/exception/StoreNotOpenException.java | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/main/java/camp/woowak/lab/menu/exception/StoreNotOpenException.java diff --git a/src/main/java/camp/woowak/lab/menu/exception/StoreNotOpenException.java b/src/main/java/camp/woowak/lab/menu/exception/StoreNotOpenException.java new file mode 100644 index 00000000..60cf8b10 --- /dev/null +++ b/src/main/java/camp/woowak/lab/menu/exception/StoreNotOpenException.java @@ -0,0 +1,9 @@ +package camp.woowak.lab.menu.exception; + +import camp.woowak.lab.common.exception.ConflictException; + +public class StoreNotOpenException extends ConflictException { + public StoreNotOpenException(String message) { + super(MenuErrorCode.NOT_OPEN_STORE, message); + } +} From d39462c05b65d88589c6cf4ae5d0e4f24f54c12d Mon Sep 17 00:00:00 2001 From: kimhyun5u <22kimhyun5u@gmail.com> Date: Sun, 18 Aug 2024 15:44:53 +0900 Subject: [PATCH 06/21] =?UTF-8?q?[feat]=20NotEqualsOwnerException=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lab/menu/exception/NotEqualsOwnerException.java | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/main/java/camp/woowak/lab/menu/exception/NotEqualsOwnerException.java diff --git a/src/main/java/camp/woowak/lab/menu/exception/NotEqualsOwnerException.java b/src/main/java/camp/woowak/lab/menu/exception/NotEqualsOwnerException.java new file mode 100644 index 00000000..9429b129 --- /dev/null +++ b/src/main/java/camp/woowak/lab/menu/exception/NotEqualsOwnerException.java @@ -0,0 +1,9 @@ +package camp.woowak.lab.menu.exception; + +import camp.woowak.lab.common.exception.BadRequestException; + +public class NotEqualsOwnerException extends BadRequestException { + public NotEqualsOwnerException(String message) { + super(MenuErrorCode.NOT_EQUALS_OWNER, message); + } +} From 2ec51f391c66a03412f4aeafe5ff69196bd06e3e Mon Sep 17 00:00:00 2001 From: kimhyun5u <22kimhyun5u@gmail.com> Date: Sun, 18 Aug 2024 15:45:02 +0900 Subject: [PATCH 07/21] =?UTF-8?q?[feat]=20InvalidMenuStockUpdateException?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../menu/exception/InvalidMenuStockUpdateException.java | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/main/java/camp/woowak/lab/menu/exception/InvalidMenuStockUpdateException.java diff --git a/src/main/java/camp/woowak/lab/menu/exception/InvalidMenuStockUpdateException.java b/src/main/java/camp/woowak/lab/menu/exception/InvalidMenuStockUpdateException.java new file mode 100644 index 00000000..d62ec3dd --- /dev/null +++ b/src/main/java/camp/woowak/lab/menu/exception/InvalidMenuStockUpdateException.java @@ -0,0 +1,9 @@ +package camp.woowak.lab.menu.exception; + +import camp.woowak.lab.common.exception.ConflictException; + +public class InvalidMenuStockUpdateException extends ConflictException { + public InvalidMenuStockUpdateException(String message) { + super(MenuErrorCode.INVALID_UPDATE_MENU_STOCK, message); + } +} From 76d638d594db24dc01353409de0f92646eb13a61 Mon Sep 17 00:00:00 2001 From: kimhyun5u <22kimhyun5u@gmail.com> Date: Sun, 18 Aug 2024 15:46:21 +0900 Subject: [PATCH 08/21] =?UTF-8?q?[feat]=20MenuErrorCode=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20-=20=EB=A9=94=EB=89=B4=20=EC=9E=AC=EA=B3=A0=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20=EB=B6=88=EA=B0=80=20=EC=97=90=EB=9F=AC,?= =?UTF-8?q?=20=EB=A7=A4=EC=9E=A5=20=EC=A0=90=EC=A3=BC=20=EB=B6=88=EC=9D=BC?= =?UTF-8?q?=EC=B9=98=20=EC=97=90=EB=9F=AC,=20=EB=A9=94=EB=89=B4=20?= =?UTF-8?q?=EC=88=98=EB=9F=89=20=EB=B3=80=EA=B2=BD=20=EC=8B=9C=EA=B0=84=20?= =?UTF-8?q?=EB=B6=88=EC=9D=BC=EC=B9=98=20=EC=97=90=EB=9F=AC=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../camp/woowak/lab/menu/exception/MenuErrorCode.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/camp/woowak/lab/menu/exception/MenuErrorCode.java b/src/main/java/camp/woowak/lab/menu/exception/MenuErrorCode.java index c3b15d1d..ef6e97dc 100644 --- a/src/main/java/camp/woowak/lab/menu/exception/MenuErrorCode.java +++ b/src/main/java/camp/woowak/lab/menu/exception/MenuErrorCode.java @@ -17,7 +17,13 @@ public enum MenuErrorCode implements ErrorCode { INVALID_STOCK_COUNT(HttpStatus.BAD_REQUEST, "m_7", "메뉴의 재고 개수는 1개 이상이어야 합니다."), NOT_FOUND_MENU_CATEGORY(HttpStatus.BAD_REQUEST, "M3", "메뉴 카테고리를 찾을 수 없습니다."), - NOT_ENOUGH_STOCK(HttpStatus.BAD_REQUEST, "M4", "재고가 부족합니다."); + NOT_ENOUGH_STOCK(HttpStatus.BAD_REQUEST, "M4", "재고가 부족합니다."), + INVALID_UPDATE_MENU_STOCK(HttpStatus.CONFLICT, "m_9", "메뉴의 재고를 변경할 수 없습니다."), + + NOT_EQUALS_OWNER(HttpStatus.BAD_REQUEST, "m_10", "매장의 점주와 일치하지 않습니다."), + NOT_OPEN_STORE(HttpStatus.CONFLICT, "m_11", "가게가 열려있지 않습니다."), + + ; private final int status; private final String errorCode; From ef93db6423b38ed4822d6390d528bebacafccb8a Mon Sep 17 00:00:00 2001 From: kimhyun5u <22kimhyun5u@gmail.com> Date: Sun, 18 Aug 2024 16:52:37 +0900 Subject: [PATCH 09/21] =?UTF-8?q?[fix]=20MenuRepository=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20-=20findByIdForUpdate=20`@Query`=20=EB=AA=85?= =?UTF-8?q?=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/camp/woowak/lab/menu/repository/MenuRepository.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/camp/woowak/lab/menu/repository/MenuRepository.java b/src/main/java/camp/woowak/lab/menu/repository/MenuRepository.java index 44c794c6..19ccb74e 100644 --- a/src/main/java/camp/woowak/lab/menu/repository/MenuRepository.java +++ b/src/main/java/camp/woowak/lab/menu/repository/MenuRepository.java @@ -21,6 +21,7 @@ public interface MenuRepository extends JpaRepository { List findAllByIdForUpdate(List ids); @Lock(LockModeType.PESSIMISTIC_WRITE) + @Query("SELECT m FROM Menu m WHERE m.id = :id") Optional findByIdForUpdate(Long id); /** From ff2ba69d142a622994148a435afc79b2e98d2ef6 Mon Sep 17 00:00:00 2001 From: kimhyun5u <22kimhyun5u@gmail.com> Date: Sun, 18 Aug 2024 17:01:19 +0900 Subject: [PATCH 10/21] =?UTF-8?q?[fix]=20UpdateMenuStockService=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20-=20=EA=B0=80=EA=B2=8C=EA=B0=80=20?= =?UTF-8?q?=EC=97=B4=EB=A0=A4=EC=9E=88=EB=8A=94=20=EA=B2=BD=EC=9A=B0=20?= =?UTF-8?q?=EC=88=98=EB=9F=89=20=EC=88=98=EC=A0=95=EC=9D=B4=20=EC=95=88?= =?UTF-8?q?=EB=90=98=EB=8F=84=EB=A1=9D=20=EB=A1=9C=EC=A7=81=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../woowak/lab/menu/service/UpdateMenuStockService.java | 4 ++-- .../lab/menu/service/UpdateMenuStockServiceTest.java | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/camp/woowak/lab/menu/service/UpdateMenuStockService.java b/src/main/java/camp/woowak/lab/menu/service/UpdateMenuStockService.java index bbdf2ad0..8fd166d3 100644 --- a/src/main/java/camp/woowak/lab/menu/service/UpdateMenuStockService.java +++ b/src/main/java/camp/woowak/lab/menu/service/UpdateMenuStockService.java @@ -37,8 +37,8 @@ public Long updateMenuStock(UpdateMenuStockCommand cmd) { } // 가게가 열려있는지 확인한다. - if (!targetMenu.getStore().isOpen()) { - throw new StoreNotOpenException("가게가 열려있지 않습니다."); + if (targetMenu.getStore().isOpen()) { + throw new NotUpdatableTimeException("가게가 열려있지 않습니다."); } // 메뉴의 재고를 변경한다. diff --git a/src/test/java/camp/woowak/lab/menu/service/UpdateMenuStockServiceTest.java b/src/test/java/camp/woowak/lab/menu/service/UpdateMenuStockServiceTest.java index dd120b71..61d16db1 100644 --- a/src/test/java/camp/woowak/lab/menu/service/UpdateMenuStockServiceTest.java +++ b/src/test/java/camp/woowak/lab/menu/service/UpdateMenuStockServiceTest.java @@ -44,7 +44,7 @@ void testUpdateMenuStock() { given(fakeMenu.getStore()).willReturn(fakeStore); given(fakeStore.isOwnedBy(any(UUID.class))).willReturn(true); given(menuRepository.findByIdForUpdate(anyLong())).willReturn(Optional.of(fakeMenu)); - given(fakeStore.isOpen()).willReturn(true); + given(fakeStore.isOpen()).willReturn(false); given(menuRepository.updateStock(anyLong(), anyInt())).willReturn(1); UpdateMenuStockCommand cmd = new UpdateMenuStockCommand(menuId, stock, vendorId); @@ -105,7 +105,7 @@ void testUpdateMenuStockNotEqualsOwner() { } @Test - @DisplayName("메뉴 재고 업데이트 테스트 - 매장이 열려있지 않은 경우") + @DisplayName("메뉴 재고 업데이트 테스트 - 매장이 열려있는 경우") void testUpdateMenuStockStoreNotOpen() { // given Long menuId = 1L; @@ -116,7 +116,7 @@ void testUpdateMenuStockStoreNotOpen() { given(fakeMenu.getStore()).willReturn(fakeStore); given(fakeStore.isOwnedBy(any(UUID.class))).willReturn(true); - given(fakeStore.isOpen()).willReturn(false); + given(fakeStore.isOpen()).willReturn(true); given(menuRepository.findByIdForUpdate(anyLong())).willReturn(Optional.of(fakeMenu)); UpdateMenuStockCommand cmd = new UpdateMenuStockCommand(menuId, stock, vendorId); @@ -143,7 +143,7 @@ void testUpdateMenuStockInvalid() { given(fakeMenu.getStore()).willReturn(fakeStore); given(fakeStore.isOwnedBy(any(UUID.class))).willReturn(true); - given(fakeStore.isOpen()).willReturn(true); + given(fakeStore.isOpen()).willReturn(false); given(menuRepository.findByIdForUpdate(anyLong())).willReturn(Optional.of(fakeMenu)); given(menuRepository.updateStock(anyLong(), anyInt())).willReturn(0); From 311b1b3d1dd5addea2ffa192f3409efae2094dec Mon Sep 17 00:00:00 2001 From: kimhyun5u <22kimhyun5u@gmail.com> Date: Sun, 18 Aug 2024 17:01:54 +0900 Subject: [PATCH 11/21] =?UTF-8?q?[fix]=20MenuErrorCode=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20-=20=EA=B0=80=EA=B2=8C=EA=B0=80=20=EB=8B=AB?= =?UTF-8?q?=ED=98=80=EC=9E=88=EC=9D=84=20=EB=95=8C=EA=B0=80=20=EC=95=84?= =?UTF-8?q?=EB=8B=8C=20=EC=97=B4=EB=A0=A4=20=EC=9E=88=EC=9D=84=20=EB=95=8C?= =?UTF-8?q?=20=EC=97=90=EB=9F=AC=20=EC=BD=94=EB=93=9C=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/camp/woowak/lab/menu/exception/MenuErrorCode.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/camp/woowak/lab/menu/exception/MenuErrorCode.java b/src/main/java/camp/woowak/lab/menu/exception/MenuErrorCode.java index ef6e97dc..2a7ab781 100644 --- a/src/main/java/camp/woowak/lab/menu/exception/MenuErrorCode.java +++ b/src/main/java/camp/woowak/lab/menu/exception/MenuErrorCode.java @@ -21,8 +21,7 @@ public enum MenuErrorCode implements ErrorCode { INVALID_UPDATE_MENU_STOCK(HttpStatus.CONFLICT, "m_9", "메뉴의 재고를 변경할 수 없습니다."), NOT_EQUALS_OWNER(HttpStatus.BAD_REQUEST, "m_10", "매장의 점주와 일치하지 않습니다."), - NOT_OPEN_STORE(HttpStatus.CONFLICT, "m_11", "가게가 열려있지 않습니다."), - + NOT_UPDATABLE_TIME(HttpStatus.CONFLICT, "m_11", "메뉴를 변경할 수 없는 시간입니다."), ; private final int status; From bdf66984a427db3320940f86d7ed0bc516e6e5d3 Mon Sep 17 00:00:00 2001 From: kimhyun5u <22kimhyun5u@gmail.com> Date: Sun, 18 Aug 2024 17:02:28 +0900 Subject: [PATCH 12/21] =?UTF-8?q?[fix]=20NotUpdatableTimeException=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20-=20=EA=B8=B0=EC=A1=B4=20StoreNotOpenExcep?= =?UTF-8?q?tion=20=EC=9D=B4=20=EC=95=84=EB=8B=8C=20=EA=B0=80=EA=B2=8C?= =?UTF-8?q?=EA=B0=80=20=EC=97=B4=EB=A0=A4=20=EC=9E=88=EC=9D=84=20=EB=95=8C?= =?UTF-8?q?=20=EB=B0=9C=EC=83=9D=ED=95=98=EB=8A=94=20=EC=98=88=EC=99=B8?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lab/menu/exception/NotUpdatableTimeException.java | 9 +++++++++ .../woowak/lab/menu/exception/StoreNotOpenException.java | 9 --------- .../woowak/lab/menu/service/UpdateMenuStockService.java | 4 ++-- .../lab/menu/service/UpdateMenuStockServiceTest.java | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) create mode 100644 src/main/java/camp/woowak/lab/menu/exception/NotUpdatableTimeException.java delete mode 100644 src/main/java/camp/woowak/lab/menu/exception/StoreNotOpenException.java diff --git a/src/main/java/camp/woowak/lab/menu/exception/NotUpdatableTimeException.java b/src/main/java/camp/woowak/lab/menu/exception/NotUpdatableTimeException.java new file mode 100644 index 00000000..8af402b3 --- /dev/null +++ b/src/main/java/camp/woowak/lab/menu/exception/NotUpdatableTimeException.java @@ -0,0 +1,9 @@ +package camp.woowak.lab.menu.exception; + +import camp.woowak.lab.common.exception.ConflictException; + +public class NotUpdatableTimeException extends ConflictException { + public NotUpdatableTimeException(String message) { + super(MenuErrorCode.NOT_UPDATABLE_TIME, message); + } +} diff --git a/src/main/java/camp/woowak/lab/menu/exception/StoreNotOpenException.java b/src/main/java/camp/woowak/lab/menu/exception/StoreNotOpenException.java deleted file mode 100644 index 60cf8b10..00000000 --- a/src/main/java/camp/woowak/lab/menu/exception/StoreNotOpenException.java +++ /dev/null @@ -1,9 +0,0 @@ -package camp.woowak.lab.menu.exception; - -import camp.woowak.lab.common.exception.ConflictException; - -public class StoreNotOpenException extends ConflictException { - public StoreNotOpenException(String message) { - super(MenuErrorCode.NOT_OPEN_STORE, message); - } -} diff --git a/src/main/java/camp/woowak/lab/menu/service/UpdateMenuStockService.java b/src/main/java/camp/woowak/lab/menu/service/UpdateMenuStockService.java index 8fd166d3..05e02702 100644 --- a/src/main/java/camp/woowak/lab/menu/service/UpdateMenuStockService.java +++ b/src/main/java/camp/woowak/lab/menu/service/UpdateMenuStockService.java @@ -7,7 +7,7 @@ import camp.woowak.lab.menu.domain.Menu; import camp.woowak.lab.menu.exception.InvalidMenuStockUpdateException; import camp.woowak.lab.menu.exception.NotEqualsOwnerException; -import camp.woowak.lab.menu.exception.StoreNotOpenException; +import camp.woowak.lab.menu.exception.NotUpdatableTimeException; import camp.woowak.lab.menu.repository.MenuRepository; import camp.woowak.lab.menu.service.command.UpdateMenuStockCommand; @@ -23,7 +23,7 @@ public UpdateMenuStockService(MenuRepository menuRepository) { * * @throws MenuNotFoundException 메뉴를 찾을 수 없는 경우 발생한다. * @throws NotEqualsOwnerException 메뉴를 소유한 가게의 주인이 아닌 경우 발생한다. - * @throws StoreNotOpenException 가게가 열려있지 않은 경우 발생한다. + * @throws NotUpdatableTimeException 가게가 열려있지 않은 경우 발생한다. * @throws InvalidMenuStockUpdateException 메뉴의 재고를 변경할 수 없는 경우 발생한다. */ @Transactional diff --git a/src/test/java/camp/woowak/lab/menu/service/UpdateMenuStockServiceTest.java b/src/test/java/camp/woowak/lab/menu/service/UpdateMenuStockServiceTest.java index 61d16db1..cd5de0ad 100644 --- a/src/test/java/camp/woowak/lab/menu/service/UpdateMenuStockServiceTest.java +++ b/src/test/java/camp/woowak/lab/menu/service/UpdateMenuStockServiceTest.java @@ -18,7 +18,7 @@ import camp.woowak.lab.menu.domain.Menu; import camp.woowak.lab.menu.exception.InvalidMenuStockUpdateException; import camp.woowak.lab.menu.exception.NotEqualsOwnerException; -import camp.woowak.lab.menu.exception.StoreNotOpenException; +import camp.woowak.lab.menu.exception.NotUpdatableTimeException; import camp.woowak.lab.menu.repository.MenuRepository; import camp.woowak.lab.menu.service.command.UpdateMenuStockCommand; import camp.woowak.lab.store.domain.Store; @@ -123,7 +123,7 @@ void testUpdateMenuStockStoreNotOpen() { // when // then - assertThrows(StoreNotOpenException.class, () -> updateMenuStockService.updateMenuStock(cmd)); + assertThrows(NotUpdatableTimeException.class, () -> updateMenuStockService.updateMenuStock(cmd)); verify(menuRepository, times(1)).findByIdForUpdate(anyLong()); verify(fakeMenu, times(2)).getStore(); verify(fakeStore, times(1)).isOwnedBy(any(UUID.class)); From 14a7cd8e2a76f3a7e9b3fd6cabebb76616e07a62 Mon Sep 17 00:00:00 2001 From: kimhyun5u <22kimhyun5u@gmail.com> Date: Sun, 18 Aug 2024 17:09:07 +0900 Subject: [PATCH 13/21] =?UTF-8?q?[test]=20MenuApiControllerTest=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20-=20testUpdateMenuStock:=20=EB=A9=94?= =?UTF-8?q?=EB=89=B4=20=EC=9E=AC=EA=B3=A0=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20=EA=B2=80=EC=A6=9D=20-=20testUpdateMenuStockFailWit?= =?UTF-8?q?hMenuIdNull:=20=EB=A9=94=EB=89=B4=20id=20=EA=B0=80=20null=20?= =?UTF-8?q?=EC=9D=B8=20=EA=B2=BD=EC=9A=B0=20=EA=B2=80=EC=A6=9D=20-=20testU?= =?UTF-8?q?pdateMenuStockFailWithStockUnderZero:=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?=EC=9A=94=EC=B2=AD=20=EC=9E=AC=EA=B3=A0=EA=B0=80=200=20?= =?UTF-8?q?=EB=AF=B8=EB=A7=8C=EC=9D=B8=20=EA=B2=BD=EC=9A=B0=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20-=20testUpdateMenuStockFail:=20=EC=A0=90=EC=A3=BC?= =?UTF-8?q?=20=EB=B6=88=EC=9D=BC=EC=B9=98=20=EC=98=88=EC=99=B8=20=ED=95=B8?= =?UTF-8?q?=EB=93=A4=EB=A7=81=20=EA=B2=80=EC=A6=9D=20-=20testUpdateMenuSto?= =?UTF-8?q?ckFailWithUnauthorized:=20=EC=9D=B8=EC=A6=9D=EB=90=98=EC=A7=80?= =?UTF-8?q?=20=EC=95=8A=EC=9D=80=20=EC=9A=94=EC=B2=AD=20=ED=95=B8=EB=93=A4?= =?UTF-8?q?=EB=A7=81=20=EA=B2=80=EC=A6=9D=20-=20testUpdateMenuStockFailWit?= =?UTF-8?q?hStoreNotOpen:=20=EB=A7=A4=EC=9E=A5=EC=9D=B4=20=EC=97=B4?= =?UTF-8?q?=EB=A0=A4=EC=9E=88=EB=8A=94=20=EA=B2=BD=EC=9A=B0=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=20=ED=95=B8=EB=93=A4=EB=A7=81=20=EA=B2=80=EC=A6=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/api/menu/MenuApiControllerTest.java | 157 ++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 src/test/java/camp/woowak/lab/web/api/menu/MenuApiControllerTest.java diff --git a/src/test/java/camp/woowak/lab/web/api/menu/MenuApiControllerTest.java b/src/test/java/camp/woowak/lab/web/api/menu/MenuApiControllerTest.java new file mode 100644 index 00000000..d3ebacaa --- /dev/null +++ b/src/test/java/camp/woowak/lab/web/api/menu/MenuApiControllerTest.java @@ -0,0 +1,157 @@ +package camp.woowak.lab.web.api.menu; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.BDDMockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.util.UUID; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.data.jpa.mapping.JpaMetamodelMappingContext; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockHttpSession; +import org.springframework.test.web.servlet.MockMvc; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import camp.woowak.lab.menu.exception.NotEqualsOwnerException; +import camp.woowak.lab.menu.exception.NotUpdatableTimeException; +import camp.woowak.lab.menu.service.UpdateMenuStockService; +import camp.woowak.lab.menu.service.command.UpdateMenuStockCommand; +import camp.woowak.lab.web.authentication.LoginVendor; +import camp.woowak.lab.web.dto.request.menu.UpdateMenuStockRequest; +import camp.woowak.lab.web.resolver.session.SessionConst; + +@WebMvcTest(MenuApiController.class) +@MockBean(JpaMetamodelMappingContext.class) +class MenuApiControllerTest { + + @Autowired + private MockMvc mockMvc; + + @MockBean + private UpdateMenuStockService updateMenuStockService; + + @Autowired + private ObjectMapper objectMapper; + + @Test + @DisplayName("메뉴 재고 업데이트 테스트 - 성공") + void testUpdateMenuStock() throws Exception { + // given + Long menuId = 1L; + int stock = 10; + MockHttpSession session = new MockHttpSession(); + session.setAttribute(SessionConst.SESSION_VENDOR_KEY, new LoginVendor(UUID.randomUUID())); + UpdateMenuStockRequest request = new UpdateMenuStockRequest(menuId, stock); + + given(updateMenuStockService.updateMenuStock(any(UpdateMenuStockCommand.class))).willReturn(menuId); + + // when + mockMvc.perform(put("/menus/stock") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request)) + .session(session)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.menuId").value(menuId)); + } + + @Test + @DisplayName("메뉴 재고 업데이트 테스트 - 실패(stock이 0 미만)") + void testUpdateMenuStockFailWithStockUnderZero() throws Exception { + // given + Long menuId = 1L; + int stock = -1; + MockHttpSession session = new MockHttpSession(); + session.setAttribute(SessionConst.SESSION_VENDOR_KEY, new LoginVendor(UUID.randomUUID())); + UpdateMenuStockRequest request = new UpdateMenuStockRequest(menuId, stock); + + // when + mockMvc.perform(put("/menus/stock") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request)) + .session(session)) + .andExpect(status().isBadRequest()); + } + + @Test + @DisplayName("메뉴 재고 업데이트 테스트 - 실패(메뉴id null)") + void testUpdateMenuStockFailWithMenuIdNull() throws Exception { + // given + Long menuId = null; + int stock = 10; + MockHttpSession session = new MockHttpSession(); + session.setAttribute(SessionConst.SESSION_VENDOR_KEY, new LoginVendor(UUID.randomUUID())); + UpdateMenuStockRequest request = new UpdateMenuStockRequest(menuId, stock); + + // when + mockMvc.perform(put("/menus/stock") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request)) + .session(session)) + .andExpect(status().isBadRequest()); + } + + @Test + @DisplayName("메뉴 재고 업데이트 테스트 - 실패(점주 불일치)") + void testUpdateMenuStockFail() throws Exception { + // given + Long menuId = 1L; + int stock = 10; + MockHttpSession session = new MockHttpSession(); + session.setAttribute(SessionConst.SESSION_VENDOR_KEY, new LoginVendor(UUID.randomUUID())); + UpdateMenuStockRequest request = new UpdateMenuStockRequest(menuId, stock); + + given(updateMenuStockService.updateMenuStock(any(UpdateMenuStockCommand.class))).willThrow( + new NotEqualsOwnerException("매장 주인이 아닙니다.")); + + // when + mockMvc.perform(put("/menus/stock") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request)) + .session(session)) + .andExpect(status().isBadRequest()); + } + + @Test + @DisplayName("메뉴 재고 업데이트 테스트 - 실패(인증되지 않음)") + void testUpdateMenuStockFailWithUnauthorized() throws Exception { + // given + Long menuId = 1L; + int stock = 10; + UpdateMenuStockRequest request = new UpdateMenuStockRequest(menuId, stock); + + // when + mockMvc.perform(put("/menus/stock") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))) + .andExpect(status().isUnauthorized()); + } + + @Test + @DisplayName("메뉴 재고 업데이트 테스트 - 실패(매장이 열려있음)") + void testUpdateMenuStockFailWithStoreNotOpen() throws Exception { + // given + Long menuId = 1L; + int stock = 10; + MockHttpSession session = new MockHttpSession(); + session.setAttribute(SessionConst.SESSION_VENDOR_KEY, new LoginVendor(UUID.randomUUID())); + UpdateMenuStockRequest request = new UpdateMenuStockRequest(menuId, stock); + + given(updateMenuStockService.updateMenuStock(any(UpdateMenuStockCommand.class))).willThrow( + new NotUpdatableTimeException("매장이 열려있습니다.")); + + // when + mockMvc.perform(put("/menus/stock") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request)) + .session(session)) + .andExpect(status().isConflict()); + } + +} From 43f2777e15016d8266ceb7364888dfdc0d10b67a Mon Sep 17 00:00:00 2001 From: kimhyun5u <22kimhyun5u@gmail.com> Date: Sun, 18 Aug 2024 17:09:49 +0900 Subject: [PATCH 14/21] =?UTF-8?q?[feat]=20MenuApiController=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20-=20updateMenuStock:=20=EB=A9=94=EB=89=B4=20?= =?UTF-8?q?=EC=9E=AC=EA=B3=A0=20=EB=B3=80=EA=B2=BD=20=EC=9A=94=EC=B2=AD=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EB=A9=94=EC=84=9C=EB=93=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lab/web/api/menu/MenuApiController.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/main/java/camp/woowak/lab/web/api/menu/MenuApiController.java diff --git a/src/main/java/camp/woowak/lab/web/api/menu/MenuApiController.java b/src/main/java/camp/woowak/lab/web/api/menu/MenuApiController.java new file mode 100644 index 00000000..8eb2610c --- /dev/null +++ b/src/main/java/camp/woowak/lab/web/api/menu/MenuApiController.java @@ -0,0 +1,39 @@ +package camp.woowak.lab.web.api.menu; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; + +import camp.woowak.lab.menu.service.UpdateMenuStockService; +import camp.woowak.lab.menu.service.command.UpdateMenuStockCommand; +import camp.woowak.lab.web.authentication.LoginVendor; +import camp.woowak.lab.web.authentication.annotation.AuthenticationPrincipal; +import camp.woowak.lab.web.dto.request.menu.UpdateMenuStockRequest; +import camp.woowak.lab.web.dto.response.menu.UpdateMenuStockResponse; +import jakarta.validation.Valid; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@RestController +public class MenuApiController { + private final UpdateMenuStockService updateMenuStockService; + + public MenuApiController(UpdateMenuStockService updateMenuStockService) { + this.updateMenuStockService = updateMenuStockService; + } + + @ResponseStatus(HttpStatus.OK) + @PutMapping("/menus/stock") + public UpdateMenuStockResponse updateMenuStock(@AuthenticationPrincipal LoginVendor vendor, + @Valid @RequestBody UpdateMenuStockRequest request) { + UpdateMenuStockCommand cmd = new UpdateMenuStockCommand(request.menuId(), request.stock(), vendor.getId()); + + Long updatedId = updateMenuStockService.updateMenuStock(cmd); + + log.info("메뉴 재고 업데이트 완료: menuId={}, newStock={}, vendorId={}", updatedId, request.stock(), vendor.getId()); + + return new UpdateMenuStockResponse(updatedId); + } +} From ef191b6c9e6f0229acb03e9e739068846a3a12c8 Mon Sep 17 00:00:00 2001 From: kimhyun5u <22kimhyun5u@gmail.com> Date: Sun, 18 Aug 2024 17:11:23 +0900 Subject: [PATCH 15/21] =?UTF-8?q?[feat]=20MenuExceptionHandler=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/api/menu/MenuExceptionHandler.java | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 src/main/java/camp/woowak/lab/web/api/menu/MenuExceptionHandler.java diff --git a/src/main/java/camp/woowak/lab/web/api/menu/MenuExceptionHandler.java b/src/main/java/camp/woowak/lab/web/api/menu/MenuExceptionHandler.java new file mode 100644 index 00000000..da8763b5 --- /dev/null +++ b/src/main/java/camp/woowak/lab/web/api/menu/MenuExceptionHandler.java @@ -0,0 +1,40 @@ +package camp.woowak.lab.web.api.menu; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ProblemDetail; +import org.springframework.web.bind.annotation.ExceptionHandler; + +import camp.woowak.lab.common.advice.DomainExceptionHandler; +import camp.woowak.lab.common.exception.HttpStatusException; +import camp.woowak.lab.menu.exception.InvalidMenuStockUpdateException; +import camp.woowak.lab.menu.exception.NotEqualsOwnerException; +import camp.woowak.lab.menu.exception.NotUpdatableTimeException; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@DomainExceptionHandler(basePackageClasses = MenuApiController.class) +public class MenuExceptionHandler { + + @ExceptionHandler(value = NotUpdatableTimeException.class) + public ProblemDetail handleInvalidMenuStockUpdateException(NotUpdatableTimeException e) { + log.warn("Conflict", e); + return getProblemDetail(HttpStatus.CONFLICT, e); + } + + @ExceptionHandler(value = NotEqualsOwnerException.class) + public ProblemDetail handleNotEqualsOwnerException(NotEqualsOwnerException e) { + log.warn("Bad Request", e); + return getProblemDetail(HttpStatus.BAD_REQUEST, e); + } + + @ExceptionHandler(value = InvalidMenuStockUpdateException.class) + public ProblemDetail handleInvalidMenuStockUpdateException(InvalidMenuStockUpdateException e) { + log.warn("Conflict", e); + return getProblemDetail(HttpStatus.CONFLICT, e); + } + + private ProblemDetail getProblemDetail(HttpStatus status, HttpStatusException e) { + return ProblemDetail.forStatusAndDetail(status, e.errorCode().getMessage()); + } + +} From 2c8851f967a8b1895007be787a4ec52eb8f05a37 Mon Sep 17 00:00:00 2001 From: kimhyun5u <22kimhyun5u@gmail.com> Date: Sun, 18 Aug 2024 17:11:34 +0900 Subject: [PATCH 16/21] =?UTF-8?q?[feat]=20UpdateMenuStockRequest=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/dto/request/menu/UpdateMenuStockRequest.java | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/main/java/camp/woowak/lab/web/dto/request/menu/UpdateMenuStockRequest.java diff --git a/src/main/java/camp/woowak/lab/web/dto/request/menu/UpdateMenuStockRequest.java b/src/main/java/camp/woowak/lab/web/dto/request/menu/UpdateMenuStockRequest.java new file mode 100644 index 00000000..672a102c --- /dev/null +++ b/src/main/java/camp/woowak/lab/web/dto/request/menu/UpdateMenuStockRequest.java @@ -0,0 +1,11 @@ +package camp.woowak.lab.web.dto.request.menu; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; + +public record UpdateMenuStockRequest( + @NotNull + Long menuId, + @Min(value = 0) + int stock) { +} From c58293e402611a766ff920c03257d1a68478793d Mon Sep 17 00:00:00 2001 From: kimhyun5u <22kimhyun5u@gmail.com> Date: Sun, 18 Aug 2024 17:11:45 +0900 Subject: [PATCH 17/21] =?UTF-8?q?[feat]=20UpdateMenuStockResponse=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lab/web/dto/response/menu/UpdateMenuStockResponse.java | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/main/java/camp/woowak/lab/web/dto/response/menu/UpdateMenuStockResponse.java diff --git a/src/main/java/camp/woowak/lab/web/dto/response/menu/UpdateMenuStockResponse.java b/src/main/java/camp/woowak/lab/web/dto/response/menu/UpdateMenuStockResponse.java new file mode 100644 index 00000000..19287ad9 --- /dev/null +++ b/src/main/java/camp/woowak/lab/web/dto/response/menu/UpdateMenuStockResponse.java @@ -0,0 +1,4 @@ +package camp.woowak.lab.web.dto.response.menu; + +public record UpdateMenuStockResponse(Long menuId) { +} From 14ab35d88ecd8a9933ba7f9c5fd5fb4b2c2e4976 Mon Sep 17 00:00:00 2001 From: kimhyun5u <22kimhyun5u@gmail.com> Date: Sun, 18 Aug 2024 18:07:49 +0900 Subject: [PATCH 18/21] =?UTF-8?q?[docs]=20UpdateMenuStockResponse=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20-=20Validate=20Message=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lab/web/dto/request/menu/UpdateMenuStockRequest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/camp/woowak/lab/web/dto/request/menu/UpdateMenuStockRequest.java b/src/main/java/camp/woowak/lab/web/dto/request/menu/UpdateMenuStockRequest.java index 672a102c..41865ea0 100644 --- a/src/main/java/camp/woowak/lab/web/dto/request/menu/UpdateMenuStockRequest.java +++ b/src/main/java/camp/woowak/lab/web/dto/request/menu/UpdateMenuStockRequest.java @@ -4,8 +4,8 @@ import jakarta.validation.constraints.NotNull; public record UpdateMenuStockRequest( - @NotNull + @NotNull(message = "메뉴 ID는 필수입니다.") Long menuId, - @Min(value = 0) + @Min(value = 0, message = "재고는 0 이상이어야 합니다.") int stock) { } From 9b4a2a25948a945ea5028f7ff8e23eb625c5237d Mon Sep 17 00:00:00 2001 From: kimhyun5u <22kimhyun5u@gmail.com> Date: Sun, 18 Aug 2024 18:10:59 +0900 Subject: [PATCH 19/21] =?UTF-8?q?[fix]=20Http=20Method=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/camp/woowak/lab/web/api/menu/MenuApiController.java | 4 ++-- .../camp/woowak/lab/web/api/menu/MenuApiControllerTest.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/camp/woowak/lab/web/api/menu/MenuApiController.java b/src/main/java/camp/woowak/lab/web/api/menu/MenuApiController.java index 8eb2610c..9188f56e 100644 --- a/src/main/java/camp/woowak/lab/web/api/menu/MenuApiController.java +++ b/src/main/java/camp/woowak/lab/web/api/menu/MenuApiController.java @@ -1,7 +1,7 @@ package camp.woowak.lab.web.api.menu; import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; @@ -25,7 +25,7 @@ public MenuApiController(UpdateMenuStockService updateMenuStockService) { } @ResponseStatus(HttpStatus.OK) - @PutMapping("/menus/stock") + @PatchMapping("/menus/stock") public UpdateMenuStockResponse updateMenuStock(@AuthenticationPrincipal LoginVendor vendor, @Valid @RequestBody UpdateMenuStockRequest request) { UpdateMenuStockCommand cmd = new UpdateMenuStockCommand(request.menuId(), request.stock(), vendor.getId()); diff --git a/src/test/java/camp/woowak/lab/web/api/menu/MenuApiControllerTest.java b/src/test/java/camp/woowak/lab/web/api/menu/MenuApiControllerTest.java index d3ebacaa..e5fbf4cb 100644 --- a/src/test/java/camp/woowak/lab/web/api/menu/MenuApiControllerTest.java +++ b/src/test/java/camp/woowak/lab/web/api/menu/MenuApiControllerTest.java @@ -111,7 +111,7 @@ void testUpdateMenuStockFail() throws Exception { new NotEqualsOwnerException("매장 주인이 아닙니다.")); // when - mockMvc.perform(put("/menus/stock") + mockMvc.perform(patch("/menus/stock") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request)) .session(session)) From 9b0e736af391ca9149f6548e774c4c0319051090 Mon Sep 17 00:00:00 2001 From: kimhyun5u <22kimhyun5u@gmail.com> Date: Sun, 18 Aug 2024 18:11:58 +0900 Subject: [PATCH 20/21] =?UTF-8?q?[fix]=20Http=20Method=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../woowak/lab/web/api/menu/MenuApiControllerTest.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/java/camp/woowak/lab/web/api/menu/MenuApiControllerTest.java b/src/test/java/camp/woowak/lab/web/api/menu/MenuApiControllerTest.java index e5fbf4cb..830acd59 100644 --- a/src/test/java/camp/woowak/lab/web/api/menu/MenuApiControllerTest.java +++ b/src/test/java/camp/woowak/lab/web/api/menu/MenuApiControllerTest.java @@ -53,7 +53,7 @@ void testUpdateMenuStock() throws Exception { given(updateMenuStockService.updateMenuStock(any(UpdateMenuStockCommand.class))).willReturn(menuId); // when - mockMvc.perform(put("/menus/stock") + mockMvc.perform(patch("/menus/stock") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request)) .session(session)) @@ -72,7 +72,7 @@ void testUpdateMenuStockFailWithStockUnderZero() throws Exception { UpdateMenuStockRequest request = new UpdateMenuStockRequest(menuId, stock); // when - mockMvc.perform(put("/menus/stock") + mockMvc.perform(patch("/menus/stock") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request)) .session(session)) @@ -90,7 +90,7 @@ void testUpdateMenuStockFailWithMenuIdNull() throws Exception { UpdateMenuStockRequest request = new UpdateMenuStockRequest(menuId, stock); // when - mockMvc.perform(put("/menus/stock") + mockMvc.perform(patch("/menus/stock") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request)) .session(session)) @@ -127,7 +127,7 @@ void testUpdateMenuStockFailWithUnauthorized() throws Exception { UpdateMenuStockRequest request = new UpdateMenuStockRequest(menuId, stock); // when - mockMvc.perform(put("/menus/stock") + mockMvc.perform(patch("/menus/stock") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request))) .andExpect(status().isUnauthorized()); @@ -147,7 +147,7 @@ void testUpdateMenuStockFailWithStoreNotOpen() throws Exception { new NotUpdatableTimeException("매장이 열려있습니다.")); // when - mockMvc.perform(put("/menus/stock") + mockMvc.perform(patch("/menus/stock") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request)) .session(session)) From 394301fad43ae5c26f7d28d757fbeaaab37f92ce Mon Sep 17 00:00:00 2001 From: kimhyun5u <22kimhyun5u@gmail.com> Date: Mon, 19 Aug 2024 00:27:17 +0900 Subject: [PATCH 21/21] =?UTF-8?q?[docs]=20UpdateMenuStockService=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20-=20NotUpdatableTimeException=20=EB=A9=94?= =?UTF-8?q?=EC=84=B8=EC=A7=80=20=EC=A0=95=ED=99=95=ED=95=98=EA=B2=8C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../camp/woowak/lab/menu/service/UpdateMenuStockService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/camp/woowak/lab/menu/service/UpdateMenuStockService.java b/src/main/java/camp/woowak/lab/menu/service/UpdateMenuStockService.java index 05e02702..e741e1f1 100644 --- a/src/main/java/camp/woowak/lab/menu/service/UpdateMenuStockService.java +++ b/src/main/java/camp/woowak/lab/menu/service/UpdateMenuStockService.java @@ -38,7 +38,7 @@ public Long updateMenuStock(UpdateMenuStockCommand cmd) { // 가게가 열려있는지 확인한다. if (targetMenu.getStore().isOpen()) { - throw new NotUpdatableTimeException("가게가 열려있지 않습니다."); + throw new NotUpdatableTimeException("가게가 열려 있습니다."); } // 메뉴의 재고를 변경한다.