|
4 | 4 | using System; |
5 | 5 | using System.Collections.Generic; |
6 | 6 | using System.Linq; |
| 7 | +using System.Reflection; |
7 | 8 | using System.Threading; |
8 | 9 | using System.Threading.Tasks; |
9 | 10 | using Azure; |
|
15 | 16 | using Microsoft.Azure.WebJobs.Extensions.Storage.Common.Listeners; |
16 | 17 | using Microsoft.Azure.WebJobs.Extensions.Storage.Common.Tests; |
17 | 18 | using Microsoft.Azure.WebJobs.Extensions.Storage.Queues.Listeners; |
| 19 | +using Microsoft.Azure.WebJobs.Extensions.Storage.Queues.Tests; |
18 | 20 | using Microsoft.Azure.WebJobs.Host; |
19 | 21 | using Microsoft.Azure.WebJobs.Host.Executors; |
20 | 22 | using Microsoft.Azure.WebJobs.Host.Protocols; |
@@ -648,6 +650,136 @@ public async Task ProcessMessageAsync_FunctionInvocationFails() |
648 | 650 | await _listener.ProcessMessageAsync(_queueMessage, TimeSpan.FromMinutes(10), cancellationToken); |
649 | 651 | } |
650 | 652 |
|
| 653 | + [Test] |
| 654 | + public async Task CompleteProcessingMessageAsync_PassesShutdownCancellationToken() |
| 655 | + { |
| 656 | + var systemShutdownCts = new CancellationTokenSource(); |
| 657 | + QueueProcessorOptions options = new QueueProcessorOptions(_mockQueue.Object, _loggerFactory, _queuesOptions); |
| 658 | + var testQueueProcessor = new TestQueueProcessor(options); |
| 659 | + |
| 660 | + _mockTriggerExecutor.Setup(x => x.ExecuteAsync(It.IsAny<QueueMessage>(), It.IsAny<CancellationToken>())) |
| 661 | + .ReturnsAsync(new FunctionResult(true)); |
| 662 | + |
| 663 | + var exceptionHandlerMock = new Mock<IWebJobsExceptionHandler>(); |
| 664 | + |
| 665 | + var listener = new QueueListener( |
| 666 | + _mockQueue.Object, |
| 667 | + null, |
| 668 | + _mockTriggerExecutor.Object, |
| 669 | + _mockExceptionDispatcher.Object, |
| 670 | + _loggerFactory, |
| 671 | + null, |
| 672 | + _queuesOptions, |
| 673 | + testQueueProcessor, |
| 674 | + new FunctionDescriptor { Id = TestFunctionId }, |
| 675 | + null, |
| 676 | + drainModeManager: null); |
| 677 | + |
| 678 | + SetCancellationToken(listener, systemShutdownCts, "_shutdownCancellationTokenSource"); |
| 679 | + |
| 680 | + // Act |
| 681 | + await listener.ProcessMessageAsync(_queueMessage, TimeSpan.FromMinutes(2), CancellationToken.None); |
| 682 | + |
| 683 | + Assert.AreEqual(systemShutdownCts.Token, testQueueProcessor.CapturedDeleteToken); |
| 684 | + } |
| 685 | + |
| 686 | + [Test] |
| 687 | + public async Task StopAsync_WhenDrainModeNotEnabled_ExecutionCancellationTokenIsCanceled() |
| 688 | + { |
| 689 | + var drainModeManagerMock = new Mock<IDrainModeManager>(); |
| 690 | + drainModeManagerMock.Setup(d => d.IsDrainModeEnabled).Returns(false); |
| 691 | + var executionCancellationTokenSource = new CancellationTokenSource(); |
| 692 | + |
| 693 | + var listener = new QueueListener( |
| 694 | + _mockQueue.Object, |
| 695 | + null, |
| 696 | + _mockTriggerExecutor.Object, |
| 697 | + _mockExceptionDispatcher.Object, |
| 698 | + _loggerFactory, |
| 699 | + null, |
| 700 | + _queuesOptions, |
| 701 | + _mockQueueProcessor.Object, |
| 702 | + new FunctionDescriptor { Id = TestFunctionId }, |
| 703 | + null, |
| 704 | + drainModeManager: drainModeManagerMock.Object); |
| 705 | + |
| 706 | + SetCancellationToken(listener, executionCancellationTokenSource, "_executionCancellationTokenSource"); |
| 707 | + |
| 708 | + // Act |
| 709 | + await listener.StartAsync(CancellationToken.None); |
| 710 | + await listener.StopAsync(CancellationToken.None); |
| 711 | + |
| 712 | + // Assert |
| 713 | + Assert.IsTrue(executionCancellationTokenSource.Token.IsCancellationRequested, "Execution token should be canceled when drain mode is not enabled."); |
| 714 | + } |
| 715 | + |
| 716 | + [Test] |
| 717 | + public async Task StopAsync_WhenDrainModeEnabled_ExecutionCancellationTokenIsNotCanceled() |
| 718 | + { |
| 719 | + // Arrange |
| 720 | + var drainModeManagerMock = new Mock<IDrainModeManager>(); |
| 721 | + drainModeManagerMock.Setup(d => d.IsDrainModeEnabled).Returns(true); |
| 722 | + var executionCancellationTokenSource = new CancellationTokenSource(); |
| 723 | + |
| 724 | + var listener = new QueueListener( |
| 725 | + _mockQueue.Object, |
| 726 | + null, |
| 727 | + _mockTriggerExecutor.Object, |
| 728 | + _mockExceptionDispatcher.Object, |
| 729 | + _loggerFactory, |
| 730 | + null, |
| 731 | + _queuesOptions, |
| 732 | + _mockQueueProcessor.Object, |
| 733 | + new FunctionDescriptor { Id = TestFunctionId }, |
| 734 | + null, |
| 735 | + drainModeManager: drainModeManagerMock.Object); |
| 736 | + |
| 737 | + SetCancellationToken(listener, executionCancellationTokenSource, "_executionCancellationTokenSource"); |
| 738 | + |
| 739 | + // Act |
| 740 | + await listener.StartAsync(CancellationToken.None); |
| 741 | + await listener.StopAsync(CancellationToken.None); |
| 742 | + |
| 743 | + // Assert |
| 744 | + Assert.IsFalse(executionCancellationTokenSource.Token.IsCancellationRequested, "Execution token should not be canceled when drain mode is enabled."); |
| 745 | + } |
| 746 | + |
| 747 | + [Test] |
| 748 | + public async Task StopAsync_ActivatesCancellation_WhenDrainModeManagerNull() |
| 749 | + { |
| 750 | + // Arrange |
| 751 | + var executionCancellationTokenSource = new CancellationTokenSource(); |
| 752 | + |
| 753 | + var listener = new QueueListener( |
| 754 | + _mockQueue.Object, |
| 755 | + null, |
| 756 | + _mockTriggerExecutor.Object, |
| 757 | + _mockExceptionDispatcher.Object, |
| 758 | + _loggerFactory, |
| 759 | + null, |
| 760 | + _queuesOptions, |
| 761 | + _mockQueueProcessor.Object, |
| 762 | + new FunctionDescriptor { Id = TestFunctionId }, |
| 763 | + null, |
| 764 | + drainModeManager: null); |
| 765 | + |
| 766 | + SetCancellationToken(listener, executionCancellationTokenSource, "_executionCancellationTokenSource"); |
| 767 | + |
| 768 | + // Act |
| 769 | + await listener.StartAsync(CancellationToken.None); |
| 770 | + |
| 771 | + await listener.StopAsync(CancellationToken.None); |
| 772 | + |
| 773 | + // Assert |
| 774 | + Assert.IsTrue(executionCancellationTokenSource.Token.IsCancellationRequested, "Execution token should be canceled when drain mode manager is null."); |
| 775 | + } |
| 776 | + |
| 777 | + private static void SetCancellationToken(QueueListener listener, CancellationTokenSource cts, string fieldName) |
| 778 | + { |
| 779 | + var shutdownTokenField = typeof(QueueListener).GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance); |
| 780 | + shutdownTokenField.SetValue(listener, cts); |
| 781 | + } |
| 782 | + |
651 | 783 | [Test] |
652 | 784 | public void Get_TargetScale_IsNotNull() |
653 | 785 | { |
@@ -676,6 +808,7 @@ public void Get_TargetScale_IsNotNull() |
676 | 808 | var result = localListener.GetTargetScaler(); |
677 | 809 | Assert.IsNotNull(result); |
678 | 810 | } |
| 811 | + |
679 | 812 | public class TestFixture : IDisposable |
680 | 813 | { |
681 | 814 | private const string TestQueuePrefix = "queuelistenertests"; |
|
0 commit comments