chore: import upstream snapshot with attribution
dotnet-build-and-test / dotnet-test-functions (push) Has been cancelled
dotnet-build-and-test / paths-filter (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Debug, windows-latest, net9.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net8.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-foundry-hosted-it (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test-check (push) Has been cancelled
dotnet-build-and-test / Integration Test Report (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
dotnet-build-and-test / dotnet-test-functions (push) Has been cancelled
dotnet-build-and-test / paths-filter (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Debug, windows-latest, net9.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net8.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-foundry-hosted-it (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test-check (push) Has been cancelled
dotnet-build-and-test / Integration Test Report (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
This commit is contained in:
+221
@@ -0,0 +1,221 @@
|
||||
// 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;
|
||||
|
||||
/// <summary>
|
||||
/// Unit tests for the <see cref="A2ACardResolverExtensions"/> class.
|
||||
/// </summary>
|
||||
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<A2AAgent>(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<A2AAgent>(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<Uri> CapturedUris { get; } = [];
|
||||
|
||||
protected override async Task<HttpResponseMessage> 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")
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user