|
16 | 16 | */ |
17 | 17 | package org.apache.activemq.artemis.core.postoffice.impl; |
18 | 18 |
|
| 19 | +import java.util.HashMap; |
| 20 | +import java.util.List; |
| 21 | +import java.util.Map; |
| 22 | + |
19 | 23 | import org.apache.activemq.artemis.api.core.Message; |
| 24 | +import org.apache.activemq.artemis.api.core.SimpleString; |
| 25 | +import org.apache.activemq.artemis.core.paging.PagingManager; |
| 26 | +import org.apache.activemq.artemis.core.paging.PagingStore; |
| 27 | +import org.apache.activemq.artemis.core.persistence.StorageManager; |
| 28 | +import org.apache.activemq.artemis.core.server.Queue; |
| 29 | +import org.apache.activemq.artemis.core.server.RouteContextList; |
| 30 | +import org.apache.activemq.artemis.core.server.RoutingContext; |
| 31 | +import org.apache.activemq.artemis.core.server.mirror.MirrorController; |
20 | 32 | import org.apache.activemq.artemis.core.settings.impl.AddressSettings; |
| 33 | +import org.apache.activemq.artemis.core.transaction.Transaction; |
21 | 34 | import org.junit.jupiter.api.Test; |
22 | 35 | import org.mockito.ArgumentCaptor; |
23 | 36 | import org.mockito.Mockito; |
24 | 37 |
|
25 | 38 | import static org.junit.jupiter.api.Assertions.assertNotNull; |
26 | 39 | import static org.junit.jupiter.api.Assertions.assertTrue; |
| 40 | +import static org.mockito.ArgumentMatchers.any; |
| 41 | +import static org.mockito.ArgumentMatchers.eq; |
| 42 | +import static org.mockito.Mockito.mock; |
| 43 | +import static org.mockito.Mockito.never; |
| 44 | +import static org.mockito.Mockito.verify; |
| 45 | +import static org.mockito.Mockito.when; |
27 | 46 |
|
28 | 47 | public class PostOfficeImplTest { |
29 | 48 |
|
@@ -305,4 +324,101 @@ public void testPrecedencExpiryDelayOverMinExpiryDelay() { |
305 | 324 |
|
306 | 325 | assertExpirationSetAsExpected(expectedExpirationLow, expectedExpirationHigh, actualExpirationSet); |
307 | 326 | } |
| 327 | + |
| 328 | + /** |
| 329 | + * Tests that mirrorController.sendMessage is NOT called when a message is dropped |
| 330 | + * during storageManager.addToPage() call in processRoute(). |
| 331 | + * This test verifies the fix for ARTEMIS-6146 where dropped messages should not be mirrored. |
| 332 | + * |
| 333 | + * WITHOUT the fix: This test will FAIL because mirrorController.sendMessage will be called |
| 334 | + * WITH the fix: This test will PASS because mirrorController.sendMessage will NOT be called |
| 335 | + */ |
| 336 | + @Test |
| 337 | + public void testProcessRouteDoesNotCallMirrorControllerWhenMessageDropped() throws Exception { |
| 338 | + // Setup mocks |
| 339 | + StorageManager storageManager = mock(StorageManager.class); |
| 340 | + PagingManager pagingManager = mock(PagingManager.class); |
| 341 | + PagingStore pagingStore = mock(PagingStore.class); |
| 342 | + MirrorController mirrorController = mock(MirrorController.class); |
| 343 | + |
| 344 | + // Create a spy of PostOfficeImpl to call the real processRoute method |
| 345 | + PostOfficeImpl postOffice = Mockito.mock(PostOfficeImpl.class); |
| 346 | + |
| 347 | + // Make processRoute call the real implementation |
| 348 | + Mockito.doCallRealMethod().when(postOffice).processRoute(any(Message.class), any(RoutingContext.class), Mockito.anyBoolean()); |
| 349 | + |
| 350 | + // Mock the getMirrorControlSource to return our mock controller |
| 351 | + when(postOffice.getMirrorControlSource()).thenReturn(mirrorController); |
| 352 | + |
| 353 | + SimpleString address = SimpleString.of("testAddress"); |
| 354 | + |
| 355 | + // Create a message with dropped tracking |
| 356 | + Message message = mock(Message.class); |
| 357 | + final boolean[] dropped = {false}; |
| 358 | + |
| 359 | + when(message.getAddressSimpleString()).thenReturn(address); |
| 360 | + when(message.hasScheduledDeliveryTime()).thenReturn(false); |
| 361 | + when(message.isDurable()).thenReturn(false); |
| 362 | + when(message.isDropped()).thenAnswer(invocation -> dropped[0]); |
| 363 | + when(message.drop()).thenAnswer(invocation -> { |
| 364 | + dropped[0] = true; |
| 365 | + return message; |
| 366 | + }); |
| 367 | + |
| 368 | + // Create route context |
| 369 | + RoutingContext context = mock(RoutingContext.class); |
| 370 | + RouteContextList routeContextList = mock(RouteContextList.class); |
| 371 | + Map<SimpleString, RouteContextList> contextListing = new HashMap<>(); |
| 372 | + contextListing.put(address, routeContextList); |
| 373 | + |
| 374 | + when(context.getContexListing()).thenReturn(contextListing); |
| 375 | + when(context.getTransaction()).thenReturn(null); |
| 376 | + when(context.isMirrorDisabled()).thenReturn(false); |
| 377 | + |
| 378 | + // Configure route context list to return empty lists (message was paged) |
| 379 | + when(routeContextList.getDurableQueues()).thenReturn(List.of()); |
| 380 | + when(routeContextList.getNonDurableQueues()).thenReturn(List.of()); |
| 381 | + |
| 382 | + // Configure paging store |
| 383 | + when(pagingStore.getAddress()).thenReturn(address); |
| 384 | + when(pagingManager.getPageStore(address)).thenReturn(pagingStore); |
| 385 | + |
| 386 | + // Critical: When addToPage is called, it will drop the message |
| 387 | + when(storageManager.addToPage(eq(pagingStore), eq(message), any(), eq(routeContextList))) |
| 388 | + .thenAnswer(invocation -> { |
| 389 | + // Simulate message.drop() being called inside addToPage |
| 390 | + message.drop(); |
| 391 | + return true; // Returns true indicating the message was added to page |
| 392 | + }); |
| 393 | + |
| 394 | + // Mock transaction creation for mirror controller |
| 395 | + Transaction mockTx = mock(Transaction.class); |
| 396 | + when(mockTx.setAsync(true)).thenReturn(mockTx); |
| 397 | + when(storageManager.generateID()).thenReturn(1L); |
| 398 | + |
| 399 | + // We need to inject the dependencies into the PostOfficeImpl mock |
| 400 | + // Since we can't easily do that, we'll use reflection to set the fields |
| 401 | + java.lang.reflect.Field storageManagerField = PostOfficeImpl.class.getDeclaredField("storageManager"); |
| 402 | + storageManagerField.setAccessible(true); |
| 403 | + storageManagerField.set(postOffice, storageManager); |
| 404 | + |
| 405 | + java.lang.reflect.Field pagingManagerField = PostOfficeImpl.class.getDeclaredField("pagingManager"); |
| 406 | + pagingManagerField.setAccessible(true); |
| 407 | + pagingManagerField.set(postOffice, pagingManager); |
| 408 | + |
| 409 | + java.lang.reflect.Field mirrorControllerField = PostOfficeImpl.class.getDeclaredField("mirrorControllerSource"); |
| 410 | + mirrorControllerField.setAccessible(true); |
| 411 | + mirrorControllerField.set(postOffice, mirrorController); |
| 412 | + |
| 413 | + // NOW call the REAL processRoute method |
| 414 | + postOffice.processRoute(message, context, false); |
| 415 | + |
| 416 | + // Verify the message was dropped |
| 417 | + assertTrue(message.isDropped(), "Message should be marked as dropped after addToPage"); |
| 418 | + |
| 419 | + // This is the KEY assertion: |
| 420 | + // WITHOUT the fix (isDropped check missing): mirrorController.sendMessage WILL be called -> TEST FAILS |
| 421 | + // WITH the fix (isDropped check present): mirrorController.sendMessage will NOT be called -> TEST PASSES |
| 422 | + verify(mirrorController, never()).sendMessage(any(), any(), any()); |
| 423 | + } |
308 | 424 | } |
0 commit comments