test(host): serialize Central-boot fixtures to close env-var race

CentralDbTestEnvironment sets five process-wide environment variables, and
Program's AddEnvironmentVariables() reads them at an unpredictable point during
host boot. With xUnit collection parallelization on, one fixture's teardown
could clear a var mid-boot for a sibling. Since the secrets adoption (G-4)
three of those keys are ${secret:...} references that fail closed, turning a
previously benign empty value into a SecretNotFoundException that aborts the
boot — an intermittent CI failure.

Adds a HostBootCollection that serializes every fixture booting a real host
while depending on that shared state, folding in the narrower "ActorSystem"
collection so its members stay serialized with each other as before. Site-role
fixtures stay parallel: they call Configuration.Sources.Clear(), dropping the
env-var provider, so they cannot participate in the race.

CentralDbTestEnvironment now also fails fast if two instances are ever live at
once, making a regression (a fixture added outside the collection) deterministic
rather than intermittent — this is what surfaced the disposed-CTS defect fixed
in the previous commit. Fixture teardown is try/finally so a throwing host
teardown can no longer strand the vars for the rest of the run.

Cost: Host.Tests runs ~2m30s -> ~4m10s; the serialized Central-boot classes are
most of the assembly's parallelism.

Fixes: Gitea #15
This commit is contained in:
Joseph Doherty
2026-07-16 23:31:28 -04:00
parent 9110a4eb01
commit d2a6107cdb
8 changed files with 95 additions and 14 deletions
@@ -86,6 +86,7 @@ public class AkkaHostedServiceAuditWiringHoconTests
/// <summary>
/// Verifies Audit Log (#23) services land in the Central composition root.
/// </summary>
[Collection(HostBootCollection.Name)]
public class CentralAuditWiringTests : IDisposable
{
private readonly WebApplicationFactory<Program> _factory;
@@ -156,9 +157,17 @@ public class CentralAuditWiringTests : IDisposable
public void Dispose()
{
_factory.Dispose();
Environment.SetEnvironmentVariable("DOTNET_ENVIRONMENT", _previousEnv);
_dbEnv.Dispose();
// try/finally: a throwing host teardown must not strand the process-wide
// environment variables for the rest of the run.
try
{
_factory.Dispose();
}
finally
{
Environment.SetEnvironmentVariable("DOTNET_ENVIRONMENT", _previousEnv);
_dbEnv.Dispose();
}
}
[Fact]