Skip to content

Commit dc4a3c2

Browse files
committed
feat: 재ê사용자 상태에 따라 재가입 여부
1 parent 420bcb7 commit dc4a3c2

2 files changed

Lines changed: 103 additions & 5 deletions

File tree

src/main/java/com/leets/tdd/auth/service/EmailVerificationService.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.leets.tdd.auth.exception.AuthErrorCode;
88
import com.leets.tdd.auth.exception.AuthException;
99
import com.leets.tdd.auth.repository.EmailVerificationRepository;
10+
import com.leets.tdd.user.domain.User;
1011
import com.leets.tdd.user.repository.UserRepository;
1112
import lombok.RequiredArgsConstructor;
1213
import org.springframework.security.crypto.password.PasswordEncoder;
@@ -143,9 +144,27 @@ public boolean consumePasswordResetVerification(String email) {
143144
email, EmailPurpose.RESET_PASSWORD, PASSWORD_RESET_COMPLETION_WINDOW);
144145
}
145146

147+
/**
148+
* 재가입(SIGNUP) 시 기존 회원(email로 찾은) 상태별 발송 가능 여부.
149+
* ACTIVE/SUSPENDED: 이미 쓰고 있는 계정 -> 이미 가입된 이메일
150+
* BANNED: 영구 제한 -> 발송 불가
151+
* DELETED: 탈퇴했던 계정(soft delete라 noShowApprovedCount/suspendedUntil/mannerTemperature는
152+
* 유지됨). 정지기간이 아직 안 지났으면(탈퇴로 정지 우회 방지) 발송 불가,
153+
* 지났거나 정지 이력이 없으면 재사용(reactivate) 대상으로 통과시켜 코드를 발송한다.
154+
*/
146155
private void validateNotAlreadyRegistered(String email) {
147-
if (userRepository.existsByEmail(email)) {
148-
throw new AuthException(AuthErrorCode.ALREADY_REGISTERED_EMAIL);
156+
userRepository.findByEmail(email).ifPresent(this::validateExistingUserForSignup);
157+
}
158+
159+
private void validateExistingUserForSignup(User user) {
160+
switch (user.getStatus()) {
161+
case ACTIVE, SUSPENDED -> throw new AuthException(AuthErrorCode.ALREADY_REGISTERED_EMAIL);
162+
case BANNED -> throw new AuthException(AuthErrorCode.ACCOUNT_BANNED);
163+
case DELETED -> {
164+
if (user.isWithinSuspensionPeriod()) {
165+
throw new AuthException(AuthErrorCode.ACCOUNT_BANNED);
166+
}
167+
}
149168
}
150169
}
151170

src/test/java/com/leets/tdd/auth/service/EmailVerificationServiceTest.java

Lines changed: 82 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.leets.tdd.auth.exception.AuthErrorCode;
88
import com.leets.tdd.auth.exception.AuthException;
99
import com.leets.tdd.auth.repository.EmailVerificationRepository;
10+
import com.leets.tdd.user.domain.User;
1011
import com.leets.tdd.user.repository.UserRepository;
1112
import org.junit.jupiter.api.DisplayName;
1213
import org.junit.jupiter.api.Test;
@@ -48,11 +49,31 @@ class EmailVerificationServiceTest {
4849
@InjectMocks
4950
private EmailVerificationService emailVerificationService;
5051

52+
private User existingUser() {
53+
return new User("abcd@gachon.ac.kr", "가나디", "encoded-pw",
54+
"old-refresh-hash", LocalDateTime.now().plusDays(30));
55+
}
56+
57+
@Test
58+
@DisplayName("SIGNUP 목적으로 이미 가입된(ACTIVE) 이메일이면 예외가 발생한다")
59+
void alreadyRegisteredEmail_active() {
60+
EmailVerificationRequest request = new EmailVerificationRequest("abcd@gachon.ac.kr", EmailPurpose.SIGNUP);
61+
when(userRepository.findByEmail("abcd@gachon.ac.kr")).thenReturn(Optional.of(existingUser()));
62+
63+
assertThatThrownBy(() -> emailVerificationService.sendVerificationCode(request))
64+
.isInstanceOf(AuthException.class)
65+
.hasMessage(AuthErrorCode.ALREADY_REGISTERED_EMAIL.getMessage());
66+
67+
verifyNoInteractions(mailService);
68+
}
69+
5170
@Test
52-
@DisplayName("SIGNUP 목적으로 이미 가입된 이메일이면 예외가 발생한다")
53-
void alreadyRegisteredEmail() {
71+
@DisplayName("SIGNUP 목적으로 이미 가입된(SUSPENDED) 이메일이면 예외가 발생한다")
72+
void alreadyRegisteredEmail_suspended() {
5473
EmailVerificationRequest request = new EmailVerificationRequest("abcd@gachon.ac.kr", EmailPurpose.SIGNUP);
55-
when(userRepository.existsByEmail("abcd@gachon.ac.kr")).thenReturn(true);
74+
User suspended = existingUser();
75+
suspended.suspend(LocalDateTime.now().plusDays(3));
76+
when(userRepository.findByEmail("abcd@gachon.ac.kr")).thenReturn(Optional.of(suspended));
5677

5778
assertThatThrownBy(() -> emailVerificationService.sendVerificationCode(request))
5879
.isInstanceOf(AuthException.class)
@@ -61,6 +82,64 @@ void alreadyRegisteredEmail() {
6182
verifyNoInteractions(mailService);
6283
}
6384

85+
@Test
86+
@DisplayName("SIGNUP 목적으로 BANNED 계정이면 이용이 제한된 계정 예외가 발생한다")
87+
void signup_bannedAccount_throwsAccountBanned() {
88+
EmailVerificationRequest request = new EmailVerificationRequest("abcd@gachon.ac.kr", EmailPurpose.SIGNUP);
89+
User banned = existingUser();
90+
banned.ban();
91+
when(userRepository.findByEmail("abcd@gachon.ac.kr")).thenReturn(Optional.of(banned));
92+
93+
assertThatThrownBy(() -> emailVerificationService.sendVerificationCode(request))
94+
.isInstanceOf(AuthException.class)
95+
.hasMessage(AuthErrorCode.ACCOUNT_BANNED.getMessage());
96+
97+
verifyNoInteractions(mailService);
98+
}
99+
100+
@Test
101+
@DisplayName("SIGNUP 목적으로 DELETED 계정이고 정지기간이 아직 안 지났으면 이용이 제한된 계정 예외가 발생한다")
102+
void signup_deletedWithinSuspension_throwsAccountBanned() {
103+
EmailVerificationRequest request = new EmailVerificationRequest("abcd@gachon.ac.kr", EmailPurpose.SIGNUP);
104+
User deleted = existingUser();
105+
deleted.suspend(LocalDateTime.now().plusDays(3));
106+
deleted.softDelete();
107+
when(userRepository.findByEmail("abcd@gachon.ac.kr")).thenReturn(Optional.of(deleted));
108+
109+
assertThatThrownBy(() -> emailVerificationService.sendVerificationCode(request))
110+
.isInstanceOf(AuthException.class)
111+
.hasMessage(AuthErrorCode.ACCOUNT_BANNED.getMessage());
112+
113+
verifyNoInteractions(mailService);
114+
}
115+
116+
@Test
117+
@DisplayName("SIGNUP 목적으로 DELETED 계정이고 정지기간이 지났으면 재가입으로 간주해 코드를 발송한다")
118+
void signup_deletedAfterSuspension_sendsCode() {
119+
EmailVerificationRequest request = new EmailVerificationRequest("abcd@gachon.ac.kr", EmailPurpose.SIGNUP);
120+
User deleted = existingUser();
121+
deleted.suspend(LocalDateTime.now().minusDays(1));
122+
deleted.softDelete();
123+
when(userRepository.findByEmail("abcd@gachon.ac.kr")).thenReturn(Optional.of(deleted));
124+
125+
assertThatCode(() -> emailVerificationService.sendVerificationCode(request)).doesNotThrowAnyException();
126+
127+
verify(mailService).sendVerificationCode(eq("abcd@gachon.ac.kr"), anyString());
128+
}
129+
130+
@Test
131+
@DisplayName("SIGNUP 목적으로 DELETED 계정이고 정지 이력이 없으면 재가입으로 간주해 코드를 발송한다")
132+
void signup_deletedWithoutSuspensionHistory_sendsCode() {
133+
EmailVerificationRequest request = new EmailVerificationRequest("abcd@gachon.ac.kr", EmailPurpose.SIGNUP);
134+
User deleted = existingUser();
135+
deleted.softDelete();
136+
when(userRepository.findByEmail("abcd@gachon.ac.kr")).thenReturn(Optional.of(deleted));
137+
138+
assertThatCode(() -> emailVerificationService.sendVerificationCode(request)).doesNotThrowAnyException();
139+
140+
verify(mailService).sendVerificationCode(eq("abcd@gachon.ac.kr"), anyString());
141+
}
142+
64143
@Test
65144
@DisplayName("5분 내 요청 횟수(3회)를 초과하면 예외가 발생한다")
66145
void exceedRequestLimit() {

0 commit comments

Comments
 (0)