diff --git a/docs/README.md b/docs/README.md index e69de29..5baaf77 100644 --- a/docs/README.md +++ b/docs/README.md @@ -0,0 +1,279 @@ +### ๐Ÿ–ฅ๏ธ ํ”„๋กœ๊ทธ๋žจ ์†Œ๊ฐœ +์ฃผ๋ฌธ์„ ๋ฐ›์•„์„œ ์ด๋ฒคํŠธ ์ ์šฉ ๋‚ด์—ญ์„ ์ถœ๋ ฅํ•˜๋Š” ํ”„๋กœ๊ทธ๋žจ์ž…๋‹ˆ๋‹ค. + +### ๐Ÿ’ก ํ•ต์‹ฌ ๊ธฐ๋Šฅ +์ ์šฉ ๊ฐ€๋Šฅํ•œ ์ด๋ฒคํŠธ ํ˜œํƒ ๋‚ด์—ญ ๊ณ„์‚ฐ + +### ๐Ÿ“Œ ์ฃผ์š” ํฌ์ธํŠธ +- ํ• ์ธ ์ด๋ฒคํŠธ์™€ ์„ ๋ฌผ ์ด๋ฒคํŠธ๋ฅผ ์ธํ„ฐํŽ˜์ด์Šค์™€ ์ถ”์ƒ ํด๋ž˜์Šค๋กœ ๋ถ„๋ฆฌ์™€ ๋™์‹œ์— ๊ณตํ†ต ๋กœ์ง ํ†ตํ•ฉ +- ์—ฌ๋Ÿฌ ์ข…๋ฅ˜์˜ ์ด๋ฒคํŠธ๋ฅผ Enum๊ณผ ํ•จ์ˆ˜ํ˜• ์ธํ„ฐํŽ˜์ด์Šค๋กœ ๊ด€๋ฆฌ +- MVC ํŒจํ„ด์— Service, Repository๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ์—ญํ•  ๋ถ„๋ฆฌ +- Enum๊ณผ EnumMap์œผ๋กœ ๋ถˆํ•„์š” ํด๋ž˜์Šค ์ œ๊ฑฐ ๋ฐ ํ†ตํ•ฉ ๊ด€๋ฆฌ +- ๊ณ„์ธตํ˜• Dto๋ฅผ ํ™œ์šฉํ•œ View์™€ Domain ์˜์กด์„ฑ ๋ถ„๋ฆฌ + +### ๐Ÿ“‰ ๋Ÿฐํƒ€์ž„ ์˜์กด๊ด€๊ณ„๋„ +![asso](https://github.com/Arachneee/java-christmas-6-Arachneee/assets/66822642/e94eb329-5991-41f3-9e21-23978a98377e) + +### ๐Ÿ“Š UML +![promotion_uml](https://github.com/Arachneee/java-christmas-6-Arachneee/assets/66822642/f085deea-f1fc-4475-bf5d-9318d445565e) + +### โš’๏ธ ํด๋ž˜์Šค ์—ญํ•  +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
๐Ÿ“ Package๐Ÿ“š Classโœ๏ธ Description
๐Ÿ—“๏ธ configPlannerConfig์˜์กด๊ด€๊ณ„ ์„ค์ •
โš™๏ธ controllerOrderController์ฃผ๋ฌธ ์ž…์ถœ๋ ฅ(View), ์ฃผ๋ฌธ(Service) ์—ฐ๊ฒฐ
OrderConverterView ์ž…๋ ฅ๊ฐ’ Order ์ธ์Šคํ„ด์Šค ๋ณ€์ˆ˜๋กœ ๋ณ€ํ™˜
๐Ÿ“ domain
⇛ order
Order์ฃผ๋ฌธ ์ผ์ž์™€ ๋ฉ”๋‰ด ์ˆ˜๋Ÿ‰์„ ๊ฐ–๋Š” ํด๋ž˜์Šค
Day12์›”์˜ ์ผ์ž ์›์‹œํƒ€์ž… ํฌ์žฅ ํด๋ž˜์Šค
domain
⇛ order
⇛ constant
Category๋ฉ”๋‰ด์˜ ์นดํ…Œ๊ณ ๋ฆฌ Enum
DayOfWeek์š”์ผ Enum
December12์›” Enum
Menu๋ฉ”๋‰ด Enum
Weekํ‰์ผ/์ฃผ๋ง Enum
domain
⇛ event
๐Ÿ“— Event์ด๋ฒคํŠธ ํ˜œํƒ ๊ณ„์‚ฐ ์ธํ„ฐํŽ˜์ด์Šค
๐Ÿ“— EventRepository์ด๋ฒคํŠธ ํ˜œํƒ ๊ฒฐ๊ณผ ์ €์žฅ ์ถ”์ƒ ํด๋ž˜์Šค
Badge๋ฐฐ์ง€ Enum
domain
⇛ event
⇛ discount
๐ŸŽ DiscountEventTypeEvent ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„, ํ• ์ธ ์ด๋ฒคํŠธ ํ†ตํ•ฉ ๊ด€๋ฆฌ Enum
๐Ÿ’พ DiscountRepositoryEventRepository ๊ตฌํ˜„, ํ• ์ธ ์ด๋ฒคํŠธ ๊ฒฐ๊ณผ EnumMap ์ €์žฅ
domain
⇛ event
⇛ gift
๐ŸŽ GiftEventTypeEvent ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„, ์„ ๋ฌผ ์ด๋ฒคํŠธ ํ†ตํ•ฉ ๊ด€๋ฆฌ Enum
๐Ÿ’พ GiftRepositoryEventRepository ๊ตฌํ˜„, ์„ ๋ฌผ ์ด๋ฒคํŠธ ๊ฒฐ๊ณผ EnumMap ์ €์žฅ
๐Ÿ•น๏ธ service
⇛ order
OrderService์ฃผ๋ฌธ ์ƒ์„ฑ ๋ฐ ์ฃผ๋ฌธ ๊ฒฐ๊ณผ ์š”์•ฝ
service
⇛ event
EventDetailService์ด๋ฒคํŠธ ์ ์šฉ ์ƒ์„ธ ๊ฒฐ๊ณผ ์š”์•ฝ
๐Ÿ“— EventService์ด๋ฒคํŠธ ํ˜œํƒ ๊ฒฐ๊ณผ ๊ณ„์‚ฐ ์ถ”์ƒ ํด๋ž˜์Šค
DiscountServiceEventService ๊ตฌํ˜„, ํ• ์ธ ํ•ดํƒ ์ ์šฉ
GiftServiceEventService ๊ตฌํ˜„, ์„ ๋ฌผ ํ•ดํƒ ์ ์šฉ
โฐ exceptionOrderException์ฃผ๋ฌธ ์ƒ์„ฑ ์˜ˆ์™ธ ๋ฐœ์ƒ
ErrorMessage์˜ˆ์™ธ ๋ฉ”์‹œ์ง€ Enum
๐Ÿ“ฌ responseOrderSummaryResponse์ฃผ๋ฌธ ๊ฒฐ๊ณผ ์š”์•ฝ DTO
OrderResponse์ฃผ๋ฌธ ํ•ญ๋ชฉ DTO
MenuCountResponse์ฃผ๋ฌธ ๋ฉ”๋‰ด ์ˆ˜๋Ÿ‰ DTO
EventDetailResponse์ด๋ฒคํŠธ ์ ์šฉ ์ƒ์„ธ ๋‚ด์—ญ DTO
EventResponse์ด๋ฒคํŠธ ์ ์šฉ ๋‚ด์—ญ DTO
GiftMenuResponse์„ ๋ฌผ ๋ฉ”๋‰ด ์ˆ˜๋Ÿ‰ DTO
๐Ÿ–ฅ๏ธ viewInputView์ž…๋ ฅ ์š”์ฒญ View
OutputView๊ฒฐ๊ณผ ์ถœ๋ ฅ View
๐Ÿ“— Reader์ž…๋ ฅ ์ธํ„ฐํŽ˜์ด์Šค
๐Ÿ“— Writer์ถœ๋ ฅ ์ธํ„ฐํŽ˜์ด์Šค
view
⇛ io
ConsoleReader์ฝ˜์†” ์ž…๋ ฅ
ConsoleWriter์ฝ˜์†” ์ถœ๋ ฅ
+
+ +
+ +### ๐Ÿ—‚๏ธ ๊ตฌํ˜„ ๊ธฐ๋Šฅ ๋ชฉ๋ก +#### โš™๏ธ Controller +- #### OrderController +- [x] ์ž…๋ ฅ ์˜ค๋ฅ˜์‹œ ๋ฐ˜๋ณต ์š”์ฒญ +- #### OrderConverter +- [x] ์ž…๋ ฅ ๋‚ ์งœ ์ˆซ์ž ๊ฒ€์ฆ +- [x] ์ž…๋ ฅ ๋‚ ์งœ ์ˆซ์ž ๋ณ€ํ™˜ +- [x] ์ค‘๋ณต ๋ฉ”๋‰ด ๊ฒ€์ฆ +- [x] ๊ฐœ์ˆ˜ ์ˆซ์ž ๊ฒ€์ฆ +- [x] ๊ฐœ์ˆ˜ ์ˆซ์ž ๋ณ€ํ™˜ +- [x] ๋ฉ”๋‰ด ๊ฐœ์ˆ˜ ํฌ๋งท ๊ฒ€์ฆ +#### ๐Ÿ“ Domail +- #### Day +- [x] ์ž…๋ ฅ ๋‚ ์งœ ๋ฒ”์œ„ ๊ฒ€์ฆ (1 ~ 31) +- [x] ๋‚ ์งœ ์ฐจ์ด ๊ณ„์‚ฐ +- [x] ๋‚ ์งœ ๋Œ€์†Œ๋น„๊ต ๊ธฐ๋Šฅ +- #### Order +- [x] ๋ฉ”๋‰ด ๊ฐœ์ˆ˜ ๋ฒ”์œ„ ๊ฒ€์ฆ (1์ด์ƒ) +- [x] ์ด ์ฃผ๋ฌธ ๋ฉ”๋‰ด ๊ฐœ์ˆ˜ ๊ฒ€์ฆ (20์ดํ•˜) +- [x] ์Œ๋ฃŒ๋งŒ ์ฃผ๋ฌธ ๊ฒ€์ฆ +- [x] ์นดํ…Œ๊ณ ๋ฆฌ๋ณ„ ์ˆ˜๋Ÿ‰ ๊ณ„์‚ฐ +- [x] ํ• ์ธ ์ „ ์ด์ฃผ๋ฌธ ๊ธˆ์•ก ๊ณ„์‚ฐ +- #### Menu +- [x] ๋ฉ”๋‰ด ์œ ๋ฌด ๊ฒ€์ฆ +- #### Week +- [x] ํ‰์ผ/์ฃผ๋ง ๊ตฌ๋ถ„ +- [x] ํ‰์ผ/์ฃผ๋ง ํ• ์ธ ์นดํ…Œ๊ณ ๋ฆฌ ์„ ํƒ +- #### DayOfWeek +- [x] ์š”์ผ ๊ตฌ๋ถ„ +- #### December +- [x] ํฌ๋ฆฌ์Šค๋งˆ์Šค ํ™•์ธ +- [x] 12์›” ๋‚ ์งœ ํ™•์ธ +- #### DiscountEventType +- [x] ์ „์ฒด ํ• ์ธ ์ ์šฉ ๊ฐ€๋Šฅ ํŒ๋ณ„ +- [x] ์ „์ฒด ํ• ์ธ ์ด๋ฒคํŠธ ์ ์šฉํ•˜๊ธฐ +- [x] ์ด์ฃผ๋ฌธ ๊ธˆ์•ก 10,000 ์ด์ƒ ํ™•์ธ +- [x] ํฌ๋ฆฌ์Šค๋งˆ์Šค ๋””๋ฐ์ด ํ• ์ธ ์ ์šฉ ์—ฌ๋ถ€ ํ™•์ธ +- [x] ํฌ๋ฆฌ์Šค๋งˆ์Šค ๋””๋ฐ์ด ํ• ์ธ ๊ธˆ์•ก ๊ณ„์‚ฐ +- [x] ํ‰์ผ/์ฃผ๋ง ํ• ์ธ ์ ์šฉ ์—ฌ๋ถ€ ํ™•์ธ +- [x] ํ‰์ผ ํ• ์ธ ์ ์šฉ ๊ธˆ์•ก ๊ณ„์‚ฐ +- [x] ์ฃผ๋ง ํ• ์ธ ์ ์šฉ ๊ธˆ์•ก ๊ณ„์‚ฐ +- [x] ํŠน๋ณ„ ํ• ์ธ ์ ์šฉ ๊ธˆ์•ก ๊ณ„์‚ฐ +- [x] ํŠน๋ณ„ ํ• ์ธ ์ ์šฉ ์—ฌ๋ถ€ ํ™•์ธ +- #### GiftEventType +- [x] ์ „์ฒด ์„ ๋ฌผ ์ด๋ฒคํŠธ ์ ์šฉ ๊ฐ€๋Šฅ ํŒ๋ณ„ +- [x] ์ „์ฒด ์„ ๋ฌผ ์ด๋ฒคํŠธ ์ ์šฉํ•˜๊ธฐ +- [x] ์ฆ์ • ์ด๋ฒคํŠธ ์ ์šฉ ์—ฌ๋ถ€ ํ™•์ธ +- [x] ์ฆ์ • ์ด๋ฒคํŠธ ์ ์šฉ ๊ธˆ์•ก ๊ณ„์‚ฐ +- #### DiscountRepository +- [x] ํ• ์ธ ์ด๋ฒคํŠธ ๊ฒฐ๊ณผ ์ €์žฅ +- [x] ํ• ์ธ ์ด๋ฒคํŠธ ์ „์ฒด ์ด์ต ๊ณ„์‚ฐ +- #### GiftRepository +- [x] ์„ ๋ฌผ ์ด๋ฒคํŠธ ๊ฒฐ๊ณผ ์ €์žฅ +- [x] ์„ ๋ฌผ ์ด๋ฒคํŠธ ์ „์ฒด ์ด์ต ๊ณ„์‚ฐ +- [x] ์„ ๋ฌผ ๋ฉ”๋‰ด ํ•ญ๋ชฉ, ์ˆ˜๋Ÿ‰ ๊ณ„์‚ฐ +- #### Badge +- [x] 12์›” ์ด๋ฒคํŠธ ๋ฐฐ์ง€ ๋“ฑ๊ธ‰ ๊ณ„์‚ฐ +#### ๐Ÿ•น๏ธ Service +- #### OrderService +- [x] ์ฃผ๋ฌธ ๋‚ด์—ญ, ์ด๋ฒคํŠธ ์ ์šฉ ๋‚ด์—ญ ์š”์•ฝํ•˜๊ธฐ +- #### EventDetailService +- [x] ์ด๋ฒคํŠธ ์ ์šฉ ๋‚ด์—ญ ๊ณ„์‹  +- [x] ์ดํ˜œํƒ ๊ธˆ์•ก ๊ณ„์‚ฐ +- [x] ํ• ์ธ ํ›„ ์˜ˆ์ƒ ๊ฒฐ์ œ ๊ธˆ์•ก ๊ณ„์‚ฐ +- #### DiscountService +- [x] ์ „์ฒด ํ• ์ธ ์ด๋ฒคํŠธ ์ ์šฉํ•˜๊ธฐ +- #### GiftService +- [x] ์ „์ฒด ์„ ๋ฌผ ์ด๋ฒคํŠธ ์ ์šฉํ•˜๊ธฐ +#### ๐Ÿ–ฅ๏ธ View +- #### InputView +- [x] ๋ฐฉ๋ฌธ ๋‚ ์งœ ์ž…๋ ฅ ๋ฐ›๊ธฐ +- [x] ๋ฉ”๋‰ด์™€ ๊ฐœ์ˆ˜ ์ž…๋ ฅ ๋ฐ›๊ธฐ +- #### OutputView +- [x] Hello ํ—ค๋” ์ถœ๋ ฅ +- [x] ์ฆ์ • ๋ฉ”๋‰ด ์ถœ๋ ฅ +- [x] ์ด๋ฒคํŠธ ํ˜œํƒ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์ถœ๋ ฅ +- [x] ์ฃผ๋ฌธ ๋ฉ”๋‰ด ์ถœ๋ ฅ +- [x] ํ• ์ธ ์ „ ์ด์ฃผ๋ฌธ ๊ธˆ์•ก ์ถœ๋ ฅ +- [x] ํ˜œํƒ ๋‚ด์—ญ ์ถœ๋ ฅ +- [x] ์ดํ˜œํƒ ๊ธˆ์•ก ์ถœ๋ ฅ +- [x] ํ• ์ธ ํ›„ ์˜ˆ์ƒ ๊ฒฐ์ œ ๊ธˆ์•ก ์ถœ๋ ฅ +- [x] 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..6faa6ba 100644 --- a/src/main/java/christmas/Application.java +++ b/src/main/java/christmas/Application.java @@ -1,7 +1,10 @@ package christmas; +import static christmas.config.PlannerConfig.orderController; + public class Application { + public static void main(String[] args) { - // TODO: ํ”„๋กœ๊ทธ๋žจ ๊ตฌํ˜„ + orderController().run(); } } diff --git a/src/main/java/christmas/config/PlannerConfig.java b/src/main/java/christmas/config/PlannerConfig.java new file mode 100644 index 0000000..5b86512 --- /dev/null +++ b/src/main/java/christmas/config/PlannerConfig.java @@ -0,0 +1,47 @@ +package christmas.config; + +import christmas.controller.OrderController; +import christmas.domain.event.discount.DiscountRepository; +import christmas.domain.event.gift.GiftRepository; +import christmas.service.event.DiscountService; +import christmas.service.event.EventDetailService; +import christmas.service.event.GiftService; +import christmas.service.order.OrderService; +import christmas.view.InputView; +import christmas.view.OutputView; +import christmas.view.io.ConsoleReader; +import christmas.view.io.ConsoleWriter; + +public class PlannerConfig { + + private PlannerConfig() { + } + + public static OrderController orderController() { + return new OrderController(inputView(), outputView(), orderService()); + } + + private static InputView inputView() { + return new InputView(new ConsoleReader(), new ConsoleWriter()); + } + + private static OutputView outputView() { + return new OutputView(new ConsoleWriter()); + } + + private static OrderService orderService() { + return new OrderService(eventDetailService()); + } + + private static EventDetailService eventDetailService() { + return new EventDetailService(discountService(), giftService()); + } + + private static DiscountService discountService() { + return new DiscountService(new DiscountRepository()); + } + + private static GiftService giftService() { + return new GiftService(new GiftRepository()); + } +} diff --git a/src/main/java/christmas/controller/OrderController.java b/src/main/java/christmas/controller/OrderController.java new file mode 100644 index 0000000..105bca7 --- /dev/null +++ b/src/main/java/christmas/controller/OrderController.java @@ -0,0 +1,55 @@ +package christmas.controller; + +import christmas.controller.converter.OrderConverter; +import christmas.domain.order.Day; +import christmas.domain.order.Order; +import christmas.response.OrderSummaryResponse; +import christmas.service.order.OrderService; +import christmas.view.InputView; +import christmas.view.OutputView; +import java.util.function.Supplier; + +public class OrderController { + + private final InputView inputView; + private final OutputView outputView; + private final OrderService orderService; + + public OrderController(final InputView inputView, final OutputView outputView, final OrderService orderService) { + this.inputView = inputView; + this.outputView = outputView; + this.orderService = orderService; + } + + public void run() { + outputView.printHello(); + + final Day orderDay = createOrderDay(); + final Order order = createOrder(orderDay); + + final OrderSummaryResponse orderSummaryResponse = orderService.createOrderSummary(order); + + outputView.printOrderSummary(orderSummaryResponse); + + inputView.close(); + } + + private Day createOrderDay() { + return getByRoof(() -> OrderConverter.convertToDay(inputView.readDate())); + } + + private Order createOrder(final Day day) { + return getByRoof(() -> Order.of(day, OrderConverter.convertToMenu(inputView.readMenuAndCount()))); + } + + private T getByRoof(final Supplier method) { + while (true) { + try { + return method.get(); + } catch (IllegalArgumentException illegalArgumentException) { + outputView.printError(illegalArgumentException.getMessage()); + } + } + } + +} diff --git a/src/main/java/christmas/controller/converter/OrderConverter.java b/src/main/java/christmas/controller/converter/OrderConverter.java new file mode 100644 index 0000000..b78c046 --- /dev/null +++ b/src/main/java/christmas/controller/converter/OrderConverter.java @@ -0,0 +1,81 @@ +package christmas.controller.converter; + +import static christmas.exception.ErrorMessage.INVALID_DAY; +import static christmas.exception.ErrorMessage.INVALID_ORDER; +import static java.util.regex.Pattern.compile; +import static java.util.stream.Collectors.toMap; + +import christmas.domain.order.Day; +import christmas.domain.order.constant.Menu; +import christmas.exception.ErrorMessage; +import christmas.exception.OrderException; +import java.util.Arrays; +import java.util.EnumMap; +import java.util.function.BinaryOperator; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public final class OrderConverter { + + private static final Pattern MENU_COUNT_PATTERN = compile("^[๊ฐ€-ํžฃA-Za-z]+-\\d+(?:,[๊ฐ€-ํžฃA-Za-z]+-\\d+)*$"); + private static final String MENU_SPLIT_SIGNAL = ","; + private static final String COUNT_SPLIT_SIGNAL = "-"; + private static final String INVALID_START = "0"; + private static final int TITLE_INDEX = 0; + private static final int COUNT_INDEX = 1; + + private OrderConverter() { + } + + public static Day convertToDay(final String input) { + return Day.from(convertToInt(input, INVALID_DAY)); + } + + private static int convertToInt(final String input, final ErrorMessage errorMessage) { + validateStartZero(input, errorMessage); + + try { + return Integer.parseInt(input); + } catch (NumberFormatException numberFormatException) { + throw OrderException.from(errorMessage); + } + } + + private static void validateStartZero(final String input, final ErrorMessage errorMessage) { + if (input.startsWith(INVALID_START)) { + throw OrderException.from(errorMessage); + } + } + + public static EnumMap convertToMenu(final String input) { + validateMenuFormat(input); + + return createMenuEnumMap(input); + } + + private static void validateMenuFormat(final String input) { + final Matcher matcher = MENU_COUNT_PATTERN.matcher(input); + + if (matcher.matches()) { + return; + } + + throw OrderException.from(INVALID_ORDER); + } + + private static EnumMap createMenuEnumMap(final String input) { + + return Arrays.stream(input.split(MENU_SPLIT_SIGNAL)) + .map(menuCount -> menuCount.split(COUNT_SPLIT_SIGNAL)) + .collect(toMap(menuCount -> Menu.from(menuCount[TITLE_INDEX]), + menuCount -> convertToInt(menuCount[COUNT_INDEX], INVALID_ORDER), + throwingDuplicateMenuException(), + () -> new EnumMap<>(Menu.class))); + } + + private static BinaryOperator throwingDuplicateMenuException() { + return (menu, other) -> { + throw OrderException.from(ErrorMessage.INVALID_ORDER); + }; + } +} diff --git a/src/main/java/christmas/domain/event/Badge.java b/src/main/java/christmas/domain/event/Badge.java new file mode 100644 index 0000000..1a9d669 --- /dev/null +++ b/src/main/java/christmas/domain/event/Badge.java @@ -0,0 +1,39 @@ +package christmas.domain.event; + +import java.util.Arrays; +import java.util.Comparator; + +public enum Badge { + SANTA("์‚ฐํƒ€", 20_000), + TREE("ํŠธ๋ฆฌ", 10_000), + STAR("๋ณ„", 5_000), + NONE("", 0); + + private final String title; + private final int minPrice; + + Badge(final String title, final int minPrice) { + this.title = title; + this.minPrice = minPrice; + } + + public static Badge from(final int totalDiscount) { + return Arrays.stream(values()) + .sorted(Comparator.comparing(Badge::getMinPrice).reversed()) + .filter(badge -> badge.isSatisfiedCondition(totalDiscount)) + .findFirst() + .orElse(NONE); + } + + private boolean isSatisfiedCondition(final int totalDiscount) { + return minPrice <= totalDiscount; + } + + public String getTitle() { + return title; + } + + public int getMinPrice() { + return minPrice; + } +} diff --git a/src/main/java/christmas/domain/event/Event.java b/src/main/java/christmas/domain/event/Event.java new file mode 100644 index 0000000..e5fd52c --- /dev/null +++ b/src/main/java/christmas/domain/event/Event.java @@ -0,0 +1,25 @@ +package christmas.domain.event; + +import christmas.domain.order.Order; +import java.util.Arrays; +import java.util.EnumMap; +import java.util.stream.Collectors; + + +public interface Event { + + int calculateBenefits(final Order order); + + String getTitle(); + + static & Event> EnumMap applyAll(final Class eventType, final Order order) { + + return Arrays.stream(eventType.getEnumConstants()) + .collect(Collectors.toMap( + event -> event, + event -> event.calculateBenefits(order), + Integer::sum, + () -> new EnumMap<>(eventType) + )); + } +} diff --git a/src/main/java/christmas/domain/event/EventRepository.java b/src/main/java/christmas/domain/event/EventRepository.java new file mode 100644 index 0000000..3686a06 --- /dev/null +++ b/src/main/java/christmas/domain/event/EventRepository.java @@ -0,0 +1,35 @@ +package christmas.domain.event; + +import static java.util.stream.Collectors.toMap; + +import java.util.EnumMap; +import java.util.Map; +import java.util.Map.Entry; + + +public abstract class EventRepository & Event> { + + private static final int ZERO = 0; + protected EnumMap eventAmounts; + + public void init(final EnumMap eventAmounts) { + this.eventAmounts = eventAmounts; + } + + public int calculateTotal() { + return eventAmounts.values().stream().mapToInt(Integer::intValue).sum(); + } + + public Map getActiveResult() { + return eventAmounts.entrySet().stream().filter(this::isActive) + .collect(toMap(this::getEventTitle, Entry::getValue)); + } + + protected boolean isActive(final Entry entry) { + return entry.getValue() != ZERO; + } + + private String getEventTitle(final Entry entry) { + return entry.getKey().getTitle(); + } +} diff --git a/src/main/java/christmas/domain/event/discount/DiscountEventType.java b/src/main/java/christmas/domain/event/discount/DiscountEventType.java new file mode 100644 index 0000000..747f646 --- /dev/null +++ b/src/main/java/christmas/domain/event/discount/DiscountEventType.java @@ -0,0 +1,59 @@ +package christmas.domain.event.discount; + +import christmas.domain.event.Event; +import christmas.domain.order.Order; +import java.util.function.Function; +import java.util.function.Predicate; + +public enum DiscountEventType implements Event { + + CHRISTMAS_D_DAY_DISCOUNT("ํฌ๋ฆฌ์Šค๋งˆ์Šค ๋””๋ฐ์ด ํ• ์ธ", Order::isAfterChristmas, + order -> order.countDayAfterFirstDay() * Amount.CHRISTMAS_ONCE.value + Amount.CHRISTMAS_OFFSET.value), + WEEKDAY_DISCOUNT("ํ‰์ผ ํ• ์ธ", Order::isWeekend, + order -> order.countWeekEventMenu() * Amount.WEEKDAY_ONCE.value), + WEEKEND_DISCOUNT("์ฃผ๋ง ํ• ์ธ", Order::isWeekday, + order -> order.countWeekEventMenu() * Amount.WEEKEND_ONCE.value), + SPECIAL_DISCOUNT("ํŠน๋ณ„ ํ• ์ธ", order -> order.isNotSunDay() && order.isNotChristmasDay(), + order -> Amount.SPECIAL_ONCE.value); + + private final String title; + private final Predicate unavailable; + private final Function calculate; + + DiscountEventType(final String title, final Predicate unavailable, + final Function calculate) { + this.title = title; + this.unavailable = unavailable; + this.calculate = calculate; + } + + @Override + public int calculateBenefits(final Order order) { + if (order.isTotalPriceUnder(Amount.BASE_MIN.value) || unavailable.test(order)) { + return Amount.ZERO.value; + } + + return calculate.apply(order); + } + + @Override + public String getTitle() { + return title; + } + + private enum Amount { + CHRISTMAS_ONCE(100), + CHRISTMAS_OFFSET(1_000), + WEEKDAY_ONCE(2_023), + WEEKEND_ONCE(2_023), + SPECIAL_ONCE(1_000), + BASE_MIN(10_000), + ZERO(0); + + private final int value; + + Amount(final int value) { + this.value = value; + } + } +} diff --git a/src/main/java/christmas/domain/event/discount/DiscountRepository.java b/src/main/java/christmas/domain/event/discount/DiscountRepository.java new file mode 100644 index 0000000..fefb20b --- /dev/null +++ b/src/main/java/christmas/domain/event/discount/DiscountRepository.java @@ -0,0 +1,6 @@ +package christmas.domain.event.discount; + +import christmas.domain.event.EventRepository; + +public class DiscountRepository extends EventRepository { +} diff --git a/src/main/java/christmas/domain/event/gift/GiftEventType.java b/src/main/java/christmas/domain/event/gift/GiftEventType.java new file mode 100644 index 0000000..cb78fae --- /dev/null +++ b/src/main/java/christmas/domain/event/gift/GiftEventType.java @@ -0,0 +1,61 @@ +package christmas.domain.event.gift; + +import christmas.domain.event.Event; +import christmas.domain.order.Order; +import christmas.domain.order.constant.Menu; +import java.util.function.Predicate; + +public enum GiftEventType implements Event { + PRESENTATION("์ฆ์ • ์ด๋ฒคํŠธ", order -> order.isTotalPriceUnder(Amount.PRESENTATION_MIN.value), + Menu.CHAMPAGNE, 1); + + private final String title; + private final Predicate unavailable; + private final Menu menu; + private final int count; + + GiftEventType(final String title, final Predicate unavailable, final Menu menu, final int count) { + this.title = title; + this.unavailable = unavailable; + this.menu = menu; + this.count = count; + } + + @Override + public int calculateBenefits(final Order order) { + if (unavailable.test(order)) { + return Amount.ZERO.value; + } + + return calculateMenuTotalPrice(); + } + + private int calculateMenuTotalPrice() { + return menu.getPrice() * count; + } + + @Override + public String getTitle() { + return title; + } + + public String getMenuTitle() { + return menu.getTitle(); + } + + public int getCount() { + return count; + } + + private enum Amount { + PRESENTATION_MIN(120_000), + ZERO(0); + + private final int value; + + Amount(final int value) { + this.value = value; + } + } + +} diff --git a/src/main/java/christmas/domain/event/gift/GiftRepository.java b/src/main/java/christmas/domain/event/gift/GiftRepository.java new file mode 100644 index 0000000..81c9bf3 --- /dev/null +++ b/src/main/java/christmas/domain/event/gift/GiftRepository.java @@ -0,0 +1,25 @@ +package christmas.domain.event.gift; + +import static java.util.stream.Collectors.toMap; + +import christmas.domain.event.EventRepository; +import java.util.Map; +import java.util.Map.Entry; + +public class GiftRepository extends EventRepository { + + public Map getActiveMenuCounts() { + return eventAmounts.entrySet().stream() + .filter(this::isActive) + .collect(toMap(this::getMenuTitle, this::getMenuCount)); + } + + private String getMenuTitle(final Entry entry) { + return entry.getKey().getMenuTitle(); + } + + private Integer getMenuCount(final Entry entry) { + return entry.getKey().getCount(); + } + +} diff --git a/src/main/java/christmas/domain/order/Day.java b/src/main/java/christmas/domain/order/Day.java new file mode 100644 index 0000000..10b987e --- /dev/null +++ b/src/main/java/christmas/domain/order/Day.java @@ -0,0 +1,88 @@ +package christmas.domain.order; + +import static christmas.domain.order.constant.December.START_DAY; +import static christmas.exception.ErrorMessage.INVALID_DAY; + +import christmas.domain.order.constant.DayOfWeek; +import christmas.domain.order.constant.December; +import christmas.domain.order.constant.Week; +import christmas.domain.order.constant.Category; +import christmas.exception.OrderException; +import java.util.Objects; + +public class Day { + + private final int day; + + private Day(final int day) { + this.day = day; + } + + public static Day from(final int day) { + validate(day); + return new Day(day); + } + + private static void validate(final int day) { + if (isOutOfRange(day)) { + throw OrderException.from(INVALID_DAY); + } + } + + private static boolean isOutOfRange(final int day) { + return !December.isInRange(day); + } + + public int gap(final Day other) { + return Math.abs(other.day - this.day); + } + + public int gapFromStartDay() { + return this.gap(from(START_DAY.getDay())); + } + + public Integer mod(final int weekCount) { + return day % weekCount; + } + + public Category getTodayEventCategory() { + return Week.from(DayOfWeek.from(this)).getEventCategory(); + } + + public boolean isOverThan(final Day other) { + return this.day > other.day; + } + + public boolean isChristmasDay() { + return December.isChristMas(day); + } + + public boolean isSunDay() { + return DayOfWeek.from(this).isSunDay(); + } + + public boolean isWeekend() { + return DayOfWeek.from(this).isWeekend(); + } + + public int getValue() { + return day; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Day day1 = (Day) o; + return day == day1.day; + } + + @Override + public int hashCode() { + return Objects.hash(day); + } +} diff --git a/src/main/java/christmas/domain/order/Order.java b/src/main/java/christmas/domain/order/Order.java new file mode 100644 index 0000000..fe5b963 --- /dev/null +++ b/src/main/java/christmas/domain/order/Order.java @@ -0,0 +1,128 @@ +package christmas.domain.order; + +import static christmas.domain.order.constant.December.CHRISTMAS_DAY; +import static christmas.domain.order.constant.December.START_DAY; + +import christmas.domain.order.constant.Category; +import christmas.domain.order.constant.Menu; +import christmas.exception.OrderException; +import christmas.exception.ErrorMessage; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +public class Order { + + private static final int TOTAL_COUNT_MAX = 20; + private static final int ONCE_COUNT_MIN = 1; + private final Day day; + private final Map menuCount; + + private Order(final Day day, final Map menuCount) { + this.day = day; + this.menuCount = menuCount; + } + + public static Order of(final Day day, final Map menuCount) { + validate(menuCount); + + return new Order(day, menuCount); + } + + private static void validate(final Map menuCount) { + validateCountRange(menuCount.values()); + validateTotalCountMax(menuCount.values()); + validateOnlyBeverage(menuCount.keySet()); + } + + private static void validateCountRange(final Collection counts) { + if (isUnderThanMin(counts)) { + throw OrderException.from(ErrorMessage.INVALID_ORDER); + } + + } + + private static boolean isUnderThanMin(final Collection counts) { + return counts.stream() + .anyMatch(count -> count < ONCE_COUNT_MIN); + } + + + private static void validateTotalCountMax(final Collection counts) { + if (calculateTotalCount(counts) > TOTAL_COUNT_MAX) { + throw OrderException.from(ErrorMessage.INVALID_ORDER); + } + } + + private static int calculateTotalCount(final Collection counts) { + return counts.stream() + .mapToInt(Integer::intValue) + .sum(); + } + + private static void validateOnlyBeverage(final Set menus) { + if (isOnlyBeverage(menus)) { + throw OrderException.from(ErrorMessage.INVALID_ORDER); + } + } + + private static boolean isOnlyBeverage(final Set menus) { + return menus.stream() + .map(Menu::getCategory) + .distinct() + .noneMatch(Category::isNotBeverage); + } + + public int calculateTotalPrice() { + return menuCount.entrySet().stream() + .mapToInt(entry -> entry.getKey().getPrice() * entry.getValue()) + .sum(); + } + + public int countDayAfterFirstDay() { + return this.day.gap(Day.from(START_DAY.getDay())); + } + + public int countWeekEventMenu() { + final Category category = day.getTodayEventCategory(); + + return menuCount.entrySet().stream() + .filter(entry -> entry.getKey().isCategory(category)) + .mapToInt(Entry::getValue) + .sum(); + } + + public boolean isTotalPriceUnder(final int price) { + return calculateTotalPrice() < price; + } + + public boolean isAfterChristmas() { + return day.isOverThan(Day.from(CHRISTMAS_DAY.getDay())); + } + + public boolean isWeekend() { + return day.isWeekend(); + } + + public boolean isWeekday() { + return !day.isWeekend(); + } + + public boolean isNotSunDay() { + return !day.isSunDay(); + } + + public boolean isNotChristmasDay() { + return !day.isChristmasDay(); + } + + public Map getMenuCount() { + return Collections.unmodifiableMap(menuCount); + } + + public int getDayInt() { + return day.getValue(); + } +} diff --git a/src/main/java/christmas/domain/order/constant/Category.java b/src/main/java/christmas/domain/order/constant/Category.java new file mode 100644 index 0000000..b4774c6 --- /dev/null +++ b/src/main/java/christmas/domain/order/constant/Category.java @@ -0,0 +1,9 @@ +package christmas.domain.order.constant; + +public enum Category { + APPETIZER, MAIN, DESSERT, BEVERAGE; + + public boolean isNotBeverage() { + return !this.equals(BEVERAGE); + } +} diff --git a/src/main/java/christmas/domain/order/constant/DayOfWeek.java b/src/main/java/christmas/domain/order/constant/DayOfWeek.java new file mode 100644 index 0000000..ac82c01 --- /dev/null +++ b/src/main/java/christmas/domain/order/constant/DayOfWeek.java @@ -0,0 +1,55 @@ +package christmas.domain.order.constant; + +import static christmas.domain.order.constant.December.START_DAY; +import static christmas.domain.order.constant.Week.WEEKDAY; +import static christmas.domain.order.constant.Week.WEEKEND; + +import christmas.domain.order.Day; +import java.util.Arrays; + +public enum DayOfWeek { + MONDAY(0, WEEKDAY), + TUESDAY(1, WEEKDAY), + WEDNESDAY(2, WEEKDAY), + THURSDAY(3, WEEKDAY), + FRIDAY(4, WEEKEND), + SATURDAY(5, WEEKEND), + SUNDAY(6, WEEKDAY); + + private static final int TOTAL_COUNT = (int) Arrays.stream(values()).count(); + private final int sequence; + private final Week week; + + DayOfWeek(final int sequence, final Week week) { + this.sequence = sequence; + this.week = week; + } + + public static DayOfWeek from(final Day day) { + return START_DAY.getDayOfWeek().getAfterDay(day); + } + + private DayOfWeek getAfterDay(final Day day) { + final int mod = day.gapFromStartDay() % TOTAL_COUNT; + return findDayOfWeekByMod(mod); + } + + private DayOfWeek findDayOfWeekByMod(final int mod) { + return Arrays.stream(values()) + .filter(dayOfWeek -> this.getGapMod(dayOfWeek.sequence) == mod) + .findFirst() + .orElseThrow(); + } + + private int getGapMod(final int mod) { + return (mod - this.sequence + TOTAL_COUNT) % TOTAL_COUNT; + } + + public boolean isSunDay() { + return this.equals(SUNDAY); + } + + public boolean isWeekend() { + return this.week.equals(WEEKEND); + } +} diff --git a/src/main/java/christmas/domain/order/constant/December.java b/src/main/java/christmas/domain/order/constant/December.java new file mode 100644 index 0000000..59c2393 --- /dev/null +++ b/src/main/java/christmas/domain/order/constant/December.java @@ -0,0 +1,35 @@ +package christmas.domain.order.constant; + +import static christmas.domain.order.constant.DayOfWeek.FRIDAY; +import static christmas.domain.order.constant.DayOfWeek.MONDAY; +import static christmas.domain.order.constant.DayOfWeek.SUNDAY; + +public enum December { + START_DAY(1, FRIDAY), + CHRISTMAS_DAY(25, MONDAY), + END_DAY(31, SUNDAY); + + private final int day; + private final DayOfWeek dayOfWeek; + + December(final int day, final DayOfWeek dayOfWeek) { + this.day = day; + this.dayOfWeek = dayOfWeek; + } + + public static boolean isInRange(final int day) { + return day >= START_DAY.getDay() && day <= END_DAY.getDay(); + } + + public static boolean isChristMas(final int day) { + return day == CHRISTMAS_DAY.getDay(); + } + + public int getDay() { + return day; + } + + public DayOfWeek getDayOfWeek() { + return dayOfWeek; + } +} diff --git a/src/main/java/christmas/domain/order/constant/Menu.java b/src/main/java/christmas/domain/order/constant/Menu.java new file mode 100644 index 0000000..f27adc7 --- /dev/null +++ b/src/main/java/christmas/domain/order/constant/Menu.java @@ -0,0 +1,63 @@ +package christmas.domain.order.constant; + + +import static christmas.domain.order.constant.Category.APPETIZER; +import static christmas.domain.order.constant.Category.BEVERAGE; +import static christmas.domain.order.constant.Category.DESSERT; +import static christmas.domain.order.constant.Category.MAIN; +import static christmas.exception.ErrorMessage.INVALID_ORDER; +import static java.util.stream.Collectors.toMap; + +import christmas.exception.OrderException; +import java.util.Arrays; +import java.util.Map; + +public enum Menu { + + BUTTON_MUSHROOM_SOUP("์–‘์†ก์ด์ˆ˜ํ”„", APPETIZER, 6_000), + TAPAS("ํƒ€ํŒŒ์Šค", APPETIZER, 5_500), + CAESAR_SALAD("์‹œ์ €์ƒ๋Ÿฌ๋“œ", APPETIZER, 8_000), + T_BONE_STEAK("ํ‹ฐ๋ณธ์Šคํ…Œ์ดํฌ", MAIN, 55_000), + BARBECUE_RIBS("๋ฐ”๋น„ํ๋ฆฝ", MAIN, 54_000), + SEAFOOD_PASTA("ํ•ด์‚ฐ๋ฌผํŒŒ์Šคํƒ€", MAIN, 35_000), + CHRISTMAS_PASTA("ํฌ๋ฆฌ์Šค๋งˆ์ŠคํŒŒ์Šคํƒ€", MAIN, 25_000), + CHOCOLATE_CAKE("์ดˆ์ฝ”์ผ€์ดํฌ", DESSERT, 15_000), + ICE_CREAM("์•„์ด์Šคํฌ๋ฆผ", DESSERT, 5_000), + ZERO_COLA("์ œ๋กœ์ฝœ๋ผ", BEVERAGE, 3_000), + RED_WINE("๋ ˆ๋“œ์™€์ธ", BEVERAGE, 60_000), + CHAMPAGNE("์ƒดํŽ˜์ธ", BEVERAGE, 25_000); + + private static final Map menus = Arrays.stream(values()) + .collect(toMap(menu -> menu.title, menu -> menu)); + private final String title; + private final Category category; + private final int price; + + Menu(final String title, final Category category, final int price) { + this.title = title; + this.category = category; + this.price = price; + } + + public static Menu from(final String title) { + return menus.computeIfAbsent(title, key -> { + throw OrderException.from(INVALID_ORDER); + }); + } + + public boolean isCategory(final Category category) { + return this.category.equals(category); + } + + public String getTitle() { + return title; + } + + public Category getCategory() { + return category; + } + + public int getPrice() { + return price; + } +} diff --git a/src/main/java/christmas/domain/order/constant/Week.java b/src/main/java/christmas/domain/order/constant/Week.java new file mode 100644 index 0000000..76fca89 --- /dev/null +++ b/src/main/java/christmas/domain/order/constant/Week.java @@ -0,0 +1,32 @@ +package christmas.domain.order.constant; + +import static christmas.domain.order.constant.Category.DESSERT; +import static christmas.domain.order.constant.Category.MAIN; + +public enum Week { + WEEKDAY(DESSERT), + WEEKEND(MAIN); + + private final Category eventCategory; + + + Week(final Category category) { + this.eventCategory = category; + } + + public static Week from(final DayOfWeek dayOfWeek) { + if (dayOfWeek.isWeekend()) { + return WEEKEND; + } + + return WEEKDAY; + } + + public boolean isWeekend() { + return this.equals(WEEKEND); + } + + public Category getEventCategory() { + return eventCategory; + } +} diff --git a/src/main/java/christmas/exception/ErrorMessage.java b/src/main/java/christmas/exception/ErrorMessage.java new file mode 100644 index 0000000..d7281e8 --- /dev/null +++ b/src/main/java/christmas/exception/ErrorMessage.java @@ -0,0 +1,18 @@ +package christmas.exception; + +public enum ErrorMessage { + + INVALID_DAY("์œ ํšจํ•˜์ง€ ์•Š์€ ๋‚ ์งœ์ž…๋‹ˆ๋‹ค. ๋‹ค์‹œ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”."), + INVALID_ORDER("์œ ํšจํ•˜์ง€ ์•Š์€ ์ฃผ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋‹ค์‹œ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”."); + + private static final String PREFIX = "[ERROR] "; + private final String message; + + ErrorMessage(final String message) { + this.message = message; + } + + public String getMessage() { + return PREFIX + message; + } +} diff --git a/src/main/java/christmas/exception/OrderException.java b/src/main/java/christmas/exception/OrderException.java new file mode 100644 index 0000000..eef374c --- /dev/null +++ b/src/main/java/christmas/exception/OrderException.java @@ -0,0 +1,14 @@ +package christmas.exception; + +public class OrderException extends IllegalArgumentException { + + private OrderException(final ErrorMessage errorMessage) { + super(errorMessage.getMessage()); + } + + public static OrderException from(final ErrorMessage errorMessage) { + return new OrderException(errorMessage); + } + + +} diff --git a/src/main/java/christmas/response/EventDetailResponse.java b/src/main/java/christmas/response/EventDetailResponse.java new file mode 100644 index 0000000..ef5ac7b --- /dev/null +++ b/src/main/java/christmas/response/EventDetailResponse.java @@ -0,0 +1,62 @@ +package christmas.response; + +import java.util.List; + +public record EventDetailResponse( + int priceBeforeEvent, + List giftMenuResponses, + List activeEvents, + int totalBenefitsAmount, + int priceAfterEvent, + String badge +) { + + public static class EventDetailResponseBuilder { + + int priceBeforeEvent; + List giftMenuResponses; + List activeEvents; + int totalBenefitsAmount; + int priceAfterEvent; + String badge; + + public static EventDetailResponseBuilder builder() { + return new EventDetailResponseBuilder(); + } + + public EventDetailResponseBuilder priceBeforeEvent(final int priceBeforeEvent) { + this.priceBeforeEvent = priceBeforeEvent; + return this; + } + + public EventDetailResponseBuilder giftMenuResponses(final List giftMenuResponses) { + this.giftMenuResponses = giftMenuResponses; + return this; + } + + public EventDetailResponseBuilder activeEvents(final List activeEvents) { + this.activeEvents = activeEvents; + return this; + } + + public EventDetailResponseBuilder totalBenefitsAmount(final int totalBenefitsAmount) { + this.totalBenefitsAmount = totalBenefitsAmount; + return this; + } + + public EventDetailResponseBuilder priceAfterEvent(final int priceAfterEvent) { + this.priceAfterEvent = priceAfterEvent; + return this; + } + + public EventDetailResponseBuilder badge(final String badge) { + this.badge = badge; + return this; + } + + public EventDetailResponse build() { + return new EventDetailResponse(priceBeforeEvent, giftMenuResponses, activeEvents, + totalBenefitsAmount, priceAfterEvent, badge); + } + } +} diff --git a/src/main/java/christmas/response/EventResponse.java b/src/main/java/christmas/response/EventResponse.java new file mode 100644 index 0000000..63e99df --- /dev/null +++ b/src/main/java/christmas/response/EventResponse.java @@ -0,0 +1,8 @@ +package christmas.response; + +public record EventResponse(String title, int amount) { + + public static EventResponse of(final String title, final int amount) { + return new EventResponse(title, amount); + } +} diff --git a/src/main/java/christmas/response/GiftMenuResponse.java b/src/main/java/christmas/response/GiftMenuResponse.java new file mode 100644 index 0000000..02e2457 --- /dev/null +++ b/src/main/java/christmas/response/GiftMenuResponse.java @@ -0,0 +1,8 @@ +package christmas.response; + +public record GiftMenuResponse(String title, int count) { + + public static GiftMenuResponse of(final String title, final int count) { + return new GiftMenuResponse(title, count); + } +} diff --git a/src/main/java/christmas/response/MenuCountResponse.java b/src/main/java/christmas/response/MenuCountResponse.java new file mode 100644 index 0000000..cdcbadb --- /dev/null +++ b/src/main/java/christmas/response/MenuCountResponse.java @@ -0,0 +1,9 @@ +package christmas.response; + +public record MenuCountResponse(String title, int count) { + + public static MenuCountResponse of(final String title, final int count) { + return new MenuCountResponse(title, count); + } + +} diff --git a/src/main/java/christmas/response/OrderResponse.java b/src/main/java/christmas/response/OrderResponse.java new file mode 100644 index 0000000..931a291 --- /dev/null +++ b/src/main/java/christmas/response/OrderResponse.java @@ -0,0 +1,11 @@ +package christmas.response; + +import java.util.List; + +public record OrderResponse(int day, List menuCount) { + + public static OrderResponse of(final int day, final List menuCount) { + return new OrderResponse(day, menuCount); + } + +} diff --git a/src/main/java/christmas/response/OrderSummaryResponse.java b/src/main/java/christmas/response/OrderSummaryResponse.java new file mode 100644 index 0000000..5bb7842 --- /dev/null +++ b/src/main/java/christmas/response/OrderSummaryResponse.java @@ -0,0 +1,9 @@ +package christmas.response; + +public record OrderSummaryResponse(OrderResponse orderResponse, EventDetailResponse eventDetailResponse) { + + public static OrderSummaryResponse of(final OrderResponse orderResponse, + final EventDetailResponse eventDetailResponse) { + return new OrderSummaryResponse(orderResponse, eventDetailResponse); + } +} diff --git a/src/main/java/christmas/service/event/DiscountService.java b/src/main/java/christmas/service/event/DiscountService.java new file mode 100644 index 0000000..08eb918 --- /dev/null +++ b/src/main/java/christmas/service/event/DiscountService.java @@ -0,0 +1,11 @@ +package christmas.service.event; + +import christmas.domain.event.discount.DiscountEventType; +import christmas.domain.event.discount.DiscountRepository; + +public class DiscountService extends EventService { + + public DiscountService(final DiscountRepository discountRepository) { + super(discountRepository); + } +} diff --git a/src/main/java/christmas/service/event/EventDetailService.java b/src/main/java/christmas/service/event/EventDetailService.java new file mode 100644 index 0000000..5ea7f69 --- /dev/null +++ b/src/main/java/christmas/service/event/EventDetailService.java @@ -0,0 +1,68 @@ +package christmas.service.event; + +import christmas.domain.event.Badge; +import christmas.domain.event.discount.DiscountEventType; +import christmas.domain.event.gift.GiftEventType; +import christmas.domain.order.Order; +import christmas.response.EventDetailResponse; +import christmas.response.EventDetailResponse.EventDetailResponseBuilder; +import christmas.response.EventResponse; +import java.util.List; +import java.util.stream.Stream; + +public class EventDetailService { + + private final DiscountService discountService; + private final GiftService giftService; + + public EventDetailService(final DiscountService discountService, final GiftService giftService) { + this.discountService = discountService; + this.giftService = giftService; + } + + public void applyEvent(final Order order) { + discountService.applyEventAll(DiscountEventType.class, order); + giftService.applyEventAll(GiftEventType.class, order); + } + + public EventDetailResponse getEventDetail(final int priceBeforeDiscount) { + final int totalDiscountBenefits = discountService.calculateTotalBenefits(); + final int totalGiftBenefits = giftService.calculateTotalBenefits(); + + final int totalBenefitsAmount = totalDiscountBenefits + totalGiftBenefits; + + return buildEventDetailResponse(priceBeforeDiscount, totalBenefitsAmount, totalDiscountBenefits); + } + + private EventDetailResponse buildEventDetailResponse( + final int priceBeforeDiscount, + final int totalBenefitsAmount, + final int totalDiscountBenefits + ) { + + return EventDetailResponseBuilder.builder() + .priceBeforeEvent(priceBeforeDiscount) + .giftMenuResponses(giftService.getGiftMenuResponse()) + .activeEvents(getAllActiveEvent()) + .totalBenefitsAmount(totalBenefitsAmount) + .priceAfterEvent(calculatePriceAfterDiscount(priceBeforeDiscount, totalDiscountBenefits)) + .badge(getBadgeTitle(totalBenefitsAmount)) + .build(); + } + + private List getAllActiveEvent() { + return Stream.concat(discountService.getActiveEventResult().stream(), + giftService.getActiveEventResult().stream()) + .toList(); + } + + private int calculatePriceAfterDiscount(final int priceBeforeDiscount, final int totalDiscountBenefits) { + return priceBeforeDiscount - totalDiscountBenefits; + } + + private String getBadgeTitle(final int totalBenefitsAmount) { + return Badge.from(totalBenefitsAmount).getTitle(); + } + + +} diff --git a/src/main/java/christmas/service/event/EventService.java b/src/main/java/christmas/service/event/EventService.java new file mode 100644 index 0000000..9baf8ad --- /dev/null +++ b/src/main/java/christmas/service/event/EventService.java @@ -0,0 +1,37 @@ +package christmas.service.event; + +import christmas.domain.event.Event; +import christmas.domain.event.EventRepository; +import christmas.domain.order.Order; +import christmas.response.EventResponse; +import java.util.EnumMap; +import java.util.List; +import java.util.Map.Entry; + +public abstract class EventService & Event> { + + protected final EventRepository eventRepository; + + public EventService(final EventRepository eventRepository) { + this.eventRepository = eventRepository; + } + + public void applyEventAll(final Class eventType, final Order order) { + final EnumMap giftAmounts = Event.applyAll(eventType, order); + eventRepository.init(giftAmounts); + } + + public List getActiveEventResult() { + return eventRepository.getActiveResult().entrySet().stream() + .map(this::createEventResponse) + .toList(); + } + + private EventResponse createEventResponse(final Entry entry) { + return EventResponse.of(entry.getKey(), entry.getValue()); + } + + public int calculateTotalBenefits() { + return eventRepository.calculateTotal(); + } +} diff --git a/src/main/java/christmas/service/event/GiftService.java b/src/main/java/christmas/service/event/GiftService.java new file mode 100644 index 0000000..d6a6f1b --- /dev/null +++ b/src/main/java/christmas/service/event/GiftService.java @@ -0,0 +1,27 @@ +package christmas.service.event; + +import christmas.domain.event.gift.GiftEventType; +import christmas.domain.event.gift.GiftRepository; +import christmas.response.GiftMenuResponse; +import java.util.List; +import java.util.Map.Entry; + +public class GiftService extends EventService { + + private final GiftRepository giftRepository; + + public GiftService(final GiftRepository giftRepository) { + super(giftRepository); + this.giftRepository = giftRepository; + } + + public List getGiftMenuResponse() { + return giftRepository.getActiveMenuCounts().entrySet().stream() + .map(this::createGiftMenuResponse) + .toList(); + } + + private GiftMenuResponse createGiftMenuResponse(final Entry entry) { + return GiftMenuResponse.of(entry.getKey(), entry.getValue()); + } +} diff --git a/src/main/java/christmas/service/order/OrderService.java b/src/main/java/christmas/service/order/OrderService.java new file mode 100644 index 0000000..b94d397 --- /dev/null +++ b/src/main/java/christmas/service/order/OrderService.java @@ -0,0 +1,50 @@ +package christmas.service.order; + +import christmas.domain.order.Order; +import christmas.domain.order.constant.Menu; +import christmas.response.EventDetailResponse; +import christmas.response.MenuCountResponse; +import christmas.response.OrderResponse; +import christmas.response.OrderSummaryResponse; +import christmas.service.event.EventDetailService; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +public class OrderService { + + private final EventDetailService eventDetailService; + + public OrderService(final EventDetailService eventDetailService) { + this.eventDetailService = eventDetailService; + } + + public OrderSummaryResponse createOrderSummary(final Order order) { + eventDetailService.applyEvent(order); + + final OrderResponse orderResponse = createOrderResponse(order); + final EventDetailResponse eventDetailResponse = createEventDetailResponse(order); + + return OrderSummaryResponse.of(orderResponse, eventDetailResponse); + } + + private OrderResponse createOrderResponse(final Order order) { + return OrderResponse.of(order.getDayInt(), convertMenuCounts(order.getMenuCount())); + } + + private List convertMenuCounts(final Map menuCount) { + return menuCount.entrySet().stream() + .map(this::createMenuCountResponse) + .toList(); + } + + private MenuCountResponse createMenuCountResponse(final Entry entry) { + return MenuCountResponse.of(entry.getKey().getTitle(), entry.getValue()); + } + + private EventDetailResponse createEventDetailResponse(final Order order) { + final int priceBeforeDiscount = order.calculateTotalPrice(); + + return eventDetailService.getEventDetail(priceBeforeDiscount); + } +} diff --git a/src/main/java/christmas/view/InputView.java b/src/main/java/christmas/view/InputView.java new file mode 100644 index 0000000..cab8b89 --- /dev/null +++ b/src/main/java/christmas/view/InputView.java @@ -0,0 +1,41 @@ +package christmas.view; + +import christmas.view.io.Reader; +import christmas.view.io.Writer; + +public class InputView { + + private final Reader reader; + private final Writer writer; + + public InputView(final Reader reader, final Writer writer) { + this.reader = reader; + this.writer = writer; + } + + public String readDate() { + writer.println(Request.VISIT_DAY.value); + + return reader.readLine(); + } + + public String readMenuAndCount() { + writer.println(Request.MENU_AND_COUNT.value); + + return reader.readLine(); + } + + public void close() { + reader.close(); + } + + private enum Request { + VISIT_DAY("12์›” ์ค‘ ์‹๋‹น ์˜ˆ์ƒ ๋ฐฉ๋ฌธ ๋‚ ์งœ๋Š” ์–ธ์ œ์ธ๊ฐ€์š”? (์ˆซ์ž๋งŒ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”!)"), + MENU_AND_COUNT("์ฃผ๋ฌธํ•˜์‹ค ๋ฉ”๋‰ด๋ฅผ ๋ฉ”๋‰ด์™€ ๊ฐœ์ˆ˜๋ฅผ ์•Œ๋ ค ์ฃผ์„ธ์š”. (e.g. ํ•ด์‚ฐ๋ฌผํŒŒ์Šคํƒ€-2,๋ ˆ๋“œ์™€์ธ-1,์ดˆ์ฝ”์ผ€์ดํฌ-1)"); + private final String value; + + Request(final String value) { + this.value = value; + } + } +} diff --git a/src/main/java/christmas/view/OutputView.java b/src/main/java/christmas/view/OutputView.java new file mode 100644 index 0000000..2f4c0b6 --- /dev/null +++ b/src/main/java/christmas/view/OutputView.java @@ -0,0 +1,169 @@ +package christmas.view; + +import static java.lang.System.lineSeparator; + +import christmas.response.EventDetailResponse; +import christmas.response.EventResponse; +import christmas.response.GiftMenuResponse; +import christmas.response.MenuCountResponse; +import christmas.response.OrderResponse; +import christmas.response.OrderSummaryResponse; +import christmas.view.io.Writer; +import java.util.List; + +public class OutputView { + + private static final int ZERO = 0; + private final Writer writer; + + public OutputView(final Writer writer) { + this.writer = writer; + } + + public void printHello() { + writer.println(Response.HELLO.value); + } + + public void printError(final String message) { + writer.println(message); + } + + public void printOrderSummary(final OrderSummaryResponse orderSummaryResponse) { + printOrder(orderSummaryResponse.orderResponse()); + printDiscountDetail(orderSummaryResponse.eventDetailResponse()); + } + + private void printOrder(final OrderResponse orderResponse) { + printPreViewHeader(orderResponse.day()); + printMenuCountAll(orderResponse.menuCount()); + } + + private void printDiscountDetail(final EventDetailResponse eventDetailResponse) { + printPriceBeforeEvent(eventDetailResponse.priceBeforeEvent()); + printGiftCountAll(eventDetailResponse.giftMenuResponses()); + printActiveEventAll(eventDetailResponse.activeEvents()); + printTotalBenefitsAmount(eventDetailResponse.totalBenefitsAmount()); + printPriceAfterEvent(eventDetailResponse.priceAfterEvent()); + printBadge(eventDetailResponse.badge()); + } + + private void printPreViewHeader(final int day) { + writer.printf(Response.PREVIEW_EVENT.value + Response.ENTER.value, day); + } + + private void printMenuCountAll(final List menuCountResponses) { + writer.println(Header.MENU.getValueWithEnter()); + + menuCountResponses.forEach(this::printMenuCount); + } + + private void printMenuCount(final MenuCountResponse menuCountResponse) { + writer.printf(Response.MENU_COUNT.value + Response.ENTER.value, + menuCountResponse.title(), menuCountResponse.count()); + } + + private void printPriceBeforeEvent(final int totalPrice) { + writer.println(Header.BEFORE_TOTAL_PRICE.getValueWithEnter()); + writer.printf(Response.POSITIVE_MONEY.value + Response.ENTER.value, totalPrice); + } + + private void printGiftCountAll(final List giftMenus) { + writer.println(Header.GIFT.getValueWithEnter()); + + if (giftMenus.isEmpty()) { + printNone(); + return; + } + + giftMenus.forEach(this::printGiftCount); + } + + private void printGiftCount(final GiftMenuResponse giftMenu) { + writer.printf(Response.MENU_COUNT.value + Response.ENTER.value, + giftMenu.title(), giftMenu.count()); + } + + private void printActiveEventAll(final List discounts) { + writer.println(Header.DISCOUNT.getValueWithEnter()); + + if (discounts.isEmpty()) { + printNone(); + return; + } + + discounts.forEach(this::printActiveEvent); + } + + private void printActiveEvent(final EventResponse discount) { + writer.printf(Response.DISCOUNT_RESULT.value + Response.ENTER.value, + discount.title(), discount.amount()); + } + + private void printTotalBenefitsAmount(final int totalDiscountAmount) { + writer.println(Header.TOTAL_DISCOUNT.getValueWithEnter()); + + if (totalDiscountAmount == ZERO) { + writer.printf(Response.POSITIVE_MONEY.value + Response.ENTER.value, totalDiscountAmount); + return; + } + + writer.printf(Response.NEGATIVE_MONEY.value + Response.ENTER.value, totalDiscountAmount); + } + + private void printPriceAfterEvent(final int afterDiscountPayment) { + writer.println(Header.AFTER_PAYMENT.getValueWithEnter()); + writer.printf(Response.POSITIVE_MONEY.value + Response.ENTER.value, afterDiscountPayment); + } + + private void printBadge(final String badge) { + writer.println(Header.BADGE.getValueWithEnter()); + + if (badge.isBlank()) { + printNone(); + return; + } + + writer.println(badge); + } + + private void printNone() { + writer.println(Response.NONE.value); + } + + private enum Response { + HELLO("์•ˆ๋…•ํ•˜์„ธ์š”! ์šฐํ…Œ์ฝ” ์‹๋‹น 12์›” ์ด๋ฒคํŠธ ํ”Œ๋ž˜๋„ˆ์ž…๋‹ˆ๋‹ค."), + PREVIEW_EVENT("12์›” %d์ผ์— ์šฐํ…Œ์ฝ” ์‹๋‹น์—์„œ ๋ฐ›์„ ์ด๋ฒคํŠธ ํ˜œํƒ ๋ฏธ๋ฆฌ ๋ณด๊ธฐ!"), + MENU_COUNT("%s %d๊ฐœ"), + DISCOUNT_RESULT("%s: -%,d์›"), + POSITIVE_MONEY("%,d์›"), + NEGATIVE_MONEY("-%,d์›"), + NONE("์—†์Œ"), + ENTER(lineSeparator()); + + private final String value; + + Response(final String value) { + this.value = value; + } + } + + private enum Header { + MENU("<์ฃผ๋ฌธ ๋ฉ”๋‰ด>"), + BEFORE_TOTAL_PRICE("<ํ• ์ธ ์ „ ์ด์ฃผ๋ฌธ ๊ธˆ์•ก>"), + GIFT("<์ฆ์ • ๋ฉ”๋‰ด>"), + DISCOUNT("<ํ˜œํƒ ๋‚ด์—ญ>"), + TOTAL_DISCOUNT("<์ดํ˜œํƒ ๊ธˆ์•ก>"), + AFTER_PAYMENT("<ํ• ์ธ ํ›„ ์˜ˆ์ƒ ๊ฒฐ์ œ ๊ธˆ์•ก>"), + BADGE("<12์›” ์ด๋ฒคํŠธ ๋ฐฐ์ง€>"); + + private final String value; + + Header(final String value) { + this.value = value; + } + + public String getValueWithEnter() { + return Response.ENTER.value + value; + } + } +} diff --git a/src/main/java/christmas/view/io/ConsoleReader.java b/src/main/java/christmas/view/io/ConsoleReader.java new file mode 100644 index 0000000..4e1c528 --- /dev/null +++ b/src/main/java/christmas/view/io/ConsoleReader.java @@ -0,0 +1,16 @@ +package christmas.view.io; + +import camp.nextstep.edu.missionutils.Console; + +public class ConsoleReader implements Reader { + + @Override + public String readLine() { + return Console.readLine(); + } + + @Override + public void close() { + Console.close(); + } +} diff --git a/src/main/java/christmas/view/io/ConsoleWriter.java b/src/main/java/christmas/view/io/ConsoleWriter.java new file mode 100644 index 0000000..c9aeefb --- /dev/null +++ b/src/main/java/christmas/view/io/ConsoleWriter.java @@ -0,0 +1,14 @@ +package christmas.view.io; + +public class ConsoleWriter implements Writer { + + @Override + public void println(final String input) { + System.out.println(input); + } + + @Override + public void printf(final String format, final Object... args) { + System.out.printf(format, args); + } +} diff --git a/src/main/java/christmas/view/io/Reader.java b/src/main/java/christmas/view/io/Reader.java new file mode 100644 index 0000000..00e3b58 --- /dev/null +++ b/src/main/java/christmas/view/io/Reader.java @@ -0,0 +1,9 @@ +package christmas.view.io; + + +public interface Reader { + + String readLine(); + + void close(); +} diff --git a/src/main/java/christmas/view/io/Writer.java b/src/main/java/christmas/view/io/Writer.java new file mode 100644 index 0000000..f909409 --- /dev/null +++ b/src/main/java/christmas/view/io/Writer.java @@ -0,0 +1,8 @@ +package christmas.view.io; + +public interface Writer { + + void println(final String input); + + void printf(final String format, final Object... args); +} diff --git a/src/test/java/christmas/ApplicationTest.java b/src/test/java/christmas/ApplicationTest.java index 514a99e..ff23bde 100644 --- a/src/test/java/christmas/ApplicationTest.java +++ b/src/test/java/christmas/ApplicationTest.java @@ -7,6 +7,7 @@ import org.junit.jupiter.api.Test; class ApplicationTest extends NsTest { + private static final String LINE_SEPARATOR = System.lineSeparator(); @Test @@ -14,13 +15,13 @@ class ApplicationTest extends NsTest { assertSimpleTest(() -> { run("3", "ํ‹ฐ๋ณธ์Šคํ…Œ์ดํฌ-1,๋ฐ”๋น„ํ๋ฆฝ-1,์ดˆ์ฝ”์ผ€์ดํฌ-2,์ œ๋กœ์ฝœ๋ผ-1"); assertThat(output()).contains( - "<์ฃผ๋ฌธ ๋ฉ”๋‰ด>", - "<ํ• ์ธ ์ „ ์ด์ฃผ๋ฌธ ๊ธˆ์•ก>", - "<์ฆ์ • ๋ฉ”๋‰ด>", - "<ํ˜œํƒ ๋‚ด์—ญ>", - "<์ดํ˜œํƒ ๊ธˆ์•ก>", - "<ํ• ์ธ ํ›„ ์˜ˆ์ƒ ๊ฒฐ์ œ ๊ธˆ์•ก>", - "<12์›” ์ด๋ฒคํŠธ ๋ฐฐ์ง€>" + "<์ฃผ๋ฌธ ๋ฉ”๋‰ด>", + "<ํ• ์ธ ์ „ ์ด์ฃผ๋ฌธ ๊ธˆ์•ก>", + "<์ฆ์ • ๋ฉ”๋‰ด>", + "<ํ˜œํƒ ๋‚ด์—ญ>", + "<์ดํ˜œํƒ ๊ธˆ์•ก>", + "<ํ• ์ธ ํ›„ ์˜ˆ์ƒ ๊ฒฐ์ œ ๊ธˆ์•ก>", + "<12์›” ์ด๋ฒคํŠธ ๋ฐฐ์ง€>" ); }); } diff --git a/src/test/java/christmas/controller/OrderControllerTest.java b/src/test/java/christmas/controller/OrderControllerTest.java new file mode 100644 index 0000000..6ef67c9 --- /dev/null +++ b/src/test/java/christmas/controller/OrderControllerTest.java @@ -0,0 +1,92 @@ +package christmas.controller; + +import static org.assertj.core.api.Assertions.assertThat; + +import christmas.domain.event.discount.DiscountRepository; +import christmas.domain.event.gift.GiftRepository; +import christmas.service.event.DiscountService; +import christmas.service.event.EventDetailService; +import christmas.service.event.GiftService; +import christmas.service.order.OrderService; +import christmas.view.InputView; +import christmas.view.OutputView; +import christmas.view.io.Reader; +import christmas.view.TestWriter; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +@DisplayName("์ฃผ๋ฌธ ์ปจํŠธ๋กค๋Ÿฌ๋Š”") +class OrderControllerTest { + + OrderController orderController; + TestWriter writer; + TestQueueReader reader; + + @BeforeEach + void setUp() { + writer = new TestWriter(); + reader = new TestQueueReader(); + + InputView inputView = new InputView(reader, writer); + OutputView outputView = new OutputView(writer); + DiscountService discountService = new DiscountService(new DiscountRepository()); + GiftService giftService = new GiftService(new GiftRepository()); + EventDetailService eventDetailService = new EventDetailService(discountService, giftService); + OrderService orderService = new OrderService(eventDetailService); + + orderController = new OrderController(inputView, outputView, orderService); + } + + @DisplayName("์ •์ƒ์ ์ธ ๋‚ ์งœ๊ฐ€ ์ž…๋ ฅ๋  ๋•Œ๊นŒ์ง€ ์žฌ์ž…๋ ฅ ๋ฐ›๋Š”๋‹ค.") + @Test + void createDay() { + // given + reader.add("A", "01", "32", "-1", "12์ผ", "12์›”1์ผ", "", " 1", "25"); + reader.add("ํ•ด์‚ฐ๋ฌผํŒŒ์Šคํƒ€-2,๋ ˆ๋“œ์™€์ธ-1"); + + // when + orderController.run(); + + // then + assertThat(writer.getString()) + .contains("12์›” 25์ผ์— ์šฐํ…Œ์ฝ” ์‹๋‹น์—์„œ ๋ฐ›์„ ์ด๋ฒคํŠธ ํ˜œํƒ ๋ฏธ๋ฆฌ ๋ณด๊ธฐ!"); + } + + @DisplayName("์ •์ƒ์ ์ธ ๋ฉ”๋‰ด๊ฐ€ ์ž…๋ ฅ๋  ๋•Œ๊นŒ์ง€ ์žฌ์ž…๋ ฅ ๋ฐ›๋Š”๋‹ค.") + @Test + void createMenuCount() { + // given + reader.add("25"); + reader.add("ํ‹ฐ๋งˆ์Šค-2", "ํ•ด์‚ฐ๋ฌผํŒŒ์Šคํƒ€-2,,", "์ œ๋กœ์ฝœ๋ผ-1", "ํ•ด์‚ฐ๋ฌผํŒŒ์Šคํƒ€-A", "ํ•ดํ† ํŒŒ์Šค์นผ-2,๋ ˆ๋“œ์™€์ธ-1", "ํ•ด์‚ฐ๋ฌผํŒŒ์Šคํƒ€-2,๋ ˆ๋“œ์™€์ธ-1"); + + // when + orderController.run(); + + // then + assertThat(writer.getString()) + .contains("<์ฃผ๋ฌธ ๋ฉ”๋‰ด>\nํ•ด์‚ฐ๋ฌผํŒŒ์Šคํƒ€ 2๊ฐœ\n๋ ˆ๋“œ์™€์ธ 1๊ฐœ"); + } + + static class TestQueueReader implements Reader { + + private static final Queue queue = new LinkedList<>(); + + public void add(String... args) { + queue.addAll(List.of(args)); + } + + @Override + public String readLine() { + return queue.poll(); + } + + @Override + public void close() { + } + } + +} \ No newline at end of file diff --git a/src/test/java/christmas/controller/converter/OrderConverterTest.java b/src/test/java/christmas/controller/converter/OrderConverterTest.java new file mode 100644 index 0000000..65c28e1 --- /dev/null +++ b/src/test/java/christmas/controller/converter/OrderConverterTest.java @@ -0,0 +1,139 @@ +package christmas.controller.converter; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +import christmas.domain.order.Day; +import christmas.domain.order.constant.Menu; +import java.util.EnumMap; +import java.util.Map; +import java.util.stream.Stream; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +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 org.junit.jupiter.params.provider.ValueSource; + +@DisplayName("์ฃผ๋ฌธ ๋ณ€ํ™˜๊ธฐ๋Š”") +class OrderConverterTest { + + @Nested + @DisplayName("๋‚ ์งœ๋กœ ๋ณ€ํ™˜ํ•  ๋•Œ") + class ConvertToDay { + + @DisplayName("๋ฌธ์ž๋ฅผ Day๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ๋‹ค.") + @ParameterizedTest + @ValueSource(strings = {"1", "2", "3", "4", "10", "14", "19", "25", "29", "30", "31"}) + void convertToDay(String input) { + // when + Day day = OrderConverter.convertToDay(input); + + // then + assertThat(day).isEqualTo(Day.from(Integer.parseInt(input))); + } + + @DisplayName("๋ฌธ์ž๊ฐ€ ์ •์ˆ˜๊ฐ€ ์•„๋‹ˆ๋ฉด ์˜ˆ์™ธ์ฒ˜๋ฆฌ๋œ๋‹ค.") + @ParameterizedTest + @ValueSource(strings = {"1.0", "๊ฐ€", ",", "-", "abd", "1 1", " ", "/"}) + void convertToDayNotInteger(String input) { + assertThatThrownBy(() -> OrderConverter.convertToDay(input)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ์œ ํšจํ•˜์ง€ ์•Š์€ ๋‚ ์งœ์ž…๋‹ˆ๋‹ค. ๋‹ค์‹œ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”."); + } + + @DisplayName("๋ฌธ์ž๊ฐ€ ์ •์ˆ˜์ด์–ด๋„ 0์œผ๋กœ ์‹œ์ž‘ํ•˜๋ฉด ์˜ˆ์™ธ์ฒ˜๋ฆฌ๋œ๋‹ค.") + @ParameterizedTest + @ValueSource(strings = {"01", "031", "0"}) + void convertToDayZero(String input) { + assertThatThrownBy(() -> OrderConverter.convertToDay(input)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ์œ ํšจํ•˜์ง€ ์•Š์€ ๋‚ ์งœ์ž…๋‹ˆ๋‹ค. ๋‹ค์‹œ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”."); + } + + @DisplayName("๋ฌธ์ž๊ฐ€ 12์›”์— ์—†์œผ๋ฉด ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋œ๋‹ค.") + @ParameterizedTest + @ValueSource(strings = {"0", "32", "-1", "33"}) + void convertToDayNotDecember(String input) { + assertThatThrownBy(() -> OrderConverter.convertToDay(input)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ์œ ํšจํ•˜์ง€ ์•Š์€ ๋‚ ์งœ์ž…๋‹ˆ๋‹ค. ๋‹ค์‹œ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”."); + } + } + + + @Nested + @DisplayName("๋ฉ”๋‰ด ์ˆ˜๋Ÿ‰์œผ๋กœ ๋ณ€ํ™˜ํ•  ๋•Œ") + class ConvertMenu { + + @DisplayName("๋ฌธ์ž๋ฅผ MenuEnumMap์œผ๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ๋‹ค.") + @ParameterizedTest + @MethodSource("menuCountProvider") + void convertToMenu(String menuCount, Map target) { + // when + EnumMap convert = OrderConverter.convertToMenu(menuCount); + + // then + assertThat(convert).containsExactlyInAnyOrderEntriesOf(target); + } + + static Stream menuCountProvider() { + return Stream.of( + arguments("ํ‹ฐ๋ณธ์Šคํ…Œ์ดํฌ-1,์ดˆ์ฝ”์ผ€์ดํฌ-2", + Map.of(Menu.T_BONE_STEAK, 1, Menu.CHOCOLATE_CAKE, 2)), + arguments("ํ‹ฐ๋ณธ์Šคํ…Œ์ดํฌ-1", + Map.of(Menu.T_BONE_STEAK, 1)), + arguments("ํ‹ฐ๋ณธ์Šคํ…Œ์ดํฌ-1,์ดˆ์ฝ”์ผ€์ดํฌ-19", + Map.of(Menu.T_BONE_STEAK, 1, Menu.CHOCOLATE_CAKE, 19)), + arguments("ํ‹ฐ๋ณธ์Šคํ…Œ์ดํฌ-1,์ดˆ์ฝ”์ผ€์ดํฌ-2,์•„์ด์Šคํฌ๋ฆผ-1", + Map.of(Menu.T_BONE_STEAK, 1, Menu.CHOCOLATE_CAKE, 2, Menu.ICE_CREAM, 1)) + ); + } + + @DisplayName("์ค‘๋ณต๋œ ๋ฉ”๋‰ด๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.") + @Test + void throwingDuplicateOrderException() { + // given + String menuCount = "ํ‹ฐ๋ณธ์Šคํ…Œ์ดํฌ-1,์ดˆ์ฝ”์ผ€์ดํฌ-2,ํ‹ฐ๋ณธ์Šคํ…Œ์ดํฌ-3"; + + assertThatThrownBy(() -> OrderConverter.convertToMenu(menuCount)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ์œ ํšจํ•˜์ง€ ์•Š์€ ์ฃผ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋‹ค์‹œ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”."); + } + + @DisplayName("์ž…๋ ฅ ๋ฉ”๋‰ด์˜ ์ˆ˜๊ฐ€ ์ˆซ์ž๊ฐ€ ์•„๋‹ ๊ฒฝ์šฐ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.") + @Test + void convertCountToInt() { + // given + String menuCount = "ํ‹ฐ๋ณธ์Šคํ…Œ์ดํฌ-A"; + + assertThatThrownBy(() -> OrderConverter.convertToMenu(menuCount)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ์œ ํšจํ•˜์ง€ ์•Š์€ ์ฃผ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋‹ค์‹œ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”."); + } + + @DisplayName("์ž…๋ ฅ ๋ฉ”๋‰ด์˜ ์ˆ˜๊ฐ€ 0์œผ๋กœ ์‹œ์ž‘ํ•˜๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.") + @Test + void convertCountToIntZero() { + // given + String menuCount = "ํ‹ฐ๋ณธ์Šคํ…Œ์ดํฌ-01"; + + assertThatThrownBy(() -> OrderConverter.convertToMenu(menuCount)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ์œ ํšจํ•˜์ง€ ์•Š์€ ์ฃผ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋‹ค์‹œ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”."); + } + + @DisplayName("์ž…๋ ฅ ๋ฉ”๋‰ด๊ฐ€ ํ˜•์‹์— ๋งž์ง€ ์•Š์„ ๊ฒฝ์šฐ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.") + @ParameterizedTest + @ValueSource(strings = {"ํ‹ฐ๋ณธ์Šคํ…Œ์ดํฌ-1,์ดˆ์ฝ”์ผ€์ดํฌ-2,", ",ํ‹ฐ๋ณธ์Šคํ…Œ์ดํฌ-1,์ดˆ์ฝ”์ผ€์ดํฌ-2", "ํ‹ฐ๋ณธ์Šคํ…Œ์ดํฌ-1,,์ดˆ์ฝ”์ผ€์ดํฌ-2", + "ํ‹ฐ๋ณธ์Šคํ…Œ์ดํฌ-1-3,์ดˆ์ฝ”์ผ€์ดํฌ-2", "ํ‹ฐ๋ณธ์Šคํ…Œ์ดํฌ--1,์ดˆ์ฝ”์ผ€์ดํฌ-2"}) + void validateFormat(String menuCount) { + assertThatThrownBy(() -> OrderConverter.convertToMenu(menuCount)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ์œ ํšจํ•˜์ง€ ์•Š์€ ์ฃผ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋‹ค์‹œ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”."); + } + } + +} \ No newline at end of file diff --git a/src/test/java/christmas/domain/event/BadgeTest.java b/src/test/java/christmas/domain/event/BadgeTest.java new file mode 100644 index 0000000..6d3ea4b --- /dev/null +++ b/src/test/java/christmas/domain/event/BadgeTest.java @@ -0,0 +1,56 @@ +package christmas.domain.event; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +@DisplayName("๋ฐฐ์ง€๋Š” ") +class BadgeTest { + + @DisplayName("ํ˜œํƒ๊ธˆ์•ก์ด 20000์› ์ด์ƒ์ด๋ฉด ์‚ฐํƒ€๋“ฑ๊ธ‰์ด๋‹ค.") + @ParameterizedTest + @ValueSource(ints = {20000, 30000, 100000}) + void santa(int discount) { + // given // when + Badge badge = Badge.from(discount); + + // then + assertThat(badge).isEqualByComparingTo(Badge.SANTA); + } + + @DisplayName("ํ˜œํƒ๊ธˆ์•ก์ด 20000์› ๋ฏธ๋งŒ 10000์› ์ด์ƒ์ด๋ฉด ํŠธ๋ฆฌ๋“ฑ๊ธ‰์ด๋‹ค.") + @ParameterizedTest + @ValueSource(ints = {10000, 15000, 19999}) + void tree(int discount) { + // given // when + Badge badge = Badge.from(discount); + + // then + assertThat(badge).isEqualByComparingTo(Badge.TREE); + } + + @DisplayName("ํ˜œํƒ๊ธˆ์•ก์ด 10000์› ๋ฏธ๋งŒ 5000์› ์ด์ƒ์ด๋ฉด ๋ณ„๋“ฑ๊ธ‰์ด๋‹ค.") + @ParameterizedTest + @ValueSource(ints = {5000, 8000, 9999}) + void star(int discount) { + // given // when + Badge badge = Badge.from(discount); + + // then + assertThat(badge).isEqualByComparingTo(Badge.STAR); + } + + @DisplayName("ํ˜œํƒ๊ธˆ์•ก์ด 5000์› ๋ฏธ๋งŒ์ด๋ฉด ๋“ฑ๊ธ‰์ด ์—†๋‹ค.") + @ParameterizedTest + @ValueSource(ints = {0, 1000, 4999}) + void none(int discount) { + // given // when + Badge badge = Badge.from(discount); + + // then + assertThat(badge).isEqualByComparingTo(Badge.NONE); + } + +} \ No newline at end of file diff --git a/src/test/java/christmas/domain/event/discount/DiscountEventTypeTest.java b/src/test/java/christmas/domain/event/discount/DiscountEventTypeTest.java new file mode 100644 index 0000000..260d345 --- /dev/null +++ b/src/test/java/christmas/domain/event/discount/DiscountEventTypeTest.java @@ -0,0 +1,298 @@ +package christmas.domain.event.discount; + +import static christmas.domain.event.discount.DiscountEventType.CHRISTMAS_D_DAY_DISCOUNT; +import static christmas.domain.event.discount.DiscountEventType.SPECIAL_DISCOUNT; +import static christmas.domain.event.discount.DiscountEventType.WEEKDAY_DISCOUNT; +import static christmas.domain.event.discount.DiscountEventType.WEEKEND_DISCOUNT; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +import christmas.domain.event.Event; +import christmas.domain.order.Day; +import christmas.domain.order.Order; +import christmas.domain.order.constant.Menu; +import java.util.EnumMap; +import java.util.Map; +import java.util.stream.Stream; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +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.CsvSource; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; + +@DisplayName("ํ• ์ธ ์ด๋ฒคํŠธ ํƒ€์ž…์€") +class DiscountEventTypeTest { + + @Nested + @DisplayName("๊ณตํ†ต ์ ์œผ๋กœ") + class Common { + + @DisplayName("ํ• ์ธ ์ •์ฑ…์„ ์ค‘๋ณต ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.") + @Test + void discountAll() { + // given + Order order = Order.of(Day.from(25), + Map.of(Menu.T_BONE_STEAK, 1, Menu.ICE_CREAM, 2, Menu.BUTTON_MUSHROOM_SOUP, 1, Menu.CHRISTMAS_PASTA, + 2)); + + // when + EnumMap discountEventTypeIntegerEnumMap = Event.applyAll(DiscountEventType.class, order); + + // then + assertThat(discountEventTypeIntegerEnumMap) + .containsExactlyInAnyOrderEntriesOf(Map.of(CHRISTMAS_D_DAY_DISCOUNT, 3400, + WEEKDAY_DISCOUNT, 2023 * 2, + WEEKEND_DISCOUNT, 0, + SPECIAL_DISCOUNT, 1000)); + } + + @DisplayName("์–ด๋–ค ์ด๋ฒคํŠธ๋„ ์ด ์ฃผ๋ฌธ ๊ธˆ์•ก์ด 10,000์›์ด ๋„˜์ง€ ์•Š์œผ๋ฉด ํ• ์ธ์ด ์ ์šฉ๋˜์ง€ ์•Š๋Š”๋‹ค.") + @ParameterizedTest + @MethodSource("eventProvider") + void calculateBenefits(DiscountEventType event, int day) { + // given + Order order = Order.of(Day.from(day), Map.of(Menu.ZERO_COLA, 1, + Menu.ICE_CREAM, 1)); + + // when + int benefits = event.calculateBenefits(order); + + // then + assertThat(benefits).isEqualTo(0); + } + + static Stream eventProvider() { + return Stream.of(arguments(CHRISTMAS_D_DAY_DISCOUNT, 25), + arguments(WEEKEND_DISCOUNT, 2), + arguments(WEEKDAY_DISCOUNT, 24), + arguments(SPECIAL_DISCOUNT, 25)); + } + } + + @Nested + @DisplayName("ํฌ๋ฆฌ์Šค๋งˆ์Šค ๋””๋ฐ์ด ํ• ์ธ์€") + class ChristmasDDayDiscount { + + @DisplayName("๋‚ ์งœ์— ๋”ฐ๋ฅธ ํ• ์ธ ๊ธˆ์•ก์„ ๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ๋‹ค.") + @ParameterizedTest + @CsvSource(value = {"1,1000", "2,1100", "3,1200", "24,3300", "25,3400"}, delimiter = ',') + void calculateAmount(int day, int discountTarget) { + // given + Day orderDay = Day.from(day); + Order order = Order.of(orderDay, Map.of(Menu.T_BONE_STEAK, 1)); + + // when + int discountAmount = CHRISTMAS_D_DAY_DISCOUNT.calculateBenefits(order); + + // then + assertThat(discountAmount).isEqualTo(discountTarget); + } + + @DisplayName("์ฃผ๋ฌธ ๋‚ ์งœ๊ฐ€ 25์ผ ์ด๋‚ด์ด๋ฉด ํ• ์ธ์ด ์ ์šฉ๋œ๋‹ค.") + @ParameterizedTest + @ValueSource(ints = {1, 2, 3, 4, 5, 6, 7, 15, 20, 23, 24, 25}) + void calculateBenefits(int day) { + // given + Day orderDay = Day.from(day); + Order order = Order.of(orderDay, Map.of(Menu.ICE_CREAM, 10)); + + // when + int discountAmount = CHRISTMAS_D_DAY_DISCOUNT.calculateBenefits(order); + + // then + assertThat(discountAmount).isEqualTo((day - 1) * 100 + 1000); + } + + @DisplayName("์ฃผ๋ฌธ ๋‚ ์งœ๊ฐ€ 25์ผ์„ ๋„˜์–ด๊ฐ€๋ฉด ํ• ์ธ ์ ์šฉ์ด ์•ˆ๋œ๋‹ค.") + @ParameterizedTest + @ValueSource(ints = {26, 27, 28, 29, 30, 31}) + void calculateBenefitsOverDay(int day) { + // given + Day orderDay = Day.from(day); + Order order = Order.of(orderDay, Map.of(Menu.ICE_CREAM, 10)); + + // when + int discountAmount = CHRISTMAS_D_DAY_DISCOUNT.calculateBenefits(order); + + // then + assertThat(discountAmount).isEqualTo(0); + } + } + + @Nested + @DisplayName("ํ‰์ผ ํ• ์ธ์€") + class WeekdayDiscount { + + @DisplayName("ํ‰์ผ์— ๋””์ €ํŠธ ๋ฉ”๋‰ด 1๊ฐœ๋‹น 2023์› ํ• ์ธ ํ•ฉ๊ณ„๋ฅผ ๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ๋‹ค.") + @ParameterizedTest + @MethodSource("weekDayMenuProvider") + void calculateAmount(Map menuCount, int discountTarget) { + // given + Day day = Day.from(3); + Order order = Order.of(day, menuCount); + + // when + int discountAmount = WEEKDAY_DISCOUNT.calculateBenefits(order); + + // then + assertThat(discountAmount).isEqualTo(discountTarget); + + } + + static Stream weekDayMenuProvider() { + return Stream.of( + arguments(Map.of(Menu.ICE_CREAM, 1, Menu.T_BONE_STEAK, 1), 2023), + arguments(Map.of(Menu.ICE_CREAM, 2), 2023 * 2), + arguments(Map.of(Menu.TAPAS, 1), 0), + arguments(Map.of(Menu.CAESAR_SALAD, 1, Menu.CHOCOLATE_CAKE, 10), 2023 * 10), + arguments(Map.of(Menu.ICE_CREAM, 3, Menu.CHOCOLATE_CAKE, 5), 2023 * 8), + arguments(Map.of(Menu.BUTTON_MUSHROOM_SOUP, 1, Menu.ZERO_COLA, 1), 0) + ); + } + + @DisplayName("ํ‰์ผ์— ํ• ์ธ์ด ์ ์šฉ๋œ๋‹ค.") + @ParameterizedTest + @ValueSource(ints = {3, 4, 5, 6, 7, + 10, 11, 12, 13, 14, + 17, 18, 19, 20, 21, + 24, 25, 26, 27, 28, + 31}) + void calculateAmountWeekend(int day) { + // given + Day orderDay = Day.from(day); + Order order = Order.of(orderDay, Map.of(Menu.ICE_CREAM, 10)); + + // when + int discountAmount = WEEKDAY_DISCOUNT.calculateBenefits(order); + + // then + assertThat(discountAmount).isEqualTo(2023 * 10); + + } + + @DisplayName("์ฃผ๋ง์— ํ• ์ธ ์ ์šฉ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค.") + @ParameterizedTest + @ValueSource(ints = {1, 2, 8, 9, 15, 16, 22, 23, 29, 30}) + void calculateBenefitsWeekday(int day) { + // given + Day orderDay = Day.from(day); + Order order = Order.of(orderDay, Map.of(Menu.ICE_CREAM, 3)); + + // when + int discountAmount = WEEKDAY_DISCOUNT.calculateBenefits(order); + + // then + assertThat(discountAmount).isEqualTo(0); + } + } + + @Nested + @DisplayName("์ฃผ๋ง ํ• ์ธ์€") + class WeekendDiscount { + + @DisplayName("์ฃผ๋ง์— ๋ฉ”์ธ ๋ฉ”๋‰ด 1๊ฐœ๋‹น 2023์› ํ• ์ธ ํ•ฉ๊ณ„๋ฅผ ๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ๋‹ค.") + @ParameterizedTest + @MethodSource("weekendMenuProvider") + void calculateBenefits(Map menuCount, int discountTarget) { + // given + Day day = Day.from(1); + Order order = Order.of(day, menuCount); + + // when + int discountAmount = WEEKEND_DISCOUNT.calculateBenefits(order); + + // then + assertThat(discountAmount).isEqualTo(discountTarget); + + } + + static Stream weekendMenuProvider() { + return Stream.of( + arguments(Map.of(Menu.BARBECUE_RIBS, 1), 2023), + arguments(Map.of(Menu.T_BONE_STEAK, 2), 2023 * 2), + arguments(Map.of(Menu.ICE_CREAM, 1), 0), + arguments(Map.of(Menu.ICE_CREAM, 1, Menu.BARBECUE_RIBS, 10), 2023 * 10), + arguments(Map.of(Menu.BARBECUE_RIBS, 3, Menu.T_BONE_STEAK, 5), 2023 * 8), + arguments(Map.of(Menu.BUTTON_MUSHROOM_SOUP, 1, Menu.ZERO_COLA, 1), 0) + ); + } + + @DisplayName("ํ‰์ผ์— ํ• ์ธ์ด ์ ์šฉ๋˜์ง€ ์•Š๋Š”๋‹ค.") + @ParameterizedTest + @ValueSource(ints = {3, 4, 5, 6, 7, + 10, 11, 12, 13, 14, + 17, 18, 19, 20, 21, + 24, 25, 26, 27, 28, + 31}) + void weekendInWeekday(int day) { + // given + Day orderDay = Day.from(day); + Order order = Order.of(orderDay, Map.of(Menu.T_BONE_STEAK, 10)); + + // when + int discountAmount = WEEKEND_DISCOUNT.calculateBenefits(order); + + // then + assertThat(discountAmount).isEqualTo(0); + + } + + @DisplayName("์ฃผ๋ง์— ํ• ์ธ ์ ์šฉ๋œ๋‹ค.") + @ParameterizedTest + @ValueSource(ints = {1, 2, 8, 9, 15, 16, 22, 23, 29, 30}) + void weekendInWeekend(int day) { + // given + Day orderDay = Day.from(day); + Order order = Order.of(orderDay, Map.of(Menu.T_BONE_STEAK, 10)); + + // when + int discountAmount = WEEKEND_DISCOUNT.calculateBenefits(order); + + // then + assertThat(discountAmount).isEqualTo(2023 * 10); + } + } + + @Nested + @DisplayName("์ŠคํŽ˜์…œ ํ• ์ธ์€") + class SpecialDiscount { + + @DisplayName("๋ณ„์ด ์žˆ๋Š” ๋‹ฌ์— 1000์› ํ• ์ธ๋œ๋‹ค.") + @ParameterizedTest + @ValueSource(ints = {3, 10, 17, 24, 25, 31}) + void calculateBenefitsAtStarDay(int day) { + // given + Day orderDay = Day.from(day); + Order order = Order.of(orderDay, Map.of(Menu.T_BONE_STEAK, 1)); + + // when + int discount = SPECIAL_DISCOUNT.calculateBenefits(order); + + // then + assertThat(discount).isEqualTo(1000); + } + + @DisplayName("๋ณ„์ด ์—†๋Š” ๋‹ฌ์— ํ• ์ธ ๋ถˆ๊ฐ€ํ•˜๋‹ค.") + @ParameterizedTest + @ValueSource(ints = {1, 2, + 4, 5, 6, 7, 8, 9, + 11, 12, 13, 14, 15, 16, + 18, 19, 20, 21, 22, 23, + 26, 27, 28, 29, 30}) + void calculateBenefitsAtNonStarDay(int day) { + // given + Day orderDay = Day.from(day); + Order order = Order.of(orderDay, Map.of(Menu.T_BONE_STEAK, 1)); + + // when + int discount = SPECIAL_DISCOUNT.calculateBenefits(order); + + // then + assertThat(discount).isEqualTo(0); + } + } + +} \ No newline at end of file diff --git a/src/test/java/christmas/domain/event/discount/DiscountRepositoryTest.java b/src/test/java/christmas/domain/event/discount/DiscountRepositoryTest.java new file mode 100644 index 0000000..9ac9c46 --- /dev/null +++ b/src/test/java/christmas/domain/event/discount/DiscountRepositoryTest.java @@ -0,0 +1,53 @@ +package christmas.domain.event.discount; + +import static christmas.domain.event.discount.DiscountEventType.CHRISTMAS_D_DAY_DISCOUNT; +import static christmas.domain.event.discount.DiscountEventType.SPECIAL_DISCOUNT; +import static christmas.domain.event.discount.DiscountEventType.WEEKDAY_DISCOUNT; +import static christmas.domain.event.discount.DiscountEventType.WEEKEND_DISCOUNT; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.EnumMap; +import java.util.Map; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +@DisplayName("ํ• ์ธ ์ €์žฅ์†Œ๋Š”") +class DiscountRepositoryTest { + + DiscountRepository discountRepository = new DiscountRepository(); + + @BeforeEach + void setUp() { + EnumMap discountCounts = new EnumMap<>(DiscountEventType.class); + discountCounts.put(SPECIAL_DISCOUNT, 1_000); + discountCounts.put(CHRISTMAS_D_DAY_DISCOUNT, 2_400); + discountCounts.put(WEEKDAY_DISCOUNT, 2_023); + discountCounts.put(WEEKEND_DISCOUNT, 0); + + discountRepository.init(discountCounts); + } + + @DisplayName("์ „์ฒด ํ• ์ธ ๊ธˆ์•ก์„ ๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ๋‹ค.") + @Test + void calculateTotal() { + // given // when + int totalDiscount = discountRepository.calculateTotal(); + + // then + assertThat(totalDiscount).isEqualTo(1000 + 2400 + 2023); + } + + @DisplayName("์ ์šฉ๋œ ํ• ์ธ๋ชฉ๋ก์„ ๊ตฌํ•  ์ˆ˜ ์žˆ๋‹ค.") + @Test + void getActiveResult() { + // given // when + Map activeResult = discountRepository.getActiveResult(); + + // then + assertThat(activeResult) + .containsExactlyInAnyOrderEntriesOf(Map.of("ํŠน๋ณ„ ํ• ์ธ", 1_000, + "ํฌ๋ฆฌ์Šค๋งˆ์Šค ๋””๋ฐ์ด ํ• ์ธ", 2_400, + "ํ‰์ผ ํ• ์ธ", 2_023)); + } +} \ No newline at end of file diff --git a/src/test/java/christmas/domain/event/gift/GiftEventTypeTest.java b/src/test/java/christmas/domain/event/gift/GiftEventTypeTest.java new file mode 100644 index 0000000..6ba6133 --- /dev/null +++ b/src/test/java/christmas/domain/event/gift/GiftEventTypeTest.java @@ -0,0 +1,104 @@ +package christmas.domain.event.gift; + +import static org.assertj.core.api.Assertions.assertThat; + +import christmas.domain.event.Event; +import christmas.domain.order.Day; +import christmas.domain.order.Order; +import christmas.domain.order.constant.Menu; +import java.util.EnumMap; +import java.util.Map; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +@DisplayName("์„ ๋ฌผ ์ด๋ฒคํŠธ ํƒ€์ž…์€") +class GiftEventTypeTest { + + @Nested + @DisplayName("๊ณตํ†ต์ ์œผ๋กœ") + class Common { + + @DisplayName("ํ• ์ธ ์ •์ฑ…์„ ์ค‘๋ณต ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.") + @Test + void discountAll() { + // given + Order order = Order.of(Day.from(25), + Map.of(Menu.T_BONE_STEAK, 2, Menu.ICE_CREAM, 2, Menu.BUTTON_MUSHROOM_SOUP, 1, Menu.CHRISTMAS_PASTA, + 2)); + + // when + EnumMap giftEventTypeResult = Event.applyAll(GiftEventType.class, order); + + // then + assertThat(giftEventTypeResult) + .containsExactlyInAnyOrderEntriesOf(Map.of(GiftEventType.PRESENTATION, 25000)); + } + + @DisplayName("ํ˜œํƒ ๊ธˆ์•ก์„ ๊ณ„์‚ฐ ํ•  ์ˆ˜ ์žˆ๋‹ค.") + @Test + void calculateBenefits() { + // given + Order order = Order.of(Day.from(1), Map.of(Menu.T_BONE_STEAK, 3)); + + // when + int benefits = GiftEventType.PRESENTATION.calculateBenefits(order); + + // then + assertThat(benefits).isEqualTo(25_000); + } + + @DisplayName("๋ฉ”๋‰ด์ด๋ฆ„์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.") + @Test + void getMenuTitle() { + // given // when + String menuTitle = GiftEventType.PRESENTATION.getMenuTitle(); + + // then + assertThat(menuTitle).isEqualTo("์ƒดํŽ˜์ธ"); + } + + @DisplayName("๋ฉ”๋‰ด์˜ ์ˆ˜๋Ÿ‰์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.") + @Test + void getCount() { + // given // when + int count = GiftEventType.PRESENTATION.getCount(); + + // then + assertThat(count).isEqualTo(1); + } + } + + @Nested + @DisplayName("์ฆ์ • ์ด๋ฒคํŠธ๋Š”") + class PresentationEvent { + + @DisplayName("ํ• ์ธ ์ „ ์ฃผ๋ฌธ๊ธˆ์•ก์ด 120,000์ด์ƒ์ผ ๋•Œ ์ ์šฉ๋œ๋‹ค.") + @Test + void calculateBenefitsTrue() { + // given + Order order = Order.of(Day.from(1), Map.of(Menu.T_BONE_STEAK, 3)); + + // when + int benefits = GiftEventType.PRESENTATION.calculateBenefits(order); + + // then + assertThat(benefits).isEqualTo(25_000); + } + + @DisplayName("ํ• ์ธ ์ „ ์ฃผ๋ฌธ๊ธˆ์•ก์ด 120,000์ดํ•˜๋ฉด ์ ์šฉ๋˜์ง€ ์•Š๋Š”๋‹ค.") + @Test + void calculateBenefitsFalse() { + // given + Order order = Order.of(Day.from(1), Map.of(Menu.T_BONE_STEAK, 2)); + + // when + int benefits = GiftEventType.PRESENTATION.calculateBenefits(order); + + // then + assertThat(benefits).isEqualTo(0); + } + } + + +} \ No newline at end of file diff --git a/src/test/java/christmas/domain/event/gift/GiftRepositoryTest.java b/src/test/java/christmas/domain/event/gift/GiftRepositoryTest.java new file mode 100644 index 0000000..05b7f41 --- /dev/null +++ b/src/test/java/christmas/domain/event/gift/GiftRepositoryTest.java @@ -0,0 +1,56 @@ +package christmas.domain.event.gift; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.EnumMap; +import java.util.Map; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +@DisplayName("์„ ๋ฌผ ์ €์žฅ์†Œ๋Š”") +class GiftRepositoryTest { + + GiftRepository giftRepository = new GiftRepository(); + + @BeforeEach + void setUp() { + EnumMap giftCounts = new EnumMap<>(GiftEventType.class); + giftCounts.put(GiftEventType.PRESENTATION, 25_000); + + giftRepository.init(giftCounts); + } + + @DisplayName("์ „์ฒด ํ• ์ธ ๊ธˆ์•ก์„ ๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ๋‹ค.") + @Test + void calculateTotal() { + // given // when + int totalDiscount = giftRepository.calculateTotal(); + + // then + assertThat(totalDiscount).isEqualTo(25_000); + } + + @DisplayName("์ ์šฉ๋œ ํ• ์ธ ๋ชฉ๋ก์„ ๊ตฌํ•  ์ˆ˜ ์žˆ๋‹ค.") + @Test + void getActiveResult() { + // given // when + Map activeResult = giftRepository.getActiveResult(); + + // then + assertThat(activeResult) + .containsExactlyInAnyOrderEntriesOf(Map.of("์ฆ์ • ์ด๋ฒคํŠธ", 25_000)); + } + + + @DisplayName("์ ์šฉ๋œ ์ฆ์ • ๋ฉ”๋‰ด์˜ ์ด๋ฆ„๊ณผ ์ˆ˜๋Ÿ‰์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.") + @Test + void getActiveMenuCounts() { + // given // when + Map activeMenuCounts = giftRepository.getActiveMenuCounts(); + + // then + assertThat(activeMenuCounts) + .containsExactlyInAnyOrderEntriesOf(Map.of("์ƒดํŽ˜์ธ", 1)); + } +} \ No newline at end of file diff --git a/src/test/java/christmas/domain/order/DayTest.java b/src/test/java/christmas/domain/order/DayTest.java new file mode 100644 index 0000000..cc2f698 --- /dev/null +++ b/src/test/java/christmas/domain/order/DayTest.java @@ -0,0 +1,316 @@ +package christmas.domain.order; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +import christmas.domain.order.constant.Category; +import java.util.stream.Stream; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +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.CsvSource; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; + +@DisplayName("๋‚ ์งœ๋Š”") +class DayTest { + + @Nested + @DisplayName("์ž…๋ ฅ ๋ฒ”์œ„๊ฐ€ 1~31์ผ ๋•Œ๋งŒ ์ƒ์„ฑ๋œ๋‹ค.") + class Create { + + @DisplayName("์ •์ƒ ์ƒ์„ฑ๋œ๋‹ค.") + @ParameterizedTest + @ValueSource(ints = {1, 2, 10, 20, 30, 31}) + void from(int day) { + // given // when + Day from = Day.from(day); + + // then + assertThat(from) + .extracting("day") + .isEqualTo(day); + } + + @DisplayName("์˜ˆ์™ธ๊ฐ€ ๋‚˜์˜จ๋‹ค.") + @ParameterizedTest + @ValueSource(ints = {-1, -2, -3, 0, 32, 33, 34}) + void fromOutOfRange(int day) { + assertThatThrownBy(() -> Day.from(day)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ์œ ํšจํ•˜์ง€ ์•Š์€ ๋‚ ์งœ์ž…๋‹ˆ๋‹ค. ๋‹ค์‹œ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”."); + } + } + + @DisplayName("๋‚ ์งœ ์ฐจ์ด๋ฅผ ๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ๋‹ค.") + @Test + void gap() { + // given + Day day1 = Day.from(1); + Day day2 = Day.from(10); + + // when + int gap = day2.gap(day1); + + // then + assertThat(gap).isEqualTo(9); + } + + @DisplayName("๋‚ ์งœ์˜ ๋‚˜๋จธ์ง€๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๋‹ค.") + @ParameterizedTest + @ValueSource(ints = {1, 2, 3, 4, 5, 6, 7, 10, 20, 23, 30, 31}) + void mod(int day) { + // given + Day orderDay = Day.from(day); + + // when + Integer mod = orderDay.mod(7); + + // then + assertThat(mod).isEqualTo(day % 7); + } + + @DisplayName("์ฃผ๋ฌธ ์ผ์ž์˜ ์ด๋ฒคํŠธ ๋ฉ”๋‰ด ์นดํ…Œ๊ณ ๋ฆฌ๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ๋‹ค.") + @ParameterizedTest + @MethodSource("eventCategoryProvider") + void checkEventCategory(int input, Category target) { + // given + Day day = Day.from(input); + + // when + Category category = day.getTodayEventCategory(); + + // then + assertThat(category).isEqualByComparingTo(target); + } + + static Stream eventCategoryProvider() { + return Stream.of( + arguments(1, Category.MAIN), + arguments(2, Category.MAIN), + arguments(8, Category.MAIN), + arguments(23, Category.MAIN), + arguments(29, Category.MAIN), + arguments(30, Category.MAIN), + arguments(3, Category.DESSERT), + arguments(4, Category.DESSERT), + arguments(5, Category.DESSERT), + arguments(10, Category.DESSERT), + arguments(21, Category.DESSERT), + arguments(25, Category.DESSERT), + arguments(28, Category.DESSERT), + arguments(31, Category.DESSERT) + ); + } + + @Nested + @DisplayName("๋‹ค๋ฅธ ๋‚ ์งœ๋ณด๋‹ค ํฐ์ง€ ํŒ๋ณ„ ํ•  ์ˆ˜ ์žˆ๋‹ค.") + class IsOverThan { + + @DisplayName("True") + @ParameterizedTest + @CsvSource(value = {"1,2", "1,31", "2,10", "24,25", "30,31"}, delimiter = ',') + void isOverThanTrue(int left, int right) { + // given + Day leftDay = Day.from(left); + Day rightDay = Day.from(right); + + // when + boolean over = rightDay.isOverThan(leftDay); + + // then + assertThat(over).isTrue(); + } + + @DisplayName("False") + @ParameterizedTest + @CsvSource(value = {"2,1", "31,1", "25,1", "25,25", "25,24", "31,31"}, delimiter = ',') + void isOverThanFalse(int left, int right) { + // given + Day leftDay = Day.from(left); + Day rightDay = Day.from(right); + + // when + boolean over = rightDay.isOverThan(leftDay); + + // then + assertThat(over).isFalse(); + } + } + + @Nested + @DisplayName("ํฌ๋ฆฌ์Šค๋งˆ์Šค์ธ์ง€ ํŒ๋ณ„ ํ•  ์ˆ˜ ์žˆ๋‹ค.") + class IsChristmasDay { + + @DisplayName("True") + @Test + void isChristmasDayTrue() { + // given + Day day = Day.from(25); + + // when + boolean christmasDay = day.isChristmasDay(); + + // then + assertThat(christmasDay).isTrue(); + } + + @DisplayName("False") + @ParameterizedTest + @ValueSource(ints = {1, 2, 3, 4, 5, 10, 24, 26, 29, 30, 31}) + void isChristmasDayFalse(int input) { + // given + Day day = Day.from(input); + + // when + boolean christmasDay = day.isChristmasDay(); + + // then + assertThat(christmasDay).isFalse(); + } + } + + + @Nested + @DisplayName("์ฃผ๋ง์ธ์ง€ ํŒ๋ณ„ ํ•  ์ˆ˜ ์žˆ๋‹ค.") + class IsWeekend { + + @DisplayName("True") + @ParameterizedTest + @ValueSource(ints = {8, 9, 15, 16, 22, 23, 29, 30}) + void isWeekendTrue(int day) { + // given + Day orderDay = Day.from(day); + + // when + boolean weekend = orderDay.isWeekend(); + + // then + assertThat(weekend).isTrue(); + } + + @DisplayName("False") + @ParameterizedTest + @ValueSource(ints = {3, 4, 5, 6, 7, + 10, 11, 12, 13, 14, + 17, 18, 19, 20, 21, + 24, 25, 26, 27, 28, + 31}) + void isWeekendFalse(int day) { + // given + Day orderDay = Day.from(day); + + // when + boolean weekend = orderDay.isWeekend(); + + // then + assertThat(weekend).isFalse(); + } + } + + @Nested + @DisplayName("์ผ์š”์ผ์ธ์ง€ ํŒ๋ณ„ ํ•  ์ˆ˜ ์žˆ๋‹ค.") + class IsSunDay { + + @DisplayName("True") + @ParameterizedTest + @ValueSource(ints = {3, 10, 17, 24, 31}) + void isSunDayTrue(int input) { + // given + Day day = Day.from(input); + + // when // then + assertThat(day.isSunDay()).isTrue(); + } + + @DisplayName("False") + @ParameterizedTest + @ValueSource(ints = {1, 2, 4, 5, 6, 7, 8, 9, 15, 25, 26}) + void isSunDayFalse(int input) { + // given + Day day = Day.from(input); + + // when // then + assertThat(day.isSunDay()).isFalse(); + } + } + + @Nested + @DisplayName("๋‚ ์งœ๊ฐ€ ๊ฐ™์€์ง€ ํŒ๋ณ„ํ•  ์ˆ˜ ์žˆ๋‹ค.") + class Equals { + + @DisplayName("True") + @Test + void equalsTrue() { + // given + Day day = Day.from(1); + Day other = Day.from(1); + + // when + boolean equals = day.equals(other); + + // then + assertThat(equals).isTrue(); + } + + @DisplayName("False") + @Test + void equalsFalse() { + // given + Day day = Day.from(1); + Day other = Day.from(2); + + // when + boolean equals = day.equals(other); + + // then + assertThat(equals).isFalse(); + } + + @DisplayName("False - Other Type") + @Test + void equalsFalse2() { + // given + Day day = Day.from(1); + Integer other = 1; + + // when + boolean equals = day.equals(other); + + // then + assertThat(equals).isFalse(); + } + } + + @Nested + @DisplayName("๊ฐ™์€ ๋‚ ์งœ์ด๋ฉด hashCode๊ฐ€ ๊ฐ™๋‹ค.") + class Hash { + + @DisplayName("True") + @Test + void hashCodeEqualTrue() { + // given // when + int hash = Day.from(1).hashCode(); + int other = Day.from(1).hashCode(); + + // then + assertThat(hash == other).isTrue(); + } + + @DisplayName("False") + @Test + void hashCodeEqualFalse() { + // given // when + int hash = Day.from(1).hashCode(); + int other = Day.from(2).hashCode(); + + // then + assertThat(hash == other).isFalse(); + } + } + + +} \ No newline at end of file diff --git a/src/test/java/christmas/domain/order/OrderTest.java b/src/test/java/christmas/domain/order/OrderTest.java new file mode 100644 index 0000000..cb8dae4 --- /dev/null +++ b/src/test/java/christmas/domain/order/OrderTest.java @@ -0,0 +1,194 @@ +package christmas.domain.order; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +import christmas.domain.order.constant.Menu; +import java.util.Map; +import java.util.stream.Stream; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +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 org.junit.jupiter.params.provider.ValueSource; + +@DisplayName("์ฃผ๋ฌธ์€") +class OrderTest { + + @Nested + @DisplayName("๋‚ ์งœ์™€ ๋ฉ”๋‰ด ์ˆ˜๋Ÿ‰ ๋งต์œผ๋กœ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.") + class Create { + + @DisplayName("์„ฑ๊ณต") + @ParameterizedTest + @ValueSource(ints = {1, 5, 19}) + void of(int count) { + // given + Day day = Day.from(1); + Map menu = Map.of(Menu.T_BONE_STEAK, count, Menu.ZERO_COLA, 1); + + // when + Order order = Order.of(day, menu); + + // then + assertThat(order) + .extracting("day", "menuCount") + .containsExactly(day, menu); + } + + @Nested + @DisplayName("์—์™ธ") + class Exceptions { + + @DisplayName("๋‹จ์ผ ์ฃผ๋ฌธ ๋ฉ”๋‰ด์˜ ์ˆ˜๊ฐ€ 1๋ฏธ๋งŒ์ด๋ฉด ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒํ•œ๋‹ค.") + @ParameterizedTest + @ValueSource(ints = {-10, -5, -1, 0}) + void validateCountRange(int count) { + // given + Day day = Day.from(1); + Map menu = Map.of(Menu.T_BONE_STEAK, count); + + // when // then + assertThatThrownBy(() -> Order.of(day, menu)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ์œ ํšจํ•˜์ง€ ์•Š์€ ์ฃผ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋‹ค์‹œ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”."); + } + + @DisplayName("์ „์ฒด ์ฃผ๋ฌธ ์ˆ˜๋Ÿ‰์ด 20๊ฐœ์ดˆ๊ณผ์ด๋ฉด ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒํ•œ๋‹ค.") + @ParameterizedTest + @ValueSource(ints = {21, 22, 100, 9999}) + void validateTotalCountMax(int count) { + // given + Day day = Day.from(1); + Map menu = Map.of(Menu.T_BONE_STEAK, count); + + // when // then + assertThatThrownBy(() -> Order.of(day, menu)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ์œ ํšจํ•˜์ง€ ์•Š์€ ์ฃผ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋‹ค์‹œ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”."); + } + + @DisplayName("์Œ๋ฃŒ๋งŒ ์ฃผ๋ฌธํ•˜๋ฉด ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒํ•œ๋‹ค.") + @Test + void validateOnlyBeverage() { + // given + Day day = Day.from(1); + Map menu = Map.of(Menu.ZERO_COLA, 10); + + // when // then + assertThatThrownBy(() -> Order.of(day, menu)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ์œ ํšจํ•˜์ง€ ์•Š์€ ์ฃผ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋‹ค์‹œ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”."); + } + } + } + + @DisplayName("์ด ์ฃผ๋ฌธ ๊ฐ€๊ฒฉ์„ ๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ๋‹ค.") + @ParameterizedTest + @MethodSource("menuProvider") + void calculateTotalPrice(Map menuCount, int target) { + // given + Order order = Order.of(Day.from(1), menuCount); + + // when + int totalPrice = order.calculateTotalPrice(); + + // then + assertThat(totalPrice).isEqualTo(target); + } + + static Stream menuProvider() { + return Stream.of( + arguments(Map.of(Menu.ICE_CREAM, 1), 5000), + arguments(Map.of(Menu.TAPAS, 2), Menu.TAPAS.getPrice() * 2), + arguments(Map.of(Menu.CAESAR_SALAD, 10), Menu.CAESAR_SALAD.getPrice() * 10), + arguments(Map.of(Menu.BUTTON_MUSHROOM_SOUP, 1, Menu.ZERO_COLA, 1), + Menu.BUTTON_MUSHROOM_SOUP.getPrice() + Menu.ZERO_COLA.getPrice()), + arguments(Map.of(Menu.BUTTON_MUSHROOM_SOUP, 3, Menu.ZERO_COLA, 4), + Menu.BUTTON_MUSHROOM_SOUP.getPrice() * 3 + Menu.ZERO_COLA.getPrice() * 4) + ); + } + + @DisplayName("์ด๋ฒคํŠธ ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋ฉ”๋‰ด ๊ฐœ์ˆ˜๋ฅผ ๊ตฌํ•  ์ˆ˜ ์žˆ๋‹ค.") + @ParameterizedTest + @MethodSource("weekCategoryProvider") + void countWeekEventMenu(int day, Map menuCount, int target) { + // given + Order order = Order.of(Day.from(day), menuCount); + + // when + int count = order.countWeekEventMenu(); + + // then + assertThat(count).isEqualTo(target); + } + + static Stream weekCategoryProvider() { + return Stream.of( + arguments(1, Map.of(Menu.BARBECUE_RIBS, 1), 1), + arguments(1, Map.of(Menu.BARBECUE_RIBS, 2, Menu.SEAFOOD_PASTA, 3), 5), + arguments(1, Map.of(Menu.BARBECUE_RIBS, 3, Menu.SEAFOOD_PASTA, 3, + Menu.ICE_CREAM, 6), 6), + arguments(3, Map.of(Menu.ICE_CREAM, 1), 1), + arguments(3, Map.of(Menu.ICE_CREAM, 2, Menu.CHOCOLATE_CAKE, 3), 5), + arguments(3, Map.of(Menu.ICE_CREAM, 3, Menu.CHOCOLATE_CAKE, 3, + Menu.SEAFOOD_PASTA, 6), 6) + ); + } + + + @Nested + @DisplayName("์ „์ฒด ์ฃผ๋ฌธ ๊ฐ€๊ฒฉ์ด ํŠน์ • ๊ฐ€๊ฒฉ๋ณด๋‹ค ์ ์€์ง€ ํŒ๋ณ„ํ•  ์ˆ˜ ์žˆ๋‹ค. ") + class IsTotalPriceUnder { + + @DisplayName("True") + @Test + void isTotalPriceUnderTrue() { + // given + Day day = Day.from(1); + Map menu = Map.of(Menu.T_BONE_STEAK, 1, Menu.ZERO_COLA, 1); + Order order = Order.of(day, menu); + + // when + boolean totalPriceUnder = order.isTotalPriceUnder(59_999); + + // then + assertThat(totalPriceUnder).isTrue(); + } + + @DisplayName("False") + @Test + void isTotalPriceUnderFalse() { + // given + Day day = Day.from(1); + Map menu = Map.of(Menu.T_BONE_STEAK, 1, Menu.ZERO_COLA, 1); + Order order = Order.of(day, menu); + + // when + boolean totalPriceUnder = order.isTotalPriceUnder(60_000); + + // then + assertThat(totalPriceUnder).isTrue(); + } + } + + @DisplayName("getter๋กœ ์กฐํšŒํ•œ ๋ฉ”๋‰ด ์นด์šดํŠธ ๋งต์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๋‹ค.") + @Test + void getMenuCount() { + // given + Day day = Day.from(1); + Map menu = Map.of(Menu.T_BONE_STEAK, 1); + Order order = Order.of(day, menu); + + // when + Map menuCount = order.getMenuCount(); + + assertThatThrownBy(() -> menuCount.put(Menu.ICE_CREAM, 2)) + .isInstanceOf(UnsupportedOperationException.class); + + } + +} \ No newline at end of file diff --git a/src/test/java/christmas/domain/order/constant/CategoryTest.java b/src/test/java/christmas/domain/order/constant/CategoryTest.java new file mode 100644 index 0000000..e7960a7 --- /dev/null +++ b/src/test/java/christmas/domain/order/constant/CategoryTest.java @@ -0,0 +1,27 @@ +package christmas.domain.order.constant; + +import static org.assertj.core.api.Assertions.assertThat; + +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; + +@DisplayName("์นดํ…Œ๊ณ ๋ฆฌ๋Š”") +class CategoryTest { + + @DisplayName("์Œ๋ฃŒ๊ฐ€ ์•„๋‹Œ์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. True") + @ParameterizedTest + @ValueSource(strings = {"APPETIZER", "MAIN", "DESSERT"}) + void isNotBeverageTrue(Category category) { + + assertThat(category.isNotBeverage()).isTrue(); + } + + @DisplayName("์Œ๋ฃŒ๊ฐ€ ์•„๋‹Œ์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. False") + @Test + void isNotBeverageFalse() { + + assertThat(Category.BEVERAGE.isNotBeverage()).isFalse(); + } +} \ No newline at end of file diff --git a/src/test/java/christmas/domain/order/constant/DayOfWeekTest.java b/src/test/java/christmas/domain/order/constant/DayOfWeekTest.java new file mode 100644 index 0000000..0ca4a5b --- /dev/null +++ b/src/test/java/christmas/domain/order/constant/DayOfWeekTest.java @@ -0,0 +1,112 @@ +package christmas.domain.order.constant; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +import christmas.domain.order.Day; +import java.util.stream.Stream; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; + +@DisplayName("์š”์ผ์€") +class DayOfWeekTest { + + @DisplayName("Day์˜ ์š”์ผ์„ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ๋‹ค.") + @ParameterizedTest + @MethodSource("dayOfWeekProvider") + void from(int input, DayOfWeek target) { + // given + Day day = Day.from(input); + + // when + DayOfWeek dayOfWeek = DayOfWeek.from(day); + + // then + assertThat(dayOfWeek).isEqualByComparingTo(target); + } + + static Stream dayOfWeekProvider() { + return Stream.of( + arguments(1, DayOfWeek.FRIDAY), + arguments(8, DayOfWeek.FRIDAY), + arguments(2, DayOfWeek.SATURDAY), + arguments(3, DayOfWeek.SUNDAY), + arguments(4, DayOfWeek.MONDAY), + arguments(5, DayOfWeek.TUESDAY), + arguments(6, DayOfWeek.WEDNESDAY), + arguments(7, DayOfWeek.THURSDAY), + arguments(25, DayOfWeek.MONDAY) + ); + } + + @Nested + @DisplayName("์ผ์š”์ผ์ธ์ง€ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ๋‹ค.") + class Sunday { + + @DisplayName("True") + @ParameterizedTest + @ValueSource(ints = {3, 10, 17, 24, 31}) + void isSunDayTrue(int input) { + // given + Day day = Day.from(input); + + // when + DayOfWeek dayOfWeek = DayOfWeek.from(day); + + // then + assertThat(dayOfWeek.isSunDay()).isTrue(); + } + + @DisplayName("False") + @ParameterizedTest + @ValueSource(ints = {1, 2, 4, 5, 6, 7, 8, 9, 15, 25, 26}) + void isSunDayFalse(int input) { + // given + Day day = Day.from(input); + + // when + DayOfWeek dayOfWeek = DayOfWeek.from(day); + + // then + assertThat(dayOfWeek.isSunDay()).isFalse(); + } + } + + @Nested + @DisplayName("์ฃผ๋ง์ธ์ง€ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ๋‹ค.") + class Weekend { + + @DisplayName("True") + @ParameterizedTest + @ValueSource(ints = {1, 2, 8, 9, 15, 16, 22, 23, 29, 30}) + void isWeekendTrue(int input) { + // given + Day day = Day.from(input); + + // when + DayOfWeek dayOfWeek = DayOfWeek.from(day); + + // then + assertThat(dayOfWeek.isWeekend()).isTrue(); + } + + @DisplayName("False") + @ParameterizedTest + @ValueSource(ints = {3, 4, 5, 6, 7, 10, 11, 12, 13, 19, 20, 21, 24, 25, 31}) + void isWeekendFalse(int input) { + // given + Day day = Day.from(input); + + // when + DayOfWeek dayOfWeek = DayOfWeek.from(day); + + // then + assertThat(dayOfWeek.isWeekend()).isFalse(); + } + } + +} \ No newline at end of file diff --git a/src/test/java/christmas/domain/order/constant/DecemberTest.java b/src/test/java/christmas/domain/order/constant/DecemberTest.java new file mode 100644 index 0000000..17c09e2 --- /dev/null +++ b/src/test/java/christmas/domain/order/constant/DecemberTest.java @@ -0,0 +1,31 @@ +package christmas.domain.order.constant; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +@DisplayName("12์›”์€") +class DecemberTest { + + @Nested + @DisplayName("12์›”์— ์žˆ๋Š” ์ผ์ธ์ง€ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ๋‹ค.") + class InRange { + + @DisplayName(" True") + @ParameterizedTest + @ValueSource(ints = {1, 2, 4, 5, 6, 7, 8, 9, 15, 25, 31}) + void isInRangeTrue(int day) { + assertThat(December.isInRange(day)).isTrue(); + } + + @DisplayName("False") + @ParameterizedTest + @ValueSource(ints = {-10, -1, 0, 32, 33, 57, 88, 999, 15000}) + void isInRangeFalse(int day) { + assertThat(December.isInRange(day)).isFalse(); + } + } +} \ No newline at end of file diff --git a/src/test/java/christmas/domain/order/constant/MenuTest.java b/src/test/java/christmas/domain/order/constant/MenuTest.java new file mode 100644 index 0000000..2c23b6b --- /dev/null +++ b/src/test/java/christmas/domain/order/constant/MenuTest.java @@ -0,0 +1,68 @@ +package christmas.domain.order.constant; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; + +@DisplayName("๋ฉ”๋‰ด๋Š”") +class MenuTest { + + + @Nested + @DisplayName("๋ฉ”๋‰ด ์ด๋ฆ„์„ Menu์— ๋งž๊ฒŒ ์ฐพ๋Š”๋‹ค.") + class Create { + @DisplayName("๋ณ€ํ™˜ ํ•  ์ˆ˜ ์žˆ๋‹ค.") + @ParameterizedTest + @CsvSource(value = {"์–‘์†ก์ด์ˆ˜ํ”„,BUTTON_MUSHROOM_SOUP", "ํƒ€ํŒŒ์Šค,TAPAS", "์‹œ์ €์ƒ๋Ÿฌ๋“œ,CAESAR_SALAD", + "ํ‹ฐ๋ณธ์Šคํ…Œ์ดํฌ,T_BONE_STEAK", "๋ฐ”๋น„ํ๋ฆฝ,BARBECUE_RIBS", "ํ•ด์‚ฐ๋ฌผํŒŒ์Šคํƒ€,SEAFOOD_PASTA", "ํฌ๋ฆฌ์Šค๋งˆ์ŠคํŒŒ์Šคํƒ€,CHRISTMAS_PASTA", + "์ดˆ์ฝ”์ผ€์ดํฌ,CHOCOLATE_CAKE", "์•„์ด์Šคํฌ๋ฆผ,ICE_CREAM", + "์ œ๋กœ์ฝœ๋ผ,ZERO_COLA", "๋ ˆ๋“œ์™€์ธ,RED_WINE", "์ƒดํŽ˜์ธ,CHAMPAGNE"}, delimiter = ',') + void from(String name, Menu target) { + // given // when + Menu menu = Menu.from(name); + + // then + assertThat(menu).isEqualByComparingTo(target); + } + + @DisplayName("ํ•˜์ง€๋งŒ Menu์— ์—†์œผ๋ฉด ์˜ˆ์™ธ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค..") + @ParameterizedTest + @ValueSource(strings = {"์ŠคํŒŒ๊ฒŒํ‹ฐ", "์น˜ํ‚จ", "ํ”ผ์ž"}) + void from(String name) { + assertThatThrownBy(() -> Menu.from(name)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ์œ ํšจํ•˜์ง€ ์•Š์€ ์ฃผ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋‹ค์‹œ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”."); + } + } + + @Nested + @DisplayName("๋ฉ”๋‰ด์˜ ์นดํ…Œ๊ณ ๋ฆฌ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.") + class FindCategory { + @DisplayName("True") + @ParameterizedTest + @CsvSource(value = {"APPETIZER,BUTTON_MUSHROOM_SOUP", "APPETIZER,TAPAS", "APPETIZER,CAESAR_SALAD", + "MAIN,T_BONE_STEAK", "MAIN,BARBECUE_RIBS", "MAIN,SEAFOOD_PASTA", "MAIN,CHRISTMAS_PASTA", + "DESSERT,CHOCOLATE_CAKE", "DESSERT,ICE_CREAM", + "BEVERAGE,ZERO_COLA", "BEVERAGE,RED_WINE", "BEVERAGE,CHAMPAGNE"}, delimiter = ',') + void isCategoryTrue(Category target, Menu menu) { + assertThat(menu.isCategory(target)).isTrue(); + } + + @DisplayName("False") + @ParameterizedTest + @CsvSource(value = {"MAIN,BUTTON_MUSHROOM_SOUP", "DESSERT,TAPAS", "DESSERT,CAESAR_SALAD", + "APPETIZER,T_BONE_STEAK", "APPETIZER,BARBECUE_RIBS", "DESSERT,SEAFOOD_PASTA", "BEVERAGE,CHRISTMAS_PASTA", + "BEVERAGE,CHOCOLATE_CAKE", "MAIN,ICE_CREAM", + "MAIN,ZERO_COLA", "DESSERT,RED_WINE", "DESSERT,CHAMPAGNE"}, delimiter = ',') + void isCategoryFalse(Category target, Menu menu) { + assertThat(menu.isCategory(target)).isFalse(); + } + } + + +} \ No newline at end of file diff --git a/src/test/java/christmas/domain/order/constant/WeekTest.java b/src/test/java/christmas/domain/order/constant/WeekTest.java new file mode 100644 index 0000000..7a5fa3c --- /dev/null +++ b/src/test/java/christmas/domain/order/constant/WeekTest.java @@ -0,0 +1,50 @@ +package christmas.domain.order.constant; + +import static org.assertj.core.api.Assertions.assertThat; + +import christmas.domain.order.Day; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +@DisplayName("์ฃผ๋ง์€") +class WeekTest { + + @Nested + @DisplayName("๋‚ ์งœ๊ฐ€ ์ฃผ๋ง์ธ์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.") + class IsWeekend { + + @DisplayName("True") + @ParameterizedTest + @ValueSource(ints = {8, 9, 15, 16, 22, 23, 29, 30}) + void isWeekendTrue(int day) { + // given + Day orderDay = Day.from(day); + Week week = Week.from(DayOfWeek.from(orderDay)); + + // when + boolean weekend = week.isWeekend(); + + // then + assertThat(weekend).isTrue(); + } + + @DisplayName("False") + @ParameterizedTest + @ValueSource(ints = {3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 17, 18, 19, 20, 21, 24, 25, 26, 27, 28, 31}) + void isWeekendFalse(int day) { + // given + Day orderDay = Day.from(day); + Week week = Week.from(DayOfWeek.from(orderDay)); + + // when + boolean weekend = week.isWeekend(); + + // then + assertThat(weekend).isFalse(); + } + } + + +} \ No newline at end of file diff --git a/src/test/java/christmas/service/event/DiscountServiceTest.java b/src/test/java/christmas/service/event/DiscountServiceTest.java new file mode 100644 index 0000000..e070b32 --- /dev/null +++ b/src/test/java/christmas/service/event/DiscountServiceTest.java @@ -0,0 +1,68 @@ +package christmas.service.event; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.groups.Tuple.tuple; + +import christmas.domain.event.discount.DiscountEventType; +import christmas.domain.event.discount.DiscountRepository; +import christmas.domain.order.Order; +import christmas.domain.order.Day; +import christmas.domain.order.constant.Menu; +import christmas.response.EventResponse; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.TestFactory; +@Nested +@DisplayName("ํ• ์ธ ์„œ๋น„์Šค") +class DiscountServiceTest { + + DiscountService discountService; + + @BeforeEach + void setUp() { + DiscountRepository discountRepository = new DiscountRepository(); + discountService = new DiscountService(discountRepository); + } + + @DisplayName("ํ• ์ธ ์ด๋ฒคํŠธ ์ ์šฉ ์‹œ๋‚˜๋ฆฌ์˜ค") + @TestFactory + Collection getActiveEventResult() { + // given + Order order = Order.of(Day.from(3), + Map.of(Menu.T_BONE_STEAK, 1, Menu.BARBECUE_RIBS, 1, + Menu.CHOCOLATE_CAKE, 2, Menu.ZERO_COLA, 1)); + + discountService.applyEventAll(DiscountEventType.class, order); + + // when // then + return List.of( + DynamicTest.dynamicTest("์ ์šฉ๋œ ํ• ์ธ ์ œ๋ชฉ๊ณผ ๊ธˆ์•ก ๋ฆฌ์ŠคํŠธ๋ฅผ ๊ตฌํ•  ์ˆ˜ ์žˆ๋‹ค.", () -> { + // given // when + List activeEventResult = discountService.getActiveEventResult(); + + // then + assertThat(activeEventResult).hasSize(3) + .extracting("title", "amount") + .containsExactlyInAnyOrder( + tuple("ํฌ๋ฆฌ์Šค๋งˆ์Šค ๋””๋ฐ์ด ํ• ์ธ", 1_200), + tuple("ํ‰์ผ ํ• ์ธ", 4_046), + tuple("ํŠน๋ณ„ ํ• ์ธ", 1_000) + ); + }), + DynamicTest.dynamicTest("์ „์ฒด ์ ์šฉ๋œ ํ˜œํƒ ๊ธˆ์•ก์„ ๊ตฌํ•  ์ˆ˜ ์žˆ๋‹ค.", () -> { + // given // when + int totalAmount = discountService.calculateTotalBenefits(); + + // then + assertThat(totalAmount).isEqualTo(1_200 + 4_046 + 1_000); + }) + ); + + } + +} \ No newline at end of file diff --git a/src/test/java/christmas/service/event/EventDetailServiceTest.java b/src/test/java/christmas/service/event/EventDetailServiceTest.java new file mode 100644 index 0000000..851d2d2 --- /dev/null +++ b/src/test/java/christmas/service/event/EventDetailServiceTest.java @@ -0,0 +1,76 @@ +package christmas.service.event; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.groups.Tuple.tuple; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +import christmas.response.EventDetailResponse; +import christmas.response.EventResponse; +import christmas.response.GiftMenuResponse; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +@Nested +@DisplayName("์ด๋ฒคํŠธ ์ƒ์„ธ ์„œ๋น„์Šค๋Š”") +class EventDetailServiceTest { + + private DiscountService discountService; + private GiftService giftService; + private EventDetailService eventDetailService; + + @BeforeEach + void setUp() { + discountService = mock(DiscountService.class); + giftService = mock(GiftService.class); + eventDetailService = new EventDetailService(discountService, giftService); + } + + @DisplayName("ํ• ์ธ ์ ์šฉ ์„ธ๋ถ€ ์‚ฌํ•ญ์„ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.") + @Test + void getEventDetail() { + // given + given(discountService.calculateTotalBenefits()).willReturn(4_023); + given(giftService.calculateTotalBenefits()).willReturn(25_000); + + given(giftService.getGiftMenuResponse()).willReturn(List.of(GiftMenuResponse.of("์ƒดํŽ˜์ธ", 1))); + + given(discountService.getActiveEventResult()).willReturn( + List.of(EventResponse.of("ํฌ๋ฆฌ์Šค๋งˆ์Šค ๋””๋ฐ์ด ํ• ์ธ", 1_000), + EventResponse.of("ํ‰์ผ ํ• ์ธ", 2_023), + EventResponse.of("ํŠน๋ณ„ ํ• ์ธ", 1_000)) + ); + + given(giftService.getActiveEventResult()).willReturn( + List.of(EventResponse.of("์ฆ์ • ์ด๋ฒคํŠธ", 25_000))); + + int priceBeforeDiscount = 150_000; + + // when + EventDetailResponse eventDetail = eventDetailService.getEventDetail(priceBeforeDiscount); + + // then + assertThat(eventDetail.priceBeforeEvent()).isEqualTo(150_000); + + assertThat(eventDetail.giftMenuResponses()).hasSize(1) + .extracting("title", "count") + .containsExactly(tuple("์ƒดํŽ˜์ธ", 1)); + + assertThat(eventDetail.activeEvents()).hasSize(4) + .extracting("title", "amount") + .containsExactlyInAnyOrder( + tuple("ํฌ๋ฆฌ์Šค๋งˆ์Šค ๋””๋ฐ์ด ํ• ์ธ", 1_000), + tuple("ํ‰์ผ ํ• ์ธ", 2_023), + tuple("ํŠน๋ณ„ ํ• ์ธ", 1_000), + tuple("์ฆ์ • ์ด๋ฒคํŠธ", 25_000) + ); + + assertThat(eventDetail.totalBenefitsAmount()).isEqualTo(29_023); + assertThat(eventDetail.priceAfterEvent()).isEqualTo(150_000 - 4_023); + assertThat(eventDetail.badge()).isEqualTo("์‚ฐํƒ€"); + } + +} \ No newline at end of file diff --git a/src/test/java/christmas/service/event/GiftServiceTest.java b/src/test/java/christmas/service/event/GiftServiceTest.java new file mode 100644 index 0000000..2c0e3c6 --- /dev/null +++ b/src/test/java/christmas/service/event/GiftServiceTest.java @@ -0,0 +1,79 @@ +package christmas.service.event; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.groups.Tuple.tuple; + +import christmas.domain.event.gift.GiftEventType; +import christmas.domain.event.gift.GiftRepository; +import christmas.domain.order.Order; +import christmas.domain.order.Day; +import christmas.domain.order.constant.Menu; +import christmas.response.EventResponse; +import christmas.response.GiftMenuResponse; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.TestFactory; + +@Nested +@DisplayName("์„ ๋ฌผ ์„œ๋น„์Šค") +class GiftServiceTest { + + GiftService giftService; + + @BeforeEach + void setUp() { + GiftRepository giftRepository = new GiftRepository(); + giftService = new GiftService(giftRepository); + } + + @DisplayName("์ฆ์ • ์ด๋ฒคํŠธ ์ ์šฉ ์‹œ๋‚˜๋ฆฌ์˜ค") + @TestFactory + Collection getActiveEventResult() { + // given + Order order = Order.of(Day.from(3), + Map.of(Menu.T_BONE_STEAK, 1, Menu.BARBECUE_RIBS, 1, + Menu.CHOCOLATE_CAKE, 2, Menu.ZERO_COLA, 1)); + + giftService.applyEventAll(GiftEventType.class, order); + + // when // then + return List.of( + DynamicTest.dynamicTest("์ ์šฉ๋œ ํ• ์ธ ์ œ๋ชฉ๊ณผ ๊ธˆ์•ก ๋ฆฌ์ŠคํŠธ๋ฅผ ๊ตฌํ•  ์ˆ˜ ์žˆ๋‹ค.", () -> { + // given // when + List activeEventResult = giftService.getActiveEventResult(); + + // then + assertThat(activeEventResult).hasSize(1) + .extracting("title", "amount") + .containsExactlyInAnyOrder( + tuple("์ฆ์ • ์ด๋ฒคํŠธ", 25_000) + ); + }), + DynamicTest.dynamicTest("์ „์ฒด ์ ์šฉ๋œ ํ˜œํƒ ๊ธˆ์•ก์„ ๊ตฌํ•  ์ˆ˜ ์žˆ๋‹ค.", () -> { + // given // when + int totalAmount = giftService.calculateTotalBenefits(); + + // then + assertThat(totalAmount).isEqualTo(25_000); + }), + DynamicTest.dynamicTest("์ „์ฒด ์ฆ์ •๋˜๋Š” ๋ฉ”๋‰ด ์ด๋ฆ„๊ณผ ์ˆ˜๋Ÿ‰์„ ๊ตฌํ•  ์ˆ˜ ์žˆ๋‹ค.", () -> { + // given // when + List giftMenuResponse = giftService.getGiftMenuResponse(); + + // then + assertThat(giftMenuResponse).hasSize(1) + .extracting("title", "count") + .containsExactlyInAnyOrder( + tuple("์ƒดํŽ˜์ธ", 1) + ); + }) + ); + + } + +} \ No newline at end of file diff --git a/src/test/java/christmas/service/order/OrderServiceTest.java b/src/test/java/christmas/service/order/OrderServiceTest.java new file mode 100644 index 0000000..347755d --- /dev/null +++ b/src/test/java/christmas/service/order/OrderServiceTest.java @@ -0,0 +1,96 @@ +package christmas.service.order; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; +import static org.mockito.BDDMockito.given; + +import christmas.domain.order.Order; +import christmas.domain.order.Day; +import christmas.domain.order.constant.Menu; +import christmas.response.EventDetailResponse; +import christmas.response.EventDetailResponse.EventDetailResponseBuilder; +import christmas.response.EventResponse; +import christmas.response.GiftMenuResponse; +import christmas.response.OrderResponse; +import christmas.response.OrderSummaryResponse; +import christmas.service.event.EventDetailService; +import java.util.List; +import java.util.Map; +import org.assertj.core.groups.Tuple; +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.mockito.Mockito; + +@Nested +@DisplayName("์ฃผ๋ฌธ ์„œ๋น„์Šค๋Š”") +class OrderServiceTest { + + EventDetailService eventDetailService; + OrderService orderService; + + @BeforeEach + void setUp() { + eventDetailService = Mockito.mock(EventDetailService.class); + orderService = new OrderService(eventDetailService); + } + + @DisplayName("์ฃผ๋ฌธ Summary๋ฅผ ์ƒ์„ฑ ํ•  ์ˆ˜ ์žˆ๋‹ค.") + @Test + void createOrderSummary() { + // given + Order order = Order.of(Day.from(3), + Map.of(Menu.T_BONE_STEAK, 1, + Menu.BARBECUE_RIBS, 1, + Menu.CHOCOLATE_CAKE, 2, + Menu.ZERO_COLA, 1)); + + given(eventDetailService.getEventDetail(order.calculateTotalPrice())) + .willReturn(EventDetailResponseBuilder.builder() + .priceBeforeEvent(142_000) + .priceAfterEvent(135_754) + .totalBenefitsAmount(31_246) + .giftMenuResponses(List.of(GiftMenuResponse.of("์ƒดํŽ˜์ธ", 1))) + .activeEvents(List.of(EventResponse.of("ํฌ๋ฆฌ์Šค๋งˆ์Šค ๋””๋ฐ์ด ํ• ์ธ", 1_200), + EventResponse.of("ํ‰์ผ ํ• ์ธ", 4_046), + EventResponse.of("ํŠน๋ณ„ ํ• ์ธ", 1_000), + EventResponse.of("์ฆ์ • ์ด๋ฒคํŠธ", 2_5000))) + .badge("์‚ฐํƒ€") + .build()); + + // when + OrderSummaryResponse orderSummary = orderService.createOrderSummary(order); + + // then + OrderResponse orderResponse = orderSummary.orderResponse(); + + assertThat(orderResponse.day()).isEqualTo(3); + assertThat(orderResponse.menuCount()).hasSize(4) + .extracting("title", "count") + .containsExactlyInAnyOrder( + tuple("ํ‹ฐ๋ณธ์Šคํ…Œ์ดํฌ", 1), + tuple("๋ฐ”๋น„ํ๋ฆฝ", 1), + tuple("์ดˆ์ฝ”์ผ€์ดํฌ", 2), + tuple("์ œ๋กœ์ฝœ๋ผ", 1) + ); + + EventDetailResponse eventDetail = orderSummary.eventDetailResponse(); + + assertThat(eventDetail.priceBeforeEvent()).isEqualTo(142_000); + assertThat(eventDetail.giftMenuResponses()).hasSize(1) + .extracting("title", "count") + .containsExactly(Tuple.tuple("์ƒดํŽ˜์ธ", 1)); + assertThat(eventDetail.activeEvents()).hasSize(4) + .extracting("title", "amount") + .containsExactlyInAnyOrder( + Tuple.tuple("ํฌ๋ฆฌ์Šค๋งˆ์Šค ๋””๋ฐ์ด ํ• ์ธ", 1_200), + Tuple.tuple("ํ‰์ผ ํ• ์ธ", 4_046), + Tuple.tuple("ํŠน๋ณ„ ํ• ์ธ", 1_000), + Tuple.tuple("์ฆ์ • ์ด๋ฒคํŠธ", 25_000) + ); + assertThat(eventDetail.totalBenefitsAmount()).isEqualTo(31_246); + assertThat(eventDetail.priceAfterEvent()).isEqualTo(135_754); + assertThat(eventDetail.badge()).isEqualTo("์‚ฐํƒ€"); + } +} \ No newline at end of file diff --git a/src/test/java/christmas/view/InputViewTest.java b/src/test/java/christmas/view/InputViewTest.java new file mode 100644 index 0000000..e24e7f3 --- /dev/null +++ b/src/test/java/christmas/view/InputViewTest.java @@ -0,0 +1,80 @@ +package christmas.view; + +import static java.lang.System.lineSeparator; +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +@Nested +@DisplayName("์ž…๋ ฅ ๋ทฐ๋Š”") +class InputViewTest { + + TestWriter writer = new TestWriter(); + InputView inputView; + + + @BeforeEach + void setUp() { + writer.clear(); + } + + @DisplayName("๋‚ ์งœ๋ฅผ ์ž…๋ ฅํ•˜๋ผ๋Š” ๋ฌธ์ž๋ฅผ ์ถœ๋ ฅํ•  ์ˆ˜ ์žˆ๋‹ค.") + @Test + void readDateWriter() { + // given + inputView = new InputView(new TestReader("3"), writer); + + // when + inputView.readDate(); + + // then + assertThat(writer.getString()) + .isEqualTo("12์›” ์ค‘ ์‹๋‹น ์˜ˆ์ƒ ๋ฐฉ๋ฌธ ๋‚ ์งœ๋Š” ์–ธ์ œ์ธ๊ฐ€์š”? (์ˆซ์ž๋งŒ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”!)" + lineSeparator()); + } + + @DisplayName("๋ฌธ์ž๋ฅผ ์ž…๋ ฅ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.") + @Test + void readDateReader() { + // given + inputView = new InputView(new TestReader("3"), writer); + + // when + String day = inputView.readDate(); + + // then + assertThat(day) + .isEqualTo("3"); + } + + @DisplayName("๋ฉ”๋‰ด์™€ ์ˆ˜๋Ÿ‰์„ ์ž…๋ ฅํ•˜๋ผ๋Š” ๋ฌธ์ž๋ฅผ ์ถœ๋ ฅํ•  ์ˆ˜ ์žˆ๋‹ค.") + @Test + void readMenuAndCountWriter() { + // given + inputView = new InputView(new TestReader("ํƒ€ํŒŒ์Šค-1,์ œ๋กœ์ฝœ๋ผ-1"), writer); + // when + inputView.readMenuAndCount(); + + // then + assertThat(writer.getString()) + .isEqualTo("์ฃผ๋ฌธํ•˜์‹ค ๋ฉ”๋‰ด๋ฅผ ๋ฉ”๋‰ด์™€ ๊ฐœ์ˆ˜๋ฅผ ์•Œ๋ ค ์ฃผ์„ธ์š”. (e.g. ํ•ด์‚ฐ๋ฌผํŒŒ์Šคํƒ€-2,๋ ˆ๋“œ์™€์ธ-1,์ดˆ์ฝ”์ผ€์ดํฌ-1)" + + lineSeparator()); + } + + @DisplayName("๋ฉ”๋‰ด์™€ ์ˆ˜๋Ÿ‰์„ ์ž…๋ ฅ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.") + @Test + void readMenuAndCountReader() { + // given + inputView = new InputView(new TestReader("ํƒ€ํŒŒ์Šค-1,์ œ๋กœ์ฝœ๋ผ-1"), writer); + + // when + inputView.readMenuAndCount(); + + // then + assertThat(writer.getString()) + .isEqualTo("์ฃผ๋ฌธํ•˜์‹ค ๋ฉ”๋‰ด๋ฅผ ๋ฉ”๋‰ด์™€ ๊ฐœ์ˆ˜๋ฅผ ์•Œ๋ ค ์ฃผ์„ธ์š”. (e.g. ํ•ด์‚ฐ๋ฌผํŒŒ์Šคํƒ€-2,๋ ˆ๋“œ์™€์ธ-1,์ดˆ์ฝ”์ผ€์ดํฌ-1)" + + lineSeparator()); + } +} \ No newline at end of file diff --git a/src/test/java/christmas/view/OutputViewTest.java b/src/test/java/christmas/view/OutputViewTest.java new file mode 100644 index 0000000..a3b5662 --- /dev/null +++ b/src/test/java/christmas/view/OutputViewTest.java @@ -0,0 +1,178 @@ +package christmas.view; + +import static java.lang.System.lineSeparator; +import static org.assertj.core.api.Assertions.assertThat; + +import christmas.exception.ErrorMessage; +import christmas.response.EventDetailResponse; +import christmas.response.EventDetailResponse.EventDetailResponseBuilder; +import christmas.response.EventResponse; +import christmas.response.GiftMenuResponse; +import christmas.response.MenuCountResponse; +import christmas.response.OrderResponse; +import christmas.response.OrderSummaryResponse; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +@Nested +@DisplayName("์ถœ๋ ฅ ๋ทฐ๋Š”") +class OutputViewTest { + + TestWriter writer = new TestWriter(); + OutputView outputView; + + + @BeforeEach + void setUp() { + writer.clear(); + outputView = new OutputView(writer); + } + + @DisplayName("์ดˆ๊ธฐ ์ธ์‚ฌ๋ฅผ ์ถœ๋ ฅํ•  ์ˆ˜ ์žˆ๋‹ค.") + @Test + void printHello() { + // given // when + outputView.printHello(); + + // then + assertThat(writer.getString()) + .isEqualTo("์•ˆ๋…•ํ•˜์„ธ์š”! ์šฐํ…Œ์ฝ” ์‹๋‹น 12์›” ์ด๋ฒคํŠธ ํ”Œ๋ž˜๋„ˆ์ž…๋‹ˆ๋‹ค." + lineSeparator()); + } + + @DisplayName("์—๋Ÿฌ๋ฉ”์‹œ์ง€๋ฅผ ์ถœ๋ ฅํ•  ์ˆ˜ ์žˆ๋‹ค.") + @Test + void printError() { + // given + String message = ErrorMessage.INVALID_ORDER.getMessage(); + + // when + outputView.printError(message); + + // then + assertThat(writer.getString()) + .isEqualTo("[ERROR] ์œ ํšจํ•˜์ง€ ์•Š์€ ์ฃผ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋‹ค์‹œ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”." + lineSeparator()); + } + + @Nested + @DisplayName("์ฃผ๋ฌธ ํ•ดํƒ ์š”์•ฝ ์ •๋ณด๋ฅผ ์ถœ๋ ฅํ•  ์ˆ˜ ์žˆ๋‹ค.") + class PrintOrderSummary { + + @DisplayName("ํ•ดํƒ์ด ์กด์žฌํ•œ๋‹ค.") + @Test + void printOrderSummary() { + // given + OrderResponse orderResponse = OrderResponse.of(3, List.of( + MenuCountResponse.of("ํ‹ฐ๋ณธ์Šคํ…Œ์ดํฌ", 1), + MenuCountResponse.of("๋ฐ”๋น„ํ๋ฆฝ", 1), + MenuCountResponse.of("์ดˆ์ฝ”์ผ€์ดํฌ", 2), + MenuCountResponse.of("์ œ๋กœ์ฝœ๋ผ", 1))); + + EventDetailResponse eventDetailResponse = EventDetailResponseBuilder.builder() + .badge("์‚ฐํƒ€") + .totalBenefitsAmount(31_246) + .priceBeforeEvent(142_000) + .priceAfterEvent(135_754) + .giftMenuResponses(List.of(GiftMenuResponse.of("์ƒดํŽ˜์ธ", 1))) + .activeEvents(List.of( + EventResponse.of("ํฌ๋ฆฌ์Šค๋งˆ์Šค ๋””๋ฐ์ด ํ• ์ธ", 1_200), + EventResponse.of("ํ‰์ผ ํ• ์ธ", 4_046), + EventResponse.of("ํŠน๋ณ„ ํ• ์ธ", 1_000), + EventResponse.of("์ฆ์ • ์ด๋ฒคํŠธ", 25_000))) + .build(); + + OrderSummaryResponse orderSummaryResponse = OrderSummaryResponse.of(orderResponse, eventDetailResponse); + + // when + outputView.printOrderSummary(orderSummaryResponse); + + // then + assertThat(writer.getString()) + .isEqualTo(""" + 12์›” 3์ผ์— ์šฐํ…Œ์ฝ” ์‹๋‹น์—์„œ ๋ฐ›์„ ์ด๋ฒคํŠธ ํ˜œํƒ ๋ฏธ๋ฆฌ ๋ณด๊ธฐ! + + <์ฃผ๋ฌธ ๋ฉ”๋‰ด> + ํ‹ฐ๋ณธ์Šคํ…Œ์ดํฌ 1๊ฐœ + ๋ฐ”๋น„ํ๋ฆฝ 1๊ฐœ + ์ดˆ์ฝ”์ผ€์ดํฌ 2๊ฐœ + ์ œ๋กœ์ฝœ๋ผ 1๊ฐœ + + <ํ• ์ธ ์ „ ์ด์ฃผ๋ฌธ ๊ธˆ์•ก> + 142,000์› + + <์ฆ์ • ๋ฉ”๋‰ด> + ์ƒดํŽ˜์ธ 1๊ฐœ + + <ํ˜œํƒ ๋‚ด์—ญ> + ํฌ๋ฆฌ์Šค๋งˆ์Šค ๋””๋ฐ์ด ํ• ์ธ: -1,200์› + ํ‰์ผ ํ• ์ธ: -4,046์› + ํŠน๋ณ„ ํ• ์ธ: -1,000์› + ์ฆ์ • ์ด๋ฒคํŠธ: -25,000์› + + <์ดํ˜œํƒ ๊ธˆ์•ก> + -31,246์› + + <ํ• ์ธ ํ›„ ์˜ˆ์ƒ ๊ฒฐ์ œ ๊ธˆ์•ก> + 135,754์› + + <12์›” ์ด๋ฒคํŠธ ๋ฐฐ์ง€> + ์‚ฐํƒ€ + """); + } + + @DisplayName("์ ์šฉ๋œ ํ˜œํƒ์ด ์—†์„ ๊ฒฝ์šฐ ์—†์Œ์„ ์ถœ๋ ฅํ•œ๋‹ค.") + @Test + void printOrderSummaryNone() { + // given + OrderResponse orderResponse = OrderResponse.of(26, List.of( + MenuCountResponse.of("ํƒ€ํŒŒ์Šค", 1), + MenuCountResponse.of("์ œ๋กœ์ฝœ๋ผ", 1))); + + EventDetailResponse eventDetailResponse = EventDetailResponseBuilder.builder() + .badge("") + .totalBenefitsAmount(0) + .priceBeforeEvent(8_500) + .priceAfterEvent(8_500) + .giftMenuResponses(List.of()) + .activeEvents(List.of()) + .build(); + + OrderSummaryResponse orderSummaryResponse = OrderSummaryResponse.of(orderResponse, eventDetailResponse); + + // when + outputView.printOrderSummary(orderSummaryResponse); + + // then + assertThat(writer.getString()) + .isEqualTo(""" + 12์›” 26์ผ์— ์šฐํ…Œ์ฝ” ์‹๋‹น์—์„œ ๋ฐ›์„ ์ด๋ฒคํŠธ ํ˜œํƒ ๋ฏธ๋ฆฌ ๋ณด๊ธฐ! + + <์ฃผ๋ฌธ ๋ฉ”๋‰ด> + ํƒ€ํŒŒ์Šค 1๊ฐœ + ์ œ๋กœ์ฝœ๋ผ 1๊ฐœ + + <ํ• ์ธ ์ „ ์ด์ฃผ๋ฌธ ๊ธˆ์•ก> + 8,500์› + + <์ฆ์ • ๋ฉ”๋‰ด> + ์—†์Œ + + <ํ˜œํƒ ๋‚ด์—ญ> + ์—†์Œ + + <์ดํ˜œํƒ ๊ธˆ์•ก> + 0์› + + <ํ• ์ธ ํ›„ ์˜ˆ์ƒ ๊ฒฐ์ œ ๊ธˆ์•ก> + 8,500์› + + <12์›” ์ด๋ฒคํŠธ ๋ฐฐ์ง€> + ์—†์Œ + """); + } + } + + +} \ No newline at end of file diff --git a/src/test/java/christmas/view/TestReader.java b/src/test/java/christmas/view/TestReader.java new file mode 100644 index 0000000..6e6a730 --- /dev/null +++ b/src/test/java/christmas/view/TestReader.java @@ -0,0 +1,21 @@ +package christmas.view; + +import christmas.view.io.Reader; + +public class TestReader implements Reader { + + private final String input; + + public TestReader(final String input) { + this.input = input; + } + + @Override + public String readLine() { + return input; + } + + @Override + public void close() { + } +} diff --git a/src/test/java/christmas/view/TestWriter.java b/src/test/java/christmas/view/TestWriter.java new file mode 100644 index 0000000..16cff85 --- /dev/null +++ b/src/test/java/christmas/view/TestWriter.java @@ -0,0 +1,28 @@ +package christmas.view; + +import static java.lang.System.lineSeparator; + +import christmas.view.io.Writer; + +public class TestWriter implements Writer { + + private static StringBuilder stringBuilder = new StringBuilder(); + + @Override + public void println(String input) { + stringBuilder.append(input).append(lineSeparator()); + } + + @Override + public void printf(String format, Object... args) { + stringBuilder.append(String.format(format, args)); + } + + public void clear() { + stringBuilder = new StringBuilder(); + } + + public String getString() { + return stringBuilder.toString(); + } +} \ No newline at end of file