diff options
Diffstat (limited to 'tests/Jellyfin.Server.Implementations.Tests/Item')
3 files changed, 254 insertions, 0 deletions
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Item/BaseItemRepositoryItemValueTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Item/BaseItemRepositoryItemValueTests.cs new file mode 100644 index 0000000000..91148501ce --- /dev/null +++ b/tests/Jellyfin.Server.Implementations.Tests/Item/BaseItemRepositoryItemValueTests.cs @@ -0,0 +1,174 @@ +using System; +using Emby.Server.Implementations.Data; +using Jellyfin.Database.Implementations.Entities; +using Jellyfin.Server.Implementations.Item; +using MediaBrowser.Controller.Entities; +using Xunit; +using BaseItemKind = Jellyfin.Data.Enums.BaseItemKind; + +namespace Jellyfin.Server.Implementations.Tests.Item; + +public sealed class BaseItemRepositoryItemValueTests : SqliteDbTestFixture +{ + private readonly BaseItemRepository _repository; + private readonly string _audioTypeName; + private readonly string _movieTypeName; + + public BaseItemRepositoryItemValueTests() + { + var itemTypeLookup = new ItemTypeLookup(); + _audioTypeName = itemTypeLookup.BaseItemKindNames[BaseItemKind.Audio]; + _movieTypeName = itemTypeLookup.BaseItemKindNames[BaseItemKind.Movie]; + _repository = CreateBaseItemRepository(itemTypeLookup); + } + + [Fact] + public void GetQueryFiltersLegacy_GroupsAndFiltersItemValues() + { + var firstItem = CreateMovieEntity(Guid.NewGuid(), "First"); + var secondItem = CreateMovieEntity(Guid.NewGuid(), "Second"); + var excludedItem = new BaseItemEntity + { + Id = Guid.NewGuid(), + Type = _audioTypeName, + Name = "Excluded Audio", + MediaType = "Audio", + IsMovie = false, + IsFolder = false, + IsVirtualItem = false + }; + var firstTag = new ItemValue + { + ItemValueId = Guid.NewGuid(), + Type = ItemValueType.Tags, + Value = "Alpha", + CleanValue = "alpha" + }; + var duplicateTag = new ItemValue + { + ItemValueId = Guid.NewGuid(), + Type = ItemValueType.Tags, + Value = "alpha", + CleanValue = "alpha" + }; + var secondTag = new ItemValue + { + ItemValueId = Guid.NewGuid(), + Type = ItemValueType.Tags, + Value = "Beta", + CleanValue = "beta" + }; + var genre = new ItemValue + { + ItemValueId = Guid.NewGuid(), + Type = ItemValueType.Genre, + Value = "Genre Leak", + CleanValue = "genre leak" + }; + var excludedTag = new ItemValue + { + ItemValueId = Guid.NewGuid(), + Type = ItemValueType.Tags, + Value = "Excluded Tag", + CleanValue = "excluded tag" + }; + var excludedGenre = new ItemValue + { + ItemValueId = Guid.NewGuid(), + Type = ItemValueType.Genre, + Value = "Excluded Genre", + CleanValue = "excluded genre" + }; + + using (var context = CreateDbContext()) + { + context.BaseItems.AddRange(firstItem, secondItem, excludedItem); + context.ItemValues.AddRange(firstTag, duplicateTag, secondTag, genre, excludedTag, excludedGenre); + context.ItemValuesMap.AddRange( + CreateMap(firstItem, firstTag), + CreateMap(firstItem, duplicateTag), + CreateMap(secondItem, secondTag), + CreateMap(firstItem, genre), + CreateMap(excludedItem, excludedTag), + CreateMap(excludedItem, excludedGenre)); + context.SaveChanges(); + } + + var result = _repository.GetQueryFiltersLegacy(new InternalItemsQuery(new Database.Implementations.Entities.User("test", "auth", "reset")) + { + IncludeItemTypes = [BaseItemKind.Movie] + }); + + Assert.Equal(["Alpha", "Beta"], result.Tags); + Assert.Equal(["Genre Leak"], result.Genres); + } + + [Fact] + public void GetGenreNames_GroupsAndFiltersMappedItemValues() + { + var movie = CreateMovieEntity(Guid.NewGuid(), "Movie"); + var audio = new BaseItemEntity + { + Id = Guid.NewGuid(), + Type = _audioTypeName, + Name = "Audio", + MediaType = "Audio", + IsFolder = false, + IsVirtualItem = false + }; + var movieGenre = CreateItemValue(ItemValueType.Genre, "Movie Genre", "movie genre"); + var duplicateMovieGenre = CreateItemValue(ItemValueType.Genre, "movie genre", "movie genre"); + var musicGenre = CreateItemValue(ItemValueType.Genre, "Music Genre", "music genre"); + var orphanedGenre = CreateItemValue(ItemValueType.Genre, "Orphaned Genre", "orphaned genre"); + + using (var context = CreateDbContext()) + { + context.BaseItems.AddRange(movie, audio); + context.ItemValues.AddRange(movieGenre, duplicateMovieGenre, musicGenre, orphanedGenre); + context.ItemValuesMap.AddRange( + CreateMap(movie, movieGenre), + CreateMap(movie, duplicateMovieGenre), + CreateMap(audio, musicGenre)); + context.SaveChanges(); + } + + Assert.Equal(["Movie Genre"], _repository.GetGenreNames()); + Assert.Equal(["Music Genre"], _repository.GetMusicGenreNames()); + } + + private BaseItemEntity CreateMovieEntity(Guid id, string name) + { + return new BaseItemEntity + { + Id = id, + Type = _movieTypeName, + Name = name, + MediaType = "Video", + IsMovie = true, + IsFolder = false, + IsVirtualItem = false + }; + } + + private static ItemValueMap CreateMap(BaseItemEntity item, ItemValue itemValue) + { + return new ItemValueMap + { + ItemId = item.Id, + ItemValueId = itemValue.ItemValueId, + Item = item, + ItemValue = itemValue + }; + } + + private static ItemValue CreateItemValue(ItemValueType type, string value, string cleanValue) + { + return new ItemValue + { + ItemValueId = Guid.NewGuid(), + Type = type, + Value = value, + CleanValue = cleanValue + }; + } +} diff --git a/tests/Jellyfin.Server.Implementations.Tests/Item/ItemPersistenceServiceSaveImagesTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Item/ItemPersistenceServiceSaveImagesTests.cs new file mode 100644 index 0000000000..7997c6d771 --- /dev/null +++ b/tests/Jellyfin.Server.Implementations.Tests/Item/ItemPersistenceServiceSaveImagesTests.cs @@ -0,0 +1,77 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Jellyfin.Database.Implementations.Entities; +using Jellyfin.Server.Implementations.Item; +using MediaBrowser.Controller; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Entities; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging.Abstractions; +using Moq; +using Xunit; + +namespace Jellyfin.Server.Implementations.Tests.Item; + +public class ItemPersistenceServiceSaveImagesTests : SqliteDbTestFixture +{ + private readonly ItemPersistenceService _service; + + public ItemPersistenceServiceSaveImagesTests() + { + _service = new ItemPersistenceService( + CreateDbContextFactory(), + Mock.Of<IServerApplicationHost>(), + NullLogger<ItemPersistenceService>.Instance); + } + + [Fact] + public async Task SaveImagesAsync_ReplacesThePreviousImages() + { + var itemId = Guid.NewGuid(); + Seed(itemId); + + await _service.SaveImagesAsync(CreateItem(itemId, "/first.jpg"), TestContext.Current.CancellationToken); + await _service.SaveImagesAsync(CreateItem(itemId, "/second.jpg"), TestContext.Current.CancellationToken); + + using var context = CreateDbContext(); + var paths = context.BaseItemImageInfos + .Where(e => e.ItemId.Equals(itemId)) + .Select(e => e.Path) + .ToList(); + + Assert.Equal(["/second.jpg"], paths); + } + + [Fact] + public async Task SaveImagesAsync_ItemDeletedFromUnderIt_IsANoOp() + { + // A scan can delete the item between the refresh reading it and the images being written. That + // must not fail the whole refresh, and must not leave the images of an item that is gone. + var itemId = Guid.NewGuid(); + + await _service.SaveImagesAsync(CreateItem(itemId, "/gone.jpg"), TestContext.Current.CancellationToken); + + using var context = CreateDbContext(); + Assert.Empty(context.BaseItemImageInfos.Where(e => e.ItemId.Equals(itemId))); + } + + private static BaseItem CreateItem(Guid itemId, string imagePath) + => new Folder + { + Id = itemId, + ImageInfos = [new ItemImageInfo { Path = imagePath, Type = ImageType.Primary }] + }; + + private void Seed(Guid itemId) + { + using var context = CreateDbContext(); + context.BaseItems.Add(new BaseItemEntity + { + Id = itemId, + Type = "Folder", + IsFolder = true + }); + context.SaveChanges(); + } +} diff --git a/tests/Jellyfin.Server.Implementations.Tests/Item/SqliteDbTestFixture.cs b/tests/Jellyfin.Server.Implementations.Tests/Item/SqliteDbTestFixture.cs index 87efa8fea5..cfc9c9496c 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Item/SqliteDbTestFixture.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Item/SqliteDbTestFixture.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using Emby.Server.Implementations.Data; using Jellyfin.Database.Implementations; using Jellyfin.Database.Implementations.Locking; @@ -58,6 +59,8 @@ public abstract class SqliteDbTestFixture : IDisposable { var factory = new Mock<IDbContextFactory<JellyfinDbContext>>(); factory.Setup(f => f.CreateDbContext()).Returns(CreateDbContext); + factory.Setup(f => f.CreateDbContextAsync(It.IsAny<CancellationToken>())) + .ReturnsAsync(CreateDbContext); return factory.Object; } |
