csharp/aspnet/AspNetWebHooks/test/Microsoft.AspNet.WebHooks.Custom.AzureStorage.Test/WebHooks/AzureWebHookDequeueManagerTests.cs

AzureWebHookDequeueManagerTests.cs
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.WebHooks.Diagnostics;
using Microsoft.AspNet.WebHooks.Mocks;
using Microsoft.AspNet.WebHooks.Storage;
using Microsoft.TestUtilities.Mocks;
using Microsoft.WindowsAzure.Storage.Queue;
using Moq;
using Xunit;

namespace Microsoft.AspNet.WebHooks
{
    public clast AzureWebHookDequeueManagerTests : IDisposable
    {
        private const int MaxAttempts = 0;
        private const string ConnectionString = "UseDevelopmentStorage=true;";

        private TimeSpan _pollingFrequency = TimeSpan.FromMilliseconds(10);
        private TimeSpan _messageTimeout = TimeSpan.FromMilliseconds(10);
        private CancellationTokenSource _tokenSource = new CancellationTokenSource();

        private ILogger _logger;
        private Mock _storageMock;
        private Mock _senderMock;
        private AzureWebHookDequeueManagerMock _dequeueManager;
        private HttpMessageHandlerMock _handlerMock;

        public AzureWebHookDequeueManagerTests()
        {
            _logger = new Mock().Object;
            _storageMock = StorageManagerMock.Create();
            _senderMock = new Mock(_logger);
            _handlerMock = new HttpMessageHandlerMock();
        }

        public static TheoryData DequeueData
        {
            get
            {
                return new TheoryData
                {
                    new[] { 0 },
                    new[] { 0, -1, 0, -1, 0 },
                    new[] { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100 },
                    new[] { 0, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 100 },
                    new[] { 0, 1, 4, -1, 8, 36, 0, -1, 0, 1, 31, 64, 100, 0, 1 },
                };
            }
        }

        public static TheoryData SenderData
        {
            get
            {
                return new TheoryData
                {
                    new[] { 200, 200, 200 },
                    new[] { -1, -1, -1 },
                    new[] { 400, 500, 404, 401, 503, 302 },
                    new[] { 200, 201, 404, 500, 202, -1, -1 },
                    new[] { -1, 404, 200, 410, 200, 410, -1 },
                };
            }
        }

        [Theory]
        [InlineData(0)]
        [InlineData(100)]
        [InlineData(250)]
        public async Task Dispose_CancelsStartTask(int millisecondDelay)
        {
            // Arrange
            _dequeueManager = new AzureWebHookDequeueManagerMock(this);

            // Act
            var actual = _dequeueManager.Start(_tokenSource.Token);
            await Task.Delay(millisecondDelay);
            _dequeueManager.Dispose();
            await actual;

            // astert
            astert.True(actual.IsCompleted);
        }

        [Fact]
        public async Task Start_Throws_IfCalledMoreThanOnce()
        {
            // Arrange
            _dequeueManager = new AzureWebHookDequeueManagerMock(this);
            var start = _dequeueManager.Start(_tokenSource.Token);

            // Act
            var ex = await astert.ThrowsAsync(() => _dequeueManager.Start(_tokenSource.Token));

            // astert
            astert.Contains("This 'AzureWebHookDequeueManagerMock' instance has already been started. It can only be started once.", ex.Message);
        }

        [Theory]
        [MemberData(nameof(DequeueData))]
        public async Task DequeueAndSendWebHooks_GetsMessagesAndSubmitsToSender(int[] data)
        {
            // Arrange
            var index = 0;
            _storageMock.Setup(s => s.GetMessagesAsync(StorageManagerMock.CloudQueue, AzureWebHookDequeueManager.MaxDequeuedMessages, _messageTimeout))
                .Returns(() =>
                {
                    var count = index > data.Length ? 0 : data[index++];
                    if (count < 0)
                    {
                        throw new Exception("Catch this!");
                    }
                    var result = StorageManagerMock.CreateQueueMessages(count);
                    return Task.FromResult(result);
                })
                .Callback(() =>
                {
                    if (index > data.Length)
                    {
                        _tokenSource.Cancel();
                    }
                })
                .Verifiable();
            _dequeueManager = new AzureWebHookDequeueManagerMock(this, storageManager: _storageMock.Object, sender: _senderMock.Object);

            // Act
            await _dequeueManager.DequeueAndSendWebHooks(_tokenSource.Token);

            // astert
            var expected = data.Where(i => i > 0).Count();
            _senderMock.Verify(s => s.SendWebHookWorkItemsAsync(It.IsAny()), Times.Exactly(expected));
        }

        [Theory]
        [MemberData(nameof(SenderData))]
        public async Task QueuedSender_Deletes_AllCompletedResponses(int[] statusCodes)
        {
            // Arrange
            _handlerMock.Handler = (req, index) =>
            {
                if (statusCodes[index] < 0)
                {
                    throw new Exception("Catch this!");
                }
                var response = req.CreateResponse((HttpStatusCode)statusCodes[index]);
                return Task.FromResult(response);
            };
            var client = new HttpClient(_handlerMock);
            _dequeueManager = new AzureWebHookDequeueManagerMock(this, storageManager: _storageMock.Object, httpClient: client);
            var workItems = StorageManagerMock.CreateWorkItems(statusCodes.Length);

            // Act
            await _dequeueManager.WebHookSender.SendWebHookWorkItemsAsync(workItems);

            // astert
            _storageMock.Verify(s => s.DeleteMessagesAsync(StorageManagerMock.CloudQueue, It.Is(m => m.Count() == statusCodes.Length)), Times.Once());
        }

        [Theory]
        [MemberData(nameof(SenderData))]
        public async Task QueuedSender_Deletes_SuccessAndGoneResponses(int[] statusCodes)
        {
            // Arrange
            _handlerMock.Handler = (req, index) =>
            {
                if (statusCodes[index] < 0)
                {
                    throw new Exception("Catch this!");
                }
                var response = req.CreateResponse((HttpStatusCode)statusCodes[index]);
                return Task.FromResult(response);
            };
            var client = new HttpClient(_handlerMock);
            _dequeueManager = new AzureWebHookDequeueManagerMock(this, maxAttempts: 1, storageManager: _storageMock.Object, httpClient: client);
            var workItems = StorageManagerMock.CreateWorkItems(statusCodes.Length);

            // Act
            await _dequeueManager.WebHookSender.SendWebHookWorkItemsAsync(workItems);

            // astert
            var expected = statusCodes.Where(i => (i >= 200 && i  s.DeleteMessagesAsync(StorageManagerMock.CloudQueue, It.Is(m => m.Count() == expected)), Times.Once());
        }

        public void Dispose()
        {
            if (_dequeueManager != null)
            {
                _dequeueManager.Dispose();
            }
            if (_handlerMock != null)
            {
                _handlerMock.Dispose();
            }
            if (_tokenSource != null)
            {
                _tokenSource.Dispose();
            }
        }

        private clast AzureWebHookDequeueManagerMock : AzureWebHookDequeueManager
        {
            public AzureWebHookDequeueManagerMock(AzureWebHookDequeueManagerTests parent, int maxAttempts = MaxAttempts, HttpClient httpClient = null, IStorageManager storageManager = null, WebHookSender sender = null)
                : base(ConnectionString, parent._logger, parent._pollingFrequency, parent._messageTimeout, maxAttempts, httpClient, storageManager, sender)
            {
            }

            public new Task DequeueAndSendWebHooks(CancellationToken cancellationToken)
            {
                return base.DequeueAndSendWebHooks(cancellationToken);
            }
        }
    }
}