/// <summary>
        /// Creates an initializes a <see cref="IMessageQueueingService"/>
        /// based on the provided <paramref name="configuration"/>
        /// </summary>
        /// <param name="configuration">The journaling configuration
        /// element</param>
        /// <returns>Returns a task whose result is an initialized
        /// <see cref="IMessageQueueingService"/></returns>
        public async Task <IMessageQueueingService> CreateMessageQueueingService(IConfiguration configuration)
        {
            var securityTokenServiceFactory = new SecurityTokenServiceFactory();
            var securityTokensSection       = configuration?.GetSection("securityTokens");
            var securityTokenService        = await securityTokenServiceFactory.InitSecurityTokenService(securityTokensSection);

            var defaultUri = new Uri("amqp://localhost:5672");
            var uri        = configuration?.GetValue("uri", defaultUri) ?? defaultUri;

            var encodingName = configuration?["encoding"];

            if (string.IsNullOrWhiteSpace(encodingName))
            {
                encodingName = "UTF-8";
            }
            var encoding = ParseEncoding(encodingName);

            var messageEncryptionServiceFactory = new MessageEncryptionServiceFactory();
            var messageEncryptionConfig         = configuration?.GetSection("encryption");
            var messageEncryptionService        = await messageEncryptionServiceFactory.InitMessageEncryptionService(messageEncryptionConfig);

            var queueingOptions = new RabbitMQMessageQueueingOptions(uri)
            {
                Encoding                 = encoding,
                SecurityTokenService     = securityTokenService,
                MessageEncryptionService = messageEncryptionService
            };

            var messageQueueingService = new RabbitMQMessageQueueingService(queueingOptions);

            return(messageQueueingService);
        }
        /// <summary>
        /// Creates an initializes a <see cref="IMessageQueueingService"/>
        /// based on the provided <paramref name="configuration"/>
        /// </summary>
        /// <param name="configuration">The journaling configuration
        /// element</param>
        /// <returns>Returns a task whose result is an initialized
        /// <see cref="IMessageQueueingService"/></returns>
        public async Task <IMessageQueueingService> CreateMessageQueueingService(QueueingElement configuration)
        {
            var securityTokenServiceFactory = new SecurityTokenServiceFactory();
            var securitTokenConfig          = configuration.SecurityTokens;
            var securityTokenService        = await securityTokenServiceFactory.InitSecurityTokenService(securitTokenConfig);

            var uri = configuration.GetUri("uri") ?? new Uri("amqp://localhost:5672");

            var encodingName = configuration.GetString("encoding");

            if (string.IsNullOrWhiteSpace(encodingName))
            {
                encodingName = "UTF-8";
            }
            var encoding = ParseEncoding(encodingName);

            var messageEncryptionServiceFactory = new MessageEncryptionServiceFactory();
            var messageEncryptionConfig         = configuration.Encryption;
            var messageEncryptionService        = await messageEncryptionServiceFactory.InitMessageEncryptionService(messageEncryptionConfig);

            var queueingOptions = new RabbitMQMessageQueueingOptions(uri)
            {
                Encoding                 = encoding,
                SecurityTokenService     = securityTokenService,
                MessageEncryptionService = messageEncryptionService
            };

            var messageQueueingService = new RabbitMQMessageQueueingService(queueingOptions);

            return(messageQueueingService);
        }
        public Task<IMessageQueueingService> CreateMessageQueueingService(QueueingElement configuration)
        {
            var uri = configuration.GetUri("uri") ?? new Uri("amqp://localhost:5672");

            var encodingName = configuration.GetString("encoding");
            if (string.IsNullOrWhiteSpace(encodingName))
            {
                encodingName = "UTF-8";
            }

            var encoding = ParseEncoding(encodingName);
            var messageQueueingService = new RabbitMQMessageQueueingService(uri, encoding: encoding);
            return Task.FromResult<IMessageQueueingService>(messageQueueingService);
        }
        public async Task Given_Queued_Message_When_Acknowledged_Then_Message_Should_Be_Deleted()
        {
            var listenerCalledEvent = new ManualResetEvent(false);
            var queueName = new QueueName(Guid.NewGuid().ToString());
            var rmqQueueingService = new RabbitMQMessageQueueingService(RabbitMQUri);
            try
            {
                var mockListener = new Mock<IQueueListener>();
                mockListener.Setup(
                    x =>
                        x.MessageReceived(It.IsAny<Message>(), It.IsAny<IQueuedMessageContext>(),
                            It.IsAny<CancellationToken>()))
                    .Callback<Message, IQueuedMessageContext, CancellationToken>((msg, ctx, ct) =>
                    {
                        ctx.Acknowledge();
                        listenerCalledEvent.Set();
                    })
                    .Returns(Task.FromResult(true));

                await rmqQueueingService.CreateQueue(queueName, mockListener.Object, new QueueOptions {MaxAttempts = 1});

                var message = new Message(new MessageHeaders
                {
                    {HeaderName.ContentType, "text/plain"},
                    {HeaderName.MessageId, Guid.NewGuid().ToString()}
                }, "Hello, world!");

                await rmqQueueingService.EnqueueMessage(queueName, message, Thread.CurrentPrincipal);
                await listenerCalledEvent.WaitOneAsync(TimeSpan.FromSeconds(1));

                // The listener is called before the message is published to the retry queue, 
                // so there is a possible race condition here.  Wait for a second to allow the 
                // publish to take place before checking the queueu and retry queue depth.
                await Task.Delay(TimeSpan.FromSeconds(1));

                var messageEqualityComparer = new MessageEqualityComparer();
                mockListener.Verify(
                    x =>
                        x.MessageReceived(It.Is<Message>(m => messageEqualityComparer.Equals(m, message)),
                            It.IsAny<IQueuedMessageContext>(), It.IsAny<CancellationToken>()), Times.Once());

                Assert.That(GetQueueDepth(queueName), Is.EqualTo(0));
                Assert.That(GetQueueDepth(queueName.GetRetryQueueName()), Is.EqualTo(0));
            }
            finally
            {
                rmqQueueingService.Dispose();
                DeleteQueue(queueName);
            }
        }
        public async Task Given_10_Existing_Messages_10_New_Messages_Then_Listener_Should_Fire_For_All_20_Messages()
        {
            var queueName = new QueueName(Guid.NewGuid().ToString());
            var rmqQueueingService = new RabbitMQMessageQueueingService(RabbitMQUri);

            try
            {
                var existingMessages = Enumerable.Range(1, 10)
                    .Select(i => new Message(new MessageHeaders
                    {
                        {HeaderName.ContentType, "text/plain"},
                        {HeaderName.MessageId, Guid.NewGuid().ToString()}
                    }, "Hello, world! (" + i + ")"))
                    .ToList();

                await Task.WhenAll(existingMessages.Select(msg => StageExistingMessage(msg, queueName)));

                var newMessages = Enumerable.Range(11, 10)
                    .Select(i => new Message(new MessageHeaders
                    {
                        {HeaderName.ContentType, "text/plain"},
                        {HeaderName.MessageId, Guid.NewGuid().ToString()}
                    }, "Hello, world! (" + i + ")"))
                    .ToList();

                var listenerCountdown = new CountdownEvent(existingMessages.Count + newMessages.Count);

                var mockListener = new Mock<IQueueListener>();
                mockListener.Setup(
                    x =>
                        x.MessageReceived(It.IsAny<Message>(), It.IsAny<IQueuedMessageContext>(),
                            It.IsAny<CancellationToken>()))
                    .Callback<Message, IQueuedMessageContext, CancellationToken>((msg, ctx, ct) =>
                    {
                        ctx.Acknowledge();
                        listenerCountdown.Signal();
                    })
                    .Returns(Task.FromResult(true));

                await rmqQueueingService.CreateQueue(queueName, mockListener.Object, new QueueOptions {MaxAttempts = 1});

                await Task.WhenAll(newMessages.Select(msg =>
                    rmqQueueingService.EnqueueMessage(queueName, msg, Thread.CurrentPrincipal)));

                var timedOut = !await listenerCountdown.WaitHandle.WaitOneAsync(TimeSpan.FromSeconds(10));

                Assert.That(timedOut, Is.False, "Timed out waiting for listeners to be called");

                var messageEqualityComparer = new MessageEqualityComparer();
                var allmessages = existingMessages.Union(newMessages);
                foreach (var message in allmessages)
                {
                    mockListener.Verify(
                        x =>
                            x.MessageReceived(It.Is<Message>(m => messageEqualityComparer.Equals(m, message)),
                                It.IsAny<IQueuedMessageContext>(), It.IsAny<CancellationToken>()), Times.Once());
                }

                Assert.That(GetQueueDepth(queueName), Is.EqualTo(0));
                Assert.That(GetQueueDepth(queueName.GetRetryQueueName()), Is.EqualTo(0));
            }
            finally
            {
                
                rmqQueueingService.Dispose();
                DeleteQueue(queueName);
            }
        }
        public async Task Given_Existing_Message_When_Creating_Queue_Then_Listener_Should_Fire()
        {
            var listenerCalledEvent = new ManualResetEvent(false);
            var queueName = new QueueName(Guid.NewGuid().ToString());
            var rmqQueueingService = new RabbitMQMessageQueueingService(RabbitMQUri);

            try
            {
                var message = new Message(new MessageHeaders
                {
                    {HeaderName.ContentType, "text/plain"},
                    {HeaderName.MessageId, Guid.NewGuid().ToString()}
                }, "Hello, world!");

                await StageExistingMessage(message, queueName);

                var mockListener = new Mock<IQueueListener>();
                mockListener.Setup(
                    x =>
                        x.MessageReceived(It.IsAny<Message>(), It.IsAny<IQueuedMessageContext>(),
                            It.IsAny<CancellationToken>()))
                    .Callback<Message, IQueuedMessageContext, CancellationToken>((msg, ctx, ct) =>
                    {
                        ctx.Acknowledge();
                        listenerCalledEvent.Set();
                    })
                    .Returns(Task.FromResult(true));

                await rmqQueueingService.CreateQueue(queueName, mockListener.Object, new QueueOptions {MaxAttempts = 1, RetryDelay = TimeSpan.FromSeconds(30)});
                await listenerCalledEvent.WaitOneAsync(TimeSpan.FromSeconds(5));

                var messageEqualityComparer = new MessageEqualityComparer();
                mockListener.Verify(
                    x =>
                        x.MessageReceived(It.Is<Message>(m => messageEqualityComparer.Equals(m, message)),
                            It.IsAny<IQueuedMessageContext>(), It.IsAny<CancellationToken>()), Times.Once());

                Assert.That(GetQueueDepth(queueName), Is.EqualTo(0));
                Assert.That(GetQueueDepth(queueName.GetRetryQueueName()), Is.EqualTo(0));
            }
            finally
            {
                DeleteQueue(queueName);
                rmqQueueingService.Dispose();
            }
        }