app/BTCPayApp.Tests/BTCPayAppTestServer.cs
2024-09-09 12:36:11 +02:00

154 lines
5.0 KiB
C#

using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Playwright;
using NBitcoin;
using Xunit.Abstractions;
using Xunit.Sdk;
namespace BTCPayApp.Tests;
class BTCPayAppTestServer : BaseWebApplicationFactory<Program>
{
public BTCPayAppTestServer(ITestOutputHelper output, bool newDir = true, Dictionary<string, string>? config = null) : base(output, config)
{
if (newDir)
{
Config.AddOrReplace("BTCPAYAPP_DIRNAME", "btcpayserver-test-" + RandomUtils.GetUInt32());
}
}
}
class BaseWebApplicationFactory<T> : WebApplicationFactory<T> where T : class
{
protected IHost? Host;
protected readonly ITestOutputHelper Output;
protected readonly Dictionary<string, string> Config;
protected readonly Task PlaywrightInstallTask;
public string ServerAddress
{
get
{
if (Host is null)
{
CreateDefaultClient();
}
return ClientOptions.BaseAddress.ToString();
}
}
public BaseWebApplicationFactory(ITestOutputHelper output, Dictionary<string, string>? config = null)
{
Output = output;
Config = config ?? new();
PlaywrightInstallTask ??= Task.Run(InstallPlaywright);
}
public class LifetimeBridge
{
public LifetimeBridge(IHostApplicationLifetime lifetime, IServer server, TaskCompletionSource<string[]> tcs )
{
lifetime.ApplicationStarted.Register(() =>
{
tcs.SetResult(server.Features.Get<IServerAddressesFeature>()!.Addresses.ToArray());
});
}
}
protected override IHost CreateHost(IHostBuilder builder)
{
// Create the host that is actually used by the
// TestServer (In Memory).
var testHost = base.CreateHost(builder);
TaskCompletionSource<string[]> tcs = new TaskCompletionSource<string[]>();
builder.ConfigureWebHost(webHostBuilder => webHostBuilder.UseKestrel().UseUrls("https://127.0.0.1:0").ConfigureServices(collection => collection.AddSingleton<LifetimeBridge>(provider => new LifetimeBridge(provider.GetRequiredService<IHostApplicationLifetime>(), provider.GetRequiredService<IServer>(), tcs))));
// configure and start the actual host using Kestrel.
Host = builder.Build();
Host.Start();
Host.Services.GetRequiredService<LifetimeBridge>();
// Extract the selected dynamic port out of the Kestrel server
// and assign it onto the client options for convenience so it
// "just works" as otherwise it'll be the default http://localhost
// URL, which won't route to the Kestrel-hosted HTTP server.
var addresses = tcs.Task.GetAwaiter().GetResult();
ClientOptions.BaseAddress = new Uri(addresses.Last().Replace("127.0.0.1", "localhost"));
testHost.Start();
return testHost;
}
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
base.ConfigureWebHost(builder);
builder
.ConfigureAppConfiguration(configurationBuilder =>
configurationBuilder.AddInMemoryCollection(Config!))
.ConfigureLogging(
logging =>
{
// check if scopes are used in normal operation
var useScopes = logging.UsesScopes();
// remove other logging providers, such as remote loggers or unnecessary event logs
logging.ClearProviders();
logging.Services.AddSingleton<ILoggerProvider>(_ =>
new WebApplicationFactoryExtensions.XunitLoggerProvider(Output, useScopes));
});
}
private static void InstallPlaywright()
{
Microsoft.Playwright.Program.Main(new[] {"install", "--with-deps"});
}
public async Task<IBrowserContext> InitializeAsync()
{
Assert.NotNull(ServerAddress);
await PlaywrightInstallTask;
Playwright = await Microsoft.Playwright.Playwright.CreateAsync();
Browser = await Playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions
{
Headless = false,
});
return await Browser.NewContextAsync(new BrowserNewContextOptions
{
IgnoreHTTPSErrors = true,
IsMobile = true
});
}
public IPlaywright? Playwright { get; set; }
public IBrowser? Browser { get; set; }
public override async ValueTask DisposeAsync()
{
await base.DisposeAsync();
if (Playwright != null)
{
if (Browser != null)
{
await Browser.DisposeAsync();
}
Playwright.Dispose();
Playwright = null;
}
}
}