From 02d4cd05679224816ea49af61a5c1791da4c03be Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 13 Feb 2026 15:25:50 +0000 Subject: [PATCH] Sync changes from main to net9 - Applied recent commits, updated versions & framework, preserved .csproj files --- src/TickerQ/Src/TickerExecutionTaskHandler.cs | 14 ++++-- .../SoftSchedulerNotifyDebounceTests.cs | 2 +- .../TickerExecutionTaskHandlerTests.cs | 48 ++++++++++++++++++- 3 files changed, 57 insertions(+), 7 deletions(-) diff --git a/src/TickerQ/Src/TickerExecutionTaskHandler.cs b/src/TickerQ/Src/TickerExecutionTaskHandler.cs index 20819fa3..77164c3a 100644 --- a/src/TickerQ/Src/TickerExecutionTaskHandler.cs +++ b/src/TickerQ/Src/TickerExecutionTaskHandler.cs @@ -257,6 +257,15 @@ private async Task RunContextFunctionAsync(InternalFunctionContext context, bool catch (Exception ex) { lastException = ex; + + context.SetProperty(x => x.ExceptionDetails, SerializeException(ex)); + + if (_serviceProvider.GetService(typeof(ITickerExceptionHandler)) is ITickerExceptionHandler handler) + await handler.HandleExceptionAsync(ex, context.TickerId, context.Type); + + await _internalTickerManager.UpdateTickerAsync(context, cancellationToken); + + context.ResetUpdateProps(); } } @@ -292,11 +301,6 @@ private async Task RunContextFunctionAsync(InternalFunctionContext context, bool _tickerQInstrumentation.LogJobFailed(context.TickerId, context.FunctionName, lastException, context.RetryCount); _tickerQInstrumentation.LogJobCompleted(context.TickerId, context.FunctionName, stopWatch.ElapsedMilliseconds, false); - var handler = _serviceProvider.GetService(typeof(ITickerExceptionHandler)) as ITickerExceptionHandler; - - if (handler != null) - await handler.HandleExceptionAsync(lastException, context.TickerId, context.Type); - await _internalTickerManager.UpdateTickerAsync(context, cancellationToken); } diff --git a/tests/TickerQ.Tests/SoftSchedulerNotifyDebounceTests.cs b/tests/TickerQ.Tests/SoftSchedulerNotifyDebounceTests.cs index 1d757e5a..27b694b0 100644 --- a/tests/TickerQ.Tests/SoftSchedulerNotifyDebounceTests.cs +++ b/tests/TickerQ.Tests/SoftSchedulerNotifyDebounceTests.cs @@ -99,7 +99,7 @@ public void NotifySafely_AlwaysInvokes_ForZeroValue() debounce.NotifySafely(0); debounce.NotifySafely(0); - callCount.Should().BeGreaterOrEqualTo(2); + callCount.Should().BeGreaterThanOrEqualTo(2); } [Fact] diff --git a/tests/TickerQ.Tests/TickerExecutionTaskHandlerTests.cs b/tests/TickerQ.Tests/TickerExecutionTaskHandlerTests.cs index ebc2444d..0effd3cf 100644 --- a/tests/TickerQ.Tests/TickerExecutionTaskHandlerTests.cs +++ b/tests/TickerQ.Tests/TickerExecutionTaskHandlerTests.cs @@ -75,7 +75,7 @@ public async Task ExecuteTaskAsync_SetsElapsedTime_OnSuccess() await _handler.ExecuteTaskAsync(context, isDue: false); - context.ElapsedTime.Should().BeGreaterOrEqualTo(0); + context.ElapsedTime.Should().BeGreaterThanOrEqualTo(0); } [Fact] @@ -136,6 +136,52 @@ await exceptionHandler.Received(1).HandleExceptionAsync( Arg.Is(context.Type)); } + [Fact] + public async Task ExecuteTaskAsync_CallsExceptionHandler_ForEachFailedAttempt() + { + var exceptionHandler = Substitute.For(); + var services = new ServiceCollection(); + services.AddSingleton(_internalManager); + services.AddSingleton(_instrumentation); + services.AddSingleton(exceptionHandler); + var sp = services.BuildServiceProvider(); + var handler = new TickerExecutionTaskHandler(sp, _clock, _instrumentation, _internalManager); + + var context = CreateContext(ct: (_, _, _) => throw new InvalidOperationException("boom")); + context.Retries = 2; + context.RetryIntervals = [0, 0]; + + await handler.ExecuteTaskAsync(context, isDue: false); + + await exceptionHandler.Received(3).HandleExceptionAsync( + Arg.Any(), + Arg.Is(context.TickerId), + Arg.Is(context.Type)); + } + + [Fact] + public async Task ExecuteTaskAsync_UpdatesExceptionDetails_OnFailedAttemptBeforeRetriesComplete() + { + var context = CreateContext(ct: (_, _, _) => throw new InvalidOperationException("boom")); + context.Retries = 1; + context.RetryIntervals = [0]; + var observedUpdates = new List<(TickerStatus Status, string ExceptionDetails)>(); + + _internalManager + .When(x => x.UpdateTickerAsync(Arg.Any(), Arg.Any())) + .Do(callInfo => + { + var ctx = callInfo.Arg(); + observedUpdates.Add((ctx.Status, ctx.ExceptionDetails)); + }); + + await _handler.ExecuteTaskAsync(context, isDue: false); + + observedUpdates.Should().Contain(x => + x.Status == TickerStatus.InProgress && + !string.IsNullOrWhiteSpace(x.ExceptionDetails)); + } + #endregion #region Cancellation Path