Init
This commit is contained in:
206
Prefab.Tests/Infrastructure/PrefabCompositeFixture.cs
Normal file
206
Prefab.Tests/Infrastructure/PrefabCompositeFixture.cs
Normal file
@@ -0,0 +1,206 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using Prefab.Handler;
|
||||
using Prefab.Web.Data;
|
||||
|
||||
namespace Prefab.Tests.Infrastructure;
|
||||
|
||||
/// <summary>
|
||||
/// Spins up Aspire infrastructure plus the Prefab web and catalog apps in-process.
|
||||
/// </summary>
|
||||
public class PrefabCompositeFixture : IAsyncLifetime
|
||||
{
|
||||
public const int DebugPrefabPort = 5173;
|
||||
public const int DebugCatalogPort = 5174;
|
||||
|
||||
private readonly PrefabHarnessOptions _options;
|
||||
private readonly TestAuthenticationStateProvider _authenticationProvider = new();
|
||||
|
||||
protected PrefabCompositeFixture(PrefabHarnessOptions? options = null)
|
||||
{
|
||||
_options = options ?? ResolveDefaultOptions();
|
||||
Infra = new AspireInfraHost(_options, "prefab-db");
|
||||
}
|
||||
|
||||
public AspireInfraHost Infra { get; }
|
||||
public KestrelInProcApp<Prefab.Web.Module> PrefabWeb { get; private set; } = default!;
|
||||
public KestrelInProcApp<Prefab.Catalog.Api.Module>? CatalogApi { get; private set; }
|
||||
public TestAuthenticationStateProvider AuthenticationStateProvider => _authenticationProvider;
|
||||
|
||||
public Uri PrefabBaseAddress => PrefabWeb.HttpAddress;
|
||||
public Uri CatalogBaseAddress => CatalogApi?.HttpAddress ?? throw new InvalidOperationException("Catalog API has not been started.");
|
||||
|
||||
protected virtual bool ShouldStartCatalog => true;
|
||||
|
||||
public async ValueTask InitializeAsync()
|
||||
{
|
||||
await Infra.InitializeAsync();
|
||||
await EnsureDatabaseInitialized(CancellationToken.None);
|
||||
|
||||
var prefabProject = Infra.GetProject("prefab-web");
|
||||
var useFixedPorts = _options.Mode == RunMode.DebugPersistent && _options.DisablePortRandomization;
|
||||
|
||||
Uri? catalogAddress = null;
|
||||
if (ShouldStartCatalog)
|
||||
{
|
||||
var catalogProject = Infra.GetProject("prefab-catalog");
|
||||
var catalogPort = useFixedPorts ? DebugCatalogPort : (int?)null;
|
||||
CatalogApi = new KestrelInProcApp<Prefab.Catalog.Api.Module>(
|
||||
Infra,
|
||||
catalogProject,
|
||||
catalogPort,
|
||||
overrides: null,
|
||||
configureServices: ConfigureTestAuthentication);
|
||||
|
||||
await CatalogApi.InitializeAsync();
|
||||
catalogAddress = CatalogApi.HttpAddress;
|
||||
}
|
||||
else
|
||||
{
|
||||
CatalogApi = null;
|
||||
}
|
||||
|
||||
var overrides = GetWebOverrides(catalogAddress);
|
||||
|
||||
var prefabPort = useFixedPorts ? DebugPrefabPort : (int?)null;
|
||||
PrefabWeb = new KestrelInProcApp<Prefab.Web.Module>(
|
||||
Infra,
|
||||
prefabProject,
|
||||
prefabPort,
|
||||
overrides,
|
||||
ConfigureTestAuthentication);
|
||||
|
||||
await PrefabWeb.InitializeAsync();
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
await Infra.DisposeAsync();
|
||||
}
|
||||
|
||||
public HttpClient CreateHttpClientForWeb() => PrefabWeb.CreateClient();
|
||||
|
||||
public IServiceScope CreateWebScope() => PrefabWeb.AppServices.CreateScope();
|
||||
|
||||
public async Task<string> GetConnectionStringAsync(string resourceName, CancellationToken cancellationToken = default) =>
|
||||
await Infra.GetConnectionStringAsync(resourceName, cancellationToken);
|
||||
|
||||
public async Task SeedAsync(Func<AppDb, CancellationToken, Task> seed, CancellationToken cancellationToken = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(seed);
|
||||
|
||||
var connectionString = await GetConnectionStringAsync("prefab-db", cancellationToken);
|
||||
var optionsBuilder = new DbContextOptionsBuilder<AppDb>().UseSqlServer(connectionString);
|
||||
|
||||
await using var db = new AppDb(optionsBuilder.Options, new HandlerContextAccessor());
|
||||
await db.Database.MigrateAsync(cancellationToken);
|
||||
await seed(db, cancellationToken);
|
||||
}
|
||||
|
||||
protected virtual async ValueTask EnsureDatabaseInitialized(CancellationToken cancellationToken)
|
||||
{
|
||||
var connectionString = await GetConnectionStringAsync("prefab-db", cancellationToken);
|
||||
var optionsBuilder = new DbContextOptionsBuilder<AppDb>().UseSqlServer(connectionString);
|
||||
|
||||
await using var db = new AppDb(optionsBuilder.Options, new HandlerContextAccessor());
|
||||
await db.Database.MigrateAsync(cancellationToken);
|
||||
}
|
||||
|
||||
protected virtual IDictionary<string, string?> GetWebOverrides(Uri? catalogAddress) =>
|
||||
new Dictionary<string, string?>
|
||||
{
|
||||
["Prefab__Catalog__Client__Transport"] = "Http",
|
||||
["Prefab__Catalog__Client__BaseAddress"] = catalogAddress?.ToString().TrimEnd('/') ?? string.Empty
|
||||
};
|
||||
|
||||
private void ConfigureTestAuthentication(IServiceCollection services)
|
||||
{
|
||||
var descriptors = services.Where(d => d.ServiceType == typeof(AuthenticationStateProvider)).ToList();
|
||||
foreach (var descriptor in descriptors)
|
||||
{
|
||||
services.Remove(descriptor);
|
||||
}
|
||||
|
||||
services.AddSingleton(_authenticationProvider);
|
||||
services.AddSingleton<AuthenticationStateProvider>(_ => _authenticationProvider);
|
||||
}
|
||||
|
||||
private static PrefabHarnessOptions ResolveDefaultOptions()
|
||||
{
|
||||
var modeEnv = Environment.GetEnvironmentVariable("PREFAB_TEST_MODE")?.Trim().ToLowerInvariant();
|
||||
var mode = modeEnv switch
|
||||
{
|
||||
"debug" => RunMode.DebugPersistent,
|
||||
"ephemeral" => RunMode.EphemeralIsolated,
|
||||
_ => RunMode.DebugPersistent
|
||||
};
|
||||
|
||||
return mode switch
|
||||
{
|
||||
RunMode.EphemeralIsolated => new PrefabHarnessOptions(
|
||||
RunMode.EphemeralIsolated,
|
||||
EnableAspireDashboard: false,
|
||||
DisablePortRandomization: false),
|
||||
_ => new PrefabHarnessOptions(
|
||||
RunMode.DebugPersistent,
|
||||
EnableAspireDashboard: true,
|
||||
DisablePortRandomization: true)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class PrefabCompositeFixture_Debug : PrefabCompositeFixture
|
||||
{
|
||||
public PrefabCompositeFixture_Debug()
|
||||
: base(new PrefabHarnessOptions(
|
||||
RunMode.DebugPersistent,
|
||||
EnableAspireDashboard: true,
|
||||
DisablePortRandomization: true))
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class PrefabCompositeFixture_Ephemeral : PrefabCompositeFixture
|
||||
{
|
||||
public PrefabCompositeFixture_Ephemeral()
|
||||
: base(new PrefabHarnessOptions(
|
||||
RunMode.EphemeralIsolated,
|
||||
EnableAspireDashboard: false,
|
||||
DisablePortRandomization: false))
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class PrefabCompositeFixture_InProcess : PrefabCompositeFixture
|
||||
{
|
||||
public PrefabCompositeFixture_InProcess()
|
||||
: base(new PrefabHarnessOptions(
|
||||
RunMode.DebugPersistent,
|
||||
EnableAspireDashboard: true,
|
||||
DisablePortRandomization: true))
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool ShouldStartCatalog => false;
|
||||
|
||||
protected override IDictionary<string, string?> GetWebOverrides(Uri? catalogAddress) =>
|
||||
new Dictionary<string, string?>
|
||||
{
|
||||
["Prefab__Catalog__Client__Transport"] = "InProcess"
|
||||
};
|
||||
}
|
||||
|
||||
[CollectionDefinition("Prefab.Debug")]
|
||||
public sealed class PrefabDebugCollection : ICollectionFixture<PrefabCompositeFixture_Debug>
|
||||
{
|
||||
}
|
||||
|
||||
[CollectionDefinition("Prefab.Ephemeral")]
|
||||
public sealed class PrefabEphemeralCollection : ICollectionFixture<PrefabCompositeFixture_Ephemeral>
|
||||
{
|
||||
}
|
||||
|
||||
[CollectionDefinition("Prefab.InProcess")]
|
||||
public sealed class PrefabInProcessCollection : ICollectionFixture<PrefabCompositeFixture_InProcess>
|
||||
{
|
||||
}
|
||||
Reference in New Issue
Block a user