mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Merge branch 'main' into copilot/fix-input-waiter-flaky-test
This commit is contained in:
@@ -0,0 +1,546 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Microsoft.Agents.AI.Workflows.Execution;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.UnitTests;
|
||||
|
||||
public sealed class RouteBuilderTests
|
||||
{
|
||||
public enum HandlerOverload
|
||||
{
|
||||
SyncWithCancellation = 0,
|
||||
SyncWithoutCancellation = 1,
|
||||
AsyncWithCancellation = 2,
|
||||
AsyncWithoutCancellation = 3,
|
||||
}
|
||||
|
||||
private sealed record TestPayload(string Value);
|
||||
|
||||
private sealed class HandlerInvocation
|
||||
{
|
||||
public object? Message { get; private set; }
|
||||
|
||||
public IWorkflowContext? Context { get; private set; }
|
||||
|
||||
public CancellationToken CancellationToken { get; private set; }
|
||||
|
||||
public int InvocationCount { get; private set; }
|
||||
|
||||
public void Capture(object? message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
this.Message = message;
|
||||
this.Context = context;
|
||||
this.CancellationToken = cancellationToken;
|
||||
this.InvocationCount++;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class TestExternalRequestContext : IExternalRequestContext, IExternalRequestSink
|
||||
{
|
||||
public List<RequestPort> RegisteredPorts { get; } = [];
|
||||
|
||||
public List<ExternalRequest> PostedRequests { get; } = [];
|
||||
|
||||
public IExternalRequestSink RegisterPort(RequestPort port)
|
||||
{
|
||||
this.RegisteredPorts.Add(port);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ValueTask PostAsync(ExternalRequest request)
|
||||
{
|
||||
this.PostedRequests.Add(request);
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(HandlerOverload.SyncWithCancellation)]
|
||||
[InlineData(HandlerOverload.SyncWithoutCancellation)]
|
||||
[InlineData(HandlerOverload.AsyncWithCancellation)]
|
||||
[InlineData(HandlerOverload.AsyncWithoutCancellation)]
|
||||
public async Task AddHandler_VoidOverloads_RouteExpectedMessageAsync(HandlerOverload overload)
|
||||
{
|
||||
// Arrange
|
||||
RouteBuilder routeBuilder = new(null);
|
||||
HandlerInvocation invocation = new();
|
||||
CancellationToken cancellationToken = new CancellationTokenSource().Token;
|
||||
RegisterVoidHandler(routeBuilder, invocation, overload);
|
||||
MessageRouter router = routeBuilder.Build();
|
||||
TestWorkflowContext context = new("executor");
|
||||
|
||||
// Act
|
||||
CallResult? result = await router.RouteMessageAsync("hello", context, cancellationToken: cancellationToken);
|
||||
|
||||
// Assert
|
||||
result.Should().NotBeNull();
|
||||
result!.IsSuccess.Should().BeTrue();
|
||||
result.IsVoid.Should().BeTrue();
|
||||
result.Result.Should().BeNull();
|
||||
invocation.InvocationCount.Should().Be(1);
|
||||
invocation.Message.Should().Be("hello");
|
||||
invocation.Context.Should().BeSameAs(context);
|
||||
|
||||
if (UsesCancellationToken(overload))
|
||||
{
|
||||
invocation.CancellationToken.Should().Be(cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(HandlerOverload.SyncWithCancellation)]
|
||||
[InlineData(HandlerOverload.SyncWithoutCancellation)]
|
||||
[InlineData(HandlerOverload.AsyncWithCancellation)]
|
||||
[InlineData(HandlerOverload.AsyncWithoutCancellation)]
|
||||
public async Task AddHandler_ResultOverloads_RouteExpectedMessageAsync(HandlerOverload overload)
|
||||
{
|
||||
// Arrange
|
||||
RouteBuilder routeBuilder = new(null);
|
||||
HandlerInvocation invocation = new();
|
||||
CancellationToken cancellationToken = new CancellationTokenSource().Token;
|
||||
RegisterResultHandler(routeBuilder, invocation, overload);
|
||||
MessageRouter router = routeBuilder.Build();
|
||||
TestWorkflowContext context = new("executor");
|
||||
|
||||
// Act
|
||||
CallResult? result = await router.RouteMessageAsync("hello", context, cancellationToken: cancellationToken);
|
||||
|
||||
// Assert
|
||||
result.Should().NotBeNull();
|
||||
result!.IsSuccess.Should().BeTrue();
|
||||
result.IsVoid.Should().BeFalse();
|
||||
result.Result.Should().Be("HELLO");
|
||||
router.DefaultOutputTypes.Should().Contain(typeof(string));
|
||||
invocation.InvocationCount.Should().Be(1);
|
||||
invocation.Message.Should().Be("hello");
|
||||
invocation.Context.Should().BeSameAs(context);
|
||||
|
||||
if (UsesCancellationToken(overload))
|
||||
{
|
||||
invocation.CancellationToken.Should().Be(cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(HandlerOverload.SyncWithCancellation)]
|
||||
[InlineData(HandlerOverload.SyncWithoutCancellation)]
|
||||
[InlineData(HandlerOverload.AsyncWithCancellation)]
|
||||
[InlineData(HandlerOverload.AsyncWithoutCancellation)]
|
||||
public async Task AddCatchAll_VoidOverloads_RouteUnexpectedMessageAsync(HandlerOverload overload)
|
||||
{
|
||||
// Arrange
|
||||
RouteBuilder routeBuilder = new(null);
|
||||
HandlerInvocation invocation = new();
|
||||
CancellationToken cancellationToken = new CancellationTokenSource().Token;
|
||||
TestPayload payload = new("hello");
|
||||
RegisterVoidCatchAll(routeBuilder, invocation, overload);
|
||||
MessageRouter router = routeBuilder.Build();
|
||||
TestWorkflowContext context = new("executor");
|
||||
|
||||
// Act
|
||||
CallResult? result = await router.RouteMessageAsync(payload, context, cancellationToken: cancellationToken);
|
||||
|
||||
// Assert
|
||||
result.Should().NotBeNull();
|
||||
result!.IsSuccess.Should().BeTrue();
|
||||
result.IsVoid.Should().BeTrue();
|
||||
result.Result.Should().BeNull();
|
||||
invocation.InvocationCount.Should().Be(1);
|
||||
invocation.Message.Should().BeEquivalentTo(new PortableValue(payload));
|
||||
invocation.Context.Should().BeSameAs(context);
|
||||
|
||||
if (UsesCancellationToken(overload))
|
||||
{
|
||||
invocation.CancellationToken.Should().Be(cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(HandlerOverload.SyncWithCancellation)]
|
||||
[InlineData(HandlerOverload.SyncWithoutCancellation)]
|
||||
[InlineData(HandlerOverload.AsyncWithCancellation)]
|
||||
[InlineData(HandlerOverload.AsyncWithoutCancellation)]
|
||||
public async Task AddCatchAll_ResultOverloads_RouteUnexpectedMessageAsync(HandlerOverload overload)
|
||||
{
|
||||
// Arrange
|
||||
RouteBuilder routeBuilder = new(null);
|
||||
HandlerInvocation invocation = new();
|
||||
CancellationToken cancellationToken = new CancellationTokenSource().Token;
|
||||
TestPayload payload = new("hello");
|
||||
RegisterResultCatchAll(routeBuilder, invocation, overload);
|
||||
MessageRouter router = routeBuilder.Build();
|
||||
TestWorkflowContext context = new("executor");
|
||||
|
||||
// Act
|
||||
CallResult? result = await router.RouteMessageAsync(payload, context, cancellationToken: cancellationToken);
|
||||
|
||||
// Assert
|
||||
result.Should().NotBeNull();
|
||||
result!.IsSuccess.Should().BeTrue();
|
||||
result.IsVoid.Should().BeFalse();
|
||||
result.Result.Should().Be("HELLO");
|
||||
invocation.InvocationCount.Should().Be(1);
|
||||
invocation.Message.Should().BeEquivalentTo(new PortableValue(payload));
|
||||
invocation.Context.Should().BeSameAs(context);
|
||||
|
||||
if (UsesCancellationToken(overload))
|
||||
{
|
||||
invocation.CancellationToken.Should().Be(cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddHandlerUntyped_VoidAndResultOverloads_RouteExpectedMessageAsync()
|
||||
{
|
||||
// Arrange
|
||||
RouteBuilder routeBuilder = new(null);
|
||||
HandlerInvocation voidInvocation = new();
|
||||
HandlerInvocation resultInvocation = new();
|
||||
CancellationToken cancellationToken = new CancellationTokenSource().Token;
|
||||
routeBuilder.AddHandlerUntyped(typeof(string), (message, context, token) =>
|
||||
{
|
||||
voidInvocation.Capture(message, context, token);
|
||||
return default;
|
||||
});
|
||||
routeBuilder.AddHandlerUntyped<int>(typeof(int), (message, context, token) =>
|
||||
{
|
||||
resultInvocation.Capture(message, context, token);
|
||||
return new((int)message + 1);
|
||||
});
|
||||
MessageRouter router = routeBuilder.Build();
|
||||
TestWorkflowContext context = new("executor");
|
||||
|
||||
// Act
|
||||
CallResult? voidResult = await router.RouteMessageAsync("hello", context, cancellationToken: cancellationToken);
|
||||
CallResult? typedResult = await router.RouteMessageAsync(41, context, cancellationToken: cancellationToken);
|
||||
|
||||
// Assert
|
||||
voidResult.Should().NotBeNull();
|
||||
voidResult!.IsVoid.Should().BeTrue();
|
||||
voidInvocation.Message.Should().Be("hello");
|
||||
voidInvocation.Context.Should().BeSameAs(context);
|
||||
voidInvocation.CancellationToken.Should().Be(cancellationToken);
|
||||
|
||||
typedResult.Should().NotBeNull();
|
||||
typedResult!.Result.Should().Be(42);
|
||||
router.DefaultOutputTypes.Should().Contain(typeof(int));
|
||||
resultInvocation.Message.Should().Be(41);
|
||||
resultInvocation.Context.Should().BeSameAs(context);
|
||||
resultInvocation.CancellationToken.Should().Be(cancellationToken);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddHandler_ForPortableValue_ThrowsInvalidOperationException()
|
||||
{
|
||||
// Arrange
|
||||
RouteBuilder routeBuilder = new(null);
|
||||
|
||||
// Act
|
||||
Action act = () => routeBuilder.AddHandler<PortableValue>((message, context) => { });
|
||||
|
||||
// Assert
|
||||
act.Should().Throw<InvalidOperationException>()
|
||||
.WithMessage("*Use AddCatchAll()*");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddHandler_DuplicateRegistrationWithoutOverwrite_ThrowsArgumentException()
|
||||
{
|
||||
// Arrange
|
||||
RouteBuilder routeBuilder = new(null);
|
||||
routeBuilder.AddHandler<string>((message, context) => { });
|
||||
|
||||
// Act
|
||||
Action act = () => routeBuilder.AddHandler<string>((message, context) => { });
|
||||
|
||||
// Assert
|
||||
act.Should().Throw<ArgumentException>()
|
||||
.WithMessage("*already registered*");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddHandler_OverwriteWithoutExistingRegistration_ThrowsArgumentException()
|
||||
{
|
||||
// Arrange
|
||||
RouteBuilder routeBuilder = new(null);
|
||||
|
||||
// Act
|
||||
Action act = () => routeBuilder.AddHandler<string>((message, context) => { }, overwrite: true);
|
||||
|
||||
// Assert
|
||||
act.Should().Throw<ArgumentException>()
|
||||
.WithMessage("*has not yet been registered*");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddHandler_OverwriteExistingRegistration_RoutesUpdatedHandlerAsync()
|
||||
{
|
||||
// Arrange
|
||||
RouteBuilder routeBuilder = new(null);
|
||||
routeBuilder.AddHandler<string>((message, context) => context.SendMessageAsync("first"));
|
||||
routeBuilder.AddHandler<string>((message, context) => context.SendMessageAsync("second"), overwrite: true);
|
||||
MessageRouter router = routeBuilder.Build();
|
||||
TestWorkflowContext context = new("executor");
|
||||
|
||||
// Act
|
||||
_ = await router.RouteMessageAsync("hello", context);
|
||||
|
||||
// Assert
|
||||
context.SentMessages.Should().ContainSingle().Which.Should().Be("second");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddCatchAll_DuplicateRegistrationWithoutOverwrite_ThrowsInvalidOperationException()
|
||||
{
|
||||
// Arrange
|
||||
RouteBuilder routeBuilder = new(null);
|
||||
routeBuilder.AddCatchAll((message, context) => { });
|
||||
|
||||
// Act
|
||||
Action act = () => routeBuilder.AddCatchAll((message, context) => { });
|
||||
|
||||
// Assert
|
||||
act.Should().Throw<InvalidOperationException>()
|
||||
.WithMessage("*already registered*");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddCatchAll_OverwriteExistingRegistration_RoutesUpdatedHandlerAsync()
|
||||
{
|
||||
// Arrange
|
||||
RouteBuilder routeBuilder = new(null);
|
||||
routeBuilder.AddCatchAll((message, context) => context.SendMessageAsync("first"));
|
||||
routeBuilder.AddCatchAll((message, context) => context.SendMessageAsync("second"), overwrite: true);
|
||||
MessageRouter router = routeBuilder.Build();
|
||||
TestWorkflowContext context = new("executor");
|
||||
|
||||
// Act
|
||||
_ = await router.RouteMessageAsync(new TestPayload("hello"), context);
|
||||
|
||||
// Assert
|
||||
context.SentMessages.Should().ContainSingle().Which.Should().Be("second");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddPortHandler_WithoutExternalRequestContext_ThrowsInvalidOperationException()
|
||||
{
|
||||
// Arrange
|
||||
RouteBuilder routeBuilder = new(null);
|
||||
|
||||
// Act
|
||||
Action act = () => routeBuilder.AddPortHandler<string, int>("port", (response, context, cancellationToken) => default, out _);
|
||||
|
||||
// Assert
|
||||
act.Should().Throw<InvalidOperationException>()
|
||||
.WithMessage("*external request context is required*");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddPortHandler_RoutesMatchingExternalResponseAsync()
|
||||
{
|
||||
// Arrange
|
||||
TestExternalRequestContext externalRequestContext = new();
|
||||
RouteBuilder routeBuilder = new(externalRequestContext);
|
||||
HandlerInvocation invocation = new();
|
||||
routeBuilder.AddPortHandler<string, int>("port", (response, context, cancellationToken) =>
|
||||
{
|
||||
invocation.Capture(response, context, cancellationToken);
|
||||
return default;
|
||||
}, out PortBinding portBinding);
|
||||
await portBinding.PostRequestAsync("request", requestId: "req-1");
|
||||
MessageRouter router = routeBuilder.Build();
|
||||
TestWorkflowContext context = new("executor");
|
||||
CancellationToken cancellationToken = new CancellationTokenSource().Token;
|
||||
ExternalResponse response = externalRequestContext.PostedRequests.Single().CreateResponse(42);
|
||||
|
||||
// Act
|
||||
CallResult? result = await router.RouteMessageAsync(response, context, cancellationToken: cancellationToken);
|
||||
|
||||
// Assert
|
||||
externalRequestContext.RegisteredPorts.Should().ContainSingle(port => port.Id == "port");
|
||||
externalRequestContext.PostedRequests.Should().ContainSingle(request => request.RequestId == "req-1");
|
||||
result.Should().NotBeNull();
|
||||
result!.IsSuccess.Should().BeTrue();
|
||||
result.Result.Should().BeSameAs(response);
|
||||
invocation.InvocationCount.Should().Be(1);
|
||||
invocation.Message.Should().Be(42);
|
||||
invocation.Context.Should().BeSameAs(context);
|
||||
invocation.CancellationToken.Should().Be(cancellationToken);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddPortHandler_UnknownPort_ReturnsExceptionResultAsync()
|
||||
{
|
||||
// Arrange
|
||||
TestExternalRequestContext externalRequestContext = new();
|
||||
RouteBuilder routeBuilder = new(externalRequestContext);
|
||||
routeBuilder.AddPortHandler<string, int>("port", (response, context, cancellationToken) => default, out _);
|
||||
MessageRouter router = routeBuilder.Build();
|
||||
ExternalRequest request = ExternalRequest.Create(RequestPort.Create<string, int>("other"), "request", requestId: "req-1");
|
||||
|
||||
// Act
|
||||
CallResult? result = await router.RouteMessageAsync(request.CreateResponse(42), new TestWorkflowContext("executor"));
|
||||
|
||||
// Assert
|
||||
result.Should().NotBeNull();
|
||||
result!.IsSuccess.Should().BeFalse();
|
||||
result.Exception.Should().BeOfType<InvalidOperationException>();
|
||||
result.Exception!.Message.Should().Contain("Unknown port");
|
||||
}
|
||||
|
||||
private static void RegisterVoidHandler(RouteBuilder routeBuilder, HandlerInvocation invocation, HandlerOverload overload)
|
||||
{
|
||||
switch (overload)
|
||||
{
|
||||
case HandlerOverload.SyncWithCancellation:
|
||||
routeBuilder.AddHandler<string>((message, context, cancellationToken) => invocation.Capture(message, context, cancellationToken));
|
||||
break;
|
||||
case HandlerOverload.SyncWithoutCancellation:
|
||||
routeBuilder.AddHandler<string>((message, context) => invocation.Capture(message, context));
|
||||
break;
|
||||
case HandlerOverload.AsyncWithCancellation:
|
||||
routeBuilder.AddHandler<string>((message, context, cancellationToken) =>
|
||||
{
|
||||
invocation.Capture(message, context, cancellationToken);
|
||||
return default;
|
||||
});
|
||||
break;
|
||||
case HandlerOverload.AsyncWithoutCancellation:
|
||||
routeBuilder.AddHandler<string>((message, context) =>
|
||||
{
|
||||
invocation.Capture(message, context);
|
||||
return default;
|
||||
});
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(overload));
|
||||
}
|
||||
}
|
||||
|
||||
private static void RegisterResultHandler(RouteBuilder routeBuilder, HandlerInvocation invocation, HandlerOverload overload)
|
||||
{
|
||||
switch (overload)
|
||||
{
|
||||
case HandlerOverload.SyncWithCancellation:
|
||||
routeBuilder.AddHandler<string, string>((message, context, cancellationToken) =>
|
||||
{
|
||||
invocation.Capture(message, context, cancellationToken);
|
||||
return NormalizeHandlerResult(message);
|
||||
});
|
||||
break;
|
||||
case HandlerOverload.SyncWithoutCancellation:
|
||||
routeBuilder.AddHandler<string, string>((message, context) =>
|
||||
{
|
||||
invocation.Capture(message, context);
|
||||
return NormalizeHandlerResult(message);
|
||||
});
|
||||
break;
|
||||
case HandlerOverload.AsyncWithCancellation:
|
||||
Func<string, IWorkflowContext, CancellationToken, ValueTask<string>> asyncHandlerWithCancellation = (message, context, cancellationToken) =>
|
||||
{
|
||||
invocation.Capture(message, context, cancellationToken);
|
||||
return new ValueTask<string>(NormalizeHandlerResult(message));
|
||||
};
|
||||
routeBuilder.AddHandler(asyncHandlerWithCancellation);
|
||||
break;
|
||||
case HandlerOverload.AsyncWithoutCancellation:
|
||||
Func<string, IWorkflowContext, ValueTask<string>> asyncHandler = (message, context) =>
|
||||
{
|
||||
invocation.Capture(message, context);
|
||||
return new ValueTask<string>(NormalizeHandlerResult(message));
|
||||
};
|
||||
routeBuilder.AddHandler(asyncHandler);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(overload));
|
||||
}
|
||||
}
|
||||
|
||||
private static void RegisterVoidCatchAll(RouteBuilder routeBuilder, HandlerInvocation invocation, HandlerOverload overload)
|
||||
{
|
||||
switch (overload)
|
||||
{
|
||||
case HandlerOverload.SyncWithCancellation:
|
||||
routeBuilder.AddCatchAll((message, context, cancellationToken) => invocation.Capture(message, context, cancellationToken));
|
||||
break;
|
||||
case HandlerOverload.SyncWithoutCancellation:
|
||||
routeBuilder.AddCatchAll((message, context) => invocation.Capture(message, context));
|
||||
break;
|
||||
case HandlerOverload.AsyncWithCancellation:
|
||||
routeBuilder.AddCatchAll((message, context, cancellationToken) =>
|
||||
{
|
||||
invocation.Capture(message, context, cancellationToken);
|
||||
return default;
|
||||
});
|
||||
break;
|
||||
case HandlerOverload.AsyncWithoutCancellation:
|
||||
routeBuilder.AddCatchAll((message, context) =>
|
||||
{
|
||||
invocation.Capture(message, context);
|
||||
return default;
|
||||
});
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(overload));
|
||||
}
|
||||
}
|
||||
|
||||
private static void RegisterResultCatchAll(RouteBuilder routeBuilder, HandlerInvocation invocation, HandlerOverload overload)
|
||||
{
|
||||
switch (overload)
|
||||
{
|
||||
case HandlerOverload.SyncWithCancellation:
|
||||
routeBuilder.AddCatchAll((message, context, cancellationToken) =>
|
||||
{
|
||||
invocation.Capture(message, context, cancellationToken);
|
||||
return NormalizeCatchAllResult(message);
|
||||
});
|
||||
break;
|
||||
case HandlerOverload.SyncWithoutCancellation:
|
||||
routeBuilder.AddCatchAll((message, context) =>
|
||||
{
|
||||
invocation.Capture(message, context);
|
||||
return NormalizeCatchAllResult(message);
|
||||
});
|
||||
break;
|
||||
case HandlerOverload.AsyncWithCancellation:
|
||||
Func<PortableValue, IWorkflowContext, CancellationToken, ValueTask<string>> asyncCatchAllWithCancellation = (message, context, cancellationToken) =>
|
||||
{
|
||||
invocation.Capture(message, context, cancellationToken);
|
||||
return new ValueTask<string>(NormalizeCatchAllResult(message));
|
||||
};
|
||||
routeBuilder.AddCatchAll(asyncCatchAllWithCancellation);
|
||||
break;
|
||||
case HandlerOverload.AsyncWithoutCancellation:
|
||||
Func<PortableValue, IWorkflowContext, ValueTask<string>> asyncCatchAll = (message, context) =>
|
||||
{
|
||||
invocation.Capture(message, context);
|
||||
return new ValueTask<string>(NormalizeCatchAllResult(message));
|
||||
};
|
||||
routeBuilder.AddCatchAll(asyncCatchAll);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(overload));
|
||||
}
|
||||
}
|
||||
|
||||
private static bool UsesCancellationToken(HandlerOverload overload) =>
|
||||
overload is HandlerOverload.SyncWithCancellation or HandlerOverload.AsyncWithCancellation;
|
||||
|
||||
private static string NormalizeHandlerResult(string message) => message.ToUpperInvariant();
|
||||
|
||||
private static string NormalizeCatchAllResult(PortableValue message) => GetPayloadValue(message).ToUpperInvariant();
|
||||
|
||||
private static string GetPayloadValue(PortableValue message)
|
||||
{
|
||||
return message.As<TestPayload>() is TestPayload payload
|
||||
? payload.Value
|
||||
: throw new InvalidOperationException("Expected catch-all message payload to deserialize as TestPayload.");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user