Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

3단계 - 테스트를 통한 코드 보호 #258

Open
wants to merge 40 commits into
base: testrace
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
082da76
doc(feedback): 2단계 피드백
testrace Mar 9, 2022
45dfbac
test: 메뉴 그룹 테스트
testrace Mar 9, 2022
dea2082
test: 상품 테스트
testrace Mar 10, 2022
0cf35a9
fix: 상품 가격 변경 버그 수정
testrace Mar 12, 2022
ae6309b
test: 식탁 테스트
testrace Mar 12, 2022
1d569db
chore: 식탁 테스트 오타
testrace Mar 12, 2022
c35ac5c
test: 메뉴 테스트
testrace Mar 13, 2022
d94d60c
test: 메뉴 테스트 수정
testrace Mar 13, 2022
cb9e3ff
test: 상품 테스트 수정
testrace Mar 13, 2022
efc2393
test: 주문 테스트
testrace Mar 13, 2022
3f02d9a
chore: 미사용 파일 삭제
testrace Mar 13, 2022
306fde1
test: 메뉴 그룹 테스트 수정
testrace Mar 16, 2022
2c13ca5
test: 상품 테스트 수정
testrace Mar 16, 2022
cb050cc
test: 식탁 관리 테스트 수정
testrace Mar 16, 2022
ad7f1f3
test: 메뉴 관리 테스트 수정
testrace Mar 19, 2022
b651be4
test: 주문 관리 테스트 수정
testrace Mar 19, 2022
46627e8
test: 상품 관리 테스트 오류 수정
testrace Mar 19, 2022
a3a0409
test: 메뉴그룹 controller 테스트
testrace Mar 19, 2022
915544b
test: 주문 테스트 중복 코드 제거
testrace Mar 19, 2022
44380f7
refactor: 예외 클래스 분리
testrace Mar 29, 2022
f2dfbf5
refactor: repository interface 분리
testrace Mar 29, 2022
e6b68b3
style: 사용하지 않는 import 제거
testrace Mar 29, 2022
f4eac1f
refactor: list의 element 검증 시 containsExactlyInAnyOrder() 활용
testrace Mar 29, 2022
8e30648
test: id null check
testrace Mar 29, 2022
bca4cd1
chore: 미사용 코드 삭제
testrace Mar 29, 2022
decba09
refactor: enum 비교
testrace Mar 29, 2022
d86cfce
test: 변수명 변경
testrace Mar 30, 2022
945a61e
test: 테스트명 및 변수명 변경
testrace Mar 30, 2022
e570be7
test: 변수명 변경
testrace Mar 30, 2022
1998e76
test: 테스트 분리
testrace Apr 3, 2022
cfdbf9f
test: 시간적 결합 제거
testrace Apr 3, 2022
17c7f41
test: 오타 수정
testrace Apr 4, 2022
26bd4ca
style: 불필요 공백 제거
testrace Apr 4, 2022
72e13ae
refactor: MenuRepository에서 data-jpa 의존 제거
testrace Apr 4, 2022
a42c022
test(feedback): 불필요한 검증 제거
testrace Apr 10, 2022
bec4eb0
style(feedback): 코드 가독성
testrace Apr 10, 2022
a3af1e7
refactor: 커스텀 예외 생성
testrace Apr 10, 2022
cd82f3e
test: 주문 테스트 빌더 패턴 적용
testrace Apr 12, 2022
fc329ae
refactor: 매장주문 빈테이블 커스텀 예외 추가
testrace Apr 12, 2022
c3ea86e
test: 메뉴 테스트 빌더 패턴 적용
testrace Apr 13, 2022
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
100 changes: 53 additions & 47 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,62 +4,66 @@
식당에서 사용하는 주문관리 시스템을 구축한다.

- 상품
- [ ] `이름`과 `가격`이 있다.
- [ ] 등록된 상품들을 조회할 수 있다.
- [ ] 이름에 비속어를 사용할 수 없다.
- [ ] 가격을 변경할 수 있다.
- [ ] 가격은 0원 이상이어야 한다.
- [ ] 가격이 해당 상품을 포함하는 메뉴의 가격보다 크면 메뉴를 진열하지 않는다.
- [x] `이름`과 `가격`이 있다.
- [x] 등록된 상품들을 조회할 수 있다.
- [x] 이름에 비속어를 사용할 수 없다.
- [x] 가격은 0원 이상이어야 한다.
- [x] 가격을 변경할 수 있다.
- [x] 0원 이하의 가격으로 변경할 수 없다.
- [x] 등록되지 않은 상품은 가격을 변경할 수 없다.
- [x] 가격이 해당 상품을 포함하는 메뉴의 가격보다 크면 메뉴를 진열하지 않는다.
- 메뉴
- [ ] `이름`, `가격`, `메뉴 그룹`, `진열 여부`, `1개 이상의 상품`이 있다.
- [ ] 포함된 상품에는 수량이 있다.
- [ ] 등록된 메뉴들을 조회할 수 있다.
- [ ] 진열 여부를 변경할 수 있다.
- [ ] 이름에 비속어를 사용할 수 없다.
- [ ] 가격을 변경할 수 있다.
- [ ] 가격은 0원 이상이어야 한다.
- [ ] 가격은 메뉴에 포함된 상품별 총 금액(상품 가격 * 상품 수량)의 합보다 작아야 한다.
- [ ] 하나의 메뉴 그룹에만 속한다.
- [ ] 1개 이상의 상품을 포함한다.
- [ ] 등록되지 않은 상품은 포함할 수 없다.
- [x] `이름`, `가격`, `메뉴 그룹`, `진열 여부`, `1개 이상의 상품`이 있다.
- [x] 포함된 상품에는 수량이 있다.
- [x] 등록된 메뉴들을 조회할 수 있다.
- [x] 진열 여부를 변경할 수 있다.
- [x] 메뉴에 포함된 상품별 총 금액(상품 가격 * 상품 수량)의 합보다 작아야 진열할 수 있다.
- [x] 이름에 비속어를 사용할 수 없다.
- [x] 가격을 변경할 수 있다.
- [x] 가격은 0원 이상이어야 한다.
- [x] 가격은 메뉴에 포함된 상품별 총 금액(상품 가격 * 상품 수량)의 합보다 작아야 한다.
- [x] 하나의 메뉴 그룹에만 속한다.
- [x] 1개 이상의 상품을 포함한다.
- [x] 등록되지 않은 상품은 포함할 수 없다.
- 메뉴 그룹
- [ ] `이름`이 있다.
- [ ] 등록된 모든 메뉴 그룹을 조회할 수 있다.
- [x] `이름`이 있다.
- [x] 등록된 모든 메뉴 그룹을 조회할 수 있다.
- 식탁
- [ ] `이름`, `손님 수`, `착석 여부`가 있다.
- [ ] 식탁의 기본 상태는 0명의 손님이며 비어있다.
- [ ] 모든 식탁을 조회할 수 있다.
- [ ] 손님이 앉을 수 있다.
- [ ] 손님이 앉으면 상태다
- [ ] 앉은 손님의 수를 변경할 수 있다.
- [ ] 주문 완료된 식탁만 정리할 수 있다.
- [ ] 정리된 식탁은 기본 상태가 된다.
- [x] `이름`, `손님 수`, `착석 여부`가 있다.
- [x] 식탁의 기본 상태는 0명의 손님이며 비어있다.
- [x] 모든 식탁을 조회할 수 있다.
- [x] 손님이 앉을 수 있다.
- [x] 손님이 앉으면 착석 상태다
- [x] 앉은 손님의 수를 변경할 수 있다.
- [x] 주문 완료된 식탁만 정리할 수 있다.
- [x] 정리된 식탁은 기본 상태가 된다.
- 주문
- [ ] `주문 유형`, `주문 메뉴의 수량 및 가격`이 있다.
- [ ] 주문 유형은 `배달`, `포장`, `식당 내 식사`다.
- [ ] 배달 주문의 `상태`는 `대기`, `수락`, `조리 완료`, `배달 중`, `배달 완료`, `주문 완료` 순이다.
- [ ] 배달 주문은 `주소`가 있어야 한다.
- [ ] 포장 또는 식당 내 식사 주문의 `상태`는 `대기`, `수락`, `조리 완료`, `주문 완료` 순이다.
- [ ] 식당 내 식사는 `식탁`에 착석 후 가능하다.
- [ ] `주문 시간`은 실시간으로 지정한다.
- [ ] 등록된 메뉴가 진열되어야 한다.
- [ ] 1개 이상의 메뉴를 주문해야 한다.
- [ ] 지불한 금액이 주문한 메뉴의 총 금액과 일치해야 한다.
- [ ] 주문의 기본 상태는 대기다.
- [x] `주문 유형`, `주문 메뉴의 수량 및 가격`이 있다.
- [x] 주문 유형은 `배달`, `포장`, `식당 내 식사`다.
- [x] 배달 주문의 `상태`는 `대기`, `수락`, `조리 완료`, `배달 중`, `배달 완료`, `주문 완료` 순이다.
- [x] 배달 주문은 `주소`가 있어야 한다.
- [x] 포장 또는 식당 내 식사 주문의 `상태`는 `대기`, `수락`, `조리 완료`, `주문 완료` 순이다.
- [x] 식당 내 식사는 `식탁`에 착석 후 가능하다.
- [x] `주문 시간`은 실시간으로 지정한다.
- [x] 등록된 메뉴가 진열되어야 한다.
- [x] 1개 이상의 메뉴를 주문해야 한다.
- [x] 지불한 금액이 주문한 메뉴의 총 금액과 일치해야 한다.
- [x] 주문의 기본 상태는 대기다.
- [x] 식당 내 식사 주문은 음수를 활용해 주문을 취소할 수 있다.
- 주문 수락
- [ ] 대기 중인 주문만 수락할 수 있다.
- [ ] 배달 주문은 배달 요청한다.
- [x] 대기 중인 주문만 수락할 수 있다.
- [x] 배달 주문은 배달 요청한다.
- 조리 완료
- [ ] 수락된 주문만 조리 완료할 수 있다.
- [x] 수락된 주문만 조리 완료할 수 있다.
- 배달 중
- [ ] 배달 주문, 조리 완료 주문만 배달 시작할 수 있다.
- [x] 배달 주문, 조리 완료 주문만 배달 시작할 수 있다.
- 배달 완료
- [ ] 배달 중인 주문만 주문 완료할 수 있다.
- [x] 배달 중인 주문만 주문 완료할 수 있다.
- 주문 완료
- [ ] 배달 완료된 배달 주문을 주문 완료할 수 있다.
- [ ] 조리 완료된 포장 주문 또는 식당 내 식사 주문을 주문 완료할 수 있다.
- [ ] 식당 내 식사 주문이 완료되면 식탁을 정리한다.
- [ ] 모든 주문을 조회할 수 있다.
- [x] 배달 완료된 배달 주문을 주문 완료할 수 있다.
- [x] 조리 완료된 포장 주문 또는 식당 내 식사 주문을 주문 완료할 수 있다.
- [x] 식당 내 식사 주문이 완료되면 식탁을 정리한다.
- [x] 모든 주문을 조회할 수 있다.


## 용어 사전
Expand Down Expand Up @@ -110,6 +114,8 @@

- [x] `가격, 진열 여부를 변경할 수 있다.`
- 여러 항목에 대한 공통 행위를 가지더라도 행위의 조건이 다르다면 분리하는 것이 요구사항을 명확하게 전달할 수 있다.
- [x] 진열여부를 변경할 수 있다.
- 진열시 예외 조건 누락됨.
- [x] `식탁을 정리할 수 있다.`
- 행위에 대한 조건이 누락됨.
- [x] `식당 내 식사는 빈 식탁이 있어야 한다.`
Expand Down
38 changes: 38 additions & 0 deletions src/main/java/kitchenpos/application/DefaultMenuGroupService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package kitchenpos.application;

import kitchenpos.domain.MenuGroup;
import kitchenpos.domain.MenuGroupRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Objects;
import java.util.UUID;

@Service
public class DefaultMenuGroupService implements MenuGroupService {
private final MenuGroupRepository menuGroupRepository;

public DefaultMenuGroupService(final MenuGroupRepository menuGroupRepository) {
this.menuGroupRepository = menuGroupRepository;
}

@Override
@Transactional
public MenuGroup create(final MenuGroup request) {
final String name = request.getName();
if (Objects.isNull(name) || name.isEmpty()) {
throw new IllegalArgumentException();
}
final MenuGroup menuGroup = new MenuGroup();
menuGroup.setId(UUID.randomUUID());
menuGroup.setName(name);
return menuGroupRepository.save(menuGroup);
}

@Override
@Transactional(readOnly = true)
public List<MenuGroup> findAll() {
return menuGroupRepository.findAll();
}
}
32 changes: 4 additions & 28 deletions src/main/java/kitchenpos/application/MenuGroupService.java
Original file line number Diff line number Diff line change
@@ -1,36 +1,12 @@
package kitchenpos.application;

import kitchenpos.domain.MenuGroup;
import kitchenpos.domain.MenuGroupRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Objects;
import java.util.UUID;
import kitchenpos.domain.MenuGroup;

@Service
public class MenuGroupService {
private final MenuGroupRepository menuGroupRepository;
public interface MenuGroupService {

public MenuGroupService(final MenuGroupRepository menuGroupRepository) {
this.menuGroupRepository = menuGroupRepository;
}
MenuGroup create(final MenuGroup request);

@Transactional
public MenuGroup create(final MenuGroup request) {
final String name = request.getName();
if (Objects.isNull(name) || name.isEmpty()) {
throw new IllegalArgumentException();
}
final MenuGroup menuGroup = new MenuGroup();
menuGroup.setId(UUID.randomUUID());
menuGroup.setName(name);
return menuGroupRepository.save(menuGroup);
}
List<MenuGroup> findAll();

@Transactional(readOnly = true)
public List<MenuGroup> findAll() {
return menuGroupRepository.findAll();
}
}
51 changes: 31 additions & 20 deletions src/main/java/kitchenpos/application/MenuService.java
Original file line number Diff line number Diff line change
@@ -1,31 +1,40 @@
package kitchenpos.application;

import kitchenpos.domain.*;
import kitchenpos.infra.PurgomalumClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.UUID;
import java.util.stream.Collectors;
import kitchenpos.domain.Menu;
import kitchenpos.domain.MenuGroup;
import kitchenpos.domain.MenuGroupRepository;
import kitchenpos.domain.MenuProduct;
import kitchenpos.domain.MenuRepository;
import kitchenpos.domain.Product;
import kitchenpos.domain.ProductRepository;
import kitchenpos.infra.ProfanityClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class MenuService {
private final MenuRepository menuRepository;
private final MenuGroupRepository menuGroupRepository;
private final ProductRepository productRepository;
private final PurgomalumClient purgomalumClient;
private final ProfanityClient profanityClient;

public MenuService(
final MenuRepository menuRepository,
final MenuGroupRepository menuGroupRepository,
final ProductRepository productRepository,
final PurgomalumClient purgomalumClient
final ProfanityClient profanityClient
) {
this.menuRepository = menuRepository;
this.menuGroupRepository = menuGroupRepository;
this.productRepository = productRepository;
this.purgomalumClient = purgomalumClient;
this.profanityClient = profanityClient;
}

@Transactional
Expand Down Expand Up @@ -70,7 +79,7 @@ public Menu create(final Menu request) {
throw new IllegalArgumentException();
}
final String name = request.getName();
if (Objects.isNull(name) || purgomalumClient.containsProfanity(name)) {
if (Objects.isNull(name) || profanityClient.containsProfanity(name)) {
throw new IllegalArgumentException();
}
final Menu menu = new Menu();
Expand All @@ -91,13 +100,14 @@ public Menu changePrice(final UUID menuId, final Menu request) {
}
final Menu menu = menuRepository.findById(menuId)
.orElseThrow(NoSuchElementException::new);
BigDecimal sum = BigDecimal.ZERO;
for (final MenuProduct menuProduct : menu.getMenuProducts()) {
final BigDecimal sum = menuProduct.getProduct()
sum = sum.add(menuProduct.getProduct()
.getPrice()
.multiply(BigDecimal.valueOf(menuProduct.getQuantity()));
if (price.compareTo(sum) > 0) {
throw new IllegalArgumentException();
}
.multiply(BigDecimal.valueOf(menuProduct.getQuantity())));
}
if (price.compareTo(sum) > 0) {
throw new IllegalArgumentException();
}
menu.setPrice(price);
return menu;
Expand All @@ -107,13 +117,14 @@ public Menu changePrice(final UUID menuId, final Menu request) {
public Menu display(final UUID menuId) {
final Menu menu = menuRepository.findById(menuId)
.orElseThrow(NoSuchElementException::new);
BigDecimal sum = BigDecimal.ZERO;
for (final MenuProduct menuProduct : menu.getMenuProducts()) {
final BigDecimal sum = menuProduct.getProduct()
sum = sum.add(menuProduct.getProduct()
.getPrice()
.multiply(BigDecimal.valueOf(menuProduct.getQuantity()));
if (menu.getPrice().compareTo(sum) > 0) {
throw new IllegalStateException();
}
.multiply(BigDecimal.valueOf(menuProduct.getQuantity())));
}
if (menu.getPrice().compareTo(sum) > 0) {
throw new IllegalStateException();
}
menu.setDisplayed(true);
return menu;
Expand Down
27 changes: 18 additions & 9 deletions src/main/java/kitchenpos/application/OrderService.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
package kitchenpos.application;

import kitchenpos.domain.*;
import kitchenpos.domain.exception.OrderDeliveryAddressException;
import kitchenpos.domain.exception.OrderDisplayException;
import kitchenpos.domain.exception.OrderInvalidQuantityException;
import kitchenpos.domain.exception.OrderLineItemNotExistException;
import kitchenpos.domain.exception.OrderLineItemNotMatchException;
import kitchenpos.domain.exception.OrderLineItemPriceException;
import kitchenpos.domain.exception.OrderTypeNotExistException;
import kitchenpos.infra.KitchenridersClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand Down Expand Up @@ -33,35 +40,36 @@ public OrderService(
public Order create(final Order request) {
final OrderType type = request.getType();
if (Objects.isNull(type)) {
throw new IllegalArgumentException();
throw new OrderTypeNotExistException();
}
final List<OrderLineItem> orderLineItemRequests = request.getOrderLineItems();
if (Objects.isNull(orderLineItemRequests) || orderLineItemRequests.isEmpty()) {
throw new IllegalArgumentException();
throw new OrderLineItemNotExistException();
}
final List<Menu> menus = menuRepository.findAllByIdIn(
orderLineItemRequests.stream()
.map(OrderLineItem::getMenuId)
.collect(Collectors.toList())
);
if (menus.size() != orderLineItemRequests.size()) {
throw new IllegalArgumentException();
throw new OrderLineItemNotMatchException();
}
final List<OrderLineItem> orderLineItems = new ArrayList<>();
for (final OrderLineItem orderLineItemRequest : orderLineItemRequests) {
final long quantity = orderLineItemRequest.getQuantity();
if (type != OrderType.EAT_IN) {
if (quantity < 0) {
throw new IllegalArgumentException();
throw new OrderInvalidQuantityException(quantity);
}
}
final Menu menu = menuRepository.findById(orderLineItemRequest.getMenuId())
.orElseThrow(NoSuchElementException::new);
if (!menu.isDisplayed()) {
throw new IllegalStateException();
throw new OrderDisplayException();
}
if (menu.getPrice().compareTo(orderLineItemRequest.getPrice()) != 0) {
throw new IllegalArgumentException();
throw new OrderLineItemPriceException(menu.getName(), menu.getPrice().longValue(),
orderLineItemRequest.getPrice().longValue());
}
final OrderLineItem orderLineItem = new OrderLineItem();
orderLineItem.setMenu(menu);
Expand All @@ -77,7 +85,7 @@ public Order create(final Order request) {
if (type == OrderType.DELIVERY) {
final String deliveryAddress = request.getDeliveryAddress();
if (Objects.isNull(deliveryAddress) || deliveryAddress.isEmpty()) {
throw new IllegalArgumentException();
throw new OrderDeliveryAddressException();
}
order.setDeliveryAddress(deliveryAddress);
}
Expand All @@ -102,9 +110,9 @@ public Order accept(final UUID orderId) {
if (order.getType() == OrderType.DELIVERY) {
BigDecimal sum = BigDecimal.ZERO;
for (final OrderLineItem orderLineItem : order.getOrderLineItems()) {
sum = orderLineItem.getMenu()
sum = sum.add(orderLineItem.getMenu()
.getPrice()
.multiply(BigDecimal.valueOf(orderLineItem.getQuantity()));
.multiply(BigDecimal.valueOf(orderLineItem.getQuantity())));
}
kitchenridersClient.requestDelivery(orderId, sum, order.getDeliveryAddress());
}
Expand Down Expand Up @@ -179,4 +187,5 @@ public Order complete(final UUID orderId) {
public List<Order> findAll() {
return orderRepository.findAll();
}

}
Loading