From 375843c31981c3804b0773477aecd30a91f8e3af Mon Sep 17 00:00:00 2001 From: JerrettDavis Date: Fri, 27 Mar 2026 13:01:17 -0500 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20flaky=20Cancellation=5FObserved=20te?= =?UTF-8?q?st=20=E2=80=94=20use=20pre-cancelled=20token?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test was racing: 10ms CancellationTokenSource timeout vs 100ms delay. On fast CI runners the operation could complete before cancellation fired. Fix: pre-cancel the token so cancellation is immediate and deterministic. Use a long delay (5000ms) so the operation would never complete without cancellation, making the test reliable regardless of runner speed. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../PatternKit.Tests/Behavioral/AsyncTemplateMethodTests.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/PatternKit.Tests/Behavioral/AsyncTemplateMethodTests.cs b/test/PatternKit.Tests/Behavioral/AsyncTemplateMethodTests.cs index 25393b9..a848052 100644 --- a/test/PatternKit.Tests/Behavioral/AsyncTemplateMethodTests.cs +++ b/test/PatternKit.Tests/Behavioral/AsyncTemplateMethodTests.cs @@ -89,8 +89,10 @@ public Task Serializes_When_Synchronized() [Fact] public async Task Cancellation_Observed() { - var template = new SampleAsyncTemplate(delayMs: 100); - using var cts = new CancellationTokenSource(10); + // Use a long delay with a pre-cancelled token to avoid timing races + var template = new SampleAsyncTemplate(delayMs: 5000); + using var cts = new CancellationTokenSource(); + cts.Cancel(); // pre-cancel so cancellation is immediate and deterministic // Assert.ThrowsAnyAsync verifies that OperationCanceledException or derived types are thrown await Assert.ThrowsAnyAsync( From 2acad8583628c708ad3613bf9ba06af66f8fec96 Mon Sep 17 00:00:00 2001 From: JerrettDavis Date: Fri, 27 Mar 2026 13:06:46 -0500 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20flaky=20Observer=20test=20=E2=80=94?= =?UTF-8?q?=20add=2030s=20timeout=20instead=20of=20blocking=20Wait()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mixed_Sync_And_Async_Handlers_Both_Invoked was using task.Wait() with no timeout, which could deadlock on CI runners under load. Added explicit 30s timeout with clear error message. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../PatternKit.Generators.Tests/ObserverGeneratorTests.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/PatternKit.Generators.Tests/ObserverGeneratorTests.cs b/test/PatternKit.Generators.Tests/ObserverGeneratorTests.cs index c935477..cdfd1a3 100644 --- a/test/PatternKit.Generators.Tests/ObserverGeneratorTests.cs +++ b/test/PatternKit.Generators.Tests/ObserverGeneratorTests.cs @@ -299,7 +299,8 @@ public static async System.Threading.Tasks.Task Run() var demoType = asm.GetType("PatternKit.Examples.Generators.Demo"); var runMethod = demoType!.GetMethod("Run"); var task = (System.Threading.Tasks.Task)runMethod!.Invoke(null, null)!; - task.Wait(); + if (!task.Wait(TimeSpan.FromSeconds(30))) + throw new TimeoutException("Demo.Run() did not complete within 30 seconds."); var result = task.Result; Assert.Equal("AsyncHandler:42", result); } @@ -629,7 +630,7 @@ public static async System.Threading.Tasks.Task Run() evt.Publish(new Temperature(10)); // Wait deterministically for async handler to complete - await tcs.Task.WaitAsync(System.TimeSpan.FromSeconds(5)); + await tcs.Task.WaitAsync(System.TimeSpan.FromSeconds(30)); return string.Join("|", log); } @@ -655,7 +656,8 @@ public static async System.Threading.Tasks.Task Run() var demoType = asm.GetType("PatternKit.Examples.Generators.Demo"); var runMethod = demoType!.GetMethod("Run"); var task = (System.Threading.Tasks.Task)runMethod!.Invoke(null, null)!; - task.Wait(); + if (!task.Wait(TimeSpan.FromSeconds(30))) + throw new TimeoutException("Demo.Run() did not complete within 30 seconds."); var result = task.Result; // Both handlers should have been invoked