diff --git a/CODE_REFACTOR_PLAN.md b/CODE_REFACTOR_PLAN.md index a1bc3c0..744abb9 100644 --- a/CODE_REFACTOR_PLAN.md +++ b/CODE_REFACTOR_PLAN.md @@ -210,7 +210,7 @@ all identified issues with an aggressive approach to modernization. ## Phase 5: Security Enhancements (Days 10-11) -### 5.1 JWT Authentication Filter Improvements ⬜ +### 5.1 JWT Authentication Filter Improvements ✅ **File**: `JwtAuthenticationFilter.java` @@ -224,7 +224,7 @@ all identified issues with an aggressive approach to modernization. **Breaking Changes**: None (internal improvements) -### 5.2 JWT Entry Point ⬜ +### 5.2 JWT Entry Point ✅ **File**: `JwtAuthenticationEntryPoint.java` @@ -240,7 +240,7 @@ all identified issues with an aggressive approach to modernization. ## Phase 6: Functional Programming Patterns (Days 12-13) -### 6.1 Mapper Enhancements ⬜ +### 6.1 Mapper Enhancements ✅ **File**: `UserMapper.java` @@ -253,7 +253,7 @@ all identified issues with an aggressive approach to modernization. **Breaking Changes**: Return type changes to Optional (BREAKING) -### 6.2 Service Method Signatures ⬜ +### 6.2 Service Method Signatures ✅ **Files**: All service classes @@ -267,7 +267,7 @@ all identified issues with an aggressive approach to modernization. **Breaking Changes**: Method signatures change (BREAKING) -### 6.3 DTO Enhancements ⬜ +### 6.3 DTO Enhancements ✅ **Files**: All DTO records diff --git a/src/main/java/org/nkcoder/dto/auth/LoginRequest.java b/src/main/java/org/nkcoder/dto/auth/LoginRequest.java index 5f7a10f..f3f0225 100644 --- a/src/main/java/org/nkcoder/dto/auth/LoginRequest.java +++ b/src/main/java/org/nkcoder/dto/auth/LoginRequest.java @@ -1,8 +1,12 @@ package org.nkcoder.dto.auth; +import static org.nkcoder.validation.ValidationMessages.EMAIL_INVALID; +import static org.nkcoder.validation.ValidationMessages.EMAIL_REQUIRED; +import static org.nkcoder.validation.ValidationMessages.PASSWORD_REQUIRED; + import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotBlank; public record LoginRequest( - @NotBlank(message = "Email is required") @Email(message = "Please provide a valid email") String email, - @NotBlank(message = "Password is required") String password) {} + @NotBlank(message = EMAIL_REQUIRED) @Email(message = EMAIL_INVALID) String email, + @NotBlank(message = PASSWORD_REQUIRED) String password) {} diff --git a/src/main/java/org/nkcoder/dto/auth/RefreshTokenRequest.java b/src/main/java/org/nkcoder/dto/auth/RefreshTokenRequest.java index 0fd30c5..c43994d 100644 --- a/src/main/java/org/nkcoder/dto/auth/RefreshTokenRequest.java +++ b/src/main/java/org/nkcoder/dto/auth/RefreshTokenRequest.java @@ -1,6 +1,8 @@ package org.nkcoder.dto.auth; +import static org.nkcoder.validation.ValidationMessages.REFRESH_TOKEN_REQUIRED; + import jakarta.validation.constraints.NotBlank; public record RefreshTokenRequest( - @NotBlank(message = "Refresh token is required") String refreshToken) {} + @NotBlank(message = REFRESH_TOKEN_REQUIRED) String refreshToken) {} diff --git a/src/main/java/org/nkcoder/dto/auth/RegisterRequest.java b/src/main/java/org/nkcoder/dto/auth/RegisterRequest.java index 4987a96..a994651 100644 --- a/src/main/java/org/nkcoder/dto/auth/RegisterRequest.java +++ b/src/main/java/org/nkcoder/dto/auth/RegisterRequest.java @@ -1,10 +1,16 @@ package org.nkcoder.dto.auth; -import static org.nkcoder.validation.PasswordValidation.COMPLEXITY_PATTERN; -import static org.nkcoder.validation.PasswordValidation.MIN_LENGTH; -import static org.nkcoder.validation.PasswordValidation.PASSWORD_COMPLEXITY; -import static org.nkcoder.validation.PasswordValidation.PASSWORD_MIN_LENGTH; -import static org.nkcoder.validation.PasswordValidation.PASSWORD_REQUIRED; +import static org.nkcoder.validation.ValidationMessages.EMAIL_INVALID; +import static org.nkcoder.validation.ValidationMessages.EMAIL_REQUIRED; +import static org.nkcoder.validation.ValidationMessages.NAME_MAX_LENGTH; +import static org.nkcoder.validation.ValidationMessages.NAME_MIN_LENGTH; +import static org.nkcoder.validation.ValidationMessages.NAME_REQUIRED; +import static org.nkcoder.validation.ValidationMessages.NAME_SIZE; +import static org.nkcoder.validation.ValidationMessages.PASSWORD_COMPLEXITY; +import static org.nkcoder.validation.ValidationMessages.PASSWORD_COMPLEXITY_PATTERN; +import static org.nkcoder.validation.ValidationMessages.PASSWORD_MIN_LENGTH; +import static org.nkcoder.validation.ValidationMessages.PASSWORD_REQUIRED; +import static org.nkcoder.validation.ValidationMessages.PASSWORD_SIZE; import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotBlank; @@ -13,7 +19,17 @@ import org.nkcoder.enums.Role; public record RegisterRequest( - @NotBlank(message = "Email is required") @Email(message = "Please provide a valid email") String email, - @NotBlank(message = PASSWORD_REQUIRED) @Size(min = MIN_LENGTH, message = PASSWORD_MIN_LENGTH) @Pattern(regexp = COMPLEXITY_PATTERN, message = PASSWORD_COMPLEXITY) String password, - @NotBlank(message = "Name is required") @Size(min = 2, max = 50, message = "Name must be between 2 and 50 " + "characters") String name, - Role role) {} + @NotBlank(message = EMAIL_REQUIRED) @Email(message = EMAIL_INVALID) String email, + @NotBlank(message = PASSWORD_REQUIRED) @Size(min = PASSWORD_MIN_LENGTH, message = PASSWORD_SIZE) @Pattern(regexp = PASSWORD_COMPLEXITY_PATTERN, message = PASSWORD_COMPLEXITY) String password, + @NotBlank(message = NAME_REQUIRED) @Size(min = NAME_MIN_LENGTH, max = NAME_MAX_LENGTH, message = NAME_SIZE) String name, + Role role) { + // Compact constructor that normalizes email to lowercase + public RegisterRequest { + if (email != null) { + email = email.toLowerCase().trim(); + } + if (name != null) { + name = name.trim(); + } + } +} diff --git a/src/main/java/org/nkcoder/dto/user/ChangePasswordRequest.java b/src/main/java/org/nkcoder/dto/user/ChangePasswordRequest.java index 9b1cfc8..6831a1b 100644 --- a/src/main/java/org/nkcoder/dto/user/ChangePasswordRequest.java +++ b/src/main/java/org/nkcoder/dto/user/ChangePasswordRequest.java @@ -1,12 +1,12 @@ package org.nkcoder.dto.user; -import static org.nkcoder.validation.PasswordValidation.COMPLEXITY_PATTERN; -import static org.nkcoder.validation.PasswordValidation.CONFIRM_PASSWORD_REQUIRED; -import static org.nkcoder.validation.PasswordValidation.CURRENT_PASSWORD_REQUIRED; -import static org.nkcoder.validation.PasswordValidation.MIN_LENGTH; -import static org.nkcoder.validation.PasswordValidation.PASSWORD_COMPLEXITY; -import static org.nkcoder.validation.PasswordValidation.PASSWORD_MIN_LENGTH; -import static org.nkcoder.validation.PasswordValidation.PASSWORD_REQUIRED; +import static org.nkcoder.validation.ValidationMessages.CONFIRM_PASSWORD_REQUIRED; +import static org.nkcoder.validation.ValidationMessages.CURRENT_PASSWORD_REQUIRED; +import static org.nkcoder.validation.ValidationMessages.PASSWORD_COMPLEXITY; +import static org.nkcoder.validation.ValidationMessages.PASSWORD_COMPLEXITY_PATTERN; +import static org.nkcoder.validation.ValidationMessages.PASSWORD_MIN_LENGTH; +import static org.nkcoder.validation.ValidationMessages.PASSWORD_REQUIRED; +import static org.nkcoder.validation.ValidationMessages.PASSWORD_SIZE; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Pattern; @@ -16,5 +16,5 @@ @PasswordMatch public record ChangePasswordRequest( @NotBlank(message = CURRENT_PASSWORD_REQUIRED) String currentPassword, - @NotBlank(message = PASSWORD_REQUIRED) @Size(min = MIN_LENGTH, message = PASSWORD_MIN_LENGTH) @Pattern(regexp = COMPLEXITY_PATTERN, message = PASSWORD_COMPLEXITY) String newPassword, + @NotBlank(message = PASSWORD_REQUIRED) @Size(min = PASSWORD_MIN_LENGTH, message = PASSWORD_SIZE) @Pattern(regexp = PASSWORD_COMPLEXITY_PATTERN, message = PASSWORD_COMPLEXITY) String newPassword, @NotBlank(message = CONFIRM_PASSWORD_REQUIRED) String confirmPassword) {} diff --git a/src/main/java/org/nkcoder/dto/user/UpdateProfileRequest.java b/src/main/java/org/nkcoder/dto/user/UpdateProfileRequest.java index 60a7689..d05b82e 100644 --- a/src/main/java/org/nkcoder/dto/user/UpdateProfileRequest.java +++ b/src/main/java/org/nkcoder/dto/user/UpdateProfileRequest.java @@ -1,8 +1,13 @@ package org.nkcoder.dto.user; +import static org.nkcoder.validation.ValidationMessages.EMAIL_INVALID; +import static org.nkcoder.validation.ValidationMessages.NAME_MAX_LENGTH; +import static org.nkcoder.validation.ValidationMessages.NAME_MIN_LENGTH; +import static org.nkcoder.validation.ValidationMessages.NAME_SIZE; + import jakarta.validation.constraints.Email; import jakarta.validation.constraints.Size; public record UpdateProfileRequest( - @Email(message = "Please provide a valid email") String email, - @Size(min = 2, max = 50, message = "Name must be between 2 and 50 characters") String name) {} + @Email(message = EMAIL_INVALID) String email, + @Size(min = NAME_MIN_LENGTH, max = NAME_MAX_LENGTH, message = NAME_SIZE) String name) {} diff --git a/src/main/java/org/nkcoder/mapper/UserMapper.java b/src/main/java/org/nkcoder/mapper/UserMapper.java index b8783c0..dbeb3a4 100644 --- a/src/main/java/org/nkcoder/mapper/UserMapper.java +++ b/src/main/java/org/nkcoder/mapper/UserMapper.java @@ -1,5 +1,7 @@ package org.nkcoder.mapper; +import java.util.Objects; +import java.util.Optional; import org.nkcoder.dto.user.UserResponse; import org.nkcoder.entity.User; import org.springframework.stereotype.Component; @@ -7,11 +9,17 @@ @Component public class UserMapper { - public UserResponse toResponse(User user) { - if (user == null) { - return null; - } + /** Converts a User entity to UserResponse, returning Optional.empty() is user is null. */ + public Optional toResponse(User user) { + return Optional.ofNullable(user).map(this::mapToResponse); + } + + public UserResponse toResponseOrThrow(User user) { + Objects.requireNonNull(user, "User must not be null"); + return mapToResponse(user); + } + private UserResponse mapToResponse(User user) { return new UserResponse( user.getId(), user.getEmail(), diff --git a/src/main/java/org/nkcoder/service/AuthService.java b/src/main/java/org/nkcoder/service/AuthService.java index 5850b54..aff4093 100644 --- a/src/main/java/org/nkcoder/service/AuthService.java +++ b/src/main/java/org/nkcoder/service/AuthService.java @@ -30,6 +30,12 @@ public class AuthService { private static final Logger logger = LoggerFactory.getLogger(AuthService.class); + public static final String USER_ALREADY_EXISTS = "User already exists"; + public static final String INVALID_CREDENTIALS = "Invalid email or password"; + public static final String INVALID_REFRESH_TOKEN = "Invalid refresh token"; + public static final String REFRESH_TOKEN_EXPIRED = "Refresh token expired"; + public static final String USER_NOT_FOUND = "User not found"; + private final UserRepository userRepository; private final RefreshTokenRepository refreshTokenRepository; private final PasswordEncoder passwordEncoder; @@ -59,7 +65,7 @@ public AuthResponse register(RegisterRequest request) { // Check if user already exists if (userRepository.existsByEmail(request.email().toLowerCase())) { - throw new ValidationException("User already exists"); + throw new ValidationException(USER_ALREADY_EXISTS); } // Create new user @@ -81,7 +87,7 @@ public AuthResponse register(RegisterRequest request) { // Save refresh token saveRefreshToken(tokens.refreshToken(), savedUser.getId(), tokenFamily); - return new AuthResponse(userMapper.toResponse(savedUser), tokens); + return new AuthResponse(userMapper.toResponseOrThrow(savedUser), tokens); } @Transactional @@ -92,11 +98,11 @@ public AuthResponse login(LoginRequest request) { User user = userRepository .findByEmail(request.email().toLowerCase()) - .orElseThrow(() -> new AuthenticationException("Invalid email or password")); + .orElseThrow(() -> new AuthenticationException(INVALID_CREDENTIALS)); // Check password if (!passwordEncoder.matches(request.password(), user.getPassword())) { - throw new AuthenticationException("Invalid email or password"); + throw new AuthenticationException(INVALID_CREDENTIALS); } // Update last login @@ -110,7 +116,7 @@ public AuthResponse login(LoginRequest request) { saveRefreshToken(tokens.refreshToken(), user.getId(), tokenFamily); logger.debug("User logged in successfully: {}", user.getId()); - return new AuthResponse(userMapper.toResponse(user), tokens); + return new AuthResponse(userMapper.toResponseOrThrow(user), tokens); } @Transactional(isolation = Isolation.SERIALIZABLE) @@ -127,32 +133,28 @@ public AuthResponse refreshTokens(String refreshToken) { RefreshToken storedToken = refreshTokenRepository .findByTokenForUpdate(refreshToken) - .orElseThrow(() -> new AuthenticationException("Invalid refresh token")); + .orElseThrow(() -> new AuthenticationException(INVALID_REFRESH_TOKEN)); // Check if token is expired if (storedToken.isExpired()) { refreshTokenRepository.deleteByToken(refreshToken); - throw new AuthenticationException("Refresh token expired"); + throw new AuthenticationException(REFRESH_TOKEN_EXPIRED); } - // Get user User user = userRepository .findById(userId) - .orElseThrow(() -> new AuthenticationException("User not found")); + .orElseThrow(() -> new AuthenticationException(USER_NOT_FOUND)); - // Delete old refresh token refreshTokenRepository.deleteByToken(refreshToken); // Generate new tokens with same token family AuthTokens tokens = generateAuthTokens(user, tokenFamily); - // Save new refresh token saveRefreshToken(tokens.refreshToken(), user.getId(), tokenFamily); logger.debug("Tokens refreshed successfully for user: {}", userId); - return new AuthResponse(userMapper.toResponse(user), tokens); - + return new AuthResponse(userMapper.toResponseOrThrow(user), tokens); } catch (JwtException e) { logger.error("Invalid refresh token: {}", e.getMessage()); @@ -163,7 +165,7 @@ public AuthResponse refreshTokens(String refreshToken) { storedToken -> refreshTokenRepository.deleteByTokenFamily(storedToken.getTokenFamily())); - throw new AuthenticationException("Invalid refresh token"); + throw new AuthenticationException(INVALID_REFRESH_TOKEN); } } @@ -171,14 +173,15 @@ public AuthResponse refreshTokens(String refreshToken) { public void logout(String refreshToken) { logger.debug("Logging out user (all devices)"); - // Get token data - RefreshToken storedToken = refreshTokenRepository.findByToken(refreshToken).orElse(null); - if (storedToken != null) { - // Delete entire token family (logout from all devices) - refreshTokenRepository.deleteByTokenFamily(storedToken.getTokenFamily()); - logger.debug( - "Logged out from all devices for token family: {}", storedToken.getTokenFamily()); - } + refreshTokenRepository + .findByToken(refreshToken) + .ifPresent( + storedToken -> { + // Delete entire token family (logout from all devices) + refreshTokenRepository.deleteByTokenFamily(storedToken.getTokenFamily()); + logger.debug( + "Logged out from all devices for token family: {}", storedToken.getTokenFamily()); + }); } @Transactional diff --git a/src/main/java/org/nkcoder/service/UserService.java b/src/main/java/org/nkcoder/service/UserService.java index d627874..3920a96 100644 --- a/src/main/java/org/nkcoder/service/UserService.java +++ b/src/main/java/org/nkcoder/service/UserService.java @@ -24,6 +24,11 @@ public class UserService { private static final Logger logger = LoggerFactory.getLogger(UserService.class); + public static final String USER_NOT_FOUND_ID = "User not found with id: "; + public static final String USER_NOT_FOUND_EMAIL = "User not found with email"; + public static final String EMAIL_ALREADY_EXISTS = "Email already exists"; + public static final String CURRENT_PASSWORD_INCORRECT = "Current password is incorrect"; + private final UserRepository userRepository; private final PasswordEncoder passwordEncoder; private final UserMapper userMapper; @@ -39,22 +44,19 @@ public UserService( @Transactional(readOnly = true) public UserResponse findById(UUID id) { logger.debug("Finding user by ID: {}", id); - User user = - userRepository - .findById(id) - .orElseThrow(() -> new ResourceNotFoundException("User not found with id: " + id)); - return userMapper.toResponse(user); + return userRepository + .findById(id) + .flatMap(userMapper::toResponse) + .orElseThrow(() -> new ResourceNotFoundException(USER_NOT_FOUND_ID + id)); } @Transactional(readOnly = true) public UserResponse findByEmail(String email) { logger.debug("Finding user by email: {}", email); - User user = - userRepository - .findByEmail(email.toLowerCase()) - .orElseThrow( - () -> new ResourceNotFoundException("User not found with email: " + email)); - return userMapper.toResponse(user); + return userRepository + .findByEmail(email.toLowerCase()) + .flatMap(userMapper::toResponse) + .orElseThrow(() -> new ResourceNotFoundException(USER_NOT_FOUND_EMAIL + email)); } @Transactional @@ -64,7 +66,7 @@ public UserResponse updateProfile(UUID userId, UpdateProfileRequest request) { User user = userRepository .findById(userId) - .orElseThrow(() -> new ResourceNotFoundException("User not found with id: " + userId)); + .orElseThrow(() -> new ResourceNotFoundException(USER_NOT_FOUND_ID + userId)); Optional.ofNullable(request.email()) .filter(StringUtils::hasText) @@ -72,7 +74,7 @@ public UserResponse updateProfile(UUID userId, UpdateProfileRequest request) { .ifPresent( email -> { if (userRepository.existsByEmail(email.toLowerCase())) { - throw new ValidationException("Email already exists"); + throw new ValidationException(EMAIL_ALREADY_EXISTS); } user.updateEmail(email); }); @@ -82,7 +84,7 @@ public UserResponse updateProfile(UUID userId, UpdateProfileRequest request) { User updatedUser = userRepository.save(user); logger.debug("Profile updated successfully for user: {}", userId); - return userMapper.toResponse(updatedUser); + return userMapper.toResponseOrThrow(updatedUser); } @Transactional @@ -92,12 +94,12 @@ public void changePassword(UUID userId, ChangePasswordRequest request) { User user = userRepository .findById(userId) - .orElseThrow(() -> new ResourceNotFoundException("User not found with id: " + userId)); + .orElseThrow(() -> new ResourceNotFoundException(USER_NOT_FOUND_ID + userId)); // Password confirmation if now validated by @PasswordMatch annotation if (!passwordEncoder.matches(request.currentPassword(), user.getPassword())) { - throw new ValidationException("Current password is incorrect"); + throw new ValidationException(CURRENT_PASSWORD_INCORRECT); } user.changePassword(passwordEncoder.encode(request.newPassword())); @@ -117,7 +119,7 @@ public void changeUserPassword(UUID userId, String newPassword) { User user = userRepository .findById(userId) - .orElseThrow(() -> new ResourceNotFoundException("User not found with id: " + userId)); + .orElseThrow(() -> new ResourceNotFoundException(USER_NOT_FOUND_ID + userId)); // Update password user.changePassword(passwordEncoder.encode(newPassword)); diff --git a/src/main/java/org/nkcoder/validation/PasswordValidation.java b/src/main/java/org/nkcoder/validation/PasswordValidation.java deleted file mode 100644 index 3c37c58..0000000 --- a/src/main/java/org/nkcoder/validation/PasswordValidation.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.nkcoder.validation; - -public class PasswordValidation { - private PasswordValidation() { - // Utility class - prevent instantiation - } - - public static final int MIN_LENGTH = 8; - - /** - * Regex requiring at least one lowercase, one uppercase, and one digit. Pattern breakdown: - - * (?=.*[a-z]) - at least one lowercase letter - (?=.*[A-Z]) - at least one uppercase letter - - * (?=.*\d) - at least one digit - .+ - at least one character (combined with lookaheads) - */ - public static final String COMPLEXITY_PATTERN = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).+$"; - - // Validation messages - public static final String CURRENT_PASSWORD_REQUIRED = "Password is required."; - public static final String PASSWORD_REQUIRED = "Password is required."; - public static final String PASSWORD_MIN_LENGTH = - "Password must be at least " + MIN_LENGTH + " characters long"; - public static final String PASSWORD_COMPLEXITY = - "Password must contain at least one lowercase letter, one uppercase letter, and one number"; - public static final String CONFIRM_PASSWORD_REQUIRED = "Password confirmation is required"; -} diff --git a/src/main/java/org/nkcoder/validation/ValidationMessages.java b/src/main/java/org/nkcoder/validation/ValidationMessages.java new file mode 100644 index 0000000..f0607b4 --- /dev/null +++ b/src/main/java/org/nkcoder/validation/ValidationMessages.java @@ -0,0 +1,37 @@ +package org.nkcoder.validation; + +public class ValidationMessages { + private ValidationMessages() { + // Utility class - prevent instantiation + } + + /** + * Regex requiring at least one lowercase, one uppercase, and one digit. Pattern breakdown: - + * (?=.*[a-z]) - at least one lowercase letter - (?=.*[A-Z]) - at least one uppercase letter - + * (?=.*\d) - at least one digit - .+ - at least one character (combined with lookaheads) + */ + public static final int PASSWORD_MIN_LENGTH = 8; + + public static final String PASSWORD_COMPLEXITY_PATTERN = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).+$"; + public static final String PASSWORD_REQUIRED = "Password is required."; + public static final String PASSWORD_SIZE = + "Password must be at least " + PASSWORD_MIN_LENGTH + " characters long"; + public static final String PASSWORD_COMPLEXITY = + "Password must contain at least one lowercase letter, one uppercase letter, and one number"; + public static final String CURRENT_PASSWORD_REQUIRED = "Current password is required"; + public static final String CONFIRM_PASSWORD_REQUIRED = "Password confirmation is required"; + + // ===== Email Validation ===== + public static final String EMAIL_REQUIRED = "Email is required"; + public static final String EMAIL_INVALID = "Please provide a valid email"; + + // ===== Name Validation ===== + public static final int NAME_MIN_LENGTH = 2; + public static final int NAME_MAX_LENGTH = 50; + public static final String NAME_REQUIRED = "Name is required"; + public static final String NAME_SIZE = + "Name must be between " + NAME_MIN_LENGTH + " and " + NAME_MAX_LENGTH + " characters"; + + // ===== Token Validation ===== + public static final String REFRESH_TOKEN_REQUIRED = "Refresh token is required"; +} diff --git a/src/test/java/org/nkcoder/service/AuthServiceTest.java b/src/test/java/org/nkcoder/service/AuthServiceTest.java index ceb2548..67a32cf 100644 --- a/src/test/java/org/nkcoder/service/AuthServiceTest.java +++ b/src/test/java/org/nkcoder/service/AuthServiceTest.java @@ -93,7 +93,7 @@ void register_success() { when(userRepository.save(any(User.class))).thenReturn(user); when(jwtUtil.generateAccessToken(userId, email, role)).thenReturn(accessToken); when(jwtUtil.generateRefreshToken(eq(userId), anyString())).thenReturn(refreshToken); - when(userMapper.toResponse(user)).thenReturn(userResponse); + when(userMapper.toResponseOrThrow(user)).thenReturn(userResponse); AuthResponse response = authService.register(request); @@ -140,7 +140,7 @@ void login_success() { when(passwordEncoder.matches(password, encodedPassword)).thenReturn(true); when(jwtUtil.generateAccessToken(userId, email, role)).thenReturn(accessToken); when(jwtUtil.generateRefreshToken(eq(userId), anyString())).thenReturn(refreshToken); - when(userMapper.toResponse(user)).thenReturn(userResponse); + when(userMapper.toResponseOrThrow(user)).thenReturn(userResponse); AuthResponse response = authService.login(request); @@ -209,7 +209,7 @@ void refreshTokens_success() { when(jwtUtil.generateAccessToken(userId, email, role)).thenReturn(accessToken); when(jwtUtil.generateRefreshToken(userId, tokenFamily)).thenReturn("new.refresh.token"); UserResponse userResponse = new UserResponse(userId, email, name, role, false, now, now, now); - when(userMapper.toResponse(user)).thenReturn(userResponse); + when(userMapper.toResponseOrThrow(user)).thenReturn(userResponse); AuthResponse response = authService.refreshTokens(refreshToken); @@ -353,7 +353,7 @@ void login_caseInsensitiveEmail() { when(passwordEncoder.matches(password, encodedPassword)).thenReturn(true); when(jwtUtil.generateAccessToken(userId, email.toLowerCase(), role)).thenReturn(accessToken); when(jwtUtil.generateRefreshToken(eq(userId), anyString())).thenReturn(refreshToken); - when(userMapper.toResponse(user)).thenReturn(userResponse); + when(userMapper.toResponse(user)).thenReturn(Optional.of(userResponse)); AuthResponse response = authService.login(request); @@ -381,7 +381,7 @@ void register_caseInsensitiveEmail() { when(userRepository.save(any(User.class))).thenReturn(user); when(jwtUtil.generateAccessToken(userId, email.toLowerCase(), role)).thenReturn(accessToken); when(jwtUtil.generateRefreshToken(eq(userId), anyString())).thenReturn(refreshToken); - when(userMapper.toResponse(user)).thenReturn(userResponse); + when(userMapper.toResponse(user)).thenReturn(Optional.of(userResponse)); AuthResponse response = authService.register(request); diff --git a/src/test/java/org/nkcoder/service/UserServiceTest.java b/src/test/java/org/nkcoder/service/UserServiceTest.java index 4216beb..0e28a1d 100644 --- a/src/test/java/org/nkcoder/service/UserServiceTest.java +++ b/src/test/java/org/nkcoder/service/UserServiceTest.java @@ -70,7 +70,7 @@ void findById_success() { UserResponse userResponse = new UserResponse(userId, email, name, role, false, now, now, now); when(userRepository.findById(userId)).thenReturn(Optional.of(user)); - when(userMapper.toResponse(user)).thenReturn(userResponse); + when(userMapper.toResponse(user)).thenReturn(Optional.of(userResponse)); UserResponse result = userService.findById(userId); @@ -97,7 +97,7 @@ void findByEmail_success() { UserResponse userResponse = new UserResponse(userId, email, name, role, false, now, now, now); when(userRepository.findByEmail(email.toLowerCase())).thenReturn(Optional.of(user)); - when(userMapper.toResponse(user)).thenReturn(userResponse); + when(userMapper.toResponse(user)).thenReturn(Optional.of(userResponse)); UserResponse result = userService.findByEmail(email); @@ -135,7 +135,7 @@ void updateProfile_success_updateNameAndEmail() { when(userRepository.findById(userId)).thenReturn(Optional.of(user)); when(userRepository.existsByEmail("new@example.com")).thenReturn(false); when(userRepository.save(any(User.class))).thenReturn(updatedUser); - when(userMapper.toResponse(updatedUser)).thenReturn(userResponse); + when(userMapper.toResponseOrThrow(updatedUser)).thenReturn(userResponse); UserResponse result = userService.updateProfile(userId, request); @@ -143,7 +143,7 @@ void updateProfile_success_updateNameAndEmail() { verify(userRepository).findById(userId); verify(userRepository).existsByEmail("new@example.com"); verify(userRepository).save(any(User.class)); - verify(userMapper).toResponse(updatedUser); + verify(userMapper).toResponseOrThrow(updatedUser); } @Test @@ -185,13 +185,13 @@ void updateProfile_updateNameOnly() { when(userRepository.findById(userId)).thenReturn(Optional.of(user)); when(userRepository.save(any(User.class))).thenReturn(updatedUser); - when(userMapper.toResponse(updatedUser)).thenReturn(userResponse); + when(userMapper.toResponseOrThrow(updatedUser)).thenReturn(userResponse); UserResponse result = userService.updateProfile(userId, request); assertEquals(userResponse, result); verify(userRepository).save(any(User.class)); - verify(userMapper).toResponse(updatedUser); + verify(userMapper).toResponseOrThrow(updatedUser); } @Test