diff options
| author | Nyanmisaka <nst799610810@gmail.com> | 2024-07-23 15:37:33 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-07-23 15:37:33 +0800 |
| commit | 00088c295445fe2710cae468e1b09f98a32e40a5 (patch) | |
| tree | 77614fb434409bc2ddf3d7d0b5830339a6374bfb /tests | |
| parent | deb36eeedaba2f1421b92d290d85d45bfe48d1f5 (diff) | |
| parent | 19dca018b2604ff8666cabaf9d0f9c8974572756 (diff) | |
Merge branch 'master' into fix-hwa-video-rotation
Diffstat (limited to 'tests')
23 files changed, 456 insertions, 130 deletions
diff --git a/tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandlerTests.cs index 1ea1797ba..31d2b486b 100644 --- a/tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandlerTests.cs +++ b/tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandlerTests.cs @@ -1,13 +1,19 @@ +using System; using System.Collections.Generic; +using System.Security.Claims; using System.Threading.Tasks; using AutoFixture; using AutoFixture.AutoMoq; +using Jellyfin.Api.Auth.DefaultAuthorizationPolicy; using Jellyfin.Api.Auth.FirstTimeSetupPolicy; using Jellyfin.Api.Constants; +using Jellyfin.Data.Entities; +using Jellyfin.Data.Enums; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Library; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; using Moq; using Xunit; @@ -17,7 +23,9 @@ namespace Jellyfin.Api.Tests.Auth.FirstTimeSetupPolicy { private readonly Mock<IConfigurationManager> _configurationManagerMock; private readonly List<IAuthorizationRequirement> _requirements; + private readonly DefaultAuthorizationHandler _defaultAuthorizationHandler; private readonly FirstTimeSetupHandler _firstTimeSetupHandler; + private readonly IAuthorizationService _authorizationService; private readonly Mock<IUserManager> _userManagerMock; private readonly Mock<IHttpContextAccessor> _httpContextAccessor; @@ -30,6 +38,21 @@ namespace Jellyfin.Api.Tests.Auth.FirstTimeSetupPolicy _httpContextAccessor = fixture.Freeze<Mock<IHttpContextAccessor>>(); _firstTimeSetupHandler = fixture.Create<FirstTimeSetupHandler>(); + _defaultAuthorizationHandler = fixture.Create<DefaultAuthorizationHandler>(); + + var services = new ServiceCollection(); + services.AddAuthorizationCore(); + services.AddLogging(); + services.AddOptions(); + services.AddSingleton<IAuthorizationHandler>(_defaultAuthorizationHandler); + services.AddSingleton<IAuthorizationHandler>(_firstTimeSetupHandler); + services.AddAuthorization(options => + { + options.AddPolicy("FirstTime", policy => policy.Requirements.Add(new FirstTimeSetupRequirement())); + options.AddPolicy("FirstTimeNoAdmin", policy => policy.Requirements.Add(new FirstTimeSetupRequirement(false, false))); + options.AddPolicy("FirstTimeSchedule", policy => policy.Requirements.Add(new FirstTimeSetupRequirement(true, false))); + }); + _authorizationService = services.BuildServiceProvider().GetRequiredService<IAuthorizationService>(); } [Theory] @@ -44,10 +67,9 @@ namespace Jellyfin.Api.Tests.Auth.FirstTimeSetupPolicy _httpContextAccessor, userRole); - var context = new AuthorizationHandlerContext(_requirements, claims, null); + var allowed = await _authorizationService.AuthorizeAsync(claims, "FirstTime"); - await _firstTimeSetupHandler.HandleAsync(context); - Assert.True(context.HasSucceeded); + Assert.True(allowed.Succeeded); } [Theory] @@ -62,10 +84,43 @@ namespace Jellyfin.Api.Tests.Auth.FirstTimeSetupPolicy _httpContextAccessor, userRole); - var context = new AuthorizationHandlerContext(_requirements, claims, null); + var allowed = await _authorizationService.AuthorizeAsync(claims, "FirstTime"); + + Assert.Equal(shouldSucceed, allowed.Succeeded); + } + + [Theory] + [InlineData(UserRoles.Administrator, true)] + [InlineData(UserRoles.Guest, false)] + [InlineData(UserRoles.User, true)] + public async Task ShouldRequireUserIfNotAdministrator(string userRole, bool shouldSucceed) + { + TestHelpers.SetupConfigurationManager(_configurationManagerMock, true); + var claims = TestHelpers.SetupUser( + _userManagerMock, + _httpContextAccessor, + userRole); + + var allowed = await _authorizationService.AuthorizeAsync(claims, "FirstTimeNoAdmin"); + + Assert.Equal(shouldSucceed, allowed.Succeeded); + } + + [Fact] + public async Task ShouldDisallowUserIfOutsideSchedule() + { + AccessSchedule[] accessSchedules = { new AccessSchedule(DynamicDayOfWeek.Everyday, 0, 0, Guid.Empty) }; + + TestHelpers.SetupConfigurationManager(_configurationManagerMock, true); + var claims = TestHelpers.SetupUser( + _userManagerMock, + _httpContextAccessor, + UserRoles.User, + accessSchedules); + + var allowed = await _authorizationService.AuthorizeAsync(claims, "FirstTimeSchedule"); - await _firstTimeSetupHandler.HandleAsync(context); - Assert.Equal(shouldSucceed, context.HasSucceeded); + Assert.False(allowed.Succeeded); } } } diff --git a/tests/Jellyfin.Controller.Tests/DirectoryServiceTests.cs b/tests/Jellyfin.Controller.Tests/DirectoryServiceTests.cs index 46439aecb..83a023384 100644 --- a/tests/Jellyfin.Controller.Tests/DirectoryServiceTests.cs +++ b/tests/Jellyfin.Controller.Tests/DirectoryServiceTests.cs @@ -80,6 +80,21 @@ namespace Jellyfin.Controller.Tests } [Fact] + public void GetDirectories_GivenPathsWithDifferentCasing_ReturnsCorrectDirectories() + { + var fileSystemMock = new Mock<IFileSystem>(); + fileSystemMock.Setup(f => f.GetFileSystemEntries(It.Is<string>(x => x == UpperCasePath), false)).Returns(_upperCaseFileSystemMetadata); + fileSystemMock.Setup(f => f.GetFileSystemEntries(It.Is<string>(x => x == LowerCasePath), false)).Returns(_lowerCaseFileSystemMetadata); + var directoryService = new DirectoryService(fileSystemMock.Object); + + var upperCaseResult = directoryService.GetDirectories(UpperCasePath); + var lowerCaseResult = directoryService.GetDirectories(LowerCasePath); + + Assert.Equal(_upperCaseFileSystemMetadata.Where(f => f.IsDirectory), upperCaseResult); + Assert.Equal(_lowerCaseFileSystemMetadata.Where(f => f.IsDirectory), lowerCaseResult); + } + + [Fact] public void GetFile_GivenFilePathsWithDifferentCasing_ReturnsCorrectFile() { const string lowerCasePath = "/music/someartist/song 1.mp3"; @@ -95,15 +110,52 @@ namespace Jellyfin.Controller.Tests Exists = false }; var fileSystemMock = new Mock<IFileSystem>(); - fileSystemMock.Setup(f => f.GetFileInfo(It.Is<string>(x => x == upperCasePath))).Returns(upperCaseFileSystemMetadata); - fileSystemMock.Setup(f => f.GetFileInfo(It.Is<string>(x => x == lowerCasePath))).Returns(lowerCaseFileSystemMetadata); + fileSystemMock.Setup(f => f.GetFileSystemInfo(It.Is<string>(x => x == upperCasePath))).Returns(upperCaseFileSystemMetadata); + fileSystemMock.Setup(f => f.GetFileSystemInfo(It.Is<string>(x => x == lowerCasePath))).Returns(lowerCaseFileSystemMetadata); + var directoryService = new DirectoryService(fileSystemMock.Object); + + var lowerCaseDirResult = directoryService.GetDirectory(lowerCasePath); + var lowerCaseFileResult = directoryService.GetFile(lowerCasePath); + var upperCaseDirResult = directoryService.GetDirectory(upperCasePath); + var upperCaseFileResult = directoryService.GetFile(upperCasePath); + + Assert.Null(lowerCaseDirResult); + Assert.Equal(lowerCaseFileSystemMetadata, lowerCaseFileResult); + Assert.Null(upperCaseDirResult); + Assert.Null(upperCaseFileResult); + } + + [Fact] + public void GetDirectory_GivenFilePathsWithDifferentCasing_ReturnsCorrectDirectory() + { + const string lowerCasePath = "/music/someartist/Lyrics"; + var lowerCaseFileSystemMetadata = new FileSystemMetadata + { + FullName = lowerCasePath, + IsDirectory = true, + Exists = true + }; + const string upperCasePath = "/music/SOMEARTIST/LYRICS"; + var upperCaseFileSystemMetadata = new FileSystemMetadata + { + FullName = upperCasePath, + IsDirectory = true, + Exists = false + }; + var fileSystemMock = new Mock<IFileSystem>(); + fileSystemMock.Setup(f => f.GetFileSystemInfo(It.Is<string>(x => x == upperCasePath))).Returns(upperCaseFileSystemMetadata); + fileSystemMock.Setup(f => f.GetFileSystemInfo(It.Is<string>(x => x == lowerCasePath))).Returns(lowerCaseFileSystemMetadata); var directoryService = new DirectoryService(fileSystemMock.Object); - var lowerCaseResult = directoryService.GetFile(lowerCasePath); - var upperCaseResult = directoryService.GetFile(upperCasePath); + var lowerCaseDirResult = directoryService.GetDirectory(lowerCasePath); + var lowerCaseFileResult = directoryService.GetFile(lowerCasePath); + var upperCaseDirResult = directoryService.GetDirectory(upperCasePath); + var upperCaseFileResult = directoryService.GetFile(upperCasePath); - Assert.Equal(lowerCaseFileSystemMetadata, lowerCaseResult); - Assert.Null(upperCaseResult); + Assert.Equal(lowerCaseFileSystemMetadata, lowerCaseDirResult); + Assert.Null(lowerCaseFileResult); + Assert.Null(upperCaseDirResult); + Assert.Null(upperCaseFileResult); } [Fact] @@ -122,11 +174,11 @@ namespace Jellyfin.Controller.Tests }; var fileSystemMock = new Mock<IFileSystem>(); - fileSystemMock.Setup(f => f.GetFileInfo(It.Is<string>(x => x == path))).Returns(cachedFileSystemMetadata); + fileSystemMock.Setup(f => f.GetFileSystemInfo(It.Is<string>(x => x == path))).Returns(cachedFileSystemMetadata); var directoryService = new DirectoryService(fileSystemMock.Object); var result = directoryService.GetFile(path); - fileSystemMock.Setup(f => f.GetFileInfo(It.Is<string>(x => x == path))).Returns(newFileSystemMetadata); + fileSystemMock.Setup(f => f.GetFileSystemInfo(It.Is<string>(x => x == path))).Returns(newFileSystemMetadata); var secondResult = directoryService.GetFile(path); Assert.Equal(cachedFileSystemMetadata, result); diff --git a/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonLowerCaseConverterTests.cs b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonLowerCaseConverterTests.cs deleted file mode 100644 index 16c69ca48..000000000 --- a/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonLowerCaseConverterTests.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System.Text.Json; -using System.Text.Json.Serialization; -using Jellyfin.Extensions.Json.Converters; -using MediaBrowser.Model.Entities; -using Xunit; - -namespace Jellyfin.Extensions.Tests.Json.Converters -{ - public class JsonLowerCaseConverterTests - { - private readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions() - { - Converters = - { - new JsonStringEnumConverter() - } - }; - - [Theory] - [InlineData(null, "{\"CollectionType\":null}")] - [InlineData(CollectionTypeOptions.Movies, "{\"CollectionType\":\"movies\"}")] - [InlineData(CollectionTypeOptions.MusicVideos, "{\"CollectionType\":\"musicvideos\"}")] - public void Serialize_CollectionTypeOptions_Correct(CollectionTypeOptions? collectionType, string expected) - { - Assert.Equal(expected, JsonSerializer.Serialize(new TestContainer(collectionType), _jsonOptions)); - } - - [Theory] - [InlineData("{\"CollectionType\":null}", null)] - [InlineData("{\"CollectionType\":\"movies\"}", CollectionTypeOptions.Movies)] - [InlineData("{\"CollectionType\":\"musicvideos\"}", CollectionTypeOptions.MusicVideos)] - public void Deserialize_CollectionTypeOptions_Correct(string json, CollectionTypeOptions? result) - { - var res = JsonSerializer.Deserialize<TestContainer>(json, _jsonOptions); - Assert.NotNull(res); - Assert.Equal(result, res!.CollectionType); - } - - [Theory] - [InlineData(null)] - [InlineData(CollectionTypeOptions.Movies)] - [InlineData(CollectionTypeOptions.MusicVideos)] - public void RoundTrip_CollectionTypeOptions_Correct(CollectionTypeOptions? value) - { - var res = JsonSerializer.Deserialize<TestContainer>(JsonSerializer.Serialize(new TestContainer(value), _jsonOptions), _jsonOptions); - Assert.NotNull(res); - Assert.Equal(value, res!.CollectionType); - } - - [Theory] - [InlineData("{\"CollectionType\":null}")] - [InlineData("{\"CollectionType\":\"movies\"}")] - [InlineData("{\"CollectionType\":\"musicvideos\"}")] - public void RoundTrip_String_Correct(string json) - { - var res = JsonSerializer.Serialize(JsonSerializer.Deserialize<TestContainer>(json, _jsonOptions), _jsonOptions); - Assert.Equal(json, res); - } - - private sealed class TestContainer - { - public TestContainer(CollectionTypeOptions? collectionType) - { - CollectionType = collectionType; - } - - [JsonConverter(typeof(JsonLowerCaseConverter<CollectionTypeOptions?>))] - public CollectionTypeOptions? CollectionType { get; set; } - } - } -} diff --git a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeExternalSourcesTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeExternalSourcesTests.cs index 263f74c90..84008cffd 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeExternalSourcesTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeExternalSourcesTests.cs @@ -35,7 +35,7 @@ namespace Jellyfin.MediaEncoding.Tests.Probing Protocol = MediaProtocol.Http, RequiredHttpHeaders = new Dictionary<string, string>() { - { "user_agent", userAgent }, + { "User-Agent", userAgent }, } }, ExtractChapters = false, @@ -44,7 +44,7 @@ namespace Jellyfin.MediaEncoding.Tests.Probing var extraArg = encoder.GetExtraArguments(req); - Assert.Contains(userAgent, extraArg, StringComparison.InvariantCulture); + Assert.Contains($"-user_agent \"{userAgent}\"", extraArg, StringComparison.InvariantCulture); } } } diff --git a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs index 1becf07f5..df51d39cb 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs @@ -18,7 +18,7 @@ namespace Jellyfin.MediaEncoding.Tests.Probing public class ProbeResultNormalizerTests { private readonly JsonSerializerOptions _jsonOptions; - private readonly ProbeResultNormalizer _probeResultNormalizer = new ProbeResultNormalizer(new NullLogger<EncoderValidatorTests>(), null); + private readonly ProbeResultNormalizer _probeResultNormalizer = new ProbeResultNormalizer(new NullLogger<EncoderValidatorTests>(), new Mock<ILocalizationManager>().Object); public ProbeResultNormalizerTests() { diff --git a/tests/Jellyfin.Model.Tests/Entities/ProviderIdsExtensionsTests.cs b/tests/Jellyfin.Model.Tests/Entities/ProviderIdsExtensionsTests.cs index 2a62ab74c..a6f416414 100644 --- a/tests/Jellyfin.Model.Tests/Entities/ProviderIdsExtensionsTests.cs +++ b/tests/Jellyfin.Model.Tests/Entities/ProviderIdsExtensionsTests.cs @@ -141,7 +141,7 @@ namespace Jellyfin.Model.Tests.Entities public void SetProviderId_Null_Remove() { var provider = new ProviderIdsExtensionsTestsObject(); - provider.SetProviderId(MetadataProvider.Imdb, null!); + Assert.Throws<ArgumentNullException>(() => provider.SetProviderId(MetadataProvider.Imdb, null!)); Assert.Empty(provider.ProviderIds); } @@ -150,8 +150,8 @@ namespace Jellyfin.Model.Tests.Entities { var provider = new ProviderIdsExtensionsTestsObject(); provider.ProviderIds[MetadataProvider.Imdb.ToString()] = ExampleImdbId; - provider.SetProviderId(MetadataProvider.Imdb, string.Empty); - Assert.Empty(provider.ProviderIds); + Assert.Throws<ArgumentException>(() => provider.SetProviderId(MetadataProvider.Imdb, string.Empty)); + Assert.Single(provider.ProviderIds); } [Fact] @@ -182,10 +182,20 @@ namespace Jellyfin.Model.Tests.Entities ProviderIds = null! }; - nullProvider.SetProviderId(MetadataProvider.Imdb, string.Empty); + Assert.Throws<ArgumentException>(() => nullProvider.SetProviderId(MetadataProvider.Imdb, string.Empty)); Assert.Null(nullProvider.ProviderIds); } + [Fact] + public void RemoveProviderId_Null_Remove() + { + var provider = new ProviderIdsExtensionsTestsObject(); + + provider.ProviderIds[MetadataProvider.Imdb.ToString()] = ExampleImdbId; + provider.RemoveProviderId(MetadataProvider.Imdb); + Assert.Empty(provider.ProviderIds); + } + private sealed class ProviderIdsExtensionsTestsObject : IHasProviderIds { public static readonly ProviderIdsExtensionsTestsObject Empty = new ProviderIdsExtensionsTestsObject(); diff --git a/tests/Jellyfin.Naming.Tests/ExternalFiles/ExternalPathParserTests.cs b/tests/Jellyfin.Naming.Tests/ExternalFiles/ExternalPathParserTests.cs index ba602b5d2..0b8b1f644 100644 --- a/tests/Jellyfin.Naming.Tests/ExternalFiles/ExternalPathParserTests.cs +++ b/tests/Jellyfin.Naming.Tests/ExternalFiles/ExternalPathParserTests.cs @@ -104,6 +104,7 @@ public class ExternalPathParserTests [InlineData(".en.cc.title", "title", "eng", false, false, true)] [InlineData(".hi.en.title", "title", "eng", false, false, true)] [InlineData(".en.hi.title", "title", "eng", false, false, true)] + [InlineData(".Subs for Chinese Audio.eng", "Subs for Chinese Audio", "eng", false, false, false)] public void ParseFile_ExtraTokens_ParseToValues(string tokens, string? title, string? language, bool isDefault = false, bool isForced = false, bool isHearingImpaired = false) { var path = "My.Video" + tokens + ".srt"; diff --git a/tests/Jellyfin.Naming.Tests/TV/TvParserHelpersTest.cs b/tests/Jellyfin.Naming.Tests/TV/TvParserHelpersTest.cs new file mode 100644 index 000000000..2d4b5b730 --- /dev/null +++ b/tests/Jellyfin.Naming.Tests/TV/TvParserHelpersTest.cs @@ -0,0 +1,31 @@ +using Emby.Naming.TV; +using MediaBrowser.Model.Entities; +using Xunit; + +namespace Jellyfin.Naming.Tests.TV; + +public class TvParserHelpersTest +{ + [Theory] + [InlineData("Ended", SeriesStatus.Ended)] + [InlineData("Cancelled", SeriesStatus.Ended)] + [InlineData("Continuing", SeriesStatus.Continuing)] + [InlineData("Returning", SeriesStatus.Continuing)] + [InlineData("Returning Series", SeriesStatus.Continuing)] + [InlineData("Unreleased", SeriesStatus.Unreleased)] + public void SeriesStatusParserTest_Valid(string statusString, SeriesStatus? status) + { + var successful = TvParserHelpers.TryParseSeriesStatus(statusString, out var parsered); + Assert.True(successful); + Assert.Equal(status, parsered); + } + + [Theory] + [InlineData("XXX")] + public void SeriesStatusParserTest_InValid(string statusString) + { + var successful = TvParserHelpers.TryParseSeriesStatus(statusString, out var parsered); + Assert.False(successful); + Assert.Null(parsered); + } +} diff --git a/tests/Jellyfin.Providers.Tests/Manager/ItemImageProviderTests.cs b/tests/Jellyfin.Providers.Tests/Manager/ItemImageProviderTests.cs index be5a401b1..5dd3eb8ab 100644 --- a/tests/Jellyfin.Providers.Tests/Manager/ItemImageProviderTests.cs +++ b/tests/Jellyfin.Providers.Tests/Manager/ItemImageProviderTests.cs @@ -209,7 +209,7 @@ namespace Jellyfin.Providers.Tests.Manager [InlineData(ImageType.Backdrop, 2, false)] [InlineData(ImageType.Primary, 1, true)] [InlineData(ImageType.Backdrop, 2, true)] - public async void RefreshImages_PopulatedItemPopulatedProviderDynamic_UpdatesImagesIfForced(ImageType imageType, int imageCount, bool forceRefresh) + public async Task RefreshImages_PopulatedItemPopulatedProviderDynamic_UpdatesImagesIfForced(ImageType imageType, int imageCount, bool forceRefresh) { var item = GetItemWithImages(imageType, imageCount, false); @@ -261,7 +261,7 @@ namespace Jellyfin.Providers.Tests.Manager [InlineData(ImageType.Backdrop, 2, true, MediaProtocol.File)] [InlineData(ImageType.Primary, 1, false, MediaProtocol.File)] [InlineData(ImageType.Backdrop, 2, false, MediaProtocol.File)] - public async void RefreshImages_EmptyItemPopulatedProviderDynamic_AddsImages(ImageType imageType, int imageCount, bool responseHasPath, MediaProtocol protocol) + public async Task RefreshImages_EmptyItemPopulatedProviderDynamic_AddsImages(ImageType imageType, int imageCount, bool responseHasPath, MediaProtocol protocol) { // Has to exist for querying DateModified time on file, results stored but not checked so not populating BaseItem.FileSystem = Mock.Of<IFileSystem>(); @@ -311,7 +311,7 @@ namespace Jellyfin.Providers.Tests.Manager [InlineData(ImageType.Primary, 1, true)] [InlineData(ImageType.Backdrop, 1, true)] [InlineData(ImageType.Backdrop, 2, true)] - public async void RefreshImages_PopulatedItemPopulatedProviderRemote_UpdatesImagesIfForced(ImageType imageType, int imageCount, bool forceRefresh) + public async Task RefreshImages_PopulatedItemPopulatedProviderRemote_UpdatesImagesIfForced(ImageType imageType, int imageCount, bool forceRefresh) { var item = GetItemWithImages(imageType, imageCount, false); @@ -366,7 +366,7 @@ namespace Jellyfin.Providers.Tests.Manager [InlineData(ImageType.Backdrop, 0, false)] // empty item, no cache to check [InlineData(ImageType.Backdrop, 1, false)] // populated item, cached so no download [InlineData(ImageType.Backdrop, 1, true)] // populated item, forced to download - public async void RefreshImages_NonStubItemPopulatedProviderRemote_DownloadsIfNecessary(ImageType imageType, int initialImageCount, bool fullRefresh) + public async Task RefreshImages_NonStubItemPopulatedProviderRemote_DownloadsIfNecessary(ImageType imageType, int initialImageCount, bool fullRefresh) { var targetImageCount = 1; @@ -429,7 +429,7 @@ namespace Jellyfin.Providers.Tests.Manager [Theory] [MemberData(nameof(GetImageTypesWithCount))] - public async void RefreshImages_EmptyItemPopulatedProviderRemoteExtras_LimitsImages(ImageType imageType, int imageCount) + public async Task RefreshImages_EmptyItemPopulatedProviderRemoteExtras_LimitsImages(ImageType imageType, int imageCount) { var item = new Video(); @@ -473,7 +473,7 @@ namespace Jellyfin.Providers.Tests.Manager [Theory] [MemberData(nameof(GetImageTypesWithCount))] - public async void RefreshImages_PopulatedItemEmptyProviderRemoteFullRefresh_DoesntClearImages(ImageType imageType, int imageCount) + public async Task RefreshImages_PopulatedItemEmptyProviderRemoteFullRefresh_DoesntClearImages(ImageType imageType, int imageCount) { var item = GetItemWithImages(imageType, imageCount, false); @@ -501,7 +501,7 @@ namespace Jellyfin.Providers.Tests.Manager [InlineData(9, false)] [InlineData(10, true)] [InlineData(null, true)] - public async void RefreshImages_ProviderRemote_FiltersByWidth(int? remoteImageWidth, bool expectedToUpdate) + public async Task RefreshImages_ProviderRemote_FiltersByWidth(int? remoteImageWidth, bool expectedToUpdate) { var imageType = ImageType.Primary; @@ -575,18 +575,22 @@ namespace Jellyfin.Providers.Tests.Manager // Has to exist for querying DateModified time on file, results stored but not checked so not populating BaseItem.FileSystem ??= Mock.Of<IFileSystem>(); - var item = new Video(); + var item = new Mock<Video> + { + CallBase = true + }; + item.Setup(m => m.IsSaveLocalMetadataEnabled()).Returns(false); var path = validPaths ? _testDataImagePath.Format : "invalid path {0}"; for (int i = 0; i < count; i++) { - item.SetImagePath(type, i, new FileSystemMetadata + item.Object.SetImagePath(type, i, new FileSystemMetadata { FullName = string.Format(CultureInfo.InvariantCulture, path, i), }); } - return item; + return item.Object; } private static ILocalImageProvider GetImageProvider(ImageType type, int count, bool validPaths) diff --git a/tests/Jellyfin.Providers.Tests/Manager/MetadataServiceTests.cs b/tests/Jellyfin.Providers.Tests/Manager/MetadataServiceTests.cs index ec4df9981..cedcaf9c0 100644 --- a/tests/Jellyfin.Providers.Tests/Manager/MetadataServiceTests.cs +++ b/tests/Jellyfin.Providers.Tests/Manager/MetadataServiceTests.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Movies; @@ -19,7 +20,7 @@ namespace Jellyfin.Providers.Tests.Manager [InlineData(true, true)] public void MergeBaseItemData_MergeMetadataSettings_MergesWhenSet(bool mergeMetadataSettings, bool defaultDate) { - var newLocked = new[] { MetadataField.Cast }; + var newLocked = new[] { MetadataField.Genres, MetadataField.Cast }; var newString = "new"; var newDate = DateTime.Now; @@ -77,7 +78,7 @@ namespace Jellyfin.Providers.Tests.Manager [Theory] [InlineData("Name", MetadataField.Name, false)] - [InlineData("OriginalTitle", null, false)] + [InlineData("OriginalTitle", null)] [InlineData("OfficialRating", MetadataField.OfficialRating)] [InlineData("CustomRating")] [InlineData("Tagline")] diff --git a/tests/Jellyfin.Providers.Tests/Manager/ProviderManagerTests.cs b/tests/Jellyfin.Providers.Tests/Manager/ProviderManagerTests.cs index 6fccce049..cced2b1e2 100644 --- a/tests/Jellyfin.Providers.Tests/Manager/ProviderManagerTests.cs +++ b/tests/Jellyfin.Providers.Tests/Manager/ProviderManagerTests.cs @@ -585,15 +585,17 @@ namespace Jellyfin.Providers.Tests.Manager IEnumerable<IMetadataService>? metadataServices = null, IEnumerable<IMetadataProvider>? metadataProviders = null, IEnumerable<IMetadataSaver>? metadataSavers = null, - IEnumerable<IExternalId>? externalIds = null) + IEnumerable<IExternalId>? externalIds = null, + IEnumerable<IExternalUrlProvider>? externalUrlProviders = null) { imageProviders ??= Array.Empty<IImageProvider>(); metadataServices ??= Array.Empty<IMetadataService>(); metadataProviders ??= Array.Empty<IMetadataProvider>(); metadataSavers ??= Array.Empty<IMetadataSaver>(); externalIds ??= Array.Empty<IExternalId>(); + externalUrlProviders ??= Array.Empty<IExternalUrlProvider>(); - providerManager.AddParts(imageProviders, metadataServices, metadataProviders, metadataSavers, externalIds); + providerManager.AddParts(imageProviders, metadataServices, metadataProviders, metadataSavers, externalIds, externalUrlProviders); } /// <summary> diff --git a/tests/Jellyfin.Providers.Tests/MediaInfo/AudioResolverTests.cs b/tests/Jellyfin.Providers.Tests/MediaInfo/AudioResolverTests.cs index d5f6873a2..290cb817a 100644 --- a/tests/Jellyfin.Providers.Tests/MediaInfo/AudioResolverTests.cs +++ b/tests/Jellyfin.Providers.Tests/MediaInfo/AudioResolverTests.cs @@ -64,7 +64,7 @@ public class AudioResolverTests [InlineData("My.Video.mp3", false, true)] [InlineData("My.Video.srt", true, false)] [InlineData("My.Video.mp3", true, true)] - public async void GetExternalStreams_MixedFilenames_PicksAudio(string file, bool metadataDirectory, bool matches) + public async Task GetExternalStreams_MixedFilenames_PicksAudio(string file, bool metadataDirectory, bool matches) { BaseItem.MediaSourceManager = Mock.Of<IMediaSourceManager>(); diff --git a/tests/Jellyfin.Providers.Tests/MediaInfo/EmbeddedImageProviderTests.cs b/tests/Jellyfin.Providers.Tests/MediaInfo/EmbeddedImageProviderTests.cs index 85963e5de..c0b41ba43 100644 --- a/tests/Jellyfin.Providers.Tests/MediaInfo/EmbeddedImageProviderTests.cs +++ b/tests/Jellyfin.Providers.Tests/MediaInfo/EmbeddedImageProviderTests.cs @@ -37,7 +37,7 @@ namespace Jellyfin.Providers.Tests.MediaInfo } [Fact] - public async void GetImage_NoStreams_ReturnsNoImage() + public async Task GetImage_NoStreams_ReturnsNoImage() { var input = new Movie(); @@ -55,7 +55,7 @@ namespace Jellyfin.Providers.Tests.MediaInfo [InlineData("clearlogo.png", null, 1, ImageType.Logo, ImageFormat.Png)] // extract extension from name [InlineData("backdrop", "image/bmp", 2, ImageType.Backdrop, ImageFormat.Bmp)] // extract extension from mimetype [InlineData("poster", null, 3, ImageType.Primary, ImageFormat.Jpg)] // default extension to jpg - public async void GetImage_Attachment_ReturnsCorrectSelection(string filename, string? mimetype, int targetIndex, ImageType type, ImageFormat? expectedFormat) + public async Task GetImage_Attachment_ReturnsCorrectSelection(string filename, string? mimetype, int targetIndex, ImageType type, ImageFormat? expectedFormat) { var attachments = new List<MediaAttachment>(); string pathPrefix = "path"; @@ -103,7 +103,7 @@ namespace Jellyfin.Providers.Tests.MediaInfo [InlineData(null, "mjpeg", 1, ImageType.Primary, ImageFormat.Jpg)] [InlineData(null, "png", 1, ImageType.Primary, ImageFormat.Png)] [InlineData(null, "webp", 1, ImageType.Primary, ImageFormat.Webp)] - public async void GetImage_Embedded_ReturnsCorrectSelection(string? label, string? codec, int targetIndex, ImageType type, ImageFormat? expectedFormat) + public async Task GetImage_Embedded_ReturnsCorrectSelection(string? label, string? codec, int targetIndex, ImageType type, ImageFormat? expectedFormat) { var streams = new List<MediaStream>(); for (int i = 1; i <= targetIndex; i++) diff --git a/tests/Jellyfin.Providers.Tests/MediaInfo/MediaInfoResolverTests.cs b/tests/Jellyfin.Providers.Tests/MediaInfo/MediaInfoResolverTests.cs index 58b67ae55..db427308c 100644 --- a/tests/Jellyfin.Providers.Tests/MediaInfo/MediaInfoResolverTests.cs +++ b/tests/Jellyfin.Providers.Tests/MediaInfo/MediaInfoResolverTests.cs @@ -182,7 +182,7 @@ public class MediaInfoResolverTests [Theory] [InlineData("https://url.com/My.Video.mkv")] [InlineData(VideoDirectoryPath)] // valid but no files found for this test - public async void GetExternalStreams_BadPaths_ReturnsNoSubtitles(string path) + public async Task GetExternalStreams_BadPaths_ReturnsNoSubtitles(string path) { // need a media source manager capable of returning something other than file protocol var mediaSourceManager = new Mock<IMediaSourceManager>(); @@ -285,7 +285,7 @@ public class MediaInfoResolverTests [Theory] [MemberData(nameof(GetExternalStreams_MergeMetadata_HandlesOverridesCorrectly_Data))] - public async void GetExternalStreams_MergeMetadata_HandlesOverridesCorrectly(string file, MediaStream[] inputStreams, MediaStream[] expectedStreams) + public async Task GetExternalStreams_MergeMetadata_HandlesOverridesCorrectly(string file, MediaStream[] inputStreams, MediaStream[] expectedStreams) { BaseItem.MediaSourceManager = Mock.Of<IMediaSourceManager>(); @@ -335,7 +335,7 @@ public class MediaInfoResolverTests [InlineData(1, 2)] [InlineData(2, 1)] [InlineData(2, 2)] - public async void GetExternalStreams_StreamIndex_HandlesFilesAndContainers(int fileCount, int streamCount) + public async Task GetExternalStreams_StreamIndex_HandlesFilesAndContainers(int fileCount, int streamCount) { BaseItem.MediaSourceManager = Mock.Of<IMediaSourceManager>(); diff --git a/tests/Jellyfin.Providers.Tests/MediaInfo/SubtitleResolverTests.cs b/tests/Jellyfin.Providers.Tests/MediaInfo/SubtitleResolverTests.cs index 8077bd791..e0d365927 100644 --- a/tests/Jellyfin.Providers.Tests/MediaInfo/SubtitleResolverTests.cs +++ b/tests/Jellyfin.Providers.Tests/MediaInfo/SubtitleResolverTests.cs @@ -64,7 +64,7 @@ public class SubtitleResolverTests [InlineData("My.Video.mp3", false, false)] [InlineData("My.Video.srt", true, true)] [InlineData("My.Video.mp3", true, false)] - public async void GetExternalStreams_MixedFilenames_PicksSubtitles(string file, bool metadataDirectory, bool matches) + public async Task GetExternalStreams_MixedFilenames_PicksSubtitles(string file, bool metadataDirectory, bool matches) { BaseItem.MediaSourceManager = Mock.Of<IMediaSourceManager>(); diff --git a/tests/Jellyfin.Providers.Tests/MediaInfo/VideoImageProviderTests.cs b/tests/Jellyfin.Providers.Tests/MediaInfo/VideoImageProviderTests.cs index 7ea6f7d9c..028f6feba 100644 --- a/tests/Jellyfin.Providers.Tests/MediaInfo/VideoImageProviderTests.cs +++ b/tests/Jellyfin.Providers.Tests/MediaInfo/VideoImageProviderTests.cs @@ -34,7 +34,7 @@ namespace Jellyfin.Providers.Tests.MediaInfo [Theory] [MemberData(nameof(GetImage_UnsupportedInput_ReturnsNoImage_TestData))] - public async void GetImage_UnsupportedInput_ReturnsNoImage(Video input) + public async Task GetImage_UnsupportedInput_ReturnsNoImage(Video input) { var mediaSourceManager = GetMediaSourceManager(input, null, new List<MediaStream>()); var videoImageProvider = new VideoImageProvider(mediaSourceManager, Mock.Of<IMediaEncoder>(), new NullLogger<VideoImageProvider>()); @@ -47,7 +47,7 @@ namespace Jellyfin.Providers.Tests.MediaInfo [Theory] [InlineData(1, 1)] // default not first stream [InlineData(5, 0)] // default out of valid range - public async void GetImage_DefaultVideoStreams_ReturnsCorrectStreamImage(int defaultIndex, int targetIndex) + public async Task GetImage_DefaultVideoStreams_ReturnsCorrectStreamImage(int defaultIndex, int targetIndex) { var input = new Movie { DefaultVideoStreamIndex = defaultIndex }; @@ -80,7 +80,7 @@ namespace Jellyfin.Providers.Tests.MediaInfo [Theory] [InlineData(null, 10)] // default time [InlineData(500, 50)] // calculated time - public async void GetImage_TimeSpan_SelectsCorrectTime(int? runTimeSeconds, long expectedSeconds) + public async Task GetImage_TimeSpan_SelectsCorrectTime(int? runTimeSeconds, long expectedSeconds) { MediaStream targetStream = new() { Type = MediaStreamType.Video, Index = 0 }; var input = new Movie diff --git a/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs b/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs index d991f5574..95a5b8179 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs @@ -20,26 +20,37 @@ namespace Jellyfin.Server.Implementations.Tests.IO _sut = _fixture.Create<ManagedFileSystem>(); } - [Theory] + [SkippableTheory] [InlineData("/Volumes/Library/Sample/Music/Playlists/", "../Beethoven/Misc/Moonlight Sonata.mp3", "/Volumes/Library/Sample/Music/Beethoven/Misc/Moonlight Sonata.mp3")] [InlineData("/Volumes/Library/Sample/Music/Playlists/", "../../Beethoven/Misc/Moonlight Sonata.mp3", "/Volumes/Library/Sample/Beethoven/Misc/Moonlight Sonata.mp3")] [InlineData("/Volumes/Library/Sample/Music/Playlists/", "Beethoven/Misc/Moonlight Sonata.mp3", "/Volumes/Library/Sample/Music/Playlists/Beethoven/Misc/Moonlight Sonata.mp3")] - public void MakeAbsolutePathCorrectlyHandlesRelativeFilePaths( + [InlineData("/Volumes/Library/Sample/Music/Playlists/", "/mnt/Beethoven/Misc/Moonlight Sonata.mp3", "/mnt/Beethoven/Misc/Moonlight Sonata.mp3")] + public void MakeAbsolutePathCorrectlyHandlesRelativeFilePathsOnUnixLike( string folderPath, string filePath, string expectedAbsolutePath) { + Skip.If(OperatingSystem.IsWindows()); + + var generatedPath = _sut.MakeAbsolutePath(folderPath, filePath); + Assert.Equal(expectedAbsolutePath, generatedPath); + } + + [SkippableTheory] + [InlineData(@"C:\\Volumes\Library\Sample\Music\Playlists\", @"..\Beethoven\Misc\Moonlight Sonata.mp3", @"C:\Volumes\Library\Sample\Music\Beethoven\Misc\Moonlight Sonata.mp3")] + [InlineData(@"C:\\Volumes\Library\Sample\Music\Playlists\", @"..\..\Beethoven\Misc\Moonlight Sonata.mp3", @"C:\Volumes\Library\Sample\Beethoven\Misc\Moonlight Sonata.mp3")] + [InlineData(@"C:\\Volumes\Library\Sample\Music\Playlists\", @"Beethoven\Misc\Moonlight Sonata.mp3", @"C:\Volumes\Library\Sample\Music\Playlists\Beethoven\Misc\Moonlight Sonata.mp3")] + [InlineData(@"C:\\Volumes\Library\Sample\Music\Playlists\", @"D:\\Beethoven\Misc\Moonlight Sonata.mp3", @"D:\\Beethoven\Misc\Moonlight Sonata.mp3")] + public void MakeAbsolutePathCorrectlyHandlesRelativeFilePathsOnWindows( + string folderPath, + string filePath, + string expectedAbsolutePath) + { + Skip.If(!OperatingSystem.IsWindows()); + var generatedPath = _sut.MakeAbsolutePath(folderPath, filePath); - if (OperatingSystem.IsWindows()) - { - var expectedWindowsPath = expectedAbsolutePath.Replace('/', '\\'); - Assert.Equal(expectedWindowsPath, generatedPath.Split(':')[1]); - } - else - { - Assert.Equal(expectedAbsolutePath, generatedPath); - } + Assert.Equal(expectedAbsolutePath, generatedPath); } [Theory] diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs index d1be07aa2..940e3c2b1 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs @@ -18,6 +18,12 @@ namespace Jellyfin.Server.Implementations.Tests.Library [InlineData("Superman: Red Son [tmdbid=618355][imdbid=tt10985510]", "imdbid", "tt10985510")] [InlineData("Superman: Red Son [tmdbid-618355][imdbid-tt10985510]", "imdbid", "tt10985510")] [InlineData("Superman: Red Son [tmdbid-618355][imdbid-tt10985510]", "tmdbid", "618355")] + [InlineData("Superman: Red Son [providera-id=1]", "providera-id", "1")] + [InlineData("Superman: Red Son [providerb-id=2]", "providerb-id", "2")] + [InlineData("Superman: Red Son [providera id=4]", "providera id", "4")] + [InlineData("Superman: Red Son [providerb id=5]", "providerb id", "5")] + [InlineData("Superman: Red Son [tmdbid=3]", "tmdbid", "3")] + [InlineData("Superman: Red Son [tvdbid-6]", "tvdbid", "6")] [InlineData("[tmdbid=618355]", "tmdbid", "618355")] [InlineData("[tmdbid-618355]", "tmdbid", "618355")] [InlineData("tmdbid=111111][tmdbid=618355]", "tmdbid", "618355")] diff --git a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs index 09e4709da..0a4a836cb 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Runtime.InteropServices; using System.Threading.Tasks; using Emby.Server.Implementations.Localization; using MediaBrowser.Controller.Configuration; @@ -127,6 +128,22 @@ namespace Jellyfin.Server.Implementations.Tests.Localization Assert.Equal(expectedLevel, level!); } + [Theory] + [InlineData("0", 0)] + [InlineData("1", 1)] + [InlineData("6", 6)] + [InlineData("12", 12)] + [InlineData("42", 42)] + [InlineData("9999", 9999)] + public async Task GetRatingLevel_GivenValidAge_Success(string value, int expectedLevel) + { + var localizationManager = Setup(new ServerConfiguration { MetadataCountryCode = "nl" }); + await localizationManager.LoadAll(); + var level = localizationManager.GetRatingLevel(value); + Assert.NotNull(level); + Assert.Equal(expectedLevel, level); + } + [Fact] public async Task GetRatingLevel_GivenUnratedString_Success() { @@ -142,6 +159,20 @@ namespace Jellyfin.Server.Implementations.Tests.Localization } [Theory] + [InlineData("-NO RATING SHOWN-")] + [InlineData(":NO RATING SHOWN:")] + public async Task GetRatingLevel_Split_Success(string value) + { + var localizationManager = Setup(new ServerConfiguration() + { + UICulture = "en-US" + }); + await localizationManager.LoadAll(); + + Assert.Null(localizationManager.GetRatingLevel(value)); + } + + [Theory] [InlineData("Default", "Default")] [InlineData("HeaderLiveTV", "Live TV")] public void GetLocalizedString_Valid_Success(string key, string expected) diff --git a/tests/Jellyfin.Server.Implementations.Tests/Sorting/PremiereDateComparerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Sorting/PremiereDateComparerTests.cs new file mode 100644 index 000000000..9dfacb2bf --- /dev/null +++ b/tests/Jellyfin.Server.Implementations.Tests/Sorting/PremiereDateComparerTests.cs @@ -0,0 +1,76 @@ +using System; +using Emby.Server.Implementations.Sorting; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Movies; +using Xunit; + +namespace Jellyfin.Server.Implementations.Tests.Sorting +{ + public class PremiereDateComparerTests + { + private readonly PremiereDateComparer _cmp = new PremiereDateComparer(); + + [Theory] + [ClassData(typeof(PremiereDateTestData))] + public void PremiereDateCompareTest(BaseItem x, BaseItem y, int expected) + { + Assert.Equal(expected, _cmp.Compare(x, y)); + Assert.Equal(-expected, _cmp.Compare(y, x)); + } + + private sealed class PremiereDateTestData : TheoryData<BaseItem, BaseItem, int> + { + public PremiereDateTestData() + { + // Happy case - Both have premier date + // Expected: x listed first + Add( + new Movie { PremiereDate = new DateTime(2018, 1, 1) }, + new Movie { PremiereDate = new DateTime(2018, 1, 3) }, + -1); + + // Both have premiere date, but y has invalid date + // Expected: y listed first + Add( + new Movie { PremiereDate = new DateTime(2019, 1, 1) }, + new Movie { PremiereDate = new DateTime(03, 1, 1) }, + 1); + + // Only x has premiere date, with earlier year than y + // Expected: x listed first + Add( + new Movie { PremiereDate = new DateTime(2020, 1, 1) }, + new Movie { ProductionYear = 2021 }, + -1); + + // Only x has premiere date, with same year as y + // Expected: y listed first + Add( + new Movie { PremiereDate = new DateTime(2022, 1, 2) }, + new Movie { ProductionYear = 2022 }, + 1); + + // Only x has a premiere date, with later year than y + // Expected: y listed first + Add( + new Movie { PremiereDate = new DateTime(2024, 3, 1) }, + new Movie { ProductionYear = 2023 }, + 1); + + // Only x has a premiere date, y has an invalid year + // Expected: y listed first + Add( + new Movie { PremiereDate = new DateTime(2025, 1, 1) }, + new Movie { ProductionYear = 0 }, + 1); + + // Only x has a premiere date, y has neither date nor year + // Expected: y listed first + Add( + new Movie { PremiereDate = new DateTime(2026, 1, 1) }, + new Movie(), + 1); + } + } + } +} diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/LibraryStructureControllerTests.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/LibraryStructureControllerTests.cs new file mode 100644 index 000000000..bf3bfdad4 --- /dev/null +++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/LibraryStructureControllerTests.cs @@ -0,0 +1,114 @@ +using System; +using System.Linq; +using System.Net; +using System.Net.Http.Json; +using System.Text.Json; +using System.Threading.Tasks; +using Jellyfin.Api.Models.LibraryStructureDto; +using Jellyfin.Extensions.Json; +using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Entities; +using Xunit; +using Xunit.Priority; + +namespace Jellyfin.Server.Integration.Tests.Controllers; + +[TestCaseOrderer(PriorityOrderer.Name, PriorityOrderer.Assembly)] +public sealed class LibraryStructureControllerTests : IClassFixture<JellyfinApplicationFactory> +{ + private readonly JellyfinApplicationFactory _factory; + private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options; + private static string? _accessToken; + + public LibraryStructureControllerTests(JellyfinApplicationFactory factory) + { + _factory = factory; + } + + [Fact] + [Priority(-1)] + public async Task Post_NewVirtualFolder_NotFound() + { + var client = _factory.CreateClient(); + client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client)); + + var body = new AddVirtualFolderDto() + { + LibraryOptions = new LibraryOptions() + { + Enabled = false + } + }; + + using var response = await client.PostAsJsonAsync("Library/VirtualFolders?name=test&refreshLibrary=true", body, _jsonOptions); + Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); + } + + [Fact] + [Priority(0)] + public async Task UpdateLibraryOptions_Invalid_NotFound() + { + var client = _factory.CreateClient(); + client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client)); + + var body = new UpdateLibraryOptionsDto() + { + Id = Guid.NewGuid(), + LibraryOptions = new LibraryOptions() + }; + + using var response = await client.PostAsJsonAsync("Library/VirtualFolders/LibraryOptions", body, _jsonOptions); + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Fact] + [Priority(0)] + public async Task UpdateLibraryOptions_Valid_Success() + { + var client = _factory.CreateClient(); + client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client)); + + using var response = await client.GetAsync("Library/VirtualFolders"); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + + var library = await response.Content.ReadFromJsonAsAsyncEnumerable<VirtualFolderInfo>(_jsonOptions) + .FirstOrDefaultAsync(x => string.Equals(x?.Name, "test", StringComparison.Ordinal)); + Assert.NotNull(library); + + var options = library.LibraryOptions; + Assert.NotNull(options); + Assert.False(options.Enabled); + options.Enabled = true; + + var body = new UpdateLibraryOptionsDto() + { + Id = Guid.Parse(library.ItemId), + LibraryOptions = options + }; + + using var response2 = await client.PostAsJsonAsync("Library/VirtualFolders/LibraryOptions", body, _jsonOptions); + Assert.Equal(HttpStatusCode.NoContent, response2.StatusCode); + } + + [Fact] + [Priority(1)] + public async Task DeleteLibrary_Invalid_NotFound() + { + var client = _factory.CreateClient(); + client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client)); + + using var response = await client.DeleteAsync("Library/VirtualFolders?name=doesntExist"); + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Fact] + [Priority(1)] + public async Task DeleteLibrary_Valid_Success() + { + var client = _factory.CreateClient(); + client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client)); + + using var response = await client.DeleteAsync("Library/VirtualFolders?name=test&refreshLibrary=true"); + Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); + } +} diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/SessionControllerTests.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/SessionControllerTests.cs index b9def13f8..ab68884f9 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Controllers/SessionControllerTests.cs +++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/SessionControllerTests.cs @@ -21,7 +21,7 @@ public class SessionControllerTests : IClassFixture<JellyfinApplicationFactory> var client = _factory.CreateClient(); client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client)); - using var response = await client.GetAsync($"Session/Sessions?userId={Guid.NewGuid()}"); + using var response = await client.GetAsync($"Sessions?controllableByUserId={Guid.NewGuid()}"); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } } diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Location/MovieNfoLocationTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Location/MovieNfoLocationTests.cs index 8019e0ab3..2f05c4ea2 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Location/MovieNfoLocationTests.cs +++ b/tests/Jellyfin.XbmcMetadata.Tests/Location/MovieNfoLocationTests.cs @@ -47,6 +47,7 @@ namespace Jellyfin.XbmcMetadata.Tests.Location var movie = new Movie() { Path = "/media/movies/Avengers Endgame", VideoType = VideoType.Dvd }; var path1 = "/media/movies/Avengers Endgame/Avengers Endgame.nfo"; var path2 = "/media/movies/Avengers Endgame/VIDEO_TS/VIDEO_TS.nfo"; + var path3 = "/media/movies/Avengers Endgame/movie.nfo"; // uses ContainingFolderPath which uses Operating system specific paths if (OperatingSystem.IsWindows()) @@ -54,12 +55,14 @@ namespace Jellyfin.XbmcMetadata.Tests.Location movie.Path = movie.Path.Replace('/', '\\'); path1 = path1.Replace('/', '\\'); path2 = path2.Replace('/', '\\'); + path3 = path3.Replace('/', '\\'); } var paths = MovieNfoSaver.GetMovieSavePaths(new ItemInfo(movie)).ToArray(); - Assert.Equal(2, paths.Length); + Assert.Equal(3, paths.Length); Assert.Contains(path1, paths); Assert.Contains(path2, paths); + Assert.Contains(path3, paths); } } } |
