diff --git a/docs/README.md b/docs/README.md index e69de29..395cb31 100644 --- a/docs/README.md +++ b/docs/README.md @@ -0,0 +1,126 @@ +# 구현 기능 목록 + + +- [X] 이벤트 플래너 시작 메세지를 출력한다. + ``` + 안녕하세요! 우테코 식당 12월 이벤트 플래너입니다. + ``` + + +- [X] 방문 날짜를 입력받고 예외 처리 한다. + - [X] 예외 처리 + 1. [X] 1 이상 31 이하의 숫자가 아닌 경우 + "[ERROR] 유효하지 않은 날짜입니다. 다시 입력해 주세요." 예외메세지 출력 + + [X] 모든 에러 메시지는 "[ERROR]"로 시작 + ``` + 12월 중 식당 예상 방문 날짜는 언제인가요? (숫자만 입력해 주세요!) + ``` + + +- [X] 메뉴와 개수를 입력받고 예외 처리 한다. + - [X] 예외 처리 + 1. [X] 고객이 메뉴판에 없는 메뉴를 입력하는 경우 + "[ERROR] 유효하지 않은 주문입니다. 다시 입력해 주세요." + 2. [X] 메뉴의 개수는 1 이상의 숫자가 아닌 경우 + "[ERROR] 유효하지 않은 주문입니다. 다시 입력해 주세요." + 3. [X] 메뉴 형식이 예시와 다른 경우 + "[ERROR] 유효하지 않은 주문입니다. 다시 입력해 주세요." + 4. [X] 중복 메뉴를 입력한 경우(e.g. 시저샐러드-1,시저샐러드-1) + "[ERROR] 유효하지 않은 주문입니다. 다시 입력해 주세요." + 5. [X] 음료만 주문한 경우 + "[ERROR] 유효하지 않은 주문입니다. 다시 입력해 주세요." + 6. [X] 메뉴 총개수가 20개 초과인 경우 + "[ERROR] 유효하지 않은 주문입니다. 다시 입력해 주세요." + + [X] 모든 에러 메시지는 "[ERROR]"로 시작 + ``` + 주문하실 메뉴를 메뉴와 개수를 알려 주세요. (e.g. 해산물파스타-2,레드와인-1,초코케이크-1) + ``` + + +- [X] 결과 시작 메세지를 출력한다. + + ``` + 12월 26일에 우테코 식당에서 받을 이벤트 혜택 미리 보기! + + ``` + + +- [X] 주문 메뉴를 출력한다. + - 출력 순서는 자유롭게 + + +- [X] 할인 전 총주문 금액을 계산한다. + + +- [X] 할인 전 총주문 금액을 출력한다. + + +- [X] 적용되는 이벤트를 계산한다. + + +- [X] 증정 메뉴를 출력한다. + - [X] 증정 이벤트에 해당하지 않는 경우, 증정 메뉴 "없음" 출력 + + +- [X] 혜택 내역을 출력한다. + - [X] 고객에게 적용된 이벤트만 출력 + - [X] 적용된 이벤트가 하나도 없다면 혜택 내역 "없음"으로 출력 + - [X] 혜택 내역에 여러 개의 이벤트가 적용된 경우, 출력 순서는 자유롭게 출력 + + +- [X] 할인 받는 금액을 계산한다. + + +- [X] 총혜택 금액을 출력한다. + - 총혜택 금액 = 할인 금액의 합계 + 증정 메뉴의 가격 + + +- [X] 할인 후 예상 결제 금액을 출력한다. + - 할인 후 예상 결제 금액 = 할인 전 총주문 금액 - 할인 금액 + + +- [X] 12월 이벤트 배지를 출력한다. + - 총혜택 금액에 따라 이벤트 배지의 이름을 다르게 출력 + + +- [X] 예외 처리 관련 + - IllegalArgumentException를 발생시키고, "[ERROR]"로 시작하는 에러 메시지를 출력 후 그 부분부터 입력을 다시 받는다. + + +--- +### 전체 출력 예시 +``` +안녕하세요! 우테코 식당 12월 이벤트 플래너입니다. +12월 중 식당 예상 방문 날짜는 언제인가요? (숫자만 입력해 주세요!) +3 +주문하실 메뉴를 메뉴와 개수를 알려 주세요. (e.g. 해산물파스타-2,레드와인-1,초코케이크-1) +티본스테이크-1,바비큐립-1,초코케이크-2,제로콜라-1 +12월 3일에 우테코 식당에서 받을 이벤트 혜택 미리 보기! + +<주문 메뉴> +티본스테이크 1개 +바비큐립 1개 +초코케이크 2개 +제로콜라 1개 + +<할인 전 총주문 금액> +142,000원 + +<증정 메뉴> +샴페인 1개 + +<혜택 내역> +크리스마스 디데이 할인: -1,200원 +평일 할인: -4,046원 +특별 할인: -1,000원 +증정 이벤트: -25,000원 + +<총혜택 금액> +-31,246원 + +<할인 후 예상 결제 금액> +135,754원 + +<12월 이벤트 배지> +산타 +``` \ No newline at end of file diff --git a/src/main/java/christmas/Application.java b/src/main/java/christmas/Application.java index b9ba6a2..d4a4f4d 100644 --- a/src/main/java/christmas/Application.java +++ b/src/main/java/christmas/Application.java @@ -1,7 +1,14 @@ package christmas; +import camp.nextstep.edu.missionutils.Console; +import christmas.controller.ChristmasController; +import christmas.view.InputView; +import christmas.view.OutputView; + public class Application { public static void main(String[] args) { - // TODO: 프로그램 구현 + ChristmasController christmasController = ChristmasController.of(InputView.getInstance(), OutputView.getInstance()); + christmasController.run(); + Console.close(); } -} +} \ No newline at end of file diff --git a/src/main/java/christmas/controller/ChristmasController.java b/src/main/java/christmas/controller/ChristmasController.java new file mode 100644 index 0000000..4807c2c --- /dev/null +++ b/src/main/java/christmas/controller/ChristmasController.java @@ -0,0 +1,79 @@ +package christmas.controller; + +import christmas.domain.orders.OrderItem; +import christmas.domain.orders.Orders; +import christmas.domain.visitingDate.VisitingDate; +import christmas.dto.OrderItemDto; +import christmas.dto.OrdersDto; +import christmas.dto.ResultDto; +import christmas.service.ChristmasManager; +import christmas.view.InputView; +import christmas.view.OutputView; + +import java.util.List; +import java.util.function.Supplier; + +public class ChristmasController { + private final InputView inputView; + private final OutputView outputView; + private ChristmasManager christmasManager; + + private ChristmasController(InputView inputView, OutputView outputView) { + this.inputView = inputView; + this.outputView = outputView; + } + + public static ChristmasController of(InputView inputView, OutputView outputView) { + return new ChristmasController(inputView, outputView); + } + + public void run() { + initializeChristmasManager(); + printOrders(); + printResult(); + } + + private void initializeChristmasManager() { + outputView.printStart(); + VisitingDate date = createVisitingDate(); + Orders orders = createOrders(); + christmasManager = ChristmasManager.of(date, orders); + outputView.printResultStart(date.provideDate()); + } + + private VisitingDate createVisitingDate() { + return readUserInput(() -> { + int input = inputView.readVisitingDate(); + return VisitingDate.from(input); + }); + } + + private Orders createOrders() { + return readUserInput(() -> { + List items = inputView.readOrders().stream() + .map(OrderItemDto::toOrderItem) + .toList(); + return Orders.from(items); + }); + } + + private T readUserInput(Supplier supplier) { + while (true) { + try { + return supplier.get(); + } catch (IllegalArgumentException e) { + outputView.printError(e.getMessage()); + } + } + } + + private void printOrders() { + OrdersDto ordersDto = christmasManager.createOrdersDto(); + outputView.printMenu(ordersDto); + } + + private void printResult() { + ResultDto resultDto = christmasManager.createResultDto(); + outputView.printResult(resultDto); + } +} \ No newline at end of file diff --git a/src/main/java/christmas/domain/badge/BadgeCondition.java b/src/main/java/christmas/domain/badge/BadgeCondition.java new file mode 100644 index 0000000..461986e --- /dev/null +++ b/src/main/java/christmas/domain/badge/BadgeCondition.java @@ -0,0 +1,33 @@ +package christmas.domain.badge; + +import java.util.Arrays; + +public enum BadgeCondition { + NONE("없음", 0L), + STAR("별", 5_000L), + TREE("트리", 10_000L), + SANTA("산타", 20_000L); + + private final String badgeName; + private final long minCondition; + + BadgeCondition(String badgeName, long minCondition) { + this.badgeName = badgeName; + this.minCondition = minCondition; + } + + public static BadgeCondition findBadgeByCondition(long totalBenefitAmount) { + return Arrays.stream(BadgeCondition.values()) + .filter(badge -> totalBenefitAmount >= badge.getMinCondition()) + .findFirst() + .orElse(NONE); + } + + public String getBadgeName() { + return badgeName; + } + + public long getMinCondition() { + return minCondition; + } +} \ No newline at end of file diff --git a/src/main/java/christmas/domain/constants/BenefitConstants.java b/src/main/java/christmas/domain/constants/BenefitConstants.java new file mode 100644 index 0000000..22042b1 --- /dev/null +++ b/src/main/java/christmas/domain/constants/BenefitConstants.java @@ -0,0 +1,14 @@ +package christmas.domain.constants; + +import christmas.domain.orders.MenuItem; + +public final class BenefitConstants { + public static final long BASE_DISCOUNT = 1_000L; + public static final long CHRISTMAS_RATE = 100L; + public static final long DAILY_RATE = 2_023L; + public static final long BASE_PRICE_CONDITION = 10_000L; + public static final long GIVE_AWAY_PRICE_CONDITION = 120_000L; + public static final long GIVE_AWAY_PRICE = MenuItem.샴페인.getPrice(); + public static final String GIVE_AWAY_ITEM = MenuItem.샴페인.name(); + public static final int GIVE_AWAY_QUANTITY = 1; +} \ No newline at end of file diff --git a/src/main/java/christmas/domain/constants/DateConstants.java b/src/main/java/christmas/domain/constants/DateConstants.java new file mode 100644 index 0000000..38feacb --- /dev/null +++ b/src/main/java/christmas/domain/constants/DateConstants.java @@ -0,0 +1,7 @@ +package christmas.domain.constants; + +public final class DateConstants { + public static final int EVENT_START = 1; + public static final int EVENT_END = 31; + public static final int CHRISTMAS_EVENT_END = 25; +} \ No newline at end of file diff --git a/src/main/java/christmas/domain/event/EventDetail.java b/src/main/java/christmas/domain/event/EventDetail.java new file mode 100644 index 0000000..671b69b --- /dev/null +++ b/src/main/java/christmas/domain/event/EventDetail.java @@ -0,0 +1,85 @@ +package christmas.domain.event; + +import christmas.domain.orders.Orders; +import christmas.domain.visitingDate.VisitingDate; + +import java.util.Arrays; +import java.util.List; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Predicate; + +import static christmas.domain.constants.DateConstants.*; +import static christmas.domain.constants.BenefitConstants.*; +import static christmas.domain.orders.MenuCategory.DESSERT; +import static christmas.domain.orders.MenuCategory.MAIN; + +public enum EventDetail { + CHRISTMAS_D_DAY( + "크리스마스 디데이 할인", + VisitingDate::isChristmasEventActive, + BASE_PRICE_CONDITION, + orders -> true, + (date, orders) -> BASE_DISCOUNT + calculatePassedDays(date) * CHRISTMAS_RATE), + WEEKDAYS( + "평일 할인", + VisitingDate::isWeekday, + BASE_PRICE_CONDITION, + orders -> orders.existsOrderItemByCategory(DESSERT), + (date, orders) -> orders.countOrderItemByCategory(DESSERT) * DAILY_RATE), + WEEKENDS( + "주말 할인", + VisitingDate::isWeekend, + BASE_PRICE_CONDITION, + orders -> orders.existsOrderItemByCategory(MAIN), + (date, orders) -> orders.countOrderItemByCategory(MAIN) * DAILY_RATE), + SPECIAL( + "특별 할인", + VisitingDate::isSpecialDay, + BASE_PRICE_CONDITION, + orders -> true, + (date, orders) -> BASE_DISCOUNT), + GIVE_AWAY( + "증정 이벤트", + VisitingDate::meetsGiveAwayDateCondition, + GIVE_AWAY_PRICE_CONDITION, + orders -> true, + (date, orders) -> GIVE_AWAY_PRICE); + + private final String eventName; + private final Predicate dateCondition; + private final long priceCondition; + private final Function itemCondition; + private final BiFunction benefitCalculator; + + EventDetail(String eventName, Predicate dateCondition, long priceCondition, + Function itemCondition, + BiFunction benefitCalculator) { + this.eventName = eventName; + this.dateCondition = dateCondition; + this.priceCondition = priceCondition; + this.itemCondition = itemCondition; + this.benefitCalculator = benefitCalculator; + } + + private static long calculatePassedDays(VisitingDate date) { + return date.provideDate() - EVENT_START; + } + + public static List findByCondition(VisitingDate date, Orders orders) { + return Arrays.stream(EventDetail.values()) + .filter(condition -> condition.dateCondition.test(date)) + .filter(condition -> orders.calculateOriginalTotalAmount() >= condition.priceCondition) + .filter(condition -> condition.itemCondition.apply(orders)) + .map(eventDetail -> MatchingEvent.of(eventDetail, eventDetail.calculateBenefitAmount(date, orders))) + .toList(); + } + + private long calculateBenefitAmount(VisitingDate date, Orders orders) { + return benefitCalculator.apply(date, orders); + } + + public String getEventName() { + return eventName; + } +} \ No newline at end of file diff --git a/src/main/java/christmas/domain/event/MatchingEvent.java b/src/main/java/christmas/domain/event/MatchingEvent.java new file mode 100644 index 0000000..3b40852 --- /dev/null +++ b/src/main/java/christmas/domain/event/MatchingEvent.java @@ -0,0 +1,29 @@ +package christmas.domain.event; + +import static christmas.domain.event.EventDetail.*; + +public class MatchingEvent { + private final EventDetail eventDetail; + private final long benefitAmount; + + private MatchingEvent(EventDetail eventDetail, long benefitAmount) { + this.eventDetail = eventDetail; + this.benefitAmount = benefitAmount; + } + + protected static MatchingEvent of(EventDetail eventDetail, long benefitAmount) { + return new MatchingEvent(eventDetail, benefitAmount); + } + + public boolean isGiveAway() { + return eventDetail == GIVE_AWAY; + } + + public String provideEventName() { + return eventDetail.getEventName(); + } + + public long provideBenefitAmount() { + return benefitAmount; + } +} \ No newline at end of file diff --git a/src/main/java/christmas/domain/event/MatchingEvents.java b/src/main/java/christmas/domain/event/MatchingEvents.java new file mode 100644 index 0000000..3f8f58d --- /dev/null +++ b/src/main/java/christmas/domain/event/MatchingEvents.java @@ -0,0 +1,48 @@ +package christmas.domain.event; + +import christmas.domain.badge.BadgeCondition; + +import java.util.List; + +import static christmas.domain.constants.BenefitConstants.GIVE_AWAY_PRICE; + +public class MatchingEvents { + private final List events; + + private MatchingEvents(List events) { + this.events = events; + } + + public static MatchingEvents from(List events) { + return new MatchingEvents(events); + } + + public long calculateTotalDiscountAmount() { + if (containsGiveAway()) { + return calculateTotalBenefitAmount() - GIVE_AWAY_PRICE; + } + return calculateTotalBenefitAmount(); + } + + public boolean containsGiveAway() { + return events.stream().anyMatch(MatchingEvent::isGiveAway); + } + + public long calculateTotalBenefitAmount() { + return events.stream() + .mapToLong(MatchingEvent::provideBenefitAmount) + .sum(); + } + + public List provideMatchingEvents() { + return List.copyOf(events); + } + + public String findBadgeName() { + return findBadge().getBadgeName(); + } + + private BadgeCondition findBadge() { + return BadgeCondition.findBadgeByCondition(calculateTotalBenefitAmount()); + } +} \ No newline at end of file diff --git a/src/main/java/christmas/domain/orders/MenuCategory.java b/src/main/java/christmas/domain/orders/MenuCategory.java new file mode 100644 index 0000000..75f199f --- /dev/null +++ b/src/main/java/christmas/domain/orders/MenuCategory.java @@ -0,0 +1,8 @@ +package christmas.domain.orders; + +public enum MenuCategory { + APPETIZER, + MAIN, + DESSERT, + DRINK +} \ No newline at end of file diff --git a/src/main/java/christmas/domain/orders/MenuItem.java b/src/main/java/christmas/domain/orders/MenuItem.java new file mode 100644 index 0000000..08221de --- /dev/null +++ b/src/main/java/christmas/domain/orders/MenuItem.java @@ -0,0 +1,44 @@ +package christmas.domain.orders; + +import java.util.Arrays; + +import static christmas.domain.orders.MenuCategory.*; +import static christmas.exception.ErrorMessage.INVALID_ORDERS; + +public enum MenuItem { + 양송이수프(APPETIZER, 6_000L), + 타파스(APPETIZER, 5_500L), + 시저샐러드(APPETIZER, 8_000L), + 티본스테이크(MAIN, 55_000L), + 바비큐립(MAIN, 54_000L), + 해산물파스타(MAIN, 35_000L), + 크리스마스파스타(MAIN, 25_000L), + 초코케이크(DESSERT, 15_000L), + 아이스크림(DESSERT, 5_000L), + 제로콜라(DRINK, 3_000L), + 레드와인(DRINK, 60_000L), + 샴페인(DRINK, 25_000L); + + private final MenuCategory category; + private final long price; + + MenuItem(MenuCategory category, long price) { + this.category = category; + this.price = price; + } + + public static MenuItem findByName(String value) { + return Arrays.stream(MenuItem.values()) + .filter(item -> item.name().equals(value)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException(INVALID_ORDERS.getMessage())); + } + + public MenuCategory getCategory() { + return category; + } + + public long getPrice() { + return price; + } +} \ No newline at end of file diff --git a/src/main/java/christmas/domain/orders/OrderItem.java b/src/main/java/christmas/domain/orders/OrderItem.java new file mode 100644 index 0000000..1751ec2 --- /dev/null +++ b/src/main/java/christmas/domain/orders/OrderItem.java @@ -0,0 +1,43 @@ +package christmas.domain.orders; + +import christmas.utils.OrderItemValidationUtils; + +public class OrderItem { + private final MenuItem menuItem; + private final int quantity; + + private OrderItem(MenuItem menuItem, int quantity) { + this.menuItem = menuItem; + this.quantity = quantity; + } + + public static OrderItem of(String menuName, int quantity) { + OrderItemValidationUtils.validatePositive(quantity); + MenuItem validMenuItem = convertToMenu(menuName); + return new OrderItem(validMenuItem, quantity); + } + + private static MenuItem convertToMenu(String menuName) { + return MenuItem.findByName(menuName); + } + + public MenuCategory convertToCategory() { + return menuItem.getCategory(); + } + + public long calculateItemOriginalPrice() { + return menuItem.getPrice() * quantity; + } + + public MenuItem provideMenuItem() { + return menuItem; + } + + public String provideMenuName() { + return menuItem.name(); + } + + public int provideQuantity() { + return quantity; + } +} \ No newline at end of file diff --git a/src/main/java/christmas/domain/orders/Orders.java b/src/main/java/christmas/domain/orders/Orders.java new file mode 100644 index 0000000..82694db --- /dev/null +++ b/src/main/java/christmas/domain/orders/Orders.java @@ -0,0 +1,47 @@ +package christmas.domain.orders; + +import christmas.utils.OrdersValidationUtils; + +import java.util.List; + +public class Orders { + private final List orderItems; + + private Orders(List orderItems) { + this.orderItems = orderItems; + } + + public static Orders from(List orderItems) { + validate(orderItems); + return new Orders(orderItems); + } + + private static void validate(List orders) { + OrdersValidationUtils.validateDuplicates(orders); + OrdersValidationUtils.validateNotOnlyDrinks(orders); + OrdersValidationUtils.validateTotalQuantity(orders); + } + + public int countOrderItemByCategory(MenuCategory comparingCategory) { + return orderItems.stream() + .filter(item -> item.convertToCategory().equals(comparingCategory)) + .mapToInt(OrderItem::provideQuantity) + .sum(); + } + + public boolean existsOrderItemByCategory(MenuCategory comparingCategory) { + return orderItems.stream() + .map(OrderItem::convertToCategory) + .anyMatch(category -> category.equals(comparingCategory)); + } + + public long calculateOriginalTotalAmount() { + return orderItems.stream() + .mapToLong(OrderItem::calculateItemOriginalPrice) + .sum(); + } + + public List provideOrderItems() { + return List.copyOf(orderItems); + } +} \ No newline at end of file diff --git a/src/main/java/christmas/domain/visitingDate/VisitingDate.java b/src/main/java/christmas/domain/visitingDate/VisitingDate.java new file mode 100644 index 0000000..0a9e3cd --- /dev/null +++ b/src/main/java/christmas/domain/visitingDate/VisitingDate.java @@ -0,0 +1,49 @@ +package christmas.domain.visitingDate; + +import java.util.List; + +import static christmas.domain.constants.DateConstants.*; +import static christmas.exception.ErrorMessage.INVALID_DATE; + +public class VisitingDate { + private final int date; + + private VisitingDate(int date) { + validateDate(date); + this.date = date; + } + + public static VisitingDate from(int date) { + return new VisitingDate(date); + } + + private void validateDate(int date) { + if (date < EVENT_START || date > EVENT_END) { + throw new IllegalArgumentException(INVALID_DATE.getMessage()); + } + } + + public boolean meetsGiveAwayDateCondition() { + return true; + } + + public boolean isChristmasEventActive() { + return date <= CHRISTMAS_EVENT_END; + } + + public boolean isWeekday() { + return date % 7 >= 3 || date % 7 == 0; + } + + public boolean isWeekend() { + return date % 7 == 1 || date % 7 == 2; + } + + public boolean isSpecialDay() { + return List.of(3, 10, 17, 24, 25, 31).contains(date); + } + + public int provideDate() { + return date; + } +} \ No newline at end of file diff --git a/src/main/java/christmas/dto/MatchingEventDto.java b/src/main/java/christmas/dto/MatchingEventDto.java new file mode 100644 index 0000000..494ca93 --- /dev/null +++ b/src/main/java/christmas/dto/MatchingEventDto.java @@ -0,0 +1,25 @@ +package christmas.dto; + +import christmas.domain.event.MatchingEvent; + +public class MatchingEventDto { + private final String eventName; + private final long benefitAmount; + + private MatchingEventDto(String eventName, long benefitAmount) { + this.eventName = eventName; + this.benefitAmount = benefitAmount; + } + + public static MatchingEventDto of(MatchingEvent event) { + return new MatchingEventDto(event.provideEventName(), event.provideBenefitAmount()); + } + + public String getEventName() { + return eventName; + } + + public long getBenefitAmount() { + return benefitAmount; + } +} diff --git a/src/main/java/christmas/dto/OrderItemDto.java b/src/main/java/christmas/dto/OrderItemDto.java new file mode 100644 index 0000000..4f95393 --- /dev/null +++ b/src/main/java/christmas/dto/OrderItemDto.java @@ -0,0 +1,35 @@ +package christmas.dto; + +import christmas.domain.orders.OrderItem; + +public class OrderItemDto { + private final String menuName; + private final int quantity; + + private OrderItemDto(String menuName, int quantity) { + this.menuName = menuName; + this.quantity = quantity; + } + + public static OrderItemDto of(String menuName, int quantity) { + return new OrderItemDto(menuName, quantity); + } + + public static OrderItemDto of(OrderItem orderItem) { + String menuName = orderItem.provideMenuName(); + int quantity = orderItem.provideQuantity(); + return new OrderItemDto(menuName, quantity); + } + + public OrderItem toOrderItem() { + return OrderItem.of(menuName, quantity); + } + + public String getMenuName() { + return menuName; + } + + public int getQuantity() { + return quantity; + } +} \ No newline at end of file diff --git a/src/main/java/christmas/dto/OrdersDto.java b/src/main/java/christmas/dto/OrdersDto.java new file mode 100644 index 0000000..6165f57 --- /dev/null +++ b/src/main/java/christmas/dto/OrdersDto.java @@ -0,0 +1,24 @@ +package christmas.dto; + +import christmas.domain.orders.Orders; + +import java.util.List; + +public class OrdersDto { + private final List orderItemDtos; + + private OrdersDto(List orderItemDtos) { + this.orderItemDtos = orderItemDtos; + } + + public static OrdersDto from(Orders orders) { + List orderItemDtos = orders.provideOrderItems().stream() + .map(OrderItemDto::of) + .toList(); + return new OrdersDto(orderItemDtos); + } + + public List getOrderItemDtos() { + return List.copyOf(orderItemDtos); + } +} \ No newline at end of file diff --git a/src/main/java/christmas/dto/ResultDto.java b/src/main/java/christmas/dto/ResultDto.java new file mode 100644 index 0000000..8943b57 --- /dev/null +++ b/src/main/java/christmas/dto/ResultDto.java @@ -0,0 +1,54 @@ +package christmas.dto; + +import christmas.domain.event.MatchingEvents; +import christmas.domain.orders.Orders; + +import java.util.List; + +public class ResultDto { + private final List events; + private final long originalTotalAmount; + private final boolean containsGiveAway; + private final long totalBenefitAmount; + private final long expectedTotalAmount; + private final String badgeName; + + private ResultDto(MatchingEvents events, Orders orders) { + this.events = events.provideMatchingEvents().stream() + .map(MatchingEventDto::of) + .toList(); + this.originalTotalAmount = orders.calculateOriginalTotalAmount(); + this.containsGiveAway = events.containsGiveAway(); + this.totalBenefitAmount = events.calculateTotalBenefitAmount(); + this.expectedTotalAmount = originalTotalAmount - events.calculateTotalDiscountAmount(); + this.badgeName = events.findBadgeName(); + } + + public static ResultDto of(MatchingEvents events, Orders orders) { + return new ResultDto(events, orders); + } + + public List getEventDetails() { + return events; + } + + public long getOriginalTotalAmount() { + return originalTotalAmount; + } + + public boolean isContainsGiveAway() { + return containsGiveAway; + } + + public long getTotalBenefitAmount() { + return totalBenefitAmount; + } + + public long getExpectedTotalAmount() { + return expectedTotalAmount; + } + + public String getBadgeName() { + return badgeName; + } +} \ No newline at end of file diff --git a/src/main/java/christmas/exception/ErrorMessage.java b/src/main/java/christmas/exception/ErrorMessage.java new file mode 100644 index 0000000..98172d9 --- /dev/null +++ b/src/main/java/christmas/exception/ErrorMessage.java @@ -0,0 +1,17 @@ +package christmas.exception; + +public enum ErrorMessage { + ERROR_CAPTION("[ERROR] "), + INVALID_DATE("유효하지 않은 날짜입니다. 다시 입력해 주세요."), + INVALID_ORDERS("유효하지 않은 주문입니다. 다시 입력해 주세요."); + + private final String message; + + ErrorMessage(String message) { + this.message = message; + } + + public String getMessage() { + return ERROR_CAPTION.message + message; + } +} \ No newline at end of file diff --git a/src/main/java/christmas/service/ChristmasManager.java b/src/main/java/christmas/service/ChristmasManager.java new file mode 100644 index 0000000..1efa7ff --- /dev/null +++ b/src/main/java/christmas/service/ChristmasManager.java @@ -0,0 +1,39 @@ +package christmas.service; + +import christmas.domain.event.EventDetail; +import christmas.domain.event.MatchingEvent; +import christmas.domain.event.MatchingEvents; +import christmas.domain.orders.Orders; +import christmas.domain.visitingDate.VisitingDate; +import christmas.dto.OrdersDto; +import christmas.dto.ResultDto; + +import java.util.List; + +public class ChristmasManager { + private final VisitingDate date; + private final Orders orders; + + private ChristmasManager(VisitingDate date, Orders orders) { + this.date = date; + this.orders = orders; + } + + public static ChristmasManager of(VisitingDate date, Orders orders) { + return new ChristmasManager(date, orders); + } + + public OrdersDto createOrdersDto() { + return OrdersDto.from(orders); + } + + public ResultDto createResultDto() { + MatchingEvents events = createMatchingEvents(); + return ResultDto.of(events, orders); + } + + private MatchingEvents createMatchingEvents() { + List eventDetails = EventDetail.findByCondition(date, orders); + return MatchingEvents.from(eventDetails); + } +} \ No newline at end of file diff --git a/src/main/java/christmas/utils/OrderItemValidationUtils.java b/src/main/java/christmas/utils/OrderItemValidationUtils.java new file mode 100644 index 0000000..1fbdbf2 --- /dev/null +++ b/src/main/java/christmas/utils/OrderItemValidationUtils.java @@ -0,0 +1,13 @@ +package christmas.utils; + +import static christmas.exception.ErrorMessage.INVALID_ORDERS; + +public class OrderItemValidationUtils { + private static final int MINIMUM_QUANTITY = 1; + + public static void validatePositive(int quantity) { + if (quantity < MINIMUM_QUANTITY) { + throw new IllegalArgumentException(INVALID_ORDERS.getMessage()); + } + } +} \ No newline at end of file diff --git a/src/main/java/christmas/utils/OrdersValidationUtils.java b/src/main/java/christmas/utils/OrdersValidationUtils.java new file mode 100644 index 0000000..9414ddf --- /dev/null +++ b/src/main/java/christmas/utils/OrdersValidationUtils.java @@ -0,0 +1,42 @@ +package christmas.utils; + +import christmas.domain.orders.MenuItem; +import christmas.domain.orders.OrderItem; + +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import static christmas.domain.orders.MenuCategory.DRINK; +import static christmas.exception.ErrorMessage.INVALID_ORDERS; + +public class OrdersValidationUtils { + private static final int MAXIMUM_ORDER_TOTAL_QUANTITY = 20; + + public static void validateDuplicates(List orders) { + Set uniqueOrders = orders.stream() + .map(OrderItem::provideMenuItem) + .collect(Collectors.toSet()); + if (orders.size() != uniqueOrders.size()) { + throw new IllegalArgumentException(INVALID_ORDERS.getMessage()); + } + } + + public static void validateNotOnlyDrinks(List orders) { + orders.stream() + .map(item -> item.provideMenuItem().getCategory()) + .filter(category -> !category.equals(DRINK)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException(INVALID_ORDERS.getMessage())); + } + + public static void validateTotalQuantity(List orders) { + int totalQuantity = orders.stream() + .mapToInt(OrderItem::provideQuantity) + .sum(); + + if (totalQuantity > MAXIMUM_ORDER_TOTAL_QUANTITY) { + throw new IllegalArgumentException(INVALID_ORDERS.getMessage()); + } + } +} \ No newline at end of file diff --git a/src/main/java/christmas/utils/Parser.java b/src/main/java/christmas/utils/Parser.java new file mode 100644 index 0000000..ffe6ed5 --- /dev/null +++ b/src/main/java/christmas/utils/Parser.java @@ -0,0 +1,33 @@ +package christmas.utils; + +import java.util.List; + +import static christmas.exception.ErrorMessage.INVALID_ORDERS; + +public class Parser { + public static Integer safeParseInt(String value, String message) { + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + throw new IllegalArgumentException(message); + } + } + + public static List safeSplit(String input, String delimiter) { + validateEmpty(input); + validateStartsOrEndsWithDelimiter(input, delimiter); + return List.of(input.split(delimiter)); + } + + private static void validateEmpty(String input) { + if (input == null || input.isEmpty()) { + throw new IllegalArgumentException(INVALID_ORDERS.getMessage()); + } + } + + private static void validateStartsOrEndsWithDelimiter(String input, String delimiter) { + if (input.startsWith(delimiter) || input.endsWith(delimiter)) { + throw new IllegalArgumentException(INVALID_ORDERS.getMessage()); + } + } +} \ No newline at end of file diff --git a/src/main/java/christmas/view/InputView.java b/src/main/java/christmas/view/InputView.java new file mode 100644 index 0000000..8745689 --- /dev/null +++ b/src/main/java/christmas/view/InputView.java @@ -0,0 +1,58 @@ +package christmas.view; + +import camp.nextstep.edu.missionutils.Console; +import christmas.dto.OrderItemDto; +import christmas.utils.Parser; + +import java.util.List; + +import static christmas.exception.ErrorMessage.INVALID_DATE; +import static christmas.exception.ErrorMessage.INVALID_ORDERS; + +public class InputView { + private static final InputView instance = new InputView(); + private static final String ASK_VISITING_DATE = "12월 중 식당 예상 방문 날짜는 언제인가요? (숫자만 입력해 주세요!)"; + private static final String ASK_ORDERS = "주문하실 메뉴를 메뉴와 개수를 알려 주세요. (e.g. 해산물파스타-2,레드와인-1,초코케이크-1)"; + private static final String ITEM_DELIMITER = ","; + private static final String ITEM_QUANTITY_DELIMITER = "-"; + private static final int REQUIRED_ORDER_ITEM_COMPONENTS = 2; + private static final int MENU_NAME_INDEX = 0; + private static final int QUANTITY_INDEX = 1; + + private InputView() { + } + + public static InputView getInstance() { + return instance; + } + + public int readVisitingDate() { + System.out.println(ASK_VISITING_DATE); + String input = Console.readLine(); + return Parser.safeParseInt(input, INVALID_DATE.getMessage()); + } + + public List readOrders() { + System.out.println(ASK_ORDERS); + String input = Console.readLine(); + List pairs = Parser.safeSplit(input, ITEM_DELIMITER); + return convertToOrderItemDtos(pairs); + } + + private List convertToOrderItemDtos(List pairs) { + return pairs.stream() + .map(this::createOrderItemDto) + .toList(); + } + + private OrderItemDto createOrderItemDto(String pair) { + List pairs = Parser.safeSplit(pair, ITEM_QUANTITY_DELIMITER); + + if (pairs.size() != REQUIRED_ORDER_ITEM_COMPONENTS) { + throw new IllegalArgumentException(INVALID_ORDERS.getMessage()); + } + Integer quantity = Parser.safeParseInt(pairs.get(QUANTITY_INDEX), INVALID_ORDERS.getMessage()); + + return OrderItemDto.of(pairs.get(MENU_NAME_INDEX), quantity); + } +} \ No newline at end of file diff --git a/src/main/java/christmas/view/OutputView.java b/src/main/java/christmas/view/OutputView.java new file mode 100644 index 0000000..4cb03a9 --- /dev/null +++ b/src/main/java/christmas/view/OutputView.java @@ -0,0 +1,110 @@ +package christmas.view; + +import christmas.dto.MatchingEventDto; +import christmas.dto.OrdersDto; +import christmas.dto.ResultDto; + +import java.util.List; + +import static christmas.view.OutputViewMessageConstants.*; + +public class OutputView { + private static final OutputView instance = new OutputView(); + + private OutputView() { + } + + public static OutputView getInstance() { + return instance; + } + + public void printError(String message) { + System.out.println(message); + } + + public void printStart() { + System.out.println(START_MESSAGE); + } + + public void printResultStart(int date) { + System.out.printf((RESULT_SRART_FORMAT) + "%n", date); + printBlank(); + } + + private void printBlank() { + System.out.println(); + } + + public void printMenu(OrdersDto ordersDto) { + System.out.println(MENU_TITLE); + ordersDto.getOrderItemDtos() + .forEach(item -> + System.out.printf((MENU_FORMAT) + "%n", item.getMenuName(), item.getQuantity())); + printBlank(); + } + + public void printResult(ResultDto dto) { + printOriginalTotalAmount(dto.getOriginalTotalAmount()); + printGiveAway(dto.isContainsGiveAway()); + printMatchingEvents(dto.getEventDetails()); + printTotalBenefitAmount(dto.getTotalBenefitAmount()); + printExpectedTotalAmount(dto.getExpectedTotalAmount()); + printBadge(dto.getBadgeName()); + } + + private void printOriginalTotalAmount(long amount) { + System.out.println(ORIGINAL_TOTAL_AMOUNT_TITLE); + System.out.printf((TOTAL_AMOUNT_FORMAT) + "%n", amount); + printBlank(); + } + + private void printGiveAway(boolean containsGiveAway) { + System.out.println(GIVE_AWAY_TITLE); + if (containsGiveAway) { + printMessage(GIVE_AWAY_FORMAT); + return; + } + printMessage(DEFAULT); + } + + private void printMessage(String message) { + System.out.println(message); + printBlank(); + } + + private void printMatchingEvents(List events) { + System.out.println(MATCHING_EVENTS_TITLE); + if (events.isEmpty()) { + printMessage(DEFAULT); + return; + } + events.forEach(this::printEventDetail); + printBlank(); + } + + private void printEventDetail(MatchingEventDto event) { + System.out.printf((MATCHING_EVENT_FORMAT) + "%n", event.getEventName(), event.getBenefitAmount()); + } + + private void printTotalBenefitAmount(long amount) { + System.out.println(TOTAL_BENEFIT_AMOUNT_TITLE); + if (amount == 0) { + printMessage(TOTAL_BENEFIT_AMOUNT_DEFAULT_MESSAGE); + return; + } + System.out.printf((TOTAL_BENEFIT_AMOUNT_FORMAT) + "%n", amount); + printBlank(); + } + + private void printExpectedTotalAmount(long amount) { + System.out.println(EXPECTED_TOTAL_AMOUNT_TITLE); + System.out.printf((TOTAL_AMOUNT_FORMAT) + "%n", amount); + printBlank(); + } + + private void printBadge(String badgeName) { + System.out.println(BADGE_TITLE); + System.out.println(badgeName); + printBlank(); + } +} \ No newline at end of file diff --git a/src/main/java/christmas/view/OutputViewMessageConstants.java b/src/main/java/christmas/view/OutputViewMessageConstants.java new file mode 100644 index 0000000..7729802 --- /dev/null +++ b/src/main/java/christmas/view/OutputViewMessageConstants.java @@ -0,0 +1,22 @@ +package christmas.view; + +import christmas.domain.constants.BenefitConstants; + +public class OutputViewMessageConstants { + public static final String START_MESSAGE = "안녕하세요! 우테코 식당 12월 이벤트 플래너입니다."; + public static final String RESULT_SRART_FORMAT = "12월 %d일에 우테코 식당에서 받을 이벤트 혜택 미리 보기!"; + public static final String MENU_TITLE = "<주문 메뉴>"; + public static final String MENU_FORMAT = "%s %d개"; + public static final String ORIGINAL_TOTAL_AMOUNT_TITLE = "<할인 전 총주문 금액>"; + public static final String TOTAL_AMOUNT_FORMAT = "%,d원"; + public static final String GIVE_AWAY_TITLE = "<증정 메뉴>"; + public static final String GIVE_AWAY_FORMAT = String.format("%s %d개", BenefitConstants.GIVE_AWAY_ITEM, BenefitConstants.GIVE_AWAY_QUANTITY); + public static final String DEFAULT = "없음"; + public static final String MATCHING_EVENTS_TITLE = "<혜택 내역>"; + public static final String MATCHING_EVENT_FORMAT = "%s: -%,d원"; + public static final String TOTAL_BENEFIT_AMOUNT_TITLE = "<총혜택 금액>"; + public static final String TOTAL_BENEFIT_AMOUNT_FORMAT = "-%,d원"; + public static final String TOTAL_BENEFIT_AMOUNT_DEFAULT_MESSAGE = "0원"; + public static final String EXPECTED_TOTAL_AMOUNT_TITLE = "<할인 후 예상 결제 금액>"; + public static final String BADGE_TITLE = "<12월 이벤트 배지>"; +} \ No newline at end of file diff --git a/src/test/java/christmas/ApplicationTest.java b/src/test/java/christmas/ApplicationTest.java index 514a99e..cf8256b 100644 --- a/src/test/java/christmas/ApplicationTest.java +++ b/src/test/java/christmas/ApplicationTest.java @@ -1,11 +1,11 @@ package christmas; -import static camp.nextstep.edu.missionutils.test.Assertions.assertSimpleTest; -import static org.assertj.core.api.Assertions.assertThat; - import camp.nextstep.edu.missionutils.test.NsTest; import org.junit.jupiter.api.Test; +import static camp.nextstep.edu.missionutils.test.Assertions.assertSimpleTest; +import static org.assertj.core.api.Assertions.assertThat; + class ApplicationTest extends NsTest { private static final String LINE_SEPARATOR = System.lineSeparator(); @@ -14,13 +14,13 @@ class ApplicationTest extends NsTest { assertSimpleTest(() -> { run("3", "티본스테이크-1,바비큐립-1,초코케이크-2,제로콜라-1"); assertThat(output()).contains( - "<주문 메뉴>", - "<할인 전 총주문 금액>", - "<증정 메뉴>", - "<혜택 내역>", - "<총혜택 금액>", - "<할인 후 예상 결제 금액>", - "<12월 이벤트 배지>" + "<주문 메뉴>", + "<할인 전 총주문 금액>", + "<증정 메뉴>", + "<혜택 내역>", + "<총혜택 금액>", + "<할인 후 예상 결제 금액>", + "<12월 이벤트 배지>" ); }); } diff --git a/src/test/java/christmas/domain/badge/BadgeConditionTest.java b/src/test/java/christmas/domain/badge/BadgeConditionTest.java new file mode 100644 index 0000000..6b43a4c --- /dev/null +++ b/src/test/java/christmas/domain/badge/BadgeConditionTest.java @@ -0,0 +1,18 @@ +package christmas.domain.badge; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import static org.assertj.core.api.Assertions.assertThat; + +class BadgeConditionTest { + @ParameterizedTest(name = "[{index}] 총혜택금액이 {0} 이면 이벤트 배지는 {1} 이다.") + @CsvSource(value = + {"0, '없음'", "1000, '없음'", "5000, '별'", "9900, '별'", + "10000, '트리'", "19000, '트리'", + "20000, '산타'", "1000000, '산타'"}) + void findBadgeNameByCondition(long amount, String badgeName) { + BadgeCondition badge = BadgeCondition.findBadgeByCondition(amount); + assertThat(badge.getBadgeName()).isEqualTo(badgeName); + } +} \ No newline at end of file diff --git a/src/test/java/christmas/domain/event/EventDetailTest.java b/src/test/java/christmas/domain/event/EventDetailTest.java new file mode 100644 index 0000000..f3ad078 --- /dev/null +++ b/src/test/java/christmas/domain/event/EventDetailTest.java @@ -0,0 +1,30 @@ +package christmas.domain.event; + +import christmas.domain.orders.MenuItem; +import christmas.domain.orders.OrderItem; +import christmas.domain.orders.Orders; +import christmas.domain.visitingDate.VisitingDate; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class EventDetailTest { + @DisplayName("findByCondition 테스트") + @Test + void findByCondition() { + // given + VisitingDate date = VisitingDate.from(1); + Orders orders = Orders.from( + List.of(OrderItem.of(MenuItem.시저샐러드.name(), 1), + OrderItem.of(MenuItem.바비큐립.name(), 1), + OrderItem.of(MenuItem.초코케이크.name(), 1), + OrderItem.of(MenuItem.레드와인.name(), 1))); + + // when, then + List events = EventDetail.findByCondition(date, orders); + assertThat(events.size()).isEqualTo(3); + } +} \ No newline at end of file diff --git a/src/test/java/christmas/domain/event/MatchingEventTest.java b/src/test/java/christmas/domain/event/MatchingEventTest.java new file mode 100644 index 0000000..472f0a1 --- /dev/null +++ b/src/test/java/christmas/domain/event/MatchingEventTest.java @@ -0,0 +1,18 @@ +package christmas.domain.event; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + + +class MatchingEventTest { + @DisplayName("matchingEvent 생성") + @Test + void create() { + // given + MatchingEvent event = MatchingEvent.of(EventDetail.CHRISTMAS_D_DAY, 1000); + + // when, then + Assertions.assertThat(event.isGiveAway()).isFalse(); + } +} \ No newline at end of file diff --git a/src/test/java/christmas/domain/event/MatchingEventsTest.java b/src/test/java/christmas/domain/event/MatchingEventsTest.java new file mode 100644 index 0000000..8ab610b --- /dev/null +++ b/src/test/java/christmas/domain/event/MatchingEventsTest.java @@ -0,0 +1,58 @@ +package christmas.domain.event; + + +import christmas.domain.badge.BadgeCondition; +import christmas.domain.orders.MenuItem; +import christmas.domain.orders.OrderItem; +import christmas.domain.orders.Orders; +import christmas.domain.visitingDate.VisitingDate; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + + +class MatchingEventsTest { + @DisplayName("10000원 이하로 주문하는 경우 해당되는 이벤트 없음") + @Test + void noMatchingEvent() { + // given + VisitingDate date = VisitingDate.from(25); + Orders orders = Orders.from( + List.of( + OrderItem.of(MenuItem.시저샐러드.name(), 1))); + + List events = EventDetail.findByCondition(date, orders); + MatchingEvents matchingEvents = MatchingEvents.from(events); + + // when, then + assertThat(matchingEvents.provideMatchingEvents().size()).isEqualTo(0); + assertThat(matchingEvents.containsGiveAway()).isFalse(); + assertThat(matchingEvents.calculateTotalDiscountAmount()).isEqualTo(0); + assertThat(matchingEvents.findBadgeName()).isEqualTo(BadgeCondition.NONE.getBadgeName()); + assertThat(matchingEvents.calculateTotalBenefitAmount()).isEqualTo(0); + } + + @DisplayName("해당되는 이벤트 찾아서 결과 출력") + @Test + void matchingEvent() { + // given + VisitingDate date = VisitingDate.from(25); + Orders orders = Orders.from( + List.of( + OrderItem.of(MenuItem.크리스마스파스타.name(), 1), + OrderItem.of(MenuItem.샴페인.name(), 1))); + + List events = EventDetail.findByCondition(date, orders); + MatchingEvents matchingEvents = MatchingEvents.from(events); + + // when, then + assertThat(matchingEvents.provideMatchingEvents().size()).isEqualTo(2); + assertThat(matchingEvents.containsGiveAway()).isFalse(); + assertThat(matchingEvents.calculateTotalDiscountAmount()).isEqualTo(4400); + assertThat(matchingEvents.findBadgeName()).isEqualTo(BadgeCondition.NONE.getBadgeName()); + assertThat(matchingEvents.calculateTotalBenefitAmount()).isEqualTo(4400); + } +} \ No newline at end of file diff --git a/src/test/java/christmas/domain/orders/MenuItemTest.java b/src/test/java/christmas/domain/orders/MenuItemTest.java new file mode 100644 index 0000000..cad65c6 --- /dev/null +++ b/src/test/java/christmas/domain/orders/MenuItemTest.java @@ -0,0 +1,17 @@ +package christmas.domain.orders; + +import christmas.domain.orders.MenuCategory; +import christmas.domain.orders.MenuItem; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class MenuItemTest { + @DisplayName("findByName 테스트") + @Test + void findByName() { + MenuItem item = MenuItem.findByName("타파스"); + assertThat(item.getCategory()).isEqualTo(MenuCategory.APPETIZER); + } +} \ No newline at end of file diff --git a/src/test/java/christmas/domain/orders/OrderItemTest.java b/src/test/java/christmas/domain/orders/OrderItemTest.java new file mode 100644 index 0000000..0fd3ba8 --- /dev/null +++ b/src/test/java/christmas/domain/orders/OrderItemTest.java @@ -0,0 +1,28 @@ +package christmas.domain.orders; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class OrderItemTest { + @DisplayName("OrderItem 정상 생성") + @Test + void create() { + // given, when + OrderItem orderItem = OrderItem.of("양송이수프", 1); + + // then + assertThat(orderItem).isNotNull(); + } + + @ParameterizedTest(name = "[{index}] 메뉴이름:{0}, 수량:{1} 인 경우, OrderItem 생성 시 예외가 발생한다.") + @CsvSource(value = {"abc, 1", "양송이수프, 0", "양송이수프, -1"}) + void exception(String menu, int quantity) { + assertThatThrownBy(() -> OrderItem.of(menu, quantity)) + .isInstanceOf(IllegalArgumentException.class); + } +} diff --git a/src/test/java/christmas/domain/orders/OrdersTest.java b/src/test/java/christmas/domain/orders/OrdersTest.java new file mode 100644 index 0000000..01468d7 --- /dev/null +++ b/src/test/java/christmas/domain/orders/OrdersTest.java @@ -0,0 +1,61 @@ +package christmas.domain.orders; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.List; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class OrdersTest { + String APPETIZER = "양송이수프"; + String MAIN = "티본스테이크"; + String DRINK1 = "제로콜라"; + String DRINK2 = "샴페인"; + List orderItems; + + private static Stream orderItemsProvider() { + OrdersTest test = new OrdersTest(); + + OrderItem appetizer1 = OrderItem.of(test.APPETIZER, 1); + OrderItem appetizer2 = OrderItem.of(test.APPETIZER, 2); + OrderItem appetizer10 = OrderItem.of(test.APPETIZER, 10); + OrderItem drink1 = OrderItem.of(test.DRINK1, 1); + OrderItem drink2 = OrderItem.of(test.DRINK2, 1); + OrderItem main10 = OrderItem.of(test.MAIN, 10); + + return Stream.of( + Arguments.of(List.of(appetizer1, appetizer2)), + Arguments.of(List.of(drink1)), + Arguments.of(List.of(drink1, drink2)), + Arguments.of(List.of(appetizer10, main10, drink1)) + ); + } + + @DisplayName("Orders 정상 생성") + @Test + void create() { + //given + OrderItem orderItem1 = OrderItem.of(APPETIZER, 1); + OrderItem orderItem2 = OrderItem.of(DRINK1, 2); + orderItems = List.of(orderItem1, orderItem2); + + //when + Orders orders = Orders.from(orderItems); + + //then + assertThat(orders).isNotNull(); + } + + @ParameterizedTest(name = "[{index}] 중복 메뉴, 음료만 주문, 메뉴 개수 총합이 20 초과인 경우, 예외가 발생한다.") + @MethodSource("orderItemsProvider") + void exception(List orderItems) { + assertThatThrownBy(() -> Orders.from(orderItems)) + .isInstanceOf(IllegalArgumentException.class); + } +} diff --git a/src/test/java/christmas/domain/visitingDate/VisitingDateTest.java b/src/test/java/christmas/domain/visitingDate/VisitingDateTest.java new file mode 100644 index 0000000..7a599d6 --- /dev/null +++ b/src/test/java/christmas/domain/visitingDate/VisitingDateTest.java @@ -0,0 +1,31 @@ +package christmas.domain.visitingDate; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class VisitingDateTest { + @DisplayName("1 이상 31 이하이면 방문 날짜 정상 생성된다.") + @Test + void create() { + // given + int date = 1; + + // when + VisitingDate visitingDate = VisitingDate.from(date); + + // then + assertThat(visitingDate).isNotNull(); + } + + @ParameterizedTest(name = "[{index}] {0} 을 전달하면 방문날짜 생성 시 예외가 발생한다.") + @ValueSource(ints = {0, -1, 32, 1000}) + void throwsException(int element) { + assertThatThrownBy(() -> VisitingDate.from(element)) + .isInstanceOf(IllegalArgumentException.class); + } +} diff --git a/src/test/java/christmas/view/InputViewTest.java b/src/test/java/christmas/view/InputViewTest.java new file mode 100644 index 0000000..53d833a --- /dev/null +++ b/src/test/java/christmas/view/InputViewTest.java @@ -0,0 +1,63 @@ +package christmas.view; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.List; + +import static christmas.exception.ErrorMessage.INVALID_ORDERS; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class InputViewTest { + private final InputView inputView = InputView.getInstance(); + InputStream originalIn = System.in; + + @AfterEach + void afterEach() { + System.setIn(originalIn); + } + + @DisplayName("readOrders() 정상 수행") + @Test + void readOrders() { + // given + + // when + + // then + + } + + @DisplayName("',,,'를 입력하면 readOrders() 예외가 발생한다.") + @Test + void readOrders_exception1() { + final byte[] buf = String.join("\n", ",,,").getBytes(); + System.setIn(new ByteArrayInputStream(buf)); + + try { + inputView.readOrders(); + } catch (IllegalArgumentException e) { + assertEquals(INVALID_ORDERS.getMessage(), e.getMessage()); + } + } + + @Test + void split() { + String ITEM_DELIMITER = ","; + String ITEM_QUANTITY_DELIMITER = "-"; + + String validSentence = "양송이수프-1,레드와인-3"; + String[] value1 = validSentence.split(ITEM_DELIMITER); + List value12 = List.of(value1); + assertThat(value12.size()).isEqualTo(2); + + List value1ToList = List.of(value1); + for (String element : value1ToList) { + System.out.println(element.split(ITEM_QUANTITY_DELIMITER).length); + } + } +} \ No newline at end of file