notify doctors work, audit work, new product advisory sprints

This commit is contained in:
master
2026-01-13 08:36:29 +02:00
parent b8868a5f13
commit 9ca7cb183e
343 changed files with 24492 additions and 3544 deletions

View File

@@ -21,12 +21,12 @@ using StellaOps.Scanner.Triage;
using StellaOps.Determinism;
using StellaOps.Scanner.WebService.Diagnostics;
using StellaOps.Scanner.WebService.Services;
using Xunit;
namespace StellaOps.Scanner.WebService.Tests;
public sealed class ScannerApplicationFactory : WebApplicationFactory<ServiceStatus>
public sealed class ScannerApplicationFactory : WebApplicationFactory<ServiceStatus>, IAsyncLifetime, IAsyncDisposable
{
private readonly ScannerWebServicePostgresFixture? postgresFixture;
private readonly bool skipPostgres;
private readonly Dictionary<string, string?> configuration = new(StringComparer.OrdinalIgnoreCase)
{
@@ -53,6 +53,10 @@ public sealed class ScannerApplicationFactory : WebApplicationFactory<ServiceSta
private Action<IDictionary<string, string?>>? configureConfiguration;
private Action<IServiceCollection>? configureServices;
private bool useTestAuthentication;
private ScannerWebServicePostgresFixture? postgresFixture;
private Task? initializationTask;
private bool initialized;
private bool disposed;
public ScannerApplicationFactory() : this(skipPostgres: false)
{
@@ -61,25 +65,16 @@ public sealed class ScannerApplicationFactory : WebApplicationFactory<ServiceSta
private ScannerApplicationFactory(bool skipPostgres)
{
this.skipPostgres = skipPostgres;
initialized = skipPostgres;
if (!skipPostgres)
{
postgresFixture = new ScannerWebServicePostgresFixture();
postgresFixture.InitializeAsync().GetAwaiter().GetResult();
return;
}
var connectionBuilder = new NpgsqlConnectionStringBuilder(postgresFixture.ConnectionString)
{
SearchPath = $"{postgresFixture.SchemaName},public"
};
configuration["scanner:storage:dsn"] = connectionBuilder.ToString();
configuration["scanner:storage:database"] = postgresFixture.SchemaName;
}
else
{
// Lightweight mode: use stub connection string
configuration["scanner:storage:dsn"] = "Host=localhost;Database=test;";
configuration["scanner:storage:database"] = "test";
}
// Lightweight mode: use stub connection string
configuration["scanner:storage:dsn"] = "Host=localhost;Database=test;";
configuration["scanner:storage:database"] = "test";
}
/// <summary>
@@ -109,8 +104,62 @@ public sealed class ScannerApplicationFactory : WebApplicationFactory<ServiceSta
return this;
}
public Task InitializeAsync()
{
initializationTask ??= InitializeCoreAsync();
return initializationTask;
}
private async Task InitializeCoreAsync()
{
if (initialized)
{
return;
}
if (skipPostgres)
{
initialized = true;
return;
}
postgresFixture = new ScannerWebServicePostgresFixture();
await postgresFixture.InitializeAsync();
var connectionBuilder = new NpgsqlConnectionStringBuilder(postgresFixture.ConnectionString)
{
SearchPath = $"{postgresFixture.SchemaName},public"
};
configuration["scanner:storage:dsn"] = connectionBuilder.ToString();
configuration["scanner:storage:database"] = postgresFixture.SchemaName;
initialized = true;
}
Task IAsyncLifetime.DisposeAsync() => DisposeAsync().AsTask();
public async ValueTask DisposeAsync()
{
if (disposed)
{
return;
}
disposed = true;
base.Dispose();
if (postgresFixture is not null)
{
await postgresFixture.DisposeAsync();
}
}
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
if (!initialized)
{
throw new InvalidOperationException("ScannerApplicationFactory must be initialized via InitializeAsync before use.");
}
configureConfiguration?.Invoke(configuration);
builder.UseEnvironment("Testing");
@@ -200,16 +249,6 @@ public sealed class ScannerApplicationFactory : WebApplicationFactory<ServiceSta
});
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing && postgresFixture is not null)
{
postgresFixture.DisposeAsync().GetAwaiter().GetResult();
}
}
private sealed class TestSurfaceValidatorRunner : ISurfaceValidatorRunner
{
public ValueTask<SurfaceValidationResult> RunAllAsync(