Implement functionality to add shows to existing collections from suggestions
Added a new endpoint for adding shows to already assembled franchises based on the same suggestion key. Updated the CollectionSuggestion and CollectionSuggestionDto records to include a CollectionId for tracking existing collections. Enhanced the CollectionSuggestions component in the frontend to support this new functionality, allowing users to add missing parts to existing collections. Updated localization strings to reflect the new addition feature in both English and Russian, ensuring clarity for users. Adjusted tests to verify the correct behavior of the addition process and its integration with existing collection logic.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using TeleWave.Application.Library.Collections;
|
||||
using TeleWave.Application.Library.Collections.AddShowsToCollection;
|
||||
using TeleWave.Application.Library.Collections.Suggest;
|
||||
using TeleWave.Application.Tests.Support;
|
||||
@@ -63,8 +64,13 @@ public class CollectionSuggestionsTests
|
||||
Assert.DoesNotContain(suggestions, s => s.Source == CollectionSuggestionSource.NameGuess);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Часть, приехавшая после сборки коллекции, сама франшизы не образует — её предлагают
|
||||
/// добавить в существующую. Иначе она не показывалась бы нигде: создавать вторую франшизу
|
||||
/// с тем же именем нельзя.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task SkipsShows_AlreadyInCollections()
|
||||
public async Task SuggestsAddition_ToExistingCollection_ByMetadata()
|
||||
{
|
||||
var fixture = new TestDb();
|
||||
var first = Movie("Рокки", 1976, franchise: "1241");
|
||||
@@ -72,17 +78,129 @@ public class CollectionSuggestionsTests
|
||||
var collection = Collection.Create("Рокки");
|
||||
collection.AddShow(first.Id);
|
||||
|
||||
await using (var seed = fixture.New())
|
||||
await SeedWithCollectionAsync(fixture, collection, first, second);
|
||||
|
||||
var suggestion = Assert.Single(await BuildAsync(fixture));
|
||||
|
||||
Assert.Equal(collection.Id, suggestion.CollectionId);
|
||||
Assert.Equal(CollectionSuggestionSource.Metadata, suggestion.Source);
|
||||
Assert.Equal("Рокки", suggestion.Name);
|
||||
// В дополнении только недостающее: то, что уже в коллекции, предлагать нечего.
|
||||
Assert.Equal([second.Id], suggestion.Parts.Select(p => p.ShowId));
|
||||
}
|
||||
|
||||
/// <summary>Без метаданных дополнение опирается на догадку — и подписано как догадка.</summary>
|
||||
[Fact]
|
||||
public async Task SuggestsAddition_ToExistingCollection_ByName()
|
||||
{
|
||||
var fixture = new TestDb();
|
||||
var first = Movie("Рокки", 1976);
|
||||
var second = Movie("Рокки 2", 1979);
|
||||
var collection = Collection.Create("Рокки");
|
||||
collection.AddShow(first.Id);
|
||||
|
||||
await SeedWithCollectionAsync(fixture, collection, first, second);
|
||||
|
||||
var suggestion = Assert.Single(await BuildAsync(fixture));
|
||||
|
||||
Assert.Equal(collection.Id, suggestion.CollectionId);
|
||||
Assert.Equal(CollectionSuggestionSource.NameGuess, suggestion.Source);
|
||||
Assert.Equal([second.Id], suggestion.Parts.Select(p => p.ShowId));
|
||||
}
|
||||
|
||||
/// <summary>Чужой фильм в существующую франшизу не уезжает — правила те же, что при сборке.</summary>
|
||||
[Fact]
|
||||
public async Task DoesNotSuggestAddition_ForUnrelatedMovie()
|
||||
{
|
||||
var fixture = new TestDb();
|
||||
var member = Movie("Рокки", 1976, franchise: "1241");
|
||||
var stranger = Movie("Человек дождя", 1988, franchise: "777");
|
||||
var collection = Collection.Create("Рокки");
|
||||
collection.AddShow(member.Id);
|
||||
|
||||
await SeedWithCollectionAsync(fixture, collection, member, stranger);
|
||||
|
||||
Assert.Empty(await BuildAsync(fixture));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Фильм, попавший в дополнение, из предложений «создать» уходит: два предложения на один
|
||||
/// фильм заставили бы выбирать вслепую, а создание к тому же упёрлось бы в занятое имя.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task AdditionWins_OverNewFranchise()
|
||||
{
|
||||
var fixture = new TestDb();
|
||||
var member = Movie("Рокки", 1976, franchise: "1241");
|
||||
var second = Movie("Рокки 2", 1979, franchise: "1241");
|
||||
var third = Movie("Рокки 3", 1982, franchise: "1241");
|
||||
var collection = Collection.Create("Рокки");
|
||||
collection.AddShow(member.Id);
|
||||
|
||||
await SeedWithCollectionAsync(fixture, collection, member, second, third);
|
||||
|
||||
var suggestion = Assert.Single(await BuildAsync(fixture));
|
||||
|
||||
Assert.Equal(collection.Id, suggestion.CollectionId);
|
||||
Assert.Equal([second.Id, third.Id], suggestion.Parts.Select(p => p.ShowId));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddSuggestedShows_AppendsToCollection()
|
||||
{
|
||||
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 SeedWithCollectionAsync(fixture, collection, first, second);
|
||||
|
||||
var key = (await BuildAsync(fixture)).Single().Key;
|
||||
|
||||
await using (var db = fixture.New())
|
||||
{
|
||||
seed.Shows.AddRange(first, second);
|
||||
seed.Collections.Add(collection);
|
||||
await seed.SaveChangesAsync(CancellationToken.None);
|
||||
var result = await new AddSuggestedShowsCommandHandler(
|
||||
db,
|
||||
new CollectionSuggestionBuilder(db),
|
||||
new CollectionPosterPicker(db)
|
||||
).Handle(new AddSuggestedShowsCommand(key), CancellationToken.None);
|
||||
|
||||
Assert.True(result.IsSuccess);
|
||||
Assert.Equal(1, result.Value);
|
||||
await db.SaveChangesAsync(CancellationToken.None);
|
||||
}
|
||||
|
||||
var suggestions = await BuildAsync(fixture);
|
||||
await using var check = fixture.New();
|
||||
var stored = check.Collections.Include(c => c.Items).Single();
|
||||
// Дополнение встаёт в конец в хронологии — франшиза остаётся в порядке просмотра.
|
||||
Assert.Equal(
|
||||
[first.Id, second.Id],
|
||||
stored.Items.OrderBy(i => i.Position).Select(i => i.ShowId)
|
||||
);
|
||||
|
||||
// Одна часть уже разобрана — на франшизу оставшегося не хватает.
|
||||
Assert.Empty(suggestions);
|
||||
// Предложение исчерпано: добавлять больше нечего.
|
||||
Assert.Empty(await BuildAsync(fixture));
|
||||
}
|
||||
|
||||
/// <summary>Ключ предложения «создать» дополнением не применяется — коллекции у него нет.</summary>
|
||||
[Fact]
|
||||
public async Task AddSuggestedShows_RejectsCreationKey()
|
||||
{
|
||||
var fixture = new TestDb();
|
||||
await SeedAsync(fixture, Movie("Рокки", 1976), Movie("Рокки 2", 1979));
|
||||
|
||||
var key = (await BuildAsync(fixture)).Single().Key;
|
||||
|
||||
await using var db = fixture.New();
|
||||
var result = await new AddSuggestedShowsCommandHandler(
|
||||
db,
|
||||
new CollectionSuggestionBuilder(db),
|
||||
new CollectionPosterPicker(db)
|
||||
).Handle(new AddSuggestedShowsCommand(key), CancellationToken.None);
|
||||
|
||||
Assert.False(result.IsSuccess);
|
||||
Assert.Equal(CollectionErrors.SuggestionNotFound.Code, result.Error.Code);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -134,6 +252,18 @@ public class CollectionSuggestionsTests
|
||||
await seed.SaveChangesAsync(CancellationToken.None);
|
||||
}
|
||||
|
||||
private static async Task SeedWithCollectionAsync(
|
||||
TestDb fixture,
|
||||
Collection collection,
|
||||
params Show[] shows
|
||||
)
|
||||
{
|
||||
await using var seed = fixture.New();
|
||||
seed.Shows.AddRange(shows);
|
||||
seed.Collections.Add(collection);
|
||||
await seed.SaveChangesAsync(CancellationToken.None);
|
||||
}
|
||||
|
||||
private static async Task<IReadOnlyList<CollectionSuggestion>> BuildAsync(TestDb fixture)
|
||||
{
|
||||
await using var db = fixture.New();
|
||||
|
||||
Reference in New Issue
Block a user