aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandlerTests.cs58
-rw-r--r--tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeExternalSourcesTests.cs4
-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/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.Integration.Tests/Controllers/LibraryStructureControllerTests.cs114
-rw-r--r--tests/Jellyfin.XbmcMetadata.Tests/Location/MovieNfoLocationTests.cs5
11 files changed, 188 insertions, 44 deletions
diff --git a/tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandlerTests.cs
index 2e6ffb5f6..31d2b486b 100644
--- a/tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandlerTests.cs
+++ b/tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandlerTests.cs
@@ -1,14 +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;
@@ -18,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;
@@ -31,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]
@@ -45,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]
@@ -63,17 +84,16 @@ 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.Equal(shouldSucceed, context.HasSucceeded);
+ Assert.Equal(shouldSucceed, allowed.Succeeded);
}
[Theory]
[InlineData(UserRoles.Administrator, true)]
[InlineData(UserRoles.Guest, false)]
[InlineData(UserRoles.User, true)]
- public async Task ShouldRequireUserIfNotRequiresAdmin(string userRole, bool shouldSucceed)
+ public async Task ShouldRequireUserIfNotAdministrator(string userRole, bool shouldSucceed)
{
TestHelpers.SetupConfigurationManager(_configurationManagerMock, true);
var claims = TestHelpers.SetupUser(
@@ -81,24 +101,26 @@ namespace Jellyfin.Api.Tests.Auth.FirstTimeSetupPolicy
_httpContextAccessor,
userRole);
- var context = new AuthorizationHandlerContext(
- new List<IAuthorizationRequirement> { new FirstTimeSetupRequirement(false, false) },
- claims,
- null);
+ var allowed = await _authorizationService.AuthorizeAsync(claims, "FirstTimeNoAdmin");
- await _firstTimeSetupHandler.HandleAsync(context);
- Assert.Equal(shouldSucceed, context.HasSucceeded);
+ Assert.Equal(shouldSucceed, allowed.Succeeded);
}
[Fact]
- public async Task ShouldAllowAdminApiKeyIfStartupWizardComplete()
+ public async Task ShouldDisallowUserIfOutsideSchedule()
{
+ AccessSchedule[] accessSchedules = { new AccessSchedule(DynamicDayOfWeek.Everyday, 0, 0, Guid.Empty) };
+
TestHelpers.SetupConfigurationManager(_configurationManagerMock, true);
- var claims = new ClaimsPrincipal(new ClaimsIdentity([new Claim(ClaimTypes.Role, UserRoles.Administrator)]));
- var context = new AuthorizationHandlerContext(_requirements, claims, null);
+ var claims = TestHelpers.SetupUser(
+ _userManagerMock,
+ _httpContextAccessor,
+ UserRoles.User,
+ accessSchedules);
+
+ var allowed = await _authorizationService.AuthorizeAsync(claims, "FirstTimeSchedule");
- await _firstTimeSetupHandler.HandleAsync(context);
- Assert.True(context.HasSucceeded);
+ Assert.False(allowed.Succeeded);
}
}
}
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.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/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.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.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);
}
}
}