mirror of
https://github.com/microsoft/autogen.git
synced 2025-08-23 16:12:22 +00:00

During the recent fix to SendMessageAsync's recurrence, we added code to ensure blocking on Publish. This adds additional resilience to the Publish delivery, ensuring that every subscribed agent receives the message, regardless of errors in the middle.
121 lines
5.1 KiB
C#
121 lines
5.1 KiB
C#
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// SendMessageTests.cs
|
|
|
|
using System.Diagnostics;
|
|
using System.Reflection;
|
|
using FluentAssertions;
|
|
using Microsoft.AutoGen.Contracts;
|
|
using Microsoft.Extensions.Logging;
|
|
using Xunit;
|
|
|
|
namespace Microsoft.AutoGen.Core.Tests;
|
|
|
|
[Trait("Category", "UnitV2")]
|
|
public partial class SendMessageTests
|
|
{
|
|
private sealed class SendOnAgent : BaseAgent, IHandle<BasicMessage>
|
|
{
|
|
private IList<Guid> targetKeys;
|
|
|
|
public SendOnAgent(AgentId id, IAgentRuntime runtime, string description, IList<Guid> targetKeys, ILogger<BaseAgent>? logger = null)
|
|
: base(id, runtime, description, logger)
|
|
{
|
|
this.targetKeys = targetKeys;
|
|
}
|
|
|
|
public async ValueTask HandleAsync(BasicMessage item, MessageContext messageContext)
|
|
{
|
|
foreach (Guid targetKey in targetKeys)
|
|
{
|
|
AgentId targetId = new(nameof(ReceiverAgent), targetKey.ToString());
|
|
BasicMessage message = new BasicMessage { Content = $"@{targetKey}: {item.Content}" };
|
|
await this.Runtime.SendMessageAsync(message, targetId);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Test_SendMessage_ReturnsValue()
|
|
{
|
|
Func<string, string> ProcessFunc = (s) => $"Processed({s})";
|
|
|
|
MessagingTestFixture fixture = new MessagingTestFixture();
|
|
|
|
await fixture.RegisterFactoryMapInstances(nameof(ProcessorAgent),
|
|
(id, runtime) => ValueTask.FromResult(new ProcessorAgent(id, runtime, ProcessFunc, string.Empty)));
|
|
|
|
AgentId targetAgent = new AgentId(nameof(ProcessorAgent), Guid.NewGuid().ToString());
|
|
object? maybeResult = await fixture.RunSendTestAsync(targetAgent, new BasicMessage { Content = "1" });
|
|
|
|
maybeResult.Should().NotBeNull()
|
|
.And.BeOfType<BasicMessage>()
|
|
.And.Match<BasicMessage>(m => m.Content == "Processed(1)");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Test_SendMessage_Cancellation()
|
|
{
|
|
MessagingTestFixture fixture = new MessagingTestFixture();
|
|
|
|
await fixture.RegisterFactoryMapInstances(nameof(CancelAgent),
|
|
(id, runtime) => ValueTask.FromResult(new CancelAgent(id, runtime, string.Empty)));
|
|
|
|
AgentId targetAgent = new AgentId(nameof(CancelAgent), Guid.NewGuid().ToString());
|
|
Func<Task> testAction = () => fixture.RunSendTestAsync(targetAgent, new BasicMessage { Content = "1" }).AsTask();
|
|
|
|
// TODO: Do we want to do the unwrap in this case?
|
|
await testAction.Should().ThrowAsync<OperationCanceledException>();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Test_SendMessage_Error()
|
|
{
|
|
MessagingTestFixture fixture = new MessagingTestFixture();
|
|
|
|
await fixture.RegisterFactoryMapInstances(nameof(ErrorAgent),
|
|
(id, runtime) => ValueTask.FromResult(new ErrorAgent(id, runtime, string.Empty)));
|
|
|
|
AgentId targetAgent = new AgentId(nameof(ErrorAgent), Guid.NewGuid().ToString());
|
|
Func<Task> testAction = () => fixture.RunSendTestAsync(targetAgent, new BasicMessage { Content = "1" }).AsTask();
|
|
|
|
(await testAction.Should().ThrowAsync<TargetInvocationException>())
|
|
.WithInnerException<TestException>();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task TesT_SendMessage_FromSendMessageHandler()
|
|
{
|
|
Guid[] targetGuids = [Guid.NewGuid(), Guid.NewGuid()];
|
|
|
|
MessagingTestFixture fixture = new MessagingTestFixture();
|
|
|
|
Dictionary<AgentId, SendOnAgent> sendAgents = fixture.GetAgentInstances<SendOnAgent>();
|
|
Dictionary<AgentId, ReceiverAgent> receiverAgents = fixture.GetAgentInstances<ReceiverAgent>();
|
|
|
|
await fixture.RegisterFactoryMapInstances(nameof(SendOnAgent),
|
|
(id, runtime) => ValueTask.FromResult(new SendOnAgent(id, runtime, string.Empty, targetGuids)));
|
|
|
|
await fixture.RegisterFactoryMapInstances(nameof(ReceiverAgent),
|
|
(id, runtime) => ValueTask.FromResult(new ReceiverAgent(id, runtime, string.Empty)));
|
|
|
|
const string HelloContent = "Hello";
|
|
AgentId targetAgent = new AgentId(nameof(SendOnAgent), Guid.NewGuid().ToString());
|
|
Task testTask = fixture.RunSendTestAsync(targetAgent, new BasicMessage { Content = HelloContent }).AsTask();
|
|
|
|
// We do not actually expect to wait the timeout here, but it is still better than waiting the 10 min
|
|
// timeout that the tests default to. A failure will fail regardless of what timeout value we set.
|
|
TimeSpan timeout = Debugger.IsAttached ? TimeSpan.FromSeconds(120) : TimeSpan.FromSeconds(10);
|
|
Task timeoutTask = Task.Delay(timeout);
|
|
|
|
Task completedTask = await Task.WhenAny([testTask, timeoutTask]);
|
|
completedTask.Should().Be(testTask, "SendOnAgent should complete before timeout");
|
|
|
|
// Check that each of the target agents received the message
|
|
foreach (var targetKey in targetGuids)
|
|
{
|
|
var targetId = new AgentId(nameof(ReceiverAgent), targetKey.ToString());
|
|
receiverAgents[targetId].Messages.Should().ContainSingle(m => m.Content == $"@{targetKey}: {HelloContent}");
|
|
}
|
|
}
|
|
}
|