Implemented several unit tests to validate the behavior of dynamic group modes, including the preservation of pinned items when switching to dynamic mode, the dropping of exclusions when reverting to static mode, and the default pinning of new elements in dynamic groups. Additional tests ensure that exclusions are correctly applied and removed, and that static groups refuse exclusions. These tests enhance the coverage and reliability of the group management functionality.
145 lines
5.4 KiB
C#
145 lines
5.4 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using NSubstitute;
|
|
using TeleWave.Application.Common.Interfaces;
|
|
using TeleWave.Application.Library.Collections.AddShowsToCollection;
|
|
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.Library;
|
|
|
|
/// <summary>Массовые операции над отмеченными шоу: раскладка по франшизам и обогащение.</summary>
|
|
public class AddShowsToCollectionTests
|
|
{
|
|
[Fact]
|
|
public async Task AddsToExistingCollection_SkippingThoseAlreadyIn()
|
|
{
|
|
var fixture = new TestDb();
|
|
var inside = Show.Create("Уже внутри", ShowKind.Single);
|
|
var outside = Show.Create("Ещё нет", ShowKind.Single);
|
|
var collection = Collection.Create("Франшиза");
|
|
collection.AddShow(inside.Id);
|
|
|
|
await using (var seed = fixture.New())
|
|
{
|
|
seed.Shows.AddRange(inside, outside);
|
|
seed.Collections.Add(collection);
|
|
await seed.SaveChangesAsync(CancellationToken.None);
|
|
}
|
|
|
|
await using var db = fixture.New();
|
|
var result = await new AddShowsToCollectionCommandHandler(db).Handle(
|
|
new AddShowsToCollectionCommand([inside.Id, outside.Id], collection.Id),
|
|
CancellationToken.None
|
|
);
|
|
await db.SaveChangesAsync(CancellationToken.None);
|
|
|
|
Assert.True(result.IsSuccess);
|
|
// Повторное добавление молча пропускается: падать из-за одного повтора бессмысленно.
|
|
Assert.Equal(1, result.Value.Added);
|
|
Assert.Equal("Франшиза", result.Value.Name);
|
|
|
|
await using var check = fixture.New();
|
|
Assert.Equal(2, check.Collections.Include(c => c.Items).Single().Items.Count);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Fails_WhenCollectionIsGone()
|
|
{
|
|
var fixture = new TestDb();
|
|
var show = Show.Create("Шоу", ShowKind.Single);
|
|
await using (var seed = fixture.New())
|
|
{
|
|
seed.Shows.Add(show);
|
|
await seed.SaveChangesAsync(CancellationToken.None);
|
|
}
|
|
|
|
await using var db = fixture.New();
|
|
var result = await new AddShowsToCollectionCommandHandler(db).Handle(
|
|
new AddShowsToCollectionCommand([show.Id], Guid.NewGuid()),
|
|
CancellationToken.None
|
|
);
|
|
|
|
Assert.False(result.IsSuccess);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Fails_WhenNoneOfTheShowsExist()
|
|
{
|
|
var fixture = new TestDb();
|
|
await using var db = fixture.New();
|
|
|
|
var result = await new AddShowsToCollectionCommandHandler(db).Handle(
|
|
new AddShowsToCollectionCommand([Guid.NewGuid()], NewCollectionName: "Франшиза"),
|
|
CancellationToken.None
|
|
);
|
|
|
|
Assert.False(result.IsSuccess);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Enrich_FailsWhenProviderUnavailable()
|
|
{
|
|
var fixture = new TestDb();
|
|
await using var db = fixture.New();
|
|
var resolver = Substitute.For<IMetadataProviderResolver>();
|
|
// Явно: NSubstitute сам подставляет заглушку вместо интерфейсного null, и без этого
|
|
// «источник не настроен» не воспроизводится.
|
|
resolver.Resolve(Arg.Any<string>()).Returns((IMetadataProvider?)null);
|
|
|
|
var result = await new EnrichShowsMetadataCommandHandler(
|
|
db,
|
|
resolver,
|
|
new ShowMetadataApplier(
|
|
db,
|
|
resolver,
|
|
Substitute.For<IImageDownloader>(),
|
|
Substitute.For<IImageStore>(),
|
|
new GenreMatcher(db)
|
|
)
|
|
).Handle(new EnrichShowsMetadataCommand([Guid.NewGuid()], "tmdb"), CancellationToken.None);
|
|
|
|
Assert.False(result.IsSuccess);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Enrich_IgnoresInterstitials()
|
|
{
|
|
// Ролики метаданными не обогащаются: у них нет ни года, ни постера.
|
|
var fixture = new TestDb();
|
|
var clip = Show.Create("Ролик", ShowKind.Interstitial);
|
|
await using (var seed = fixture.New())
|
|
{
|
|
seed.Shows.Add(clip);
|
|
await seed.SaveChangesAsync(CancellationToken.None);
|
|
}
|
|
|
|
var provider = Substitute.For<IMetadataProvider>();
|
|
var resolver = Substitute.For<IMetadataProviderResolver>();
|
|
resolver.Resolve("tmdb").Returns(provider);
|
|
|
|
await using var db = fixture.New();
|
|
var result = await new EnrichShowsMetadataCommandHandler(
|
|
db,
|
|
resolver,
|
|
new ShowMetadataApplier(
|
|
db,
|
|
resolver,
|
|
Substitute.For<IImageDownloader>(),
|
|
Substitute.For<IImageStore>(),
|
|
new GenreMatcher(db)
|
|
)
|
|
).Handle(new EnrichShowsMetadataCommand([clip.Id], "tmdb"), CancellationToken.None);
|
|
|
|
Assert.True(result.IsSuccess);
|
|
Assert.Equal(0, result.Value.Applied);
|
|
Assert.Empty(result.Value.Skipped);
|
|
await provider
|
|
.DidNotReceive()
|
|
.SearchShowsAsync(Arg.Any<string>(), Arg.Any<ShowKind>(), Arg.Any<CancellationToken>());
|
|
}
|
|
}
|