test+fix: eliminate Windows full-suite temp-file-lock flakiness
Full-host tests passed in isolation but failed 2-4/run under parallel execution on
Windows with 'the process cannot access the file ... because it is being used by
another process' (macOS never sees it — Unix deletes open files). Two shared-state
parallel collisions, discovered while verifying TST-08:
1. Self-signed cert generation. GatewayTlsBootstrapTests sets process-global
Kestrel/TLS env vars that GatewayApplication.Build reads; a parallel host-building
test inherits them mid-run and both generate a cert at the same path, racing on the
fixed-name '<path>.tmp'. Fixes:
- SelfSignedCertificateProvider: stage the PFX in a unique '<path>.<guid>.tmp' instead
of a fixed name, so concurrent/interrupted writers never collide on the temp file
(real robustness: two instances or a restart-during-write no longer clash). The
final atomic Move (last-writer-wins) still yields an equivalent cert.
- TestHostEnvironmentInitializer: default MxGateway__Tls__SelfSignedCertPath to a
per-process temp path so the suite never writes the shared ProgramData default or
fights the deployed service; first host-building test generates, the rest load it.
- GatewayTlsBootstrapTests: [Collection] with DisableParallelization so its global
env-var mutation cannot bleed into parallel tests (new GlobalEnvironmentCollection).
2. AuthStoreHealthCheckTests opened a real SQLite file; Microsoft.Data.Sqlite's
connection pool keeps the .db handle open after 'await using', so the finally
File.Delete threw. Reuse the existing TempDatabaseDirectory helper, which calls
SqliteConnection.ClearAllPools() before deleting.
Server build clean; affected classes 17/17 on macOS. Windows full-suite verified separately.
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
namespace ZB.MOM.WW.MxGateway.Tests.TestSupport;
|
||||
|
||||
/// <summary>
|
||||
/// xUnit collection for tests that mutate <em>process-global</em> state (environment variables
|
||||
/// read by <c>GatewayApplication.Build</c>, e.g. <c>Kestrel__Endpoints__…</c> and
|
||||
/// <c>MxGateway__Tls__SelfSignedCertPath</c>). <c>DisableParallelization</c> keeps such a test from
|
||||
/// running concurrently with any other collection: otherwise a parallel host-building test inherits
|
||||
/// the mutated variables mid-run and the two race on the same generated-certificate file (on Windows,
|
||||
/// "the process cannot access the file … because it is being used by another process"). Membership is
|
||||
/// deliberately narrow — only add classes that set/clear real environment variables, not ones that
|
||||
/// pass configuration through <c>Build([...])</c> command-line args.
|
||||
/// </summary>
|
||||
[CollectionDefinition(Name, DisableParallelization = true)]
|
||||
public sealed class GlobalEnvironmentCollection
|
||||
{
|
||||
/// <summary>The collection name applied via <c>[Collection(GlobalEnvironmentCollection.Name)]</c>.</summary>
|
||||
public const string Name = "GlobalEnvironmentMutation";
|
||||
}
|
||||
@@ -3,7 +3,8 @@ using System.Runtime.CompilerServices;
|
||||
namespace ZB.MOM.WW.MxGateway.Tests.TestSupport;
|
||||
|
||||
/// <summary>
|
||||
/// Defaults the host environment to Development for the whole test assembly.
|
||||
/// Defaults the host environment to Development and isolates the test process's on-disk
|
||||
/// gateway paths, for the whole test assembly.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Many tests build the full gateway host through <c>GatewayApplication.Build</c> against the dev
|
||||
@@ -15,6 +16,15 @@ namespace ZB.MOM.WW.MxGateway.Tests.TestSupport;
|
||||
/// <c>GatewayOptionsValidator</c> with its <c>isProduction</c> constructor (no host environment) and
|
||||
/// are unaffected; a test that needs Production can still pass <c>--environment=Production</c>, which
|
||||
/// overrides this default.
|
||||
/// <para>
|
||||
/// The self-signed-cert path is also defaulted to a per-process temp file. Otherwise every
|
||||
/// full-host test that triggers HTTPS-cert generation writes the shared
|
||||
/// <c>CommonApplicationData/MxGateway/certs/gateway-selfsigned.pfx</c> default — parallel xUnit
|
||||
/// test classes then collide on that path's temp file (Windows: "the process cannot access the
|
||||
/// file ... because it is being used by another process"), and on a shared CI/dev box the suite
|
||||
/// also fights the deployed gateway service for the same file. Pointing at a per-process path
|
||||
/// isolates the suite: the first host-building test generates the cert, the rest load it.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
internal static class TestHostEnvironmentInitializer
|
||||
{
|
||||
@@ -25,5 +35,17 @@ internal static class TestHostEnvironmentInitializer
|
||||
{
|
||||
Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Development");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("MxGateway__Tls__SelfSignedCertPath")))
|
||||
{
|
||||
// ProcessId keeps the path stable within this test process (so a valid cert
|
||||
// generated by the first host-building test is reused by the rest) yet unique
|
||||
// across processes (so concurrent test runs / the deployed service never share it).
|
||||
string certPath = Path.Combine(
|
||||
Path.GetTempPath(),
|
||||
$"mxgw-tests-{Environment.ProcessId}",
|
||||
"gateway-selfsigned.pfx");
|
||||
Environment.SetEnvironmentVariable("MxGateway__Tls__SelfSignedCertPath", certPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user