Skip to content

Commit 15aa7b9

Browse files
committed
CLAP-112 Test: LoginAttemptService 단위 테스트 코드 작성
<footer> - 관련: #75
1 parent f4c321b commit 15aa7b9

File tree

2 files changed

+166
-4
lines changed

2 files changed

+166
-4
lines changed

src/test/java/clap/server/TestDataFactory.java

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public static Member createAdmin() {
2828
.emailNotificationEnabled(true)
2929
.imageUrl(null)
3030
.status(MemberStatus.ACTIVE)
31-
.password("1111")
31+
.password("Password123!")
3232
.department(createDepartment())
3333
.build();
3434
}
@@ -43,7 +43,7 @@ public static Member createManagerWithReviewer() {
4343
.emailNotificationEnabled(true)
4444
.imageUrl(null)
4545
.status(MemberStatus.ACTIVE)
46-
.password("1111")
46+
.password("Password456!")
4747
.department(createDepartment())
4848
.build();
4949
}
@@ -58,7 +58,7 @@ public static Member createManager() {
5858
.emailNotificationEnabled(true)
5959
.imageUrl(null)
6060
.status(MemberStatus.ACTIVE)
61-
.password("1111")
61+
.password("Password789!")
6262
.department(createDepartment())
6363
.build();
6464
}
@@ -73,7 +73,7 @@ public static Member createUser() {
7373
.emailNotificationEnabled(true)
7474
.imageUrl(null)
7575
.status(MemberStatus.ACTIVE)
76-
.password("1111")
76+
.password("Password000!")
7777
.department(createDepartment())
7878
.build();
7979
}
@@ -90,6 +90,33 @@ public static MemberInfo createAdminInfo() {
9090
.build();
9191
}
9292

93+
public static Member createNotApprovedUser() {
94+
return Member.builder()
95+
.memberId(4L)
96+
.memberInfo(createNotApprovedUserInfo())
97+
.admin(createAdmin())
98+
.kakaoworkNotificationEnabled(true)
99+
.agitNotificationEnabled(true)
100+
.emailNotificationEnabled(true)
101+
.imageUrl(null)
102+
.status(MemberStatus.APPROVAL_REQUEST)
103+
.password("Password000!")
104+
.department(createDepartment())
105+
.build();
106+
}
107+
108+
public static MemberInfo createNotApprovedUserInfo() {
109+
return MemberInfo.builder()
110+
.name("홍길동(등록 대기중인 사용자)")
111+
.email("atom8426@naver.com")
112+
.nickname("atom.user")
113+
.isReviewer(false)
114+
.department(null)
115+
.role(MemberRole.ROLE_USER)
116+
.departmentRole("인프라")
117+
.build();
118+
}
119+
93120
public static MemberInfo createManagerWithReviewerInfo() {
94121
return MemberInfo.builder()
95122
.name("홍길동(리뷰어)")
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package clap.server.application.service.auth;
2+
3+
import clap.server.application.port.outbound.auth.loginLog.CommandLoginLogPort;
4+
import clap.server.application.port.outbound.auth.loginLog.LoadLoginLogPort;
5+
import clap.server.domain.model.auth.LoginLog;
6+
import clap.server.exception.AuthException;
7+
import clap.server.exception.code.AuthErrorCode;
8+
import org.junit.jupiter.api.BeforeEach;
9+
import org.junit.jupiter.api.DisplayName;
10+
import org.junit.jupiter.api.Test;
11+
import org.junit.jupiter.api.extension.ExtendWith;
12+
import org.mockito.InjectMocks;
13+
import org.mockito.Mock;
14+
import org.mockito.junit.jupiter.MockitoExtension;
15+
16+
import java.time.LocalDateTime;
17+
import java.util.Optional;
18+
19+
import static org.junit.jupiter.api.Assertions.*;
20+
import static org.mockito.Mockito.*;
21+
22+
@ExtendWith(MockitoExtension.class)
23+
class LoginAttemptServiceTest {
24+
25+
@InjectMocks
26+
private LoginAttemptService loginAttemptService;
27+
28+
@Mock
29+
private LoadLoginLogPort loadLoginLogPort;
30+
31+
@Mock
32+
private CommandLoginLogPort commandLoginLogPort;
33+
34+
private final String clientIp = "192.168.1.1";
35+
private LoginLog existingLoginLog;
36+
private LoginLog lockedAccountLoginLog;
37+
private LoginLog lockTimeExpiredLoginLog;
38+
39+
public static LoginLog createLoginLog(String clientIp, int count, boolean isLocked, LocalDateTime lastAttemptAt){
40+
return LoginLog.builder()
41+
.attemptNickname("testUser")
42+
.lastAttemptAt(lastAttemptAt)
43+
.failedCount(count)
44+
.isLocked(isLocked)
45+
.clientIp(clientIp)
46+
.build();
47+
}
48+
49+
@BeforeEach
50+
void setUp() {
51+
existingLoginLog = createLoginLog(clientIp, 3, false, LocalDateTime.now());
52+
lockedAccountLoginLog = createLoginLog(clientIp, 5, true, LocalDateTime.now());
53+
lockTimeExpiredLoginLog = createLoginLog(clientIp, 5, true, LocalDateTime.now().minusMinutes(31));
54+
}
55+
56+
@Test
57+
@DisplayName("로그인에 실패하면 IP를 통해 로그인 실패 기록이 저장된다.")
58+
void recordFailedAttempt_NewIP() {
59+
String nickname = "testUser";
60+
when(loadLoginLogPort.findByClientIp(clientIp)).thenReturn(Optional.empty());
61+
62+
loginAttemptService.recordFailedAttempt(clientIp, nickname);
63+
64+
verify(commandLoginLogPort).save(any(LoginLog.class));
65+
}
66+
67+
@Test
68+
@DisplayName("기존 IP로 로그인에 실패하면 로그인 실패 기록이 갱신된다.")
69+
void recordFailedAttempt_ExistingIP_BeforeLock() {
70+
String nickname = "testUser";
71+
LoginLog existingLog = existingLoginLog;;
72+
73+
when(loadLoginLogPort.findByClientIp(clientIp)).thenReturn(Optional.of(existingLog));
74+
75+
loginAttemptService.recordFailedAttempt(clientIp, nickname);
76+
77+
verify(commandLoginLogPort).save(existingLog);
78+
assertEquals(4, existingLog.getFailedCount());
79+
assertFalse(existingLog.isLocked());
80+
}
81+
82+
@Test
83+
@DisplayName("기존 IP로 로그인에 5회 실패하면 계정이 잠긴다.")
84+
void recordFailedAttempt_AccountLock() {
85+
String nickname = "testUser";
86+
LoginLog existingLog = existingLoginLog;
87+
existingLog.recordFailedAttempt();
88+
89+
when(loadLoginLogPort.findByClientIp(clientIp)).thenReturn(Optional.of(existingLog));
90+
91+
assertThrows(AuthException.class, () -> loginAttemptService.recordFailedAttempt(clientIp, nickname));
92+
93+
verify(commandLoginLogPort).save(existingLog);
94+
assertTrue(existingLog.isLocked());
95+
}
96+
97+
@Test
98+
@DisplayName("로그인 5회 실패가 아닐시에는 계정 잠금처리가 되지 않는다.")
99+
void checkAccountIsLocked_NotLocked() {
100+
LoginLog loginLog = existingLoginLog;
101+
loginLog.setLocked(false);
102+
103+
when(loadLoginLogPort.findByClientIp(clientIp)).thenReturn(Optional.of(loginLog));
104+
105+
assertDoesNotThrow(() -> loginAttemptService.checkAccountIsLocked(clientIp));
106+
}
107+
108+
@Test
109+
@DisplayName("잠긴 계정에 대해 로그인 시도시에 AUTH_012 예외가 발생한다.")
110+
void checkAccountIsLocked_Locked() {
111+
LoginLog loginLog = lockedAccountLoginLog;
112+
113+
when(loadLoginLogPort.findByClientIp(clientIp)).thenReturn(Optional.of(loginLog));
114+
115+
AuthException exception = assertThrows(AuthException.class, () ->
116+
loginAttemptService.checkAccountIsLocked(clientIp)
117+
);
118+
119+
assertEquals(AuthErrorCode.ACCOUNT_IS_LOCKED.getMessage(), exception.getMessage(),
120+
"잠긴 계정에 대해 로그인 시도시에 AUTH_012 예외가 발생해야 합니다.");
121+
}
122+
123+
@Test
124+
@DisplayName("30분이 지난 후 잠김 여부를 확인하면 AUTH_012 예외를 반환하지 않는다.")
125+
void checkAccountIsLocked_LockTimeExpired() {
126+
LoginLog loginLog = lockTimeExpiredLoginLog;
127+
128+
when(loadLoginLogPort.findByClientIp(clientIp)).thenReturn(Optional.of(loginLog));
129+
130+
loginAttemptService.checkAccountIsLocked(clientIp);
131+
132+
verify(commandLoginLogPort).deleteById(clientIp);
133+
}
134+
135+
}

0 commit comments

Comments
 (0)