|
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