diff --git a/src/main/java/com/diareat/diareat/user/controller/UserController.java b/src/main/java/com/diareat/diareat/user/controller/UserController.java index 5024b92..66693b6 100644 --- a/src/main/java/com/diareat/diareat/user/controller/UserController.java +++ b/src/main/java/com/diareat/diareat/user/controller/UserController.java @@ -28,7 +28,7 @@ public ApiResponse saveUser(CreateUserDto createUserDto) { // 회원 기본정보 조회 @Operation(summary = "[프로필] 회원 기본정보 조회", description = "회원 기본정보를 조회합니다.") - @GetMapping("{userId}/info/simple/") + @GetMapping("{userId}/info/simple") public ApiResponse getSimpleUserInfo(@PathVariable Long userId) { return ApiResponse.success(userService.getSimpleUserInfo(userId), ResponseCode.USER_CREATE_SUCCESS.getMessage()); } diff --git a/src/main/java/com/diareat/diareat/user/dto/ResponseSearchUserDto.java b/src/main/java/com/diareat/diareat/user/dto/ResponseSearchUserDto.java index ef4a23a..811fa92 100644 --- a/src/main/java/com/diareat/diareat/user/dto/ResponseSearchUserDto.java +++ b/src/main/java/com/diareat/diareat/user/dto/ResponseSearchUserDto.java @@ -10,7 +10,7 @@ public class ResponseSearchUserDto { private Long userId; private String name; private String image; - private boolean isFollow; // 유저가 이미 팔로우한 유저인지 확인 + private boolean follow; // 유저가 이미 팔로우한 유저인지 확인 public static ResponseSearchUserDto of(Long userId, String name, String image, boolean isFollow) { return new ResponseSearchUserDto(userId, name, image, isFollow); diff --git a/src/main/java/com/diareat/diareat/user/service/UserService.java b/src/main/java/com/diareat/diareat/user/service/UserService.java index 80db78a..22e85ef 100644 --- a/src/main/java/com/diareat/diareat/user/service/UserService.java +++ b/src/main/java/com/diareat/diareat/user/service/UserService.java @@ -56,14 +56,14 @@ public void updateUserInfo(UpdateUserDto updateUserDto) { user.updateUser(updateUserDto.getName(), updateUserDto.getHeight(), updateUserDto.getWeight(), updateUserDto.getAge()); } - // 회원 기준영양소 조회 + // 회원 기준섭취량 조회 @Transactional(readOnly = true) public ResponseUserNutritionDto getUserNutrition(Long userId) { User user = getUserById(userId); return ResponseUserNutritionDto.from(user); } - // 회원 기준영양소 직접 수정 + // 회원 기준섭취량 직접 수정 @Transactional public void updateBaseNutrition(UpdateUserNutritionDto updateUserNutritionDto) { User user = getUserById(updateUserNutritionDto.getUserId()); diff --git a/src/main/java/com/diareat/diareat/util/api/ApiBody.java b/src/main/java/com/diareat/diareat/util/api/ApiBody.java deleted file mode 100644 index d4fca17..0000000 --- a/src/main/java/com/diareat/diareat/util/api/ApiBody.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.diareat.diareat.util.api; - -public class ApiBody { - - private final T data; - private final T msg; - - public ApiBody(T data, T msg) { - this.data = data; - this.msg = msg; - } - - public T getData() { - return data; - } -} diff --git a/src/main/java/com/diareat/diareat/util/api/ApiHeader.java b/src/main/java/com/diareat/diareat/util/api/ApiHeader.java index f6c4fb9..7788e9a 100644 --- a/src/main/java/com/diareat/diareat/util/api/ApiHeader.java +++ b/src/main/java/com/diareat/diareat/util/api/ApiHeader.java @@ -1,18 +1,15 @@ package com.diareat.diareat.util.api; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor public class ApiHeader { private int code; private String message; - public ApiHeader(int code, String message) { - this.code = code; - this.message = message; - } - - public ApiHeader() { - } - public int getCode() { return code; } diff --git a/src/main/java/com/diareat/diareat/util/api/ApiResponse.java b/src/main/java/com/diareat/diareat/util/api/ApiResponse.java index 4fe2374..ba96c5e 100644 --- a/src/main/java/com/diareat/diareat/util/api/ApiResponse.java +++ b/src/main/java/com/diareat/diareat/util/api/ApiResponse.java @@ -8,24 +8,22 @@ public class ApiResponse { private ApiHeader header; - private ApiBody body; + private T data; + private String msg; private static final int SUCCESS = 200; - public ApiResponse(ApiHeader header, ApiBody body) { - this.header = header; - this.body = body; - } - - public ApiResponse(ApiHeader header) { + private ApiResponse(ApiHeader header, T data, String msg) { this.header = header; + this.data = data; + this.msg = msg; } public static ApiResponse success(T data, String message) { - return new ApiResponse(new ApiHeader(SUCCESS, "SUCCESS"), new ApiBody(data, message)); + return new ApiResponse(new ApiHeader(SUCCESS, "SUCCESS"), data, message); } public static ApiResponse fail(ResponseCode responseCode) { - return new ApiResponse(new ApiHeader(responseCode.getHttpStatusCode(), responseCode.getMessage()), new ApiBody(null, responseCode.getMessage())); + return new ApiResponse(new ApiHeader(responseCode.getHttpStatusCode(), responseCode.getMessage()), null, responseCode.getMessage()); } } \ No newline at end of file diff --git a/src/test/java/com/diareat/diareat/controller/UserControllerTest.java b/src/test/java/com/diareat/diareat/controller/UserControllerTest.java new file mode 100644 index 0000000..7fbdb95 --- /dev/null +++ b/src/test/java/com/diareat/diareat/controller/UserControllerTest.java @@ -0,0 +1,222 @@ +package com.diareat.diareat.controller; + +import com.diareat.diareat.user.controller.UserController; +import com.diareat.diareat.user.domain.BaseNutrition; +import com.diareat.diareat.user.domain.User; +import com.diareat.diareat.user.dto.*; +import com.diareat.diareat.user.service.UserService; +import com.diareat.diareat.util.api.ApiResponse; +import com.diareat.diareat.util.api.ResponseCode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + + +import java.util.List; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +@WebMvcTest(controllers = UserController.class) +class UserControllerTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private WebApplicationContext webApplicationContext; + + @MockBean + private UserService userService; + + private final Long testUserId = 1L; + private final ObjectMapper mapper = new ObjectMapper(); + private final User testUser = User.createUser("test", "test","test", 180, 70, 0, 20, BaseNutrition.createNutrition(2000, 300, 80, 80)); + + @BeforeEach + void setUp() { + mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build(); + testUser.setId(testUserId); + when(userService.getSimpleUserInfo(testUserId)).thenReturn(ResponseSimpleUserDto.of(testUser.getName(), testUser.getImage(), 100.0)); + when(userService.getUserInfo(testUserId)).thenReturn(ResponseUserDto.from(testUser)); + } + + @DisplayName("회원정보 저장") + @Test + @WithMockUser("test") + void testSaveUser() throws Exception { + // Given + ApiResponse expectedResponse = ApiResponse.success(testUserId, ResponseCode.USER_CREATE_SUCCESS.getMessage()); + String json = "{\"name\":\"test\",\"image\":\"test\",\"keyCode\":\"test\",\"gender\":0,\"height\":180,\"weight\":70,\"age\":20}"; + when(userService.saveUser(any(CreateUserDto.class))).thenReturn(testUserId); + + // When & Then + mockMvc.perform( MockMvcRequestBuilders + .post("/api/user/save") + .contentType(MediaType.APPLICATION_JSON) + .content(json) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.jsonPath("$.header.code").value(expectedResponse.getHeader().getCode())) + .andExpect(MockMvcResultMatchers.jsonPath("$.header.message").value(expectedResponse.getHeader().getMessage())) + .andExpect(MockMvcResultMatchers.jsonPath("$.msg").value(expectedResponse.getMsg())) + .andExpect(MockMvcResultMatchers.jsonPath("$.data").value(expectedResponse.getData())); + } + + + @DisplayName("회원 기본정보 조회") + @Test + @WithMockUser("test") + void testGetSimpleUserInfo() throws Exception { + // Given + ApiResponse expectedResponse = ApiResponse.success( + ResponseSimpleUserDto.of(testUser.getName(), testUser.getImage(), 100.0), ResponseCode.USER_CREATE_SUCCESS.getMessage()); + + // When & Then + mockMvc.perform( MockMvcRequestBuilders + .get("/api/user/1/info/simple", 1L) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.jsonPath("$.header.code").value(expectedResponse.getHeader().getCode())) + .andExpect(MockMvcResultMatchers.jsonPath("$.header.message").value(expectedResponse.getHeader().getMessage())) + .andExpect(MockMvcResultMatchers.jsonPath("$.data.name").value(expectedResponse.getData().getName())) + .andExpect(MockMvcResultMatchers.jsonPath("$.data.image").value(expectedResponse.getData().getImage())) + .andExpect(MockMvcResultMatchers.jsonPath("$.data.nutritionScore").value(expectedResponse.getData().getNutritionScore())); + } + + @DisplayName("회원정보 조회") + @Test + @WithMockUser("test") + void getUserInfo() throws Exception { + // Given + ApiResponse expectedResponse = ApiResponse.success( + ResponseUserDto.from(testUser), ResponseCode.USER_CREATE_SUCCESS.getMessage()); + + // When & Then + mockMvc.perform( MockMvcRequestBuilders + .get("/api/user/1/info", 1L) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.jsonPath("$.header.code").value(expectedResponse.getHeader().getCode())) + .andExpect(MockMvcResultMatchers.jsonPath("$.header.message").value(expectedResponse.getHeader().getMessage())) + .andExpect(MockMvcResultMatchers.jsonPath("$.data.name").value(expectedResponse.getData().getName())) + .andExpect(MockMvcResultMatchers.jsonPath("$.data.height").value(expectedResponse.getData().getHeight())) + .andExpect(MockMvcResultMatchers.jsonPath("$.data.weight").value(expectedResponse.getData().getWeight())) + .andExpect(MockMvcResultMatchers.jsonPath("$.data.age").value(expectedResponse.getData().getAge())); + } + + @DisplayName("회원정보 수정") + @Test + @WithMockUser("test") + void updateUser() throws Exception { + // Given + ApiResponse expectedResponse = ApiResponse.success(null, ResponseCode.USER_UPDATE_SUCCESS.getMessage()); + UpdateUserDto user = UpdateUserDto.of(testUserId, "test2", 170, 80, 21, true); + String json = mapper.writeValueAsString(user); + + // When & Then + mockMvc.perform( MockMvcRequestBuilders + .put("/api/user/update") + .contentType(MediaType.APPLICATION_JSON) + .content(json) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.jsonPath("$.header.code").value(expectedResponse.getHeader().getCode())) + .andExpect(MockMvcResultMatchers.jsonPath("$.header.message").value(expectedResponse.getHeader().getMessage())) + .andExpect(MockMvcResultMatchers.jsonPath("$.msg").value(expectedResponse.getMsg())); + } + + @DisplayName("회원 기준섭취량 조회") + @Test + @WithMockUser("test") + void getUserNutrition() throws Exception { + // Given + when(userService.getUserNutrition(testUserId)).thenReturn(ResponseUserNutritionDto.of(2000, 300, 300, 80)); + ApiResponse expectedResponse = ApiResponse.success( + ResponseUserNutritionDto.of(2000, 300, 300, 80), ResponseCode.USER_READ_SUCCESS.getMessage()); + + // When & Then + mockMvc.perform( MockMvcRequestBuilders + .get("/api/user/1/nutrition", 1L) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.jsonPath("$.header.code").value(expectedResponse.getHeader().getCode())) + .andExpect(MockMvcResultMatchers.jsonPath("$.header.message").value(expectedResponse.getHeader().getMessage())) + .andExpect(MockMvcResultMatchers.jsonPath("$.data.calorie").value(expectedResponse.getData().getCalorie())) + .andExpect(MockMvcResultMatchers.jsonPath("$.data.protein").value(expectedResponse.getData().getProtein())) + .andExpect(MockMvcResultMatchers.jsonPath("$.data.fat").value(expectedResponse.getData().getFat())) + .andExpect(MockMvcResultMatchers.jsonPath("$.data.carbohydrate").value(expectedResponse.getData().getCarbohydrate())); + } + + @DisplayName("회원 기준섭취량 직접 수정") + @Test + @WithMockUser("test") + void updateUserNutrition() throws Exception { + // Given + ApiResponse expectedResponse = ApiResponse.success(null, ResponseCode.USER_UPDATE_SUCCESS.getMessage()); + UpdateUserNutritionDto nutrition = UpdateUserNutritionDto.of(testUserId, 2000, 300, 300, 80); + String json = mapper.writeValueAsString(nutrition); + + // When & Then + mockMvc.perform( MockMvcRequestBuilders + .put("/api/user/1/nutrition") + .contentType(MediaType.APPLICATION_JSON) + .content(json) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.jsonPath("$.header.code").value(expectedResponse.getHeader().getCode())) + .andExpect(MockMvcResultMatchers.jsonPath("$.header.message").value(expectedResponse.getHeader().getMessage())) + .andExpect(MockMvcResultMatchers.jsonPath("$.msg").value(expectedResponse.getMsg())); + } + + @DisplayName("회원의 친구 검색 결과 조회") + @Test + @WithMockUser("test") + void searchUser() throws Exception { + // Given + when(userService.searchUser(testUserId, "test")).thenReturn(List.of(ResponseSearchUserDto.of(2L, "test2", "test2", true))); + ApiResponse> expectedResponse = ApiResponse.success( + List.of(ResponseSearchUserDto.of(2L, "test2", "test2", true)), ResponseCode.USER_SEARCH_SUCCESS.getMessage()); + + // When & Then + mockMvc.perform( MockMvcRequestBuilders + .get("/api/user/1/search/test").param("name", "test") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.jsonPath("$.header.code").value(expectedResponse.getHeader().getCode())) + .andExpect(MockMvcResultMatchers.jsonPath("$.header.message").value(expectedResponse.getHeader().getMessage())) + .andExpect(MockMvcResultMatchers.jsonPath("$.data[0].userId").value(2L)) + .andExpect(MockMvcResultMatchers.jsonPath("$.data[0].name").value(expectedResponse.getData().get(0).getName())) + .andExpect(MockMvcResultMatchers.jsonPath("$.data[0].image").value(expectedResponse.getData().get(0).getImage())) + .andExpect(MockMvcResultMatchers.jsonPath("$.data[0].follow").value(expectedResponse.getData().get(0).isFollow())); + } + + @DisplayName("회원이 특정 회원 팔로우 및 팔로우 취소") + @Test + @WithMockUser("test") + void followUser() throws Exception { + // Given + ApiResponse expectedResponse = ApiResponse.success(null, ResponseCode.USER_UPDATE_SUCCESS.getMessage()); + + // When & Then + mockMvc.perform( MockMvcRequestBuilders + .post("/api/user/1/follow/2") + //.delete("/api/user/1/follow/2") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.jsonPath("$.header.code").value(expectedResponse.getHeader().getCode())) + .andExpect(MockMvcResultMatchers.jsonPath("$.header.message").value(expectedResponse.getHeader().getMessage())) + .andExpect(MockMvcResultMatchers.jsonPath("$.msg").value(expectedResponse.getMsg())); + } +} diff --git a/src/test/java/com/diareat/diareat/service/UserServiceTest.java b/src/test/java/com/diareat/diareat/service/UserServiceTest.java index 0d062e9..98641d4 100644 --- a/src/test/java/com/diareat/diareat/service/UserServiceTest.java +++ b/src/test/java/com/diareat/diareat/service/UserServiceTest.java @@ -7,6 +7,7 @@ import com.diareat.diareat.user.repository.FollowRepository; import com.diareat.diareat.user.repository.UserRepository; import com.diareat.diareat.user.service.UserService; +import com.diareat.diareat.util.exception.UserException; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -54,6 +55,22 @@ void saveUser() { // setId() 메서드로 테스트 진행함 verify(userRepository, times(1)).save(any(User.class)); } + @DisplayName("회원정보 저장 닉네임 중복") + @Test + void saveUserDupliacedName() { + // Given + CreateUserDto createUserDto = CreateUserDto.of("test", "profile.jpg", "testPassword", 0, 75, 1, 25); + BaseNutrition baseNutrition = BaseNutrition.createNutrition(2000, 300, 80, 80); + User user = User.createUser(createUserDto.getName(), createUserDto.getImage(), createUserDto.getKeyCode(), createUserDto.getHeight(), createUserDto.getWeight(), createUserDto.getGender(), createUserDto.getAge(), baseNutrition); + user.setId(1L); // 테스트 커밋 중 User에 setId() 메서드 임시적으로 삽입하여 테스트 진행함 + + given(userRepository.existsByName("test")).willReturn(true); + + // When -> 예외처리 + assertThrows(UserException.class, () -> userService.saveUser(createUserDto)); + } + + @DisplayName("회원 기본정보 조회") @Test void getSimpleUserInfo() { // Given @@ -69,6 +86,7 @@ void getSimpleUserInfo() { assertEquals("profile.jpg", result.getImage()); } + @DisplayName("회원정보 조회") @Test void getUserInfo() { // Given @@ -84,6 +102,7 @@ void getUserInfo() { assertEquals(30, result.getAge()); } + @DisplayName("회원정보 수정") @Test void updateUserInfo() { // given @@ -102,6 +121,7 @@ void updateUserInfo() { assertEquals(25, user.getAge()); } + @DisplayName("회원 기준섭취량 조회") @Test void getUserNutrition() { // 임시 코드 사용, 추후 로직 개편 시 테스트코드 수정 // Given @@ -119,6 +139,7 @@ void getUserNutrition() { // 임시 코드 사용, 추후 로직 개편 시 테 assertEquals(80, result.getFat()); } + @DisplayName("회원 기준섭취량 직접 수정") @Test void updateBaseNutrition() { // Given @@ -137,6 +158,7 @@ void updateBaseNutrition() { assertEquals(80, user.getBaseNutrition().getFat()); } + @DisplayName("회원 탈퇴") @Test void deleteUser() { // Given @@ -150,6 +172,7 @@ void deleteUser() { verify(userRepository, times(1)).deleteById(userId); } + @DisplayName("회원의 친구 검색 결과 조회") @Test void searchUser() { // setId() 메서드로 테스트 진행함 // Given @@ -183,6 +206,7 @@ void searchUser() { // setId() 메서드로 테스트 진행함 verify(userRepository, times(1)).findAllByNameContaining(name); } + @DisplayName("회원이 특정 회원 팔로우") @Test void followUser() { // Given @@ -203,6 +227,22 @@ void followUser() { verify(followRepository, times(1)).save(any(Follow.class)); } + @DisplayName("회원이 특정 회원 팔로우 중복 요청") + @Test + void followerUserDuplicate() { + // Given + Long userId = 1L; // 팔로우 요청을 보낸 사용자의 ID + Long followId = 2L; // 팔로우할 사용자의 ID + + given(userRepository.existsById(userId)).willReturn(true); // userId에 해당하는 사용자가 존재함 + given(userRepository.existsById(followId)).willReturn(true); // followId에 해당하는 사용자가 존재함 + given(followRepository.existsByFromUserAndToUser(userId, followId)).willReturn(true); // 아직 팔로우 중이 아님 + + // When -> 예외처리 + assertThrows(UserException.class, () -> userService.followUser(userId, followId)); + } + + @DisplayName("회원이 특정 회원 팔로우 취소") @Test void unfollowUser() { // Given @@ -219,4 +259,19 @@ void unfollowUser() { // Then verify(followRepository, times(1)).delete(any(Follow.class)); } -} \ No newline at end of file + + @DisplayName("회원이 특정 회원 팔로우 취소 중복 요청") + @Test + void unfollowUserDuplicate() { + // Given + Long userId = 1L; // 팔로우 취소를 요청한 사용자의 ID + Long unfollowId = 2L; // 팔로우를 취소할 사용자의 ID + + given(userRepository.existsById(userId)).willReturn(true); // userId에 해당하는 사용자가 존재함 + given(userRepository.existsById(unfollowId)).willReturn(true); // unfollowId에 해당하는 사용자가 존재함 + given(followRepository.existsByFromUserAndToUser(userId, unfollowId)).willReturn(false); + + // When -> 예외처리 + assertThrows(UserException.class, () -> userService.unfollowUser(userId, unfollowId)); + } +}