using System.Text; using Bugsnag; using ErsatzTV.Core; using ErsatzTV.Scanner.Core.Metadata.Nfo; using FluentAssertions; using Microsoft.Extensions.Logging; using Microsoft.IO; using NSubstitute; using NUnit.Framework; using Serilog; namespace ErsatzTV.Scanner.Tests.Core.Metadata.Nfo; [TestFixture] public class EpisodeNfoReaderTests { [SetUp] public void SetUp() => _episodeNfoReader = new EpisodeNfoReader( new RecyclableMemoryStreamManager(), Substitute.For(), _logger); private readonly ILogger _logger; public EpisodeNfoReaderTests() { Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() .WriteTo.Console() .CreateLogger(); ILoggerFactory loggerFactory = new LoggerFactory().AddSerilog(Log.Logger); _logger = loggerFactory.CreateLogger(); } private EpisodeNfoReader _episodeNfoReader; [Test] public async Task One() { var stream = new MemoryStream( Encoding.UTF8.GetBytes( @" ")); Either> result = await _episodeNfoReader.Read(stream); result.IsRight.Should().BeTrue(); foreach (List list in result.RightToSeq()) { list.Count.Should().Be(1); } } [Test] public async Task Two() { var stream = new MemoryStream( Encoding.UTF8.GetBytes( @" show episode-one 1 1 show episode-two 2 1 ")); Either> result = await _episodeNfoReader.Read(stream); result.IsRight.Should().BeTrue(); foreach (List list in result.RightToSeq()) { list.Count.Should().Be(2); list.All(nfo => nfo.ShowTitle == "show").Should().BeTrue(); list.All(nfo => nfo.Season == 1).Should().BeTrue(); list.Count(nfo => nfo.Title == "episode-one" && nfo.Episode == 1).Should().Be(1); list.Count(nfo => nfo.Title == "episode-two" && nfo.Episode == 2).Should().Be(1); } } [Test] public async Task UniqueIds() { var stream = new MemoryStream( Encoding.UTF8.GetBytes( @" 12345 tt54321 ")); Either> result = await _episodeNfoReader.Read(stream); result.IsRight.Should().BeTrue(); foreach (List list in result.RightToSeq()) { list.Count.Should().Be(1); list[0].UniqueIds.Count.Should().Be(2); list[0].UniqueIds.Count(id => id.Default && id.Type == "tvdb" && id.Guid == "12345").Should().Be(1); list[0].UniqueIds.Count(id => !id.Default && id.Type == "imdb" && id.Guid == "tt54321").Should().Be(1); } } [Test] public async Task No_ContentRating() { var stream = new MemoryStream( Encoding.UTF8.GetBytes( @" ")); Either> result = await _episodeNfoReader.Read(stream); result.IsRight.Should().BeTrue(); foreach (List list in result.RightToSeq()) { list.Count.Should().Be(1); list[0].ContentRating.Should().BeNullOrEmpty(); } } [Test] public async Task ContentRating() { var stream = new MemoryStream( Encoding.UTF8.GetBytes( @" US:Something US:Something / US:SomethingElse ")); Either> result = await _episodeNfoReader.Read(stream); result.IsRight.Should().BeTrue(); foreach (List list in result.RightToSeq()) { list.Count.Should().Be(2); list.Count(nfo => nfo.ContentRating == "US:Something").Should().Be(1); list.Count(nfo => nfo.ContentRating == "US:Something / US:SomethingElse").Should().Be(1); } } [Test] public async Task No_Plot() { var stream = new MemoryStream( Encoding.UTF8.GetBytes( @" ")); Either> result = await _episodeNfoReader.Read(stream); result.IsRight.Should().BeTrue(); foreach (List list in result.RightToSeq()) { list.Count.Should().Be(1); list[0].Plot.Should().BeNullOrEmpty(); } } [Test] public async Task Plot() { var stream = new MemoryStream( Encoding.UTF8.GetBytes( @" Some Plot ")); Either> result = await _episodeNfoReader.Read(stream); result.IsRight.Should().BeTrue(); foreach (List list in result.RightToSeq()) { list.Count.Should().Be(1); list[0].Plot.Should().Be("Some Plot"); } } [Test] public async Task Actors() { var stream = new MemoryStream( Encoding.UTF8.GetBytes( @" Name 1 Role 1 Thumb 1 Name 2 Role 2 Thumb 2 ")); Either> result = await _episodeNfoReader.Read(stream); result.IsRight.Should().BeTrue(); foreach (List list in result.RightToSeq()) { list.Count.Should().Be(1); list[0].Actors.Count.Should().Be(2); list[0].Actors.Count(a => a.Name == "Name 1" && a.Role == "Role 1" && a.Thumb == "Thumb 1") .Should().Be(1); list[0].Actors.Count(a => a.Name == "Name 2" && a.Role == "Role 2" && a.Thumb == "Thumb 2") .Should().Be(1); } } [Test] public async Task Writers() { var stream = new MemoryStream( Encoding.UTF8.GetBytes( @" Writer 1 Writer 2 Writer 3 ")); Either> result = await _episodeNfoReader.Read(stream); result.IsRight.Should().BeTrue(); foreach (List list in result.RightToSeq()) { list.Count.Should().Be(2); list.Count(nfo => nfo.Writers.Count == 1 && nfo.Writers[0] == "Writer 1").Should().Be(1); list.Count(nfo => nfo.Writers.Count == 2 && nfo.Writers[0] == "Writer 2" && nfo.Writers[1] == "Writer 3") .Should().Be(1); } } [Test] public async Task Directors() { var stream = new MemoryStream( Encoding.UTF8.GetBytes( @" Director 1 Director 2 Director 3 ")); Either> result = await _episodeNfoReader.Read(stream); result.IsRight.Should().BeTrue(); foreach (List list in result.RightToSeq()) { list.Count.Should().Be(2); list.Count(nfo => nfo.Directors.Count == 1 && nfo.Directors[0] == "Director 1").Should().Be(1); list.Count( nfo => nfo.Directors.Count == 2 && nfo.Directors[0] == "Director 2" && nfo.Directors[1] == "Director 3") .Should().Be(1); } } [Test] public async Task Genres() { var stream = new MemoryStream( Encoding.UTF8.GetBytes( @" Genre 1 Genre 2 Genre 3 ")); Either> result = await _episodeNfoReader.Read(stream); result.IsRight.Should().BeTrue(); foreach (List list in result.RightToSeq()) { list.Count.Should().Be(2); list.Count(nfo => nfo.Genres is ["Genre 1"]).Should().Be(1); list.Count(nfo => nfo.Genres is ["Genre 2", "Genre 3"]).Should().Be(1); } } [Test] public async Task Tags() { var stream = new MemoryStream( Encoding.UTF8.GetBytes( @" Tag 1 Tag 2 Tag 3 ")); Either> result = await _episodeNfoReader.Read(stream); result.IsRight.Should().BeTrue(); foreach (List list in result.RightToSeq()) { list.Count.Should().Be(2); list.Count(nfo => nfo.Tags is ["Tag 1"]).Should().Be(1); list.Count(nfo => nfo.Tags is ["Tag 2", "Tag 3"]).Should().Be(1); } } [Test] public async Task FullSample_Should_Return_Nfo() { await using var stream = new MemoryStream( Encoding.UTF8.GetBytes( @" Filmed Before a Live Studio Audience WandaVision 7.500000 18766 7.500000 42 6.952780 3621 0 0 1 1 -1 -1 Wanda and Vision struggle to conceal their powers during dinner with Vision’s boss and his wife. 26 https://image.tmdb.org/t/p/original/cbe8l0Hnbvu07ePgoOopyWYrcdL.jpg https://image.tmdb.org/t/p/original/oNCzeCXFanVEWNpzRzyffhLLfZs.jpg Australia:TV-14 1 2021-03-27 1830976 tt9601584 1830976 8042515 Sci-Fi & Fantasy Mystery Drama Jac Schaeffer Matt Shakman 2021-01-15 2021 2021-01-15 Disney+ (US) Randall Park Jimmy Woo 4 https://image.tmdb.org/t/p/original/1QJ4cBQZoOaLR8Hc3V2NgBLvB0f.jpg Kat Dennings Darcy Lewis / The Escape Artist 5 https://image.tmdb.org/t/p/original/rrfyo9z1wW5nY9ZsFlj1Ozfj9g2.jpg 0.000000 0.000000 2021-02-02 11:57:44 ")); Either> result = await _episodeNfoReader.Read(stream); result.IsRight.Should().BeTrue(); foreach (EpisodeNfo nfo in result.RightToSeq().Flatten()) { nfo.ShowTitle.Should().Be("WandaVision"); nfo.Title.Should().Be("Filmed Before a Live Studio Audience"); nfo.Episode.Should().Be(1); nfo.Season.Should().Be(1); nfo.ContentRating.Should().Be("Australia:TV-14"); nfo.Aired.IsSome.Should().BeTrue(); foreach (DateTime aired in nfo.Aired) { aired.Should().Be(new DateTime(2021, 01, 15)); } nfo.Plot.Should().Be( "Wanda and Vision struggle to conceal their powers during dinner with Vision’s boss and his wife."); nfo.Actors.Should().BeEquivalentTo( new List { new() { Name = "Randall Park", Order = 4, Role = "Jimmy Woo", Thumb = "https://image.tmdb.org/t/p/original/1QJ4cBQZoOaLR8Hc3V2NgBLvB0f.jpg" }, new() { Name = "Kat Dennings", Order = 5, Role = "Darcy Lewis / The Escape Artist", Thumb = "https://image.tmdb.org/t/p/original/rrfyo9z1wW5nY9ZsFlj1Ozfj9g2.jpg" } }); nfo.Writers.Should().BeEquivalentTo(new List { "Jac Schaeffer" }); nfo.Directors.Should().BeEquivalentTo(new List { "Matt Shakman" }); nfo.UniqueIds.Should().BeEquivalentTo( new List { new() { Type = "imdb", Guid = "tt9601584", Default = false }, new() { Type = "tmdb", Guid = "1830976", Default = true }, new() { Type = "tvdb", Guid = "8042515", Default = false } }); } } [Test] public async Task Invalid_Characters_Should_Abort_And_Return_Nfo() { string sourceFile = Path.Combine( TestContext.CurrentContext.TestDirectory, "Resources", "Nfo", "EpisodeInvalidCharacters.nfo"); Either> result = await _episodeNfoReader.ReadFromFile(sourceFile); result.IsRight.Should().BeTrue(); foreach (List list in result.RightToSeq()) { list.Count.Should().Be(1); list[0].Title.Should().Be("Test Title"); } } }