diff --git a/src/main/java/camp/woowak/lab/menu/domain/Menu.java b/src/main/java/camp/woowak/lab/menu/domain/Menu.java index e5985670..6b6f2dae 100644 --- a/src/main/java/camp/woowak/lab/menu/domain/Menu.java +++ b/src/main/java/camp/woowak/lab/menu/domain/Menu.java @@ -1,18 +1,49 @@ package camp.woowak.lab.menu.domain; import camp.woowak.lab.store.domain.Store; +import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; @Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) public class Menu { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "store_id", nullable = false) private Store store; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "menu_category_id", nullable = false) + private MenuCategory menuCategory; + + @Column(nullable = false) + private String name; + + @Column(nullable = false) + private Integer price; + + @Column(nullable = false) + private String imageUrl; + + public Menu(Store store, MenuCategory menuCategory, String name, Integer price, String imageUrl) { + MenuValidator.validate(store, menuCategory, name, price, imageUrl); + this.store = store; + this.menuCategory = menuCategory; + this.name = name; + this.price = price; + this.imageUrl = imageUrl; + } + } diff --git a/src/main/java/camp/woowak/lab/menu/domain/MenuCategory.java b/src/main/java/camp/woowak/lab/menu/domain/MenuCategory.java new file mode 100644 index 00000000..2e8d10b2 --- /dev/null +++ b/src/main/java/camp/woowak/lab/menu/domain/MenuCategory.java @@ -0,0 +1,36 @@ +package camp.woowak.lab.menu.domain; + +import camp.woowak.lab.store.domain.Store; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class MenuCategory { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "store_id", nullable = false) + private Store store; + + @Column(nullable = false) + private String name; + + public MenuCategory(Store store, String name) { + MenuCategoryValidator.validate(store, name); + this.store = store; + this.name = name; + } + +} diff --git a/src/main/java/camp/woowak/lab/menu/domain/MenuCategoryValidator.java b/src/main/java/camp/woowak/lab/menu/domain/MenuCategoryValidator.java new file mode 100644 index 00000000..26f9c71b --- /dev/null +++ b/src/main/java/camp/woowak/lab/menu/domain/MenuCategoryValidator.java @@ -0,0 +1,38 @@ +package camp.woowak.lab.menu.domain; + +import camp.woowak.lab.menu.exception.InvalidMenuCategoryCreationException; +import camp.woowak.lab.store.domain.Store; + +public class MenuCategoryValidator { + + private static final int MAX_NAME_LENGTH = 10; + + public static void validate(final Store store, final String name) { + validateNotNull(store, name); + validateNotBlank(name); + validateNameLength(name); + } + + private static void validateNotNull(final Object... targets) { + for (Object target : targets) { + if (target == null) { + throw new InvalidMenuCategoryCreationException(target + "은 Null 이 될 수 없습니다."); + } + } + } + + private static void validateNotBlank(final String... targets) { + for (String target : targets) { + if (target.isBlank()) { + throw new InvalidMenuCategoryCreationException(target + "은 빈 문자열이거나 공백 문자열이 포함될 수 없습니다."); + } + } + } + + private static void validateNameLength(final String name) { + if (name.length() > MAX_NAME_LENGTH) { + throw new InvalidMenuCategoryCreationException("메뉴 카테고리 이름은 " + MAX_NAME_LENGTH + "글자까지 가능합니다."); + } + } + +} diff --git a/src/main/java/camp/woowak/lab/menu/domain/MenuValidator.java b/src/main/java/camp/woowak/lab/menu/domain/MenuValidator.java new file mode 100644 index 00000000..db0bc7e9 --- /dev/null +++ b/src/main/java/camp/woowak/lab/menu/domain/MenuValidator.java @@ -0,0 +1,46 @@ +package camp.woowak.lab.menu.domain; + +import camp.woowak.lab.menu.exception.InvalidMenuCreationException; +import camp.woowak.lab.store.domain.Store; + +public class MenuValidator { + + private static final int MAX_NAME_LENGTH = 10; + + public static void validate(final Store store, final MenuCategory menuCategory, final String name, + final Integer price, final String imageUrl) { + validateNotNull(store, menuCategory, name, price, imageUrl); + validateNotBlank(name, imageUrl); + validateNameLength(name); + validatePriceNegative(price); + } + + private static void validateNotNull(final Object... targets) { + for (Object target : targets) { + if (target == null) { + throw new InvalidMenuCreationException(target + "은 Null 이 될 수 없습니다."); + } + } + } + + private static void validateNotBlank(final String... targets) { + for (String target : targets) { + if (target.isBlank()) { + throw new InvalidMenuCreationException(target + "은 빈 문자열이거나 공백 문자열이 포함될 수 없습니다."); + } + } + } + + private static void validateNameLength(final String name) { + if (name.length() > MAX_NAME_LENGTH) { + throw new InvalidMenuCreationException("메뉴 이름은 " + MAX_NAME_LENGTH + "글자까지 가능합니다."); + } + } + + private static void validatePriceNegative(final Integer price) { + if (price <= 0) { + throw new InvalidMenuCreationException("메뉴의 가격은 양수만 가능합니다"); + } + } + +} diff --git a/src/main/java/camp/woowak/lab/menu/exception/InvalidMenuCategoryCreationException.java b/src/main/java/camp/woowak/lab/menu/exception/InvalidMenuCategoryCreationException.java new file mode 100644 index 00000000..5b3aeba8 --- /dev/null +++ b/src/main/java/camp/woowak/lab/menu/exception/InvalidMenuCategoryCreationException.java @@ -0,0 +1,10 @@ +package camp.woowak.lab.menu.exception; + +// TODO: extends CustomException +public class InvalidMenuCategoryCreationException extends RuntimeException { + + public InvalidMenuCategoryCreationException(String message) { + super(message); + } + +} diff --git a/src/main/java/camp/woowak/lab/menu/exception/InvalidMenuCreationException.java b/src/main/java/camp/woowak/lab/menu/exception/InvalidMenuCreationException.java new file mode 100644 index 00000000..3e7ed471 --- /dev/null +++ b/src/main/java/camp/woowak/lab/menu/exception/InvalidMenuCreationException.java @@ -0,0 +1,10 @@ +package camp.woowak.lab.menu.exception; + +// TODO: extends CustomException +public class InvalidMenuCreationException extends RuntimeException { + + public InvalidMenuCreationException(String message) { + super(message); + } + +} diff --git a/src/main/java/camp/woowak/lab/menu/exception/NotFoundMenuCategoryException.java b/src/main/java/camp/woowak/lab/menu/exception/NotFoundMenuCategoryException.java new file mode 100644 index 00000000..4e8ef121 --- /dev/null +++ b/src/main/java/camp/woowak/lab/menu/exception/NotFoundMenuCategoryException.java @@ -0,0 +1,10 @@ +package camp.woowak.lab.menu.exception; + +// TODO: extends CustomException +public class NotFoundMenuCategoryException extends RuntimeException { + + public NotFoundMenuCategoryException(String message) { + super(message); + } + +} diff --git a/src/main/java/camp/woowak/lab/menu/repository/MenuCategoryRepository.java b/src/main/java/camp/woowak/lab/menu/repository/MenuCategoryRepository.java new file mode 100644 index 00000000..708658c5 --- /dev/null +++ b/src/main/java/camp/woowak/lab/menu/repository/MenuCategoryRepository.java @@ -0,0 +1,13 @@ +package camp.woowak.lab.menu.repository; + +import java.util.Optional; + +import org.springframework.data.jpa.repository.JpaRepository; + +import camp.woowak.lab.menu.domain.MenuCategory; + +public interface MenuCategoryRepository extends JpaRepository { + + Optional findByStoreIdAndName(Long storeId, String name); + +} diff --git a/src/main/java/camp/woowak/lab/menu/repository/MenuRepository.java b/src/main/java/camp/woowak/lab/menu/repository/MenuRepository.java new file mode 100644 index 00000000..89305b0d --- /dev/null +++ b/src/main/java/camp/woowak/lab/menu/repository/MenuRepository.java @@ -0,0 +1,8 @@ +package camp.woowak.lab.menu.repository; + +import org.springframework.data.jpa.repository.JpaRepository; + +import camp.woowak.lab.menu.domain.Menu; + +public interface MenuRepository extends JpaRepository { +} diff --git a/src/main/java/camp/woowak/lab/store/domain/Store.java b/src/main/java/camp/woowak/lab/store/domain/Store.java index d67aeb3b..16fc444a 100644 --- a/src/main/java/camp/woowak/lab/store/domain/Store.java +++ b/src/main/java/camp/woowak/lab/store/domain/Store.java @@ -2,6 +2,7 @@ import java.time.LocalDateTime; +import camp.woowak.lab.store.exception.NotEqualsOwnerException; import camp.woowak.lab.vendor.domain.Vendor; import jakarta.persistence.Column; import jakarta.persistence.Embedded; @@ -14,10 +15,12 @@ import jakarta.persistence.ManyToOne; import jakarta.persistence.OneToOne; import lombok.AccessLevel; +import lombok.Getter; import lombok.NoArgsConstructor; @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter public class Store { @Id @@ -35,8 +38,6 @@ public class Store { @Column(nullable = false) private String name; - // TODO: 위치 정보에 대한 요구사항 논의 후 수정 예정. - // i.g) 송파구로 특정, 도시 정보로 특정 등 요구사항이 정의되어야 엔티티 설계를 진행할 수 있음 @Embedded private StoreAddress storeAddress; @@ -52,7 +53,7 @@ public class Store { public Store(Vendor owner, StoreCategory storeCategory, String name, String address, String phoneNumber, Integer minOrderPrice, LocalDateTime startTime, LocalDateTime endTime ) { - StoreValidator.validate(name, address, minOrderPrice, startTime, endTime); + StoreValidator.validate(owner, storeCategory, name, address, minOrderPrice, startTime, endTime); this.owner = owner; this.storeCategory = storeCategory; this.name = name; @@ -62,4 +63,10 @@ public Store(Vendor owner, StoreCategory storeCategory, String name, String addr this.storeTime = new StoreTime(startTime, endTime); } + public void validateOwner(Vendor owner) { + if (!this.owner.equals(owner)) { + throw new NotEqualsOwnerException("가게의 점주가 일치하지 않습니다." + this.owner + ", " + owner); + } + } + } diff --git a/src/main/java/camp/woowak/lab/store/domain/StoreValidator.java b/src/main/java/camp/woowak/lab/store/domain/StoreValidator.java index d17d0a3d..b8393cc4 100644 --- a/src/main/java/camp/woowak/lab/store/domain/StoreValidator.java +++ b/src/main/java/camp/woowak/lab/store/domain/StoreValidator.java @@ -5,6 +5,7 @@ import java.time.LocalDateTime; import camp.woowak.lab.store.exception.StoreException; +import camp.woowak.lab.vendor.domain.Vendor; public class StoreValidator { @@ -15,9 +16,11 @@ public class StoreValidator { private static final int MIN_NAME_LENGTH = 2; private static final int MAX_NAME_LENGTH = 10; - public static void validate(final String name, final String address, final Integer minOrderPrice, + public static void validate(final Vendor owner, StoreCategory storeCategory, final String name, + final String address, final Integer minOrderPrice, final LocalDateTime startTime, final LocalDateTime endTime ) { + validateNotNull(owner, storeCategory, name, address, minOrderPrice, startTime, endTime); validateName(name); validateAddress(address); validateMinOrderPrice(minOrderPrice); @@ -25,6 +28,14 @@ public static void validate(final String name, final String address, final Integ validateTime(startTime, endTime); } + private static void validateNotNull(Object... targets) { + for (Object target : targets) { + if (target == null) { + throw new StoreException(NULL_EXIST); + } + } + } + private static void validateName(final String name) { if (MIN_NAME_LENGTH <= name.length() && name.length() <= MAX_NAME_LENGTH) { return; @@ -32,7 +43,6 @@ private static void validateName(final String name) { throw new StoreException(INVALID_NAME_RANGE); } - // TODO: 가게 위치 비즈니스 요구사항 구체화하면, 주소 검증 로직 수정 예정 private static void validateAddress(final String address) { if (StoreAddress.DEFAULT_DISTRICT.equals(address)) { return; diff --git a/src/main/java/camp/woowak/lab/store/exception/NotEqualsOwnerException.java b/src/main/java/camp/woowak/lab/store/exception/NotEqualsOwnerException.java new file mode 100644 index 00000000..f122216c --- /dev/null +++ b/src/main/java/camp/woowak/lab/store/exception/NotEqualsOwnerException.java @@ -0,0 +1,9 @@ +package camp.woowak.lab.store.exception; + +public class NotEqualsOwnerException extends RuntimeException { + + public NotEqualsOwnerException(String message) { + super(message); + } + +} diff --git a/src/main/java/camp/woowak/lab/store/exception/NotFoundStoreException.java b/src/main/java/camp/woowak/lab/store/exception/NotFoundStoreException.java new file mode 100644 index 00000000..42848feb --- /dev/null +++ b/src/main/java/camp/woowak/lab/store/exception/NotFoundStoreException.java @@ -0,0 +1,10 @@ +package camp.woowak.lab.store.exception; + +// TODO: 404Exception 상속하도록 수정 +public class NotFoundStoreException extends RuntimeException { + + public NotFoundStoreException(String message) { + super(message); + } + +} diff --git a/src/main/java/camp/woowak/lab/store/exception/StoreException.java b/src/main/java/camp/woowak/lab/store/exception/StoreException.java index 628c6d6c..a108d391 100644 --- a/src/main/java/camp/woowak/lab/store/exception/StoreException.java +++ b/src/main/java/camp/woowak/lab/store/exception/StoreException.java @@ -15,6 +15,7 @@ public StoreException(final ErrorCode errorCode) { @Getter public enum ErrorCode { + NULL_EXIST("NULL 값이 존재합니다."), INVALID_NAME_RANGE("가게 이름은 2글자 ~ 10글자 이어야합니다."), INVALID_ADDRESS("가게 주소는 송파구만 가능합니다."), diff --git a/src/main/java/camp/woowak/lab/store/service/StoreMenuRegistrationService.java b/src/main/java/camp/woowak/lab/store/service/StoreMenuRegistrationService.java new file mode 100644 index 00000000..6b8af6b4 --- /dev/null +++ b/src/main/java/camp/woowak/lab/store/service/StoreMenuRegistrationService.java @@ -0,0 +1,67 @@ +package camp.woowak.lab.store.service; + +import java.util.List; + +import org.springframework.stereotype.Service; + +import camp.woowak.lab.menu.domain.Menu; +import camp.woowak.lab.menu.domain.MenuCategory; +import camp.woowak.lab.menu.exception.NotFoundMenuCategoryException; +import camp.woowak.lab.menu.repository.MenuCategoryRepository; +import camp.woowak.lab.menu.repository.MenuRepository; +import camp.woowak.lab.store.domain.Store; +import camp.woowak.lab.store.exception.NotFoundStoreException; +import camp.woowak.lab.store.repository.StoreRepository; +import camp.woowak.lab.store.service.dto.StoreMenuRegistrationRequest; +import camp.woowak.lab.vendor.domain.Vendor; +import lombok.RequiredArgsConstructor; + +/** + *

Description

+ * 가게 메뉴 등록을 담당 + * + *

User Story

+ * 점주는 음식 상품을 등록할 수 있다. + */ +@Service +@RequiredArgsConstructor +public class StoreMenuRegistrationService { + + private final StoreRepository storeRepository; + private final MenuRepository menuRepository; + private final MenuCategoryRepository menuCategoryRepository; + + public void storeMenuRegistration(final Vendor owner, final StoreMenuRegistrationRequest request) { + Store store = findStoreBy(request.storeId()); + store.validateOwner(owner); + + List menuLineItems = request.menuItems(); + List menus = createMenus(store, menuLineItems); + + menuRepository.saveAll(menus); + } + + private Store findStoreBy(final Long storeId) { + return storeRepository.findById(storeId) + .orElseThrow(() -> new NotFoundStoreException("존재하지 않는 가게입니다.")); + } + + private List createMenus(final Store store, + final List menuLineItems + ) { + return menuLineItems.stream() + .map(menuLineItem -> createMenu(store, menuLineItem)) + .toList(); + } + + private Menu createMenu(final Store store, final StoreMenuRegistrationRequest.MenuLineItem menuLineItem) { + MenuCategory menuCategory = findMenuCategoryBy(store, menuLineItem.categoryName()); + return new Menu(store, menuCategory, menuLineItem.name(), menuLineItem.price(), menuLineItem.imageUrl()); + } + + private MenuCategory findMenuCategoryBy(final Store store, final String manuCategoryName) { + return menuCategoryRepository.findByStoreIdAndName(store.getId(), manuCategoryName) + .orElseThrow(() -> new NotFoundMenuCategoryException(store + ", " + manuCategoryName + " 의 메뉴카테고리가 없습니다.")); + } + +} diff --git a/src/main/java/camp/woowak/lab/store/service/dto/StoreMenuRegistrationRequest.java b/src/main/java/camp/woowak/lab/store/service/dto/StoreMenuRegistrationRequest.java new file mode 100644 index 00000000..5a588aaa --- /dev/null +++ b/src/main/java/camp/woowak/lab/store/service/dto/StoreMenuRegistrationRequest.java @@ -0,0 +1,31 @@ +package camp.woowak.lab.store.service.dto; + +import java.util.List; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +public record StoreMenuRegistrationRequest( + @NotBlank(message = "가게 ID는 필수값입니다.") + Long storeId, + + @NotNull(message = "등록할 메뉴는 필수값입니다.") + List menuItems +) { + + public record MenuLineItem( + @NotBlank(message = "메뉴 이름은 필수값입니다.") + String name, + + @NotBlank(message = "사진은 필수값입니다.") + String imageUrl, + + @NotBlank(message = "메뉴 카테고리 이름은 필수값입니다.") + String categoryName, + + @NotNull(message = "메뉴 가격은 필수값입니다.") + Integer price + ) { + } + +} diff --git a/src/main/java/camp/woowak/lab/vendor/domain/Vendor.java b/src/main/java/camp/woowak/lab/vendor/domain/Vendor.java index d6f35a9b..0e39e56d 100644 --- a/src/main/java/camp/woowak/lab/vendor/domain/Vendor.java +++ b/src/main/java/camp/woowak/lab/vendor/domain/Vendor.java @@ -10,8 +10,10 @@ import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.OneToOne; +import lombok.Getter; @Entity +@Getter public class Vendor { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/src/test/java/camp/woowak/lab/menu/domain/MenuCategoryTest.java b/src/test/java/camp/woowak/lab/menu/domain/MenuCategoryTest.java new file mode 100644 index 00000000..87febf38 --- /dev/null +++ b/src/test/java/camp/woowak/lab/menu/domain/MenuCategoryTest.java @@ -0,0 +1,118 @@ +package camp.woowak.lab.menu.domain; + +import static org.assertj.core.api.Assertions.*; + +import java.time.LocalDateTime; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import camp.woowak.lab.menu.exception.InvalidMenuCategoryCreationException; +import camp.woowak.lab.payaccount.domain.PayAccount; +import camp.woowak.lab.payaccount.domain.TestPayAccount; +import camp.woowak.lab.store.domain.Store; +import camp.woowak.lab.store.domain.StoreAddress; +import camp.woowak.lab.store.domain.StoreCategory; +import camp.woowak.lab.vendor.domain.Vendor; +import camp.woowak.lab.web.authentication.NoOpPasswordEncoder; +import camp.woowak.lab.web.authentication.PasswordEncoder; + +class MenuCategoryTest { + + @Nested + @DisplayName("메뉴 카테고리 생성은") + class MenuCategoryCreateTest { + + Store storeFixture = createValidStore(); + + @Nested + @DisplayName("메뉴 카테고리에 대한 가게가") + class StoreOfMenu { + + @Test + @DisplayName("[Exception] Null 이면 InvalidMenuCategoryCreationException 이 발생한다") + void isNull() { + // given & when & then + assertThatCode(() -> new MenuCategory(null, "가게")) + .isInstanceOf(InvalidMenuCategoryCreationException.class); + } + + } + + @Nested + @DisplayName("이름이") + class MenuName { + + @Test + @DisplayName("[Success] 10글자 이하만 가능하다") + void success() { + // given & when & then + assertThatCode(() -> new MenuCategory(storeFixture, "1234567890")) + .doesNotThrowAnyException(); + } + + @Test + @DisplayName("[Exception] Null 이면 InvalidMenuCreationException 이 발생한다") + void isNull() { + // given & when & then + assertThatCode(() -> new MenuCategory(storeFixture, null)) + .isInstanceOf(InvalidMenuCategoryCreationException.class); + } + + @Test + @DisplayName("[Exception] 빈 문자열이면 InvalidMenuCreationException 이 발생한다") + void isEmpty() { + // given & when & then + assertThatCode(() -> new MenuCategory(storeFixture, "")) + .isInstanceOf(InvalidMenuCategoryCreationException.class); + } + + @Test + @DisplayName("[Exception] 공백 문자열이면 InvalidMenuCreationException 이 발생한다") + void isBlank() { + // given & when & then + assertThatCode(() -> new MenuCategory(storeFixture, " ")) + .isInstanceOf(InvalidMenuCategoryCreationException.class); + } + + @Test + @DisplayName("[Exception] 10 글자를 초과하면 InvalidMenuCreationException 이 발생한다") + void greaterThanMaxNameLength() { + // given & when & then + assertThatCode(() -> new MenuCategory(storeFixture, "12345678901")) + .isInstanceOf(InvalidMenuCategoryCreationException.class); + } + + } + + } + + private Store createValidStore() { + LocalDateTime validStartDateFixture = LocalDateTime.of(2020, 1, 1, 1, 1); + LocalDateTime validEndDateFixture = LocalDateTime.of(2020, 1, 1, 2, 1); + String validNameFixture = "3K1K 가게"; + String validAddressFixture = StoreAddress.DEFAULT_DISTRICT; + String validPhoneNumberFixture = "02-1234-5678"; + Integer validMinOrderPriceFixture = 5000; + + return new Store(createVendor(), createStoreCategory(), validNameFixture, validAddressFixture, + validPhoneNumberFixture, + validMinOrderPriceFixture, + validStartDateFixture, validEndDateFixture); + } + + private Vendor createVendor() { + PayAccount payAccount = new TestPayAccount(1L); + PasswordEncoder passwordEncoder = new NoOpPasswordEncoder(); + + return new Vendor("vendor", + "validEmail@validEmail.com", + "validPassword", "010-0000-0000", payAccount, passwordEncoder); + } + + private StoreCategory createStoreCategory() { + return new StoreCategory("양식"); + } + +} \ No newline at end of file diff --git a/src/test/java/camp/woowak/lab/menu/domain/MenuTest.java b/src/test/java/camp/woowak/lab/menu/domain/MenuTest.java new file mode 100644 index 00000000..79cb2c43 --- /dev/null +++ b/src/test/java/camp/woowak/lab/menu/domain/MenuTest.java @@ -0,0 +1,205 @@ +package camp.woowak.lab.menu.domain; + +import static org.assertj.core.api.Assertions.*; + +import java.time.LocalDateTime; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import camp.woowak.lab.menu.exception.InvalidMenuCreationException; +import camp.woowak.lab.payaccount.domain.PayAccount; +import camp.woowak.lab.payaccount.domain.TestPayAccount; +import camp.woowak.lab.store.domain.Store; +import camp.woowak.lab.store.domain.StoreAddress; +import camp.woowak.lab.store.domain.StoreCategory; +import camp.woowak.lab.vendor.domain.Vendor; +import camp.woowak.lab.web.authentication.NoOpPasswordEncoder; +import camp.woowak.lab.web.authentication.PasswordEncoder; + +class MenuTest { + + Store storeFixture = createValidStore(); + MenuCategory menuCategoryFixture = createValidMenuCategory(); + + @Nested + @DisplayName("메뉴 생성은") + class MenuCreateTest { + + @Nested + @DisplayName("메뉴가 속한 가게가") + class MenuOfStore { + + @Test + @DisplayName("[Exception] Null 이면 InvalidMenuCreationException 이 발생한다") + void isNull() { + // given & when & then + assertThatCode(() -> new Menu(null, menuCategoryFixture, "1234", 1000, "image")) + .isInstanceOf(InvalidMenuCreationException.class); + } + + } + + @Nested + @DisplayName("메뉴카테고리가") + class MenuCategory { + + @Test + @DisplayName("[Exception] Null 이면 InvalidMenuCreationException 이 발생한다") + void isNull() { + // given & when & then + assertThatCode(() -> new Menu(storeFixture, null, "1234", 1000, "image")) + .isInstanceOf(InvalidMenuCreationException.class); + } + + } + + @Nested + @DisplayName("메뉴 이름이") + class MenuName { + + @Test + @DisplayName("[Success] 10글자 이하만 가능하다") + void success() { + // given & when & then + assertThatCode(() -> new Menu(storeFixture, menuCategoryFixture, "1234567890", 1000, "image")) + .doesNotThrowAnyException(); + } + + @Test + @DisplayName("[Exception] Null 이면 InvalidMenuCreationException 이 발생한다") + void isNull() { + // given & when & then + assertThatCode(() -> new Menu(storeFixture, menuCategoryFixture, null, 1000, "image")) + .isInstanceOf(InvalidMenuCreationException.class); + } + + @Test + @DisplayName("[Exception] 빈 문자열이면 InvalidMenuCreationException 이 발생한다") + void isEmpty() { + // given & when & then + assertThatCode(() -> new Menu(storeFixture, menuCategoryFixture, "", 1000, "image")) + .isInstanceOf(InvalidMenuCreationException.class); + } + + @Test + @DisplayName("[Exception] 공백 문자열이면 InvalidMenuCreationException 이 발생한다") + void isBlank() { + // given & when & then + assertThatCode(() -> new Menu(storeFixture, menuCategoryFixture, " ", 1000, "image")) + .isInstanceOf(InvalidMenuCreationException.class); + } + + @Test + @DisplayName("[Exception] 10 글자를 초과하면 InvalidMenuCreationException 이 발생한다") + void greaterThanMaxNameLength() { + // given & when & then + assertThatCode(() -> new Menu(storeFixture, menuCategoryFixture, "12345678901", 1000, "image")) + .isInstanceOf(InvalidMenuCreationException.class); + } + + } + + @Nested + @DisplayName("메뉴 가격이") + class MenuPrice { + + @Test + @DisplayName("[Success] 양수만 가능하다") + void success() { + // given & when & then + assertThatCode(() -> new Menu(storeFixture, menuCategoryFixture, "1234567890", 1, "image")) + .doesNotThrowAnyException(); + } + + @Test + @DisplayName("[Exception] Null 이면 InvalidMenuCreationException 이 발생한다") + void isNull() { + // given & when & then + assertThatCode(() -> new Menu(storeFixture, menuCategoryFixture, "메뉴이름", null, "image")) + .isInstanceOf(InvalidMenuCreationException.class); + } + + @Test + @DisplayName("[Exception] 음수면 InvalidMenuCreationException 이 발생한다") + void isNegative() { + // given & when & then + assertThatCode(() -> new Menu(storeFixture, menuCategoryFixture, "메뉴이름", -1, "image")) + .isInstanceOf(InvalidMenuCreationException.class); + } + + @Test + @DisplayName("[Exception] 0이면 InvalidMenuCreationException 이 발생한다") + void isZero() { + // given & when & then + assertThatCode(() -> new Menu(storeFixture, menuCategoryFixture, "메뉴이름", 0, "image")) + .isInstanceOf(InvalidMenuCreationException.class); + } + + } + + @Nested + @DisplayName("메뉴 사진 url 이") + class MenuDescription { + + @Test + @DisplayName("[Exception] Null 이면 InvalidMenuCreationException 이 발생한다") + void isNull() { + // given & when & then + assertThatCode(() -> new Menu(storeFixture, menuCategoryFixture, null, 1000, "image")) + .isInstanceOf(InvalidMenuCreationException.class); + } + + @Test + @DisplayName("[Exception] 빈 문자열이면 InvalidMenuCreationException 이 발생한다") + void isEmpty() { + // given & when & then + assertThatCode(() -> new Menu(storeFixture, menuCategoryFixture, "", 1000, "image")) + .isInstanceOf(InvalidMenuCreationException.class); + } + + @Test + @DisplayName("[Exception] 공백 문자열이면 InvalidMenuCreationException 이 발생한다") + void isBlank() { + // given & when & then + assertThatCode(() -> new Menu(storeFixture, menuCategoryFixture, " ", 1000, "image")) + .isInstanceOf(InvalidMenuCreationException.class); + } + + } + + } + + private Store createValidStore() { + LocalDateTime validStartDateFixture = LocalDateTime.of(2020, 1, 1, 1, 1); + LocalDateTime validEndDateFixture = LocalDateTime.of(2020, 1, 1, 2, 1); + String validNameFixture = "3K1K 가게"; + String validAddressFixture = StoreAddress.DEFAULT_DISTRICT; + String validPhoneNumberFixture = "02-1234-5678"; + Integer validMinOrderPriceFixture = 5000; + + return new Store(createVendor(), createStoreCategory(), validNameFixture, validAddressFixture, + validPhoneNumberFixture, + validMinOrderPriceFixture, + validStartDateFixture, validEndDateFixture); + } + + private MenuCategory createValidMenuCategory() { + return new MenuCategory(storeFixture, "1234567890"); + } + + private Vendor createVendor() { + PayAccount payAccount = new TestPayAccount(1L); + PasswordEncoder passwordEncoder = new NoOpPasswordEncoder(); + + return new Vendor("vendor", + "validEmail@validEmail.com", + "validPassword", "010-0000-0000", payAccount, passwordEncoder); + } + + private StoreCategory createStoreCategory() { + return new StoreCategory("양식"); + } + +} \ No newline at end of file diff --git a/src/test/java/camp/woowak/lab/menu/repository/MenuCategoryRepositoryTest.java b/src/test/java/camp/woowak/lab/menu/repository/MenuCategoryRepositoryTest.java new file mode 100644 index 00000000..37b7ceb5 --- /dev/null +++ b/src/test/java/camp/woowak/lab/menu/repository/MenuCategoryRepositoryTest.java @@ -0,0 +1,139 @@ +package camp.woowak.lab.menu.repository; + +import static org.assertj.core.api.Assertions.*; + +import java.time.LocalDateTime; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; + +import camp.woowak.lab.infra.date.DateTimeProvider; +import camp.woowak.lab.menu.domain.MenuCategory; +import camp.woowak.lab.payaccount.domain.PayAccount; +import camp.woowak.lab.payaccount.repository.PayAccountRepository; +import camp.woowak.lab.store.domain.Store; +import camp.woowak.lab.store.domain.StoreAddress; +import camp.woowak.lab.store.domain.StoreCategory; +import camp.woowak.lab.store.repository.StoreCategoryRepository; +import camp.woowak.lab.store.repository.StoreRepository; +import camp.woowak.lab.vendor.domain.Vendor; +import camp.woowak.lab.vendor.repository.VendorRepository; +import camp.woowak.lab.web.authentication.NoOpPasswordEncoder; +import camp.woowak.lab.web.authentication.PasswordEncoder; + +@DataJpaTest +class MenuCategoryRepositoryTest { + + @Autowired + VendorRepository vendorRepository; + + @Autowired + StoreRepository storeRepository; + + @Autowired + MenuCategoryRepository menuCategoryRepository; + + @Autowired + StoreCategoryRepository storeCategoryRepository; + + @Autowired + PayAccountRepository payAccountRepository; + + @Nested + @DisplayName("가게와 메뉴카테고리 이름으로 메뉴카테고리를 조회하는 기능은") + class FindByStoreAndNameTest { + + @Test + @DisplayName("[Success] 가게와 메뉴카테고리 이름이 있으면 조회를 성공한다") + void success() { + // given + PayAccount payAccount = payAccountRepository.save(new PayAccount()); + + Vendor vendor = createVendor(payAccount); + String categoryName = "돈가스"; + StoreCategory storeCategory = new StoreCategory(categoryName); + + vendorRepository.saveAndFlush(vendor); + storeCategoryRepository.saveAndFlush(storeCategory); + + Store store = createStore(vendor, storeCategory); + storeRepository.saveAndFlush(store); + MenuCategory menuCategory = new MenuCategory(store, categoryName); + menuCategoryRepository.save(menuCategory); + + // when & then + assertThat(menuCategoryRepository.findByStoreIdAndName(store.getId(), categoryName)) + .isPresent() + .containsSame(menuCategory); + } + + @Test + @DisplayName("[Exception] 가게가 없으면 빈 Optional 을 반환한다") + void notExistStore() { + // given + String categoryName = "돈가스"; + + // when & then + assertThat(menuCategoryRepository.findByStoreIdAndName(1234567L, categoryName)).isEmpty(); + } + + @Test + @DisplayName("[Exception] 메뉴카테고리 이름이 없으면 빈 Optional 을 반환한다") + void notExistMenuCategoryName() { + // given + String categoryName = "돈가스"; + String notExistCategoryName = "xxx"; + PayAccount payAccount = payAccountRepository.save(new PayAccount()); + + Vendor vendor = createVendor(payAccount); + vendorRepository.saveAndFlush(vendor); + + StoreCategory storeCategory = new StoreCategory(categoryName); + storeCategoryRepository.saveAndFlush(storeCategory); + + Store store = createStore(vendor, storeCategory); + storeRepository.saveAndFlush(store); + + MenuCategory menuCategory = new MenuCategory(store, categoryName); + menuCategoryRepository.save(menuCategory); + + // when & then + assertThat(menuCategoryRepository.findByStoreIdAndName(store.getId(), notExistCategoryName)) + .isEmpty(); + } + + } + + private Store createStore(Vendor vendor, StoreCategory storeCategory) { + DateTimeProvider fixedStartTime = () -> LocalDateTime.of(2024, 8, 24, 1, 0, 0); + DateTimeProvider fixedEndTime = () -> LocalDateTime.of(2024, 8, 24, 5, 0, 0); + + LocalDateTime validStartTimeFixture = fixedStartTime.now(); + LocalDateTime validEndTimeFixture = fixedEndTime.now(); + + String validNameFixture = "3K1K 가게"; + String validAddressFixture = StoreAddress.DEFAULT_DISTRICT; + String validPhoneNumberFixture = "02-0000-0000"; + Integer validMinOrderPriceFixture = 5000; + + return new Store(vendor, + storeCategory, + validNameFixture, + validAddressFixture, + validPhoneNumberFixture, + validMinOrderPriceFixture, + validStartTimeFixture, + validEndTimeFixture + ); + } + + private Vendor createVendor(PayAccount payAccount) { + PasswordEncoder passwordEncoder = new NoOpPasswordEncoder(); + return new Vendor("vendorName", "vendorEmail@example.com", "vendorPassword", "010-0000-0000", payAccount, + passwordEncoder); + } + +} \ No newline at end of file diff --git a/src/test/java/camp/woowak/lab/store/domain/StoreTest.java b/src/test/java/camp/woowak/lab/store/domain/StoreTest.java index 20b96e69..5d59985c 100644 --- a/src/test/java/camp/woowak/lab/store/domain/StoreTest.java +++ b/src/test/java/camp/woowak/lab/store/domain/StoreTest.java @@ -10,7 +10,12 @@ import org.junit.jupiter.api.Test; import camp.woowak.lab.infra.date.DateTimeProvider; +import camp.woowak.lab.payaccount.domain.PayAccount; +import camp.woowak.lab.payaccount.domain.TestPayAccount; import camp.woowak.lab.store.exception.StoreException; +import camp.woowak.lab.vendor.domain.Vendor; +import camp.woowak.lab.web.authentication.NoOpPasswordEncoder; +import camp.woowak.lab.web.authentication.PasswordEncoder; class StoreTest { @@ -41,9 +46,10 @@ void validMinOrderPrice() { int validMinOrderPrice = 5000; // when & then - assertThatCode(() -> new Store(null, null, validNameFixture, validAddressFixture, null, - validMinOrderPrice, - validStartTimeFixture, validEndTimeFixture)) + assertThatCode( + () -> new Store(createVendor(), createStoreCategory(), validNameFixture, validAddressFixture, null, + validMinOrderPrice, + validStartTimeFixture, validEndTimeFixture)) .doesNotThrowAnyException(); } @@ -54,9 +60,10 @@ void lessThanMinOrderPrice() { int lessThanMinOrderPrice = 4999; // when & then - assertThatThrownBy(() -> new Store(null, null, validNameFixture, validAddressFixture, null, - lessThanMinOrderPrice, - validStartTimeFixture, validEndTimeFixture)) + assertThatThrownBy( + () -> new Store(createVendor(), createStoreCategory(), validNameFixture, validAddressFixture, null, + lessThanMinOrderPrice, + validStartTimeFixture, validEndTimeFixture)) .isInstanceOf(StoreException.class) .hasMessage(INVALID_MIN_ORDER_PRICE.getMessage()); } @@ -68,9 +75,10 @@ void validUnitOrderPrice() { int validMinOrderPrice = 10000; // when & then - assertThatCode(() -> new Store(null, null, validNameFixture, validAddressFixture, null, - validMinOrderPrice, - validStartTimeFixture, validEndTimeFixture)) + assertThatCode( + () -> new Store(createVendor(), createStoreCategory(), validNameFixture, validAddressFixture, null, + validMinOrderPrice, + validStartTimeFixture, validEndTimeFixture)) .doesNotThrowAnyException(); } @@ -81,9 +89,10 @@ void inValidUnitOrderPrice() { int inValidUnitOrderPrice = 5001; // when & then - assertThatThrownBy(() -> new Store(null, null, validNameFixture, validAddressFixture, null, - inValidUnitOrderPrice, - validStartTimeFixture, validEndTimeFixture)) + assertThatThrownBy( + () -> new Store(createVendor(), createStoreCategory(), validNameFixture, validAddressFixture, null, + inValidUnitOrderPrice, + validStartTimeFixture, validEndTimeFixture)) .isInstanceOf(StoreException.class) .hasMessage(INVALID_UNIT_OF_MIN_ORDER_PRICE.getMessage()); } @@ -102,7 +111,8 @@ void storeStartTimeBeforeThanEndTime() { // when & then assertThatCode( - () -> new Store(null, null, validNameFixture, validAddressFixture, null, validMinOrderPriceFixture, + () -> new Store(createVendor(), createStoreCategory(), validNameFixture, validAddressFixture, null, + validMinOrderPriceFixture, validStartTime, validEndTime)) .doesNotThrowAnyException(); } @@ -115,7 +125,8 @@ void endTimeSameWithStartTime() { // when & then assertThatThrownBy( - () -> new Store(null, null, validNameFixture, validAddressFixture, null, validMinOrderPriceFixture, + () -> new Store(createVendor(), createStoreCategory(), validNameFixture, validAddressFixture, null, + validMinOrderPriceFixture, validStartTimeFixture, endTimeSameWithStartTime)) .isInstanceOf(StoreException.class) .hasMessage(INVALID_TIME.getMessage()); @@ -129,7 +140,8 @@ void endTimeBeforeThanStartTime() { // when & then assertThatThrownBy( - () -> new Store(null, null, validNameFixture, validAddressFixture, null, validMinOrderPriceFixture, + () -> new Store(createVendor(), createStoreCategory(), validNameFixture, validAddressFixture, null, + validMinOrderPriceFixture, validStartTimeFixture, endTimeBeforeThanStartTime)) .isInstanceOf(StoreException.class) .hasMessage(INVALID_TIME.getMessage()); @@ -144,7 +156,8 @@ void validStartTimeUnit() { // when & then assertThatCode( - () -> new Store(null, null, validNameFixture, validAddressFixture, null, validMinOrderPriceFixture, + () -> new Store(createVendor(), createStoreCategory(), validNameFixture, validAddressFixture, null, + validMinOrderPriceFixture, validStartTimeUnitMinute, validEndTimeUnitMinute)) .doesNotThrowAnyException(); } @@ -158,7 +171,8 @@ void startTimeWithSeconds() { // when & then assertThatThrownBy( - () -> new Store(null, null, validNameFixture, validAddressFixture, null, validMinOrderPriceFixture, + () -> new Store(createVendor(), createStoreCategory(), validNameFixture, validAddressFixture, null, + validMinOrderPriceFixture, startTimeWithSeconds, validEndTimeFixture)) .isInstanceOf(StoreException.class) .hasMessage(INVALID_TIME_UNIT.getMessage()); @@ -172,8 +186,10 @@ void startTimeWithNanoSeconds() { LocalDateTime startTimeWithNanoSeconds = inValidUnitDateTimeProvider.now(); // when & then - assertThatThrownBy(() -> new Store(null, null, validNameFixture, validAddressFixture, null, 5000, - startTimeWithNanoSeconds, validEndTimeFixture)) + assertThatThrownBy( + () -> new Store(createVendor(), createStoreCategory(), validNameFixture, validAddressFixture, null, + 5000, + startTimeWithNanoSeconds, validEndTimeFixture)) .isInstanceOf(StoreException.class) .hasMessage(INVALID_TIME_UNIT.getMessage()); } @@ -186,8 +202,10 @@ void endTimeWithSeconds() { LocalDateTime endTimeWithSeconds = inValidUnitDateTimeProvider.now(); // when & then - assertThatThrownBy(() -> new Store(null, null, validNameFixture, validAddressFixture, null, 5000, - validStartTimeFixture, endTimeWithSeconds)) + assertThatThrownBy( + () -> new Store(createVendor(), createStoreCategory(), validNameFixture, validAddressFixture, null, + 5000, + validStartTimeFixture, endTimeWithSeconds)) .isInstanceOf(StoreException.class) .hasMessage(INVALID_TIME_UNIT.getMessage()); } @@ -200,8 +218,10 @@ void endTimeWithNanoSeconds() { LocalDateTime endTimeWithNanoSeconds = inValidUnitDateTimeProvider.now(); // when & then - assertThatThrownBy(() -> new Store(null, null, validNameFixture, validAddressFixture, null, 5000, - validStartTimeFixture, endTimeWithNanoSeconds)) + assertThatThrownBy( + () -> new Store(createVendor(), createStoreCategory(), validNameFixture, validAddressFixture, null, + 5000, + validStartTimeFixture, endTimeWithNanoSeconds)) .isInstanceOf(StoreException.class) .hasMessage(INVALID_TIME_UNIT.getMessage()); } @@ -219,9 +239,11 @@ void validStoreName() { String lengthValidStoreName = validNameFixture; // when & then - assertThatCode(() -> new Store(null, null, lengthValidStoreName, validAddressFixture, null, - validMinOrderPriceFixture, - validStartTimeFixture, validEndTimeFixture)) + assertThatCode( + () -> new Store(createVendor(), createStoreCategory(), lengthValidStoreName, validAddressFixture, + null, + validMinOrderPriceFixture, + validStartTimeFixture, validEndTimeFixture)) .doesNotThrowAnyException(); } @@ -232,9 +254,11 @@ void lessThanMinLengthName() { String lessThanMinLengthName = "헤"; // when & then - assertThatThrownBy(() -> new Store(null, null, lessThanMinLengthName, validAddressFixture, null, - validMinOrderPriceFixture, - validStartTimeFixture, validEndTimeFixture)) + assertThatThrownBy( + () -> new Store(createVendor(), createStoreCategory(), lessThanMinLengthName, validAddressFixture, + null, + validMinOrderPriceFixture, + validStartTimeFixture, validEndTimeFixture)) .isInstanceOf(StoreException.class) .hasMessage(INVALID_NAME_RANGE.getMessage()); } @@ -247,7 +271,8 @@ void greaterThanMaxLengthName() { // when & then assertThatThrownBy( - () -> new Store(null, null, greaterThanMaxLengthName, validAddressFixture, null, + () -> new Store(createVendor(), createStoreCategory(), greaterThanMaxLengthName, + validAddressFixture, null, validMinOrderPriceFixture, validStartTimeFixture, validEndTimeFixture)) .isInstanceOf(StoreException.class) @@ -267,7 +292,8 @@ void onlySongPa() { String validAddress = "송파"; // when & then assertThatCode( - () -> new Store(null, null, validNameFixture, validAddress, null, validMinOrderPriceFixture, + () -> new Store(createVendor(), createStoreCategory(), validNameFixture, validAddress, null, + validMinOrderPriceFixture, validStartTimeFixture, validEndTimeFixture)) .doesNotThrowAnyException(); } @@ -279,7 +305,8 @@ void notSongPa() { String validAddress = "강남"; // when & then assertThatThrownBy( - () -> new Store(null, null, validNameFixture, validAddress, null, validMinOrderPriceFixture, + () -> new Store(createVendor(), createStoreCategory(), validNameFixture, validAddress, null, + validMinOrderPriceFixture, validStartTimeFixture, validEndTimeFixture)) .isInstanceOf(StoreException.class) .hasMessage(INVALID_ADDRESS.getMessage()); @@ -289,4 +316,17 @@ void notSongPa() { } + private Vendor createVendor() { + PayAccount payAccount = new TestPayAccount(1L); + PasswordEncoder passwordEncoder = new NoOpPasswordEncoder(); + + return new Vendor("vendor", + "validEmail@validEmail.com", + "validPassword", "010-0000-0000", payAccount, passwordEncoder); + } + + private StoreCategory createStoreCategory() { + return new StoreCategory("양식"); + } + } \ No newline at end of file diff --git a/src/test/java/camp/woowak/lab/store/service/StoreMenuRegistrationServiceTest.java b/src/test/java/camp/woowak/lab/store/service/StoreMenuRegistrationServiceTest.java new file mode 100644 index 00000000..c46fd33b --- /dev/null +++ b/src/test/java/camp/woowak/lab/store/service/StoreMenuRegistrationServiceTest.java @@ -0,0 +1,122 @@ +package camp.woowak.lab.store.service; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; + +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 camp.woowak.lab.menu.domain.MenuCategory; +import camp.woowak.lab.menu.repository.MenuCategoryRepository; +import camp.woowak.lab.menu.repository.MenuRepository; +import camp.woowak.lab.payaccount.domain.PayAccount; +import camp.woowak.lab.payaccount.domain.TestPayAccount; +import camp.woowak.lab.store.domain.Store; +import camp.woowak.lab.store.domain.StoreAddress; +import camp.woowak.lab.store.domain.StoreCategory; +import camp.woowak.lab.store.exception.NotFoundStoreException; +import camp.woowak.lab.store.repository.StoreRepository; +import camp.woowak.lab.store.service.dto.StoreMenuRegistrationRequest; +import camp.woowak.lab.vendor.domain.Vendor; +import camp.woowak.lab.web.authentication.NoOpPasswordEncoder; +import camp.woowak.lab.web.authentication.PasswordEncoder; + +@ExtendWith(MockitoExtension.class) +class StoreMenuRegistrationServiceTest { + + @Mock + StoreRepository storeRepository; + + @Mock + MenuRepository menuRepository; + + @Mock + MenuCategoryRepository menuCategoryRepository; + + @InjectMocks + StoreMenuRegistrationService storeMenuRegistrationService; + + Vendor owner = createVendor(); + Store storeFixture = createValidStore(owner); + + MenuCategory menuCategoryFixture = createValidMenuCategory(); + + @Test + @DisplayName("[Success] 메뉴 등록 성공") + void storeMenuRegistrationSuccess() { + // given + Long storeId = 1L; + + List menuItems = List.of( + new StoreMenuRegistrationRequest.MenuLineItem("메뉴1", "image1.jpg", "카테고리1", 10000) + ); + StoreMenuRegistrationRequest request = new StoreMenuRegistrationRequest(storeId, menuItems); + + when(storeRepository.findById(storeId)).thenReturn(Optional.of(storeFixture)); + when(menuCategoryRepository.findByStoreIdAndName(storeFixture.getId(), "카테고리1")).thenReturn( + Optional.of(menuCategoryFixture)); + + // when + storeMenuRegistrationService.storeMenuRegistration(owner, request); + + // then + verify(storeRepository).findById(storeId); + verify(menuCategoryRepository).findByStoreIdAndName(storeFixture.getId(), "카테고리1"); + verify(menuRepository).saveAll(anyList()); + } + + @Test + @DisplayName("[Exception] 존재하지 않는 가게") + void storeMenuRegistrationStoreNotFound() { + // given + Long storeId = 1L; + + List menuItems = List.of( + new StoreMenuRegistrationRequest.MenuLineItem("메뉴1", "image1.jpg", "카테고리1", 10000) + ); + StoreMenuRegistrationRequest request = new StoreMenuRegistrationRequest(storeId, menuItems); + + when(storeRepository.findById(storeId)).thenReturn(Optional.empty()); + + // when & then + assertThatThrownBy(() -> storeMenuRegistrationService.storeMenuRegistration(owner, request)) + .isInstanceOf(NotFoundStoreException.class); + } + + private Store createValidStore(Vendor owner) { + LocalDateTime validStartDateFixture = LocalDateTime.of(2020, 1, 1, 1, 1); + LocalDateTime validEndDateFixture = LocalDateTime.of(2020, 1, 1, 2, 1); + String validNameFixture = "3K1K 가게"; + String validAddressFixture = StoreAddress.DEFAULT_DISTRICT; + String validPhoneNumberFixture = "02-1234-5678"; + Integer validMinOrderPriceFixture = 5000; + + return new Store(owner, createStoreCategory(), validNameFixture, validAddressFixture, + validPhoneNumberFixture, + validMinOrderPriceFixture, + validStartDateFixture, validEndDateFixture); + } + + private MenuCategory createValidMenuCategory() { + return new MenuCategory(storeFixture, "1234567890"); + } + + private Vendor createVendor() { + PayAccount payAccount = new TestPayAccount(1L); + PasswordEncoder passwordEncoder = new NoOpPasswordEncoder(); + return new Vendor("vendorName", "vendorEmail@example.com", "vendorPassword", "010-0000-0000", payAccount, + passwordEncoder); + } + + private StoreCategory createStoreCategory() { + return new StoreCategory("양식"); + } +} \ No newline at end of file