Files
ScadaBridge/tests/ZB.MOM.WW.ScadaBridge.Host.Tests/HostBootCollection.cs
T
Joseph Doherty d2a6107cdb 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
2026-07-16 23:31:28 -04:00

26 lines
1.3 KiB
C#

namespace ZB.MOM.WW.ScadaBridge.Host.Tests;
/// <summary>
/// Serializes every fixture that boots a real host while depending on process-wide
/// state — the <c>DOTNET_ENVIRONMENT</c> role selector and the environment variables
/// <see cref="CentralDbTestEnvironment"/> sets.
///
/// <c>Program</c>'s configuration builder calls <c>AddEnvironmentVariables()</c> at an
/// unpredictable point during boot, so a sibling fixture's teardown (which restores or
/// clears those vars) can otherwise be observed mid-boot as a transient null. Since the
/// secrets adoption (G-4) three of those keys are <c>${secret:...}</c> references that
/// fail closed, which turns that race into a hard <c>SecretNotFoundException</c>.
/// xUnit runs classes in a shared collection sequentially, which removes the overlap.
///
/// Site-role fixtures are deliberately not members: they call
/// <c>Configuration.Sources.Clear()</c>, dropping the environment-variable provider, so
/// they neither read nor write the shared state and stay free to run in parallel.
///
/// Supersedes the narrower "ActorSystem" collection, whose members are folded in here.
/// </summary>
[CollectionDefinition(HostBootCollection.Name)]
public class HostBootCollection
{
public const string Name = "HostBoot";
}