public async Task Given_Queued_Message_When_Context_Acknowledged_Then_Message_Should_Be_Acknowledged()
        {
            var listenerCalledEvent = new ManualResetEvent(false);
            var baseDirectory = GetTempDirectory();
            var queueName = new QueueName(Guid.NewGuid().ToString());

            var sqlQueueingService = new SQLiteMessageQueueingService(baseDirectory);
            sqlQueueingService.Init();

            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 sqlQueueingService.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 sqlQueueingService.EnqueueMessage(queueName, message, Thread.CurrentPrincipal);
            await listenerCalledEvent.WaitOneAsync(TimeSpan.FromSeconds(1));

            // The listener is called before the row is updated, so there is a possible
            // race condition here.  Wait for a second to allow the update to take place
            // before enumerating the rows to see that they were actually deleted.
            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());

            var sqlQueueInspector = new SQLiteMessageQueueInspector(baseDirectory, queueName);
            var queuedMessages = (await sqlQueueInspector.EnumerateMessages()).ToList();

            Assert.That(queuedMessages, Is.Empty);
        }
        public async Task Given_Auto_Acknowledge_Queue_When_Listener_Throws_Then_Message_Should_Not_Be_Acknowledged()
        {
            var listenerCalledEvent = new ManualResetEvent(false);
            var baseDirectory = GetTempDirectory();
            var queueName = new QueueName(Guid.NewGuid().ToString());

            var sqlQueueingService = new SQLiteMessageQueueingService(baseDirectory);
            sqlQueueingService.Init();

            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) =>
                {
                    listenerCalledEvent.Set();
                    throw new Exception();
                });

            await sqlQueueingService
                .CreateQueue(queueName, mockListener.Object, new QueueOptions
                {
                    AutoAcknowledge = true,
                    MaxAttempts = 2, // So the message doesn't get moved to the DLQ
                    RetryDelay = TimeSpan.FromSeconds(30)
                });

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

            await sqlQueueingService.EnqueueMessage(queueName, message, Thread.CurrentPrincipal);

            var listenerCalled = await listenerCalledEvent
                .WaitOneAsync(Debugger.IsAttached ? TimeSpan.FromMinutes(5) : TimeSpan.FromSeconds(3));

            Assert.That(listenerCalled, Is.True);

            // The listener is called before the row is updated, so there is a possible
            // race condition here.  Wait for a second to allow the update to take place
            // before enumerating the rows to see that they were actually not updated.
            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());

            var sqlQueueInspector = new SQLiteMessageQueueInspector(baseDirectory, queueName);
            var queuedMessages = (await sqlQueueInspector.EnumerateMessages()).ToList();

            Assert.That(queuedMessages[0].Message, Is.EqualTo(message).Using(messageEqualityComparer));
        }