using TeleWave.Application.Library.Collections; using TeleWave.Application.Library.Collections.Suggest; using TeleWave.Application.Programming.Groups; using TeleWave.Application.Programming.Groups.Suggest; using TeleWave.Application.Tests.Support; using TeleWave.Domain.Library; using TeleWave.Domain.Programming; using Xunit; namespace TeleWave.Application.Tests.Programming; /// /// Отдача предложений наружу: уже созданное помечается, но из списка не пропадает — исчезнувшее /// без следа предложение читается как сбой, а «уже есть» объясняет, почему кнопки нет. /// public class SuggestionQueriesTests { [Fact] public async Task Groups_MarkExistingByName() { var fixture = new TestDb(); var genre = Genre.Create("Мультфильм", "animation"); var first = WithGenre(Show.Create("A", ShowKind.Series), genre); var second = WithGenre(Show.Create("B", ShowKind.Series), genre); // Регистр не важен: в слоте две «мультфильм» всё равно неразличимы. var existing = Group.Create("мультфильм"); await using (var seed = fixture.New()) { seed.Genres.Add(genre); seed.Shows.AddRange(first, second); seed.Groups.Add(existing); await seed.SaveChangesAsync(CancellationToken.None); } await using var db = fixture.New(); var suggestions = await new SuggestGroupsQueryHandler( db, new GroupSuggestionBuilder(db, new GroupElementResolver(db)) ).Handle(new SuggestGroupsQuery(), CancellationToken.None); var genreSuggestion = Assert.Single(suggestions, s => s.Kind == GroupSuggestionKind.Genre); Assert.True(genreSuggestion.AlreadyExists); Assert.Equal(2, genreSuggestion.ShowCount); } [Fact] public async Task Groups_MarkExistingByFilter() { // Группу могли переименовать — правило всё равно то же самое. var fixture = new TestDb(); var genre = Genre.Create("Мультфильм", "animation"); var first = WithGenre(Show.Create("A", ShowKind.Series), genre); var second = WithGenre(Show.Create("B", ShowKind.Series), genre); var renamed = Group.Create("Своё название"); renamed.SetFilter(new GroupFilter(GenreIds: [genre.Id]).ToJson()); await using (var seed = fixture.New()) { seed.Genres.Add(genre); seed.Shows.AddRange(first, second); seed.Groups.Add(renamed); await seed.SaveChangesAsync(CancellationToken.None); } await using var db = fixture.New(); var suggestions = await new SuggestGroupsQueryHandler( db, new GroupSuggestionBuilder(db, new GroupElementResolver(db)) ).Handle(new SuggestGroupsQuery(), CancellationToken.None); Assert.True( Assert.Single(suggestions, s => s.Kind == GroupSuggestionKind.Genre).AlreadyExists ); } [Fact] public async Task Groups_EmptyLibrary_SuggestsNothing() { var fixture = new TestDb(); await using var db = fixture.New(); var suggestions = await new SuggestGroupsQueryHandler( db, new GroupSuggestionBuilder(db, new GroupElementResolver(db)) ).Handle(new SuggestGroupsQuery(), CancellationToken.None); Assert.Empty(suggestions); } [Fact] public async Task Collections_MarkExistingAndCarryParts() { var fixture = new TestDb(); var first = Movie("Рокки", 1976); var second = Movie("Рокки 2", 1979); var existing = Collection.Create("рокки"); await using (var seed = fixture.New()) { seed.Shows.AddRange(first, second); seed.Collections.Add(existing); await seed.SaveChangesAsync(CancellationToken.None); } await using var db = fixture.New(); var suggestions = await new SuggestCollectionsQueryHandler( db, new CollectionSuggestionBuilder(db) ).Handle(new SuggestCollectionsQuery(), CancellationToken.None); var suggestion = Assert.Single(suggestions); Assert.True(suggestion.AlreadyExists); // Состав едет наружу целиком: по нему видно, промахнулась ли догадка. Assert.Equal([first.Id, second.Id], suggestion.Parts.Select(p => p.ShowId)); Assert.Equal(1976, suggestion.Parts[0].Year); } [Fact] public async Task Collections_EmptyLibrary_SuggestsNothing() { var fixture = new TestDb(); await using var db = fixture.New(); var suggestions = await new SuggestCollectionsQueryHandler( db, new CollectionSuggestionBuilder(db) ).Handle(new SuggestCollectionsQuery(), CancellationToken.None); Assert.Empty(suggestions); } [Fact] public async Task Collections_CreateFromSuggestion_FailsOnUnknownKey() { var fixture = new TestDb(); await using (var seed = fixture.New()) { seed.Shows.AddRange(Movie("Рокки", 1976), Movie("Рокки 2", 1979)); await seed.SaveChangesAsync(CancellationToken.None); } await using var db = fixture.New(); var result = await new CreateCollectionFromSuggestionCommandHandler( db, new CollectionSuggestionBuilder(db), new CollectionPosterPicker(db) ).Handle( new CreateCollectionFromSuggestionCommand("name:нет-такого"), CancellationToken.None ); Assert.False(result.IsSuccess); } [Fact] public async Task Collections_CreateFromSuggestion_BuildsFranchiseAndRefusesDuplicate() { var fixture = new TestDb(); var first = Movie("Рокки", 1976); var second = Movie("Рокки 2", 1979); await using (var seed = fixture.New()) { seed.Shows.AddRange(first, second); await seed.SaveChangesAsync(CancellationToken.None); } await using var db = fixture.New(); var handler = new CreateCollectionFromSuggestionCommandHandler( db, new CollectionSuggestionBuilder(db), new CollectionPosterPicker(db) ); var created = await handler.Handle( new CreateCollectionFromSuggestionCommand("name:рокки"), CancellationToken.None ); Assert.True(created.IsSuccess); await db.SaveChangesAsync(CancellationToken.None); await using var check = fixture.New(); var collection = check.Collections.Single(); Assert.Equal("Рокки", collection.Name); // Повторное создание — конфликт: одноимённые франшизы неразличимы. var again = await handler.Handle( new CreateCollectionFromSuggestionCommand("name:рокки"), CancellationToken.None ); Assert.False(again.IsSuccess); } private static Show WithGenre(Show show, Genre genre) { show.SetGenres([genre.Id]); return show; } private static Show Movie(string name, int year) { var show = Show.Create(name, ShowKind.Single); show.ApplyMetadata("manual", Guid.NewGuid().ToString("N"), null, year, null); return show; } }