diff --git a/src/main/java/com/gg/server/admin/item/service/ItemAdminService.java b/src/main/java/com/gg/server/admin/item/service/ItemAdminService.java index 5c4267656..3c78b55eb 100644 --- a/src/main/java/com/gg/server/admin/item/service/ItemAdminService.java +++ b/src/main/java/com/gg/server/admin/item/service/ItemAdminService.java @@ -27,6 +27,11 @@ public class ItemAdminService { private final ItemAdminRepository itemAdminRepository; private final AsyncNewItemImageUploader asyncNewItemImageUploader; + /** + *
Item 히스토리를 반환한다.
+ * @param pageable + * @return + */ @Transactional(readOnly = true) public ItemListResponseDto getAllItemHistory(Pageable pageable) { Page아이템 수정 시 신규 이미지가 존재하는 경우
+ *기존 아이템의 item.isVisible 를 false 로 변경한다
+ * @param itemId 타겟 아이템 + * @param itemUpdateRequestDto 타겟 아이템 변경 dto + * @param itemImageFile 신규 이미지 + * @param user 바꾸는 유저 id + * @throws IOException IOException + * @throws ItemNotFoundException 아이템 없음 + * @throws ItemNotAvailableException 접근 불가한 아이템 + */ @Transactional public void updateItem(Long itemId, ItemUpdateRequestDto itemUpdateRequestDto, MultipartFile itemImageFile, UserDto user) throws IOException { @@ -50,10 +65,17 @@ public void updateItem(Long itemId, ItemUpdateRequestDto itemUpdateRequestDto, itemAdminRepository.save(newItem); } - // 아이템 수정 시 신규 이미지가 존재하지 않는 경우 + /** + *아이템 수정 시 신규 이미지가 존재하지 않는 경우
+ *기존 아이템의 item.isVisible 를 false 로 변경한다
+ * @param itemId 타겟 아이템 + * @param itemUpdateRequestDto 타겟 아이템 변경 dto + * @param user 바꾸는 유저 id + * @throws ItemNotFoundException 아이템 없음 + * @throws ItemNotAvailableException 접근 불가한 아이템 + */ @Transactional - public void updateItem(Long itemId, ItemUpdateRequestDto itemUpdateRequestDto, - UserDto user) { + public void updateItem(Long itemId, ItemUpdateRequestDto itemUpdateRequestDto, UserDto user) { Item item = itemAdminRepository.findById(itemId).orElseThrow(ItemNotFoundException::new); if (!item.getIsVisible()) { throw new ItemNotAvailableException(); @@ -63,6 +85,13 @@ public void updateItem(Long itemId, ItemUpdateRequestDto itemUpdateRequestDto, itemAdminRepository.save(newItem); } + /** + *아이템 삭제
+ *item.isVisible 를 false 로 변경한다
+ * @param itemId 타겟 id + * @param user 삭제하는 유저 + * @throws ItemNotFoundException 아이템 없음 + */ @Transactional public void deleteItem(Long itemId, UserDto user) { Item item = itemAdminRepository.findById(itemId).orElseThrow(ItemNotFoundException::new); diff --git a/src/main/java/com/gg/server/domain/item/service/ItemService.java b/src/main/java/com/gg/server/domain/item/service/ItemService.java index 3c8a4177b..1dfa3279b 100644 --- a/src/main/java/com/gg/server/domain/item/service/ItemService.java +++ b/src/main/java/com/gg/server/domain/item/service/ItemService.java @@ -48,34 +48,43 @@ public class ItemService { private final NotiService notiService; private final UserCoinChangeService userCoinChangeService; + /** + *모든 아이템을 가져온다.
+ * @return + */ @Transactional(readOnly = true) public ItemStoreListResponseDto getAllItems() { - List게스트 유저가 아닌 유저가 아이템을 구매하는 메서드 이다
+ *할인 중 이라면 할인가를 적용하고, 구매 후 영수증을 db에 저장한다.
+ * @param itemId 타겟 아이템 Id + * @param userDto 구매 유저 정보 + * @throws ItemNotFoundException 존재하지 않는 아이템 + * @throws ItemNotPurchasableException 구매할 수 없는 아이템 + * @throws UserNotFoundException 타겟 유저 없음 + * @throws KakaoPurchaseException 게스트(카카오) 유저가 아이템을 구매하려 할때 + * @throws + */ @Transactional public void purchaseItem(Long itemId, UserDto userDto) { - Item item = itemRepository.findById(itemId) - .orElseThrow(() -> { - throw new ItemNotFoundException(); - }); + Item item = itemRepository.findById(itemId).orElseThrow(ItemNotFoundException::new); if (!item.getIsVisible()) { throw new ItemNotPurchasableException(); } //세일가격 존재할때 세일가로 결정 - Integer finalPrice; + Integer finalPrice = item.getPrice(); if (item.getDiscount() != null && item.getDiscount() > 0) { - finalPrice = item.getPrice() - (item.getPrice() * item.getDiscount() / 100); - } else { - finalPrice = item.getPrice(); + finalPrice -= (item.getPrice() * item.getDiscount() / 100); } User payUser = userRepository.findById(userDto.getId()) - .orElseThrow(() -> new UserNotFoundException()); + .orElseThrow(UserNotFoundException::new); if (payUser.getRoleType() == RoleType.GUEST) { throw new KakaoPurchaseException(); @@ -88,22 +97,29 @@ public void purchaseItem(Long itemId, UserDto userDto) { receiptRepository.save(receipt); } + /** + *게스트 유저가 아닌 유저들 끼리 선물을 주는 메서드 이다
+ *할인 중 이라면 할인가를 적용하고, 구매 후 영수증을 db에 저장한다.
+ * @param itemId 타겟 아이템 id + * @param ownerId 선물 받는 owner intraId + * @param userDto 구매자 id + * @throws ItemNotFoundException 존재하지 않는 아이템 + * @throws ItemNotPurchasableException 구매할 수 없는 아이템 + * @throws UserNotFoundException 타겟 유저 없음 + * @throws KakaoPurchaseException 게스트(카카오) 유저가 아이템을 구매하려 할때 + * @throws KakaoGiftException 게스트(카카오) 유저에게 선물하려 할때 + */ @Transactional public void giftItem(Long itemId, String ownerId, UserDto userDto) { - Item item = itemRepository.findById(itemId) - .orElseThrow(() -> { - throw new ItemNotFoundException(); - }); + Item item = itemRepository.findById(itemId).orElseThrow(ItemNotFoundException::new); if (!item.getIsVisible()) { throw new ItemNotPurchasableException(); } //세일가격 존재할때 세일가로 결정 - Integer finalPrice; + Integer finalPrice = item.getPrice(); if (item.getDiscount() != null && item.getDiscount() > 0) { - finalPrice = item.getPrice() - (item.getPrice() * item.getDiscount() / 100); - } else { - finalPrice = item.getPrice(); + finalPrice -= (item.getPrice() * item.getDiscount() / 100); } User payUser = userRepository.findById(userDto.getId()) @@ -128,34 +144,55 @@ public void giftItem(Long itemId, String ownerId, UserDto userDto) { notiService.createGiftNoti(owner, payUser, item.getName()); } + /** + *유저가 구매한 아이템 중 상태가 BEFORE, USING, WAITING 인 것들을 찾는 메서드이다.
+ * @param userDto 유저 정보 + * @param pageable 페이지 + * @return + */ @Transactional(readOnly = true) public UserItemListResponseDto getItemByUser(UserDto userDto, Pageable pageable) { Page해당 아이템의 주인이 맞는지 체크한다.
+ * @param loginUser 로그인 유저 + * @param receipt 영수증 + */ public void checkItemOwner(User loginUser, Receipt receipt) { if (!receipt.getOwnerIntraId().equals(loginUser.getIntraId())) { throw new ReceiptNotOwnerException(); } } + /** + *해당 아이템의 타입이 영수증과 맞는지 체크한다.
+ * @param receipt 영수증 + * @param itemType 아이템 타입 + */ public void checkItemType(Receipt receipt, ItemType itemType) { if (!receipt.getItem().getType().equals(itemType)) { throw new ItemTypeException(); } } + /** + *아이템의 상태를 체크한다.
+ *메가폰인데 ItemStatus.WAITING 가 아니거나 사용중이라면 예외 발생
+ *메가폰이 아닌 아이템인데 ItemStatus.BEFORE 인경우 예외 발생
+ * @param receipt + */ public void checkItemStatus(Receipt receipt) { if (receipt.getItem().getType().equals(ItemType.MEGAPHONE)) { - if (!(receipt.getStatus().equals(ItemStatus.WAITING) || receipt.getStatus().equals(ItemStatus.USING))) { - throw new ItemStatusException(); - } - } else { - if (!receipt.getStatus().equals(ItemStatus.BEFORE)) { + if (!(receipt.getStatus().equals(ItemStatus.WAITING) + || receipt.getStatus().equals(ItemStatus.USING))) { throw new ItemStatusException(); } + } else if (!receipt.getStatus().equals(ItemStatus.BEFORE)) { + throw new ItemStatusException(); } } } diff --git a/src/test/java/com/gg/server/admin/item/service/ItemAdminServiceUnitTest.java b/src/test/java/com/gg/server/admin/item/service/ItemAdminServiceUnitTest.java new file mode 100644 index 000000000..d4fb85e17 --- /dev/null +++ b/src/test/java/com/gg/server/admin/item/service/ItemAdminServiceUnitTest.java @@ -0,0 +1,185 @@ +package com.gg.server.admin.item.service; + +import static com.gg.server.utils.ReflectionUtilsForUnitTest.*; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.BDDMockito.*; + +import java.util.ArrayList; +import java.util.Optional; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.web.multipart.MultipartFile; + +import com.gg.server.admin.item.data.ItemAdminRepository; +import com.gg.server.admin.item.dto.ItemUpdateRequestDto; +import com.gg.server.admin.item.exception.ItemNotFoundException; +import com.gg.server.domain.item.data.Item; +import com.gg.server.domain.item.exception.ItemNotAvailableException; +import com.gg.server.domain.user.data.User; +import com.gg.server.domain.user.dto.UserDto; +import com.gg.server.domain.user.type.RacketType; +import com.gg.server.domain.user.type.RoleType; +import com.gg.server.domain.user.type.SnsType; +import com.gg.server.global.utils.aws.AsyncNewItemImageUploader; +import com.gg.server.utils.annotation.UnitTest; + +@UnitTest +@ExtendWith(MockitoExtension.class) +class ItemAdminServiceUnitTest { + @Mock + ItemAdminRepository itemAdminRepository; + @Mock + AsyncNewItemImageUploader asyncNewItemImageUploader; + @InjectMocks + ItemAdminService itemAdminService; + + User user; + Item item; + + @BeforeEach + void beforeEach() { + item = new Item(); + setFieldWithReflection(item, "isVisible", true); + user = new User("testUser", "", "", RacketType.NONE, RoleType.USER, 0, SnsType.NONE, 1L); + } + + @Nested + @DisplayName("getAllItemHistoryTest") + class GetAllItemHistoryTest { + @Test + @DisplayName("success") + void success() { + // given + given(itemAdminRepository.findAll(any(Pageable.class))).willReturn(new PageImpl<>(new ArrayList<>())); + // when, then + itemAdminService.getAllItemHistory(mock(Pageable.class)); + verify(itemAdminRepository, times(1)).findAll(any(Pageable.class)); + } + } + + @Nested + @DisplayName("updateItemTest 파라미터 4개 짜리") + class UpdateItem4ParamsTest { + @Test + @DisplayName("success") + void success() throws Exception { + // given + given(itemAdminRepository.findById(any(Long.class))).willReturn(Optional.of(item)); + given(itemAdminRepository.save(any(Item.class))).willReturn(mock(Item.class)); + // when, then + itemAdminService.updateItem(1L, mock(ItemUpdateRequestDto.class), + mock(MultipartFile.class), UserDto.from(user)); + setFieldWithReflection(item, "isVisible", true); + itemAdminService.updateItem(1L, mock(ItemUpdateRequestDto.class), + null, UserDto.from(user)); + verify(itemAdminRepository, times(2)).findById(any(Long.class)); + verify(asyncNewItemImageUploader, times(1)).upload(any(), any()); + verify(itemAdminRepository, times(2)).save(any(Item.class)); + } + + @Test + @DisplayName("ItemNotAvailableTest") + void itemNotAvailableTest() throws Exception { + // given + setFieldWithReflection(item, "isVisible", false); + given(itemAdminRepository.findById(any(Long.class))).willReturn(Optional.of(item)); + // when, then + assertThatThrownBy( + () -> itemAdminService.updateItem(1L, mock(ItemUpdateRequestDto.class), mock(MultipartFile.class), + UserDto.from(user))) + .isInstanceOf(ItemNotAvailableException.class); + verify(itemAdminRepository, times(1)).findById(any(Long.class)); + } + + @Test + @DisplayName("ItemNotFoundTest") + void itemNotFoundTest() throws Exception { + // given + given(itemAdminRepository.findById(any(Long.class))).willReturn(Optional.empty()); + // when, then + assertThatThrownBy( + () -> itemAdminService.updateItem(1L, mock(ItemUpdateRequestDto.class), mock(MultipartFile.class), + UserDto.from(user))) + .isInstanceOf(ItemNotFoundException.class); + verify(itemAdminRepository, times(1)).findById(any(Long.class)); + } + } + + @Nested + @DisplayName("updateItemTest 파라미터 3개 짜리") + class UpdateItem3ParamsTest { + @Test + @DisplayName("success") + void success() throws Exception { + // given + given(itemAdminRepository.findById(any(Long.class))).willReturn(Optional.of(item)); + given(itemAdminRepository.save(any(Item.class))).willReturn(mock(Item.class)); + // when, then + itemAdminService.updateItem(1L, mock(ItemUpdateRequestDto.class), UserDto.from(user)); + assertThat(item.getDeleterIntraId()).isEqualTo(user.getIntraId()); + verify(itemAdminRepository, times(1)).findById(any(Long.class)); + verify(itemAdminRepository, times(1)).save(any(Item.class)); + } + + @Test + @DisplayName("ItemNotAvailableTest") + void itemNotAvailableTest() throws Exception { + // given + setFieldWithReflection(item, "isVisible", false); + given(itemAdminRepository.findById(any(Long.class))).willReturn(Optional.of(item)); + // when, then + assertThatThrownBy( + () -> itemAdminService.updateItem(1L, mock(ItemUpdateRequestDto.class), UserDto.from(user))) + .isInstanceOf(ItemNotAvailableException.class); + verify(itemAdminRepository, times(1)).findById(any(Long.class)); + } + + @Test + @DisplayName("ItemNotFoundTest") + void itemNotFoundTest() throws Exception { + // given + given(itemAdminRepository.findById(any(Long.class))).willReturn(Optional.empty()); + // when, then + assertThatThrownBy( + () -> itemAdminService.updateItem(1L, mock(ItemUpdateRequestDto.class), UserDto.from(user))) + .isInstanceOf(ItemNotFoundException.class); + verify(itemAdminRepository, times(1)).findById(any(Long.class)); + } + } + + @Nested + @DisplayName("deleteItemTest") + class DeleteItemTest { + @Test + @DisplayName("success") + void success() { + // given + given(itemAdminRepository.findById(any(Long.class))).willReturn(Optional.of(item)); + // when, then + itemAdminService.deleteItem(1L, UserDto.from(user)); + assertThat(item.getIsVisible()).isFalse(); + verify(itemAdminRepository, times(1)).findById(any(Long.class)); + } + + @Test + @DisplayName("ItemNotFoundTest") + void itemNotFoundTest() { + // given + given(itemAdminRepository.findById(any(Long.class))).willReturn(Optional.empty()); + // when, then + assertThatThrownBy(() -> itemAdminService.deleteItem(1L, UserDto.from(user))) + .isInstanceOf(ItemNotFoundException.class); + verify(itemAdminRepository, times(1)).findById(any(Long.class)); + } + } +} diff --git a/src/test/java/com/gg/server/domain/item/data/ItemUnitTest.java b/src/test/java/com/gg/server/domain/item/data/ItemUnitTest.java new file mode 100644 index 000000000..805a7fcc5 --- /dev/null +++ b/src/test/java/com/gg/server/domain/item/data/ItemUnitTest.java @@ -0,0 +1,46 @@ +package com.gg.server.domain.item.data; + +import static com.gg.server.utils.ReflectionUtilsForUnitTest.*; +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import com.gg.server.utils.annotation.UnitTest; + +@UnitTest +@ExtendWith(MockitoExtension.class) +class ItemUnitTest { + @Nested + @DisplayName("imageUpdateTest") + class ImageUpdateTest { + @Test + @DisplayName("success") + void success() { + Item item = new Item(); + String after = "after"; + setFieldWithReflection(item, "imageUri", "before"); + item.imageUpdate(after); + assertThat(item.getImageUri()).isEqualTo(after); + } + } + + @Nested + @DisplayName("setVisibilityTest") + class SetVisibilityTest { + @Test + @DisplayName("success") + void success() { + Item item = new Item(); + String intraId = "intraId"; + setFieldWithReflection(item, "isVisible", true); + item.setVisibility(intraId); + assertThat(item.getIsVisible()).isFalse(); + assertThat(item.getDeleterIntraId()).isEqualTo(intraId); + } + } + +} diff --git a/src/test/java/com/gg/server/domain/item/service/ItemServiceUnitTest.java b/src/test/java/com/gg/server/domain/item/service/ItemServiceUnitTest.java new file mode 100644 index 000000000..e52cb9b05 --- /dev/null +++ b/src/test/java/com/gg/server/domain/item/service/ItemServiceUnitTest.java @@ -0,0 +1,470 @@ +package com.gg.server.domain.item.service; + +import static com.gg.server.utils.ReflectionUtilsForUnitTest.*; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.BDDMockito.*; +import static org.mockito.Mockito.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; + +import com.gg.server.domain.coin.service.UserCoinChangeService; +import com.gg.server.domain.item.data.Item; +import com.gg.server.domain.item.data.ItemRepository; +import com.gg.server.domain.item.data.UserItemRepository; +import com.gg.server.domain.item.exception.ItemNotFoundException; +import com.gg.server.domain.item.exception.ItemNotPurchasableException; +import com.gg.server.domain.item.exception.ItemTypeException; +import com.gg.server.domain.item.exception.KakaoGiftException; +import com.gg.server.domain.item.exception.KakaoPurchaseException; +import com.gg.server.domain.item.type.ItemType; +import com.gg.server.domain.noti.service.NotiService; +import com.gg.server.domain.receipt.data.Receipt; +import com.gg.server.domain.receipt.data.ReceiptRepository; +import com.gg.server.domain.receipt.exception.ItemStatusException; +import com.gg.server.domain.receipt.exception.ReceiptNotOwnerException; +import com.gg.server.domain.receipt.type.ItemStatus; +import com.gg.server.domain.user.data.User; +import com.gg.server.domain.user.data.UserRepository; +import com.gg.server.domain.user.dto.UserDto; +import com.gg.server.domain.user.exception.UserNotFoundException; +import com.gg.server.domain.user.type.RacketType; +import com.gg.server.domain.user.type.RoleType; +import com.gg.server.domain.user.type.SnsType; +import com.gg.server.utils.annotation.UnitTest; + +@UnitTest +@ExtendWith(MockitoExtension.class) +class ItemServiceUnitTest { + @Mock + ItemRepository itemRepository; + @Mock + UserRepository userRepository; + @Mock + ReceiptRepository receiptRepository; + @Mock + NotiService notiService; + @Mock// 내부 로직에서 void 값 반환으로 사용하는 곳 있음 + UserCoinChangeService userCoinChangeService; + @Mock + UserItemRepository userItemRepository; + @InjectMocks + ItemService itemService; + + @Nested + @DisplayName("getAllItems method unitTest") + class GetAllItemsTest { + @Test + @DisplayName("success") + void success() { + // given + List