From 2edfe47ebf41be681904574081030a77887470a8 Mon Sep 17 00:00:00 2001 From: Iris Sanchez Date: Mon, 6 Oct 2025 21:59:43 +0200 Subject: [PATCH 1/4] test(user-user-service): add more edge cases tests --- .../SU/user/utils/UserServiceHelper.java | 2 +- .../SU/user/utils/UserServiceHelperTest.java | 151 +++++++++++++++++- 2 files changed, 146 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/SleepUp/SU/user/utils/UserServiceHelper.java b/src/main/java/com/SleepUp/SU/user/utils/UserServiceHelper.java index 434b888..f114da4 100644 --- a/src/main/java/com/SleepUp/SU/user/utils/UserServiceHelper.java +++ b/src/main/java/com/SleepUp/SU/user/utils/UserServiceHelper.java @@ -104,7 +104,7 @@ public User updateUserDataAdmin(UserRequestAdmin userRequestAdmin, User user){ User updatedUser = updateUser(userData, user); - Role role = userRequestAdmin.role() != null && !userRequestAdmin.role().getRoleName().isEmpty() + Role role = userRequestAdmin.role() != null ? userRequestAdmin.role() : updatedUser.getRole(); diff --git a/src/test/java/com/SleepUp/SU/user/utils/UserServiceHelperTest.java b/src/test/java/com/SleepUp/SU/user/utils/UserServiceHelperTest.java index c9d0e1a..1c2b33a 100644 --- a/src/test/java/com/SleepUp/SU/user/utils/UserServiceHelperTest.java +++ b/src/test/java/com/SleepUp/SU/user/utils/UserServiceHelperTest.java @@ -1,10 +1,16 @@ package com.SleepUp.SU.user.utils; +import com.SleepUp.SU.user.dto.UserMapper; +import com.SleepUp.SU.user.dto.UserRequest; +import com.SleepUp.SU.user.dto.UserRequestAdmin; import com.SleepUp.SU.user.entity.User; import com.SleepUp.SU.user.repository.UserRepository; +import com.SleepUp.SU.user.role.Role; +import com.SleepUp.SU.utils.EntityUtil; import com.SleepUp.SU.utils.exceptions.UserEmailAlreadyExistsException; import com.SleepUp.SU.utils.exceptions.UserUsernameAlreadyExistsException; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -13,26 +19,43 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.test.context.ActiveProfiles; +import org.springframework.transaction.annotation.Transactional; import java.util.Optional; +import java.util.function.Consumer; import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; @ActiveProfiles("test") @ExtendWith(MockitoExtension.class) public class UserServiceHelperTest { - @InjectMocks - private UserServiceHelper userServiceHelper; + @Mock + private PasswordEncoder passwordEncoder; + + @Mock + private UserMapper userMapper; @Mock private UserRepository userRepository; + @Mock + private EntityUtil entityUtil; + + @InjectMocks + private UserServiceHelper userServiceHelper; + + private UserServiceHelper spyUserServiceHelper; + + @BeforeEach + void setUp(){ + spyUserServiceHelper = spy(userServiceHelper); + } + @Nested class findById{ @@ -46,7 +69,7 @@ void findByUserId_validId_shouldReturnUser() { User result = userServiceHelper.getUserEntityById(99L); assertNotNull(result); - assertDoesNotThrow(() ->userServiceHelper.getUserEntityById(99L)); + assertDoesNotThrow(() -> userServiceHelper.getUserEntityById(99L)); assertEquals(99L, result.getId()); assertEquals("test", result.getUsername()); } @@ -89,6 +112,122 @@ void validateUserDoesNotExist_whenEmailExists_shouldThrowException() { assertEquals("User with email 'testUser@email.com' already exists", exception.getMessage()); } + } + + @Test + @Transactional + void createUser_shouldEncodePasswordAndSaveUser() { + UserRequest request = new UserRequest("user1", "name1", "user1@email.com", "pass"); + Role role = Role.USER; + + when(passwordEncoder.encode(request.password())).thenReturn("encodedPass"); + User userEntity = new User(); + when(userMapper.toEntity(request, "encodedPass", role)).thenReturn(userEntity); + when(userRepository.save(userEntity)).thenReturn(userEntity); + + User result = userServiceHelper.createUser(request, role); + + verify(passwordEncoder).encode("pass"); + verify(userMapper).toEntity(request, "encodedPass", role); + verify(userRepository).save(userEntity); + + assertSame(userEntity, result); + } + + @Nested + class UpdateUserTests { + + @Test + void updateUser_shouldValidateUsernameIfChanged() { + UserRequest request = new UserRequest("newUsername", "name", "email@example.com", "password"); + User existingUser = spy(new User()); + existingUser.setUsername("oldUsername"); + existingUser.setEmail("email@example.com"); + existingUser.setPassword("oldPass"); + + doNothing().when(spyUserServiceHelper).validateUsernameDoesNotExist("newUsername"); + doAnswer(invocation -> { + Object newValue = invocation.getArgument(0); + @SuppressWarnings("unchecked") + Consumer setter = invocation.getArgument(2); + setter.accept(newValue); + return null; + }).when(entityUtil).updateField(any(), any(), any()); + doReturn("encodedPassword").when(spyUserServiceHelper).getEncodePassword("password"); + + User updatedUser = spyUserServiceHelper.updateUser(request, existingUser); + + verify(spyUserServiceHelper).validateUsernameDoesNotExist("newUsername"); + verify(spyUserServiceHelper, never()).validateUsernameDoesNotExist("email@example.com"); + assertEquals("encodedPassword", updatedUser.getPassword()); + assertEquals("newUsername", updatedUser.getUsername()); + } + + @Test + void updateUser_shouldValidateEmailIfChanged() { + UserRequest request = new UserRequest("username", "name", "newEmail@example.com", null); + User existingUser = spy(new User()); + existingUser.setUsername("username"); + existingUser.setEmail("oldEmail@example.com"); + existingUser.setPassword("oldPass"); + + doNothing().when(entityUtil).updateField(any(), any(), any()); + + User updatedUser = spyUserServiceHelper.updateUser(request, existingUser); + + + assertEquals("oldPass", updatedUser.getPassword()); + } + + @Test + void updateUser_shouldKeepExistingPasswordWhenRequestPasswordIsNullOrEmpty() { + UserRequest request1 = new UserRequest("username", "name", "email@example.com", null); + UserRequest request2 = new UserRequest("username", "name", "email@example.com", ""); + User existingUser = new User(); + existingUser.setPassword("oldPass"); + + User updated1 = userServiceHelper.updateUser(request1, existingUser); + assertEquals("oldPass", updated1.getPassword()); + + User updated2 = userServiceHelper.updateUser(request2, existingUser); + assertEquals("oldPass", updated2.getPassword()); + } + + @Test + void updateUserData_shouldDelegateToUpdateUser() { + UserRequest request = new UserRequest("username", "name", "email@example.com", "pass"); + User user = new User(); + + doReturn(user).when(spyUserServiceHelper).updateUser(request, user); + + User result = spyUserServiceHelper.updateUserData(request, user); + + verify(spyUserServiceHelper).updateUser(request, user); + assertSame(user, result); + } + + @Test + void updateUserDataAdmin_shouldConvertAndUpdateUserRoleConditional() { + Role newRole = Role.ADMIN; + UserRequestAdmin adminRequest = new UserRequestAdmin("username", "name", "email@example.com", "pass", newRole); + User existingUser = new User(); + existingUser.setRole(Role.USER); + UserServiceHelper spyUserServiceHelper = spy(userServiceHelper); + doReturn(existingUser).when(spyUserServiceHelper).updateUser(any(UserRequest.class), eq(existingUser)); + + User updatedUser = spyUserServiceHelper.updateUserDataAdmin(adminRequest, existingUser); + + assertEquals(newRole, updatedUser.getRole()); + + UserRequestAdmin adminRequestEmptyRole = new UserRequestAdmin("username", "name", "email@example.com", "pass", null); + User user2 = new User(); + user2.setRole(newRole); + doReturn(user2).when(spyUserServiceHelper).updateUser(any(UserRequest.class), eq(user2)); + + User updatedUser2 = spyUserServiceHelper.updateUserDataAdmin(adminRequestEmptyRole, user2); + + assertEquals(newRole, updatedUser2.getRole()); + } } } From be162a3effedc7bb458a55954cc901bb9cd41034 Mon Sep 17 00:00:00 2001 From: Iris Sanchez Date: Mon, 6 Oct 2025 22:34:23 +0200 Subject: [PATCH 2/4] test(user-admin-controller): add integration tests for getUserById --- .../user/admin/UserAdminControllerTest.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/test/java/com/SleepUp/SU/user/admin/UserAdminControllerTest.java b/src/test/java/com/SleepUp/SU/user/admin/UserAdminControllerTest.java index 03d6077..7ed4afe 100644 --- a/src/test/java/com/SleepUp/SU/user/admin/UserAdminControllerTest.java +++ b/src/test/java/com/SleepUp/SU/user/admin/UserAdminControllerTest.java @@ -76,6 +76,36 @@ void setUp() { .build(); } + @Nested + class GetUserByIdTest { + @Test + void getUserById_whenAdminRole_shouldReturnUserResponse() throws Exception { + mockMvc.perform(get(USER_PATH_ID, 1L) + .with(user(adminCustomUserDetails)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.username").value("User1")) + .andExpect(jsonPath("$.name").value("Name1")) + .andExpect(jsonPath("$.email").value("user1@example.com")) + .andExpect(jsonPath("$.role").value("USER")); + } + + @Test + void getUserById_whenNotAdminRole_shouldReturnForbidden() throws Exception { + mockMvc.perform(get(USER_PATH_ID, 1L) + .with(user(userCustomUserDetails)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isForbidden()); + } + + @Test + void getUserById_whenNoAuthentication_shouldReturnUnauthorized() throws Exception { + mockMvc.perform(get(USER_PATH_ID, 1L) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isUnauthorized()); + } + } + @Nested class CreateUserTest { From 61992c64ac4d77fb99e118d61f91048817649213 Mon Sep 17 00:00:00 2001 From: Iris Sanchez Date: Mon, 6 Oct 2025 22:52:08 +0200 Subject: [PATCH 3/4] test(user-admin-service): add delete user by id test; add tests for edge cases --- .../user/admin/UserAdminServiceImplTest.java | 255 ++++++++++++++---- 1 file changed, 207 insertions(+), 48 deletions(-) diff --git a/src/test/java/com/SleepUp/SU/user/admin/UserAdminServiceImplTest.java b/src/test/java/com/SleepUp/SU/user/admin/UserAdminServiceImplTest.java index 2b68d04..9a23a53 100644 --- a/src/test/java/com/SleepUp/SU/user/admin/UserAdminServiceImplTest.java +++ b/src/test/java/com/SleepUp/SU/user/admin/UserAdminServiceImplTest.java @@ -1,41 +1,45 @@ package com.SleepUp.SU.user.admin; -import com.SleepUp.SU.user.entity.CustomUserDetails; -import com.SleepUp.SU.user.entity.User; -import com.SleepUp.SU.user.repository.UserRepository; +import com.SleepUp.SU.accommodation.entity.Accommodation; +import com.SleepUp.SU.accommodation.repository.AccommodationRepository; +import com.SleepUp.SU.reservation.entity.Reservation; +import com.SleepUp.SU.reservation.repository.ReservationRepository; +import com.SleepUp.SU.reservation.status.BookingStatus; import com.SleepUp.SU.user.dto.UserMapper; import com.SleepUp.SU.user.dto.UserRequest; import com.SleepUp.SU.user.dto.UserRequestAdmin; import com.SleepUp.SU.user.dto.UserResponse; +import com.SleepUp.SU.user.entity.CustomUserDetails; +import com.SleepUp.SU.user.entity.User; +import com.SleepUp.SU.user.repository.UserRepository; import com.SleepUp.SU.user.role.Role; import com.SleepUp.SU.user.utils.UserServiceHelper; +import com.SleepUp.SU.utils.EntityUtil; +import com.SleepUp.SU.utils.email.EmailService; import com.SleepUp.SU.utils.exceptions.UserNotFoundByIdException; +import com.SleepUp.SU.utils.exceptions.UserNotFoundByUsernameException; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.test.context.ActiveProfiles; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.ArgumentMatchers.any; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; -@ActiveProfiles("test") @ExtendWith(MockitoExtension.class) public class UserAdminServiceImplTest { @InjectMocks - private UserAdminServiceImpl userAdminServiceImpl; + private UserAdminServiceImpl userAdminService; @Mock private UserServiceHelper userServiceHelper; @@ -47,57 +51,91 @@ public class UserAdminServiceImplTest { private UserMapper userMapper; @Mock - private PasswordEncoder passwordEncoder; + private EntityUtil mapperUtil; + @Mock + private AccommodationRepository accommodationRepository; - @Nested - class LoadUserTest { + @Mock + private ReservationRepository reservationRepository; + @Mock + private EmailService emailService; + + @Nested + class GetAllUsersTests { @Test - void loadUserByUsername_existingUser_shouldReturnUserDetails() { - UserRequest userRequest = new UserRequest("userTest", "nameTest", "usertest@test.com", "password123"); - User userSaved = new User(); - userSaved.setId(1L); - userSaved.setUsername("userTest"); - userSaved.setName("nameTest"); - userSaved.setEmail("usertest@email.com"); - userSaved.setPassword("password123"); - - when(userServiceHelper.getUserEntityByUsername("userTest")).thenReturn(userSaved); - - List authorities = List.of( - new SimpleGrantedAuthority("ROLE_USER") + void getAllUsers_shouldReturnMappedUserResponses() { + List users = List.of( + new User(), + new User() ); - UserDetails userLogExpected = new CustomUserDetails(userSaved); + List responses = List.of( + new UserResponse(1L, "user1", "Name1", "email1@test.com", Role.USER), + new UserResponse(2L, "user2", "Name2", "email2@test.com", Role.ADMIN) + ); + + when(userRepository.findAll()).thenReturn(users); + when(mapperUtil.mapEntitiesToDTOs(anyList(), any())).thenReturn((List) responses); + + List result = userAdminService.getAllUsers(); + assertEquals(2, result.size()); + assertEquals("user1", result.getFirst().username()); + verify(userRepository).findAll(); + verify(mapperUtil).mapEntitiesToDTOs(anyList(), any()); + } + } + + @Nested + class GetUserByIdTests { + @Test + void getUserById_existingUser_shouldReturnResponse() { + User user = new User(); + user.setId(1L); + UserResponse userResponse = new UserResponse(1L, "user", "Name", "email@test.com", Role.USER); - UserDetails userLogResponse = userAdminServiceImpl.loadUserByUsername("userTest"); + when(userServiceHelper.getUserEntityById(1L)).thenReturn(user); + when(userMapper.toResponse(user)).thenReturn(userResponse); - assertEquals(userLogExpected.getUsername(), userLogResponse.getUsername()); - assertEquals(userLogExpected.getAuthorities(), userLogResponse.getAuthorities()); - assertEquals(userLogExpected.getPassword(), userLogResponse.getPassword()); + UserResponse result = userAdminService.getUserById(1L); + assertEquals(1L, result.id()); + assertEquals("user", result.username()); + verify(userServiceHelper).getUserEntityById(1L); + verify(userMapper).toResponse(user); } + } + @Nested + class CreateUserTests { @Test - void loadUserByUsername_nonExistingUser_shouldThrowException() { + void createUser_shouldCreateSendEmailAndReturnResponse() { + UserRequest request = new UserRequest("user", "Name", "email@test.com", "password"); + Role role = Role.USER; + User savedUser = new User(); + UserResponse response = new UserResponse(1L,"user","Name","email@test.com",role); - when(userServiceHelper.getUserEntityByUsername("userTest")) - .thenThrow(new UsernameNotFoundException("userTest does not exist.")); + when(userServiceHelper.createUser(request, role)).thenReturn(savedUser); + doNothing().when(emailService).sendWelcomeEmail(savedUser); + when(userMapper.toResponse(savedUser)).thenReturn(response); - assertThrows(UsernameNotFoundException.class, () -> userAdminServiceImpl.loadUserByUsername("userTest")); - } + UserResponse result = userAdminService.createUser(request, role); + + verify(userServiceHelper).createUser(request, role); + verify(emailService).sendWelcomeEmail(savedUser); + verify(userMapper).toResponse(savedUser); + assertEquals("user", result.username()); + } } @Nested - class UpdateUserTest { - + class UpdateUserTests { @Test - void updateUser_existingUser_shouldReturnUpdatedUser() { - + void updateUser_existingUser_shouldReturnUpdatedUserResponse() { UserRequestAdmin request = UserRequestAdmin.builder() .username("updatedUser") .name("Updated Name") @@ -128,16 +166,137 @@ void updateUser_existingUser_shouldReturnUpdatedUser() { when(userServiceHelper.getUserEntityById(1L)).thenReturn(existingUser); when(userServiceHelper.updateUserDataAdmin(eq(request), any(User.class))).thenReturn(updatedUser); - when(userMapper.toResponse(any(User.class))).thenReturn(userResponse); + when(userMapper.toResponse(updatedUser)).thenReturn(userResponse); - UserResponse response = userAdminServiceImpl.updateUser(1L, request); + UserResponse response = userAdminService.updateUser(1L, request); + + verify(userServiceHelper).getUserEntityById(1L); + verify(userServiceHelper).updateUserDataAdmin(eq(request), any(User.class)); + verify(userMapper).toResponse(updatedUser); assertEquals("updatedUser", response.username()); - assertEquals("Updated Name", response.name()); - assertEquals("updated@test.com", response.email()); assertEquals(Role.ADMIN, response.role()); + } + + @Test + void updateUser_userNotFound_shouldThrow() { + UserRequestAdmin request = UserRequestAdmin.builder().build(); + + when(userServiceHelper.getUserEntityById(1L)).thenThrow(new UserNotFoundByIdException(1L)); + + assertThrows(UserNotFoundByIdException.class, () -> userAdminService.updateUser(1L, request)); + } + } + + @Nested + class DeleteUserByIdTests { + + @Test + void deleteUserById_cannotDeleteReplacementUser() { + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> userAdminService.deleteUserById(1L)); + assertEquals("Cannot delete replacement user with ID 1", exception.getMessage()); + } + + @Test + void deleteUserById_userNotExists_shouldThrow() { + when(userRepository.existsById(2L)).thenReturn(false); + + assertThrows(UserNotFoundByIdException.class, () -> userAdminService.deleteUserById(2L)); + } + + @Test + void deleteUserById_replacementUserNotFound_shouldThrow() { + when(userRepository.existsById(3L)).thenReturn(true); + when(userRepository.findById(1L)).thenReturn(Optional.empty()); + + RuntimeException ex = assertThrows(RuntimeException.class, + () -> userAdminService.deleteUserById(3L)); + assertEquals("Replacement user with ID 1 not found", ex.getMessage()); + } + + @Test + void deleteUserById_withAccommodationsAndReservations_shouldReassignAndDelete() { + long userId = 4L; + User replacementUser = new User(); + replacementUser.setId(1L); + + Accommodation acc1 = new Accommodation(); + Accommodation acc2 = new Accommodation(); + List accommodations = List.of(acc1, acc2); + + Reservation res1 = new Reservation(); + Reservation res2 = new Reservation(); + List reservations = List.of(res1, res2); + + when(userRepository.existsById(userId)).thenReturn(true); + when(userRepository.findById(1L)).thenReturn(Optional.of(replacementUser)); + when(accommodationRepository.findByManagedBy_Id(userId)).thenReturn(accommodations); + when(reservationRepository.findByUser_Id(userId)).thenReturn(reservations); + + doNothing().when(userRepository).deleteById(userId); + + userAdminService.deleteUserById(userId); + + assertEquals(replacementUser, acc1.getManagedBy()); + assertEquals(replacementUser, acc2.getManagedBy()); + + for (Reservation reservation : reservations) { + assertEquals(replacementUser, reservation.getUser()); + assertEquals(BookingStatus.CANCELLED, reservation.getBookingStatus()); + } + verify(accommodationRepository).saveAll(accommodations); + verify(reservationRepository).saveAll(reservations); + verify(userRepository).deleteById(userId); } + @Test + void deleteUserById_noAccommodationsOrReservations_shouldDeleteDirectly() { + long userId = 5L; + User replacementUser = new User(); + + when(userRepository.existsById(userId)).thenReturn(true); + when(userRepository.findById(1L)).thenReturn(Optional.of(replacementUser)); + when(accommodationRepository.findByManagedBy_Id(userId)).thenReturn(new ArrayList<>()); + when(reservationRepository.findByUser_Id(userId)).thenReturn(new ArrayList<>()); + + doNothing().when(userRepository).deleteById(userId); + + userAdminService.deleteUserById(userId); + + verify(accommodationRepository, never()).saveAll(any()); + verify(reservationRepository, never()).saveAll(any()); + verify(userRepository).deleteById(userId); + } } + + @Nested + class LoadUserByUsernameTests { + + @Test + void loadUserByUsername_existingUser_shouldReturnCustomUserDetails() { + User user = new User(); + user.setUsername("testUser"); + + when(userServiceHelper.getUserEntityByUsername("testUser")).thenReturn(user); + + CustomUserDetails details = (CustomUserDetails) userAdminService.loadUserByUsername("testUser"); + + assertEquals("testUser", details.getUsername()); + verify(userServiceHelper).getUserEntityByUsername("testUser"); + } + + @Test + void loadUserByUsername_userNotFound_shouldThrowUsernameNotFoundException() { + when(userServiceHelper.getUserEntityByUsername("unknownUser")) + .thenThrow(new UserNotFoundByUsernameException("unknownUser")); + + UsernameNotFoundException ex = assertThrows(UsernameNotFoundException.class, + () -> userAdminService.loadUserByUsername("unknownUser")); + assertTrue(ex.getMessage().contains("unknownUser")); + verify(userServiceHelper).getUserEntityByUsername("unknownUser"); + } + } + } From d0c3e884a2dcdfc61a377f0a7b375405ca504f77 Mon Sep 17 00:00:00 2001 From: Iris Sanchez Date: Mon, 6 Oct 2025 23:32:51 +0200 Subject: [PATCH 4/4] test(reservation-owner-controller): add integration tests for getAll and update methods --- .../SleepUp/SU/security/jwt/JwtService.java | 2 +- .../ReservationOwnerControllerTest.java | 182 ++++++++++++------ 2 files changed, 124 insertions(+), 60 deletions(-) diff --git a/src/main/java/com/SleepUp/SU/security/jwt/JwtService.java b/src/main/java/com/SleepUp/SU/security/jwt/JwtService.java index f0df250..af1c598 100644 --- a/src/main/java/com/SleepUp/SU/security/jwt/JwtService.java +++ b/src/main/java/com/SleepUp/SU/security/jwt/JwtService.java @@ -34,7 +34,7 @@ public JwtService(AppProperties appProperties) { AppProperties.JwtProperties jwt = appProperties.getJwt(); this.secretKey = Keys.hmacShaKeyFor(Decoders.BASE64.decode(jwt.getSecret())); this.jwtExpirationMs = jwt.getExpirationMs(); - this.jwtRefreshExpirationMs = jwt.getRefreshExpirationMs() != null ? jwt.getRefreshExpirationMs() : DEFAULT_REFRESH_EXPIRATION_MS; + this.jwtRefreshExpirationMs = jwt.getRefreshExpirationMs(); } public String generateRefreshToken(UserDetails userDetails) { diff --git a/src/test/java/com/SleepUp/SU/reservation/accommodationOwner/ReservationOwnerControllerTest.java b/src/test/java/com/SleepUp/SU/reservation/accommodationOwner/ReservationOwnerControllerTest.java index 70f9f87..010d2b8 100644 --- a/src/test/java/com/SleepUp/SU/reservation/accommodationOwner/ReservationOwnerControllerTest.java +++ b/src/test/java/com/SleepUp/SU/reservation/accommodationOwner/ReservationOwnerControllerTest.java @@ -1,15 +1,13 @@ package com.SleepUp.SU.reservation.accommodationOwner; - import com.SleepUp.SU.reservation.dto.ReservationAuthRequest; import com.SleepUp.SU.reservation.dto.ReservationResponseDetail; -import com.SleepUp.SU.reservation.dto.ReservationResponseSummary; -import com.SleepUp.SU.reservation.reservationGuest.ReservationGuestServiceImpl; +import com.SleepUp.SU.reservation.entity.Reservation; +import com.SleepUp.SU.reservation.repository.ReservationRepository; import com.SleepUp.SU.reservation.status.BookingStatus; import com.SleepUp.SU.user.entity.CustomUserDetails; import com.SleepUp.SU.user.entity.User; import com.SleepUp.SU.user.repository.UserRepository; -import com.SleepUp.SU.user.role.Role; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; @@ -18,36 +16,32 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; -import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.servlet.MockMvc; +import org.springframework.transaction.annotation.Transactional; import java.time.LocalDate; import java.time.LocalDateTime; -import java.util.List; -import static org.mockito.Mockito.when; -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.anonymous; +import static org.hamcrest.Matchers.containsString; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @SpringBootTest @AutoConfigureMockMvc @ActiveProfiles("test") -@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) -public class ReservationOwnerControllerTest { +@Transactional +class ReservationOwnerControllerTest { - private static final String RESERVATIONS_ACCOMMODATION_PATH = "/reservations/accommodation/{id}"; private static final String RESERVATION_STATUS_PATH = "/reservations/{id}/status"; - private static final String RESERVATION_BY_ID_PATH = "/reservations/{id}"; @Autowired private MockMvc mockMvc; - @MockitoBean - private ReservationOwnerService reservationOwnerServiceImpl; + @Autowired + private ReservationRepository reservationRepository; @Autowired private ObjectMapper objectMapper; @@ -55,54 +49,124 @@ public class ReservationOwnerControllerTest { @Autowired private UserRepository userRepository; - private CustomUserDetails principal; - private Long accommodationId = 123L; + private CustomUserDetails customUserDetailsGuest; + private CustomUserDetails customUserDetailsOwner; + private Reservation reservation; @BeforeEach - void setUp() { - User testUser = userRepository.findByUsername("TestUser").orElseGet(() -> { - User u = new User(); - u.setUsername("TestUser"); - u.setEmail("testuser@example.com"); - u.setName("Test User"); - u.setRole(Role.USER); - return userRepository.save(u); - }); - principal = new CustomUserDetails(testUser); + public void setUp() { + User savedUser = userRepository.findByUsername("User2") + .orElseThrow(() -> new RuntimeException("User2 not found")); + + customUserDetailsGuest = new CustomUserDetails(savedUser); + + reservation = reservationRepository.findByUser_Id(savedUser.getId()).getFirst(); + + User owner = reservation.getAccommodation().getManagedBy(); + + customUserDetailsOwner = new CustomUserDetails(owner); + } -// @Nested -// class UpdateReservationStatusTest { -// @Test -// void updateReservationStatus_authorized_shouldReturnOk() throws Exception { -// Long id = 42L; -// ReservationAuthRequest authRequest = new ReservationAuthRequest(BookingStatus.CANCELLED); -// -// ReservationResponseDetail detailDto = new ReservationResponseDetail( -// 42L, -// "alice", -// 2, -// "Beach House", -// LocalDate.of(2025, 9, 25), -// LocalDate.of(2025, 9, 30), -// BookingStatus.CANCELLED, -// true, -// LocalDateTime.of(2025, 9, 1, 10, 30) -// ); -// -// when(reservationOwnerServiceImpl.updateStatus(id, authRequest)) -// .thenReturn(detailDto); -// -// mockMvc.perform(patch(RESERVATION_STATUS_PATH, id) -// .with(user(principal)) -// .contentType(MediaType.APPLICATION_JSON) -// .content(objectMapper.writeValueAsString(authRequest))) -// .andExpect(status().isOk()) -// .andExpect(content().json(objectMapper.writeValueAsString(detailDto))); -// } -// -// } + @Nested + class GetAllReservationsForMyAccommodationTest { + + @Test + void getAllReservations_authorized_shouldReturnList() throws Exception { + Long accommodationId = reservation.getAccommodation().getId(); + + mockMvc.perform(get("/accommodations/{id}/reservations", accommodationId) + .with(user(customUserDetailsOwner))) + .andExpect(status().isOk()) + .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$").isArray()) + .andExpect(jsonPath("$[0].id").value(1)) + .andExpect(jsonPath("$[0].userName").value("Name4")) + .andExpect(jsonPath("$[0].guestNumber").isEmpty()) + .andExpect(jsonPath("$[0].accommodationName").value("Hotel ABC")) + .andExpect(jsonPath("$[0].checkInDate").value("2025-09-21")) + .andExpect(jsonPath("$[0].checkOutDate").value("2025-09-24")) + .andExpect(jsonPath("$[0].bookingStatus").value("CONFIRMED")) + .andExpect(jsonPath("$[0].totalPrice").value(450.00)); + } + + @Test + void getAllReservations_whenNotOwner_shouldReturnForbidden() throws Exception { + Long accommodationId = reservation.getAccommodation().getId(); + + mockMvc.perform(get("/accommodations/{id}/reservations", accommodationId) + .with(user(customUserDetailsGuest))) + .andDo(print()) + .andExpect(status().isForbidden()) + .andExpect(jsonPath("$.message").value(containsString("Access denied"))); + } + } -} + @Nested + class UpdateReservationStatusTest { + @Test + void updateReservationStatus_authorized_shouldReturnOk() throws Exception { + Long id = reservation.getId(); + ReservationAuthRequest authRequest = new ReservationAuthRequest(BookingStatus.CANCELLED); + + ReservationResponseDetail detailDto = new ReservationResponseDetail( + id, + "alice", + 2, + "Beach House", + LocalDate.of(2025, 9, 25), + LocalDate.of(2025, 9, 30), + BookingStatus.CANCELLED, + true, + LocalDateTime.of(2025, 9, 1, 10, 30), null + ); + + mockMvc.perform(patch(RESERVATION_STATUS_PATH, id) + .with(user(customUserDetailsOwner)) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(authRequest))) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").value(2)) + .andExpect(jsonPath("$.userName").value("Name2")) + .andExpect(jsonPath("$.guestNumber").isEmpty()) + .andExpect(jsonPath("$.accommodationName").value("Hotel ABC")) + .andExpect(jsonPath("$.checkInDate").value("2025-10-02")) + .andExpect(jsonPath("$.checkOutDate").value("2025-10-06")) + .andExpect(jsonPath("$.bookingStatus").value("CANCELLED")) + .andExpect(jsonPath("$.emailSent").value(false)) + .andExpect(jsonPath("$.totalPrice").value(600.00)); + + } + + @Test + void updateReservationStatus_whenNotAccommodationOwner_shouldThrowForbidden() throws Exception { + Long id = reservation.getId(); + ReservationAuthRequest authRequest = new ReservationAuthRequest(BookingStatus.CANCELLED); + + ReservationResponseDetail detailDto = new ReservationResponseDetail( + id, + "alice", + 2, + "Beach House", + LocalDate.of(2025, 9, 25), + LocalDate.of(2025, 9, 30), + BookingStatus.CANCELLED, + true, + LocalDateTime.of(2025, 9, 1, 10, 30), null + ); + + mockMvc.perform(patch(RESERVATION_STATUS_PATH, id) + .with(user(customUserDetailsGuest)) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(authRequest))) + .andDo(print()) + .andExpect(status().isForbidden()) + .andExpect(jsonPath("$.message").value(containsString("This reservation does not belong to any of your accommodations."))); + + } + + } +}