From 0cd5c62466d5ddca1797a055e41818751f215f88 Mon Sep 17 00:00:00 2001 From: Sev7eNup <79143581+Sev7eNup@users.noreply.github.com> Date: Fri, 6 Nov 2026 00:02:00 +0100 Subject: [PATCH] Run the blocking parts of the rotation race test on dedicated threads The test parks the rotation inside Unprotect and then needs a second thread for the concurrent save. Both ran on the thread pool, so a busy runner could leave the save unscheduled for the full 10 s barrier and the test failed with "test barrier". Dedicated threads remove the dependency on thread-pool growth. Co-Authored-By: Claude Opus 5.5 --- .../RuntimeOverridesSecretRotationTests.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/NodePilot.Api.Tests/Configuration/RuntimeOverridesSecretRotationTests.cs b/tests/NodePilot.Api.Tests/Configuration/RuntimeOverridesSecretRotationTests.cs index 2b949bae..d9e28132 100644 --- a/tests/NodePilot.Api.Tests/Configuration/RuntimeOverridesSecretRotationTests.cs +++ b/tests/NodePilot.Api.Tests/Configuration/RuntimeOverridesSecretRotationTests.cs @@ -100,12 +100,14 @@ public async Task Rotation_ConcurrentSettingsSaveWaits_ThenUsesResealedDocumentA return _old.Unprotect(bytes); }); protector.Setup(x => x.Protect(It.IsAny())).Returns((string value) => _current.Protect(value)); - var rotation = Task.Run(() => _writer.ReencryptSecrets(protector.Object, CancellationToken.None)); + // Both parts block on purpose, so they get dedicated threads instead of waiting for the + // thread pool to grow under a parallel test run. + var rotation = RunOnDedicatedThread(() => _writer.ReencryptSecrets(protector.Object, CancellationToken.None)); Task? save = null; try { entered.Wait(TimeSpan.FromSeconds(10)).Should().BeTrue(); - save = Task.Run(() => { saveStarted.Set(); _writer.MutateAndWrite(root => root["Unrelated"] = 99); }); + save = RunOnDedicatedThread(() => { saveStarted.Set(); _writer.MutateAndWrite(root => root["Unrelated"] = 99); }); saveStarted.Wait(TimeSpan.FromSeconds(10)).Should().BeTrue(); resume.Set(); (await rotation.WaitAsync(TimeSpan.FromSeconds(10))).Skipped.Should().Be(0); @@ -122,6 +124,12 @@ public async Task Rotation_ConcurrentSettingsSaveWaits_ThenUsesResealedDocumentA } } + private static Task RunOnDedicatedThread(Func work) => + Task.Factory.StartNew(work, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); + + private static Task RunOnDedicatedThread(Action work) => + Task.Factory.StartNew(work, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); + private string Encrypt(string value) => EncryptingJsonConfigurationProvider.EncryptForPersist(value, _old); private string Decrypt(string value) => _current.Unprotect(Convert.FromBase64String( value[EncryptingJsonConfigurationProvider.EncryptedValuePrefix.Length..]));