From d0ec2cbcca6739ea8eedc04bdc999c0ecf25789b Mon Sep 17 00:00:00 2001 From: "jinwoo.oh" Date: Mon, 17 Feb 2025 23:15:31 +0900 Subject: [PATCH 1/7] =?UTF-8?q?=F0=9F=9A=80=201=EB=8B=A8=EA=B3=84=20-=20?= =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=84=B0=EB=A7=81(=EC=83=81=ED=92=88)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit doc : 요구사항 작성 --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 4a91e3f71..2439978f4 100644 --- a/README.md +++ b/README.md @@ -199,3 +199,13 @@ docker compose -p kitchenpos up -d - `Order`는 접수 대기 ➜ 접수 ➜ 서빙 ➜ 계산 완료 순서로 진행된다. - `OrderLineItem`는 가격과 수량을 가진다. - `OrderLineItem`의 수량은 1보다 커야 한다. + + +#### step1 요구사항 + +- 키친포스의 요구 사항과 용어 사전, 모델링을 기반으로 상품 CONTEXT를 리팩터링한다. +- 상품 CONTEXT의 도메인 계층만 먼저 구현한다. +- products 패키지 밑에 tobe.domain 패키지를 만들고 거기서부터 구현을 시작한다. +- 용어 사전과 모델링이 부자연스럽거나 불완전하거나 잘못된 경우 지속적으로 수정한다. +- 새로운 모델에 맞게끔 클래스, 메서드, 모듈의 이름을 다시 지으면서 코드를 리팩터링한다. +- REPOSITORY 구현 시 자신에게 익숙하고 편한 것을 선택하여 진행한다. From e6cc1f67afcaabf87837b1a34f86d18fd78edd85 Mon Sep 17 00:00:00 2001 From: "jinwoo.oh" Date: Mon, 17 Feb 2025 23:17:54 +0900 Subject: [PATCH 2/7] =?UTF-8?q?feat=20:=20=EC=9A=94=EA=B5=AC=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EA=B8=B0=EC=A4=80=EC=9C=BC=EB=A1=9C=20=EB=A6=AC?= =?UTF-8?q?=ED=8E=99=ED=86=A0=EB=A7=81=20=EC=A7=84=ED=96=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 2 +- .../menus/application/MenuService.java | 6 +- .../kitchenpos/menus/domain/MenuProduct.java | 2 +- .../application/ProductService.java | 8 +- .../domain/JpaProductRepository.java | 2 +- .../products/{ => asis}/domain/Product.java | 2 +- .../{ => asis}/domain/ProductRepository.java | 2 +- .../infra/DefaultPurgomalumClient.java | 2 +- .../{ => asis}/infra/PurgomalumClient.java | 2 +- .../tobe/application/ProductService.java | 74 +++++++++++++++++++ .../application/dto/ProductCreateRequest.java | 21 ++++++ .../dto/ProductPriceUpdateRequest.java | 15 ++++ .../tobe/application/dto/ProductResponse.java | 33 +++++++++ .../products/tobe/domain/Product.java | 56 ++++++++++++++ .../products/tobe/domain/ProductName.java | 34 +++++++++ .../products/tobe/domain/ProductPrice.java | 33 +++++++++ .../tobe/domain/ProductRepository.java | 16 ++++ .../tobe/infra/DefaultPurgomalumClient.java | 25 +++++++ .../tobe/infra/JpaProductRepository.java | 10 +++ .../products/tobe/infra/PurgomalumClient.java | 5 ++ .../products/ui/ProductRestController.java | 16 ++-- 21 files changed, 346 insertions(+), 20 deletions(-) rename src/main/java/kitchenpos/products/{ => asis}/application/ProductService.java (92%) rename src/main/java/kitchenpos/products/{ => asis}/domain/JpaProductRepository.java (81%) rename src/main/java/kitchenpos/products/{ => asis}/domain/Product.java (95%) rename src/main/java/kitchenpos/products/{ => asis}/domain/ProductRepository.java (86%) rename src/main/java/kitchenpos/products/{ => asis}/infra/DefaultPurgomalumClient.java (95%) rename src/main/java/kitchenpos/products/{ => asis}/infra/PurgomalumClient.java (67%) create mode 100644 src/main/java/kitchenpos/products/tobe/application/ProductService.java create mode 100644 src/main/java/kitchenpos/products/tobe/application/dto/ProductCreateRequest.java create mode 100644 src/main/java/kitchenpos/products/tobe/application/dto/ProductPriceUpdateRequest.java create mode 100644 src/main/java/kitchenpos/products/tobe/application/dto/ProductResponse.java create mode 100644 src/main/java/kitchenpos/products/tobe/domain/Product.java create mode 100644 src/main/java/kitchenpos/products/tobe/domain/ProductName.java create mode 100644 src/main/java/kitchenpos/products/tobe/domain/ProductPrice.java create mode 100644 src/main/java/kitchenpos/products/tobe/domain/ProductRepository.java create mode 100644 src/main/java/kitchenpos/products/tobe/infra/DefaultPurgomalumClient.java create mode 100644 src/main/java/kitchenpos/products/tobe/infra/JpaProductRepository.java create mode 100644 src/main/java/kitchenpos/products/tobe/infra/PurgomalumClient.java diff --git a/build.gradle.kts b/build.gradle.kts index 67ab16154..bace56f3b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -13,7 +13,7 @@ group = "camp.nextstep.edu" version = "0.0.1-SNAPSHOT" java { - sourceCompatibility = JavaVersion.VERSION_21 + sourceCompatibility = JavaVersion.VERSION_17 } repositories { diff --git a/src/main/java/kitchenpos/menus/application/MenuService.java b/src/main/java/kitchenpos/menus/application/MenuService.java index abefa5bcf..88ef31359 100644 --- a/src/main/java/kitchenpos/menus/application/MenuService.java +++ b/src/main/java/kitchenpos/menus/application/MenuService.java @@ -5,9 +5,9 @@ import kitchenpos.menus.domain.MenuGroupRepository; import kitchenpos.menus.domain.MenuProduct; import kitchenpos.menus.domain.MenuRepository; -import kitchenpos.products.domain.Product; -import kitchenpos.products.domain.ProductRepository; -import kitchenpos.products.infra.PurgomalumClient; +import kitchenpos.products.asis.domain.Product; +import kitchenpos.products.asis.domain.ProductRepository; +import kitchenpos.products.asis.infra.PurgomalumClient; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; diff --git a/src/main/java/kitchenpos/menus/domain/MenuProduct.java b/src/main/java/kitchenpos/menus/domain/MenuProduct.java index b47ca26cb..14addb1fb 100644 --- a/src/main/java/kitchenpos/menus/domain/MenuProduct.java +++ b/src/main/java/kitchenpos/menus/domain/MenuProduct.java @@ -10,7 +10,7 @@ import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import jakarta.persistence.Transient; -import kitchenpos.products.domain.Product; +import kitchenpos.products.asis.domain.Product; import java.util.UUID; diff --git a/src/main/java/kitchenpos/products/application/ProductService.java b/src/main/java/kitchenpos/products/asis/application/ProductService.java similarity index 92% rename from src/main/java/kitchenpos/products/application/ProductService.java rename to src/main/java/kitchenpos/products/asis/application/ProductService.java index 20cf63996..a4edbe9d4 100644 --- a/src/main/java/kitchenpos/products/application/ProductService.java +++ b/src/main/java/kitchenpos/products/asis/application/ProductService.java @@ -1,11 +1,11 @@ -package kitchenpos.products.application; +package kitchenpos.products.asis.application; import kitchenpos.menus.domain.Menu; import kitchenpos.menus.domain.MenuProduct; import kitchenpos.menus.domain.MenuRepository; -import kitchenpos.products.domain.Product; -import kitchenpos.products.domain.ProductRepository; -import kitchenpos.products.infra.PurgomalumClient; +import kitchenpos.products.asis.domain.Product; +import kitchenpos.products.asis.domain.ProductRepository; +import kitchenpos.products.asis.infra.PurgomalumClient; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; diff --git a/src/main/java/kitchenpos/products/domain/JpaProductRepository.java b/src/main/java/kitchenpos/products/asis/domain/JpaProductRepository.java similarity index 81% rename from src/main/java/kitchenpos/products/domain/JpaProductRepository.java rename to src/main/java/kitchenpos/products/asis/domain/JpaProductRepository.java index 90b069779..5faa9c9c4 100644 --- a/src/main/java/kitchenpos/products/domain/JpaProductRepository.java +++ b/src/main/java/kitchenpos/products/asis/domain/JpaProductRepository.java @@ -1,4 +1,4 @@ -package kitchenpos.products.domain; +package kitchenpos.products.asis.domain; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/kitchenpos/products/domain/Product.java b/src/main/java/kitchenpos/products/asis/domain/Product.java similarity index 95% rename from src/main/java/kitchenpos/products/domain/Product.java rename to src/main/java/kitchenpos/products/asis/domain/Product.java index ee2a7dfa9..0cdf3eb11 100644 --- a/src/main/java/kitchenpos/products/domain/Product.java +++ b/src/main/java/kitchenpos/products/asis/domain/Product.java @@ -1,4 +1,4 @@ -package kitchenpos.products.domain; +package kitchenpos.products.asis.domain; import jakarta.persistence.Column; import jakarta.persistence.Entity; diff --git a/src/main/java/kitchenpos/products/domain/ProductRepository.java b/src/main/java/kitchenpos/products/asis/domain/ProductRepository.java similarity index 86% rename from src/main/java/kitchenpos/products/domain/ProductRepository.java rename to src/main/java/kitchenpos/products/asis/domain/ProductRepository.java index 3637e4232..074dcfe8e 100644 --- a/src/main/java/kitchenpos/products/domain/ProductRepository.java +++ b/src/main/java/kitchenpos/products/asis/domain/ProductRepository.java @@ -1,4 +1,4 @@ -package kitchenpos.products.domain; +package kitchenpos.products.asis.domain; import java.util.List; import java.util.Optional; diff --git a/src/main/java/kitchenpos/products/infra/DefaultPurgomalumClient.java b/src/main/java/kitchenpos/products/asis/infra/DefaultPurgomalumClient.java similarity index 95% rename from src/main/java/kitchenpos/products/infra/DefaultPurgomalumClient.java rename to src/main/java/kitchenpos/products/asis/infra/DefaultPurgomalumClient.java index 87dba885c..6c9afdeba 100644 --- a/src/main/java/kitchenpos/products/infra/DefaultPurgomalumClient.java +++ b/src/main/java/kitchenpos/products/asis/infra/DefaultPurgomalumClient.java @@ -1,4 +1,4 @@ -package kitchenpos.products.infra; +package kitchenpos.products.asis.infra; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.stereotype.Component; diff --git a/src/main/java/kitchenpos/products/infra/PurgomalumClient.java b/src/main/java/kitchenpos/products/asis/infra/PurgomalumClient.java similarity index 67% rename from src/main/java/kitchenpos/products/infra/PurgomalumClient.java rename to src/main/java/kitchenpos/products/asis/infra/PurgomalumClient.java index 4002a2bb8..7c5f6b503 100644 --- a/src/main/java/kitchenpos/products/infra/PurgomalumClient.java +++ b/src/main/java/kitchenpos/products/asis/infra/PurgomalumClient.java @@ -1,4 +1,4 @@ -package kitchenpos.products.infra; +package kitchenpos.products.asis.infra; public interface PurgomalumClient { boolean containsProfanity(String text); diff --git a/src/main/java/kitchenpos/products/tobe/application/ProductService.java b/src/main/java/kitchenpos/products/tobe/application/ProductService.java new file mode 100644 index 000000000..cb4395ca3 --- /dev/null +++ b/src/main/java/kitchenpos/products/tobe/application/ProductService.java @@ -0,0 +1,74 @@ +package kitchenpos.products.tobe.application; + +import java.math.BigDecimal; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.UUID; +import kitchenpos.menus.domain.Menu; +import kitchenpos.menus.domain.MenuProduct; +import kitchenpos.menus.domain.MenuRepository; +import kitchenpos.products.tobe.application.dto.ProductCreateRequest; +import kitchenpos.products.tobe.application.dto.ProductPriceUpdateRequest; +import kitchenpos.products.tobe.application.dto.ProductResponse; +import kitchenpos.products.tobe.domain.Product; +import kitchenpos.products.tobe.domain.ProductRepository; +import kitchenpos.products.tobe.infra.PurgomalumClient; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class ProductService { + + private final ProductRepository jpaProductRepository; + private final MenuRepository menuRepository; + private final PurgomalumClient purgomalumClient; + + public ProductService( + final ProductRepository productRepository, + final MenuRepository menuRepository, + final PurgomalumClient purgomalumClient + ) { + this.jpaProductRepository = productRepository; + this.menuRepository = menuRepository; + this.purgomalumClient = purgomalumClient; + } + + @Transactional + public ProductResponse create(ProductCreateRequest request) { + Product product = jpaProductRepository.save(Product.of(request.getName(), request.getPrice(), purgomalumClient)); + + return ProductResponse.toResponse(product); + } + + @Transactional + public ProductResponse changePrice(UUID productId, ProductPriceUpdateRequest request) { + + Product product = jpaProductRepository.findById(productId) + .orElseThrow(NoSuchElementException::new); + product.updatePrice(request.getPrice()); + + final List menus = menuRepository.findAllByProductId(productId); + for (final Menu menu : menus) { + BigDecimal sum = BigDecimal.ZERO; + for (final MenuProduct menuProduct : menu.getMenuProducts()) { + sum = sum.add( + menuProduct.getProduct() + .getPrice() + .multiply(BigDecimal.valueOf(menuProduct.getQuantity())) + ); + } + if (menu.getPrice().compareTo(sum) > 0) { + menu.setDisplayed(false); + } + } + return ProductResponse.toResponse(product); + } + + @Transactional(readOnly = true) + public List findAll() { + return jpaProductRepository.findAll() + .stream() + .map(ProductResponse::toResponse) + .toList(); + } +} diff --git a/src/main/java/kitchenpos/products/tobe/application/dto/ProductCreateRequest.java b/src/main/java/kitchenpos/products/tobe/application/dto/ProductCreateRequest.java new file mode 100644 index 000000000..376a9efe5 --- /dev/null +++ b/src/main/java/kitchenpos/products/tobe/application/dto/ProductCreateRequest.java @@ -0,0 +1,21 @@ +package kitchenpos.products.tobe.application.dto; + +import java.math.BigDecimal; + +public class ProductCreateRequest { + private final String name; + private final BigDecimal price; + + public ProductCreateRequest(String name, BigDecimal price) { + this.name = name; + this.price = price; + } + + public String getName() { + return name; + } + + public BigDecimal getPrice() { + return price; + } +} diff --git a/src/main/java/kitchenpos/products/tobe/application/dto/ProductPriceUpdateRequest.java b/src/main/java/kitchenpos/products/tobe/application/dto/ProductPriceUpdateRequest.java new file mode 100644 index 000000000..6ec7cda13 --- /dev/null +++ b/src/main/java/kitchenpos/products/tobe/application/dto/ProductPriceUpdateRequest.java @@ -0,0 +1,15 @@ +package kitchenpos.products.tobe.application.dto; + +import java.math.BigDecimal; + +public class ProductPriceUpdateRequest { + private final BigDecimal price; + + public ProductPriceUpdateRequest(BigDecimal price) { + this.price = price; + } + + public BigDecimal getPrice() { + return price; + } +} diff --git a/src/main/java/kitchenpos/products/tobe/application/dto/ProductResponse.java b/src/main/java/kitchenpos/products/tobe/application/dto/ProductResponse.java new file mode 100644 index 000000000..55567734c --- /dev/null +++ b/src/main/java/kitchenpos/products/tobe/application/dto/ProductResponse.java @@ -0,0 +1,33 @@ +package kitchenpos.products.tobe.application.dto; + +import java.math.BigDecimal; +import java.util.UUID; +import kitchenpos.products.tobe.domain.Product; + +public class ProductResponse { + private final UUID id; + private final String name; + private final BigDecimal price; + + public ProductResponse(UUID id, String name, BigDecimal price) { + this.id = id; + this.name = name; + this.price = price; + } + + public UUID getId() { + return id; + } + + public String getName() { + return name; + } + + public BigDecimal getPrice() { + return price; + } + + public static ProductResponse toResponse(Product product) { + return new ProductResponse(product.getId(), product.getName(), product.getPrice()); + } +} diff --git a/src/main/java/kitchenpos/products/tobe/domain/Product.java b/src/main/java/kitchenpos/products/tobe/domain/Product.java new file mode 100644 index 000000000..5d85c92cc --- /dev/null +++ b/src/main/java/kitchenpos/products/tobe/domain/Product.java @@ -0,0 +1,56 @@ +package kitchenpos.products.tobe.domain; + +import jakarta.persistence.Column; +import jakarta.persistence.Embedded; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; + +import java.math.BigDecimal; +import java.util.UUID; +import kitchenpos.products.tobe.infra.PurgomalumClient; + +@Table(name = "product") +@Entity +public class Product { + + @Column(name = "id", columnDefinition = "binary(16)") + @Id + private UUID id; + + @Embedded + private ProductName name; + + @Embedded + private ProductPrice price; + + protected Product() { + } + + private Product(ProductName name, ProductPrice price) { + this.id = UUID.randomUUID(); + this.name = name; + this.price = price; + } + + public static Product of(String name, BigDecimal price, PurgomalumClient purgomalumClient) { + return new Product(ProductName.from(name, purgomalumClient), + ProductPrice.from(price)); + } + + public UUID getId() { + return id; + } + + public String getName() { + return name.getName(); + } + + public BigDecimal getPrice() { + return price.getPrice(); + } + + public void updatePrice(BigDecimal price) { + this.price = ProductPrice.from(price); + } +} diff --git a/src/main/java/kitchenpos/products/tobe/domain/ProductName.java b/src/main/java/kitchenpos/products/tobe/domain/ProductName.java new file mode 100644 index 000000000..24e7c6058 --- /dev/null +++ b/src/main/java/kitchenpos/products/tobe/domain/ProductName.java @@ -0,0 +1,34 @@ +package kitchenpos.products.tobe.domain; + +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import kitchenpos.products.tobe.infra.PurgomalumClient; + +@Embeddable +public class ProductName { + + @Column(name = "name", nullable = false) + private String name; + + protected ProductName() { + } + + private ProductName(String name) { + this.name = name; + } + + public static ProductName from(String name, PurgomalumClient purgomalumClient) { + if (name == null || name.isEmpty()) { + throw new IllegalArgumentException("상품명은 필수로 입력해야 합니다."); + } + + if (purgomalumClient.containsProfanity(name)) { + throw new IllegalArgumentException("비속어가 포함되어 있습니다."); + } + return new ProductName(name); + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/kitchenpos/products/tobe/domain/ProductPrice.java b/src/main/java/kitchenpos/products/tobe/domain/ProductPrice.java new file mode 100644 index 000000000..5c00c4a8a --- /dev/null +++ b/src/main/java/kitchenpos/products/tobe/domain/ProductPrice.java @@ -0,0 +1,33 @@ +package kitchenpos.products.tobe.domain; + +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import java.math.BigDecimal; + +@Embeddable +public class ProductPrice { + + @Column(name = "price", nullable = false) + private BigDecimal price; + + protected ProductPrice() {} + + private ProductPrice(BigDecimal price) { + this.price = price; + } + + public static ProductPrice from(BigDecimal price) { + validate(price); + return new ProductPrice(price); + } + + private static void validate(BigDecimal price) { + if (price == null || price.compareTo(BigDecimal.ZERO) < 0) { + throw new IllegalArgumentException("가격은 0 이상이어야 합니다."); + } + } + + public BigDecimal getPrice() { + return price; + } +} diff --git a/src/main/java/kitchenpos/products/tobe/domain/ProductRepository.java b/src/main/java/kitchenpos/products/tobe/domain/ProductRepository.java new file mode 100644 index 000000000..a41daecdb --- /dev/null +++ b/src/main/java/kitchenpos/products/tobe/domain/ProductRepository.java @@ -0,0 +1,16 @@ +package kitchenpos.products.tobe.domain; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +public interface ProductRepository { + Product save(Product product); + + Optional findById(UUID id); + + List findAll(); + + List findAllByIdIn(List ids); +} + diff --git a/src/main/java/kitchenpos/products/tobe/infra/DefaultPurgomalumClient.java b/src/main/java/kitchenpos/products/tobe/infra/DefaultPurgomalumClient.java new file mode 100644 index 000000000..45722d636 --- /dev/null +++ b/src/main/java/kitchenpos/products/tobe/infra/DefaultPurgomalumClient.java @@ -0,0 +1,25 @@ +package kitchenpos.products.tobe.infra; + +import java.net.URI; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +@Component +public class DefaultPurgomalumClient implements PurgomalumClient { + private final RestTemplate restTemplate; + + public DefaultPurgomalumClient(final RestTemplateBuilder restTemplateBuilder) { + this.restTemplate = restTemplateBuilder.build(); + } + + @Override + public boolean containsProfanity(final String text) { + final URI url = UriComponentsBuilder.fromUriString("https://www.purgomalum.com/service/containsprofanity") + .queryParam("text", text) + .build() + .toUri(); + return Boolean.parseBoolean(restTemplate.getForObject(url, String.class)); + } +} diff --git a/src/main/java/kitchenpos/products/tobe/infra/JpaProductRepository.java b/src/main/java/kitchenpos/products/tobe/infra/JpaProductRepository.java new file mode 100644 index 000000000..a462cd8e2 --- /dev/null +++ b/src/main/java/kitchenpos/products/tobe/infra/JpaProductRepository.java @@ -0,0 +1,10 @@ +package kitchenpos.products.tobe.infra; + +import java.util.UUID; +import kitchenpos.products.tobe.domain.Product; +import kitchenpos.products.tobe.domain.ProductRepository; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface JpaProductRepository extends JpaRepository, ProductRepository { + +} diff --git a/src/main/java/kitchenpos/products/tobe/infra/PurgomalumClient.java b/src/main/java/kitchenpos/products/tobe/infra/PurgomalumClient.java new file mode 100644 index 000000000..284264d28 --- /dev/null +++ b/src/main/java/kitchenpos/products/tobe/infra/PurgomalumClient.java @@ -0,0 +1,5 @@ +package kitchenpos.products.tobe.infra; + +public interface PurgomalumClient { + boolean containsProfanity(String text); +} diff --git a/src/main/java/kitchenpos/products/ui/ProductRestController.java b/src/main/java/kitchenpos/products/ui/ProductRestController.java index c71c795a4..6d00a8a1f 100644 --- a/src/main/java/kitchenpos/products/ui/ProductRestController.java +++ b/src/main/java/kitchenpos/products/ui/ProductRestController.java @@ -1,7 +1,10 @@ package kitchenpos.products.ui; -import kitchenpos.products.application.ProductService; -import kitchenpos.products.domain.Product; +import kitchenpos.products.tobe.application.ProductService; +import kitchenpos.products.asis.domain.Product; +import kitchenpos.products.tobe.application.dto.ProductCreateRequest; +import kitchenpos.products.tobe.application.dto.ProductPriceUpdateRequest; +import kitchenpos.products.tobe.application.dto.ProductResponse; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -25,19 +28,20 @@ public ProductRestController(final ProductService productService) { } @PostMapping - public ResponseEntity create(@RequestBody final Product request) { - final Product response = productService.create(request); + public ResponseEntity create(@RequestBody final ProductCreateRequest request) { + final ProductResponse response = productService.create(request); return ResponseEntity.created(URI.create("/api/products/" + response.getId())) .body(response); } @PutMapping("/{productId}/price") - public ResponseEntity changePrice(@PathVariable final UUID productId, @RequestBody final Product request) { + public ResponseEntity changePrice(@PathVariable final UUID productId, + @RequestBody final ProductPriceUpdateRequest request) { return ResponseEntity.ok(productService.changePrice(productId, request)); } @GetMapping - public ResponseEntity> findAll() { + public ResponseEntity> findAll() { return ResponseEntity.ok(productService.findAll()); } } From 0021fa236567d260982920f5ed5e738f34b43cf1 Mon Sep 17 00:00:00 2001 From: "jinwoo.oh" Date: Mon, 17 Feb 2025 23:18:12 +0900 Subject: [PATCH 3/7] =?UTF-8?q?test=20:=20=EB=A6=AC=ED=8E=99=ED=86=A0?= =?UTF-8?q?=EB=A7=81=20=EB=90=9C=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/kitchenpos/Fixtures.java | 2 +- .../menus/application/MenuServiceTest.java | 6 +- .../application/FakePurgomalumClient.java | 2 +- .../InMemoryProductRepository.java | 4 +- .../application/ProductServiceTest.java | 7 +- .../products/tobe/FakePurgomalumClient.java | 19 +++ .../tobe/InMemoryProductRepository.java | 38 ++++++ .../products/tobe/ProductServiceTest.java | 118 ++++++++++++++++++ 8 files changed, 186 insertions(+), 10 deletions(-) create mode 100644 src/test/java/kitchenpos/products/tobe/FakePurgomalumClient.java create mode 100644 src/test/java/kitchenpos/products/tobe/InMemoryProductRepository.java create mode 100644 src/test/java/kitchenpos/products/tobe/ProductServiceTest.java diff --git a/src/test/java/kitchenpos/Fixtures.java b/src/test/java/kitchenpos/Fixtures.java index 434768a52..beb469ae7 100644 --- a/src/test/java/kitchenpos/Fixtures.java +++ b/src/test/java/kitchenpos/Fixtures.java @@ -8,7 +8,7 @@ import kitchenpos.menus.domain.Menu; import kitchenpos.menus.domain.MenuGroup; import kitchenpos.menus.domain.MenuProduct; -import kitchenpos.products.domain.Product; +import kitchenpos.products.asis.domain.Product; import java.math.BigDecimal; import java.time.LocalDateTime; diff --git a/src/test/java/kitchenpos/menus/application/MenuServiceTest.java b/src/test/java/kitchenpos/menus/application/MenuServiceTest.java index 277679118..a09686b20 100644 --- a/src/test/java/kitchenpos/menus/application/MenuServiceTest.java +++ b/src/test/java/kitchenpos/menus/application/MenuServiceTest.java @@ -6,9 +6,9 @@ import kitchenpos.menus.domain.MenuRepository; import kitchenpos.products.application.FakePurgomalumClient; import kitchenpos.products.application.InMemoryProductRepository; -import kitchenpos.products.domain.Product; -import kitchenpos.products.domain.ProductRepository; -import kitchenpos.products.infra.PurgomalumClient; +import kitchenpos.products.asis.domain.Product; +import kitchenpos.products.asis.domain.ProductRepository; +import kitchenpos.products.asis.infra.PurgomalumClient; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/kitchenpos/products/application/FakePurgomalumClient.java b/src/test/java/kitchenpos/products/application/FakePurgomalumClient.java index 3c4114798..dbb181cb1 100644 --- a/src/test/java/kitchenpos/products/application/FakePurgomalumClient.java +++ b/src/test/java/kitchenpos/products/application/FakePurgomalumClient.java @@ -1,6 +1,6 @@ package kitchenpos.products.application; -import kitchenpos.products.infra.PurgomalumClient; +import kitchenpos.products.asis.infra.PurgomalumClient; import java.util.Arrays; import java.util.List; diff --git a/src/test/java/kitchenpos/products/application/InMemoryProductRepository.java b/src/test/java/kitchenpos/products/application/InMemoryProductRepository.java index b55c5ec5e..8286eee9b 100644 --- a/src/test/java/kitchenpos/products/application/InMemoryProductRepository.java +++ b/src/test/java/kitchenpos/products/application/InMemoryProductRepository.java @@ -1,7 +1,7 @@ package kitchenpos.products.application; -import kitchenpos.products.domain.Product; -import kitchenpos.products.domain.ProductRepository; +import kitchenpos.products.asis.domain.Product; +import kitchenpos.products.asis.domain.ProductRepository; import java.util.ArrayList; import java.util.HashMap; diff --git a/src/test/java/kitchenpos/products/application/ProductServiceTest.java b/src/test/java/kitchenpos/products/application/ProductServiceTest.java index 74a31073e..3d170d646 100644 --- a/src/test/java/kitchenpos/products/application/ProductServiceTest.java +++ b/src/test/java/kitchenpos/products/application/ProductServiceTest.java @@ -3,9 +3,10 @@ import kitchenpos.menus.application.InMemoryMenuRepository; import kitchenpos.menus.domain.Menu; import kitchenpos.menus.domain.MenuRepository; -import kitchenpos.products.domain.Product; -import kitchenpos.products.domain.ProductRepository; -import kitchenpos.products.infra.PurgomalumClient; +import kitchenpos.products.asis.application.ProductService; +import kitchenpos.products.asis.domain.Product; +import kitchenpos.products.asis.domain.ProductRepository; +import kitchenpos.products.asis.infra.PurgomalumClient; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/kitchenpos/products/tobe/FakePurgomalumClient.java b/src/test/java/kitchenpos/products/tobe/FakePurgomalumClient.java new file mode 100644 index 000000000..1fae41eb3 --- /dev/null +++ b/src/test/java/kitchenpos/products/tobe/FakePurgomalumClient.java @@ -0,0 +1,19 @@ +package kitchenpos.products.tobe; + +import java.util.Arrays; +import java.util.List; +import kitchenpos.products.tobe.infra.PurgomalumClient; + +public class FakePurgomalumClient implements PurgomalumClient { + private static final List profanities; + + static { + profanities = Arrays.asList("비속어", "욕설"); + } + + @Override + public boolean containsProfanity(final String text) { + return profanities.stream() + .anyMatch(profanity -> text.contains(profanity)); + } +} diff --git a/src/test/java/kitchenpos/products/tobe/InMemoryProductRepository.java b/src/test/java/kitchenpos/products/tobe/InMemoryProductRepository.java new file mode 100644 index 000000000..795164d27 --- /dev/null +++ b/src/test/java/kitchenpos/products/tobe/InMemoryProductRepository.java @@ -0,0 +1,38 @@ +package kitchenpos.products.tobe; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import kitchenpos.products.tobe.domain.Product; +import kitchenpos.products.tobe.domain.ProductRepository; + +public class InMemoryProductRepository implements ProductRepository { + private final Map products = new HashMap<>(); + + @Override + public Product save(final Product product) { + products.put(product.getId(), product); + return product; + } + + @Override + public Optional findById(final UUID id) { + return Optional.ofNullable(products.get(id)); + } + + @Override + public List findAll() { + return new ArrayList<>(products.values()); + } + + @Override + public List findAllByIdIn(final List ids) { + return products.values() + .stream() + .filter(product -> ids.contains(product.getId())) + .toList(); + } +} diff --git a/src/test/java/kitchenpos/products/tobe/ProductServiceTest.java b/src/test/java/kitchenpos/products/tobe/ProductServiceTest.java new file mode 100644 index 000000000..7d03eee7d --- /dev/null +++ b/src/test/java/kitchenpos/products/tobe/ProductServiceTest.java @@ -0,0 +1,118 @@ +package kitchenpos.products.tobe; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; + +import java.math.BigDecimal; +import java.util.List; +import java.util.UUID; +import kitchenpos.menus.application.InMemoryMenuRepository; +import kitchenpos.menus.domain.MenuRepository; +import kitchenpos.products.tobe.application.ProductService; +import kitchenpos.products.tobe.application.dto.ProductCreateRequest; +import kitchenpos.products.tobe.application.dto.ProductPriceUpdateRequest; +import kitchenpos.products.tobe.application.dto.ProductResponse; +import kitchenpos.products.tobe.infra.PurgomalumClient; +import kitchenpos.products.tobe.domain.ProductRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullSource; +import org.junit.jupiter.params.provider.ValueSource; + +class ProductServiceTest { + + private ProductRepository productRepository; + private MenuRepository menuRepository; + private PurgomalumClient purgomalumClient; + private ProductService productService; + + @BeforeEach + void setUp() { + productRepository = new InMemoryProductRepository(); + menuRepository = new InMemoryMenuRepository(); + purgomalumClient = new FakePurgomalumClient(); + productService = new ProductService(productRepository, menuRepository, purgomalumClient); + } + + @DisplayName("상품을 등록할 수 있다.") + @Test + void create() { + final ProductCreateRequest expected = createProductRequest("후라이드", 16_000L); + final ProductResponse actual = productService.create(expected); + assertThat(actual).isNotNull(); + assertAll( + () -> assertThat(actual.getId()).isNotNull(), + () -> assertThat(actual.getName()).isEqualTo(expected.getName()), + () -> assertThat(actual.getPrice()).isEqualTo(expected.getPrice()) + ); + } + + @DisplayName("상품의 가격이 올바르지 않으면 등록할 수 없다.") + @ValueSource(strings = "-1000") + @NullSource + @ParameterizedTest + void create(final BigDecimal price) { + final ProductCreateRequest expected = createProductRequest("후라이드", price); + assertThatThrownBy(() -> productService.create(expected)) + .isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("상품의 이름이 올바르지 않으면 등록할 수 없다.") + @ValueSource(strings = {"비속어", "욕설이 포함된 이름"}) + @NullSource + @ParameterizedTest + void create(final String name) { + final ProductCreateRequest expected = createProductRequest(name, 16_000L); + assertThatThrownBy(() -> productService.create(expected)) + .isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("상품의 가격을 변경할 수 있다.") + @Test + void changePrice() { + final UUID productId = productService.create(createProductRequest("후라이드", 16_000L)).getId(); + final ProductPriceUpdateRequest expected = changePriceRequest(15_000L); + final ProductResponse actual = productService.changePrice(productId, expected); + assertThat(actual.getPrice()).isEqualTo(expected.getPrice()); + } + + @DisplayName("상품의 가격이 올바르지 않으면 변경할 수 없다.") + @ValueSource(strings = "-1000") + @NullSource + @ParameterizedTest + void changePrice(final BigDecimal price) { + final UUID productId = productService.create(createProductRequest("후라이드", 16_000L)).getId(); + final ProductPriceUpdateRequest expected = changePriceRequest(price); + assertThatThrownBy(() -> productService.changePrice(productId, expected)) + .isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("상품의 목록을 조회할 수 있다.") + @Test + void findAll() { + productService.create(createProductRequest("후라이드", 16_000L)); + productService.create(createProductRequest("양념치킨", 16_000L)); + + final List actual = productService.findAll(); + assertThat(actual).hasSize(2); + } + + private ProductCreateRequest createProductRequest(final String name, final long price) { + return createProductRequest(name, BigDecimal.valueOf(price)); + } + + private ProductCreateRequest createProductRequest(final String name, final BigDecimal price) { + return new ProductCreateRequest(name, price); + } + + private ProductPriceUpdateRequest changePriceRequest(final long price) { + return changePriceRequest(BigDecimal.valueOf(price)); + } + + private ProductPriceUpdateRequest changePriceRequest(final BigDecimal price) { + return new ProductPriceUpdateRequest(price); + } +} From 48bdba298c48dc2f47ffb3954424b945da828d3d Mon Sep 17 00:00:00 2001 From: "jinwoo.oh" Date: Tue, 18 Feb 2025 20:10:53 +0900 Subject: [PATCH 4/7] =?UTF-8?q?refect=20:=20PurgomalumClient=20domain?= =?UTF-8?q?=EC=98=81=EC=97=AD=EC=9C=BC=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../products/tobe/infra/DefaultPurgomalumClient.java | 1 + .../kitchenpos/products/tobe/infra/PurgomalumClient.java | 5 ----- .../java/kitchenpos/products/tobe/FakePurgomalumClient.java | 2 +- 3 files changed, 2 insertions(+), 6 deletions(-) delete mode 100644 src/main/java/kitchenpos/products/tobe/infra/PurgomalumClient.java diff --git a/src/main/java/kitchenpos/products/tobe/infra/DefaultPurgomalumClient.java b/src/main/java/kitchenpos/products/tobe/infra/DefaultPurgomalumClient.java index 45722d636..28f9447b3 100644 --- a/src/main/java/kitchenpos/products/tobe/infra/DefaultPurgomalumClient.java +++ b/src/main/java/kitchenpos/products/tobe/infra/DefaultPurgomalumClient.java @@ -1,6 +1,7 @@ package kitchenpos.products.tobe.infra; import java.net.URI; +import kitchenpos.products.tobe.domain.PurgomalumClient; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; diff --git a/src/main/java/kitchenpos/products/tobe/infra/PurgomalumClient.java b/src/main/java/kitchenpos/products/tobe/infra/PurgomalumClient.java deleted file mode 100644 index 284264d28..000000000 --- a/src/main/java/kitchenpos/products/tobe/infra/PurgomalumClient.java +++ /dev/null @@ -1,5 +0,0 @@ -package kitchenpos.products.tobe.infra; - -public interface PurgomalumClient { - boolean containsProfanity(String text); -} diff --git a/src/test/java/kitchenpos/products/tobe/FakePurgomalumClient.java b/src/test/java/kitchenpos/products/tobe/FakePurgomalumClient.java index 1fae41eb3..0cdda10d6 100644 --- a/src/test/java/kitchenpos/products/tobe/FakePurgomalumClient.java +++ b/src/test/java/kitchenpos/products/tobe/FakePurgomalumClient.java @@ -2,7 +2,7 @@ import java.util.Arrays; import java.util.List; -import kitchenpos.products.tobe.infra.PurgomalumClient; +import kitchenpos.products.tobe.domain.PurgomalumClient; public class FakePurgomalumClient implements PurgomalumClient { private static final List profanities; From 2d2f2ff34d75409afb6a4540b2a9601496b35266 Mon Sep 17 00:00:00 2001 From: "jinwoo.oh" Date: Tue, 18 Feb 2025 20:11:25 +0900 Subject: [PATCH 5/7] =?UTF-8?q?feat=20:=20=EC=BB=A4=EC=8A=A4=ED=84=B0=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tobe/application/ProductService.java | 2 +- .../products/tobe/domain/Product.java | 1 - .../products/tobe/domain/ProductName.java | 9 ++++---- .../products/tobe/domain/ProductPrice.java | 21 ++++++++++++++++++- .../tobe/domain/PurgomalumClient.java | 5 +++++ .../tobe/exception/InvalidPriceException.java | 11 ++++++++++ .../ProductNameRequiredException.java | 11 ++++++++++ .../tobe/exception/ProfanityException.java | 11 ++++++++++ 8 files changed, 64 insertions(+), 7 deletions(-) create mode 100644 src/main/java/kitchenpos/products/tobe/domain/PurgomalumClient.java create mode 100644 src/main/java/kitchenpos/products/tobe/exception/InvalidPriceException.java create mode 100644 src/main/java/kitchenpos/products/tobe/exception/ProductNameRequiredException.java create mode 100644 src/main/java/kitchenpos/products/tobe/exception/ProfanityException.java diff --git a/src/main/java/kitchenpos/products/tobe/application/ProductService.java b/src/main/java/kitchenpos/products/tobe/application/ProductService.java index cb4395ca3..2bea00287 100644 --- a/src/main/java/kitchenpos/products/tobe/application/ProductService.java +++ b/src/main/java/kitchenpos/products/tobe/application/ProductService.java @@ -12,7 +12,7 @@ import kitchenpos.products.tobe.application.dto.ProductResponse; import kitchenpos.products.tobe.domain.Product; import kitchenpos.products.tobe.domain.ProductRepository; -import kitchenpos.products.tobe.infra.PurgomalumClient; +import kitchenpos.products.tobe.domain.PurgomalumClient; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; diff --git a/src/main/java/kitchenpos/products/tobe/domain/Product.java b/src/main/java/kitchenpos/products/tobe/domain/Product.java index 5d85c92cc..9f783abd2 100644 --- a/src/main/java/kitchenpos/products/tobe/domain/Product.java +++ b/src/main/java/kitchenpos/products/tobe/domain/Product.java @@ -8,7 +8,6 @@ import java.math.BigDecimal; import java.util.UUID; -import kitchenpos.products.tobe.infra.PurgomalumClient; @Table(name = "product") @Entity diff --git a/src/main/java/kitchenpos/products/tobe/domain/ProductName.java b/src/main/java/kitchenpos/products/tobe/domain/ProductName.java index 24e7c6058..535fa9e55 100644 --- a/src/main/java/kitchenpos/products/tobe/domain/ProductName.java +++ b/src/main/java/kitchenpos/products/tobe/domain/ProductName.java @@ -2,7 +2,8 @@ import jakarta.persistence.Column; import jakarta.persistence.Embeddable; -import kitchenpos.products.tobe.infra.PurgomalumClient; +import kitchenpos.products.tobe.exception.ProductNameRequiredException; +import kitchenpos.products.tobe.exception.ProfanityException; @Embeddable public class ProductName { @@ -19,11 +20,11 @@ private ProductName(String name) { public static ProductName from(String name, PurgomalumClient purgomalumClient) { if (name == null || name.isEmpty()) { - throw new IllegalArgumentException("상품명은 필수로 입력해야 합니다."); + throw new ProductNameRequiredException(); } - + if (purgomalumClient.containsProfanity(name)) { - throw new IllegalArgumentException("비속어가 포함되어 있습니다."); + throw new ProfanityException(); } return new ProductName(name); } diff --git a/src/main/java/kitchenpos/products/tobe/domain/ProductPrice.java b/src/main/java/kitchenpos/products/tobe/domain/ProductPrice.java index 5c00c4a8a..33caff960 100644 --- a/src/main/java/kitchenpos/products/tobe/domain/ProductPrice.java +++ b/src/main/java/kitchenpos/products/tobe/domain/ProductPrice.java @@ -3,6 +3,8 @@ import jakarta.persistence.Column; import jakarta.persistence.Embeddable; import java.math.BigDecimal; +import java.util.Objects; +import kitchenpos.products.tobe.exception.InvalidPriceException; @Embeddable public class ProductPrice { @@ -23,11 +25,28 @@ public static ProductPrice from(BigDecimal price) { private static void validate(BigDecimal price) { if (price == null || price.compareTo(BigDecimal.ZERO) < 0) { - throw new IllegalArgumentException("가격은 0 이상이어야 합니다."); + throw new InvalidPriceException(); } } public BigDecimal getPrice() { return price; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ProductPrice that = (ProductPrice) o; + return Objects.equals(price, that.price); + } + + @Override + public int hashCode() { + return Objects.hashCode(price); + } } diff --git a/src/main/java/kitchenpos/products/tobe/domain/PurgomalumClient.java b/src/main/java/kitchenpos/products/tobe/domain/PurgomalumClient.java new file mode 100644 index 000000000..d2bed1485 --- /dev/null +++ b/src/main/java/kitchenpos/products/tobe/domain/PurgomalumClient.java @@ -0,0 +1,5 @@ +package kitchenpos.products.tobe.domain; + +public interface PurgomalumClient { + boolean containsProfanity(String text); +} diff --git a/src/main/java/kitchenpos/products/tobe/exception/InvalidPriceException.java b/src/main/java/kitchenpos/products/tobe/exception/InvalidPriceException.java new file mode 100644 index 000000000..d4b7b4a23 --- /dev/null +++ b/src/main/java/kitchenpos/products/tobe/exception/InvalidPriceException.java @@ -0,0 +1,11 @@ +package kitchenpos.products.tobe.exception; + +public class InvalidPriceException extends IllegalArgumentException { + public InvalidPriceException(String message) { + super(message); + } + + public InvalidPriceException() { + super("가격은 0 이상이어야 합니다."); + } +} diff --git a/src/main/java/kitchenpos/products/tobe/exception/ProductNameRequiredException.java b/src/main/java/kitchenpos/products/tobe/exception/ProductNameRequiredException.java new file mode 100644 index 000000000..747132bb2 --- /dev/null +++ b/src/main/java/kitchenpos/products/tobe/exception/ProductNameRequiredException.java @@ -0,0 +1,11 @@ +package kitchenpos.products.tobe.exception; + +public class ProductNameRequiredException extends IllegalArgumentException { + public ProductNameRequiredException(String message) { + super(message); + } + + public ProductNameRequiredException() { + super("상품명은 필수로 입력해야 합니다."); + } +} diff --git a/src/main/java/kitchenpos/products/tobe/exception/ProfanityException.java b/src/main/java/kitchenpos/products/tobe/exception/ProfanityException.java new file mode 100644 index 000000000..2068d3803 --- /dev/null +++ b/src/main/java/kitchenpos/products/tobe/exception/ProfanityException.java @@ -0,0 +1,11 @@ +package kitchenpos.products.tobe.exception; + +public class ProfanityException extends IllegalArgumentException { + public ProfanityException(String message) { + super(message); + } + + public ProfanityException() { + super("비속어가 포함되어 있습니다."); + } +} From 7039a932352c397ce8c776c2ca73c4e1532f9186 Mon Sep 17 00:00:00 2001 From: "jinwoo.oh" Date: Tue, 18 Feb 2025 20:11:37 +0900 Subject: [PATCH 6/7] =?UTF-8?q?test=20:=20=EB=8F=84=EB=A9=94=EC=9D=B8=20?= =?UTF-8?q?=EA=B0=9D=EC=B2=B4=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../products/tobe/ProductServiceTest.java | 2 +- .../products/tobe/domain/ProductNameTest.java | 74 ++++++++++++++++++ .../tobe/domain/ProductPriceTest.java | 77 +++++++++++++++++++ 3 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 src/test/java/kitchenpos/products/tobe/domain/ProductNameTest.java create mode 100644 src/test/java/kitchenpos/products/tobe/domain/ProductPriceTest.java diff --git a/src/test/java/kitchenpos/products/tobe/ProductServiceTest.java b/src/test/java/kitchenpos/products/tobe/ProductServiceTest.java index 7d03eee7d..e66283a68 100644 --- a/src/test/java/kitchenpos/products/tobe/ProductServiceTest.java +++ b/src/test/java/kitchenpos/products/tobe/ProductServiceTest.java @@ -13,7 +13,7 @@ import kitchenpos.products.tobe.application.dto.ProductCreateRequest; import kitchenpos.products.tobe.application.dto.ProductPriceUpdateRequest; import kitchenpos.products.tobe.application.dto.ProductResponse; -import kitchenpos.products.tobe.infra.PurgomalumClient; +import kitchenpos.products.tobe.domain.PurgomalumClient; import kitchenpos.products.tobe.domain.ProductRepository; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; diff --git a/src/test/java/kitchenpos/products/tobe/domain/ProductNameTest.java b/src/test/java/kitchenpos/products/tobe/domain/ProductNameTest.java new file mode 100644 index 000000000..e37cdc614 --- /dev/null +++ b/src/test/java/kitchenpos/products/tobe/domain/ProductNameTest.java @@ -0,0 +1,74 @@ +package kitchenpos.products.tobe.domain; + +import kitchenpos.products.tobe.exception.ProductNameRequiredException; +import kitchenpos.products.tobe.exception.ProfanityException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class ProductNameTest { + + private PurgomalumClient purgomalumClient; + + @BeforeEach + void setUp() { + purgomalumClient = Mockito.mock(PurgomalumClient.class); + } + + @Test + @DisplayName("상품명이 null일 때 ProductNameRequiredException이 발생해야 한다.") + void testFrom_NullName_ThrowsProductNameRequiredException() { + // Arrange + String name = null; + + // Act & Assert + assertThrows(ProductNameRequiredException.class, () -> { + ProductName.from(name, purgomalumClient); + }); + } + + @Test + @DisplayName("상품명이 비어 있을 때 ProductNameRequiredException이 발생해야 한다.") + void testFrom_EmptyName_ThrowsProductNameRequiredException() { + // Arrange + String name = ""; + + // Act & Assert + assertThrows(ProductNameRequiredException.class, () -> { + ProductName.from(name, purgomalumClient); + }); + } + + @Test + @DisplayName("상품명에 비속어가 포함되어 있을 때 ProfanityException이 발생해야 한다.") + void testFrom_NameWithProfanity_ThrowsProfanityException() { + // Arrange + String name = "badword"; + Mockito.when(purgomalumClient.containsProfanity(name)).thenReturn(true); + + // Act & Assert + assertThrows(ProfanityException.class, () -> { + ProductName.from(name, purgomalumClient); + }); + } + + @Test + @DisplayName("유효한 상품명이 주어졌을 때 ProductName 객체가 반환되어야 한다.") + void testFrom_ValidName_ReturnsProductName() { + // Arrange + String name = "Valid Product"; + Mockito.when(purgomalumClient.containsProfanity(name)).thenReturn(false); + + // Act + ProductName productName = ProductName.from(name, purgomalumClient); + + // Assert + assertNotNull(productName); + assertEquals(name, productName.getName()); + } +} diff --git a/src/test/java/kitchenpos/products/tobe/domain/ProductPriceTest.java b/src/test/java/kitchenpos/products/tobe/domain/ProductPriceTest.java new file mode 100644 index 000000000..31ee51545 --- /dev/null +++ b/src/test/java/kitchenpos/products/tobe/domain/ProductPriceTest.java @@ -0,0 +1,77 @@ +package kitchenpos.products.tobe.domain; + +import static org.junit.jupiter.api.Assertions.*; + +import kitchenpos.products.tobe.exception.InvalidPriceException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; + +class ProductPriceTest { + + @Test + @DisplayName("가격이 null일 때 InvalidPriceException이 발생해야 한다.") + void testFrom_NullPrice_ThrowsInvalidPriceException() { + // Arrange + BigDecimal price = null; + + // Act & Assert + assertThrows(InvalidPriceException.class, () -> { + ProductPrice.from(price); + }); + } + + @Test + @DisplayName("가격이 0보다 작을 때 InvalidPriceException이 발생해야 한다.") + void testFrom_NegativePrice_ThrowsInvalidPriceException() { + // Arrange + BigDecimal price = new BigDecimal("-10.00"); + + // Act & Assert + assertThrows(InvalidPriceException.class, () -> { + ProductPrice.from(price); + }); + } + + @Test + @DisplayName("가격이 0일 때 ProductPrice 객체가 반환되어야 한다.") + void testFrom_ZeroPrice_ReturnsProductPrice() { + // Arrange + BigDecimal price = BigDecimal.ZERO; + + // Act + ProductPrice productPrice = ProductPrice.from(price); + + // Assert + assertNotNull(productPrice); + assertEquals(price, productPrice.getPrice()); + } + + @Test + @DisplayName("가격이 0보다 클 때 ProductPrice 객체가 반환되어야 한다.") + void testFrom_PositivePrice_ReturnsProductPrice() { + // Arrange + BigDecimal price = new BigDecimal("10.00"); + + // Act + ProductPrice productPrice = ProductPrice.from(price); + + // Assert + assertNotNull(productPrice); + assertEquals(price, productPrice.getPrice()); + } + + @Test + @DisplayName("동일한 가격을 가진 두 ProductPrice 객체는 같아야 한다.") + void testEquals_SamePrice_ReturnsTrue() { + // Arrange + BigDecimal price = new BigDecimal("10.00"); + ProductPrice productPrice1 = ProductPrice.from(price); + ProductPrice productPrice2 = ProductPrice.from(price); + + // Act & Assert + assertEquals(productPrice1, productPrice2); + assertEquals(productPrice1.hashCode(), productPrice2.hashCode()); + } +} From 500063da2cb1a05f81187af152bbd694c9b9a83f Mon Sep 17 00:00:00 2001 From: "jinwoo.oh" Date: Tue, 18 Feb 2025 20:15:12 +0900 Subject: [PATCH 7/7] =?UTF-8?q?refect=20:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=EB=AA=85=20=EB=8F=99=EC=9D=BC=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kitchenpos/products/tobe/application/ProductService.java | 2 +- src/main/java/kitchenpos/products/tobe/domain/Product.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/kitchenpos/products/tobe/application/ProductService.java b/src/main/java/kitchenpos/products/tobe/application/ProductService.java index 2bea00287..674c50050 100644 --- a/src/main/java/kitchenpos/products/tobe/application/ProductService.java +++ b/src/main/java/kitchenpos/products/tobe/application/ProductService.java @@ -45,7 +45,7 @@ public ProductResponse changePrice(UUID productId, ProductPriceUpdateRequest req Product product = jpaProductRepository.findById(productId) .orElseThrow(NoSuchElementException::new); - product.updatePrice(request.getPrice()); + product.changePrice(request.getPrice()); final List menus = menuRepository.findAllByProductId(productId); for (final Menu menu : menus) { diff --git a/src/main/java/kitchenpos/products/tobe/domain/Product.java b/src/main/java/kitchenpos/products/tobe/domain/Product.java index 9f783abd2..c824346f9 100644 --- a/src/main/java/kitchenpos/products/tobe/domain/Product.java +++ b/src/main/java/kitchenpos/products/tobe/domain/Product.java @@ -49,7 +49,7 @@ public BigDecimal getPrice() { return price.getPrice(); } - public void updatePrice(BigDecimal price) { + public void changePrice(BigDecimal price) { this.price = ProductPrice.from(price); } }