diff --git a/backend/build.gradle b/backend/build.gradle index 35c13751..293a749b 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -86,7 +86,10 @@ subprojects { '**/ImageType*', // domain-cvi-oauth-service '**/KakaoProfile*', - '**/NaverProfile*' + '**/NaverProfile*', + '**/UserInformation*', + '**/AuthRequest*', + '**/OAuthConfig*' ] + Qdomains)})) } finalizedBy 'jacocoTestCoverageVerification' @@ -108,7 +111,7 @@ subprojects { // domain-cvi "**/Sort.java, **/Filter.java, **/ImageType.java, " + // domain-cvi-oauth-service - "**/KakaoProfile.java, **/NaverProfile.java" + "**/KakaoProfile.java, **/NaverProfile.java, **/UserInformation.java, **/AuthRequest.java, **/OAuthConfig.java" } } } diff --git a/backend/domain-cvi-oauth-service/build.gradle b/backend/domain-cvi-oauth-service/build.gradle index 957a874a..2489ad81 100644 --- a/backend/domain-cvi-oauth-service/build.gradle +++ b/backend/domain-cvi-oauth-service/build.gradle @@ -16,7 +16,7 @@ jacocoTestCoverageVerification { // 코드 커버리지 측정항목 시행 limit { counter = 'METHOD' value = 'COVEREDRATIO' - minimum = 0.85 + minimum = 1.00 } } } diff --git a/backend/domain-cvi-oauth-service/src/main/java/com/cvi/config/OAuthConfig.java b/backend/domain-cvi-oauth-service/src/main/java/com/cvi/config/OAuthConfig.java new file mode 100644 index 00000000..ae11a66a --- /dev/null +++ b/backend/domain-cvi-oauth-service/src/main/java/com/cvi/config/OAuthConfig.java @@ -0,0 +1,14 @@ +package com.cvi.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class OAuthConfig { + + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } +} diff --git a/backend/domain-cvi-oauth-service/src/main/java/com/cvi/parser/KakaoAuthorization.java b/backend/domain-cvi-oauth-service/src/main/java/com/cvi/parser/KakaoAuthorization.java index e238a2b5..ecee0df4 100644 --- a/backend/domain-cvi-oauth-service/src/main/java/com/cvi/parser/KakaoAuthorization.java +++ b/backend/domain-cvi-oauth-service/src/main/java/com/cvi/parser/KakaoAuthorization.java @@ -8,6 +8,7 @@ import com.cvi.exception.MappingFailureException; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; @@ -20,14 +21,15 @@ @Slf4j @Component +@RequiredArgsConstructor public class KakaoAuthorization implements Authorization { private static final String PROFILE_REQUEST_URL = "https://kapi.kakao.com/v2/user/me"; private static final String TOKEN_REQUEST_URL = "https://kauth.kakao.com/oauth/token"; private static final String CALLBACK_URL_SUFFIX = "/auth/kakao/callback"; - private final RestTemplate restTemplate = new RestTemplate(); - private final ObjectMapper objectMapper = new ObjectMapper(); + private final RestTemplate restTemplate; + private final ObjectMapper objectMapper; @Override public UserInformation requestProfile(String code, String state, String requestOrigin) { @@ -93,10 +95,10 @@ public HttpEntity> createProfileRequest(OAuthToken @Override public ResponseEntity sendRequest(HttpEntity> request, String url) { return restTemplate.exchange( - url, - HttpMethod.POST, - request, - String.class + url, + HttpMethod.POST, + request, + String.class ); } } diff --git a/backend/domain-cvi-oauth-service/src/main/java/com/cvi/parser/NaverAuthorization.java b/backend/domain-cvi-oauth-service/src/main/java/com/cvi/parser/NaverAuthorization.java index bd30fb70..0bf5c409 100644 --- a/backend/domain-cvi-oauth-service/src/main/java/com/cvi/parser/NaverAuthorization.java +++ b/backend/domain-cvi-oauth-service/src/main/java/com/cvi/parser/NaverAuthorization.java @@ -8,6 +8,7 @@ import com.cvi.exception.MappingFailureException; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpEntity; @@ -21,13 +22,14 @@ @Component @Slf4j -public class NaverAuthorization implements Authorization { +@RequiredArgsConstructor +public class NaverAuthorization implements Authorization { private static final String PROFILE_REQUEST_URL = "https://openapi.naver.com/v1/nid/me"; private static final String TOKEN_REQUEST_URL = "https://nid.naver.com/oauth2.0/token"; - private final RestTemplate restTemplate = new RestTemplate(); - private final ObjectMapper objectMapper = new ObjectMapper(); + private final RestTemplate restTemplate; + private final ObjectMapper objectMapper; @Value("${security.auth.naver.client-secret}") private String clientSecret; @@ -97,10 +99,10 @@ public HttpEntity> createProfileRequest(OAuthToken @Override public ResponseEntity sendRequest(HttpEntity> request, String url) { return restTemplate.exchange( - url, - HttpMethod.POST, - request, - String.class + url, + HttpMethod.POST, + request, + String.class ); } } diff --git a/backend/domain-cvi-oauth-service/src/test/java/com/cvi/parser/AuthorizationManagerTest.java b/backend/domain-cvi-oauth-service/src/test/java/com/cvi/parser/AuthorizationManagerTest.java index 8abb45f6..c79f277d 100644 --- a/backend/domain-cvi-oauth-service/src/test/java/com/cvi/parser/AuthorizationManagerTest.java +++ b/backend/domain-cvi-oauth-service/src/test/java/com/cvi/parser/AuthorizationManagerTest.java @@ -2,6 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.willReturn; import static org.mockito.Mockito.spy; @@ -17,6 +18,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.springframework.web.client.RestTemplate; @DisplayName("Authorization 매니저 도메인 테스트") class AuthorizationManagerTest { @@ -29,13 +31,14 @@ class AuthorizationManagerTest { private Map authorizationMap = new HashMap<>(); private AuthorizationManager authorizationManager = new AuthorizationManager(authorizationMap); + private final ObjectMapper objectMapper = new ObjectMapper(); + private final RestTemplate restTemplate = new RestTemplate(); + + private final Authorization naverAuthorization = spy(new NaverAuthorization(restTemplate, objectMapper)); + private final Authorization kakaoAuthorization = spy(new KakaoAuthorization(restTemplate, objectMapper)); - private NaverAuthorization naverAuthorization = spy(new NaverAuthorization()); - private KakaoAuthorization kakaoAuthorization = spy(new KakaoAuthorization()); - private ObjectMapper objectMapper = new ObjectMapper(); private UserInformation naverUserInfo; - private UserInformation kakaoUserInfo; @BeforeEach void setUp() throws JsonProcessingException { @@ -44,9 +47,6 @@ void setUp() throws JsonProcessingException { NaverProfile naverProfile = objectMapper.readValue(NAVER_PROFILE_RESPONSE, NaverProfile.class); naverUserInfo = UserInformation.of(naverProfile); - - KakaoProfile kakaoProfile = objectMapper.readValue(KAKAO_PROFILE_RESPONSE, KakaoProfile.class); - kakaoUserInfo = UserInformation.of(kakaoProfile); } @DisplayName("Naver Authorization 매니저 유저 정보 요청 - 성공") @@ -60,25 +60,28 @@ void requestNaverUserInfo() { assertThat(expected).isEqualTo(naverUserInfo); } - @DisplayName("Kakao Authorization 매니저 유저 정보 요청 - 성공") + @DisplayName("Authorization 매니저 유저 정보 요청 - 실패 - Provider가 Null인 경우") @Test - void requestKakaoUserInfo() { + void requestUserInfoFailureWhenNullSocialProvider() { //given - willReturn(kakaoUserInfo).given(kakaoAuthorization).requestProfile(SOCIAL_CODE, null, REQUEST_ORIGIN); //when - UserInformation expected = authorizationManager.requestUserInfo(SocialProvider.KAKAO, SOCIAL_CODE, null, REQUEST_ORIGIN); //then - assertThat(expected).isEqualTo(kakaoUserInfo); + assertThatThrownBy(() -> authorizationManager.requestUserInfo(null, SOCIAL_CODE, STATE, REQUEST_ORIGIN)) + .isExactlyInstanceOf(InvalidOperationException.class) + .hasMessage("해당 OAuth 제공자가 존재하지 않습니다 입력값: null"); } - @DisplayName("Authorization 매니저 유저 정보 요청 - 실패 - Provider가 유효하지 않은 경우 - Naver") + @DisplayName("Authorization 매니저 유저 정보 요청 - 실패 - Provider가 유효하지 않은 경우") @Test - void requestUserInfoFailureWhenInvalidSocialProviderNaver() { + void requestUserInfoFailureWhenInvalidSocialProvider() { //given //when + authorizationMap = spy(HashMap.class); + authorizationManager = new AuthorizationManager(authorizationMap); + willReturn(false).given(authorizationMap).containsKey(any()); //then - assertThatThrownBy(() -> authorizationManager.requestUserInfo(null, SOCIAL_CODE, STATE, REQUEST_ORIGIN)) - .isExactlyInstanceOf(InvalidOperationException.class) - .hasMessage("해당 OAuth 제공자가 존재하지 않습니다 입력값: null"); + assertThatThrownBy(() -> authorizationManager.requestUserInfo(SocialProvider.KAKAO, SOCIAL_CODE, STATE, REQUEST_ORIGIN)) + .isExactlyInstanceOf(InvalidOperationException.class) + .hasMessage("해당 OAuth 제공자가 존재하지 않습니다 입력값: kakaoAuthorization"); } } diff --git a/backend/domain-cvi-oauth-service/src/test/java/com/cvi/parser/KakaoAuthorizationTest.java b/backend/domain-cvi-oauth-service/src/test/java/com/cvi/parser/KakaoAuthorizationTest.java index 6c35a2aa..c345d202 100644 --- a/backend/domain-cvi-oauth-service/src/test/java/com/cvi/parser/KakaoAuthorizationTest.java +++ b/backend/domain-cvi-oauth-service/src/test/java/com/cvi/parser/KakaoAuthorizationTest.java @@ -1,26 +1,28 @@ package com.cvi.parser; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.BDDMockito.willReturn; -import static org.mockito.Mockito.spy; - import com.cvi.dto.oauthtoken.KakaoOAuthToken; import com.cvi.dto.oauthtoken.OAuthToken; import com.cvi.dto.profile.SocialProfile; import com.cvi.dto.profile.UserInformation; import com.cvi.exception.MappingFailureException; +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.http.HttpEntity; +import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.BDDMockito.willReturn; +import static org.mockito.Mockito.spy; @DisplayName("Kakao Authorization 도메인 테스트") class KakaoAuthorizationTest { - private static final String TOKEN_RESPONSE = "{\"token_type\":\"bearer\",\"access_token\":\"{ACCESS_TOKEN received from Social Provider}\",\"expires_in\":\"43199\",\"refresh_token\":\"{REFRESH_TOKEN received from Social Provider}\",\"refresh_token_expires_in\":\"25184000\",\"scope\":\"account_email profile\"}"; private static final String PROFILE_RESPONSE = "{\"id\":1816688137,\"connected_at\":\"2021-07-22T05:43:16Z\",\"properties\":{\"nickname\":\"김영빈\"},\"kakao_account\":{\"profile_nickname_needs_agreement\":false,\"profile_image_needs_agreement\":false,\"profile\":{\"nickname\":\"김영빈\",\"thumbnail_image_url\":\"http://k.kakaocdn.net/dn/dpk9l1/btqmGhA2lKL/Oz0wDuJn1YV2DIn92f6DVK/img_110x110.jpg\",\"profile_image_url\":\"http://k.kakaocdn.net/dn/dpk9l1/btqmGhA2lKL/Oz0wDuJn1YV2DIn92f6DVK/img_640x640.jpg\",\"is_default_image\":true}}}"; private static final String TOKEN_REQUEST_URL = "https://kauth.kakao.com/oauth/token"; @@ -35,11 +37,12 @@ class KakaoAuthorizationTest { private static final String SCOPE = "account_email profile"; private static final String REQUEST_ORIGIN = "http://localhost:9000"; - private KakaoAuthorization kakaoAuthorization = spy(new KakaoAuthorization()); + private final RestTemplate restTemplate = spy(RestTemplate.class); + private final ObjectMapper objectMapper = new ObjectMapper(); + private final Authorization kakaoAuthorization = new KakaoAuthorization(restTemplate, objectMapper); private HttpEntity> kakaoTokenRequest; private ResponseEntity tokenResponse; private ResponseEntity profileResponse; - private HttpEntity> kakaoProfileRequest; @BeforeEach void beforeEach() { @@ -47,10 +50,10 @@ void beforeEach() { tokenResponse = ResponseEntity.ok(TOKEN_RESPONSE); profileResponse = ResponseEntity.ok(PROFILE_RESPONSE); - kakaoProfileRequest = kakaoAuthorization.createProfileRequest(kakaoAuthorization.mapToOAuthToken(tokenResponse)); + HttpEntity> kakaoProfileRequest = kakaoAuthorization.createProfileRequest(kakaoAuthorization.mapToOAuthToken(tokenResponse)); - willReturn(tokenResponse).given(kakaoAuthorization).sendRequest(kakaoTokenRequest, TOKEN_REQUEST_URL); - willReturn(profileResponse).given(kakaoAuthorization).sendRequest(kakaoProfileRequest, PROFILE_REQUEST_URL); + willReturn(tokenResponse).given(restTemplate).exchange(TOKEN_REQUEST_URL, HttpMethod.POST, kakaoTokenRequest, String.class); + willReturn(profileResponse).given(restTemplate).exchange(PROFILE_REQUEST_URL, HttpMethod.POST, kakaoProfileRequest, String.class); } @DisplayName("카카오 프로필 요청 테스트 - 성공") @@ -84,11 +87,11 @@ void requestToken() { void requestTokenFailure() { //given HttpEntity> invalidToken = kakaoAuthorization.createTokenRequest("INVALID_TOKEN", null, REQUEST_ORIGIN); - willReturn(new ResponseEntity<>("{\"ERROR\":\"ERROR\"}", HttpStatus.BAD_REQUEST)).given(kakaoAuthorization).sendRequest(invalidToken, TOKEN_REQUEST_URL); + willReturn(new ResponseEntity<>("{\"ERROR\":\"ERROR\"}", HttpStatus.BAD_REQUEST)).given(restTemplate).exchange(TOKEN_REQUEST_URL, HttpMethod.POST, invalidToken, String.class); //when //then assertThatThrownBy(() -> kakaoAuthorization.requestToken("INVALID_TOKEN", null, REQUEST_ORIGIN)) - .isExactlyInstanceOf(MappingFailureException.class); + .isExactlyInstanceOf(MappingFailureException.class); } @DisplayName("토큰 매핑 테스트 - 성공") @@ -113,7 +116,7 @@ void mapToOAuthTokenFailureWhenNotValidTokenResponse() { //when //then assertThatThrownBy(() -> kakaoAuthorization.mapToOAuthToken(ResponseEntity.ok("NOT_VALID_TOKEN"))) - .isExactlyInstanceOf(MappingFailureException.class); + .isExactlyInstanceOf(MappingFailureException.class); } @DisplayName("프로필 요청 테스트 - 성공") @@ -133,11 +136,11 @@ void parseProfileFailure() { //given OAuthToken oAuthToken = kakaoAuthorization.mapToOAuthToken(tokenResponse); HttpEntity> invalidProfileRequest = kakaoAuthorization.createProfileRequest(oAuthToken); - willReturn(new ResponseEntity<>("{\"ERROR\":\"ERROR\"}", HttpStatus.BAD_REQUEST)).given(kakaoAuthorization).sendRequest(invalidProfileRequest, PROFILE_REQUEST_URL); + willReturn(new ResponseEntity<>("{\"ERROR\":\"ERROR\"}", HttpStatus.BAD_REQUEST)).given(restTemplate).exchange(PROFILE_REQUEST_URL, HttpMethod.POST, invalidProfileRequest, String.class); //when //then assertThatThrownBy(() -> kakaoAuthorization.parseProfile(oAuthToken)) - .isExactlyInstanceOf(MappingFailureException.class); + .isExactlyInstanceOf(MappingFailureException.class); } @DisplayName("프로필 매핑 테스트 - 성공") @@ -158,6 +161,15 @@ void mapToProfileFailureWhenInvalidProfileResponse() { //when //then assertThatThrownBy(() -> kakaoAuthorization.mapToProfile(ResponseEntity.ok("NOT_VALID_PROFILE"))) - .isExactlyInstanceOf(MappingFailureException.class); + .isExactlyInstanceOf(MappingFailureException.class); + } + + @DisplayName("외부요청 테스트 - 성공") + @Test + void sendRequest() { + //given + //when + //then + assertThat(kakaoAuthorization.sendRequest(kakaoTokenRequest, TOKEN_REQUEST_URL)).isEqualTo(tokenResponse); } } diff --git a/backend/domain-cvi-oauth-service/src/test/java/com/cvi/parser/NaverAuthorizationTest.java b/backend/domain-cvi-oauth-service/src/test/java/com/cvi/parser/NaverAuthorizationTest.java index e9aceb90..0920f8ca 100644 --- a/backend/domain-cvi-oauth-service/src/test/java/com/cvi/parser/NaverAuthorizationTest.java +++ b/backend/domain-cvi-oauth-service/src/test/java/com/cvi/parser/NaverAuthorizationTest.java @@ -10,13 +10,16 @@ import com.cvi.dto.profile.SocialProfile; import com.cvi.dto.profile.UserInformation; import com.cvi.exception.MappingFailureException; +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.http.HttpEntity; +import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; @DisplayName("Naver Authorization 도메인 테스트") class NaverAuthorizationTest { @@ -35,11 +38,12 @@ class NaverAuthorizationTest { private static final String EXPIRE_TIME = "3600"; private static final String REQUEST_ORIGIN = "http://localhost:9000"; - private NaverAuthorization naverAuthorization = spy(new NaverAuthorization()); + private final RestTemplate restTemplate = spy(RestTemplate.class); + private final ObjectMapper objectMapper = new ObjectMapper(); + private final Authorization naverAuthorization = new NaverAuthorization(restTemplate, objectMapper); private HttpEntity> naverTokenRequest; private ResponseEntity tokenResponse; private ResponseEntity profileResponse; - private HttpEntity> naverProfileRequest; @BeforeEach void beforeEach() { @@ -47,10 +51,9 @@ void beforeEach() { tokenResponse = ResponseEntity.ok(TOKEN_RESPONSE); profileResponse = ResponseEntity.ok(PROFILE_RESPONSE); - naverProfileRequest = naverAuthorization.createProfileRequest(naverAuthorization.mapToOAuthToken(tokenResponse)); - - willReturn(tokenResponse).given(naverAuthorization).sendRequest(naverTokenRequest, TOKEN_REQUEST_URL); - willReturn(profileResponse).given(naverAuthorization).sendRequest(naverProfileRequest, PROFILE_REQUEST_URL); + HttpEntity> naverProfileRequest = naverAuthorization.createProfileRequest(naverAuthorization.mapToOAuthToken(tokenResponse)); + willReturn(tokenResponse).given(restTemplate).exchange(TOKEN_REQUEST_URL, HttpMethod.POST, naverTokenRequest, String.class); + willReturn(profileResponse).given(restTemplate).exchange(PROFILE_REQUEST_URL, HttpMethod.POST, naverProfileRequest, String.class); } @DisplayName("네이버 프로필 요청 테스트 - 성공") @@ -82,7 +85,7 @@ void requestToken() { void requestTokenFailure() { //given HttpEntity> invalidToken = naverAuthorization.createTokenRequest("INVALID_TOKEN", STATE, REQUEST_ORIGIN); - willReturn(new ResponseEntity<>("{\"ERROR\":\"ERROR\"}", HttpStatus.BAD_REQUEST)).given(naverAuthorization).sendRequest(invalidToken, TOKEN_REQUEST_URL); + willReturn(new ResponseEntity<>("{\"ERROR\":\"ERROR\"}", HttpStatus.BAD_REQUEST)).given(restTemplate).exchange(TOKEN_REQUEST_URL, HttpMethod.POST, invalidToken, String.class); //when //then assertThatThrownBy(() -> naverAuthorization.requestToken("INVALID_TOKEN", STATE, REQUEST_ORIGIN)) @@ -129,7 +132,7 @@ void parseProfileFailure() { //given OAuthToken oAuthToken = naverAuthorization.mapToOAuthToken(tokenResponse); HttpEntity> invalidProfileRequest = naverAuthorization.createProfileRequest(oAuthToken); - willReturn(new ResponseEntity<>("{\"ERROR\":\"ERROR\"}", HttpStatus.BAD_REQUEST)).given(naverAuthorization).sendRequest(invalidProfileRequest, PROFILE_REQUEST_URL); + willReturn(new ResponseEntity<>("{\"ERROR\":\"ERROR\"}", HttpStatus.BAD_REQUEST)).given(restTemplate).exchange(PROFILE_REQUEST_URL, HttpMethod.POST, invalidProfileRequest, String.class); //when //then assertThatThrownBy(() -> naverAuthorization.parseProfile(oAuthToken)) @@ -156,4 +159,13 @@ void mapToProfileFailureWhenInvalidProfileResponse() { assertThatThrownBy(() -> naverAuthorization.mapToProfile(ResponseEntity.ok("NOT_VALID_PROFILE"))) .isExactlyInstanceOf(MappingFailureException.class); } + + @DisplayName("외부요청 테스트 - 성공") + @Test + void sendRequest() { + //given + //when + //then + assertThat(naverAuthorization.sendRequest(naverTokenRequest, TOKEN_REQUEST_URL)).isEqualTo(tokenResponse); + } }