diff --git a/app/src/main/java/com/ibrahimokic/ordermanagement/adapters/AdminConsoleAdapter.java b/app/src/main/java/com/ibrahimokic/ordermanagement/adapters/AdminConsoleAdapter.java index bf809ab..d3868cc 100644 --- a/app/src/main/java/com/ibrahimokic/ordermanagement/adapters/AdminConsoleAdapter.java +++ b/app/src/main/java/com/ibrahimokic/ordermanagement/adapters/AdminConsoleAdapter.java @@ -8,6 +8,7 @@ import com.ibrahimokic.ordermanagement.service.ProductService; import com.ibrahimokic.ordermanagement.service.UserService; import com.ibrahimokic.ordermanagement.utils.Utils; +import jakarta.validation.ConstraintViolationException; import lombok.RequiredArgsConstructor; import java.time.LocalDate; @@ -250,7 +251,15 @@ private void createUserAccountForm() { .build(); saveUserAndReturnToMenu(newUserAccount); - } catch (Exception e) { + } + catch (ConstraintViolationException e) { + e.getConstraintViolations().forEach(violation -> + System.out.println(violation.getMessage()) + ); + Utils.returnBackToTheMainMenu(scanner); + adminUserManagementOptions(); + } + catch (Exception e) { System.out.println("ERROR: "+ e.getMessage()); Utils.returnBackToTheMainMenu(scanner); adminUserManagementOptions(); @@ -306,7 +315,13 @@ private void saveUserAndReturnToMenu(User user) { try { userService.createUser(user); System.out.println("OM-APP: New account '" + user.getFirstName() + " " + user.getLastName() + "' with role '"+ user.getRole() +"' has been successfully created."); - } catch (Exception e) { + } + catch (ConstraintViolationException e) { + e.getConstraintViolations().forEach(violation -> + System.out.println(violation.getMessage()) + ); + } + catch (Exception e) { System.out.println("ERROR: An error occurred while creating an user, please check your inputs."); } diff --git a/app/src/main/java/com/ibrahimokic/ordermanagement/domain/dto/OrderItemDto.java b/app/src/main/java/com/ibrahimokic/ordermanagement/domain/dto/OrderItemDto.java index 70233dc..f062533 100644 --- a/app/src/main/java/com/ibrahimokic/ordermanagement/domain/dto/OrderItemDto.java +++ b/app/src/main/java/com/ibrahimokic/ordermanagement/domain/dto/OrderItemDto.java @@ -23,11 +23,9 @@ public class OrderItemDto { @JsonProperty("quantity") @NotNull - @NotBlank(message = "Quantity is required") private Integer quantity; @JsonProperty("item_price") @NotNull - @NotBlank(message = "Item price is required") private BigDecimal itemPrice; } diff --git a/app/src/main/java/com/ibrahimokic/ordermanagement/domain/dto/ProductDto.java b/app/src/main/java/com/ibrahimokic/ordermanagement/domain/dto/ProductDto.java index f9f7e3d..fc933e3 100644 --- a/app/src/main/java/com/ibrahimokic/ordermanagement/domain/dto/ProductDto.java +++ b/app/src/main/java/com/ibrahimokic/ordermanagement/domain/dto/ProductDto.java @@ -29,7 +29,6 @@ public class ProductDto { @JsonProperty("price") @NotNull - @NotBlank(message = "Price is required") private BigDecimal price; @JsonProperty("available_from") @@ -45,6 +44,6 @@ public class ProductDto { private LocalDate availableUntil; @JsonProperty("available_quantity") - @NotBlank(message = "Available quantity is required") + @NotNull private int availableQuantity; } diff --git a/app/src/main/java/com/ibrahimokic/ordermanagement/domain/dto/UserDto.java b/app/src/main/java/com/ibrahimokic/ordermanagement/domain/dto/UserDto.java index 9cca419..4c02e10 100644 --- a/app/src/main/java/com/ibrahimokic/ordermanagement/domain/dto/UserDto.java +++ b/app/src/main/java/com/ibrahimokic/ordermanagement/domain/dto/UserDto.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; import jakarta.validation.constraints.*; import lombok.AllArgsConstructor; import lombok.Builder; @@ -62,5 +63,6 @@ public class UserDto { @JsonProperty("address") @NotNull + @Valid private AddressDto address; } diff --git a/app/src/main/java/com/ibrahimokic/ordermanagement/domain/entity/Address.java b/app/src/main/java/com/ibrahimokic/ordermanagement/domain/entity/Address.java index 52c4e0f..0f1ba0b 100644 --- a/app/src/main/java/com/ibrahimokic/ordermanagement/domain/entity/Address.java +++ b/app/src/main/java/com/ibrahimokic/ordermanagement/domain/entity/Address.java @@ -1,6 +1,8 @@ package com.ibrahimokic.ordermanagement.domain.entity; import jakarta.persistence.*; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -19,15 +21,23 @@ public class Address { private Long addressId; @Column(name = "street") + @NotNull + @NotBlank(message = "Street is required") private String street; @Column(name = "zip") + @NotNull + @NotBlank(message = "Zip is required") private String zip; @Column(name = "city") + @NotNull + @NotBlank(message = "City is required") private String city; @Column(name = "country") + @NotNull + @NotBlank(message = "Country is required") private String country; } diff --git a/app/src/main/java/com/ibrahimokic/ordermanagement/domain/entity/Order.java b/app/src/main/java/com/ibrahimokic/ordermanagement/domain/entity/Order.java index b83e9a2..9e4ace6 100644 --- a/app/src/main/java/com/ibrahimokic/ordermanagement/domain/entity/Order.java +++ b/app/src/main/java/com/ibrahimokic/ordermanagement/domain/entity/Order.java @@ -1,5 +1,6 @@ package com.ibrahimokic.ordermanagement.domain.entity; +import com.fasterxml.jackson.annotation.JsonIgnore; import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Builder; diff --git a/app/src/main/java/com/ibrahimokic/ordermanagement/domain/entity/OrderItem.java b/app/src/main/java/com/ibrahimokic/ordermanagement/domain/entity/OrderItem.java index 229ef3c..7ff29dc 100644 --- a/app/src/main/java/com/ibrahimokic/ordermanagement/domain/entity/OrderItem.java +++ b/app/src/main/java/com/ibrahimokic/ordermanagement/domain/entity/OrderItem.java @@ -1,6 +1,7 @@ package com.ibrahimokic.ordermanagement.domain.entity; import jakarta.persistence.*; +import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -29,8 +30,10 @@ public class OrderItem { private Product product; @Column(nullable = false) + @NotNull private int quantity; @Column(name = "item_price", nullable = false, precision = 10, scale = 2) + @NotNull private BigDecimal itemPrice; } diff --git a/app/src/main/java/com/ibrahimokic/ordermanagement/domain/entity/Product.java b/app/src/main/java/com/ibrahimokic/ordermanagement/domain/entity/Product.java index 99689e6..1c1d3a4 100644 --- a/app/src/main/java/com/ibrahimokic/ordermanagement/domain/entity/Product.java +++ b/app/src/main/java/com/ibrahimokic/ordermanagement/domain/entity/Product.java @@ -30,7 +30,6 @@ public class Product { private String productName; @Column(nullable = false) - @NotBlank(message = "Price is required") @NotNull private BigDecimal price; @@ -47,7 +46,6 @@ public class Product { private LocalDate availableUntil; @Column(name = "available_quantity", nullable = false) - @NotBlank(message = "Available quantity is required") @NotNull private int availableQuantity; } diff --git a/app/src/main/java/com/ibrahimokic/ordermanagement/domain/entity/User.java b/app/src/main/java/com/ibrahimokic/ordermanagement/domain/entity/User.java index b69096f..d61ab45 100644 --- a/app/src/main/java/com/ibrahimokic/ordermanagement/domain/entity/User.java +++ b/app/src/main/java/com/ibrahimokic/ordermanagement/domain/entity/User.java @@ -28,10 +28,7 @@ public class User { @Column(name = "password", nullable = false) @JsonIgnore @NotBlank(message = "Password is required") - @Size( - min = 5, - message = "The password '${validatedValue}' must be greater then {min}" - ) + @Size( min = 5, message = "The password '${validatedValue}' must be greater then {min}") private String password; @Column(name = "email", nullable = false) @@ -67,5 +64,4 @@ public class User { public boolean checkUserPassword(String password) { return password.equals(this.password); } - public boolean checkEmailAlreadyExists(String email) { return email.equals(this.email); } } diff --git a/app/src/main/java/com/ibrahimokic/ordermanagement/mapper/Mapper.java b/app/src/main/java/com/ibrahimokic/ordermanagement/mapper/Mapper.java index c415db4..29fe1cb 100644 --- a/app/src/main/java/com/ibrahimokic/ordermanagement/mapper/Mapper.java +++ b/app/src/main/java/com/ibrahimokic/ordermanagement/mapper/Mapper.java @@ -1,7 +1,5 @@ package com.ibrahimokic.ordermanagement.mapper; -import com.ibrahimokic.ordermanagement.domain.dto.OrderItemDto; - import java.util.Collections; import java.util.List; @@ -13,8 +11,4 @@ public interface Mapper { default List mapListToEntityList(List b) { return Collections.emptyList(); } - - default List mapDtoListToEntityList(List a) { - return Collections.emptyList(); - } } diff --git a/app/src/main/java/com/ibrahimokic/ordermanagement/mapper/impl/OrderItemMapperImpl.java b/app/src/main/java/com/ibrahimokic/ordermanagement/mapper/impl/OrderItemMapperImpl.java index 18ef417..c7735df 100644 --- a/app/src/main/java/com/ibrahimokic/ordermanagement/mapper/impl/OrderItemMapperImpl.java +++ b/app/src/main/java/com/ibrahimokic/ordermanagement/mapper/impl/OrderItemMapperImpl.java @@ -31,11 +31,4 @@ public List mapListToEntityList(List orderItemDtos) { .map(this::mapFrom) .collect(Collectors.toList()); } - - @Override - public List mapDtoListToEntityList(@Valid List orderItems) { - return orderItems.stream() - .map(orderItemDto -> mapTo(mapFrom(orderItemDto))) - .collect(Collectors.toList()); - } } diff --git a/app/src/main/java/com/ibrahimokic/ordermanagement/mapper/impl/OrderMapperImpl.java b/app/src/main/java/com/ibrahimokic/ordermanagement/mapper/impl/OrderMapperImpl.java index 739f771..799b4da 100644 --- a/app/src/main/java/com/ibrahimokic/ordermanagement/mapper/impl/OrderMapperImpl.java +++ b/app/src/main/java/com/ibrahimokic/ordermanagement/mapper/impl/OrderMapperImpl.java @@ -14,8 +14,8 @@ public OrderMapperImpl(ModelMapper modelMapper) { } @Override public OrderDto mapTo(Order order) { - OrderDto orderDto; - orderDto = modelMapper.map(order, OrderDto.class); + OrderDto orderDto = modelMapper.map(order, OrderDto.class); + System.out.println(orderDto); orderDto.setUserId(order.getUser().getUserId()); return orderDto; } diff --git a/app/src/main/java/com/ibrahimokic/ordermanagement/service/impl/OrderServiceImpl.java b/app/src/main/java/com/ibrahimokic/ordermanagement/service/impl/OrderServiceImpl.java index c6c480c..bf4bbdb 100644 --- a/app/src/main/java/com/ibrahimokic/ordermanagement/service/impl/OrderServiceImpl.java +++ b/app/src/main/java/com/ibrahimokic/ordermanagement/service/impl/OrderServiceImpl.java @@ -18,6 +18,7 @@ import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.Optional; @Service @@ -109,7 +110,7 @@ public Optional createNewOrder(@Valid OrderDto orderDto) { .sourceAddress(addressMapper.mapFrom(orderDto.getSourceAddress())) .orderDate(orderDto.getOrderDate()) .orderItems(null) - .totalAmount(new BigDecimal(0)) + .totalAmount(BigDecimal.ZERO) .build(); Optional optionalUser = userRepository.findById(orderDto.getUserId()); @@ -161,10 +162,10 @@ public Optional createNewOrder(@Valid OrderDto orderDto) { order.setTotalAmount(Utils.calculateTotalProductsPriceAmount(orderItems)); orderRepository.save(order); - - return Optional.ofNullable(orderMapper.mapTo(order)); + OrderDto mappedOrderDto = orderMapper.mapTo(order); + return Optional.of(Objects.requireNonNullElse(mappedOrderDto, orderDto)); } catch (Exception e) { - throw new RuntimeException("Error creating order: " + e.getMessage()); + throw new RuntimeException("Error occurred while creating an order: " + e.getMessage()); } } diff --git a/app/src/main/java/com/ibrahimokic/ordermanagement/service/impl/UserServiceImpl.java b/app/src/main/java/com/ibrahimokic/ordermanagement/service/impl/UserServiceImpl.java index 8d97189..134129e 100644 --- a/app/src/main/java/com/ibrahimokic/ordermanagement/service/impl/UserServiceImpl.java +++ b/app/src/main/java/com/ibrahimokic/ordermanagement/service/impl/UserServiceImpl.java @@ -44,8 +44,7 @@ public User createUser(User user) { try { User userExists = userRepository.findByEmail(user.getEmail()); - if(userExists != null) - { + if(userExists != null) { throw new RuntimeException("User with that email already exists in the database."); } diff --git a/app/src/test/java/com/ibrahimokic/ordermanagement/mapper/OrderMapperImplTest.java b/app/src/test/java/com/ibrahimokic/ordermanagement/mapper/OrderMapperImplTest.java index f937038..219e760 100644 --- a/app/src/test/java/com/ibrahimokic/ordermanagement/mapper/OrderMapperImplTest.java +++ b/app/src/test/java/com/ibrahimokic/ordermanagement/mapper/OrderMapperImplTest.java @@ -31,7 +31,6 @@ public class OrderMapperImplTest { @Test void testMapToOrderDto() { - // Given User user = User.builder().userId(101L).build(); Address deliveryAddress = Address.builder().addressId(1L).build(); Address sourceAddress = Address.builder().addressId(2L).build(); @@ -56,16 +55,12 @@ void testMapToOrderDto() { when(modelMapper.map(order, OrderDto.class)).thenReturn(expectedOrderDto); - // When OrderDto result = orderMapper.mapTo(order); - - // Then assertEquals(expectedOrderDto, result); } @Test void testMapFromOrderDto() { - // Given AddressDto deliveryAddressDto = AddressDto.builder().addressId(1L).build(); AddressDto sourceAddressDto = AddressDto.builder().addressId(2L).build(); OrderDto orderDto = OrderDto.builder() @@ -91,10 +86,7 @@ void testMapFromOrderDto() { when(modelMapper.map(orderDto, Order.class)).thenReturn(expectedOrder); - // When Order result = orderMapper.mapFrom(orderDto); - - // Then assertEquals(expectedOrder, result); } } diff --git a/app/src/test/java/com/ibrahimokic/ordermanagement/service/OrderServiceImplTest.java b/app/src/test/java/com/ibrahimokic/ordermanagement/service/OrderServiceImplTest.java index cd53259..3db9350 100644 --- a/app/src/test/java/com/ibrahimokic/ordermanagement/service/OrderServiceImplTest.java +++ b/app/src/test/java/com/ibrahimokic/ordermanagement/service/OrderServiceImplTest.java @@ -1,7 +1,11 @@ package com.ibrahimokic.ordermanagement.service; +import com.ibrahimokic.ordermanagement.domain.dto.AddressDto; import com.ibrahimokic.ordermanagement.domain.dto.OrderDto; -import com.ibrahimokic.ordermanagement.domain.entity.Order; +import com.ibrahimokic.ordermanagement.domain.dto.OrderItemDto; +import com.ibrahimokic.ordermanagement.domain.entity.*; +import com.ibrahimokic.ordermanagement.mapper.impl.AddressMapperImpl; +import com.ibrahimokic.ordermanagement.mapper.impl.OrderItemMapperImpl; import com.ibrahimokic.ordermanagement.mapper.impl.OrderMapperImpl; import com.ibrahimokic.ordermanagement.repository.*; import com.ibrahimokic.ordermanagement.service.impl.OrderServiceImpl; @@ -9,9 +13,13 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; +import java.math.BigDecimal; +import java.time.LocalDate; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Optional; @@ -20,9 +28,27 @@ @ExtendWith(MockitoExtension.class) public class OrderServiceImplTest { + @Mock + private ProductRepository productRepository; + + @Mock + private AddressRepository addressRepository; + + @Mock + private AddressMapperImpl addressMapper; + + @Mock + private OrderItemMapperImpl orderItemMapper; + + @Mock + private OrderItemRepository orderItemRepository; + @Mock private OrderRepository orderRepository; + @Mock + private UserRepository userRepository; + @Mock private OrderMapperImpl orderMapper; @@ -31,7 +57,7 @@ public class OrderServiceImplTest { @Test void testGetAllOrdersWithDetails() { - Order order = new Order(); + Order order = Order.builder().build(); List orders = new ArrayList<>(); orders.add(order); @@ -47,7 +73,7 @@ void testGetAllOrdersWithDetails() { @Test void testGetAllOrders() { - Order order = new Order(); + Order order = Order.builder().build(); List orders = new ArrayList<>(); orders.add(order); @@ -62,7 +88,7 @@ void testGetAllOrders() { @Test void testGetOrderById() { Long orderId = 1L; - Order order = new Order(); + Order order = Order.builder().build(); when(orderRepository.findById(orderId)).thenReturn(Optional.of(order)); when(orderMapper.mapTo(order)).thenReturn(new OrderDto()); @@ -77,7 +103,7 @@ void testGetOrderById() { @Test void testDeleteOrderById() { Long orderId = 1L; - Order order = new Order(); + Order order = Order.builder().build(); when(orderRepository.findById(orderId)).thenReturn(Optional.of(order)); @@ -87,4 +113,171 @@ void testDeleteOrderById() { verify(orderRepository, times(1)).findById(orderId); verify(orderRepository, times(1)).deleteById(orderId); } + + @Test + public void testCreateNewOrderShouldFail() { + User user = User.builder() + .userId(1L) + .username("testUser") + .password("password") + .email("test@example.com") + .role("ROLE_USER") + .firstName("John") + .lastName("Doe") + .birthDate(LocalDate.of(1990, 1, 1)) + .build(); + + Product product = Product.builder() + .productName("Chair") + .price(new BigDecimal(400)) + .productId(1L) + .availableQuantity(40) + .availableFrom(LocalDate.of(2024, 4, 20)) + .availableUntil(LocalDate.of(2024, 8, 30)) + .build(); + + when(userRepository.findById(1L)).thenReturn(Optional.of(user)); + when(productRepository.findById(1L)).thenReturn(Optional.of(product)); + + AddressDto address = AddressDto.builder() + .zip("75000") + .street("Slatina 15") + .city("Tuzla") + .country("Bosnia and Herzegovina") + .build(); + + OrderDto orderDto = OrderDto.builder() + .orderDate(LocalDate.now()) + .totalAmount(BigDecimal.ZERO) + .deliveryAddress(address) + .sourceAddress(address) + .userId(user.getUserId()) + .build(); + + List orderItemDtos = new ArrayList<>(); + OrderItemDto orderItemDto = OrderItemDto.builder().build(); + orderItemDto.setProductId(1L); + orderItemDto.setQuantity(1); + orderItemDto.setItemPrice(new BigDecimal(400)); + orderItemDtos.add(orderItemDto); + orderDto.setOrderItems(orderItemDtos); + + Optional result = orderService.createNewOrder(orderDto); + + assertNull(result.get().getDeliveryAddress().getAddressId()); + } + + @Test + public void testCreateNewOrderShouldSuccess() { + User user = User.builder() + .userId(1L) + .username("testUser") + .password("password") + .email("test@example.com") + .role("ROLE_USER") + .firstName("John") + .lastName("Doe") + .birthDate(LocalDate.of(1990, 1, 1)) + .build(); + + Product product = Product.builder() + .productName("Chair") + .price(new BigDecimal(400)) + .productId(1L) + .availableQuantity(40) + .availableFrom(LocalDate.of(2024, 4, 20)) + .availableUntil(LocalDate.of(2024, 8, 30)) + .build(); + + when(userRepository.findById(1L)).thenReturn(Optional.of(user)); + when(productRepository.findById(1L)).thenReturn(Optional.of(product)); + + AddressDto addressDto = AddressDto.builder() + .zip("string") + .street("string") + .city("string") + .country("string") + .build(); + + OrderDto orderDto = OrderDto.builder() + .orderDate(LocalDate.now()) + .totalAmount(BigDecimal.ZERO) + .deliveryAddress(addressDto) + .sourceAddress(addressDto) + .userId(user.getUserId()) + .build(); + + List orderItemDtos = new ArrayList<>(); + OrderItemDto orderItemDto = OrderItemDto.builder().build(); + orderItemDto.setProductId(1L); + orderItemDto.setQuantity(1); + orderItemDto.setItemPrice(new BigDecimal(400)); + orderItemDtos.add(orderItemDto); + orderDto.setOrderItems(orderItemDtos); + + Optional result = orderService.createNewOrder(orderDto); + + assertTrue(result.isPresent()); + } + + @Test + public void testUpdateOrderShouldSuccess() { + User user = User.builder() + .userId(1L) + .username("testUser") + .password("password") + .email("test@example.com") + .role("ROLE_USER") + .firstName("John") + .lastName("Doe") + .birthDate(LocalDate.of(1990, 1, 1)) + .build(); + + Product product = Product.builder() + .productName("Chair") + .price(new BigDecimal(400)) + .productId(1L) + .availableQuantity(40) + .availableFrom(LocalDate.of(2024, 4, 20)) + .availableUntil(LocalDate.of(2024, 8, 30)) + .build(); + + Address addressDto = Address.builder() + .zip("string") + .street("string") + .city("string") + .country("string") + .build(); + + Order order = Order.builder() + .orderId(1L) + .orderDate(LocalDate.now()) + .totalAmount(BigDecimal.ZERO) + .deliveryAddress(addressDto) + .sourceAddress(addressDto) + .user(user) + .build(); + + List orderItems = new ArrayList<>(); + OrderItem orderItem = OrderItem.builder().build(); + orderItem.setProduct(product); + orderItem.setQuantity(1); + orderItem.setItemPrice(new BigDecimal(400)); + orderItems.add(orderItem); + order.setOrderItems(orderItems); + + Mockito.lenient().when(userRepository.findById(1L)).thenReturn(Optional.of(user)); + Mockito.lenient().when(orderRepository.findById(1L)).thenReturn(Optional.of(order)); + + OrderDto orderDto = OrderDto.builder() + .userId(1L) + .orderDate(LocalDate.of(2024,6,5)) + .sourceAddress(AddressDto.builder().zip("75000").street("Slatina 20").country("Bosnia and Herzegovina").city("Tuzla").build()) + .deliveryAddress(AddressDto.builder().zip("75000").street("Slatina 20").country("Bosnia and Herzegovina").city("Tuzla").build()) + .orderItems(Collections.emptyList()) + .build(); + + boolean updatedOrder = orderService.updateOrder(1L, orderDto); + assertTrue(updatedOrder); + } }