-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
회원가입 시 자동으로 공식 계정 친구 추가
- Loading branch information
Showing
7 changed files
with
257 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
13 changes: 13 additions & 0 deletions
13
src/main/java/today/seasoning/seasoning/user/event/SignUpEvent.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package today.seasoning.seasoning.user.event; | ||
|
||
import lombok.Getter; | ||
import lombok.RequiredArgsConstructor; | ||
import today.seasoning.seasoning.user.domain.User; | ||
|
||
@Getter | ||
@RequiredArgsConstructor | ||
public class SignUpEvent { | ||
|
||
private final User signUpUser; | ||
|
||
} |
33 changes: 33 additions & 0 deletions
33
src/main/java/today/seasoning/seasoning/user/event/SignUpEventHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package today.seasoning.seasoning.user.event; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.transaction.annotation.Propagation; | ||
import org.springframework.transaction.annotation.Transactional; | ||
import org.springframework.transaction.event.TransactionPhase; | ||
import org.springframework.transaction.event.TransactionalEventListener; | ||
import today.seasoning.seasoning.friendship.domain.Friendship; | ||
import today.seasoning.seasoning.friendship.domain.FriendshipRepository; | ||
import today.seasoning.seasoning.user.domain.User; | ||
import today.seasoning.seasoning.user.domain.UserRepository; | ||
|
||
@Component | ||
@RequiredArgsConstructor | ||
public class SignUpEventHandler { | ||
|
||
@Value("${OFFICIAL_ACCOUNT_USER_ID}") | ||
private Long officialAccountUserId; | ||
|
||
private final UserRepository userRepository; | ||
private final FriendshipRepository friendshipRepository; | ||
|
||
@Transactional(propagation = Propagation.REQUIRES_NEW) | ||
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) | ||
public void addOfficialAccountFriend(SignUpEvent event) { | ||
User signUpUser = event.getSignUpUser(); | ||
User officialUser = userRepository.findByIdOrElseThrow(officialAccountUserId); | ||
friendshipRepository.save(new Friendship(signUpUser, officialUser)); | ||
friendshipRepository.save(new Friendship(officialUser, signUpUser)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
78 changes: 78 additions & 0 deletions
78
src/test/java/today/seasoning/seasoning/user/event/SignUpEventHandlerIntegrationTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package today.seasoning.seasoning.user.event; | ||
|
||
import org.assertj.core.api.SoftAssertions; | ||
import org.assertj.core.api.junit.jupiter.InjectSoftAssertions; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.context.ApplicationEventPublisher; | ||
import org.springframework.test.context.jdbc.Sql; | ||
import org.springframework.transaction.support.TransactionTemplate; | ||
import today.seasoning.seasoning.BaseIntegrationTest; | ||
import today.seasoning.seasoning.common.enums.LoginType; | ||
import today.seasoning.seasoning.friendship.domain.FriendshipRepository; | ||
import today.seasoning.seasoning.user.domain.User; | ||
import today.seasoning.seasoning.user.domain.UserRepository; | ||
|
||
@DisplayName("회원가입 이벤트 핸들러 통합 테스트") | ||
@Sql(scripts = "classpath:data/insert_official_account_user.sql") | ||
class SignUpEventHandlerIntegrationTest extends BaseIntegrationTest { | ||
|
||
@Autowired | ||
UserRepository userRepository; | ||
|
||
@Autowired | ||
TransactionTemplate transactionTemplate; | ||
|
||
@Autowired | ||
FriendshipRepository friendshipRepository; | ||
|
||
@Autowired | ||
ApplicationEventPublisher applicationEventPublisher; | ||
|
||
@InjectSoftAssertions | ||
SoftAssertions softAssertions; | ||
|
||
@Value("${OFFICIAL_ACCOUNT_USER_ID}") | ||
private Long officialAccountUserId; | ||
|
||
private User user; | ||
|
||
@BeforeEach | ||
void registerUser() { | ||
user = userRepository.save(new User("nickname0", "https://test.org/user0.jpg", "user0@email.com", LoginType.KAKAO)); | ||
} | ||
|
||
@Test | ||
@DisplayName("회원가입 이벤트 발생 시, 공식 계정을 신규 회원의 친구로 등록한다") | ||
void test1() { | ||
//given | ||
SignUpEvent signUpEvent = new SignUpEvent(user); | ||
|
||
//when | ||
transactionTemplate.executeWithoutResult(status -> applicationEventPublisher.publishEvent(signUpEvent)); | ||
|
||
//then | ||
softAssertions.assertThat(friendshipRepository.count()).isEqualTo(2); | ||
softAssertions.assertThat(friendshipRepository.existsByUserIdAndFriendId(officialAccountUserId, user.getId())).isTrue(); | ||
softAssertions.assertThat(friendshipRepository.existsByUserIdAndFriendId(user.getId(), officialAccountUserId)).isTrue(); | ||
} | ||
|
||
@Test | ||
@DisplayName("회원가입이 실패한 경우, 발행된 회원가입 이벤트는 처리되지 않는다") | ||
void test2() { | ||
//given | ||
SignUpEvent signUpEvent = new SignUpEvent(user); | ||
|
||
//when | ||
transactionTemplate.executeWithoutResult(transactionStatus -> { | ||
applicationEventPublisher.publishEvent(signUpEvent); | ||
transactionStatus.setRollbackOnly(); | ||
}); | ||
|
||
//then | ||
softAssertions.assertThat(friendshipRepository.count()).isEqualTo(0); | ||
} | ||
} |
119 changes: 119 additions & 0 deletions
119
src/test/java/today/seasoning/seasoning/user/service/kakao/KakaoLoginServiceTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
package today.seasoning.seasoning.user.service.kakao; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.mockito.BDDMockito.any; | ||
import static org.mockito.BDDMockito.anyString; | ||
import static org.mockito.BDDMockito.given; | ||
import static org.mockito.BDDMockito.mockStatic; | ||
import static org.mockito.BDDMockito.verify; | ||
import static org.mockito.Mockito.never; | ||
import static today.seasoning.seasoning.common.enums.LoginType.KAKAO; | ||
|
||
import java.util.Optional; | ||
import org.junit.jupiter.api.AfterAll; | ||
import org.junit.jupiter.api.BeforeAll; | ||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.extension.ExtendWith; | ||
import org.mockito.ArgumentCaptor; | ||
import org.mockito.InjectMocks; | ||
import org.mockito.Mock; | ||
import org.mockito.MockedStatic; | ||
import org.mockito.junit.jupiter.MockitoExtension; | ||
import org.springframework.context.ApplicationEventPublisher; | ||
import today.seasoning.seasoning.common.util.JwtUtil; | ||
import today.seasoning.seasoning.user.domain.User; | ||
import today.seasoning.seasoning.user.domain.UserRepository; | ||
import today.seasoning.seasoning.user.dto.LoginResult; | ||
import today.seasoning.seasoning.user.dto.SocialUserProfileDto; | ||
import today.seasoning.seasoning.user.event.SignUpEvent; | ||
|
||
@DisplayName("카카오 로그인 단위 테스트") | ||
@ExtendWith(MockitoExtension.class) | ||
class KakaoLoginServiceTest { | ||
|
||
@Mock | ||
private ExchangeKakaoAccessToken exchangeKakaoAccessToken; | ||
@Mock | ||
private FetchKakaoUserProfile fetchKakaoUserProfile; | ||
@Mock | ||
private ApplicationEventPublisher eventPublisher; | ||
@Mock | ||
private UserRepository userRepository; | ||
@InjectMocks | ||
KakaoLoginService kakaoLoginService; | ||
|
||
private static MockedStatic<JwtUtil> jwtUtil; | ||
|
||
@BeforeAll | ||
static void beforeAll() { | ||
jwtUtil = mockStatic(JwtUtil.class); | ||
} | ||
|
||
@AfterAll | ||
static void afterAll() { | ||
jwtUtil.close(); | ||
} | ||
|
||
@Test | ||
@DisplayName("비회원 로그인") | ||
void test() { | ||
//given | ||
SocialUserProfileDto newUserProfile = new SocialUserProfileDto("user", "user@test.org", "https://test.org/image.jpg"); | ||
User newUser = newUserProfile.toEntity(KAKAO); | ||
|
||
given(fetchKakaoUserProfile.doFetch(any())).willReturn(newUserProfile); | ||
// 가입된 회원이 아닌 경우 | ||
given(userRepository.find(anyString(), any())).willReturn(Optional.empty()); | ||
given(userRepository.save(any(User.class))).willReturn(newUser); | ||
|
||
//when: 카카오 로그인 시 | ||
LoginResult loginResult = kakaoLoginService.handleKakaoLogin("pseudo-authorization-code"); | ||
|
||
//then: 사용자를 신규 회원으로 등록해야 한다 | ||
ArgumentCaptor<User> userCaptor = ArgumentCaptor.forClass(User.class); | ||
verify(userRepository).save(userCaptor.capture()); | ||
User savedUser = userCaptor.getValue(); | ||
|
||
//then 1: 등록된 회원의 정보는 프로필 정보와 일치해야 한다 | ||
assertThat(savedUser.getLoginType()).isEqualTo(KAKAO); | ||
assertThat(savedUser.getEmail()).isEqualTo(newUserProfile.getEmail()); | ||
assertThat(savedUser.getNickname()).isEqualTo(newUserProfile.getNickname()); | ||
assertThat(savedUser.getProfileImageUrl()).isEqualTo(newUserProfile.getProfileImageUrl()); | ||
|
||
//then 2: 등록된 회원에 대한 회원가입 이벤트가 발행되어야 한다 | ||
ArgumentCaptor<SignUpEvent> publishedEventCaptor = ArgumentCaptor.forClass(SignUpEvent.class); | ||
verify(eventPublisher).publishEvent(publishedEventCaptor.capture()); | ||
SignUpEvent publishedEvent = publishedEventCaptor.getValue(); | ||
|
||
assertThat(publishedEvent.getSignUpUser()).isEqualTo(newUser); | ||
|
||
//then 3: LoginResult.firstLogin의 값은 true이어야 한다 | ||
assertThat(loginResult.isFirstLogin()).isTrue(); | ||
} | ||
|
||
@Test | ||
@DisplayName("회원 로그인") | ||
void test2() { | ||
//given | ||
SocialUserProfileDto userProfile = new SocialUserProfileDto("user", "user@test.org", "https://test.org/image.jpg"); | ||
User user = userProfile.toEntity(KAKAO); | ||
|
||
given(fetchKakaoUserProfile.doFetch(any())).willReturn(userProfile); | ||
// 가입된 회원의 경우 | ||
given(userRepository.find(anyString(), any())).willReturn(Optional.of(user)); | ||
|
||
//when: 카카오 로그인 시 | ||
LoginResult loginResult = kakaoLoginService.handleKakaoLogin("pseudo-authorization-code"); | ||
|
||
//then 1: 등록된 회원에 대한 토큰을 발행해야 한다 | ||
jwtUtil.verify(() -> JwtUtil.createToken(user.getId())); | ||
|
||
//then 2: 회원가입 이벤트는 발행되지 않아야 한다 | ||
verify(eventPublisher, never()).publishEvent(any(SignUpEvent.class)); | ||
|
||
//then 3: LoginResult.firstLogin의 값은 false이어야 한다 | ||
assertThat(loginResult.isFirstLogin()).isFalse(); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
INSERT INTO user (id, account_id, email, login_type, nickname, role) | ||
VALUES (1, 'seasoning', 'seasoning@kakao.com', 'KAKAO', '시즈닝', 'USER'); |