using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using FluentAssertions; using Moq; using NUnit.Framework; using NzbDrone.Common.Http; using NzbDrone.Core.Download; using NzbDrone.Core.Download.Clients.UTorrent; using NzbDrone.Core.MediaFiles.TorrentInfo; using NzbDrone.Test.Common; namespace NzbDrone.Core.Test.Download.DownloadClientTests.UTorrentTests { [TestFixture] public class UTorrentFixture : DownloadClientFixtureBase { protected UTorrentTorrent _queued; protected UTorrentTorrent _downloading; protected UTorrentTorrent _failed; protected UTorrentTorrent _completed; [SetUp] public void Setup() { Subject.Definition = new DownloadClientDefinition(); Subject.Definition.Settings = new UTorrentSettings { Host = "127.0.0.1", Port = 2222, Username = "admin", Password = "pass", MusicCategory = "lidarr" }; _queued = new UTorrentTorrent { Hash = "HASH", Status = UTorrentTorrentStatus.Queued | UTorrentTorrentStatus.Loaded, Name = _title, Size = 1000, Remaining = 1000, Progress = 0, Label = "lidarr", DownloadUrl = _downloadUrl, RootDownloadPath = "somepath" }; _downloading = new UTorrentTorrent { Hash = "HASH", Status = UTorrentTorrentStatus.Started | UTorrentTorrentStatus.Loaded, Name = _title, Size = 1000, Remaining = 100, Progress = 0.9, Label = "lidarr", DownloadUrl = _downloadUrl, RootDownloadPath = "somepath" }; _failed = new UTorrentTorrent { Hash = "HASH", Status = UTorrentTorrentStatus.Error, Name = _title, Size = 1000, Remaining = 100, Progress = 0.9, Label = "lidarr", DownloadUrl = _downloadUrl, RootDownloadPath = "somepath" }; _completed = new UTorrentTorrent { Hash = "HASH", Status = UTorrentTorrentStatus.Checked | UTorrentTorrentStatus.Loaded, Name = _title, Size = 1000, Remaining = 0, Progress = 1.0, Label = "lidarr", DownloadUrl = _downloadUrl, RootDownloadPath = "somepath" }; Mocker.GetMock() .Setup(s => s.GetHashFromTorrentFile(It.IsAny())) .Returns("CBC2F069FE8BB2F544EAE707D75BCD3DE9DCF951"); Mocker.GetMock() .Setup(s => s.Get(It.IsAny())) .Returns(r => new HttpResponse(r, new HttpHeader(), Array.Empty())); } protected void GivenRedirectToMagnet() { var httpHeader = new HttpHeader(); httpHeader["Location"] = "magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR&tr=udp"; Mocker.GetMock() .Setup(s => s.Get(It.IsAny())) .Returns(r => new HttpResponse(r, httpHeader, Array.Empty(), System.Net.HttpStatusCode.SeeOther)); } protected void GivenRedirectToTorrent() { var httpHeader = new HttpHeader(); httpHeader["Location"] = "http://test.lidarr.audio/not-a-real-torrent.torrent"; Mocker.GetMock() .Setup(s => s.Get(It.Is(h => h.Url.ToString() == _downloadUrl))) .Returns(r => new HttpResponse(r, httpHeader, Array.Empty(), System.Net.HttpStatusCode.Found)); } protected void GivenFailedDownload() { Mocker.GetMock() .Setup(s => s.AddTorrentFromUrl(It.IsAny(), It.IsAny())) .Throws(); } protected void GivenSuccessfulDownload() { Mocker.GetMock() .Setup(s => s.AddTorrentFromUrl(It.IsAny(), It.IsAny())) .Callback(() => { PrepareClientToReturnQueuedItem(); }); } protected virtual void GivenTorrents(List torrents, string cacheNumber = null) { if (torrents == null) { torrents = new List(); } Mocker.GetMock() .Setup(s => s.GetTorrents(It.IsAny(), It.IsAny())) .Returns(new UTorrentResponse { Torrents = torrents, CacheNumber = cacheNumber }); } protected virtual void GivenDifferentialTorrents(string oldCacheNumber, List changed, List removed, string cacheNumber) { if (changed == null) { changed = new List(); } if (removed == null) { removed = new List(); } Mocker.GetMock() .Setup(s => s.GetTorrents(oldCacheNumber, It.IsAny())) .Returns(new UTorrentResponse { TorrentsChanged = changed, TorrentsRemoved = removed, CacheNumber = cacheNumber }); } protected void PrepareClientToReturnQueuedItem() { GivenTorrents(new List { _queued }); } protected void PrepareClientToReturnDownloadingItem() { GivenTorrents(new List { _downloading }); } protected void PrepareClientToReturnFailedItem() { GivenTorrents(new List { _failed }); } protected void PrepareClientToReturnCompletedItem() { GivenTorrents(new List { _completed }); } [Test] public void queued_item_should_have_required_properties() { PrepareClientToReturnQueuedItem(); var item = Subject.GetItems().Single(); VerifyQueued(item); } [Test] public void downloading_item_should_have_required_properties() { PrepareClientToReturnDownloadingItem(); var item = Subject.GetItems().Single(); VerifyDownloading(item); } [Test] public void failed_item_should_have_required_properties() { PrepareClientToReturnFailedItem(); var item = Subject.GetItems().Single(); VerifyWarning(item); } [Test] public void completed_download_should_have_required_properties() { PrepareClientToReturnCompletedItem(); var item = Subject.GetItems().Single(); VerifyCompleted(item); item.CanBeRemoved.Should().BeTrue(); item.CanMoveFiles.Should().BeTrue(); } [Test] public async Task Download_should_return_unique_id() { GivenSuccessfulDownload(); var remoteAlbum = CreateRemoteAlbum(); var id = await Subject.Download(remoteAlbum, CreateIndexer()); id.Should().NotBeNullOrEmpty(); } [Test] public void GetItems_should_ignore_downloads_from_other_categories() { _completed.Label = "myowncat"; PrepareClientToReturnCompletedItem(); var items = Subject.GetItems(); items.Should().BeEmpty(); } // Proxy.GetTorrents does not return original url. So item has to be found via magnet url. [TestCase("magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR&tr=udp", "CBC2F069FE8BB2F544EAE707D75BCD3DE9DCF951")] public async Task Download_should_get_hash_from_magnet_url(string magnetUrl, string expectedHash) { GivenSuccessfulDownload(); var remoteAlbum = CreateRemoteAlbum(); remoteAlbum.Release.DownloadUrl = magnetUrl; var id = await Subject.Download(remoteAlbum, CreateIndexer()); id.Should().Be(expectedHash); } [TestCase(UTorrentTorrentStatus.Loaded, DownloadItemStatus.Queued)] [TestCase(UTorrentTorrentStatus.Loaded | UTorrentTorrentStatus.Checking, DownloadItemStatus.Queued)] [TestCase(UTorrentTorrentStatus.Loaded | UTorrentTorrentStatus.Queued, DownloadItemStatus.Queued)] [TestCase(UTorrentTorrentStatus.Loaded | UTorrentTorrentStatus.Started, DownloadItemStatus.Downloading)] [TestCase(UTorrentTorrentStatus.Loaded | UTorrentTorrentStatus.Queued | UTorrentTorrentStatus.Started, DownloadItemStatus.Downloading)] public void GetItems_should_return_queued_item_as_downloadItemStatus(UTorrentTorrentStatus apiStatus, DownloadItemStatus expectedItemStatus) { _queued.Status = apiStatus; PrepareClientToReturnQueuedItem(); var item = Subject.GetItems().Single(); item.Status.Should().Be(expectedItemStatus); } [TestCase(UTorrentTorrentStatus.Loaded | UTorrentTorrentStatus.Checking, DownloadItemStatus.Queued)] [TestCase(UTorrentTorrentStatus.Loaded | UTorrentTorrentStatus.Checked | UTorrentTorrentStatus.Queued, DownloadItemStatus.Queued)] [TestCase(UTorrentTorrentStatus.Loaded | UTorrentTorrentStatus.Started, DownloadItemStatus.Downloading)] [TestCase(UTorrentTorrentStatus.Loaded | UTorrentTorrentStatus.Queued | UTorrentTorrentStatus.Started, DownloadItemStatus.Downloading)] public void GetItems_should_return_downloading_item_as_downloadItemStatus(UTorrentTorrentStatus apiStatus, DownloadItemStatus expectedItemStatus) { _downloading.Status = apiStatus; PrepareClientToReturnDownloadingItem(); var item = Subject.GetItems().Single(); item.Status.Should().Be(expectedItemStatus); } [TestCase(UTorrentTorrentStatus.Loaded | UTorrentTorrentStatus.Checking, DownloadItemStatus.Queued, true)] [TestCase(UTorrentTorrentStatus.Loaded | UTorrentTorrentStatus.Checked, DownloadItemStatus.Completed, true)] [TestCase(UTorrentTorrentStatus.Loaded | UTorrentTorrentStatus.Checked | UTorrentTorrentStatus.Queued, DownloadItemStatus.Completed, false)] [TestCase(UTorrentTorrentStatus.Loaded | UTorrentTorrentStatus.Checked | UTorrentTorrentStatus.Started, DownloadItemStatus.Completed, false)] [TestCase(UTorrentTorrentStatus.Loaded | UTorrentTorrentStatus.Checked | UTorrentTorrentStatus.Queued | UTorrentTorrentStatus.Paused, DownloadItemStatus.Completed, false)] public void GetItems_should_return_completed_item_as_downloadItemStatus(UTorrentTorrentStatus apiStatus, DownloadItemStatus expectedItemStatus, bool expectedValue) { _completed.Status = apiStatus; PrepareClientToReturnCompletedItem(); var item = Subject.GetItems().Single(); item.Status.Should().Be(expectedItemStatus); item.CanBeRemoved.Should().Be(expectedValue); item.CanMoveFiles.Should().Be(expectedValue); } [Test] public void should_return_status_with_outputdirs() { var configItems = new Dictionary(); configItems.Add("dir_active_download_flag", "true"); configItems.Add("dir_active_download", @"C:\Downloads\Downloading\utorrent".AsOsAgnostic()); configItems.Add("dir_completed_download", @"C:\Downloads\Finished\utorrent".AsOsAgnostic()); configItems.Add("dir_completed_download_flag", "true"); configItems.Add("dir_add_label", "true"); Mocker.GetMock() .Setup(v => v.GetConfig(It.IsAny())) .Returns(configItems); var result = Subject.GetStatus(); result.IsLocalhost.Should().BeTrue(); result.OutputRootFolders.Should().NotBeNull(); result.OutputRootFolders.First().Should().Be(@"C:\Downloads\Finished\utorrent\lidarr".AsOsAgnostic()); } [Test] public void should_combine_drive_letter() { WindowsOnly(); _completed.RootDownloadPath = "D:"; PrepareClientToReturnCompletedItem(); var item = Subject.GetItems().Single(); item.OutputPath.Should().Be(@"D:\" + _title); } [Test] public async Task Download_should_handle_http_redirect_to_magnet() { GivenRedirectToMagnet(); GivenSuccessfulDownload(); var remoteAlbum = CreateRemoteAlbum(); var id = await Subject.Download(remoteAlbum, CreateIndexer()); id.Should().NotBeNullOrEmpty(); } [Test] public async Task Download_should_handle_http_redirect_to_torrent() { GivenRedirectToTorrent(); GivenSuccessfulDownload(); var remoteAlbum = CreateRemoteAlbum(); var id = await Subject.Download(remoteAlbum, CreateIndexer()); id.Should().NotBeNullOrEmpty(); } [Test] public void GetItems_should_query_with_cache_id_if_available() { _downloading.Status = UTorrentTorrentStatus.Started; GivenTorrents(new List { _downloading }, "abc"); var item1 = Subject.GetItems().Single(); Mocker.GetMock().Verify(v => v.GetTorrents(null, It.IsAny()), Times.Once()); GivenTorrents(new List { _downloading, _queued }, "abc2"); GivenDifferentialTorrents("abc", new List { _queued }, new List(), "abc2"); GivenDifferentialTorrents("abc2", new List(), new List(), "abc2"); var item2 = Subject.GetItems().Single(); Mocker.GetMock().Verify(v => v.GetTorrents("abc", It.IsAny()), Times.Once()); var item3 = Subject.GetItems().Single(); Mocker.GetMock().Verify(v => v.GetTorrents("abc2", It.IsAny()), Times.Once()); } } }