111 lines
3.6 KiB
C#
111 lines
3.6 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.Hosting;
|
|
|
|
namespace Prefab.Tests.Infrastructure;
|
|
|
|
/// <summary>
|
|
/// Hosts a project entry point in-process with Kestrel so integration tests hit real HTTP.
|
|
/// </summary>
|
|
public sealed class KestrelInProcApp<TEntryPoint> : WebApplicationFactory<TEntryPoint>, IAsyncLifetime
|
|
where TEntryPoint : class
|
|
{
|
|
private readonly AspireInfraHost _infra;
|
|
private readonly ProjectResource _project;
|
|
private readonly int? _port;
|
|
private readonly IDictionary<string, string?> _overrides;
|
|
private readonly Action<IServiceCollection>? _configureServices;
|
|
private Dictionary<string, string?> _config = new(StringComparer.OrdinalIgnoreCase);
|
|
private Uri? _httpAddress;
|
|
|
|
public KestrelInProcApp(
|
|
AspireInfraHost infra,
|
|
ProjectResource project,
|
|
int? port,
|
|
IDictionary<string, string?>? overrides = null,
|
|
Action<IServiceCollection>? configureServices = null)
|
|
{
|
|
_infra = infra;
|
|
_project = project;
|
|
_port = port;
|
|
_overrides = overrides ?? new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase);
|
|
_configureServices = configureServices;
|
|
|
|
this.UseKestrel(_port ?? 0);
|
|
}
|
|
|
|
public Uri HttpAddress => _httpAddress ?? throw new InvalidOperationException("Application not started.");
|
|
public IServiceProvider AppServices => Services;
|
|
|
|
public async ValueTask InitializeAsync()
|
|
{
|
|
_config = await AspireInfraHost.ResolveConfigForProjectAsync(_project, _infra.Services);
|
|
|
|
foreach (var (key, value) in _overrides)
|
|
{
|
|
_config[key.Replace("__", ":")] = value;
|
|
}
|
|
|
|
using var client = CreateClient();
|
|
_httpAddress = client.BaseAddress ?? ResolveServerAddress();
|
|
}
|
|
|
|
protected override void ConfigureWebHost(IWebHostBuilder builder)
|
|
{
|
|
foreach (var (key, value) in _config)
|
|
{
|
|
if (string.Equals(key, "ASPNETCORE_URLS", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (value is null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
builder.UseSetting(key, value);
|
|
}
|
|
|
|
builder.UseSetting("ASPNETCORE_ENVIRONMENT", "Testing");
|
|
|
|
builder.ConfigureServices(services =>
|
|
{
|
|
RemoveHostedService(services, "Prefab.Web.Workers.DataSeeder");
|
|
RemoveHostedService(services, "Prefab.Catalog.Api.Workers.DataSeeder");
|
|
_configureServices?.Invoke(services);
|
|
});
|
|
}
|
|
|
|
public override ValueTask DisposeAsync() => base.DisposeAsync();
|
|
|
|
private Uri ResolveServerAddress()
|
|
{
|
|
var server = Services.GetRequiredService<IServer>();
|
|
var feature = server.Features.Get<IServerAddressesFeature>();
|
|
var address = feature?.Addresses.FirstOrDefault();
|
|
if (address is not null && Uri.TryCreate(address, UriKind.Absolute, out var uri))
|
|
{
|
|
return uri;
|
|
}
|
|
|
|
// Fallback to localhost with known (or dynamic) port.
|
|
var port = _port.GetValueOrDefault(0);
|
|
return new Uri($"http://127.0.0.1:{port}");
|
|
}
|
|
|
|
private static void RemoveHostedService(IServiceCollection services, string typeFullName)
|
|
{
|
|
var descriptors = services
|
|
.Where(s => s.ServiceType == typeof(IHostedService) && s.ImplementationType?.FullName == typeFullName)
|
|
.ToList();
|
|
|
|
foreach (var descriptor in descriptors)
|
|
{
|
|
services.Remove(descriptor);
|
|
}
|
|
}
|
|
}
|