Added new API endpoints for bulk tagging and enriching shows, allowing for mass updates of genres and audience ratings. Implemented backend logic to handle bulk operations and updated the Dependency Injection configuration to include necessary services. Enhanced the frontend with new components for selecting shows and applying bulk actions, improving the user experience for managing multiple shows simultaneously. Localization updates were made to support these new features in both English and Russian.
138 lines
4.7 KiB
C#
138 lines
4.7 KiB
C#
using NSubstitute;
|
|
using TeleWave.Application.Common.Interfaces;
|
|
using TeleWave.Application.Library.Genres;
|
|
using TeleWave.Application.Metadata;
|
|
using TeleWave.Application.Metadata.EnrichShows;
|
|
using TeleWave.Application.Tests.Support;
|
|
using TeleWave.Domain.Library;
|
|
using Xunit;
|
|
|
|
namespace TeleWave.Application.Tests.Metadata;
|
|
|
|
/// <summary>
|
|
/// Массовое обогащение метаданными. Главное свойство — осторожность: чужой постер и чужой рейтинг
|
|
/// у шоу в эфире хуже, чем непроставленные метаданные.
|
|
/// </summary>
|
|
public class EnrichShowsTests
|
|
{
|
|
[Fact]
|
|
public async Task Applies_WhenTitleMatchesExactly()
|
|
{
|
|
var fixture = new TestDb();
|
|
var show = Show.Create("Гравити Фолз", ShowKind.Series);
|
|
show.SetOriginalName("Gravity Falls");
|
|
await SeedAsync(fixture, show);
|
|
|
|
var provider = ProviderWith([
|
|
new MetadataCandidate("42", "Gravity Falls", 2012, null, null, ShowKind.Series),
|
|
]);
|
|
provider
|
|
.GetShowAsync("42", Arg.Any<ShowKind>(), Arg.Any<CancellationToken>())
|
|
.Returns(new ShowMetadata("42", "Gravity Falls", 2012, "desc", null, [], null));
|
|
|
|
var result = await RunAsync(fixture, provider, show.Id);
|
|
|
|
Assert.Equal(1, result.Applied);
|
|
Assert.Empty(result.Skipped);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Skips_WhenSeveralCandidatesShareTheTitle()
|
|
{
|
|
// Два одноимённых — угадывать нельзя: у них разные постеры и разные рейтинги.
|
|
var fixture = new TestDb();
|
|
var show = Show.Create("Gravity Falls", ShowKind.Series);
|
|
await SeedAsync(fixture, show);
|
|
|
|
var provider = ProviderWith([
|
|
new MetadataCandidate("1", "Gravity Falls", null, null, null, ShowKind.Series),
|
|
new MetadataCandidate("2", "Gravity Falls", null, null, null, ShowKind.Series),
|
|
]);
|
|
|
|
var result = await RunAsync(fixture, provider, show.Id);
|
|
|
|
Assert.Equal(0, result.Applied);
|
|
Assert.Single(result.Skipped);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Skips_WhenOnlyLooseMatchFound()
|
|
{
|
|
var fixture = new TestDb();
|
|
var show = Show.Create("Пилот", ShowKind.Series);
|
|
await SeedAsync(fixture, show);
|
|
|
|
var provider = ProviderWith([
|
|
new MetadataCandidate("7", "Совсем другое шоу", null, null, null, ShowKind.Series),
|
|
]);
|
|
|
|
var result = await RunAsync(fixture, provider, show.Id);
|
|
|
|
Assert.Equal(0, result.Applied);
|
|
Assert.Single(result.Skipped);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Skips_AlreadyLinked()
|
|
{
|
|
// Ручной выбор источника массовая операция переписывать не должна.
|
|
var fixture = new TestDb();
|
|
var show = Show.Create("Gravity Falls", ShowKind.Series);
|
|
show.ApplyMetadata("tmdb", "999", null, null, null);
|
|
await SeedAsync(fixture, show);
|
|
|
|
var provider = ProviderWith([
|
|
new MetadataCandidate("42", "Gravity Falls", null, null, null, ShowKind.Series),
|
|
]);
|
|
|
|
var result = await RunAsync(fixture, provider, show.Id);
|
|
|
|
Assert.Equal(0, result.Applied);
|
|
Assert.Single(result.Skipped);
|
|
}
|
|
|
|
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 IMetadataProvider ProviderWith(IReadOnlyList<MetadataCandidate> candidates)
|
|
{
|
|
var provider = Substitute.For<IMetadataProvider>();
|
|
provider
|
|
.SearchShowsAsync(Arg.Any<string>(), Arg.Any<ShowKind>(), Arg.Any<CancellationToken>())
|
|
.Returns(candidates);
|
|
return provider;
|
|
}
|
|
|
|
private static async Task<EnrichShowsResultDto> RunAsync(
|
|
TestDb fixture,
|
|
IMetadataProvider provider,
|
|
Guid showId
|
|
)
|
|
{
|
|
var resolver = Substitute.For<IMetadataProviderResolver>();
|
|
resolver.Resolve("tmdb").Returns(provider);
|
|
|
|
await using var db = fixture.New();
|
|
var applier = new ShowMetadataApplier(
|
|
db,
|
|
resolver,
|
|
Substitute.For<IImageDownloader>(),
|
|
Substitute.For<IImageStore>(),
|
|
new GenreMatcher(db)
|
|
);
|
|
|
|
var result = await new EnrichShowsMetadataCommandHandler(db, resolver, applier).Handle(
|
|
new EnrichShowsMetadataCommand([showId], "tmdb"),
|
|
CancellationToken.None
|
|
);
|
|
|
|
Assert.True(result.IsSuccess);
|
|
await db.SaveChangesAsync(CancellationToken.None);
|
|
return result.Value;
|
|
}
|
|
}
|