package de.neitzel.roleplay.business; import de.neitzel.roleplay.common.CreateUserRequest; import de.neitzel.roleplay.common.UpdateUserRequest; import de.neitzel.roleplay.common.UserSummary; import de.neitzel.roleplay.data.UserEntity; import de.neitzel.roleplay.data.UserRepository; import io.quarkus.elytron.security.common.BcryptUtil; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import java.util.List; import java.util.UUID; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.when; /** * Unit tests for {@link UserService}. */ @ExtendWith(MockitoExtension.class) class UserServiceTest { @Mock private UserRepository userRepository; private UserService userService; @BeforeEach void setUp() { userService = new UserService(userRepository); } @Test void listUsersReturnsMappedSummaries() { UserEntity entity = new UserEntity(); entity.setId(UUID.fromString("11111111-1111-1111-1111-111111111111")); entity.setUsername("alice"); entity.setRole("user"); when(userRepository.listAll()).thenReturn(List.of(entity)); List result = userService.listUsers(); assertEquals(1, result.size()); UserSummary summary = result.get(0); assertEquals(UUID.fromString("11111111-1111-1111-1111-111111111111"), summary.getId()); assertEquals("alice", summary.getUsername()); assertEquals("user", summary.getRole()); } @Test void createUserRejectsBlankUsername() { CreateUserRequest request = new CreateUserRequest("", "secret", "user"); IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> userService.createUser(request)); assertEquals("Username is required", e.getMessage()); } @Test void createUserRejectsBlankPassword() { CreateUserRequest request = new CreateUserRequest("alice", "", "user"); IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> userService.createUser(request)); assertEquals("Password is required", e.getMessage()); } @Test void createUserRejectsInvalidRole() { CreateUserRequest request = new CreateUserRequest("alice", "secret", "superuser"); IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> userService.createUser(request)); assertEquals("Role must be 'admin' or 'user'", e.getMessage()); } @Test void createUserRejectsDuplicateUsername() { CreateUserRequest request = new CreateUserRequest("alice", "secret", "user"); when(userRepository.findByUsername("alice")).thenReturn(new UserEntity()); IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> userService.createUser(request)); assertEquals("Username already exists: alice", e.getMessage()); } @Test void changePasswordUpdatesPasswordWhenCurrentMatches() { UserEntity entity = new UserEntity(); entity.setId(UUID.randomUUID()); entity.setUsername("alice"); entity.setPassword(BcryptUtil.bcryptHash("oldpass")); entity.setRole("user"); when(userRepository.findByUsername("alice")).thenReturn(entity); userService.changePassword("alice", "oldpass", "newpass"); assertTrue(BcryptUtil.matches("newpass", entity.getPassword())); } @Test void changePasswordThrowsWhenCurrentPasswordWrong() { UserEntity entity = new UserEntity(); entity.setUsername("alice"); entity.setPassword(BcryptUtil.bcryptHash("oldpass")); when(userRepository.findByUsername("alice")).thenReturn(entity); IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> userService.changePassword("alice", "wrong", "newpass")); assertEquals("Current password is incorrect", e.getMessage()); } @Test void changePasswordThrowsWhenUserNotFound() { when(userRepository.findByUsername("nobody")).thenReturn(null); IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> userService.changePassword("nobody", "old", "new")); assertEquals("User not found: nobody", e.getMessage()); } @Test void updateUserSuccessWithoutNewPassword() { UserEntity entity = new UserEntity(); entity.setId(UUID.fromString("22222222-2222-2222-2222-222222222222")); entity.setUsername("alice"); entity.setRole("user"); when(userRepository.findById(UUID.fromString("22222222-2222-2222-2222-222222222222"))).thenReturn(entity); // Same username as entity: uniqueness check not invoked UpdateUserRequest request = new UpdateUserRequest("alice", null, "admin"); UserSummary result = userService.updateUser(entity.getId(), request, "admin"); assertEquals("alice", result.getUsername()); assertEquals("admin", result.getRole()); assertEquals("admin", entity.getRole()); } @Test void updateUserThrowsWhenUserNotFound() { when(userRepository.findById(UUID.fromString("99999999-9999-9999-9999-999999999999"))).thenReturn(null); UpdateUserRequest request = new UpdateUserRequest("bob", "secret", "user"); IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> userService.updateUser(UUID.fromString("99999999-9999-9999-9999-999999999999"), request, "admin")); assertEquals("User not found: 99999999-9999-9999-9999-999999999999", e.getMessage()); } @Test void updateUserThrowsWhenUsernameTakenByOther() { UserEntity entity = new UserEntity(); entity.setId(UUID.fromString("22222222-2222-2222-2222-222222222222")); entity.setUsername("alice"); entity.setRole("user"); UserEntity other = new UserEntity(); other.setUsername("bob"); when(userRepository.findById(entity.getId())).thenReturn(entity); when(userRepository.findByUsername("bob")).thenReturn(other); UpdateUserRequest request = new UpdateUserRequest("bob", null, "user"); IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> userService.updateUser(entity.getId(), request, "admin")); assertEquals("Username already exists: bob", e.getMessage()); } @Test void deleteUserThrowsWhenDeletingSelf() { UserEntity entity = new UserEntity(); entity.setId(UUID.randomUUID()); entity.setUsername("admin"); entity.setRole("admin"); when(userRepository.findById(entity.getId())).thenReturn(entity); IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> userService.deleteUser(entity.getId(), "admin")); assertEquals("Cannot delete your own user account", e.getMessage()); } @Test void deleteUserThrowsWhenDeletingLastAdmin() { UserEntity entity = new UserEntity(); entity.setId(UUID.randomUUID()); entity.setUsername("soleadmin"); entity.setRole("admin"); when(userRepository.findById(entity.getId())).thenReturn(entity); when(userRepository.count("role = ?1", "admin")).thenReturn(1L); IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> userService.deleteUser(entity.getId(), "user")); assertEquals("Cannot delete the last admin user", e.getMessage()); } }