diff --git a/README.md b/README.md index fb9680097..2acb820ec 100644 --- a/README.md +++ b/README.md @@ -11,68 +11,68 @@ docker compose -p kitchenpos up -d - 식당 음식 판매 정보 관리 기기 구현하기 - 메뉴 그룹 - 메뉴 그룹이란 메뉴들의 집합을 의미한다. - - 새로운 메뉴 그룹을 추가할 수 있다. - - 메뉴 그룹 추가 시 이름이 반드시 존재해야 한다. - - 모든 메뉴 그룹을 조회할 수 있다. + - [O] 새로운 메뉴 그룹을 추가할 수 있다. + - [O] 메뉴 그룹 추가 시 이름이 반드시 존재해야 한다. + - [] 모든 메뉴 그룹을 조회할 수 있다. - 메뉴 - 메뉴란 식당에서 판매되고 있는 음식과 해당 음식에 관련된 정보들이다. - - 새로운 메뉴를 추가할 수 있다. - - 메뉴는 반드시 메뉴 그룹에 포함 되어야 한다. - - 메뉴는 반드시 가격, 이름, 상품 정보를 가지고 있어야 한다. - - 메뉴의 상품 수량은 기존 상품의 수량과 같아야 한다. - - 메뉴의 가격은 상품 (수량 * 가격) 보다 클 수 없다. - - 메뉴 추가 시 메뉴의 이름이 부적절한지 검사한다. - - 메뉴의 가격을 변경할 수 있다. - - 메뉴의 가격은 해당 상품의 (수량 * 가격)이다. - - 메뉴의 가격은 상품 (수량 * 가격) 보다 클 수 없다. - - 메뉴를 고객에게 보여줄 수 있으며 숨길 수 있다. - - 모든 메뉴를 조회할 수 있다. + - [O] 새로운 메뉴를 추가할 수 있다. + - [O] 메뉴는 반드시 메뉴 그룹에 포함 되어야 한다. + - [O] 메뉴는 반드시 가격, 이름, 상품 정보를 가지고 있어야 한다. + - [O] 메뉴의 상품 수량은 기존 상품의 수량과 같아야 한다. + - [O] 메뉴의 가격은 상품 포함된 상품의 총 가격 보다 클 수 없다. + - [O] 메뉴 추가 시 메뉴의 이름이 부적절한지 검사한다. + - [O] 메뉴의 가격을 변경할 수 있다. + - [O] 메뉴의 가격은 해당 상품 총 가격 보다 클 수 없다. + - [O] 메뉴를 노출할 수 있다. + - [O] 메뉴를 숨길 수 있다. + - [] 모든 메뉴를 조회할 수 있다. - 주문 - 주문이란 식당에 있는 메뉴에 있는 음식에 주문한는 것을 의미한다. - - 주문 시 주문 항목은 반드시 존재해야 한다. - - 주문 항목 개수는 메뉴의 개수와 일치해야 한다. - - 주문 항목에 있는 메뉴들은 판매 되고 있는 메뉴들이어야 한다. - - 주문 가격은 주문 항목에 있는 메뉴의 가격 총합과 같아야 한다. - - 주문을 하게 되면 즉시 해당 주문의 상태는 주문 대기 상태가 된다. - - 손님은 식당에서 음식을 주문할 수 있다. - - 식당 주문 시 주문 항목에 반드시 수량이 존재해야 한다. - - 식당 주문 시 자리가 존재해야 하며 앉을 수 있어야 한다. - - 주문이 접수가 되면 손님에게 음식을 제공한다. - - 손님에게 음식이 제공되면 주문 상태를 주문 완료로 변경한다. - - 주문 상태가 주문 완료가 되면 매장 주인은 테이블을 정리한다. - - 손님은 배달을 주문 할 수 있다. - - 배달 주문 시 주소 정보가 반드시 있어야 한다. - - 배달 주문이 접수가 되면 라이더에게 주문 정보와 배송지 정보를 전달하며 배달을 요청한다. - - 배달을 요청할 경우 해당 주문의 상태는 접수 상태로 변경된다. - - 배달이 시작되면 주문의 상태를 배달 중으로 변경한다. - - 배달이 완료되면 주문의 상태를 배달 완료로 변경한다. - - 배달 완료가 확인되면 주문의 상태를 주문 상태를 주문 완료로 변경한다. - - 손님은 포장 주문을 할 수 있다. - - 주문이 접수가 되면 손님에게 음식을 제공한다. - - 손님에게 음식이 제공되면 주문 상태를 주문 완료로 변경한다. - - 주문을 했을 때 주문이 가능하다면 주문 접수를 진행한다. - - 주문의 상태는 대기, 접수, 전달, 배달 중, 배달 완료, 완료를 가진다. - - 주문을 하게 되면 가장 먼저 주문 대기 상태가 된 후 접수 상태로 변경된다. - - 모든 주문을 조회할 수 있다. + - [O] 주문의 상태는 대기, 접수, 제공, 배달 중, 배달 완료, 완료가 존재한다. + - [O] 주문의 유형은 매장 식사, 포장, 배달이 존재한다. + - [O] 주문 시 주문 유형 및 항목은 반드시 존재 해야한다. + - [O] 주문 항목 개수는 메뉴의 개수와 일치해야 한다. + - [O] 주문 항목에 있는 메뉴들은 판매 되고 있는 메뉴들이어야 한다. + - [] 주문 가격은 주문 항목에 있는 메뉴의 가격 총합과 같아야 한다. + - [O] 주문을 하게 되면 해당 주문의 상태는 주문 대기 상태가 된다. + - [O] 손님은 식당에서 음식을 주문할 수 있다. + - [O] 식당 주문 시 주문 테이블이 존재해야 하며 빈 테이블일 수 없다. + - [O] 주문이 접수 되면 손님에게 음식을 제공한다. + - [O] 손님에게 음식이 제공되면 주문 상태를 주문 완료로 변경한다. + - [O] 식당 주문 완료가 되면 매장 주인은 테이블을 정리한다. + - [O] 손님은 배달 주문을 할 수 있다. + - [O] 주문 항목에 반드시 수량이 존재해야 한다. + - [O] 배달 주문 시 배달 주소지가 반드시 있어야 한다. + - [O] 라이더에게 주문 정보와 배달 주소시를 전달 후 배달을 요청하며 주문을 접수한다. + - [O] 배달이 시작되면 주문의 상태를 배달 중으로 변경한다. + - [O] 배달이 완료되면 주문의 상태를 배달 완료로 변경한다. + - [O] 배달 완료가 확인되면 주문의 상태를 주문 상태를 주문 완료로 변경한다. + - [O] 손님은 포장 주문을 할 수 있다. + - [O] 주문 항목에 반드시 수량이 존재해야 한다. + - [O] 주문이 접수 되면 손님에게 음식을 제공한다. + - [O] 손님에게 음식이 제공되면 주문 상태를 주문 완료로 변경한다. + - [] 모든 주문을 조회할 수 있다. - 테이블 - 테이블이란 식당에서 식사할 수 있는 탁자를 의미한다. - - 새로운 테이블을 추가할 수 있다. - - 새로운 테이블을 추가 시 테이블의 이름은 반드시 존재해야 한다. - - 추가된 테이블은 손님이 바로 식사 할 수 있다. - - 손님은 테이블에 앉을 수 있다. - - 매장 주인은 테이블을 정리할 수 있다. - - 주문이 완료 된 상태의 테이블에서만 청소가 가능하다. - - 테이블에 앉는 손님의 수는 변경될 수 있다. - - 현재 테이블은 손님이 앉아있는 상태여야 한다. - - 앉아 있는 손님은 1명 이상이어야 한다. - - 모든 테이블을 조회 할 수 있다. + - [O] 새로운 테이블을 추가할 수 있다. + - [O] 새로운 테이블을 추가 시 테이블의 이름은 반드시 존재해야 한다. + - [O] 추가된 테이블은 손님이 바로 식사 할 수 있다. + - [O] 손님은 테이블에 앉을 수 있다. + - [O] 매장 주인은 테이블을 정리할 수 있다. + - [] 주문이 완료 된 상태의 테이블에서만 청소가 가능하다. + - [O] 테이블에 앉는 손님의 수는 변경될 수 있다. + - [O] 현재 테이블은 손님이 앉아있는 상태여야 한다. + - [O] 손님은 0명 이상이어야 한다. + - [] 모든 테이블을 조회 할 수 있다. - 상품 - 상품이란 메뉴에 구성되는 상품을 의미한다. - - 새로운 상품을 추가 할 수 있다. - - 상품은 이름과 가격 정보가 존재해야 하며 상품의 이름이 부적절한지 검사한다. - - 상품의 가격을 변경할 수 있다. - - 해당 상품으로 구성된 메뉴의 가격이 변경된 상품의 가격 총합보다 크다면 메뉴를 노출하지 않는다. - - 모든 상품을 조회 할 수 있다. + - [O] 새로운 상품을 추가 할 수 있다. + - [O] 상품의 가격은 반드시 존재해야 하며 0보다 커야 한다. + - [O] 상품의 이름은 반드시 존재해야 하며 부적절한지 검사한다. + - [O] 상품의 가격을 변경할 수 있다. + - [O] 해당 상품으로 구성된 메뉴의 가격이 변경된 상품의 가격 총합보다 크다면 메뉴를 노출하지 않는다. + - [] 모든 상품을 조회 할 수 있다. ## 용어 사전 diff --git a/src/main/java/kitchenpos/application/MenuService.java b/src/main/java/kitchenpos/application/MenuService.java index 7df104203..e42c7c339 100644 --- a/src/main/java/kitchenpos/application/MenuService.java +++ b/src/main/java/kitchenpos/application/MenuService.java @@ -75,6 +75,7 @@ public Menu create(final Menu request) { menuProduct.setQuantity(quantity); menuProducts.add(menuProduct); } + if (price.compareTo(sum) > 0) { throw new IllegalArgumentException(); } diff --git a/src/test/java/kitchenpos/menu/fixture/MenuFixture.java b/src/test/java/kitchenpos/menu/fixture/MenuFixture.java new file mode 100644 index 000000000..58cc91ac0 --- /dev/null +++ b/src/test/java/kitchenpos/menu/fixture/MenuFixture.java @@ -0,0 +1,97 @@ +package kitchenpos.menu.fixture; + +import kitchenpos.domain.Menu; +import kitchenpos.domain.MenuGroup; +import kitchenpos.domain.MenuProduct; +import kitchenpos.domain.Product; + +import java.math.BigDecimal; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +public class MenuFixture { + private MenuGroupFixture menuGroupFixture = new MenuGroupFixture(); + private MenuProductFixture menuProductFixture = new MenuProductFixture(); + + public Menu 메뉴 = create( + "메뉴_A", new BigDecimal(10000), menuGroupFixture.메뉴_그룹_A, + true, List.of(menuProductFixture.메뉴_상품) + ); + public Menu 메뉴_A_가격_10000 = create( + "메뉴_A", new BigDecimal(10000), menuGroupFixture.메뉴_그룹_A, + true, List.of(menuProductFixture.메뉴_상품_A_수량_10) + ); + public Menu 메뉴_B_가격_5000 = create( + "메뉴_B", new BigDecimal(5000), menuGroupFixture.메뉴_그룹_A, + true, List.of(menuProductFixture.메뉴_상품_B) + ); + public Menu 메뉴_C_가격_100000 = create( + "메뉴_C", new BigDecimal(100000), menuGroupFixture.메뉴_그룹_A, + false, List.of(menuProductFixture.메뉴_상품_C) + ); + public Menu 메뉴_그룹_없는_메뉴 = create( + "메뉴", new BigDecimal(10000), null, + true, List.of(menuProductFixture.메뉴_상품_A_수량_10) + ); + public Menu 가격_없는_메뉴 = create( + "메뉴", null, null, + true, List.of(menuProductFixture.메뉴_상품_A_수량_10) + ); + public Menu 상품_없는_메뉴 = create( + "메뉴", new BigDecimal(10000), menuGroupFixture.메뉴_그룹_A, + true, null + ); + public Menu 이름_없는_메뉴 = create( + null, new BigDecimal(10000), menuGroupFixture.메뉴_그룹_A, + true, List.of(menuProductFixture.메뉴_상품_A_수량_10) + ); + public Menu 부적절한_이름_메뉴 = create( + "fuck", new BigDecimal(10000), menuGroupFixture.메뉴_그룹_A, + true, List.of(menuProductFixture.메뉴_상품_A_수량_10) + ); + public Menu 비활성화_메뉴 = create( + "메뉴_A", new BigDecimal(10000), menuGroupFixture.메뉴_그룹_A, + false, List.of(menuProductFixture.메뉴_상품) + ); + public Menu 메뉴_가격_11000_상품_가격_10000 = create( + "메뉴", new BigDecimal(11000), menuGroupFixture.메뉴_그룹_A, + true, List.of(menuProductFixture.메뉴_상품_A_수량_10) + ); + + public static Menu create(String name, BigDecimal price, MenuGroup menuGroup, + boolean isDisplayed, List menuProducts) { + return create(UUID.randomUUID(), name, price, menuGroup, isDisplayed, menuProducts); + } + + public static Menu create(UUID id, String name, BigDecimal price, MenuGroup menuGroup, + boolean isDisplayed, List menuProducts) { + Menu menu = new Menu(); + menu.setId(id); + menu.setName(name); + menu.setPrice(price); + menu.setMenuGroup(menuGroup); + menu.setDisplayed(isDisplayed); + menu.setMenuProducts(menuProducts); + + return menu; + } + + public static MenuGroup extractMenuGroupFrom(Menu menu) { + return menu.getMenuGroup(); + } + + public static List extractProductsFrom(Menu menu) { + List products = menu.getMenuProducts() + .stream() + .map(menuProduct -> menuProduct.getProduct()) + .collect(Collectors.toList()); + + return products; + } + + public static Product extractProductFrom(Menu menu) { + MenuProduct menuProduct = menu.getMenuProducts().get(0); + return menuProduct.getProduct(); + } +} diff --git a/src/test/java/kitchenpos/menu/fixture/MenuGroupFixture.java b/src/test/java/kitchenpos/menu/fixture/MenuGroupFixture.java new file mode 100644 index 000000000..5ec3d5c5c --- /dev/null +++ b/src/test/java/kitchenpos/menu/fixture/MenuGroupFixture.java @@ -0,0 +1,21 @@ +package kitchenpos.menu.fixture; + +import kitchenpos.domain.MenuGroup; + +import java.util.UUID; + +public class MenuGroupFixture { + public MenuGroup 메뉴_그룹_A = MenuGroupFixture.create(UUID.randomUUID(), "그룹A"); + + public static MenuGroup create(String name) { + return create(UUID.randomUUID(), name); + } + + public static MenuGroup create(UUID id, String name) { + MenuGroup menuGroup = new MenuGroup(); + menuGroup.setId(id); + menuGroup.setName(name); + + return menuGroup; + } +} diff --git a/src/test/java/kitchenpos/menu/fixture/MenuProductFixture.java b/src/test/java/kitchenpos/menu/fixture/MenuProductFixture.java new file mode 100644 index 000000000..51d5905f1 --- /dev/null +++ b/src/test/java/kitchenpos/menu/fixture/MenuProductFixture.java @@ -0,0 +1,21 @@ +package kitchenpos.menu.fixture; + +import kitchenpos.domain.MenuProduct; +import kitchenpos.domain.Product; + +public class MenuProductFixture { + private ProductFixture productFixture = new ProductFixture(); + + public MenuProduct 메뉴_상품 = create(productFixture.상품, 10); + public MenuProduct 메뉴_상품_A_수량_10 = create(productFixture.상품_A_가격_1000,10); + public MenuProduct 메뉴_상품_B = create(productFixture.상품_B, 10); + public MenuProduct 메뉴_상품_C = create(productFixture.상품_C_가격_10000, 10); + + public static MenuProduct create(Product product, long quantity) { + MenuProduct menuProduct = new MenuProduct(); + menuProduct.setProduct(product); + menuProduct.setQuantity(quantity); + + return menuProduct; + } +} diff --git a/src/test/java/kitchenpos/menu/fixture/ProductFixture.java b/src/test/java/kitchenpos/menu/fixture/ProductFixture.java new file mode 100644 index 000000000..f8a4f4f3d --- /dev/null +++ b/src/test/java/kitchenpos/menu/fixture/ProductFixture.java @@ -0,0 +1,29 @@ +package kitchenpos.menu.fixture; + +import kitchenpos.domain.Product; + +import java.math.BigDecimal; +import java.util.UUID; + +public class ProductFixture { + public Product 상품 = create( "상품", new BigDecimal(1000)); + public Product 상품_A_가격_1000 = create( "상품A", new BigDecimal(1000)); + public Product 상품_B = create("상품B", new BigDecimal(500)); + public Product 상품_C_가격_10000 = create("상품C", new BigDecimal(10000)); + public Product 가격_없는_상품 = create(UUID.randomUUID(), "상품", null); + public Product 가격_음수_상품 = create(UUID.randomUUID(), "상품", new BigDecimal(-1)); + public Product 부적절한_이름_상품 = create(UUID.randomUUID(), "fuck", new BigDecimal(-1)); + + public static Product create(String name, BigDecimal price) { + return create(UUID.randomUUID(), name, price); + } + + public static Product create(UUID id, String name, BigDecimal price) { + Product product = new Product(); + product.setId(id); + product.setName(name); + product.setPrice(price); + + return product; + } +} diff --git a/src/test/java/kitchenpos/menu/service/MenuGroupServiceTest.java b/src/test/java/kitchenpos/menu/service/MenuGroupServiceTest.java new file mode 100644 index 000000000..84eec7c60 --- /dev/null +++ b/src/test/java/kitchenpos/menu/service/MenuGroupServiceTest.java @@ -0,0 +1,55 @@ +package kitchenpos.menu.service; + +import kitchenpos.application.MenuGroupService; +import kitchenpos.domain.MenuGroup; +import kitchenpos.domain.MenuGroupRepository; +import kitchenpos.menu.fixture.MenuGroupFixture; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.mockito.AdditionalAnswers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +@DisplayName("메뉴 그룹 서비스 테스트") +@ExtendWith(MockitoExtension.class) +public class MenuGroupServiceTest { + @Mock + private MenuGroupRepository menuGroupRepository; + @InjectMocks + private MenuGroupService menuGroupService; + + private MenuGroupFixture menuGroupFixture; + + @BeforeEach + void setUp() { + menuGroupFixture = new MenuGroupFixture(); + } + + @Test + @DisplayName("새로운 메뉴 그룹을 추가할 수 있다.") + void create() { + MenuGroup 한식 = menuGroupFixture.메뉴_그룹_A; + + Mockito.when(menuGroupRepository.save(Mockito.any())) + .then(AdditionalAnswers.returnsFirstArg()); + MenuGroup result = menuGroupService.create(한식); + + Assertions.assertThat(result.getId()).isNotNull(); + } + + @ParameterizedTest + @NullAndEmptySource + @DisplayName("메뉴 그룹 추가 시 이름이 반드시 존재해야 한다.") + void test(String name) { + Assertions.assertThatThrownBy( + () -> menuGroupService.create(MenuGroupFixture.create(name)) + ).isInstanceOf(IllegalArgumentException.class); + } +} diff --git a/src/test/java/kitchenpos/menu/service/MenuServiceTest.java b/src/test/java/kitchenpos/menu/service/MenuServiceTest.java new file mode 100644 index 000000000..400e5e7da --- /dev/null +++ b/src/test/java/kitchenpos/menu/service/MenuServiceTest.java @@ -0,0 +1,219 @@ +package kitchenpos.menu.service; + +import kitchenpos.application.MenuService; +import kitchenpos.domain.*; +import kitchenpos.infra.PurgomalumClient; +import kitchenpos.menu.fixture.MenuFixture; +import kitchenpos.menu.fixture.MenuProductFixture; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.AdditionalAnswers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.ArrayList; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Optional; + +@DisplayName("메뉴 서비스 테스트") +@ExtendWith(MockitoExtension.class) +public class MenuServiceTest { + @Mock + private MenuRepository menuRepository; + @Mock + private MenuGroupRepository menuGroupRepository; + @Mock + private ProductRepository productRepository; + @Mock + private PurgomalumClient purgomalumClient; + @InjectMocks + private MenuService menuService; + + private MenuFixture menuFixture; + + @BeforeEach + void setUp() { + menuFixture = new MenuFixture(); + } + + @Test + @DisplayName("새로운 메뉴를 추가할 수 있다.") + void create() { + Menu 메뉴 = menuFixture.메뉴_A_가격_10000; + + mockingMenuGroupRepositoryForCreate(메뉴); + mockingProductRepositoryForCreate(메뉴); + mockingPurgomalumClientForCreate(false); + mockingMenuRepositoryForCreate(); + Menu result = menuService.create(메뉴); + + Assertions.assertThat(result.getId()).isNotNull(); + } + + @Test + @DisplayName("메뉴는 반드시 메뉴 그룹에 포함 되어야 한다.") + void create_exception_menuGroup() { + Menu 메뉴_그룹_없는_메뉴 = menuFixture.메뉴_그룹_없는_메뉴; + + Assertions.assertThatThrownBy( + () -> menuService.create(메뉴_그룹_없는_메뉴) + ).isInstanceOf(NoSuchElementException.class); + } + + @Test + @DisplayName("메뉴는 반드시 가격 정보를 가지고 있어야 한다.") + void create_exception_price_null() { + Menu 가격_없는_메뉴 = menuFixture.가격_없는_메뉴; + + Assertions.assertThatThrownBy( + () -> menuService.create(가격_없는_메뉴) + ).isInstanceOf(IllegalArgumentException.class); + } + + @Test + @DisplayName("메뉴는 반드시 상품 정보를 가지고 있어야 한다.") + void create_exception_menuProduct_null() { + Menu 상품_없는_메뉴 = menuFixture.상품_없는_메뉴; + + mockingMenuGroupRepositoryForCreate(상품_없는_메뉴); + + Assertions.assertThatThrownBy( + () -> menuService.create(상품_없는_메뉴) + ).isInstanceOf(IllegalArgumentException.class); + } + + @Test + @DisplayName("메뉴는 반드시 이름 정보를 가지고 있어야 한다.") + void create_exception_name_null() { + Menu 이름_없는_메뉴 = menuFixture.이름_없는_메뉴; + + mockingMenuGroupRepositoryForCreate(이름_없는_메뉴); + mockingProductRepositoryForCreate(이름_없는_메뉴); + + Assertions.assertThatThrownBy( + () -> menuService.create(이름_없는_메뉴) + ).isInstanceOf(IllegalArgumentException.class); + } + + @Test + @DisplayName("메뉴 추가 시 메뉴의 이름이 부적절한지 검사한다.") + void create_exception_containsProfanity() { + Menu 부적절한_이름_메뉴 = menuFixture.부적절한_이름_메뉴; + + mockingMenuGroupRepositoryForCreate(부적절한_이름_메뉴); + mockingProductRepositoryForCreate(부적절한_이름_메뉴); + mockingPurgomalumClientForCreate(true); + + Assertions.assertThatThrownBy( + () -> menuService.create(부적절한_이름_메뉴) + ).isInstanceOf(IllegalArgumentException.class); + } + + @Test + @DisplayName("메뉴의 상품 수량은 기존 상품의 수량과 같아야 한다.") + void create_exception_same_quantity() { + Menu 메뉴 = menuFixture.메뉴_A_가격_10000; + List newMenuProduct = new ArrayList<>(메뉴.getMenuProducts()); + newMenuProduct.add(new MenuProductFixture().메뉴_상품); + + mockingMenuGroupRepositoryForCreate(메뉴); + 메뉴.setMenuProducts(newMenuProduct); + + Assertions.assertThatThrownBy( + () -> menuService.create(메뉴) + ).isInstanceOf(IllegalArgumentException.class); + } + + @Test + @DisplayName("메뉴의 가격은 상품 포함된 상품의 총 가격 보다 클 수 없다.") + void create_exception_price_difference() { + Menu 상품_가격보다_큰_메뉴 = menuFixture.메뉴_가격_11000_상품_가격_10000; + + mockingMenuGroupRepositoryForCreate(상품_가격보다_큰_메뉴); + mockingProductRepositoryForCreate(상품_가격보다_큰_메뉴); + + Assertions.assertThatThrownBy( + () -> menuService.create(상품_가격보다_큰_메뉴) + ).isInstanceOf(IllegalArgumentException.class); + } + + private void mockingMenuGroupRepositoryForCreate(Menu menu) { + Mockito.when(menuGroupRepository.findById(Mockito.any())) + .thenReturn(Optional.of(MenuFixture.extractMenuGroupFrom(menu))); + } + + private void mockingProductRepositoryForCreate(Menu menu) { + Mockito.when(productRepository.findAllByIdIn(Mockito.any())) + .thenReturn(MenuFixture.extractProductsFrom(menu)); + Mockito.when(productRepository.findById(Mockito.any())) + .thenReturn(Optional.of(MenuFixture.extractProductFrom(menu))); + } + + private void mockingPurgomalumClientForCreate(boolean result) { + Mockito.when(purgomalumClient.containsProfanity(Mockito.any())) + .thenReturn(result); + } + + private void mockingMenuRepositoryForCreate() { + Mockito.when(menuRepository.save(Mockito.any())) + .then(AdditionalAnswers.returnsFirstArg()); + } + + @Test + @DisplayName("메뉴의 가격을 변경할 수 있다.") + void changePrice() { + Menu 메뉴_A = menuFixture.메뉴_A_가격_10000; + Menu 메뉴_B = menuFixture.메뉴_B_가격_5000; + + mockingMenuRepositoryFindBy(메뉴_A); + menuService.changePrice(메뉴_A.getId(), 메뉴_B); + + Assertions.assertThat(메뉴_A.getPrice()).isEqualTo(메뉴_B.getPrice()); + } + + @Test + @DisplayName("메뉴의 가격은 해당 상품 총 가격 보다 클 수 없다.") + void changePrice_exception_price() { + Menu 메뉴_B = menuFixture.메뉴_B_가격_5000; + Menu 메뉴_A = menuFixture.메뉴_A_가격_10000; + + mockingMenuRepositoryFindBy(메뉴_B); + + Assertions.assertThatThrownBy( + () -> menuService.changePrice(메뉴_B.getId(), 메뉴_A) + ).isInstanceOf(IllegalArgumentException.class); + } + + @Test + @DisplayName("메뉴를 노출할 수 있다.") + void display() { + Menu 메뉴_C = menuFixture.메뉴_C_가격_100000; + + mockingMenuRepositoryFindBy(메뉴_C); + menuService.display(메뉴_C.getId()); + + Assertions.assertThat(메뉴_C.isDisplayed()).isEqualTo(true); + } + + @Test + @DisplayName("메뉴를 숨길 수 있다.") + void hide() { + Menu 메뉴_A = menuFixture.메뉴_A_가격_10000; + + mockingMenuRepositoryFindBy(메뉴_A); + menuService.hide(메뉴_A.getId()); + + Assertions.assertThat(메뉴_A.isDisplayed()).isEqualTo(false); + } + + private void mockingMenuRepositoryFindBy(Menu menu) { + Mockito.when(menuRepository.findById(Mockito.any())) + .thenReturn(Optional.of(menu)); + } +} diff --git a/src/test/java/kitchenpos/menu/service/ProductServiceTest.java b/src/test/java/kitchenpos/menu/service/ProductServiceTest.java new file mode 100644 index 000000000..306ba87b1 --- /dev/null +++ b/src/test/java/kitchenpos/menu/service/ProductServiceTest.java @@ -0,0 +1,109 @@ +package kitchenpos.menu.service; + +import kitchenpos.application.ProductService; +import kitchenpos.domain.MenuRepository; +import kitchenpos.domain.Product; +import kitchenpos.domain.ProductRepository; +import kitchenpos.infra.PurgomalumClient; +import kitchenpos.menu.fixture.MenuFixture; +import kitchenpos.menu.fixture.ProductFixture; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.AdditionalAnswers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.List; +import java.util.Optional; + +@DisplayName("상품 서비스 테스트") +@ExtendWith(MockitoExtension.class) +public class ProductServiceTest { + @Mock + private ProductRepository productRepository; + @Mock + private MenuRepository menuRepository; + @Mock + private PurgomalumClient purgomalumClient; + @InjectMocks + private ProductService productService; + + private ProductFixture productFixture; + private MenuFixture menuFixture; + + @BeforeEach + void setUp() { + productFixture = new ProductFixture(); + menuFixture = new MenuFixture(); + } + + @Test + @DisplayName("새로운 상품을 추가 할 수 있다.") + void create() { + Product 떡볶이 = productFixture.상품_A_가격_1000; + + Mockito.when(purgomalumClient.containsProfanity(Mockito.any())) + .thenReturn(false); + Mockito.when(productRepository.save(Mockito.any())) + .then(AdditionalAnswers.returnsFirstArg()); + Product result = productService.create(떡볶이); + + Assertions.assertThat(result.getId()).isNotNull(); + } + + @Test + @DisplayName("상품의 가격은 반드시 존재해야 하며 0보다 커야 한다.") + void create_exception_상품_가격() { + List 상품_목록 = List.of(productFixture.가격_없는_상품, productFixture.가격_음수_상품); + + for (Product 상품 : 상품_목록) + { + Assertions.assertThatThrownBy( + () -> productService.create(상품) + ).isInstanceOf(IllegalArgumentException.class); + } + } + + @Test + @DisplayName("상품의 이름은 반드시 존재해야 하며 부적절한지 검사한다.") + void create_exception_상품_이름() { + Assertions.assertThatThrownBy( + () -> productService.create(productFixture.부적절한_이름_상품) + ).isInstanceOf(IllegalArgumentException.class); + } + + @Test + @DisplayName("상품의 가격을 변경할 수 있다.") + void changePrice() { + Product 상품_A = productFixture.상품_A_가격_1000; + Product 상품_B = productFixture.상품_C_가격_10000; + + Mockito.when(productRepository.findById(Mockito.any())) + .thenReturn(Optional.of(상품_A)); + Mockito.when(menuRepository.findAllByProductId(Mockito.any())) + .thenReturn(List.of(menuFixture.메뉴_A_가격_10000)); + productService.changePrice(상품_A.getId(), 상품_B); + + Assertions.assertThat(상품_A.getPrice()).isEqualTo(상품_B.getPrice()); + } + + @Test + @DisplayName("해당 상품으로 구성된 메뉴의 가격이 변경된 상품의 가격 총합보다 크다면 메뉴를 노출하지 않는다.") + void changePrice_exception_price() { + Product 상품_C_가격_10000 = productFixture.상품_C_가격_10000; + Product 상품_A_가격_1000 = productFixture.상품_A_가격_1000; + + Mockito.when(productRepository.findById(Mockito.any())) + .thenReturn(Optional.of(상품_C_가격_10000)); + Mockito.when(menuRepository.findAllByProductId(Mockito.any())) + .thenReturn(List.of(menuFixture.메뉴_C_가격_100000)); + productService.changePrice(상품_C_가격_10000.getId(), 상품_A_가격_1000); + + Assertions.assertThat(menuFixture.메뉴_C_가격_100000.isDisplayed()).isEqualTo(false); + } +} diff --git a/src/test/java/kitchenpos/order/fixture/OrderFixture.java b/src/test/java/kitchenpos/order/fixture/OrderFixture.java new file mode 100644 index 000000000..2faae2cca --- /dev/null +++ b/src/test/java/kitchenpos/order/fixture/OrderFixture.java @@ -0,0 +1,78 @@ +package kitchenpos.order.fixture; + +import kitchenpos.domain.*; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.UUID; + +public class OrderFixture { + private OrderLineItemFixture orderLineItemFixture = new OrderLineItemFixture(); + private OrderTableFixture orderTableFixture = new OrderTableFixture(); + + public Order 매장_주문_A = createEatIn(List.of(orderLineItemFixture.주문_항목_A), orderTableFixture.손님_있는_주문_테이블); + public Order 포장_주문_A = createTakeOut(List.of(orderLineItemFixture.주문_항목_A)); + public Order 배달_주문_A = createDelivery(List.of(orderLineItemFixture.주문_항목_A), "행궁동"); + + public Order 주문_유형_없는_주문 = create(null, List.of(orderLineItemFixture.주문_항목)); + public Order 주문_항목_없는_주문 = create(OrderType.EAT_IN, null); + + public static Order createDeliveryHasStatus(List orderLineItems, String deliveryAddress, + OrderStatus orderStatus) + { + Order order = createDelivery(orderLineItems, deliveryAddress); + order.setStatus(orderStatus); + + return order; + } + + public static Order createEatInHasStatus(List orderLineItems, OrderTable orderTable, + OrderStatus orderStatus) + { + Order order = createEatIn(orderLineItems, orderTable); + order.setStatus(orderStatus); + + return order; + } + + public static Order createHasStatus(OrderType type, List orderLineItems, OrderStatus status) { + Order order = create(type, orderLineItems); + order.setStatus(status); + + return order; + } + + public static Order createEatIn(List orderLineItems, OrderTable orderTable) { + Order order = create(OrderType.EAT_IN, orderLineItems); + order.setOrderTable(orderTable); + + return order; + } + + public static Order createTakeOut(List orderLineItems) { + return create(OrderType.TAKEOUT, orderLineItems); + } + + public static Order createDelivery(List orderLineItems, String deliveryAddress) { + Order deliveryOrder = create(OrderType.DELIVERY, orderLineItems); + deliveryOrder.setDeliveryAddress(deliveryAddress); + return deliveryOrder; + } + + public static Order create(OrderType type, List orderLineItems) { + return create(UUID.randomUUID(), type, OrderStatus.WAITING, LocalDateTime.now(), orderLineItems); + } + + public static Order create(UUID id, OrderType type, OrderStatus status, LocalDateTime orderDateTime, + List orderLineItems) + { + Order order = new Order(); + order.setId(id); + order.setType(type); + order.setStatus(status); + order.setOrderDateTime(orderDateTime); + order.setOrderLineItems(orderLineItems); + + return order; + } +} diff --git a/src/test/java/kitchenpos/order/fixture/OrderLineItemFixture.java b/src/test/java/kitchenpos/order/fixture/OrderLineItemFixture.java new file mode 100644 index 000000000..24b8e5c56 --- /dev/null +++ b/src/test/java/kitchenpos/order/fixture/OrderLineItemFixture.java @@ -0,0 +1,24 @@ +package kitchenpos.order.fixture; + +import kitchenpos.domain.Menu; +import kitchenpos.domain.OrderLineItem; +import kitchenpos.menu.fixture.MenuFixture; + +import java.math.BigDecimal; + +public class OrderLineItemFixture { + private MenuFixture menuFixture = new MenuFixture(); + + public OrderLineItem 주문_항목 = create(menuFixture.메뉴, 1, new BigDecimal(10000)); + public OrderLineItem 주문_항목_A = create(menuFixture.메뉴_A_가격_10000, 1, new BigDecimal(10000)); + public OrderLineItem 수량_없는_주문_항목 = create(menuFixture.메뉴, 1, new BigDecimal(10000)); + + public static OrderLineItem create(Menu menu, long quantity, BigDecimal price) { + OrderLineItem orderLineItem = new OrderLineItem(); + orderLineItem.setMenu(menu); + orderLineItem.setQuantity(quantity); + orderLineItem.setPrice(price); + + return orderLineItem; + } +} diff --git a/src/test/java/kitchenpos/order/fixture/OrderTableFixture.java b/src/test/java/kitchenpos/order/fixture/OrderTableFixture.java new file mode 100644 index 000000000..f3fcda8f2 --- /dev/null +++ b/src/test/java/kitchenpos/order/fixture/OrderTableFixture.java @@ -0,0 +1,31 @@ +package kitchenpos.order.fixture; + +import kitchenpos.domain.OrderTable; + +import java.util.UUID; + +public class OrderTableFixture { + public OrderTable 주문_테이블_A = create("주문_테이블_A", 5); + public OrderTable 주문_테이블_B = create("주문_테이블_B", 7); + public OrderTable 손님_있는_주문_테이블 = create("손님_있는_주문_테이블", 5, true); + public OrderTable 이름_없는_주문_테이블 = create(null, 5); + public OrderTable 손님_음수_주문_테이블 = create("주문_테이블", -1); + + public static OrderTable create(String name, int numberOfGuest) { + return create(UUID.randomUUID(), name, numberOfGuest, false); + } + + public static OrderTable create(String name, int numberOfQuest, boolean occupied) { + return create(UUID.randomUUID(), name, numberOfQuest, occupied); + } + + public static OrderTable create(UUID id, String name, int numberOfGuest, boolean occupied) { + OrderTable orderTable = new OrderTable(); + orderTable.setId(id); + orderTable.setName(name); + orderTable.setNumberOfGuests(numberOfGuest); + orderTable.setOccupied(occupied); + + return orderTable; + } +} diff --git a/src/test/java/kitchenpos/order/service/OrderServiceTest.java b/src/test/java/kitchenpos/order/service/OrderServiceTest.java new file mode 100644 index 000000000..6c7810791 --- /dev/null +++ b/src/test/java/kitchenpos/order/service/OrderServiceTest.java @@ -0,0 +1,320 @@ +package kitchenpos.order.service; + +import kitchenpos.application.OrderService; +import kitchenpos.domain.*; +import kitchenpos.infra.KitchenridersClient; +import kitchenpos.menu.fixture.MenuFixture; +import kitchenpos.order.fixture.OrderFixture; +import kitchenpos.order.fixture.OrderLineItemFixture; +import kitchenpos.order.fixture.OrderTableFixture; +import org.assertj.core.api.Assertions; +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.AdditionalAnswers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Optional; + +@DisplayName("주문 서비스 테스트") +@ExtendWith(MockitoExtension.class) +public class OrderServiceTest { + @Mock + private OrderRepository orderRepository; + @Mock + private MenuRepository menuRepository; + @Mock + private OrderTableRepository orderTableRepository; + @Mock + private KitchenridersClient kitchenridersClient; + @InjectMocks + private OrderService orderService; + + private OrderFixture orderFixture; + private OrderLineItemFixture orderLineItemFixture; + private OrderTableFixture orderTableFixture; + private MenuFixture menuFixture; + + @BeforeEach + void setUp() { + orderFixture = new OrderFixture(); + orderLineItemFixture = new OrderLineItemFixture(); + orderTableFixture = new OrderTableFixture(); + menuFixture = new MenuFixture(); + } + + @Test + @DisplayName("주문 시 주문 유형 및 항목은 반드시 존재 해야한다.") + void create_exception_status_item_null() { + List exceptionOrders = List.of(orderFixture.주문_유형_없는_주문, orderFixture.주문_항목_없는_주문); + + for (Order exceptionOrder : exceptionOrders) + { + Assertions.assertThatThrownBy( + () -> orderService.create(exceptionOrder) + ).isInstanceOf(IllegalArgumentException.class); + } + } + + @Test + @DisplayName("주문 항목에 있는 메뉴들은 판매 되고 있는 메뉴들이어야 한다.") + void create_exception_hide() { + OrderLineItem 비활성화_메뉴_주문_항목 = OrderLineItemFixture.create( + menuFixture.비활성화_메뉴, 1, new BigDecimal(1000) + ); + Order 주문 = OrderFixture.create(OrderType.EAT_IN, List.of(비활성화_메뉴_주문_항목)); + + mockingMenuRepository(RepositoryMethod.FIND_ALL, menuFixture.비활성화_메뉴); + mockingMenuRepository(RepositoryMethod.FIND, menuFixture.비활성화_메뉴); + + Assertions.assertThatThrownBy( + () -> orderService.create(주문) + ).isInstanceOf(IllegalStateException.class); + } + + @Test + @DisplayName("주문 가격은 주문 항목에 있는 메뉴의 가격 총합과 같아야 한다.") + void create_exception_price() { + Order 주문 = orderFixture.매장_주문_A; + + mockingMenuRepository(RepositoryMethod.FIND_ALL, menuFixture.메뉴_B_가격_5000); + mockingMenuRepository(RepositoryMethod.FIND, menuFixture.메뉴_B_가격_5000); + + Assertions.assertThatThrownBy( + () -> orderService.create(주문) + ).isInstanceOf(IllegalArgumentException.class); + } + + @Nested + @DisplayName("매장 식사 주문 테스트") + class eatInTest { + @Test + @DisplayName("손님은 식당에서 음식을 주문할 수 있다.") + void create_eatIn() { + Order 매장_주문 = orderFixture.매장_주문_A; + OrderTable 주문_테이블 = orderTableFixture.손님_있는_주문_테이블; + + mockingMenuRepository(RepositoryMethod.FIND, menuFixture.메뉴_A_가격_10000); + mockingMenuRepository(RepositoryMethod.FIND_ALL, menuFixture.메뉴_A_가격_10000); + mockingOrderTableRepository(주문_테이블); + mockingOrderRepository(RepositoryMethod.SAVE, 매장_주문); + Order result = orderService.create(매장_주문); + + Assertions.assertThat(result.getOrderTable()).isEqualTo(주문_테이블); + } + + @Test + @DisplayName("식당 주문 시 주문 테이블이 존재해야 하며 빈 테이블일 수 없다.") + void create_eatIn_exception_occupied() { + Order 주문 = orderFixture.매장_주문_A; + OrderTable 빈_테이블 = orderTableFixture.주문_테이블_A; + + mockingMenuRepository(RepositoryMethod.FIND, menuFixture.메뉴_A_가격_10000); + mockingMenuRepository(RepositoryMethod.FIND_ALL, menuFixture.메뉴_A_가격_10000); + mockingOrderTableRepository(빈_테이블); + + Assertions.assertThatThrownBy( + () -> orderService.create(주문) + ).isInstanceOf(IllegalStateException.class); + } + + @Test + @DisplayName("주문이 접수되면 손님에게 음식을 제공한다.") + void serve() { + Order 매장_접수_주문 = OrderFixture.createHasStatus( + OrderType.EAT_IN, List.of(orderLineItemFixture.주문_항목_A), OrderStatus.ACCEPTED + ); + + mockingOrderRepository(RepositoryMethod.FIND, 매장_접수_주문); + orderService.serve(매장_접수_주문.getId()); + + Assertions.assertThat(매장_접수_주문.getStatus()).isEqualTo(OrderStatus.SERVED); + } + + @Test + @DisplayName("식당 주문 완료가 되면 매장 주인은 테이블을 정리한다.") + void complete_eatIn() { + Order 매장_서빙_주문 = OrderFixture.createEatInHasStatus( + List.of(orderLineItemFixture.주문_항목_A), orderTableFixture.손님_있는_주문_테이블, OrderStatus.SERVED + ); + + mockingOrderRepository(RepositoryMethod.FIND, 매장_서빙_주문); + orderService.complete(매장_서빙_주문.getId()); + + Assertions.assertThat(매장_서빙_주문.getOrderTable().isOccupied()).isEqualTo(false); + } + } + + @Nested + @DisplayName("포장 주문 테스트") + class takeOutTest { + @Test + @DisplayName("손님은 포장 주문을 할 수 있다.") + void create_takeOut() { + Order 포장_주문 = orderFixture.포장_주문_A; + + mockingMenuRepository(RepositoryMethod.FIND, menuFixture.메뉴_A_가격_10000); + mockingMenuRepository(RepositoryMethod.FIND_ALL, menuFixture.메뉴_A_가격_10000); + mockingOrderRepository(RepositoryMethod.SAVE, 포장_주문); + Order result = orderService.create(포장_주문); + + Assertions.assertThat(result.getStatus()).isEqualTo(OrderStatus.WAITING); + } + + @Test + @DisplayName("포장 주문 시 주문 항목 수량이 존재해야 한다.") + void create_takeOut_exception_quantity() { + Order 주문_항목_없는_포장_주문 = OrderFixture.createTakeOut(List.of(orderLineItemFixture.수량_없는_주문_항목)); + + Assertions.assertThatThrownBy( + () -> orderService.create(주문_항목_없는_포장_주문) + ).isInstanceOf(IllegalArgumentException.class); + } + + @Test + @DisplayName("주문이 접수되면 손님에게 음식을 제공한다.") + void serve() { + Order 포장_접수_주문 = OrderFixture.createHasStatus( + OrderType.TAKEOUT, List.of(orderLineItemFixture.주문_항목_A), OrderStatus.ACCEPTED + ); + + mockingOrderRepository(RepositoryMethod.FIND, 포장_접수_주문); + orderService.serve(포장_접수_주문.getId()); + + Assertions.assertThat(포장_접수_주문.getStatus()).isEqualTo(OrderStatus.SERVED); + } + + @Test + @DisplayName("손님에게 음식이 제공되면 주문 상태를 주문 완료로 변경한다.") + void complete_takeOut() { + Order 포장_서빙_주문 = OrderFixture.createHasStatus( + OrderType.TAKEOUT, List.of(orderLineItemFixture.주문_항목_A), OrderStatus.SERVED + ); + + mockingOrderRepository(RepositoryMethod.FIND, 포장_서빙_주문); + Order result = orderService.complete(포장_서빙_주문.getId()); + + Assertions.assertThat(result.getStatus()).isEqualTo(OrderStatus.COMPLETED); + } + } + + @Nested + @DisplayName("배달 주문 테스트") + class deliveryTest { + @Test + @DisplayName("손님은 배달 주문을 할 수 있다.") + void create_delivery() { + Order 배달_주문 = orderFixture.배달_주문_A; + + mockingMenuRepository(RepositoryMethod.FIND, menuFixture.메뉴_A_가격_10000); + mockingMenuRepository(RepositoryMethod.FIND_ALL, menuFixture.메뉴_A_가격_10000); + mockingOrderRepository(RepositoryMethod.SAVE, 배달_주문); + Order result = orderService.create(배달_주문); + + Assertions.assertThat(result.getDeliveryAddress()).isEqualTo("행궁동"); + } + + @Test + @DisplayName("배달 주문 시 주문 항목 수량이 존재해야 한다.") + void create_delivery_exception_quantity() { + Order 주문_항목_없는_배달_주문 = OrderFixture.createDelivery(List.of(orderLineItemFixture.수량_없는_주문_항목), "행궁동"); + + Assertions.assertThatThrownBy( + () -> orderService.create(주문_항목_없는_배달_주문) + ).isInstanceOf(IllegalArgumentException.class); + } + + @Test + @DisplayName("배달 주문 시 배송지 정보가 존재해야 한다.") + void create_delivery_exception_address() { + Order 배송지_없는_배달_주문 = OrderFixture.createDelivery(List.of(orderLineItemFixture.주문_항목_A), null); + + mockingMenuRepository(RepositoryMethod.FIND, menuFixture.메뉴_A_가격_10000); + mockingMenuRepository(RepositoryMethod.FIND_ALL, menuFixture.메뉴_A_가격_10000); + + Assertions.assertThatThrownBy( + () -> orderService.create(배송지_없는_배달_주문) + ).isInstanceOf(IllegalArgumentException.class); + } + + @Test + @DisplayName("라이더에게 주문 정보와 배달 주소시를 전달 후 배달을 요청하며 주문을 접수한다.") + void accept_delivery() { + Order 대기_주문 = OrderFixture.createHasStatus( + OrderType.DELIVERY, List.of(orderLineItemFixture.주문_항목_A), OrderStatus.WAITING + ); + + mockingOrderRepository(RepositoryMethod.FIND, 대기_주문); + Mockito.doNothing() + .when(kitchenridersClient) + .requestDelivery(Mockito.any(), Mockito.any(), Mockito.any()); + Order result = orderService.accept(대기_주문.getId()); + + Assertions.assertThat(result.getStatus()).isEqualTo(OrderStatus.ACCEPTED); + } + + @Test + @DisplayName("배달이 시작되면 주문의 상태를 배달 중으로 변경한다.") + void startDelivery() { + Order 배달_주문 = OrderFixture.createDeliveryHasStatus( + List.of(orderLineItemFixture.주문_항목_A), "행궁동", OrderStatus.SERVED + ); + + mockingOrderRepository(RepositoryMethod.FIND, 배달_주문); + orderService.startDelivery(배달_주문.getId()); + + Assertions.assertThat(배달_주문.getStatus()).isEqualTo(OrderStatus.DELIVERING); + } + + @Test + @DisplayName("배달이 배달이 완료되면 주문의 상태를 배달 완료로 변경한다.") + void completeDelivery() { + Order 배달_주문 = OrderFixture.createDeliveryHasStatus( + List.of(orderLineItemFixture.주문_항목_A), "행궁동", OrderStatus.DELIVERING + ); + + mockingOrderRepository(RepositoryMethod.FIND, 배달_주문); + orderService.completeDelivery(배달_주문.getId()); + + Assertions.assertThat(배달_주문.getStatus()).isEqualTo(OrderStatus.DELIVERED); + } + } + + private void mockingMenuRepository(RepositoryMethod method, Menu menu) { + if (method == RepositoryMethod.FIND) { + Mockito.when(menuRepository.findById(Mockito.any())) + .thenReturn(Optional.of(menu)); + } + if (method == RepositoryMethod.FIND_ALL) { + Mockito.when(menuRepository.findAllByIdIn(Mockito.any())) + .thenReturn(List.of(menu)); + } + } + + private void mockingOrderRepository(RepositoryMethod method, Order order) { + if (method == RepositoryMethod.SAVE) { + Mockito.when(orderRepository.save(Mockito.any())) + .then(AdditionalAnswers.returnsFirstArg()); + } + if (method == RepositoryMethod.FIND) { + Mockito.when(orderRepository.findById(Mockito.any())) + .thenReturn(Optional.of(order)); + } + } + + private enum RepositoryMethod { + FIND_ALL, FIND, SAVE + } + + private void mockingOrderTableRepository(OrderTable orderTable) { + Mockito.when(orderTableRepository.findById(Mockito.any())) + .thenReturn(Optional.of(orderTable)); + } +} diff --git a/src/test/java/kitchenpos/order/service/OrderTableServiceTest.java b/src/test/java/kitchenpos/order/service/OrderTableServiceTest.java new file mode 100644 index 000000000..53264e5aa --- /dev/null +++ b/src/test/java/kitchenpos/order/service/OrderTableServiceTest.java @@ -0,0 +1,119 @@ +package kitchenpos.order.service; + +import kitchenpos.application.OrderTableService; +import kitchenpos.domain.*; +import kitchenpos.order.fixture.OrderTableFixture; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Optional; + +@DisplayName("주문 테이블 서비스 테스트") +@ExtendWith(MockitoExtension.class) +public class OrderTableServiceTest { + @Mock + private OrderTableRepository orderTableRepository; + @Mock + private OrderRepository orderRepository; + @InjectMocks + private OrderTableService orderTableService; + + private OrderTableFixture orderTableFixture; + + @BeforeEach + void setUp() { + orderTableFixture = new OrderTableFixture(); + } + + @Test + @DisplayName("새로운 테이블을 추가할 수 있다.") + void create() { + OrderTable 주문_테이블 = orderTableFixture.주문_테이블_A; + + mockingOrderTableRepository(OrderTableRepositoryMethod.SAVE, 주문_테이블); + OrderTable result = orderTableService.create(주문_테이블); + + Assertions.assertThat(result.getId()).isNotNull(); + Assertions.assertThat(result.isOccupied()).isEqualTo(false); + } + + @Test + @DisplayName("새로운 테이블을 추가 시 테이블의 이름은 반드시 존재해야 한다.") + void create_exception_name() { + OrderTable 이름_없는_주문_테이블 = orderTableFixture.이름_없는_주문_테이블; + + Assertions.assertThatThrownBy( + () -> orderTableService.create(이름_없는_주문_테이블) + ).isInstanceOf(IllegalArgumentException.class); + } + + @Test + @DisplayName("손님은 테이블에 앉을 수 있다.") + void sit() { + OrderTable 주문_테이블 = orderTableFixture.주문_테이블_A; + + mockingOrderTableRepository(OrderTableRepositoryMethod.FIND, 주문_테이블); + orderTableService.sit(주문_테이블.getId()); + + Assertions.assertThat(주문_테이블.isOccupied()).isEqualTo(true); + } + + @Test + @DisplayName("매장 주인은 테이블을 정리할 수 있다.") + void clear() { + OrderTable 손님_있는_주문_테이블 = orderTableFixture.손님_있는_주문_테이블; + + mockingOrderTableRepository(OrderTableRepositoryMethod.FIND, 손님_있는_주문_테이블); + Mockito.when(orderRepository.existsByOrderTableAndStatusNot(Mockito.any(), Mockito.any())) + .thenReturn(false); + orderTableService.clear(손님_있는_주문_테이블.getId()); + + Assertions.assertThat(손님_있는_주문_테이블.getNumberOfGuests()).isEqualTo(0); + } + + @Test + @DisplayName("테이블에 앉는 손님의 수는 변경될 수 있다.") + void changeNumberOfGuests() { + OrderTable 주문_테이블_A = orderTableFixture.주문_테이블_A; + OrderTable 주문_테이블_B = orderTableFixture.주문_테이블_B; + + mockingOrderTableRepository(OrderTableRepositoryMethod.FIND, 주문_테이블_A); + orderTableService.sit(주문_테이블_A.getId()); + orderTableService.changeNumberOfGuests(주문_테이블_A.getId(), 주문_테이블_B); + + Assertions.assertThat(주문_테이블_A.getNumberOfGuests()).isEqualTo(주문_테이블_B.getNumberOfGuests()); + } + + @Test + @DisplayName("손님은 0명 이상이어야 한다.") + void changeNumberOfGuests_exception_number() { + OrderTable 주문_테이블_A = orderTableFixture.주문_테이블_A; + OrderTable 손님_음수_주문_테이블 = orderTableFixture.손님_음수_주문_테이블; + + Assertions.assertThatThrownBy( + () -> orderTableService.changeNumberOfGuests(주문_테이블_A.getId(), 손님_음수_주문_테이블) + ).isInstanceOf(IllegalArgumentException.class); + } + + private void mockingOrderTableRepository(OrderTableRepositoryMethod method, OrderTable orderTable) { + if (method == OrderTableRepositoryMethod.SAVE) { + Mockito.when(orderTableRepository.save(Mockito.any())) + .thenReturn(orderTable); + } + if (method == OrderTableRepositoryMethod.FIND) { + Mockito.when(orderTableRepository.findById(orderTable.getId())) + .thenReturn(Optional.of(orderTable)); + } + } + + private enum OrderTableRepositoryMethod { + SAVE, FIND + } +}