using Microsoft.EntityFrameworkCore;
using TeleWave.Application.Library.Collections.AddShowsToCollection;
using TeleWave.Application.Library.Collections.Suggest;
using TeleWave.Application.Tests.Support;
using TeleWave.Domain.Library;
using Xunit;
namespace TeleWave.Application.Tests.Library;
///
/// Сборка франшиз: предложения по метаданным и по названиям плюс массовое добавление в коллекцию.
/// Ошибка здесь означает чужой фильм в середине франшизы, поэтому осторожность важнее полноты.
///
public class CollectionSuggestionsTests
{
[Fact]
public async Task SuggestsFranchise_FromMetadata()
{
var fixture = new TestDb();
var first = Movie("Гарри Поттер и философский камень", 2001, franchise: "1241");
var second = Movie("Гарри Поттер и тайная комната", 2002, franchise: "1241");
var other = Movie("Совсем другое", 1999, franchise: "777");
await SeedAsync(fixture, first, second, other);
var suggestions = await BuildAsync(fixture);
var franchise = Assert.Single(
suggestions,
s => s.Source == CollectionSuggestionSource.Metadata
);
Assert.Equal("Франшиза 1241", franchise.Name);
// Порядок — по году: это и есть порядок просмотра.
Assert.Equal([first.Id, second.Id], franchise.Parts.Select(p => p.ShowId));
}
[Fact]
public async Task SuggestsByName_WhenMetadataMissing()
{
var fixture = new TestDb();
var first = Movie("Рокки", 1976);
var second = Movie("Рокки 2", 1979);
await SeedAsync(fixture, first, second);
var suggestions = await BuildAsync(fixture);
var guess = Assert.Single(
suggestions,
s => s.Source == CollectionSuggestionSource.NameGuess
);
Assert.Equal(2, guess.Parts.Count);
}
[Fact]
public async Task DoesNotGuess_OnShortCommonStart()
{
// «Человек-паук» и «Человек дождя» — не франшиза, хотя начало совпадает.
var fixture = new TestDb();
await SeedAsync(fixture, Movie("Человек дождя", 1988), Movie("Человек-слон", 1980));
var suggestions = await BuildAsync(fixture);
Assert.DoesNotContain(suggestions, s => s.Source == CollectionSuggestionSource.NameGuess);
}
[Fact]
public async Task SkipsShows_AlreadyInCollections()
{
var fixture = new TestDb();
var first = Movie("Рокки", 1976, franchise: "1241");
var second = Movie("Рокки 2", 1979, franchise: "1241");
var collection = Collection.Create("Рокки");
collection.AddShow(first.Id);
await using (var seed = fixture.New())
{
seed.Shows.AddRange(first, second);
seed.Collections.Add(collection);
await seed.SaveChangesAsync(CancellationToken.None);
}
var suggestions = await BuildAsync(fixture);
// Одна часть уже разобрана — на франшизу оставшегося не хватает.
Assert.Empty(suggestions);
}
[Fact]
public async Task AddShowsToCollection_CreatesNewAndOrdersByYear()
{
var fixture = new TestDb();
var later = Movie("Вторая", 1979);
var earlier = Movie("Первая", 1976);
await SeedAsync(fixture, later, earlier);
await using var db = fixture.New();
var result = await new AddShowsToCollectionCommandHandler(db).Handle(
new AddShowsToCollectionCommand([later.Id, earlier.Id], NewCollectionName: "Франшиза"),
CancellationToken.None
);
Assert.True(result.IsSuccess);
await db.SaveChangesAsync(CancellationToken.None);
await using var check = fixture.New();
var collection = check.Collections.Include(c => c.Items).Single();
Assert.Equal(2, result.Value.Added);
Assert.Equal(
[earlier.Id, later.Id],
collection.Items.OrderBy(i => i.Position).Select(i => i.ShowId)
);
}
private static Show Movie(string name, int? year, string? franchise = null)
{
var show = Show.Create(name, ShowKind.Single);
if (franchise is not null)
show.ApplyMetadata(
"tmdb",
Guid.NewGuid().ToString("N"),
null,
year,
null,
new MetadataFranchise(franchise, $"Франшиза {franchise}")
);
else
show.ApplyMetadata("manual", Guid.NewGuid().ToString("N"), null, year, null);
return show;
}
private static async Task SeedAsync(TestDb fixture, params Show[] shows)
{
await using var seed = fixture.New();
seed.Shows.AddRange(shows);
await seed.SaveChangesAsync(CancellationToken.None);
}
private static async Task> BuildAsync(TestDb fixture)
{
await using var db = fixture.New();
return await new CollectionSuggestionBuilder(db).BuildAsync(CancellationToken.None);
}
}