From 17864c85edc335b063d1d45cac429bde62682985 Mon Sep 17 00:00:00 2001 From: zzangkkmin Date: Mon, 24 Feb 2025 17:16:27 +0900 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=8D=BB=20test=20:=20=EC=B5=9C?= =?UTF-8?q?=EC=8B=A0=20=EB=B3=80=EA=B2=BD=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/user/UserControllerTest.java | 352 +++++++++--------- 1 file changed, 176 insertions(+), 176 deletions(-) diff --git a/src/test/java/com/sparta/spring_deep/_delivery/domain/user/UserControllerTest.java b/src/test/java/com/sparta/spring_deep/_delivery/domain/user/UserControllerTest.java index 39a5995..09077db 100644 --- a/src/test/java/com/sparta/spring_deep/_delivery/domain/user/UserControllerTest.java +++ b/src/test/java/com/sparta/spring_deep/_delivery/domain/user/UserControllerTest.java @@ -1,176 +1,176 @@ -//package com.sparta.spring_deep._delivery.domain.user; -// -//import com.fasterxml.jackson.databind.ObjectMapper; -//import com.sparta.spring_deep._delivery.domain.user.controller.UserController; -//import com.sparta.spring_deep._delivery.domain.user.dto.LoginRequestDto; -//import com.sparta.spring_deep._delivery.domain.user.dto.LoginResponseDto; -//import com.sparta.spring_deep._delivery.domain.user.dto.PasswordChangeDto; -//import com.sparta.spring_deep._delivery.domain.user.dto.UserDto; -//import com.sparta.spring_deep._delivery.domain.user.details.UserDetailsImpl; -//import com.sparta.spring_deep._delivery.domain.user.entity.User; -//import com.sparta.spring_deep._delivery.domain.user.service.UserService; -//import com.sparta.spring_deep._delivery.util.JwtUtil; -//import jakarta.validation.Valid; -//import org.junit.jupiter.api.BeforeEach; -//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.core.MethodParameter; -//import org.springframework.http.MediaType; -//import org.springframework.security.core.annotation.AuthenticationPrincipal; -//import org.springframework.test.web.servlet.MockMvc; -//import org.springframework.test.web.servlet.setup.MockMvcBuilders; -//import org.springframework.web.bind.support.WebDataBinderFactory; -//import org.springframework.web.context.request.NativeWebRequest; -//import org.springframework.web.method.support.HandlerMethodArgumentResolver; -//import org.springframework.web.method.support.ModelAndViewContainer; -// -//import static org.mockito.ArgumentMatchers.any; -//import static org.mockito.ArgumentMatchers.eq; -//import static org.mockito.Mockito.when; -//import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; -//import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -//import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; -//import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -//import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; -// -//@ExtendWith(MockitoExtension.class) -//public class UserControllerTest { -// -// @InjectMocks -// private UserController userController; -// -// @Mock -// private UserService userService; -// -// @Mock -// private JwtUtil jwtUtil; -// -// private MockMvc mockMvc; -// -// private ObjectMapper objectMapper = new ObjectMapper(); -// -// // 간단한 커스텀 인자 해결자: @AuthenticationPrincipal로 주입되는 값을 더미 UserDetailsImpl로 설정 -// private static class FakeAuthenticationPrincipalResolver implements HandlerMethodArgumentResolver { -// -// @Override -// public boolean supportsParameter(MethodParameter parameter) { -// return parameter.hasParameterAnnotation(AuthenticationPrincipal.class); -// } -// -// @Override -// public Object resolveArgument(MethodParameter parameter, -// ModelAndViewContainer mavContainer, -// NativeWebRequest webRequest, -// WebDataBinderFactory binderFactory) { -// User dummyUser = new User(); -// dummyUser.setUsername("testuser"); -// // 필요한 경우 추가 속성을 설정합니다. -// return new UserDetailsImpl(dummyUser); -// } -// } -// -// @BeforeEach -// void setup() { -// mockMvc = MockMvcBuilders.standaloneSetup(userController) -// .setCustomArgumentResolvers(new FakeAuthenticationPrincipalResolver()) -// .build(); -// } -// -// @Test -// void testSignupSuccess() throws Exception { -// UserDto userDto = new UserDto(); -// userDto.setUsername("testuser"); -// userDto.setPassword("password"); -// userDto.setEmail("test@example.com"); -// -// User user = new User(); -// user.setUsername("testuser"); -// user.setEmail("test@example.com"); -// -// when(userService.registerUser(any(UserDto.class))).thenReturn(user); -// -// mockMvc.perform(post("/api/users/signup") -// .contentType(MediaType.APPLICATION_JSON) -// .content(objectMapper.writeValueAsString(userDto))) -// .andExpect(status().isCreated()) -// .andExpect(jsonPath("$.username").value("testuser")); -// } -// -// @Test -// void testLoginSuccess() throws Exception { -// LoginRequestDto loginRequestDto = new LoginRequestDto(); -// loginRequestDto.setUsername("testuser"); -// loginRequestDto.setPassword("password"); -// -// LoginResponseDto loginResponseDto = new LoginResponseDto("jwt-token", "testuser", "test@example.com", null, null); -// when(userService.login(any(LoginRequestDto.class))).thenReturn(loginResponseDto); -// -// mockMvc.perform(post("/api/users/login") -// .contentType(MediaType.APPLICATION_JSON) -// .content(objectMapper.writeValueAsString(loginRequestDto))) -// .andExpect(status().isOk()) -// .andExpect(jsonPath("$.token").value("jwt-token")); -// } -// -// @Test -// void testLogoutSuccess() throws Exception { -// String token = "Bearer jwt-token"; -// -// mockMvc.perform(post("/api/users/logout") -// .header("Authorization", token)) -// .andExpect(status().isOk()) -// .andExpect(content().string("You've been logged out successfully.")); -// } -// -// @Test -// void testGetCurrentUser() throws Exception { -// // @AuthenticationPrincipal는 FakeAuthenticationPrincipalResolver를 통해 주입됨 -// mockMvc.perform(get("/api/users/me")) -// .andExpect(status().isOk()); -// } -// -// @Test -// void testUpdateUserSuccess() throws Exception { -// String username = "testuser"; -// UserDto userDto = new UserDto(); -// userDto.setEmail("newemail@example.com"); -// -// User updatedUser = new User(); -// updatedUser.setUsername(username); -// updatedUser.setEmail("newemail@example.com"); -// -// when(userService.updateUser(eq(username), any(UserDto.class))).thenReturn(updatedUser); -// -// mockMvc.perform(put("/api/users/" + username) -// .contentType(MediaType.APPLICATION_JSON) -// .content(objectMapper.writeValueAsString(userDto))) -// .andExpect(status().isOk()) -// .andExpect(jsonPath("$.email").value("newemail@example.com")); -// } -// -// @Test -// void testChangePasswordSuccess() throws Exception { -// String username = "testuser"; -// PasswordChangeDto passwordChangeDto = new PasswordChangeDto(); -// passwordChangeDto.setOldPassword("oldPass"); -// passwordChangeDto.setNewPassword("newPass"); -// -// mockMvc.perform(put("/api/users/" + username + "/password") -// .contentType(MediaType.APPLICATION_JSON) -// .content(objectMapper.writeValueAsString(passwordChangeDto))) -// .andExpect(status().isOk()) -// .andExpect(content().string("Password updated successfully")); -// } -// -// @Test -// void testDeleteUserSuccess() throws Exception { -// String username = "testuser"; -// -// mockMvc.perform(delete("/api/users/" + username)) -// .andExpect(status().isOk()) -// .andExpect(content().string("User deleted successfully")); -// } -//} +package com.sparta.spring_deep._delivery.domain.user; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.sparta.spring_deep._delivery.domain.user.controller.UserController; +import com.sparta.spring_deep._delivery.domain.user.dto.LoginRequestDto; +import com.sparta.spring_deep._delivery.domain.user.dto.LoginResponseDto; +import com.sparta.spring_deep._delivery.domain.user.dto.PasswordChangeDto; +import com.sparta.spring_deep._delivery.domain.user.dto.UserDto; +import com.sparta.spring_deep._delivery.domain.user.details.UserDetailsImpl; +import com.sparta.spring_deep._delivery.domain.user.entity.User; +import com.sparta.spring_deep._delivery.domain.user.service.UserService; +import com.sparta.spring_deep._delivery.domain.user.jwt.JwtUtil; +import jakarta.validation.Valid; +import org.junit.jupiter.api.BeforeEach; +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.core.MethodParameter; +import org.springframework.http.MediaType; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +@ExtendWith(MockitoExtension.class) +public class UserControllerTest { + + @InjectMocks + private UserController userController; + + @Mock + private UserService userService; + + @Mock + private JwtUtil jwtUtil; + + private MockMvc mockMvc; + + private ObjectMapper objectMapper = new ObjectMapper(); + + // 간단한 커스텀 인자 해결자: @AuthenticationPrincipal로 주입되는 값을 더미 UserDetailsImpl로 설정 + private static class FakeAuthenticationPrincipalResolver implements HandlerMethodArgumentResolver { + + @Override + public boolean supportsParameter(MethodParameter parameter) { + return parameter.hasParameterAnnotation(AuthenticationPrincipal.class); + } + + @Override + public Object resolveArgument(MethodParameter parameter, + ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, + WebDataBinderFactory binderFactory) { + User dummyUser = new User(); + dummyUser.setUsername("testuser"); + // 필요한 경우 추가 속성을 설정합니다. + return new UserDetailsImpl(dummyUser); + } + } + + @BeforeEach + void setup() { + mockMvc = MockMvcBuilders.standaloneSetup(userController) + .setCustomArgumentResolvers(new FakeAuthenticationPrincipalResolver()) + .build(); + } + + @Test + void testSignupSuccess() throws Exception { + UserDto userDto = new UserDto(); + userDto.setUsername("testuser"); + userDto.setPassword("password"); + userDto.setEmail("test@example.com"); + + User user = new User(); + user.setUsername("testuser"); + user.setEmail("test@example.com"); + + when(userService.registerUser(any(UserDto.class))).thenReturn(user); + + mockMvc.perform(post("/api/users/signup") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(userDto))) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$.username").value("testuser")); + } + + @Test + void testLoginSuccess() throws Exception { + LoginRequestDto loginRequestDto = new LoginRequestDto(); + loginRequestDto.setUsername("testuser"); + loginRequestDto.setPassword("password"); + + LoginResponseDto loginResponseDto = new LoginResponseDto("jwt-token", "testuser", "test@example.com", null, null); + //when(userService.login(any(LoginRequestDto.class))).thenReturn(loginResponseDto); + + mockMvc.perform(post("/api/users/login") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(loginRequestDto))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.token").value("jwt-token")); + } + + @Test + void testLogoutSuccess() throws Exception { + String token = "Bearer jwt-token"; + + mockMvc.perform(post("/api/users/logout") + .header("Authorization", token)) + .andExpect(status().isOk()) + .andExpect(content().string("You've been logged out successfully.")); + } + + @Test + void testGetCurrentUser() throws Exception { + // @AuthenticationPrincipal는 FakeAuthenticationPrincipalResolver를 통해 주입됨 + mockMvc.perform(get("/api/users/me")) + .andExpect(status().isOk()); + } + + @Test + void testUpdateUserSuccess() throws Exception { + String username = "testuser"; + UserDto userDto = new UserDto(); + userDto.setEmail("newemail@example.com"); + + User updatedUser = new User(); + updatedUser.setUsername(username); + updatedUser.setEmail("newemail@example.com"); + + when(userService.updateUser(eq(username), any(UserDto.class))).thenReturn(updatedUser); + + mockMvc.perform(put("/api/users/" + username) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(userDto))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.email").value("newemail@example.com")); + } + + @Test + void testChangePasswordSuccess() throws Exception { + String username = "testuser"; + PasswordChangeDto passwordChangeDto = new PasswordChangeDto(); + passwordChangeDto.setOldPassword("oldPass"); + passwordChangeDto.setNewPassword("newPass"); + + mockMvc.perform(put("/api/users/" + username + "/password") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(passwordChangeDto))) + .andExpect(status().isOk()) + .andExpect(content().string("Password updated successfully")); + } + + @Test + void testDeleteUserSuccess() throws Exception { + String username = "testuser"; + + mockMvc.perform(delete("/api/users/" + username)) + .andExpect(status().isOk()) + .andExpect(content().string("User deleted successfully")); + } +} From 6cdc9ecbe2bd2bb9a6b92691b2189bcde817ec16 Mon Sep 17 00:00:00 2001 From: zzangkkmin Date: Tue, 25 Feb 2025 02:30:10 +0900 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=8D=BB=20test=20:=20UserControllerTes?= =?UTF-8?q?t=20=EC=9E=91=EC=84=B1=20=EC=99=84=EB=A3=8C=20(17=20testcases)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/controller/UserController.java | 14 +- .../domain/user/dto/LoginRequestDto.java | 9 + .../domain/user/dto/LoginResponseDto.java | 19 - .../domain/user/dto/PasswordChangeDto.java | 9 + .../domain/user/service/UserService.java | 10 +- .../domain/user/UserControllerTest.java | 469 +++++++++++++++--- .../domain/user/UserServiceTest.java | 183 ------- 7 files changed, 434 insertions(+), 279 deletions(-) delete mode 100644 src/main/java/com/sparta/spring_deep/_delivery/domain/user/dto/LoginResponseDto.java delete mode 100644 src/test/java/com/sparta/spring_deep/_delivery/domain/user/UserServiceTest.java diff --git a/src/main/java/com/sparta/spring_deep/_delivery/domain/user/controller/UserController.java b/src/main/java/com/sparta/spring_deep/_delivery/domain/user/controller/UserController.java index 1478176..30a98f4 100644 --- a/src/main/java/com/sparta/spring_deep/_delivery/domain/user/controller/UserController.java +++ b/src/main/java/com/sparta/spring_deep/_delivery/domain/user/controller/UserController.java @@ -53,7 +53,7 @@ public ResponseEntity signup(@Valid @RequestBody UserDto userDto, } @PostMapping("/users/logout") - public ResponseEntity logout(@RequestHeader(value = "Authorization") String token) { + public ResponseEntity logout(@RequestHeader(value = "Authorization", required = false) String token) { // 클라이언트쪽에서 JWT 토큰 무효화해야 함! if (token != null && token.startsWith("Bearer ")) { String jwtToken = token.substring(7); @@ -62,7 +62,7 @@ public ResponseEntity logout(@RequestHeader(value = "Authorization") String t return ResponseEntity.ok().body("You've been logged out successfully."); } logger.error("Invalid token for logout attempt: {}", token); - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Invalid Token"); + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid Token"); } @GetMapping("/users/me") @@ -91,7 +91,15 @@ public ResponseEntity updateUser(@PathVariable String username, @PreAuthorize("authentication.name == #username") @PutMapping("/users/{username}/password") public ResponseEntity changePassword(@PathVariable String username, - @RequestBody PasswordChangeDto passwordChangeDto) { + @Valid @RequestBody PasswordChangeDto passwordChangeDto, BindingResult bindingResult) { + + if (bindingResult.hasErrors()) { + FieldError fieldError = bindingResult.getFieldError(); + String errorMsg = fieldError != null ? fieldError.getDefaultMessage() : "Invalid input"; + logger.error("Sign up error: {}", errorMsg); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorMsg); + } + userService.changePassword(username, passwordChangeDto); return ResponseEntity.ok("Password updated successfully"); } diff --git a/src/main/java/com/sparta/spring_deep/_delivery/domain/user/dto/LoginRequestDto.java b/src/main/java/com/sparta/spring_deep/_delivery/domain/user/dto/LoginRequestDto.java index 0cb45a2..a806c48 100644 --- a/src/main/java/com/sparta/spring_deep/_delivery/domain/user/dto/LoginRequestDto.java +++ b/src/main/java/com/sparta/spring_deep/_delivery/domain/user/dto/LoginRequestDto.java @@ -1,11 +1,20 @@ package com.sparta.spring_deep._delivery.domain.user.dto; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; import lombok.Getter; import lombok.Setter; @Setter @Getter public class LoginRequestDto { + @NotBlank(message = "사용자 아이디는 필수 입력값입니다.") + @Pattern(regexp = "^[a-z0-9]{4,10}$", + message = "사용자 아이디는 영문 소문자, 숫자만 사용하여 4~10자리여야 합니다.") private String username; + + @NotBlank(message = "비밀번호는 필수 입력값입니다.") + @Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,15}$", + message = "비밀번호는 8~15자리여야 하며, 영문 대소문자, 숫자, 특수문자를 포함해야 합니다.") private String password; } diff --git a/src/main/java/com/sparta/spring_deep/_delivery/domain/user/dto/LoginResponseDto.java b/src/main/java/com/sparta/spring_deep/_delivery/domain/user/dto/LoginResponseDto.java deleted file mode 100644 index 8f2e801..0000000 --- a/src/main/java/com/sparta/spring_deep/_delivery/domain/user/dto/LoginResponseDto.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.sparta.spring_deep._delivery.domain.user.dto; - -import com.sparta.spring_deep._delivery.domain.user.entity.IsPublic; -import com.sparta.spring_deep._delivery.domain.user.entity.UserRole; -import java.util.List; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.Setter; - -@Getter -@Setter -@RequiredArgsConstructor -public class LoginResponseDto { - private final String token; - private final String username; - private final String email; - private final UserRole role; - private final IsPublic isPublic; -} diff --git a/src/main/java/com/sparta/spring_deep/_delivery/domain/user/dto/PasswordChangeDto.java b/src/main/java/com/sparta/spring_deep/_delivery/domain/user/dto/PasswordChangeDto.java index 4a22a96..5498638 100644 --- a/src/main/java/com/sparta/spring_deep/_delivery/domain/user/dto/PasswordChangeDto.java +++ b/src/main/java/com/sparta/spring_deep/_delivery/domain/user/dto/PasswordChangeDto.java @@ -1,11 +1,20 @@ package com.sparta.spring_deep._delivery.domain.user.dto; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; import lombok.Getter; import lombok.Setter; @Getter @Setter public class PasswordChangeDto { + @NotBlank(message = "비밀번호는 필수 입력값입니다.") + @Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,15}$", + message = "비밀번호는 8~15자리여야 하며, 영문 대소문자, 숫자, 특수문자를 포함해야 합니다.") private String oldPassword; + + @NotBlank(message = "비밀번호는 필수 입력값입니다.") + @Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,15}$", + message = "비밀번호는 8~15자리여야 하며, 영문 대소문자, 숫자, 특수문자를 포함해야 합니다.") private String newPassword; } diff --git a/src/main/java/com/sparta/spring_deep/_delivery/domain/user/service/UserService.java b/src/main/java/com/sparta/spring_deep/_delivery/domain/user/service/UserService.java index ae1949b..83b2d41 100644 --- a/src/main/java/com/sparta/spring_deep/_delivery/domain/user/service/UserService.java +++ b/src/main/java/com/sparta/spring_deep/_delivery/domain/user/service/UserService.java @@ -6,12 +6,14 @@ import com.sparta.spring_deep._delivery.domain.user.jwt.JwtUtil; import com.sparta.spring_deep._delivery.domain.user.repository.UserRepository; import com.sparta.spring_deep._delivery.exception.DuplicateResourceException; +import com.sparta.spring_deep._delivery.exception.GlobalExceptionHandler; import com.sparta.spring_deep._delivery.exception.OwnershipMismatchException; import com.sparta.spring_deep._delivery.exception.ResourceNotFoundException; import java.time.LocalDateTime; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Import; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; @@ -93,14 +95,18 @@ public void deleteUser(String userName) { userRepository.save(user); } - public void changePassword(String userName, PasswordChangeDto passwordChangeDto) { + public User changePassword(String userName, PasswordChangeDto passwordChangeDto) { + if (passwordChangeDto.getOldPassword().equals(passwordChangeDto.getNewPassword())) { + throw new DuplicateResourceException("새 비밀번호는 이전 비밀번호와 달라야 합니다."); + } + log.info("change password " + userName); User user = userRepository.findByUsernameAndIsDeletedFalse(userName) .orElseThrow(ResourceNotFoundException::new); user.setPassword(passwordEncoder.encode(passwordChangeDto.getNewPassword())); user.update(userName); - userRepository.save(user); + return userRepository.save(user); } } diff --git a/src/test/java/com/sparta/spring_deep/_delivery/domain/user/UserControllerTest.java b/src/test/java/com/sparta/spring_deep/_delivery/domain/user/UserControllerTest.java index 09077db..94b72f6 100644 --- a/src/test/java/com/sparta/spring_deep/_delivery/domain/user/UserControllerTest.java +++ b/src/test/java/com/sparta/spring_deep/_delivery/domain/user/UserControllerTest.java @@ -3,15 +3,22 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.sparta.spring_deep._delivery.domain.user.controller.UserController; import com.sparta.spring_deep._delivery.domain.user.dto.LoginRequestDto; -import com.sparta.spring_deep._delivery.domain.user.dto.LoginResponseDto; import com.sparta.spring_deep._delivery.domain.user.dto.PasswordChangeDto; import com.sparta.spring_deep._delivery.domain.user.dto.UserDto; import com.sparta.spring_deep._delivery.domain.user.details.UserDetailsImpl; +import com.sparta.spring_deep._delivery.domain.user.entity.IsPublic; import com.sparta.spring_deep._delivery.domain.user.entity.User; +import com.sparta.spring_deep._delivery.domain.user.entity.UserRole; +import com.sparta.spring_deep._delivery.domain.user.jwt.JwtAuthenticationFilter; import com.sparta.spring_deep._delivery.domain.user.service.UserService; import com.sparta.spring_deep._delivery.domain.user.jwt.JwtUtil; -import jakarta.validation.Valid; +import com.sparta.spring_deep._delivery.exception.DuplicateResourceException; +import com.sparta.spring_deep._delivery.exception.ResourceNotFoundException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; @@ -19,17 +26,34 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.core.MethodParameter; import org.springframework.http.MediaType; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; @@ -79,98 +103,399 @@ void setup() { .build(); } - @Test - void testSignupSuccess() throws Exception { - UserDto userDto = new UserDto(); - userDto.setUsername("testuser"); - userDto.setPassword("password"); - userDto.setEmail("test@example.com"); - - User user = new User(); - user.setUsername("testuser"); - user.setEmail("test@example.com"); + @Nested + @DisplayName("회원 가입 테스트") + class SignupTests { + // 테스트에 사용할 UserDto 생성 헬퍼 메서드 + private UserDto createUserDto(String username, String password, String email) { + UserDto userDto = new UserDto(); + userDto.setUsername(username); + userDto.setPassword(password); + userDto.setEmail(email); + return userDto; + } - when(userService.registerUser(any(UserDto.class))).thenReturn(user); + // userService의 registerUser 스텁 설정 헬퍼 메서드 + private void stubUserService(String username, String email) { + User user = new User(); + user.setUsername(username); + user.setEmail(email); + lenient().when(userService.registerUser(any(UserDto.class))).thenReturn(user); + } - mockMvc.perform(post("/api/users/signup") + // 공통 POST 요청 헬퍼 메서드 + private ResultActions performSignup(UserDto userDto) throws Exception { + return mockMvc.perform(post("/api/users/signup") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(userDto))) - .andExpect(status().isCreated()) - .andExpect(jsonPath("$.username").value("testuser")); - } + .content(objectMapper.writeValueAsString(userDto))); + } - @Test - void testLoginSuccess() throws Exception { - LoginRequestDto loginRequestDto = new LoginRequestDto(); - loginRequestDto.setUsername("testuser"); - loginRequestDto.setPassword("password"); + @Test + @DisplayName("회원 가입 성공") + void testSignupSuccess() throws Exception { + UserDto userDto = createUserDto("testuser", "Password!@3", "test@example.com"); + stubUserService("testuser", "test@example.com"); - LoginResponseDto loginResponseDto = new LoginResponseDto("jwt-token", "testuser", "test@example.com", null, null); - //when(userService.login(any(LoginRequestDto.class))).thenReturn(loginResponseDto); + performSignup(userDto) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$.username").value("testuser")); + } - mockMvc.perform(post("/api/users/login") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(loginRequestDto))) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.token").value("jwt-token")); + @Test + @DisplayName("회원 가입 실패 - 유효하지 않은 사용자명") + void testSignupInvalidUsernameFailure() throws Exception { + UserDto userDto = createUserDto("US!A", "Password!@3", "test@example.com"); + stubUserService("US!A", "test@example.com"); + + performSignup(userDto) + .andExpect(status().isBadRequest()); + } + + @Test + @DisplayName("회원 가입 실패 - 유효하지 않은 비밀번호") + void testSignupInvalidPasswordFailure() throws Exception { + UserDto userDto = createUserDto("testuser", "ps", "test@example.com"); + stubUserService("testuser", "test@example.com"); + + performSignup(userDto) + .andExpect(status().isBadRequest()); + } + + @Test + @DisplayName("회원 가입 실패 - 유효하지 않은 이메일") + void testSignupInvalidEmailFailure() throws Exception { + UserDto userDto = createUserDto("testuser", "Password!@3", "testexample.com"); + stubUserService("testuser", "testexample.com"); + + performSignup(userDto) + .andExpect(status().isBadRequest()); + } } - @Test - void testLogoutSuccess() throws Exception { - String token = "Bearer jwt-token"; + @Nested + @DisplayName("로그인 테스트") + class LoginTests { + // LoginRequestDto를 기반으로 MockHttpServletRequest 생성 + private MockHttpServletRequest createRequest(LoginRequestDto loginRequestDto) throws Exception { + String loginJson = objectMapper.writeValueAsString(loginRequestDto); + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setContentType("application/json"); + request.setContent(loginJson.getBytes(StandardCharsets.UTF_8)); + return request; + } + + // 모의 AuthenticationManager를 주입한 JwtAuthenticationFilter 생성 + private JwtAuthenticationFilter createFilter(AuthenticationManager authenticationManager) { + JwtAuthenticationFilter filter = new JwtAuthenticationFilter(jwtUtil); + filter.setAuthenticationManager(authenticationManager); + return filter; + } + + @Test + @DisplayName("로그인 성공") + void testLoginAuthentication() throws Exception { + // given: 올바른 로그인 요청 데이터 생성 + LoginRequestDto loginRequestDto = new LoginRequestDto(); + loginRequestDto.setUsername("testuser"); + loginRequestDto.setPassword("Password!@3"); + MockHttpServletRequest request = createRequest(loginRequestDto); + MockHttpServletResponse response = new MockHttpServletResponse(); + + // AuthenticationManager 모의 설정: 성공적인 인증 결과 반환 + AuthenticationManager authenticationManager = mock(AuthenticationManager.class); + UsernamePasswordAuthenticationToken expectedToken = + new UsernamePasswordAuthenticationToken("testuser", "Password!@3", new ArrayList<>()); + when(authenticationManager.authenticate(any())).thenReturn(expectedToken); - mockMvc.perform(post("/api/users/logout") - .header("Authorization", token)) - .andExpect(status().isOk()) - .andExpect(content().string("You've been logged out successfully.")); + JwtAuthenticationFilter filter = createFilter(authenticationManager); + + // when: attemptAuthentication 호출 + Authentication authResult = filter.attemptAuthentication(request, response); + + // then: 반환된 Authentication 객체 검증 + assertNotNull(authResult); + assertEquals("testuser", authResult.getPrincipal()); + + // authenticate 메서드가 올바른 토큰으로 호출되었는지 검증 + verify(authenticationManager).authenticate(argThat(authentication -> { + if (authentication instanceof UsernamePasswordAuthenticationToken) { + UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authentication; + return "testuser".equals(token.getPrincipal()) && + "Password!@3".equals(token.getCredentials()); + } + return false; + })); + } + + @Test + @DisplayName("로그인 실패 - 잘못된 자격 증명") + void testLoginAuthenticationFail() throws Exception { + // given: 잘못된 비밀번호를 사용하는 로그인 요청 데이터 생성 + LoginRequestDto loginRequestDto = new LoginRequestDto(); + loginRequestDto.setUsername("testuser"); + loginRequestDto.setPassword("WrongPassword"); + MockHttpServletRequest request = createRequest(loginRequestDto); + MockHttpServletResponse response = new MockHttpServletResponse(); + + // AuthenticationManager 모의 설정: 인증 시 BadCredentialsException 발생 + AuthenticationManager authenticationManager = mock(AuthenticationManager.class); + when(authenticationManager.authenticate(any())) + .thenThrow(new BadCredentialsException("Bad credentials")); + + JwtAuthenticationFilter filter = createFilter(authenticationManager); + + // when & then: attemptAuthentication 호출 시 예외 발생 검증 + Exception exception = assertThrows(BadCredentialsException.class, () -> { + filter.attemptAuthentication(request, response); + }); + assertEquals("Bad credentials", exception.getMessage()); + + // authenticate 메서드 호출 여부 검증 + verify(authenticationManager).authenticate(any()); + } } - @Test - void testGetCurrentUser() throws Exception { - // @AuthenticationPrincipal는 FakeAuthenticationPrincipalResolver를 통해 주입됨 - mockMvc.perform(get("/api/users/me")) - .andExpect(status().isOk()); + @Nested + @DisplayName("사용자 정보 가져오기 테스트") + class GetUserInfoTests { + // 성공 케이스: 인증된 사용자의 정보를 반환 + @Test + @DisplayName("현재 사용자 정보 조회 - 성공") + void testGetCurrentUserSuccess() throws Exception { + // 테스트용 User 객체 생성 + User user = new User(); + user.setUsername("testuser"); + user.setPassword("Password!@3"); + user.setEmail("test@example.com"); + user.setRole(UserRole.CUSTOMER); + user.setIsPublic(IsPublic.PUBLIC); + + // 커스텀 UserDetails 객체 생성 (UserDetailsImpl가 User 정보를 포함하도록 구현) + UserDetailsImpl userDetails = new UserDetailsImpl(user); + + // Authentication 객체 생성: principal에 userDetails를 담음 + UsernamePasswordAuthenticationToken authToken = + new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); + + mockMvc.perform(get("/api/users/me") + .with(authentication(authToken))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.username").value("testuser")); + } + + // 실패 케이스: 인증정보가 없는 경우 (보통 Security 설정에 따라 401 Unauthorized 반환) + @Test + @DisplayName("현재 사용자 정보 조회 - 인증 실패") + void testGetCurrentUserFailure() throws Exception { + // FakeAuthenticationPrincipalResolver를 제거한 별도의 MockMvc 인스턴스 생성 + MockMvc mockMvcNoAuth = MockMvcBuilders.standaloneSetup(userController).build(); + + // 인증정보가 없으므로, @AuthenticationPrincipal이 null이 됩니다. + // 컨트롤러에서는 userDetails.getUser() 호출 시 NullPointerException이 발생할 수 있는데, + // 실제 애플리케이션에서는 Spring Security가 미인증 요청을 가로채고 401을 반환하도록 설정되어야 합니다. + // 여기서는 단순히 예외 발생을 검증할 수 있습니다. + assertThrows(Exception.class, () -> { + mockMvcNoAuth.perform(get("api/users/me")).andReturn(); + }); + } } - @Test - void testUpdateUserSuccess() throws Exception { - String username = "testuser"; - UserDto userDto = new UserDto(); - userDto.setEmail("newemail@example.com"); + @Nested + @DisplayName("회원 정보 수정 테스트") + class UpdateUserTests { - User updatedUser = new User(); - updatedUser.setUsername(username); - updatedUser.setEmail("newemail@example.com"); + // UserDto 객체 생성 헬퍼 메서드 + private UserDto createUserDto(String username, String password, String email) { + UserDto userDto = new UserDto(); + userDto.setUsername(username); + userDto.setPassword(password); + userDto.setEmail(email); + return userDto; + } - when(userService.updateUser(eq(username), any(UserDto.class))).thenReturn(updatedUser); + // 업데이트된 User 객체 생성 헬퍼 메서드 + private User createUpdatedUser(String username, String email) { + User user = new User(); + user.setUsername(username); + user.setEmail(email); + return user; + } - mockMvc.perform(put("/api/users/" + username) + // PUT 요청 실행 헬퍼 메서드 + private ResultActions performUpdate(String username, UserDto userDto) throws Exception { + return mockMvc.perform(put("/api/users/" + username) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(userDto))) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.email").value("newemail@example.com")); + .content(objectMapper.writeValueAsString(userDto))); + } + + @Test + @DisplayName("회원 정보 수정 성공") + void testUpdateUserSuccess() throws Exception { + String username = "testuser"; + String password = "Password!@3"; + String email = "newemail@example.com"; + + UserDto userDto = createUserDto(username, password, email); + User updatedUser = createUpdatedUser(username, email); + lenient().when(userService.updateUser(eq(username), any(UserDto.class))).thenReturn(updatedUser); + + performUpdate(username, userDto) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.email").value(email)); + } + + @Test + @DisplayName("회원 정보 수정 실패 - 유효하지 않은 비밀번호") + void testUpdateUserFail() throws Exception { + String username = "testuser"; + String password = "ps"; + String email = "newemail@example.com"; + + UserDto userDto = createUserDto(username, password, email); + User updatedUser = createUpdatedUser(username, email); + lenient().when(userService.updateUser(eq(username), any(UserDto.class))).thenReturn(updatedUser); + + performUpdate(username, userDto) + .andExpect(status().isBadRequest()); + } } - @Test - void testChangePasswordSuccess() throws Exception { - String username = "testuser"; - PasswordChangeDto passwordChangeDto = new PasswordChangeDto(); - passwordChangeDto.setOldPassword("oldPass"); - passwordChangeDto.setNewPassword("newPass"); + @Nested + @DisplayName("회원 비밀번호 수정 테스트") + class ChangePasswordTests { - mockMvc.perform(put("/api/users/" + username + "/password") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(passwordChangeDto))) - .andExpect(status().isOk()) - .andExpect(content().string("Password updated successfully")); + // UserDto 객체 생성 헬퍼 메서드 + private PasswordChangeDto createPasswordChangeDto(String oldPassword, String newPassword) { + PasswordChangeDto passwordChangeDto = new PasswordChangeDto(); + passwordChangeDto.setOldPassword(oldPassword); + passwordChangeDto.setNewPassword(newPassword); + return passwordChangeDto; + } + + // 업데이트된 User 객체 생성 헬퍼 메서드 + private User createUpdatedUser(String username, String password) { + User user = new User(); + user.setUsername(username); + user.setPassword(password); + return user; + } + + @Test + @DisplayName("회원 비밀번호 변경 성공") + void testChangePasswordSuccess() throws Exception { + String username = "testuser"; + PasswordChangeDto passwordChangeDto = createPasswordChangeDto("Password!@3", + "Password!@4"); + + User updatedUser = createUpdatedUser(username, "Password!@4"); + lenient().when(userService.changePassword(eq(username), any(PasswordChangeDto.class))).thenReturn(updatedUser); + + mockMvc.perform(put("/api/users/" + username + "/password") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(passwordChangeDto))) + .andExpect(status().isOk()); + } + + @Test + @DisplayName("회원 비밀번호 변경 실패 - 같은 비번") + void testChangePasswordFail() throws Exception { + String username = "testuser"; + PasswordChangeDto passwordChangeDto = createPasswordChangeDto("Password!@3", + "Password!@3"); + + doThrow(new DuplicateResourceException("새 비밀번호는 이전 비밀번호와 달라야 합니다.")).when(userService) + .changePassword(eq(username), any(PasswordChangeDto.class)); + + // when & then = 예외처리 검출 + Exception exception = assertThrows(Exception.class, () -> { + mockMvc.perform(put("/api/users/" + username + "/password") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(passwordChangeDto))) + .andReturn(); + }); + + // 발생한 예외는 보통 NestedServletException으로 wrapping 되어 있으므로 unwrap하여 root cause를 확인 + Throwable rootCause = exception; + while(rootCause.getCause() != null && !(rootCause instanceof DuplicateResourceException)) { + rootCause = rootCause.getCause(); + } + + assertTrue(rootCause instanceof DuplicateResourceException); + } + } + + @Nested + @DisplayName("로그아웃 테스트") + class LogoutTests { + @Test + @DisplayName("로그아웃 성공") + void testLogoutSuccess() throws Exception { + String token = "Bearer jwt-token"; + + mockMvc.perform(post("/api/users/logout") + .header("Authorization", token)) + .andExpect(status().isOk()) + .andExpect(content().string("You've been logged out successfully.")); + } + + @Test + @DisplayName("로그아웃 실패 - Authorization 헤더 누락") + void testLogoutFailureMissingToken() throws Exception { + // Authorization 헤더 없이 요청을 보내면 인증 실패(401) 상태 코드가 반환되는지 검증 + mockMvc.perform(post("/api/users/logout")) + .andExpect(status().isUnauthorized()); + } + + @Test + @DisplayName("로그아웃 실패 - 잘못된 토큰 형식") + void testLogoutFailureInvalidToken() throws Exception { + // "Bearer " 형식을 갖추지 않거나 유효하지 않은 토큰으로 요청을 보내면 실패하도록 가정 + String invalidToken = "InvalidTokenFormat"; + + mockMvc.perform(post("/api/users/logout") + .header("Authorization", invalidToken)) + .andExpect(status().isUnauthorized()); + } } - @Test - void testDeleteUserSuccess() throws Exception { - String username = "testuser"; + @Nested + @DisplayName("사용자 삭제 테스트") + class DeleteUserTests { + @Test + @DisplayName("사용자 삭제 성공") + void testDeleteUserSuccess() throws Exception { + String username = "testuser"; + + mockMvc.perform(delete("/api/users/" + username)) + .andExpect(status().isOk()) + .andExpect(content().string("User deleted successfully")); + } + + @Test + @DisplayName("유저 삭제 실패 - 존재하지 않는 사용자") + void testDeleteUserFailure() throws Exception { + String username = "nonexistentUser"; + + // userService.deleteUser(username) 호출 시 ResourceNotFoundException 발생하도록 Stub 설정 + doThrow(new ResourceNotFoundException("User not found")) + .when(userService).deleteUser(eq(username)); - mockMvc.perform(delete("/api/users/" + username)) - .andExpect(status().isOk()) - .andExpect(content().string("User deleted successfully")); + // when & then = 예외처리 검출 + Exception exception = assertThrows(Exception.class, () -> { + mockMvc.perform(delete("/api/users/" + username)) + .andExpect(status().isNotFound()) + .andExpect(content().string("User not found")); + }); + + // 발생한 예외는 보통 NestedServletException으로 wrapping 되어 있으므로 unwrap하여 root cause를 확인 + Throwable rootCause = exception; + while(rootCause.getCause() != null && !(rootCause instanceof ResourceNotFoundException)) { + rootCause = rootCause.getCause(); + } + assertTrue(rootCause instanceof ResourceNotFoundException); + } } + + + } diff --git a/src/test/java/com/sparta/spring_deep/_delivery/domain/user/UserServiceTest.java b/src/test/java/com/sparta/spring_deep/_delivery/domain/user/UserServiceTest.java deleted file mode 100644 index c323ba1..0000000 --- a/src/test/java/com/sparta/spring_deep/_delivery/domain/user/UserServiceTest.java +++ /dev/null @@ -1,183 +0,0 @@ -//package com.sparta.spring_deep._delivery.domain.user; -// -//import static org.junit.jupiter.api.Assertions.assertEquals; -//import static org.junit.jupiter.api.Assertions.assertNotNull; -//import static org.mockito.ArgumentMatchers.any; -//import static org.mockito.Mockito.argThat; -//import static org.mockito.Mockito.mock; -//import static org.mockito.Mockito.times; -//import static org.mockito.Mockito.verify; -//import static org.mockito.Mockito.when; -// -//import com.sparta.spring_deep._delivery.domain.user.dto.LoginRequestDto; -//import com.sparta.spring_deep._delivery.domain.user.dto.LoginResponseDto; -//import com.sparta.spring_deep._delivery.domain.user.dto.PasswordChangeDto; -//import com.sparta.spring_deep._delivery.domain.user.dto.UserDto; -//import com.sparta.spring_deep._delivery.domain.user.entity.IsPublic; -//import com.sparta.spring_deep._delivery.domain.user.entity.User; -//import com.sparta.spring_deep._delivery.domain.user.entity.UserRole; -//import com.sparta.spring_deep._delivery.domain.user.repository.UserRepository; -//import com.sparta.spring_deep._delivery.domain.user.service.UserService; -//import com.sparta.spring_deep._delivery.util.JwtUtil; -//import java.util.Optional; -//import org.junit.jupiter.api.BeforeEach; -//import org.junit.jupiter.api.Test; -//import org.junit.jupiter.api.TestInfo; -//import org.junit.jupiter.api.extension.ExtendWith; -//import org.mockito.InjectMocks; -//import org.mockito.Mock; -//import org.mockito.MockitoAnnotations; -//import org.mockito.junit.jupiter.MockitoExtension; -//import org.springframework.security.authentication.AuthenticationManager; -//import org.springframework.security.core.Authentication; -//import org.springframework.security.crypto.password.PasswordEncoder; -// -//@ExtendWith(MockitoExtension.class) -//public class UserServiceTest { -// -// @Mock -// private UserRepository userRepository; -// -// @Mock -// private PasswordEncoder passwordEncoder; -// -// @Mock -// private AuthenticationManager authenticationManager; -// -// @Mock -// private JwtUtil jwtUtil; -// -// @InjectMocks -// private UserService userService; -// -// @BeforeEach -// void setup(TestInfo testInfo) { -// // testLoginSuccess 테스트만 셋팅 제외 -// if (testInfo.getTestMethod().isPresent() && !testInfo.getTestMethod().get().getName() -// .equals("testLoginSuccess")) { -// MockitoAnnotations.openMocks(this); -// } -// } -// -// @Test -// public void testRegisterUserSuccess() { -// MockitoAnnotations.openMocks(this); -// -// UserDto userDto = new UserDto(); -// userDto.setUsername("testuser"); -// userDto.setPassword("password"); -// userDto.setEmail("test@example.com"); -// userDto.setRole(UserRole.CUSTOMER); -// userDto.setIsPublic(IsPublic.PUBLIC); -// -// when(userRepository.existsByUsername("testuser")).thenReturn(false); -// when(userRepository.existsByEmail("test@example.com")).thenReturn(false); -// when(passwordEncoder.encode("password")).thenReturn("encodedPassword"); -// -// User user = new User(); -// user.setUsername("testuser"); -// user.setEmail("test@example.com"); -// -// when(userRepository.save(any(User.class))).thenReturn(user); -// -// User registeredUser = userService.registerUser(userDto); -// assertNotNull(registeredUser); -// assertEquals("testuser", registeredUser.getUsername()); -// verify(userRepository, times(1)).save(any(User.class)); -// } -// -// @Test -// public void testLoginSuccess() { -// LoginRequestDto loginRequestDto = new LoginRequestDto(); -// loginRequestDto.setUsername("testuser"); -// loginRequestDto.setPassword("password123"); -// -// // 모의 Authentication 객체 생성 -// Authentication authentication = mock(Authentication.class); -// // any()를 사용하여 전달되는 인자와 관계없이 항상 모의 객체를 반환하도록 설정 -// when(authenticationManager.authenticate(argThat(token -> -// token.getPrincipal().equals(loginRequestDto.getUsername()) && -// token.getCredentials().equals(loginRequestDto.getPassword()) -// ))).thenReturn(authentication); -// -// // 모의 UserDetailsImpl 객체 생성 및 설정 -// com.sparta.spring_deep._delivery.domain.user.details.UserDetailsImpl userDetails = -// mock(com.sparta.spring_deep._delivery.domain.user.details.UserDetailsImpl.class); -// -// User user = new User(); -// user.setUsername("testuser"); -// user.setEmail("test@example.com"); -// user.setIsPublic(IsPublic.PUBLIC); -// user.setRole(UserRole.CUSTOMER); -// -// when(userDetails.getUser()).thenReturn(user); -// when(userDetails.getUsername()).thenReturn("testuser"); -// when(authentication.getPrincipal()).thenReturn(userDetails); -// -// when(jwtUtil.createJwt("testuser", UserRole.CUSTOMER)).thenReturn("jwt-token"); -// -// LoginResponseDto loginResponseDto = userService.login(loginRequestDto); -// assertNotNull(loginResponseDto); -// assertEquals("jwt-token", loginResponseDto.getToken()); -// verify(authenticationManager, times(1)).authenticate(any()); -// } -// -// @Test -// public void testUpdateUserSuccess() { -// String username = "testuser"; -// UserDto userDto = new UserDto(); -// userDto.setEmail("newemail@example.com"); -// userDto.setRole(UserRole.CUSTOMER); -// userDto.setIsPublic(IsPublic.PRIVATE); -// -// User user = new User(); -// user.setUsername("testuser"); -// user.setEmail("oldemail@example.com"); -// -// when(userRepository.findByUsernameAndIsDeletedFalse(username)).thenReturn( -// Optional.of(user)); -// when(userRepository.existsByEmail("newemail@example.com")).thenReturn(false); -// when(userRepository.save(user)).thenReturn(user); -// -// User updatedUser = userService.updateUser(username, userDto); -// assertEquals("newemail@example.com", updatedUser.getEmail()); -// verify(userRepository, times(1)).save(user); -// } -// -// @Test -// public void testChangePasswordSuccess() { -// String username = "testuser"; -// PasswordChangeDto passwordChangeDto = new PasswordChangeDto(); -// passwordChangeDto.setOldPassword("oldPass"); -// passwordChangeDto.setNewPassword("newPass"); -// -// User user = new User(); -// user.setUsername("testuser"); -// user.setPassword("oldEncodedPassword"); -// -// when(userRepository.findByUsernameAndIsDeletedFalse(username)).thenReturn( -// Optional.of(user)); -// when(passwordEncoder.encode("newPass")).thenReturn("newEncodedPass"); -// when(userRepository.save(user)).thenReturn(user); -// -// userService.changePassword(username, passwordChangeDto); -// assertEquals("newEncodedPass", user.getPassword()); -// verify(userRepository, times(1)).save(user); -// } -// -// @Test -// public void testDeleteUserSuccess() { -// String username = "testuser"; -// -// User user = new User(); -// user.setUsername("testuser"); -// -// when(userRepository.findByUsernameAndIsDeletedFalse(username)).thenReturn( -// Optional.of(user)); -// when(userRepository.save(user)).thenReturn(user); -// -// userService.deleteUser(username); -// // soft delete의 경우, 삭제 플래그가 설정되었는지 검증 가능 (User.delete() 내부 로직에 따라) -// verify(userRepository, times(1)).save(user); -// } -//}