// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using A2A;
using Microsoft.Extensions.AI;
using Moq;
using Moq.Protected;
namespace Microsoft.Agents.AI.Hosting.A2A.UnitTests;
///
/// Unit tests for the class.
///
public sealed class AIAgentExtensionsTests
{
///
/// Verifies that when messageSendParams.Metadata is null, the options passed to RunAsync are null.
///
[Fact]
public async Task MapA2A_WhenMetadataIsNull_PassesNullOptionsToRunAsync()
{
// Arrange
AgentRunOptions? capturedOptions = null;
ITaskManager taskManager = CreateAgentMock(options => capturedOptions = options).Object.MapA2A();
// Act
await InvokeOnMessageReceivedAsync(taskManager, new MessageSendParams
{
Message = new AgentMessage { MessageId = "test-id", Role = MessageRole.User, Parts = [new TextPart { Text = "Hello" }] },
Metadata = null
});
// Assert
Assert.Null(capturedOptions);
}
///
/// Verifies that when messageSendParams.Metadata has values, the options.AdditionalProperties contains the converted values.
///
[Fact]
public async Task MapA2A_WhenMetadataHasValues_PassesOptionsWithAdditionalPropertiesToRunAsync()
{
// Arrange
AgentRunOptions? capturedOptions = null;
ITaskManager taskManager = CreateAgentMock(options => capturedOptions = options).Object.MapA2A();
// Act
await InvokeOnMessageReceivedAsync(taskManager, new MessageSendParams
{
Message = new AgentMessage { MessageId = "test-id", Role = MessageRole.User, Parts = [new TextPart { Text = "Hello" }] },
Metadata = new Dictionary
{
["key1"] = JsonSerializer.SerializeToElement("value1"),
["key2"] = JsonSerializer.SerializeToElement(42)
}
});
// Assert
Assert.NotNull(capturedOptions);
Assert.NotNull(capturedOptions.AdditionalProperties);
Assert.Equal(2, capturedOptions.AdditionalProperties.Count);
Assert.True(capturedOptions.AdditionalProperties.ContainsKey("key1"));
Assert.True(capturedOptions.AdditionalProperties.ContainsKey("key2"));
}
///
/// Verifies that when messageSendParams.Metadata is an empty dictionary, the options passed to RunAsync is null
/// because the ToAdditionalProperties extension method returns null for empty dictionaries.
///
[Fact]
public async Task MapA2A_WhenMetadataIsEmptyDictionary_PassesNullOptionsToRunAsync()
{
// Arrange
AgentRunOptions? capturedOptions = null;
ITaskManager taskManager = CreateAgentMock(options => capturedOptions = options).Object.MapA2A();
// Act
await InvokeOnMessageReceivedAsync(taskManager, new MessageSendParams
{
Message = new AgentMessage { MessageId = "test-id", Role = MessageRole.User, Parts = [new TextPart { Text = "Hello" }] },
Metadata = []
});
// Assert
Assert.Null(capturedOptions);
}
///
/// Verifies that when the agent response has AdditionalProperties, the returned AgentMessage.Metadata contains the converted values.
///
[Fact]
public async Task MapA2A_WhenResponseHasAdditionalProperties_ReturnsAgentMessageWithMetadataAsync()
{
// Arrange
AdditionalPropertiesDictionary additionalProps = new()
{
["responseKey1"] = "responseValue1",
["responseKey2"] = 123
};
AgentResponse response = new([new ChatMessage(ChatRole.Assistant, "Test response")])
{
AdditionalProperties = additionalProps
};
ITaskManager taskManager = CreateAgentMockWithResponse(response).Object.MapA2A();
// Act
A2AResponse a2aResponse = await InvokeOnMessageReceivedAsync(taskManager, new MessageSendParams
{
Message = new AgentMessage { MessageId = "test-id", Role = MessageRole.User, Parts = [new TextPart { Text = "Hello" }] }
});
// Assert
AgentMessage agentMessage = Assert.IsType(a2aResponse);
Assert.NotNull(agentMessage.Metadata);
Assert.Equal(2, agentMessage.Metadata.Count);
Assert.True(agentMessage.Metadata.ContainsKey("responseKey1"));
Assert.True(agentMessage.Metadata.ContainsKey("responseKey2"));
Assert.Equal("responseValue1", agentMessage.Metadata["responseKey1"].GetString());
Assert.Equal(123, agentMessage.Metadata["responseKey2"].GetInt32());
}
///
/// Verifies that when the agent response has null AdditionalProperties, the returned AgentMessage.Metadata is null.
///
[Fact]
public async Task MapA2A_WhenResponseHasNullAdditionalProperties_ReturnsAgentMessageWithNullMetadataAsync()
{
// Arrange
AgentResponse response = new([new ChatMessage(ChatRole.Assistant, "Test response")])
{
AdditionalProperties = null
};
ITaskManager taskManager = CreateAgentMockWithResponse(response).Object.MapA2A();
// Act
A2AResponse a2aResponse = await InvokeOnMessageReceivedAsync(taskManager, new MessageSendParams
{
Message = new AgentMessage { MessageId = "test-id", Role = MessageRole.User, Parts = [new TextPart { Text = "Hello" }] }
});
// Assert
AgentMessage agentMessage = Assert.IsType(a2aResponse);
Assert.Null(agentMessage.Metadata);
}
///
/// Verifies that when the agent response has empty AdditionalProperties, the returned AgentMessage.Metadata is null.
///
[Fact]
public async Task MapA2A_WhenResponseHasEmptyAdditionalProperties_ReturnsAgentMessageWithNullMetadataAsync()
{
// Arrange
AgentResponse response = new([new ChatMessage(ChatRole.Assistant, "Test response")])
{
AdditionalProperties = []
};
ITaskManager taskManager = CreateAgentMockWithResponse(response).Object.MapA2A();
// Act
A2AResponse a2aResponse = await InvokeOnMessageReceivedAsync(taskManager, new MessageSendParams
{
Message = new AgentMessage { MessageId = "test-id", Role = MessageRole.User, Parts = [new TextPart { Text = "Hello" }] }
});
// Assert
AgentMessage agentMessage = Assert.IsType(a2aResponse);
Assert.Null(agentMessage.Metadata);
}
private static Mock CreateAgentMock(Action optionsCallback)
{
Mock agentMock = new() { CallBase = true };
agentMock.SetupGet(x => x.Name).Returns("TestAgent");
agentMock.Setup(x => x.GetNewThreadAsync()).ReturnsAsync(new TestAgentThread());
agentMock
.Protected()
.Setup>("RunCoreAsync",
ItExpr.IsAny>(),
ItExpr.IsAny(),
ItExpr.IsAny(),
ItExpr.IsAny())
.Callback, AgentThread?, AgentRunOptions?, CancellationToken>(
(_, _, options, _) => optionsCallback(options))
.ReturnsAsync(new AgentResponse([new ChatMessage(ChatRole.Assistant, "Test response")]));
return agentMock;
}
private static Mock CreateAgentMockWithResponse(AgentResponse response)
{
Mock agentMock = new() { CallBase = true };
agentMock.SetupGet(x => x.Name).Returns("TestAgent");
agentMock.Setup(x => x.GetNewThreadAsync()).ReturnsAsync(new TestAgentThread());
agentMock
.Protected()
.Setup>("RunCoreAsync",
ItExpr.IsAny>(),
ItExpr.IsAny(),
ItExpr.IsAny(),
ItExpr.IsAny())
.ReturnsAsync(response);
return agentMock;
}
private static async Task InvokeOnMessageReceivedAsync(ITaskManager taskManager, MessageSendParams messageSendParams)
{
Func>? handler = taskManager.OnMessageReceived;
Assert.NotNull(handler);
return await handler.Invoke(messageSendParams, CancellationToken.None);
}
private sealed class TestAgentThread : AgentThread;
}