diff --git a/dotnet/src/Microsoft.Agents.AI.A2A/A2AAgent.cs b/dotnet/src/Microsoft.Agents.AI.A2A/A2AAgent.cs index bdb667d195..1eb0f8449e 100644 --- a/dotnet/src/Microsoft.Agents.AI.A2A/A2AAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI.A2A/A2AAgent.cs @@ -26,6 +26,8 @@ namespace Microsoft.Agents.AI.A2A; /// 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; @@ -211,6 +213,13 @@ protected override async IAsyncEnumerable RunCoreStreamingA /// public override string? Description => this._description; + /// + 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 GetA2ASessionAsync(AgentSession? session, AgentRunOptions? options, CancellationToken cancellationToken) { // Aligning with other agent implementations that support background responses, where diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/AIAgentMetadata.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/AIAgentMetadata.cs index 6fe73c80cd..76389831ea 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/AIAgentMetadata.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/AIAgentMetadata.cs @@ -12,7 +12,7 @@ namespace Microsoft.Agents.AI; /// telemetry, and logging purposes. /// [DebuggerDisplay("ProviderName = {ProviderName}")] -public class AIAgentMetadata +public sealed class AIAgentMetadata { /// /// Initializes a new instance of the class. diff --git a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/A2AAgentTests.cs b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/A2AAgentTests.cs index fd2453cb0d..a903973806 100644 --- a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/A2AAgentTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/A2AAgentTests.cs @@ -1027,6 +1027,127 @@ public async Task RunStreamingAsync_WithInvalidSessionType_ThrowsInvalidOperatio await Assert.ThrowsAsync(async () => await this._agent.RunStreamingAsync(inputMessages, invalidSession).ToListAsync()); } + #region GetService Method Tests + + /// + /// Verify that GetService returns A2AClient when requested. + /// + [Fact] + public void GetService_RequestingA2AClient_ReturnsA2AClient() + { + // Arrange & Act + var result = this._agent.GetService(typeof(A2AClient)); + + // Assert + Assert.NotNull(result); + Assert.Same(this._a2aClient, result); + } + + /// + /// Verify that GetService returns AIAgentMetadata when requested. + /// + [Fact] + public void GetService_RequestingAIAgentMetadata_ReturnsMetadata() + { + // Arrange & Act + var result = this._agent.GetService(typeof(AIAgentMetadata)); + + // Assert + Assert.NotNull(result); + Assert.IsType(result); + var metadata = (AIAgentMetadata)result; + Assert.Equal("a2a", metadata.ProviderName); + } + + /// + /// Verify that GetService returns null for unknown service types. + /// + [Fact] + public void GetService_RequestingUnknownServiceType_ReturnsNull() + { + // Arrange & Act + var result = this._agent.GetService(typeof(string)); + + // Assert + Assert.Null(result); + } + + /// + /// Verify that GetService with serviceKey parameter returns null for unknown service types. + /// + [Fact] + public void GetService_WithServiceKey_ReturnsNull() + { + // Arrange & Act + var result = this._agent.GetService(typeof(string), "test-key"); + + // Assert + Assert.Null(result); + } + + /// + /// Verify that GetService calls base.GetService() first and returns the agent itself when requesting A2AAgent type. + /// + [Fact] + public void GetService_RequestingA2AAgentType_ReturnsBaseImplementation() + { + // Arrange & Act + var result = this._agent.GetService(typeof(A2AAgent)); + + // Assert + Assert.NotNull(result); + Assert.Same(this._agent, result); + } + + /// + /// Verify that GetService calls base.GetService() first and returns the agent itself when requesting AIAgent type. + /// + [Fact] + public void GetService_RequestingAIAgentType_ReturnsBaseImplementation() + { + // Arrange & Act + var result = this._agent.GetService(typeof(AIAgent)); + + // Assert + Assert.NotNull(result); + Assert.Same(this._agent, result); + } + + /// + /// Verify that GetService calls base.GetService() first but continues to derived logic when base returns null. + /// + [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); + } + + /// + /// Verify that GetService returns consistent AIAgentMetadata across multiple calls. + /// + [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(result1); + var metadata = (AIAgentMetadata)result1; + Assert.Equal("a2a", metadata.ProviderName); + } + + #endregion + public void Dispose() { this._handler.Dispose();