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 responseDtos = itemAdminRepository.findAll(pageable) @@ -34,7 +39,17 @@ public ItemListResponseDto getAllItemHistory(Pageable pageable) { return new ItemListResponseDto(responseDtos.getContent(), responseDtos.getTotalPages()); } - // 아이템 수정 시 신규 이미지가 존재하는 경우 + /** + *

아이템 수정 시 신규 이미지가 존재하는 경우

+ *

기존 아이템의 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 itemStoreListResponseDto = itemRepository.findAllByCreatedAtDesc() .stream().map(ItemStoreResponseDto::new).collect(Collectors.toList()); return new ItemStoreListResponseDto(itemStoreListResponseDto); } + /** + *

게스트 유저가 아닌 유저가 아이템을 구매하는 메서드 이다

+ *

할인 중 이라면 할인가를 적용하고, 구매 후 영수증을 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 receipts = userItemRepository.findByOwnerIntraId(userDto.getIntraId(), pageable); - Page responseDtos = receipts.map(UserItemResponseDto::new); - return new UserItemListResponseDto(responseDtos.getContent(), responseDtos.getTotalPages()); + Page responseDto = receipts.map(UserItemResponseDto::new); + return new UserItemListResponseDto(responseDto.getContent(), responseDto.getTotalPages()); } + /** + *

해당 아이템의 주인이 맞는지 체크한다.

+ * @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 items = new ArrayList<>(); + given(itemRepository.findAllByCreatedAtDesc()).willReturn(items); + // when, then + itemService.getAllItems(); + verify(itemRepository, times(1)).findAllByCreatedAtDesc(); + } + } + + @Nested + @DisplayName("purchaseItem method unitTest") + class PurchaseItemTest { + User payUser; + UserDto userDto; + Item item; + + @BeforeEach + void beforeEach() { + payUser = new User("", "", "", RacketType.NONE, RoleType.USER, 0, SnsType.NONE, 1L); + setFieldWithReflection(payUser, "id", 1L); + userDto = UserDto.from(payUser); + item = new Item(); + setFieldWithReflection(item, "isVisible", true); + } + + @Test + @DisplayName("success -> no discount") + void successNoDiscount() { + // given + given(itemRepository.findById(any(Long.class))).willReturn(Optional.of(item)); + given(userRepository.findById(any(Long.class))).willReturn(Optional.of(payUser)); + given(receiptRepository.save(any(Receipt.class))).willReturn(mock(Receipt.class)); + // when, then 1 + itemService.purchaseItem(1L, userDto); + // when, then 2 + setFieldWithReflection(item, "discount", 0); + itemService.purchaseItem(1L, userDto); + verify(itemRepository, times(2)).findById(any(Long.class)); + verify(userRepository, times(2)).findById(any(Long.class)); + verify(userCoinChangeService, times(2)).purchaseItemCoin(any(), any(), any()); + verify(receiptRepository, times(2)).save(any(Receipt.class)); + + } + + @Test + @DisplayName("success -> discount") + void successDiscount() { + // given + setFieldWithReflection(item, "price", 100); + setFieldWithReflection(item, "discount", 10); + given(itemRepository.findById(any(Long.class))).willReturn(Optional.of(item)); + given(userRepository.findById(any(Long.class))).willReturn(Optional.of(payUser)); + given(receiptRepository.save(any(Receipt.class))).willReturn(mock(Receipt.class)); + // when, then + itemService.purchaseItem(1L, userDto); + verify(itemRepository, times(1)).findById(any(Long.class)); + verify(userRepository, times(1)).findById(any(Long.class)); + verify(userCoinChangeService, times(1)).purchaseItemCoin(any(), any(), any()); + verify(receiptRepository, times(1)).save(any(Receipt.class)); + } + + @Test + @DisplayName("item not found") + void itemNotFound() { + // given + given(itemRepository.findById(any(Long.class))).willReturn(Optional.empty()); + // when, then + assertThatThrownBy(() -> itemService.purchaseItem(1L, userDto)) + .isInstanceOf(ItemNotFoundException.class); + verify(itemRepository, times(1)).findById(any(Long.class)); + } + + @Test + @DisplayName("Item Not Purchasable") + void itemNotPurchasable() { + // given + setFieldWithReflection(item, "isVisible", false); + given(itemRepository.findById(any(Long.class))).willReturn(Optional.of(item)); + // when, then + assertThatThrownBy(() -> itemService.purchaseItem(1L, userDto)) + .isInstanceOf(ItemNotPurchasableException.class); + verify(itemRepository, times(1)).findById(any(Long.class)); + } + + @Test + @DisplayName("User Not Found") + void userNotFound() { + // given + given(itemRepository.findById(any(Long.class))).willReturn(Optional.of(item)); + given(userRepository.findById(any(Long.class))).willReturn(Optional.empty()); + // when, then + assertThatThrownBy(() -> itemService.purchaseItem(1L, userDto)) + .isInstanceOf(UserNotFoundException.class); + verify(itemRepository, times(1)).findById(any(Long.class)); + verify(userRepository, times(1)).findById(any(Long.class)); + } + + @Test + @DisplayName("GuestUser Exception") + void guestUserException() { + // given + setFieldWithReflection(payUser, "roleType", RoleType.GUEST); + given(itemRepository.findById(any(Long.class))).willReturn(Optional.of(item)); + given(userRepository.findById(any(Long.class))).willReturn(Optional.of(payUser)); + // when, then + assertThatThrownBy(() -> itemService.purchaseItem(1L, userDto)) + .isInstanceOf(KakaoPurchaseException.class); + verify(itemRepository, times(1)).findById(any(Long.class)); + verify(userRepository, times(1)).findById(any(Long.class)); + } + + } + + @Nested + @DisplayName("giftItemTest method unitTest") + class GiftItemTest { + User payUser; + User owner; + UserDto userDto; + Item item; + + @BeforeEach + void beforeEach() { + payUser = new User("", "", "", RacketType.NONE, RoleType.USER, 0, SnsType.NONE, 1L); + setFieldWithReflection(payUser, "id", 1L); + userDto = UserDto.from(payUser); + owner = new User("", "", "", RacketType.NONE, RoleType.USER, 0, SnsType.NONE, 1L); + item = new Item(); + setFieldWithReflection(item, "isVisible", true); + } + + @Test + @DisplayName("success -> No Discount") + void successNoDiscount() { + // given + given(itemRepository.findById(any(Long.class))).willReturn(Optional.of(item)); + given(userRepository.findById(any(Long.class))).willReturn(Optional.of(payUser)); + given(receiptRepository.save(any(Receipt.class))).willReturn(mock(Receipt.class)); + given(userRepository.findByIntraId(any(String.class))).willReturn(Optional.of(owner)); + // when, then 1 + itemService.giftItem(1L, "owner", userDto); + // when, then 2 + setFieldWithReflection(item, "discount", 0); + itemService.giftItem(1L, "owner", userDto); + verify(itemRepository, times(2)).findById(any(Long.class)); + verify(userRepository, times(2)).findById(any(Long.class)); + verify(userRepository, times(2)).findByIntraId(any(String.class)); + verify(userCoinChangeService, times(2)).giftItemCoin(any(), any(), any(), any()); + verify(receiptRepository, times(2)).save(any(Receipt.class)); + verify(notiService, times(2)).createGiftNoti(any(), any(), any()); + + } + + @Test + @DisplayName("success -> Discount") + void successDiscount() { + // given + setFieldWithReflection(item, "price", 100); + setFieldWithReflection(item, "discount", 10); + given(itemRepository.findById(any(Long.class))).willReturn(Optional.of(item)); + given(userRepository.findById(any(Long.class))).willReturn(Optional.of(payUser)); + given(receiptRepository.save(any(Receipt.class))).willReturn(mock(Receipt.class)); + given(userRepository.findByIntraId(any(String.class))).willReturn(Optional.of(owner)); + // when, then + itemService.giftItem(1L, "owner", userDto); + verify(itemRepository, times(1)).findById(any(Long.class)); + verify(userRepository, times(1)).findById(any(Long.class)); + verify(userRepository, times(1)).findByIntraId(any(String.class)); + verify(userCoinChangeService, times(1)).giftItemCoin(any(), any(), any(), any()); + verify(receiptRepository, times(1)).save(any(Receipt.class)); + verify(notiService, times(1)).createGiftNoti(any(), any(), any()); + } + + @Test + @DisplayName("guest Owner") + void guestOwnerTest() { + // given + setFieldWithReflection(owner, "roleType", RoleType.GUEST); + given(itemRepository.findById(any(Long.class))).willReturn(Optional.of(item)); + given(userRepository.findById(any(Long.class))).willReturn(Optional.of(payUser)); + given(userRepository.findByIntraId(any(String.class))).willReturn(Optional.of(owner)); + // when, then + assertThatThrownBy(() -> itemService.giftItem(1L, "owner", userDto)) + .isInstanceOf(KakaoGiftException.class); + verify(itemRepository, times(1)).findById(any(Long.class)); + verify(userRepository, times(1)).findById(any(Long.class)); + verify(userRepository, times(1)).findByIntraId(any(String.class)); + } + + @Test + @DisplayName("Owner not Found") + void ownerNotFoundTest() { + // given + setFieldWithReflection(owner, "roleType", RoleType.GUEST); + given(itemRepository.findById(any(Long.class))).willReturn(Optional.of(item)); + given(userRepository.findById(any(Long.class))).willReturn(Optional.of(payUser)); + given(userRepository.findByIntraId(any(String.class))).willReturn(Optional.empty()); + // when, then + assertThatThrownBy(() -> itemService.giftItem(1L, "owner", userDto)) + .isInstanceOf(UserNotFoundException.class); + verify(itemRepository, times(1)).findById(any(Long.class)); + verify(userRepository, times(1)).findById(any(Long.class)); + verify(userRepository, times(1)).findByIntraId(any(String.class)); + } + + @Test + @DisplayName("guest payUser") + void guestPayUserTest() { + // given + setFieldWithReflection(payUser, "roleType", RoleType.GUEST); + given(itemRepository.findById(any(Long.class))).willReturn(Optional.of(item)); + given(userRepository.findById(any(Long.class))).willReturn(Optional.of(payUser)); + // when, then + assertThatThrownBy(() -> itemService.giftItem(1L, "owner", userDto)) + .isInstanceOf(KakaoPurchaseException.class); + verify(itemRepository, times(1)).findById(any(Long.class)); + verify(userRepository, times(1)).findById(any(Long.class)); + } + + @Test + @DisplayName("payUser Not Found") + void payUserNotFoundTest() { + // given + setFieldWithReflection(payUser, "roleType", RoleType.GUEST); + given(itemRepository.findById(any(Long.class))).willReturn(Optional.of(item)); + given(userRepository.findById(any(Long.class))).willReturn(Optional.empty()); + // when, then + assertThatThrownBy(() -> itemService.giftItem(1L, "owner", userDto)) + .isInstanceOf(UserNotFoundException.class); + verify(itemRepository, times(1)).findById(any(Long.class)); + verify(userRepository, times(1)).findById(any(Long.class)); + } + + @Test + @DisplayName("Item Not Purchasable") + void itemNotPurchasableTest() { + // given + setFieldWithReflection(item, "isVisible", false); + given(itemRepository.findById(any(Long.class))).willReturn(Optional.of(item)); + // when, then + assertThatThrownBy(() -> itemService.giftItem(1L, "owner", userDto)) + .isInstanceOf(ItemNotPurchasableException.class); + verify(itemRepository, times(1)).findById(any(Long.class)); + } + + @Test + @DisplayName("Item Not Found") + void itemNotFoundTest() { + // given + given(itemRepository.findById(any(Long.class))).willReturn(Optional.empty()); + // when, then + assertThatThrownBy(() -> itemService.giftItem(1L, "owner", userDto)) + .isInstanceOf(ItemNotFoundException.class); + verify(itemRepository, times(1)).findById(any(Long.class)); + } + } + + @Nested + @DisplayName("getItemByUser method unitTest") + class GetItemByUserTest { + @Test + @DisplayName("success") + void success() { + // given + User user = new User("intraId", "", "", RacketType.NONE, RoleType.USER, 0, SnsType.NONE, 1L); + given(userItemRepository.findByOwnerIntraId(any(String.class), any(Pageable.class))) + .willReturn(new PageImpl<>(new ArrayList<>())); + // when, then + itemService.getItemByUser(UserDto.from(user), mock(Pageable.class)); + verify(userItemRepository, times(1)).findByOwnerIntraId(any(String.class), any(Pageable.class)); + } + } + + @Nested + @DisplayName("checkItemOwner method unitTest") + class CheckItemOwnerTest { + User user; + Receipt receipt; + + @BeforeEach + void beforeEach() { + user = new User("intraId", "", "", RacketType.NONE, RoleType.USER, 0, SnsType.NONE, 1L); + receipt = new Receipt(); + setFieldWithReflection(receipt, "ownerIntraId", user.getIntraId()); + } + + @Test + @DisplayName("success") + void success() { + // given + // when, then + itemService.checkItemOwner(user, receipt); + } + + @Test + @DisplayName("Receipt Not Owner Exception") + void receiptNotOwnerTest() { + // given + setFieldWithReflection(receipt, "ownerIntraId", user.getIntraId() + "1234"); + // when, then + assertThatThrownBy(() -> itemService.checkItemOwner(user, receipt)) + .isInstanceOf(ReceiptNotOwnerException.class); + } + } + + @Nested + @DisplayName("checkItemType method unitTest") + class CheckItemTypeTest { + Receipt receipt; + + @BeforeEach + void beforeEach() { + receipt = new Receipt(); + Item item = new Item(); + setFieldWithReflection(item, "type", ItemType.MEGAPHONE); + setFieldWithReflection(receipt, "item", item); + } + + @Test + @DisplayName("success") + void success() { + // given + // when, then + itemService.checkItemType(receipt, receipt.getItem().getType()); + } + + @Test + @DisplayName("Receipt Not Owner Exception") + void receiptNotOwnerTest() { + // given + // when, then + assertThatThrownBy(() -> itemService.checkItemType(receipt, ItemType.EDGE)) + .isInstanceOf(ItemTypeException.class); + } + } + + @Nested + @DisplayName("checkItemStatus method unitTest") + class CheckItemStatusTest { + Receipt receipt; + + @BeforeEach + void beforeEach() { + receipt = new Receipt(); + Item item = new Item(); + setFieldWithReflection(item, "type", ItemType.MEGAPHONE); + setFieldWithReflection(receipt, "status", ItemStatus.BEFORE); + setFieldWithReflection(receipt, "item", item); + } + + @Test + @DisplayName("success Megaphone1") + void successMegaphone1() { + // given + setFieldWithReflection(receipt, "status", ItemStatus.USING); + // when, then + itemService.checkItemStatus(receipt); + } + + @Test + @DisplayName("success Megaphone2") + void successMegaphone2() { + // given + setFieldWithReflection(receipt, "status", ItemStatus.WAITING); + // when, then + itemService.checkItemStatus(receipt); + } + + @Test + @DisplayName("success Megaphone") + void successNotMegaphone() { + // given + setFieldWithReflection(receipt.getItem(), "type", ItemType.EDGE); + // when, then + itemService.checkItemStatus(receipt); + } + + @Test + @DisplayName("Receipt Not Owner Exception") + void failMegaphone() { + // given + // when, then + assertThatThrownBy(() -> itemService.checkItemStatus(receipt)) + .isInstanceOf(ItemStatusException.class); + } + + @Test + @DisplayName("Receipt Not Owner Exception") + void failNotMegaphone() { + // given + setFieldWithReflection(receipt.getItem(), "type", ItemType.EDGE); + setFieldWithReflection(receipt, "status", ItemStatus.USING); + // when, then + assertThatThrownBy(() -> itemService.checkItemStatus(receipt)) + .isInstanceOf(ItemStatusException.class); + } + } +}