From db9bc7ba2e5b0ccab03400c8b5f6e2e1d0e88def Mon Sep 17 00:00:00 2001 From: giibeom Date: Fri, 25 Nov 2022 03:12:13 +0900 Subject: [PATCH] =?UTF-8?q?feat(user):=20=ED=9A=8C=EC=9B=90=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20API=EC=97=90=20=EC=9D=B8=EC=A6=9D(Auth)=20=EA=B3=BC?= =?UTF-8?q?=EC=A0=95=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/presentation/UserController.java | 6 +- .../presentation/UserControllerMockTest.java | 188 +++++++++++++----- 2 files changed, 139 insertions(+), 55 deletions(-) diff --git a/app/src/main/java/com/codesoom/assignment/user/presentation/UserController.java b/app/src/main/java/com/codesoom/assignment/user/presentation/UserController.java index a948b0f9..5a087e2b 100644 --- a/app/src/main/java/com/codesoom/assignment/user/presentation/UserController.java +++ b/app/src/main/java/com/codesoom/assignment/user/presentation/UserController.java @@ -6,6 +6,8 @@ import com.codesoom.assignment.user.presentation.dto.UserRegistrationData; import com.codesoom.assignment.user.presentation.dto.UserResultData; import org.springframework.http.HttpStatus; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.PatchMapping; @@ -36,8 +38,10 @@ UserResultData create(@RequestBody @Valid final UserRegistrationData registratio } @PatchMapping("{id}") + @PreAuthorize("isAuthenticated()") UserResultData update(@PathVariable final Long id, - @RequestBody @Valid final UserModificationData modificationData) { + @RequestBody @Valid final UserModificationData modificationData, + final Authentication authentication) { User user = userService.updateUser(id, modificationData); return getUserResultData(user); } diff --git a/app/src/test/java/com/codesoom/assignment/user/presentation/UserControllerMockTest.java b/app/src/test/java/com/codesoom/assignment/user/presentation/UserControllerMockTest.java index f7e71ef1..101483bb 100644 --- a/app/src/test/java/com/codesoom/assignment/user/presentation/UserControllerMockTest.java +++ b/app/src/test/java/com/codesoom/assignment/user/presentation/UserControllerMockTest.java @@ -3,6 +3,7 @@ import com.codesoom.assignment.MockMvcCharacterEncodingCustomizer; import com.codesoom.assignment.common.utils.JsonUtil; import com.codesoom.assignment.session.application.AuthenticationService; +import com.codesoom.assignment.session.application.exception.InvalidTokenException; import com.codesoom.assignment.support.UserFixture; import com.codesoom.assignment.user.application.UserService; import com.codesoom.assignment.user.application.exception.UserNotFoundException; @@ -18,10 +19,13 @@ 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.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; +import static com.codesoom.assignment.support.AuthHeaderFixture.INVALID_VALUE_TOKEN_1; +import static com.codesoom.assignment.support.AuthHeaderFixture.VALID_TOKEN_1; import static com.codesoom.assignment.support.IdFixture.ID_MAX; import static com.codesoom.assignment.support.IdFixture.ID_MIN; import static com.codesoom.assignment.support.UserFixture.USER_1; @@ -55,6 +59,15 @@ class UserControllerMockTest { @MockBean private AuthenticationService authenticationService; + @BeforeEach + void setUp() { + given(authenticationService.parseToken(eq(VALID_TOKEN_1.토큰_값()))) + .willReturn(VALID_TOKEN_1.아이디()); + + given(authenticationService.parseToken(eq(INVALID_VALUE_TOKEN_1.토큰_값()))) + .willThrow(new InvalidTokenException(INVALID_VALUE_TOKEN_1.토큰_값())); + } + @Nested @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) class 회원_등록_API는 { @@ -144,85 +157,150 @@ void it_responses_400() throws Exception { class 회원_수정_API는 { @Nested - @DisplayName("찾을 수 있는 id가 주어질 때") - class Context_with_exist_id { + @DisplayName("인증 토큰이 없다면") + class Context_with_not_exist_token { - @BeforeEach - void setUp() { - given(userService.updateUser(eq(ID_MIN.value()), any(UserModificationData.class))) - .willReturn(USER_1.회원_엔티티_생성(ID_MIN.value())); + @Test + @DisplayName("401 코드로 응답한다") + void it_responses_401() throws Exception { + ResultActions perform = mockMvc.perform( + patch(REQUEST_USER_URL + "/" + ID_MIN.value()) + .contentType(MediaType.APPLICATION_JSON) + .content(JsonUtil.writeValueAsString(USER_1.수정_요청_데이터_생성())) + ); + + perform.andExpect(status().isUnauthorized()); + + // 아니 왜 해당 테스트를 단일로 실행하면 통과 되는데, 전체 실행을 하면 invoke가 됐다고 실패가 되지...? + // 해당 구문은 isAuthenticated()에서 실패돼서 컨트롤러로 못들어오는 로직이여야 하는데.. + // 심지어 위에 status().isUnauthorized() 코드는 검증 성공으로 뜸.... (verfiy 구문 주석하면 테스트 올패스) + verify(userService, never()).updateUser(eq(ID_MIN.value()), any(UserModificationData.class)); } + } - @Nested - @DisplayName("유효한 회원 정보가 주어지면") - class Context_with_valid_user { - - @Test - @DisplayName("200 코드로 응답한다") - void it_responses_200() throws Exception { - ResultActions perform = 회원_수정_API_요청(ID_MIN.value(), USER_1); - - perform.andExpect(status().isOk()); - USER_이름_이메일_값_검증(perform, USER_1); + @Nested + @DisplayName("유효하지 않은 인증 토큰이 주어지면") + class Context_with_invalid_token { - verify(userService).updateUser(eq(ID_MIN.value()), any(UserModificationData.class)); - } + @Test + @DisplayName("401 코드로 응답한다") + void it_responses_401() throws Exception { + ResultActions perform = 회원_수정_API_요청( + INVALID_VALUE_TOKEN_1.인증_헤더값(), + ID_MIN.value(), + USER_1 + ); + + perform.andExpect(status().isUnauthorized()); + + // 아니 왜 해당 테스트를 단일로 실행하면 통과 되는데, 전체 실행을 하면 invoke가 됐다고 실패가 되지...? + // 해당 구문은 isAuthenticated()에서 실패돼서 컨트롤러로 못들어오는 로직이여야 하는데.. + // 심지어 위에 status().isUnauthorized() 코드는 검증 성공으로 뜸.... (verfiy 구문 주석하면 테스트 올패스) + verify(userService, never()).updateUser(eq(ID_MIN.value()), any(UserModificationData.class)); } + } + + @Nested + @DisplayName("유효한 인증 토큰이 주어지고") + class Context_with_valid_token { @Nested - @DisplayName("유효하지 않은 회원 정보가 주어지면") - class Context_with_invalid_user { + @DisplayName("찾을 수 있는 id가 주어질 때") + class Context_with_exist_id { + + @BeforeEach + void setUp() { + given(userService.updateUser(eq(ID_MIN.value()), any(UserModificationData.class))) + .willReturn(USER_1.회원_엔티티_생성(ID_MIN.value())); + } @Nested - @DisplayName("이름이 공백일 경우") - class Context_with_empty_name { + @DisplayName("유효한 회원 정보가 주어지면") + class Context_with_valid_user { @Test - @DisplayName("400 코드로 응답한다") - void it_responses_400() throws Exception { - ResultActions perform = 회원_수정_API_요청(ID_MIN.value(), USER_INVALID_NAME); - - perform.andExpect(status().isBadRequest()); - - verify(userService, never()).updateUser(eq(ID_MIN.value()), any(UserModificationData.class)); + @DisplayName("200 코드로 응답한다") + void it_responses_200() throws Exception { + ResultActions perform = 회원_수정_API_요청( + VALID_TOKEN_1.인증_헤더값(), + ID_MIN.value(), + USER_1 + ); + + perform.andExpect(status().isOk()); + USER_이름_이메일_값_검증(perform, USER_1); + + verify(userService).updateUser(eq(ID_MIN.value()), any(UserModificationData.class)); } } @Nested - @DisplayName("비밀번호가 4글자 미만일 경우") - class Context_with_invalid_password { + @DisplayName("유효하지 않은 회원 정보가 주어지면") + class Context_with_invalid_user { + + @Nested + @DisplayName("이름이 공백일 경우") + class Context_with_empty_name { + + @Test + @DisplayName("400 코드로 응답한다") + void it_responses_400() throws Exception { + ResultActions perform = 회원_수정_API_요청( + VALID_TOKEN_1.인증_헤더값(), + ID_MIN.value(), + USER_INVALID_NAME + ); + + perform.andExpect(status().isBadRequest()); + + verify(userService, never()).updateUser(eq(ID_MIN.value()), any(UserModificationData.class)); + } + } - @Test - @DisplayName("400 코드로 응답한다") - void it_responses_400() throws Exception { - ResultActions perform = 회원_수정_API_요청(ID_MIN.value(), USER_INVALID_PASSWORD); + @Nested + @DisplayName("비밀번호가 4글자 미만일 경우") + class Context_with_invalid_password { + + @Test + @DisplayName("400 코드로 응답한다") + void it_responses_400() throws Exception { + ResultActions perform = 회원_수정_API_요청( + VALID_TOKEN_1.인증_헤더값(), + ID_MIN.value(), + USER_INVALID_PASSWORD + ); - perform.andExpect(status().isBadRequest()); + perform.andExpect(status().isBadRequest()); - verify(userService, never()).updateUser(eq(ID_MIN.value()), any(UserModificationData.class)); + verify(userService, never()).updateUser(eq(ID_MIN.value()), any(UserModificationData.class)); + } } } } - } - @Nested - @DisplayName("찾을 수 없는 id가 주어질 때") - class Context_with_not_exist_id { + @Nested + @DisplayName("찾을 수 없는 id가 주어질 때") + class Context_with_not_exist_id { - @BeforeEach - void setUp() { - given(userService.updateUser(eq(ID_MAX.value()), any(UserModificationData.class))) - .willThrow(new UserNotFoundException(ID_MAX.value())); - } + @BeforeEach + void setUp() { + given(userService.updateUser(eq(ID_MAX.value()), any(UserModificationData.class))) + .willThrow(new UserNotFoundException(ID_MAX.value())); + } - @Test - @DisplayName("404 코드로 응답한다") - void it_responses_404() throws Exception { - ResultActions perform = 회원_수정_API_요청(ID_MAX.value(), USER_1); + @Test + @DisplayName("404 코드로 응답한다") + void it_responses_404() throws Exception { + ResultActions perform = 회원_수정_API_요청( + VALID_TOKEN_1.인증_헤더값(), + ID_MAX.value(), + USER_1 + ); - perform.andExpect(status().isNotFound()); + perform.andExpect(status().isNotFound()); - verify(userService).updateUser(eq(ID_MAX.value()), any(UserModificationData.class)); + verify(userService).updateUser(eq(ID_MAX.value()), any(UserModificationData.class)); + } } } } @@ -276,10 +354,12 @@ void it_responses_404() throws Exception { ); } - private ResultActions 회원_수정_API_요청(Long userId, + private ResultActions 회원_수정_API_요청(String authHeader, + Long userId, UserFixture userFixture) throws Exception { return mockMvc.perform( patch(REQUEST_USER_URL + "/" + userId) + .header(HttpHeaders.AUTHORIZATION, authHeader) .contentType(MediaType.APPLICATION_JSON) .content(JsonUtil.writeValueAsString(userFixture.수정_요청_데이터_생성())) );