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.Parser.Model; using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Tv; using NzbDrone.Test.Common; namespace NzbDrone.Core.Test.Download { [TestFixture] public class DownloadServiceFixture : CoreTest { private RemoteEpisode _parseResult; private List _downloadClients; [SetUp] public void Setup() { _downloadClients = new List(); Mocker.GetMock() .Setup(v => v.GetDownloadClients()) .Returns(_downloadClients); Mocker.GetMock() .Setup(v => v.GetDownloadClient(It.IsAny(), It.IsAny())) .Returns((v, i) => _downloadClients.FirstOrDefault(d => d.Protocol == v)); var episodes = Builder.CreateListOfSize(2) .TheFirst(1).With(s => s.Id = 12) .TheNext(1).With(s => s.Id = 99) .All().With(s => s.SeriesId = 5) .Build().ToList(); var releaseInfo = Builder.CreateNew() .With(v => v.DownloadProtocol = DownloadProtocol.Usenet) .With(v => v.DownloadUrl = "http://test.site/download1.ext") .Build(); _parseResult = Builder.CreateNew() .With(c => c.Series = Builder.CreateNew().Build()) .With(c => c.Release = releaseInfo) .With(c => c.Episodes = episodes) .Build(); } private Mock WithUsenetClient() { var mock = new Mock(MockBehavior.Default); mock.SetupGet(s => s.Definition).Returns(Builder.CreateNew().Build()); _downloadClients.Add(mock.Object); mock.SetupGet(v => v.Protocol).Returns(DownloadProtocol.Usenet); return mock; } private Mock WithTorrentClient() { var mock = new Mock(MockBehavior.Default); mock.SetupGet(s => s.Definition).Returns(Builder.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())); Subject.DownloadReport(_parseResult); VerifyEventPublished(); } [Test] public void Download_report_should_grab_using_client() { var mock = WithUsenetClient(); mock.Setup(s => s.Download(It.IsAny())); Subject.DownloadReport(_parseResult); mock.Verify(s => s.Download(It.IsAny()), Times.Once()); } [Test] public void Download_report_should_not_publish_on_failed_grab_event() { var mock = WithUsenetClient(); mock.Setup(s => s.Download(It.IsAny())) .Throws(new WebException()); Assert.Throws(() => Subject.DownloadReport(_parseResult)); VerifyEventNotPublished(); } [Test] public void Download_report_should_trigger_indexer_backoff_on_indexer_error() { var mock = WithUsenetClient(); mock.Setup(s => s.Download(It.IsAny())) .Callback(v => { throw new ReleaseDownloadException(v.Release, "Error", new WebException()); }); Assert.Throws(() => Subject.DownloadReport(_parseResult)); Mocker.GetMock() .Verify(v => v.RecordFailure(It.IsAny(), It.IsAny()), 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())) .Callback(v => { throw new ReleaseDownloadException(v.Release, "Error", new TooManyRequestsException(request, response)); }); Assert.Throws(() => Subject.DownloadReport(_parseResult)); Mocker.GetMock() .Verify(v => v.RecordFailure(It.IsAny(), 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())) .Callback(v => { throw new ReleaseDownloadException(v.Release, "Error", new TooManyRequestsException(request, response)); }); Assert.Throws(() => Subject.DownloadReport(_parseResult)); Mocker.GetMock() .Verify(v => v.RecordFailure(It.IsAny(), It.IsInRange(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())) .Throws(new DownloadClientException("Some Error")); Assert.Throws(() => Subject.DownloadReport(_parseResult)); Mocker.GetMock() .Verify(v => v.RecordFailure(It.IsAny(), It.IsAny()), 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())) .Callback(v => { throw new ReleaseUnavailableException(v.Release, "Error", new WebException()); }); Assert.Throws(() => Subject.DownloadReport(_parseResult)); Mocker.GetMock() .Verify(v => v.RecordFailure(It.IsAny(), It.IsAny()), Times.Never()); } [Test] public void should_not_attempt_download_if_client_isnt_configured() { Assert.Throws(() => Subject.DownloadReport(_parseResult)); Mocker.GetMock().Verify(c => c.Download(It.IsAny()), Times.Never()); VerifyEventNotPublished(); } [Test] public void should_attempt_download_even_if_client_is_disabled() { var mockUsenet = WithUsenetClient(); Mocker.GetMock() .Setup(v => v.GetBlockedProviders()) .Returns(new List { new DownloadClientStatus { ProviderId = _downloadClients.First().Definition.Id, DisabledTill = DateTime.UtcNow.AddHours(3) } }); Subject.DownloadReport(_parseResult); Mocker.GetMock().Verify(c => c.GetBlockedProviders(), Times.Never()); mockUsenet.Verify(c => c.Download(It.IsAny()), Times.Once()); VerifyEventPublished(); } [Test] public void should_send_download_to_correct_usenet_client() { var mockTorrent = WithTorrentClient(); var mockUsenet = WithUsenetClient(); Subject.DownloadReport(_parseResult); mockTorrent.Verify(c => c.Download(It.IsAny()), Times.Never()); mockUsenet.Verify(c => c.Download(It.IsAny()), Times.Once()); } [Test] public void should_send_download_to_correct_torrent_client() { var mockTorrent = WithTorrentClient(); var mockUsenet = WithUsenetClient(); _parseResult.Release.DownloadProtocol = DownloadProtocol.Torrent; Subject.DownloadReport(_parseResult); mockTorrent.Verify(c => c.Download(It.IsAny()), Times.Once()); mockUsenet.Verify(c => c.Download(It.IsAny()), Times.Never()); } } }