Add unit tests for dynamic group mode handling and exclusions
ci / build-backend (push) Successful in 1m36s
ci / build-frontend (push) Successful in 59s
ci / tests (push) Successful in 1m38s
ci / sonar (push) Successful in 4m5s

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.
This commit is contained in:
Leonid Pershin
2026-07-27 06:37:10 +03:00
parent 97fa77287f
commit f8923b5275
8 changed files with 1230 additions and 0 deletions
@@ -0,0 +1,203 @@
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;
/// <summary>
/// Отдача предложений наружу: уже созданное помечается, но из списка не пропадает — исчезнувшее
/// без следа предложение читается как сбой, а «уже есть» объясняет, почему кнопки нет.
/// </summary>
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)
).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)
);
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;
}
}