112 lines
3.8 KiB
C#
112 lines
3.8 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|