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.
132 lines
4.7 KiB
C#
132 lines
4.7 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using NSubstitute;
|
|
using TeleWave.Application.Common.Interfaces;
|
|
using TeleWave.Application.Library.Genres;
|
|
using TeleWave.Application.Metadata;
|
|
using TeleWave.Application.Metadata.ApplyShowMetadata;
|
|
using TeleWave.Application.Tests.Support;
|
|
using TeleWave.Domain.Library;
|
|
using Xunit;
|
|
|
|
namespace TeleWave.Application.Tests.Metadata;
|
|
|
|
public class ApplyShowMetadataTests
|
|
{
|
|
[Theory]
|
|
[InlineData(ShowKind.Series)]
|
|
[InlineData(ShowKind.Single)]
|
|
public async Task PassesShowKindToProvider(ShowKind kind)
|
|
{
|
|
// Тип определяет, в какое пространство идентификаторов пойдёт TMDb, и берётся он у самого
|
|
// шоу, а не из команды: иначе запросом можно было бы записать сериалу метаданные
|
|
// одноимённого фильма.
|
|
var arranged = await ArrangeAsync(kind);
|
|
Respond(arranged.Provider, new ShowMetadata("42", "A", 2000, null, null));
|
|
|
|
var result = await ApplyAsync(arranged);
|
|
|
|
Assert.True(result);
|
|
await arranged.Provider.Received(1).GetShowAsync("42", kind, Arg.Any<CancellationToken>());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task AppliesRecognizedRating()
|
|
{
|
|
var arranged = await ArrangeAsync(ShowKind.Single);
|
|
Respond(arranged.Provider, new ShowMetadata("42", "A", 2000, null, null, null, "PG-13"));
|
|
|
|
await ApplyAsync(arranged);
|
|
|
|
Assert.Equal(ShowAudience.Pg13, await SavedAudienceAsync(arranged));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task KeepsRating_WhenSourceGivesNothingUsable()
|
|
{
|
|
// «Not Rated» — это отсутствие данных. Снять им проставленный рейтинг нельзя: обновление
|
|
// метаданных тихо открыло бы взрослому шоу дорогу в детское время.
|
|
var arranged = await ArrangeAsync(ShowKind.Single, ShowAudience.R);
|
|
Respond(
|
|
arranged.Provider,
|
|
new ShowMetadata("42", "A", 2000, null, null, null, "Not Rated")
|
|
);
|
|
|
|
await ApplyAsync(arranged);
|
|
|
|
Assert.Equal(ShowAudience.R, await SavedAudienceAsync(arranged));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Fails_ForInterstitial()
|
|
{
|
|
var arranged = await ArrangeAsync(ShowKind.Interstitial);
|
|
|
|
Assert.False(await ApplyAsync(arranged));
|
|
await arranged
|
|
.Provider.DidNotReceive()
|
|
.GetShowAsync(Arg.Any<string>(), Arg.Any<ShowKind>(), Arg.Any<CancellationToken>());
|
|
}
|
|
|
|
private static void Respond(IMetadataProvider provider, ShowMetadata meta) =>
|
|
provider
|
|
.GetShowAsync("42", Arg.Any<ShowKind>(), Arg.Any<CancellationToken>())
|
|
.Returns(meta);
|
|
|
|
private static async Task<bool> ApplyAsync(Arranged arranged)
|
|
{
|
|
await using var db = arranged.Fixture.New();
|
|
var handler = new ApplyShowMetadataCommandHandler(
|
|
db,
|
|
new ShowMetadataApplier(
|
|
db,
|
|
arranged.Resolver,
|
|
Substitute.For<IImageDownloader>(),
|
|
Substitute.For<IImageStore>(),
|
|
new GenreMatcher(db)
|
|
)
|
|
);
|
|
|
|
var result = await handler.Handle(
|
|
new ApplyShowMetadataCommand(arranged.ShowId, "tmdb", "42"),
|
|
CancellationToken.None
|
|
);
|
|
|
|
// В обычном пути сохраняет UnitOfWorkBehavior; при прямом вызове хендлера его нет.
|
|
if (result.IsSuccess)
|
|
await db.SaveChangesAsync(CancellationToken.None);
|
|
return result.IsSuccess;
|
|
}
|
|
|
|
private static async Task<ShowAudience?> SavedAudienceAsync(Arranged arranged)
|
|
{
|
|
await using var db = arranged.Fixture.New();
|
|
return (await db.Shows.FirstAsync(s => s.Id == arranged.ShowId)).Audience;
|
|
}
|
|
|
|
private sealed record Arranged(
|
|
TestDb Fixture,
|
|
IMetadataProvider Provider,
|
|
IMetadataProviderResolver Resolver,
|
|
Guid ShowId
|
|
);
|
|
|
|
private static async Task<Arranged> ArrangeAsync(ShowKind kind, ShowAudience? audience = null)
|
|
{
|
|
var fixture = new TestDb();
|
|
var show = Show.Create("A", kind);
|
|
show.SetAudience(audience);
|
|
|
|
await using (var seed = fixture.New())
|
|
{
|
|
seed.Shows.Add(show);
|
|
await seed.SaveChangesAsync(CancellationToken.None);
|
|
}
|
|
|
|
var provider = Substitute.For<IMetadataProvider>();
|
|
var resolver = Substitute.For<IMetadataProviderResolver>();
|
|
resolver.Resolve("tmdb").Returns(provider);
|
|
|
|
return new Arranged(fixture, provider, resolver, show.Id);
|
|
}
|
|
}
|