RolePlay/src/test/java/de/neitzel/roleplay/business/UserServiceTest.java
Konrad Neitzel 3218cb1a2a Implement user management enhancements and password change functionality
- Add change password feature in UserService, allowing users to update their password after verifying the current one.
- Introduce UpdateUserRequest model for updating user details, including username, password, and role.
- Implement RESTful endpoints for changing passwords and updating user information in AuthResource and UsersResource.
- Enhance OpenAPI specification to document new endpoints for password change and user updates.
- Create frontend components for user management, including a dedicated UsersPage for admin users to manage user accounts.
- Add unit tests for UserService to ensure correct behavior of password changes and user updates.
2026-02-22 17:55:45 +01:00

197 lines
7.7 KiB
Java

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<UserSummary> 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());
}
}