diff --git a/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index 70086788a..6a967392b 100644 --- a/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -213,6 +213,7 @@ + diff --git a/src/NzbDrone.Core.Test/TvTests/EpisodeRepositoryTests/ByAirDateFixture.cs b/src/NzbDrone.Core.Test/TvTests/EpisodeRepositoryTests/ByAirDateFixture.cs new file mode 100644 index 000000000..2f6c0cef5 --- /dev/null +++ b/src/NzbDrone.Core.Test/TvTests/EpisodeRepositoryTests/ByAirDateFixture.cs @@ -0,0 +1,71 @@ +using System; +using FizzWare.NBuilder; +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Core.Tv; + +namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests +{ + [TestFixture] + public class ByAirDateFixture : DbTest + { + private const int SERIES_ID = 1; + private const string AIR_DATE = "2014-04-02"; + + private void GivenEpisode(int seasonNumber) + { + var episode = Builder.CreateNew() + .With(e => e.SeriesId = 1) + .With(e => e.SeasonNumber = seasonNumber) + .With(e => e.AirDate = AIR_DATE) + .BuildNew(); + + Db.Insert(episode); + } + + [Test] + public void should_throw_when_multiple_regular_episodes_are_found() + { + GivenEpisode(1); + GivenEpisode(2); + + Assert.Throws(() => Subject.Get(SERIES_ID, AIR_DATE)); + Assert.Throws(() => Subject.Find(SERIES_ID, AIR_DATE)); + } + + [Test] + public void should_throw_when_get_finds_no_episode() + { + Assert.Throws(() => Subject.Get(SERIES_ID, AIR_DATE)); + } + + [Test] + public void should_get_episode_when_single_episode_exists_for_air_date() + { + GivenEpisode(1); + + Subject.Get(SERIES_ID, AIR_DATE).Should().NotBeNull(); + Subject.Find(SERIES_ID, AIR_DATE).Should().NotBeNull(); + } + + [Test] + public void should_get_episode_when_regular_episode_and_special_share_the_same_air_date() + { + GivenEpisode(1); + GivenEpisode(0); + + Subject.Get(SERIES_ID, AIR_DATE).Should().NotBeNull(); + Subject.Find(SERIES_ID, AIR_DATE).Should().NotBeNull(); + } + + [Test] + public void should_get_special_when_its_the_only_episode_for_the_date_provided() + { + GivenEpisode(0); + + Subject.Get(SERIES_ID, AIR_DATE).Should().NotBeNull(); + Subject.Find(SERIES_ID, AIR_DATE).Should().NotBeNull(); + } + } +} diff --git a/src/NzbDrone.Core/Tv/EpisodeRepository.cs b/src/NzbDrone.Core/Tv/EpisodeRepository.cs index ed750848f..2a6a1792e 100644 --- a/src/NzbDrone.Core/Tv/EpisodeRepository.cs +++ b/src/NzbDrone.Core/Tv/EpisodeRepository.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using Marr.Data.QGen; +using NLog; using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore.Extentions; using NzbDrone.Core.Messaging.Events; @@ -14,8 +16,8 @@ namespace NzbDrone.Core.Tv { Episode Find(int seriesId, int season, int episodeNumber); Episode Find(int seriesId, int absoluteEpisodeNumber); - Episode Get(int seriesId, String date); - Episode Find(int seriesId, String date); + Episode Get(int seriesId, string date); + Episode Find(int seriesId, string date); List GetEpisodes(int seriesId); List GetEpisodes(int seriesId, int seasonNumber); List GetEpisodeByFileId(int fileId); @@ -32,11 +34,13 @@ namespace NzbDrone.Core.Tv public class EpisodeRepository : BasicRepository, IEpisodeRepository { private readonly IDatabase _database; + private readonly Logger _logger; - public EpisodeRepository(IDatabase database, IEventAggregator eventAggregator) + public EpisodeRepository(IDatabase database, IEventAggregator eventAggregator, Logger logger) : base(database, eventAggregator) { _database = database; + _logger = logger; } public Episode Find(int seriesId, int season, int episodeNumber) @@ -54,18 +58,21 @@ namespace NzbDrone.Core.Tv .SingleOrDefault(); } - public Episode Get(int seriesId, String date) + public Episode Get(int seriesId, string date) { - return Query.Where(s => s.SeriesId == seriesId) - .AndWhere(s => s.AirDate == date) - .Single(); + var episode = FindOneByAirDate(seriesId, date); + + if (episode == null) + { + throw new InvalidOperationException("Expected at one episode"); + } + + return episode; } - public Episode Find(int seriesId, String date) + public Episode Find(int seriesId, string date) { - return Query.Where(s => s.SeriesId == seriesId) - .AndWhere(s => s.AirDate == date) - .SingleOrDefault(); + return FindOneByAirDate(seriesId, date); } public List GetEpisodes(int seriesId) @@ -207,5 +214,28 @@ namespace NzbDrone.Core.Tv return String.Format("({0})", String.Join(" OR ", clauses)); } + + private Episode FindOneByAirDate(int seriesId, string date) + { + var episodes = Query.Where(s => s.SeriesId == seriesId) + .AndWhere(s => s.AirDate == date) + .ToList(); + + if (!episodes.Any()) return null; + + if (episodes.Count == 1) return episodes.First(); + + _logger.Debug("Multiple episodes with the same air date were found, will exclude specials"); + + var regularEpisodes = episodes.Where(e => e.SeasonNumber > 0).ToList(); + + if (regularEpisodes.Count == 1) + { + _logger.Debug("Left with one episode after excluding specials"); + return regularEpisodes.First(); + } + + throw new InvalidOperationException("Multiple episodes with the same air date found"); + } } }