Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using Microsoft.Extensions.Hosting;
using Wolverine.ErrorHandling;
using Xunit;

namespace CoreTests.Bugs;

public class Bug_2023_invoke_with_discard_error_handling
{
[Fact]
public async Task should_throw_the_exception_from_invoke()
{
using var host = await Host.CreateDefaultBuilder()
.UseWolverine(opts =>
{
// By default, this should NOT apply to inline executions (e.g. InvokeAsync),
// and an exception inside an inline execution should simply be surfaced.
// However, as of 3.13.0, this configuration causes the error not to be thrown.
opts.OnAnyException()
.Discard()
.And((runtime, context, ex) => {
// Do some application-specific error handling here...
return new ValueTask();
});
}).StartAsync();

var bus = host.MessageBus();

await Should.ThrowAsync<Exception>(async () =>
{
await bus.InvokeAsync(new Request());
});


}
}

public class RequestHandler {
/*
This is the exception that should appear to the user, but it does not.
This should not be caught by the Wolverine error handling because according
to the documentation,

`When using IMessageBus.InvokeAsync() to execute a message inline,
only the `Retry` and `Retry With Cooldown` error policies are applied
to the execution automatically.`

So, the `Discard/And` error policies should NOT be applied automatically,
and the error should be thrown (which was the case in 3.9.1, and got broken
by a commit in 3.13.0).
*/
public string Handle(Request request)
=> throw new Exception(@"User-facing error message that should appear to the user.");
}

public class Request { }
11 changes: 7 additions & 4 deletions src/Testing/CoreTests/ErrorHandling/LambdaContinuationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,13 @@ public async Task execute_as_inline_with_no_invoke_usage()
return new ValueTask();
}, new Exception());

var result = await continuation.ExecuteInlineAsync(Substitute.For<IEnvelopeLifecycle>(),
new MockWolverineRuntime(), DateTimeOffset.UtcNow, null, CancellationToken.None);

result.ShouldBe(InvokeResult.Stop);
await Should.ThrowAsync<Exception>(async () =>
{
var result = await continuation.ExecuteInlineAsync(Substitute.For<IEnvelopeLifecycle>(),
new MockWolverineRuntime(), DateTimeOffset.UtcNow, null, CancellationToken.None);

});

wasCalled.ShouldBeFalse();
}

Expand Down
10 changes: 10 additions & 0 deletions src/Wolverine/ErrorHandling/FailureRuleCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,16 @@ internal IContinuation DetermineExecutionContinuation(Exception e, Envelope enve
continue;
}

if (continuation is CompositeContinuation composite)
{
foreach (var inner in composite.Inner)
{
if (inner is IInlineContinuation inline) return inline;
}

continue;
}

if (continuation is IInlineContinuation retry)
{
return retry;
Expand Down
9 changes: 7 additions & 2 deletions src/Wolverine/ErrorHandling/PolicyExpression.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Diagnostics;
using System.Runtime.ExceptionServices;
using Wolverine.ErrorHandling.Matches;
using Wolverine.Runtime;
using Wolverine.Runtime.Handlers;
Expand Down Expand Up @@ -371,10 +372,14 @@ public ValueTask ExecuteAsync(IEnvelopeLifecycle lifecycle, IWolverineRuntime ru
public async ValueTask<InvokeResult> ExecuteInlineAsync(IEnvelopeLifecycle lifecycle, IWolverineRuntime runtime, DateTimeOffset now,
Activity? activity, CancellationToken cancellation)
{
if (InvokeUsage == null) return InvokeResult.Stop;
if (InvokeUsage == null)
{
ExceptionDispatchInfo.Throw(_exception);
return InvokeResult.Stop;
}

await _action(runtime, lifecycle, _exception);

return InvokeUsage.Value;
}
}
Expand Down
Loading