From 4b32224d16ecc4ac384c42ded1286a442efde1f3 Mon Sep 17 00:00:00 2001 From: dawidg90 Date: Sat, 14 Jun 2025 21:27:46 +0200 Subject: [PATCH 01/11] Orders and payments added [wip]. --- .../server/controller/OrderController.java | 40 ++++++++ src/main/java/dg/shop/server/model/Order.java | 53 +++++++++++ .../java/dg/shop/server/model/OrderItem.java | 37 ++++++++ .../java/dg/shop/server/model/Payment.java | 46 +++++++++ .../shop/server/payload/order/OrderDTO.java | 16 ++++ .../server/payload/order/OrderItemDTO.java | 10 ++ .../server/payload/order/OrderRequestDTO.java | 9 ++ .../shop/server/payload/order/PaymentDTO.java | 11 +++ .../repository/OrderItemRepository.java | 9 ++ .../server/repository/OrderRepository.java | 9 ++ .../server/repository/PaymentRepository.java | 9 ++ .../service/address/AddressService.java | 4 + .../service/address/AddressServiceImpl.java | 18 ++-- .../shop/server/service/cart/CartService.java | 2 + .../server/service/cart/CartServiceImpl.java | 22 ++--- .../service/category/CategoryService.java | 4 + .../service/category/CategoryServiceImpl.java | 6 +- .../service/order/OrderItemService.java | 18 ++++ .../service/order/OrderItemServiceImpl.java | 50 ++++++++++ .../server/service/order/OrderService.java | 15 +++ .../service/order/OrderServiceImpl.java | 94 +++++++++++++++++++ .../server/service/order/PaymentService.java | 10 ++ .../service/order/PaymentServiceImpl.java | 27 ++++++ .../service/product/ProductService.java | 4 + .../service/product/ProductServiceImpl.java | 16 +--- 25 files changed, 505 insertions(+), 34 deletions(-) create mode 100644 src/main/java/dg/shop/server/controller/OrderController.java create mode 100644 src/main/java/dg/shop/server/model/Order.java create mode 100644 src/main/java/dg/shop/server/model/OrderItem.java create mode 100644 src/main/java/dg/shop/server/model/Payment.java create mode 100644 src/main/java/dg/shop/server/payload/order/OrderDTO.java create mode 100644 src/main/java/dg/shop/server/payload/order/OrderItemDTO.java create mode 100644 src/main/java/dg/shop/server/payload/order/OrderRequestDTO.java create mode 100644 src/main/java/dg/shop/server/payload/order/PaymentDTO.java create mode 100644 src/main/java/dg/shop/server/repository/OrderItemRepository.java create mode 100644 src/main/java/dg/shop/server/repository/OrderRepository.java create mode 100644 src/main/java/dg/shop/server/repository/PaymentRepository.java create mode 100644 src/main/java/dg/shop/server/service/order/OrderItemService.java create mode 100644 src/main/java/dg/shop/server/service/order/OrderItemServiceImpl.java create mode 100644 src/main/java/dg/shop/server/service/order/OrderService.java create mode 100644 src/main/java/dg/shop/server/service/order/OrderServiceImpl.java create mode 100644 src/main/java/dg/shop/server/service/order/PaymentService.java create mode 100644 src/main/java/dg/shop/server/service/order/PaymentServiceImpl.java diff --git a/src/main/java/dg/shop/server/controller/OrderController.java b/src/main/java/dg/shop/server/controller/OrderController.java new file mode 100644 index 0000000..334d67c --- /dev/null +++ b/src/main/java/dg/shop/server/controller/OrderController.java @@ -0,0 +1,40 @@ +package dg.shop.server.controller; + +import dg.shop.server.payload.order.OrderDTO; +import dg.shop.server.payload.order.OrderRequestDTO; +import dg.shop.server.service.order.OrderService; +import dg.shop.server.util.AuthUtils; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class OrderController { + + private OrderService orderService; + + private AuthUtils authUtils; + + public OrderController(OrderService orderService, AuthUtils authUtils) { + this.orderService = orderService; + this.authUtils = authUtils; + } + + @PostMapping("/api/order/users/payments/{paymentMethod}") + public ResponseEntity orderProducts(@PathVariable String paymentMethod, @RequestBody OrderRequestDTO orderRequestDTO) { + String email = authUtils.signedInEmail(); + OrderDTO order = orderService.placeOrder( + email, + orderRequestDTO.addressId(), + paymentMethod, + orderRequestDTO.paymentGatewayName(), + orderRequestDTO.paymentGatewayPaymentId(), + orderRequestDTO.paymentGatewayStatus(), + orderRequestDTO.paymentGatewayResponseMessage() + ); + return new ResponseEntity<>(order, HttpStatus.CREATED); + } +} diff --git a/src/main/java/dg/shop/server/model/Order.java b/src/main/java/dg/shop/server/model/Order.java new file mode 100644 index 0000000..d255d18 --- /dev/null +++ b/src/main/java/dg/shop/server/model/Order.java @@ -0,0 +1,53 @@ +package dg.shop.server.model; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; +import jakarta.validation.constraints.Email; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; + +@Entity +@Data +@Table(name = "orders") +@NoArgsConstructor +@AllArgsConstructor +public class Order { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long orderId; + + @Email + @Column(nullable = false) + private String email; + + @OneToMany(mappedBy = "order", cascade = CascadeType.ALL) + private List orderItems = new ArrayList<>(); + + private LocalDate orderDate; + + @OneToOne + @JoinColumn(name = "payment_id") + private Payment payment; + + private double totalAmount; + + private String orderStatus; + + @ManyToOne + @JoinColumn(name = "address_id") + private Address address; +} diff --git a/src/main/java/dg/shop/server/model/OrderItem.java b/src/main/java/dg/shop/server/model/OrderItem.java new file mode 100644 index 0000000..159fe56 --- /dev/null +++ b/src/main/java/dg/shop/server/model/OrderItem.java @@ -0,0 +1,37 @@ +package dg.shop.server.model; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Entity +@Data +@Table(name = "order_items") +@NoArgsConstructor +@AllArgsConstructor +public class OrderItem { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long orderItemId; + + @ManyToOne + @JoinColumn(name = "product_id") + private Product product; + + @ManyToOne + @JoinColumn(name = "order_id") + private Order order; + + private int quantity; + + private double discount; + + private double orderedProductPrice; +} diff --git a/src/main/java/dg/shop/server/model/Payment.java b/src/main/java/dg/shop/server/model/Payment.java new file mode 100644 index 0000000..6e0bf13 --- /dev/null +++ b/src/main/java/dg/shop/server/model/Payment.java @@ -0,0 +1,46 @@ +package dg.shop.server.model; + +import dg.shop.server.validation.RequiredSize; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Entity +@Data +@Table(name = "payments") +@NoArgsConstructor +@AllArgsConstructor +public class Payment { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long paymentId; + + @OneToOne(mappedBy = "payment", cascade = CascadeType.ALL) + private Order order; + + @NotBlank + @RequiredSize(min = 4, sizeMessage = "Payment method must contain at least 4 characters") + private String paymentMethod; + + private String paymentGatewayPaymentId; + private String paymentGatewayStatus; + private String paymentGatewayResponseMessage; + private String paymentGatewayName; + + public Payment(String paymentMethod, String paymentGatewayPaymentId, String paymentGatewayStatus, String paymentGatewayResponseMessage, String paymentGatewayName) { + this.paymentMethod = paymentMethod; + this.paymentGatewayPaymentId = paymentGatewayPaymentId; + this.paymentGatewayStatus = paymentGatewayStatus; + this.paymentGatewayResponseMessage = paymentGatewayResponseMessage; + this.paymentGatewayName = paymentGatewayName; + } +} diff --git a/src/main/java/dg/shop/server/payload/order/OrderDTO.java b/src/main/java/dg/shop/server/payload/order/OrderDTO.java new file mode 100644 index 0000000..4857624 --- /dev/null +++ b/src/main/java/dg/shop/server/payload/order/OrderDTO.java @@ -0,0 +1,16 @@ +package dg.shop.server.payload.order; + +import jakarta.validation.constraints.Email; + +import java.time.LocalDate; +import java.util.List; + +public record OrderDTO(long orderId, + @Email String email, + List orderedItems, + LocalDate orderDate, + PaymentDTO paymentDTO, + double totalAmount, + String orderStatus, + long addressId) { +} diff --git a/src/main/java/dg/shop/server/payload/order/OrderItemDTO.java b/src/main/java/dg/shop/server/payload/order/OrderItemDTO.java new file mode 100644 index 0000000..a393a15 --- /dev/null +++ b/src/main/java/dg/shop/server/payload/order/OrderItemDTO.java @@ -0,0 +1,10 @@ +package dg.shop.server.payload.order; + +import dg.shop.server.payload.product.ProductDTO; + +public record OrderItemDTO(long orderItemId, + ProductDTO productDTO, + int quantity, + double discount, + double orderedProductPrice) { +} diff --git a/src/main/java/dg/shop/server/payload/order/OrderRequestDTO.java b/src/main/java/dg/shop/server/payload/order/OrderRequestDTO.java new file mode 100644 index 0000000..9713d2c --- /dev/null +++ b/src/main/java/dg/shop/server/payload/order/OrderRequestDTO.java @@ -0,0 +1,9 @@ +package dg.shop.server.payload.order; + +public record OrderRequestDTO(long addressId, + String paymentMethod, + String paymentGatewayName, + String paymentGatewayPaymentId, + String paymentGatewayStatus, + String paymentGatewayResponseMessage) { +} diff --git a/src/main/java/dg/shop/server/payload/order/PaymentDTO.java b/src/main/java/dg/shop/server/payload/order/PaymentDTO.java new file mode 100644 index 0000000..2c96444 --- /dev/null +++ b/src/main/java/dg/shop/server/payload/order/PaymentDTO.java @@ -0,0 +1,11 @@ +package dg.shop.server.payload.order; + +import dg.shop.server.validation.RequiredSize; + +public record PaymentDTO(long paymentId, + @RequiredSize(min = 4, sizeMessage = "Payment method must contain at least 4 characters") String paymentMethod, + String paymentGatewayPaymentId, + String paymentGatewayStatus, + String paymentGatewayResponseMessage, + String paymentGatewayName) { +} diff --git a/src/main/java/dg/shop/server/repository/OrderItemRepository.java b/src/main/java/dg/shop/server/repository/OrderItemRepository.java new file mode 100644 index 0000000..dfcb1ad --- /dev/null +++ b/src/main/java/dg/shop/server/repository/OrderItemRepository.java @@ -0,0 +1,9 @@ +package dg.shop.server.repository; + +import dg.shop.server.model.OrderItem; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface OrderItemRepository extends JpaRepository { +} diff --git a/src/main/java/dg/shop/server/repository/OrderRepository.java b/src/main/java/dg/shop/server/repository/OrderRepository.java new file mode 100644 index 0000000..375e202 --- /dev/null +++ b/src/main/java/dg/shop/server/repository/OrderRepository.java @@ -0,0 +1,9 @@ +package dg.shop.server.repository; + +import dg.shop.server.model.Order; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface OrderRepository extends JpaRepository { +} diff --git a/src/main/java/dg/shop/server/repository/PaymentRepository.java b/src/main/java/dg/shop/server/repository/PaymentRepository.java new file mode 100644 index 0000000..ec54bc0 --- /dev/null +++ b/src/main/java/dg/shop/server/repository/PaymentRepository.java @@ -0,0 +1,9 @@ +package dg.shop.server.repository; + +import dg.shop.server.model.Payment; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface PaymentRepository extends JpaRepository { +} diff --git a/src/main/java/dg/shop/server/service/address/AddressService.java b/src/main/java/dg/shop/server/service/address/AddressService.java index 5ed2cc8..6efd66c 100644 --- a/src/main/java/dg/shop/server/service/address/AddressService.java +++ b/src/main/java/dg/shop/server/service/address/AddressService.java @@ -21,4 +21,8 @@ public interface AddressService { AddressDTO updateAddress(long addressId, AddressDTO addressDTO); String deleteAddress(long addressId); + + AddressDTO toDTO(Address address); + + Address toAddress(AddressDTO addressDTO); } diff --git a/src/main/java/dg/shop/server/service/address/AddressServiceImpl.java b/src/main/java/dg/shop/server/service/address/AddressServiceImpl.java index bdc4fc4..3ad5119 100644 --- a/src/main/java/dg/shop/server/service/address/AddressServiceImpl.java +++ b/src/main/java/dg/shop/server/service/address/AddressServiceImpl.java @@ -73,6 +73,16 @@ public String deleteAddress(long addressId) { return "Address with ID: %d was deleted".formatted(toDelete.getAddressId()); } + @Override + public AddressDTO toDTO(Address address) { + return new AddressDTO(address.getAddressId(), address.getStreet(), address.getHouseNumber(), address.getFlatNumber(), address.getPostalCode(), address.getCity()); + } + + @Override + public Address toAddress(AddressDTO addressDTO) { + return new Address(addressDTO.street(), addressDTO.houseNumber(), addressDTO.flatNumber(), addressDTO.postalCode(), addressDTO.city()); + } + private void updateExistingAddress(Address existingAddress, AddressDTO addressDTO) { if (addressDTO.street() != null && !existingAddress.getStreet().equals(addressDTO.street())) { existingAddress.setStreet(addressDTO.street()); @@ -90,12 +100,4 @@ private void updateExistingAddress(Address existingAddress, AddressDTO addressDT existingAddress.setCity(addressDTO.city()); } } - - private AddressDTO toDTO(Address address) { - return new AddressDTO(address.getAddressId(), address.getStreet(), address.getHouseNumber(), address.getFlatNumber(), address.getPostalCode(), address.getCity()); - } - - private Address toAddress(AddressDTO addressDTO) { - return new Address(addressDTO.street(), addressDTO.houseNumber(), addressDTO.flatNumber(), addressDTO.postalCode(), addressDTO.city()); - } } diff --git a/src/main/java/dg/shop/server/service/cart/CartService.java b/src/main/java/dg/shop/server/service/cart/CartService.java index d35289c..e8f8d36 100644 --- a/src/main/java/dg/shop/server/service/cart/CartService.java +++ b/src/main/java/dg/shop/server/service/cart/CartService.java @@ -31,4 +31,6 @@ public interface CartService { Cart getCart(long cartId); CartRepository getRepository(); + + CartDTO toDTO(Cart cart); } diff --git a/src/main/java/dg/shop/server/service/cart/CartServiceImpl.java b/src/main/java/dg/shop/server/service/cart/CartServiceImpl.java index c4ae914..9afca32 100644 --- a/src/main/java/dg/shop/server/service/cart/CartServiceImpl.java +++ b/src/main/java/dg/shop/server/service/cart/CartServiceImpl.java @@ -167,6 +167,15 @@ public CartRepository getRepository() { return this.cartRepository; } + @Override + public CartDTO toDTO(Cart cart) { + List productDTOs = cart.getCartItems() + .stream() + .map(item -> productService.toDTO(item.getProduct(), item.getQuantity())) + .toList(); + return new CartDTO(cart.getCartId(), cart.getTotalPrice(), productDTOs); + } + private void setCartItemQuantity(String operation, CartItem cartItem, Product product) { switch (operation) { case "increase" -> { @@ -231,17 +240,4 @@ private CartItem createCartItem(Product product, int quantity, Cart cart) { newCartItem.setProductPrice(product.getSpecialPrice()); return cartItemService.persist(newCartItem); } - - private CartDTO toDTO(Cart cart) { -// cart.getCartItems().forEach( item -> item.getProduct().setQuantity(item.getQuantity())); - List productDTOs = cart.getCartItems() - .stream() - .map(item -> toDTO(item.getProduct(), item.getQuantity())) - .toList(); - return new CartDTO(cart.getCartId(), cart.getTotalPrice(), productDTOs); - } - - private ProductDTO toDTO(Product product, int quantity) { - return new ProductDTO(product.getId(), product.getName(), product.getImage(), product.getDescription(), quantity, product.getPrice(), product.getDiscount(), product.getSpecialPrice()); - } } diff --git a/src/main/java/dg/shop/server/service/category/CategoryService.java b/src/main/java/dg/shop/server/service/category/CategoryService.java index b54e05e..9da8e54 100644 --- a/src/main/java/dg/shop/server/service/category/CategoryService.java +++ b/src/main/java/dg/shop/server/service/category/CategoryService.java @@ -18,4 +18,8 @@ public interface CategoryService { CategoryRepository getRepository(); Category getCategory(long categoryId); + + CategoryDTO toDTO(Category category); + + Category toCategory(CategoryDTO categoryDTO); } diff --git a/src/main/java/dg/shop/server/service/category/CategoryServiceImpl.java b/src/main/java/dg/shop/server/service/category/CategoryServiceImpl.java index 39a0373..78bcb74 100644 --- a/src/main/java/dg/shop/server/service/category/CategoryServiceImpl.java +++ b/src/main/java/dg/shop/server/service/category/CategoryServiceImpl.java @@ -88,11 +88,13 @@ public Category getCategory(long categoryId) { .orElseThrow(() -> new ResourceNotFoundException("Category", "id", categoryId)); } - private CategoryDTO toDTO(Category category) { + @Override + public CategoryDTO toDTO(Category category) { return new CategoryDTO(category.getId(), category.getName(), category.getProducts()); } - private Category toCategory(CategoryDTO categoryDTO) { + @Override + public Category toCategory(CategoryDTO categoryDTO) { return new Category(categoryDTO.id(), categoryDTO.name(), categoryDTO.products()); } } diff --git a/src/main/java/dg/shop/server/service/order/OrderItemService.java b/src/main/java/dg/shop/server/service/order/OrderItemService.java new file mode 100644 index 0000000..878fb38 --- /dev/null +++ b/src/main/java/dg/shop/server/service/order/OrderItemService.java @@ -0,0 +1,18 @@ +package dg.shop.server.service.order; + +import dg.shop.server.model.CartItem; +import dg.shop.server.model.Order; +import dg.shop.server.model.OrderItem; +import dg.shop.server.payload.order.OrderItemDTO; + +import java.util.List; + +public interface OrderItemService { + List persist(List orderItems); + + OrderItem persist(OrderItem orderItem); + + OrderItemDTO toDTO(OrderItem orderItem); + + OrderItem toOrderItem(CartItem cartItem, Order savedOrder); +} diff --git a/src/main/java/dg/shop/server/service/order/OrderItemServiceImpl.java b/src/main/java/dg/shop/server/service/order/OrderItemServiceImpl.java new file mode 100644 index 0000000..8e2334c --- /dev/null +++ b/src/main/java/dg/shop/server/service/order/OrderItemServiceImpl.java @@ -0,0 +1,50 @@ +package dg.shop.server.service.order; + +import dg.shop.server.model.CartItem; +import dg.shop.server.model.Order; +import dg.shop.server.model.OrderItem; +import dg.shop.server.payload.order.OrderItemDTO; +import dg.shop.server.repository.OrderItemRepository; +import dg.shop.server.service.product.ProductService; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class OrderItemServiceImpl implements OrderItemService { + + private OrderItemRepository orderItemRepository; + + private ProductService productService; + + public OrderItemServiceImpl(OrderItemRepository orderItemRepository, ProductService productService) { + this.orderItemRepository = orderItemRepository; + this.productService = productService; + } + + @Override + public List persist(List orderItems) { + return orderItemRepository.saveAll(orderItems); + } + + @Override + public OrderItem persist(OrderItem orderItem) { + return orderItemRepository.save(orderItem); + } + + @Override + public OrderItemDTO toDTO(OrderItem orderItem) { + return new OrderItemDTO(orderItem.getOrderItemId(), productService.toDTO(orderItem.getProduct()), orderItem.getQuantity(), orderItem.getDiscount(), orderItem.getOrderedProductPrice()); + } + + @Override + public OrderItem toOrderItem(CartItem cartItem, Order savedOrder) { + OrderItem orderItem = new OrderItem(); + orderItem.setProduct(cartItem.getProduct()); + orderItem.setQuantity(cartItem.getQuantity()); + orderItem.setDiscount(cartItem.getDiscount()); + orderItem.setOrderedProductPrice(cartItem.getProductPrice()); + orderItem.setOrder(savedOrder); + return orderItem; + } +} diff --git a/src/main/java/dg/shop/server/service/order/OrderService.java b/src/main/java/dg/shop/server/service/order/OrderService.java new file mode 100644 index 0000000..77f9d72 --- /dev/null +++ b/src/main/java/dg/shop/server/service/order/OrderService.java @@ -0,0 +1,15 @@ +package dg.shop.server.service.order; + +import dg.shop.server.model.Order; +import dg.shop.server.model.OrderItem; +import dg.shop.server.payload.order.OrderDTO; +import jakarta.transaction.Transactional; + +import java.util.List; + +public interface OrderService { + @Transactional + OrderDTO placeOrder(String email, long addressId, String paymentMethod, String paymentGatewayName, String paymentGatewayPaymentId, String paymentGatewayStatus, String paymentGatewayResponseMessage); + + OrderDTO toDTO(Order order, List orderedItems); +} diff --git a/src/main/java/dg/shop/server/service/order/OrderServiceImpl.java b/src/main/java/dg/shop/server/service/order/OrderServiceImpl.java new file mode 100644 index 0000000..d62ae57 --- /dev/null +++ b/src/main/java/dg/shop/server/service/order/OrderServiceImpl.java @@ -0,0 +1,94 @@ +package dg.shop.server.service.order; + +import dg.shop.server.exception.ApiException; +import dg.shop.server.model.Address; +import dg.shop.server.model.Cart; +import dg.shop.server.model.CartItem; +import dg.shop.server.model.Order; +import dg.shop.server.model.OrderItem; +import dg.shop.server.model.Payment; +import dg.shop.server.model.Product; +import dg.shop.server.payload.order.OrderDTO; +import dg.shop.server.payload.order.OrderItemDTO; +import dg.shop.server.payload.order.PaymentDTO; +import dg.shop.server.payload.product.ProductDTO; +import dg.shop.server.repository.OrderRepository; +import dg.shop.server.service.address.AddressService; +import dg.shop.server.service.cart.CartService; +import dg.shop.server.service.product.ProductService; +import jakarta.transaction.Transactional; +import org.springframework.stereotype.Service; + +import java.time.LocalDate; +import java.util.List; + +@Service +public class OrderServiceImpl implements OrderService { + + private OrderRepository orderRepository; + + private CartService cartService; + + private AddressService addressService; + + private PaymentService paymentService; + + private OrderItemService orderItemService; + + private ProductService productService; + + public OrderServiceImpl(OrderRepository orderRepository, CartService cartService, AddressService addressService, PaymentService paymentService, OrderItemService orderItemService, ProductService productService) { + this.orderRepository = orderRepository; + this.cartService = cartService; + this.addressService = addressService; + this.paymentService = paymentService; + this.orderItemService = orderItemService; + this.productService = productService; + } + + @Override + @Transactional + public OrderDTO placeOrder(String email, long addressId, String paymentMethod, String paymentGatewayName, String paymentGatewayPaymentId, String paymentGatewayStatus, String paymentGatewayResponseMessage) { + Cart cart = cartService.getCartByUserEmail(email); + Address address = addressService.getAddress(addressId); + Order order = new Order(); + order.setEmail(email); + order.setOrderDate(LocalDate.now()); + order.setTotalAmount(cart.getTotalPrice()); + order.setOrderStatus("Order Accepted."); + order.setAddress(address); + + Payment payment = new Payment(paymentMethod, paymentGatewayPaymentId, paymentGatewayStatus, paymentGatewayResponseMessage, paymentGatewayName); + payment.setOrder(order); + payment = paymentService.persist(payment); + order.setPayment(payment); + + Order savedOrder = orderRepository.save(order); + List cartItems = cart.getCartItems(); + if (cartItems.isEmpty()) { + throw new ApiException("Cart is empty"); + } + + List orderItems = cartItems.stream() + .map(cartItem -> orderItemService.toOrderItem(cartItem, savedOrder)) + .toList(); + orderItems = orderItemService.persist(orderItems); + cart.getCartItems().forEach(item -> { + int quantity = item.getQuantity(); + Product product = item.getProduct(); + product.setQuantity(product.getQuantity() - quantity); + productService.persist(product); + + cartService.deleteProduct(cart.getCartId(), item.getProduct().getId()); + }); + return toDTO(savedOrder, orderItems); + } + + @Override + public OrderDTO toDTO(Order order, List orderedItems) { + List orderItemDTOList = orderedItems.stream() + .map(orderItem -> orderItemService.toDTO(orderItem)) + .toList(); + return new OrderDTO(order.getOrderId(), order.getEmail(), orderItemDTOList, order.getOrderDate(), paymentService.toDTO(order.getPayment()), order.getTotalAmount(), order.getOrderStatus(), order.getAddress().getAddressId()); + } +} diff --git a/src/main/java/dg/shop/server/service/order/PaymentService.java b/src/main/java/dg/shop/server/service/order/PaymentService.java new file mode 100644 index 0000000..ac2d691 --- /dev/null +++ b/src/main/java/dg/shop/server/service/order/PaymentService.java @@ -0,0 +1,10 @@ +package dg.shop.server.service.order; + +import dg.shop.server.model.Payment; +import dg.shop.server.payload.order.PaymentDTO; + +public interface PaymentService { + Payment persist(Payment payment); + + PaymentDTO toDTO(Payment payment); +} diff --git a/src/main/java/dg/shop/server/service/order/PaymentServiceImpl.java b/src/main/java/dg/shop/server/service/order/PaymentServiceImpl.java new file mode 100644 index 0000000..d7238c9 --- /dev/null +++ b/src/main/java/dg/shop/server/service/order/PaymentServiceImpl.java @@ -0,0 +1,27 @@ +package dg.shop.server.service.order; + +import dg.shop.server.model.Payment; +import dg.shop.server.payload.order.PaymentDTO; +import dg.shop.server.repository.PaymentRepository; +import org.springframework.stereotype.Service; + +@Service +public class PaymentServiceImpl implements PaymentService{ + + private PaymentRepository paymentRepository; + + public PaymentServiceImpl(PaymentRepository paymentRepository) { + this.paymentRepository = paymentRepository; + } + + @Override + public Payment persist(Payment payment) { + return paymentRepository.save(payment); + } + + @Override + public PaymentDTO toDTO(Payment payment) { + return new PaymentDTO(payment.getPaymentId(), payment.getPaymentMethod(), payment.getPaymentGatewayPaymentId(), + payment.getPaymentGatewayStatus(), payment.getPaymentGatewayResponseMessage(), payment.getPaymentGatewayName()); + } +} diff --git a/src/main/java/dg/shop/server/service/product/ProductService.java b/src/main/java/dg/shop/server/service/product/ProductService.java index 814a7c8..004cdf9 100644 --- a/src/main/java/dg/shop/server/service/product/ProductService.java +++ b/src/main/java/dg/shop/server/service/product/ProductService.java @@ -31,4 +31,8 @@ public interface ProductService { Product getProduct(long productId); Product persist(Product product); + + ProductDTO toDTO(Product product); + + ProductDTO toDTO(Product product, int quantity); } diff --git a/src/main/java/dg/shop/server/service/product/ProductServiceImpl.java b/src/main/java/dg/shop/server/service/product/ProductServiceImpl.java index 40c10e9..e53f0f3 100644 --- a/src/main/java/dg/shop/server/service/product/ProductServiceImpl.java +++ b/src/main/java/dg/shop/server/service/product/ProductServiceImpl.java @@ -142,7 +142,7 @@ public ProductDTO updateProduct(long productId, ProductDTO productDTO) { List cartList = cartService.getAllCartsByProductId(productId); List cartDTOList = cartList.stream() - .map(this::toDTO) + .map(cart -> cartService.toDTO(cart)) .toList(); cartDTOList.forEach(cartDTO -> cartService.updateProductInCarts(cartDTO.cartId(), productId)); @@ -230,20 +230,14 @@ private ProductResponse toProductResponse(List productDTOS, Page productDTOs = cart.getCartItems() - .stream() - .map(item -> toDTO(item.getProduct(), item.getQuantity())) - .toList(); - return new CartDTO(cart.getCartId(), cart.getTotalPrice(), productDTOs); - } - - private ProductDTO toDTO(Product product, int quantity) { + @Override + public ProductDTO toDTO(Product product, int quantity) { return new ProductDTO(product.getId(), product.getName(), product.getImage(), product.getDescription(), quantity, product.getPrice(), product.getDiscount(), product.getSpecialPrice()); } } From 9418d517fbb05a430514ae352aecbc61e0fc664b Mon Sep 17 00:00:00 2001 From: dawidg90 Date: Sat, 14 Jun 2025 22:36:50 +0200 Subject: [PATCH 02/11] Unit tests fixed. --- .../service/cart/CartServiceImplTest.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/test/java/dg/shop/server/service/cart/CartServiceImplTest.java b/src/test/java/dg/shop/server/service/cart/CartServiceImplTest.java index 33841e2..f6f74d0 100644 --- a/src/test/java/dg/shop/server/service/cart/CartServiceImplTest.java +++ b/src/test/java/dg/shop/server/service/cart/CartServiceImplTest.java @@ -8,6 +8,7 @@ import dg.shop.server.model.Product; import dg.shop.server.model.User; import dg.shop.server.payload.cart.CartDTO; +import dg.shop.server.payload.product.ProductDTO; import dg.shop.server.repository.CartRepository; import dg.shop.server.service.cart.item.CartItemService; import dg.shop.server.service.product.ProductService; @@ -25,6 +26,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; @@ -32,6 +34,7 @@ import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -47,7 +50,7 @@ public class CartServiceImplTest { void setUp() { cartRepository = mock(CartRepository.class); cartItemService = mock(CartItemService.class); - productService = mock(ProductService.class); + productService = spy(ProductService.class); authUtils = mock(AuthUtils.class); cartService = new CartServiceImpl(cartRepository, cartItemService, authUtils); @@ -128,6 +131,8 @@ void addProductToCartSuccess() { doReturn(existingCart).when(cartRepository).findCartByEmail(anyString()); Product product = new Product(4L, "ABook", "abook.png", "Great book", 10, 58.5d, 0.0d, 58.5d, mock(Category.class), mock(User.class), List.of()); doReturn(product).when(productService).getProduct(anyLong()); + ProductDTO productDTO = new ProductDTO(4L, "ABook", "abook.png", "Great book", 3, 58.5d, 0.0d, 58.5d); + doReturn(productDTO).when(productService).toDTO(any(Product.class), anyInt()); CartItem addedProduct = new CartItem(3L, existingCart, product, 3, 0.0d, 58.5d); doReturn(addedProduct).when(cartItemService).persist(any(CartItem.class)); Cart updatedCart = new Cart(5L, user, List.of(addedProduct), 175.5d); @@ -150,6 +155,8 @@ void addProductToCartSuccessWhenCartDoesNotExistsBefore() { doReturn(newCart).when(cartRepository).save(any(Cart.class)); Product product = new Product(4L, "ABook", "abook.png", "Great book", 10, 58.5d, 0.0d, 58.5d, mock(Category.class), mock(User.class), List.of()); doReturn(product).when(productService).getProduct(anyLong()); + ProductDTO productDTO = new ProductDTO(4L, "ABook", "abook.png", "Great book", 3, 58.5d, 0.0d, 58.5d); + doReturn(productDTO).when(productService).toDTO(any(Product.class), anyInt()); CartItem addedProduct = new CartItem(3L, newCart, product, 3, 0.0d, 58.5d); doReturn(addedProduct).when(cartItemService).persist(any(CartItem.class)); Cart updatedCart = new Cart(5L, user, List.of(addedProduct), 175.5d); @@ -313,10 +320,12 @@ void updateProductQuantityByAddingAmount() { doReturn("user@test.com").when(authUtils).signedInEmail(); Cart cart = new Cart(); Product product = new Product(21L, "ABook", "image.png", "ABook", 15, 24.0d, 0.0d, 24.0d, mock(Category.class), mock(User.class), List.of()); + ProductDTO productDTO = new ProductDTO(21L, "ABook", "image.png", "ABook", 2, 24.0d, 0.0d, 24.0d); CartItem itemInCart = new CartItem(34L, cart, product, 1, 0.0d, 24.0d); Cart existingCart = new Cart(55L, mock(User.class), List.of(itemInCart), 24.0d); doReturn(existingCart).when(cartRepository).findCartByEmail("user@test.com"); doReturn(product).when(productService).getProduct(21L); + doReturn(productDTO).when(productService).toDTO(any(Product.class), anyInt()); doReturn(itemInCart).when(cartItemService).findByProductIdAndCartId(existingCart.getCartId(), product.getId()); CartItem updatedItemInCart = new CartItem(34L, cart, product, 2, 0.0d, 48.0d); doReturn(updatedItemInCart).when(cartItemService).persist(itemInCart); @@ -335,10 +344,12 @@ void updateProductQuantityBySubtractingAmount() { doReturn("user@test.com").when(authUtils).signedInEmail(); Cart cart = new Cart(); Product product = new Product(21L, "ABook", "image.png", "ABook", 15, 24.0d, 0.0d, 24.0d, mock(Category.class), mock(User.class), List.of()); + ProductDTO productDTO = new ProductDTO(21L, "ABook", "image.png", "ABook", 1, 24.0d, 0.0d, 24.0d); CartItem itemInCart = new CartItem(34L, cart, product, 2, 0.0d, 24.0d); Cart existingCart = new Cart(55L, mock(User.class), List.of(itemInCart), 48.0d); doReturn(existingCart).when(cartRepository).findCartByEmail("user@test.com"); doReturn(product).when(productService).getProduct(21L); + doReturn(productDTO).when(productService).toDTO(any(Product.class), anyInt()); doReturn(itemInCart).when(cartItemService).findByProductIdAndCartId(existingCart.getCartId(), product.getId()); CartItem updatedItemInCart = new CartItem(34L, cart, product, 1, 0.0d, 24.0d); doReturn(updatedItemInCart).when(cartItemService).persist(itemInCart); @@ -358,11 +369,15 @@ void updateProductQuantityByAddingAmountForOneProductWhenTwoProductsAreInCart() Cart cart = new Cart(); Product product = new Product(21L, "ABook", "image.png", "ABook", 15, 24.0d, 0.0d, 24.0d, mock(Category.class), mock(User.class), List.of()); Product product2 = new Product(22L, "CBook", "imagec.png", "CBook", 12, 12.0d, 0.0d, 12.0d, mock(Category.class), mock(User.class), List.of()); + ProductDTO product1DTO = new ProductDTO(21L, "ABook", "image.png", "ABook", 2, 24.0d, 0.0d, 24.0d); + ProductDTO product2DTO = new ProductDTO(22L, "CBook", "imagec.png", "CBook", 1, 12.0d, 0.0d, 12.0d); CartItem itemInCart = new CartItem(34L, cart, product, 1, 0.0d, 24.0d); CartItem itemInCart2 = new CartItem(35L, cart, product2, 1, 0.0d, 12.0d); Cart existingCart = new Cart(55L, mock(User.class), List.of(itemInCart, itemInCart2), 36.0d); doReturn(existingCart).when(cartRepository).findCartByEmail("user@test.com"); doReturn(product).when(productService).getProduct(21L); + doReturn(product1DTO).when(productService).toDTO(eq(product), anyInt()); + doReturn(product2DTO).when(productService).toDTO(eq(product2), anyInt()); doReturn(itemInCart).when(cartItemService).findByProductIdAndCartId(existingCart.getCartId(), product.getId()); CartItem updatedItemInCart = new CartItem(34L, cart, product, 2, 0.0d, 48.0d); doReturn(updatedItemInCart).when(cartItemService).persist(itemInCart); From de733286a3c4019fc2e213403915d8efb41fbda6 Mon Sep 17 00:00:00 2001 From: dawidg90 Date: Sun, 15 Jun 2025 11:54:36 +0200 Subject: [PATCH 03/11] Order logic updated. --- src/main/java/dg/shop/server/model/Order.java | 14 +++++++ .../java/dg/shop/server/model/OrderItem.java | 12 ++++++ .../java/dg/shop/server/model/Payment.java | 13 +++++++ .../shop/server/service/cart/CartService.java | 2 + .../server/service/cart/CartServiceImpl.java | 7 ++++ .../service/order/OrderServiceImpl.java | 10 +++-- .../resources/db/migration/V0.0.1__Init.sql | 39 +++++++++++++++++++ 7 files changed, 94 insertions(+), 3 deletions(-) diff --git a/src/main/java/dg/shop/server/model/Order.java b/src/main/java/dg/shop/server/model/Order.java index d255d18..1249dc4 100644 --- a/src/main/java/dg/shop/server/model/Order.java +++ b/src/main/java/dg/shop/server/model/Order.java @@ -50,4 +50,18 @@ public class Order { @ManyToOne @JoinColumn(name = "address_id") private Address address; + + @Override + public String toString() { + return "Order{" + + "address id=" + address.getAddressId() + + ", orderId=" + orderId + + ", email='" + email + '\'' + + ", orderItems=" + orderItems + + ", orderDate=" + orderDate + + ", payment id=" + payment.getPaymentId() + + ", totalAmount=" + totalAmount + + ", orderStatus='" + orderStatus + '\'' + + '}'; + } } diff --git a/src/main/java/dg/shop/server/model/OrderItem.java b/src/main/java/dg/shop/server/model/OrderItem.java index 159fe56..7d5e658 100644 --- a/src/main/java/dg/shop/server/model/OrderItem.java +++ b/src/main/java/dg/shop/server/model/OrderItem.java @@ -34,4 +34,16 @@ public class OrderItem { private double discount; private double orderedProductPrice; + + @Override + public String toString() { + return "OrderItem{" + + "discount=" + discount + + ", orderItemId=" + orderItemId + + ", product id=" + product.getId() + + ", order id=" + order.getOrderId() + + ", quantity=" + quantity + + ", orderedProductPrice=" + orderedProductPrice + + '}'; + } } diff --git a/src/main/java/dg/shop/server/model/Payment.java b/src/main/java/dg/shop/server/model/Payment.java index 6e0bf13..713cff1 100644 --- a/src/main/java/dg/shop/server/model/Payment.java +++ b/src/main/java/dg/shop/server/model/Payment.java @@ -43,4 +43,17 @@ public Payment(String paymentMethod, String paymentGatewayPaymentId, String paym this.paymentGatewayResponseMessage = paymentGatewayResponseMessage; this.paymentGatewayName = paymentGatewayName; } + + @Override + public String toString() { + return "Payment{" + + "order id=" + order.getOrderId() + + ", paymentId=" + paymentId + + ", paymentMethod='" + paymentMethod + '\'' + + ", paymentGatewayPaymentId='" + paymentGatewayPaymentId + '\'' + + ", paymentGatewayStatus='" + paymentGatewayStatus + '\'' + + ", paymentGatewayResponseMessage='" + paymentGatewayResponseMessage + '\'' + + ", paymentGatewayName='" + paymentGatewayName + '\'' + + '}'; + } } diff --git a/src/main/java/dg/shop/server/service/cart/CartService.java b/src/main/java/dg/shop/server/service/cart/CartService.java index e8f8d36..5748e19 100644 --- a/src/main/java/dg/shop/server/service/cart/CartService.java +++ b/src/main/java/dg/shop/server/service/cart/CartService.java @@ -33,4 +33,6 @@ public interface CartService { CartRepository getRepository(); CartDTO toDTO(Cart cart); + + String deleteCart(long cartId); } diff --git a/src/main/java/dg/shop/server/service/cart/CartServiceImpl.java b/src/main/java/dg/shop/server/service/cart/CartServiceImpl.java index 9afca32..f6c44ca 100644 --- a/src/main/java/dg/shop/server/service/cart/CartServiceImpl.java +++ b/src/main/java/dg/shop/server/service/cart/CartServiceImpl.java @@ -176,6 +176,13 @@ public CartDTO toDTO(Cart cart) { return new CartDTO(cart.getCartId(), cart.getTotalPrice(), productDTOs); } + @Override + public String deleteCart(long cartId) { + Cart toDelete = this.getCart(cartId); + cartRepository.delete(toDelete); + return "Cart with ID: %d was deleted.".formatted(cartId); + } + private void setCartItemQuantity(String operation, CartItem cartItem, Product product) { switch (operation) { case "increase" -> { diff --git a/src/main/java/dg/shop/server/service/order/OrderServiceImpl.java b/src/main/java/dg/shop/server/service/order/OrderServiceImpl.java index d62ae57..b3db3b0 100644 --- a/src/main/java/dg/shop/server/service/order/OrderServiceImpl.java +++ b/src/main/java/dg/shop/server/service/order/OrderServiceImpl.java @@ -15,6 +15,7 @@ import dg.shop.server.repository.OrderRepository; import dg.shop.server.service.address.AddressService; import dg.shop.server.service.cart.CartService; +import dg.shop.server.service.cart.item.CartItemService; import dg.shop.server.service.product.ProductService; import jakarta.transaction.Transactional; import org.springframework.stereotype.Service; @@ -37,13 +38,16 @@ public class OrderServiceImpl implements OrderService { private ProductService productService; - public OrderServiceImpl(OrderRepository orderRepository, CartService cartService, AddressService addressService, PaymentService paymentService, OrderItemService orderItemService, ProductService productService) { + private CartItemService cartItemService; + + public OrderServiceImpl(OrderRepository orderRepository, CartService cartService, AddressService addressService, PaymentService paymentService, OrderItemService orderItemService, ProductService productService, CartItemService cartItemService) { this.orderRepository = orderRepository; this.cartService = cartService; this.addressService = addressService; this.paymentService = paymentService; this.orderItemService = orderItemService; this.productService = productService; + this.cartItemService = cartItemService; } @Override @@ -78,9 +82,9 @@ public OrderDTO placeOrder(String email, long addressId, String paymentMethod, S Product product = item.getProduct(); product.setQuantity(product.getQuantity() - quantity); productService.persist(product); - - cartService.deleteProduct(cart.getCartId(), item.getProduct().getId()); + cartItemService.deleteById(item.getCartItemId()); }); + cartService.deleteCart(cart.getCartId()); return toDTO(savedOrder, orderItems); } diff --git a/src/main/resources/db/migration/V0.0.1__Init.sql b/src/main/resources/db/migration/V0.0.1__Init.sql index 9bb5130..a3a52b1 100644 --- a/src/main/resources/db/migration/V0.0.1__Init.sql +++ b/src/main/resources/db/migration/V0.0.1__Init.sql @@ -93,6 +93,45 @@ CREATE TABLE `cart_items` ( CONSTRAINT `FKpcttvuq4mxppo8sxggjtn5i2c` FOREIGN KEY (`cart_id`) REFERENCES `carts` (`cart_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +CREATE TABLE `payments` ( + `payment_id` bigint NOT NULL AUTO_INCREMENT, + `payment_gateway_name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `payment_gateway_payment_id` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `payment_gateway_response_message` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `payment_gateway_status` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `payment_method` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + PRIMARY KEY (`payment_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +CREATE TABLE `orders` ( + `order_date` date DEFAULT NULL, + `total_amount` double NOT NULL, + `address_id` bigint DEFAULT NULL, + `order_id` bigint NOT NULL AUTO_INCREMENT, + `payment_id` bigint DEFAULT NULL, + `email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `order_status` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + PRIMARY KEY (`order_id`), + UNIQUE KEY `UKhaujdjk1ohmeixjhnhslchrp1` (`payment_id`), + KEY `FKhlglkvf5i60dv6dn397ethgpt` (`address_id`), + CONSTRAINT `FK8aol9f99s97mtyhij0tvfj41f` FOREIGN KEY (`payment_id`) REFERENCES `payments` (`payment_id`), + CONSTRAINT `FKhlglkvf5i60dv6dn397ethgpt` FOREIGN KEY (`address_id`) REFERENCES `addresses` (`address_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +CREATE TABLE `order_items` ( + `discount` double NOT NULL, + `ordered_product_price` double NOT NULL, + `quantity` int NOT NULL, + `order_id` bigint DEFAULT NULL, + `order_item_id` bigint NOT NULL AUTO_INCREMENT, + `product_id` bigint DEFAULT NULL, + PRIMARY KEY (`order_item_id`), + KEY `FKbioxgbv59vetrxe0ejfubep1w` (`order_id`), + KEY `FKocimc7dtr037rh4ls4l95nlfi` (`product_id`), + CONSTRAINT `FKbioxgbv59vetrxe0ejfubep1w` FOREIGN KEY (`order_id`) REFERENCES `orders` (`order_id`), + CONSTRAINT `FKocimc7dtr037rh4ls4l95nlfi` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + INSERT INTO `roles` (`role_id`, `user_role`) VALUES (1, 'ROLE_ADMIN'); INSERT INTO `roles` (`role_id`, `user_role`) VALUES (2, 'ROLE_SELLER'); INSERT INTO `roles` (`role_id`, `user_role`) VALUES (3, 'ROLE_USER'); From 1c4f0f1dff65e9989b31c5f368828e7e8cc9eebe Mon Sep 17 00:00:00 2001 From: dawidg90 Date: Mon, 16 Jun 2025 01:31:06 +0200 Subject: [PATCH 04/11] Concurrent modification fail. --- src/main/java/dg/shop/server/model/Cart.java | 2 +- src/main/java/dg/shop/server/model/Order.java | 2 +- .../java/dg/shop/server/model/Payment.java | 2 +- .../java/dg/shop/server/model/Product.java | 2 +- src/main/java/dg/shop/server/model/User.java | 10 +- .../shop/server/service/cart/CartService.java | 2 + .../server/service/cart/CartServiceImpl.java | 2 + .../service/cart/item/CartItemService.java | 2 + .../cart/item/CartItemServiceImpl.java | 2 + .../service/order/OrderServiceImpl.java | 3 +- .../controller/OrderControllerTest.java | 158 ++++++++++++++++++ 11 files changed, 179 insertions(+), 8 deletions(-) create mode 100644 src/test/java/dg/shop/server/controller/OrderControllerTest.java diff --git a/src/main/java/dg/shop/server/model/Cart.java b/src/main/java/dg/shop/server/model/Cart.java index c68caad..35dc469 100644 --- a/src/main/java/dg/shop/server/model/Cart.java +++ b/src/main/java/dg/shop/server/model/Cart.java @@ -30,7 +30,7 @@ public class Cart { @JoinColumn(name = "user_id") private User user; - @OneToMany(mappedBy = "cart", cascade = { CascadeType.ALL }, orphanRemoval = true) + @OneToMany(mappedBy = "cart", cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE }, orphanRemoval = true) private List cartItems = new ArrayList<>(); private double totalPrice = 0.0d; diff --git a/src/main/java/dg/shop/server/model/Order.java b/src/main/java/dg/shop/server/model/Order.java index 1249dc4..667974d 100644 --- a/src/main/java/dg/shop/server/model/Order.java +++ b/src/main/java/dg/shop/server/model/Order.java @@ -34,7 +34,7 @@ public class Order { @Column(nullable = false) private String email; - @OneToMany(mappedBy = "order", cascade = CascadeType.ALL) + @OneToMany(mappedBy = "order", cascade = { CascadeType.PERSIST, CascadeType.MERGE }) private List orderItems = new ArrayList<>(); private LocalDate orderDate; diff --git a/src/main/java/dg/shop/server/model/Payment.java b/src/main/java/dg/shop/server/model/Payment.java index 713cff1..a5f2d2d 100644 --- a/src/main/java/dg/shop/server/model/Payment.java +++ b/src/main/java/dg/shop/server/model/Payment.java @@ -24,7 +24,7 @@ public class Payment { @GeneratedValue(strategy = GenerationType.IDENTITY) private long paymentId; - @OneToOne(mappedBy = "payment", cascade = CascadeType.ALL) + @OneToOne(mappedBy = "payment", cascade = { CascadeType.PERSIST, CascadeType.MERGE} ) private Order order; @NotBlank diff --git a/src/main/java/dg/shop/server/model/Product.java b/src/main/java/dg/shop/server/model/Product.java index d58f3e1..e60401e 100644 --- a/src/main/java/dg/shop/server/model/Product.java +++ b/src/main/java/dg/shop/server/model/Product.java @@ -44,7 +44,7 @@ public class Product { @JoinColumn(name = "seller_id") private User seller; - @OneToMany(mappedBy = "product", cascade = { CascadeType.ALL }, fetch = FetchType.EAGER) + @OneToMany(mappedBy = "product", cascade = { CascadeType.PERSIST, CascadeType.MERGE }, fetch = FetchType.EAGER) private List products = new ArrayList<>(); @Override diff --git a/src/main/java/dg/shop/server/model/User.java b/src/main/java/dg/shop/server/model/User.java index 3cf3899..1c2e928 100644 --- a/src/main/java/dg/shop/server/model/User.java +++ b/src/main/java/dg/shop/server/model/User.java @@ -60,7 +60,7 @@ public class User { @Column(name = "password") private String password; - @ManyToMany(cascade = { CascadeType.ALL }, fetch = FetchType.EAGER) + @ManyToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE }, fetch = FetchType.EAGER) @JoinTable( name = "user_role", joinColumns = @JoinColumn(name = "user_id"), @@ -72,13 +72,17 @@ public class User { @Getter @Setter - @OneToMany(mappedBy = "user", cascade = { CascadeType.ALL }) + @OneToMany(mappedBy = "user", cascade = { CascadeType.PERSIST, CascadeType.MERGE }) private List
addresses = new ArrayList<>(); @ToString.Exclude - @OneToOne(mappedBy = "user", cascade = { CascadeType.ALL }, orphanRemoval = true) + @OneToOne(mappedBy = "user", cascade = { CascadeType.PERSIST, CascadeType.MERGE }, orphanRemoval = true) private Cart cart; + @ToString.Exclude + @OneToMany(mappedBy = "seller", cascade = {CascadeType.PERSIST, CascadeType.MERGE}, orphanRemoval = true) + private Set products; + public User(String username, String password, String email) { this.email = email; this.password = password; diff --git a/src/main/java/dg/shop/server/service/cart/CartService.java b/src/main/java/dg/shop/server/service/cart/CartService.java index 5748e19..2a28538 100644 --- a/src/main/java/dg/shop/server/service/cart/CartService.java +++ b/src/main/java/dg/shop/server/service/cart/CartService.java @@ -11,6 +11,7 @@ public interface CartService { @Transactional CartDTO addProductToCart(long productId, int quantity); + @Transactional List getAllCarts(); Cart getCartByUserEmail(String email); @@ -34,5 +35,6 @@ public interface CartService { CartDTO toDTO(Cart cart); + @Transactional String deleteCart(long cartId); } diff --git a/src/main/java/dg/shop/server/service/cart/CartServiceImpl.java b/src/main/java/dg/shop/server/service/cart/CartServiceImpl.java index f6c44ca..5f52f8a 100644 --- a/src/main/java/dg/shop/server/service/cart/CartServiceImpl.java +++ b/src/main/java/dg/shop/server/service/cart/CartServiceImpl.java @@ -57,6 +57,7 @@ public CartDTO addProductToCart(long productId, int quantity) { } @Override + @Transactional public List getAllCarts() { List cartList = cartRepository.findAll(); if (cartList.isEmpty()) { @@ -177,6 +178,7 @@ public CartDTO toDTO(Cart cart) { } @Override + @Transactional public String deleteCart(long cartId) { Cart toDelete = this.getCart(cartId); cartRepository.delete(toDelete); diff --git a/src/main/java/dg/shop/server/service/cart/item/CartItemService.java b/src/main/java/dg/shop/server/service/cart/item/CartItemService.java index 13c79b5..54bcad9 100644 --- a/src/main/java/dg/shop/server/service/cart/item/CartItemService.java +++ b/src/main/java/dg/shop/server/service/cart/item/CartItemService.java @@ -2,6 +2,7 @@ import dg.shop.server.model.CartItem; import dg.shop.server.repository.CartItemRepository; +import jakarta.transaction.Transactional; public interface CartItemService { CartItemRepository getRepository(); @@ -12,5 +13,6 @@ public interface CartItemService { void deleteById(long cartItemId); + @Transactional void deleteByProductIdAndCartId(long cartId, long productId); } diff --git a/src/main/java/dg/shop/server/service/cart/item/CartItemServiceImpl.java b/src/main/java/dg/shop/server/service/cart/item/CartItemServiceImpl.java index aeaad3b..f2d5032 100644 --- a/src/main/java/dg/shop/server/service/cart/item/CartItemServiceImpl.java +++ b/src/main/java/dg/shop/server/service/cart/item/CartItemServiceImpl.java @@ -2,6 +2,7 @@ import dg.shop.server.model.CartItem; import dg.shop.server.repository.CartItemRepository; +import jakarta.transaction.Transactional; import org.springframework.stereotype.Service; @Service @@ -33,6 +34,7 @@ public void deleteById(long cartItemId) { } @Override + @Transactional public void deleteByProductIdAndCartId(long cartId, long productId) { getRepository().deleteCartItemByProductIdAndCartId(cartId, productId); } diff --git a/src/main/java/dg/shop/server/service/order/OrderServiceImpl.java b/src/main/java/dg/shop/server/service/order/OrderServiceImpl.java index b3db3b0..700bee3 100644 --- a/src/main/java/dg/shop/server/service/order/OrderServiceImpl.java +++ b/src/main/java/dg/shop/server/service/order/OrderServiceImpl.java @@ -21,6 +21,7 @@ import org.springframework.stereotype.Service; import java.time.LocalDate; +import java.util.ArrayList; import java.util.List; @Service @@ -82,7 +83,7 @@ public OrderDTO placeOrder(String email, long addressId, String paymentMethod, S Product product = item.getProduct(); product.setQuantity(product.getQuantity() - quantity); productService.persist(product); - cartItemService.deleteById(item.getCartItemId()); + cartService.deleteProduct(cart.getCartId(), product.getId()); }); cartService.deleteCart(cart.getCartId()); return toDTO(savedOrder, orderItems); diff --git a/src/test/java/dg/shop/server/controller/OrderControllerTest.java b/src/test/java/dg/shop/server/controller/OrderControllerTest.java new file mode 100644 index 0000000..ac5a266 --- /dev/null +++ b/src/test/java/dg/shop/server/controller/OrderControllerTest.java @@ -0,0 +1,158 @@ +package dg.shop.server.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import dg.shop.server.model.Cart; +import dg.shop.server.model.Product; +import dg.shop.server.model.Role; +import dg.shop.server.model.User; +import dg.shop.server.model.UserRole; +import dg.shop.server.payload.address.AddressDTO; +import dg.shop.server.payload.cart.CartDTO; +import dg.shop.server.payload.category.CategoryDTO; +import dg.shop.server.payload.order.OrderRequestDTO; +import dg.shop.server.payload.product.ProductDTO; +import dg.shop.server.repository.CategoryRepository; +import dg.shop.server.repository.ProductRepository; +import dg.shop.server.repository.RoleRepository; +import dg.shop.server.repository.UserRepository; +import dg.shop.server.service.address.AddressService; +import dg.shop.server.service.cart.CartService; +import dg.shop.server.service.cart.item.CartItemService; +import dg.shop.server.service.category.CategoryService; +import dg.shop.server.service.order.OrderService; +import dg.shop.server.service.product.ProductService; +import dg.shop.server.util.AuthUtils; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +import java.util.List; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +class OrderControllerTest { + + @Autowired + private WebApplicationContext context; + + @Autowired + private OrderService orderService; + + @Autowired + private AuthUtils authUtils; + + @Autowired + private CartService cartService; + + @Autowired + private UserRepository userRepository; + + @Autowired + private PasswordEncoder passwordEncoder; + + @Autowired + private RoleRepository roleRepository; + + @Autowired + private CategoryRepository categoryRepository; + + @Autowired + private ProductRepository productRepository; + + @Autowired + private ProductService productService; + + @Autowired + private CategoryService categoryService; + + @Autowired + private CartItemService cartItemService; + + @Autowired + private AddressService addressService; + + private MockMvc mockMvc; + private ObjectMapper objectMapper; + + @BeforeEach + void setUp() { + mockMvc = MockMvcBuilders + .webAppContextSetup(context) + .apply(springSecurity()) + .build(); + objectMapper = new ObjectMapper(); + } + + @AfterEach + void tearDown() { +// cartItemService.getRepository().deleteAll(); +// cartService.getRepository().deleteAll(); +// categoryService.getRepository().deleteAll(); +// productService.getRepository().deleteAll(); +// userRepository.deleteAll(); +// roleRepository.deleteAll(); + } + + @Test + @DisplayName("[POST] Place order - success") + void testPlaceOrderSuccess() throws Exception { + User user = createTestUser("admin"); + AddressDTO address = addressService.createAddress(new AddressDTO(0L, "Street", 33, 1, "123456", "City"), user); + user.setAddresses(List.of(addressService.getAddress(address.addressId()))); + signInUser(user); + CategoryDTO category = categoryService.createCategory(new CategoryDTO("Games")); + ProductDTO product = productService.createProduct(new ProductDTO(0L, "Product", "product.png", "A Product", 10, 25.0d, 0.0d, 25.0d), category.id()); + CartDTO cart = cartService.addProductToCart(product.id(), 1); +// assertEquals(1, cartService.getAllCarts().size()); + OrderRequestDTO orderRequestDTO = new OrderRequestDTO(address.addressId(), "card", "PayU", "uid_VEF43FD45GXR56R", "success", "Payment Completed"); + mockMvc.perform(post("/api/order/users/payments/{paymentMethod}", "card") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(orderRequestDTO))) + .andExpect(status().isCreated()); + signOutUser(); +// assertTrue(cartItemService.getRepository().findAll().isEmpty()); +// assertTrue(cartService.getAllCarts().isEmpty()); + } + + private User createTestUser(String username) { + roleRepository.save(new Role(UserRole.ROLE_ADMIN)); + + User user = new User(username, passwordEncoder.encode("it_user_10"), "%s@test.com".formatted(username)); + user.setRoles(Set.of(new Role(UserRole.ROLE_ADMIN))); + return userRepository.save(user); + } + + private Cart createCartForUser(User user) { + Cart newCart = new Cart(); + newCart.setTotalPrice(0.0d); + newCart.setUser(user); + return cartService.getRepository().save(newCart); + } + + private void signInUser(User user) { + SecurityContextHolder.getContext() + .setAuthentication( + new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword(), List.of(new SimpleGrantedAuthority("ADMIN")))); + } + + private void signOutUser() { + SecurityContextHolder.getContext().setAuthentication(null); + } +} \ No newline at end of file From 153c6d8c100da986cdabecc3e79699f97ae82e43 Mon Sep 17 00:00:00 2001 From: dawidg90 Date: Mon, 16 Jun 2025 19:26:30 +0200 Subject: [PATCH 05/11] Dirty changes. --- .../java/dg/shop/server/model/Address.java | 8 ---- src/main/java/dg/shop/server/model/Cart.java | 2 +- src/main/java/dg/shop/server/model/Order.java | 2 +- .../java/dg/shop/server/model/Product.java | 3 +- src/main/java/dg/shop/server/model/User.java | 6 +-- .../server/repository/CartRepository.java | 5 +++ .../service/address/AddressService.java | 3 ++ .../service/address/AddressServiceImpl.java | 5 +++ .../shop/server/service/cart/CartService.java | 2 +- .../server/service/cart/CartServiceImpl.java | 7 ++-- .../server/service/order/OrderService.java | 3 ++ .../service/order/OrderServiceImpl.java | 29 +++++++++------ .../controller/OrderControllerTest.java | 37 ++++++++++++------- 13 files changed, 68 insertions(+), 44 deletions(-) diff --git a/src/main/java/dg/shop/server/model/Address.java b/src/main/java/dg/shop/server/model/Address.java index 31cd118..628402e 100644 --- a/src/main/java/dg/shop/server/model/Address.java +++ b/src/main/java/dg/shop/server/model/Address.java @@ -6,20 +6,12 @@ import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToMany; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; -import jakarta.persistence.Transient; -import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Positive; import jakarta.validation.constraints.PositiveOrZero; -import jakarta.validation.constraints.Size; import lombok.Data; import lombok.NoArgsConstructor; -import lombok.ToString; - -import java.util.ArrayList; -import java.util.List; @Data @Entity diff --git a/src/main/java/dg/shop/server/model/Cart.java b/src/main/java/dg/shop/server/model/Cart.java index 35dc469..c68caad 100644 --- a/src/main/java/dg/shop/server/model/Cart.java +++ b/src/main/java/dg/shop/server/model/Cart.java @@ -30,7 +30,7 @@ public class Cart { @JoinColumn(name = "user_id") private User user; - @OneToMany(mappedBy = "cart", cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE }, orphanRemoval = true) + @OneToMany(mappedBy = "cart", cascade = { CascadeType.ALL }, orphanRemoval = true) private List cartItems = new ArrayList<>(); private double totalPrice = 0.0d; diff --git a/src/main/java/dg/shop/server/model/Order.java b/src/main/java/dg/shop/server/model/Order.java index 667974d..53ca798 100644 --- a/src/main/java/dg/shop/server/model/Order.java +++ b/src/main/java/dg/shop/server/model/Order.java @@ -34,7 +34,7 @@ public class Order { @Column(nullable = false) private String email; - @OneToMany(mappedBy = "order", cascade = { CascadeType.PERSIST, CascadeType.MERGE }) + @OneToMany(mappedBy = "order", cascade = { CascadeType.ALL }) private List orderItems = new ArrayList<>(); private LocalDate orderDate; diff --git a/src/main/java/dg/shop/server/model/Product.java b/src/main/java/dg/shop/server/model/Product.java index e60401e..4aada1e 100644 --- a/src/main/java/dg/shop/server/model/Product.java +++ b/src/main/java/dg/shop/server/model/Product.java @@ -14,6 +14,7 @@ import lombok.Data; import lombok.NoArgsConstructor; import lombok.ToString; +import org.hibernate.annotations.Cascade; import java.util.ArrayList; import java.util.List; @@ -44,7 +45,7 @@ public class Product { @JoinColumn(name = "seller_id") private User seller; - @OneToMany(mappedBy = "product", cascade = { CascadeType.PERSIST, CascadeType.MERGE }, fetch = FetchType.EAGER) + @OneToMany(mappedBy = "product", cascade = { CascadeType.ALL }, fetch = FetchType.EAGER) private List products = new ArrayList<>(); @Override diff --git a/src/main/java/dg/shop/server/model/User.java b/src/main/java/dg/shop/server/model/User.java index 1c2e928..a8d6f4b 100644 --- a/src/main/java/dg/shop/server/model/User.java +++ b/src/main/java/dg/shop/server/model/User.java @@ -60,7 +60,7 @@ public class User { @Column(name = "password") private String password; - @ManyToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE }, fetch = FetchType.EAGER) + @ManyToMany(cascade = { CascadeType.ALL }, fetch = FetchType.EAGER) @JoinTable( name = "user_role", joinColumns = @JoinColumn(name = "user_id"), @@ -72,11 +72,11 @@ public class User { @Getter @Setter - @OneToMany(mappedBy = "user", cascade = { CascadeType.PERSIST, CascadeType.MERGE }) + @OneToMany(mappedBy = "user", cascade = { CascadeType.ALL }) private List
addresses = new ArrayList<>(); @ToString.Exclude - @OneToOne(mappedBy = "user", cascade = { CascadeType.PERSIST, CascadeType.MERGE }, orphanRemoval = true) + @OneToOne(mappedBy = "user", cascade = { CascadeType.ALL }, orphanRemoval = true) private Cart cart; @ToString.Exclude diff --git a/src/main/java/dg/shop/server/repository/CartRepository.java b/src/main/java/dg/shop/server/repository/CartRepository.java index 33fa6fb..e9fa7fa 100644 --- a/src/main/java/dg/shop/server/repository/CartRepository.java +++ b/src/main/java/dg/shop/server/repository/CartRepository.java @@ -2,6 +2,7 @@ import dg.shop.server.model.Cart; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; @@ -18,4 +19,8 @@ public interface CartRepository extends JpaRepository { @Query("SELECT cart FROM Cart cart JOIN FETCH cart.cartItems item JOIN FETCH item.product product WHERE product.id = ?1") List findCartsByProductId(long productId); + + @Modifying + @Query("DELETE FROM Cart cart WHERE cart.cartId = ?1") + void deleteCartById(long cartId); } diff --git a/src/main/java/dg/shop/server/service/address/AddressService.java b/src/main/java/dg/shop/server/service/address/AddressService.java index 6efd66c..993c314 100644 --- a/src/main/java/dg/shop/server/service/address/AddressService.java +++ b/src/main/java/dg/shop/server/service/address/AddressService.java @@ -3,6 +3,7 @@ import dg.shop.server.model.Address; import dg.shop.server.model.User; import dg.shop.server.payload.address.AddressDTO; +import dg.shop.server.repository.AddressRepository; import jakarta.validation.Valid; import java.util.List; @@ -25,4 +26,6 @@ public interface AddressService { AddressDTO toDTO(Address address); Address toAddress(AddressDTO addressDTO); + + AddressRepository getRepository(); } diff --git a/src/main/java/dg/shop/server/service/address/AddressServiceImpl.java b/src/main/java/dg/shop/server/service/address/AddressServiceImpl.java index 3ad5119..29d6c1e 100644 --- a/src/main/java/dg/shop/server/service/address/AddressServiceImpl.java +++ b/src/main/java/dg/shop/server/service/address/AddressServiceImpl.java @@ -83,6 +83,11 @@ public Address toAddress(AddressDTO addressDTO) { return new Address(addressDTO.street(), addressDTO.houseNumber(), addressDTO.flatNumber(), addressDTO.postalCode(), addressDTO.city()); } + @Override + public AddressRepository getRepository() { + return this.addressRepository; + } + private void updateExistingAddress(Address existingAddress, AddressDTO addressDTO) { if (addressDTO.street() != null && !existingAddress.getStreet().equals(addressDTO.street())) { existingAddress.setStreet(addressDTO.street()); diff --git a/src/main/java/dg/shop/server/service/cart/CartService.java b/src/main/java/dg/shop/server/service/cart/CartService.java index 2a28538..dc1ff80 100644 --- a/src/main/java/dg/shop/server/service/cart/CartService.java +++ b/src/main/java/dg/shop/server/service/cart/CartService.java @@ -36,5 +36,5 @@ public interface CartService { CartDTO toDTO(Cart cart); @Transactional - String deleteCart(long cartId); + String deleteCart(Cart toDelete); } diff --git a/src/main/java/dg/shop/server/service/cart/CartServiceImpl.java b/src/main/java/dg/shop/server/service/cart/CartServiceImpl.java index 5f52f8a..6fb08e0 100644 --- a/src/main/java/dg/shop/server/service/cart/CartServiceImpl.java +++ b/src/main/java/dg/shop/server/service/cart/CartServiceImpl.java @@ -179,10 +179,9 @@ public CartDTO toDTO(Cart cart) { @Override @Transactional - public String deleteCart(long cartId) { - Cart toDelete = this.getCart(cartId); - cartRepository.delete(toDelete); - return "Cart with ID: %d was deleted.".formatted(cartId); + public String deleteCart(Cart toDelete) { + cartRepository.deleteCartById(toDelete.getCartId()); + return "Cart with ID: %d was deleted.".formatted(toDelete.getCartId()); } private void setCartItemQuantity(String operation, CartItem cartItem, Product product) { diff --git a/src/main/java/dg/shop/server/service/order/OrderService.java b/src/main/java/dg/shop/server/service/order/OrderService.java index 77f9d72..cf7d959 100644 --- a/src/main/java/dg/shop/server/service/order/OrderService.java +++ b/src/main/java/dg/shop/server/service/order/OrderService.java @@ -3,6 +3,7 @@ import dg.shop.server.model.Order; import dg.shop.server.model.OrderItem; import dg.shop.server.payload.order.OrderDTO; +import dg.shop.server.repository.OrderRepository; import jakarta.transaction.Transactional; import java.util.List; @@ -12,4 +13,6 @@ public interface OrderService { OrderDTO placeOrder(String email, long addressId, String paymentMethod, String paymentGatewayName, String paymentGatewayPaymentId, String paymentGatewayStatus, String paymentGatewayResponseMessage); OrderDTO toDTO(Order order, List orderedItems); + + OrderRepository getRepository(); } diff --git a/src/main/java/dg/shop/server/service/order/OrderServiceImpl.java b/src/main/java/dg/shop/server/service/order/OrderServiceImpl.java index 700bee3..4d6a0dc 100644 --- a/src/main/java/dg/shop/server/service/order/OrderServiceImpl.java +++ b/src/main/java/dg/shop/server/service/order/OrderServiceImpl.java @@ -10,8 +10,6 @@ import dg.shop.server.model.Product; import dg.shop.server.payload.order.OrderDTO; import dg.shop.server.payload.order.OrderItemDTO; -import dg.shop.server.payload.order.PaymentDTO; -import dg.shop.server.payload.product.ProductDTO; import dg.shop.server.repository.OrderRepository; import dg.shop.server.service.address.AddressService; import dg.shop.server.service.cart.CartService; @@ -21,7 +19,6 @@ import org.springframework.stereotype.Service; import java.time.LocalDate; -import java.util.ArrayList; import java.util.List; @Service @@ -56,12 +53,7 @@ public OrderServiceImpl(OrderRepository orderRepository, CartService cartService public OrderDTO placeOrder(String email, long addressId, String paymentMethod, String paymentGatewayName, String paymentGatewayPaymentId, String paymentGatewayStatus, String paymentGatewayResponseMessage) { Cart cart = cartService.getCartByUserEmail(email); Address address = addressService.getAddress(addressId); - Order order = new Order(); - order.setEmail(email); - order.setOrderDate(LocalDate.now()); - order.setTotalAmount(cart.getTotalPrice()); - order.setOrderStatus("Order Accepted."); - order.setAddress(address); + Order order = populateOrder(email, cart, address); Payment payment = new Payment(paymentMethod, paymentGatewayPaymentId, paymentGatewayStatus, paymentGatewayResponseMessage, paymentGatewayName); payment.setOrder(order); @@ -83,12 +75,22 @@ public OrderDTO placeOrder(String email, long addressId, String paymentMethod, S Product product = item.getProduct(); product.setQuantity(product.getQuantity() - quantity); productService.persist(product); - cartService.deleteProduct(cart.getCartId(), product.getId()); + cartItemService.deleteByProductIdAndCartId(cart.getCartId(), product.getId()); }); - cartService.deleteCart(cart.getCartId()); + cartService.deleteCart(cart); return toDTO(savedOrder, orderItems); } + private Order populateOrder(String email, Cart cart, Address address) { + Order order = new Order(); + order.setEmail(email); + order.setOrderDate(LocalDate.now()); + order.setTotalAmount(cart.getTotalPrice()); + order.setOrderStatus("Order Accepted."); + order.setAddress(address); + return order; + } + @Override public OrderDTO toDTO(Order order, List orderedItems) { List orderItemDTOList = orderedItems.stream() @@ -96,4 +98,9 @@ public OrderDTO toDTO(Order order, List orderedItems) { .toList(); return new OrderDTO(order.getOrderId(), order.getEmail(), orderItemDTOList, order.getOrderDate(), paymentService.toDTO(order.getPayment()), order.getTotalAmount(), order.getOrderStatus(), order.getAddress().getAddressId()); } + + @Override + public OrderRepository getRepository() { + return this.orderRepository; + } } diff --git a/src/test/java/dg/shop/server/controller/OrderControllerTest.java b/src/test/java/dg/shop/server/controller/OrderControllerTest.java index ac5a266..b54d628 100644 --- a/src/test/java/dg/shop/server/controller/OrderControllerTest.java +++ b/src/test/java/dg/shop/server/controller/OrderControllerTest.java @@ -12,6 +12,8 @@ import dg.shop.server.payload.order.OrderRequestDTO; import dg.shop.server.payload.product.ProductDTO; import dg.shop.server.repository.CategoryRepository; +import dg.shop.server.repository.OrderItemRepository; +import dg.shop.server.repository.PaymentRepository; import dg.shop.server.repository.ProductRepository; import dg.shop.server.repository.RoleRepository; import dg.shop.server.repository.UserRepository; @@ -40,10 +42,13 @@ import java.util.List; import java.util.Set; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @SpringBootTest @@ -88,6 +93,12 @@ class OrderControllerTest { @Autowired private AddressService addressService; + @Autowired + private OrderItemRepository orderItemRepository; + + @Autowired + private PaymentRepository paymentRepository; + private MockMvc mockMvc; private ObjectMapper objectMapper; @@ -100,16 +111,6 @@ void setUp() { objectMapper = new ObjectMapper(); } - @AfterEach - void tearDown() { -// cartItemService.getRepository().deleteAll(); -// cartService.getRepository().deleteAll(); -// categoryService.getRepository().deleteAll(); -// productService.getRepository().deleteAll(); -// userRepository.deleteAll(); -// roleRepository.deleteAll(); - } - @Test @DisplayName("[POST] Place order - success") void testPlaceOrderSuccess() throws Exception { @@ -120,15 +121,23 @@ void testPlaceOrderSuccess() throws Exception { CategoryDTO category = categoryService.createCategory(new CategoryDTO("Games")); ProductDTO product = productService.createProduct(new ProductDTO(0L, "Product", "product.png", "A Product", 10, 25.0d, 0.0d, 25.0d), category.id()); CartDTO cart = cartService.addProductToCart(product.id(), 1); -// assertEquals(1, cartService.getAllCarts().size()); + assertEquals(1, cartService.getAllCarts().size()); + OrderRequestDTO orderRequestDTO = new OrderRequestDTO(address.addressId(), "card", "PayU", "uid_VEF43FD45GXR56R", "success", "Payment Completed"); mockMvc.perform(post("/api/order/users/payments/{paymentMethod}", "card") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(orderRequestDTO))) - .andExpect(status().isCreated()); + .andExpect(status().isCreated()) + .andExpect(jsonPath("$.email", is("admin@test.com"))) + .andExpect(jsonPath("$.orderedItems", hasSize(1))) + .andExpect(jsonPath("$.totalAmount", is(25.0d))) + .andExpect(jsonPath("$.orderStatus", is("Order Accepted."))) + .andExpect(jsonPath("$.addressId", is(1))) + .andExpect(jsonPath("$.paymentDTO.paymentMethod", is("card"))); + + assertTrue(cartItemService.getRepository().findAll().isEmpty()); + assertTrue(cartService.getRepository().findAll().isEmpty()); signOutUser(); -// assertTrue(cartItemService.getRepository().findAll().isEmpty()); -// assertTrue(cartService.getAllCarts().isEmpty()); } private User createTestUser(String username) { From bccbf736baa1125de1af0de7c00e04bbe5ec7e4b Mon Sep 17 00:00:00 2001 From: dawidg90 Date: Thu, 19 Jun 2025 13:53:08 +0200 Subject: [PATCH 06/11] Dirty changes. --- src/main/java/dg/shop/server/model/Cart.java | 6 +- .../java/dg/shop/server/model/Category.java | 12 +- src/main/java/dg/shop/server/model/Order.java | 6 +- .../java/dg/shop/server/model/Payment.java | 6 +- .../java/dg/shop/server/model/Product.java | 4 - src/main/java/dg/shop/server/model/User.java | 20 +- .../server/payload/category/CategoryDTO.java | 5 +- .../server/repository/AddressRepository.java | 5 + .../server/repository/CartItemRepository.java | 9 + .../server/repository/CartRepository.java | 3 - .../server/repository/ProductRepository.java | 6 + .../service/address/AddressServiceImpl.java | 7 +- .../server/service/cart/CartServiceImpl.java | 11 +- .../service/cart/item/CartItemService.java | 7 + .../cart/item/CartItemServiceImpl.java | 16 ++ .../service/category/CategoryServiceImpl.java | 4 +- .../service/order/OrderServiceImpl.java | 5 +- .../service/product/ProductServiceImpl.java | 2 +- .../resources/application-test.properties | 11 +- .../resources/db/migration/V0.0.1__Init.sql | 217 ++++++++---------- .../controller/AddressControllerTest.java | 17 +- .../server/controller/AuthControllerTest.java | 12 +- .../server/controller/CartControllerTest.java | 40 ++-- .../controller/CategoryControllerTest.java | 15 +- .../controller/OrderControllerTest.java | 16 +- .../controller/ProductControllerTest.java | 7 +- .../address/AddressServiceImplTest.java | 4 +- .../service/cart/CartServiceImplTest.java | 64 +++--- .../category/CategoryServiceImplTest.java | 2 +- .../product/ProductServiceImplTest.java | 5 +- 30 files changed, 286 insertions(+), 258 deletions(-) diff --git a/src/main/java/dg/shop/server/model/Cart.java b/src/main/java/dg/shop/server/model/Cart.java index c68caad..2c3666c 100644 --- a/src/main/java/dg/shop/server/model/Cart.java +++ b/src/main/java/dg/shop/server/model/Cart.java @@ -30,15 +30,15 @@ public class Cart { @JoinColumn(name = "user_id") private User user; - @OneToMany(mappedBy = "cart", cascade = { CascadeType.ALL }, orphanRemoval = true) - private List cartItems = new ArrayList<>(); +// @OneToMany(mappedBy = "cart", cascade = { CascadeType.ALL }, orphanRemoval = true) +// private List cartItems = new ArrayList<>(); private double totalPrice = 0.0d; @Override public String toString() { return "Cart{" + - "cartItems size=" + cartItems.size() + +// "cartItems size=" + cartItems.size() + ", cartId=" + cartId + ", user=" + user + ", totalPrice=" + totalPrice + diff --git a/src/main/java/dg/shop/server/model/Category.java b/src/main/java/dg/shop/server/model/Category.java index 9b514be..5a9861d 100644 --- a/src/main/java/dg/shop/server/model/Category.java +++ b/src/main/java/dg/shop/server/model/Category.java @@ -27,11 +27,11 @@ public class Category { @Column private String name; - @ToString.Exclude - @OneToMany(mappedBy = "category", cascade = { CascadeType.ALL }) - private List products; +// @ToString.Exclude +// @OneToMany(mappedBy = "category", cascade = { CascadeType.ALL }) +// private List products; - public Category(long id, String name) { - this(id, name, List.of()); - } +// public Category(long id, String name) { +// this(id, name, List.of()); +// } } diff --git a/src/main/java/dg/shop/server/model/Order.java b/src/main/java/dg/shop/server/model/Order.java index 53ca798..02e2556 100644 --- a/src/main/java/dg/shop/server/model/Order.java +++ b/src/main/java/dg/shop/server/model/Order.java @@ -34,8 +34,8 @@ public class Order { @Column(nullable = false) private String email; - @OneToMany(mappedBy = "order", cascade = { CascadeType.ALL }) - private List orderItems = new ArrayList<>(); +// @OneToMany(mappedBy = "order", cascade = { CascadeType.ALL }) +// private List orderItems = new ArrayList<>(); private LocalDate orderDate; @@ -57,7 +57,7 @@ public String toString() { "address id=" + address.getAddressId() + ", orderId=" + orderId + ", email='" + email + '\'' + - ", orderItems=" + orderItems + +// ", orderItems=" + orderItems + ", orderDate=" + orderDate + ", payment id=" + payment.getPaymentId() + ", totalAmount=" + totalAmount + diff --git a/src/main/java/dg/shop/server/model/Payment.java b/src/main/java/dg/shop/server/model/Payment.java index a5f2d2d..d6316f1 100644 --- a/src/main/java/dg/shop/server/model/Payment.java +++ b/src/main/java/dg/shop/server/model/Payment.java @@ -24,8 +24,8 @@ public class Payment { @GeneratedValue(strategy = GenerationType.IDENTITY) private long paymentId; - @OneToOne(mappedBy = "payment", cascade = { CascadeType.PERSIST, CascadeType.MERGE} ) - private Order order; +// @OneToOne(mappedBy = "payment", cascade = { CascadeType.PERSIST, CascadeType.MERGE} ) +// private Order order; @NotBlank @RequiredSize(min = 4, sizeMessage = "Payment method must contain at least 4 characters") @@ -47,7 +47,7 @@ public Payment(String paymentMethod, String paymentGatewayPaymentId, String paym @Override public String toString() { return "Payment{" + - "order id=" + order.getOrderId() + +// "order id=" + order.getOrderId() + ", paymentId=" + paymentId + ", paymentMethod='" + paymentMethod + '\'' + ", paymentGatewayPaymentId='" + paymentGatewayPaymentId + '\'' + diff --git a/src/main/java/dg/shop/server/model/Product.java b/src/main/java/dg/shop/server/model/Product.java index 4aada1e..99a8839 100644 --- a/src/main/java/dg/shop/server/model/Product.java +++ b/src/main/java/dg/shop/server/model/Product.java @@ -45,9 +45,6 @@ public class Product { @JoinColumn(name = "seller_id") private User seller; - @OneToMany(mappedBy = "product", cascade = { CascadeType.ALL }, fetch = FetchType.EAGER) - private List products = new ArrayList<>(); - @Override public String toString() { return "Product{" + @@ -61,7 +58,6 @@ public String toString() { ", discount=" + discount + ", specialPrice=" + specialPrice + ", seller=" + seller + - ", products=" + products.size() + '}'; } } diff --git a/src/main/java/dg/shop/server/model/User.java b/src/main/java/dg/shop/server/model/User.java index a8d6f4b..61a7fb2 100644 --- a/src/main/java/dg/shop/server/model/User.java +++ b/src/main/java/dg/shop/server/model/User.java @@ -70,18 +70,18 @@ public class User { @Setter private Set roles = new HashSet<>(); - @Getter - @Setter - @OneToMany(mappedBy = "user", cascade = { CascadeType.ALL }) - private List
addresses = new ArrayList<>(); +// @Getter +// @Setter +// @OneToMany(mappedBy = "user", cascade = { CascadeType.ALL }) +// private List
addresses = new ArrayList<>(); - @ToString.Exclude - @OneToOne(mappedBy = "user", cascade = { CascadeType.ALL }, orphanRemoval = true) - private Cart cart; +// @ToString.Exclude +// @OneToOne(mappedBy = "user", cascade = { CascadeType.ALL }, orphanRemoval = true) +// private Cart cart; - @ToString.Exclude - @OneToMany(mappedBy = "seller", cascade = {CascadeType.PERSIST, CascadeType.MERGE}, orphanRemoval = true) - private Set products; +// @ToString.Exclude +// @OneToMany(mappedBy = "seller", cascade = {CascadeType.PERSIST, CascadeType.MERGE}, orphanRemoval = true) +// private Set products; public User(String username, String password, String email) { this.email = email; diff --git a/src/main/java/dg/shop/server/payload/category/CategoryDTO.java b/src/main/java/dg/shop/server/payload/category/CategoryDTO.java index d55c0c6..9a5a80b 100644 --- a/src/main/java/dg/shop/server/payload/category/CategoryDTO.java +++ b/src/main/java/dg/shop/server/payload/category/CategoryDTO.java @@ -6,9 +6,8 @@ import java.util.List; public record CategoryDTO(long id, - @Size(min = 3, message = "Category name have to be at least 3 chars long.") String name, - List products) { + @Size(min = 3, message = "Category name have to be at least 3 chars long.") String name) { public CategoryDTO(String name) { - this(0L, name, List.of()); + this(0L, name); } } diff --git a/src/main/java/dg/shop/server/repository/AddressRepository.java b/src/main/java/dg/shop/server/repository/AddressRepository.java index 3039073..d3370e7 100644 --- a/src/main/java/dg/shop/server/repository/AddressRepository.java +++ b/src/main/java/dg/shop/server/repository/AddressRepository.java @@ -2,8 +2,13 @@ import dg.shop.server.model.Address; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; +import java.util.List; + @Repository public interface AddressRepository extends JpaRepository { + @Query("SELECT address FROM Address address WHERE address.user.userId = ?1") + List
findUserAddresses(long userId); } diff --git a/src/main/java/dg/shop/server/repository/CartItemRepository.java b/src/main/java/dg/shop/server/repository/CartItemRepository.java index 2c12359..f67a7fb 100644 --- a/src/main/java/dg/shop/server/repository/CartItemRepository.java +++ b/src/main/java/dg/shop/server/repository/CartItemRepository.java @@ -1,11 +1,14 @@ package dg.shop.server.repository; +import dg.shop.server.model.Cart; import dg.shop.server.model.CartItem; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; +import java.util.List; + @Repository public interface CartItemRepository extends JpaRepository { @@ -15,4 +18,10 @@ public interface CartItemRepository extends JpaRepository { @Modifying @Query("DELETE FROM CartItem item WHERE item.cart.id = ?1 AND item.product.id = ?2") void deleteCartItemByProductIdAndCartId(long cartId, long productId); + + @Query("SELECT item FROM CartItem item JOIN FETCH item.cart cart WHERE cart.cartId = ?1") + List findAllCartItemsFromCart(long cartId); + + @Query("SELECT item FROM CartItem item JOIN FETCH item.product product WHERE product.id = ?1") + List findCartItemsByProductId(long productId); } diff --git a/src/main/java/dg/shop/server/repository/CartRepository.java b/src/main/java/dg/shop/server/repository/CartRepository.java index e9fa7fa..25e3002 100644 --- a/src/main/java/dg/shop/server/repository/CartRepository.java +++ b/src/main/java/dg/shop/server/repository/CartRepository.java @@ -17,9 +17,6 @@ public interface CartRepository extends JpaRepository { @Query("SELECT cart FROM Cart cart WHERE cart.user.email = ?1 AND cart.id = ?2") Cart findCartByEmailAndCartId(String email, long cartId); - @Query("SELECT cart FROM Cart cart JOIN FETCH cart.cartItems item JOIN FETCH item.product product WHERE product.id = ?1") - List findCartsByProductId(long productId); - @Modifying @Query("DELETE FROM Cart cart WHERE cart.cartId = ?1") void deleteCartById(long cartId); diff --git a/src/main/java/dg/shop/server/repository/ProductRepository.java b/src/main/java/dg/shop/server/repository/ProductRepository.java index 5006882..f5d5e4f 100644 --- a/src/main/java/dg/shop/server/repository/ProductRepository.java +++ b/src/main/java/dg/shop/server/repository/ProductRepository.java @@ -5,11 +5,17 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; +import java.util.List; + @Repository public interface ProductRepository extends JpaRepository { Page findByCategory(Category category, Pageable pageDetails); Page findByNameLikeIgnoreCase(String keyword, Pageable pageDetails); + + @Query("SELECT product FROM Product product JOIN FETCH product.category category WHERE category.id = ?1") + List findAllByCategoryId(long categoryId); } diff --git a/src/main/java/dg/shop/server/service/address/AddressServiceImpl.java b/src/main/java/dg/shop/server/service/address/AddressServiceImpl.java index 29d6c1e..6c584c0 100644 --- a/src/main/java/dg/shop/server/service/address/AddressServiceImpl.java +++ b/src/main/java/dg/shop/server/service/address/AddressServiceImpl.java @@ -21,10 +21,6 @@ public AddressServiceImpl(AddressRepository addressRepository) { @Override public AddressDTO createAddress(AddressDTO addressDTO, User user) { Address address = toAddress(addressDTO); - List
addressList = user.getAddresses(); - addressList.add(address); - user.setAddresses(addressList); - address.setUser(user); Address saved = addressRepository.save(address); return toDTO(saved); @@ -52,7 +48,8 @@ public Address getAddress(long addressId) { @Override public List getUserAddresses(User signedInUser) { - return signedInUser.getAddresses() + List
userAddresses = addressRepository.findUserAddresses(signedInUser.getUserId()); + return userAddresses .stream() .map(this::toDTO) .toList(); diff --git a/src/main/java/dg/shop/server/service/cart/CartServiceImpl.java b/src/main/java/dg/shop/server/service/cart/CartServiceImpl.java index 6fb08e0..093764d 100644 --- a/src/main/java/dg/shop/server/service/cart/CartServiceImpl.java +++ b/src/main/java/dg/shop/server/service/cart/CartServiceImpl.java @@ -111,7 +111,7 @@ public CartDTO updateProductQuantity(long productId, String operation) { private void removeItemFromCartWhenQuantityIsZero(CartItem updatedItem, Cart userCart) { if (updatedItem.getQuantity() <= 0) { cartItemService.deleteById(updatedItem.getCartItemId()); - userCart.getCartItems().remove(updatedItem); +// userCart.getCartItems().remove(updatedItem); } } @@ -130,7 +130,7 @@ public String deleteProduct(long cartId, long productId) { private void updateCartOnRemove(Cart cart, CartItem cartItem, long cartId, long productId) { cartItemService.deleteByProductIdAndCartId(cartId, productId); - cart.getCartItems().remove(cartItem); +// cart.getCartItems().remove(cartItem); cart.setTotalPrice(cart.getTotalPrice() - (cartItem.getProductPrice() * cartItem.getQuantity())); cartRepository.save(cart); } @@ -153,7 +153,7 @@ public void updateProductInCarts(long cartId, long productId) { @Override public List getAllCartsByProductId(long productId) { - return cartRepository.findCartsByProductId(productId); + return cartItemService.findCartsByProductId(productId); } @Override @@ -170,7 +170,8 @@ public CartRepository getRepository() { @Override public CartDTO toDTO(Cart cart) { - List productDTOs = cart.getCartItems() + List cartItemList = cartItemService.getCartItemsFromCart(cart.getCartId()); + List productDTOs = cartItemList .stream() .map(item -> productService.toDTO(item.getProduct(), item.getQuantity())) .toList(); @@ -201,7 +202,7 @@ private void setCartItemQuantity(String operation, CartItem cartItem, Product pr } private Cart updateCart(CartItem cartItem, int quantity, Cart cart, Product product) { - cart.getCartItems().add(cartItem); +// cart.getCartItems().add(cartItem); cart.setTotalPrice(cart.getTotalPrice() + (product.getSpecialPrice() * quantity)); return cartRepository.save(cart); } diff --git a/src/main/java/dg/shop/server/service/cart/item/CartItemService.java b/src/main/java/dg/shop/server/service/cart/item/CartItemService.java index 54bcad9..c9fc559 100644 --- a/src/main/java/dg/shop/server/service/cart/item/CartItemService.java +++ b/src/main/java/dg/shop/server/service/cart/item/CartItemService.java @@ -1,9 +1,12 @@ package dg.shop.server.service.cart.item; +import dg.shop.server.model.Cart; import dg.shop.server.model.CartItem; import dg.shop.server.repository.CartItemRepository; import jakarta.transaction.Transactional; +import java.util.List; + public interface CartItemService { CartItemRepository getRepository(); @@ -15,4 +18,8 @@ public interface CartItemService { @Transactional void deleteByProductIdAndCartId(long cartId, long productId); + + List getCartItemsFromCart(long cartId); + + List findCartsByProductId(long productId); } diff --git a/src/main/java/dg/shop/server/service/cart/item/CartItemServiceImpl.java b/src/main/java/dg/shop/server/service/cart/item/CartItemServiceImpl.java index f2d5032..5b4a072 100644 --- a/src/main/java/dg/shop/server/service/cart/item/CartItemServiceImpl.java +++ b/src/main/java/dg/shop/server/service/cart/item/CartItemServiceImpl.java @@ -1,10 +1,13 @@ package dg.shop.server.service.cart.item; +import dg.shop.server.model.Cart; import dg.shop.server.model.CartItem; import dg.shop.server.repository.CartItemRepository; import jakarta.transaction.Transactional; import org.springframework.stereotype.Service; +import java.util.List; + @Service public class CartItemServiceImpl implements CartItemService{ private CartItemRepository cartItemRepository; @@ -38,4 +41,17 @@ public void deleteById(long cartItemId) { public void deleteByProductIdAndCartId(long cartId, long productId) { getRepository().deleteCartItemByProductIdAndCartId(cartId, productId); } + + @Override + public List getCartItemsFromCart(long cartId) { + return cartItemRepository.findAllCartItemsFromCart(cartId); + } + + @Override + public List findCartsByProductId(long productId) { + List cartItemsByProductId = cartItemRepository.findCartItemsByProductId(productId); + return cartItemsByProductId.stream() + .map(CartItem::getCart) + .toList(); + } } diff --git a/src/main/java/dg/shop/server/service/category/CategoryServiceImpl.java b/src/main/java/dg/shop/server/service/category/CategoryServiceImpl.java index 78bcb74..4be5d1b 100644 --- a/src/main/java/dg/shop/server/service/category/CategoryServiceImpl.java +++ b/src/main/java/dg/shop/server/service/category/CategoryServiceImpl.java @@ -90,11 +90,11 @@ public Category getCategory(long categoryId) { @Override public CategoryDTO toDTO(Category category) { - return new CategoryDTO(category.getId(), category.getName(), category.getProducts()); + return new CategoryDTO(category.getId(), category.getName()); } @Override public Category toCategory(CategoryDTO categoryDTO) { - return new Category(categoryDTO.id(), categoryDTO.name(), categoryDTO.products()); + return new Category(categoryDTO.id(), categoryDTO.name()); } } diff --git a/src/main/java/dg/shop/server/service/order/OrderServiceImpl.java b/src/main/java/dg/shop/server/service/order/OrderServiceImpl.java index 4d6a0dc..c4b7433 100644 --- a/src/main/java/dg/shop/server/service/order/OrderServiceImpl.java +++ b/src/main/java/dg/shop/server/service/order/OrderServiceImpl.java @@ -56,12 +56,11 @@ public OrderDTO placeOrder(String email, long addressId, String paymentMethod, S Order order = populateOrder(email, cart, address); Payment payment = new Payment(paymentMethod, paymentGatewayPaymentId, paymentGatewayStatus, paymentGatewayResponseMessage, paymentGatewayName); - payment.setOrder(order); payment = paymentService.persist(payment); order.setPayment(payment); Order savedOrder = orderRepository.save(order); - List cartItems = cart.getCartItems(); + List cartItems = cartItemService.getCartItemsFromCart(cart.getCartId()); if (cartItems.isEmpty()) { throw new ApiException("Cart is empty"); } @@ -70,7 +69,7 @@ public OrderDTO placeOrder(String email, long addressId, String paymentMethod, S .map(cartItem -> orderItemService.toOrderItem(cartItem, savedOrder)) .toList(); orderItems = orderItemService.persist(orderItems); - cart.getCartItems().forEach(item -> { + cartItems.forEach(item -> { int quantity = item.getQuantity(); Product product = item.getProduct(); product.setQuantity(product.getQuantity() - quantity); diff --git a/src/main/java/dg/shop/server/service/product/ProductServiceImpl.java b/src/main/java/dg/shop/server/service/product/ProductServiceImpl.java index e53f0f3..2f08cdd 100644 --- a/src/main/java/dg/shop/server/service/product/ProductServiceImpl.java +++ b/src/main/java/dg/shop/server/service/product/ProductServiceImpl.java @@ -51,7 +51,7 @@ public ProductServiceImpl(ProductRepository productRepository, CartService cartS @Override public ProductDTO createProduct(ProductDTO productDTO, long categoryId) { Category existingCategory = categoryService.getCategory(categoryId); - List productsInCategory = existingCategory.getProducts(); + List productsInCategory = productRepository.findAllByCategoryId(existingCategory.getId()); boolean isNotPresent = checkForProductExistence(productDTO, productsInCategory); if (isNotPresent) { Product product = toProduct(productDTO); diff --git a/src/main/resources/application-test.properties b/src/main/resources/application-test.properties index 8a615dd..9de5487 100644 --- a/src/main/resources/application-test.properties +++ b/src/main/resources/application-test.properties @@ -1,11 +1,16 @@ spring.application.name=server-test spring.h2.console.enabled=true -spring.datasource.url=jdbc:h2:mem:devshop +spring.datasource.url=jdbc:h2:mem:devshop;DB_CLOSE_DELAY=-1;MODE=MySQL; +spring.datasource.driverClassName=org.h2.Driver +spring.datasource.username=sa +spring.datasource.password= +spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect dir.product.images=images -spring.flyway.enabled=false -spring.jpa.hibernate.ddl-auto=create +spring.flyway.enabled=true +spring.jpa.hibernate.ddl-auto=none +spring.flyway.cleanDisabled=false dg.shop.server.jwt.secret=${JWT_SECRET} dg.shop.server.jwt.expiration.hours=${JWT_TTL_HOURS} diff --git a/src/main/resources/db/migration/V0.0.1__Init.sql b/src/main/resources/db/migration/V0.0.1__Init.sql index a3a52b1..1f27e29 100644 --- a/src/main/resources/db/migration/V0.0.1__Init.sql +++ b/src/main/resources/db/migration/V0.0.1__Init.sql @@ -1,141 +1,128 @@ -CREATE TABLE `categories` ( - `id` bigint NOT NULL AUTO_INCREMENT, - `name` varchar(255) DEFAULT NULL, - PRIMARY KEY (`id`) +CREATE TABLE categories ( + id bigint NOT NULL AUTO_INCREMENT, + name varchar(255) DEFAULT NULL, + PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -CREATE TABLE `roles` ( - `role_id` int NOT NULL AUTO_INCREMENT, - `user_role` enum('ROLE_ADMIN','ROLE_SELLER','ROLE_USER') DEFAULT NULL, - PRIMARY KEY (`role_id`) +CREATE TABLE roles ( + role_id int NOT NULL AUTO_INCREMENT, + user_role enum('ROLE_ADMIN','ROLE_SELLER','ROLE_USER') DEFAULT NULL, + PRIMARY KEY (role_id) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -CREATE TABLE `users` ( - `user_id` bigint NOT NULL AUTO_INCREMENT, - `email` varchar(50) NOT NULL, - `password` varchar(64) NOT NULL, - `username` varchar(30) NOT NULL, - PRIMARY KEY (`user_id`), - UNIQUE KEY `UKr43af9ap4edm43mmtq01oddj6` (`username`), - UNIQUE KEY `UK6dotkott2kjsp8vw4d0m25fb7` (`email`) +CREATE TABLE users ( + user_id bigint NOT NULL AUTO_INCREMENT, + email varchar(50) NOT NULL, + password varchar(64) NOT NULL, + username varchar(30) NOT NULL, + PRIMARY KEY (user_id), + UNIQUE KEY UK_username (username), + UNIQUE KEY UK_email (email) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -CREATE TABLE `addresses` ( - `flat_number` int NOT NULL, - `house_number` int NOT NULL, - `address_id` bigint NOT NULL AUTO_INCREMENT, - `user_id` bigint DEFAULT NULL, - `city` varchar(255) NOT NULL, - `postal_code` varchar(255) NOT NULL, - `street` varchar(255) NOT NULL, - PRIMARY KEY (`address_id`), - KEY `FK1fa36y2oqhao3wgg2rw1pi459` (`user_id`), - CONSTRAINT `FK1fa36y2oqhao3wgg2rw1pi459` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`) +CREATE TABLE addresses ( + flat_number int NOT NULL, + house_number int NOT NULL, + address_id bigint NOT NULL AUTO_INCREMENT, + user_id bigint DEFAULT NULL, + city varchar(255) NOT NULL, + postal_code varchar(255) NOT NULL, + street varchar(255) NOT NULL, + PRIMARY KEY (address_id), + CONSTRAINT FK_user_id_in_address FOREIGN KEY (user_id) REFERENCES users (user_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -CREATE TABLE `user_role` ( - `user_id` bigint NOT NULL, - `role_id` int NOT NULL, - PRIMARY KEY (`user_id`,`role_id`), - KEY `FKt7e7djp752sqn6w22i6ocqy6q` (`role_id`), - CONSTRAINT `FKj345gk1bovqvfame88rcx7yyx` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`), - CONSTRAINT `FKt7e7djp752sqn6w22i6ocqy6q` FOREIGN KEY (`role_id`) REFERENCES `roles` (`role_id`) +CREATE TABLE user_role ( + user_id bigint NOT NULL, + role_id int NOT NULL, + PRIMARY KEY (user_id,role_id), + CONSTRAINT FK_user_id_in_user_role FOREIGN KEY (user_id) REFERENCES users (user_id), + CONSTRAINT FK_role_id_in_user_role FOREIGN KEY (role_id) REFERENCES roles (role_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -CREATE TABLE `user_address` ( - `user_id` bigint NOT NULL, - `address_id` bigint NOT NULL, - KEY `FKpv7y2l6mvly37lngi3doarqhd` (`address_id`), - KEY `FKrmincuqpi8m660j1c57xj7twr` (`user_id`), - CONSTRAINT `FKpv7y2l6mvly37lngi3doarqhd` FOREIGN KEY (`address_id`) REFERENCES `addresses` (`address_id`), - CONSTRAINT `FKrmincuqpi8m660j1c57xj7twr` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`) +CREATE TABLE user_address ( + user_id bigint NOT NULL, + address_id bigint NOT NULL, + CONSTRAINT FK_address_id_in_user_address FOREIGN KEY (address_id) REFERENCES addresses (address_id), + CONSTRAINT FK_user_id_in_user_address FOREIGN KEY (user_id) REFERENCES users (user_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -CREATE TABLE `carts` ( - `cart_id` bigint NOT NULL AUTO_INCREMENT, - `total_price` double NOT NULL, - `user_id` bigint DEFAULT NULL, - PRIMARY KEY (`cart_id`), - UNIQUE KEY `UK64t7ox312pqal3p7fg9o503c2` (`user_id`), - CONSTRAINT `FKb5o626f86h46m4s7ms6ginnop` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`) +CREATE TABLE carts ( + cart_id bigint NOT NULL AUTO_INCREMENT, + total_price double NOT NULL, + user_id bigint DEFAULT NULL, + UNIQUE KEY UK_user_id_in_carts (user_id), + CONSTRAINT FK_user_id_in_carts FOREIGN KEY (user_id) REFERENCES users (user_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -CREATE TABLE `products` ( - `id` bigint NOT NULL AUTO_INCREMENT, - `description` varchar(255) DEFAULT NULL, - `discount` double NOT NULL, - `image` varchar(255) DEFAULT NULL, - `name` varchar(255) DEFAULT NULL, - `price` double NOT NULL, - `quantity` int NOT NULL, - `special_price` double NOT NULL, - `category_id` bigint DEFAULT NULL, - `seller_id` bigint DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `FKog2rp4qthbtt2lfyhfo32lsw9` (`category_id`), - KEY `FKbgw3lyxhsml3kfqnfr45o0vbj` (`seller_id`), - CONSTRAINT `FKbgw3lyxhsml3kfqnfr45o0vbj` FOREIGN KEY (`seller_id`) REFERENCES `users` (`user_id`), - CONSTRAINT `FKog2rp4qthbtt2lfyhfo32lsw9` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) +CREATE TABLE products ( + id bigint NOT NULL AUTO_INCREMENT, + description varchar(255) DEFAULT NULL, + discount double NOT NULL, + image varchar(255) DEFAULT NULL, + name varchar(255) DEFAULT NULL, + price double NOT NULL, + quantity int NOT NULL, + special_price double NOT NULL, + category_id bigint DEFAULT NULL, + seller_id bigint DEFAULT NULL, + PRIMARY KEY (id), + CONSTRAINT FK_seller_id_in_products FOREIGN KEY (seller_id) REFERENCES users (user_id), + CONSTRAINT FK_category_id_in_products FOREIGN KEY (category_id) REFERENCES categories (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; - -CREATE TABLE `cart_items` ( - `cart_item_id` bigint NOT NULL AUTO_INCREMENT, - `discount` double NOT NULL, - `product_price` double NOT NULL, - `quantity` int NOT NULL, - `cart_id` bigint DEFAULT NULL, - `product_id` bigint DEFAULT NULL, - PRIMARY KEY (`cart_item_id`), - KEY `FKpcttvuq4mxppo8sxggjtn5i2c` (`cart_id`), - KEY `FK1re40cjegsfvw58xrkdp6bac6` (`product_id`), - CONSTRAINT `FK1re40cjegsfvw58xrkdp6bac6` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`), - CONSTRAINT `FKpcttvuq4mxppo8sxggjtn5i2c` FOREIGN KEY (`cart_id`) REFERENCES `carts` (`cart_id`) +CREATE TABLE cart_items ( + cart_item_id bigint NOT NULL AUTO_INCREMENT, + discount double NOT NULL, + product_price double NOT NULL, + quantity int NOT NULL, + cart_id bigint DEFAULT NULL, + product_id bigint DEFAULT NULL, + PRIMARY KEY (cart_item_id), + CONSTRAINT FK_product_id_in_cart_item FOREIGN KEY (product_id) REFERENCES products (id), + CONSTRAINT FK_cart_id_in_cart_item FOREIGN KEY (cart_id) REFERENCES carts (cart_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -CREATE TABLE `payments` ( - `payment_id` bigint NOT NULL AUTO_INCREMENT, - `payment_gateway_name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `payment_gateway_payment_id` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `payment_gateway_response_message` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `payment_gateway_status` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `payment_method` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, - PRIMARY KEY (`payment_id`) +CREATE TABLE payments ( + payment_id bigint NOT NULL AUTO_INCREMENT, + payment_gateway_name varchar(255) DEFAULT NULL, + payment_gateway_payment_id varchar(255) DEFAULT NULL, + payment_gateway_response_message varchar(255) DEFAULT NULL, + payment_gateway_status varchar(255) DEFAULT NULL, + payment_method varchar(255) NOT NULL, + PRIMARY KEY (payment_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -CREATE TABLE `orders` ( - `order_date` date DEFAULT NULL, - `total_amount` double NOT NULL, - `address_id` bigint DEFAULT NULL, - `order_id` bigint NOT NULL AUTO_INCREMENT, - `payment_id` bigint DEFAULT NULL, - `email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, - `order_status` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - PRIMARY KEY (`order_id`), - UNIQUE KEY `UKhaujdjk1ohmeixjhnhslchrp1` (`payment_id`), - KEY `FKhlglkvf5i60dv6dn397ethgpt` (`address_id`), - CONSTRAINT `FK8aol9f99s97mtyhij0tvfj41f` FOREIGN KEY (`payment_id`) REFERENCES `payments` (`payment_id`), - CONSTRAINT `FKhlglkvf5i60dv6dn397ethgpt` FOREIGN KEY (`address_id`) REFERENCES `addresses` (`address_id`) +CREATE TABLE orders ( + order_date date DEFAULT NULL, + total_amount double NOT NULL, + address_id bigint DEFAULT NULL, + order_id bigint NOT NULL AUTO_INCREMENT, + payment_id bigint DEFAULT NULL, + email varchar(255) NOT NULL, + order_status varchar(255) DEFAULT NULL, + PRIMARY KEY (order_id), + UNIQUE KEY UK_payment_id_in_orders (payment_id), + CONSTRAINT FK_payment_id_in_orders FOREIGN KEY (payment_id) REFERENCES payments (payment_id), + CONSTRAINT FK_address_id_in_orders FOREIGN KEY (address_id) REFERENCES addresses (address_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -CREATE TABLE `order_items` ( - `discount` double NOT NULL, - `ordered_product_price` double NOT NULL, - `quantity` int NOT NULL, - `order_id` bigint DEFAULT NULL, - `order_item_id` bigint NOT NULL AUTO_INCREMENT, - `product_id` bigint DEFAULT NULL, - PRIMARY KEY (`order_item_id`), - KEY `FKbioxgbv59vetrxe0ejfubep1w` (`order_id`), - KEY `FKocimc7dtr037rh4ls4l95nlfi` (`product_id`), - CONSTRAINT `FKbioxgbv59vetrxe0ejfubep1w` FOREIGN KEY (`order_id`) REFERENCES `orders` (`order_id`), - CONSTRAINT `FKocimc7dtr037rh4ls4l95nlfi` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) +CREATE TABLE order_items ( + discount double NOT NULL, + ordered_product_price double NOT NULL, + quantity int NOT NULL, + order_id bigint DEFAULT NULL, + order_item_id bigint NOT NULL AUTO_INCREMENT, + product_id bigint DEFAULT NULL, + PRIMARY KEY (order_item_id), + CONSTRAINT FK_order_id_in_order_items FOREIGN KEY (order_id) REFERENCES orders (order_id), + CONSTRAINT FK_product_id_order_items FOREIGN KEY (product_id) REFERENCES products (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -INSERT INTO `roles` (`role_id`, `user_role`) VALUES (1, 'ROLE_ADMIN'); -INSERT INTO `roles` (`role_id`, `user_role`) VALUES (2, 'ROLE_SELLER'); -INSERT INTO `roles` (`role_id`, `user_role`) VALUES (3, 'ROLE_USER'); +INSERT INTO roles (role_id, user_role) VALUES (1, 'ROLE_ADMIN'); +INSERT INTO roles (role_id, user_role) VALUES (2, 'ROLE_SELLER'); +INSERT INTO roles (role_id, user_role) VALUES (3, 'ROLE_USER'); -INSERT INTO `users` (`email`, `password`, `username`) VALUES ('postman@test.com', '$2a$10$hWDrBPvlioCJ5nKy89sb9uAaxvZMkQC/kKsa32FTYasXnmhCailgu', 'postman'); +INSERT INTO users (email, password, username) VALUES ('postman@test.com', '$2a$10$hWDrBPvlioCJ5nKy89sb9uAaxvZMkQC/kKsa32FTYasXnmhCailgu', 'postman'); -INSERT INTO `user_role` (`user_id`, `role_id`) VALUES (1, 1); \ No newline at end of file +INSERT INTO user_role (user_id, role_id) VALUES (1, 1); \ No newline at end of file diff --git a/src/test/java/dg/shop/server/controller/AddressControllerTest.java b/src/test/java/dg/shop/server/controller/AddressControllerTest.java index eda07ad..539b287 100644 --- a/src/test/java/dg/shop/server/controller/AddressControllerTest.java +++ b/src/test/java/dg/shop/server/controller/AddressControllerTest.java @@ -11,11 +11,13 @@ import dg.shop.server.repository.UserRepository; import dg.shop.server.service.address.AddressService; import dg.shop.server.util.AuthUtils; +import org.flywaydb.core.Flyway; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; @@ -76,13 +78,9 @@ void setUp() { .apply(springSecurity()) .build(); objectMapper = new ObjectMapper(); - } - - @AfterEach - void tearDown() { - addressRepository.deleteAll(); - userRepository.deleteAll(); - roleRepository.deleteAll(); + Flyway flyway = context.getBean(Flyway.class); + flyway.clean(); + flyway.migrate(); } @Test @@ -145,7 +143,6 @@ void getSignedInUserAddressesWhenUserHasTwoAddresses() throws Exception { User testUser = createTestUser("test_user"); AddressDTO address1 = createTestAddress(new AddressDTO(0L, "StreetA", 1, 2, "111111", "CityA"), testUser); AddressDTO address2 = createTestAddress(new AddressDTO(0L, "StreetA", 10, 0, "222222", "CityB"), testUser); - testUser.setAddresses(List.of(getAddress(address1), getAddress(address2))); signInUser(testUser); mockMvc.perform(get("/api/addresses/user")) .andExpect(status().isOk()) @@ -449,10 +446,10 @@ private AddressDTO createTestAddress(AddressDTO addressDTO, User user) { } private User createTestUser(String username) { - roleRepository.save(new Role(UserRole.ROLE_USER)); + Role role = roleRepository.saveAndFlush(new Role(UserRole.ROLE_USER)); User user = new User(username, passwordEncoder.encode("it_user_10"), "%s@test.com".formatted(username)); - user.setRoles(Set.of(new Role(UserRole.ROLE_USER))); + user.setRoles(Set.of(role)); return userRepository.save(user); } diff --git a/src/test/java/dg/shop/server/controller/AuthControllerTest.java b/src/test/java/dg/shop/server/controller/AuthControllerTest.java index 3849c59..30ddb79 100644 --- a/src/test/java/dg/shop/server/controller/AuthControllerTest.java +++ b/src/test/java/dg/shop/server/controller/AuthControllerTest.java @@ -8,6 +8,7 @@ import dg.shop.server.repository.UserRepository; import dg.shop.server.security.request.LoginRequest; import dg.shop.server.security.request.SignupRequest; +import jakarta.transaction.Transactional; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -43,6 +44,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @SpringBootTest +@Transactional public class AuthControllerTest { @Autowired @@ -75,12 +77,6 @@ void setUp() { objectMapper = new ObjectMapper(); } - @AfterEach - void tearDown() { - userRepository.deleteAll(); - roleRepository.deleteAll(); - } - @Test @DisplayName("[POST] User Sign In - user exists and signed in properly") void userSignInWhenUserSignedInProperly() throws Exception { @@ -222,7 +218,7 @@ void userSignUpWhenUserWithGivenEmailAlreadyExists() throws Exception { user.setRoles(Set.of(new Role(UserRole.ROLE_USER))); userRepository.save(user); - SignupRequest signupRequest = new SignupRequest("it_admin", "it_admin10", "ituser@test.com", null); + SignupRequest signupRequest = new SignupRequest("userWithExistingEmail", "it_admin10", "ituser@test.com", null); mockMvc.perform(post("/api/auth/signup") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(signupRequest))) @@ -233,7 +229,7 @@ void userSignUpWhenUserWithGivenEmailAlreadyExists() throws Exception { @Test @DisplayName("[POST] User Sign Up - user not exists and signed up properly but role does not exists") void userSignUpWhenUserSignedUpProperlyButRoleDoesNotExists() throws Exception { - SignupRequest signupRequest = new SignupRequest("it_admin", "it_admin10", "itadmin@dgshop.pl", Set.of("admin")); + SignupRequest signupRequest = new SignupRequest("ituser1", "it_admin10", "itadmin@dgshop.pl", Set.of("admin")); mockMvc.perform(post("/api/auth/signup") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(signupRequest))) diff --git a/src/test/java/dg/shop/server/controller/CartControllerTest.java b/src/test/java/dg/shop/server/controller/CartControllerTest.java index ce6b7a6..cb37b44 100644 --- a/src/test/java/dg/shop/server/controller/CartControllerTest.java +++ b/src/test/java/dg/shop/server/controller/CartControllerTest.java @@ -20,6 +20,7 @@ import dg.shop.server.service.category.CategoryService; import dg.shop.server.service.product.ProductService; import dg.shop.server.util.AuthUtils; +import jakarta.transaction.Transactional; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -50,6 +51,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @SpringBootTest +@Transactional public class CartControllerTest { @Autowired @@ -100,16 +102,6 @@ void setUp() { objectMapper = new ObjectMapper(); } - @AfterEach - void tearDown() { - cartItemService.getRepository().deleteAll(); - cartRepository.deleteAll(); - categoryService.getRepository().deleteAll(); - productService.getRepository().deleteAll(); - userRepository.deleteAll(); - roleRepository.deleteAll(); - } - @Test @DisplayName("[GET] Get all carts - two carts exists") @WithMockUser @@ -172,7 +164,7 @@ void addProductToCartSuccessfully() throws Exception { Category books = new Category(0L, "Books"); Category savedCategory = categoryRepository.save(books); - Product aBook = new Product(0L, "ABook", "abook.png", "A Book", 14, 12.5d, 0.0d, 12.5d, savedCategory, itUser, List.of()); + Product aBook = new Product(0L, "ABook", "abook.png", "A Book", 14, 12.5d, 0.0d, 12.5d, savedCategory, itUser); Product savedBook = productRepository.save(aBook); mockMvc.perform(post("/api/carts/products/{productId}/quantity/{quantity}", savedBook.getId(), 3)) @@ -205,7 +197,7 @@ void addProductToCarWhenCartItemAlreadyExistsInCart() throws Exception { Category books = new Category(0L, "Books"); Category savedCategory = categoryRepository.save(books); - Product aBook = new Product(0L, "ABook", "abook.png", "A Book", 14, 12.5d, 0.0d, 12.5d, savedCategory, itUser, List.of()); + Product aBook = new Product(0L, "ABook", "abook.png", "A Book", 14, 12.5d, 0.0d, 12.5d, savedCategory, itUser); Product savedBook = productRepository.save(aBook); cartService.addProductToCart(savedBook.getId(), 1); @@ -225,7 +217,7 @@ void addProductToCarWhenProductStockIsZero() throws Exception { Category books = new Category(0L, "Books"); Category savedCategory = categoryRepository.save(books); - Product aBook = new Product(0L, "ABook", "abook.png", "A Book", 0, 12.5d, 0.0d, 12.5d, savedCategory, itUser, List.of()); + Product aBook = new Product(0L, "ABook", "abook.png", "A Book", 0, 12.5d, 0.0d, 12.5d, savedCategory, itUser); Product savedBook = productRepository.save(aBook); mockMvc.perform(post("/api/carts/products/{productId}/quantity/{quantity}", savedBook.getId(), 3)) @@ -244,7 +236,7 @@ void addProductToCarWhenProductStockIsLowerThanRequestedAmount() throws Exceptio Category books = new Category(0L, "Books"); Category savedCategory = categoryRepository.save(books); - Product aBook = new Product(0L, "ABook", "abook.png", "A Book", 1, 12.5d, 0.0d, 12.5d, savedCategory, itUser, List.of()); + Product aBook = new Product(0L, "ABook", "abook.png", "A Book", 1, 12.5d, 0.0d, 12.5d, savedCategory, itUser); Product savedBook = productRepository.save(aBook); mockMvc.perform(post("/api/carts/products/{productId}/quantity/{quantity}", savedBook.getId(), 2)) @@ -287,7 +279,7 @@ void updateCartProductWhenCartItemDoesNotExistsInTheCart() throws Exception { signInUser(user); createCartForUser(user); - CategoryDTO category = categoryService.createCategory(new CategoryDTO("Games")); + CategoryDTO category = categoryService.createCategory(new CategoryDTO("CategoryA")); ProductDTO product = productService.createProduct(new ProductDTO(0L, "Witcher 3", "w3.png", "GOTY 2015", 10, 120.0d, 50.0d, 60.0d), category.id()); mockMvc.perform(put("/api/cart/products/{productId}/quantity/{operation}", product.id(), "increase")) @@ -303,7 +295,7 @@ void updateCartProductWhenCartItemExistsInTheCartAndAmountWasIncreased() throws signInUser(user); createCartForUser(user); - CategoryDTO category = categoryService.createCategory(new CategoryDTO("Games")); + CategoryDTO category = categoryService.createCategory(new CategoryDTO("CategoryD")); ProductDTO product = productService.createProduct(new ProductDTO(0L, "Witcher 3", "w3.png", "GOTY 2015", 10, 120.0d, 50.0d, 60.0d), category.id()); CartDTO cart = cartService.addProductToCart(product.id(), 1); @@ -321,7 +313,7 @@ void updateCartProductWhenTwoCartItemExistsInTheCartAndAmountOfOneWasIncreased() signInUser(user); createCartForUser(user); - CategoryDTO category = categoryService.createCategory(new CategoryDTO("Games")); + CategoryDTO category = categoryService.createCategory(new CategoryDTO("CategoryB")); ProductDTO product = productService.createProduct(new ProductDTO(0L, "Witcher 3", "w3.png", "GOTY 2015", 10, 120.0d, 50.0d, 60.0d), category.id()); ProductDTO product2 = productService.createProduct(new ProductDTO(0L, "Cyberpunk 2077", "cp2077.png", "CP 2077", 30, 100.0d, 30.0d, 70.0d), category.id()); cartService.addProductToCart(product.id(), 2); @@ -342,7 +334,7 @@ void updateCartProductWhenCartItemExistsInTheCartAndAmountWasDecreased() throws signInUser(user); createCartForUser(user); - CategoryDTO category = categoryService.createCategory(new CategoryDTO("Games")); + CategoryDTO category = categoryService.createCategory(new CategoryDTO("CategoryG")); ProductDTO product = productService.createProduct(new ProductDTO(0L, "Witcher 3", "w3.png", "GOTY 2015", 10, 120.0d, 50.0d, 60.0d), category.id()); CartDTO cart = cartService.addProductToCart(product.id(), 2); @@ -360,7 +352,7 @@ void updateCartProductWhenCartItemExistsInTheCartAndAmountWasDecreasedToZero() t signInUser(user); createCartForUser(user); - CategoryDTO category = categoryService.createCategory(new CategoryDTO("Games")); + CategoryDTO category = categoryService.createCategory(new CategoryDTO("CategoryF")); ProductDTO product = productService.createProduct(new ProductDTO(0L, "Witcher 3", "w3.png", "GOTY 2015", 10, 120.0d, 50.0d, 60.0d), category.id()); CartDTO cart = cartService.addProductToCart(product.id(), 1); @@ -377,7 +369,7 @@ void updateCartProductWhenTwoCartItemExistsInTheCartAndAmountOfOneWasDecreasedTo signInUser(user); createCartForUser(user); - CategoryDTO category = categoryService.createCategory(new CategoryDTO("Games")); + CategoryDTO category = categoryService.createCategory(new CategoryDTO("CategoryE")); ProductDTO product = productService.createProduct(new ProductDTO(0L, "Witcher 3", "w3.png", "GOTY 2015", 10, 120.0d, 50.0d, 60.0d), category.id()); ProductDTO product2 = productService.createProduct(new ProductDTO(0L, "Cyberpunk 2077", "cp2077.png", "CP 2077", 30, 100.0d, 30.0d, 70.0d), category.id()); cartService.addProductToCart(product.id(), 2); @@ -397,7 +389,7 @@ void deleteProductFromCartWhenCartItemExistsAndWasSuccessfullyRemoved() throws E signInUser(user); createCartForUser(user); - CategoryDTO category = categoryService.createCategory(new CategoryDTO("Games")); + CategoryDTO category = categoryService.createCategory(new CategoryDTO("CategoryH")); ProductDTO product = productService.createProduct(new ProductDTO(0L, "Witcher 3", "w3.png", "GOTY 2015", 10, 120.0d, 50.0d, 60.0d), category.id()); CartDTO cart = cartService.addProductToCart(product.id(), 1); @@ -427,7 +419,7 @@ void deleteProductFromCartWhenCartItemDoesNotExists() throws Exception { signInUser(user); createCartForUser(user); - CategoryDTO category = categoryService.createCategory(new CategoryDTO("Games")); + CategoryDTO category = categoryService.createCategory(new CategoryDTO("CategoryC")); ProductDTO product = productService.createProduct(new ProductDTO(0L, "Witcher 3", "w3.png", "GOTY 2015", 10, 120.0d, 50.0d, 60.0d), category.id()); CartDTO cart = cartService.addProductToCart(product.id(), 1); @@ -439,10 +431,10 @@ void deleteProductFromCartWhenCartItemDoesNotExists() throws Exception { } private User createTestUser(String username) { - roleRepository.save(new Role(UserRole.ROLE_USER)); + Role userRole = roleRepository.save(new Role(UserRole.ROLE_USER)); User user = new User(username, passwordEncoder.encode("it_user_10"), "%s@test.com".formatted(username)); - user.setRoles(Set.of(new Role(UserRole.ROLE_USER))); + user.setRoles(Set.of(userRole)); return userRepository.save(user); } diff --git a/src/test/java/dg/shop/server/controller/CategoryControllerTest.java b/src/test/java/dg/shop/server/controller/CategoryControllerTest.java index 1f9e9a2..dbb442f 100644 --- a/src/test/java/dg/shop/server/controller/CategoryControllerTest.java +++ b/src/test/java/dg/shop/server/controller/CategoryControllerTest.java @@ -4,9 +4,10 @@ import com.fasterxml.jackson.databind.ObjectMapper; import dg.shop.server.payload.category.CategoryDTO; import dg.shop.server.payload.category.CategoryResponse; +import dg.shop.server.repository.CategoryRepository; import dg.shop.server.repository.UserRepository; import dg.shop.server.service.category.CategoryService; -import org.junit.jupiter.api.AfterEach; +import jakarta.transaction.Transactional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -33,6 +34,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @SpringBootTest +@Transactional class CategoryControllerTest { @Autowired @@ -41,6 +43,9 @@ class CategoryControllerTest { @Autowired private CategoryService categoryService; + @Autowired + private CategoryRepository categoryRepository; + @Autowired private UserRepository userRepository; @@ -56,12 +61,6 @@ void setUp() { objectMapper = new ObjectMapper(); } - @AfterEach - void tearDown() { - categoryService.getRepository().deleteAll(); - userRepository.deleteAll(); - } - @Test @WithMockUser(username = "it_admin", roles = {"ADMIN"}) @DisplayName("[POST] Create category - category was created") @@ -198,7 +197,7 @@ void getAllCategoriesWhenTenExists() throws Exception { @Test @WithMockUser("it_user") - @DisplayName("[GET] Get all categories - 10 categories were created") + @DisplayName("[GET] Get all categories - 10 categories were created and sorted in ascending order") void getAllCategoriesWhenTenExistsAndWasSortedInDescendingOrder() throws Exception { for (int i = 0; i < 10; i++) { CategoryDTO categoryDTO = new CategoryDTO("Category %s".formatted(i + 1)); diff --git a/src/test/java/dg/shop/server/controller/OrderControllerTest.java b/src/test/java/dg/shop/server/controller/OrderControllerTest.java index b54d628..6e79d79 100644 --- a/src/test/java/dg/shop/server/controller/OrderControllerTest.java +++ b/src/test/java/dg/shop/server/controller/OrderControllerTest.java @@ -111,12 +111,26 @@ void setUp() { objectMapper = new ObjectMapper(); } + @AfterEach + void tearDown() { + //TODO clear db + System.out.println("Users: " + userRepository.findAll().size()); + System.out.println("Roles: " + roleRepository.findAll().size()); + System.out.println("Payments: " + paymentRepository.findAll().size()); + System.out.println("Addresses: " + addressService.getRepository().findAll().size()); + System.out.println("Order Items: " + orderItemRepository.findAll().size()); + System.out.println("Orders: " + orderService.getRepository().findAll().size()); + System.out.println("Products: " + productRepository.findAll().size()); + System.out.println("Carts: " + cartService.getRepository().findAll().size()); + System.out.println("Cart Items: " + cartItemService.getRepository().findAll().size()); + System.out.println("Categories: " + categoryRepository.findAll().size()); + } + @Test @DisplayName("[POST] Place order - success") void testPlaceOrderSuccess() throws Exception { User user = createTestUser("admin"); AddressDTO address = addressService.createAddress(new AddressDTO(0L, "Street", 33, 1, "123456", "City"), user); - user.setAddresses(List.of(addressService.getAddress(address.addressId()))); signInUser(user); CategoryDTO category = categoryService.createCategory(new CategoryDTO("Games")); ProductDTO product = productService.createProduct(new ProductDTO(0L, "Product", "product.png", "A Product", 10, 25.0d, 0.0d, 25.0d), category.id()); diff --git a/src/test/java/dg/shop/server/controller/ProductControllerTest.java b/src/test/java/dg/shop/server/controller/ProductControllerTest.java index 3e5b19d..e8c582b 100644 --- a/src/test/java/dg/shop/server/controller/ProductControllerTest.java +++ b/src/test/java/dg/shop/server/controller/ProductControllerTest.java @@ -7,6 +7,7 @@ import dg.shop.server.payload.product.ProductResponse; import dg.shop.server.service.category.CategoryService; import dg.shop.server.service.product.ProductService; +import org.flywaydb.core.Flyway; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -65,12 +66,14 @@ void setUp() { .apply(springSecurity()) .build(); objectMapper = new ObjectMapper(); + + Flyway flyway = context.getBean(Flyway.class); + flyway.clean(); + flyway.migrate(); } @AfterEach void tearDown() { - productService.getRepository().deleteAll(); - categoryService.getRepository().deleteAll(); cleanupTestFiles(); } diff --git a/src/test/java/dg/shop/server/service/address/AddressServiceImplTest.java b/src/test/java/dg/shop/server/service/address/AddressServiceImplTest.java index dd91bf8..1e80103 100644 --- a/src/test/java/dg/shop/server/service/address/AddressServiceImplTest.java +++ b/src/test/java/dg/shop/server/service/address/AddressServiceImplTest.java @@ -17,6 +17,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -75,7 +76,6 @@ void getUserAddressesWhenUserIsNotSignedIn() { @DisplayName("Get User address - user is signed in with no address") void getUserAddressWhenUserIsSignedIn() { User user = new User("test", "user_test_12", "test@test.com"); - user.setAddresses(List.of()); List userAddresses = addressService.getUserAddresses(user); assertTrue(userAddresses.isEmpty()); } @@ -85,7 +85,7 @@ void getUserAddressWhenUserIsSignedIn() { void getUserAddressWhenUserIsSignedInWithOneAddress() { User user = new User("test", "user_test_12", "test@test.com"); Address address = new Address("Street", 1, 2, "11-111", "City"); - user.setAddresses(List.of(address)); + doReturn(List.of(address)).when(addressRepository).findUserAddresses(anyLong()); List userAddresses = addressService.getUserAddresses(user); assertEquals(1, userAddresses.size()); } diff --git a/src/test/java/dg/shop/server/service/cart/CartServiceImplTest.java b/src/test/java/dg/shop/server/service/cart/CartServiceImplTest.java index f6f74d0..aa009fc 100644 --- a/src/test/java/dg/shop/server/service/cart/CartServiceImplTest.java +++ b/src/test/java/dg/shop/server/service/cart/CartServiceImplTest.java @@ -17,7 +17,6 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -81,7 +80,7 @@ void getCartByUserEmailWhenNoCartRelatedToGivenEmail() { @Test @DisplayName("Get user cart related to email - cart found") void getCartByUserEmailWhenCartExists() { - Cart toReturn = new Cart(1L, mock(User.class), List.of(), 34.0d); + Cart toReturn = new Cart(1L, mock(User.class), 34.0d); doReturn(toReturn).when(cartRepository).findCartByEmail(anyString()); Cart cart = cartService.getCartByUserEmail("existing@test.com"); assertNotNull(cart); @@ -98,7 +97,7 @@ void getUserCartWhenNoCartRelatedToGivenEmailAndUserId() { @Test @DisplayName("Get user cart DTO related to email - model found") void getUserCartWhenCartRelatedToGivenEmailAndUserIdExists() { - Cart toReturn = new Cart(1L, mock(User.class), List.of(), 34.0d); + Cart toReturn = new Cart(1L, mock(User.class), 34.0d); doReturn(toReturn).when(cartRepository).findCartByEmailAndCartId(anyString(), anyLong()); CartDTO cartDTO = cartService.getUserCart("existing@test.com", 3L); assertNotNull(cartDTO); @@ -115,7 +114,7 @@ void getCartWhenNoCartWithGivenIdExists() { @Test @DisplayName("Get cart - cart with given id does exist") void getCartWhenCartWithGivenIdExists() { - Cart toReturn = new Cart(5L, mock(User.class), List.of(), 123.5d); + Cart toReturn = new Cart(5L, mock(User.class), 123.5d); doReturn(Optional.of(toReturn)).when(cartRepository).findById(5L); Cart cart = cartService.getCart(5L); assertNotNull(cart); @@ -127,15 +126,16 @@ void getCartWhenCartWithGivenIdExists() { void addProductToCartSuccess() { doReturn("user@test.com").when(authUtils).signedInEmail(); User user = new User("user", "pass", "user@test.com"); - Cart existingCart = new Cart(5L, user, new ArrayList<>(), 0.0d); + Cart existingCart = new Cart(5L, user, 0.0d); doReturn(existingCart).when(cartRepository).findCartByEmail(anyString()); - Product product = new Product(4L, "ABook", "abook.png", "Great book", 10, 58.5d, 0.0d, 58.5d, mock(Category.class), mock(User.class), List.of()); + Product product = new Product(4L, "ABook", "abook.png", "Great book", 10, 58.5d, 0.0d, 58.5d, mock(Category.class), mock(User.class)); doReturn(product).when(productService).getProduct(anyLong()); ProductDTO productDTO = new ProductDTO(4L, "ABook", "abook.png", "Great book", 3, 58.5d, 0.0d, 58.5d); doReturn(productDTO).when(productService).toDTO(any(Product.class), anyInt()); CartItem addedProduct = new CartItem(3L, existingCart, product, 3, 0.0d, 58.5d); doReturn(addedProduct).when(cartItemService).persist(any(CartItem.class)); - Cart updatedCart = new Cart(5L, user, List.of(addedProduct), 175.5d); + doReturn(List.of(addedProduct)).when(cartItemService).getCartItemsFromCart(anyLong()); + Cart updatedCart = new Cart(5L, user, 175.5d); doReturn(updatedCart).when(cartRepository).save(any(Cart.class)); CartDTO cartDTO = cartService.addProductToCart(4L, 3); @@ -151,15 +151,16 @@ void addProductToCartSuccessWhenCartDoesNotExistsBefore() { doReturn("user@test.com").when(authUtils).signedInEmail(); User user = new User("user", "pass", "user@test.com"); doReturn(user).when(authUtils).signedInUser(); - Cart newCart = new Cart(5L, user, new ArrayList<>(), 0.0d); + Cart newCart = new Cart(5L, user, 0.0d); doReturn(newCart).when(cartRepository).save(any(Cart.class)); - Product product = new Product(4L, "ABook", "abook.png", "Great book", 10, 58.5d, 0.0d, 58.5d, mock(Category.class), mock(User.class), List.of()); + Product product = new Product(4L, "ABook", "abook.png", "Great book", 10, 58.5d, 0.0d, 58.5d, mock(Category.class), mock(User.class)); doReturn(product).when(productService).getProduct(anyLong()); ProductDTO productDTO = new ProductDTO(4L, "ABook", "abook.png", "Great book", 3, 58.5d, 0.0d, 58.5d); doReturn(productDTO).when(productService).toDTO(any(Product.class), anyInt()); CartItem addedProduct = new CartItem(3L, newCart, product, 3, 0.0d, 58.5d); doReturn(addedProduct).when(cartItemService).persist(any(CartItem.class)); - Cart updatedCart = new Cart(5L, user, List.of(addedProduct), 175.5d); + doReturn(List.of(addedProduct)).when(cartItemService).getCartItemsFromCart(anyLong()); + Cart updatedCart = new Cart(5L, user, 175.5d); doReturn(updatedCart).when(cartRepository).save(eq(newCart)); CartDTO cartDTO = cartService.addProductToCart(4L, 3); @@ -174,7 +175,7 @@ void addProductToCartSuccessWhenCartDoesNotExistsBefore() { void addProductToCartWhenCartExistsButProductNot() { doReturn("user@test.com").when(authUtils).signedInEmail(); User user = new User("user", "pass", "user@test.com"); - Cart existingCart = new Cart(5L, user, List.of(), 0.0d); + Cart existingCart = new Cart(5L, user, 0.0d); doReturn(existingCart).when(cartRepository).findCartByEmail(anyString()); doThrow(ResourceNotFoundException.class).when(productService).getProduct(anyLong()); @@ -187,9 +188,9 @@ void addProductToCartWhenProductAlreadyInCart() { doReturn("user@test.com").when(authUtils).signedInEmail(); User user = new User("user", "pass", "user@test.com"); CartItem productInCart = mock(CartItem.class); - Cart existingCart = new Cart(5L, user, List.of(productInCart), 0.0d); + Cart existingCart = new Cart(5L, user, 0.0d); doReturn(existingCart).when(cartRepository).findCartByEmail(anyString()); - Product product = new Product(4L, "ABook", "abook.png", "Great book", 10, 58.5d, 0.0d, 58.5d, mock(Category.class), mock(User.class), List.of()); + Product product = new Product(4L, "ABook", "abook.png", "Great book", 10, 58.5d, 0.0d, 58.5d, mock(Category.class), mock(User.class)); doReturn(product).when(productService).getProduct(anyLong()); CartItem addedProduct = new CartItem(3L, existingCart, product, 3, 0.0d, 58.5d); doReturn(addedProduct).when(cartItemService).findByProductIdAndCartId(existingCart.getCartId(), product.getId()); @@ -204,9 +205,9 @@ void addProductToCartWhenProductQuantityIsZero() { doReturn("user@test.com").when(authUtils).signedInEmail(); User user = new User("user", "pass", "user@test.com"); CartItem productInCart = mock(CartItem.class); - Cart existingCart = new Cart(5L, user, List.of(productInCart), 0.0d); + Cart existingCart = new Cart(5L, user, 0.0d); doReturn(existingCart).when(cartRepository).findCartByEmail(anyString()); - Product product = new Product(4L, "ABook", "abook.png", "Great book", 0, 58.5d, 0.0d, 58.5d, mock(Category.class), mock(User.class), List.of()); + Product product = new Product(4L, "ABook", "abook.png", "Great book", 0, 58.5d, 0.0d, 58.5d, mock(Category.class), mock(User.class)); doReturn(product).when(productService).getProduct(anyLong()); String message = assertThrows(ApiException.class, () -> cartService.addProductToCart(4L, 3)).getMessage(); @@ -219,9 +220,9 @@ void addProductToCartWhenProductQuantityIsLessThanRequiredOrderQuantity() { doReturn("user@test.com").when(authUtils).signedInEmail(); User user = new User("user", "pass", "user@test.com"); CartItem productInCart = mock(CartItem.class); - Cart existingCart = new Cart(5L, user, List.of(productInCart), 0.0d); + Cart existingCart = new Cart(5L, user, 0.0d); doReturn(existingCart).when(cartRepository).findCartByEmail(anyString()); - Product product = new Product(4L, "ABook", "abook.png", "Great book", 2, 58.5d, 0.0d, 58.5d, mock(Category.class), mock(User.class), List.of()); + Product product = new Product(4L, "ABook", "abook.png", "Great book", 2, 58.5d, 0.0d, 58.5d, mock(Category.class), mock(User.class)); doReturn(product).when(productService).getProduct(anyLong()); String message = assertThrows(ApiException.class, () -> cartService.addProductToCart(4L, 3)).getMessage(); @@ -233,7 +234,7 @@ void addProductToCartWhenProductQuantityIsLessThanRequiredOrderQuantity() { void deleteProductSuccess() { CartItem inCart = mock(CartItem.class); CartItem inCart2 = mock(CartItem.class); - Cart cart = new Cart(4L, mock(User.class), new ArrayList<>(List.of(inCart, inCart2)), 75.0d); + Cart cart = new Cart(4L, mock(User.class), 75.0d); doReturn(Optional.of(cart)).when(cartRepository).findById(anyLong()); doReturn(inCart).when(cartItemService).findByProductIdAndCartId(anyLong(), anyLong()); doReturn(10.0d).when(inCart).getProductPrice(); @@ -269,7 +270,7 @@ void deleteProductWhenCartItemWasNotFound() { @Test @DisplayName("Update product in carts - product price has changed") void updateProductInCartsWhenProductPriceHasChanged() { - Cart mockCart = new Cart(4L, mock(User.class), List.of(), 100.0d); + Cart mockCart = new Cart(4L, mock(User.class), 100.0d); doReturn(Optional.of(mockCart)).when(cartRepository).findById(anyLong()); Product mockProduct = mock(Product.class); doReturn(mockProduct).when(productService).getProduct(anyLong()); @@ -319,10 +320,11 @@ void updateProductInCartsWhenCartsItemDoesNotExists() { void updateProductQuantityByAddingAmount() { doReturn("user@test.com").when(authUtils).signedInEmail(); Cart cart = new Cart(); - Product product = new Product(21L, "ABook", "image.png", "ABook", 15, 24.0d, 0.0d, 24.0d, mock(Category.class), mock(User.class), List.of()); + Product product = new Product(21L, "ABook", "image.png", "ABook", 15, 24.0d, 0.0d, 24.0d, mock(Category.class), mock(User.class)); ProductDTO productDTO = new ProductDTO(21L, "ABook", "image.png", "ABook", 2, 24.0d, 0.0d, 24.0d); CartItem itemInCart = new CartItem(34L, cart, product, 1, 0.0d, 24.0d); - Cart existingCart = new Cart(55L, mock(User.class), List.of(itemInCart), 24.0d); + Cart existingCart = new Cart(55L, mock(User.class), 24.0d); + doReturn(List.of(itemInCart)).when(cartItemService).getCartItemsFromCart(55L); doReturn(existingCart).when(cartRepository).findCartByEmail("user@test.com"); doReturn(product).when(productService).getProduct(21L); doReturn(productDTO).when(productService).toDTO(any(Product.class), anyInt()); @@ -343,14 +345,15 @@ void updateProductQuantityByAddingAmount() { void updateProductQuantityBySubtractingAmount() { doReturn("user@test.com").when(authUtils).signedInEmail(); Cart cart = new Cart(); - Product product = new Product(21L, "ABook", "image.png", "ABook", 15, 24.0d, 0.0d, 24.0d, mock(Category.class), mock(User.class), List.of()); + Product product = new Product(21L, "ABook", "image.png", "ABook", 15, 24.0d, 0.0d, 24.0d, mock(Category.class), mock(User.class)); ProductDTO productDTO = new ProductDTO(21L, "ABook", "image.png", "ABook", 1, 24.0d, 0.0d, 24.0d); CartItem itemInCart = new CartItem(34L, cart, product, 2, 0.0d, 24.0d); - Cart existingCart = new Cart(55L, mock(User.class), List.of(itemInCart), 48.0d); + Cart existingCart = new Cart(55L, mock(User.class), 48.0d); doReturn(existingCart).when(cartRepository).findCartByEmail("user@test.com"); doReturn(product).when(productService).getProduct(21L); doReturn(productDTO).when(productService).toDTO(any(Product.class), anyInt()); doReturn(itemInCart).when(cartItemService).findByProductIdAndCartId(existingCart.getCartId(), product.getId()); + doReturn(List.of(itemInCart)).when(cartItemService).getCartItemsFromCart(anyLong()); CartItem updatedItemInCart = new CartItem(34L, cart, product, 1, 0.0d, 24.0d); doReturn(updatedItemInCart).when(cartItemService).persist(itemInCart); @@ -367,18 +370,19 @@ void updateProductQuantityBySubtractingAmount() { void updateProductQuantityByAddingAmountForOneProductWhenTwoProductsAreInCart() { doReturn("user@test.com").when(authUtils).signedInEmail(); Cart cart = new Cart(); - Product product = new Product(21L, "ABook", "image.png", "ABook", 15, 24.0d, 0.0d, 24.0d, mock(Category.class), mock(User.class), List.of()); - Product product2 = new Product(22L, "CBook", "imagec.png", "CBook", 12, 12.0d, 0.0d, 12.0d, mock(Category.class), mock(User.class), List.of()); + Product product = new Product(21L, "ABook", "image.png", "ABook", 15, 24.0d, 0.0d, 24.0d, mock(Category.class), mock(User.class)); + Product product2 = new Product(22L, "CBook", "imagec.png", "CBook", 12, 12.0d, 0.0d, 12.0d, mock(Category.class), mock(User.class)); ProductDTO product1DTO = new ProductDTO(21L, "ABook", "image.png", "ABook", 2, 24.0d, 0.0d, 24.0d); ProductDTO product2DTO = new ProductDTO(22L, "CBook", "imagec.png", "CBook", 1, 12.0d, 0.0d, 12.0d); CartItem itemInCart = new CartItem(34L, cart, product, 1, 0.0d, 24.0d); CartItem itemInCart2 = new CartItem(35L, cart, product2, 1, 0.0d, 12.0d); - Cart existingCart = new Cart(55L, mock(User.class), List.of(itemInCart, itemInCart2), 36.0d); + Cart existingCart = new Cart(55L, mock(User.class), 36.0d); doReturn(existingCart).when(cartRepository).findCartByEmail("user@test.com"); doReturn(product).when(productService).getProduct(21L); doReturn(product1DTO).when(productService).toDTO(eq(product), anyInt()); doReturn(product2DTO).when(productService).toDTO(eq(product2), anyInt()); doReturn(itemInCart).when(cartItemService).findByProductIdAndCartId(existingCart.getCartId(), product.getId()); + doReturn(List.of(itemInCart)).when(cartItemService).getCartItemsFromCart(anyLong()); CartItem updatedItemInCart = new CartItem(34L, cart, product, 2, 0.0d, 48.0d); doReturn(updatedItemInCart).when(cartItemService).persist(itemInCart); @@ -395,9 +399,9 @@ void updateProductQuantityByAddingAmountForOneProductWhenTwoProductsAreInCart() void updateProductQuantityBySubtractingAmountToZero() { doReturn("user@test.com").when(authUtils).signedInEmail(); Cart cart = new Cart(); - Product product = new Product(21L, "ABook", "image.png", "ABook", 15, 24.0d, 0.0d, 24.0d, mock(Category.class), mock(User.class), List.of()); + Product product = new Product(21L, "ABook", "image.png", "ABook", 15, 24.0d, 0.0d, 24.0d, mock(Category.class), mock(User.class)); CartItem itemInCart = new CartItem(34L, cart, product, 1, 0.0d, 24.0d); - Cart existingCart = new Cart(55L, mock(User.class), new ArrayList<>(List.of(itemInCart)), 24.0d); + Cart existingCart = new Cart(55L, mock(User.class), 24.0d); doReturn(existingCart).when(cartRepository).findCartByEmail("user@test.com"); doReturn(product).when(productService).getProduct(21L); doReturn(itemInCart).when(cartItemService).findByProductIdAndCartId(existingCart.getCartId(), product.getId()); @@ -445,9 +449,9 @@ void updateProductQuantityWhenCartItemWasNotFound() { void updateProductQuantityWhenOperationIsNotSupported() { doReturn("user@test.com").when(authUtils).signedInEmail(); Cart cart = new Cart(); - Product product = new Product(21L, "ABook", "image.png", "ABook", 15, 24.0d, 0.0d, 24.0d, mock(Category.class), mock(User.class), List.of()); + Product product = new Product(21L, "ABook", "image.png", "ABook", 15, 24.0d, 0.0d, 24.0d, mock(Category.class), mock(User.class)); CartItem itemInCart = new CartItem(34L, cart, product, 1, 0.0d, 24.0d); - Cart existingCart = new Cart(55L, mock(User.class), List.of(itemInCart), 24.0d); + Cart existingCart = new Cart(55L, mock(User.class), 24.0d); doReturn(existingCart).when(cartRepository).findCartByEmail("user@test.com"); doReturn(product).when(productService).getProduct(21L); doReturn(itemInCart).when(cartItemService).findByProductIdAndCartId(existingCart.getCartId(), product.getId()); diff --git a/src/test/java/dg/shop/server/service/category/CategoryServiceImplTest.java b/src/test/java/dg/shop/server/service/category/CategoryServiceImplTest.java index 5f370c5..2e64ad1 100644 --- a/src/test/java/dg/shop/server/service/category/CategoryServiceImplTest.java +++ b/src/test/java/dg/shop/server/service/category/CategoryServiceImplTest.java @@ -109,7 +109,7 @@ void deleteCategoryWhenExists() throws JsonProcessingException { doReturn(Optional.of(comics)).when(categoryRepository).findById(eq(id)); CategoryDTO deletedCategory = categoryService.deleteCategory(id); assertEquals(""" - {"id":3,"name":"Comics","products":[]}""", new ObjectMapper().writeValueAsString(deletedCategory)); + {"id":3,"name":"Comics"}""", new ObjectMapper().writeValueAsString(deletedCategory)); verify(categoryRepository, times(1)).findById(eq(id)); verify(categoryRepository, times(1)).delete(eq(comics)); } diff --git a/src/test/java/dg/shop/server/service/product/ProductServiceImplTest.java b/src/test/java/dg/shop/server/service/product/ProductServiceImplTest.java index 4ff732a..f150988 100644 --- a/src/test/java/dg/shop/server/service/product/ProductServiceImplTest.java +++ b/src/test/java/dg/shop/server/service/product/ProductServiceImplTest.java @@ -64,7 +64,6 @@ void createProductWhenCategoryDoesNotExist() { void createProductWhenCategoryExistsAndHasNoProducts() { Category mockedCategory = mock(Category.class); doReturn(mockedCategory).when(categoryService).getCategory(anyLong()); - doReturn(List.of()).when(mockedCategory).getProducts(); ProductDTO toCreate = createPopulatedProductDTO(); Product savedProduct = createSavedProduct(5L, "ProductName"); doReturn(savedProduct).when(productRepository).save(any(Product.class)); @@ -78,7 +77,7 @@ void createProductWhenCategoryExistsAndHasTheSameProductDefined() { Category mockedCategory = mock(Category.class); doReturn(mockedCategory).when(categoryService).getCategory(anyLong()); Product savedProduct = createSavedProduct(5L, "ProductName"); - doReturn(List.of(savedProduct)).when(mockedCategory).getProducts(); + doReturn(List.of(savedProduct)).when(productRepository).findAllByCategoryId(anyLong()); ProductDTO toCreate = createPopulatedProductDTO(); String message = assertThrows(ApiException.class, () -> productService.createProduct(toCreate, 3L)) .getMessage(); @@ -216,7 +215,7 @@ void updateProductWhenDescriptionAndPriceChanged() { existing.getQuantity(), 50.0d, existing.getDiscount(), existing.getSpecialPrice()); Product updatedProduct = new Product(existing.getId(), existing.getName(), existing.getImage(), toUpdate.description(), existing.getQuantity(), toUpdate.price(), existing.getDiscount(), - existing.getSpecialPrice(), existing.getCategory(), existing.getSeller(), existing.getProducts()); + existing.getSpecialPrice(), existing.getCategory(), existing.getSeller()); doReturn(updatedProduct).when(productRepository).save(eq(existing)); ProductDTO updated = productService.updateProduct(productId, toUpdate); From 60b8525f4be8240af5a801285c9d0da7b03eaa4c Mon Sep 17 00:00:00 2001 From: dawidg90 Date: Mon, 23 Jun 2025 18:24:08 +0200 Subject: [PATCH 07/11] Tests refactor --- src/main/java/dg/shop/server/model/Cart.java | 8 - .../server/payload/product/ProductDTO.java | 10 +- .../controller/AddressControllerTest.java | 135 +++++------- .../server/controller/AuthControllerTest.java | 123 +++++------ .../server/controller/BaseControllerTest.java | 42 ++++ .../server/controller/CartControllerTest.java | 68 ++---- .../controller/CategoryControllerTest.java | 83 +++----- .../controller/OrderControllerTest.java | 46 +--- .../controller/ProductControllerTest.java | 200 ++++++++---------- 9 files changed, 295 insertions(+), 420 deletions(-) create mode 100644 src/test/java/dg/shop/server/controller/BaseControllerTest.java diff --git a/src/main/java/dg/shop/server/model/Cart.java b/src/main/java/dg/shop/server/model/Cart.java index 2c3666c..0a50d58 100644 --- a/src/main/java/dg/shop/server/model/Cart.java +++ b/src/main/java/dg/shop/server/model/Cart.java @@ -1,21 +1,16 @@ package dg.shop.server.model; -import jakarta.persistence.CascadeType; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; -import jakarta.persistence.OneToMany; import jakarta.persistence.OneToOne; import jakarta.persistence.Table; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import java.util.ArrayList; -import java.util.List; - @Entity @Data @Table(name = "carts") @@ -30,9 +25,6 @@ public class Cart { @JoinColumn(name = "user_id") private User user; -// @OneToMany(mappedBy = "cart", cascade = { CascadeType.ALL }, orphanRemoval = true) -// private List cartItems = new ArrayList<>(); - private double totalPrice = 0.0d; @Override diff --git a/src/main/java/dg/shop/server/payload/product/ProductDTO.java b/src/main/java/dg/shop/server/payload/product/ProductDTO.java index c42e6f0..4a7931f 100644 --- a/src/main/java/dg/shop/server/payload/product/ProductDTO.java +++ b/src/main/java/dg/shop/server/payload/product/ProductDTO.java @@ -5,7 +5,13 @@ import jakarta.validation.constraints.PositiveOrZero; import jakarta.validation.constraints.Size; -public record ProductDTO(long id, @Size(min = 3, max = 100) String name, @Nullable String image, @Size(min = 6, max = 200) String description, @PositiveOrZero int quantity, - @Positive double price, @PositiveOrZero double discount, @PositiveOrZero double specialPrice) { +public record ProductDTO(long id, + @Size(min = 3, max = 100) String name, + @Nullable String image, + @Size(min = 6, max = 200) String description, + @PositiveOrZero int quantity, + @Positive double price, + @PositiveOrZero double discount, + @PositiveOrZero double specialPrice) { public ProductDTO() { this(0L, "", "", "", 0, 0.0, 0.0, 0.0); } } diff --git a/src/test/java/dg/shop/server/controller/AddressControllerTest.java b/src/test/java/dg/shop/server/controller/AddressControllerTest.java index 539b287..77fef62 100644 --- a/src/test/java/dg/shop/server/controller/AddressControllerTest.java +++ b/src/test/java/dg/shop/server/controller/AddressControllerTest.java @@ -1,6 +1,5 @@ package dg.shop.server.controller; -import com.fasterxml.jackson.databind.ObjectMapper; import dg.shop.server.model.Address; import dg.shop.server.model.Role; import dg.shop.server.model.User; @@ -11,31 +10,22 @@ import dg.shop.server.repository.UserRepository; import dg.shop.server.service.address.AddressService; import dg.shop.server.util.AuthUtils; -import org.flywaydb.core.Flyway; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.test.context.support.WithMockUser; -import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.context.WebApplicationContext; import java.util.List; import java.util.Set; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; -import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; @@ -44,11 +34,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -@SpringBootTest -class AddressControllerTest { - - @Autowired - private WebApplicationContext context; +class AddressControllerTest extends BaseControllerTest { @Autowired private AddressRepository addressRepository; @@ -68,26 +54,11 @@ class AddressControllerTest { @Autowired private AddressService addressService; - private MockMvc mockMvc; - private ObjectMapper objectMapper; - - @BeforeEach - void setUp() { - mockMvc = MockMvcBuilders - .webAppContextSetup(context) - .apply(springSecurity()) - .build(); - objectMapper = new ObjectMapper(); - Flyway flyway = context.getBean(Flyway.class); - flyway.clean(); - flyway.migrate(); - } - @Test @DisplayName("[GET] Get all addresses - no address exists") @WithMockUser void getAllAddressesNoAddressExists() throws Exception { - mockMvc.perform(MockMvcRequestBuilders.get("/api/addresses")) + getMockMvc().perform(MockMvcRequestBuilders.get("/api/addresses")) .andExpect(status().isOk()) .andExpect(content().json("[]")); } @@ -99,7 +70,7 @@ void getAllAddressesThreeAddressesExist() throws Exception { addressRepository.save(new Address("StreetA", 1, 2, "111111", "CityA")); addressRepository.save(new Address("StreetA", 10, 0, "222222", "CityB")); addressRepository.save(new Address("StreetA", 15, 22, "333333", "CityC")); - mockMvc.perform(get("/api/addresses")) + getMockMvc().perform(get("/api/addresses")) .andExpect(status().isOk()) .andExpect(jsonPath("$", hasSize(3))); } @@ -108,7 +79,7 @@ void getAllAddressesThreeAddressesExist() throws Exception { @DisplayName("[GET] Get address by id - no address found") @WithMockUser void getAddressByIdWhenNoAddressFound() throws Exception { - mockMvc.perform(get("/api/addresses/{addressId}", 5)) + getMockMvc().perform(get("/api/addresses/{addressId}", 5)) .andExpect(status().isNotFound()) .andExpect(jsonPath("message", is("Address with ID: 5 not found"))) .andExpect(jsonPath("status", is(false))); @@ -119,7 +90,7 @@ void getAddressByIdWhenNoAddressFound() throws Exception { @WithMockUser void getAddressByIdWhenAddressFound() throws Exception { Address address = addressRepository.save(new Address("StreetA", 1, 2, "111111", "CityA")); - mockMvc.perform(get("/api/addresses/{addressId}", address.getAddressId())) + getMockMvc().perform(get("/api/addresses/{addressId}", address.getAddressId())) .andExpect(status().isOk()) .andExpect(jsonPath("street", is("StreetA"))) .andExpect(jsonPath("postalCode", is("111111"))) @@ -131,7 +102,7 @@ void getAddressByIdWhenAddressFound() throws Exception { void getSignedInUserAddressesWhenUserHasNoAddress() throws Exception { User testUser = createTestUser("test_user"); signInUser(testUser); - mockMvc.perform(get("/api/addresses/user")) + getMockMvc().perform(get("/api/addresses/user")) .andExpect(status().isOk()) .andExpect(jsonPath("$", hasSize(0))); signOutUser(); @@ -144,7 +115,7 @@ void getSignedInUserAddressesWhenUserHasTwoAddresses() throws Exception { AddressDTO address1 = createTestAddress(new AddressDTO(0L, "StreetA", 1, 2, "111111", "CityA"), testUser); AddressDTO address2 = createTestAddress(new AddressDTO(0L, "StreetA", 10, 0, "222222", "CityB"), testUser); signInUser(testUser); - mockMvc.perform(get("/api/addresses/user")) + getMockMvc().perform(get("/api/addresses/user")) .andExpect(status().isOk()) .andExpect(jsonPath("$", hasSize(2))); signOutUser(); @@ -154,9 +125,9 @@ void getSignedInUserAddressesWhenUserHasTwoAddresses() throws Exception { @DisplayName("[POST] Create address - empty payload") @WithMockUser void createAddressWhenPayloadIsEmpty() throws Exception { - mockMvc.perform(post("/api/addresses") + getMockMvc().perform(post("/api/addresses") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(null))) + .content(getObjectMapper().writeValueAsString(null))) .andExpect(status().isBadRequest()); } @@ -166,9 +137,9 @@ void createAddressWhenAddressWereCreatedSuccessfully() throws Exception { AddressDTO toCreate = new AddressDTO(0L, "AStreet", 4, 5, "445544", "ACity"); User user = createTestUser("user1"); signInUser(user); - mockMvc.perform(post("/api/addresses") + getMockMvc().perform(post("/api/addresses") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(toCreate))) + .content(getObjectMapper().writeValueAsString(toCreate))) .andExpect(status().isCreated()) .andExpect(jsonPath("$.city", is("ACity"))); signOutUser(); @@ -179,9 +150,9 @@ void createAddressWhenAddressWereCreatedSuccessfully() throws Exception { @WithMockUser void createAddressWhenStreetNameIsTooShort() throws Exception { AddressDTO toCreate = new AddressDTO(0L, "AStr", 4, 5, "445544", "ACity"); - mockMvc.perform(post("/api/addresses") + getMockMvc().perform(post("/api/addresses") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(toCreate))) + .content(getObjectMapper().writeValueAsString(toCreate))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("street", is("Street name must have at least 5 characters"))); } @@ -191,9 +162,9 @@ void createAddressWhenStreetNameIsTooShort() throws Exception { @WithMockUser void createAddressWhenStreetNameIsBlank() throws Exception { AddressDTO toCreate = new AddressDTO(0L, "", 4, 5, "445544", "ACity"); - mockMvc.perform(post("/api/addresses") + getMockMvc().perform(post("/api/addresses") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(toCreate))) + .content(getObjectMapper().writeValueAsString(toCreate))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("street", is("must not be blank"))); } @@ -203,9 +174,9 @@ void createAddressWhenStreetNameIsBlank() throws Exception { @WithMockUser void createAddressWhenHouseNumberIsZero() throws Exception { AddressDTO toCreate = new AddressDTO(0L, "AStreet", 0, 5, "445544", "ACity"); - mockMvc.perform(post("/api/addresses") + getMockMvc().perform(post("/api/addresses") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(toCreate))) + .content(getObjectMapper().writeValueAsString(toCreate))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("houseNumber", is("must be greater than 0"))); } @@ -216,9 +187,9 @@ void createAddressWhenFlatNumberIsZero() throws Exception { AddressDTO toCreate = new AddressDTO(0L, "AStreet", 4, 0, "445544", "ACity"); User user = createTestUser("thUser1"); signInUser(user); - mockMvc.perform(post("/api/addresses") + getMockMvc().perform(post("/api/addresses") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(toCreate))) + .content(getObjectMapper().writeValueAsString(toCreate))) .andExpect(status().isCreated()) .andExpect(jsonPath("flatNumber", is(0))); signOutUser(); @@ -229,9 +200,9 @@ void createAddressWhenFlatNumberIsZero() throws Exception { @WithMockUser void createAddressWhenFlatNumberIsNegative() throws Exception { AddressDTO toCreate = new AddressDTO(0L, "AStreet", 4, -5, "445544", "ACity"); - mockMvc.perform(post("/api/addresses") + getMockMvc().perform(post("/api/addresses") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(toCreate))) + .content(getObjectMapper().writeValueAsString(toCreate))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("flatNumber", is("must be greater than or equal to 0"))); } @@ -241,9 +212,9 @@ void createAddressWhenFlatNumberIsNegative() throws Exception { @WithMockUser void createAddressWhenPostalCodeIsTooShort() throws Exception { AddressDTO toCreate = new AddressDTO(0L, "AStreet", 4, 5, "445", "ACity"); - mockMvc.perform(post("/api/addresses") + getMockMvc().perform(post("/api/addresses") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(toCreate))) + .content(getObjectMapper().writeValueAsString(toCreate))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("postalCode", is("Postal code must have at least 6 characters"))); } @@ -253,9 +224,9 @@ void createAddressWhenPostalCodeIsTooShort() throws Exception { @WithMockUser void createAddressWhenPostalCodeIsBlank() throws Exception { AddressDTO toCreate = new AddressDTO(0L, "AStreet", 4, 5, "", "ACity"); - mockMvc.perform(post("/api/addresses") + getMockMvc().perform(post("/api/addresses") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(toCreate))) + .content(getObjectMapper().writeValueAsString(toCreate))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("postalCode", is("must not be blank"))); } @@ -265,9 +236,9 @@ void createAddressWhenPostalCodeIsBlank() throws Exception { @WithMockUser void createAddressWhenCtyNameIsTooShort() throws Exception { AddressDTO toCreate = new AddressDTO(0L, "AStreet", 4, 5, "445445", "Ci"); - mockMvc.perform(post("/api/addresses") + getMockMvc().perform(post("/api/addresses") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(toCreate))) + .content(getObjectMapper().writeValueAsString(toCreate))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("city", is("City name must have at least 3 characters"))); } @@ -277,9 +248,9 @@ void createAddressWhenCtyNameIsTooShort() throws Exception { @WithMockUser void createAddressWhenCityNameIsBlank() throws Exception { AddressDTO toCreate = new AddressDTO(0L, "AStreet", 4, 5, "333222", ""); - mockMvc.perform(post("/api/addresses") + getMockMvc().perform(post("/api/addresses") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(toCreate))) + .content(getObjectMapper().writeValueAsString(toCreate))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("city", is("must not be blank"))); } @@ -288,7 +259,7 @@ void createAddressWhenCityNameIsBlank() throws Exception { @DisplayName("[DELETE] Delete address - given address does not exists") @WithMockUser void deleteAddressWhenAddressNotExists() throws Exception { - mockMvc.perform(delete("/api/addresses/{addressId}", 4)) + getMockMvc().perform(delete("/api/addresses/{addressId}", 4)) .andExpect(status().isNotFound()) .andExpect(jsonPath("message", is("Address with ID: 4 not found"))); } @@ -298,7 +269,7 @@ void deleteAddressWhenAddressNotExists() throws Exception { @WithMockUser void deleteAddressWhenAddressDeletedSuccessfully() throws Exception { Address toDelete = addressRepository.save(new Address("ACity", 1, 2, "111111", "City")); - mockMvc.perform(delete("/api/addresses/{addressId}", toDelete.getAddressId())) + getMockMvc().perform(delete("/api/addresses/{addressId}", toDelete.getAddressId())) .andExpect(status().isOk()) .andExpect(content().string("Address with ID: %s was deleted".formatted(toDelete.getAddressId()))); } @@ -307,9 +278,9 @@ void deleteAddressWhenAddressDeletedSuccessfully() throws Exception { @DisplayName("[PUT] Update address - empty payload") @WithMockUser void updateAddressWhenPayloadIsEmpty() throws Exception { - mockMvc.perform(put("/api/addresses/{addressId}", 1) + getMockMvc().perform(put("/api/addresses/{addressId}", 1) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(null))) + .content(getObjectMapper().writeValueAsString(null))) .andExpect(status().isBadRequest()); } @@ -321,9 +292,9 @@ void updateAddressWhenAddressWereCreatedSuccessfully() throws Exception { AddressDTO saved = createTestAddress(existingAddress, user); signInUser(user); AddressDTO toUpdate = new AddressDTO(0L, "AStreet", 4, 5, "445544", "ACity"); - mockMvc.perform(put("/api/addresses/{addressId}", saved.addressId()) + getMockMvc().perform(put("/api/addresses/{addressId}", saved.addressId()) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(toUpdate))) + .content(getObjectMapper().writeValueAsString(toUpdate))) .andExpect(status().isOk()) .andExpect(jsonPath("$.city", is("ACity"))); signOutUser(); @@ -334,9 +305,9 @@ void updateAddressWhenAddressWereCreatedSuccessfully() throws Exception { @WithMockUser void updateAddressWhenStreetNameIsTooShort() throws Exception { AddressDTO toUpdate = new AddressDTO(0L, "AStr", 4, 5, "445544", "ACity"); - mockMvc.perform(put("/api/addresses/{addressId}", 1) + getMockMvc().perform(put("/api/addresses/{addressId}", 1) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(toUpdate))) + .content(getObjectMapper().writeValueAsString(toUpdate))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("street", is("Street name must have at least 5 characters"))); } @@ -346,9 +317,9 @@ void updateAddressWhenStreetNameIsTooShort() throws Exception { @WithMockUser void updateAddressWhenStreetNameIsBlank() throws Exception { AddressDTO toCreate = new AddressDTO(0L, "", 4, 5, "445544", "ACity"); - mockMvc.perform(put("/api/addresses/{addressId}", 1) + getMockMvc().perform(put("/api/addresses/{addressId}", 1) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(toCreate))) + .content(getObjectMapper().writeValueAsString(toCreate))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("street", is("must not be blank"))); } @@ -358,9 +329,9 @@ void updateAddressWhenStreetNameIsBlank() throws Exception { @WithMockUser void updateAddressWhenHouseNumberIsZero() throws Exception { AddressDTO toCreate = new AddressDTO(0L, "AStreet", 0, 5, "445544", "ACity"); - mockMvc.perform(put("/api/addresses/{addressId}", 1) + getMockMvc().perform(put("/api/addresses/{addressId}", 1) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(toCreate))) + .content(getObjectMapper().writeValueAsString(toCreate))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("houseNumber", is("must be greater than 0"))); } @@ -373,9 +344,9 @@ void updateAddressWhenFlatNumberIsZero() throws Exception { AddressDTO saved = createTestAddress(existingAddress, user); signInUser(user); AddressDTO toUpdate = new AddressDTO(0L, "AStreet", 4, 0, "445544", "ACity"); - mockMvc.perform(put("/api/addresses/{addressId}", saved.addressId()) + getMockMvc().perform(put("/api/addresses/{addressId}", saved.addressId()) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(toUpdate))) + .content(getObjectMapper().writeValueAsString(toUpdate))) .andExpect(status().isOk()) .andExpect(jsonPath("flatNumber", is(0))); signOutUser(); @@ -386,9 +357,9 @@ void updateAddressWhenFlatNumberIsZero() throws Exception { @WithMockUser void updateAddressWhenFlatNumberIsNegative() throws Exception { AddressDTO toCreate = new AddressDTO(0L, "AStreet", 4, -5, "445544", "ACity"); - mockMvc.perform(put("/api/addresses/{addressId}", 1) + getMockMvc().perform(put("/api/addresses/{addressId}", 1) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(toCreate))) + .content(getObjectMapper().writeValueAsString(toCreate))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("flatNumber", is("must be greater than or equal to 0"))); } @@ -398,9 +369,9 @@ void updateAddressWhenFlatNumberIsNegative() throws Exception { @WithMockUser void updateAddressWhenPostalCodeIsTooShort() throws Exception { AddressDTO toCreate = new AddressDTO(0L, "AStreet", 4, 5, "445", "ACity"); - mockMvc.perform(put("/api/addresses/{addressId}", 1) + getMockMvc().perform(put("/api/addresses/{addressId}", 1) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(toCreate))) + .content(getObjectMapper().writeValueAsString(toCreate))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("postalCode", is("Postal code must have at least 6 characters"))); } @@ -410,9 +381,9 @@ void updateAddressWhenPostalCodeIsTooShort() throws Exception { @WithMockUser void updateAddressWhenPostalCodeIsBlank() throws Exception { AddressDTO toCreate = new AddressDTO(0L, "AStreet", 4, 5, "", "ACity"); - mockMvc.perform(put("/api/addresses/{addressId}", 1) + getMockMvc().perform(put("/api/addresses/{addressId}", 1) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(toCreate))) + .content(getObjectMapper().writeValueAsString(toCreate))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("postalCode", is("must not be blank"))); } @@ -422,9 +393,9 @@ void updateAddressWhenPostalCodeIsBlank() throws Exception { @WithMockUser void updateAddressWhenCtyNameIsTooShort() throws Exception { AddressDTO toCreate = new AddressDTO(0L, "AStreet", 4, 5, "445445", "Ci"); - mockMvc.perform(put("/api/addresses/{addressId}", 1) + getMockMvc().perform(put("/api/addresses/{addressId}", 1) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(toCreate))) + .content(getObjectMapper().writeValueAsString(toCreate))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("city", is("City name must have at least 3 characters"))); } @@ -434,9 +405,9 @@ void updateAddressWhenCtyNameIsTooShort() throws Exception { @WithMockUser void updateAddressWhenCityNameIsBlank() throws Exception { AddressDTO toCreate = new AddressDTO(0L, "AStreet", 4, 5, "333222", ""); - mockMvc.perform(put("/api/addresses/{addressId}", 1) + getMockMvc().perform(put("/api/addresses/{addressId}", 1) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(toCreate))) + .content(getObjectMapper().writeValueAsString(toCreate))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("city", is("must not be blank"))); } diff --git a/src/test/java/dg/shop/server/controller/AuthControllerTest.java b/src/test/java/dg/shop/server/controller/AuthControllerTest.java index 30ddb79..0446924 100644 --- a/src/test/java/dg/shop/server/controller/AuthControllerTest.java +++ b/src/test/java/dg/shop/server/controller/AuthControllerTest.java @@ -1,6 +1,5 @@ package dg.shop.server.controller; -import com.fasterxml.jackson.databind.ObjectMapper; import dg.shop.server.model.Role; import dg.shop.server.model.User; import dg.shop.server.model.UserRole; @@ -35,7 +34,6 @@ import static org.hamcrest.Matchers.notNullValue; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; -import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; @@ -43,12 +41,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -@SpringBootTest -@Transactional -public class AuthControllerTest { - - @Autowired - private WebApplicationContext context; +public class AuthControllerTest extends BaseControllerTest{ @Autowired private UserRepository userRepository; @@ -65,18 +58,6 @@ public class AuthControllerTest { @Value("${dg.shop.server.jwt.cookie.name}") private String cookieName; - private MockMvc mockMvc; - private ObjectMapper objectMapper; - - @BeforeEach - void setUp() { - mockMvc = MockMvcBuilders - .webAppContextSetup(context) - .apply(springSecurity()) - .build(); - objectMapper = new ObjectMapper(); - } - @Test @DisplayName("[POST] User Sign In - user exists and signed in properly") void userSignInWhenUserSignedInProperly() throws Exception { @@ -85,9 +66,9 @@ void userSignInWhenUserSignedInProperly() throws Exception { userRepository.save(user); LoginRequest loginRequest = new LoginRequest("it_user", "it_user_10"); - mockMvc.perform(post("/api/auth/signin") + getMockMvc().perform(post("/api/auth/signin") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(loginRequest))) + .content(getObjectMapper().writeValueAsString(loginRequest))) .andExpect(status().isOk()) .andExpect(jsonPath("username", is("it_user"))) .andExpect(jsonPath("$.roles", hasSize(1))) @@ -104,9 +85,9 @@ void userSignInWhenUserUsedWrongCredentials() throws Exception { userRepository.save(user); LoginRequest loginRequest = new LoginRequest("it_user", "wrong_password"); - mockMvc.perform(post("/api/auth/signin") + getMockMvc().perform(post("/api/auth/signin") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(loginRequest))) + .content(getObjectMapper().writeValueAsString(loginRequest))) .andExpect(status().isNotFound()) .andExpect(jsonPath("message", is("Bad credentials"))) .andExpect(jsonPath("status", is(false))); @@ -120,9 +101,9 @@ void userSignInWhenUserUsedOnlyPassword() throws Exception { userRepository.save(user); LoginRequest loginRequest = new LoginRequest("", "wrong_password"); - mockMvc.perform(post("/api/auth/signin") + getMockMvc().perform(post("/api/auth/signin") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(loginRequest))) + .content(getObjectMapper().writeValueAsString(loginRequest))) .andExpect(status().isBadRequest()); } @@ -134,18 +115,18 @@ void userSignInWhenUserUsedOnlyLogin() throws Exception { userRepository.save(user); LoginRequest loginRequest = new LoginRequest("it_user", ""); - mockMvc.perform(post("/api/auth/signin") + getMockMvc().perform(post("/api/auth/signin") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(loginRequest))) + .content(getObjectMapper().writeValueAsString(loginRequest))) .andExpect(status().isBadRequest()); } @Test @DisplayName("[POST] User Sign In - empty payload") void userSignInWhenPayloadIsEmpty() throws Exception { - mockMvc.perform(post("/api/auth/signin") + getMockMvc().perform(post("/api/auth/signin") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(null))) + .content(getObjectMapper().writeValueAsString(null))) .andExpect(status().isBadRequest()); } @@ -156,14 +137,14 @@ void userSignUpWhenUserSignedUpProperlyWithGivenRoles() throws Exception { roleRepository.save(new Role(UserRole.ROLE_SELLER)); roleRepository.save(new Role(UserRole.ROLE_ADMIN)); - SignupRequest signupRequest = new SignupRequest("it_admin", "it_admin10", "itadmin@dgshop.pl", Set.of("user", "seller", "admin")); - mockMvc.perform(post("/api/auth/signup") + SignupRequest signupRequest = new SignupRequest("adminuser", "it_admin10", "itadmin@dgshop.pl", Set.of("user", "seller", "admin")); + getMockMvc().perform(post("/api/auth/signup") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(signupRequest))) + .content(getObjectMapper().writeValueAsString(signupRequest))) .andExpect(status().isOk()) .andExpect(jsonPath("message", is("User registered successfully"))); - Optional user = userRepository.findByUsername("it_admin"); + Optional user = userRepository.findByUsername("adminuser"); assertEquals(3, user.get().getRoles().size()); } @@ -172,23 +153,23 @@ void userSignUpWhenUserSignedUpProperlyWithGivenRoles() throws Exception { void userSignUpWhenUserSignedUpProperlyWithUserRole() throws Exception { roleRepository.save(new Role(UserRole.ROLE_USER)); - SignupRequest signupRequest = new SignupRequest("it_user", "it_user_10", "ituser@dgshop.pl", null); - mockMvc.perform(post("/api/auth/signup") + SignupRequest signupRequest = new SignupRequest("testuser", "it_user_10", "ituser@dgshop.pl", null); + getMockMvc().perform(post("/api/auth/signup") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(signupRequest))) + .content(getObjectMapper().writeValueAsString(signupRequest))) .andExpect(status().isOk()) .andExpect(jsonPath("message", is("User registered successfully"))); - Optional user = userRepository.findByUsername("it_user"); + Optional user = userRepository.findByUsername("testuser"); assertEquals(1, user.get().getRoles().size()); } @Test @DisplayName("[POST] User Sign Up - empty payload") void userSignUpWhenPayloadIsEmpty() throws Exception { - mockMvc.perform(post("/api/auth/signup") + getMockMvc().perform(post("/api/auth/signup") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(null))) + .content(getObjectMapper().writeValueAsString(null))) .andExpect(status().isBadRequest()); } @@ -202,9 +183,9 @@ void userSignUpWhenUserWithGivenNameAlreadyExists() throws Exception { userRepository.save(user); SignupRequest signupRequest = new SignupRequest("it_user", "it_admin10", "itadmin@dgshop.pl", null); - mockMvc.perform(post("/api/auth/signup") + getMockMvc().perform(post("/api/auth/signup") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(signupRequest))) + .content(getObjectMapper().writeValueAsString(signupRequest))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("message", is("Error: Username is already taken"))); } @@ -219,9 +200,9 @@ void userSignUpWhenUserWithGivenEmailAlreadyExists() throws Exception { userRepository.save(user); SignupRequest signupRequest = new SignupRequest("userWithExistingEmail", "it_admin10", "ituser@test.com", null); - mockMvc.perform(post("/api/auth/signup") + getMockMvc().perform(post("/api/auth/signup") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(signupRequest))) + .content(getObjectMapper().writeValueAsString(signupRequest))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("message", is("Error: Email is already taken"))); } @@ -230,9 +211,9 @@ void userSignUpWhenUserWithGivenEmailAlreadyExists() throws Exception { @DisplayName("[POST] User Sign Up - user not exists and signed up properly but role does not exists") void userSignUpWhenUserSignedUpProperlyButRoleDoesNotExists() throws Exception { SignupRequest signupRequest = new SignupRequest("ituser1", "it_admin10", "itadmin@dgshop.pl", Set.of("admin")); - mockMvc.perform(post("/api/auth/signup") + getMockMvc().perform(post("/api/auth/signup") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(signupRequest))) + .content(getObjectMapper().writeValueAsString(signupRequest))) .andExpect(status().isNotFound()) .andExpect(jsonPath("message", is("Role with name: UserRole.ROLE_ADMIN not found"))) .andExpect(jsonPath("status", is(false))); @@ -242,9 +223,9 @@ void userSignUpWhenUserSignedUpProperlyButRoleDoesNotExists() throws Exception { @DisplayName("[POST] User Sign Up - user not exists but used too short username") void userSignUpWhenUserUsedTooShortUsername() throws Exception { SignupRequest signupRequest = new SignupRequest("it", "it_admin10", "itadmin@dgshop.pl", null); - mockMvc.perform(post("/api/auth/signup") + getMockMvc().perform(post("/api/auth/signup") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(signupRequest))) + .content(getObjectMapper().writeValueAsString(signupRequest))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("username", is("size must be between 5 and 30"))); } @@ -253,9 +234,9 @@ void userSignUpWhenUserUsedTooShortUsername() throws Exception { @DisplayName("[POST] User Sign Up - user not exists but used too long username") void userSignUpWhenUserUsedTooLongUsername() throws Exception { SignupRequest signupRequest = new SignupRequest("it_username_is_too_long_for_some_reason", "it_admin10", "itadmin@dgshop.pl", null); - mockMvc.perform(post("/api/auth/signup") + getMockMvc().perform(post("/api/auth/signup") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(signupRequest))) + .content(getObjectMapper().writeValueAsString(signupRequest))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("username", is("size must be between 5 and 30"))); } @@ -264,9 +245,9 @@ void userSignUpWhenUserUsedTooLongUsername() throws Exception { @DisplayName("[POST] User Sign Up - user not exists but username is blank") void userSignUpWhenUsernameIsBlank() throws Exception { SignupRequest signupRequest = new SignupRequest("", "it_admin10", "itadmin@dgshop.pl", null); - mockMvc.perform(post("/api/auth/signup") + getMockMvc().perform(post("/api/auth/signup") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(signupRequest))) + .content(getObjectMapper().writeValueAsString(signupRequest))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("username", is("must not be blank"))); } @@ -275,9 +256,9 @@ void userSignUpWhenUsernameIsBlank() throws Exception { @DisplayName("[POST] User Sign Up - user not exists but used too short password") void userSignUpWhenUserUsedTooShortPassword() throws Exception { SignupRequest signupRequest = new SignupRequest("it_user", "it", "itadmin@dgshop.pl", null); - mockMvc.perform(post("/api/auth/signup") + getMockMvc().perform(post("/api/auth/signup") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(signupRequest))) + .content(getObjectMapper().writeValueAsString(signupRequest))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("password", is("size must be between 10 and 64"))); } @@ -287,9 +268,9 @@ void userSignUpWhenUserUsedTooShortPassword() throws Exception { void userSignUpWhenUserUsedTooLongPassword() throws Exception { SignupRequest signupRequest = new SignupRequest("it_user", "it_admin10it_admin20it_admin30it_admin40it_admin50it_admin60it_admin70", "itadmin@dgshop.pl", null); - mockMvc.perform(post("/api/auth/signup") + getMockMvc().perform(post("/api/auth/signup") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(signupRequest))) + .content(getObjectMapper().writeValueAsString(signupRequest))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("password", is("size must be between 10 and 64"))); } @@ -298,9 +279,9 @@ void userSignUpWhenUserUsedTooLongPassword() throws Exception { @DisplayName("[POST] User Sign Up - user not exists but password is blank") void userSignUpWhenPasswordIsBlank() throws Exception { SignupRequest signupRequest = new SignupRequest("it_admin", "", "itadmin@dgshop.pl", null); - mockMvc.perform(post("/api/auth/signup") + getMockMvc().perform(post("/api/auth/signup") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(signupRequest))) + .content(getObjectMapper().writeValueAsString(signupRequest))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("password", is("must not be blank"))); } @@ -309,9 +290,9 @@ void userSignUpWhenPasswordIsBlank() throws Exception { @DisplayName("[POST] User Sign Up - user not exists but used too short email") void userSignUpWhenUserUsedTooShortEmail() throws Exception { SignupRequest signupRequest = new SignupRequest("it_user", "it", "it@dg.pl", null); - mockMvc.perform(post("/api/auth/signup") + getMockMvc().perform(post("/api/auth/signup") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(signupRequest))) + .content(getObjectMapper().writeValueAsString(signupRequest))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("email", is("size must be between 10 and 50"))); } @@ -321,9 +302,9 @@ void userSignUpWhenUserUsedTooShortEmail() throws Exception { void userSignUpWhenUserUsedTooLongEmail() throws Exception { SignupRequest signupRequest = new SignupRequest("it_user", "it_admin10", "itadmin10_it_admin20it_admin30it_admin40it_admin50@dgshop.pl", null); - mockMvc.perform(post("/api/auth/signup") + getMockMvc().perform(post("/api/auth/signup") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(signupRequest))) + .content(getObjectMapper().writeValueAsString(signupRequest))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("email", is("size must be between 10 and 50"))); } @@ -332,9 +313,9 @@ void userSignUpWhenUserUsedTooLongEmail() throws Exception { @DisplayName("[POST] User Sign Up - user not exists but email is blank") void userSignUpWhenEmailIsBlank() throws Exception { SignupRequest signupRequest = new SignupRequest("it_admin", "it_admin10", "", null); - mockMvc.perform(post("/api/auth/signup") + getMockMvc().perform(post("/api/auth/signup") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(signupRequest))) + .content(getObjectMapper().writeValueAsString(signupRequest))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("email", is("must not be blank"))); } @@ -343,9 +324,9 @@ void userSignUpWhenEmailIsBlank() throws Exception { @DisplayName("[POST] User Sign Up - user not exists but email is not well formed") void userSignUpWhenEmailIsNotWellFormed() throws Exception { SignupRequest signupRequest = new SignupRequest("it_admin", "it_admin10", "it_admin@dg@.x", null); - mockMvc.perform(post("/api/auth/signup") + getMockMvc().perform(post("/api/auth/signup") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(signupRequest))) + .content(getObjectMapper().writeValueAsString(signupRequest))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("email", is("must be a well-formed email address"))); } @@ -361,7 +342,7 @@ void getCurrentlySignedInUsernameWhenUserSignedIn() throws Exception { Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(loginRequest.username(), loginRequest.password())); SecurityContextHolder.getContext().setAuthentication(authentication); - mockMvc.perform(get("/api/auth/username")) + getMockMvc().perform(get("/api/auth/username")) .andExpect(status().isOk()) .andExpect(content().string("it_user")); @@ -372,7 +353,7 @@ void getCurrentlySignedInUsernameWhenUserSignedIn() throws Exception { @Test @DisplayName("[GET] Get username of currently singed in user - no user signed") void getCurrentlySignedInUsernameWhenUserIsNotSignedIn() throws Exception { - mockMvc.perform(get("/api/auth/username")) + getMockMvc().perform(get("/api/auth/username")) .andExpect(status().isOk()) .andExpect(content().string("")); } @@ -380,7 +361,7 @@ void getCurrentlySignedInUsernameWhenUserIsNotSignedIn() throws Exception { @Test @DisplayName("[POST] Sign out user") void signOut() throws Exception { - mockMvc.perform(post("/api/auth/signout")) + getMockMvc().perform(post("/api/auth/signout")) .andExpect(status().isOk()) .andExpect(jsonPath("message", is("You have been signed out."))) .andExpect(cookie().path(cookieName, "/api")) @@ -390,7 +371,7 @@ void signOut() throws Exception { @Test @DisplayName("[GET] Get user details - no user is signed in") void getCurrentlySignedInUserDetailsWhenNoUserIsSignedIn() throws Exception { - mockMvc.perform(get("/api/auth/user")) + getMockMvc().perform(get("/api/auth/user")) .andExpect(status().isBadRequest()) .andExpect(jsonPath("message", is("You need to sign in first."))); } @@ -405,7 +386,7 @@ void getCurrentlySignedInUserDetailsWhenUserIsSignedIn() throws Exception { Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("it_user", "it_user_10")); SecurityContextHolder.getContext().setAuthentication(authentication); - mockMvc.perform(get("/api/auth/user")) + getMockMvc().perform(get("/api/auth/user")) .andExpect(status().isOk()) .andExpect(jsonPath("username", is("it_user"))) .andExpect(jsonPath("$.roles", hasSize(1))) diff --git a/src/test/java/dg/shop/server/controller/BaseControllerTest.java b/src/test/java/dg/shop/server/controller/BaseControllerTest.java new file mode 100644 index 0000000..ef5c8eb --- /dev/null +++ b/src/test/java/dg/shop/server/controller/BaseControllerTest.java @@ -0,0 +1,42 @@ +package dg.shop.server.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.transaction.Transactional; +import lombok.Getter; +import org.flywaydb.core.Flyway; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; + +@SpringBootTest +@Transactional +public class BaseControllerTest { + @Autowired + private WebApplicationContext context; + + @Getter + private MockMvc mockMvc; + + @Getter + private ObjectMapper objectMapper; + + @Autowired + private Flyway flyway; + + @BeforeEach + void setUp() { + mockMvc = MockMvcBuilders + .webAppContextSetup(context) + .apply(springSecurity()) + .build(); + objectMapper = new ObjectMapper(); + + flyway.clean(); + flyway.migrate(); + } +} diff --git a/src/test/java/dg/shop/server/controller/CartControllerTest.java b/src/test/java/dg/shop/server/controller/CartControllerTest.java index cb37b44..962d62b 100644 --- a/src/test/java/dg/shop/server/controller/CartControllerTest.java +++ b/src/test/java/dg/shop/server/controller/CartControllerTest.java @@ -1,6 +1,5 @@ package dg.shop.server.controller; -import com.fasterxml.jackson.databind.ObjectMapper; import dg.shop.server.model.Cart; import dg.shop.server.model.Category; import dg.shop.server.model.Product; @@ -20,28 +19,20 @@ import dg.shop.server.service.category.CategoryService; import dg.shop.server.service.product.ProductService; import dg.shop.server.util.AuthUtils; -import jakarta.transaction.Transactional; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.test.context.support.WithMockUser; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.context.WebApplicationContext; import java.util.List; import java.util.Set; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; -import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; @@ -50,12 +41,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -@SpringBootTest -@Transactional -public class CartControllerTest { - - @Autowired - private WebApplicationContext context; +public class CartControllerTest extends BaseControllerTest{ @Autowired private CartService cartService; @@ -90,18 +76,6 @@ public class CartControllerTest { @Autowired private CartItemService cartItemService; - private MockMvc mockMvc; - private ObjectMapper objectMapper; - - @BeforeEach - void setUp() { - mockMvc = MockMvcBuilders - .webAppContextSetup(context) - .apply(springSecurity()) - .build(); - objectMapper = new ObjectMapper(); - } - @Test @DisplayName("[GET] Get all carts - two carts exists") @WithMockUser @@ -111,7 +85,7 @@ void getAllCartsWhenTwoExists() throws Exception { createCartForUser(itUser); createCartForUser(itUser2); - mockMvc.perform(get("/api/carts")) + getMockMvc().perform(get("/api/carts")) .andExpect(status().isOk()) .andExpect(jsonPath("$", hasSize(2))); } @@ -120,7 +94,7 @@ void getAllCartsWhenTwoExists() throws Exception { @DisplayName("[GET] Get all carts - no carts exist") @WithMockUser void getAllCartsWhenNoCartsExists() throws Exception { - mockMvc.perform(get("/api/carts")) + getMockMvc().perform(get("/api/carts")) .andExpect(status().isOk()) .andExpect(content().json("[]")); } @@ -133,7 +107,7 @@ void getSignedInUserCart() throws Exception { signInUser(itUser); - mockMvc.perform(get("/api/carts/users/cart")) + getMockMvc().perform(get("/api/carts/users/cart")) .andExpect(status().isOk()) .andExpect(content().json(""" {"cartId":%d,"totalPrice":0.0,"products":[]} @@ -148,7 +122,7 @@ void getSignedInUserCartWhenUserCartNotFound() throws Exception { signInUser(itUser); - mockMvc.perform(get("/api/carts/users/cart")) + getMockMvc().perform(get("/api/carts/users/cart")) .andExpect(status().isNotFound()) .andExpect(jsonPath("message", is("Cart with user.email: it_user@test.com not found"))); @@ -167,7 +141,7 @@ void addProductToCartSuccessfully() throws Exception { Product aBook = new Product(0L, "ABook", "abook.png", "A Book", 14, 12.5d, 0.0d, 12.5d, savedCategory, itUser); Product savedBook = productRepository.save(aBook); - mockMvc.perform(post("/api/carts/products/{productId}/quantity/{quantity}", savedBook.getId(), 3)) + getMockMvc().perform(post("/api/carts/products/{productId}/quantity/{quantity}", savedBook.getId(), 3)) .andExpect(status().isCreated()) .andExpect(jsonPath("$.products", hasSize(1))); @@ -181,7 +155,7 @@ void addProductToCartWhenProductDoesNotExists() throws Exception { signInUser(itUser); - mockMvc.perform(post("/api/carts/products/{productId}/quantity/{quantity}", 44, 3)) + getMockMvc().perform(post("/api/carts/products/{productId}/quantity/{quantity}", 44, 3)) .andExpect(status().isNotFound()) .andExpect(jsonPath("message", is("Product with ID: 44 not found"))); @@ -201,7 +175,7 @@ void addProductToCarWhenCartItemAlreadyExistsInCart() throws Exception { Product savedBook = productRepository.save(aBook); cartService.addProductToCart(savedBook.getId(), 1); - mockMvc.perform(post("/api/carts/products/{productId}/quantity/{quantity}", savedBook.getId(), 3)) + getMockMvc().perform(post("/api/carts/products/{productId}/quantity/{quantity}", savedBook.getId(), 3)) .andExpect(status().isBadRequest()) .andExpect(jsonPath("message", is("Product ABook already exists in the cart."))); @@ -220,7 +194,7 @@ void addProductToCarWhenProductStockIsZero() throws Exception { Product aBook = new Product(0L, "ABook", "abook.png", "A Book", 0, 12.5d, 0.0d, 12.5d, savedCategory, itUser); Product savedBook = productRepository.save(aBook); - mockMvc.perform(post("/api/carts/products/{productId}/quantity/{quantity}", savedBook.getId(), 3)) + getMockMvc().perform(post("/api/carts/products/{productId}/quantity/{quantity}", savedBook.getId(), 3)) .andExpect(status().isBadRequest()) .andExpect(jsonPath("message", is("ABook is not available"))); @@ -239,7 +213,7 @@ void addProductToCarWhenProductStockIsLowerThanRequestedAmount() throws Exceptio Product aBook = new Product(0L, "ABook", "abook.png", "A Book", 1, 12.5d, 0.0d, 12.5d, savedCategory, itUser); Product savedBook = productRepository.save(aBook); - mockMvc.perform(post("/api/carts/products/{productId}/quantity/{quantity}", savedBook.getId(), 2)) + getMockMvc().perform(post("/api/carts/products/{productId}/quantity/{quantity}", savedBook.getId(), 2)) .andExpect(status().isBadRequest()) .andExpect(jsonPath("message", is("Please, make an order of the ABook less than or equal to the available quantity: 1"))); @@ -252,7 +226,7 @@ void updateCartProductWhenCartDoesNotExists() throws Exception { User user = createTestUser("user1"); signInUser(user); - mockMvc.perform(put("/api/cart/products/{productId}/quantity/{operation}", 1, "increase")) + getMockMvc().perform(put("/api/cart/products/{productId}/quantity/{operation}", 1, "increase")) .andExpect(status().isNotFound()) .andExpect(jsonPath("message", is("Cart with email: user1@test.com not found"))); signOutUser(); @@ -266,7 +240,7 @@ void updateCartProductWhenProductDoesNotExists() throws Exception { createCartForUser(user); - mockMvc.perform(put("/api/cart/products/{productId}/quantity/{operation}", 1, "increase")) + getMockMvc().perform(put("/api/cart/products/{productId}/quantity/{operation}", 1, "increase")) .andExpect(status().isNotFound()) .andExpect(jsonPath("message", is("Product with ID: 1 not found"))); signOutUser(); @@ -282,7 +256,7 @@ void updateCartProductWhenCartItemDoesNotExistsInTheCart() throws Exception { CategoryDTO category = categoryService.createCategory(new CategoryDTO("CategoryA")); ProductDTO product = productService.createProduct(new ProductDTO(0L, "Witcher 3", "w3.png", "GOTY 2015", 10, 120.0d, 50.0d, 60.0d), category.id()); - mockMvc.perform(put("/api/cart/products/{productId}/quantity/{operation}", product.id(), "increase")) + getMockMvc().perform(put("/api/cart/products/{productId}/quantity/{operation}", product.id(), "increase")) .andExpect(status().isBadRequest()) .andExpect(jsonPath("message", is("Product Witcher 3 is not available in the cart"))); signOutUser(); @@ -299,7 +273,7 @@ void updateCartProductWhenCartItemExistsInTheCartAndAmountWasIncreased() throws ProductDTO product = productService.createProduct(new ProductDTO(0L, "Witcher 3", "w3.png", "GOTY 2015", 10, 120.0d, 50.0d, 60.0d), category.id()); CartDTO cart = cartService.addProductToCart(product.id(), 1); - mockMvc.perform(put("/api/cart/products/{productId}/quantity/{operation}", product.id(), "increase")) + getMockMvc().perform(put("/api/cart/products/{productId}/quantity/{operation}", product.id(), "increase")) .andExpect(status().isOk()) .andExpect(jsonPath("$.products", hasSize(1))) .andExpect(jsonPath("$.products[0].quantity", is(2))); @@ -319,7 +293,7 @@ void updateCartProductWhenTwoCartItemExistsInTheCartAndAmountOfOneWasIncreased() cartService.addProductToCart(product.id(), 2); cartService.addProductToCart(product2.id(), 1); - mockMvc.perform(put("/api/cart/products/{productId}/quantity/{operation}", product.id(), "increase")) + getMockMvc().perform(put("/api/cart/products/{productId}/quantity/{operation}", product.id(), "increase")) .andExpect(status().isOk()) .andExpect(jsonPath("$.products", hasSize(2))) .andExpect(jsonPath("$.products[0].quantity", is(3))) @@ -338,7 +312,7 @@ void updateCartProductWhenCartItemExistsInTheCartAndAmountWasDecreased() throws ProductDTO product = productService.createProduct(new ProductDTO(0L, "Witcher 3", "w3.png", "GOTY 2015", 10, 120.0d, 50.0d, 60.0d), category.id()); CartDTO cart = cartService.addProductToCart(product.id(), 2); - mockMvc.perform(put("/api/cart/products/{productId}/quantity/{operation}", product.id(), "decrease")) + getMockMvc().perform(put("/api/cart/products/{productId}/quantity/{operation}", product.id(), "decrease")) .andExpect(status().isOk()) .andExpect(jsonPath("$.products", hasSize(1))) .andExpect(jsonPath("$.products[0].quantity", is(1))); @@ -356,7 +330,7 @@ void updateCartProductWhenCartItemExistsInTheCartAndAmountWasDecreasedToZero() t ProductDTO product = productService.createProduct(new ProductDTO(0L, "Witcher 3", "w3.png", "GOTY 2015", 10, 120.0d, 50.0d, 60.0d), category.id()); CartDTO cart = cartService.addProductToCart(product.id(), 1); - mockMvc.perform(put("/api/cart/products/{productId}/quantity/{operation}", product.id(), "decrease")) + getMockMvc().perform(put("/api/cart/products/{productId}/quantity/{operation}", product.id(), "decrease")) .andExpect(status().isOk()) .andExpect(jsonPath("$.products", hasSize(0))); signOutUser(); @@ -375,7 +349,7 @@ void updateCartProductWhenTwoCartItemExistsInTheCartAndAmountOfOneWasDecreasedTo cartService.addProductToCart(product.id(), 2); cartService.addProductToCart(product2.id(), 1); - mockMvc.perform(put("/api/cart/products/{productId}/quantity/{operation}", product2.id(), "decrease")) + getMockMvc().perform(put("/api/cart/products/{productId}/quantity/{operation}", product2.id(), "decrease")) .andExpect(status().isOk()) .andExpect(jsonPath("$.products", hasSize(1))) .andExpect(jsonPath("$.products[0].quantity", is(2))); @@ -393,7 +367,7 @@ void deleteProductFromCartWhenCartItemExistsAndWasSuccessfullyRemoved() throws E ProductDTO product = productService.createProduct(new ProductDTO(0L, "Witcher 3", "w3.png", "GOTY 2015", 10, 120.0d, 50.0d, 60.0d), category.id()); CartDTO cart = cartService.addProductToCart(product.id(), 1); - mockMvc.perform(delete("/api/cart/{cartId}/product/{productId}", cart.cartId(), product.id())) + getMockMvc().perform(delete("/api/cart/{cartId}/product/{productId}", cart.cartId(), product.id())) .andExpect(status().isOk()) .andExpect(content().string("Product Witcher 3 removed from the cart.")); signOutUser(); @@ -405,7 +379,7 @@ void deleteProductFromCartWhenCartDoesNotExists() throws Exception { User user = createTestUser("user1"); signInUser(user); - mockMvc.perform(delete("/api/cart/{cartId}/product/{productId}", 1, 1)) + getMockMvc().perform(delete("/api/cart/{cartId}/product/{productId}", 1, 1)) .andExpect(status().isNotFound()) .andExpect(jsonPath("message", is("Cart with ID: 1 not found"))) .andExpect(jsonPath("status", is(false))); @@ -423,7 +397,7 @@ void deleteProductFromCartWhenCartItemDoesNotExists() throws Exception { ProductDTO product = productService.createProduct(new ProductDTO(0L, "Witcher 3", "w3.png", "GOTY 2015", 10, 120.0d, 50.0d, 60.0d), category.id()); CartDTO cart = cartService.addProductToCart(product.id(), 1); - mockMvc.perform(delete("/api/cart/{cartId}/product/{productId}", cart.cartId(), 1000)) + getMockMvc().perform(delete("/api/cart/{cartId}/product/{productId}", cart.cartId(), 1000)) .andExpect(status().isNotFound()) .andExpect(jsonPath("message", is("Product with ID: 1000 not found"))) .andExpect(jsonPath("status", is(false))); diff --git a/src/test/java/dg/shop/server/controller/CategoryControllerTest.java b/src/test/java/dg/shop/server/controller/CategoryControllerTest.java index dbb442f..83009bf 100644 --- a/src/test/java/dg/shop/server/controller/CategoryControllerTest.java +++ b/src/test/java/dg/shop/server/controller/CategoryControllerTest.java @@ -1,30 +1,22 @@ package dg.shop.server.controller; import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; import dg.shop.server.payload.category.CategoryDTO; import dg.shop.server.payload.category.CategoryResponse; import dg.shop.server.repository.CategoryRepository; import dg.shop.server.repository.UserRepository; import dg.shop.server.service.category.CategoryService; -import jakarta.transaction.Transactional; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.security.test.context.support.WithMockUser; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.context.WebApplicationContext; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; @@ -33,12 +25,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -@SpringBootTest -@Transactional -class CategoryControllerTest { - - @Autowired - private WebApplicationContext context; +class CategoryControllerTest extends BaseControllerTest { @Autowired private CategoryService categoryService; @@ -49,25 +36,13 @@ class CategoryControllerTest { @Autowired private UserRepository userRepository; - private MockMvc mockMvc; - private ObjectMapper objectMapper; - - @BeforeEach - void setUp() { - mockMvc = MockMvcBuilders - .webAppContextSetup(context) - .apply(springSecurity()) - .build(); - objectMapper = new ObjectMapper(); - } - @Test @WithMockUser(username = "it_admin", roles = {"ADMIN"}) @DisplayName("[POST] Create category - category was created") void createCategory() throws Exception { - mockMvc.perform(post("/api/admin/categories") + getMockMvc().perform(post("/api/admin/categories") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(new CategoryDTO("Games")))) + .content(getObjectMapper().writeValueAsString(new CategoryDTO("Games")))) .andExpect(status().isCreated()) .andExpect(jsonPath("name", is("Games"))); } @@ -79,9 +54,9 @@ void createCategoryWhenAlreadyExists() throws Exception { CategoryDTO categoryDTO = new CategoryDTO("Games"); categoryService.createCategory(categoryDTO); - mockMvc.perform(post("/api/admin/categories") + getMockMvc().perform(post("/api/admin/categories") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(categoryDTO))) + .content(getObjectMapper().writeValueAsString(categoryDTO))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("message", is("Category with name '%s' already exists".formatted(categoryDTO.name())))) .andExpect(jsonPath("status", is(false))); @@ -91,9 +66,9 @@ void createCategoryWhenAlreadyExists() throws Exception { @WithMockUser(username = "it_admin", roles = {"ADMIN"}) @DisplayName("[POST] Create category - empty payload") void createCategoryPayloadIsEmpty() throws Exception { - mockMvc.perform(post("/api/admin/categories") + getMockMvc().perform(post("/api/admin/categories") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(null))) + .content(getObjectMapper().writeValueAsString(null))) .andExpect(status().isBadRequest()); } @@ -101,9 +76,9 @@ void createCategoryPayloadIsEmpty() throws Exception { @WithMockUser(username = "it_admin", roles = {"ADMIN"}) @DisplayName(" [POST] Create category - category name is shorter than minimum size") void createCategoryWhenCategoryNameIsShorterThanMinimumSize() throws Exception { - mockMvc.perform(post("/api/admin/categories") + getMockMvc().perform(post("/api/admin/categories") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(new CategoryDTO("AB")))) + .content(getObjectMapper().writeValueAsString(new CategoryDTO("AB")))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("name", is("Category name have to be at least 3 chars long."))); } @@ -112,7 +87,7 @@ void createCategoryWhenCategoryNameIsShorterThanMinimumSize() throws Exception { @WithMockUser("it_user") @DisplayName("[GET] Get all categories - missing request params, using defaults") void getAllCategoriesWhenRequestParamsMissing() throws Exception { - mockMvc.perform(get("/api/public/categories")) + getMockMvc().perform(get("/api/public/categories")) .andExpect(status().isOk()) .andExpect(content().json(""" { @@ -129,7 +104,7 @@ void getAllCategoriesWhenRequestParamsMissing() throws Exception { @WithMockUser("it_user") @DisplayName("[GET] Get all categories - empty DB") void getAllCategoriesWhenEmptyDb() throws Exception { - mockMvc.perform(get("/api/public/categories") + getMockMvc().perform(get("/api/public/categories") .param("pageNumber", "0") .param("pageSize", "1")) .andExpect(status().isOk()) @@ -151,14 +126,14 @@ void getAllCategoriesWhenOneExists() throws Exception { CategoryDTO categoryDTO = new CategoryDTO("Games"); categoryService.createCategory(categoryDTO); - MockHttpServletResponse response = mockMvc.perform(get("/api/public/categories") + MockHttpServletResponse response = getMockMvc().perform(get("/api/public/categories") .param("pageNumber", "0") .param("pageSize", "1")) .andExpect(status().isOk()) .andReturn() .getResponse(); String responseBody = response.getContentAsString(); - CategoryResponse categoryResponse = objectMapper.readValue(responseBody, new TypeReference<>() { + CategoryResponse categoryResponse = getObjectMapper().readValue(responseBody, new TypeReference<>() { }); assertEquals(1, categoryResponse.categories().size()); assertEquals("Games", categoryResponse.categories().get(0).name()); @@ -178,14 +153,14 @@ void getAllCategoriesWhenTenExists() throws Exception { categoryService.createCategory(categoryDTO); } - MockHttpServletResponse response = mockMvc.perform(get("/api/public/categories") + MockHttpServletResponse response = getMockMvc().perform(get("/api/public/categories") .param("pageNumber", "1") .param("pageSize", "3")) .andExpect(status().isOk()) .andReturn() .getResponse(); String responseBody = response.getContentAsString(); - CategoryResponse categoryResponse = objectMapper.readValue(responseBody, new TypeReference<>() { + CategoryResponse categoryResponse = getObjectMapper().readValue(responseBody, new TypeReference<>() { }); assertEquals(3, categoryResponse.categories().size()); assertEquals(1, categoryResponse.pageNumber()); @@ -204,7 +179,7 @@ void getAllCategoriesWhenTenExistsAndWasSortedInDescendingOrder() throws Excepti categoryService.createCategory(categoryDTO); } - MockHttpServletResponse response = mockMvc.perform(get("/api/public/categories") + MockHttpServletResponse response = getMockMvc().perform(get("/api/public/categories") .param("pageNumber", "0") .param("pageSize", "5") .param("sortOrder", "desc")) @@ -212,7 +187,7 @@ void getAllCategoriesWhenTenExistsAndWasSortedInDescendingOrder() throws Excepti .andReturn() .getResponse(); String responseBody = response.getContentAsString(); - CategoryResponse categoryResponse = objectMapper.readValue(responseBody, new TypeReference<>() { + CategoryResponse categoryResponse = getObjectMapper().readValue(responseBody, new TypeReference<>() { }); assertEquals(5, categoryResponse.categories().size()); assertEquals(0, categoryResponse.pageNumber()); @@ -226,7 +201,7 @@ void getAllCategoriesWhenTenExistsAndWasSortedInDescendingOrder() throws Excepti @WithMockUser(username = "it_admin", roles = {"ADMIN"}) @DisplayName("[DELETE] Delete category - category does not exists") void deleteCategoryWhenDoesNotExists() throws Exception { - mockMvc.perform(delete("/api/admin/categories/1")) + getMockMvc().perform(delete("/api/admin/categories/1")) .andExpect(status().isNotFound()) .andExpect(jsonPath("message", is("Category with id: %d not found".formatted(1L)))) .andExpect(jsonPath("status", is(false))); @@ -237,7 +212,7 @@ void deleteCategoryWhenDoesNotExists() throws Exception { @DisplayName("[DELETE] Delete category - category exists") void deleteCategoryWhenOneExists() throws Exception { CategoryDTO categoryDTO = categoryService.createCategory(new CategoryDTO("Music")); - mockMvc.perform(delete("/api/admin/categories/%d".formatted(categoryDTO.id()))) + getMockMvc().perform(delete("/api/admin/categories/%d".formatted(categoryDTO.id()))) .andExpect(status().isOk()) .andExpect(jsonPath("name", is("Music"))); } @@ -246,9 +221,9 @@ void deleteCategoryWhenOneExists() throws Exception { @WithMockUser(username = "it_admin", roles = {"ADMIN"}) @DisplayName("[PUT] Update category - category does not exists") void updateCategoryWhenDoesNotExists() throws Exception { - mockMvc.perform(put("/api/admin/categories/1") + getMockMvc().perform(put("/api/admin/categories/1") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(new CategoryDTO("Games")))) + .content(getObjectMapper().writeValueAsString(new CategoryDTO("Games")))) .andExpect(status().isNotFound()) .andExpect(jsonPath("message", is("Category with id: %d not found".formatted(1L)))) .andExpect(jsonPath("status", is(false))); @@ -259,9 +234,9 @@ void updateCategoryWhenDoesNotExists() throws Exception { @DisplayName("[PUT] Update category - category exists") void updateCategoryWhenOneExists() throws Exception { CategoryDTO categoryDTO = categoryService.createCategory(new CategoryDTO("Comics")); - mockMvc.perform(put("/api/admin/categories/{categoryId}", categoryDTO.id()) + getMockMvc().perform(put("/api/admin/categories/{categoryId}", categoryDTO.id()) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(new CategoryDTO("Games")))) + .content(getObjectMapper().writeValueAsString(new CategoryDTO("Games")))) .andExpect(status().isOk()) .andExpect(jsonPath("name", is("Games"))); } @@ -272,9 +247,9 @@ void updateCategoryWhenOneExists() throws Exception { void updateCategoryWhenOneExistsAndHasTheSameName() throws Exception { CategoryDTO categoryDTO = new CategoryDTO("Comics"); CategoryDTO savedDto = categoryService.createCategory(categoryDTO); - mockMvc.perform(put("/api/admin/categories/{categoryId}", savedDto.id()) + getMockMvc().perform(put("/api/admin/categories/{categoryId}", savedDto.id()) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(savedDto))) + .content(getObjectMapper().writeValueAsString(savedDto))) .andExpect(status().isOk()) .andExpect(jsonPath("name", is("Comics"))); } @@ -284,9 +259,9 @@ void updateCategoryWhenOneExistsAndHasTheSameName() throws Exception { @DisplayName("[PUT] Update category - empty payload") void updateCategoryWhenPayloadIsEmpty() throws Exception { CategoryDTO categoryDTO = categoryService.createCategory(new CategoryDTO("Comics")); - mockMvc.perform(put("/api/admin/categories/{categoryId}", categoryDTO.id()) + getMockMvc().perform(put("/api/admin/categories/{categoryId}", categoryDTO.id()) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(null))) + .content(getObjectMapper().writeValueAsString(null))) .andExpect(status().isBadRequest()); } @@ -295,9 +270,9 @@ void updateCategoryWhenPayloadIsEmpty() throws Exception { @DisplayName("[PUT] Update category - category name is shorter than minimum value") void updateCategoryWhenCategoryNameIsShorterThanMinimumSize() throws Exception { CategoryDTO categoryDTO = categoryService.createCategory(new CategoryDTO("Comics")); - mockMvc.perform(put("/api/admin/categories/{categoryId}", categoryDTO.id()) + getMockMvc().perform(put("/api/admin/categories/{categoryId}", categoryDTO.id()) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(new CategoryDTO("AB")))) + .content(getObjectMapper().writeValueAsString(new CategoryDTO("AB")))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("name", is("Category name have to be at least 3 chars long."))); } diff --git a/src/test/java/dg/shop/server/controller/OrderControllerTest.java b/src/test/java/dg/shop/server/controller/OrderControllerTest.java index 6e79d79..d5118c0 100644 --- a/src/test/java/dg/shop/server/controller/OrderControllerTest.java +++ b/src/test/java/dg/shop/server/controller/OrderControllerTest.java @@ -1,8 +1,6 @@ package dg.shop.server.controller; -import com.fasterxml.jackson.databind.ObjectMapper; import dg.shop.server.model.Cart; -import dg.shop.server.model.Product; import dg.shop.server.model.Role; import dg.shop.server.model.User; import dg.shop.server.model.UserRole; @@ -24,20 +22,14 @@ import dg.shop.server.service.order.OrderService; import dg.shop.server.service.product.ProductService; import dg.shop.server.util.AuthUtils; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.context.WebApplicationContext; import java.util.List; import java.util.Set; @@ -46,16 +38,11 @@ import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -@SpringBootTest -class OrderControllerTest { - - @Autowired - private WebApplicationContext context; +class OrderControllerTest extends BaseControllerTest { @Autowired private OrderService orderService; @@ -99,33 +86,6 @@ class OrderControllerTest { @Autowired private PaymentRepository paymentRepository; - private MockMvc mockMvc; - private ObjectMapper objectMapper; - - @BeforeEach - void setUp() { - mockMvc = MockMvcBuilders - .webAppContextSetup(context) - .apply(springSecurity()) - .build(); - objectMapper = new ObjectMapper(); - } - - @AfterEach - void tearDown() { - //TODO clear db - System.out.println("Users: " + userRepository.findAll().size()); - System.out.println("Roles: " + roleRepository.findAll().size()); - System.out.println("Payments: " + paymentRepository.findAll().size()); - System.out.println("Addresses: " + addressService.getRepository().findAll().size()); - System.out.println("Order Items: " + orderItemRepository.findAll().size()); - System.out.println("Orders: " + orderService.getRepository().findAll().size()); - System.out.println("Products: " + productRepository.findAll().size()); - System.out.println("Carts: " + cartService.getRepository().findAll().size()); - System.out.println("Cart Items: " + cartItemService.getRepository().findAll().size()); - System.out.println("Categories: " + categoryRepository.findAll().size()); - } - @Test @DisplayName("[POST] Place order - success") void testPlaceOrderSuccess() throws Exception { @@ -138,9 +98,9 @@ void testPlaceOrderSuccess() throws Exception { assertEquals(1, cartService.getAllCarts().size()); OrderRequestDTO orderRequestDTO = new OrderRequestDTO(address.addressId(), "card", "PayU", "uid_VEF43FD45GXR56R", "success", "Payment Completed"); - mockMvc.perform(post("/api/order/users/payments/{paymentMethod}", "card") + getMockMvc().perform(post("/api/order/users/payments/{paymentMethod}", "card") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(orderRequestDTO))) + .content(getObjectMapper().writeValueAsString(orderRequestDTO))) .andExpect(status().isCreated()) .andExpect(jsonPath("$.email", is("admin@test.com"))) .andExpect(jsonPath("$.orderedItems", hasSize(1))) diff --git a/src/test/java/dg/shop/server/controller/ProductControllerTest.java b/src/test/java/dg/shop/server/controller/ProductControllerTest.java index e8c582b..f445cf2 100644 --- a/src/test/java/dg/shop/server/controller/ProductControllerTest.java +++ b/src/test/java/dg/shop/server/controller/ProductControllerTest.java @@ -1,15 +1,12 @@ package dg.shop.server.controller; import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; import dg.shop.server.payload.category.CategoryDTO; import dg.shop.server.payload.product.ProductDTO; import dg.shop.server.payload.product.ProductResponse; import dg.shop.server.service.category.CategoryService; import dg.shop.server.service.product.ProductService; -import org.flywaydb.core.Flyway; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -20,9 +17,6 @@ import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockMultipartFile; import org.springframework.security.test.context.support.WithMockUser; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.context.WebApplicationContext; import java.io.File; import java.io.InputStream; @@ -31,7 +25,6 @@ import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart; @@ -42,10 +35,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @SpringBootTest(properties = {"dir.product.images=src/test/resources/imagesfortests"}) -class ProductControllerTest { - - @Autowired - private WebApplicationContext context; +class ProductControllerTest extends BaseControllerTest { @Autowired private ProductService productService; @@ -53,25 +43,9 @@ class ProductControllerTest { @Autowired private CategoryService categoryService; - private MockMvc mockMvc; - private ObjectMapper objectMapper; - @Value("${dir.product.images}") private String path; - @BeforeEach - void setUp() { - mockMvc = MockMvcBuilders - .webAppContextSetup(context) - .apply(springSecurity()) - .build(); - objectMapper = new ObjectMapper(); - - Flyway flyway = context.getBean(Flyway.class); - flyway.clean(); - flyway.migrate(); - } - @AfterEach void tearDown() { cleanupTestFiles(); @@ -93,9 +67,9 @@ void createProductWhenCategoryExists() throws Exception { CategoryDTO categoryDTO = categoryService.createCategory(new CategoryDTO("Games")); ProductDTO productDTO = new ProductDTO(0L, "Witcher 3", "w3.png", "GOTY 2015", 450, 50.0d, 15.0d, 0.0d); - mockMvc.perform(post("/api/admin/categories/{categoryId}/product", categoryDTO.id()) + getMockMvc().perform(post("/api/admin/categories/{categoryId}/product", categoryDTO.id()) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(productDTO))) + .content(getObjectMapper().writeValueAsString(productDTO))) .andExpect(status().isCreated()) .andExpect(jsonPath("name", is("Witcher 3"))) .andExpect(jsonPath("quantity", is(450))) @@ -111,9 +85,9 @@ void createProductWhenCategoryExists() throws Exception { void createProductWhenCategoryDoesNotExists() throws Exception { ProductDTO productDTO = new ProductDTO(0L, "Witcher 3", "w3.png", "GOTY 2015", 450, 50.0d, 15.0d, 0.0d); - mockMvc.perform(post("/api/admin/categories/{categoryId}/product", 1) + getMockMvc().perform(post("/api/admin/categories/{categoryId}/product", 1) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(productDTO))) + .content(getObjectMapper().writeValueAsString(productDTO))) .andExpect(status().isNotFound()) .andExpect(jsonPath("message", is("Category with id: 1 not found"))) .andExpect(jsonPath("status", is(false))); @@ -127,9 +101,9 @@ void createProductWhenProductWithSameNameAlreadyExists() throws Exception { ProductDTO productDTO = new ProductDTO(0L, "Witcher 3", "w3.png", "GOTY 2015", 450, 50.0d, 15.0d, 0.0d); productService.createProduct(productDTO, categoryDTO.id()); - mockMvc.perform(post("/api/admin/categories/{categoryId}/product", categoryDTO.id()) + getMockMvc().perform(post("/api/admin/categories/{categoryId}/product", categoryDTO.id()) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(productDTO))) + .content(getObjectMapper().writeValueAsString(productDTO))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("message", is("Product with name: Witcher 3 already exists"))); } @@ -141,9 +115,9 @@ void createProductWhenProductNameHasLessThan3Characters() throws Exception { CategoryDTO categoryDTO = categoryService.createCategory(new CategoryDTO("Games")); ProductDTO productDTO = new ProductDTO(0L, "AB", "w3.png", "GOTY 2015", 450, 50.0d, 15.0d, 0.0d); - mockMvc.perform(post("/api/admin/categories/{categoryId}/product", categoryDTO.id()) + getMockMvc().perform(post("/api/admin/categories/{categoryId}/product", categoryDTO.id()) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(productDTO))) + .content(getObjectMapper().writeValueAsString(productDTO))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("name", is("size must be between 3 and 100"))); } @@ -157,9 +131,9 @@ void createProductWhenProductNameHasMoreThan100Characters() throws Exception { new ProductDTO(0L, "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ", "w3.png", "GOTY 2015", 450, 50.0d, 15.0d, 0.0d); - mockMvc.perform(post("/api/admin/categories/{categoryId}/product", categoryDTO.id()) + getMockMvc().perform(post("/api/admin/categories/{categoryId}/product", categoryDTO.id()) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(productDTO))) + .content(getObjectMapper().writeValueAsString(productDTO))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("name", is("size must be between 3 and 100"))); } @@ -171,9 +145,9 @@ void createProductWhenProductImageIsNotSpecified() throws Exception { CategoryDTO categoryDTO = categoryService.createCategory(new CategoryDTO("Games")); ProductDTO productDTO = new ProductDTO(0L, "Witcher 3", null, "GOTY 2015", 450, 50.0d, 15.0d, 0.0d); - mockMvc.perform(post("/api/admin/categories/{categoryId}/product", categoryDTO.id()) + getMockMvc().perform(post("/api/admin/categories/{categoryId}/product", categoryDTO.id()) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(productDTO))) + .content(getObjectMapper().writeValueAsString(productDTO))) .andExpect(status().isCreated()) .andExpect(jsonPath("image", is("placeholder.png"))); } @@ -186,9 +160,9 @@ void createProductWhenProductDescriptionHasLessThan6Characters() throws Exceptio ProductDTO productDTO = new ProductDTO(0L, "Witcher 3", "w3.png", "GOTY", 450, 50.0d, 15.0d, 0.0d); - mockMvc.perform(post("/api/admin/categories/{categoryId}/product", categoryDTO.id()) + getMockMvc().perform(post("/api/admin/categories/{categoryId}/product", categoryDTO.id()) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(productDTO))) + .content(getObjectMapper().writeValueAsString(productDTO))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("description", is("size must be between 6 and 200"))); } @@ -203,9 +177,9 @@ void createProductWhenProductDescriptionHasMoreThan200Characters() throws Except "OPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ", 450, 50.0d, 15.0d, 0.0d); - mockMvc.perform(post("/api/admin/categories/{categoryId}/product", categoryDTO.id()) + getMockMvc().perform(post("/api/admin/categories/{categoryId}/product", categoryDTO.id()) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(productDTO))) + .content(getObjectMapper().writeValueAsString(productDTO))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("description", is("size must be between 6 and 200"))); } @@ -217,9 +191,9 @@ void createProductWhenProductQuantityIsZero() throws Exception { CategoryDTO categoryDTO = categoryService.createCategory(new CategoryDTO("Games")); ProductDTO productDTO = new ProductDTO(0L, "Witcher 3", "w3.png", "GOTY 2015", 0, 50.0d, 20.0d, 0.0d); - mockMvc.perform(post("/api/admin/categories/{categoryId}/product", categoryDTO.id()) + getMockMvc().perform(post("/api/admin/categories/{categoryId}/product", categoryDTO.id()) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(productDTO))) + .content(getObjectMapper().writeValueAsString(productDTO))) .andExpect(status().isCreated()) .andExpect(jsonPath("name", is("Witcher 3"))) .andExpect(jsonPath("quantity", is(0))); @@ -232,9 +206,9 @@ void createProductWhenProductQuantityIsNegative() throws Exception { CategoryDTO categoryDTO = categoryService.createCategory(new CategoryDTO("Games")); ProductDTO productDTO = new ProductDTO(0L, "Witcher 3", "w3.png", "GOTY 2015", -10, 50.0d, 20.0d, 0.0d); - mockMvc.perform(post("/api/admin/categories/{categoryId}/product", categoryDTO.id()) + getMockMvc().perform(post("/api/admin/categories/{categoryId}/product", categoryDTO.id()) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(productDTO))) + .content(getObjectMapper().writeValueAsString(productDTO))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("quantity", is("must be greater than or equal to 0"))); } @@ -246,9 +220,9 @@ void createProductWhenProductPriceIsZero() throws Exception { CategoryDTO categoryDTO = categoryService.createCategory(new CategoryDTO("Games")); ProductDTO productDTO = new ProductDTO(0L, "Witcher 3", "w3.png", "GOTY 2015", 10, 0.0d, 20.0d, 0.0d); - mockMvc.perform(post("/api/admin/categories/{categoryId}/product", categoryDTO.id()) + getMockMvc().perform(post("/api/admin/categories/{categoryId}/product", categoryDTO.id()) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(productDTO))) + .content(getObjectMapper().writeValueAsString(productDTO))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("price", is("must be greater than 0"))); } @@ -260,9 +234,9 @@ void createProductWhenProductDiscountIsZero() throws Exception { CategoryDTO categoryDTO = categoryService.createCategory(new CategoryDTO("Games")); ProductDTO productDTO = new ProductDTO(0L, "Witcher 3", "w3.png", "GOTY 2015", 10, 45.0d, 0.0d, 0.0d); - mockMvc.perform(post("/api/admin/categories/{categoryId}/product", categoryDTO.id()) + getMockMvc().perform(post("/api/admin/categories/{categoryId}/product", categoryDTO.id()) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(productDTO))) + .content(getObjectMapper().writeValueAsString(productDTO))) .andExpect(status().isCreated()) .andExpect(jsonPath("price", is(45.0d))) .andExpect(jsonPath("discount", is(0.0d))) @@ -276,9 +250,9 @@ void createProductWhenProductDiscountIsNegative() throws Exception { CategoryDTO categoryDTO = categoryService.createCategory(new CategoryDTO("Games")); ProductDTO productDTO = new ProductDTO(0L, "Witcher 3", "w3.png", "GOTY 2015", 10, 50.0d, -20.0d, 0.0d); - mockMvc.perform(post("/api/admin/categories/{categoryId}/product", categoryDTO.id()) + getMockMvc().perform(post("/api/admin/categories/{categoryId}/product", categoryDTO.id()) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(productDTO))) + .content(getObjectMapper().writeValueAsString(productDTO))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("discount", is("must be greater than or equal to 0"))); } @@ -290,9 +264,9 @@ void createProductWhenProductSpecialIsNegative() throws Exception { CategoryDTO categoryDTO = categoryService.createCategory(new CategoryDTO("Games")); ProductDTO productDTO = new ProductDTO(0L, "Witcher 3", "w3.png", "GOTY 2015", 10, 50.0d, 20.0d, -70.0d); - mockMvc.perform(post("/api/admin/categories/{categoryId}/product", categoryDTO.id()) + getMockMvc().perform(post("/api/admin/categories/{categoryId}/product", categoryDTO.id()) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(productDTO))) + .content(getObjectMapper().writeValueAsString(productDTO))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("specialPrice", is("must be greater than or equal to 0"))); } @@ -301,7 +275,7 @@ void createProductWhenProductSpecialIsNegative() throws Exception { @WithMockUser(username = "it_user") @DisplayName("[GET] Get all products - db is empty and default params were used") void getAllProductsWhenDBIsEmptyUsingDefaultParams() throws Exception { - mockMvc.perform(get("/api/public/products")) + getMockMvc().perform(get("/api/public/products")) .andExpect(status().isOk()) .andExpect(content().json(""" { @@ -318,7 +292,7 @@ void getAllProductsWhenDBIsEmptyUsingDefaultParams() throws Exception { @WithMockUser(username = "it_user") @DisplayName("[GET] Get all products - db is empty and 'pageSize' and 'sortBy' params were used") void getAllProductsWhenDBIsEmptyUsingPageSizeAndSortByParams() throws Exception { - mockMvc.perform(get("/api/public/products") + getMockMvc().perform(get("/api/public/products") .param("pageSize", "4") .param("sortBy", "name")) .andExpect(status().isOk()) @@ -344,14 +318,14 @@ void getAllProductsWhenTwoProductsExistsUsingDefaultParams() throws Exception { ProductDTO productDTO2 = new ProductDTO(0L, "Amnesia", "amnesia.png", "Scary horror", 50, 50.0d, 25.0d, 0.0d); productService.createProduct(productDTO2, categoryDTO.id()); - MockHttpServletResponse response = mockMvc.perform(get("/api/public/products")) + MockHttpServletResponse response = getMockMvc().perform(get("/api/public/products")) .andExpect(status().isOk()) .andExpect(jsonPath("pageSize", is(50))) .andExpect(jsonPath("totalElements", is(2))) .andReturn() .getResponse(); String responseBody = response.getContentAsString(); - ProductResponse productResponse = objectMapper.readValue(responseBody, new TypeReference<>() { + ProductResponse productResponse = getObjectMapper().readValue(responseBody, new TypeReference<>() { }); assertEquals(2, productResponse.products().size()); } @@ -367,7 +341,7 @@ void getAllProductsWhenTwoProductsExistsUsingPageSizeParam() throws Exception { ProductDTO productDTO2 = new ProductDTO(0L, "Amnesia", "amnesia.png", "Scary horror", 50, 50.0d, 25.0d, 0.0d); productService.createProduct(productDTO2, categoryDTO.id()); - MockHttpServletResponse response = mockMvc.perform(get("/api/public/products") + MockHttpServletResponse response = getMockMvc().perform(get("/api/public/products") .param("pageSize", "1")) .andExpect(status().isOk()) .andExpect(jsonPath("pageSize", is(1))) @@ -377,7 +351,7 @@ void getAllProductsWhenTwoProductsExistsUsingPageSizeParam() throws Exception { .andReturn() .getResponse(); String responseBody = response.getContentAsString(); - ProductResponse productResponse = objectMapper.readValue(responseBody, new TypeReference<>() { + ProductResponse productResponse = getObjectMapper().readValue(responseBody, new TypeReference<>() { }); assertEquals(1, productResponse.products().size()); } @@ -393,7 +367,7 @@ void getAllProductsWhenTwoProductsExistsUsingPageSizeAndPageNumberParams() throw ProductDTO productDTO2 = new ProductDTO(0L, "Amnesia", "amnesia.png", "Scary horror", 50, 50.0d, 25.0d, 0.0d); productService.createProduct(productDTO2, categoryDTO.id()); - MockHttpServletResponse response = mockMvc.perform(get("/api/public/products") + MockHttpServletResponse response = getMockMvc().perform(get("/api/public/products") .param("pageSize", "1") .param("pageNumber", "1")) .andExpect(status().isOk()) @@ -404,7 +378,7 @@ void getAllProductsWhenTwoProductsExistsUsingPageSizeAndPageNumberParams() throw .andReturn() .getResponse(); String responseBody = response.getContentAsString(); - ProductResponse productResponse = objectMapper.readValue(responseBody, new TypeReference<>() { + ProductResponse productResponse = getObjectMapper().readValue(responseBody, new TypeReference<>() { }); assertEquals(1, productResponse.products().size()); } @@ -420,7 +394,7 @@ void getAllProductsWhenTwoProductsExistsUsingSortByAndSortOrderParams() throws E ProductDTO productDTO2 = new ProductDTO(0L, "Amnesia", "amnesia.png", "Scary horror", 50, 50.0d, 25.0d, 0.0d); productService.createProduct(productDTO2, categoryDTO.id()); - MockHttpServletResponse response = mockMvc.perform(get("/api/public/products") + MockHttpServletResponse response = getMockMvc().perform(get("/api/public/products") .param("sortBy", "name") .param("sortOrder", "desc")) .andExpect(status().isOk()) @@ -431,7 +405,7 @@ void getAllProductsWhenTwoProductsExistsUsingSortByAndSortOrderParams() throws E .andReturn() .getResponse(); String responseBody = response.getContentAsString(); - ProductResponse productResponse = objectMapper.readValue(responseBody, new TypeReference<>() { + ProductResponse productResponse = getObjectMapper().readValue(responseBody, new TypeReference<>() { }); assertEquals(2, productResponse.products().size()); assertEquals("Witcher 3", productResponse.products().getFirst().name()); @@ -442,7 +416,7 @@ void getAllProductsWhenTwoProductsExistsUsingSortByAndSortOrderParams() throws E @WithMockUser(username = "it_user") @DisplayName("[GET] Get all products by category - category does not exists") void getAllProductsByCategoryWhenCategoryDoesNotExists() throws Exception { - mockMvc.perform(get("/api/public/categories/{categoryId}/products", 1)) + getMockMvc().perform(get("/api/public/categories/{categoryId}/products", 1)) .andExpect(status().isNotFound()) .andExpect(jsonPath("message", is("Category with id: 1 not found"))) .andExpect(jsonPath("status", is(false))); @@ -453,7 +427,7 @@ void getAllProductsByCategoryWhenCategoryDoesNotExists() throws Exception { @DisplayName("[GET] Get all products by category - category exists, no products defined") void getAllProductsByCategoryWhenCategoryExistsAndNoProductsDefined() throws Exception { CategoryDTO category = categoryService.createCategory(new CategoryDTO("Games")); - mockMvc.perform(get("/api/public/categories/{categoryId}/products", category.id())) + getMockMvc().perform(get("/api/public/categories/{categoryId}/products", category.id())) .andExpect(status().isOk()) .andExpect(jsonPath("totalElements", is(0))); } @@ -466,7 +440,7 @@ void getAllProductsByCategoryWhenCategoryExistsAndOneProductsDefined() throws Ex ProductDTO productDTO = new ProductDTO(0L, "Witcher 3", "w3.png", "GOTY 2015", 1000, 125.0d, 10.0d, 0.0d); productService.createProduct(productDTO, categoryDTO.id()); - mockMvc.perform(get("/api/public/categories/{categoryId}/products", categoryDTO.id())) + getMockMvc().perform(get("/api/public/categories/{categoryId}/products", categoryDTO.id())) .andExpect(status().isOk()) .andExpect(jsonPath("totalElements", is(1))); } @@ -483,11 +457,11 @@ void getAllProductsByCategoryWhenTwoCategoriesExistsAndEachHasOneProductDefined( ProductDTO productDTO2 = new ProductDTO(0L, "Cyberpunk - Przebudzenie", "cpp.png", "Cyberpunk story", 30, 40.0d, 0.0d, 0.0d); productService.createProduct(productDTO2, categoryDTO2.id()); - mockMvc.perform(get("/api/public/categories/{categoryId}/products", categoryDTO.id())) + getMockMvc().perform(get("/api/public/categories/{categoryId}/products", categoryDTO.id())) .andExpect(status().isOk()) .andExpect(jsonPath("totalElements", is(1))); - mockMvc.perform(get("/api/public/categories/{categoryId}/products", categoryDTO2.id())) + getMockMvc().perform(get("/api/public/categories/{categoryId}/products", categoryDTO2.id())) .andExpect(status().isOk()) .andExpect(jsonPath("totalElements", is(1))); } @@ -504,7 +478,7 @@ void getAllProductsByCategoryWhenTwoCategoriesExistsAndOneHasTwoProductsDefinedA CategoryDTO categoryDTO2 = categoryService.createCategory(new CategoryDTO("Books")); - MockHttpServletResponse response = mockMvc.perform(get("/api/public/categories/{categoryId}/products", categoryDTO.id()) + MockHttpServletResponse response = getMockMvc().perform(get("/api/public/categories/{categoryId}/products", categoryDTO.id()) .param("sortBy", "name") .param("sortOrder", "desc")) .andExpect(status().isOk()) @@ -512,12 +486,12 @@ void getAllProductsByCategoryWhenTwoCategoriesExistsAndOneHasTwoProductsDefinedA .andReturn() .getResponse(); String responseBody = response.getContentAsString(); - ProductResponse productResponse = objectMapper.readValue(responseBody, new TypeReference<>() { + ProductResponse productResponse = getObjectMapper().readValue(responseBody, new TypeReference<>() { }); assertEquals("Witcher 3", productResponse.products().getFirst().name()); assertEquals("Amnesia", productResponse.products().getLast().name()); - mockMvc.perform(get("/api/public/categories/{categoryId}/products", categoryDTO2.id())) + getMockMvc().perform(get("/api/public/categories/{categoryId}/products", categoryDTO2.id())) .andExpect(status().isOk()) .andExpect(jsonPath("totalElements", is(0))); } @@ -526,7 +500,7 @@ void getAllProductsByCategoryWhenTwoCategoriesExistsAndOneHasTwoProductsDefinedA @WithMockUser(username = "it_user") @DisplayName("[GET] Get all products by keyword - no products defined") void getAllProductsByKeywordWhenNoProductsExists() throws Exception { - mockMvc.perform(get("/api/public/products/keyword/{keyword}", "notExists")) + getMockMvc().perform(get("/api/public/products/keyword/{keyword}", "notExists")) .andExpect(status().isOk()) .andExpect(jsonPath("totalElements", is(0))); } @@ -539,7 +513,7 @@ void getAllProductsByKeywordWhenCategoryExistsAndHasOneProductDefinedAndKeywordD ProductDTO productDTO = new ProductDTO(0L, "Witcher 3", "w3.png", "GOTY 2015", 1000, 125.0d, 10.0d, 0.0d); productService.createProduct(productDTO, categoryDTO.id()); - mockMvc.perform(get("/api/public/products/keyword/{keyword}", "cyberpunk")) + getMockMvc().perform(get("/api/public/products/keyword/{keyword}", "cyberpunk")) .andExpect(status().isOk()) .andExpect(jsonPath("totalElements", is(0))); } @@ -552,7 +526,7 @@ void getAllProductsByKeywordWhenCategoryExistsAndHasOneProductDefinedAndKeywordI ProductDTO productDTO = new ProductDTO(0L, "Witcher 3", "w3.png", "GOTY 2015", 1000, 125.0d, 10.0d, 0.0d); productService.createProduct(productDTO, categoryDTO.id()); - mockMvc.perform(get("/api/public/products/keyword")) + getMockMvc().perform(get("/api/public/products/keyword")) .andExpect(status().isOk()) .andExpect(jsonPath("totalElements", is(0))); } @@ -569,11 +543,11 @@ void getAllProductsByKeywordWhenTwoCategoriesExistsAndEachHasOneProductDefined() ProductDTO productDTO2 = new ProductDTO(0L, "Cyberpunk - Przebudzenie", "cpp.png", "Cyberpunk story", 30, 40.0d, 0.0d, 0.0d); productService.createProduct(productDTO2, categoryDTO2.id()); - mockMvc.perform(get("/api/public/products/keyword/{keyword}", "3")) + getMockMvc().perform(get("/api/public/products/keyword/{keyword}", "3")) .andExpect(status().isOk()) .andExpect(jsonPath("totalElements", is(1))); - mockMvc.perform(get("/api/public/products/keyword/{keyword}", "cyberpunk")) + getMockMvc().perform(get("/api/public/products/keyword/{keyword}", "cyberpunk")) .andExpect(status().isOk()) .andExpect(jsonPath("totalElements", is(1))); } @@ -590,7 +564,7 @@ void getAllProductsByKeywordWhenTwoCategoriesExistsAndEachHasOneProductDefinedAn ProductDTO productDTO2 = new ProductDTO(0L, "Wittchen Suitcase", "wittchen.png", "Good suitcase for travel", 10, 400.0d, 10.0d, 0.0d); productService.createProduct(productDTO2, categoryDTO2.id()); - mockMvc.perform(get("/api/public/products/keyword/{keyword}", "wit")) + getMockMvc().perform(get("/api/public/products/keyword/{keyword}", "wit")) .andExpect(status().isOk()) .andExpect(jsonPath("totalElements", is(2))); } @@ -604,9 +578,9 @@ void updateProductWhenProductExists() throws Exception { ProductDTO existingProductDTO = productService.createProduct(productDTO, categoryDTO.id()); ProductDTO updatedDTO = new ProductDTO(existingProductDTO.id(), "Witcher 3: Wild Hunt", "w3.png", "Game of the year 2015", 50, 150.0d, 55.0d, 0.0d); - mockMvc.perform(put("/api/admin/products/{productId}", existingProductDTO.id()) + getMockMvc().perform(put("/api/admin/products/{productId}", existingProductDTO.id()) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(updatedDTO))) + .content(getObjectMapper().writeValueAsString(updatedDTO))) .andExpect(status().isOk()) .andExpect(jsonPath("name", is("Witcher 3: Wild Hunt"))) .andExpect(jsonPath("quantity", is(50))) @@ -621,9 +595,9 @@ void updateProductWhenProductExists() throws Exception { void updateProductWhenProductDoesNotExists() throws Exception { ProductDTO productDTO = new ProductDTO(0L, "Witcher 3", "w3.png", "GOTY 2015", 450, 50.0d, 15.0d, 0.0d); - mockMvc.perform(put("/api/admin/products/{productId}", 1) + getMockMvc().perform(put("/api/admin/products/{productId}", 1) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(productDTO))) + .content(getObjectMapper().writeValueAsString(productDTO))) .andExpect(status().isNotFound()) .andExpect(jsonPath("message", is("Product with ID: 1 not found"))) .andExpect(jsonPath("status", is(false))); @@ -633,9 +607,9 @@ void updateProductWhenProductDoesNotExists() throws Exception { @WithMockUser(username = "it_admin", roles = {"ADMIN"}) @DisplayName("[PUT] Update product - empty payload") void updateProductWhenPayloadIsEmpty() throws Exception { - mockMvc.perform(put("/api/admin/products/{productId}", 1) + getMockMvc().perform(put("/api/admin/products/{productId}", 1) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(null))) + .content(getObjectMapper().writeValueAsString(null))) .andExpect(status().isBadRequest()); } @@ -648,9 +622,9 @@ void updateProductWhenProductWithSameNameAlreadyExistsButDifferentQuantity() thr ProductDTO existingProductDTO = productService.createProduct(productDTO, categoryDTO.id()); ProductDTO updatedDTO = new ProductDTO(existingProductDTO.id(), "Witcher 3", "w3.png", "GOTY 2015", 10, 50.0d, 15.0d, 0.0d); - mockMvc.perform(put("/api/admin/products/{productId}", existingProductDTO.id()) + getMockMvc().perform(put("/api/admin/products/{productId}", existingProductDTO.id()) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(updatedDTO))) + .content(getObjectMapper().writeValueAsString(updatedDTO))) .andExpect(status().isOk()) .andExpect(jsonPath("quantity", is(10))) .andExpect(jsonPath("image", is("w3.png"))) @@ -668,9 +642,9 @@ void updateProductWhenProductWithSameNameDescriptionPriceDiscountAndQuantityAlre ProductDTO existingProductDTO = productService.createProduct(productDTO, categoryDTO.id()); ProductDTO updatedDTO = new ProductDTO(existingProductDTO.id(), "Witcher 3", "w3.png", "GOTY 2015", 450, 50.0d, 15.0d, 0.0d); - mockMvc.perform(put("/api/admin/products/{productId}", existingProductDTO.id()) + getMockMvc().perform(put("/api/admin/products/{productId}", existingProductDTO.id()) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(updatedDTO))) + .content(getObjectMapper().writeValueAsString(updatedDTO))) .andExpect(status().isOk()) .andExpect(jsonPath("name", is("Witcher 3"))) .andExpect(jsonPath("quantity", is(450))) @@ -689,9 +663,9 @@ void updateProductWhenProductNameHasLessThan3Characters() throws Exception { ProductDTO existingPRoductDTO = productService.createProduct(productDTO, categoryDTO.id()); ProductDTO updatedProductDTO = new ProductDTO(existingPRoductDTO.id(), "Wi", "w3.png", "GOTY 2015", 450, 50.0d, 15.0d, 0.0d); - mockMvc.perform(put("/api/admin/products/{productId}", existingPRoductDTO.id()) + getMockMvc().perform(put("/api/admin/products/{productId}", existingPRoductDTO.id()) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(updatedProductDTO))) + .content(getObjectMapper().writeValueAsString(updatedProductDTO))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("name", is("size must be between 3 and 100"))); } @@ -707,9 +681,9 @@ void updateProductWhenProductNameHasMoreThan100Characters() throws Exception { ProductDTO updatedProductDTO = new ProductDTO(existingProductDTO.id(), "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ", "w3.png", "GOTY 2015", 450, 50.0d, 15.0d, 0.0d); - mockMvc.perform(put("/api/admin/products/{productId}", existingProductDTO.id()) + getMockMvc().perform(put("/api/admin/products/{productId}", existingProductDTO.id()) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(updatedProductDTO))) + .content(getObjectMapper().writeValueAsString(updatedProductDTO))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("name", is("size must be between 3 and 100"))); } @@ -725,9 +699,9 @@ void updateProductWhenProductDescriptionHasLessThan6Characters() throws Exceptio ProductDTO updatedProductDTO = new ProductDTO(existingProductDTO.id(), "Witcher 3", "w3.png", "GOTY", 450, 50.0d, 15.0d, 0.0d); - mockMvc.perform(put("/api/admin/products/{productId}", existingProductDTO.id()) + getMockMvc().perform(put("/api/admin/products/{productId}", existingProductDTO.id()) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(updatedProductDTO))) + .content(getObjectMapper().writeValueAsString(updatedProductDTO))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("description", is("size must be between 6 and 200"))); } @@ -745,9 +719,9 @@ void updateProductWhenProductDescriptionHasMoreThan200Characters() throws Except "OPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ", 450, 50.0d, 15.0d, 0.0d); - mockMvc.perform(put("/api/admin/products/{productId}", existingProductDTO.id()) + getMockMvc().perform(put("/api/admin/products/{productId}", existingProductDTO.id()) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(updatedProductDTO))) + .content(getObjectMapper().writeValueAsString(updatedProductDTO))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("description", is("size must be between 6 and 200"))); } @@ -761,9 +735,9 @@ void updateProductWhenProductQuantityIsZero() throws Exception { ProductDTO existingProductDTO = productService.createProduct(productDTO, categoryDTO.id()); ProductDTO updatedProductDTO = new ProductDTO(0L, "Witcher 3", "w3.png", "GOTY 2015", 0, 50.0d, 20.0d, 0.0d); - mockMvc.perform(put("/api/admin/products/{productId}", existingProductDTO.id()) + getMockMvc().perform(put("/api/admin/products/{productId}", existingProductDTO.id()) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(updatedProductDTO))) + .content(getObjectMapper().writeValueAsString(updatedProductDTO))) .andExpect(status().isOk()) .andExpect(jsonPath("name", is("Witcher 3"))) .andExpect(jsonPath("quantity", is(0))); @@ -778,9 +752,9 @@ void updateProductWhenProductQuantityIsNegative() throws Exception { ProductDTO existingProductDTO = productService.createProduct(productDTO, categoryDTO.id()); ProductDTO updatedProductDTO = new ProductDTO(0L, "Witcher 3", "w3.png", "GOTY 2015", -10, 50.0d, 20.0d, 0.0d); - mockMvc.perform(put("/api/admin/products/{productId}", existingProductDTO.id()) + getMockMvc().perform(put("/api/admin/products/{productId}", existingProductDTO.id()) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(updatedProductDTO))) + .content(getObjectMapper().writeValueAsString(updatedProductDTO))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("quantity", is("must be greater than or equal to 0"))); } @@ -794,9 +768,9 @@ void updateProductWhenProductPriceIsZero() throws Exception { ProductDTO existingProductDTO = productService.createProduct(productDTO, categoryDTO.id()); ProductDTO updatedProductDTO = new ProductDTO(0L, "Witcher 3", "w3.png", "GOTY 2015", 10, 0.0d, 20.0d, 0.0d); - mockMvc.perform(put("/api/admin/products/{productId}", existingProductDTO.id()) + getMockMvc().perform(put("/api/admin/products/{productId}", existingProductDTO.id()) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(updatedProductDTO))) + .content(getObjectMapper().writeValueAsString(updatedProductDTO))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("price", is("must be greater than 0"))); } @@ -810,9 +784,9 @@ void updateProductWhenProductDiscountIsZero() throws Exception { ProductDTO existingProductDTO = productService.createProduct(productDTO, categoryDTO.id()); ProductDTO updatedProductDTO = new ProductDTO(0L, "Witcher 3", "w3.png", "GOTY 2015", 10, 45.0d, 0.0d, 0.0d); - mockMvc.perform(put("/api/admin/products/{productId}", existingProductDTO.id()) + getMockMvc().perform(put("/api/admin/products/{productId}", existingProductDTO.id()) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(updatedProductDTO))) + .content(getObjectMapper().writeValueAsString(updatedProductDTO))) .andExpect(status().isOk()) .andExpect(jsonPath("price", is(45.0d))) .andExpect(jsonPath("discount", is(0.0d))) @@ -828,9 +802,9 @@ void updateProductWhenProductDiscountIsNegative() throws Exception { ProductDTO existingPRoductDTO = productService.createProduct(productDTO, categoryDTO.id()); ProductDTO updatedProductDTO = new ProductDTO(0L, "Witcher 3", "w3.png", "GOTY 2015", 10, 50.0d, -20.0d, 0.0d); - mockMvc.perform(put("/api/admin/products/{productId}", existingPRoductDTO.id()) + getMockMvc().perform(put("/api/admin/products/{productId}", existingPRoductDTO.id()) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(updatedProductDTO))) + .content(getObjectMapper().writeValueAsString(updatedProductDTO))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("discount", is("must be greater than or equal to 0"))); } @@ -844,9 +818,9 @@ void updateProductWhenProductSpecialIsNegative() throws Exception { ProductDTO existingProductDTO = productService.createProduct(productDTO, categoryDTO.id()); ProductDTO updatedProductDTO = new ProductDTO(0L, "Witcher 3", "w3.png", "GOTY 2015", 10, 50.0d, 20.0d, -70.0d); - mockMvc.perform(put("/api/admin/products/{productId}", existingProductDTO.id()) + getMockMvc().perform(put("/api/admin/products/{productId}", existingProductDTO.id()) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(updatedProductDTO))) + .content(getObjectMapper().writeValueAsString(updatedProductDTO))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("specialPrice", is("must be greater than or equal to 0"))); } @@ -858,7 +832,7 @@ void updateProductImageWhenProductDoesNotExists() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("test-images/java.png"); MockMultipartFile multipartFile = new MockMultipartFile("image", inputStream); - mockMvc.perform(multipart(HttpMethod.PUT, "/api/admin/products/{productId}/image", 1) + getMockMvc().perform(multipart(HttpMethod.PUT, "/api/admin/products/{productId}/image", 1) .file(multipartFile)) .andExpect(status().isNotFound()) .andExpect(jsonPath("message", is("Product with ID: 1 not found"))); @@ -875,7 +849,7 @@ void updateProductImageWhenProductExists() throws Exception { ProductDTO productDTO = new ProductDTO(0L, "Witcher 3", "w3.png", "GOTY 2015", 10, 50.0d, 20.0d, -70.0d); ProductDTO existingProductDTO = productService.createProduct(productDTO, categoryDTO.id()); - mockMvc.perform(multipart(HttpMethod.PUT, "/api/admin/products/{productId}/image", existingProductDTO.id()) + getMockMvc().perform(multipart(HttpMethod.PUT, "/api/admin/products/{productId}/image", existingProductDTO.id()) .file(multipartFile)) .andExpect(status().isOk()) .andExpect(jsonPath("name", is("Witcher 3"))) @@ -890,7 +864,7 @@ void deleteProductWhenProductExists() throws Exception { ProductDTO productDTO = new ProductDTO(0L, "Witcher 3", "w3.png", "GOTY 2015", 1000, 125.0d, 10.0d, 0.0d); ProductDTO product = productService.createProduct(productDTO, categoryDTO.id()); - mockMvc.perform(delete("/api/admin/products/{productId}", product.id())) + getMockMvc().perform(delete("/api/admin/products/{productId}", product.id())) .andExpect(status().isOk()) .andExpect(jsonPath("name", is("Witcher 3"))) .andExpect(jsonPath("description", is("GOTY 2015"))) @@ -901,7 +875,7 @@ void deleteProductWhenProductExists() throws Exception { @WithMockUser(username = "it_admin", roles = {"ADMIN"}) @DisplayName("[DELETE] Delete product - product does not exists") void deleteProductWhenProductDoesNotExists() throws Exception { - mockMvc.perform(delete("/api/admin/products/{productId}", 1L)) + getMockMvc().perform(delete("/api/admin/products/{productId}", 1L)) .andExpect(status().isNotFound()) .andExpect(jsonPath("message", is("Product with ID: 1 not found"))) .andExpect(jsonPath("status", is(false))); From 5e599a6c2d8019abe13b53fadcd0f97049ded759 Mon Sep 17 00:00:00 2001 From: dawidg90 Date: Wed, 25 Jun 2025 19:55:05 +0200 Subject: [PATCH 08/11] Tests fixed. --- src/main/java/dg/shop/server/model/User.java | 20 +------ .../resources/application-test.properties | 6 +- .../resources/db/migration/V0.0.1__Init.sql | 4 ++ .../controller/AddressControllerTest.java | 7 +++ .../server/controller/AuthControllerTest.java | 58 +++++++++++-------- .../server/controller/BaseControllerTest.java | 2 - .../server/controller/CartControllerTest.java | 4 +- 7 files changed, 51 insertions(+), 50 deletions(-) diff --git a/src/main/java/dg/shop/server/model/User.java b/src/main/java/dg/shop/server/model/User.java index 61a7fb2..f4b5115 100644 --- a/src/main/java/dg/shop/server/model/User.java +++ b/src/main/java/dg/shop/server/model/User.java @@ -10,8 +10,6 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.JoinTable; import jakarta.persistence.ManyToMany; -import jakarta.persistence.OneToMany; -import jakarta.persistence.OneToOne; import jakarta.persistence.Table; import jakarta.persistence.UniqueConstraint; import jakarta.validation.constraints.Email; @@ -21,11 +19,8 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import lombok.ToString; -import java.util.ArrayList; import java.util.HashSet; -import java.util.List; import java.util.Set; @Data @@ -60,7 +55,7 @@ public class User { @Column(name = "password") private String password; - @ManyToMany(cascade = { CascadeType.ALL }, fetch = FetchType.EAGER) + @ManyToMany(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER) @JoinTable( name = "user_role", joinColumns = @JoinColumn(name = "user_id"), @@ -70,19 +65,6 @@ public class User { @Setter private Set roles = new HashSet<>(); -// @Getter -// @Setter -// @OneToMany(mappedBy = "user", cascade = { CascadeType.ALL }) -// private List
addresses = new ArrayList<>(); - -// @ToString.Exclude -// @OneToOne(mappedBy = "user", cascade = { CascadeType.ALL }, orphanRemoval = true) -// private Cart cart; - -// @ToString.Exclude -// @OneToMany(mappedBy = "seller", cascade = {CascadeType.PERSIST, CascadeType.MERGE}, orphanRemoval = true) -// private Set products; - public User(String username, String password, String email) { this.email = email; this.password = password; diff --git a/src/main/resources/application-test.properties b/src/main/resources/application-test.properties index 9de5487..bdfb30b 100644 --- a/src/main/resources/application-test.properties +++ b/src/main/resources/application-test.properties @@ -1,10 +1,10 @@ spring.application.name=server-test spring.h2.console.enabled=true -spring.datasource.url=jdbc:h2:mem:devshop;DB_CLOSE_DELAY=-1;MODE=MySQL; -spring.datasource.driverClassName=org.h2.Driver +spring.datasource.url=jdbc:h2:mem:devshop +spring.datasource.driver-class-name=org.h2.Driver spring.datasource.username=sa spring.datasource.password= -spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect +spring.jpa.database-platform=org.hibernate.dialect.H2Dialect dir.product.images=images diff --git a/src/main/resources/db/migration/V0.0.1__Init.sql b/src/main/resources/db/migration/V0.0.1__Init.sql index 1f27e29..f1e3fa4 100644 --- a/src/main/resources/db/migration/V0.0.1__Init.sql +++ b/src/main/resources/db/migration/V0.0.1__Init.sql @@ -1,3 +1,7 @@ +SET MODE MYSQL; + +CREATE SCHEMA IF NOT EXISTS "public"; + CREATE TABLE categories ( id bigint NOT NULL AUTO_INCREMENT, name varchar(255) DEFAULT NULL, diff --git a/src/test/java/dg/shop/server/controller/AddressControllerTest.java b/src/test/java/dg/shop/server/controller/AddressControllerTest.java index 77fef62..accf2be 100644 --- a/src/test/java/dg/shop/server/controller/AddressControllerTest.java +++ b/src/test/java/dg/shop/server/controller/AddressControllerTest.java @@ -10,6 +10,7 @@ import dg.shop.server.repository.UserRepository; import dg.shop.server.service.address.AddressService; import dg.shop.server.util.AuthUtils; +import jakarta.transaction.Transactional; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -99,6 +100,7 @@ void getAddressByIdWhenAddressFound() throws Exception { @Test @DisplayName("[GET] Get signed in user addresses - user has no address") + @Transactional void getSignedInUserAddressesWhenUserHasNoAddress() throws Exception { User testUser = createTestUser("test_user"); signInUser(testUser); @@ -110,6 +112,7 @@ void getSignedInUserAddressesWhenUserHasNoAddress() throws Exception { @Test @DisplayName("[GET] Get signed in user addresses - user has two addresses") + @Transactional void getSignedInUserAddressesWhenUserHasTwoAddresses() throws Exception { User testUser = createTestUser("test_user"); AddressDTO address1 = createTestAddress(new AddressDTO(0L, "StreetA", 1, 2, "111111", "CityA"), testUser); @@ -133,6 +136,7 @@ void createAddressWhenPayloadIsEmpty() throws Exception { @Test @DisplayName("[POST] Create address - success") + @Transactional void createAddressWhenAddressWereCreatedSuccessfully() throws Exception { AddressDTO toCreate = new AddressDTO(0L, "AStreet", 4, 5, "445544", "ACity"); User user = createTestUser("user1"); @@ -183,6 +187,7 @@ void createAddressWhenHouseNumberIsZero() throws Exception { @Test @DisplayName("[POST] Create address - flat number is zero") + @Transactional void createAddressWhenFlatNumberIsZero() throws Exception { AddressDTO toCreate = new AddressDTO(0L, "AStreet", 4, 0, "445544", "ACity"); User user = createTestUser("thUser1"); @@ -286,6 +291,7 @@ void updateAddressWhenPayloadIsEmpty() throws Exception { @Test @DisplayName("[PUT] Update address - success") + @Transactional void updateAddressWhenAddressWereCreatedSuccessfully() throws Exception { User user = createTestUser("user1"); AddressDTO existingAddress = new AddressDTO(0L, "Street", 1, 2, "111111", "City"); @@ -338,6 +344,7 @@ void updateAddressWhenHouseNumberIsZero() throws Exception { @Test @DisplayName("[PUT] Update address - flat number is zero") + @Transactional void updateAddressWhenFlatNumberIsZero() throws Exception { User user = createTestUser("thUser1"); AddressDTO existingAddress = new AddressDTO(0L, "AStreet", 4, 2, "445544", "ACity"); diff --git a/src/test/java/dg/shop/server/controller/AuthControllerTest.java b/src/test/java/dg/shop/server/controller/AuthControllerTest.java index 0446924..b4e6e6d 100644 --- a/src/test/java/dg/shop/server/controller/AuthControllerTest.java +++ b/src/test/java/dg/shop/server/controller/AuthControllerTest.java @@ -9,21 +9,16 @@ import dg.shop.server.security.request.SignupRequest; import jakarta.transaction.Transactional; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.context.WebApplicationContext; import java.util.Optional; import java.util.Set; @@ -41,7 +36,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -public class AuthControllerTest extends BaseControllerTest{ +public class AuthControllerTest extends BaseControllerTest { @Autowired private UserRepository userRepository; @@ -60,9 +55,11 @@ public class AuthControllerTest extends BaseControllerTest{ @Test @DisplayName("[POST] User Sign In - user exists and signed in properly") + @Transactional void userSignInWhenUserSignedInProperly() throws Exception { + Role userRole = roleRepository.save(new Role(UserRole.ROLE_USER)); User user = new User("it_user", passwordEncoder.encode("it_user_10"), "ituser@test.com"); - user.setRoles(Set.of(new Role(UserRole.ROLE_USER))); + user.setRoles(Set.of(userRole)); userRepository.save(user); LoginRequest loginRequest = new LoginRequest("it_user", "it_user_10"); @@ -75,13 +72,14 @@ void userSignInWhenUserSignedInProperly() throws Exception { .andExpect(jsonPath("$.roles[0]").value("ROLE_USER")) .andExpect(cookie().path(cookieName, is("/api"))) .andExpect(cookie().value(cookieName, notNullValue())); + + user.setRoles(null); } @Test @DisplayName("[POST] User Sign In - user exists and used wrong credentials") void userSignInWhenUserUsedWrongCredentials() throws Exception { User user = new User("it_user", passwordEncoder.encode("it_user_10"), "ituser@test.com"); - user.setRoles(Set.of(new Role(UserRole.ROLE_USER))); userRepository.save(user); LoginRequest loginRequest = new LoginRequest("it_user", "wrong_password"); @@ -91,13 +89,13 @@ void userSignInWhenUserUsedWrongCredentials() throws Exception { .andExpect(status().isNotFound()) .andExpect(jsonPath("message", is("Bad credentials"))) .andExpect(jsonPath("status", is(false))); + user.setRoles(null); } @Test @DisplayName("[POST] User Sign In - user exists and used only password") void userSignInWhenUserUsedOnlyPassword() throws Exception { User user = new User("it_user", passwordEncoder.encode("it_user_10"), "ituser@test.com"); - user.setRoles(Set.of(new Role(UserRole.ROLE_USER))); userRepository.save(user); LoginRequest loginRequest = new LoginRequest("", "wrong_password"); @@ -105,13 +103,13 @@ void userSignInWhenUserUsedOnlyPassword() throws Exception { .contentType(MediaType.APPLICATION_JSON) .content(getObjectMapper().writeValueAsString(loginRequest))) .andExpect(status().isBadRequest()); + user.setRoles(null); } @Test @DisplayName("[POST] User Sign In - user exists and used only login") void userSignInWhenUserUsedOnlyLogin() throws Exception { User user = new User("it_user", passwordEncoder.encode("it_user_10"), "ituser@test.com"); - user.setRoles(Set.of(new Role(UserRole.ROLE_USER))); userRepository.save(user); LoginRequest loginRequest = new LoginRequest("it_user", ""); @@ -119,6 +117,8 @@ void userSignInWhenUserUsedOnlyLogin() throws Exception { .contentType(MediaType.APPLICATION_JSON) .content(getObjectMapper().writeValueAsString(loginRequest))) .andExpect(status().isBadRequest()); + + user.setRoles(null); } @Test @@ -133,11 +133,7 @@ void userSignInWhenPayloadIsEmpty() throws Exception { @Test @DisplayName("[POST] User Sign Up - user not exists and signed up properly with given roles") void userSignUpWhenUserSignedUpProperlyWithGivenRoles() throws Exception { - roleRepository.save(new Role(UserRole.ROLE_USER)); - roleRepository.save(new Role(UserRole.ROLE_SELLER)); - roleRepository.save(new Role(UserRole.ROLE_ADMIN)); - - SignupRequest signupRequest = new SignupRequest("adminuser", "it_admin10", "itadmin@dgshop.pl", Set.of("user", "seller", "admin")); + SignupRequest signupRequest = new SignupRequest("adminuser", "it_admin10", "adminuser@dgshop.pl", Set.of("user", "seller", "admin")); getMockMvc().perform(post("/api/auth/signup") .contentType(MediaType.APPLICATION_JSON) .content(getObjectMapper().writeValueAsString(signupRequest))) @@ -146,14 +142,13 @@ void userSignUpWhenUserSignedUpProperlyWithGivenRoles() throws Exception { Optional user = userRepository.findByUsername("adminuser"); assertEquals(3, user.get().getRoles().size()); + user.get().setRoles(null); } @Test @DisplayName("[POST] User Sign Up - user not exists and signed up properly with user role") void userSignUpWhenUserSignedUpProperlyWithUserRole() throws Exception { - roleRepository.save(new Role(UserRole.ROLE_USER)); - - SignupRequest signupRequest = new SignupRequest("testuser", "it_user_10", "ituser@dgshop.pl", null); + SignupRequest signupRequest = new SignupRequest("testuser", "it_user_10", "testuser@dgshop.pl", null); getMockMvc().perform(post("/api/auth/signup") .contentType(MediaType.APPLICATION_JSON) .content(getObjectMapper().writeValueAsString(signupRequest))) @@ -176,10 +171,7 @@ void userSignUpWhenPayloadIsEmpty() throws Exception { @Test @DisplayName("[POST] User Sign Up - user with given username already exists") void userSignUpWhenUserWithGivenNameAlreadyExists() throws Exception { - roleRepository.save(new Role(UserRole.ROLE_USER)); - User user = new User("it_user", passwordEncoder.encode("it_user_10"), "ituser@test.com"); - user.setRoles(Set.of(new Role(UserRole.ROLE_USER))); userRepository.save(user); SignupRequest signupRequest = new SignupRequest("it_user", "it_admin10", "itadmin@dgshop.pl", null); @@ -188,15 +180,14 @@ void userSignUpWhenUserWithGivenNameAlreadyExists() throws Exception { .content(getObjectMapper().writeValueAsString(signupRequest))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("message", is("Error: Username is already taken"))); + + user.setRoles(null); } @Test @DisplayName("[POST] User Sign Up - user with given email already exists") void userSignUpWhenUserWithGivenEmailAlreadyExists() throws Exception { - roleRepository.save(new Role(UserRole.ROLE_USER)); - User user = new User("it_user", passwordEncoder.encode("it_user_10"), "ituser@test.com"); - user.setRoles(Set.of(new Role(UserRole.ROLE_USER))); userRepository.save(user); SignupRequest signupRequest = new SignupRequest("userWithExistingEmail", "it_admin10", "ituser@test.com", null); @@ -205,11 +196,15 @@ void userSignUpWhenUserWithGivenEmailAlreadyExists() throws Exception { .content(getObjectMapper().writeValueAsString(signupRequest))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("message", is("Error: Email is already taken"))); + + user.setRoles(null); } @Test @DisplayName("[POST] User Sign Up - user not exists and signed up properly but role does not exists") void userSignUpWhenUserSignedUpProperlyButRoleDoesNotExists() throws Exception { + userRepository.deleteAll(); + roleRepository.deleteAll(); SignupRequest signupRequest = new SignupRequest("ituser1", "it_admin10", "itadmin@dgshop.pl", Set.of("admin")); getMockMvc().perform(post("/api/auth/signup") .contentType(MediaType.APPLICATION_JSON) @@ -348,6 +343,7 @@ void getCurrentlySignedInUsernameWhenUserSignedIn() throws Exception { SecurityContextHolder.getContext().setAuthentication(null); assertNull(SecurityContextHolder.getContext().getAuthentication()); + user.setRoles(null); } @Test @@ -378,9 +374,11 @@ void getCurrentlySignedInUserDetailsWhenNoUserIsSignedIn() throws Exception { @Test @DisplayName("[GET] Get user details - user is signed in") + @Transactional void getCurrentlySignedInUserDetailsWhenUserIsSignedIn() throws Exception { + Role userRole = roleRepository.save(new Role(UserRole.ROLE_USER)); User user = new User("it_user", passwordEncoder.encode("it_user_10"), "ituser@test.com"); - user.setRoles(Set.of(new Role(UserRole.ROLE_USER))); + user.setRoles(Set.of(userRole)); userRepository.save(user); Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("it_user", "it_user_10")); @@ -394,5 +392,15 @@ void getCurrentlySignedInUserDetailsWhenUserIsSignedIn() throws Exception { SecurityContextHolder.getContext().setAuthentication(null); assertNull(SecurityContextHolder.getContext().getAuthentication()); + + user.setRoles(null); + } + + @AfterEach + void tearDown() { + userRepository.deleteAll(); + roleRepository.deleteAllInBatch(); + System.out.println("After User: " + userRepository.findAll().size()); + System.out.println("After Role: " + roleRepository.findAll().size()); } } \ No newline at end of file diff --git a/src/test/java/dg/shop/server/controller/BaseControllerTest.java b/src/test/java/dg/shop/server/controller/BaseControllerTest.java index ef5c8eb..393dbaa 100644 --- a/src/test/java/dg/shop/server/controller/BaseControllerTest.java +++ b/src/test/java/dg/shop/server/controller/BaseControllerTest.java @@ -1,7 +1,6 @@ package dg.shop.server.controller; import com.fasterxml.jackson.databind.ObjectMapper; -import jakarta.transaction.Transactional; import lombok.Getter; import org.flywaydb.core.Flyway; import org.junit.jupiter.api.BeforeEach; @@ -14,7 +13,6 @@ import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; @SpringBootTest -@Transactional public class BaseControllerTest { @Autowired private WebApplicationContext context; diff --git a/src/test/java/dg/shop/server/controller/CartControllerTest.java b/src/test/java/dg/shop/server/controller/CartControllerTest.java index 962d62b..86d8134 100644 --- a/src/test/java/dg/shop/server/controller/CartControllerTest.java +++ b/src/test/java/dg/shop/server/controller/CartControllerTest.java @@ -19,6 +19,7 @@ import dg.shop.server.service.category.CategoryService; import dg.shop.server.service.product.ProductService; import dg.shop.server.util.AuthUtils; +import jakarta.transaction.Transactional; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -41,7 +42,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -public class CartControllerTest extends BaseControllerTest{ +@Transactional +public class CartControllerTest extends BaseControllerTest { @Autowired private CartService cartService; From 03fb0bc62c6b7c403b8fb196d1098429bc6c39c6 Mon Sep 17 00:00:00 2001 From: dawidg90 Date: Wed, 25 Jun 2025 23:04:21 +0200 Subject: [PATCH 09/11] Add missing test cases for Order Controller. --- .../server/service/cart/CartServiceImpl.java | 2 - .../controller/OrderControllerTest.java | 67 ++++++++++++++++--- 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/src/main/java/dg/shop/server/service/cart/CartServiceImpl.java b/src/main/java/dg/shop/server/service/cart/CartServiceImpl.java index 093764d..1308758 100644 --- a/src/main/java/dg/shop/server/service/cart/CartServiceImpl.java +++ b/src/main/java/dg/shop/server/service/cart/CartServiceImpl.java @@ -84,7 +84,6 @@ public CartDTO getUserCart(String email, long cartId) { @Override @Transactional public CartDTO updateProductQuantity(long productId, String operation) { -// Cart existingCart = authUtils.signedInUser().getCart(); Cart userCart = getExistingCart(); if (userCart == null) { throw new ResourceNotFoundException("Cart", "email", authUtils.signedInEmail()); @@ -111,7 +110,6 @@ public CartDTO updateProductQuantity(long productId, String operation) { private void removeItemFromCartWhenQuantityIsZero(CartItem updatedItem, Cart userCart) { if (updatedItem.getQuantity() <= 0) { cartItemService.deleteById(updatedItem.getCartItemId()); -// userCart.getCartItems().remove(updatedItem); } } diff --git a/src/test/java/dg/shop/server/controller/OrderControllerTest.java b/src/test/java/dg/shop/server/controller/OrderControllerTest.java index d5118c0..791a754 100644 --- a/src/test/java/dg/shop/server/controller/OrderControllerTest.java +++ b/src/test/java/dg/shop/server/controller/OrderControllerTest.java @@ -1,6 +1,5 @@ package dg.shop.server.controller; -import dg.shop.server.model.Cart; import dg.shop.server.model.Role; import dg.shop.server.model.User; import dg.shop.server.model.UserRole; @@ -37,6 +36,7 @@ import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; @@ -114,6 +114,64 @@ void testPlaceOrderSuccess() throws Exception { signOutUser(); } + @Test + @DisplayName("[POST] Place order - cart does not exists") + void testPlaceOrderWhenCartDoesNotExists() throws Exception { + User user = createTestUser("admin"); + signInUser(user); + OrderRequestDTO orderRequestDTO = new OrderRequestDTO(0L, "card", "PayU", "uid_VEF43FD45GXR56R", "success", "Payment Completed"); + getMockMvc().perform(post("/api/order/users/payments/{paymentMethod}", "card") + .contentType(MediaType.APPLICATION_JSON) + .content(getObjectMapper().writeValueAsString(orderRequestDTO))) + .andExpect(status().isNotFound()) + .andExpect(jsonPath("message", is("Cart with user.email: admin@test.com not found"))); + signOutUser(); + } + + @Test + @DisplayName("[POST] Place order - address does not exists") + void testPlaceOrderWhenAddresDoesNotExists() throws Exception { + User user = createTestUser("admin"); + signInUser(user); + CategoryDTO category = categoryService.createCategory(new CategoryDTO("Games")); + ProductDTO product = productService.createProduct(new ProductDTO(0L, "Product", "product.png", "A Product", 10, 25.0d, 0.0d, 25.0d), category.id()); + CartDTO cart = cartService.addProductToCart(product.id(), 1); + assertEquals(1, cartService.getAllCarts().size()); + + OrderRequestDTO orderRequestDTO = new OrderRequestDTO(4L, "card", "PayU", "uid_VEF43FD45GXR56R", "success", "Payment Completed"); + getMockMvc().perform(post("/api/order/users/payments/{paymentMethod}", "card") + .contentType(MediaType.APPLICATION_JSON) + .content(getObjectMapper().writeValueAsString(orderRequestDTO))) + .andExpect(status().isNotFound()) + .andExpect(jsonPath("message", is("Address with ID: 4 not found"))); + signOutUser(); + } + + @Test + @DisplayName("[POST] Place order - cart item list is empty") + void testPlaceOrderWhenCartItemListIsEmpty() throws Exception { + User user = createTestUser("admin"); + AddressDTO address = addressService.createAddress(new AddressDTO(0L, "Street", 33, 1, "123456", "City"), user); + signInUser(user); + CategoryDTO category = categoryService.createCategory(new CategoryDTO("Games")); + ProductDTO product = productService.createProduct(new ProductDTO(0L, "Product", "product.png", "A Product", 10, 25.0d, 0.0d, 25.0d), category.id()); + CartDTO cart = cartService.addProductToCart(product.id(), 1); + CartDTO emptyCart = cartService.updateProductQuantity(product.id(), "decrease"); + assertEquals(1, cartService.getAllCarts().size()); + + OrderRequestDTO orderRequestDTO = new OrderRequestDTO(address.addressId(), "card", "PayU", "uid_VEF43FD45GXR56R", "success", "Payment Completed"); + getMockMvc().perform(post("/api/order/users/payments/{paymentMethod}", "card") + .contentType(MediaType.APPLICATION_JSON) + .content(getObjectMapper().writeValueAsString(orderRequestDTO))) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("message", is("Cart is empty"))) + .andExpect(jsonPath("status", is(false))); + + assertTrue(cartItemService.getRepository().findAll().isEmpty()); + assertFalse(cartService.getRepository().findAll().isEmpty()); + signOutUser(); + } + private User createTestUser(String username) { roleRepository.save(new Role(UserRole.ROLE_ADMIN)); @@ -122,13 +180,6 @@ private User createTestUser(String username) { return userRepository.save(user); } - private Cart createCartForUser(User user) { - Cart newCart = new Cart(); - newCart.setTotalPrice(0.0d); - newCart.setUser(user); - return cartService.getRepository().save(newCart); - } - private void signInUser(User user) { SecurityContextHolder.getContext() .setAuthentication( From d31d3d81b7e9395f64ca0232f86bb77cffd104a0 Mon Sep 17 00:00:00 2001 From: tof3r Date: Wed, 1 Apr 2026 15:41:07 +0200 Subject: [PATCH 10/11] README updated. --- README.md | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a568cd2..3641d54 100644 --- a/README.md +++ b/README.md @@ -1 +1,39 @@ -# shopserver \ No newline at end of file +## Sample REST API for ecommerce shop + +--- +#### Technology Stack: +* Java 21 +* Spring Boot 3.5.0 +* Spring MVC +* Spring Data JPA +* Spring Security +* Lombok 1.18.36 +* Flyway 11.9.0 +* JWT 0.12.6 +* Hibernate Validator 8.0.1 +* (Testing) H2 database 2.3.232 +* (Testing) Spring Security Test + +#### Basic functionalities: +* Creating and managing products +* Creating and managing product categories +* Authorize safely using JWT +* Creating and managing users +* Placing and managing orders and payments + +To be done: +* Adding Swagger +* more functionalities... +* more improvements... + +--- +#### [DEV] +To make it work go to Edit Configurations/Edit Configuration Templates/JUnit and add: +1. `-Dspring.profiles.active=test` in the VM options +2. In the environment variables: + 1. `LOG_LEVEL=DEBUG` - or any other + 2. `COOKIE_NAME=dgshopserver` - name of the cookie + 3. `JWT_SECRET=` String which is your secret to compute HMAC SHA key for JWT + 4. `JWT_TTL_HOURS=` number of hours for how long the JWT token is valid + +NOTE: all the environment variables must be separated by semicolon `;` in one line \ No newline at end of file From 6ba97cb593fd14b4531af59d5c89fb58391b67df Mon Sep 17 00:00:00 2001 From: tof3r Date: Wed, 1 Apr 2026 16:00:17 +0200 Subject: [PATCH 11/11] Removed commented stuff out. --- src/main/java/dg/shop/server/model/Cart.java | 1 - src/main/java/dg/shop/server/model/Category.java | 13 ------------- src/main/java/dg/shop/server/model/Order.java | 8 -------- src/main/java/dg/shop/server/model/Payment.java | 6 ------ .../shop/server/service/cart/CartServiceImpl.java | 2 -- 5 files changed, 30 deletions(-) diff --git a/src/main/java/dg/shop/server/model/Cart.java b/src/main/java/dg/shop/server/model/Cart.java index 0a50d58..c5e9173 100644 --- a/src/main/java/dg/shop/server/model/Cart.java +++ b/src/main/java/dg/shop/server/model/Cart.java @@ -30,7 +30,6 @@ public class Cart { @Override public String toString() { return "Cart{" + -// "cartItems size=" + cartItems.size() + ", cartId=" + cartId + ", user=" + user + ", totalPrice=" + totalPrice + diff --git a/src/main/java/dg/shop/server/model/Category.java b/src/main/java/dg/shop/server/model/Category.java index 5a9861d..7c922b5 100644 --- a/src/main/java/dg/shop/server/model/Category.java +++ b/src/main/java/dg/shop/server/model/Category.java @@ -1,18 +1,13 @@ package dg.shop.server.model; -import jakarta.persistence.CascadeType; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; -import jakarta.persistence.OneToMany; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import lombok.ToString; - -import java.util.List; @Data @NoArgsConstructor @@ -26,12 +21,4 @@ public class Category { @Column private String name; - -// @ToString.Exclude -// @OneToMany(mappedBy = "category", cascade = { CascadeType.ALL }) -// private List products; - -// public Category(long id, String name) { -// this(id, name, List.of()); -// } } diff --git a/src/main/java/dg/shop/server/model/Order.java b/src/main/java/dg/shop/server/model/Order.java index 02e2556..ca606fd 100644 --- a/src/main/java/dg/shop/server/model/Order.java +++ b/src/main/java/dg/shop/server/model/Order.java @@ -1,6 +1,5 @@ package dg.shop.server.model; -import jakarta.persistence.CascadeType; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; @@ -8,7 +7,6 @@ import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; -import jakarta.persistence.OneToMany; import jakarta.persistence.OneToOne; import jakarta.persistence.Table; import jakarta.validation.constraints.Email; @@ -17,8 +15,6 @@ import lombok.NoArgsConstructor; import java.time.LocalDate; -import java.util.ArrayList; -import java.util.List; @Entity @Data @@ -34,9 +30,6 @@ public class Order { @Column(nullable = false) private String email; -// @OneToMany(mappedBy = "order", cascade = { CascadeType.ALL }) -// private List orderItems = new ArrayList<>(); - private LocalDate orderDate; @OneToOne @@ -57,7 +50,6 @@ public String toString() { "address id=" + address.getAddressId() + ", orderId=" + orderId + ", email='" + email + '\'' + -// ", orderItems=" + orderItems + ", orderDate=" + orderDate + ", payment id=" + payment.getPaymentId() + ", totalAmount=" + totalAmount + diff --git a/src/main/java/dg/shop/server/model/Payment.java b/src/main/java/dg/shop/server/model/Payment.java index d6316f1..2063e86 100644 --- a/src/main/java/dg/shop/server/model/Payment.java +++ b/src/main/java/dg/shop/server/model/Payment.java @@ -1,12 +1,10 @@ package dg.shop.server.model; import dg.shop.server.validation.RequiredSize; -import jakarta.persistence.CascadeType; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; -import jakarta.persistence.OneToOne; import jakarta.persistence.Table; import jakarta.validation.constraints.NotBlank; import lombok.AllArgsConstructor; @@ -24,9 +22,6 @@ public class Payment { @GeneratedValue(strategy = GenerationType.IDENTITY) private long paymentId; -// @OneToOne(mappedBy = "payment", cascade = { CascadeType.PERSIST, CascadeType.MERGE} ) -// private Order order; - @NotBlank @RequiredSize(min = 4, sizeMessage = "Payment method must contain at least 4 characters") private String paymentMethod; @@ -47,7 +42,6 @@ public Payment(String paymentMethod, String paymentGatewayPaymentId, String paym @Override public String toString() { return "Payment{" + -// "order id=" + order.getOrderId() + ", paymentId=" + paymentId + ", paymentMethod='" + paymentMethod + '\'' + ", paymentGatewayPaymentId='" + paymentGatewayPaymentId + '\'' + diff --git a/src/main/java/dg/shop/server/service/cart/CartServiceImpl.java b/src/main/java/dg/shop/server/service/cart/CartServiceImpl.java index 1308758..b2da787 100644 --- a/src/main/java/dg/shop/server/service/cart/CartServiceImpl.java +++ b/src/main/java/dg/shop/server/service/cart/CartServiceImpl.java @@ -128,7 +128,6 @@ public String deleteProduct(long cartId, long productId) { private void updateCartOnRemove(Cart cart, CartItem cartItem, long cartId, long productId) { cartItemService.deleteByProductIdAndCartId(cartId, productId); -// cart.getCartItems().remove(cartItem); cart.setTotalPrice(cart.getTotalPrice() - (cartItem.getProductPrice() * cartItem.getQuantity())); cartRepository.save(cart); } @@ -200,7 +199,6 @@ private void setCartItemQuantity(String operation, CartItem cartItem, Product pr } private Cart updateCart(CartItem cartItem, int quantity, Cart cart, Product product) { -// cart.getCartItems().add(cartItem); cart.setTotalPrice(cart.getTotalPrice() + (product.getSpecialPrice() * quantity)); return cartRepository.save(cart); }