Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions src/main/java/until/the/eternity/common/enums/ItemCategory.java
Original file line number Diff line number Diff line change
Expand Up @@ -88,18 +88,18 @@ public enum ItemCategory {
PAGE("페이지", "서적"),

// 소모품
POTION("포션", "소모폼"),
FOOD("음식", "소모폼"),
HERB("허브", "소모폼"),
DUNGEON_PASS("던전 통행증", "소모폼"),
ALBAN_TRAINING_STONE("알반 훈련석", "소모폼"),
GEM_STONE("개조석", "소모폼"),
JEWEL("보석", "소모폼"),
TRANSFORMATION_MEDAL("변신 메달", "소모폼"),
DYE_AMPULE("염색 앰플", "소모폼"),
SKETCH("스케치", "소모폼"),
FINZBEADS("핀즈비즈", "소모폼"),
ETC_CONSUMABLE("기타 소모품", "소모폼"),
POTION("포션", "소모품"),
FOOD("음식", "소모품"),
HERB("허브", "소모품"),
DUNGEON_PASS("던전 통행증", "소모품"),
ALBAN_TRAINING_STONE("알반 훈련석", "소모품"),
GEM_STONE("개조석", "소모품"),
JEWEL("보석", "소모품"),
TRANSFORMATION_MEDAL("변신 메달", "소모품"),
DYE_AMPULE("염색 앰플", "소모품"),
SKETCH("스케치", "소모품"),
FINZBEADS("핀즈비즈", "소모품"),
ETC_CONSUMABLE("기타 소모품", "소모품"),

// 토템
TOTEM("토템", "토템"),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package until.the.eternity.iteminfo.domain.entity;

import jakarta.persistence.Column;
import jakarta.persistence.EmbeddedId;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AccessLevel;
import lombok.Getter;
Expand All @@ -14,15 +14,7 @@
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class ItemInfo {

@Id
@Column(name = "name", nullable = false)
private String name;

@Column(name = "sub_category", nullable = false)
private String subCategory;

@Column(name = "top_category", nullable = false)
private String topCategory;
@EmbeddedId private ItemInfoId id;

@Column(name = "description")
private String description;
Expand Down Expand Up @@ -53,4 +45,17 @@ public class ItemInfo {

@Column(name = "max_alteration_count")
private Byte maxAlterationCount;

// Helper methods for backward compatibility
public String getName() {
return id != null ? id.getName() : null;
}

public String getSubCategory() {
return id != null ? id.getSubCategory() : null;
}

Comment thread
dev-ant marked this conversation as resolved.
public String getTopCategory() {
return id != null ? id.getTopCategory() : null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package until.the.eternity.iteminfo.domain.entity;

import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import java.io.Serializable;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Embeddable
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@EqualsAndHashCode
public class ItemInfoId implements Serializable {

@Column(name = "name", nullable = false)
private String name;

@Column(name = "sub_category", nullable = false)
private String subCategory;

@Column(name = "top_category", nullable = false)
private String topCategory;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import until.the.eternity.iteminfo.domain.entity.ItemInfo;
import until.the.eternity.iteminfo.domain.entity.ItemInfoId;

public interface ItemInfoJpaRepository
extends JpaRepository<ItemInfo, String>, JpaSpecificationExecutor<ItemInfo> {
extends JpaRepository<ItemInfo, ItemInfoId>, JpaSpecificationExecutor<ItemInfo> {

List<ItemInfo> findByTopCategory(String topCategory);
List<ItemInfo> findByIdTopCategory(String topCategory);

List<ItemInfo> findBySubCategory(String subCategory);
List<ItemInfo> findByIdSubCategory(String subCategory);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ public List<ItemInfo> findAll() {

@Override
public List<ItemInfo> findByTopCategory(String topCategory) {
return jpaRepository.findByTopCategory(topCategory);
return jpaRepository.findByIdTopCategory(topCategory);
}

@Override
public List<ItemInfo> findBySubCategory(String subCategory) {
return jpaRepository.findBySubCategory(subCategory);
return jpaRepository.findByIdSubCategory(subCategory);
}
}
3 changes: 3 additions & 0 deletions src/main/resources/db/migration/V10__alter_item_info_pk.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ALTER TABLE item_info
DROP PRIMARY KEY,
ADD PRIMARY KEY (name, sub_category, top_category);
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package until.the.eternity.iteminfo.application.service;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*;

import java.util.List;
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.junit.jupiter.MockitoExtension;
import until.the.eternity.iteminfo.domain.entity.ItemInfo;
import until.the.eternity.iteminfo.domain.repository.ItemInfoRepositoryPort;
import until.the.eternity.iteminfo.interfaces.rest.dto.response.ItemCategoryResponse;
import until.the.eternity.iteminfo.interfaces.rest.dto.response.ItemInfoResponse;

@ExtendWith(MockitoExtension.class)
class ItemInfoServiceTest {

@Mock private ItemInfoRepositoryPort itemInfoRepository;

@InjectMocks private ItemInfoService itemInfoService;

@Test
@DisplayName("모든 아이템 카테고리를 조회하면 카테고리 목록을 반환한다")
void findItemCategories_should_return_all_categories() {
// when
List<ItemCategoryResponse> result = itemInfoService.findItemCategories();

// then
assertThat(result).isNotEmpty();
assertThat(result).extracting("subCategory").contains("한손 장비", "검", "활");
assertThat(result).extracting("topCategory").contains("근거리 장비", "원거리 장비");
}

@Test
@DisplayName("모든 아이템 정보를 조회하면 아이템 목록을 반환한다")
void findAll_should_return_all_items() {
// given
ItemInfo item1 = createItemInfo("나뭇가지", "한손검", "무기");
ItemInfo item2 = createItemInfo("숏소드", "한손검", "무기");
when(itemInfoRepository.findAll()).thenReturn(List.of(item1, item2));

// when
List<ItemInfoResponse> result = itemInfoService.findAll();

// then
assertThat(result).hasSize(2);
assertThat(result).extracting("name").containsExactly("나뭇가지", "숏소드");
verify(itemInfoRepository).findAll();
}

@Test
@DisplayName("상위 카테고리로 아이템을 조회하면 해당 카테고리의 아이템 목록을 반환한다")
void findByTopCategory_should_return_items_by_top_category() {
// given
String topCategory = "소모품";
ItemInfo item1 = createItemInfo("염색 앰플", "염색 앰플", topCategory);
ItemInfo item2 = createItemInfo("지정 색상 염색 앰플", "염색 앰플", topCategory);
when(itemInfoRepository.findByTopCategory(topCategory)).thenReturn(List.of(item1, item2));

// when
List<ItemInfoResponse> result = itemInfoService.findByTopCategory(topCategory);

// then
assertThat(result).hasSize(2);
assertThat(result).extracting("topCategory").containsOnly("소모품");
assertThat(result).extracting("name").containsExactly("염색 앰플", "지정 색상 염색 앰플");
verify(itemInfoRepository).findByTopCategory(topCategory);
}

@Test
@DisplayName("하위 카테고리로 아이템을 조회하면 해당 카테고리의 아이템 목록을 반환한다")
void findBySubCategory_should_return_items_by_sub_category() {
// given
String topCategory = "소모품";
String subCategory = "염색 앰플";
ItemInfo item1 = createItemInfo("염색 앰플", subCategory, topCategory);
ItemInfo item2 = createItemInfo("지정 색상 염색 앰플", subCategory, topCategory);
when(itemInfoRepository.findBySubCategory(subCategory)).thenReturn(List.of(item1, item2));

// when
List<ItemInfoResponse> result = itemInfoService.findBySubCategory(subCategory);

// then
assertThat(result).hasSize(2);
assertThat(result).extracting("subCategory").containsOnly("염색 앰플");
assertThat(result).extracting("name").containsExactly("염색 앰플", "지정 색상 염색 앰플");
verify(itemInfoRepository).findBySubCategory(subCategory);
}

@Test
@DisplayName("아이템 정보가 없으면 빈 목록을 반환한다")
void findAll_should_return_empty_list_when_no_data() {
// given
when(itemInfoRepository.findAll()).thenReturn(List.of());

// when
List<ItemInfoResponse> result = itemInfoService.findAll();

// then
assertThat(result).isEmpty();
verify(itemInfoRepository).findAll();
}

private ItemInfo createItemInfo(String name, String subCategory, String topCategory) {
ItemInfo itemInfo = mock(ItemInfo.class);

when(itemInfo.getName()).thenReturn(name);
when(itemInfo.getSubCategory()).thenReturn(subCategory);
when(itemInfo.getTopCategory()).thenReturn(topCategory);

return itemInfo;
}
}