From 307754a0e0a88ac331c1e8bb98e46ce51d004fd6 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Mon, 20 Apr 2020 15:39:55 -0400 Subject: [PATCH] Create a derived version of WebApplicationFactory<> that works with the Jellyfin server --- MediaBrowser.sln | 4 +- .../JellyfinApplicationFactory.cs | 124 ++++++++++++++++++ .../MediaBrowser.Api.Tests.csproj | 22 ++++ 3 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs create mode 100644 tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj diff --git a/MediaBrowser.sln b/MediaBrowser.sln index 1c84622ac0..daac9b0a2e 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -1,4 +1,4 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 +Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26730.3 MinimumVisualStudioVersion = 10.0.40219.1 @@ -62,6 +62,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Server.Implementat EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Controller.Tests", "tests\Jellyfin.Controller.Tests\Jellyfin.Controller.Tests.csproj", "{462584F7-5023-4019-9EAC-B98CA458C0A0}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Api.Tests", "tests\MediaBrowser.Api.Tests\MediaBrowser.Api.Tests.csproj", "{7C93C84F-105C-48E5-A878-406FA0A5B296}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs b/tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs new file mode 100644 index 0000000000..0bd9909f5b --- /dev/null +++ b/tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +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.Hosting; +using Microsoft.Extensions.Logging; +using Serilog; +using Serilog.Extensions.Logging; + +namespace MediaBrowser.Api.Tests +{ + /// + /// Factory for bootstrapping the Jellyfin application in memory for functional end to end tests. + /// + public class JellyfinApplicationFactory : WebApplicationFactory + { + private static readonly string _testPathRoot = Path.Combine(Path.GetTempPath(), "jellyfin-test-data"); + private static readonly ConcurrentBag _appHosts = new ConcurrentBag(); + + /// + /// Initializes a new instance of . + /// + public JellyfinApplicationFactory() + { + // Perform static initialization that only needs to happen once per test-run + Log.Logger = new LoggerConfiguration().WriteTo.Console().CreateLogger(); + Program.PerformStaticInitialization(); + } + + /// + protected override IWebHostBuilder CreateWebHostBuilder() + { + return new WebHostBuilder(); + } + + /// + protected override void ConfigureWebHost(IWebHostBuilder builder) + { + // Specify the startup command line options + var commandLineOpts = new StartupOptions + { + NoWebClient = true, + NoAutoRunWebApp = 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).Wait(); + + // Create a copy of the application configuration to use for startup + var startupConfig = Program.CreateAppConfiguration(commandLineOpts, appPaths); + + // Create the app host and initialize it + ILoggerFactory loggerFactory = new SerilogLoggerFactory(); + var appHost = new CoreAppHost( + appPaths, + loggerFactory, + commandLineOpts, + new ManagedFileSystem(loggerFactory.CreateLogger(), appPaths), + new SkiaEncoder(loggerFactory.CreateLogger(), appPaths), + new NetworkManager(loggerFactory.CreateLogger())); + _appHosts.Add(appHost); + var serviceCollection = new ServiceCollection(); + appHost.InitAsync(serviceCollection, startupConfig).Wait(); + + // Configure the web host builder + Program.ConfigureWebHostBuilder(builder, appHost, serviceCollection, commandLineOpts, startupConfig, appPaths); + } + + /// + 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(); + appHost.ServiceProvider = testServer.Services; + appHost.InitializeServices(); + appHost.FindParts(); + appHost.RunStartupTasksAsync().Wait(); + + return testServer; + } + + /// + protected override void Dispose(bool disposing) + { + foreach (var host in _appHosts) + { + host.Dispose(); + } + + _appHosts.Clear(); + + base.Dispose(disposing); + } + } +} diff --git a/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj b/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj new file mode 100644 index 0000000000..313cf9a3d7 --- /dev/null +++ b/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj @@ -0,0 +1,22 @@ + + + + netcoreapp3.1 + false + true + enable + + + + + + + + + + + + + + +