diff --git a/src/NzbDrone.Api/Queue/QueueActionModule.cs b/src/NzbDrone.Api/Queue/QueueActionModule.cs index 1b23c4a7a..9882e60e6 100644 --- a/src/NzbDrone.Api/Queue/QueueActionModule.cs +++ b/src/NzbDrone.Api/Queue/QueueActionModule.cs @@ -55,7 +55,7 @@ namespace NzbDrone.Api.Queue if (pendingRelease != null) { - _pendingReleaseService.RemovePendingQueueItem(id); + _pendingReleaseService.RemovePendingQueueItems(pendingRelease.Id); return new object().AsResponse(); } diff --git a/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/RemovePendingFixture.cs b/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/RemovePendingFixture.cs new file mode 100644 index 000000000..cdb1e45bf --- /dev/null +++ b/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/RemovePendingFixture.cs @@ -0,0 +1,119 @@ +using System.Collections.Generic; +using System.Linq; +using Moq; +using NUnit.Framework; +using NzbDrone.Core.Download.Pending; +using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.Test.Framework; + +namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests +{ + [TestFixture] + public class RemovePendingFixture : CoreTest + { + + private List _pending; + + [SetUp] + public void Setup() + { + _pending = new List(); + + Mocker.GetMock() + .Setup(s => s.AllBySeriesId(It.IsAny())) + .Returns(_pending); + + Mocker.GetMock() + .Setup(s => s.Get(It.IsAny())) + .Returns(r => _pending.Single(c => c.Id == r)); + } + + private void AddPending(int id, int seasonNumber, int[] episodes) + { + _pending.Add(new PendingRelease + { + Id = id, + ParsedEpisodeInfo = new ParsedEpisodeInfo { SeasonNumber = seasonNumber, EpisodeNumbers = episodes } + }); + } + + [Test] + public void should_remove_same_release() + { + AddPending(id: 1, seasonNumber: 2, episodes: new[] { 3 }); + + Subject.RemovePendingQueueItems(1); + + AssertRemoved(1); + } + + + [Test] + public void should_remove_multiple_releases_release() + { + AddPending(id: 1, seasonNumber: 2, episodes: new[] { 1 }); + AddPending(id: 2, seasonNumber: 2, episodes: new[] { 2 }); + AddPending(id: 3, seasonNumber: 2, episodes: new[] { 3 }); + AddPending(id: 4, seasonNumber: 2, episodes: new[] { 3 }); + + Subject.RemovePendingQueueItems(3); + + AssertRemoved(3, 4); + } + + [Test] + public void should_not_remove_diffrent_season() + { + AddPending(id: 1, seasonNumber: 2, episodes: new[] { 1 }); + AddPending(id: 2, seasonNumber: 2, episodes: new[] { 1 }); + AddPending(id: 3, seasonNumber: 3, episodes: new[] { 1 }); + AddPending(id: 4, seasonNumber: 3, episodes: new[] { 1 }); + + Subject.RemovePendingQueueItems(1); + + AssertRemoved(1, 2); + } + + [Test] + public void should_not_remove_diffrent_episodes() + { + AddPending(id: 1, seasonNumber: 2, episodes: new[] { 1 }); + AddPending(id: 2, seasonNumber: 2, episodes: new[] { 1 }); + AddPending(id: 3, seasonNumber: 2, episodes: new[] { 2 }); + AddPending(id: 4, seasonNumber: 2, episodes: new[] { 3 }); + + Subject.RemovePendingQueueItems(1); + + AssertRemoved(1, 2); + } + + [Test] + public void should_not_remove_multiepisodes() + { + AddPending(id: 1, seasonNumber: 2, episodes: new[] { 1 }); + AddPending(id: 2, seasonNumber: 2, episodes: new[] { 1, 2 }); + + Subject.RemovePendingQueueItems(1); + + AssertRemoved(1); + } + + [Test] + public void should_not_remove_singleepisodes() + { + AddPending(id: 1, seasonNumber: 2, episodes: new[] { 1 }); + AddPending(id: 2, seasonNumber: 2, episodes: new[] { 1, 2 }); + + Subject.RemovePendingQueueItems(2); + + AssertRemoved(2); + } + + + private void AssertRemoved(params int[] ids) + { + Mocker.GetMock().Verify(c => c.DeleteMany(It.Is>(s => s.SequenceEqual(ids)))); + } + } + +} diff --git a/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index b1b406d93..212d4ab1d 100644 --- a/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -166,6 +166,7 @@ + diff --git a/src/NzbDrone.Core/Download/Pending/PendingReleaseService.cs b/src/NzbDrone.Core/Download/Pending/PendingReleaseService.cs index dd4c11966..40cba94fc 100644 --- a/src/NzbDrone.Core/Download/Pending/PendingReleaseService.cs +++ b/src/NzbDrone.Core/Download/Pending/PendingReleaseService.cs @@ -21,12 +21,12 @@ namespace NzbDrone.Core.Download.Pending public interface IPendingReleaseService { void Add(DownloadDecision decision); - + List GetPending(); List GetPendingRemoteEpisodes(int seriesId); List GetPendingQueue(); Queue.Queue FindPendingQueueItem(int queueId); - void RemovePendingQueueItem(int queueId); + void RemovePendingQueueItems(int episodeId); RemoteEpisode OldestPendingRelease(int seriesId, IEnumerable episodeIds); } @@ -63,7 +63,7 @@ namespace NzbDrone.Core.Download.Pending _logger = logger; } - + public void Add(DownloadDecision decision) { var alreadyPending = GetPendingReleases(); @@ -74,7 +74,7 @@ namespace NzbDrone.Core.Download.Pending .Intersect(episodeIds) .Any()); - if (existingReports.Any(MatchingReleasePredicate(decision))) + if (existingReports.Any(MatchingReleasePredicate(decision.RemoteEpisode.Release))) { _logger.Debug("This release is already pending, not adding again"); return; @@ -152,11 +152,17 @@ namespace NzbDrone.Core.Download.Pending return GetPendingQueue().SingleOrDefault(p => p.Id == queueId); } - public void RemovePendingQueueItem(int queueId) + public void RemovePendingQueueItems(int queueId) { - var id = FindPendingReleaseId(queueId); + var targetItem = _repository.Get(queueId); + var seriesReleases = _repository.AllBySeriesId(targetItem.SeriesId); + + + var releasesToRemove = seriesReleases.Where( + c => c.ParsedEpisodeInfo.SeasonNumber == targetItem.ParsedEpisodeInfo.SeasonNumber && + c.ParsedEpisodeInfo.EpisodeNumbers.SequenceEqual(targetItem.ParsedEpisodeInfo.EpisodeNumbers)); - _repository.Delete(id); + _repository.DeleteMany(releasesToRemove.Select(c => c.Id)); } public RemoteEpisode OldestPendingRelease(int seriesId, IEnumerable episodeIds) @@ -223,11 +229,11 @@ namespace NzbDrone.Core.Download.Pending _eventAggregator.PublishEvent(new PendingReleasesUpdatedEvent()); } - private Func MatchingReleasePredicate(DownloadDecision decision) + private static Func MatchingReleasePredicate(ReleaseInfo release) { - return p => p.Title == decision.RemoteEpisode.Release.Title && - p.Release.PublishDate == decision.RemoteEpisode.Release.PublishDate && - p.Release.Indexer == decision.RemoteEpisode.Release.Indexer; + return p => p.Title == release.Title && + p.Release.PublishDate == release.PublishDate && + p.Release.Indexer == release.Indexer; } private int GetDelay(RemoteEpisode remoteEpisode) @@ -236,7 +242,7 @@ namespace NzbDrone.Core.Download.Pending var delay = delayProfile.GetProtocolDelay(remoteEpisode.Release.DownloadProtocol); var minimumAge = _configService.MinimumAge; - return new [] { delay, minimumAge }.Max(); + return new[] { delay, minimumAge }.Max(); } private void RemoveGrabbed(RemoteEpisode remoteEpisode) @@ -278,7 +284,7 @@ namespace NzbDrone.Core.Download.Pending foreach (var rejectedRelease in rejected) { - var matching = pending.SingleOrDefault(MatchingReleasePredicate(rejectedRelease)); + var matching = pending.SingleOrDefault(MatchingReleasePredicate(rejectedRelease.RemoteEpisode.Release)); if (matching != null) {