|
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 |
|
@@ -204,34 +211,82 @@ public void publishOnGranted(Object source, Context context, String role) { |
204 | 211 | initiator.destroy(); |
205 | 212 | } |
206 | 213 |
|
| 214 | + @Test |
| 215 | + public void testRevokeLeadershipCalledWhenLockNotAcquiredButStillLeader() throws Exception { |
| 216 | + // Initialize mocks and objects needed for the revoke leadership when fenced lock is no longer acquired |
| 217 | + HazelcastInstance hazelcastInstance = mock(); |
| 218 | + Candidate candidate = mock(); |
| 219 | + FencedLock fencedLock = mock(); |
| 220 | + LeaderEventPublisher leaderEventPublisher = mock(); |
| 221 | + |
| 222 | + CPSubsystem cpSubsystem = mock(CPSubsystem.class); |
| 223 | + given(candidate.getRole()).willReturn("role"); |
| 224 | + given(hazelcastInstance.getCPSubsystem()).willReturn(cpSubsystem); |
| 225 | + given(cpSubsystem.getLock(anyString())).willReturn(fencedLock); |
| 226 | + given(fencedLock.getGroupId()) |
| 227 | + .willReturn(new CPGroupId() { |
| 228 | + |
| 229 | + @Override |
| 230 | + public String getName() { |
| 231 | + return ""; |
| 232 | + } |
| 233 | + |
| 234 | + @Override |
| 235 | + public long getId() { |
| 236 | + return 0; |
| 237 | + } |
| 238 | + }); |
| 239 | + |
| 240 | + LeaderInitiator leaderInitiator = new LeaderInitiator(hazelcastInstance, candidate); |
| 241 | + leaderInitiator.setLeaderEventPublisher(leaderEventPublisher); |
| 242 | + |
| 243 | + // Simulate that the lock is currently held by this thread |
| 244 | + given(fencedLock.isLockedByCurrentThread()).willReturn(true, false); |
| 245 | + given(fencedLock.tryLock(anyLong(), any(TimeUnit.class))).willReturn(false); // Lock acquisition fails |
| 246 | + |
| 247 | + // Start the LeaderInitiator to trigger the leader election process |
| 248 | + leaderInitiator.start(); |
| 249 | + |
| 250 | + // Simulate the lock acquisition check process |
| 251 | + Thread.sleep(1000); // Give time for the async task to run |
| 252 | + |
| 253 | + // Verify that revokeLeadership was called due to lock not being acquired |
| 254 | + // unlock is part of revokeLeadership |
| 255 | + verify(fencedLock).unlock(); |
| 256 | + // verify revoke event is published |
| 257 | + verify(leaderEventPublisher).publishOnRevoked(any(Object.class), any(Context.class), anyString()); |
| 258 | + |
| 259 | + leaderInitiator.destroy(); |
| 260 | + } |
| 261 | + |
207 | 262 | @Configuration |
208 | 263 | public static class TestConfig { |
209 | 264 |
|
210 | 265 | @Bean |
211 | | - public TestCandidate candidate() { |
| 266 | + TestCandidate candidate() { |
212 | 267 | return new TestCandidate(); |
213 | 268 | } |
214 | 269 |
|
215 | 270 | @Bean |
216 | | - public Config hazelcastConfig() { |
| 271 | + Config hazelcastConfig() { |
217 | 272 | Config config = new Config(); |
218 | 273 | config.getCPSubsystemConfig() |
219 | 274 | .setSessionHeartbeatIntervalSeconds(1); |
220 | 275 | return config; |
221 | 276 | } |
222 | 277 |
|
223 | 278 | @Bean(destroyMethod = "shutdown") |
224 | | - public HazelcastInstance hazelcastInstance() { |
| 279 | + HazelcastInstance hazelcastInstance() { |
225 | 280 | return Hazelcast.newHazelcastInstance(hazelcastConfig()); |
226 | 281 | } |
227 | 282 |
|
228 | 283 | @Bean |
229 | | - public LeaderInitiator initiator() { |
| 284 | + LeaderInitiator initiator() { |
230 | 285 | return new LeaderInitiator(hazelcastInstance(), candidate()); |
231 | 286 | } |
232 | 287 |
|
233 | 288 | @Bean |
234 | | - public TestEventListener testEventListener() { |
| 289 | + TestEventListener testEventListener() { |
235 | 290 | return new TestEventListener(); |
236 | 291 | } |
237 | 292 |
|
|
0 commit comments