Files
TeleWave/backend/tests/TeleWave.Application.Tests/Metadata/ShowMetadataApplierTests.cs
T
Leonid Pershin 3fecd81333
ci / build-backend (push) Successful in 1m37s
ci / build-frontend (push) Successful in 53s
ci / tests (push) Successful in 1m46s
ci / sonar (push) Successful in 4m38s
Enhance ShowMetadataApplier with logging for poster retrieval failures
Updated the ShowMetadataApplier class to include logging for scenarios where show posters are not retrieved successfully. Introduced logging statements to capture warnings when poster URLs are empty or when downloads fail, improving traceability and debugging capabilities. Additionally, updated related tests to ensure proper logging behavior during poster processing.
2026-07-30 02:00:07 +03:00

174 lines
6.2 KiB
C#

using System.Text;
using Microsoft.Extensions.Logging.Abstractions;
using NSubstitute;
using TeleWave.Application.Common.Interfaces;
using TeleWave.Application.Library.Genres;
using TeleWave.Application.Metadata;
using TeleWave.Application.Tests.Support;
using TeleWave.Domain.Images;
using TeleWave.Domain.Library;
using Xunit;
namespace TeleWave.Application.Tests.Metadata;
/// <summary>
/// Применение метаданных: постер в реестр изображений, жанры, франшиза и отказы. Общий путь для
/// точечного и массового применения — расхождение между ними отлаживать было бы нечем.
/// </summary>
public class ShowMetadataApplierTests
{
[Fact]
public async Task RegistersPoster_InImageRegistry()
{
var fixture = new TestDb();
var show = Show.Create("Шоу", ShowKind.Single);
await SeedAsync(fixture, show);
var downloader = Substitute.For<IImageDownloader>();
downloader
.DownloadAsync("https://poster", Arg.Any<CancellationToken>())
.Returns(new DownloadedImage(Encoding.UTF8.GetBytes("img"), ".jpg"));
var store = Substitute.For<IImageStore>();
await using var db = fixture.New();
var result = await Applier(db, Meta(posterUrl: "https://poster"), downloader, store)
.ApplyAsync(await LoadAsync(db, show.Id), "tmdb", "42", CancellationToken.None);
Assert.True(result.IsSuccess);
await db.SaveChangesAsync(CancellationToken.None);
await using var check = fixture.New();
var image = check.Images.Single();
Assert.Equal(ImageCategory.ShowPoster, image.Category);
Assert.Equal(image.Id, check.Shows.Single().PosterImageId);
await store
.Received(1)
.SaveAsync(image.Id, ".jpg", Arg.Any<byte[]>(), Arg.Any<CancellationToken>());
}
[Fact]
public async Task KeepsPoster_WhenDownloadFails()
{
// Источник отдал ссылку, но картинка не скачалась — это не повод валить применение.
var fixture = new TestDb();
var show = Show.Create("Шоу", ShowKind.Single);
await SeedAsync(fixture, show);
var downloader = Substitute.For<IImageDownloader>();
downloader
.DownloadAsync(Arg.Any<string>(), Arg.Any<CancellationToken>())
.Returns((DownloadedImage?)null);
await using var db = fixture.New();
var result = await Applier(db, Meta(posterUrl: "https://poster"), downloader)
.ApplyAsync(await LoadAsync(db, show.Id), "tmdb", "42", CancellationToken.None);
Assert.True(result.IsSuccess);
Assert.Empty(db.Images);
}
[Fact]
public async Task RemembersFranchise_FromSource()
{
var fixture = new TestDb();
var show = Show.Create("Рокки", ShowKind.Single);
await SeedAsync(fixture, show);
await using var db = fixture.New();
await Applier(db, Meta(franchise: new MetadataFranchise("1241", "Рокки")))
.ApplyAsync(await LoadAsync(db, show.Id), "tmdb", "42", CancellationToken.None);
await db.SaveChangesAsync(CancellationToken.None);
await using var check = fixture.New();
var stored = check.Shows.Single();
Assert.Equal("1241", stored.FranchiseExternalId);
Assert.Equal("Рокки", stored.FranchiseName);
}
[Fact]
public async Task Fails_WhenProviderUnavailable()
{
var fixture = new TestDb();
var show = Show.Create("Шоу", ShowKind.Single);
await SeedAsync(fixture, show);
await using var db = fixture.New();
var resolver = Substitute.For<IMetadataProviderResolver>();
var applier = new ShowMetadataApplier(
db,
resolver,
Substitute.For<IImageDownloader>(),
Substitute.For<IImageStore>(),
new GenreMatcher(db),
NullLogger<ShowMetadataApplier>.Instance
);
var result = await applier.ApplyAsync(
await LoadAsync(db, show.Id),
"tmdb",
"42",
CancellationToken.None
);
Assert.False(result.IsSuccess);
}
[Fact]
public async Task Fails_WhenSourceKnowsNothing()
{
var fixture = new TestDb();
var show = Show.Create("Шоу", ShowKind.Single);
await SeedAsync(fixture, show);
await using var db = fixture.New();
var result = await Applier(db, meta: null)
.ApplyAsync(await LoadAsync(db, show.Id), "tmdb", "42", CancellationToken.None);
Assert.False(result.IsSuccess);
}
private static ShowMetadata Meta(
string? posterUrl = null,
MetadataFranchise? franchise = null
) => new("42", "Шоу", 2000, "описание", posterUrl, [], null, franchise);
private static ShowMetadataApplier Applier(
Infrastructure.Persistence.AppDbContext db,
ShowMetadata? meta,
IImageDownloader? downloader = null,
IImageStore? store = null
)
{
var provider = Substitute.For<IMetadataProvider>();
provider
.GetShowAsync(Arg.Any<string>(), Arg.Any<ShowKind>(), Arg.Any<CancellationToken>())
.Returns(meta);
var resolver = Substitute.For<IMetadataProviderResolver>();
resolver.Resolve("tmdb").Returns(provider);
return new ShowMetadataApplier(
db,
resolver,
downloader ?? Substitute.For<IImageDownloader>(),
store ?? Substitute.For<IImageStore>(),
new GenreMatcher(db),
NullLogger<ShowMetadataApplier>.Instance
);
}
private static async Task SeedAsync(TestDb fixture, Show show)
{
await using var seed = fixture.New();
seed.Shows.Add(show);
await seed.SaveChangesAsync(CancellationToken.None);
}
private static async Task<Show> LoadAsync(
Infrastructure.Persistence.AppDbContext db,
Guid showId
) =>
await Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.FirstAsync(
db.Shows,
s => s.Id == showId
);
}