context: using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Net;
  using FizzWare.NBuilder;
  using Moq;
  using NUnit.Framework;
  using NzbDrone.Common.Http;
  using NzbDrone.Core.Download;
  using NzbDrone.Core.Download.Clients;
  using NzbDrone.Core.Exceptions;
  using NzbDrone.Core.Indexers;
  using NzbDrone.Core.Music;
  using NzbDrone.Core.Parser.Model;
  using NzbDrone.Core.Test.Framework;
  
  namespace NzbDrone.Core.Test.Download
  {
      [TestFixture]
      public class DownloadServiceFixture : CoreTest<DownloadService>
      {
          private RemoteAlbum _parseResult;
          private List<IDownloadClient> _downloadClients;
          [SetUp]
          public void Setup()
          {
              _downloadClients = new List<IDownloadClient>();
  
              Mocker.GetMock<IProvideDownloadClient>()
                  .Setup(v => v.GetDownloadClients())
                  .Returns(_downloadClients);
  
              Mocker.GetMock<IProvideDownloadClient>()
                  .Setup(v => v.GetDownloadClient(It.IsAny<DownloadProtocol>()))
                  .Returns<DownloadProtocol>(v => _downloadClients.FirstOrDefault(d => d.Protocol == v));
  
              var episodes = Builder<Album>.CreateListOfSize(2)
                  .TheFirst(1).With(s => s.Id = 12)
                  .TheNext(1).With(s => s.Id = 99)
                  .All().With(s => s.ArtistId = 5)
                  .Build().ToList();
  
              var releaseInfo = Builder<ReleaseInfo>.CreateNew()
                  .With(v => v.DownloadProtocol = DownloadProtocol.Usenet)
                  .With(v => v.DownloadUrl = "http://test.site/download1.ext")
                  .Build();
  
              _parseResult = Builder<RemoteAlbum>.CreateNew()
                     .With(c => c.Artist = Builder<Artist>.CreateNew().Build())
                     .With(c => c.Release = releaseInfo)
                     .With(c => c.Albums = episodes)
                     .Build();
          }
  
          private Mock<IDownloadClient> WithUsenetClient()
          {
              var mock = new Mock<IDownloadClient>(MockBehavior.Default);
              mock.SetupGet(s => s.Definition).Returns(Builder<IndexerDefinition>.CreateNew().Build());
  
              _downloadClients.Add(mock.Object);
  
              mock.SetupGet(v => v.Protocol).Returns(DownloadProtocol.Usenet);
  
              return mock;
          }
  
          private Mock<IDownloadClient> WithTorrentClient()
          {
              var mock = new Mock<IDownloadClient>(MockBehavior.Default);
              mock.SetupGet(s => s.Definition).Returns(Builder<IndexerDefinition>.CreateNew().Build());
  
              _downloadClients.Add(mock.Object);
  
              mock.SetupGet(v => v.Protocol).Returns(DownloadProtocol.Torrent);
  
              return mock;
          }
  
          [Test]
          public void Download_report_should_publish_on_grab_event()
          {
              var mock = WithUsenetClient();
              mock.Setup(s => s.Download(It.IsAny<RemoteAlbum>()));
  
              Subject.DownloadReport(_parseResult);
  
              VerifyEventPublished<AlbumGrabbedEvent>();
          }
  
          [Test]
          public void Download_report_should_grab_using_client()
          {
              var mock = WithUsenetClient();
              mock.Setup(s => s.Download(It.IsAny<RemoteAlbum>()));
  
              Subject.DownloadReport(_parseResult);
  
              mock.Verify(s => s.Download(It.IsAny<RemoteAlbum>()), Times.Once());
          }
  
          [Test]
          public void Download_report_should_not_publish_on_failed_grab_event()
          {
              var mock = WithUsenetClient();
              mock.Setup(s => s.Download(It.IsAny<RemoteAlbum>()))
                  .Throws(new WebException());
  
              Assert.Throws<WebException>(() => Subject.DownloadReport(_parseResult));
  
              VerifyEventNotPublished<AlbumGrabbedEvent>();
          }
  
          [Test]
          public void Download_report_should_trigger_indexer_backoff_on_indexer_error()
          {
              var mock = WithUsenetClient();
              mock.Setup(s => s.Download(It.IsAny<RemoteAlbum>()))
                  .Callback<RemoteAlbum>(v =>
                  {
                      throw new ReleaseDownloadException(v.Release, "Error", new WebException());
                  });
  
              Assert.Throws<ReleaseDownloadException>(() => Subject.DownloadReport(_parseResult));
  
              Mocker.GetMock<IIndexerStatusService>()
                  .Verify(v => v.RecordFailure(It.IsAny<int>(), It.IsAny<TimeSpan>()), Times.Once());
          }
  
          [Test]
          public void Download_report_should_trigger_indexer_backoff_on_http429_with_long_time()
          {
              var request = new HttpRequest("http://my.indexer.com");
              var response = new HttpResponse(request, new HttpHeader(), new byte[0], (HttpStatusCode)429);
              response.Headers["Retry-After"] = "300";
  
              var mock = WithUsenetClient();
              mock.Setup(s => s.Download(It.IsAny<RemoteAlbum>()))
                  .Callback<RemoteAlbum>(v =>
                  {
                      throw new ReleaseDownloadException(v.Release, "Error", new TooManyRequestsException(request, response));
                  });
  
              Assert.Throws<ReleaseDownloadException>(() => Subject.DownloadReport(_parseResult));
  
              Mocker.GetMock<IIndexerStatusService>()
                  .Verify(v => v.RecordFailure(It.IsAny<int>(), TimeSpan.FromMinutes(5.0)), Times.Once());
          }
  
          [Test]
          public void Download_report_should_trigger_indexer_backoff_on_http429_based_on_date()
          {
              var request = new HttpRequest("http://my.indexer.com");
              var response = new HttpResponse(request, new HttpHeader(), new byte[0], (HttpStatusCode)429);
              response.Headers["Retry-After"] = DateTime.UtcNow.AddSeconds(300).ToString("r");
  
              var mock = WithUsenetClient();
              mock.Setup(s => s.Download(It.IsAny<RemoteAlbum>()))
                  .Callback<RemoteAlbum>(v =>
                  {
                      throw new ReleaseDownloadException(v.Release, "Error", new TooManyRequestsException(request, response));
                  });
  
              Assert.Throws<ReleaseDownloadException>(() => Subject.DownloadReport(_parseResult));
  
              Mocker.GetMock<IIndexerStatusService>()
                  .Verify(v => v.RecordFailure(It.IsAny<int>(),
                      It.IsInRange<TimeSpan>(TimeSpan.FromMinutes(4.9), TimeSpan.FromMinutes(5.1), Moq.Range.Inclusive)), Times.Once());
          }
  
          [Test]
          public void Download_report_should_not_trigger_indexer_backoff_on_downloadclient_error()
          {
              var mock = WithUsenetClient();
              mock.Setup(s => s.Download(It.IsAny<RemoteAlbum>()))
                  .Throws(new DownloadClientException("Some Error"));
  
              Assert.Throws<DownloadClientException>(() => Subject.DownloadReport(_parseResult));
  
              Mocker.GetMock<IIndexerStatusService>()
                  .Verify(v => v.RecordFailure(It.IsAny<int>(), It.IsAny<TimeSpan>()), Times.Never());
          }
  
          [Test]
          public void Download_report_should_not_trigger_indexer_backoff_on_indexer_404_error()
          {
              var mock = WithUsenetClient();
              mock.Setup(s => s.Download(It.IsAny<RemoteAlbum>()))
                  .Callback<RemoteAlbum>(v =>
                  {
                      throw new ReleaseUnavailableException(v.Release, "Error", new WebException());
                  });
  
              Assert.Throws<ReleaseUnavailableException>(() => Subject.DownloadReport(_parseResult));
  
              Mocker.GetMock<IIndexerStatusService>()
                  .Verify(v => v.RecordFailure(It.IsAny<int>(), It.IsAny<TimeSpan>()), Times.Never());
          }
  
          [Test]
          public void should_not_attempt_download_if_client_isnt_configured()
          {
              Assert.Throws<DownloadClientUnavailableException>(() => Subject.DownloadReport(_parseResult));
  
              Mocker.GetMock<IDownloadClient>().Verify(c => c.Download(It.IsAny<RemoteAlbum>()), Times.Never());
              VerifyEventNotPublished<AlbumGrabbedEvent>();
          }
  
          [Test]
          public void should_attempt_download_even_if_client_is_disabled()
          {
  
gt:             var mockUsenet = WithUsenetClient();