Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file.
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.cleanengine.coin.user.info.application;

import com.cleanengine.coin.user.info.infra.AccountRepository;
import com.cleanengine.coin.user.info.infra.WalletRepository;
import com.cleanengine.coin.user.info.presentation.UserInfoDTO;
import com.cleanengine.coin.user.info.infra.UserRepository;
import org.springframework.stereotype.Service;
Expand All @@ -10,13 +8,9 @@
public class UserService {

private final UserRepository userRepository;
private final AccountRepository accountRepository;
private final WalletRepository walletRepository;

public UserService(UserRepository userRepository, AccountRepository accountRepository, WalletRepository walletRepository) {
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
this.accountRepository = accountRepository;
this.walletRepository = walletRepository;
}

public UserInfoDTO retrieveUserInfoByUserId(Integer userId) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package com.cleanengine.coin.user.info.presentation;

import com.cleanengine.coin.common.response.ApiResponse;
import com.cleanengine.coin.common.response.ErrorResponse;
import com.cleanengine.coin.common.response.ErrorStatus;
import com.cleanengine.coin.user.domain.Account;
import com.cleanengine.coin.user.domain.Wallet;
import com.cleanengine.coin.user.info.application.AccountService;
import com.cleanengine.coin.user.info.application.WalletService;
import com.cleanengine.coin.user.login.application.JWTUtil;
import com.cleanengine.coin.user.info.application.UserService;
import com.cleanengine.coin.user.login.infra.CustomOAuth2User;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
Expand All @@ -23,30 +23,30 @@ public class UserController {
private final UserService userService;
private final AccountService accountService;
private final WalletService walletService;
private final JWTUtil jwtUtil;

public UserController(UserService userService, AccountService accountService, WalletService walletService, JWTUtil jwtUtil) {
public UserController(UserService userService, AccountService accountService, WalletService walletService) {
this.userService = userService;
this.accountService = accountService;
this.walletService = walletService;
this.jwtUtil = jwtUtil;
}

@GetMapping("/api/userinfo")
public ApiResponse<UserInfoDTO> retrieveUserInfo(HttpServletRequest request) {
public ApiResponse<UserInfoDTO> retrieveUserInfo() {

Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication.getPrincipal() instanceof CustomOAuth2User oAuth2User) {
Integer userId = oAuth2User.getUserId();
UserInfoDTO userInfoDTO = userService.retrieveUserInfoByUserId(userId);
if (userInfoDTO == null) {
return ApiResponse.fail(ErrorResponse.of(ErrorStatus.UNAUTHORIZED_RESOURCE));
}
Account account = accountService.retrieveAccountByUserId(userId);
List<Wallet> wallets = walletService.retrieveWalletsByAccountId(account.getId());
userInfoDTO.setWallets(wallets);

return ApiResponse.success(userInfoDTO, HttpStatus.OK);
}

throw new IllegalStateException("인증된 사용자를 찾을 수 없습니다.");
return ApiResponse.fail(ErrorResponse.of(ErrorStatus.UNAUTHORIZED_RESOURCE));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class UserInfoDTO {

private Integer userId;
Expand All @@ -26,4 +25,17 @@ public class UserInfoDTO {

private List<Wallet> wallets;

private UserInfoDTO(Integer userId, String email, String nickname, String provider, Double cash, List<Wallet> wallets) {
this.userId = userId;
this.email = email;
this.nickname = nickname;
this.provider = provider;
this.cash = cash;
this.wallets = wallets;
}

public static UserInfoDTO of(Integer userId, String email, String nickname, String provider, Double cash, List<Wallet> wallets) {
return new UserInfoDTO(userId, email, nickname, provider, cash, wallets);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,9 @@ public CustomOAuth2UserService(UserRepository userRepository, OAuthRepository oA
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
OAuth2User oAuth2User = super.loadUser(userRequest);

String registrationId = userRequest.getClientRegistration().getRegistrationId();

OAuth2Response oAuth2Response = new KakaoResponse(oAuth2User.getAttributes());
/* 추후 OAuth 플랫폼 추가 시 이런 식으로 Response 분기처리
if (registrationId.equals("kakao")) {
if (userRequest.getClientRegistration().getRegistrationId().equals("kakao")) {
oAuth2Response = new KakaoResponse(oAuth2User.getAttributes());
}
else {
Expand Down Expand Up @@ -66,9 +64,7 @@ public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2Authentic
oAuthRepository.save(newOAuth);
accountService.createNewAccount(newUser.getId(), CommonValues.INITIAL_USER_CASH);

UserOAuthDetails userOAuthDetails = new UserOAuthDetails(newUser, newOAuth);

return new CustomOAuth2User(userOAuthDetails);
return new CustomOAuth2User(UserOAuthDetails.of(newUser, newOAuth));
}
else {
OAuth existOAuth = oAuthRepository.findByProviderAndProviderUserId(provider, providerUserId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,7 @@ protected void doFilterInternal(HttpServletRequest request, @NotNull HttpServlet
}

private static Authentication getAuthentication(Integer userId) {
UserOAuthDetails userOAuthDetails = new UserOAuthDetails();
userOAuthDetails.setUserId(userId);
UserOAuthDetails userOAuthDetails = UserOAuthDetails.of(userId);
CustomOAuth2User customOAuth2User = new CustomOAuth2User(userOAuthDetails);

// 스프링 시큐리티 인증 토큰 생성
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,10 @@

import com.cleanengine.coin.user.domain.OAuth;
import com.cleanengine.coin.user.domain.User;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.*;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class UserOAuthDetails {

private Integer userId;
Expand All @@ -23,11 +18,29 @@ public class UserOAuthDetails {

private String name;

public UserOAuthDetails(User user, OAuth oAuth) {
this.userId = user.getId();
this.provider = oAuth.getProvider();
this.providerUserId = oAuth.getProviderUserId();
this.email = oAuth.getEmail();
this.name = oAuth.getNickname();
@Builder
private UserOAuthDetails(Integer userId, String provider, String providerUserId, String email, String name) {
this.userId = userId;
this.provider = provider;
this.providerUserId = providerUserId;
this.email = email;
this.name = name;
}

public static UserOAuthDetails of(User user, OAuth oAuth) {
return UserOAuthDetails.builder()
.userId(user.getId())
.provider(oAuth.getProvider())
.providerUserId(oAuth.getProviderUserId())
.email(oAuth.getEmail())
.name(oAuth.getNickname())
.build();
}

public static UserOAuthDetails of(int userId) {
return UserOAuthDetails.builder()
.userId(userId)
.build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,7 @@ public class WithCustomMockUserSecurityContextFactory implements WithSecurityCon
public SecurityContext createSecurityContext(WithCustomMockUser annotation) {
SecurityContext context = SecurityContextHolder.createEmptyContext();

UserOAuthDetails userOAuthDetails = new UserOAuthDetails();
userOAuthDetails.setUserId(annotation.id());
userOAuthDetails.setName(annotation.name());

UserOAuthDetails userOAuthDetails = UserOAuthDetails.of(annotation.id());
CustomOAuth2User customOAuth2User = new CustomOAuth2User(userOAuthDetails);

Authentication authentication = new UsernamePasswordAuthenticationToken(customOAuth2User,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.*;

@DisplayName("계좌 단위테스트")
class AccountTest {

@DisplayName("계좌의 예수금을 5000만큼 증가시킨다.")
Expand Down Expand Up @@ -40,10 +41,7 @@ void decreaseCash() {
Account account = Account.of(1, 6000.0);

// when, then
account.decreaseCash(5000.0);

// then
assertEquals(1000.0, account.getCash());
assertEquals(1000.0, account.decreaseCash(5000.0).getCash());
}

@DisplayName("계좌의 예수금을 0만큼 감소시키면 예외가 발생한다.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import static org.assertj.core.api.Assertions.assertThat;

@ActiveProfiles({"dev", "it", "h2-mem"})
@DisplayName("계좌 서비스 - h2 통합테스트")
@SpringBootTest
class AccountServiceTest {

Expand All @@ -19,19 +20,31 @@ class AccountServiceTest {

@DisplayName("유저 ID와 예수금으로 신규 계좌를 생성한다.")
@Test
void test() {
void createNewAccount() {
// given
int userId = 3;
double cash = CommonValues.INITIAL_USER_CASH;

// when
accountService.createNewAccount(userId, cash);
Account account = accountService.retrieveAccountByUserId(userId);
Account account = accountService.createNewAccount(userId, cash);
assertThat(account).isNotNull();

Account retrievedAccount = accountService.retrieveAccountByUserId(userId);

// then
assertThat(account).isNotNull()
assertThat(retrievedAccount).isNotNull()
.extracting(Account::getUserId, Account::getCash)
.containsExactly(userId, cash);
}

@DisplayName("존재하지 않는 userId로 조회 시 null을 반환한다.")
@Test
void retrieveAccountByInvalidUserId() {
// given, when
Account account = accountService.retrieveAccountByUserId(1000);

// then
assertThat(account).isNull();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package com.cleanengine.coin.user.info.presentation;

import com.cleanengine.coin.configuration.SecurityEndpoints;
import com.cleanengine.coin.user.domain.Account;
import com.cleanengine.coin.user.info.application.AccountService;
import com.cleanengine.coin.user.info.application.UserService;
import com.cleanengine.coin.user.info.application.WalletService;
import com.cleanengine.coin.user.login.application.CustomOAuth2UserService;
import com.cleanengine.coin.user.login.application.CustomSuccessHandler;
import com.cleanengine.coin.user.login.application.JWTUtil;
import com.cleanengine.coin.user.login.infra.CustomOAuth2User;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.stubbing.Answer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

import java.util.Collection;
import java.util.List;

import static org.hamcrest.Matchers.is;
import static org.mockito.Mockito.*;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;

@WebMvcTest(UserController.class)
public class UserControllerTest {

@Autowired
private MockMvc mockMvc;

@MockitoBean
private UserService userService;

@MockitoBean
private AccountService accountService;

@MockitoBean
private WalletService walletService;

@MockitoBean
private JWTUtil jwtUtil;

@MockitoBean
private CustomOAuth2UserService customOAuth2UserService;

@MockitoBean
private CustomSuccessHandler customSuccessHandler;

@MockitoBean
private SecurityEndpoints.EndpointConfig endpointConfig;

@Mock
private CustomOAuth2User customOAuth2User;

@AfterEach
void tearDown() {
SecurityContextHolder.clearContext();
}

@Test
@DisplayName("정상적으로 존재하는 사용자 정보를 통해 조회에 성공한다.")
public void testRetrieveUserInfoSuccess() throws Exception {
int userId = 1;
String email = "test@test.com";
String nickname = "test";
String provider = "kakao";
double cash = 1000.0;

when(customOAuth2User.getUserId()).thenReturn(userId);
when(customOAuth2User.getAttributes()).thenReturn(null);
Collection<GrantedAuthority> authorities = List.of(new SimpleGrantedAuthority("ROLE_USER"));
when(customOAuth2User.getAuthorities()).thenAnswer((Answer<Collection<? extends GrantedAuthority>>) invocation -> authorities)
;

Authentication authenticationToken = new UsernamePasswordAuthenticationToken(
customOAuth2User, null, authorities
);

UserInfoDTO userInfoDTO = UserInfoDTO.of(userId, email, nickname, provider, cash, null);
when(userService.retrieveUserInfoByUserId(userId)).thenReturn(userInfoDTO);

Account account = Account.of(userId, cash);
when(accountService.retrieveAccountByUserId(userId)).thenReturn(account);

mockMvc.perform(get("/api/userinfo")
.with(authentication(authenticationToken)))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.isSuccess", is(true)))
.andExpect(MockMvcResultMatchers.jsonPath("$.data.cash", is((int) cash)));

verify(userService, times(1)).retrieveUserInfoByUserId(userId);
verify(accountService, times(1)).retrieveAccountByUserId(userId);
verify(walletService, times(1)).retrieveWalletsByAccountId(account.getId());
}

@Test
@DisplayName("인증되지 않은 사용자가 private api 접근 시 리디렉션 응답을 반환한다.")
public void testRetrieveUserInfoUnauthorized() throws Exception {
mockMvc.perform(get("/api/userinfo"))
.andExpect(MockMvcResultMatchers.status().is3xxRedirection());
verifyNoInteractions(userService, accountService, walletService);
}

}
Loading