using Microsoft.Extensions.Options; using TeleWave.Application.Tests.TestSupport; using TeleWave.Domain.Library; using TeleWave.Infrastructure.Metadata; using Xunit; namespace TeleWave.Application.Tests.Metadata; /// /// Разбор ответов OMDb. Особенности источника, ради которых тесты и написаны: «N/A» вместо /// отсутствующего значения, флаг Response вместо кода ошибки и жанры одной строкой. /// public class OmdbMetadataProviderTests { private static OmdbMetadataProvider Provider(FakeHttpClientFactory http) => new( http, Options.Create(new MetadataOptions { Omdb = new OmdbOptions { ApiKey = "key" } }) ); [Fact] public async Task SearchShows_KeepsOnlyShowsWithId() { var http = FakeHttpClientFactory.Json( """ {"Search":[ {"imdbID":"tt1","Title":"Сериал","Year":"2001–2005","Type":"series","Poster":"http://p/1.jpg"}, {"imdbID":"tt2","Title":"Серия","Type":"episode"}, {"Title":"Без идентификатора","Type":"movie"}, {"imdbID":"tt3","Title":"Фильм","Year":"N/A","Type":"movie","Poster":"N/A"} ]} """ ); var found = await Provider(http) .SearchShowsAsync("сериал", ShowKind.Series, CancellationToken.None); Assert.Contains("type=series", http.LastUrl); Assert.Equal(2, found.Count); Assert.Equal("tt1", found[0].ExternalId); Assert.Equal(2001, found[0].Year); Assert.Equal("http://p/1.jpg", found[0].PosterUrl); Assert.Equal(ShowKind.Series, found[0].Kind); // Отдельные серии просачиваются в выдачу и шоу не годятся, запись без imdbID — тоже. Assert.Equal("tt3", found[1].ExternalId); Assert.Equal(ShowKind.Single, found[1].Kind); Assert.Null(found[1].Year); Assert.Null(found[1].PosterUrl); } [Fact] public async Task SearchShows_Single_AsksForMovies() { var http = FakeHttpClientFactory.Json("{}"); var found = await Provider(http) .SearchShowsAsync("фильм", ShowKind.Single, CancellationToken.None); Assert.Contains("type=movie", http.LastUrl); Assert.Empty(found); } [Fact] public async Task GetShow_ReadsRatingAndSplitsGenres() { var http = FakeHttpClientFactory.Json( """ {"Response":"True","Title":"Сериал","Year":"2001–2005","Plot":"описание", "Poster":"http://p/1.jpg","Genre":"Action, Sci-Fi","Rated":"TV-14"} """ ); var show = await Provider(http) .GetShowAsync("tt1", ShowKind.Series, CancellationToken.None); Assert.Contains("i=tt1", http.LastUrl); Assert.NotNull(show); Assert.Equal("tt1", show.ExternalId); Assert.Equal("Сериал", show.Title); Assert.Equal(2001, show.Year); Assert.Equal("описание", show.Overview); Assert.Equal(["Action", "Sci-Fi"], show.Genres); Assert.Equal("TV-14", show.ContentRating); } [Fact] public async Task GetShow_TreatsNotAvailableAsMissing() { var http = FakeHttpClientFactory.Json( """{"Response":"True","Title":"N/A","Plot":"N/A","Poster":"N/A","Genre":"N/A","Rated":"N/A"}""" ); var show = await Provider(http) .GetShowAsync("tt1", ShowKind.Single, CancellationToken.None); Assert.NotNull(show); Assert.Equal("—", show.Title); Assert.Null(show.Overview); Assert.Null(show.PosterUrl); Assert.Null(show.ContentRating); Assert.Empty(show.Genres!); } [Fact] public async Task GetShow_ReturnsNull_WhenResponseIsFalse() { var http = FakeHttpClientFactory.Json( """{"Response":"False","Error":"Movie not found!"}""" ); var show = await Provider(http) .GetShowAsync("tt0", ShowKind.Series, CancellationToken.None); Assert.Null(show); } [Fact] public async Task GetShow_ReturnsNull_WhenRequestFails() { var show = await Provider(FakeHttpClientFactory.Failing()) .GetShowAsync("tt1", ShowKind.Series, CancellationToken.None); Assert.Null(show); } [Fact] public async Task GetEpisode_ReadsTitleAndReleaseDate() { var http = FakeHttpClientFactory.Json( """ {"Response":"True","Title":"Серия","Plot":"о серии","Poster":"http://p/s.jpg", "Released":"07 Jun 2001"} """ ); var episode = await Provider(http).GetEpisodeAsync("tt1", 2, 3, CancellationToken.None); Assert.Contains("Season=2", http.LastUrl); Assert.Contains("Episode=3", http.LastUrl); Assert.NotNull(episode); Assert.Equal("Серия", episode.Title); Assert.Equal("http://p/s.jpg", episode.StillUrl); Assert.Equal(new DateOnly(2001, 6, 7), episode.AirDate); } [Fact] public async Task GetEpisode_ReturnsNull_WhenResponseIsFalse() { var http = FakeHttpClientFactory.Json("""{"Response":"False"}"""); var episode = await Provider(http).GetEpisodeAsync("tt1", 9, 9, CancellationToken.None); Assert.Null(episode); } [Fact] public async Task GetSeasonEpisodeCount_CountsEpisodes() { var http = FakeHttpClientFactory.Json( """{"Response":"True","Episodes":[{"Episode":"1"},{"Episode":"2"}]}""" ); var count = await Provider(http) .GetSeasonEpisodeCountAsync("tt1", 2, CancellationToken.None); Assert.Equal(2, count); } [Fact] public async Task GetSeasonEpisodeCount_ReturnsNull_WhenSeasonMissing() { var http = FakeHttpClientFactory.Json("""{"Response":"True"}"""); var count = await Provider(http) .GetSeasonEpisodeCountAsync("tt1", 9, CancellationToken.None); Assert.Null(count); } [Fact] public async Task GetSeasonNumbers_NumbersSeasonsContinuously() { // Перечня сезонов OMDb не отдаёт — только их количество строкой. var http = FakeHttpClientFactory.Json("""{"Response":"True","totalSeasons":"3"}"""); var seasons = await Provider(http).GetSeasonNumbersAsync("tt1", CancellationToken.None); Assert.Equal([1, 2, 3], seasons); } [Fact] public async Task GetSeasonNumbers_ReturnsEmpty_WhenTotalIsNotANumber() { var http = FakeHttpClientFactory.Json("""{"Response":"True","totalSeasons":"N/A"}"""); var seasons = await Provider(http).GetSeasonNumbersAsync("tt1", CancellationToken.None); Assert.Empty(seasons); } [Fact] public async Task GetSeasonNumbers_ReturnsEmpty_WhenResponseIsFalse() { var http = FakeHttpClientFactory.Json("""{"Response":"False"}"""); var seasons = await Provider(http).GetSeasonNumbersAsync("tt1", CancellationToken.None); Assert.Empty(seasons); } }