test
Some checks failed
CodeQL / Analyze (csharp) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
dotnet-build-and-test / paths-filter (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test (Debug, windows-latest, net9.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test (Release, integration, true, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test (Release, integration, true, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test (Release, ubuntu-latest, net8.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test-check (push) Has been cancelled
Python - Merge - Tests / paths-filter (push) Has been cancelled
Python - Merge - Tests / Python Tests - Core (integration, ubuntu-latest, 3.10) (push) Has been cancelled
Python - Merge - Tests / Python Tests - Azure AI (integration, ubuntu-latest, 3.10) (push) Has been cancelled
Python - Merge - Tests / python-integration-tests-check (push) Has been cancelled
Python - Lab Tests / paths-filter (push) Has been cancelled
Python - Lab Tests / Python Lab Tests (ubuntu-latest, 3.10) (push) Has been cancelled
Python - Lab Tests / Python Lab Tests (ubuntu-latest, 3.11) (push) Has been cancelled
Python - Lab Tests / Python Lab Tests (ubuntu-latest, 3.12) (push) Has been cancelled
Python - Lab Tests / Python Lab Tests (ubuntu-latest, 3.13) (push) Has been cancelled
Python - Lab Tests / Python Lab Tests (ubuntu-latest, 3.14) (push) Has been cancelled
Python - Lab Tests / Python Lab Tests (windows-latest, 3.10) (push) Has been cancelled
Python - Lab Tests / Python Lab Tests (windows-latest, 3.11) (push) Has been cancelled
Python - Lab Tests / Python Lab Tests (windows-latest, 3.12) (push) Has been cancelled
Python - Lab Tests / Python Lab Tests (windows-latest, 3.13) (push) Has been cancelled
Python - Lab Tests / Python Lab Tests (windows-latest, 3.14) (push) Has been cancelled
Check .md links / markdown-link-check (push) Has been cancelled
Some checks failed
CodeQL / Analyze (csharp) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
dotnet-build-and-test / paths-filter (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test (Debug, windows-latest, net9.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test (Release, integration, true, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test (Release, integration, true, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test (Release, ubuntu-latest, net8.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test-check (push) Has been cancelled
Python - Merge - Tests / paths-filter (push) Has been cancelled
Python - Merge - Tests / Python Tests - Core (integration, ubuntu-latest, 3.10) (push) Has been cancelled
Python - Merge - Tests / Python Tests - Azure AI (integration, ubuntu-latest, 3.10) (push) Has been cancelled
Python - Merge - Tests / python-integration-tests-check (push) Has been cancelled
Python - Lab Tests / paths-filter (push) Has been cancelled
Python - Lab Tests / Python Lab Tests (ubuntu-latest, 3.10) (push) Has been cancelled
Python - Lab Tests / Python Lab Tests (ubuntu-latest, 3.11) (push) Has been cancelled
Python - Lab Tests / Python Lab Tests (ubuntu-latest, 3.12) (push) Has been cancelled
Python - Lab Tests / Python Lab Tests (ubuntu-latest, 3.13) (push) Has been cancelled
Python - Lab Tests / Python Lab Tests (ubuntu-latest, 3.14) (push) Has been cancelled
Python - Lab Tests / Python Lab Tests (windows-latest, 3.10) (push) Has been cancelled
Python - Lab Tests / Python Lab Tests (windows-latest, 3.11) (push) Has been cancelled
Python - Lab Tests / Python Lab Tests (windows-latest, 3.12) (push) Has been cancelled
Python - Lab Tests / Python Lab Tests (windows-latest, 3.13) (push) Has been cancelled
Python - Lab Tests / Python Lab Tests (windows-latest, 3.14) (push) Has been cancelled
Check .md links / markdown-link-check (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using A2A;
|
||||
using Microsoft.Agents.AI.Hosting.A2A.UnitTests.Internal;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting.Server;
|
||||
using Microsoft.AspNetCore.TestHost;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Microsoft.Agents.AI.Hosting.A2A.UnitTests;
|
||||
|
||||
public sealed class A2AIntegrationTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Verifies that calling the A2A card endpoint with MapA2A returns an agent card with a URL populated.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task MapA2A_WithAgentCard_CardEndpointReturnsCardWithUrlAsync()
|
||||
{
|
||||
// Arrange
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
builder.WebHost.UseTestServer();
|
||||
|
||||
IChatClient mockChatClient = new DummyChatClient();
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
IHostedAgentBuilder agentBuilder = builder.AddAIAgent("test-agent", "Test instructions", chatClientServiceKey: "chat-client");
|
||||
builder.Services.AddLogging();
|
||||
|
||||
using WebApplication app = builder.Build();
|
||||
|
||||
var agentCard = new AgentCard
|
||||
{
|
||||
Name = "Test Agent",
|
||||
Description = "A test agent for A2A communication",
|
||||
Version = "1.0"
|
||||
};
|
||||
|
||||
// Map A2A with the agent card
|
||||
app.MapA2A(agentBuilder, "/a2a/test-agent", agentCard);
|
||||
|
||||
await app.StartAsync();
|
||||
|
||||
try
|
||||
{
|
||||
// Get the test server client
|
||||
TestServer testServer = app.Services.GetRequiredService<IServer>() as TestServer
|
||||
?? throw new InvalidOperationException("TestServer not found");
|
||||
var httpClient = testServer.CreateClient();
|
||||
|
||||
// Act - Query the agent card endpoint
|
||||
var requestUri = new Uri("/a2a/test-agent/v1/card", UriKind.Relative);
|
||||
var response = await httpClient.GetAsync(requestUri);
|
||||
|
||||
// Assert
|
||||
Assert.True(response.IsSuccessStatusCode, $"Expected successful response but got {response.StatusCode}");
|
||||
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
var jsonDoc = JsonDocument.Parse(content);
|
||||
var root = jsonDoc.RootElement;
|
||||
|
||||
// Verify the card has expected properties
|
||||
Assert.True(root.TryGetProperty("name", out var nameProperty));
|
||||
Assert.Equal("Test Agent", nameProperty.GetString());
|
||||
|
||||
Assert.True(root.TryGetProperty("description", out var descProperty));
|
||||
Assert.Equal("A test agent for A2A communication", descProperty.GetString());
|
||||
|
||||
// Verify the card has a URL property and it's not null/empty
|
||||
Assert.True(root.TryGetProperty("url", out var urlProperty));
|
||||
Assert.NotEqual(JsonValueKind.Null, urlProperty.ValueKind);
|
||||
|
||||
var url = urlProperty.GetString();
|
||||
Assert.NotNull(url);
|
||||
Assert.NotEmpty(url);
|
||||
Assert.StartsWith("http", url, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
// agentCard's URL matches the agent endpoint
|
||||
Assert.Equal($"{testServer.BaseAddress.ToString().TrimEnd('/')}/a2a/test-agent", url);
|
||||
}
|
||||
finally
|
||||
{
|
||||
await app.StopAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
// 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;
|
||||
|
||||
/// <summary>
|
||||
/// Unit tests for the <see cref="AIAgentExtensions"/> class.
|
||||
/// </summary>
|
||||
public sealed class AIAgentExtensionsTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Verifies that when messageSendParams.Metadata is null, the options passed to RunAsync are null.
|
||||
/// </summary>
|
||||
[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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that when messageSendParams.Metadata has values, the options.AdditionalProperties contains the converted values.
|
||||
/// </summary>
|
||||
[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<string, JsonElement>
|
||||
{
|
||||
["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"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
[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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that when the agent response has AdditionalProperties, the returned AgentMessage.Metadata contains the converted values.
|
||||
/// </summary>
|
||||
[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<AgentMessage>(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());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that when the agent response has null AdditionalProperties, the returned AgentMessage.Metadata is null.
|
||||
/// </summary>
|
||||
[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<AgentMessage>(a2aResponse);
|
||||
Assert.Null(agentMessage.Metadata);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that when the agent response has empty AdditionalProperties, the returned AgentMessage.Metadata is null.
|
||||
/// </summary>
|
||||
[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<AgentMessage>(a2aResponse);
|
||||
Assert.Null(agentMessage.Metadata);
|
||||
}
|
||||
|
||||
private static Mock<AIAgent> CreateAgentMock(Action<AgentRunOptions?> optionsCallback)
|
||||
{
|
||||
Mock<AIAgent> agentMock = new() { CallBase = true };
|
||||
agentMock.SetupGet(x => x.Name).Returns("TestAgent");
|
||||
agentMock.Setup(x => x.GetNewThreadAsync()).ReturnsAsync(new TestAgentThread());
|
||||
agentMock
|
||||
.Protected()
|
||||
.Setup<Task<AgentResponse>>("RunCoreAsync",
|
||||
ItExpr.IsAny<IEnumerable<ChatMessage>>(),
|
||||
ItExpr.IsAny<AgentThread?>(),
|
||||
ItExpr.IsAny<AgentRunOptions?>(),
|
||||
ItExpr.IsAny<CancellationToken>())
|
||||
.Callback<IEnumerable<ChatMessage>, AgentThread?, AgentRunOptions?, CancellationToken>(
|
||||
(_, _, options, _) => optionsCallback(options))
|
||||
.ReturnsAsync(new AgentResponse([new ChatMessage(ChatRole.Assistant, "Test response")]));
|
||||
|
||||
return agentMock;
|
||||
}
|
||||
|
||||
private static Mock<AIAgent> CreateAgentMockWithResponse(AgentResponse response)
|
||||
{
|
||||
Mock<AIAgent> agentMock = new() { CallBase = true };
|
||||
agentMock.SetupGet(x => x.Name).Returns("TestAgent");
|
||||
agentMock.Setup(x => x.GetNewThreadAsync()).ReturnsAsync(new TestAgentThread());
|
||||
agentMock
|
||||
.Protected()
|
||||
.Setup<Task<AgentResponse>>("RunCoreAsync",
|
||||
ItExpr.IsAny<IEnumerable<ChatMessage>>(),
|
||||
ItExpr.IsAny<AgentThread?>(),
|
||||
ItExpr.IsAny<AgentRunOptions?>(),
|
||||
ItExpr.IsAny<CancellationToken>())
|
||||
.ReturnsAsync(response);
|
||||
|
||||
return agentMock;
|
||||
}
|
||||
|
||||
private static async Task<A2AResponse> InvokeOnMessageReceivedAsync(ITaskManager taskManager, MessageSendParams messageSendParams)
|
||||
{
|
||||
Func<MessageSendParams, CancellationToken, Task<A2AResponse>>? handler = taskManager.OnMessageReceived;
|
||||
Assert.NotNull(handler);
|
||||
return await handler.Invoke(messageSendParams, CancellationToken.None);
|
||||
}
|
||||
|
||||
private sealed class TestAgentThread : AgentThread;
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Agents.AI.Hosting.A2A.Converters;
|
||||
using Microsoft.Extensions.AI;
|
||||
|
||||
namespace Microsoft.Agents.AI.Hosting.A2A.UnitTests.Converters;
|
||||
|
||||
/// <summary>
|
||||
/// Unit tests for the <see cref="AdditionalPropertiesDictionaryExtensions"/> class.
|
||||
/// </summary>
|
||||
public sealed class AdditionalPropertiesDictionaryExtensionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void ToA2AMetadata_WithNullAdditionalProperties_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
AdditionalPropertiesDictionary? additionalProperties = null;
|
||||
|
||||
// Act
|
||||
Dictionary<string, JsonElement>? result = additionalProperties.ToA2AMetadata();
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ToA2AMetadata_WithEmptyAdditionalProperties_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
AdditionalPropertiesDictionary additionalProperties = [];
|
||||
|
||||
// Act
|
||||
Dictionary<string, JsonElement>? result = additionalProperties.ToA2AMetadata();
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ToA2AMetadata_WithStringValue_ReturnsMetadataWithJsonElement()
|
||||
{
|
||||
// Arrange
|
||||
AdditionalPropertiesDictionary additionalProperties = new()
|
||||
{
|
||||
{ "stringKey", "stringValue" }
|
||||
};
|
||||
|
||||
// Act
|
||||
Dictionary<string, JsonElement>? result = additionalProperties.ToA2AMetadata();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Single(result);
|
||||
Assert.True(result.ContainsKey("stringKey"));
|
||||
Assert.Equal("stringValue", result["stringKey"].GetString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ToA2AMetadata_WithNumericValue_ReturnsMetadataWithJsonElement()
|
||||
{
|
||||
// Arrange
|
||||
AdditionalPropertiesDictionary additionalProperties = new()
|
||||
{
|
||||
{ "numberKey", 42 }
|
||||
};
|
||||
|
||||
// Act
|
||||
Dictionary<string, JsonElement>? result = additionalProperties.ToA2AMetadata();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Single(result);
|
||||
Assert.True(result.ContainsKey("numberKey"));
|
||||
Assert.Equal(42, result["numberKey"].GetInt32());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ToA2AMetadata_WithBooleanValue_ReturnsMetadataWithJsonElement()
|
||||
{
|
||||
// Arrange
|
||||
AdditionalPropertiesDictionary additionalProperties = new()
|
||||
{
|
||||
{ "booleanKey", true }
|
||||
};
|
||||
|
||||
// Act
|
||||
Dictionary<string, JsonElement>? result = additionalProperties.ToA2AMetadata();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Single(result);
|
||||
Assert.True(result.ContainsKey("booleanKey"));
|
||||
Assert.True(result["booleanKey"].GetBoolean());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ToA2AMetadata_WithMultipleProperties_ReturnsMetadataWithAllProperties()
|
||||
{
|
||||
// Arrange
|
||||
AdditionalPropertiesDictionary additionalProperties = new()
|
||||
{
|
||||
{ "stringKey", "stringValue" },
|
||||
{ "numberKey", 42 },
|
||||
{ "booleanKey", true }
|
||||
};
|
||||
|
||||
// Act
|
||||
Dictionary<string, JsonElement>? result = additionalProperties.ToA2AMetadata();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(3, result.Count);
|
||||
|
||||
Assert.True(result.ContainsKey("stringKey"));
|
||||
Assert.Equal("stringValue", result["stringKey"].GetString());
|
||||
|
||||
Assert.True(result.ContainsKey("numberKey"));
|
||||
Assert.Equal(42, result["numberKey"].GetInt32());
|
||||
|
||||
Assert.True(result.ContainsKey("booleanKey"));
|
||||
Assert.True(result["booleanKey"].GetBoolean());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ToA2AMetadata_WithArrayValue_ReturnsMetadataWithJsonElement()
|
||||
{
|
||||
// Arrange
|
||||
int[] arrayValue = [1, 2, 3];
|
||||
AdditionalPropertiesDictionary additionalProperties = new()
|
||||
{
|
||||
{ "arrayKey", arrayValue }
|
||||
};
|
||||
|
||||
// Act
|
||||
Dictionary<string, JsonElement>? result = additionalProperties.ToA2AMetadata();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Single(result);
|
||||
Assert.True(result.ContainsKey("arrayKey"));
|
||||
Assert.Equal(JsonValueKind.Array, result["arrayKey"].ValueKind);
|
||||
Assert.Equal(3, result["arrayKey"].GetArrayLength());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ToA2AMetadata_WithNullValue_ReturnsMetadataWithNullJsonElement()
|
||||
{
|
||||
// Arrange
|
||||
AdditionalPropertiesDictionary additionalProperties = new()
|
||||
{
|
||||
{ "nullKey", null! }
|
||||
};
|
||||
|
||||
// Act
|
||||
Dictionary<string, JsonElement>? result = additionalProperties.ToA2AMetadata();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Single(result);
|
||||
Assert.True(result.ContainsKey("nullKey"));
|
||||
Assert.Equal(JsonValueKind.Null, result["nullKey"].ValueKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ToA2AMetadata_WithJsonElementValue_ReturnsMetadataWithJsonElement()
|
||||
{
|
||||
// Arrange
|
||||
JsonElement jsonElement = JsonSerializer.SerializeToElement(new { name = "test", value = 123 });
|
||||
AdditionalPropertiesDictionary additionalProperties = new()
|
||||
{
|
||||
{ "jsonElementKey", jsonElement }
|
||||
};
|
||||
|
||||
// Act
|
||||
Dictionary<string, JsonElement>? result = additionalProperties.ToA2AMetadata();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Single(result);
|
||||
Assert.True(result.ContainsKey("jsonElementKey"));
|
||||
Assert.Equal(JsonValueKind.Object, result["jsonElementKey"].ValueKind);
|
||||
Assert.Equal("test", result["jsonElementKey"].GetProperty("name").GetString());
|
||||
Assert.Equal(123, result["jsonElementKey"].GetProperty("value").GetInt32());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Linq;
|
||||
using A2A;
|
||||
using Microsoft.Agents.AI.Hosting.A2A.Converters;
|
||||
using Microsoft.Extensions.AI;
|
||||
|
||||
namespace Microsoft.Agents.AI.Hosting.A2A.UnitTests.Converters;
|
||||
|
||||
public class MessageConverterTests
|
||||
{
|
||||
[Fact]
|
||||
public void ToChatMessages_MessageSendParams_Null_ReturnsEmptyCollection()
|
||||
{
|
||||
MessageSendParams? messageSendParams = null;
|
||||
|
||||
var result = messageSendParams!.ToChatMessages();
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.Empty(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ToChatMessages_MessageSendParams_WithNullMessage_ReturnsEmptyCollection()
|
||||
{
|
||||
var messageSendParams = new MessageSendParams
|
||||
{
|
||||
Message = null!
|
||||
};
|
||||
|
||||
var result = messageSendParams.ToChatMessages();
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.Empty(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ToChatMessages_MessageSendParams_WithMessageWithoutParts_ReturnsEmptyCollection()
|
||||
{
|
||||
var messageSendParams = new MessageSendParams
|
||||
{
|
||||
Message = new AgentMessage
|
||||
{
|
||||
MessageId = "test-id",
|
||||
Role = MessageRole.User,
|
||||
Parts = null!
|
||||
}
|
||||
};
|
||||
|
||||
var result = messageSendParams.ToChatMessages();
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.Empty(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ToChatMessages_MessageSendParams_WithValidTextMessage_ReturnsCorrectChatMessage()
|
||||
{
|
||||
var messageSendParams = new MessageSendParams
|
||||
{
|
||||
Message = new AgentMessage
|
||||
{
|
||||
MessageId = "test-id",
|
||||
Role = MessageRole.User,
|
||||
Parts =
|
||||
[
|
||||
new TextPart { Text = "Hello, world!" }
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
var result = messageSendParams.ToChatMessages();
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.Single(result);
|
||||
|
||||
var chatMessage = result.First();
|
||||
Assert.Equal("test-id", chatMessage.MessageId);
|
||||
Assert.Equal(ChatRole.User, chatMessage.Role);
|
||||
Assert.Single(chatMessage.Contents);
|
||||
|
||||
var textContent = Assert.IsType<TextContent>(chatMessage.Contents.First());
|
||||
Assert.Equal("Hello, world!", textContent.Text);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,479 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using A2A;
|
||||
using Microsoft.Agents.AI.Hosting.A2A.UnitTests.Internal;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Microsoft.Agents.AI.Hosting.A2A.UnitTests;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for MicrosoftAgentAIHostingA2AEndpointRouteBuilderExtensions.MapA2A method.
|
||||
/// </summary>
|
||||
public sealed class EndpointRouteA2ABuilderExtensionsTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Verifies that MapA2A throws ArgumentNullException for null endpoints.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MapA2A_WithAgentBuilder_NullEndpoints_ThrowsArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
AspNetCore.Routing.IEndpointRouteBuilder endpoints = null!;
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
IChatClient mockChatClient = new DummyChatClient();
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
IHostedAgentBuilder agentBuilder = builder.AddAIAgent("agent", "Instructions", chatClientServiceKey: "chat-client");
|
||||
|
||||
// Act & Assert
|
||||
ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() =>
|
||||
endpoints.MapA2A(agentBuilder, "/a2a"));
|
||||
|
||||
Assert.Equal("endpoints", exception.ParamName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that MapA2A throws ArgumentNullException for null agentBuilder.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MapA2A_WithAgentBuilder_NullAgentBuilder_ThrowsArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
IChatClient mockChatClient = new DummyChatClient();
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
builder.AddAIAgent("agent", "Instructions", chatClientServiceKey: "chat-client");
|
||||
builder.Services.AddLogging();
|
||||
using WebApplication app = builder.Build();
|
||||
IHostedAgentBuilder agentBuilder = null!;
|
||||
|
||||
// Act & Assert
|
||||
ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() =>
|
||||
app.MapA2A(agentBuilder, "/a2a"));
|
||||
|
||||
Assert.Equal("agentBuilder", exception.ParamName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that MapA2A with IHostedAgentBuilder correctly maps the agent with default task manager configuration.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MapA2A_WithAgentBuilder_DefaultConfiguration_Succeeds()
|
||||
{
|
||||
// Arrange
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
IChatClient mockChatClient = new DummyChatClient();
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
IHostedAgentBuilder agentBuilder = builder.AddAIAgent("agent", "Instructions", chatClientServiceKey: "chat-client");
|
||||
builder.Services.AddLogging();
|
||||
using WebApplication app = builder.Build();
|
||||
|
||||
// Act & Assert - Should not throw
|
||||
var result = app.MapA2A(agentBuilder, "/a2a");
|
||||
Assert.NotNull(result);
|
||||
Assert.NotNull(app);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that MapA2A with IHostedAgentBuilder and custom task manager configuration succeeds.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MapA2A_WithAgentBuilder_CustomTaskManagerConfiguration_Succeeds()
|
||||
{
|
||||
// Arrange
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
IChatClient mockChatClient = new DummyChatClient();
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
IHostedAgentBuilder agentBuilder = builder.AddAIAgent("agent", "Instructions", chatClientServiceKey: "chat-client");
|
||||
builder.Services.AddLogging();
|
||||
using WebApplication app = builder.Build();
|
||||
|
||||
// Act & Assert - Should not throw
|
||||
var result = app.MapA2A(agentBuilder, "/a2a", taskManager => { });
|
||||
Assert.NotNull(result);
|
||||
Assert.NotNull(app);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that MapA2A with IHostedAgentBuilder and agent card succeeds.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MapA2A_WithAgentBuilder_WithAgentCard_Succeeds()
|
||||
{
|
||||
// Arrange
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
IChatClient mockChatClient = new DummyChatClient();
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
IHostedAgentBuilder agentBuilder = builder.AddAIAgent("agent", "Instructions", chatClientServiceKey: "chat-client");
|
||||
builder.Services.AddLogging();
|
||||
using WebApplication app = builder.Build();
|
||||
|
||||
var agentCard = new AgentCard
|
||||
{
|
||||
Name = "Test Agent",
|
||||
Description = "A test agent for A2A communication"
|
||||
};
|
||||
|
||||
// Act & Assert - Should not throw
|
||||
var result = app.MapA2A(agentBuilder, "/a2a", agentCard);
|
||||
Assert.NotNull(result);
|
||||
Assert.NotNull(app);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that MapA2A with IHostedAgentBuilder, agent card, and custom task manager configuration succeeds.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MapA2A_WithAgentBuilder_WithAgentCardAndCustomConfiguration_Succeeds()
|
||||
{
|
||||
// Arrange
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
IChatClient mockChatClient = new DummyChatClient();
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
IHostedAgentBuilder agentBuilder = builder.AddAIAgent("agent", "Instructions", chatClientServiceKey: "chat-client");
|
||||
builder.Services.AddLogging();
|
||||
using WebApplication app = builder.Build();
|
||||
|
||||
var agentCard = new AgentCard
|
||||
{
|
||||
Name = "Test Agent",
|
||||
Description = "A test agent for A2A communication"
|
||||
};
|
||||
|
||||
// Act & Assert - Should not throw
|
||||
var result = app.MapA2A(agentBuilder, "/a2a", agentCard, taskManager => { });
|
||||
Assert.NotNull(result);
|
||||
Assert.NotNull(app);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that MapA2A throws ArgumentNullException for null endpoints when using string agent name.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MapA2A_WithAgentName_NullEndpoints_ThrowsArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
AspNetCore.Routing.IEndpointRouteBuilder endpoints = null!;
|
||||
|
||||
// Act & Assert
|
||||
ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() =>
|
||||
endpoints.MapA2A("agent", "/a2a"));
|
||||
|
||||
Assert.Equal("endpoints", exception.ParamName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that MapA2A with string agent name correctly maps the agent.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MapA2A_WithAgentName_DefaultConfiguration_Succeeds()
|
||||
{
|
||||
// Arrange
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
IChatClient mockChatClient = new DummyChatClient();
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
builder.AddAIAgent("agent", "Instructions", chatClientServiceKey: "chat-client");
|
||||
builder.Services.AddLogging();
|
||||
using WebApplication app = builder.Build();
|
||||
|
||||
// Act & Assert - Should not throw
|
||||
var result = app.MapA2A("agent", "/a2a");
|
||||
Assert.NotNull(result);
|
||||
Assert.NotNull(app);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that MapA2A with string agent name and custom task manager configuration succeeds.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MapA2A_WithAgentName_CustomTaskManagerConfiguration_Succeeds()
|
||||
{
|
||||
// Arrange
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
IChatClient mockChatClient = new DummyChatClient();
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
builder.AddAIAgent("agent", "Instructions", chatClientServiceKey: "chat-client");
|
||||
builder.Services.AddLogging();
|
||||
using WebApplication app = builder.Build();
|
||||
|
||||
// Act & Assert - Should not throw
|
||||
var result = app.MapA2A("agent", "/a2a", taskManager => { });
|
||||
Assert.NotNull(result);
|
||||
Assert.NotNull(app);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that MapA2A with string agent name and agent card succeeds.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MapA2A_WithAgentName_WithAgentCard_Succeeds()
|
||||
{
|
||||
// Arrange
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
IChatClient mockChatClient = new DummyChatClient();
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
builder.AddAIAgent("agent", "Instructions", chatClientServiceKey: "chat-client");
|
||||
builder.Services.AddLogging();
|
||||
using WebApplication app = builder.Build();
|
||||
|
||||
var agentCard = new AgentCard
|
||||
{
|
||||
Name = "Test Agent",
|
||||
Description = "A test agent for A2A communication"
|
||||
};
|
||||
|
||||
// Act & Assert - Should not throw
|
||||
var result = app.MapA2A("agent", "/a2a", agentCard);
|
||||
Assert.NotNull(result);
|
||||
Assert.NotNull(app);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that MapA2A with string agent name, agent card, and custom task manager configuration succeeds.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MapA2A_WithAgentName_WithAgentCardAndCustomConfiguration_Succeeds()
|
||||
{
|
||||
// Arrange
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
IChatClient mockChatClient = new DummyChatClient();
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
builder.AddAIAgent("agent", "Instructions", chatClientServiceKey: "chat-client");
|
||||
builder.Services.AddLogging();
|
||||
using WebApplication app = builder.Build();
|
||||
|
||||
var agentCard = new AgentCard
|
||||
{
|
||||
Name = "Test Agent",
|
||||
Description = "A test agent for A2A communication"
|
||||
};
|
||||
|
||||
// Act & Assert - Should not throw
|
||||
var result = app.MapA2A("agent", "/a2a", agentCard, taskManager => { });
|
||||
Assert.NotNull(result);
|
||||
Assert.NotNull(app);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that MapA2A throws ArgumentNullException for null endpoints when using AIAgent.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MapA2A_WithAIAgent_NullEndpoints_ThrowsArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
AspNetCore.Routing.IEndpointRouteBuilder endpoints = null!;
|
||||
|
||||
// Act & Assert
|
||||
ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() =>
|
||||
endpoints.MapA2A((AIAgent)null!, "/a2a"));
|
||||
|
||||
Assert.Equal("endpoints", exception.ParamName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that MapA2A with AIAgent correctly maps the agent.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MapA2A_WithAIAgent_DefaultConfiguration_Succeeds()
|
||||
{
|
||||
// Arrange
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
IChatClient mockChatClient = new DummyChatClient();
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
builder.AddAIAgent("agent", "Instructions", chatClientServiceKey: "chat-client");
|
||||
builder.Services.AddLogging();
|
||||
using WebApplication app = builder.Build();
|
||||
AIAgent agent = app.Services.GetRequiredKeyedService<AIAgent>("agent");
|
||||
|
||||
// Act & Assert - Should not throw
|
||||
var result = app.MapA2A(agent, "/a2a");
|
||||
Assert.NotNull(result);
|
||||
Assert.NotNull(app);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that MapA2A with AIAgent and custom task manager configuration succeeds.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MapA2A_WithAIAgent_CustomTaskManagerConfiguration_Succeeds()
|
||||
{
|
||||
// Arrange
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
IChatClient mockChatClient = new DummyChatClient();
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
builder.AddAIAgent("agent", "Instructions", chatClientServiceKey: "chat-client");
|
||||
builder.Services.AddLogging();
|
||||
using WebApplication app = builder.Build();
|
||||
AIAgent agent = app.Services.GetRequiredKeyedService<AIAgent>("agent");
|
||||
|
||||
// Act & Assert - Should not throw
|
||||
var result = app.MapA2A(agent, "/a2a", taskManager => { });
|
||||
Assert.NotNull(result);
|
||||
Assert.NotNull(app);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that MapA2A with AIAgent and agent card succeeds.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MapA2A_WithAIAgent_WithAgentCard_Succeeds()
|
||||
{
|
||||
// Arrange
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
IChatClient mockChatClient = new DummyChatClient();
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
builder.AddAIAgent("agent", "Instructions", chatClientServiceKey: "chat-client");
|
||||
builder.Services.AddLogging();
|
||||
using WebApplication app = builder.Build();
|
||||
AIAgent agent = app.Services.GetRequiredKeyedService<AIAgent>("agent");
|
||||
|
||||
var agentCard = new AgentCard
|
||||
{
|
||||
Name = "Test Agent",
|
||||
Description = "A test agent for A2A communication"
|
||||
};
|
||||
|
||||
// Act & Assert - Should not throw
|
||||
var result = app.MapA2A(agent, "/a2a", agentCard);
|
||||
Assert.NotNull(result);
|
||||
Assert.NotNull(app);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that MapA2A with AIAgent, agent card, and custom task manager configuration succeeds.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MapA2A_WithAIAgent_WithAgentCardAndCustomConfiguration_Succeeds()
|
||||
{
|
||||
// Arrange
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
IChatClient mockChatClient = new DummyChatClient();
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
builder.AddAIAgent("agent", "Instructions", chatClientServiceKey: "chat-client");
|
||||
builder.Services.AddLogging();
|
||||
using WebApplication app = builder.Build();
|
||||
AIAgent agent = app.Services.GetRequiredKeyedService<AIAgent>("agent");
|
||||
|
||||
var agentCard = new AgentCard
|
||||
{
|
||||
Name = "Test Agent",
|
||||
Description = "A test agent for A2A communication"
|
||||
};
|
||||
|
||||
// Act & Assert - Should not throw
|
||||
var result = app.MapA2A(agent, "/a2a", agentCard, taskManager => { });
|
||||
Assert.NotNull(result);
|
||||
Assert.NotNull(app);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that MapA2A throws ArgumentNullException for null endpoints when using ITaskManager.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MapA2A_WithTaskManager_NullEndpoints_ThrowsArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
AspNetCore.Routing.IEndpointRouteBuilder endpoints = null!;
|
||||
ITaskManager taskManager = null!;
|
||||
|
||||
// Act & Assert
|
||||
ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() =>
|
||||
endpoints.MapA2A(taskManager, "/a2a"));
|
||||
|
||||
Assert.Equal("endpoints", exception.ParamName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that multiple agents can be mapped to different paths.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MapA2A_MultipleAgents_Succeeds()
|
||||
{
|
||||
// Arrange
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
IChatClient mockChatClient = new DummyChatClient();
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
IHostedAgentBuilder agent1Builder = builder.AddAIAgent("agent1", "Instructions1", chatClientServiceKey: "chat-client");
|
||||
IHostedAgentBuilder agent2Builder = builder.AddAIAgent("agent2", "Instructions2", chatClientServiceKey: "chat-client");
|
||||
builder.Services.AddLogging();
|
||||
using WebApplication app = builder.Build();
|
||||
|
||||
// Act & Assert - Should not throw
|
||||
app.MapA2A(agent1Builder, "/a2a/agent1");
|
||||
app.MapA2A(agent2Builder, "/a2a/agent2");
|
||||
Assert.NotNull(app);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that custom paths can be specified for A2A endpoints.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MapA2A_WithCustomPath_AcceptsValidPath()
|
||||
{
|
||||
// Arrange
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
IChatClient mockChatClient = new DummyChatClient();
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
IHostedAgentBuilder agentBuilder = builder.AddAIAgent("agent", "Instructions", chatClientServiceKey: "chat-client");
|
||||
builder.Services.AddLogging();
|
||||
using WebApplication app = builder.Build();
|
||||
|
||||
// Act & Assert - Should not throw
|
||||
app.MapA2A(agentBuilder, "/custom/a2a/path");
|
||||
Assert.NotNull(app);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that task manager configuration callback is invoked correctly.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MapA2A_WithAgentBuilder_TaskManagerConfigurationCallbackInvoked()
|
||||
{
|
||||
// Arrange
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
IChatClient mockChatClient = new DummyChatClient();
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
IHostedAgentBuilder agentBuilder = builder.AddAIAgent("agent", "Instructions", chatClientServiceKey: "chat-client");
|
||||
builder.Services.AddLogging();
|
||||
using WebApplication app = builder.Build();
|
||||
|
||||
bool configureCallbackInvoked = false;
|
||||
|
||||
// Act
|
||||
app.MapA2A(agentBuilder, "/a2a", taskManager =>
|
||||
{
|
||||
configureCallbackInvoked = true;
|
||||
Assert.NotNull(taskManager);
|
||||
});
|
||||
|
||||
// Assert
|
||||
Assert.True(configureCallbackInvoked);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that agent card with all properties is accepted.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MapA2A_WithAgentBuilder_FullAgentCard_Succeeds()
|
||||
{
|
||||
// Arrange
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
IChatClient mockChatClient = new DummyChatClient();
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
IHostedAgentBuilder agentBuilder = builder.AddAIAgent("agent", "Instructions", chatClientServiceKey: "chat-client");
|
||||
builder.Services.AddLogging();
|
||||
using WebApplication app = builder.Build();
|
||||
|
||||
var agentCard = new AgentCard
|
||||
{
|
||||
Name = "Test Agent",
|
||||
Description = "A comprehensive test agent"
|
||||
};
|
||||
|
||||
// Act & Assert - Should not throw
|
||||
var result = app.MapA2A(agentBuilder, "/a2a", agentCard);
|
||||
Assert.NotNull(result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.AI;
|
||||
|
||||
namespace Microsoft.Agents.AI.Hosting.A2A.UnitTests.Internal;
|
||||
|
||||
internal sealed class DummyChatClient : IChatClient
|
||||
{
|
||||
public void Dispose()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<ChatResponse> GetResponseAsync(IEnumerable<ChatMessage> messages, ChatOptions? options = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public object? GetService(Type serviceType, object? serviceKey = null) =>
|
||||
serviceType.IsInstanceOfType(this) ? this : null;
|
||||
|
||||
public IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(IEnumerable<ChatMessage> messages, ChatOptions? options = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>$(TargetFrameworksCore)</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.TestHost" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="!$([MSBuild]::IsTargetFrameworkCompatible($(TargetFramework), 'net10.0'))">
|
||||
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" />
|
||||
<PackageReference Include="System.Linq.AsyncEnumerable" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Microsoft.Agents.AI.Hosting.A2A.AspNetCore\Microsoft.Agents.AI.Hosting.A2A.AspNetCore.csproj" />
|
||||
<ProjectReference Include="..\..\src\Microsoft.Agents.AI.Hosting.A2A\Microsoft.Agents.AI.Hosting.A2A.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user