public static HSMessageWrapper FromCloudQueueMessage(CloudQueueMessage msg, CloudQueue originQ) { if (msg == null) { return(null); } HSMessageWrapper result = new HSMessageWrapper(msg.AsBytes); FieldInfo[] fields_of_class = msg.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); foreach (FieldInfo fi in fields_of_class) { fi.SetValue(result, fi.GetValue(msg)); } result._origin = originQ; return(result); }
public void Poison_message_is_rejected() { // Arrange var onMessageInvokeCount = 0; var onQueueEmptyInvokeCount = 0; var onErrorInvokeCount = 0; var isRejected = false; var retries = 3; var lockObject = new Object(); var cloudMessage = new CloudQueueMessage("Message"); var queueName = "myqueue"; var mockQueue = GetMockQueue(queueName); var mockQueueClient = GetMockQueueClient(mockQueue); var mockBlobContainer = GetMockBlobContainer(); var mockBlobClient = GetMockBlobClient(mockBlobContainer); var mockStorageAccount = GetMockStorageAccount(mockBlobClient, mockQueueClient); mockQueue.Setup(q => q.GetMessagesAsync(It.IsAny <int>(), It.IsAny <TimeSpan?>(), It.IsAny <QueueRequestOptions>(), It.IsAny <OperationContext>(), It.IsAny <CancellationToken>())).ReturnsAsync((int messageCount, TimeSpan? visibilityTimeout, QueueRequestOptions options, OperationContext operationContext, CancellationToken cancellationToken) => { if (cloudMessage != null) { lock (lockObject) { if (cloudMessage != null) { // DequeueCount is a private property. Therefore we must use reflection to change its value var dequeueCountProperty = cloudMessage.GetType().GetProperty("DequeueCount"); dequeueCountProperty.SetValue(cloudMessage, retries + 1); // intentionally set 'DequeueCount' to a value exceeding maxRetries to simulate a poison message return(new[] { cloudMessage }); } } } return(Enumerable.Empty <CloudQueueMessage>()); }); mockQueue.Setup(q => q.DeleteMessageAsync(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <QueueRequestOptions>(), It.IsAny <OperationContext>(), It.IsAny <CancellationToken>())).Returns((string messageId, string popReceipt, QueueRequestOptions options, OperationContext operationContext, CancellationToken cancellationToken) => { lock (lockObject) { cloudMessage = null; } return(Task.FromResult(true)); }); var messagePump = new AsyncMessagePump("myqueue", mockStorageAccount.Object, 1, TimeSpan.FromMinutes(1), retries); messagePump.OnMessage = (message, cancellationToken) => { Interlocked.Increment(ref onMessageInvokeCount); throw new Exception("An error occured when attempting to process the message"); }; messagePump.OnQueueEmpty = cancellationToken => { Interlocked.Increment(ref onQueueEmptyInvokeCount); messagePump.Stop(); }; messagePump.OnError = (message, exception, isPoison) => { Interlocked.Increment(ref onErrorInvokeCount); if (isPoison) { lock (lockObject) { isRejected = true; cloudMessage = null; } } }; // Act messagePump.Start(); // Assert onMessageInvokeCount.ShouldBe(1); onQueueEmptyInvokeCount.ShouldBeGreaterThan(0); onErrorInvokeCount.ShouldBe(1); isRejected.ShouldBeTrue(); mockQueue.Verify(q => q.GetMessagesAsync(It.IsAny <int>(), It.IsAny <TimeSpan?>(), It.IsAny <QueueRequestOptions>(), It.IsAny <OperationContext>(), It.IsAny <CancellationToken>()), Times.AtLeast(2)); mockQueue.Verify(q => q.DeleteMessageAsync(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <QueueRequestOptions>(), It.IsAny <OperationContext>(), It.IsAny <CancellationToken>()), Times.Exactly(1)); }
public void Poison_message_is_rejected() { // Arrange var onMessageInvokeCount = 0; var onQueueEmptyInvokeCount = 0; var onErrorInvokeCount = 0; var isRejected = false; var retries = 3; var lockObject = new Object(); var cloudMessage = new CloudQueueMessage("Message"); var mockStorageUri = new Uri("http://bogus/myaccount"); var mockQueue = new Mock <CloudQueue>(MockBehavior.Strict, mockStorageUri); mockQueue.Setup(q => q.GetMessageAsync(It.IsAny <TimeSpan?>(), It.IsAny <QueueRequestOptions>(), It.IsAny <OperationContext>(), It.IsAny <CancellationToken>())).Returns((TimeSpan? visibilityTimeout, QueueRequestOptions options, OperationContext operationContext, CancellationToken cancellationToken) => { if (cloudMessage == null) { return(null); } lock (lockObject) { if (cloudMessage != null) { // DequeueCount is a private property. Therefore we must use reflection to change its value var t = cloudMessage.GetType(); t.InvokeMember("DequeueCount", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.SetProperty | BindingFlags.Instance, null, cloudMessage, new object[] { cloudMessage.DequeueCount + 1 }); } return(Task.FromResult(cloudMessage)); } }); mockQueue.Setup(q => q.DeleteMessageAsync(It.IsAny <CloudQueueMessage>())).Returns((CloudQueueMessage message) => { lock (lockObject) { cloudMessage = null; } return(Task.FromResult(true)); }); var messagePump = new AsyncMessagePump(mockQueue.Object, 1, 1, TimeSpan.FromMinutes(1), retries); messagePump.OnMessage = (message, cancellationToken) => { Interlocked.Increment(ref onMessageInvokeCount); throw new Exception("An error occured when attempting to process the message"); }; messagePump.OnQueueEmpty = cancellationToken => { Interlocked.Increment(ref onQueueEmptyInvokeCount); // Run the 'OnStop' on a different thread so we don't block it Task.Run(() => { messagePump.Stop(); }).ConfigureAwait(false); }; messagePump.OnError = (message, exception, isPoison) => { Interlocked.Increment(ref onErrorInvokeCount); if (isPoison) { lock (lockObject) { isRejected = true; cloudMessage = null; } } }; // Act messagePump.Start(); // Assert Assert.AreEqual(retries + 1, onMessageInvokeCount); Assert.IsTrue(onQueueEmptyInvokeCount > 0); Assert.AreEqual(retries + 1, onErrorInvokeCount); Assert.IsTrue(isRejected); mockQueue.Verify(q => q.GetMessageAsync(It.IsAny <TimeSpan?>(), It.IsAny <QueueRequestOptions>(), It.IsAny <OperationContext>(), It.IsAny <CancellationToken>()), Times.AtLeast(retries)); }