// Copyright (c) Microsoft. All rights reserved. using System; using System.Collections; using System.Collections.Generic; using System.Net; using System.Net.Http; using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using A2A; namespace Microsoft.Agents.AI.A2A.UnitTests; /// /// Unit tests for the class. /// public sealed class A2ACardResolverExtensionsTests : IDisposable { private readonly HttpClient _httpClient; private readonly HttpMessageHandlerStub _handler; private readonly A2ACardResolver _resolver; public A2ACardResolverExtensionsTests() { this._handler = new HttpMessageHandlerStub(); this._httpClient = new HttpClient(this._handler, false); this._resolver = new A2ACardResolver(new Uri("http://test-host"), httpClient: this._httpClient); } [Fact] public async Task GetAIAgentAsync_WithValidAgentCard_ReturnsAIAgentAsync() { // Arrange this._handler.ResponsesToReturn.Enqueue(new AgentCard { Name = "Test Agent", Description = "A test agent for unit testing", SupportedInterfaces = [new AgentInterface { Url = "http://test-endpoint/agent" }] }); // Act var agent = await this._resolver.GetAIAgentAsync(); // Assert Assert.NotNull(agent); Assert.IsType(agent); Assert.Equal("Test Agent", agent.Name); Assert.Equal("A test agent for unit testing", agent.Description); // Verify that there was only one request made to retrieve the agent card Assert.Single(this._handler.CapturedUris); Assert.StartsWith("http://test-host/", this._handler.CapturedUris[0].ToString()); } [Fact] public async Task RunIAgentAsync_WithUrlFromAgentCard_SendsRequestToTheUrlAsync() { // Arrange this._handler.ResponsesToReturn.Enqueue(new AgentCard { SupportedInterfaces = [new AgentInterface { Url = "http://test-endpoint/agent" }] }); this._handler.ResponsesToReturn.Enqueue(new Message { Role = Role.Agent, Parts = [Part.FromText("Response")], }); var agent = await this._resolver.GetAIAgentAsync(httpClient: this._httpClient); // Act await agent.RunAsync("Test input"); // Assert Assert.Equal(2, this._handler.CapturedUris.Count); // One for getting the card, one for sending the message to the agent Assert.Equal(new Uri("http://test-endpoint/agent"), this._handler.CapturedUris[1]); } [Fact] public async Task GetAIAgentAsync_WithOptions_PassesOptionsToFactoryAsync() { // Arrange this._handler.ResponsesToReturn.Enqueue(new AgentCard { Name = "Options Agent", Description = "Agent with multiple interfaces", SupportedInterfaces = [ new AgentInterface { Url = "http://httpjson/agent", ProtocolBinding = ProtocolBindingNames.HttpJson }, new AgentInterface { Url = "http://jsonrpc/agent", ProtocolBinding = ProtocolBindingNames.JsonRpc }, ] }); this._handler.ResponsesToReturn.Enqueue(new Message { Role = Role.Agent, Parts = [Part.FromText("Response")], }); var options = new A2AClientOptions { PreferredBindings = [ProtocolBindingNames.JsonRpc] }; var agent = await this._resolver.GetAIAgentAsync(httpClient: this._httpClient, options: options); // Act await agent.RunAsync("Test input"); // Assert Assert.Equal(2, this._handler.CapturedUris.Count); Assert.Equal(new Uri("http://jsonrpc/agent"), this._handler.CapturedUris[1]); } [Fact] public async Task GetAIAgentAsync_WithAgentOptions_OverridesCardValuesAsync() { // Arrange this._handler.ResponsesToReturn.Enqueue(new AgentCard { Name = "Card Agent", Description = "Card description", SupportedInterfaces = [new AgentInterface { Url = "http://test-endpoint/agent" }] }); var agentOptions = new A2AAgentOptions { Id = "custom-id", Name = "Custom Agent", Description = "Custom description" }; // Act var agent = await this._resolver.GetAIAgentAsync(agentOptions, httpClient: this._httpClient); // Assert Assert.NotNull(agent); Assert.IsType(agent); Assert.Equal("custom-id", agent.Id); Assert.Equal("Custom Agent", agent.Name); Assert.Equal("Custom description", agent.Description); } [Fact] public async Task GetAIAgentAsync_WithAgentOptions_FallsBackToCardValuesAsync() { // Arrange this._handler.ResponsesToReturn.Enqueue(new AgentCard { Name = "Card Agent", Description = "Card description", SupportedInterfaces = [new AgentInterface { Url = "http://test-endpoint/agent" }] }); var agentOptions = new A2AAgentOptions { Id = "custom-id" }; // Act var agent = await this._resolver.GetAIAgentAsync(agentOptions, httpClient: this._httpClient); // Assert Assert.NotNull(agent); Assert.Equal("custom-id", agent.Id); Assert.Equal("Card Agent", agent.Name); Assert.Equal("Card description", agent.Description); } public void Dispose() { this._handler.Dispose(); this._httpClient.Dispose(); } internal sealed class HttpMessageHandlerStub : HttpMessageHandler { public Queue ResponsesToReturn { get; } = new(); public List CapturedUris { get; } = []; protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { this.CapturedUris.Add(request.RequestUri!); var response = this.ResponsesToReturn.Dequeue(); if (response is AgentCard agentCard) { var json = JsonSerializer.Serialize(agentCard); return new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(json, Encoding.UTF8, "application/json") }; } else if (response is Message message) { var sendMessageResponse = new SendMessageResponse { Message = message }; var jsonRpcResponse = new JsonRpcResponse { Id = "response-id", Result = JsonSerializer.SerializeToNode(sendMessageResponse, A2AJsonUtilities.DefaultOptions) }; return new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(JsonSerializer.Serialize(jsonRpcResponse, A2AJsonUtilities.DefaultOptions), Encoding.UTF8, "application/json") }; } // Return empty agent card if none specified var emptyCard = new AgentCard(); var emptyJson = JsonSerializer.Serialize(emptyCard); return new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(emptyJson, Encoding.UTF8, "application/json") }; } } }