132 lines
5.3 KiB
C#
132 lines
5.3 KiB
C#
using ErsatzTV.Application;
|
|
using ErsatzTV.Application.MediaCollections;
|
|
using ErsatzTV.Application.Search;
|
|
using ErsatzTV.Core;
|
|
using ErsatzTV.Core.Domain;
|
|
using ErsatzTV.Core.Interfaces.Repositories;
|
|
using ErsatzTV.Infrastructure.Data;
|
|
using ErsatzTV.Tests.Support;
|
|
using LanguageExt;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using NSubstitute;
|
|
using NUnit.Framework;
|
|
using Shouldly;
|
|
using Unit = LanguageExt.Unit;
|
|
|
|
namespace ErsatzTV.Tests.Integration;
|
|
|
|
/// <summary>
|
|
/// End-to-end create -> read -> add item -> remove item -> delete against the in-memory SQLite harness,
|
|
/// exercising the real EF Core handlers and collection item persistence.
|
|
/// </summary>
|
|
[TestFixture]
|
|
public class CollectionLifecycleIntegrationTests : MediaCollectionHandlerTestBase
|
|
{
|
|
[Test]
|
|
public async Task Create_Read_AddItem_RemoveItem_Delete()
|
|
{
|
|
int movieId = await SeedMovie();
|
|
IMediaCollectionRepository mediaCollectionRepository = Substitute.For<IMediaCollectionRepository>();
|
|
mediaCollectionRepository.PlayoutIdsUsingCollection(Arg.Any<int>()).Returns([]);
|
|
IMovieRepository movieRepository = Substitute.For<IMovieRepository>();
|
|
movieRepository.AllMoviesExist(Arg.Any<List<int>>()).Returns(true);
|
|
ITelevisionRepository televisionRepository = Substitute.For<ITelevisionRepository>();
|
|
televisionRepository.AllShowsExist(Arg.Any<List<int>>()).Returns(true);
|
|
televisionRepository.AllSeasonsExist(Arg.Any<List<int>>()).Returns(true);
|
|
televisionRepository.AllEpisodesExist(Arg.Any<List<int>>()).Returns(true);
|
|
|
|
var createHandler = new CreateCollectionHandler(Db.Factory, SearchTargets);
|
|
Either<BaseError, MediaCollectionViewModel> created =
|
|
await createHandler.Handle(new CreateCollection("Integration"), CancellationToken.None);
|
|
|
|
int collectionId = created.Match(Left: _ => throw new AssertionException("create failed"), Right: r => r.Id);
|
|
collectionId.ShouldBeGreaterThan(0);
|
|
|
|
var getHandler = new GetCollectionByIdHandler(Db.Factory);
|
|
Option<MediaCollectionViewModel> afterCreate =
|
|
await getHandler.Handle(new GetCollectionById(collectionId), CancellationToken.None);
|
|
afterCreate.IsSome.ShouldBeTrue();
|
|
afterCreate.Match(
|
|
Some: vm => vm.Name.ShouldBe("Integration"),
|
|
None: () => throw new AssertionException("expected collection to exist"));
|
|
|
|
var addHandler = new AddItemsToCollectionHandler(
|
|
Db.Factory,
|
|
mediaCollectionRepository,
|
|
movieRepository,
|
|
televisionRepository,
|
|
Worker,
|
|
System.Threading.Channels.Channel.CreateUnbounded<ISearchIndexBackgroundServiceRequest>().Writer);
|
|
Either<BaseError, Unit> added =
|
|
await addHandler.Handle(MakeAddItems(collectionId, movieId), CancellationToken.None);
|
|
added.IsRight.ShouldBeTrue();
|
|
|
|
await using (TvContext context = Db.CreateContext())
|
|
{
|
|
bool itemExists = await context.CollectionItems
|
|
.AnyAsync(ci => ci.CollectionId == collectionId && ci.MediaItemId == movieId);
|
|
itemExists.ShouldBeTrue();
|
|
}
|
|
|
|
var removeHandler = new RemoveItemsFromCollectionHandler(
|
|
Db.Factory,
|
|
mediaCollectionRepository,
|
|
Worker,
|
|
System.Threading.Channels.Channel.CreateUnbounded<ISearchIndexBackgroundServiceRequest>().Writer);
|
|
Either<BaseError, Unit> removed =
|
|
await removeHandler.Handle(
|
|
new RemoveItemsFromCollection(collectionId) { MediaItemIds = [movieId] },
|
|
CancellationToken.None);
|
|
removed.IsRight.ShouldBeTrue();
|
|
|
|
await using (TvContext context = Db.CreateContext())
|
|
{
|
|
bool itemExists = await context.CollectionItems
|
|
.AnyAsync(ci => ci.CollectionId == collectionId && ci.MediaItemId == movieId);
|
|
itemExists.ShouldBeFalse();
|
|
}
|
|
|
|
var deleteHandler = new DeleteCollectionHandler(Db.Factory, SearchTargets);
|
|
Either<BaseError, Unit> deleted =
|
|
await deleteHandler.Handle(new DeleteCollection(collectionId), CancellationToken.None);
|
|
deleted.IsRight.ShouldBeTrue();
|
|
|
|
Option<MediaCollectionViewModel> afterDelete =
|
|
await getHandler.Handle(new GetCollectionById(collectionId), CancellationToken.None);
|
|
afterDelete.IsNone.ShouldBeTrue();
|
|
}
|
|
|
|
private async Task<int> SeedMovie()
|
|
{
|
|
await using TvContext context = Db.CreateContext();
|
|
var mediaSource = new LocalMediaSource();
|
|
var library = new LocalLibrary
|
|
{
|
|
Name = "Movies",
|
|
MediaKind = LibraryMediaKind.Movies,
|
|
MediaSource = mediaSource,
|
|
Paths = []
|
|
};
|
|
var libraryPath = new LibraryPath
|
|
{
|
|
Path = "/media/movies",
|
|
Library = library,
|
|
LibraryFolders = [],
|
|
MediaItems = []
|
|
};
|
|
var movie = new Movie
|
|
{
|
|
LibraryPath = libraryPath,
|
|
MovieMetadata = [],
|
|
MediaVersions = [],
|
|
Collections = []
|
|
};
|
|
context.Movies.Add(movie);
|
|
await context.SaveChangesAsync();
|
|
return movie.Id;
|
|
}
|
|
|
|
private static AddItemsToCollection MakeAddItems(int collectionId, int movieId) =>
|
|
new(collectionId, [movieId], [], [], [], [], [], [], [], [], []);
|
|
}
|