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:
@@ -163,7 +163,15 @@ public sealed class SelfSignedCertificateProvider
|
||||
// temp file empty, harden its permissions, and only then write the PFX into
|
||||
// the already-protected file. The temp path is in the same directory as the
|
||||
// target so the Move is atomic and preserves the hardened DACL/mode.
|
||||
string temp = path + ".tmp";
|
||||
//
|
||||
// The temp name carries a unique suffix rather than a fixed "<path>.tmp": two
|
||||
// processes (or two parallel callers) generating to the same target must not
|
||||
// collide on one temp file. On Windows a fixed name makes the second writer's
|
||||
// File.Create/Move fail with "the process cannot access the file ... because it
|
||||
// is being used by another process"; a unique name lets each generation stage
|
||||
// independently, and the final atomic Move (last-writer-wins) still yields a
|
||||
// valid, equivalent certificate at the shared path.
|
||||
string temp = $"{path}.{Guid.NewGuid():N}.tmp";
|
||||
using (File.Create(temp)) { }
|
||||
HardenPermissions(temp);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user