diff options
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/Jellyfin.Server.Implementations.Tests/Dto/DtoServiceImageInheritanceTests.cs | 137 | ||||
| -rw-r--r-- | tests/Jellyfin.Server.Implementations.Tests/Users/UserManagerLockHelperTests.cs | 93 |
2 files changed, 230 insertions, 0 deletions
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Dto/DtoServiceImageInheritanceTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Dto/DtoServiceImageInheritanceTests.cs new file mode 100644 index 0000000000..96625ae670 --- /dev/null +++ b/tests/Jellyfin.Server.Implementations.Tests/Dto/DtoServiceImageInheritanceTests.cs @@ -0,0 +1,137 @@ +using System; +using Emby.Server.Implementations.Dto; +using Emby.Server.Implementations.Playlists; +using Jellyfin.Data.Enums; +using MediaBrowser.Common; +using MediaBrowser.Controller.Chapters; +using MediaBrowser.Controller.Drawing; +using MediaBrowser.Controller.Dto; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.LiveTv; +using MediaBrowser.Controller.MediaEncoding; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Controller.Trickplay; +using MediaBrowser.Model.Entities; +using Moq; +using Xunit; + +namespace Jellyfin.Server.Implementations.Tests.Dto; + +public class DtoServiceImageInheritanceTests +{ + [Fact] + public void GetBaseItemDto_PlaylistsUserViewWithDisplayParentPrimary_UsesDisplayParentPrimaryImage() + { + var displayParent = new PlaylistsFolder + { + Id = Guid.NewGuid(), + ImageInfos = + [ + new ItemImageInfo + { + Type = ImageType.Primary, + Path = "/images/playlists-custom.jpg", + DateModified = new DateTime(2026, 1, 15, 12, 0, 0, DateTimeKind.Utc) + } + ] + }; + + var userView = new UserView + { + Id = Guid.NewGuid(), + ViewType = CollectionType.playlists, + DisplayParentId = displayParent.Id, + ImageInfos = + [ + new ItemImageInfo + { + Type = ImageType.Primary, + Path = "/images/generated.png", + DateModified = new DateTime(2026, 1, 10, 12, 0, 0, DateTimeKind.Utc) + } + ] + }; + + var dtoService = BuildDtoService(displayParent); + + var dto = dtoService.GetBaseItemDto(userView, new DtoOptions(false)); + + Assert.NotNull(dto.ParentPrimaryImageItemId); + Assert.Equal(displayParent.Id, dto.ParentPrimaryImageItemId); + Assert.Equal("/images/playlists-custom.jpg", dto.ParentPrimaryImageTag); + Assert.False(dto.ImageTags?.ContainsKey(ImageType.Primary)); + } + + [Fact] + public void GetBaseItemDto_PlaylistsUserViewWithoutDisplayParentPrimary_KeepsOwnPrimaryImage() + { + var displayParent = new PlaylistsFolder + { + Id = Guid.NewGuid(), + ImageInfos = [] + }; + + var userView = new UserView + { + Id = Guid.NewGuid(), + ViewType = CollectionType.playlists, + DisplayParentId = displayParent.Id, + ImageInfos = + [ + new ItemImageInfo + { + Type = ImageType.Primary, + Path = "/images/generated.png", + DateModified = new DateTime(2026, 1, 10, 12, 0, 0, DateTimeKind.Utc) + } + ] + }; + + var dtoService = BuildDtoService(displayParent); + + var dto = dtoService.GetBaseItemDto(userView, new DtoOptions(false)); + + Assert.Null(dto.ParentPrimaryImageItemId); + Assert.Null(dto.ParentPrimaryImageTag); + Assert.NotNull(dto.ImageTags); + Assert.True(dto.ImageTags.ContainsKey(ImageType.Primary)); + Assert.Equal("/images/generated.png", dto.ImageTags[ImageType.Primary]); + } + + private static DtoService BuildDtoService(BaseItem displayParent) + { + var libraryManager = new Mock<ILibraryManager>(); + var userDataManager = new Mock<IUserDataManager>(); + var imageProcessor = new Mock<IImageProcessor>(); + var providerManager = new Mock<IProviderManager>(); + var recordingsManager = new Mock<IRecordingsManager>(); + var appHost = new Mock<IApplicationHost>(); + var mediaSourceManager = new Mock<IMediaSourceManager>(); + var liveTvManager = new Mock<ILiveTvManager>(); + var trickplayManager = new Mock<ITrickplayManager>(); + var chapterManager = new Mock<IChapterManager>(); + var logger = new Mock<Microsoft.Extensions.Logging.ILogger<DtoService>>(); + + libraryManager + .Setup(x => x.GetItemById(displayParent.Id)) + .Returns(displayParent); + + imageProcessor + .Setup(x => x.GetImageCacheTag(It.IsAny<BaseItem>(), It.IsAny<ItemImageInfo>())) + .Returns<BaseItem, ItemImageInfo>((_, image) => image.Path); + + return new DtoService( + logger.Object, + libraryManager.Object, + userDataManager.Object, + imageProcessor.Object, + providerManager.Object, + recordingsManager.Object, + appHost.Object, + mediaSourceManager.Object, + new Lazy<ILiveTvManager>(() => liveTvManager.Object), + trickplayManager.Object, + chapterManager.Object); + } +} diff --git a/tests/Jellyfin.Server.Implementations.Tests/Users/UserManagerLockHelperTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Users/UserManagerLockHelperTests.cs new file mode 100644 index 0000000000..8149938b4d --- /dev/null +++ b/tests/Jellyfin.Server.Implementations.Tests/Users/UserManagerLockHelperTests.cs @@ -0,0 +1,93 @@ +using System; +using System.Threading.Tasks; +using Jellyfin.Server.Implementations.Users; +using Xunit; + +namespace Jellyfin.Server.Implementations.Tests.Users +{ + public class UserManagerLockHelperTests + { + [Fact] + public async Task LockAsync_WhenNested_DoesNotAcquireSecondLockAndRestoresStateOnDispose() + { + UserManager.LockHelper.IsNestedLock.Value = 0; + using var helper = new UserManager.LockHelper(); + var key = Guid.NewGuid(); + + Assert.True(helper.ShouldLock()); + + var outerHandle = await helper.LockAsync(key); + Assert.False(helper.ShouldLock()); + + var innerHandle = await helper.LockAsync(key); + Assert.False(helper.ShouldLock()); + + innerHandle.Dispose(); + Assert.False(helper.ShouldLock()); + + outerHandle.Dispose(); + Assert.True(helper.ShouldLock()); + } + + [Fact] + public async Task LockAsync_WithSameKey_BlocksSecondLockUntilFirstIsReleased() + { + UserManager.LockHelper.IsNestedLock.Value = 0; + using var helper = new UserManager.LockHelper(); + var key = Guid.NewGuid(); + + var firstAcquired = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously); + var releaseFirst = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously); + var secondEntered = false; + + var firstTask = Task.Run( + async () => + { + using var firstHandle = await helper.LockAsync(key); + firstAcquired.SetResult(true); + await releaseFirst.Task; + }, + TestContext.Current.CancellationToken); + + await firstAcquired.Task; + + var secondTask = Task.Run( + async () => + { + using var secondHandle = await helper.LockAsync(key); + secondEntered = true; + }, + TestContext.Current.CancellationToken); + + await Task.Delay(100, TestContext.Current.CancellationToken); + Assert.False(secondEntered); + + releaseFirst.SetResult(true); + + await Task.WhenAll(firstTask, secondTask); + Assert.True(secondEntered); + } + + [Fact] + public async Task LockAsync_WhenDisposed_ThrowsObjectDisposedException() + { + UserManager.LockHelper.IsNestedLock.Value = 0; + using var helper = new UserManager.LockHelper(); + helper.Dispose(); + + await Assert.ThrowsAsync<ObjectDisposedException>(async () => await helper.LockAsync(Guid.NewGuid())); + } + + [Fact] + public void Dispose_WhenCalledMultipleTimes_DoesNotThrow() + { + UserManager.LockHelper.IsNestedLock.Value = 0; + using var helper = new UserManager.LockHelper(); + + helper.Dispose(); + var ex = Record.Exception(() => helper.Dispose()); + + Assert.Null(ex); + } + } +} |
