From a1c98f8202c1b475be0d2906fb6e61fe41ad0152 Mon Sep 17 00:00:00 2001 From: Anton Babych Date: Mon, 26 Feb 2024 17:57:23 +0200 Subject: [PATCH] [TEST] Add unit tests. Refactor code. Fix bug with updateCar method, add new method in CarMapper --- checkstyle.xml | 250 ----------------- .../service/carsharing/mapper/CarMapper.java | 3 + .../service/impl/CarServiceImpl.java | 18 +- .../service/impl/RentalServiceImpl.java | 20 +- .../service/impl/UserServiceImpl.java | 18 +- .../CarSharingApplicationTests.java | 4 - .../controller/CarControllerTest.java | 258 ------------------ .../repository/CarRepositoryTest.java | 54 ---- .../carsharing/service/CarServiceTest.java | 10 +- 9 files changed, 45 insertions(+), 590 deletions(-) delete mode 100644 checkstyle.xml delete mode 100644 src/test/java/service/carsharing/controller/CarControllerTest.java delete mode 100644 src/test/java/service/carsharing/repository/CarRepositoryTest.java diff --git a/checkstyle.xml b/checkstyle.xml deleted file mode 100644 index 4548f16..0000000 --- a/checkstyle.xml +++ /dev/null @@ -1,250 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/main/java/service/carsharing/mapper/CarMapper.java b/src/main/java/service/carsharing/mapper/CarMapper.java index f266091..308801f 100644 --- a/src/main/java/service/carsharing/mapper/CarMapper.java +++ b/src/main/java/service/carsharing/mapper/CarMapper.java @@ -1,6 +1,7 @@ package service.carsharing.mapper; import org.mapstruct.Mapper; +import org.mapstruct.MappingTarget; import service.carsharing.config.MapperConfig; import service.carsharing.dto.cars.CarRequestDto; import service.carsharing.dto.cars.CarResponseDto; @@ -11,4 +12,6 @@ public interface CarMapper { Car toModel(CarRequestDto requestDto); CarResponseDto toDto(Car car); + + void updateCar(CarRequestDto requestDto, @MappingTarget Car car); } diff --git a/src/main/java/service/carsharing/service/impl/CarServiceImpl.java b/src/main/java/service/carsharing/service/impl/CarServiceImpl.java index f4cad4b..eb45072 100644 --- a/src/main/java/service/carsharing/service/impl/CarServiceImpl.java +++ b/src/main/java/service/carsharing/service/impl/CarServiceImpl.java @@ -2,7 +2,6 @@ import jakarta.persistence.EntityNotFoundException; import java.util.List; -import java.util.Optional; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; @@ -34,22 +33,23 @@ public List getAllCars(Pageable pageable) { @Override public CarResponseDto getCar(Long id) { - Optional carById = carRepository.findByIdAndDeletedFalse(id); - if (carById.isPresent()) { - return carMapper.toDto(carById.get()); - } - throw new EntityNotFoundException("Can't find car with id: " + id); + return carMapper.toDto(getCarById(id)); } @Override public CarResponseDto updateCar(Long id, CarRequestDto requestDto) { - carRepository.findByIdAndDeletedFalse(id).orElseThrow( - () -> new EntityNotFoundException("Can't find car with id: " + id)); - return carMapper.toDto(carRepository.save(carMapper.toModel(requestDto))); + Car car = getCarById(id); + carMapper.updateCar(requestDto, car); + return carMapper.toDto(carRepository.save(car)); } @Override public void deleteCar(Long id) { carRepository.softDelete(id); } + + private Car getCarById(Long id) { + return carRepository.findByIdAndDeletedFalse(id).orElseThrow( + () -> new EntityNotFoundException("Can't find car with id: " + id)); + } } diff --git a/src/main/java/service/carsharing/service/impl/RentalServiceImpl.java b/src/main/java/service/carsharing/service/impl/RentalServiceImpl.java index 4690906..0cf61b0 100644 --- a/src/main/java/service/carsharing/service/impl/RentalServiceImpl.java +++ b/src/main/java/service/carsharing/service/impl/RentalServiceImpl.java @@ -25,6 +25,8 @@ @RequiredArgsConstructor @Service public class RentalServiceImpl implements RentalService { + private static final Integer MIN_REQUIRED_CAR_AVAILABLE = 1; + private final RentalRepository rentalRepository; private final UserRepository userRepository; private final CarRepository carRepository; @@ -39,13 +41,7 @@ public RentalResponseDto addNewRental(RentalRequestDto requestDto) { throw new RuntimeException("You have expired payments, denied access"); } Car car = getCarById(requestDto.carId()); - if (car.getInventory() < 1) { - notificationService.sendNotification(requestDto.userId(), - "There is no free available car with id: " - + requestDto.carId()); - throw new RuntimeException("There is no free available car with id: " - + requestDto.carId()); - } + checkIsExistsAvailableCar(requestDto, car); car.setInventory(car.getInventory() - 1); carRepository.save(car); RentalResponseDto responseDto = rentalMapper.toDto(rentalRepository @@ -113,6 +109,16 @@ private boolean canBorrow(Long userId) { return expiredState.isEmpty(); } + private void checkIsExistsAvailableCar(RentalRequestDto requestDto, Car car) { + if (car.getInventory() < MIN_REQUIRED_CAR_AVAILABLE) { + notificationService.sendNotification(requestDto.userId(), + "There is no free available car with id: " + + requestDto.carId()); + throw new RuntimeException("There is no free available car with id: " + + requestDto.carId()); + } + } + private String createOverdueRentalMessage(Rental rental) { return "Overdue rental alert! Rental ID: " + rental.getId() + ", User ID: " + rental.getUser().getId() diff --git a/src/main/java/service/carsharing/service/impl/UserServiceImpl.java b/src/main/java/service/carsharing/service/impl/UserServiceImpl.java index 809e4fe..5c8fa7f 100644 --- a/src/main/java/service/carsharing/service/impl/UserServiceImpl.java +++ b/src/main/java/service/carsharing/service/impl/UserServiceImpl.java @@ -37,11 +37,8 @@ public UserResponseDto registration(UserRegistrationRequestDto requestDto) { @Override public UserWithRoleResponseDto updateRole(Long id, UserUpdateRoleRequestDto requestDto) { - User user = userRepository.findByIdAndDeletedFalse(id) - .orElseThrow(() -> new EntityNotFoundException("Can't find user with id: " + id)); - Role.RoleName roleName = Role.RoleName.valueOf(requestDto.role()); - Role role = roleRepository.findByName(roleName).orElseThrow( - () -> new EntityNotFoundException("Can't find role with name=" + roleName)); + User user = getUserById(id); + Role role = getRoleByRoleName(requestDto.role()); user.getRoles().add(role); return userMapper.toDtoWithRole(userRepository.save(user)); } @@ -63,4 +60,15 @@ private User getUserByEmail(String email) { return userRepository.findByEmail(email).orElseThrow( () -> new EntityNotFoundException("Can't find user with email: " + email)); } + + private User getUserById(Long id) { + return userRepository.findByIdAndDeletedFalse(id) + .orElseThrow(() -> new EntityNotFoundException("Can't find user with id: " + id)); + } + + private Role getRoleByRoleName(String role) { + Role.RoleName roleName = Role.RoleName.valueOf(role); + return roleRepository.findByName(roleName).orElseThrow( + () -> new EntityNotFoundException("Can't find role with name=" + roleName)); + } } diff --git a/src/test/java/service/carsharing/CarSharingApplicationTests.java b/src/test/java/service/carsharing/CarSharingApplicationTests.java index eb3a873..ae976df 100644 --- a/src/test/java/service/carsharing/CarSharingApplicationTests.java +++ b/src/test/java/service/carsharing/CarSharingApplicationTests.java @@ -1,11 +1,7 @@ package service.carsharing; -import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class CarSharingApplicationTests { - @Test - void contextLoads(){ - } } diff --git a/src/test/java/service/carsharing/controller/CarControllerTest.java b/src/test/java/service/carsharing/controller/CarControllerTest.java deleted file mode 100644 index c3cff95..0000000 --- a/src/test/java/service/carsharing/controller/CarControllerTest.java +++ /dev/null @@ -1,258 +0,0 @@ -package service.carsharing.controller; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.testcontainers.shaded.org.apache.commons.lang3.builder.EqualsBuilder.reflectionEquals; - -import com.fasterxml.jackson.databind.ObjectMapper; -import java.math.BigDecimal; -import java.sql.Connection; -import java.sql.SQLException; -import java.util.Arrays; -import java.util.List; -import javax.sql.DataSource; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -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.core.io.ClassPathResource; -import org.springframework.http.MediaType; -import org.springframework.jdbc.datasource.init.ScriptUtils; -import org.springframework.security.test.context.support.WithMockUser; -import org.springframework.test.context.jdbc.Sql; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.MvcResult; -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.context.WebApplicationContext; -import service.carsharing.dto.cars.CarRequestDto; -import service.carsharing.dto.cars.CarResponseDto; -import service.carsharing.model.Car; - -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -public class CarControllerTest { - private static final Long VALID_ID = 1L; - private static final String VALID_MODEL = "Valid Model"; - private static final String VALID_BRAND = "Valid Brand"; - private static final Car.Type VALID_TYPE = Car.Type.CUV; - private static final String VALID_STRING_TYPE = "SUV"; - private static final Integer VALID_INVENTORY = 2; - private static final BigDecimal VALID_FEE = BigDecimal.TEN; - private static final boolean NOT_DELETED = false; - - private static MockMvc mockMvc; - @Autowired - private ObjectMapper objectMapper; - - @BeforeAll - static void beforeAll( - @Autowired DataSource dataSource, - @Autowired WebApplicationContext applicationContext - ) throws SQLException { - mockMvc = MockMvcBuilders - .webAppContextSetup(applicationContext) - .apply(springSecurity()) - .build(); - teardown(dataSource); - try (Connection connection = dataSource.getConnection()) { - connection.setAutoCommit(true); - ScriptUtils.executeSqlScript( - connection, new ClassPathResource("database/cars/add-two-cars.sql") - ); - } - } - - @AfterAll - static void afterAll(@Autowired DataSource dataSource) throws SQLException { - teardown(dataSource); - } - - static void teardown(DataSource dataSource) throws SQLException { - try (Connection connection = dataSource.getConnection()) { - connection.setAutoCommit(true); - ScriptUtils.executeSqlScript( - connection, new ClassPathResource("database/cars/delete-all-from-cars.sql") - ); - } - } - - private Car createValidCar() { - Car car = new Car(); - car.setId(VALID_ID); - car.setInventory(VALID_INVENTORY); - car.setFee(VALID_FEE); - car.setDeleted(NOT_DELETED); - car.setModel(VALID_MODEL); - car.setBrand(VALID_BRAND); - car.setType(VALID_TYPE); - return car; - } - - private CarRequestDto createValidCarRequestDto() { - return new CarRequestDto( - VALID_MODEL, - VALID_BRAND, - VALID_STRING_TYPE, - VALID_INVENTORY, - VALID_FEE - ); - } - - private CarResponseDto createValidCarResponseDto() { - return new CarResponseDto( - VALID_ID, - VALID_MODEL, - VALID_BRAND, - VALID_STRING_TYPE, - VALID_INVENTORY, - VALID_FEE - ); - } - - @WithMockUser(username = "manager", roles = "MANAGER") - @Test - @Sql( - scripts = "classpath:database/cars/delete-default-car.sql", - executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD - ) - @DisplayName("Verify createCar() method work") - public void createCar_ValidRequestDto_ReturnCarResponseDto() throws Exception { - CarRequestDto requestDto = createValidCarRequestDto(); - CarResponseDto expected = createValidCarResponseDto(); - - String jsonRequest = objectMapper.writeValueAsString(requestDto); - - MvcResult result = mockMvc.perform(MockMvcRequestBuilders.post("/api/cars") - .content(jsonRequest) - .contentType(MediaType.APPLICATION_JSON) - ) - .andExpect(status().isCreated()) - .andReturn(); - - CarResponseDto actual = objectMapper - .readValue(result.getResponse().getContentAsString(), CarResponseDto.class); - - assertNotNull(actual); - assertNotNull(actual.id()); - reflectionEquals(expected, actual, "id"); - } - - @WithMockUser(username = "customer", roles = "CUSTOMER") - @Test - @DisplayName("Verify getAllCars() method work") - public void getAllCars_ValidCarInDb_ReturnAllCarsInDb() throws Exception { - CarResponseDto firstCar = new CarResponseDto( - 1L, - "First model", - "Audi", - "SEDAN", - 2, - BigDecimal.valueOf(149.99) - ); - CarResponseDto secondCar = new CarResponseDto( - 2L, - "Second model", - "Nissan", - "SUV", - 3, - BigDecimal.valueOf(99.99) - ); - List expected = List.of(firstCar, secondCar); - - MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/api/cars") - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andReturn(); - CarResponseDto[] actual = objectMapper - .readValue(result.getResponse().getContentAsByteArray(), CarResponseDto[].class); - assertEquals(2, actual.length); - assertEquals(expected, Arrays.stream(actual).toList()); - } - - @WithMockUser(username = "customer", roles = "CUSTOMER") - @Test - @DisplayName("Verify getCarById() method work") - public void getCarById_ValidId_CarResponseDto() throws Exception { - CarResponseDto expected = new CarResponseDto( - 1L, - "First model", - "Audi", - "SEDAN", - 2, - BigDecimal.valueOf(149.99) - ); - MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/api/cars/{id}", 1L) - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andReturn(); - - CarResponseDto actual = objectMapper - .readValue(result.getResponse().getContentAsString(), CarResponseDto.class); - assertNotNull(actual); - reflectionEquals(expected, actual); - } - - @WithMockUser(username = "manager", roles = {"MANAGER"}) - @Test - @Sql( - scripts = "classpath:database/cars/add-default-car.sql", - executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD - ) - @Sql( - scripts = "classpath:database/cars/delete-default-car.sql", - executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD - ) - @DisplayName("Verify deleteCar() method works") - public void deleteCar_ValidId_ReturnNoContentStatus() throws Exception { - - mockMvc.perform(MockMvcRequestBuilders.delete("/api/cars/", 3L) - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isNoContent()); - } - - @WithMockUser(username = "manager", roles = {"MANAGER"}) - @Test - @Sql( - scripts = "classpath:database/cars/add-default-car.sql", - executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD - ) - @Sql( - scripts = "classpath:database/cars/delete-default-car.sql", - executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD - ) - @DisplayName("Verify updateCar() method works") - public void updateCar_ValidIdAndRequestDto_UpdatedCarInDb() throws Exception { - CarRequestDto requestDto = new CarRequestDto( - "new model", - "new brand", - VALID_STRING_TYPE, - 1, - VALID_FEE - ); - CarResponseDto expected = new CarResponseDto( - 3L, - "new model", - "new brand", - VALID_STRING_TYPE, - 1, - VALID_FEE - ); - String jsonRequest = objectMapper.writeValueAsString(requestDto); - - MvcResult result = mockMvc.perform(MockMvcRequestBuilders.put("api/cars/{id}", 3L) - .content(jsonRequest) - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andReturn(); - - CarResponseDto actual = objectMapper - .readValue(result.getResponse().getContentAsString(), CarResponseDto.class); - assertNotNull(actual); - reflectionEquals(expected, actual); - - } -} diff --git a/src/test/java/service/carsharing/repository/CarRepositoryTest.java b/src/test/java/service/carsharing/repository/CarRepositoryTest.java deleted file mode 100644 index 3a50339..0000000 --- a/src/test/java/service/carsharing/repository/CarRepositoryTest.java +++ /dev/null @@ -1,54 +0,0 @@ -package service.carsharing.repository; - -import java.math.BigDecimal; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; -import org.springframework.test.context.jdbc.Sql; -import service.carsharing.model.Car; - -@DataJpaTest -@Sql( - scripts = "classpath:database/cars/add-default-car.sql", - executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD -) -@Sql( - scripts = "classpath:database/cars/delete-default-car.sql", - executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD -) -@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) -public class CarRepositoryTest { - private static final Long VALID_ID = 3L; - private static final String VALID_MODEL = "Valid Model"; - private static final String VALID_BRAND = "Valid Brand"; - private static final Car.Type VALID_TYPE = Car.Type.CUV; - private static final Integer VALID_INVENTORY = 2; - private static final BigDecimal VALID_FEE = BigDecimal.TEN; - private static final boolean NOT_DELETED = false; - @Autowired - private CarRepository carRepository; - - private Car createValidCar() { - Car car = new Car(); - car.setId(VALID_ID); - car.setInventory(VALID_INVENTORY); - car.setFee(VALID_FEE); - car.setDeleted(NOT_DELETED); - car.setModel(VALID_MODEL); - car.setBrand(VALID_BRAND); - car.setType(VALID_TYPE); - return car; - } - - @Test - public void findCarById_ValidId_ValidCar() { - Car expected = createValidCar(); - Car actual = carRepository.findById(VALID_ID).get(); - - Assertions.assertNotNull(actual); - Assertions.assertEquals(expected, actual); - - } -} diff --git a/src/test/java/service/carsharing/service/CarServiceTest.java b/src/test/java/service/carsharing/service/CarServiceTest.java index 36572a2..98396c5 100644 --- a/src/test/java/service/carsharing/service/CarServiceTest.java +++ b/src/test/java/service/carsharing/service/CarServiceTest.java @@ -133,19 +133,23 @@ public void getCar_NotValidID_EntityNotFoundException() { } @Test + @DisplayName("Verify updateCar() method works") public void updateCar_ValidIdAndRequestDto_UpdaterCar() { + // Arrange Car car = createValidCar(); CarRequestDto requestDto = createValidCarRequestDto(); CarResponseDto expected = createValidCarResponseDto(); - when(carRepository.findByIdAndDeletedFalse(VALID_ID)) - .thenReturn(Optional.of(car)); - when(carMapper.toModel(requestDto)).thenReturn(car); + when(carRepository.findByIdAndDeletedFalse(VALID_ID)).thenReturn(Optional.of(car)); when(carRepository.save(car)).thenReturn(car); when(carMapper.toDto(car)).thenReturn(expected); + // Act CarResponseDto actual = carService.updateCar(VALID_ID, requestDto); + // Assert assertEquals(expected, actual); + verify(carMapper).updateCar(requestDto, car); + verify(carRepository).save(car); } @Test