diff options
Diffstat (limited to 'tests/Jellyfin.Api.Tests')
14 files changed, 795 insertions, 137 deletions
diff --git a/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs index 3b3d03c8b..4ea5094b6 100644 --- a/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs +++ b/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs @@ -1,13 +1,13 @@ using System; using System.Linq; using System.Security.Claims; -using System.Text.Encodings.Web; using System.Threading.Tasks; using AutoFixture; using AutoFixture.AutoMoq; using Jellyfin.Api.Auth; using Jellyfin.Api.Constants; -using MediaBrowser.Controller.Entities; +using Jellyfin.Data.Entities; +using Jellyfin.Data.Enums; using MediaBrowser.Controller.Net; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; @@ -24,12 +24,6 @@ namespace Jellyfin.Api.Tests.Auth private readonly IFixture _fixture; private readonly Mock<IAuthService> _jellyfinAuthServiceMock; - private readonly Mock<IOptionsMonitor<AuthenticationSchemeOptions>> _optionsMonitorMock; - private readonly Mock<ISystemClock> _clockMock; - private readonly Mock<IServiceProvider> _serviceProviderMock; - private readonly Mock<IAuthenticationService> _authenticationServiceMock; - private readonly UrlEncoder _urlEncoder; - private readonly HttpContext _context; private readonly CustomAuthenticationHandler _sut; private readonly AuthenticationScheme _scheme; @@ -45,26 +39,23 @@ namespace Jellyfin.Api.Tests.Auth AllowFixtureCircularDependencies(); _jellyfinAuthServiceMock = _fixture.Freeze<Mock<IAuthService>>(); - _optionsMonitorMock = _fixture.Freeze<Mock<IOptionsMonitor<AuthenticationSchemeOptions>>>(); - _clockMock = _fixture.Freeze<Mock<ISystemClock>>(); - _serviceProviderMock = _fixture.Freeze<Mock<IServiceProvider>>(); - _authenticationServiceMock = _fixture.Freeze<Mock<IAuthenticationService>>(); + var optionsMonitorMock = _fixture.Freeze<Mock<IOptionsMonitor<AuthenticationSchemeOptions>>>(); + var serviceProviderMock = _fixture.Freeze<Mock<IServiceProvider>>(); + var authenticationServiceMock = _fixture.Freeze<Mock<IAuthenticationService>>(); _fixture.Register<ILoggerFactory>(() => new NullLoggerFactory()); - _urlEncoder = UrlEncoder.Default; + serviceProviderMock.Setup(s => s.GetService(typeof(IAuthenticationService))) + .Returns(authenticationServiceMock.Object); - _serviceProviderMock.Setup(s => s.GetService(typeof(IAuthenticationService))) - .Returns(_authenticationServiceMock.Object); - - _optionsMonitorMock.Setup(o => o.Get(It.IsAny<string>())) + optionsMonitorMock.Setup(o => o.Get(It.IsAny<string>())) .Returns(new AuthenticationSchemeOptions { ForwardAuthenticate = null }); - _context = new DefaultHttpContext + HttpContext context = new DefaultHttpContext { - RequestServices = _serviceProviderMock.Object + RequestServices = serviceProviderMock.Object }; _scheme = new AuthenticationScheme( @@ -73,22 +64,7 @@ namespace Jellyfin.Api.Tests.Auth typeof(CustomAuthenticationHandler)); _sut = _fixture.Create<CustomAuthenticationHandler>(); - _sut.InitializeAsync(_scheme, _context).Wait(); - } - - [Fact] - public async Task HandleAuthenticateAsyncShouldFailWithNullUser() - { - _jellyfinAuthServiceMock.Setup( - a => a.Authenticate( - It.IsAny<HttpRequest>(), - It.IsAny<AuthenticatedAttribute>())) - .Returns((User?)null); - - var authenticateResult = await _sut.AuthenticateAsync(); - - Assert.False(authenticateResult.Succeeded); - Assert.Equal("Invalid user", authenticateResult.Failure.Message); + _sut.InitializeAsync(_scheme, context).Wait(); } [Fact] @@ -98,8 +74,7 @@ namespace Jellyfin.Api.Tests.Auth _jellyfinAuthServiceMock.Setup( a => a.Authenticate( - It.IsAny<HttpRequest>(), - It.IsAny<AuthenticatedAttribute>())) + It.IsAny<HttpRequest>())) .Throws(new SecurityException(errorMessage)); var authenticateResult = await _sut.AuthenticateAsync(); @@ -121,10 +96,10 @@ namespace Jellyfin.Api.Tests.Auth [Fact] public async Task HandleAuthenticateAsyncShouldAssignNameClaim() { - var user = SetupUser(); + var authorizationInfo = SetupUser(); var authenticateResult = await _sut.AuthenticateAsync(); - Assert.True(authenticateResult.Principal.HasClaim(ClaimTypes.Name, user.Name)); + Assert.True(authenticateResult.Principal.HasClaim(ClaimTypes.Name, authorizationInfo.User.Username)); } [Theory] @@ -132,10 +107,10 @@ namespace Jellyfin.Api.Tests.Auth [InlineData(false)] public async Task HandleAuthenticateAsyncShouldAssignRoleClaim(bool isAdmin) { - var user = SetupUser(isAdmin); + var authorizationInfo = SetupUser(isAdmin); var authenticateResult = await _sut.AuthenticateAsync(); - var expectedRole = user.Policy.IsAdministrator ? UserRoles.Administrator : UserRoles.User; + var expectedRole = authorizationInfo.User.HasPermission(PermissionKind.IsAdministrator) ? UserRoles.Administrator : UserRoles.User; Assert.True(authenticateResult.Principal.HasClaim(ClaimTypes.Role, expectedRole)); } @@ -148,18 +123,18 @@ namespace Jellyfin.Api.Tests.Auth Assert.Equal(_scheme.Name, authenticatedResult.Ticket.AuthenticationScheme); } - private User SetupUser(bool isAdmin = false) + private AuthorizationInfo SetupUser(bool isAdmin = false) { - var user = _fixture.Create<User>(); - user.Policy.IsAdministrator = isAdmin; + var authorizationInfo = _fixture.Create<AuthorizationInfo>(); + authorizationInfo.User = _fixture.Create<User>(); + authorizationInfo.User.SetPermission(PermissionKind.IsAdministrator, isAdmin); _jellyfinAuthServiceMock.Setup( a => a.Authenticate( - It.IsAny<HttpRequest>(), - It.IsAny<AuthenticatedAttribute>())) - .Returns(user); + It.IsAny<HttpRequest>())) + .Returns(authorizationInfo); - return user; + return authorizationInfo; } private void AllowFixtureCircularDependencies() diff --git a/tests/Jellyfin.Api.Tests/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandlerTests.cs new file mode 100644 index 000000000..a62fd8d5a --- /dev/null +++ b/tests/Jellyfin.Api.Tests/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandlerTests.cs @@ -0,0 +1,53 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using AutoFixture; +using AutoFixture.AutoMoq; +using Jellyfin.Api.Auth.DefaultAuthorizationPolicy; +using Jellyfin.Api.Constants; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Controller.Library; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Moq; +using Xunit; + +namespace Jellyfin.Api.Tests.Auth.DefaultAuthorizationPolicy +{ + public class DefaultAuthorizationHandlerTests + { + private readonly Mock<IConfigurationManager> _configurationManagerMock; + private readonly List<IAuthorizationRequirement> _requirements; + private readonly DefaultAuthorizationHandler _sut; + private readonly Mock<IUserManager> _userManagerMock; + private readonly Mock<IHttpContextAccessor> _httpContextAccessor; + + public DefaultAuthorizationHandlerTests() + { + var fixture = new Fixture().Customize(new AutoMoqCustomization()); + _configurationManagerMock = fixture.Freeze<Mock<IConfigurationManager>>(); + _requirements = new List<IAuthorizationRequirement> { new DefaultAuthorizationRequirement() }; + _userManagerMock = fixture.Freeze<Mock<IUserManager>>(); + _httpContextAccessor = fixture.Freeze<Mock<IHttpContextAccessor>>(); + + _sut = fixture.Create<DefaultAuthorizationHandler>(); + } + + [Theory] + [InlineData(UserRoles.Administrator)] + [InlineData(UserRoles.Guest)] + [InlineData(UserRoles.User)] + public async Task ShouldSucceedOnUser(string userRole) + { + TestHelpers.SetupConfigurationManager(_configurationManagerMock, true); + var claims = TestHelpers.SetupUser( + _userManagerMock, + _httpContextAccessor, + userRole); + + var context = new AuthorizationHandlerContext(_requirements, claims, null); + + await _sut.HandleAsync(context); + Assert.True(context.HasSucceeded); + } + } +} diff --git a/tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandlerTests.cs index e40af703f..ee42216e4 100644 --- a/tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandlerTests.cs +++ b/tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandlerTests.cs @@ -1,13 +1,13 @@ using System.Collections.Generic; -using System.Security.Claims; using System.Threading.Tasks; using AutoFixture; using AutoFixture.AutoMoq; using Jellyfin.Api.Auth.FirstTimeSetupOrElevatedPolicy; using Jellyfin.Api.Constants; using MediaBrowser.Common.Configuration; -using MediaBrowser.Model.Configuration; +using MediaBrowser.Controller.Library; using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; using Moq; using Xunit; @@ -18,12 +18,16 @@ namespace Jellyfin.Api.Tests.Auth.FirstTimeSetupOrElevatedPolicy private readonly Mock<IConfigurationManager> _configurationManagerMock; private readonly List<IAuthorizationRequirement> _requirements; private readonly FirstTimeSetupOrElevatedHandler _sut; + private readonly Mock<IUserManager> _userManagerMock; + private readonly Mock<IHttpContextAccessor> _httpContextAccessor; public FirstTimeSetupOrElevatedHandlerTests() { var fixture = new Fixture().Customize(new AutoMoqCustomization()); _configurationManagerMock = fixture.Freeze<Mock<IConfigurationManager>>(); _requirements = new List<IAuthorizationRequirement> { new FirstTimeSetupOrElevatedRequirement() }; + _userManagerMock = fixture.Freeze<Mock<IUserManager>>(); + _httpContextAccessor = fixture.Freeze<Mock<IHttpContextAccessor>>(); _sut = fixture.Create<FirstTimeSetupOrElevatedHandler>(); } @@ -34,9 +38,13 @@ namespace Jellyfin.Api.Tests.Auth.FirstTimeSetupOrElevatedPolicy [InlineData(UserRoles.User)] public async Task ShouldSucceedIfStartupWizardIncomplete(string userRole) { - SetupConfigurationManager(false); - var user = SetupUser(userRole); - var context = new AuthorizationHandlerContext(_requirements, user, null); + TestHelpers.SetupConfigurationManager(_configurationManagerMock, false); + var claims = TestHelpers.SetupUser( + _userManagerMock, + _httpContextAccessor, + userRole); + + var context = new AuthorizationHandlerContext(_requirements, claims, null); await _sut.HandleAsync(context); Assert.True(context.HasSucceeded); @@ -48,30 +56,16 @@ namespace Jellyfin.Api.Tests.Auth.FirstTimeSetupOrElevatedPolicy [InlineData(UserRoles.User, false)] public async Task ShouldRequireAdministratorIfStartupWizardComplete(string userRole, bool shouldSucceed) { - SetupConfigurationManager(true); - var user = SetupUser(userRole); - var context = new AuthorizationHandlerContext(_requirements, user, null); + TestHelpers.SetupConfigurationManager(_configurationManagerMock, true); + var claims = TestHelpers.SetupUser( + _userManagerMock, + _httpContextAccessor, + userRole); + + var context = new AuthorizationHandlerContext(_requirements, claims, null); await _sut.HandleAsync(context); Assert.Equal(shouldSucceed, context.HasSucceeded); } - - private static ClaimsPrincipal SetupUser(string role) - { - var claims = new[] { new Claim(ClaimTypes.Role, role) }; - var identity = new ClaimsIdentity(claims); - return new ClaimsPrincipal(identity); - } - - private void SetupConfigurationManager(bool startupWizardCompleted) - { - var commonConfiguration = new BaseApplicationConfiguration - { - IsStartupWizardCompleted = startupWizardCompleted - }; - - _configurationManagerMock.Setup(c => c.CommonConfiguration) - .Returns(commonConfiguration); - } } } diff --git a/tests/Jellyfin.Api.Tests/Auth/IgnoreSchedulePolicy/IgnoreScheduleHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/IgnoreSchedulePolicy/IgnoreScheduleHandlerTests.cs new file mode 100644 index 000000000..7150c90bb --- /dev/null +++ b/tests/Jellyfin.Api.Tests/Auth/IgnoreSchedulePolicy/IgnoreScheduleHandlerTests.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using AutoFixture; +using AutoFixture.AutoMoq; +using Jellyfin.Api.Auth.IgnoreParentalControlPolicy; +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 Moq; +using Xunit; + +namespace Jellyfin.Api.Tests.Auth.IgnoreSchedulePolicy +{ + public class IgnoreScheduleHandlerTests + { + private readonly Mock<IConfigurationManager> _configurationManagerMock; + private readonly List<IAuthorizationRequirement> _requirements; + private readonly IgnoreParentalControlHandler _sut; + private readonly Mock<IUserManager> _userManagerMock; + private readonly Mock<IHttpContextAccessor> _httpContextAccessor; + + /// <summary> + /// Globally disallow access. + /// </summary> + private readonly AccessSchedule[] _accessSchedules = { new AccessSchedule(DynamicDayOfWeek.Everyday, 0, 0, Guid.Empty) }; + + public IgnoreScheduleHandlerTests() + { + var fixture = new Fixture().Customize(new AutoMoqCustomization()); + _configurationManagerMock = fixture.Freeze<Mock<IConfigurationManager>>(); + _requirements = new List<IAuthorizationRequirement> { new IgnoreParentalControlRequirement() }; + _userManagerMock = fixture.Freeze<Mock<IUserManager>>(); + _httpContextAccessor = fixture.Freeze<Mock<IHttpContextAccessor>>(); + + _sut = fixture.Create<IgnoreParentalControlHandler>(); + } + + [Theory] + [InlineData(UserRoles.Administrator, true)] + [InlineData(UserRoles.User, true)] + [InlineData(UserRoles.Guest, true)] + public async Task ShouldAllowScheduleCorrectly(string role, bool shouldSucceed) + { + TestHelpers.SetupConfigurationManager(_configurationManagerMock, true); + var claims = TestHelpers.SetupUser( + _userManagerMock, + _httpContextAccessor, + role, + _accessSchedules); + + var context = new AuthorizationHandlerContext(_requirements, claims, null); + + await _sut.HandleAsync(context); + Assert.Equal(shouldSucceed, context.HasSucceeded); + } + } +} diff --git a/tests/Jellyfin.Api.Tests/Auth/LocalAccessPolicy/LocalAccessHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/LocalAccessPolicy/LocalAccessHandlerTests.cs new file mode 100644 index 000000000..09ffa8468 --- /dev/null +++ b/tests/Jellyfin.Api.Tests/Auth/LocalAccessPolicy/LocalAccessHandlerTests.cs @@ -0,0 +1,58 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using AutoFixture; +using AutoFixture.AutoMoq; +using Jellyfin.Api.Auth.LocalAccessPolicy; +using Jellyfin.Api.Constants; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Library; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Moq; +using Xunit; + +namespace Jellyfin.Api.Tests.Auth.LocalAccessPolicy +{ + public class LocalAccessHandlerTests + { + private readonly Mock<IConfigurationManager> _configurationManagerMock; + private readonly List<IAuthorizationRequirement> _requirements; + private readonly LocalAccessHandler _sut; + private readonly Mock<IUserManager> _userManagerMock; + private readonly Mock<IHttpContextAccessor> _httpContextAccessor; + private readonly Mock<INetworkManager> _networkManagerMock; + + public LocalAccessHandlerTests() + { + var fixture = new Fixture().Customize(new AutoMoqCustomization()); + _configurationManagerMock = fixture.Freeze<Mock<IConfigurationManager>>(); + _requirements = new List<IAuthorizationRequirement> { new LocalAccessRequirement() }; + _userManagerMock = fixture.Freeze<Mock<IUserManager>>(); + _httpContextAccessor = fixture.Freeze<Mock<IHttpContextAccessor>>(); + _networkManagerMock = fixture.Freeze<Mock<INetworkManager>>(); + + _sut = fixture.Create<LocalAccessHandler>(); + } + + [Theory] + [InlineData(true, true)] + [InlineData(false, false)] + public async Task LocalAccessOnly(bool isInLocalNetwork, bool shouldSucceed) + { + _networkManagerMock + .Setup(n => n.IsInLocalNetwork(It.IsAny<string>())) + .Returns(isInLocalNetwork); + + TestHelpers.SetupConfigurationManager(_configurationManagerMock, true); + var claims = TestHelpers.SetupUser( + _userManagerMock, + _httpContextAccessor, + UserRoles.User); + + var context = new AuthorizationHandlerContext(_requirements, claims, null); + await _sut.HandleAsync(context); + Assert.Equal(shouldSucceed, context.HasSucceeded); + } + } +} diff --git a/tests/Jellyfin.Api.Tests/Auth/RequiresElevationPolicy/RequiresElevationHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/RequiresElevationPolicy/RequiresElevationHandlerTests.cs index cd05a8328..ffe88fcde 100644 --- a/tests/Jellyfin.Api.Tests/Auth/RequiresElevationPolicy/RequiresElevationHandlerTests.cs +++ b/tests/Jellyfin.Api.Tests/Auth/RequiresElevationPolicy/RequiresElevationHandlerTests.cs @@ -1,20 +1,35 @@ using System.Collections.Generic; -using System.Security.Claims; using System.Threading.Tasks; +using AutoFixture; +using AutoFixture.AutoMoq; using Jellyfin.Api.Auth.RequiresElevationPolicy; using Jellyfin.Api.Constants; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Controller.Library; using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Moq; using Xunit; namespace Jellyfin.Api.Tests.Auth.RequiresElevationPolicy { public class RequiresElevationHandlerTests { + private readonly Mock<IConfigurationManager> _configurationManagerMock; + private readonly List<IAuthorizationRequirement> _requirements; private readonly RequiresElevationHandler _sut; + private readonly Mock<IUserManager> _userManagerMock; + private readonly Mock<IHttpContextAccessor> _httpContextAccessor; public RequiresElevationHandlerTests() { - _sut = new RequiresElevationHandler(); + var fixture = new Fixture().Customize(new AutoMoqCustomization()); + _configurationManagerMock = fixture.Freeze<Mock<IConfigurationManager>>(); + _requirements = new List<IAuthorizationRequirement> { new RequiresElevationRequirement() }; + _userManagerMock = fixture.Freeze<Mock<IUserManager>>(); + _httpContextAccessor = fixture.Freeze<Mock<IHttpContextAccessor>>(); + + _sut = fixture.Create<RequiresElevationHandler>(); } [Theory] @@ -23,13 +38,13 @@ namespace Jellyfin.Api.Tests.Auth.RequiresElevationPolicy [InlineData(UserRoles.Guest, false)] public async Task ShouldHandleRolesCorrectly(string role, bool shouldSucceed) { - var requirements = new List<IAuthorizationRequirement> { new RequiresElevationRequirement() }; - - var claims = new[] { new Claim(ClaimTypes.Role, role) }; - var identity = new ClaimsIdentity(claims); - var user = new ClaimsPrincipal(identity); + TestHelpers.SetupConfigurationManager(_configurationManagerMock, true); + var claims = TestHelpers.SetupUser( + _userManagerMock, + _httpContextAccessor, + role); - var context = new AuthorizationHandlerContext(requirements, user, null); + var context = new AuthorizationHandlerContext(_requirements, claims, null); await _sut.HandleAsync(context); Assert.Equal(shouldSucceed, context.HasSucceeded); diff --git a/tests/Jellyfin.Api.Tests/BrandingServiceTests.cs b/tests/Jellyfin.Api.Tests/BrandingServiceTests.cs new file mode 100644 index 000000000..6fc287420 --- /dev/null +++ b/tests/Jellyfin.Api.Tests/BrandingServiceTests.cs @@ -0,0 +1,49 @@ +using System.Text.Json; +using System.Threading.Tasks; +using MediaBrowser.Model.Branding; +using Xunit; + +namespace Jellyfin.Api.Tests +{ + public sealed class BrandingServiceTests : IClassFixture<JellyfinApplicationFactory> + { + private readonly JellyfinApplicationFactory _factory; + + public BrandingServiceTests(JellyfinApplicationFactory factory) + { + _factory = factory; + } + + [Fact] + public async Task GetConfiguration_ReturnsCorrectResponse() + { + // Arrange + var client = _factory.CreateClient(); + + // Act + var response = await client.GetAsync("/Branding/Configuration"); + + // Assert + response.EnsureSuccessStatusCode(); + Assert.Equal("application/json; charset=utf-8", response.Content.Headers.ContentType.ToString()); + var responseBody = await response.Content.ReadAsStreamAsync(); + _ = await JsonSerializer.DeserializeAsync<BrandingOptions>(responseBody); + } + + [Theory] + [InlineData("/Branding/Css")] + [InlineData("/Branding/Css.css")] + public async Task GetCss_ReturnsCorrectResponse(string url) + { + // Arrange + var client = _factory.CreateClient(); + + // Act + var response = await client.GetAsync(url); + + // Assert + response.EnsureSuccessStatusCode(); + Assert.Equal("text/css; charset=utf-8", response.Content.Headers.ContentType.ToString()); + } + } +} diff --git a/tests/Jellyfin.Api.Tests/GetPathValueTests.cs b/tests/Jellyfin.Api.Tests/GetPathValueTests.cs deleted file mode 100644 index b01d1af1f..000000000 --- a/tests/Jellyfin.Api.Tests/GetPathValueTests.cs +++ /dev/null @@ -1,45 +0,0 @@ -using MediaBrowser.Api; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Configuration; -using MediaBrowser.Model.Services; -using Microsoft.Extensions.Logging.Abstractions; -using Moq; -using Xunit; - -namespace Jellyfin.Api.Tests -{ - public class GetPathValueTests - { - [Theory] - [InlineData("https://localhost:8096/ScheduledTasks/1234/Triggers", "", 1, "1234")] - [InlineData("https://localhost:8096/emby/ScheduledTasks/1234/Triggers", "", 1, "1234")] - [InlineData("https://localhost:8096/mediabrowser/ScheduledTasks/1234/Triggers", "", 1, "1234")] - [InlineData("https://localhost:8096/jellyfin/2/ScheduledTasks/1234/Triggers", "jellyfin/2", 1, "1234")] - [InlineData("https://localhost:8096/jellyfin/2/emby/ScheduledTasks/1234/Triggers", "jellyfin/2", 1, "1234")] - [InlineData("https://localhost:8096/jellyfin/2/mediabrowser/ScheduledTasks/1234/Triggers", "jellyfin/2", 1, "1234")] - [InlineData("https://localhost:8096/JELLYFIN/2/ScheduledTasks/1234/Triggers", "jellyfin/2", 1, "1234")] - [InlineData("https://localhost:8096/JELLYFIN/2/Emby/ScheduledTasks/1234/Triggers", "jellyfin/2", 1, "1234")] - [InlineData("https://localhost:8096/JELLYFIN/2/MediaBrowser/ScheduledTasks/1234/Triggers", "jellyfin/2", 1, "1234")] - public void GetPathValueTest(string path, string baseUrl, int index, string value) - { - var reqMock = Mock.Of<IRequest>(x => x.PathInfo == path); - var conf = new ServerConfiguration() - { - BaseUrl = baseUrl - }; - - var confManagerMock = Mock.Of<IServerConfigurationManager>(x => x.Configuration == conf); - - var service = new BrandingService( - new NullLogger<BrandingService>(), - confManagerMock, - Mock.Of<IHttpResultFactory>()) - { - Request = reqMock - }; - - Assert.Equal(value, service.GetPathValue(index).ToString()); - } - } -} diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index 9c4b7b0b0..aae436fb7 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -13,20 +13,32 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="AutoFixture" Version="4.11.0" /> - <PackageReference Include="AutoFixture.AutoMoq" Version="4.11.0" /> - <PackageReference Include="AutoFixture.Xunit2" Version="4.11.0" /> - <PackageReference Include="Microsoft.Extensions.Options" Version="3.1.4" /> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" /> + <PackageReference Include="AutoFixture" Version="4.13.0" /> + <PackageReference Include="AutoFixture.AutoMoq" Version="4.13.0" /> + <PackageReference Include="AutoFixture.Xunit2" Version="4.13.0" /> + <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.9" /> + <PackageReference Include="Microsoft.Extensions.Options" Version="3.1.9" /> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" /> <PackageReference Include="xunit" Version="2.4.1" /> - <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" /> - <PackageReference Include="coverlet.collector" Version="1.2.1" /> - <PackageReference Include="Moq" Version="4.13.1" /> + <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" /> + <PackageReference Include="coverlet.collector" Version="1.3.0" /> + <PackageReference Include="Moq" Version="4.14.6" /> + </ItemGroup> + + <!-- Code Analyzers --> + <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> + <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> </ItemGroup> <ItemGroup> - <ProjectReference Include="../../MediaBrowser.Api/MediaBrowser.Api.csproj" /> - <ProjectReference Include="../../Jellyfin.Api/Jellyfin.Api.csproj" /> + <ProjectReference Include="..\..\Jellyfin.Server\Jellyfin.Server.csproj" /> </ItemGroup> + <PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> + <CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet> + </PropertyGroup> + </Project> diff --git a/tests/Jellyfin.Api.Tests/JellyfinApplicationFactory.cs b/tests/Jellyfin.Api.Tests/JellyfinApplicationFactory.cs new file mode 100644 index 000000000..bd3d35687 --- /dev/null +++ b/tests/Jellyfin.Api.Tests/JellyfinApplicationFactory.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections.Concurrent; +using System.IO; +using Emby.Server.Implementations; +using Emby.Server.Implementations.IO; +using Emby.Server.Implementations.Networking; +using Jellyfin.Drawing.Skia; +using Jellyfin.Server; +using MediaBrowser.Common; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Serilog; +using Serilog.Extensions.Logging; + +namespace Jellyfin.Api.Tests +{ + /// <summary> + /// Factory for bootstrapping the Jellyfin application in memory for functional end to end tests. + /// </summary> + public class JellyfinApplicationFactory : WebApplicationFactory<Startup> + { + private static readonly string _testPathRoot = Path.Combine(Path.GetTempPath(), "jellyfin-test-data"); + private static readonly ConcurrentBag<IDisposable> _disposableComponents = new ConcurrentBag<IDisposable>(); + + /// <summary> + /// Initializes a new instance of the <see cref="JellyfinApplicationFactory"/> class. + /// </summary> + public JellyfinApplicationFactory() + { + // Perform static initialization that only needs to happen once per test-run + Log.Logger = new LoggerConfiguration().WriteTo.Console().CreateLogger(); + Program.PerformStaticInitialization(); + } + + /// <inheritdoc/> + protected override IWebHostBuilder CreateWebHostBuilder() + { + return new WebHostBuilder(); + } + + /// <inheritdoc/> + protected override void ConfigureWebHost(IWebHostBuilder builder) + { + // Specify the startup command line options + var commandLineOpts = new StartupOptions + { + NoWebClient = true + }; + + // Use a temporary directory for the application paths + var webHostPathRoot = Path.Combine(_testPathRoot, "test-host-" + Path.GetFileNameWithoutExtension(Path.GetRandomFileName())); + Directory.CreateDirectory(Path.Combine(webHostPathRoot, "logs")); + Directory.CreateDirectory(Path.Combine(webHostPathRoot, "config")); + Directory.CreateDirectory(Path.Combine(webHostPathRoot, "cache")); + Directory.CreateDirectory(Path.Combine(webHostPathRoot, "jellyfin-web")); + var appPaths = new ServerApplicationPaths( + webHostPathRoot, + Path.Combine(webHostPathRoot, "logs"), + Path.Combine(webHostPathRoot, "config"), + Path.Combine(webHostPathRoot, "cache"), + Path.Combine(webHostPathRoot, "jellyfin-web")); + + // Create the logging config file + // TODO: We shouldn't need to do this since we are only logging to console + Program.InitLoggingConfigFile(appPaths).GetAwaiter().GetResult(); + + // Create a copy of the application configuration to use for startup + var startupConfig = Program.CreateAppConfiguration(commandLineOpts, appPaths); + + ILoggerFactory loggerFactory = new SerilogLoggerFactory(); + var serviceCollection = new ServiceCollection(); + _disposableComponents.Add(loggerFactory); + + // Create the app host and initialize it + var appHost = new CoreAppHost( + appPaths, + loggerFactory, + commandLineOpts, + new ManagedFileSystem(loggerFactory.CreateLogger<ManagedFileSystem>(), appPaths), + new NetworkManager(loggerFactory.CreateLogger<NetworkManager>()), + serviceCollection); + _disposableComponents.Add(appHost); + appHost.Init(); + + // Configure the web host builder + Program.ConfigureWebHostBuilder(builder, appHost, serviceCollection, commandLineOpts, startupConfig, appPaths); + } + + /// <inheritdoc/> + protected override TestServer CreateServer(IWebHostBuilder builder) + { + // Create the test server using the base implementation + var testServer = base.CreateServer(builder); + + // Finish initializing the app host + var appHost = (CoreAppHost)testServer.Services.GetRequiredService<IApplicationHost>(); + appHost.ServiceProvider = testServer.Services; + appHost.InitializeServices().GetAwaiter().GetResult(); + appHost.RunStartupTasksAsync().GetAwaiter().GetResult(); + + return testServer; + } + + /// <inheritdoc/> + protected override void Dispose(bool disposing) + { + foreach (var disposable in _disposableComponents) + { + disposable.Dispose(); + } + + _disposableComponents.Clear(); + + base.Dispose(disposing); + } + } +} diff --git a/tests/Jellyfin.Api.Tests/ModelBinders/CommaDelimitedArrayModelBinderTests.cs b/tests/Jellyfin.Api.Tests/ModelBinders/CommaDelimitedArrayModelBinderTests.cs new file mode 100644 index 000000000..89c7d62f7 --- /dev/null +++ b/tests/Jellyfin.Api.Tests/ModelBinders/CommaDelimitedArrayModelBinderTests.cs @@ -0,0 +1,225 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Threading.Tasks; +using Jellyfin.Api.ModelBinders; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.Extensions.Primitives; +using Moq; +using Xunit; + +namespace Jellyfin.Api.Tests.ModelBinders +{ + public sealed class CommaDelimitedArrayModelBinderTests + { + [Fact] + public async Task BindModelAsync_CorrectlyBindsValidCommaDelimitedStringArrayQuery() + { + var queryParamName = "test"; + var queryParamValues = new[] { "lol", "xd" }; + var queryParamString = "lol,xd"; + var queryParamType = typeof(string[]); + + var modelBinder = new CommaDelimitedArrayModelBinder(); + var valueProvider = new QueryStringValueProvider( + new BindingSource(string.Empty, string.Empty, false, false), + new QueryCollection(new Dictionary<string, StringValues> { { queryParamName, new StringValues(queryParamString) } }), + CultureInfo.InvariantCulture); + var bindingContextMock = new Mock<ModelBindingContext>(); + bindingContextMock.Setup(b => b.ValueProvider).Returns(valueProvider); + bindingContextMock.Setup(b => b.ModelName).Returns(queryParamName); + bindingContextMock.Setup(b => b.ModelType).Returns(queryParamType); + bindingContextMock.SetupProperty(b => b.Result); + + await modelBinder.BindModelAsync(bindingContextMock.Object); + + Assert.True(bindingContextMock.Object.Result.IsModelSet); + Assert.Equal((string[])bindingContextMock.Object.Result.Model, queryParamValues); + } + + [Fact] + public async Task BindModelAsync_CorrectlyBindsValidCommaDelimitedIntArrayQuery() + { + var queryParamName = "test"; + var queryParamValues = new[] { 42, 0 }; + var queryParamString = "42,0"; + var queryParamType = typeof(int[]); + + var modelBinder = new CommaDelimitedArrayModelBinder(); + var valueProvider = new QueryStringValueProvider( + new BindingSource(string.Empty, string.Empty, false, false), + new QueryCollection(new Dictionary<string, StringValues> { { queryParamName, new StringValues(queryParamString) } }), + CultureInfo.InvariantCulture); + var bindingContextMock = new Mock<ModelBindingContext>(); + bindingContextMock.Setup(b => b.ValueProvider).Returns(valueProvider); + bindingContextMock.Setup(b => b.ModelName).Returns(queryParamName); + bindingContextMock.Setup(b => b.ModelType).Returns(queryParamType); + bindingContextMock.SetupProperty(b => b.Result); + + await modelBinder.BindModelAsync(bindingContextMock.Object); + + Assert.True(bindingContextMock.Object.Result.IsModelSet); + Assert.Equal((int[])bindingContextMock.Object.Result.Model, queryParamValues); + } + + [Fact] + public async Task BindModelAsync_CorrectlyBindsValidCommaDelimitedEnumArrayQuery() + { + var queryParamName = "test"; + var queryParamValues = new[] { TestType.How, TestType.Much }; + var queryParamString = "How,Much"; + var queryParamType = typeof(TestType[]); + + var modelBinder = new CommaDelimitedArrayModelBinder(); + var valueProvider = new QueryStringValueProvider( + new BindingSource(string.Empty, string.Empty, false, false), + new QueryCollection(new Dictionary<string, StringValues> { { queryParamName, new StringValues(queryParamString) } }), + CultureInfo.InvariantCulture); + var bindingContextMock = new Mock<ModelBindingContext>(); + bindingContextMock.Setup(b => b.ValueProvider).Returns(valueProvider); + bindingContextMock.Setup(b => b.ModelName).Returns(queryParamName); + bindingContextMock.Setup(b => b.ModelType).Returns(queryParamType); + bindingContextMock.SetupProperty(b => b.Result); + + await modelBinder.BindModelAsync(bindingContextMock.Object); + + Assert.True(bindingContextMock.Object.Result.IsModelSet); + Assert.Equal((TestType[])bindingContextMock.Object.Result.Model, queryParamValues); + } + + [Fact] + public async Task BindModelAsync_CorrectlyBindsValidCommaDelimitedEnumArrayQueryWithDoubleCommas() + { + var queryParamName = "test"; + var queryParamValues = new[] { TestType.How, TestType.Much }; + var queryParamString = "How,,Much"; + var queryParamType = typeof(TestType[]); + + var modelBinder = new CommaDelimitedArrayModelBinder(); + var valueProvider = new QueryStringValueProvider( + new BindingSource(string.Empty, string.Empty, false, false), + new QueryCollection(new Dictionary<string, StringValues> { { queryParamName, new StringValues(queryParamString) } }), + CultureInfo.InvariantCulture); + var bindingContextMock = new Mock<ModelBindingContext>(); + bindingContextMock.Setup(b => b.ValueProvider).Returns(valueProvider); + bindingContextMock.Setup(b => b.ModelName).Returns(queryParamName); + bindingContextMock.Setup(b => b.ModelType).Returns(queryParamType); + bindingContextMock.SetupProperty(b => b.Result); + + await modelBinder.BindModelAsync(bindingContextMock.Object); + + Assert.True(bindingContextMock.Object.Result.IsModelSet); + Assert.Equal((TestType[])bindingContextMock.Object.Result.Model, queryParamValues); + } + + [Fact] + public async Task BindModelAsync_CorrectlyBindsValidEnumArrayQuery() + { + var queryParamName = "test"; + var queryParamValues = new[] { TestType.How, TestType.Much }; + var queryParamString1 = "How"; + var queryParamString2 = "Much"; + var queryParamType = typeof(TestType[]); + + var modelBinder = new CommaDelimitedArrayModelBinder(); + + var valueProvider = new QueryStringValueProvider( + new BindingSource(string.Empty, string.Empty, false, false), + new QueryCollection(new Dictionary<string, StringValues> + { + { queryParamName, new StringValues(new[] { queryParamString1, queryParamString2 }) }, + }), + CultureInfo.InvariantCulture); + var bindingContextMock = new Mock<ModelBindingContext>(); + bindingContextMock.Setup(b => b.ValueProvider).Returns(valueProvider); + bindingContextMock.Setup(b => b.ModelName).Returns(queryParamName); + bindingContextMock.Setup(b => b.ModelType).Returns(queryParamType); + bindingContextMock.SetupProperty(b => b.Result); + + await modelBinder.BindModelAsync(bindingContextMock.Object); + + Assert.True(bindingContextMock.Object.Result.IsModelSet); + Assert.Equal((TestType[])bindingContextMock.Object.Result.Model, queryParamValues); + } + + [Fact] + public async Task BindModelAsync_CorrectlyBindsEmptyEnumArrayQuery() + { + var queryParamName = "test"; + var queryParamValues = Array.Empty<TestType>(); + var queryParamType = typeof(TestType[]); + + var modelBinder = new CommaDelimitedArrayModelBinder(); + + var valueProvider = new QueryStringValueProvider( + new BindingSource(string.Empty, string.Empty, false, false), + new QueryCollection(new Dictionary<string, StringValues> + { + { queryParamName, new StringValues(value: null) }, + }), + CultureInfo.InvariantCulture); + var bindingContextMock = new Mock<ModelBindingContext>(); + bindingContextMock.Setup(b => b.ValueProvider).Returns(valueProvider); + bindingContextMock.Setup(b => b.ModelName).Returns(queryParamName); + bindingContextMock.Setup(b => b.ModelType).Returns(queryParamType); + bindingContextMock.SetupProperty(b => b.Result); + + await modelBinder.BindModelAsync(bindingContextMock.Object); + + Assert.True(bindingContextMock.Object.Result.IsModelSet); + Assert.Equal((TestType[])bindingContextMock.Object.Result.Model, queryParamValues); + } + + [Fact] + public async Task BindModelAsync_ThrowsIfCommaDelimitedEnumArrayQueryIsInvalid() + { + var queryParamName = "test"; + var queryParamString = "🔥,😢"; + var queryParamType = typeof(TestType[]); + + var modelBinder = new CommaDelimitedArrayModelBinder(); + var valueProvider = new QueryStringValueProvider( + new BindingSource(string.Empty, string.Empty, false, false), + new QueryCollection(new Dictionary<string, StringValues> { { queryParamName, new StringValues(queryParamString) } }), + CultureInfo.InvariantCulture); + var bindingContextMock = new Mock<ModelBindingContext>(); + bindingContextMock.Setup(b => b.ValueProvider).Returns(valueProvider); + bindingContextMock.Setup(b => b.ModelName).Returns(queryParamName); + bindingContextMock.Setup(b => b.ModelType).Returns(queryParamType); + bindingContextMock.SetupProperty(b => b.Result); + + Func<Task> act = async () => await modelBinder.BindModelAsync(bindingContextMock.Object); + + await Assert.ThrowsAsync<FormatException>(act); + } + + [Fact] + public async Task BindModelAsync_ThrowsIfCommaDelimitedEnumArrayQueryIsInvalid2() + { + var queryParamName = "test"; + var queryParamString1 = "How"; + var queryParamString2 = "😱"; + var queryParamType = typeof(TestType[]); + + var modelBinder = new CommaDelimitedArrayModelBinder(); + + var valueProvider = new QueryStringValueProvider( + new BindingSource(string.Empty, string.Empty, false, false), + new QueryCollection(new Dictionary<string, StringValues> + { + { queryParamName, new StringValues(new[] { queryParamString1, queryParamString2 }) }, + }), + CultureInfo.InvariantCulture); + var bindingContextMock = new Mock<ModelBindingContext>(); + bindingContextMock.Setup(b => b.ValueProvider).Returns(valueProvider); + bindingContextMock.Setup(b => b.ModelName).Returns(queryParamName); + bindingContextMock.Setup(b => b.ModelType).Returns(queryParamType); + bindingContextMock.SetupProperty(b => b.Result); + + Func<Task> act = async () => await modelBinder.BindModelAsync(bindingContextMock.Object); + + await Assert.ThrowsAsync<FormatException>(act); + } + } +} diff --git a/tests/Jellyfin.Api.Tests/ModelBinders/TestType.cs b/tests/Jellyfin.Api.Tests/ModelBinders/TestType.cs new file mode 100644 index 000000000..544a74637 --- /dev/null +++ b/tests/Jellyfin.Api.Tests/ModelBinders/TestType.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Jellyfin.Api.Tests.ModelBinders +{ + public enum TestType + { +#pragma warning disable SA1602 // Enumeration items should be documented + How, + Much, + Is, + The, + Fish +#pragma warning restore SA1602 // Enumeration items should be documented + } +} diff --git a/tests/Jellyfin.Api.Tests/OpenApiSpecTests.cs b/tests/Jellyfin.Api.Tests/OpenApiSpecTests.cs new file mode 100644 index 000000000..3a85b5514 --- /dev/null +++ b/tests/Jellyfin.Api.Tests/OpenApiSpecTests.cs @@ -0,0 +1,42 @@ +using System.IO; +using System.Reflection; +using System.Text.Json; +using System.Threading.Tasks; +using MediaBrowser.Model.Branding; +using Xunit; +using Xunit.Abstractions; + +namespace Jellyfin.Api.Tests +{ + public sealed class OpenApiSpecTests : IClassFixture<JellyfinApplicationFactory> + { + private readonly JellyfinApplicationFactory _factory; + private readonly ITestOutputHelper _outputHelper; + + public OpenApiSpecTests(JellyfinApplicationFactory factory, ITestOutputHelper outputHelper) + { + _factory = factory; + _outputHelper = outputHelper; + } + + [Fact] + public async Task GetSpec_ReturnsCorrectResponse() + { + // Arrange + var client = _factory.CreateClient(); + + // Act + var response = await client.GetAsync("/api-docs/openapi.json"); + + // Assert + response.EnsureSuccessStatusCode(); + Assert.Equal("application/json; charset=utf-8", response.Content.Headers.ContentType.ToString()); + + // Write out for publishing + var responseBody = await response.Content.ReadAsStringAsync(); + string outputPath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? ".", "openapi.json")); + _outputHelper.WriteLine("Writing OpenAPI Spec JSON to '{0}'.", outputPath); + File.WriteAllText(outputPath, responseBody); + } + } +} diff --git a/tests/Jellyfin.Api.Tests/TestHelpers.cs b/tests/Jellyfin.Api.Tests/TestHelpers.cs new file mode 100644 index 000000000..a4dd4e409 --- /dev/null +++ b/tests/Jellyfin.Api.Tests/TestHelpers.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Net; +using System.Security.Claims; +using Jellyfin.Api.Constants; +using Jellyfin.Data.Entities; +using Jellyfin.Data.Enums; +using Jellyfin.Server.Implementations.Users; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Configuration; +using Microsoft.AspNetCore.Http; +using Moq; +using AccessSchedule = Jellyfin.Data.Entities.AccessSchedule; + +namespace Jellyfin.Api.Tests +{ + public static class TestHelpers + { + public static ClaimsPrincipal SetupUser( + Mock<IUserManager> userManagerMock, + Mock<IHttpContextAccessor> httpContextAccessorMock, + string role, + IEnumerable<AccessSchedule>? accessSchedules = null) + { + var user = new User( + "jellyfin", + typeof(DefaultAuthenticationProvider).FullName, + typeof(DefaultPasswordResetProvider).FullName); + + // Set administrator flag. + user.SetPermission(PermissionKind.IsAdministrator, role.Equals(UserRoles.Administrator, StringComparison.OrdinalIgnoreCase)); + + // Add access schedules if set. + if (accessSchedules != null) + { + foreach (var accessSchedule in accessSchedules) + { + user.AccessSchedules.Add(accessSchedule); + } + } + + var claims = new[] + { + new Claim(ClaimTypes.Role, role), + new Claim(ClaimTypes.Name, "jellyfin"), + new Claim(InternalClaimTypes.UserId, Guid.Empty.ToString("N", CultureInfo.InvariantCulture)), + new Claim(InternalClaimTypes.DeviceId, Guid.Empty.ToString("N", CultureInfo.InvariantCulture)), + new Claim(InternalClaimTypes.Device, "test"), + new Claim(InternalClaimTypes.Client, "test"), + new Claim(InternalClaimTypes.Version, "test"), + new Claim(InternalClaimTypes.Token, "test"), + }; + + var identity = new ClaimsIdentity(claims); + + userManagerMock + .Setup(u => u.GetUserById(It.IsAny<Guid>())) + .Returns(user); + + httpContextAccessorMock + .Setup(h => h.HttpContext.Connection.RemoteIpAddress) + .Returns(new IPAddress(0)); + + return new ClaimsPrincipal(identity); + } + + public static void SetupConfigurationManager(in Mock<IConfigurationManager> configurationManagerMock, bool startupWizardCompleted) + { + var commonConfiguration = new BaseApplicationConfiguration + { + IsStartupWizardCompleted = startupWizardCompleted + }; + + configurationManagerMock + .Setup(c => c.CommonConfiguration) + .Returns(commonConfiguration); + } + } +} |
