|
1 | 1 | package io.f1.backend.domain.game.websocket; |
2 | 2 |
|
3 | 3 | import static org.junit.jupiter.api.Assertions.*; |
| 4 | +import static org.mockito.ArgumentMatchers.any; |
| 5 | +import static org.mockito.ArgumentMatchers.anyLong; |
| 6 | +import static org.mockito.ArgumentMatchers.anyString; |
4 | 7 | import static org.mockito.ArgumentMatchers.eq; |
| 8 | +import static org.mockito.Mockito.never; |
5 | 9 | import static org.mockito.Mockito.times; |
6 | 10 | import static org.mockito.Mockito.verify; |
7 | 11 |
|
|
11 | 15 | import io.f1.backend.domain.user.dto.UserPrincipal; |
12 | 16 | import io.f1.backend.domain.user.entity.User; |
13 | 17 |
|
| 18 | +import java.util.concurrent.Executors; |
14 | 19 | import org.junit.jupiter.api.BeforeEach; |
15 | 20 | import org.junit.jupiter.api.DisplayName; |
16 | 21 | import org.junit.jupiter.api.Test; |
@@ -48,6 +53,9 @@ void setUp() { |
48 | 53 | ReflectionTestUtils.setField(sessionService, "userIdSession", new ConcurrentHashMap<>()); |
49 | 54 | ReflectionTestUtils.setField( |
50 | 55 | sessionService, "userIdLatestSession", new ConcurrentHashMap<>()); |
| 56 | + |
| 57 | + ReflectionTestUtils.setField(sessionService, "scheduler", Executors.newScheduledThreadPool(2)); |
| 58 | + |
51 | 59 | } |
52 | 60 |
|
53 | 61 | @Test |
@@ -138,4 +146,84 @@ void removeSession_shouldRemoveAndPutLatestSession() { |
138 | 146 | // userIdLatestSession에 업데이트되었는지 확인 |
139 | 147 | assertFalse(userIdLatestSession.containsKey(userId1)); |
140 | 148 | } |
| 149 | + |
| 150 | + @Test |
| 151 | + @DisplayName("handleUserDisconnect: 연결 끊김 시 userIdLatestSession에 이전 세션 ID가 저장되는지 확인") |
| 152 | + void handleUserDisconnect_shouldStoreOldSessionIdInLatestSession() { |
| 153 | + // given |
| 154 | + sessionService.addSession(sessionId1, userId1); // 유저의 현재 활성 세션 |
| 155 | + sessionService.addRoomId(roomId1, sessionId1); |
| 156 | + |
| 157 | + User user = new User("provider", "providerId", LocalDateTime.now()); |
| 158 | + user.setId(userId1); |
| 159 | + UserPrincipal principal = new UserPrincipal(user, new HashMap<>()); |
| 160 | + |
| 161 | + // when |
| 162 | + sessionService.handleUserDisconnect(sessionId1, principal); |
| 163 | + |
| 164 | + // then |
| 165 | + Map<Long, String> userIdLatestSession = |
| 166 | + (Map<Long, String>) ReflectionTestUtils.getField(sessionService, "userIdLatestSession"); |
| 167 | + |
| 168 | + assertTrue(userIdLatestSession.containsKey(userId1)); |
| 169 | + assertEquals(sessionId1, userIdLatestSession.get(userId1)); |
| 170 | + |
| 171 | + // 재연결 상태 변경 검증 |
| 172 | + verify(roomService, times(1)) |
| 173 | + .changeConnectedStatus(roomId1, sessionId1, ConnectionState.DISCONNECTED); |
| 174 | + } |
| 175 | + |
| 176 | + @Test |
| 177 | + @DisplayName("handleUserDisconnect 후 5초 내 재연결: userIdLatestSession이 정리되고 exitIfNotPlaying이 호출되지 않음") |
| 178 | + void handleUserDisconnect_reconnectWithin5Seconds_shouldCleanLatestSession() throws InterruptedException { |
| 179 | + // given |
| 180 | + sessionService.addSession(sessionId1, userId1); // 초기 세션 |
| 181 | + sessionService.addRoomId(roomId1, sessionId1); |
| 182 | + |
| 183 | + User user = new User("provider", "providerId", LocalDateTime.now()); |
| 184 | + user.setId(userId1); |
| 185 | + UserPrincipal principal = new UserPrincipal(user, new HashMap<>()); |
| 186 | + |
| 187 | + sessionService.handleUserDisconnect(sessionId1, principal); // 세션1 끊김, userIdLatestSession에 세션1 저장 |
| 188 | + |
| 189 | + // 5초 타이머가 실행되기 전에 새로운 세션으로 재연결 시도 (userIdSession 업데이트) |
| 190 | + sessionService.addSession(sessionId2, userId1); // userId1의 새 세션은 sessionId2 |
| 191 | + sessionService.addRoomId(roomId1, sessionId2); // 새 세션도 룸에 추가 |
| 192 | + |
| 193 | + // when (5초가 경과했다고 가정) |
| 194 | + Thread.sleep(5100); |
| 195 | + |
| 196 | + // then |
| 197 | + Map<Long, String> userIdLatestSession = |
| 198 | + (Map<Long, String>) ReflectionTestUtils.getField(sessionService, "userIdLatestSession"); |
| 199 | + Map<String, Long> sessionIdUser = |
| 200 | + (Map<String, Long>) ReflectionTestUtils.getField(sessionService, "sessionIdUser"); |
| 201 | + Map<String, Long> sessionIdRoom = |
| 202 | + (Map<String, Long>) ReflectionTestUtils.getField(sessionService, "sessionIdRoom"); |
| 203 | + Map<Long, String> userIdSession = |
| 204 | + (Map<Long, String>) ReflectionTestUtils.getField(sessionService, "userIdSession"); |
| 205 | + |
| 206 | + |
| 207 | + // userIdLatestSession은 정리되어야 함 |
| 208 | + assertFalse(userIdLatestSession.containsKey(userId1)); |
| 209 | + assertNull(userIdLatestSession.get(userId1)); |
| 210 | + |
| 211 | + // roomService.exitIfNotPlaying은 호출되지 않아야 함 (재연결 성공했으므로) |
| 212 | + verify(roomService, never()).exitIfNotPlaying(anyLong(), anyString(), any(UserPrincipal.class)); |
| 213 | + |
| 214 | + // 세션 관련 맵들이 올바르게 정리되었는지 확인 |
| 215 | + // sessionId1에 대한 정보는 모두 삭제되어야 함 |
| 216 | + assertFalse(sessionIdUser.containsKey(sessionId1)); // sessionId1은 sessionIdUser에서 삭제 |
| 217 | + assertFalse(sessionIdRoom.containsKey(sessionId1)); // sessionId1은 sessionIdRoom에서 삭제 |
| 218 | + |
| 219 | + // userIdSession은 sessionId2로 업데이트되어 있어야 함 |
| 220 | + assertTrue(userIdSession.containsKey(userId1)); |
| 221 | + assertEquals(sessionId2, userIdSession.get(userId1)); |
| 222 | + |
| 223 | + // sessionId2에 대한 정보는 남아있어야 함 |
| 224 | + assertTrue(sessionIdUser.containsKey(sessionId2)); |
| 225 | + assertEquals(userId1, sessionIdUser.get(sessionId2)); |
| 226 | + assertTrue(sessionIdRoom.containsKey(sessionId2)); |
| 227 | + assertEquals(roomId1, sessionIdRoom.get(sessionId2)); |
| 228 | + } |
141 | 229 | } |
0 commit comments