public Task Fail_WhenReturn() { #region Arrange const ulong taskId = 4; var modelMock = new Mock <IModel>(); modelMock.Setup(m => m.NextPublishSeqNo) .Returns(taskId); var propertiesMock = new FakeOptions { Headers = new Dictionary <string, object>() }; var confirmableChannel = new PublishConfirmableChannel(modelMock.Object); #endregion Arrange #region Act var publishTask = confirmableChannel.BasicPublishAsync( string.Empty, string.Empty, true, propertiesMock, ReadOnlyMemory <byte> .Empty ); Assert.IsTrue(propertiesMock.Headers.ContainsKey("publishTag"), "publishTag header not set!"); Assert.IsTrue(confirmableChannel.HasTaskWith(taskId), "PublishTaskInfo not added!"); // emulate rabbitmq internal encoding. propertiesMock.Headers["publishTag"] = Encoding.UTF8.GetBytes(taskId.ToString()); modelMock.Raise(m => m.BasicReturn += null, new BasicReturnEventArgs { ReplyCode = 500, ReplyText = "Something went wrong!", BasicProperties = propertiesMock }); #endregion Act #region Assert return(publishTask.ContinueWith(t => { Assert.IsTrue(t.IsFaulted, "Task should be faulted"); Assert.AreEqual("The message was returned by RabbitMQ: 500-Something went wrong!", t.Exception?.InnerException?.Message, "Error message does not equal!"); Assert.IsFalse(confirmableChannel.HasTaskWith(taskId), "PublishTaskInfo not removed!"); })); #endregion Assert }
public Task Fail_WhenNotConfirmed() { #region Arrange const int taskId = 3; var modelMock = new Mock <IModel>(); modelMock.Setup(m => m.NextPublishSeqNo) .Returns(taskId); var propertiesMock = new FakeOptions { Headers = new Dictionary <string, object>() }; var confirmableChannel = new PublishConfirmableChannel(modelMock.Object); #endregion Arrange #region Act var publishTask = confirmableChannel.BasicPublishAsync( string.Empty, string.Empty, true, propertiesMock, ReadOnlyMemory <byte> .Empty ); Assert.IsTrue(confirmableChannel.HasTaskWith(taskId), "PublishTaskInfo not added!"); modelMock.Raise(m => m.ModelShutdown += null, new ShutdownEventArgs( ShutdownInitiator.Application, 500, "REASON_FROM_RABBIT" ) ); #endregion Act #region Assert return(publishTask.ContinueWith(t => { Assert.IsTrue(t.IsFaulted, "Task should be faulted."); Assert.AreEqual( "The message was not confirmed by RabbitMQ within the specified period. REASON_FROM_RABBIT", t.Exception?.InnerException?.Message ); Assert.IsFalse(confirmableChannel.HasTaskWith(taskId)); })); #endregion Assert }
public Task Fail_WhenNack() { #region Arrange const int taskId = 2; var modelMock = new Mock <IModel>(); modelMock.Setup(m => m.NextPublishSeqNo) .Returns(taskId); var propertiesMock = new FakeOptions { Headers = new Dictionary <string, object>() }; var confirmableChannel = new PublishConfirmableChannel(modelMock.Object); #endregion Arrange #region Act var publishTask = confirmableChannel.BasicPublishAsync( string.Empty, string.Empty, true, propertiesMock, ReadOnlyMemory <byte> .Empty ); Assert.IsTrue(confirmableChannel.HasTaskWith(taskId), "PublishTaskInfo not added!"); modelMock.Raise(m => m.BasicNacks += null, new BasicNackEventArgs { DeliveryTag = taskId, Multiple = false }); #endregion Act #region Assert return(publishTask.ContinueWith(t => { Assert.IsTrue(t.IsFaulted); Assert.AreEqual("The message was not acknowledged by RabbitMQ", t.Exception?.InnerException?.Message); Assert.IsFalse(confirmableChannel.HasTaskWith(taskId)); })); #endregion Assert }
public Task Success_WhenAck() { #region Arrange const int taskId = 1; var modelMock = new Mock <IModel>(); modelMock.Setup(m => m.NextPublishSeqNo) .Returns(taskId); var propertiesMock = new FakeOptions { Headers = new Dictionary <string, object>() }; var confirmableChannel = new PublishConfirmableChannel(modelMock.Object); #endregion Arrange #region Act var publishTask = confirmableChannel.BasicPublishAsync( string.Empty, string.Empty, true, propertiesMock, ReadOnlyMemory <byte> .Empty ); Assert.IsTrue(confirmableChannel.HasTaskWith(taskId), "PublishTaskInfo not added!"); modelMock.Raise(m => m.BasicAcks += null, new BasicAckEventArgs { DeliveryTag = taskId, Multiple = false }); #endregion Act #region Assert return(publishTask.ContinueWith(t => { Assert.IsTrue(t.IsCompletedSuccessfully, "Task should complete successful"); Assert.IsFalse(confirmableChannel.HasTaskWith(taskId), "PublishTaskInfo not removed!"); })); #endregion Assert }
public Task Fail_WhenTimedOut() { #region Arrange const int taskId = 5; var modelMock = new Mock <IModel>(); modelMock.Setup(m => m.NextPublishSeqNo) .Returns(taskId); var propertiesMock = new FakeOptions { Headers = new Dictionary <string, object>() }; // timeout confirm immediatelly - we can add task to tracker, but timeout cancel it fast var confirmTimeout = TimeSpan.FromMilliseconds(10); var confirmableChannel = new PublishConfirmableChannel(modelMock.Object, confirmTimeout); #endregion Arrange #region Act var publishTask = confirmableChannel.BasicPublishAsync( string.Empty, string.Empty, true, propertiesMock, ReadOnlyMemory <byte> .Empty, 0 ); Assert.IsTrue(confirmableChannel.HasTaskWith(taskId), "PublishTaskInfo not added!"); #endregion Act #region Assert return(publishTask.ContinueWith(t => { Assert.IsTrue(t.IsCanceled, "Task should be cancelled."); Assert.IsNull(t.Exception); Assert.IsFalse(confirmableChannel.HasTaskWith(taskId)); })); #endregion Assert }
/// <summary> /// Подписаться на сообщения. /// </summary> /// <param name="messageHandler">Обработчик сообщений.</param> /// <param name="settings">Настройки очереди.</param> /// <param name="onUnsubscribed"> /// Функция обратного вызова, для отслеживания ситуации, когда остановлено потребление сообщений. /// </param> /// <returns>Канал, на котором работает подписчик.</returns> /// <typeparam name="TMessage">Тип сообщения.</typeparam> public async Task <IModel> SubscribeAsync <TMessage>( AcknowledgableMessageHandler <TMessage> messageHandler, SubscriberSettings settings, Action <bool, string>?onUnsubscribed = null ) where TMessage : class, IMessage { var channel = await BindAsync <TMessage>(settings); if (settings.UsePublisherConfirms) { // создаем канал с подтверждениями публикаций сообщений channel = new PublishConfirmableChannel( channel, TimeSpan.FromSeconds(10), _logger ); } channel.BasicQos(0, settings.ScalingSettings.MessagesPerConsumer, false); // если стоит лимит на канал, то делаем глобальным. if (settings.ScalingSettings.MessagesPerChannel > 0) { channel.BasicQos(0, settings.ScalingSettings.MessagesPerChannel, true); } var queueName = _namingConvention.QueueNamingConvention(typeof(TMessage), settings); for (var i = 0; i < settings.ScalingSettings.ConsumersPerChannel; i++) { var consumer = GetBasicConsumer(channel, settings, queueName, messageHandler, onUnsubscribed); channel.BasicConsume( queue: queueName, autoAck: settings.AutoAck, exclusive: settings.Exclusive, consumer: consumer, consumerTag: _namingConvention.ConsumerTagNamingConvention(settings, channel.ChannelNumber, i) ); } channel.CallbackException += (sender, ea) => { _logger.RabbitHandlerRestarted(ea.Exception); onUnsubscribed?.Invoke(false, ea.Exception?.Message ?? "Channel callback exception."); }; channel.ModelShutdown += (sender, ea) => { // если отключаем с админки if (ea.Initiator == ShutdownInitiator.Peer && (ea.ReplyText.Contains("stop") || ea.ReplyText.Contains("Closed via management plugin"))) { _logger.RabbitHandlerForceStopped(ea); onUnsubscribed?.Invoke(true, ea.ToString()); // force } else { _logger.RabbitHandlerRestartedAfterReconnect(ea); onUnsubscribed?.Invoke(false, ea.ToString()); } }; return(channel); }