Files
TeleWave/backend/tests/TeleWave.Application.Tests/Media/MovieImportHandlerTests.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

185 lines
6.6 KiB
C#

using Microsoft.Extensions.Logging.Abstractions;
using NSubstitute;
using TeleWave.Application.Common.Interfaces;
using TeleWave.Application.Library.Genres;
using TeleWave.Application.Media.ManualInbox;
using TeleWave.Application.Media.MovieImport;
using TeleWave.Application.Metadata;
using TeleWave.Application.Tests.Support;
using TeleWave.Domain.Library;
using TeleWave.Domain.Media;
using Xunit;
namespace TeleWave.Application.Tests.Media;
/// <summary>
/// Пути разбора фильмов, где всё идёт не по-хорошему: источник не настроен, ассет пропал, файл уже
/// в чужом шоу. Каждый из них должен быть тихим отказом, а не исключением: разбор пакетный.
/// </summary>
public class MovieImportHandlerTests
{
private const string Provider = "tmdb";
private static IMetadataProviderResolver EmptyResolver()
{
var resolver = Substitute.For<IMetadataProviderResolver>();
resolver.AvailableKeys.Returns([]);
// Явный null: без него NSubstitute подсунул бы автозаглушку провайдера, и «источник
// не настроен» перестало бы быть тем, что проверяет тест.
resolver.Resolve(Arg.Any<string>()).Returns((IMetadataProvider?)null);
return resolver;
}
private static ShowMetadataApplier Applier(
IAppDbContext db,
IMetadataProviderResolver resolver
) =>
new(
db,
resolver,
Substitute.For<IImageDownloader>(),
Substitute.For<IImageStore>(),
new GenreMatcher(db),
NullLogger<ShowMetadataApplier>.Instance
);
[Fact]
public async Task Match_ReturnsNothing_WhenProviderIsNotConfigured()
{
// Ключа источника нет — искать нечем, и это не ошибка: таблица разбора просто пуста.
var fixture = new TestDb();
await using var db = fixture.New();
var matcher = new MovieMatcher(db, EmptyResolver());
var rows = await new MatchMoviesQueryHandler(matcher).Handle(
new MatchMoviesQuery(["Gladiator.2000.mkv"], Provider),
CancellationToken.None
);
Assert.Empty(rows);
}
[Fact]
public async Task MatchOne_ReturnsNotFound_WhenProviderIsNotConfigured()
{
var fixture = new TestDb();
await using var db = fixture.New();
var matcher = new MovieMatcher(db, EmptyResolver());
var row = await new MatchMovieQueryHandler(matcher).Handle(
new MatchMovieQuery("file.mkv", "Гладиатор", 2000, Provider),
CancellationToken.None
);
Assert.Equal(MovieMatchStatus.NotFound, row.Status);
Assert.Equal("file.mkv", row.Name);
}
[Fact]
public async Task Import_ReportsMissingAsset()
{
var fixture = new TestDb();
await using var db = fixture.New();
var storage = Substitute.For<IMediaStorage>();
storage.ListManualInbox(Arg.Any<int>()).Returns([]);
var result = await new ImportMoviesCommandHandler(
db,
new ManualInboxTaker(db, storage),
Substitute.For<IMediaProcessingQueue>(),
Applier(db, EmptyResolver())
).Handle(
new ImportMoviesCommand(
[new MovieImportItem(null, Guid.NewGuid(), "Пропавший", null, null)],
Provider
),
CancellationToken.None
);
Assert.True(result.IsSuccess);
Assert.Equal(0, result.Value.Created);
Assert.Single(result.Value.Failed);
}
[Fact]
public async Task AutoAttach_DoesNothing_WhenProviderIsNotConfigured()
{
var fixture = new TestDb();
var asset = MediaAsset.Register("a.mkv", ".mkv", MediaSource.Inbox);
await using (var seed = fixture.New())
{
seed.MediaAssets.Add(asset);
await seed.SaveChangesAsync(CancellationToken.None);
}
await using var db = fixture.New();
var resolver = EmptyResolver();
var result = await new AutoAttachMovieCommandHandler(
db,
resolver,
new MovieMatcher(db, resolver),
Applier(db, resolver)
).Handle(new AutoAttachMovieCommand(asset.Id, "a.mkv"), CancellationToken.None);
Assert.True(result.IsSuccess);
Assert.False(result.Value);
}
[Fact]
public async Task AutoAttach_FailsOnMissingAsset()
{
var fixture = new TestDb();
await using var db = fixture.New();
var provider = Substitute.For<IMetadataProvider>();
var resolver = Substitute.For<IMetadataProviderResolver>();
resolver.AvailableKeys.Returns([Provider]);
resolver.Resolve(Provider).Returns(provider);
var result = await new AutoAttachMovieCommandHandler(
db,
resolver,
new MovieMatcher(db, resolver),
Applier(db, resolver)
).Handle(new AutoAttachMovieCommand(Guid.NewGuid(), "a.mkv"), CancellationToken.None);
Assert.False(result.IsSuccess);
}
[Fact]
public async Task AutoAttach_SkipsAssetAlreadyInShow()
{
// Ассет мог уехать в шоу вручную, пока файл ждал очереди, — второе шоу по нему не заводим.
var fixture = new TestDb();
var asset = MediaAsset.Register("a.mkv", ".mkv", MediaSource.Inbox);
var show = Show.Create("Уже есть", ShowKind.Single);
show.AddEpisode(asset.Id);
await using (var seed = fixture.New())
{
seed.MediaAssets.Add(asset);
seed.Shows.Add(show);
await seed.SaveChangesAsync(CancellationToken.None);
}
await using var db = fixture.New();
var provider = Substitute.For<IMetadataProvider>();
var resolver = Substitute.For<IMetadataProviderResolver>();
resolver.AvailableKeys.Returns([Provider]);
resolver.Resolve(Provider).Returns(provider);
var result = await new AutoAttachMovieCommandHandler(
db,
resolver,
new MovieMatcher(db, resolver),
Applier(db, resolver)
).Handle(new AutoAttachMovieCommand(asset.Id, "a.mkv"), CancellationToken.None);
Assert.True(result.IsSuccess);
Assert.False(result.Value);
await provider
.DidNotReceive()
.SearchShowsAsync(Arg.Any<string>(), Arg.Any<ShowKind>(), Arg.Any<CancellationToken>());
}
}