55
66import io .f1 .backend .global .config .RedissonTestContainerConfig ;
77import io .f1 .backend .global .exception .CustomException ;
8- import java .util .concurrent .CountDownLatch ;
9- import java .util .concurrent .ExecutorService ;
10- import java .util .concurrent .Executors ;
11- import java .util .concurrent .atomic .AtomicInteger ;
12- import java .util .concurrent .atomic .AtomicReference ;
8+
139import org .junit .jupiter .api .DisplayName ;
1410import org .junit .jupiter .api .Test ;
1511import org .springframework .beans .factory .annotation .Autowired ;
1915import org .springframework .test .context .DynamicPropertyRegistry ;
2016import org .springframework .test .context .DynamicPropertySource ;
2117
18+ import java .util .concurrent .CountDownLatch ;
19+ import java .util .concurrent .ExecutorService ;
20+ import java .util .concurrent .Executors ;
21+ import java .util .concurrent .atomic .AtomicInteger ;
22+ import java .util .concurrent .atomic .AtomicReference ;
23+
2224@ SpringBootTest
2325@ Import ({RedissonTestContainerConfig .class , DistributedLockIntegrationTest .TestLockService .class })
2426class DistributedLockIntegrationTest {
2527
2628 @ DynamicPropertySource
2729 static void redisProperties (DynamicPropertyRegistry registry ) {
28- registry .add ("spring.datasource.data.redis.host" , RedissonTestContainerConfig .redisContainer ::getHost );
29- registry .add ("spring.datasource.data.redis.port" , () -> RedissonTestContainerConfig .redisContainer .getFirstMappedPort ());
30+ registry .add (
31+ "spring.datasource.data.redis.host" ,
32+ RedissonTestContainerConfig .redisContainer ::getHost );
33+ registry .add (
34+ "spring.datasource.data.redis.port" ,
35+ () -> RedissonTestContainerConfig .redisContainer .getFirstMappedPort ());
3036 }
3137
32- @ Autowired
33- private TestLockService testLockService ;
38+ @ Autowired private TestLockService testLockService ;
3439
3540 private final Long ROOM_ID = 1L ;
3641
@@ -47,31 +52,35 @@ void testDistributedLock_WhenMultipleThreads_OnlyOneSuccess() throws Exception {
4752
4853 // When: 여러 스레드가 동시에 락 획득 시도
4954 for (int i = 0 ; i < threadCount ; i ++) {
50- executorService .submit (() -> {
51- try {
52- testLockService .executeWithLock (ROOM_ID );
53- successCount .incrementAndGet ();
54- } catch (IllegalStateException e ) {
55- // 락 획득 실패로 인한 예외는 예상된 동작
56- failCount .incrementAndGet ();
57- } catch (Exception e ) {
58- // 기타 예외는 실패로 간주
59- failCount .incrementAndGet ();
60- e .printStackTrace ();
61- } finally {
62- latch .countDown ();
63- }
64- });
55+ executorService .submit (
56+ () -> {
57+ try {
58+ testLockService .executeWithLock (ROOM_ID );
59+ successCount .incrementAndGet ();
60+ } catch (IllegalStateException e ) {
61+ // 락 획득 실패로 인한 예외는 예상된 동작
62+ failCount .incrementAndGet ();
63+ } catch (Exception e ) {
64+ // 기타 예외는 실패로 간주
65+ failCount .incrementAndGet ();
66+ e .printStackTrace ();
67+ } finally {
68+ latch .countDown ();
69+ }
70+ });
6571 }
6672
6773 latch .await ();
6874 executorService .shutdown ();
6975
7076 // Then: 하나의 스레드만 락 획득에 성공하고, 나머지는 모두 실패하는지 검증
7177 assertAll (
72- () -> assertEquals (1 , successCount .get (), "락 획득에 성공한 스레드는 1개여야 합니다" ),
73- () -> assertEquals (threadCount - 1 , failCount .get (), "락 획득에 실패한 스레드는 " + (threadCount - 1 ) + "개여야 합니다" )
74- );
78+ () -> assertEquals (1 , successCount .get (), "락 획득에 성공한 스레드는 1개여야 합니다" ),
79+ () ->
80+ assertEquals (
81+ threadCount - 1 ,
82+ failCount .get (),
83+ "락 획득에 실패한 스레드는 " + (threadCount - 1 ) + "개여야 합니다" ));
7584 }
7685
7786 @ DisplayName ("단일 스레드에서 락 획득이 정상적으로 동작하는지 검증" )
@@ -94,38 +103,39 @@ void testDistributedLock_DifferentKeys_BothSuccess() throws Exception {
94103 AtomicInteger failCount = new AtomicInteger (0 );
95104
96105 // When: 서로 다른 키로 락 획득 시도
97- executorService .submit (() -> {
98- try {
99- testLockService .executeWithLock (1L );
100- successCount .incrementAndGet ();
101- } catch (CustomException e ) {
102- failCount .incrementAndGet ();
103- e .getMessage ();
104- } finally {
105- latch .countDown ();
106- }
107- });
108-
109- executorService .submit (() -> {
110- try {
111- testLockService .executeWithLock (2L );
112- successCount .incrementAndGet ();
113- } catch (Exception e ) {
114- failCount .incrementAndGet ();
115- e .getMessage ();
116- } finally {
117- latch .countDown ();
118- }
119- });
106+ executorService .submit (
107+ () -> {
108+ try {
109+ testLockService .executeWithLock (1L );
110+ successCount .incrementAndGet ();
111+ } catch (CustomException e ) {
112+ failCount .incrementAndGet ();
113+ e .getMessage ();
114+ } finally {
115+ latch .countDown ();
116+ }
117+ });
118+
119+ executorService .submit (
120+ () -> {
121+ try {
122+ testLockService .executeWithLock (2L );
123+ successCount .incrementAndGet ();
124+ } catch (Exception e ) {
125+ failCount .incrementAndGet ();
126+ e .getMessage ();
127+ } finally {
128+ latch .countDown ();
129+ }
130+ });
120131
121132 latch .await ();
122133 executorService .shutdown ();
123134
124135 // Then: 서로 다른 키이므로 둘 다 성공해야 함
125136 assertAll (
126- () -> assertEquals (2 , successCount .get (), "서로 다른 키로 락을 사용하면 둘 다 성공해야 한다" ),
127- () -> assertEquals (0 , failCount .get (), "락 획득을 실패한 스레드는 없어야 한다" )
128- );
137+ () -> assertEquals (2 , successCount .get (), "서로 다른 키로 락을 사용하면 둘 다 성공해야 한다" ),
138+ () -> assertEquals (0 , failCount .get (), "락 획득을 실패한 스레드는 없어야 한다" ));
129139 }
130140
131141 @ DisplayName ("gameStart 중에는 handlePlayerReady가 동일한 roomId로 락을 획득할 수 없어야 한다" )
@@ -141,43 +151,44 @@ void testHandlePlayerReadyFailsWhenGameStartHoldsLock() throws Exception {
141151 AtomicReference <String > successMethod = new AtomicReference <>();
142152
143153 // Thread A: gameStart 락 선점
144- executorService .submit (() -> {
145- try {
146- testLockService .gameStartSimulate (ROOM_ID , gameStartLocked );
147- successMethod .set ("gameStart" );
148- successCount .incrementAndGet ();
149- } catch (Exception e ) {
150- failCount .incrementAndGet ();
151- e .getMessage ();
152- } finally {
153- latch .countDown ();
154- }
155- });
154+ executorService .submit (
155+ () -> {
156+ try {
157+ testLockService .gameStartSimulate (ROOM_ID , gameStartLocked );
158+ successMethod .set ("gameStart" );
159+ successCount .incrementAndGet ();
160+ } catch (Exception e ) {
161+ failCount .incrementAndGet ();
162+ e .getMessage ();
163+ } finally {
164+ latch .countDown ();
165+ }
166+ });
156167
157168 // Thread A: gameStart가 락 획득한 후에 handlePlayerReady 시도
158- executorService .submit (() -> {
159- try {
160- gameStartLocked .await ();
161- testLockService .handlePlayerReadySimulate (ROOM_ID , gameStartLocked );
162- successMethod .set ("handlePlayerReady" );
163- successCount .incrementAndGet ();
164- } catch (Exception e ) {
165- failCount .incrementAndGet ();
166- e .getMessage ();
167- } finally {
168- latch .countDown ();
169- }
170- });
169+ executorService .submit (
170+ () -> {
171+ try {
172+ gameStartLocked .await ();
173+ testLockService .handlePlayerReadySimulate (ROOM_ID , gameStartLocked );
174+ successMethod .set ("handlePlayerReady" );
175+ successCount .incrementAndGet ();
176+ } catch (Exception e ) {
177+ failCount .incrementAndGet ();
178+ e .getMessage ();
179+ } finally {
180+ latch .countDown ();
181+ }
182+ });
171183
172184 latch .await ();
173185 executorService .shutdown ();
174186
175187 // Then
176188 assertAll (
177- () -> assertEquals (1 , successCount .get (), "성공한 스레드는 1개여야 한다." ),
178- () -> assertEquals (1 , failCount .get (), "실패한 스레드는 1개여야 한다." ),
179- () -> assertEquals ("gameStart" , successMethod .get (), "gameStart 락을 획득해야 한다" )
180- );
189+ () -> assertEquals (1 , successCount .get (), "성공한 스레드는 1개여야 한다." ),
190+ () -> assertEquals (1 , failCount .get (), "실패한 스레드는 1개여야 한다." ),
191+ () -> assertEquals ("gameStart" , successMethod .get (), "gameStart 락을 획득해야 한다" ));
181192 }
182193
183194 @ DisplayName ("handlePlayerReady 중에는 gameStart가 동일한 roomId로 락을 획득할 수 없어야 한다" )
@@ -186,59 +197,63 @@ void testGameStartFailsWhenHandlePlayerReadyHoldsLock() throws Exception {
186197 // Given
187198 ExecutorService executorService = Executors .newFixedThreadPool (2 );
188199 CountDownLatch latch = new CountDownLatch (2 );
189- CountDownLatch handlePlayerReadyLocked = new CountDownLatch (1 ); // handlePlayerReady가 락 획득 신호용
200+ CountDownLatch handlePlayerReadyLocked =
201+ new CountDownLatch (1 ); // handlePlayerReady가 락 획득 신호용
190202
191203 AtomicInteger successCount = new AtomicInteger (0 );
192204 AtomicInteger failCount = new AtomicInteger (0 );
193205 AtomicReference <String > successMethod = new AtomicReference <>();
194206
195207 // Thread B: handlePlayerReady가 락 먼저 획득
196- executorService .submit (() -> {
197- try {
198- testLockService .handlePlayerReadySimulate (ROOM_ID , handlePlayerReadyLocked );
199- successMethod .set ("handlePlayerReady" );
200- successCount .incrementAndGet ();
201- } catch (CustomException e ) {
202- failCount .incrementAndGet ();
203- e .getMessage ();
204- } finally {
205- handlePlayerReadyLocked .countDown (); // 락 획득 알림
206- latch .countDown ();
207- }
208- });
208+ executorService .submit (
209+ () -> {
210+ try {
211+ testLockService .handlePlayerReadySimulate (ROOM_ID , handlePlayerReadyLocked );
212+ successMethod .set ("handlePlayerReady" );
213+ successCount .incrementAndGet ();
214+ } catch (CustomException e ) {
215+ failCount .incrementAndGet ();
216+ e .getMessage ();
217+ } finally {
218+ handlePlayerReadyLocked .countDown (); // 락 획득 알림
219+ latch .countDown ();
220+ }
221+ });
209222
210223 // Thread A: handlePlayerReady가 락 획득한 후에 gameStart 시도
211- executorService .submit (() -> {
212- try {
213- handlePlayerReadyLocked .await (); // handlePlayerReady 락 획득 신호 기다림
214- testLockService .gameStartSimulate (ROOM_ID , handlePlayerReadyLocked );
215- successMethod .set ("gameStart" );
216- successCount .incrementAndGet ();
217- } catch (CustomException e ) {
218- failCount .incrementAndGet ();
219- e .getMessage ();
220- } catch (Exception e ) {
221- failCount .incrementAndGet ();
222-
223- } finally {
224- latch .countDown ();
225- }
226- });
224+ executorService .submit (
225+ () -> {
226+ try {
227+ handlePlayerReadyLocked .await (); // handlePlayerReady 락 획득 신호 기다림
228+ testLockService .gameStartSimulate (ROOM_ID , handlePlayerReadyLocked );
229+ successMethod .set ("gameStart" );
230+ successCount .incrementAndGet ();
231+ } catch (CustomException e ) {
232+ failCount .incrementAndGet ();
233+ e .getMessage ();
234+ } catch (Exception e ) {
235+ failCount .incrementAndGet ();
236+
237+ } finally {
238+ latch .countDown ();
239+ }
240+ });
227241
228242 latch .await ();
229243 executorService .shutdown ();
230244
231245 // Then
232246 assertAll (
233- () -> assertEquals (1 , successCount .get (), "성공한 스레드는 1개여야 한다." ),
234- () -> assertEquals (1 , failCount .get (), "실패한 스레드는 1개여야 한다." ),
235- () -> assertEquals ("handlePlayerReady" , successMethod .get (), "handlePlayerReady만 락을 획득해야 한다" )
236- );
247+ () -> assertEquals (1 , successCount .get (), "성공한 스레드는 1개여야 한다." ),
248+ () -> assertEquals (1 , failCount .get (), "실패한 스레드는 1개여야 한다." ),
249+ () ->
250+ assertEquals (
251+ "handlePlayerReady" ,
252+ successMethod .get (),
253+ "handlePlayerReady만 락을 획득해야 한다" ));
237254 }
238255
239- /**
240- * 테스트용 서비스 클래스
241- */
256+ /** 테스트용 서비스 클래스 */
242257 @ Service
243258 static class TestLockService {
244259
0 commit comments