|
24 | 24 | import com.hazelcast.config.Config; |
25 | 25 | import com.hazelcast.core.Hazelcast; |
26 | 26 | import com.hazelcast.core.HazelcastInstance; |
27 | | -import org.junit.Test; |
28 | | -import org.junit.runner.RunWith; |
| 27 | +import com.hazelcast.cp.CPGroupId; |
| 28 | +import com.hazelcast.cp.CPSubsystem; |
| 29 | +import com.hazelcast.cp.lock.FencedLock; |
| 30 | +import org.junit.jupiter.api.Test; |
29 | 31 |
|
30 | 32 | import org.springframework.beans.factory.annotation.Autowired; |
31 | 33 | import org.springframework.context.ApplicationListener; |
32 | 34 | import org.springframework.context.annotation.Bean; |
33 | 35 | import org.springframework.context.annotation.Configuration; |
| 36 | +import org.springframework.integration.leader.Candidate; |
34 | 37 | import org.springframework.integration.leader.Context; |
35 | 38 | import org.springframework.integration.leader.DefaultCandidate; |
36 | 39 | import org.springframework.integration.leader.event.AbstractLeaderEvent; |
37 | 40 | import org.springframework.integration.leader.event.DefaultLeaderEventPublisher; |
38 | 41 | import org.springframework.integration.leader.event.LeaderEventPublisher; |
39 | 42 | import org.springframework.test.annotation.DirtiesContext; |
40 | | -import org.springframework.test.context.ContextConfiguration; |
41 | | -import org.springframework.test.context.junit4.SpringRunner; |
| 43 | +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; |
42 | 44 |
|
43 | 45 | import static org.assertj.core.api.Assertions.assertThat; |
44 | 46 | import static org.mockito.ArgumentMatchers.any; |
| 47 | +import static org.mockito.ArgumentMatchers.anyLong; |
| 48 | +import static org.mockito.ArgumentMatchers.anyString; |
| 49 | +import static org.mockito.BDDMockito.given; |
45 | 50 | import static org.mockito.BDDMockito.willAnswer; |
| 51 | +import static org.mockito.Mockito.mock; |
46 | 52 | import static org.mockito.Mockito.spy; |
| 53 | +import static org.mockito.Mockito.verify; |
47 | 54 |
|
48 | 55 | /** |
49 | 56 | * Tests for hazelcast leader election. |
|
53 | 60 | * @author Dave Syer |
54 | 61 | * @author Artem Bilan |
55 | 62 | * @author Mael Le Guével |
| 63 | + * @author Emil Palm |
56 | 64 | */ |
57 | | -@RunWith(SpringRunner.class) |
58 | | -@ContextConfiguration |
| 65 | +@SpringJUnitConfig |
59 | 66 | @DirtiesContext |
60 | 67 | public class LeaderInitiatorTests { |
61 | 68 |
|
@@ -205,35 +212,82 @@ public void publishOnGranted(Object source, Context context, String role) { |
205 | 212 | initiator.destroy(); |
206 | 213 | } |
207 | 214 |
|
| 215 | + @Test |
| 216 | + public void testRevokeLeadershipCalledWhenLockNotAcquiredButStillLeader() throws Exception { |
| 217 | + // Initialize mocks and objects needed for the revoke leadership when fenced lock is no longer acquired |
| 218 | + HazelcastInstance hazelcastInstance = mock(); |
| 219 | + Candidate candidate = mock(); |
| 220 | + FencedLock fencedLock = mock(); |
| 221 | + LeaderEventPublisher leaderEventPublisher = mock(); |
| 222 | + |
| 223 | + CPSubsystem cpSubsystem = mock(CPSubsystem.class); |
| 224 | + given(candidate.getRole()).willReturn("role"); |
| 225 | + given(hazelcastInstance.getCPSubsystem()).willReturn(cpSubsystem); |
| 226 | + given(cpSubsystem.getLock(anyString())).willReturn(fencedLock); |
| 227 | + given(fencedLock.getGroupId()) |
| 228 | + .willReturn(new CPGroupId() { |
| 229 | + |
| 230 | + @Override |
| 231 | + public String getName() { |
| 232 | + return ""; |
| 233 | + } |
| 234 | + |
| 235 | + @Override |
| 236 | + public long getId() { |
| 237 | + return 0; |
| 238 | + } |
| 239 | + }); |
| 240 | + |
| 241 | + LeaderInitiator leaderInitiator = new LeaderInitiator(hazelcastInstance, candidate); |
| 242 | + leaderInitiator.setLeaderEventPublisher(leaderEventPublisher); |
| 243 | + |
| 244 | + // Simulate that the lock is currently held by this thread |
| 245 | + given(fencedLock.isLockedByCurrentThread()).willReturn(true, false); |
| 246 | + given(fencedLock.tryLock(anyLong(), any(TimeUnit.class))).willReturn(false); // Lock acquisition fails |
| 247 | + |
| 248 | + // Start the LeaderInitiator to trigger the leader election process |
| 249 | + leaderInitiator.start(); |
| 250 | + |
| 251 | + // Simulate the lock acquisition check process |
| 252 | + Thread.sleep(1000); // Give time for the async task to run |
| 253 | + |
| 254 | + // Verify that revokeLeadership was called due to lock not being acquired |
| 255 | + // unlock is part of revokeLeadership |
| 256 | + verify(fencedLock).unlock(); |
| 257 | + // verify revoke event is published |
| 258 | + verify(leaderEventPublisher).publishOnRevoked(any(Object.class), any(Context.class), anyString()); |
| 259 | + |
| 260 | + leaderInitiator.destroy(); |
| 261 | + } |
208 | 262 |
|
209 | 263 | @Configuration |
210 | 264 | public static class TestConfig { |
211 | 265 |
|
212 | 266 | @Bean |
213 | | - public TestCandidate candidate() { |
| 267 | + TestCandidate candidate() { |
214 | 268 | return new TestCandidate(); |
215 | 269 | } |
216 | 270 |
|
217 | 271 | @Bean |
218 | | - public Config hazelcastConfig() { |
| 272 | + Config hazelcastConfig() { |
219 | 273 | Config config = new Config(); |
220 | 274 | config.getCPSubsystemConfig() |
221 | 275 | .setSessionHeartbeatIntervalSeconds(1); |
222 | 276 | return config; |
223 | 277 | } |
224 | 278 |
|
225 | 279 | @Bean(destroyMethod = "shutdown") |
226 | | - public HazelcastInstance hazelcastInstance() { |
| 280 | + HazelcastInstance hazelcastInstance() { |
227 | 281 | return Hazelcast.newHazelcastInstance(hazelcastConfig()); |
228 | 282 | } |
229 | 283 |
|
230 | 284 | @Bean |
231 | | - public LeaderInitiator initiator() { |
| 285 | + LeaderInitiator initiator() { |
232 | 286 | return new LeaderInitiator(hazelcastInstance(), candidate()); |
233 | 287 | } |
234 | 288 |
|
235 | 289 | @Bean |
236 | | - public TestEventListener testEventListener() { |
| 290 | + TestEventListener testEventListener() { |
237 | 291 | return new TestEventListener(); |
238 | 292 | } |
239 | 293 |
|
|
0 commit comments