Skip to content

Commit

Permalink
Merge pull request #61 from Project-Catcher/feat-hg-signout
Browse files Browse the repository at this point in the history
feat: 회원탈퇴 기능 구현
  • Loading branch information
dev-khg authored Dec 11, 2023
2 parents df2276c + 5183636 commit 3fc82c9
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 11 deletions.
2 changes: 1 addition & 1 deletion src/main/java/com/catcher/common/BaseResponseStatus.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public enum BaseResponseStatus {
CODE_NOT_MATCH(2115, "인증번호가 일치하지 않습니다."),
PASSWORD_NOT_MATCH(2116, "패스워드가 일치하지 않습니다."),
EXPIRED_CODE(2117, "코드가 만료되었습니다."),

USERS_NOT_LOGIN(2118, "로그인 정보를 찾을 수 없습니다."),

/**
* 3000 : Response 오류
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/com/catcher/core/domain/entity/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.catcher.core.domain.entity.enums.UserRole;
import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.Where;

import java.time.ZonedDateTime;
import java.util.Date;
Expand All @@ -16,6 +17,7 @@
@Table(name = "users")
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Builder
@Where(clause = "deleted_at is null")
public class User extends BaseTimeEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Expand Down Expand Up @@ -71,4 +73,8 @@ public class User extends BaseTimeEntity {
public void changePassword(String newPassword) {
this.password = newPassword;
}

public void signOut() {
this.deletedAt = ZonedDateTime.now();
}
}
8 changes: 8 additions & 0 deletions src/main/java/com/catcher/core/service/UserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@ public boolean checkUsernameExist(String username) {
return userRepository.findByUsername(username).isPresent();
}

public void signOutUser(User user) {
if(user == null) {
throw new BaseException(USERS_NOT_LOGIN);
}

user.signOut();
}

private TokenDto checkAuthenticationAndGetTokenDto(String username, String password) {
try {
CatcherUser authentication = (CatcherUser) authenticationManager.authenticate(
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/com/catcher/resource/UserController.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,14 @@ public CommonResponse<Void> logout(HttpServletRequest request,
return success();
}

@Operation(summary = "회원탈퇴")
@DeleteMapping("/sign-out")
public CommonResponse<Void> signOut(@CurrentUser User user) {
userService.signOutUser(user);

return success();
}

@Operation(summary = "이메일 인증코드 발송")
@PostMapping({FIND_ID_URL, FIND_PW_URL})
public CommonResponse<Void> sendEmailWithAuthCode(
Expand Down
77 changes: 67 additions & 10 deletions src/test/java/com/catcher/resource/UserControllerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
import com.catcher.app.AppApplication;
import com.catcher.common.BaseResponseStatus;
import com.catcher.common.CatcherControllerAdvice;
import com.catcher.common.GlobalExceptionHandlerFilter;
import com.catcher.common.response.CommonResponse;
import com.catcher.config.JwtFilter;
import com.catcher.config.JwtTokenProvider;
import com.catcher.core.database.DBManager;
import com.catcher.core.database.UserRepository;
import com.catcher.core.domain.entity.User;
Expand All @@ -23,7 +26,6 @@
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.security.crypto.password.PasswordEncoder;
Expand All @@ -39,6 +41,7 @@
import java.time.ZonedDateTime;
import java.util.UUID;

import static com.catcher.common.BaseResponseStatus.USERS_NOT_LOGIN;
import static com.catcher.core.domain.entity.enums.UserProvider.CATCHER;
import static com.catcher.utils.JwtUtils.REFRESH_TOKEN_NAME;
import static com.catcher.utils.KeyGenerator.AuthType.BLACK_LIST_ACCESS_TOKEN;
Expand All @@ -62,6 +65,8 @@ class UserControllerTest {
ObjectMapper objectMapper;
@Autowired
DBManager dbManager;
@Autowired
JwtTokenProvider jwtTokenProvider;

User user;

Expand All @@ -75,6 +80,8 @@ void beforeEach() {
.standaloneSetup(userController)
.setControllerAdvice(new CatcherControllerAdvice())
.addFilter(new CharacterEncodingFilter("UTF-8", true))
.addFilter(new GlobalExceptionHandlerFilter(objectMapper))
.addFilter(new JwtFilter(jwtTokenProvider, dbManager))
.setCustomArgumentResolvers(new AuthenticationPrincipalArgumentResolver())
.build();
user = userRepository.save(createUser());
Expand Down Expand Up @@ -258,7 +265,7 @@ void invalid_phone_email_signup() throws Exception {

@DisplayName("정상 회원가입 시 토큰 발행")
@Test
void valid_signup() throws Exception{
void valid_signup() throws Exception {
//given
UserCreateRequest userCreateRequest = userCreateRequest(
createRandomUUID(),
Expand Down Expand Up @@ -357,20 +364,20 @@ void valid_logout() throws Exception {
//then
assertThat(dbManager.getValue(KeyGenerator.generateKey(accessToken, BLACK_LIST_ACCESS_TOKEN))).isPresent();
}
@Autowired
RedisTemplate redisTemplate;

@DisplayName("비정상 토큰으로 로그아웃 시, 200 정상 응답")

@DisplayName("비정상 토큰으로 로그아웃 시, 400 응답")
@Test
void invalid_logout() throws Exception {
UserLoginRequest userLoginRequest = new UserLoginRequest(user.getUsername(), rawPassword);
String invalidAccessToken = "gadsklgasg.fadsfklalsfjks.fadsklfsa";

//when
ResultActions logoutResult = mockMvc.perform(delete("/users/logout")
.contentType(MediaType.APPLICATION_JSON)
.header(HttpHeaders.AUTHORIZATION, invalidAccessToken)
.header(HttpHeaders.AUTHORIZATION, "Bearer " + invalidAccessToken)
.content(objectMapper.writeValueAsString(userLoginRequest))
).andExpect(status().isOk());
).andExpect(status().isBadRequest());

//then
assertThat(dbManager.getValue(KeyGenerator.generateKey(invalidAccessToken, BLACK_LIST_ACCESS_TOKEN))).isEmpty();
}
Expand All @@ -383,16 +390,66 @@ void valid_getMyInfo() throws Exception {

//when
MvcResult mvcResult = mockMvc.perform(get("/users/info")
.contentType(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
).andReturn();

CommonResponse<UserInfoResponse> commonResponse = objectMapper.readValue(mvcResult.getResponse().getContentAsString(), new TypeReference<>(){});
CommonResponse<UserInfoResponse> commonResponse = objectMapper.readValue(mvcResult.getResponse().getContentAsString(), new TypeReference<>() {
});

//then
assertThat(commonResponse.isSuccess()).isTrue();
assertThat(commonResponse.getResult().getUsername()).isEqualTo("test");
}

@DisplayName("로그인하지 않은 회원이 회원탈퇴 시 예외 반환")
@Test
void invalid_sign_out() throws Exception {
// given

// when
MvcResult mvcResult = mockMvc.perform(delete("/users/sign-out")
.contentType(MediaType.APPLICATION_JSON)
).andReturn();
CommonResponse commonResponse = objectMapper.readValue(mvcResult.getResponse().getContentAsString(), CommonResponse.class);

// then
assertThat(commonResponse.isSuccess()).isFalse();
assertThat(commonResponse.getCode()).isEqualTo(USERS_NOT_LOGIN.getCode());
assertThat(commonResponse.getResult()).isEqualTo(USERS_NOT_LOGIN.getMessage());
}

@DisplayName("탈퇴한 유저는 로그인에 실패해야 한다.")
@Test
void sign_out_user_login() throws Exception {
//given
UserLoginRequest userLoginRequest = new UserLoginRequest(user.getUsername(), rawPassword);
//로그인 요청
ResultActions loginAction = mockMvc.perform(post("/users/login")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(userLoginRequest))

);
String accessToken = (String) objectMapper.readValue(loginAction.andReturn().getResponse().getContentAsString(), CommonResponse.class).getResult();
// 회원 탈퇴
ResultActions signOutAction = mockMvc.perform(delete("/users/sign-out")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(userLoginRequest))
.header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken)
);

//when
//로그인 요청
ResultActions reLoginAction = mockMvc.perform(post("/users/login")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(userLoginRequest))

);

//then
CommonResponse commonResponse = objectMapper.readValue(reLoginAction.andReturn().getResponse().getContentAsString(), CommonResponse.class);
assertThat(commonResponse.isSuccess()).isFalse();
}

private UserCreateRequest userCreateRequest(String username, String nickname, String phone, String email) {
return UserCreateRequest.builder()
.nickname(nickname)
Expand Down

0 comments on commit 3fc82c9

Please sign in to comment.