You've already forked Sonarr
							
							
				mirror of
				https://github.com/Sonarr/Sonarr.git
				synced 2025-10-31 00:07:55 +02:00 
			
		
		
		
	DeleteInvalidEpisodes with tests added to delete episodes that TheTvDb no longer has (previously bad data).
This commit is contained in:
		
							
								
								
									
										323
									
								
								NzbDrone.Core.Test/EpisodeProviderTest_DeleteInvalidEpisodes.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										323
									
								
								NzbDrone.Core.Test/EpisodeProviderTest_DeleteInvalidEpisodes.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,323 @@ | ||||
| // ReSharper disable RedundantUsingDirective | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using AutoMoq; | ||||
| using FizzWare.NBuilder; | ||||
| using FluentAssertions; | ||||
| using Moq; | ||||
| using NUnit.Framework; | ||||
| using NzbDrone.Core.Providers; | ||||
| using NzbDrone.Core.Providers.Core; | ||||
| using NzbDrone.Core.Repository; | ||||
| using NzbDrone.Core.Repository.Quality; | ||||
| using NzbDrone.Core.Test.Framework; | ||||
| using PetaPoco; | ||||
| using TvdbLib.Data; | ||||
|  | ||||
| namespace NzbDrone.Core.Test | ||||
| { | ||||
|     [TestFixture] | ||||
|     // ReSharper disable InconsistentNaming | ||||
|     public class EpisodeProviderTest_DeleteInvalidEpisodes : TestBase | ||||
|     { | ||||
|         [Test] | ||||
|         public void Delete_None() | ||||
|         { | ||||
|             //Setup | ||||
|             const int seriesId = 71663; | ||||
|             const int episodeCount = 10; | ||||
|  | ||||
|             var tvDbSeries = Builder<TvdbSeries>.CreateNew().With( | ||||
|                 c => c.Episodes = | ||||
|                      new List<TvdbEpisode>(Builder<TvdbEpisode>.CreateListOfSize(episodeCount). | ||||
|                                                WhereAll() | ||||
|                                                .Have(l => l.Language = new TvdbLanguage(0, "eng", "a")) | ||||
|                                                .Build()) | ||||
|                 ).With(c => c.Id = seriesId).Build(); | ||||
|  | ||||
|             var fakeSeries = Builder<Series>.CreateNew() | ||||
|                 .With(c => c.SeriesId = seriesId) | ||||
|                 .Build(); | ||||
|  | ||||
|             var fakeEpisode = Builder<Episode>.CreateNew() | ||||
|                 .With(e => e.SeriesId = seriesId) | ||||
|                 .With(e => e.SeasonNumber = 20) | ||||
|                 .With(e => e.EpisodeNumber = 20) | ||||
|                 .Build(); | ||||
|  | ||||
|             var mocker = new AutoMoqer(); | ||||
|  | ||||
|             var db = MockLib.GetEmptyDatabase(); | ||||
|             mocker.SetConstant(db); | ||||
|  | ||||
|             db.Insert(fakeSeries); | ||||
|             db.Insert(fakeEpisode); | ||||
|  | ||||
|             //Act | ||||
|             mocker.Resolve<EpisodeProvider>().DeleteInvalidEpisodes(fakeSeries, tvDbSeries); | ||||
|  | ||||
|             //Assert | ||||
|             var result = db.Fetch<Episode>(); | ||||
|             result.Should().HaveCount(1); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void Delete_TvDbId() | ||||
|         { | ||||
|             //Setup | ||||
|             const int seriesId = 71663; | ||||
|             const int episodeCount = 10; | ||||
|  | ||||
|             var tvDbSeries = Builder<TvdbSeries>.CreateNew().With( | ||||
|                 c => c.Episodes = | ||||
|                      new List<TvdbEpisode>(Builder<TvdbEpisode>.CreateListOfSize(episodeCount). | ||||
|                                                WhereAll() | ||||
|                                                .Have(l => l.Language = new TvdbLanguage(0, "eng", "a")) | ||||
|                                                .Build()) | ||||
|                 ).With(c => c.Id = seriesId).Build(); | ||||
|  | ||||
|             var fakeSeries = Builder<Series>.CreateNew() | ||||
|                 .With(c => c.SeriesId = seriesId) | ||||
|                 .Build(); | ||||
|  | ||||
|             var fakeEpisode = Builder<Episode>.CreateNew() | ||||
|                 .With(e => e.SeriesId = seriesId) | ||||
|                 .With(e => e.SeasonNumber = 20) | ||||
|                 .With(e => e.EpisodeNumber = 20) | ||||
|                 .With(e => e.TvDbEpisodeId = 300) | ||||
|                 .Build(); | ||||
|  | ||||
|             var mocker = new AutoMoqer(); | ||||
|  | ||||
|             var db = MockLib.GetEmptyDatabase(); | ||||
|             mocker.SetConstant(db); | ||||
|  | ||||
|             db.Insert(fakeSeries); | ||||
|             db.Insert(fakeEpisode); | ||||
|  | ||||
|             //Act | ||||
|             mocker.Resolve<EpisodeProvider>().DeleteInvalidEpisodes(fakeSeries, tvDbSeries); | ||||
|  | ||||
|             //Assert | ||||
|             var result = db.Fetch<Episode>(); | ||||
|             result.Should().HaveCount(0); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void Delete_EpisodeNumber() | ||||
|         { | ||||
|             //Setup | ||||
|             const int seriesId = 71663; | ||||
|             const int episodeCount = 10; | ||||
|  | ||||
|             var tvDbSeries = Builder<TvdbSeries>.CreateNew().With( | ||||
|                 c => c.Episodes = | ||||
|                      new List<TvdbEpisode>(Builder<TvdbEpisode>.CreateListOfSize(episodeCount). | ||||
|                                                WhereAll() | ||||
|                                                .Have(l => l.Language = new TvdbLanguage(0, "eng", "a")) | ||||
|                                                .Build()) | ||||
|                 ).With(c => c.Id = seriesId).Build(); | ||||
|  | ||||
|             var fakeSeries = Builder<Series>.CreateNew() | ||||
|                 .With(c => c.SeriesId = seriesId) | ||||
|                 .Build(); | ||||
|  | ||||
|             var fakeEpisode = Builder<Episode>.CreateNew() | ||||
|                 .With(e => e.SeriesId = seriesId) | ||||
|                 .With(e => e.SeasonNumber = 1) | ||||
|                 .With(e => e.EpisodeNumber = 20) | ||||
|                 .With(e => e.TvDbEpisodeId = 1) | ||||
|                 .Build(); | ||||
|  | ||||
|             var mocker = new AutoMoqer(); | ||||
|  | ||||
|             var db = MockLib.GetEmptyDatabase(); | ||||
|             mocker.SetConstant(db); | ||||
|  | ||||
|             db.Insert(fakeSeries); | ||||
|             db.Insert(fakeEpisode); | ||||
|  | ||||
|             //Act | ||||
|             mocker.Resolve<EpisodeProvider>().DeleteInvalidEpisodes(fakeSeries, tvDbSeries); | ||||
|  | ||||
|             //Assert | ||||
|             var result = db.Fetch<Episode>(); | ||||
|             result.Should().HaveCount(0); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void Delete_Both() | ||||
|         { | ||||
|             //Setup | ||||
|             const int seriesId = 71663; | ||||
|             const int episodeCount = 10; | ||||
|  | ||||
|             var tvDbSeries = Builder<TvdbSeries>.CreateNew().With( | ||||
|                 c => c.Episodes = | ||||
|                      new List<TvdbEpisode>(Builder<TvdbEpisode>.CreateListOfSize(episodeCount). | ||||
|                                                WhereAll() | ||||
|                                                .Have(l => l.Language = new TvdbLanguage(0, "eng", "a")) | ||||
|                                                .Build()) | ||||
|                 ).With(c => c.Id = seriesId).Build(); | ||||
|  | ||||
|             var fakeSeries = Builder<Series>.CreateNew() | ||||
|                 .With(c => c.SeriesId = seriesId) | ||||
|                 .Build(); | ||||
|  | ||||
|             var fakeEpisode1 = Builder<Episode>.CreateNew() | ||||
|                 .With(e => e.SeriesId = seriesId) | ||||
|                 .With(e => e.SeasonNumber = 1) | ||||
|                 .With(e => e.EpisodeNumber = 20) | ||||
|                 .With(e => e.TvDbEpisodeId = 1) | ||||
|                 .Build(); | ||||
|  | ||||
|             var fakeEpisode2 = Builder<Episode>.CreateNew() | ||||
|                 .With(e => e.SeriesId = seriesId) | ||||
|                 .With(e => e.SeasonNumber = 1) | ||||
|                 .With(e => e.EpisodeNumber = 1) | ||||
|                 .With(e => e.TvDbEpisodeId = 300) | ||||
|                 .Build(); | ||||
|  | ||||
|             //This should not be deleted | ||||
|             var fakeEpisode3 = Builder<Episode>.CreateNew() | ||||
|                 .With(e => e.SeriesId = seriesId) | ||||
|                 .With(e => e.SeasonNumber = 1) | ||||
|                 .With(e => e.EpisodeNumber = 1) | ||||
|                 .With(e => e.TvDbEpisodeId = 1) | ||||
|                 .With(e => e.Title = "Not Deleted") | ||||
|                 .Build(); | ||||
|  | ||||
|             var mocker = new AutoMoqer(); | ||||
|  | ||||
|             var db = MockLib.GetEmptyDatabase(); | ||||
|             mocker.SetConstant(db); | ||||
|  | ||||
|             db.Insert(fakeSeries); | ||||
|             db.Insert(fakeEpisode1); | ||||
|             db.Insert(fakeEpisode2); | ||||
|             db.Insert(fakeEpisode3); | ||||
|  | ||||
|             //Act | ||||
|             mocker.Resolve<EpisodeProvider>().DeleteInvalidEpisodes(fakeSeries, tvDbSeries); | ||||
|  | ||||
|             //Assert | ||||
|             var result = db.Fetch<Episode>(); | ||||
|             result.Should().HaveCount(1); | ||||
|             result.First().Title.Should().Be("Not Deleted"); | ||||
|         } | ||||
|  | ||||
|         //Other series, by season/episode + by tvdbid | ||||
|         [Test] | ||||
|         public void Delete_TvDbId_multiple_series() | ||||
|         { | ||||
|             //Setup | ||||
|             const int seriesId = 71663; | ||||
|             const int episodeCount = 10; | ||||
|  | ||||
|             var tvDbSeries = Builder<TvdbSeries>.CreateNew().With( | ||||
|                 c => c.Episodes = | ||||
|                      new List<TvdbEpisode>(Builder<TvdbEpisode>.CreateListOfSize(episodeCount). | ||||
|                                                WhereAll() | ||||
|                                                .Have(l => l.Language = new TvdbLanguage(0, "eng", "a")) | ||||
|                                                .Build()) | ||||
|                 ).With(c => c.Id = seriesId).Build(); | ||||
|  | ||||
|             var fakeSeries = Builder<Series>.CreateNew() | ||||
|                 .With(c => c.SeriesId = seriesId) | ||||
|                 .Build(); | ||||
|  | ||||
|             var fakeEpisode = Builder<Episode>.CreateNew() | ||||
|                 .With(e => e.SeriesId = seriesId) | ||||
|                 .With(e => e.SeasonNumber = 20) | ||||
|                 .With(e => e.EpisodeNumber = 20) | ||||
|                 .With(e => e.TvDbEpisodeId = 300) | ||||
|                 .Build(); | ||||
|  | ||||
|             //Other Series | ||||
|             var otherFakeSeries = Builder<Series>.CreateNew() | ||||
|                 .With(c => c.SeriesId = 12345) | ||||
|                 .Build(); | ||||
|  | ||||
|             var otherFakeEpisode = Builder<Episode>.CreateNew() | ||||
|                 .With(e => e.SeriesId = 12345) | ||||
|                 .With(e => e.SeasonNumber = 20) | ||||
|                 .With(e => e.EpisodeNumber = 20) | ||||
|                 .With(e => e.TvDbEpisodeId = 300) | ||||
|                 .Build(); | ||||
|  | ||||
|             var mocker = new AutoMoqer(); | ||||
|  | ||||
|             var db = MockLib.GetEmptyDatabase(); | ||||
|             mocker.SetConstant(db); | ||||
|  | ||||
|             db.Insert(fakeSeries); | ||||
|             db.Insert(fakeEpisode); | ||||
|             db.Insert(otherFakeSeries); | ||||
|             db.Insert(otherFakeEpisode); | ||||
|  | ||||
|             //Act | ||||
|             mocker.Resolve<EpisodeProvider>().DeleteInvalidEpisodes(fakeSeries, tvDbSeries); | ||||
|  | ||||
|             //Assert | ||||
|             var result = db.Fetch<Episode>(); | ||||
|             result.Should().HaveCount(1); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void Delete_EpisodeNumber_multiple_series() | ||||
|         { | ||||
|             //Setup | ||||
|             const int seriesId = 71663; | ||||
|             const int episodeCount = 10; | ||||
|  | ||||
|             var tvDbSeries = Builder<TvdbSeries>.CreateNew().With( | ||||
|                 c => c.Episodes = | ||||
|                      new List<TvdbEpisode>(Builder<TvdbEpisode>.CreateListOfSize(episodeCount). | ||||
|                                                WhereAll() | ||||
|                                                .Have(l => l.Language = new TvdbLanguage(0, "eng", "a")) | ||||
|                                                .Build()) | ||||
|                 ).With(c => c.Id = seriesId).Build(); | ||||
|  | ||||
|             var fakeSeries = Builder<Series>.CreateNew() | ||||
|                 .With(c => c.SeriesId = seriesId) | ||||
|                 .Build(); | ||||
|  | ||||
|             var fakeEpisode = Builder<Episode>.CreateNew() | ||||
|                 .With(e => e.SeriesId = seriesId) | ||||
|                 .With(e => e.SeasonNumber = 1) | ||||
|                 .With(e => e.EpisodeNumber = 20) | ||||
|                 .With(e => e.TvDbEpisodeId = 1) | ||||
|                 .Build(); | ||||
|  | ||||
|             //Other Series | ||||
|             var otherFakeSeries = Builder<Series>.CreateNew() | ||||
|                 .With(c => c.SeriesId = 12345) | ||||
|                 .Build(); | ||||
|  | ||||
|             var otherFakeEpisode = Builder<Episode>.CreateNew() | ||||
|                 .With(e => e.SeriesId = 12345) | ||||
|                 .With(e => e.SeasonNumber = 1) | ||||
|                 .With(e => e.EpisodeNumber = 4) | ||||
|                 .With(e => e.TvDbEpisodeId = 2) | ||||
|                 .Build(); | ||||
|  | ||||
|             var mocker = new AutoMoqer(); | ||||
|  | ||||
|             var db = MockLib.GetEmptyDatabase(); | ||||
|             mocker.SetConstant(db); | ||||
|  | ||||
|             db.Insert(fakeSeries); | ||||
|             db.Insert(fakeEpisode); | ||||
|             db.Insert(otherFakeSeries); | ||||
|             db.Insert(otherFakeEpisode); | ||||
|  | ||||
|             //Act | ||||
|             mocker.Resolve<EpisodeProvider>().DeleteInvalidEpisodes(fakeSeries, tvDbSeries); | ||||
|  | ||||
|             //Assert | ||||
|             var result = db.Fetch<Episode>(); | ||||
|             result.Should().HaveCount(1); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -90,6 +90,7 @@ | ||||
|   <ItemGroup> | ||||
|     <Compile Include="BacklogSearchJobTest.cs" /> | ||||
|     <Compile Include="BannerDownloadJobTest.cs" /> | ||||
|     <Compile Include="EpisodeProviderTest_DeleteInvalidEpisodes.cs" /> | ||||
|     <Compile Include="InventoryProvider_IsAcceptableSizeTest.cs" /> | ||||
|     <Compile Include="QualityTypeProviderTest.cs" /> | ||||
|     <Compile Include="MisnamedProviderTest.cs" /> | ||||
|   | ||||
| @@ -6,6 +6,7 @@ using NLog; | ||||
| using NzbDrone.Core.Model; | ||||
| using NzbDrone.Core.Repository; | ||||
| using PetaPoco; | ||||
| using TvdbLib.Data; | ||||
|  | ||||
| namespace NzbDrone.Core.Providers | ||||
| { | ||||
| @@ -262,6 +263,9 @@ namespace NzbDrone.Core.Providers | ||||
|  | ||||
|             Logger.Info("Finished episode refresh for series: {0}. Successful: {1} - Failed: {2} ", | ||||
|                          tvDbSeriesInfo.SeriesName, successCount, failCount); | ||||
|  | ||||
|             //DeleteInvalidEpisodes | ||||
|             DeleteInvalidEpisodes(series, tvDbSeriesInfo); | ||||
|         } | ||||
|  | ||||
|         public virtual void UpdateEpisode(Episode episode) | ||||
| @@ -364,5 +368,38 @@ namespace NzbDrone.Core.Providers | ||||
|             episode.Series = _seriesProvider.GetSeries(episode.SeriesId); | ||||
|             return episode; | ||||
|         } | ||||
|  | ||||
|         public virtual void DeleteInvalidEpisodes(Series series, TvdbSeries tvDbSeriesInfo) | ||||
|         { | ||||
|             Logger.Info("Starting deletion of invalid episode for series: {0}", series.Title.WithDefault(series.SeriesId)); | ||||
|  | ||||
|             var seasons = tvDbSeriesInfo.Episodes.Select(e => e.SeasonNumber).Distinct(); | ||||
|  | ||||
|             foreach (var s in seasons) | ||||
|             { | ||||
|                 //Avoiding accessing modified closure | ||||
|                 var season = s; | ||||
|                 Logger.Trace("Processing invalid episodes for {0}, Season: {1}", series.SeriesId, season); | ||||
|  | ||||
|                 var episodesInSeason = tvDbSeriesInfo.Episodes.Where(e => e.SeasonNumber == season).Select(e => e.EpisodeNumber); | ||||
|                 var episodesString = String.Join(", ", episodesInSeason); | ||||
|                 var seasonQuery = String.Format("DELETE FROM Episodes WHERE SeriesId = {0} AND SeasonNumber = {1} AND EpisodeNumber NOT IN ({2})", | ||||
|                                             series.SeriesId, season, episodesString); | ||||
|  | ||||
|                 _database.Execute(seasonQuery); | ||||
|             } | ||||
|  | ||||
|             //Delete Episodes not matching TvDbIds for this series | ||||
|             var tvDbIds = tvDbSeriesInfo.Episodes.Select(e => e.Id); | ||||
|             var tvDbIdString = String.Join(", ", tvDbIds); | ||||
|  | ||||
|             var tvDbIdQuery = String.Format("DELETE FROM Episodes WHERE SeriesId = {0} AND TvDbEpisodeId > 0 AND TvDbEpisodeId NOT IN ({1})", | ||||
|                                                                                     series.SeriesId, tvDbIdString); | ||||
|  | ||||
|             Logger.Trace("Deleting nivalid episodes by TvDbId for {0}", series.SeriesId); | ||||
|             _database.Execute(tvDbIdQuery); | ||||
|  | ||||
|             Logger.Trace("Finished deleting invalid episodes for {0}", series.SeriesId); | ||||
|         } | ||||
|     } | ||||
| } | ||||