Skip to content

Commit

Permalink
test: #55, #11 동시성 처리 테스트 코드 추가
Browse files Browse the repository at this point in the history
  • Loading branch information
KarmaPol committed Feb 25, 2024
1 parent f2501a0 commit 259caa8
Show file tree
Hide file tree
Showing 2 changed files with 198 additions and 135 deletions.
272 changes: 137 additions & 135 deletions api/src/main/java/com/mm/api/domain/auth/service/AuthService.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
package com.mm.api.domain.auth.service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.mm.api.domain.auth.dto.request.LoginKakaoRequest;
import com.mm.api.domain.auth.dto.request.RefreshTokenRequest;
import com.mm.api.domain.auth.dto.response.TokenResponse;
Expand All @@ -19,145 +28,138 @@
import com.mm.coreinfraredis.repository.RedisRefreshTokenRepository;
import com.mm.coresecurity.jwt.JwtTokenProvider;
import com.mm.coresecurity.oauth.OAuth2UserDetails;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;

@Service
@Transactional
@RequiredArgsConstructor
public class AuthService {
@Value("${kakao.client-id}")
private String KAKAO_CLIENT_ID;
@Value("${kakao.client-secret}")
private String KAKAO_CLIENT_SECRET;

private final KakaoAuthLoginClient kakaoAuthLoginClient;
private final KakaoAuthInfoClient kakaoAuthInfoClient;

private final RedisRefreshTokenRepository redisRefreshTokenRepository;
private final JwtTokenProvider jwtTokenProvider;
private final MemberRepository memberRepository;
private final GroupRepository groupRepository;
private final MemberService memberService;

public TokenResponse refreshAccessToken(RefreshTokenRequest request) {
Long memberId = redisRefreshTokenRepository.findByRefreshToken(request.refreshToken())
.orElseThrow(() -> new CustomException(ErrorCode.REFRESH_TOKEN_EXPIRED));

Member member = memberRepository.findById(memberId)
.orElseThrow(() -> new CustomException(ErrorCode.MEMBER_NOT_FOUND));

OAuth2UserDetails oauth2UserDetails = createOauth2UserDetails(member);

String accessToken = jwtTokenProvider.generateAccessToken(oauth2UserDetails);
String refreshToken = jwtTokenProvider.generateRefreshToken();

redisRefreshTokenRepository.save(refreshToken, memberId);

return new TokenResponse(accessToken, refreshToken);
}

public TokenResponse loginKakao(LoginKakaoRequest request) {
KakaoAuthLoginResponse kakaoLoginAccessToken = getKakaoLoginAccessToken(request);
KakaoAuthInfoResponse kakaoAuthInfoResponse = getKakaoAuthInfoResponse(kakaoLoginAccessToken);
return kakaoLoginSuccessHandler(kakaoAuthInfoResponse.kakaoAccount().getEmail());
}

public void logout(RefreshTokenRequest request) {
redisRefreshTokenRepository.delete(request.refreshToken());
}

public MemberInfoResponse getMe(OAuth2UserDetails userDetails) {
return memberService.getMemberInfo(userDetails.getId());
}

public void getWithdrawl(OAuth2UserDetails userDetails) {
Member member = getMember(userDetails.getId());
memberRepository.delete(member);
}

public TokenResponse getSuperToken() {
Member member = memberRepository.findById(1L)
.orElseThrow(() -> new CustomException(ErrorCode.MEMBER_NOT_FOUND));

OAuth2UserDetails oauth2UserDetails = createOauth2UserDetails(member);

String accessToken = jwtTokenProvider.generateSuperToken(oauth2UserDetails);

return new TokenResponse(accessToken, null);
}

private Member getMember(Long memberId) {
return memberRepository.findById(memberId)
.orElseThrow(() -> new CustomException(ErrorCode.MEMBER_NOT_FOUND));
}

private KakaoAuthInfoResponse getKakaoAuthInfoResponse(KakaoAuthLoginResponse kakaoLoginAccessToken) {
return kakaoAuthInfoClient.getInfo("Bearer " + kakaoLoginAccessToken.accessToken());
}

private KakaoAuthLoginResponse getKakaoLoginAccessToken(LoginKakaoRequest request) {
Map<String, Object> parmaMap = new HashMap<>();
parmaMap.put("grant_type", "authorization_code");
parmaMap.put("client_id", KAKAO_CLIENT_ID);
parmaMap.put("client_secret", KAKAO_CLIENT_SECRET);
parmaMap.put("redirect_uri", request.redirectUri());
parmaMap.put("code", request.code());

return kakaoAuthLoginClient.getAccessToken(parmaMap);
}

private TokenResponse kakaoLoginSuccessHandler(String email) {
Member member = getMemberElseCreateMember(email);
List<SimpleGrantedAuthority> authorities = member.getGroups()
.getGroupPermissions()
.stream()
.map(groupPermission -> new SimpleGrantedAuthority(groupPermission.getPermission().getName()))
.toList();

OAuth2UserDetails userDetails = OAuth2UserDetails.builder()
.id(member.getId())
.provider(OAuthProvider.KAKAO)
.authorities(authorities)
.build();

String accessToken = jwtTokenProvider.generateAccessToken(userDetails);
String refreshToken = jwtTokenProvider.generateRefreshToken();

redisRefreshTokenRepository.save(refreshToken, member.getId());

return new TokenResponse(accessToken, refreshToken);
}

private Member getMemberElseCreateMember(String email) {
return memberRepository.findByEmail(email).orElseGet(() -> {
Groups userGroup = groupRepository.findByName("USER_GROUP").orElseThrow(RuntimeException::new);
Member member = Member.builder()
.email(email)
.groups(userGroup)
.build();
return memberRepository.save(member);
});
}

private OAuth2UserDetails createOauth2UserDetails(Member member) {
List<SimpleGrantedAuthority> authorities = member.getGroups()
.getGroupPermissions()
.stream()
.map(groupPermission -> new SimpleGrantedAuthority(groupPermission.getPermission().getName()))
.toList();

return OAuth2UserDetails.builder()
.id(member.getId())
.provider(OAuthProvider.KAKAO) // 일단 카카오로
.authorities(authorities)
.build();
}
@Value("${kakao.client-id}")
private String KAKAO_CLIENT_ID;
@Value("${kakao.client-secret}")
private String KAKAO_CLIENT_SECRET;

private final KakaoAuthLoginClient kakaoAuthLoginClient;
private final KakaoAuthInfoClient kakaoAuthInfoClient;

private final RedisRefreshTokenRepository redisRefreshTokenRepository;
private final JwtTokenProvider jwtTokenProvider;
private final MemberRepository memberRepository;
private final GroupRepository groupRepository;
private final MemberService memberService;

public TokenResponse refreshAccessToken(RefreshTokenRequest request) {
Long memberId = redisRefreshTokenRepository.findByRefreshToken(request.refreshToken())
.orElseThrow(() -> new CustomException(ErrorCode.REFRESH_TOKEN_EXPIRED));

Member member = memberRepository.findById(memberId)
.orElseThrow(() -> new CustomException(ErrorCode.MEMBER_NOT_FOUND));

OAuth2UserDetails oauth2UserDetails = createOauth2UserDetails(member);

String accessToken = jwtTokenProvider.generateAccessToken(oauth2UserDetails);
String refreshToken = jwtTokenProvider.generateRefreshToken();

redisRefreshTokenRepository.save(refreshToken, memberId);

return new TokenResponse(accessToken, refreshToken);
}

public TokenResponse loginKakao(LoginKakaoRequest request) {
KakaoAuthLoginResponse kakaoLoginAccessToken = getKakaoLoginAccessToken(request);
KakaoAuthInfoResponse kakaoAuthInfoResponse = getKakaoAuthInfoResponse(kakaoLoginAccessToken);
return kakaoLoginSuccessHandler(kakaoAuthInfoResponse.kakaoAccount().getEmail());
}

public void logout(RefreshTokenRequest request) {
redisRefreshTokenRepository.delete(request.refreshToken());
}

public MemberInfoResponse getMe(OAuth2UserDetails userDetails) {
return memberService.getMemberInfo(userDetails.getId());
}

public void getWithdrawl(OAuth2UserDetails userDetails) {
Member member = getMember(userDetails.getId());
memberRepository.delete(member);
}

public TokenResponse getSuperToken() {
Member member = memberRepository.findById(1L)
.orElseThrow(() -> new CustomException(ErrorCode.MEMBER_NOT_FOUND));

OAuth2UserDetails oauth2UserDetails = createOauth2UserDetails(member);

String accessToken = jwtTokenProvider.generateSuperToken(oauth2UserDetails);

return new TokenResponse(accessToken, null);
}

public OAuth2UserDetails createOauth2UserDetails(Member member) {
List<SimpleGrantedAuthority> authorities = member.getGroups()
.getGroupPermissions()
.stream()
.map(groupPermission -> new SimpleGrantedAuthority(groupPermission.getPermission().getName()))
.toList();

return OAuth2UserDetails.builder()
.id(member.getId())
.provider(OAuthProvider.KAKAO) // 일단 카카오로
.authorities(authorities)
.build();
}

private Member getMember(Long memberId) {
return memberRepository.findById(memberId)
.orElseThrow(() -> new CustomException(ErrorCode.MEMBER_NOT_FOUND));
}

private KakaoAuthInfoResponse getKakaoAuthInfoResponse(KakaoAuthLoginResponse kakaoLoginAccessToken) {
return kakaoAuthInfoClient.getInfo("Bearer " + kakaoLoginAccessToken.accessToken());
}

private KakaoAuthLoginResponse getKakaoLoginAccessToken(LoginKakaoRequest request) {
Map<String, Object> parmaMap = new HashMap<>();
parmaMap.put("grant_type", "authorization_code");
parmaMap.put("client_id", KAKAO_CLIENT_ID);
parmaMap.put("client_secret", KAKAO_CLIENT_SECRET);
parmaMap.put("redirect_uri", request.redirectUri());
parmaMap.put("code", request.code());

return kakaoAuthLoginClient.getAccessToken(parmaMap);
}

private TokenResponse kakaoLoginSuccessHandler(String email) {
Member member = getMemberElseCreateMember(email);
List<SimpleGrantedAuthority> authorities = member.getGroups()
.getGroupPermissions()
.stream()
.map(groupPermission -> new SimpleGrantedAuthority(groupPermission.getPermission().getName()))
.toList();

OAuth2UserDetails userDetails = OAuth2UserDetails.builder()
.id(member.getId())
.provider(OAuthProvider.KAKAO)
.authorities(authorities)
.build();

String accessToken = jwtTokenProvider.generateAccessToken(userDetails);
String refreshToken = jwtTokenProvider.generateRefreshToken();

redisRefreshTokenRepository.save(refreshToken, member.getId());

return new TokenResponse(accessToken, refreshToken);
}

private Member getMemberElseCreateMember(String email) {
return memberRepository.findByEmail(email).orElseGet(() -> {
Groups userGroup = groupRepository.findByName("USER_GROUP").orElseThrow(RuntimeException::new);
Member member = Member.builder()
.email(email)
.groups(userGroup)
.build();
return memberRepository.save(member);
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.mm.api.domain.point.service;

import static org.junit.jupiter.api.Assertions.*;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;

import com.mm.api.domain.auth.service.AuthService;
import com.mm.coredomain.domain.Member;
import com.mm.coredomain.repository.MemberRepository;
import com.mm.coresecurity.oauth.OAuth2UserDetails;

@SpringBootTest
@ActiveProfiles("stage")
class PointServiceTest {
@Autowired
private AuthService authService;
@Autowired
private PointService pointService;
@Autowired
private MemberRepository memberRepository;

@Test
void 포인트_출금요청_동시성처리_성공() throws InterruptedException {
// given
Member member = Member.builder()
.email("test")
.point(10000)
.build();

Member savedMember = memberRepository.saveAndFlush(member);
OAuth2UserDetails userDetails = OAuth2UserDetails.builder()
.id(savedMember.getId())
.build();

ExecutorService executorService = Executors.newFixedThreadPool(5);
CountDownLatch countDownLatch = new CountDownLatch(5);

// when
for (int i = 0; i < 5; i++) {
executorService.submit(() -> {
try {
pointService.postPointsWithdraw(userDetails, 1000);
} finally {
countDownLatch.countDown();
}
});
}
countDownLatch.await();

// then
Integer point = memberRepository.findById(savedMember.getId()).orElseThrow().getPoint();
assertEquals(5000, point);
}
}

0 comments on commit 259caa8

Please sign in to comment.