This commit is contained in:
2025-10-27 17:39:18 -04:00
commit 31f723bea4
1579 changed files with 642409 additions and 0 deletions

View File

@@ -0,0 +1,111 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
namespace Prefab.Tests.Infrastructure.Aspire;
internal sealed class AppHost(DistributedApplication application, EnvironmentVariableScope? environmentScope) : IAsyncDisposable
{
public DistributedApplication Application => application;
public static async Task<AppHost> StartAsync(IReadOnlyDictionary<string, string?>? environmentOverrides, CancellationToken cancellationToken)
{
EnvironmentVariableScope? scope = null;
if (environmentOverrides is not null && environmentOverrides.Count > 0)
{
scope = new EnvironmentVariableScope(environmentOverrides);
}
DistributedApplication? application = null;
try
{
string[] defaultArgs =
[
"SqlServer:UseDataVolume=false",
"SqlServer:HostPort=",
"Parameters:prefab-sql-password=PrefabTests!1234",
];
var builder = await DistributedApplicationTestingBuilder
.CreateAsync<Projects.Prefab_AppHost>(defaultArgs, static (_, hostOptions) =>
{
hostOptions ??= new HostApplicationBuilderSettings();
hostOptions.Configuration ??= new ConfigurationManager();
}, cancellationToken);
application = await builder.BuildAsync(cancellationToken);
await application.StartAsync(cancellationToken);
var notifications = application.ResourceNotifications;
await notifications.WaitForResourceHealthyAsync("prefab-sql", cancellationToken);
await notifications.WaitForResourceHealthyAsync("prefab-catalog", cancellationToken);
await notifications.WaitForResourceHealthyAsync("prefab-web", cancellationToken);
return new AppHost(application, scope);
}
catch
{
if (application is not null)
{
await application.DisposeAsync();
}
scope?.Dispose();
throw;
}
}
public HttpClient CreateHttpClient(string resourceName) =>
application.CreateHttpClient(resourceName);
public async Task<string> GetSqlConnectionStringAsync(string resourceName, CancellationToken cancellationToken)
{
var connectionString = await application.GetConnectionStringAsync(resourceName, cancellationToken).ConfigureAwait(false);
if (string.IsNullOrWhiteSpace(connectionString))
{
throw new InvalidOperationException($"SQL resource '{resourceName}' did not provide a connection string.");
}
return connectionString;
}
public async ValueTask DisposeAsync()
{
try
{
await application.StopAsync(CancellationToken.None);
}
catch
{
// best effort
}
await application.DisposeAsync();
environmentScope?.Dispose();
}
}
internal sealed class EnvironmentVariableScope : IDisposable
{
private readonly IReadOnlyDictionary<string, string?> _overrides;
private readonly Dictionary<string, string?> _originalValues = new(StringComparer.OrdinalIgnoreCase);
public EnvironmentVariableScope(IReadOnlyDictionary<string, string?> overrides)
{
_overrides = overrides;
foreach (var pair in overrides)
{
_originalValues[pair.Key] = Environment.GetEnvironmentVariable(pair.Key);
Environment.SetEnvironmentVariable(pair.Key, pair.Value);
}
}
public void Dispose()
{
foreach (var pair in _overrides)
{
_originalValues.TryGetValue(pair.Key, out var value);
Environment.SetEnvironmentVariable(pair.Key, value);
}
}
}