aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandlerTests.cs67
-rw-r--r--tests/Jellyfin.Controller.Tests/DirectoryServiceTests.cs68
-rw-r--r--tests/Jellyfin.Extensions.Tests/Json/Converters/JsonLowerCaseConverterTests.cs71
-rw-r--r--tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeExternalSourcesTests.cs4
-rw-r--r--tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs2
-rw-r--r--tests/Jellyfin.Model.Tests/Entities/ProviderIdsExtensionsTests.cs18
-rw-r--r--tests/Jellyfin.Naming.Tests/ExternalFiles/ExternalPathParserTests.cs1
-rw-r--r--tests/Jellyfin.Naming.Tests/TV/TvParserHelpersTest.cs31
-rw-r--r--tests/Jellyfin.Providers.Tests/Manager/ItemImageProviderTests.cs24
-rw-r--r--tests/Jellyfin.Providers.Tests/Manager/MetadataServiceTests.cs5
-rw-r--r--tests/Jellyfin.Providers.Tests/Manager/ProviderManagerTests.cs6
-rw-r--r--tests/Jellyfin.Providers.Tests/MediaInfo/AudioResolverTests.cs2
-rw-r--r--tests/Jellyfin.Providers.Tests/MediaInfo/EmbeddedImageProviderTests.cs6
-rw-r--r--tests/Jellyfin.Providers.Tests/MediaInfo/MediaInfoResolverTests.cs6
-rw-r--r--tests/Jellyfin.Providers.Tests/MediaInfo/SubtitleResolverTests.cs2
-rw-r--r--tests/Jellyfin.Providers.Tests/MediaInfo/VideoImageProviderTests.cs6
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs33
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs6
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs31
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Sorting/PremiereDateComparerTests.cs76
-rw-r--r--tests/Jellyfin.Server.Integration.Tests/Controllers/LibraryStructureControllerTests.cs114
-rw-r--r--tests/Jellyfin.Server.Integration.Tests/Controllers/SessionControllerTests.cs2
-rw-r--r--tests/Jellyfin.XbmcMetadata.Tests/Location/MovieNfoLocationTests.cs5
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);
}
}
}