diff --git a/app/src/main/java/com/codesoom/assignment/ProductNotFoundException.java b/app/src/main/java/com/codesoom/assignment/ProductNotFoundException.java deleted file mode 100644 index 7d8009d42..000000000 --- a/app/src/main/java/com/codesoom/assignment/ProductNotFoundException.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.codesoom.assignment; - -public class ProductNotFoundException extends RuntimeException { - public ProductNotFoundException(Long id) { - super("Product not found: " + id); - } -} diff --git a/app/src/main/java/com/codesoom/assignment/application/ProductService.java b/app/src/main/java/com/codesoom/assignment/application/ProductService.java deleted file mode 100644 index 7d498285d..000000000 --- a/app/src/main/java/com/codesoom/assignment/application/ProductService.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.codesoom.assignment.application; - -import com.codesoom.assignment.ProductNotFoundException; -import com.codesoom.assignment.domain.Product; -import com.codesoom.assignment.domain.ProductRepository; -import com.codesoom.assignment.dto.ProductData; -import org.springframework.stereotype.Service; - -import javax.transaction.Transactional; -import java.util.List; - -@Service -@Transactional -public class ProductService { - private final ProductRepository productRepository; - - public ProductService(ProductRepository productRepository) { - this.productRepository = productRepository; - } - - public List getProducts() { - return productRepository.findAll(); - } - - public Product getProduct(Long id) { - return findProduct(id); - } - - public Product createProduct(ProductData productData) { - Product product = Product.builder() - .name(productData.getName()) - .maker(productData.getMaker()) - .price(productData.getPrice()) - .imageUrl(productData.getImageUrl()) - .build(); - return productRepository.save(product); - } - - public Product updateProduct(Long id, ProductData productData) { - Product product = findProduct(id); - - product.change( - productData.getName(), - productData.getMaker(), - productData.getPrice(), - productData.getImageUrl() - ); - - return product; - } - - public Product deleteProduct(Long id) { - Product product = findProduct(id); - - productRepository.delete(product); - - return product; - } - - private Product findProduct(Long id) { - return productRepository.findById(id) - .orElseThrow(() -> new ProductNotFoundException(id)); - } -} diff --git a/app/src/main/java/com/codesoom/assignment/application/common/exception/BaseException.java b/app/src/main/java/com/codesoom/assignment/application/common/exception/BaseException.java new file mode 100644 index 000000000..645b8e220 --- /dev/null +++ b/app/src/main/java/com/codesoom/assignment/application/common/exception/BaseException.java @@ -0,0 +1,31 @@ +package com.codesoom.assignment.application.common.exception; + +import com.codesoom.assignment.dto.ErrorResponse; + +import java.util.ArrayList; +import java.util.List; + +public abstract class BaseException extends RuntimeException { + public final List errors = new ArrayList<>(); + + public BaseException() { + } + + public BaseException(String message) { + super(message); + } + + public abstract int getStatusCode(); + + public void addValidation(String source, String type, String message) { + errors.add(new ErrorValidation(source, type, message)); + } + + public ErrorResponse toErrorResponse() { + return new ErrorResponse( + String.valueOf(getStatusCode()), + getMessage(), + errors + ); + } +} diff --git a/app/src/main/java/com/codesoom/assignment/application/common/exception/ErrorValidation.java b/app/src/main/java/com/codesoom/assignment/application/common/exception/ErrorValidation.java new file mode 100644 index 000000000..ea8764344 --- /dev/null +++ b/app/src/main/java/com/codesoom/assignment/application/common/exception/ErrorValidation.java @@ -0,0 +1,18 @@ +package com.codesoom.assignment.application.common.exception; + +import lombok.Builder; +import lombok.Getter; + +@Getter +public class ErrorValidation { + private final String source; + private final String type; + private final String message; + + @Builder + public ErrorValidation(String source, String type, String message) { + this.source = source; + this.type = type; + this.message = message; + } +} diff --git a/app/src/main/java/com/codesoom/assignment/application/product/ProductCreator.java b/app/src/main/java/com/codesoom/assignment/application/product/ProductCreator.java new file mode 100644 index 000000000..186b79919 --- /dev/null +++ b/app/src/main/java/com/codesoom/assignment/application/product/ProductCreator.java @@ -0,0 +1,25 @@ +package com.codesoom.assignment.application.product; + +import com.codesoom.assignment.domain.product.Product; +import com.codesoom.assignment.domain.product.ProductRepository; +import com.codesoom.assignment.dto.product.ProductData; +import org.springframework.stereotype.Service; + +@Service +public class ProductCreator { + private final ProductRepository productRepository; + + public ProductCreator(ProductRepository productRepository) { + this.productRepository = productRepository; + } + + public Product createProduct(ProductData productData) { + Product product = Product.builder() + .name(productData.getName()) + .maker(productData.getMaker()) + .price(productData.getPrice()) + .imageUrl(productData.getImageUrl()) + .build(); + return productRepository.save(product); + } +} diff --git a/app/src/main/java/com/codesoom/assignment/application/product/ProductDeleter.java b/app/src/main/java/com/codesoom/assignment/application/product/ProductDeleter.java new file mode 100644 index 000000000..f427b7960 --- /dev/null +++ b/app/src/main/java/com/codesoom/assignment/application/product/ProductDeleter.java @@ -0,0 +1,25 @@ +package com.codesoom.assignment.application.product; + +import com.codesoom.assignment.domain.product.Product; +import com.codesoom.assignment.domain.product.ProductRepository; +import org.springframework.stereotype.Service; + +@Service +public class ProductDeleter { + + private final ProductRepository productRepository; + private final ProductReader productReader; + + public ProductDeleter(ProductRepository productRepository, ProductReader productReader) { + this.productRepository = productRepository; + this.productReader = productReader; + } + + public Product deleteProduct(Long id) { + Product product = productReader.getProduct(id); + + productRepository.delete(product); + + return product; + } +} diff --git a/app/src/main/java/com/codesoom/assignment/application/product/ProductReader.java b/app/src/main/java/com/codesoom/assignment/application/product/ProductReader.java new file mode 100644 index 000000000..a1b3f913a --- /dev/null +++ b/app/src/main/java/com/codesoom/assignment/application/product/ProductReader.java @@ -0,0 +1,31 @@ +package com.codesoom.assignment.application.product; + +import com.codesoom.assignment.infra.product.exception.ProductNotFoundException; +import com.codesoom.assignment.domain.product.Product; +import com.codesoom.assignment.domain.product.ProductRepository; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class ProductReader { + + private final ProductRepository productRepository; + + public ProductReader(ProductRepository productRepository) { + this.productRepository = productRepository; + } + + public List getProducts() { + return productRepository.findAll(); + } + + public Product getProduct(Long id) { + return findProduct(id); + } + + private Product findProduct(Long id) { + return productRepository.findById(id) + .orElseThrow(() -> new ProductNotFoundException()); + } +} diff --git a/app/src/main/java/com/codesoom/assignment/application/product/ProductUpdater.java b/app/src/main/java/com/codesoom/assignment/application/product/ProductUpdater.java new file mode 100644 index 000000000..95be1b7d2 --- /dev/null +++ b/app/src/main/java/com/codesoom/assignment/application/product/ProductUpdater.java @@ -0,0 +1,35 @@ +package com.codesoom.assignment.application.product; + +import com.codesoom.assignment.domain.product.Product; +import com.codesoom.assignment.domain.product.ProductRepository; +import com.codesoom.assignment.dto.product.ProductData; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; + +@Service +public class ProductUpdater { + + private final ProductRepository productRepository; + private final ProductReader productReader; + + public ProductUpdater(ProductRepository productRepository, ProductReader productReader) { + this.productRepository = productRepository; + this.productReader = productReader; + } + + @Transactional + public Product updateProduct(Long id, ProductData productData) { + Product product = productReader.getProduct(id); + + product.change( + productData.getName(), + productData.getMaker(), + productData.getPrice(), + productData.getImageUrl() + ); + + return productRepository.save(product); + } + +} diff --git a/app/src/main/java/com/codesoom/assignment/application/user/UserCreator.java b/app/src/main/java/com/codesoom/assignment/application/user/UserCreator.java new file mode 100644 index 000000000..a5a31ef81 --- /dev/null +++ b/app/src/main/java/com/codesoom/assignment/application/user/UserCreator.java @@ -0,0 +1,20 @@ +package com.codesoom.assignment.application.user; + +import com.codesoom.assignment.domain.user.User; +import com.codesoom.assignment.domain.user.UserRepository; +import com.codesoom.assignment.dto.user.UserData; +import org.springframework.stereotype.Service; + +@Service +public class UserCreator { + + private final UserRepository userRepository; + + public UserCreator(UserRepository userRepository) { + this.userRepository = userRepository; + } + + public User createUser(UserData userRequest) { + return userRepository.save(userRequest.toUser()); + } +} diff --git a/app/src/main/java/com/codesoom/assignment/application/user/UserDeleter.java b/app/src/main/java/com/codesoom/assignment/application/user/UserDeleter.java new file mode 100644 index 000000000..2ea267cc0 --- /dev/null +++ b/app/src/main/java/com/codesoom/assignment/application/user/UserDeleter.java @@ -0,0 +1,22 @@ +package com.codesoom.assignment.application.user; + +import com.codesoom.assignment.domain.user.User; +import com.codesoom.assignment.domain.user.UserRepository; +import org.springframework.stereotype.Service; + +@Service +public class UserDeleter { + private final UserRepository userRepository; + + private final UserReader userReader; + + public UserDeleter(UserRepository userRepository, UserReader userReader) { + this.userRepository = userRepository; + this.userReader = userReader; + } + + public void delete(Long id) { + User user = userReader.getUser(id); + userRepository.deleteById(user.getId()); + } +} diff --git a/app/src/main/java/com/codesoom/assignment/application/user/UserReader.java b/app/src/main/java/com/codesoom/assignment/application/user/UserReader.java new file mode 100644 index 000000000..d78f92752 --- /dev/null +++ b/app/src/main/java/com/codesoom/assignment/application/user/UserReader.java @@ -0,0 +1,20 @@ + +package com.codesoom.assignment.application.user; + +import com.codesoom.assignment.domain.user.User; +import com.codesoom.assignment.domain.user.UserRepository; +import org.springframework.stereotype.Service; + +@Service +public class UserReader { + private final UserRepository userRepository; + + public UserReader(UserRepository userRepository) { + this.userRepository = userRepository; + } + + public User getUser(Long id) { + return userRepository.findById(id) + .orElseThrow(() -> new IllegalArgumentException("해당하는 유저가 없습니다.")); + } +} diff --git a/app/src/main/java/com/codesoom/assignment/application/user/UserUpdater.java b/app/src/main/java/com/codesoom/assignment/application/user/UserUpdater.java new file mode 100644 index 000000000..93325c82f --- /dev/null +++ b/app/src/main/java/com/codesoom/assignment/application/user/UserUpdater.java @@ -0,0 +1,30 @@ +package com.codesoom.assignment.application.user; + +import com.codesoom.assignment.domain.user.User; +import com.codesoom.assignment.domain.user.UserRepository; +import com.codesoom.assignment.dto.user.UserData; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; + +@Service +public class UserUpdater { + + private UserRepository userRepository; + + private UserReader userReader; + + public UserUpdater(UserRepository userRepository, UserReader userReader) { + this.userRepository = userRepository; + this.userReader = userReader; + } + + @Transactional + public User update(Long id, UserData userRequest) { + User user = userReader.getUser(id); + user.change(userRequest.getName(), userRequest.getEmail(), userRequest.getPassword()); + + return userRepository.save(user); + + } +} diff --git a/app/src/main/java/com/codesoom/assignment/controllers/ExceptionAdvice.java b/app/src/main/java/com/codesoom/assignment/controllers/ExceptionAdvice.java new file mode 100644 index 000000000..322cd139e --- /dev/null +++ b/app/src/main/java/com/codesoom/assignment/controllers/ExceptionAdvice.java @@ -0,0 +1,46 @@ +package com.codesoom.assignment.controllers; + +import com.codesoom.assignment.application.common.exception.ErrorValidation; +import com.codesoom.assignment.infra.product.exception.ProductNotFoundException; +import com.codesoom.assignment.dto.ErrorResponse; +import org.springframework.http.HttpStatus; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; + +import java.util.ArrayList; +import java.util.List; + +@ControllerAdvice +public class ExceptionAdvice { + @ResponseBody + @ResponseStatus(HttpStatus.NOT_FOUND) + @ExceptionHandler(ProductNotFoundException.class) + public ErrorResponse handleProductTaskNotFound() { + return new ErrorResponse("해당하는 상품이 존재하지 않습니다"); + } + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler(MethodArgumentNotValidException.class) + @ResponseBody + public ErrorResponse InvalidRequestHandler(MethodArgumentNotValidException e) { + + List fieldErrors = e.getBindingResult().getFieldErrors(); + List errors = new ArrayList<>(); + + for (FieldError fieldError : fieldErrors) { + errors.add(new ErrorValidation(fieldError.getField(),"BAD_REQUEST",fieldError.getDefaultMessage())); + } + + ErrorResponse response = ErrorResponse.builder() + .code("400") + .message("잘못된 요청입니다.") + .errors(errors) + .build(); + + return response; + } + +} diff --git a/app/src/main/java/com/codesoom/assignment/controllers/NotFoundErrorAdvice.java b/app/src/main/java/com/codesoom/assignment/controllers/NotFoundErrorAdvice.java deleted file mode 100644 index 6eb90a810..000000000 --- a/app/src/main/java/com/codesoom/assignment/controllers/NotFoundErrorAdvice.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.codesoom.assignment.controllers; - -import com.codesoom.assignment.ProductNotFoundException; -import com.codesoom.assignment.dto.ErrorResponse; -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ControllerAdvice; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.bind.annotation.ResponseStatus; - -@ControllerAdvice -public class NotFoundErrorAdvice { - @ResponseBody - @ResponseStatus(HttpStatus.NOT_FOUND) - @ExceptionHandler(ProductNotFoundException.class) - public ErrorResponse handleProductTaskNotFound() { - return new ErrorResponse("Product not found"); - } -} diff --git a/app/src/main/java/com/codesoom/assignment/controllers/ProductController.java b/app/src/main/java/com/codesoom/assignment/controllers/ProductController.java deleted file mode 100644 index c29572b06..000000000 --- a/app/src/main/java/com/codesoom/assignment/controllers/ProductController.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.codesoom.assignment.controllers; - -import com.codesoom.assignment.application.ProductService; -import com.codesoom.assignment.domain.Product; -import com.codesoom.assignment.dto.ProductData; -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.*; - -import javax.validation.Valid; -import java.util.List; - -@RestController -@RequestMapping("/products") -public class ProductController { - private final ProductService productService; - - public ProductController(ProductService productService) { - this.productService = productService; - } - - @GetMapping - public List list() { - return productService.getProducts(); - } - - @GetMapping("{id}") - public Product detail(@PathVariable Long id) { - return productService.getProduct(id); - } - - @PostMapping - @ResponseStatus(HttpStatus.CREATED) - public Product create(@RequestBody @Valid ProductData productData) { - return productService.createProduct(productData); - } - - @PatchMapping("{id}") - public Product update( - @PathVariable Long id, - @RequestBody @Valid ProductData productData - ) { - return productService.updateProduct(id, productData); - } - - @DeleteMapping("{id}") - @ResponseStatus(HttpStatus.NO_CONTENT) - public void destroy(@PathVariable Long id) { - productService.deleteProduct(id); - } -} diff --git a/app/src/main/java/com/codesoom/assignment/controllers/product/ProductCreateController.java b/app/src/main/java/com/codesoom/assignment/controllers/product/ProductCreateController.java new file mode 100644 index 000000000..cfe6d8afb --- /dev/null +++ b/app/src/main/java/com/codesoom/assignment/controllers/product/ProductCreateController.java @@ -0,0 +1,23 @@ +package com.codesoom.assignment.controllers.product; + +import com.codesoom.assignment.application.product.ProductCreator; +import com.codesoom.assignment.domain.product.Product; +import com.codesoom.assignment.dto.product.ProductData; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; + +@RestController +@RequestMapping("/products") +@RequiredArgsConstructor +public class ProductCreateController { + private final ProductCreator productCreator; + + @PostMapping + @ResponseStatus(HttpStatus.CREATED) + public Product create(@RequestBody @Valid ProductData productData) { + return productCreator.createProduct(productData); + } +} diff --git a/app/src/main/java/com/codesoom/assignment/controllers/product/ProductDeleteController.java b/app/src/main/java/com/codesoom/assignment/controllers/product/ProductDeleteController.java new file mode 100644 index 000000000..75d4a0a76 --- /dev/null +++ b/app/src/main/java/com/codesoom/assignment/controllers/product/ProductDeleteController.java @@ -0,0 +1,20 @@ +package com.codesoom.assignment.controllers.product; + +import com.codesoom.assignment.application.product.ProductDeleter; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/products") +@RequiredArgsConstructor +public class ProductDeleteController { + private final ProductDeleter productDeleter; + + @DeleteMapping("{id}") + @ResponseStatus(HttpStatus.NO_CONTENT) + public void destroy(@PathVariable Long id) { + + productDeleter.deleteProduct(id); + } +} diff --git a/app/src/main/java/com/codesoom/assignment/controllers/product/ProductReadController.java b/app/src/main/java/com/codesoom/assignment/controllers/product/ProductReadController.java new file mode 100644 index 000000000..9c85ed820 --- /dev/null +++ b/app/src/main/java/com/codesoom/assignment/controllers/product/ProductReadController.java @@ -0,0 +1,29 @@ +package com.codesoom.assignment.controllers.product; + +import com.codesoom.assignment.application.product.ProductReader; +import com.codesoom.assignment.domain.product.Product; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +@RequestMapping("/products") +@RequiredArgsConstructor +public class ProductReadController { + private final ProductReader productReader; + + @GetMapping + public List list() { + return productReader.getProducts(); + } + + @GetMapping("{id}") + public Product detail(@PathVariable Long id) { + return productReader.getProduct(id); + } + +} diff --git a/app/src/main/java/com/codesoom/assignment/controllers/product/ProductUpdateController.java b/app/src/main/java/com/codesoom/assignment/controllers/product/ProductUpdateController.java new file mode 100644 index 000000000..4348726f9 --- /dev/null +++ b/app/src/main/java/com/codesoom/assignment/controllers/product/ProductUpdateController.java @@ -0,0 +1,25 @@ +package com.codesoom.assignment.controllers.product; + +import com.codesoom.assignment.application.product.ProductUpdater; +import com.codesoom.assignment.domain.product.Product; +import com.codesoom.assignment.dto.product.ProductData; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; + +@RestController +@RequestMapping("/products") +@RequiredArgsConstructor +public class ProductUpdateController { + + private final ProductUpdater productUpdater; + + @PatchMapping("{id}") + public Product update( + @PathVariable Long id, + @RequestBody @Valid ProductData productData + ) { + return productUpdater.updateProduct(id, productData); + } +} diff --git a/app/src/main/java/com/codesoom/assignment/controllers/user/UserCreateController.java b/app/src/main/java/com/codesoom/assignment/controllers/user/UserCreateController.java new file mode 100644 index 000000000..1929a2281 --- /dev/null +++ b/app/src/main/java/com/codesoom/assignment/controllers/user/UserCreateController.java @@ -0,0 +1,24 @@ +package com.codesoom.assignment.controllers.user; + +import com.codesoom.assignment.application.user.UserCreator; +import com.codesoom.assignment.domain.user.User; +import com.codesoom.assignment.dto.user.UserData; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; + +@RestController +@RequestMapping("/users") +@RequiredArgsConstructor +public class UserCreateController { + private final UserCreator userCreator; + + @PostMapping + @ResponseStatus(HttpStatus.CREATED) + public User create(@RequestBody @Valid UserData userData) { + return userCreator.createUser(userData); + } + +} diff --git a/app/src/main/java/com/codesoom/assignment/controllers/user/UserDeleteController.java b/app/src/main/java/com/codesoom/assignment/controllers/user/UserDeleteController.java new file mode 100644 index 000000000..83a502a8a --- /dev/null +++ b/app/src/main/java/com/codesoom/assignment/controllers/user/UserDeleteController.java @@ -0,0 +1,20 @@ +package com.codesoom.assignment.controllers.user; + +import com.codesoom.assignment.application.user.UserDeleter; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/users") +@RequiredArgsConstructor +public class UserDeleteController { + private final UserDeleter userDeleter; + + @DeleteMapping({"/{id}"}) + @ResponseStatus(HttpStatus.NO_CONTENT) + public void delete(@PathVariable Long id) { + userDeleter.delete(id); + } + +} diff --git a/app/src/main/java/com/codesoom/assignment/controllers/user/UserUpdateController.java b/app/src/main/java/com/codesoom/assignment/controllers/user/UserUpdateController.java new file mode 100644 index 000000000..47414d8a4 --- /dev/null +++ b/app/src/main/java/com/codesoom/assignment/controllers/user/UserUpdateController.java @@ -0,0 +1,20 @@ +package com.codesoom.assignment.controllers.user; + +import com.codesoom.assignment.application.user.UserUpdater; +import com.codesoom.assignment.domain.user.User; +import com.codesoom.assignment.dto.user.UserData; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/users") +@RequiredArgsConstructor +public class UserUpdateController { + private final UserUpdater userUpdater; + + @PatchMapping({"/{id}"}) + public User update(@PathVariable Long id, + @RequestBody UserData userData) { + return userUpdater.update(id, userData); + } +} diff --git a/app/src/main/java/com/codesoom/assignment/domain/Product.java b/app/src/main/java/com/codesoom/assignment/domain/product/Product.java similarity index 93% rename from app/src/main/java/com/codesoom/assignment/domain/Product.java rename to app/src/main/java/com/codesoom/assignment/domain/product/Product.java index 12fb46ab5..2c21dbe45 100644 --- a/app/src/main/java/com/codesoom/assignment/domain/Product.java +++ b/app/src/main/java/com/codesoom/assignment/domain/product/Product.java @@ -1,4 +1,4 @@ -package com.codesoom.assignment.domain; +package com.codesoom.assignment.domain.product; import lombok.AllArgsConstructor; import lombok.Builder; diff --git a/app/src/main/java/com/codesoom/assignment/domain/ProductRepository.java b/app/src/main/java/com/codesoom/assignment/domain/product/ProductRepository.java similarity index 76% rename from app/src/main/java/com/codesoom/assignment/domain/ProductRepository.java rename to app/src/main/java/com/codesoom/assignment/domain/product/ProductRepository.java index 15064c487..740d420a9 100644 --- a/app/src/main/java/com/codesoom/assignment/domain/ProductRepository.java +++ b/app/src/main/java/com/codesoom/assignment/domain/product/ProductRepository.java @@ -1,4 +1,4 @@ -package com.codesoom.assignment.domain; +package com.codesoom.assignment.domain.product; import java.util.List; import java.util.Optional; @@ -11,4 +11,6 @@ public interface ProductRepository { Product save(Product product); void delete(Product product); + + void deleteAll(); } diff --git a/app/src/main/java/com/codesoom/assignment/domain/user/User.java b/app/src/main/java/com/codesoom/assignment/domain/user/User.java new file mode 100644 index 000000000..1569f801f --- /dev/null +++ b/app/src/main/java/com/codesoom/assignment/domain/user/User.java @@ -0,0 +1,33 @@ +package com.codesoom.assignment.domain.user; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +@Entity +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class User { + @Id + @GeneratedValue + private Long id; + + private String name; + + private String email; + + private String password; + + public void change(String name, String email, String password) { + this.name = name; + this.email = email; + this.password = password; + } +} diff --git a/app/src/main/java/com/codesoom/assignment/domain/user/UserRepository.java b/app/src/main/java/com/codesoom/assignment/domain/user/UserRepository.java new file mode 100644 index 000000000..f7883aff6 --- /dev/null +++ b/app/src/main/java/com/codesoom/assignment/domain/user/UserRepository.java @@ -0,0 +1,15 @@ +package com.codesoom.assignment.domain.user; + +import com.codesoom.assignment.domain.product.Product; + +import java.util.Optional; + +public interface UserRepository { + User save(User user); + + Optional findById(Long id); + + void deleteById(Long id); + + void deleteAll(); +} diff --git a/app/src/main/java/com/codesoom/assignment/dto/ErrorResponse.java b/app/src/main/java/com/codesoom/assignment/dto/ErrorResponse.java index aa329697e..cd186d8b4 100644 --- a/app/src/main/java/com/codesoom/assignment/dto/ErrorResponse.java +++ b/app/src/main/java/com/codesoom/assignment/dto/ErrorResponse.java @@ -1,13 +1,32 @@ package com.codesoom.assignment.dto; +import com.codesoom.assignment.application.common.exception.ErrorValidation; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +@Builder +@NoArgsConstructor public class ErrorResponse { + private String code; private String message; + private List errors; + public ErrorResponse(String message) { this.message = message; } - public String getMessage() { - return message; + public ErrorResponse(String code, String message, List errors) { + this.code = code; + this.message = message; + this.errors = errors != null ? errors : List.of(); } + + } diff --git a/app/src/main/java/com/codesoom/assignment/dto/ProductData.java b/app/src/main/java/com/codesoom/assignment/dto/ProductData.java deleted file mode 100644 index ecbc4d320..000000000 --- a/app/src/main/java/com/codesoom/assignment/dto/ProductData.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.codesoom.assignment.dto; - -import lombok.*; - -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; - -@Setter -@Getter -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class ProductData { - private Long id; - - @NotBlank - private String name; - - @NotBlank - private String maker; - - @NotNull - private Integer price; - - private String imageUrl; -} diff --git a/app/src/main/java/com/codesoom/assignment/dto/product/ProductData.java b/app/src/main/java/com/codesoom/assignment/dto/product/ProductData.java new file mode 100644 index 000000000..347db2026 --- /dev/null +++ b/app/src/main/java/com/codesoom/assignment/dto/product/ProductData.java @@ -0,0 +1,29 @@ +package com.codesoom.assignment.dto.product; + +import lombok.*; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.PositiveOrZero; + +@Setter +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ProductData { + private Long id; + + @NotBlank (message = "상품 이름을 입력해주세요.") + private String name; + + @NotBlank (message = "상품 제조사를 입력해주세요.") + private String maker; + + @NotNull (message = "상품 가격을 입력해주세요.") + @PositiveOrZero (message = "상품 가격은 0 이상이어야 합니다.") + private Integer price; + + private String imageUrl; + +} diff --git a/app/src/main/java/com/codesoom/assignment/dto/user/UserData.java b/app/src/main/java/com/codesoom/assignment/dto/user/UserData.java new file mode 100644 index 000000000..2eb26be4e --- /dev/null +++ b/app/src/main/java/com/codesoom/assignment/dto/user/UserData.java @@ -0,0 +1,28 @@ +package com.codesoom.assignment.dto.user; + +import com.codesoom.assignment.domain.user.User; +import lombok.*; + +import javax.validation.constraints.NotBlank; + +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class UserData { + @NotBlank(message = "이름을 입력해주세요.") + private String name; + @NotBlank(message = "이메일을 입력해주세요.") + private String email; + @NotBlank(message = "비밀번호를 입력해주세요.") + private String password; + + public User toUser() { + return User.builder() + .name(name) + .email(email) + .password(password) + .build(); + } +} diff --git a/app/src/main/java/com/codesoom/assignment/infra/JpaProductRepository.java b/app/src/main/java/com/codesoom/assignment/infra/product/JpaProductRepository.java similarity index 71% rename from app/src/main/java/com/codesoom/assignment/infra/JpaProductRepository.java rename to app/src/main/java/com/codesoom/assignment/infra/product/JpaProductRepository.java index c9b8160e6..3db3782bf 100644 --- a/app/src/main/java/com/codesoom/assignment/infra/JpaProductRepository.java +++ b/app/src/main/java/com/codesoom/assignment/infra/product/JpaProductRepository.java @@ -1,7 +1,7 @@ -package com.codesoom.assignment.infra; +package com.codesoom.assignment.infra.product; -import com.codesoom.assignment.domain.Product; -import com.codesoom.assignment.domain.ProductRepository; +import com.codesoom.assignment.domain.product.Product; +import com.codesoom.assignment.domain.product.ProductRepository; import org.springframework.context.annotation.Primary; import org.springframework.data.repository.CrudRepository; diff --git a/app/src/main/java/com/codesoom/assignment/infra/product/exception/ProductNotFoundException.java b/app/src/main/java/com/codesoom/assignment/infra/product/exception/ProductNotFoundException.java new file mode 100644 index 000000000..1865de57a --- /dev/null +++ b/app/src/main/java/com/codesoom/assignment/infra/product/exception/ProductNotFoundException.java @@ -0,0 +1,16 @@ +package com.codesoom.assignment.infra.product.exception; + +import com.codesoom.assignment.application.common.exception.BaseException; + +public class ProductNotFoundException extends BaseException { + public static final String MESSAGE = "해당하는 상품이 존재하지 않습니다"; + + public ProductNotFoundException() { + super(MESSAGE); + } + + @Override + public int getStatusCode() { + return 404; + } +} diff --git a/app/src/main/java/com/codesoom/assignment/infra/user/JpaUserRepository.java b/app/src/main/java/com/codesoom/assignment/infra/user/JpaUserRepository.java new file mode 100644 index 000000000..8c06817fb --- /dev/null +++ b/app/src/main/java/com/codesoom/assignment/infra/user/JpaUserRepository.java @@ -0,0 +1,12 @@ +package com.codesoom.assignment.infra.user; + +import com.codesoom.assignment.domain.user.User; +import com.codesoom.assignment.domain.user.UserRepository; +import org.springframework.context.annotation.Primary; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.repository.CrudRepository; + +@Primary +public interface JpaUserRepository extends + UserRepository, CrudRepository { +} diff --git a/app/src/test/java/com/codesoom/assignment/application/JpaTest.java b/app/src/test/java/com/codesoom/assignment/application/JpaTest.java new file mode 100644 index 000000000..c175b05a3 --- /dev/null +++ b/app/src/test/java/com/codesoom/assignment/application/JpaTest.java @@ -0,0 +1,23 @@ +package com.codesoom.assignment.application; + +import com.codesoom.assignment.domain.product.ProductRepository; +import com.codesoom.assignment.domain.user.UserRepository; +import org.checkerframework.checker.units.qual.A; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; + +@DataJpaTest +public class JpaTest { + @Autowired + public UserRepository userRepository; + @Autowired + public ProductRepository productRepository; + + public UserRepository getUserRepository() { + return userRepository; + } + + public ProductRepository getProductRepository() { + return productRepository; + } +} diff --git a/app/src/test/java/com/codesoom/assignment/application/ProductServiceTest.java b/app/src/test/java/com/codesoom/assignment/application/ProductServiceTest.java deleted file mode 100644 index 18e00cf13..000000000 --- a/app/src/test/java/com/codesoom/assignment/application/ProductServiceTest.java +++ /dev/null @@ -1,138 +0,0 @@ -package com.codesoom.assignment.application; - -import com.codesoom.assignment.ProductNotFoundException; -import com.codesoom.assignment.domain.Product; -import com.codesoom.assignment.domain.ProductRepository; -import com.codesoom.assignment.dto.ProductData; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.List; -import java.util.Optional; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -class ProductServiceTest { - private ProductService productService; - - private ProductRepository productRepository = mock(ProductRepository.class); - - @BeforeEach - void setUp() { - productService = new ProductService(productRepository); - - Product product = Product.builder() - .id(1L) - .name("쥐돌이") - .maker("냥이월드") - .price(5000) - .build(); - - given(productRepository.findAll()).willReturn(List.of(product)); - - given(productRepository.findById(1L)).willReturn(Optional.of(product)); - - given(productRepository.save(any(Product.class))).will(invocation -> { - Product source = invocation.getArgument(0); - return Product.builder() - .id(2L) - .name(source.getName()) - .maker(source.getMaker()) - .price(source.getPrice()) - .build(); - }); - } - - @Test - void getProductsWithNoProduct() { - given(productRepository.findAll()).willReturn(List.of()); - - assertThat(productService.getProducts()).isEmpty(); - } - - @Test - void getProducts() { - List products = productService.getProducts(); - - assertThat(products).isNotEmpty(); - - Product product = products.get(0); - - assertThat(product.getName()).isEqualTo("쥐돌이"); - } - - @Test - void getProductWithExsitedId() { - Product product = productService.getProduct(1L); - - assertThat(product).isNotNull(); - assertThat(product.getName()).isEqualTo("쥐돌이"); - } - - @Test - void getProductWithNotExsitedId() { - assertThatThrownBy(() -> productService.getProduct(1000L)) - .isInstanceOf(ProductNotFoundException.class); - } - - @Test - void createProduct() { - ProductData productData = ProductData.builder() - .name("쥐돌이") - .maker("냥이월드") - .price(5000) - .build(); - - Product product = productService.createProduct(productData); - - verify(productRepository).save(any(Product.class)); - - assertThat(product.getId()).isEqualTo(2L); - assertThat(product.getName()).isEqualTo("쥐돌이"); - assertThat(product.getMaker()).isEqualTo("냥이월드"); - } - - @Test - void updateProductWithExistedId() { - ProductData productData = ProductData.builder() - .name("쥐순이") - .maker("냥이월드") - .price(5000) - .build(); - - Product product = productService.updateProduct(1L, productData); - - assertThat(product.getId()).isEqualTo(1L); - assertThat(product.getName()).isEqualTo("쥐순이"); - } - - @Test - void updateProductWithNotExistedId() { - ProductData productData = ProductData.builder() - .name("쥐순이") - .maker("냥이월드") - .price(5000) - .build(); - - assertThatThrownBy(() -> productService.updateProduct(1000L, productData)) - .isInstanceOf(ProductNotFoundException.class); - } - - @Test - void deleteProductWithExistedId() { - productService.deleteProduct(1L); - - verify(productRepository).delete(any(Product.class)); - } - - @Test - void deleteProductWithNotExistedId() { - assertThatThrownBy(() -> productService.deleteProduct(1000L)) - .isInstanceOf(ProductNotFoundException.class); - } -} diff --git a/app/src/test/java/com/codesoom/assignment/application/product/ProductCreatorTest.java b/app/src/test/java/com/codesoom/assignment/application/product/ProductCreatorTest.java new file mode 100644 index 000000000..cea22a5d9 --- /dev/null +++ b/app/src/test/java/com/codesoom/assignment/application/product/ProductCreatorTest.java @@ -0,0 +1,52 @@ +package com.codesoom.assignment.application.product; + +import com.codesoom.assignment.application.JpaTest; +import com.codesoom.assignment.domain.product.Product; +import com.codesoom.assignment.dto.product.ProductData; +import org.junit.jupiter.api.*; +import org.assertj.core.api.Assertions; + +@SuppressWarnings({"InnerClassMayBeStatic", "NonAsciiCharacters"}) +@DisplayName("ProductCreator 클래스") +class ProductCreatorTest extends JpaTest { + + private final String TEST_NAME = "testName"; + private final String TEST_MAKER = "testMaker"; + private final int TEST_PRICE = 1000; + private final String TEST_IMAGE_URL = "testImageUrl"; + + private ProductData createProductData() { + return ProductData.builder() + .name(TEST_NAME) + .price(TEST_PRICE) + .maker(TEST_MAKER) + .imageUrl(TEST_IMAGE_URL) + .build(); + } + + @Nested + @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) + class createProduct_메서드는 { + private ProductCreator productCreator; + private ProductData productData; + @Nested + @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) + class 상품정보가_주어지면 { + @BeforeEach + void setUp() { + productCreator = new ProductCreator(getProductRepository()); + productData = createProductData(); + } + + @Test + @DisplayName("해당상품정보를_저장후_해당상품정보를_리턴한다") + void 해당상품정보를_저장후_해당상품정보를_리턴한다() { + Product product = productCreator.createProduct(productData); + Assertions.assertThat(product.getName()).isEqualTo(TEST_NAME); + Assertions.assertThat(product.getMaker()).isEqualTo(TEST_MAKER); + Assertions.assertThat(product.getPrice()).isEqualTo(TEST_PRICE); + Assertions.assertThat(product.getImageUrl()).isEqualTo(TEST_IMAGE_URL); + } + } + } +} diff --git a/app/src/test/java/com/codesoom/assignment/application/product/ProductDeleterTest.java b/app/src/test/java/com/codesoom/assignment/application/product/ProductDeleterTest.java new file mode 100644 index 000000000..f21145e1a --- /dev/null +++ b/app/src/test/java/com/codesoom/assignment/application/product/ProductDeleterTest.java @@ -0,0 +1,60 @@ +package com.codesoom.assignment.application.product; + +import com.codesoom.assignment.infra.product.exception.ProductNotFoundException; +import com.codesoom.assignment.application.JpaTest; +import com.codesoom.assignment.domain.product.Product; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.*; + +@SuppressWarnings({"InnerClassMayBeStatic", "NonAsciiCharacters"}) +@DisplayName("ProductDeleter 클래스") +class ProductDeleterTest extends JpaTest { + private final String PRODUCT_NAME = "testProduct"; + private final String PRODUCT_IMAGE_URL = "testImage"; + private final int PRODUCT_PRICE = 1000; + private final String PRODUCT_MAKER = "testMaker"; + + private Product createProduct() { + Product testProduct = Product.builder() + .name(PRODUCT_NAME) + .imageUrl(PRODUCT_IMAGE_URL) + .price(PRODUCT_PRICE) + .maker(PRODUCT_MAKER) + .build(); + return testProduct; + } + + @Nested + @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) + class deleteProduct_메서드는 { + ProductDeleter productDeleter = new ProductDeleter(productRepository, new ProductReader(productRepository)); + + @Nested + @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) + class 해당하는_상품이_없다면 { + @Test + @DisplayName("ProductNotFoundException을_반환한다") + void ProductNotFoundException을_반환한다() { + Assertions.assertThatThrownBy(() -> productDeleter.deleteProduct(1L)) + .isInstanceOf(ProductNotFoundException.class); + } + } + + @Nested + @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) + class 해당하는_상품이_있다면 { + @Test + @DisplayName("해당하는_상품을_삭제한다") + void 해당하는_상품을_삭제한다() { + Product product = createProduct(); + productRepository.save(product); + + productDeleter.deleteProduct(product.getId()); + + Assertions.assertThat(productRepository.findById(product.getId())).isEmpty(); + } + } + } + + +} diff --git a/app/src/test/java/com/codesoom/assignment/application/product/ProductReaderTest.java b/app/src/test/java/com/codesoom/assignment/application/product/ProductReaderTest.java new file mode 100644 index 000000000..2420a6624 --- /dev/null +++ b/app/src/test/java/com/codesoom/assignment/application/product/ProductReaderTest.java @@ -0,0 +1,55 @@ +package com.codesoom.assignment.application.product; + +import com.codesoom.assignment.application.JpaTest; +import com.codesoom.assignment.domain.product.Product; +import org.junit.jupiter.api.*; + +import org.assertj.core.api.Assertions; + +@SuppressWarnings({"InnerClassMayBeStatic", "NonAsciiCharacters"}) +@DisplayName("ProductReader 클래스") +class ProductReaderTest extends JpaTest { + + private final String TEST_NAME = "testName"; + private final String TEST_MAKER = "testMaker"; + private final int TEST_PRICE = 1000; + private final String TEST_IMAGE_URL = "testImageUrl"; + + private Product createProduct(){ + return Product.builder() + .name(TEST_NAME) + .maker(TEST_MAKER) + .price(TEST_PRICE) + .imageUrl(TEST_IMAGE_URL) + .build(); + } + + private ProductReader productReader; + + @Nested + @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) + class getProduct_메서드는 { + + @Nested + @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) + class 상품_아이디가_주어지면 { + private long savedProductId; + + @BeforeEach + void setUp() { + savedProductId = getProductRepository().save(createProduct()).getId(); + productReader = new ProductReader(getProductRepository()); + } + + @DisplayName("해당_상품정보를_리턴한다") + @Test + void it_returns_product() { + Product product = productReader.getProduct(savedProductId); + Assertions.assertThat(product.getName()).isEqualTo(TEST_NAME); + Assertions.assertThat(product.getMaker()).isEqualTo(TEST_MAKER); + Assertions.assertThat(product.getPrice()).isEqualTo(TEST_PRICE); + Assertions.assertThat(product.getImageUrl()).isEqualTo(TEST_IMAGE_URL); + } + } + } +} diff --git a/app/src/test/java/com/codesoom/assignment/application/product/ProductUpdaterTest.java b/app/src/test/java/com/codesoom/assignment/application/product/ProductUpdaterTest.java new file mode 100644 index 000000000..834846cda --- /dev/null +++ b/app/src/test/java/com/codesoom/assignment/application/product/ProductUpdaterTest.java @@ -0,0 +1,81 @@ +package com.codesoom.assignment.application.product; + +import com.codesoom.assignment.infra.product.exception.ProductNotFoundException; +import com.codesoom.assignment.application.JpaTest; +import com.codesoom.assignment.domain.product.Product; +import com.codesoom.assignment.dto.product.ProductData; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.*; + +@SuppressWarnings({"InnerClassMayBeStatic", "NonAsciiCharacters"}) +@DisplayName("ProductUpdater 클래스") +class ProductUpdaterTest extends JpaTest { + private final String PRODUCT_NAME = "testProduct"; + private final String PRODUCT_IMAGE_URL = "testImage"; + private final int PRODUCT_PRICE = 1000; + private final String PRODUCT_MAKER = "testMaker"; + + private Product createProduct() { + Product testProduct = Product.builder() + .name(PRODUCT_NAME) + .imageUrl(PRODUCT_IMAGE_URL) + .price(PRODUCT_PRICE) + .maker(PRODUCT_MAKER) + .build(); + return testProduct; + } + + @Nested + @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) + class updateProduct_메서드는 { + + ProductUpdater productUpdater = new ProductUpdater(productRepository, new ProductReader(productRepository)); + + @Nested + @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) + class 수정할_상품이_없다면 { + @BeforeEach + void setUp() { + productRepository.deleteAll(); + } + + @Test + @DisplayName("ProductNotFoundException을_반환한다") + void ProductNotFoundException을_반환한다() { + Assertions.assertThatThrownBy(() -> productUpdater.updateProduct(1L, null)) + .isInstanceOf(ProductNotFoundException.class); + } + } + + @Nested + @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) + class 수정할_상품이_있다면 { + private Product product; + + @BeforeEach + void setUp() { + productRepository.deleteAll(); + product = createProduct(); + productRepository.save(product); + + } + + @Test + @DisplayName("해당_상품정보를_수정한_후_해당_상품을_반환한다") + void 해당_상품정보를_수정한_후_해당_상품을_반환한다() { + ProductData productData = ProductData.builder() + .name("updatedName") + .maker("updatedMaker") + .price(2000) + .imageUrl("updatedImageUrl") + .build(); + Product updatedProduct = productUpdater.updateProduct(product.getId(), productData); + + Assertions.assertThat(updatedProduct.getName()).isEqualTo(productData.getName()); + Assertions.assertThat(updatedProduct.getMaker()).isEqualTo(productData.getMaker()); + Assertions.assertThat(updatedProduct.getPrice()).isEqualTo(productData.getPrice()); + Assertions.assertThat(updatedProduct.getImageUrl()).isEqualTo(productData.getImageUrl()); + } + } + } +} diff --git a/app/src/test/java/com/codesoom/assignment/application/user/UserCreatorTest.java b/app/src/test/java/com/codesoom/assignment/application/user/UserCreatorTest.java new file mode 100644 index 000000000..9e128fe2a --- /dev/null +++ b/app/src/test/java/com/codesoom/assignment/application/user/UserCreatorTest.java @@ -0,0 +1,54 @@ +package com.codesoom.assignment.application.user; + +import com.codesoom.assignment.application.JpaTest; +import com.codesoom.assignment.domain.user.User; +import com.codesoom.assignment.dto.user.UserData; +import org.junit.jupiter.api.*; + +import org.assertj.core.api.Assertions; + +@SuppressWarnings({"InnerClassMayBeStatic", "NonAsciiCharacters"}) +@DisplayName("UserCreator 클래스") +class UserCreatorTest extends JpaTest { + private final String TEST_NAME = "testName"; + private final String TEST_EMAIL = "test@Email.com"; + private final String TEST_PASSWORD = "testPassword"; + + private UserData createUserRequest() { + return new UserData( + TEST_NAME, + TEST_EMAIL, + TEST_PASSWORD + ); + } + + @Nested + @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) + class createUser_메서드는 { + + private UserData USER_REQUEST; + private UserCreator userCreator; + + @Nested + @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) + class 유저_정보가_주어지면 { + + @BeforeEach + void setUp() { + USER_REQUEST = createUserRequest(); + userCreator = new UserCreator(getUserRepository()); + } + + @Test + @DisplayName("해당_유저정보를_저장_후_저장한_유저정보를_리턴한다") + void it_saves_and_returns_user() { + User user = userCreator.createUser(USER_REQUEST); + + Assertions.assertThat(user.getName()).isEqualTo(TEST_NAME); + Assertions.assertThat(user.getEmail()).isEqualTo(TEST_EMAIL); + Assertions.assertThat(user.getPassword()).isEqualTo(TEST_PASSWORD); + } + + } + } +} diff --git a/app/src/test/java/com/codesoom/assignment/application/user/UserDeleterTest.java b/app/src/test/java/com/codesoom/assignment/application/user/UserDeleterTest.java new file mode 100644 index 000000000..99814bb68 --- /dev/null +++ b/app/src/test/java/com/codesoom/assignment/application/user/UserDeleterTest.java @@ -0,0 +1,47 @@ +package com.codesoom.assignment.application.user; + +import com.codesoom.assignment.application.JpaTest; +import com.codesoom.assignment.domain.user.User; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.*; + +@SuppressWarnings({"NonAsciiCharacters", "NonAsciiCharacters"}) +@DisplayName("UserDeleter 클래스") +class UserDeleterTest extends JpaTest { + private final String TEST_NAME = "testName"; + private final String TEST_EMAIL = "test@Email.com"; + private final String TEST_PASSWORD = "testPassword"; + + private User createUser() { + return User.builder() + .name(TEST_NAME) + .email(TEST_EMAIL) + .password(TEST_PASSWORD) + .build(); + } + + @Nested + @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) + class deleteUser_메서드는 { + + @Nested + @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) + class 유저아이디가_주어지면 { + private long deleteId; + + @BeforeEach + void setUp() { + User user = createUser(); + deleteId = getUserRepository().save(user).getId(); + } + + @Test + @DisplayName("해당_유저정보를_삭제한다") + void it_deletes_user() { + UserDeleter userDeleter = new UserDeleter(getUserRepository(), new UserReader(getUserRepository())); + userDeleter.delete(deleteId); + Assertions.assertThat(getUserRepository().findById(deleteId)).isEmpty(); + } + } + } +} diff --git a/app/src/test/java/com/codesoom/assignment/application/user/UserReaderTest.java b/app/src/test/java/com/codesoom/assignment/application/user/UserReaderTest.java new file mode 100644 index 000000000..c67154502 --- /dev/null +++ b/app/src/test/java/com/codesoom/assignment/application/user/UserReaderTest.java @@ -0,0 +1,51 @@ +package com.codesoom.assignment.application.user; + +import com.codesoom.assignment.application.JpaTest; +import com.codesoom.assignment.dto.user.UserData; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.*; + + +@SuppressWarnings({"InnerClassMayBeStatic", "NonAsciiCharacters"}) +@DisplayName("UserReader 클래스") +class UserReaderTest extends JpaTest { + private final String TEST_NAME = "testName"; + private final String TEST_EMAIL = "test@Email.com"; + private final String TEST_PASSWORD = "testPassword"; + + private UserData createUserRequest() { + return new UserData( + TEST_NAME, + TEST_EMAIL, + TEST_PASSWORD + ); + } + + @Nested + @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) + class getUser_메서드는 { + @Nested + @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) + class 유저_아이디가_주어지면 { + private UserData USER_REQUEST; + private Long savedUserId; + private UserReader userReader; + + @BeforeEach + void setUp() { + USER_REQUEST = createUserRequest(); + savedUserId = getUserRepository().save(USER_REQUEST.toUser()).getId(); + userReader = new UserReader(getUserRepository()); + } + + @DisplayName("해당_유저정보를_리턴한다") + @Test + void it_returns_user() { + Assertions.assertThat(userReader.getUser(savedUserId).getName()).isEqualTo(TEST_NAME); + Assertions.assertThat(userReader.getUser(savedUserId).getEmail()).isEqualTo(TEST_EMAIL); + Assertions.assertThat(userReader.getUser(savedUserId).getPassword()).isEqualTo(TEST_PASSWORD); + } + } + } + +} diff --git a/app/src/test/java/com/codesoom/assignment/application/user/UserUpdaterTest.java b/app/src/test/java/com/codesoom/assignment/application/user/UserUpdaterTest.java new file mode 100644 index 000000000..868c57a5b --- /dev/null +++ b/app/src/test/java/com/codesoom/assignment/application/user/UserUpdaterTest.java @@ -0,0 +1,57 @@ +package com.codesoom.assignment.application.user; + +import com.codesoom.assignment.application.JpaTest; +import com.codesoom.assignment.domain.user.User; +import com.codesoom.assignment.dto.user.UserData; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.*; + + +@SuppressWarnings({"InnerClassMayBeStatic", "NonAsciiCharacters"}) +@DisplayName("UserUpdater 클래스") +class UserUpdaterTest extends JpaTest { + private final String TEST_NAME = "testName"; + private final String TEST_EMAIL = "test@Email.com"; + private final String TEST_PASSWORD = "testPassword"; + + private UserData createUserRequest() { + return new UserData( + TEST_NAME, + TEST_EMAIL, + TEST_PASSWORD + ); + } + + @Nested + @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) + class updateUser_메서드는 { + private UserUpdater userUpdater = new UserUpdater(userRepository, new UserReader(userRepository)); + @Nested + @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) + class 수정할_유저가_존재한다면 { + private Long id; + @BeforeEach + void setUp() { + userRepository.deleteAll(); + UserData userData = createUserRequest(); + id = userRepository.save(userData.toUser()).getId(); + } + + @DisplayName("해당 유저정보를 수정 후 수정한 유저정보를 리턴한다") + @Test + void it_updates_and_returns_user() { + UserData request = UserData.builder() + .name("newName") + .email("newEmail") + .password("newPassword") + .build(); + + User user = userUpdater.update(id, request); + + Assertions.assertThat(user.getName()).isEqualTo("newName"); + Assertions.assertThat(user.getEmail()).isEqualTo("newEmail"); + Assertions.assertThat(user.getPassword()).isEqualTo("newPassword"); + } + } + } +} diff --git a/app/src/test/java/com/codesoom/assignment/controllers/ProductControllerTest.java b/app/src/test/java/com/codesoom/assignment/controllers/ProductControllerTest.java deleted file mode 100644 index 64c0a1479..000000000 --- a/app/src/test/java/com/codesoom/assignment/controllers/ProductControllerTest.java +++ /dev/null @@ -1,195 +0,0 @@ -package com.codesoom.assignment.controllers; - -import com.codesoom.assignment.ProductNotFoundException; -import com.codesoom.assignment.application.ProductService; -import com.codesoom.assignment.domain.Product; -import com.codesoom.assignment.dto.ProductData; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.http.MediaType; -import org.springframework.test.web.servlet.MockMvc; - -import java.util.List; - -import static org.hamcrest.Matchers.containsString; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.verify; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -@WebMvcTest(ProductController.class) -class ProductControllerTest { - @Autowired - private MockMvc mockMvc; - - @MockBean - private ProductService productService; - - @BeforeEach - void setUp() { - Product product = Product.builder() - .id(1L) - .name("쥐돌이") - .maker("냥이월드") - .price(5000) - .build(); - - given(productService.getProducts()).willReturn(List.of(product)); - - given(productService.getProduct(1L)).willReturn(product); - - given(productService.getProduct(1000L)) - .willThrow(new ProductNotFoundException(1000L)); - - given(productService.createProduct(any(ProductData.class))) - .willReturn(product); - - given(productService.updateProduct(eq(1L), any(ProductData.class))) - .will(invocation -> { - Long id = invocation.getArgument(0); - ProductData productData = invocation.getArgument(1); - return Product.builder() - .id(id) - .name(productData.getName()) - .maker(productData.getMaker()) - .price(productData.getPrice()) - .build(); - }); - - given(productService.updateProduct(eq(1000L), any(ProductData.class))) - .willThrow(new ProductNotFoundException(1000L)); - - given(productService.deleteProduct(1000L)) - .willThrow(new ProductNotFoundException(1000L)); - } - - @Test - void list() throws Exception { - mockMvc.perform( - get("/products") - .accept(MediaType.APPLICATION_JSON_UTF8) - ) - .andExpect(status().isOk()) - .andExpect(content().string(containsString("쥐돌이"))); - } - - @Test - void deatilWithExsitedProduct() throws Exception { - mockMvc.perform( - get("/products/1") - .accept(MediaType.APPLICATION_JSON_UTF8) - ) - .andExpect(status().isOk()) - .andExpect(content().string(containsString("쥐돌이"))); - } - - @Test - void deatilWithNotExsitedProduct() throws Exception { - mockMvc.perform(get("/products/1000")) - .andExpect(status().isNotFound()); - } - - @Test - void create() throws Exception { - mockMvc.perform( - post("/products") - .accept(MediaType.APPLICATION_JSON_UTF8) - .contentType(MediaType.APPLICATION_JSON) - .content("{\"name\":\"쥐돌이\",\"maker\":\"냥이월드\"," + - "\"price\":5000}") - ) - .andExpect(status().isCreated()) - .andExpect(content().string(containsString("쥐돌이"))); - - verify(productService).createProduct(any(ProductData.class)); - } - - @Test - void createWithValidAttributes() throws Exception { - mockMvc.perform( - post("/products") - .accept(MediaType.APPLICATION_JSON_UTF8) - .contentType(MediaType.APPLICATION_JSON) - .content("{\"name\":\"쥐돌이\",\"maker\":\"냥이월드\"," + - "\"price\":5000}") - ) - .andExpect(status().isCreated()) - .andExpect(content().string(containsString("쥐돌이"))); - - verify(productService).createProduct(any(ProductData.class)); - } - - @Test - void createWithInvalidAttributes() throws Exception { - mockMvc.perform( - post("/products") - .accept(MediaType.APPLICATION_JSON_UTF8) - .contentType(MediaType.APPLICATION_JSON) - .content("{\"name\":\"\",\"maker\":\"\"," + - "\"price\":0}") - ) - .andExpect(status().isBadRequest()); - } - - @Test - void updateWithExistedProduct() throws Exception { - mockMvc.perform( - patch("/products/1") - .accept(MediaType.APPLICATION_JSON_UTF8) - .contentType(MediaType.APPLICATION_JSON) - .content("{\"name\":\"쥐순이\",\"maker\":\"냥이월드\"," + - "\"price\":5000}") - ) - .andExpect(status().isOk()) - .andExpect(content().string(containsString("쥐순이"))); - - verify(productService).updateProduct(eq(1L), any(ProductData.class)); - } - - @Test - void updateWithNotExistedProduct() throws Exception { - mockMvc.perform( - patch("/products/1000") - .contentType(MediaType.APPLICATION_JSON) - .content("{\"name\":\"쥐순이\",\"maker\":\"냥이월드\"," + - "\"price\":5000}") - ) - .andExpect(status().isNotFound()); - - verify(productService).updateProduct(eq(1000L), any(ProductData.class)); - } - - @Test - void updateWithInvalidAttributes() throws Exception { - mockMvc.perform( - patch("/products/1") - .accept(MediaType.APPLICATION_JSON_UTF8) - .contentType(MediaType.APPLICATION_JSON) - .content("{\"name\":\"\",\"maker\":\"\"," + - "\"price\":0}") - ) - .andExpect(status().isBadRequest()); - } - - @Test - void destroyWithExistedProduct() throws Exception { - mockMvc.perform(delete("/products/1")) - .andExpect(status().isNoContent()); - - verify(productService).deleteProduct(1L); - } - - @Test - void destroyWithNotExistedProduct() throws Exception { - mockMvc.perform(delete("/products/1000")) - .andExpect(status().isNotFound()); - - verify(productService).deleteProduct(1000L); - } -} diff --git a/app/src/test/java/com/codesoom/assignment/controllers/prouct/ProductControllerTest.java b/app/src/test/java/com/codesoom/assignment/controllers/prouct/ProductControllerTest.java new file mode 100644 index 000000000..b6f1ffc06 --- /dev/null +++ b/app/src/test/java/com/codesoom/assignment/controllers/prouct/ProductControllerTest.java @@ -0,0 +1,234 @@ +package com.codesoom.assignment.controllers.prouct; +import com.codesoom.assignment.domain.product.Product; +import com.codesoom.assignment.domain.product.ProductRepository; +import com.codesoom.assignment.dto.product.ProductData; + +import com.codesoom.assignment.infra.product.exception.ProductNotFoundException; +import com.fasterxml.jackson.databind.ObjectMapper; +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.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.web.servlet.MockMvc; + +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.springframework.http.MediaType.APPLICATION_JSON; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@AutoConfigureMockMvc +class ProductControllerTest { + + @Autowired + MockMvc mockMvc; + + @Autowired + ProductRepository productRepository; + + @Autowired + ObjectMapper objectMapper; + + @BeforeEach + void setUp() { + productRepository.deleteAll(); + } + + @Test + @DisplayName("상품을 등록 후 생성된 상품정보를 반환한다.") + void createProduct() throws Exception { + // given + ProductData productData = ProductData.builder().name("catToy").maker("CatMaker").price(1200).imageUrl("test/img.jpg").build(); + String jsonString = objectMapper.writeValueAsString(productData); + + // expected + mockMvc.perform(post("/products") + .contentType(APPLICATION_JSON) + .content(jsonString)) + .andExpect(status().isCreated()) + .andExpect(jsonPath("name").value("catToy")) + .andExpect(jsonPath("maker").value("CatMaker")) + .andExpect(jsonPath("price").value(1200)) + .andExpect(jsonPath("imageUrl").value("test/img.jpg")) + .andDo(print()); + } + + @Test + @DisplayName("상품 리스트 요청 시 상품리스트 반환한다.") + void getProducts() throws Exception { + // given + Product product = Product.builder() + .name("catToy1") + .price(2000) + .maker("maker1") + .imageUrl("test/img1.jpg") + .build(); + + productRepository.save(product); + + // expected + mockMvc.perform(get("/products")) + .andExpect(status().isOk()) + .andExpect(jsonPath("[0].name").value("catToy1")) + .andExpect(jsonPath("[0].maker").value("maker1")) + .andExpect(jsonPath("[0].price").value(2000)) + .andExpect(jsonPath("[0].imageUrl").value("test/img1.jpg")) + .andDo(print()); + } + + @Test + @DisplayName("단일 상품 조회 시 해당 상품정보를 반환한다.") + void getProduct() throws Exception { + // given + Product product = Product.builder() + .name("catToy1") + .price(2000) + .maker("maker1") + .imageUrl("test/img1.jpg") + .build(); + + Product savedProduct = productRepository.save(product); + + // expected + mockMvc.perform(get("/products/" + savedProduct.getId())) + .andExpect(status().isOk()) + .andExpect(jsonPath("name").value("catToy1")) + .andExpect(jsonPath("maker").value("maker1")) + .andExpect(jsonPath("price").value(2000)) + .andExpect(jsonPath("imageUrl").value("test/img1.jpg")) + .andDo(print()); + } + + @Test + @DisplayName("단일 상품 조회 시 없는 경우 ProductNotFound 예외 발생") + void getProductNotFound() throws Exception { + + // expected + mockMvc.perform(get("/products/" + 1L)) + .andExpect(status().isNotFound()) + .andExpect(result -> assertTrue(result.getResolvedException() instanceof ProductNotFoundException)) + .andExpect(jsonPath("message").value(ProductNotFoundException.MESSAGE)) + .andDo(print()); + } + + @Test + @DisplayName("상품 수정 요청시 해당 상품정보를 수정한다.") + void updateProduct() throws Exception { + // given + Product product = Product.builder() + .name("catToy1") + .price(2000) + .maker("maker1") + .imageUrl("test/img1.jpg") + .build(); + Product savedProduct = productRepository.save(product); + ProductData productRequest = ProductData.builder().name("update").maker("update").price(3000).imageUrl("test/update.jpg").build(); + + // expected + mockMvc.perform(patch("/products/" + savedProduct.getId()) + .contentType(APPLICATION_JSON) + .content(objectMapper.writeValueAsString(productRequest)) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("name").value("update")) + .andExpect(jsonPath("maker").value("update")) + .andExpect(jsonPath("price").value(3000)) + .andExpect(jsonPath("imageUrl").value("test/update.jpg")) + .andDo(print()); + } + + @Test + @DisplayName("상품 수정 요청시 없는 경우 ProductNotFound 예외 발생") + void updateProductNotFound() throws Exception { + // given + ProductData productData = ProductData.builder().name("update").maker("update").price(3000).imageUrl("test/update.jpg").build(); + + // expected + mockMvc.perform(patch("/products/" + 100L) + .contentType(APPLICATION_JSON) + .content(objectMapper.writeValueAsString(productData)) + ) + .andExpect(status().isNotFound()) + .andExpect(result -> assertTrue(result.getResolvedException() instanceof ProductNotFoundException)) + .andExpect(jsonPath("message").value(ProductNotFoundException.MESSAGE)) + .andDo(print()); + } + + @Test + @DisplayName("상품 삭제 요청 시 해당 상품을 삭제한다.") + void deleteProducts() throws Exception { + // given + Product product = Product.builder() + .name("deleteTarget") + .price(2000) + .maker("deleteMaker") + .imageUrl("test/delete.jpg") + .build(); + productRepository.save(product); + + // expected + mockMvc.perform(delete("/products/" + product.getId())) + .andExpect(status().isNoContent()) + .andDo(print()); + + } + + @Test + @DisplayName("상품 삭제 요청 시 없는 경우 ProductNotFound 예외 발생") + void deleteProductsNotFound() throws Exception { + // expected + mockMvc.perform(delete("/products/" + 100L)) + .andExpect(status().isNotFound()) + .andExpect(result -> assertTrue(result.getResolvedException() instanceof ProductNotFoundException)) + .andExpect(jsonPath("message").value(ProductNotFoundException.MESSAGE)) + .andDo(print()); + } + + @Test + @DisplayName("상품 생성시 모든 항목의 값이 없는 요청인 경우 에러를 반환한다.") + void createProductInvalidRequest() throws Exception { + // given + ProductData productData = ProductData.builder().name("").maker("").price(0).imageUrl("").build(); + + // expected + mockMvc.perform(post("/products") + .contentType(APPLICATION_JSON) + .content(objectMapper.writeValueAsString(productData))) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("errors[0].source").value("name")) + .andExpect(jsonPath("errors[0].type").value("BAD_REQUEST")) + .andExpect(jsonPath("errors[0].message").value("상품 이름을 입력해주세요.")) + .andExpect(jsonPath("errors[1].source").value("maker")) + .andExpect(jsonPath("errors[1].type").value("BAD_REQUEST")) + .andExpect(jsonPath("errors[1].message").value("상품 제조사를 입력해주세요.")) + .andDo(print()); + } + + @ParameterizedTest + @DisplayName("상품 생성시 올바르지 않은 요청 케이스별 테스트 요청인 경우 에러응답을 반환한다.") + @MethodSource("provideInvalidProductRequests") + void createProductInvalidRequestCase(ProductData productRequest) throws Exception { + mockMvc.perform(post("/products") + .contentType(APPLICATION_JSON) + .content(objectMapper.writeValueAsString(productRequest))) + .andExpect(status().isBadRequest()) + .andDo(print()); + } + + private static Stream provideInvalidProductRequests() { + return Stream.of( + Arguments.of(ProductData.builder().name("").maker("testMaker").price(1000).imageUrl("").build()), + Arguments.of(ProductData.builder().name("testName").maker("").price(1000).imageUrl("").build()), + Arguments.of(ProductData.builder().name("testName").maker("testMaker").price(-10).imageUrl("").build()) + ); + } +} diff --git a/app/src/test/java/com/codesoom/assignment/controllers/user/UserControllerTest.java b/app/src/test/java/com/codesoom/assignment/controllers/user/UserControllerTest.java new file mode 100644 index 000000000..8391eb1de --- /dev/null +++ b/app/src/test/java/com/codesoom/assignment/controllers/user/UserControllerTest.java @@ -0,0 +1,264 @@ +package com.codesoom.assignment.controllers.user; + +import com.codesoom.assignment.domain.user.User; +import com.codesoom.assignment.domain.user.UserRepository; +import com.codesoom.assignment.dto.user.UserData; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.web.servlet.MockMvc; + +import java.util.stream.Stream; + +import static org.springframework.http.MediaType.APPLICATION_JSON; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@AutoConfigureMockMvc +@SuppressWarnings({"InnerClassMayBeStatic", "NonAsciiCharacters"}) +@DisplayName("UserController 클래스") +class UserControllerTest { + + private final String TEST_NAME = "testName"; + private final String TEST_EMAIL = "test@Email"; + private final String TEST_PASSWORD = "testPassword"; + @Autowired + MockMvc mockMvc; + @Autowired + ObjectMapper objectMapper; + @Autowired + UserRepository userRepository; + + private UserData USER_REQUEST; + + private static Stream provideInvalidUserRequests() { + return Stream.of( + Arguments.of(UserData.builder().name("").email("").password("").build()), + Arguments.of(UserData.builder().name("testName").email("").password("").build()), + Arguments.of(UserData.builder().name("").email("test@Email").password("").build()), + Arguments.of(UserData.builder().name("").email("").password("testPassword").build()) + ); + } + + @Nested + @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) + class create_메서드는 { + + @Nested + @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) + class 올바르지_않은_유저정보_요청이_오면 { + + @ParameterizedTest + @DisplayName("상품 생성시 올바르지 않은 요청 케이스별 테스트 요청인 경우 에러응답을 반환한다.") + @MethodSource("com.codesoom.assignment.controllers.user.UserControllerTest#provideInvalidUserRequests") + void createProductInvalidRequestCase(UserData user) throws Exception { + + mockMvc.perform(post("/users") + .contentType(APPLICATION_JSON) + .content(objectMapper.writeValueAsString(user))) + .andExpect(status().isBadRequest()) + .andDo(print()); + } + + } + + @Nested + @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) + class 유저_정보_요청이_오면 { + + @BeforeEach + void setUp() { + USER_REQUEST = UserData.builder() + .name(TEST_NAME) + .email(TEST_EMAIL) + .password(TEST_PASSWORD) + .build(); + } + + @DisplayName("해당_유저정보를_등록_후_저장한_유저정보를_리턴한다") + @Test + void it_saves_and_returns_user() throws Exception { + String jsonString = objectMapper.writeValueAsString(USER_REQUEST); + + mockMvc.perform(post("/users") + .contentType("application/json") + .content(jsonString)) + .andExpect(status().isCreated()) + .andExpect(jsonPath("name").value("testName")) + .andExpect(jsonPath("email").value("test@Email")) + .andExpect(jsonPath("password").value("testPassword")) + .andDo(print()); + } + } + + @Nested + @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) + class 이름이_없는_유저_정보_요청이_오면 { + @BeforeEach + void setUp() { + USER_REQUEST = UserData.builder() + .name("") + .email(TEST_EMAIL) + .password(TEST_PASSWORD) + .build(); + } + + @DisplayName("에러정보를_리턴한다") + @Test + void it_validate_and_returns_error() throws Exception { + String jsonString = objectMapper.writeValueAsString(USER_REQUEST); + + mockMvc.perform(post("/users") + .contentType("application/json") + .content(jsonString)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("errors[0].source").value("name")) + .andExpect(jsonPath("errors[0].type").value("BAD_REQUEST")) + .andExpect(jsonPath("errors[0].message").value("이름을 입력해주세요.")) + .andDo(print()); + } + } + + @Nested + @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) + class 이메일이_없는_유저_정보_요청이_오면 { + @BeforeEach + void setUp() { + USER_REQUEST = UserData.builder() + .name(TEST_NAME) + .email("") + .password(TEST_PASSWORD) + .build(); + } + + @DisplayName("에러정보를_리턴한다") + @Test + void it_validate_and_returns_error() throws Exception { + String jsonString = objectMapper.writeValueAsString(USER_REQUEST); + + mockMvc.perform(post("/users") + .contentType("application/json") + .content(jsonString)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("errors[0].source").value("email")) + .andExpect(jsonPath("errors[0].type").value("BAD_REQUEST")) + .andExpect(jsonPath("errors[0].message").value("이메일을 입력해주세요.")) + .andDo(print()); + } + } + + @Nested + @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) + class 패스워드가_없는_유저_정보_요청이_오면 { + @BeforeEach + void setUp() { + USER_REQUEST = UserData.builder() + .name(TEST_NAME) + .email(TEST_EMAIL) + .build(); + } + + @DisplayName("에러정보를_리턴한다") + @Test + void it_validate_and_returns_error() throws Exception { + String jsonString = objectMapper.writeValueAsString(USER_REQUEST); + + mockMvc.perform(post("/users") + .contentType("application/json") + .content(jsonString)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("errors[0].source").value("password")) + .andExpect(jsonPath("errors[0].type").value("BAD_REQUEST")) + .andExpect(jsonPath("errors[0].message").value("비밀번호를 입력해주세요.")) + .andDo(print()); + } + } + } + + @Nested + @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) + class update_메서드 { + + @Nested + @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) + class 유저_수정_요청이_오면 { + private Long id; + + @BeforeEach + void setUp() { + userRepository.deleteAll(); + User user = User.builder() + .name(TEST_NAME) + .email(TEST_EMAIL) + .password(TEST_PASSWORD) + .build(); + + id = userRepository.save(user).getId(); + } + + @DisplayName("해당_유저정보를_수정_후_수정한_유저정보를_리턴한다") + @Test + void it_updates_and_returns_user() throws Exception { + UserData request = UserData.builder() + .name("newName") + .email("newEmail") + .password("newPassword") + .build(); + + String jsonString = objectMapper.writeValueAsString(request); + + mockMvc.perform(patch("/users/" + id) + .contentType("application/json") + .content(jsonString)) + .andExpect(status().isOk()) + .andExpect(jsonPath("name").value("newName")) + .andExpect(jsonPath("email").value("newEmail")) + .andExpect(jsonPath("password").value("newPassword")) + .andDo(print()); + + } + + } + } + + + @Nested + @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) + class delete_메서드는 { + + @Nested + @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) + class 유저_삭제_요청이_오면 { + private Long id; + + @BeforeEach + void setUp() { + userRepository.deleteAll(); + User user = User.builder() + .name(TEST_NAME) + .email(TEST_EMAIL) + .password(TEST_PASSWORD) + .build(); + + id = userRepository.save(user).getId(); + } + + @DisplayName("해당_유저정보를_삭제한다") + @Test + void it_deletes_user() throws Exception { + mockMvc.perform(delete("/users/" + id) + .contentType("application/json")) + .andExpect(status().isNoContent()) + .andDo(print()); + } + } + } +} diff --git a/app/src/test/java/com/codesoom/assignment/domain/ProductTest.java b/app/src/test/java/com/codesoom/assignment/domain/ProductTest.java index 19babb1ea..16a6288e3 100644 --- a/app/src/test/java/com/codesoom/assignment/domain/ProductTest.java +++ b/app/src/test/java/com/codesoom/assignment/domain/ProductTest.java @@ -1,5 +1,6 @@ package com.codesoom.assignment.domain; +import com.codesoom.assignment.domain.product.Product; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat;