|
33 | 33 | import static org.hamcrest.Matchers.instanceOf; |
34 | 34 | import static org.hamcrest.Matchers.is; |
35 | 35 | import static org.hamcrest.Matchers.startsWith; |
| 36 | +import static org.junit.jupiter.api.Assertions.assertFalse; |
36 | 37 | import static org.junit.jupiter.api.Assertions.assertThrows; |
37 | 38 | import static org.mockito.ArgumentMatchers.eq; |
38 | 39 | import static org.mockito.BDDMockito.given; |
| 40 | +import static org.mockito.Mockito.doThrow; |
39 | 41 | import static org.mockito.Mockito.atLeast; |
40 | 42 | import static org.mockito.Mockito.times; |
41 | 43 | import static org.mockito.Mockito.verify; |
@@ -290,4 +292,34 @@ void test_shouldInitStream_withEmptyS3PathPrefix() { |
290 | 292 | void test_shouldInitStream_withNullS3PathPrefix() { |
291 | 293 | assertThrows(IllegalArgumentException.class, () -> new LeaderScheduler(coordinator, mongoDBSourceConfig, null, Duration.ofMillis(100))); |
292 | 294 | } |
| 295 | + |
| 296 | + @Test |
| 297 | + void test_saveProgressStateThrowsException_schedulerContinuesAndReacquiresLeader() { |
| 298 | + given(mongoDBSourceConfig.getCollections()).willReturn(List.of()); |
| 299 | + leaderScheduler = new LeaderScheduler(coordinator, mongoDBSourceConfig, TEST_S3_PATH_PREFIX, Duration.ofMillis(100)); |
| 300 | + leaderPartition = new LeaderPartition(); |
| 301 | + given(coordinator.acquireAvailablePartition(LeaderPartition.PARTITION_TYPE)).willReturn(Optional.of(leaderPartition)); |
| 302 | + doThrow(new RuntimeException("saveProgressState failed")) |
| 303 | + .when(coordinator).saveProgressStateForPartition(eq(leaderPartition), eq(Duration.ofMinutes(DEFAULT_EXTEND_LEASE_MINUTES))); |
| 304 | + |
| 305 | + final ExecutorService executorService = Executors.newSingleThreadExecutor(); |
| 306 | + final Future<?> future = executorService.submit(() -> leaderScheduler.run()); |
| 307 | + |
| 308 | + // Verify saveProgressStateForPartition was called (and threw internally) |
| 309 | + await() |
| 310 | + .atMost(Duration.ofSeconds(2)) |
| 311 | + .untilAsserted(() -> verify(coordinator, atLeast(1)).saveProgressStateForPartition(leaderPartition, Duration.ofMinutes(DEFAULT_EXTEND_LEASE_MINUTES))); |
| 312 | + |
| 313 | + // Verify the scheduler reacquires the leader after failure — |
| 314 | + // leaderPartition was reset to null, so acquireAvailablePartition is called again |
| 315 | + await() |
| 316 | + .atMost(Duration.ofSeconds(2)) |
| 317 | + .untilAsserted(() -> verify(coordinator, atLeast(2)).acquireAvailablePartition(LeaderPartition.PARTITION_TYPE)); |
| 318 | + |
| 319 | + // Scheduler is still running — didn't crash |
| 320 | + assertFalse(future.isDone()); |
| 321 | + |
| 322 | + future.cancel(true); |
| 323 | + executorService.shutdownNow(); |
| 324 | + } |
293 | 325 | } |
0 commit comments