11package com .appsmith .server .aspect ;
22
3+ import com .appsmith .caching .components .InstanceIdProvider ;
34import org .junit .jupiter .api .Test ;
45import org .springframework .beans .factory .annotation .Autowired ;
56import org .springframework .boot .test .context .SpringBootTest ;
7+ import org .springframework .boot .test .mock .mockito .MockBean ;
68import org .springframework .data .redis .core .ReactiveRedisOperations ;
79import reactor .core .publisher .Mono ;
810import reactor .test .StepVerifier ;
1618import static org .junit .jupiter .api .Assertions .assertNull ;
1719import static org .junit .jupiter .api .Assertions .assertThrows ;
1820import static org .junit .jupiter .api .Assertions .assertTrue ;
21+ import static org .mockito .Mockito .when ;
1922
2023@ SpringBootTest
2124class DistributedLockAspectTest {
@@ -26,45 +29,61 @@ class DistributedLockAspectTest {
2629 @ Autowired
2730 private ReactiveRedisOperations <String , String > redisOperations ;
2831
29- private static final String LOCK_PREFIX = "lock:" ;
32+ @ MockBean
33+ private InstanceIdProvider instanceIdProvider ;
34+
35+ private static final String LOCK_PREFIX = "lock" ;
36+ private static final String TEST_INSTANCE_ID = "test-instance-123" ;
37+
38+ private String getLockKey (String key ) {
39+ return LOCK_PREFIX + ":" + TEST_INSTANCE_ID + ":" + key ;
40+ }
3041
3142 @ Test
3243 void testMonoOperation () {
44+ when (instanceIdProvider .getInstanceId ()).thenReturn (Mono .just (TEST_INSTANCE_ID ));
45+
3346 StepVerifier .create (testLockService .monoOperation ())
3447 .expectNext ("mono-success" )
3548 .verifyComplete ();
3649
3750 // Verify lock is released
38- StepVerifier .create (redisOperations .hasKey (LOCK_PREFIX + "mono-test" ))
51+ StepVerifier .create (redisOperations .hasKey (getLockKey ( "mono-test" ) ))
3952 .expectNext (false )
4053 .verifyComplete ();
4154 }
4255
4356 @ Test
4457 void testFluxOperation () {
58+ when (instanceIdProvider .getInstanceId ()).thenReturn (Mono .just (TEST_INSTANCE_ID ));
59+
4560 StepVerifier .create (testLockService .fluxOperation ().collectList ())
4661 .expectNext (List .of ("flux-success-1" , "flux-success-2" ))
4762 .verifyComplete ();
4863
4964 // Verify lock is released
50- StepVerifier .create (redisOperations .hasKey (LOCK_PREFIX + "flux-test" ))
65+ StepVerifier .create (redisOperations .hasKey (getLockKey ( "flux-test" ) ))
5166 .expectNext (false )
5267 .verifyComplete ();
5368 }
5469
5570 @ Test
5671 void testBlockingOperation () {
72+ when (instanceIdProvider .getInstanceId ()).thenReturn (Mono .just (TEST_INSTANCE_ID ));
73+
5774 String result = testLockService .blockingOperation ();
5875 assertEquals ("blocking-success" , result );
5976
6077 // Verify lock is released
61- StepVerifier .create (redisOperations .hasKey (LOCK_PREFIX + "blocking-test" ))
78+ StepVerifier .create (redisOperations .hasKey (getLockKey ( "blocking-test" ) ))
6279 .expectNext (false )
6380 .verifyComplete ();
6481 }
6582
6683 @ Test
6784 void testConcurrentAccess () throws InterruptedException {
85+ when (instanceIdProvider .getInstanceId ()).thenReturn (Mono .just (TEST_INSTANCE_ID ));
86+
6887 AtomicReference <String > thread1Result = new AtomicReference <>();
6988 AtomicReference <String > thread2Result = new AtomicReference <>();
7089 CountDownLatch thread1Started = new CountDownLatch (1 );
@@ -106,13 +125,15 @@ void testConcurrentAccess() throws InterruptedException {
106125
107126 @ Test
108127 void testPersistentLock () {
128+ when (instanceIdProvider .getInstanceId ()).thenReturn (Mono .just (TEST_INSTANCE_ID ));
129+
109130 // First operation acquires lock and doesn't release it
110131 StepVerifier .create (testLockService .operationWithPersistentLock ())
111132 .expectNext ("success" )
112133 .verifyComplete ();
113134
114135 // Verify lock still exists after operation completes
115- StepVerifier .create (redisOperations .hasKey (LOCK_PREFIX + "persistent-lock" ))
136+ StepVerifier .create (redisOperations .hasKey (getLockKey ( "persistent-lock" ) ))
116137 .expectNext (true )
117138 .verifyComplete ();
118139
@@ -121,20 +142,22 @@ void testPersistentLock() {
121142 .verifyComplete (); // Completes empty because lock is still held
122143
123144 // Cleanup: Release lock for other tests
124- StepVerifier .create (testLockService .releaseLock ("persistent-lock" , redisOperations ))
145+ StepVerifier .create (testLockService .releaseLock ("persistent-lock" , redisOperations , TEST_INSTANCE_ID ))
125146 .expectNext (1L )
126147 .verifyComplete ();
127148 }
128149
129150 @ Test
130151 void testPersistentLockExpiration () {
152+ when (instanceIdProvider .getInstanceId ()).thenReturn (Mono .just (TEST_INSTANCE_ID ));
153+
131154 // Execute operation with short-lived lock
132155 StepVerifier .create (Mono .just (testLockService .operationWithShortLivedLock ()))
133156 .expectNext ("success" )
134157 .verifyComplete ();
135158
136159 // Verify lock exists immediately after
137- StepVerifier .create (redisOperations .hasKey (LOCK_PREFIX + "short-lived-lock" ))
160+ StepVerifier .create (redisOperations .hasKey (getLockKey ( "short-lived-lock" ) ))
138161 .expectNext (true )
139162 .verifyComplete ();
140163
@@ -146,50 +169,56 @@ void testPersistentLockExpiration() {
146169 }
147170
148171 // Verify lock has expired
149- StepVerifier .create (redisOperations .hasKey (LOCK_PREFIX + "short-lived-lock" ))
172+ StepVerifier .create (redisOperations .hasKey (getLockKey ( "short-lived-lock" ) ))
150173 .expectNext (false )
151174 .verifyComplete ();
152175 }
153176
154177 @ Test
155178 void testLockReleasedOnBlockingError () {
179+ when (instanceIdProvider .getInstanceId ()).thenReturn (Mono .just (TEST_INSTANCE_ID ));
180+
156181 // Execute operation that throws error
157182 assertThrows (RuntimeException .class , () -> testLockService .blockingMethodWithError ());
158183
159184 // Verify lock is released despite shouldReleaseLock = false
160- StepVerifier .create (redisOperations .hasKey ("lock: error-lock" ))
185+ StepVerifier .create (redisOperations .hasKey (getLockKey ( " error-lock") ))
161186 .expectNext (false )
162187 .verifyComplete ();
163188 }
164189
165190 @ Test
166191 void testLockReleasedOnReactiveError () {
192+ when (instanceIdProvider .getInstanceId ()).thenReturn (Mono .just (TEST_INSTANCE_ID ));
193+
167194 // Execute operation that returns Mono.error
168195 StepVerifier .create (testLockService .reactiveMethodWithError ())
169196 .expectError (RuntimeException .class )
170197 .verify ();
171198
172199 // Verify lock is released despite shouldReleaseLock = false
173- StepVerifier .create (redisOperations .hasKey ("lock: error-lock" ))
200+ StepVerifier .create (redisOperations .hasKey (getLockKey ( " error-lock") ))
174201 .expectNext (false )
175202 .verifyComplete ();
176203 }
177204
178205 @ Test
179206 void testLockReleasedOnErrorAllowsSubsequentExecution () {
207+ when (instanceIdProvider .getInstanceId ()).thenReturn (Mono .just (TEST_INSTANCE_ID ));
208+
180209 // First call throws error
181210 assertThrows (RuntimeException .class , () -> testLockService .blockingMethodWithError ());
182211
183212 // Verify we can acquire the same lock immediately after error
184213 AtomicBoolean lockAcquired = new AtomicBoolean (false );
185- StepVerifier .create (redisOperations .opsForValue ().setIfAbsent ("lock: error-lock" , "test-value" ))
214+ StepVerifier .create (redisOperations .opsForValue ().setIfAbsent (getLockKey ( " error-lock") , "test-value" ))
186215 .consumeNextWith (result -> lockAcquired .set (result ))
187216 .verifyComplete ();
188217
189218 // Should be able to acquire lock after error
190219 assertTrue (lockAcquired .get ());
191220
192221 // Cleanup
193- redisOperations .delete ("lock: error-lock" ).block ();
222+ redisOperations .delete (getLockKey ( " error-lock") ).block ();
194223 }
195224}
0 commit comments