Skip to content

Commit

Permalink
feat(user): 회원 수정 API에 인증(Auth) 과정 적용
Browse files Browse the repository at this point in the history
  • Loading branch information
giibeom committed Nov 24, 2022
1 parent 261b4a9 commit db9bc7b
Show file tree
Hide file tree
Showing 2 changed files with 139 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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는 {
Expand Down Expand Up @@ -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));
}
}
}
}
Expand Down Expand Up @@ -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.수정_요청_데이터_생성()))
);
Expand Down

0 comments on commit db9bc7b

Please sign in to comment.