Skip to content

Commit

Permalink
Test(#222) : oauth controller (#225)
Browse files Browse the repository at this point in the history
* Test : oauth controller

oauth 링크 반환 여부
토큰 발급 여부

* Fix : 유저 이미지 생성 필드 수정

date -> datetime

* Test : 실패 테스트

oauth 타입이 존재하지 않는경우
차단된 회원일 경우
탈퇴한 회원일 경우
휴면 회원일 경우

* Fix : 휴면 회원일 때 예상되는 상태코드 수정

403-> 423

* Test : 리프레시 토큰으로 엑세스 토큰 재발급 테스트

* Test : 회원탈퇴 테스트
  • Loading branch information
Astin01 authored Oct 18, 2024
1 parent f92ac88 commit 5786bff
Show file tree
Hide file tree
Showing 6 changed files with 303 additions and 4 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ dependencies {
asciidoctorExt 'org.springframework.restdocs:spring-restdocs-asciidoctor'
testImplementation 'com.h2database:h2'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.mockito:mockito-core:3.+'
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
@JsonIgnoreProperties(ignoreUnknown = true)
public class KakaoUserResponse {

public KakaoUserResponse(Long id){
this.id = id;
}
//회원 번호
@JsonProperty("id")
public Long id;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import java.time.LocalDate;
import java.time.LocalDateTime;
import lombok.Getter;
import lombok.NoArgsConstructor;

Expand All @@ -25,9 +26,9 @@ public class UserImage {
private String address;

@Column(name = "user_image_created_date")
private LocalDate createdDate;
private LocalDateTime createdDate;

public UserImage(String address, LocalDate createdDate) {
public UserImage(String address, LocalDateTime createdDate) {
this.address = address;
this.createdDate = createdDate;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package solitour_backend.solitour.user_image.service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -21,7 +22,7 @@ public class UserImageService {

@Transactional
public UserImage saveUserImage(String imageUrl) {
UserImage userImage = new UserImage(imageUrl, LocalDate.now());
UserImage userImage = new UserImage(imageUrl, LocalDateTime.now());

userImageRepository.save(userImage);

Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ CREATE TABLE `user_image`
(
`user_image_id` BIGINT NOT NULL AUTO_INCREMENT,
`user_image_address` VARCHAR(200) NOT NULL,
`user_image_created_date` DATE NOT NULL,
`user_image_created_date` DATETIME NOT NULL,
CONSTRAINT PK_user_image PRIMARY KEY (`user_image_id`)
);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,293 @@
package solitour_backend.solitour.oauth.controller;

import static org.hamcrest.Matchers.notNullValue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.modifyUris;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import static org.mockito.Mockito.*;
import jakarta.servlet.http.Cookie;
import jakarta.transaction.Transactional;
import java.util.Optional;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.BDDMockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.HttpStatus;
import org.springframework.mock.web.MockCookie;
import org.springframework.restdocs.RestDocumentationContextProvider;
import org.springframework.restdocs.RestDocumentationExtension;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import solitour_backend.solitour.auth.entity.Token;
import solitour_backend.solitour.auth.entity.TokenRepository;
import solitour_backend.solitour.auth.exception.UnsupportedLoginTypeException;
import solitour_backend.solitour.auth.service.OauthService;
import solitour_backend.solitour.auth.service.TokenService;
import solitour_backend.solitour.auth.service.dto.response.AccessTokenResponse;
import solitour_backend.solitour.auth.service.dto.response.LoginResponse;
import solitour_backend.solitour.auth.service.dto.response.OauthLinkResponse;
import solitour_backend.solitour.auth.support.JwtTokenProvider;
import solitour_backend.solitour.auth.support.kakao.KakaoConnector;
import solitour_backend.solitour.user.entity.User;
import solitour_backend.solitour.user.exception.BlockedUserException;
import solitour_backend.solitour.user.exception.DeletedUserException;
import solitour_backend.solitour.user.exception.DormantUserException;
import solitour_backend.solitour.user.user_status.UserStatus;

@ActiveProfiles("test")
@AutoConfigureMockMvc
@ExtendWith({MockitoExtension.class, RestDocumentationExtension.class})
@AutoConfigureRestDocs
@Transactional
@SpringBootTest
class OauthControllerTest {

@Autowired
private MockMvc mockMvc;

@MockBean
private OauthService oauthService;

@MockBean
private JwtTokenProvider jwtTokenProvider;

@MockBean
private TokenRepository tokenRepository;

@MockBean
private KakaoConnector kakaoConnector;

private final String kakaoRedirectUrl = "http://localhost:8080/oauth2/callback/kakao";

private final String kakaoOauthLink = "https://kauth.kakao.com/oauth/authorize?response_type=code&redirect_uri=http://localhost:8080/oauth2/callback/kakao&client_id=cleintId";

@MockBean
private TokenService tokenService;


@BeforeEach
void setUp(WebApplicationContext webApplicationContext,
RestDocumentationContextProvider restDocumentation) {

this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.apply(documentationConfiguration(restDocumentation)
.operationPreprocessors()
.withRequestDefaults(modifyUris(), prettyPrint())
.withResponseDefaults(prettyPrint()))
.build();
}

@DisplayName("정상적으로 Oauth 인증 링크가 반환되는지 테스트한다")
@Test
void access() throws Exception {
BDDMockito.given(oauthService.generateAuthUrl(eq("kakao"), anyString()))
.willReturn(new OauthLinkResponse(kakaoOauthLink));

mockMvc.perform(get("/api/auth/oauth2/login")
.queryParam("type", "kakao")
.queryParam("redirectUrl", kakaoRedirectUrl)
)
.andExpectAll(
status().isOk(),
jsonPath("oauthLink", notNullValue()))
.andDo(
document("oauthLink",
preprocessRequest(prettyPrint()),
preprocessResponse(prettyPrint())
)
);
}

@DisplayName("정상적으로 로그인될 시 토큰이 발급된다")
@Test
void login() throws Exception {
Cookie accessCookie = new Cookie("access_token", "eyJhbGciOiJIUzI1NiJ9");
Cookie refreshCookie = new Cookie("refresh_token", "l9Fp78G5l6RWbG9SMvOVnb0pnrEkWPHMPBmQw8c");

BDDMockito.given(oauthService.requestAccessToken(anyString(), anyString(), anyString()))
.willReturn(new LoginResponse(accessCookie, refreshCookie, UserStatus.PENDING));

String code = "code";
mockMvc.perform(get("/api/auth/oauth2/login")
.queryParam("code", code)
.queryParam("type", "kakao")
.queryParam("redirectUrl", kakaoRedirectUrl))
.andExpectAll(
status().isOk(),
header().exists("Set-Cookie"),
jsonPath("$").value("PENDING"))
.andDo(
document("oauthLogin",
preprocessRequest(prettyPrint()),
preprocessResponse(prettyPrint())
)
);
}

@DisplayName("재발급")
@Test
void reissueRefreshToken() throws Exception {
Cookie accessCookie = new MockCookie("access_token", "accessToken");
Cookie refreshCookie = new MockCookie("refresh_token", "refreshToken");
passRefreshLogin(accessCookie);

mockMvc.perform(post("/api/auth/oauth2/token/refresh")
.cookie(accessCookie)
.cookie(refreshCookie)
)
.andExpectAll(
status().isOk(),
header().exists("Set-Cookie")
)
.andDo(
document("oauthReissue",
preprocessRequest(prettyPrint()),
preprocessResponse(prettyPrint())
)
);
}

@DisplayName("회원탈퇴")
@Test
void deleteUser() throws Exception {
Cookie accessCookie = new MockCookie("access_token", "accessToken");
Cookie refreshCookie = new MockCookie("refresh_token", "refreshToken");
passLogin();
BDDMockito.given(kakaoConnector.refreshToken(any()))
.willReturn("refreshToken");

doNothing().when(oauthService).revokeToken("kakao", "refreshToken");
doNothing().when(oauthService).logout(any(), any());
doNothing().when(oauthService).deleteUser(1L);

mockMvc.perform(delete("/api/auth/oauth2")
.queryParam("type", "kakao")
.cookie(accessCookie)
.cookie(refreshCookie)
)
.andExpectAll(
status().isNoContent()
)
.andDo(
document("oauthDelete",
preprocessRequest(prettyPrint()),
preprocessResponse(prettyPrint())
)
);
}


@DisplayName("Oauth 타입이 존재하지 않는경우 404코드가 반환된다")
@Test
void unsupportedLoginType() throws Exception {
BDDMockito.given(oauthService.generateAuthUrl(argThat(provider ->
!provider.equals("google") && !provider.equals("kakao") && !provider.equals("naver")), anyString()))
.willThrow(new UnsupportedLoginTypeException("지원하지 않는 oauth 로그인입니다."));

mockMvc.perform(get("/api/auth/oauth2/login")
.queryParam("type", "facebook")
.queryParam("redirectUrl", "redirectUrl")
)
.andExpectAll(
status().isBadRequest());
}

@DisplayName("차단된 회원일 경우 403코드가 반환된다")
@Test
void blockedUser() throws Exception {
BDDMockito.given(oauthService.requestAccessToken("kakao", "code", "redirectUrl"))
.willThrow(new BlockedUserException("차단된 회원입니다."));

mockMvc.perform(get("/api/auth/oauth2/login")
.queryParam("code", "code")
.queryParam("type", "kakao")
.queryParam("redirectUrl", "redirectUrl")
)
.andExpectAll(
status().isForbidden());
}

@DisplayName("탈퇴한 회원일 경우 403코드가 반환된다")
@Test
void dormantUser() throws Exception {
BDDMockito.given(oauthService.requestAccessToken("kakao", "code", "redirectUrl"))
.willThrow(new DeletedUserException("탈퇴된 회원입니다."));

mockMvc.perform(get("/api/auth/oauth2/login")
.queryParam("code", "code")
.queryParam("type", "kakao")
.queryParam("redirectUrl", "redirectUrl")
)
.andExpectAll(
status().isForbidden());
}

@DisplayName("휴면 회원일 경우 423코드가 반환된다")
@Test
void lockedUser() throws Exception {
BDDMockito.given(oauthService.requestAccessToken("kakao", "code", "redirectUrl"))
.willThrow(new DormantUserException("휴면 회원입니다."));

mockMvc.perform(get("/api/auth/oauth2/login")
.queryParam("code", "code")
.queryParam("type", "kakao")
.queryParam("redirectUrl", "redirectUrl")
)
.andExpectAll(
status().isLocked());
}

private void passRefreshLogin(Cookie accessCookie) {
User user = User.builder()
.oauthId("oauthId")
.build();
Token token = new Token(user, "refreshToken");

BDDMockito.given(oauthService.reissueAccessToken(1L))
.willReturn(new AccessTokenResponse(accessCookie));
BDDMockito.given(jwtTokenProvider.validateTokenNotUsable(any()))
.willReturn(false);
BDDMockito.given(jwtTokenProvider.getPayload(any()))
.willReturn(1L);
BDDMockito.given(tokenRepository.findByUserId(1L))
.willReturn(Optional.of(token));
}

private void passLogin() {
User user = User.builder()
.oauthId("oauthId")
.build();
Token token = new Token(user, "accessToken");

BDDMockito.given(jwtTokenProvider.validateTokenNotUsable(any()))
.willReturn(false);
BDDMockito.given(jwtTokenProvider.getPayload(any()))
.willReturn(1L);
BDDMockito.given(tokenRepository.findByUserId(1L))
.willReturn(Optional.of(token));
}

}

0 comments on commit 5786bff

Please sign in to comment.