From 9aaab9ee421504b2cfec84e4cb03df82b6272888 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Wed, 11 Nov 2026 15:09:20 -0500 Subject: [PATCH] test: #871 fix two hosted-CI races that failed master runs 1218-1220 - MeshPskProviderRefreshTests: GatedResolver.Entered was set by the priming refresh and never reset, so Wait() returned before the held refresh reached the store and the mid-refresh call-count snapshot raced its increment (expected 1, was 2). Hold() now resets it. - FailedCredentialLockoutTests all-bad burst: the 15 waiters could queue before a slot holder ran the increment after BeginAsync (authenticated 0 vs 5). The wait now covers both conditions; the security assertions are unchanged. Both looped 20x locally, 0 failures. Claude-Session: https://claude.ai/code/session_01GT67AD99zQQqpLCVm3p7xk --- .../Communication/MeshPskProviderRefreshTests.cs | 8 +++++++- .../FailedCredentialLockoutTests.cs | 4 +++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests/Communication/MeshPskProviderRefreshTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests/Communication/MeshPskProviderRefreshTests.cs index 02aafc1b6..36c209b7d 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests/Communication/MeshPskProviderRefreshTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests/Communication/MeshPskProviderRefreshTests.cs @@ -232,7 +232,13 @@ public sealed class MeshPskProviderRefreshTests public int Calls => Volatile.Read(ref _calls); public ManualResetEventSlim Entered { get; } = new(); - public void Hold() => _gate = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + // Reset Entered too: the priming refresh already set it, so without the reset Wait() returned before the + // held call had even reached the store and the call-count snapshot raced its increment (hosted-CI flake). + public void Hold() + { + Entered.Reset(); + _gate = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + } public void Release() => _gate?.TrySetResult(); public async Task GetAsync(SecretName name, CancellationToken ct) diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/FailedCredentialLockoutTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/FailedCredentialLockoutTests.cs index 0c0d5cfdf..85a4741e4 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/FailedCredentialLockoutTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/FailedCredentialLockoutTests.cs @@ -162,7 +162,9 @@ public sealed class FailedCredentialLockoutTests lockout.Complete(Client, succeeded: false); }, Ct)).ToArray(); - await WaitUntilAsync(() => lockout.WaitingCount(Client) == 15); + // Wait for the slot holders' increments too: the 15 waiters can queue before a slot holder's thread has run + // the line after BeginAsync, which read authenticated == 0 on the 2-core hosted runner. + await WaitUntilAsync(() => lockout.WaitingCount(Client) == 15 && Volatile.Read(ref authenticated) == 5); authenticated.ShouldBe(5); release.SetResult(); await Task.WhenAll(attempts);