diff --git a/tests/Jellyfin.Providers.Tests/Manager/ProviderManagerTests.cs b/tests/Jellyfin.Providers.Tests/Manager/ProviderManagerTests.cs index d59e4070f9..ba91f5ed2d 100644 --- a/tests/Jellyfin.Providers.Tests/Manager/ProviderManagerTests.cs +++ b/tests/Jellyfin.Providers.Tests/Manager/ProviderManagerTests.cs @@ -57,33 +57,10 @@ namespace Jellyfin.Providers.Tests.Manager providerList.Add(MockIImageProvider(nameProvider(i), item, order: order)); } - var libraryOptions = new LibraryOptions(); - if (libraryOrder != null) - { - libraryOptions.TypeOptions = new[] - { - new TypeOptions - { - Type = item.GetType().Name, - ImageFetcherOrder = libraryOrder.Select(nameProvider).ToArray() - } - }; - } + var libraryOptions = CreateLibraryOptions(item.GetType().Name, imageFetcherOrder: libraryOrder?.Select(nameProvider).ToArray()); + var serverConfiguration = CreateServerConfiguration(item.GetType().Name, imageFetcherOrder: serverOrder?.Select(nameProvider).ToArray()); - var serverConfiguration = new ServerConfiguration(); - if (serverOrder != null) - { - serverConfiguration.MetadataOptions = new[] - { - new MetadataOptions - { - ItemType = item.GetType().Name, - ImageFetcherOrder = serverOrder.Select(nameProvider).ToArray() - } - }; - } - - var providerManager = GetProviderManager(serverConfiguration: serverConfiguration, libraryOptions: libraryOptions); + using var providerManager = GetProviderManager(serverConfiguration: serverConfiguration, libraryOptions: libraryOptions); AddParts(providerManager, imageProviders: providerList); var refreshOptions = new ImageRefreshOptions(Mock.Of(MockBehavior.Strict)); @@ -119,12 +96,19 @@ namespace Jellyfin.Providers.Tests.Manager [InlineData(typeof(IDynamicImageProvider), true, true)] [InlineData(typeof(IRemoteImageProvider), false, false)] [InlineData(typeof(IDynamicImageProvider), false, false)] - public void GetImageProviders_CanRefreshImagesEnabled_WhenLocalOrEnabled(Type providerType, bool enabled, bool expected) + public void GetImageProviders_CanRefreshImagesBaseItemEnabled_WhenLocalOrEnabled(Type providerType, bool enabled, bool expected) { GetImageProviders_CanRefreshImages_Tester(providerType, true, expected, baseItemEnabled: enabled); } - private static void GetImageProviders_CanRefreshImages_Tester(Type providerType, bool supports, bool expected, bool errorOnSupported = false, bool itemLocked = false, bool fullRefresh = false, bool baseItemEnabled = true) + private static void GetImageProviders_CanRefreshImages_Tester( + Type providerType, + bool supports, + bool expected, + bool errorOnSupported = false, + bool itemLocked = false, + bool fullRefresh = false, + bool baseItemEnabled = true) { var item = new Movie { @@ -150,19 +134,12 @@ namespace Jellyfin.Providers.Tests.Manager baseItemManager.Setup(i => i.IsImageFetcherEnabled(item, It.IsAny(), providerName)) .Returns(baseItemEnabled); - var providerManager = GetProviderManager(baseItemManager: baseItemManager.Object); + using var providerManager = GetProviderManager(baseItemManager: baseItemManager.Object); AddParts(providerManager, imageProviders: new[] { provider }); - var actualProviders = providerManager.GetImageProviders(item, refreshOptions); + var actualProviders = providerManager.GetImageProviders(item, refreshOptions).ToArray(); - if (expected) - { - Assert.Single(actualProviders); - } - else - { - Assert.Empty(actualProviders); - } + Assert.Equal(expected ? 1 : 0, actualProviders.Length); } private static TheoryData GetMetadataProvidersOrderData() @@ -212,7 +189,14 @@ namespace Jellyfin.Providers.Tests.Manager [Theory] [MemberData(nameof(GetMetadataProvidersOrderData))] - public void GetMetadataProviders_ProviderOrder_MatchesExpected(string[] providers, int[]? libraryLocalOrder, int[]? libraryRemoteOrder, int[]? serverLocalOrder, int[]? serverRemoteOrder, int?[]? hasOrderOrder, int[] expectedOrder) + public void GetMetadataProviders_ProviderOrder_MatchesExpected( + string[] providers, + int[]? libraryLocalOrder, + int[]? libraryRemoteOrder, + int[]? serverLocalOrder, + int[]? serverRemoteOrder, + int?[]? hasOrderOrder, + int[] expectedOrder) { var item = new MetadataTestItem(); @@ -225,50 +209,20 @@ namespace Jellyfin.Providers.Tests.Manager providerList.Add(MockIMetadataProviderMapper(providers[i], nameProvider(i), order: order)); } - var libraryOptions = new LibraryOptions(); - if (libraryLocalOrder != null) - { - libraryOptions.LocalMetadataReaderOrder = libraryLocalOrder.Select(nameProvider).ToArray(); - } - - if (libraryRemoteOrder != null) - { - libraryOptions.TypeOptions = new[] - { - new TypeOptions - { - Type = item.GetType().Name, - MetadataFetcherOrder = libraryRemoteOrder.Select(nameProvider).ToArray() - } - }; - } - - var serverConfiguration = new ServerConfiguration(); - if (serverLocalOrder != null || serverRemoteOrder != null) - { - serverConfiguration.MetadataOptions = new[] - { - new MetadataOptions - { - ItemType = item.GetType().Name - } - }; - if (serverLocalOrder != null) - { - serverConfiguration.MetadataOptions[0].LocalMetadataReaderOrder = serverLocalOrder.Select(nameProvider).ToArray(); - } - - if (serverRemoteOrder != null) - { - serverConfiguration.MetadataOptions[0].MetadataFetcherOrder = serverRemoteOrder.Select(nameProvider).ToArray(); - } - } + var libraryOptions = CreateLibraryOptions( + item.GetType().Name, + localMetadataReaderOrder: libraryLocalOrder?.Select(nameProvider).ToArray(), + metadataFetcherOrder: libraryRemoteOrder?.Select(nameProvider).ToArray()); + var serverConfiguration = CreateServerConfiguration( + item.GetType().Name, + localMetadataReaderOrder: serverLocalOrder?.Select(nameProvider).ToArray(), + metadataFetcherOrder: serverRemoteOrder?.Select(nameProvider).ToArray()); var baseItemManager = new Mock(MockBehavior.Strict); baseItemManager.Setup(i => i.IsMetadataFetcherEnabled(item, It.IsAny(), It.IsAny())) .Returns(true); - var providerManager = GetProviderManager(serverConfiguration: serverConfiguration, baseItemManager: baseItemManager.Object); + using var providerManager = GetProviderManager(serverConfiguration: serverConfiguration, baseItemManager: baseItemManager.Object); AddParts(providerManager, metadataProviders: providerList); var actualProviders = providerManager.GetMetadataProviders(item, libraryOptions).ToList(); @@ -278,6 +232,87 @@ namespace Jellyfin.Providers.Tests.Manager Assert.Equal(expectedOrder, actualOrder); } + [Theory] + [InlineData(typeof(IMetadataProvider))] + [InlineData(typeof(ILocalMetadataProvider))] + [InlineData(typeof(IRemoteMetadataProvider))] + [InlineData(typeof(ICustomMetadataProvider))] + public void GetMetadataProviders_CanRefreshMetadataBasic_ReturnsTrue(Type providerType) + { + GetMetadataProviders_CanRefreshMetadata_Tester(providerType, true); + } + + [Theory] + [InlineData(typeof(ILocalMetadataProvider), false, true)] + [InlineData(typeof(IRemoteMetadataProvider), false, false)] + [InlineData(typeof(ICustomMetadataProvider), false, false)] + [InlineData(typeof(ILocalMetadataProvider), true, true)] + [InlineData(typeof(ICustomMetadataProvider), true, false)] + public void GetMetadataProviders_CanRefreshMetadataLocked_WhenLocalOrForced(Type providerType, bool forced, bool expected) + { + GetMetadataProviders_CanRefreshMetadata_Tester(providerType, expected, itemLocked: true, providerForced: forced); + } + + [Theory] + [InlineData(typeof(ILocalMetadataProvider), false, true)] + [InlineData(typeof(ICustomMetadataProvider), false, true)] + [InlineData(typeof(IRemoteMetadataProvider), false, false)] + [InlineData(typeof(IRemoteMetadataProvider), true, true)] + public void GetMetadataProviders_CanRefreshMetadataBaseItemEnabled_WhenEnabledOrNotRemote(Type providerType, bool baseItemEnabled, bool expected) + { + GetMetadataProviders_CanRefreshMetadata_Tester(providerType, expected, baseItemEnabled: baseItemEnabled); + } + + [Theory] + [InlineData(typeof(IRemoteMetadataProvider), false, true)] + [InlineData(typeof(ICustomMetadataProvider), false, true)] + [InlineData(typeof(ILocalMetadataProvider), false, false)] + [InlineData(typeof(ILocalMetadataProvider), true, true)] + public void GetMetadataProviders_CanRefreshMetadataSupportsLocal_WhenSupportsOrNotLocal(Type providerType, bool supportsLocalMetadata, bool expected) + { + GetMetadataProviders_CanRefreshMetadata_Tester(providerType, expected, supportsLocalMetadata: supportsLocalMetadata); + } + + [Theory] + [InlineData(typeof(ICustomMetadataProvider), true)] + [InlineData(typeof(IRemoteMetadataProvider), false)] + [InlineData(typeof(ILocalMetadataProvider), false)] + public void GetMetadataProviders_CanRefreshMetadataOwned_WhenNotLocal(Type providerType, bool expected) + { + GetMetadataProviders_CanRefreshMetadata_Tester(providerType, expected, ownedItem: true); + } + + private static void GetMetadataProviders_CanRefreshMetadata_Tester( + Type providerType, + bool expected, + bool itemLocked = false, + bool baseItemEnabled = true, + bool providerForced = false, + bool supportsLocalMetadata = true, + bool ownedItem = false) + { + var item = new MetadataTestItem + { + IsLocked = itemLocked, + OwnerId = ownedItem ? Guid.NewGuid() : Guid.Empty, + EnableLocalMetadata = supportsLocalMetadata + }; + + var providerName = "provider"; + var provider = MockIMetadataProviderMapper(providerType.Name, providerName, forced: providerForced); + + var baseItemManager = new Mock(MockBehavior.Strict); + baseItemManager.Setup(i => i.IsMetadataFetcherEnabled(item, It.IsAny(), providerName)) + .Returns(baseItemEnabled); + + using var providerManager = GetProviderManager(baseItemManager: baseItemManager.Object); + AddParts(providerManager, metadataProviders: new[] { provider }); + + var actualProviders = providerManager.GetMetadataProviders(item, new LibraryOptions()).ToArray(); + + Assert.Equal(expected ? 1 : 0, actualProviders.Length); + } + private static IImageProvider MockIImageProvider(string name, BaseItem expectedType, bool supports = true, int? order = null, bool errorOnSupported = false) where TProviderType : class, IImageProvider { @@ -297,7 +332,7 @@ namespace Jellyfin.Providers.Tests.Manager if (errorOnSupported) { provider.Setup(p => p.Supports(It.IsAny())) - .Throws(new ArgumentException()); + .Throws(new ArgumentException("Provider threw exception on Supports(item)")); } else { @@ -308,25 +343,31 @@ namespace Jellyfin.Providers.Tests.Manager return provider.Object; } - private static IMetadataProvider MockIMetadataProviderMapper(string typeName, string providerName, int? order = null) + private static IMetadataProvider MockIMetadataProviderMapper(string typeName, string providerName, int? order = null, bool forced = false) where TItemType : BaseItem, IHasLookupInfo where TLookupInfoType : ItemLookupInfo, new() => typeName switch { - "ILocalMetadataProvider" => MockIMetadataProvider, TItemType>(providerName, order), - "IRemoteMetadataProvider" => MockIMetadataProvider, TItemType>(providerName, order), - "ICustomMetadataProvider" => MockIMetadataProvider, TItemType>(providerName, order), - _ => MockIMetadataProvider, TItemType>(providerName, order) + "ILocalMetadataProvider" => MockIMetadataProvider, TItemType>(providerName, order, forced), + "IRemoteMetadataProvider" => MockIMetadataProvider, TItemType>(providerName, order, forced), + "ICustomMetadataProvider" => MockIMetadataProvider, TItemType>(providerName, order, forced), + _ => MockIMetadataProvider, TItemType>(providerName, order, forced) }; - private static IMetadataProvider MockIMetadataProvider(string name, int? order = null) + private static IMetadataProvider MockIMetadataProvider(string name, int? order = null, bool forced = false) where TProviderType : class, IMetadataProvider where TItemType : BaseItem { + Mock? forcedProvider = null; + if (forced) + { + forcedProvider = new Mock(); + } + Mock? hasOrder = null; if (order != null) { - hasOrder = new Mock(MockBehavior.Strict); + hasOrder = forcedProvider == null ? new Mock() : forcedProvider.As(); hasOrder.Setup(i => i.Order) .Returns((int)order); } @@ -340,7 +381,71 @@ namespace Jellyfin.Providers.Tests.Manager return provider.Object; } - private static ProviderManager GetProviderManager(ServerConfiguration? serverConfiguration = null, LibraryOptions? libraryOptions = null, IBaseItemManager? baseItemManager = null) + private static LibraryOptions CreateLibraryOptions( + string typeName, + string[]? imageFetcherOrder = null, + string[]? localMetadataReaderOrder = null, + string[]? metadataFetcherOrder = null) + { + var libraryOptions = new LibraryOptions + { + LocalMetadataReaderOrder = localMetadataReaderOrder + }; + + // only create type options if populating it with something + if (imageFetcherOrder != null || metadataFetcherOrder != null) + { + imageFetcherOrder ??= Array.Empty(); + metadataFetcherOrder ??= Array.Empty(); + + libraryOptions.TypeOptions = new[] + { + new TypeOptions + { + Type = typeName, + ImageFetcherOrder = imageFetcherOrder, + MetadataFetcherOrder = metadataFetcherOrder + } + }; + } + + return libraryOptions; + } + + private static ServerConfiguration CreateServerConfiguration( + string typeName, + string[]? imageFetcherOrder = null, + string[]? localMetadataReaderOrder = null, + string[]? metadataFetcherOrder = null) + { + var serverConfiguration = new ServerConfiguration(); + + // only create type options if populating it with something + if (imageFetcherOrder != null || localMetadataReaderOrder != null || metadataFetcherOrder != null) + { + imageFetcherOrder ??= Array.Empty(); + localMetadataReaderOrder ??= Array.Empty(); + metadataFetcherOrder ??= Array.Empty(); + + serverConfiguration.MetadataOptions = new[] + { + new MetadataOptions + { + ItemType = typeName, + ImageFetcherOrder = imageFetcherOrder, + LocalMetadataReaderOrder = localMetadataReaderOrder, + MetadataFetcherOrder = metadataFetcherOrder + } + }; + } + + return serverConfiguration; + } + + private static ProviderManager GetProviderManager( + ServerConfiguration? serverConfiguration = null, + LibraryOptions? libraryOptions = null, + IBaseItemManager? baseItemManager = null) { var serverConfigurationManager = new Mock(MockBehavior.Strict); serverConfigurationManager.Setup(i => i.Configuration) @@ -382,11 +487,13 @@ namespace Jellyfin.Providers.Tests.Manager } /// - /// Simple extension to force SupportsLocalMetadata to true. + /// Simple extension to make SupportsLocalMetadata directly settable. /// public class MetadataTestItem : BaseItem, IHasLookupInfo { - public override bool SupportsLocalMetadata => true; + public bool EnableLocalMetadata { get; set; } = true; + + public override bool SupportsLocalMetadata => EnableLocalMetadata; public MetadataTestItemInfo GetLookupInfo() {