aboutsummaryrefslogtreecommitdiff
path: root/tests/Jellyfin.Server.Implementations.Tests/Library/LibraryManager/ResolveAlternateVersionTests.cs
diff options
context:
space:
mode:
Diffstat (limited to 'tests/Jellyfin.Server.Implementations.Tests/Library/LibraryManager/ResolveAlternateVersionTests.cs')
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Library/LibraryManager/ResolveAlternateVersionTests.cs145
1 files changed, 145 insertions, 0 deletions
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/LibraryManager/ResolveAlternateVersionTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/LibraryManager/ResolveAlternateVersionTests.cs
new file mode 100644
index 0000000000..31109b2968
--- /dev/null
+++ b/tests/Jellyfin.Server.Implementations.Tests/Library/LibraryManager/ResolveAlternateVersionTests.cs
@@ -0,0 +1,145 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using AutoFixture;
+using AutoFixture.AutoMoq;
+using Emby.Naming.Common;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Controller.MediaSegments;
+using MediaBrowser.Controller.Persistence;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Controller.Resolvers;
+using MediaBrowser.Controller.Sorting;
+using MediaBrowser.Model.IO;
+using Moq;
+using Xunit;
+
+namespace Jellyfin.Server.Implementations.Tests.Library.LibraryManager;
+
+public sealed class ResolveAlternateVersionTests : IDisposable
+{
+ private const string PrimaryPath = "/movies/Up/Up.mkv";
+ private const string AlternatePath = "/movies/Up/Up - 1080p.mkv";
+
+ private readonly Emby.Server.Implementations.Library.LibraryManager _libraryManager;
+ private readonly Mock<IItemPersistenceService> _persistenceServiceMock;
+ private readonly Folder _staleParent;
+ private readonly ILibraryManager? _previousLibraryManager;
+ private readonly IMediaSourceManager? _previousMediaSourceManager;
+ private readonly IItemRepository? _previousItemRepository;
+
+ public ResolveAlternateVersionTests()
+ {
+ var fixture = new Fixture().Customize(new AutoMoqCustomization());
+ fixture.Register(() => new NamingOptions());
+ fixture.Freeze<Mock<IServerConfigurationManager>>()
+ .Setup(c => c.ApplicationPaths.ProgramDataPath).Returns("/data");
+ _persistenceServiceMock = fixture.Freeze<Mock<IItemPersistenceService>>();
+ var itemRepositoryMock = fixture.Freeze<Mock<IItemRepository>>();
+ fixture.Freeze<Mock<IFileSystem>>()
+ .Setup(f => f.GetFileInfo(It.IsAny<string>()))
+ .Returns<string>(path => new FileSystemMetadata { FullName = path });
+
+ _libraryManager = fixture.Build<Emby.Server.Implementations.Library.LibraryManager>()
+ .Do(s => s.AddParts(
+ fixture.Create<IEnumerable<IResolverIgnoreRule>>(),
+ [],
+ fixture.Create<IEnumerable<IIntroProvider>>(),
+ fixture.Create<IEnumerable<IBaseItemComparer>>(),
+ fixture.Create<IEnumerable<ILibraryPostScanTask>>()))
+ .Create();
+
+ // BaseItem resolves these through process-wide statics; restored in Dispose.
+ _previousLibraryManager = BaseItem.LibraryManager;
+ _previousMediaSourceManager = BaseItem.MediaSourceManager;
+ _previousItemRepository = BaseItem.ItemRepository;
+ BaseItem.LibraryManager = _libraryManager;
+
+ var mediaSourceManagerMock = new Mock<IMediaSourceManager>();
+ mediaSourceManagerMock.Setup(m => m.GetMediaStreams(It.IsAny<Guid>())).Returns([]);
+ mediaSourceManagerMock.Setup(m => m.GetMediaAttachments(It.IsAny<Guid>())).Returns([]);
+ BaseItem.MediaSourceManager = mediaSourceManagerMock.Object;
+
+ // A reloaded listing comes back empty, so a stale entry surviving is visible.
+ itemRepositoryMock.Setup(i => i.GetItemList(It.IsAny<InternalItemsQuery>())).Returns([]);
+ BaseItem.ItemRepository = itemRepositoryMock.Object;
+
+ BaseItem.FileSystem ??= fixture.Create<IFileSystem>();
+ BaseItem.MediaSegmentManager ??= fixture.Create<IMediaSegmentManager>();
+ BaseItem.ConfigurationManager ??= fixture.Create<IServerConfigurationManager>();
+ Video.RecordingsManager ??= fixture.Create<IRecordingsManager>();
+
+ var primary = new Movie
+ {
+ Name = "Up",
+ Path = PrimaryPath,
+ LocalAlternateVersions = [AlternatePath],
+ Id = _libraryManager.GetNewItemId(PrimaryPath, typeof(Movie))
+ };
+
+ _staleParent = new Folder
+ {
+ Name = "Up",
+ Path = "/movies/Up",
+ Id = _libraryManager.GetNewItemId("/movies/Up", typeof(Folder))
+ };
+
+ var staleAlternate = new Video
+ {
+ Name = "Up - 1080p",
+ Path = AlternatePath,
+ OwnerId = primary.Id,
+ ParentId = _staleParent.Id,
+ Id = _libraryManager.GetNewItemId(AlternatePath, typeof(Video))
+ };
+ staleAlternate.SetPrimaryVersionId(primary.Id);
+
+ itemRepositoryMock
+ .Setup(i => i.RetrieveItem(It.IsAny<Guid>()))
+ .Returns<Guid>(id => id.Equals(primary.Id) ? primary
+ : id.Equals(staleAlternate.Id) ? staleAlternate
+ : id.Equals(_staleParent.Id) ? _staleParent
+ : null!);
+
+ StaleAlternateId = staleAlternate.Id;
+ }
+
+ private Guid StaleAlternateId { get; }
+
+ public void Dispose()
+ {
+ BaseItem.LibraryManager = _previousLibraryManager!;
+ BaseItem.MediaSourceManager = _previousMediaSourceManager!;
+ BaseItem.ItemRepository = _previousItemRepository!;
+ }
+
+ [Fact]
+ public void ResolveAlternateVersion_StaleWrongTypeItem_DropsRowWithoutResavingPrimary()
+ {
+ // The alternate is stored under the id of the generic Video type while its primary is a Movie.
+ _libraryManager.ResolveAlternateVersion(AlternatePath, typeof(Movie), null, null);
+
+ _persistenceServiceMock.Verify(
+ p => p.DeleteItem(It.Is<IReadOnlyList<Guid>>(ids => ids.Count == 1 && ids[0].Equals(StaleAlternateId))),
+ Times.Once);
+
+ // Saving the primary is what re-enters this method before the stale row is gone.
+ _persistenceServiceMock.Verify(
+ p => p.SaveItems(It.IsAny<IReadOnlyList<BaseItem>>(), It.IsAny<CancellationToken>()),
+ Times.Never);
+ }
+
+ [Fact]
+ public void ResolveAlternateVersion_StaleWrongTypeItem_DropsCachedParentListing()
+ {
+ _staleParent.Children = [new Video { Name = "Up - 1080p", Path = AlternatePath }];
+
+ _libraryManager.ResolveAlternateVersion(AlternatePath, typeof(Movie), null, null);
+
+ Assert.Empty(_staleParent.Children);
+ }
+}