Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions dotnet/src/Microsoft.Agents.AI.A2A/A2AAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ namespace Microsoft.Agents.AI.A2A;
/// </remarks>
public sealed class A2AAgent : AIAgent
{
private static readonly AIAgentMetadata s_agentMetadata = new("a2a");

private readonly A2AClient _a2aClient;
private readonly string? _id;
private readonly string? _name;
Expand Down Expand Up @@ -211,6 +213,13 @@ protected override async IAsyncEnumerable<AgentResponseUpdate> RunCoreStreamingA
/// <inheritdoc/>
public override string? Description => this._description;

/// <inheritdoc/>
public override object? GetService(Type serviceType, object? serviceKey = null)
=> base.GetService(serviceType, serviceKey)
?? (serviceType == typeof(A2AClient) ? this._a2aClient
: serviceType == typeof(AIAgentMetadata) ? s_agentMetadata
: null);

private async ValueTask<A2AAgentSession> GetA2ASessionAsync(AgentSession? session, AgentRunOptions? options, CancellationToken cancellationToken)
{
// Aligning with other agent implementations that support background responses, where
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace Microsoft.Agents.AI;
/// telemetry, and logging purposes.
/// </remarks>
[DebuggerDisplay("ProviderName = {ProviderName}")]
public class AIAgentMetadata
public sealed class AIAgentMetadata
{
/// <summary>
/// Initializes a new instance of the <see cref="AIAgentMetadata"/> class.
Expand Down
121 changes: 121 additions & 0 deletions dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/A2AAgentTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1027,6 +1027,127 @@ public async Task RunStreamingAsync_WithInvalidSessionType_ThrowsInvalidOperatio
await Assert.ThrowsAsync<InvalidOperationException>(async () => await this._agent.RunStreamingAsync(inputMessages, invalidSession).ToListAsync());
}

#region GetService Method Tests

/// <summary>
/// Verify that GetService returns A2AClient when requested.
/// </summary>
[Fact]
public void GetService_RequestingA2AClient_ReturnsA2AClient()
{
// Arrange & Act
var result = this._agent.GetService(typeof(A2AClient));

// Assert
Assert.NotNull(result);
Assert.Same(this._a2aClient, result);
}

/// <summary>
/// Verify that GetService returns AIAgentMetadata when requested.
/// </summary>
[Fact]
public void GetService_RequestingAIAgentMetadata_ReturnsMetadata()
{
// Arrange & Act
var result = this._agent.GetService(typeof(AIAgentMetadata));

// Assert
Assert.NotNull(result);
Assert.IsType<AIAgentMetadata>(result);
var metadata = (AIAgentMetadata)result;
Assert.Equal("a2a", metadata.ProviderName);
}

/// <summary>
/// Verify that GetService returns null for unknown service types.
/// </summary>
[Fact]
public void GetService_RequestingUnknownServiceType_ReturnsNull()
{
// Arrange & Act
var result = this._agent.GetService(typeof(string));

// Assert
Assert.Null(result);
}

/// <summary>
/// Verify that GetService with serviceKey parameter returns null for unknown service types.
/// </summary>
[Fact]
public void GetService_WithServiceKey_ReturnsNull()
{
// Arrange & Act
var result = this._agent.GetService(typeof(string), "test-key");

// Assert
Assert.Null(result);
}

/// <summary>
/// Verify that GetService calls base.GetService() first and returns the agent itself when requesting A2AAgent type.
/// </summary>
[Fact]
public void GetService_RequestingA2AAgentType_ReturnsBaseImplementation()
{
// Arrange & Act
var result = this._agent.GetService(typeof(A2AAgent));

// Assert
Assert.NotNull(result);
Assert.Same(this._agent, result);
}

/// <summary>
/// Verify that GetService calls base.GetService() first and returns the agent itself when requesting AIAgent type.
/// </summary>
[Fact]
public void GetService_RequestingAIAgentType_ReturnsBaseImplementation()
{
// Arrange & Act
var result = this._agent.GetService(typeof(AIAgent));

// Assert
Assert.NotNull(result);
Assert.Same(this._agent, result);
}

/// <summary>
/// Verify that GetService calls base.GetService() first but continues to derived logic when base returns null.
/// </summary>
[Fact]
public void GetService_RequestingA2AClientWithServiceKey_CallsBaseFirstThenDerivedLogic()
{
// Arrange & Act - Request A2AClient with a service key (base.GetService will return null due to serviceKey)
var result = this._agent.GetService(typeof(A2AClient), "some-key");

// Assert
Assert.NotNull(result);
Assert.Same(this._a2aClient, result);
}

/// <summary>
/// Verify that GetService returns consistent AIAgentMetadata across multiple calls.
/// </summary>
[Fact]
public void GetService_RequestingAIAgentMetadata_ReturnsConsistentMetadata()
{
// Arrange & Act
var result1 = this._agent.GetService(typeof(AIAgentMetadata));
var result2 = this._agent.GetService(typeof(AIAgentMetadata));

// Assert
Assert.NotNull(result1);
Assert.NotNull(result2);
Assert.Same(result1, result2); // Should return the same instance
Assert.IsType<AIAgentMetadata>(result1);
var metadata = (AIAgentMetadata)result1;
Assert.Equal("a2a", metadata.ProviderName);
}

#endregion

public void Dispose()
{
this._handler.Dispose();
Expand Down
Loading