diff --git a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AIAgentMetadataTests.cs b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AIAgentMetadataTests.cs new file mode 100644 index 0000000000..764f7f2122 --- /dev/null +++ b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AIAgentMetadataTests.cs @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft. All rights reserved. + +namespace Microsoft.Agents.AI.Abstractions.UnitTests; + +/// +/// Unit tests for the class. +/// +public class AIAgentMetadataTests +{ + [Fact] + public void Constructor_WithNoArguments_SetsProviderNameToNull() + { + // Arrange & Act + AIAgentMetadata metadata = new(); + + // Assert + Assert.Null(metadata.ProviderName); + } + + [Fact] + public void Constructor_WithProviderName_SetsProperty() + { + // Arrange + const string ProviderName = "TestProvider"; + + // Act + AIAgentMetadata metadata = new(ProviderName); + + // Assert + Assert.Equal(ProviderName, metadata.ProviderName); + } + + [Fact] + public void Constructor_WithNullProviderName_SetsProviderNameToNull() + { + // Arrange & Act + AIAgentMetadata metadata = new(null); + + // Assert + Assert.Null(metadata.ProviderName); + } +} diff --git a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AIAgentTests.cs b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AIAgentTests.cs index cd2cbd4700..c65cb66e59 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AIAgentTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AIAgentTests.cs @@ -364,6 +364,74 @@ public void GetService_Generic_ReturnsNullForUnrelatedType() #endregion + #region Name and Description Property Tests + + /// + /// Verify that Name property returns the value from the derived class. + /// + [Fact] + public void Name_ReturnsValueFromDerivedClass() + { + // Arrange + var agent = new MockAgentWithName("TestAgentName", "TestAgentDescription"); + + // Act + string? name = agent.Name; + + // Assert + Assert.Equal("TestAgentName", name); + } + + /// + /// Verify that Description property returns the value from the derived class. + /// + [Fact] + public void Description_ReturnsValueFromDerivedClass() + { + // Arrange + var agent = new MockAgentWithName("TestAgentName", "TestAgentDescription"); + + // Act + string? description = agent.Description; + + // Assert + Assert.Equal("TestAgentDescription", description); + } + + /// + /// Verify that Name property returns null when not overridden. + /// + [Fact] + public void Name_ReturnsNullByDefault() + { + // Arrange + var agent = new MockAgent(); + + // Act + string? name = agent.Name; + + // Assert + Assert.Null(name); + } + + /// + /// Verify that Description property returns null when not overridden. + /// + [Fact] + public void Description_ReturnsNullByDefault() + { + // Arrange + var agent = new MockAgent(); + + // Act + string? description = agent.Description; + + // Assert + Assert.Null(description); + } + + #endregion + /// /// Typed mock session. /// @@ -402,6 +470,44 @@ protected override IAsyncEnumerable RunCoreStreamingAsync( throw new NotImplementedException(); } + private sealed class MockAgentWithName : AIAgent + { + private readonly string? _name; + private readonly string? _description; + + public MockAgentWithName(string? name, string? description) + { + this._name = name; + this._description = description; + } + + public override string? Name => this._name; + public override string? Description => this._description; + + public override ValueTask CreateSessionAsync(CancellationToken cancellationToken = default) + => throw new NotImplementedException(); + + public override ValueTask DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) + => throw new NotImplementedException(); + + public override JsonElement SerializeSession(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null) + => throw new NotImplementedException(); + + protected override Task RunCoreAsync( + IEnumerable messages, + AgentSession? session = null, + AgentRunOptions? options = null, + CancellationToken cancellationToken = default) => + throw new NotImplementedException(); + + protected override IAsyncEnumerable RunCoreStreamingAsync( + IEnumerable messages, + AgentSession? session = null, + AgentRunOptions? options = null, + CancellationToken cancellationToken = default) => + throw new NotImplementedException(); + } + private static async IAsyncEnumerable ToAsyncEnumerableAsync(IEnumerable values) { await Task.Yield(); diff --git a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AIContextProviderTests.cs b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AIContextProviderTests.cs index b287c8b304..b6aabd081e 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AIContextProviderTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AIContextProviderTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. using System; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.Threading; using System.Threading.Tasks; @@ -155,6 +156,111 @@ public void GetService_Generic_ReturnsNullForUnrelatedType() #endregion + #region InvokingContext Tests + + [Fact] + public void InvokingContext_RequestMessages_SetterThrowsForNull() + { + // Arrange + var messages = new ReadOnlyCollection([new(ChatRole.User, "Hello")]); + var context = new AIContextProvider.InvokingContext(messages); + + // Act & Assert + Assert.Throws(() => context.RequestMessages = null!); + } + + [Fact] + public void InvokingContext_RequestMessages_SetterRoundtrips() + { + // Arrange + var initialMessages = new ReadOnlyCollection([new(ChatRole.User, "Hello")]); + var newMessages = new List { new(ChatRole.User, "New message") }; + var context = new AIContextProvider.InvokingContext(initialMessages); + + // Act + context.RequestMessages = newMessages; + + // Assert + Assert.Same(newMessages, context.RequestMessages); + } + + #endregion + + #region InvokedContext Tests + + [Fact] + public void InvokedContext_RequestMessages_SetterThrowsForNull() + { + // Arrange + var messages = new ReadOnlyCollection([new(ChatRole.User, "Hello")]); + var context = new AIContextProvider.InvokedContext(messages, aiContextProviderMessages: null); + + // Act & Assert + Assert.Throws(() => context.RequestMessages = null!); + } + + [Fact] + public void InvokedContext_RequestMessages_SetterRoundtrips() + { + // Arrange + var initialMessages = new ReadOnlyCollection([new(ChatRole.User, "Hello")]); + var newMessages = new List { new(ChatRole.User, "New message") }; + var context = new AIContextProvider.InvokedContext(initialMessages, aiContextProviderMessages: null); + + // Act + context.RequestMessages = newMessages; + + // Assert + Assert.Same(newMessages, context.RequestMessages); + } + + [Fact] + public void InvokedContext_AIContextProviderMessages_Roundtrips() + { + // Arrange + var requestMessages = new ReadOnlyCollection([new(ChatRole.User, "Hello")]); + var aiContextMessages = new List { new(ChatRole.System, "AI context message") }; + var context = new AIContextProvider.InvokedContext(requestMessages, aiContextProviderMessages: null); + + // Act + context.AIContextProviderMessages = aiContextMessages; + + // Assert + Assert.Same(aiContextMessages, context.AIContextProviderMessages); + } + + [Fact] + public void InvokedContext_ResponseMessages_Roundtrips() + { + // Arrange + var requestMessages = new ReadOnlyCollection([new(ChatRole.User, "Hello")]); + var responseMessages = new List { new(ChatRole.Assistant, "Response message") }; + var context = new AIContextProvider.InvokedContext(requestMessages, aiContextProviderMessages: null); + + // Act + context.ResponseMessages = responseMessages; + + // Assert + Assert.Same(responseMessages, context.ResponseMessages); + } + + [Fact] + public void InvokedContext_InvokeException_Roundtrips() + { + // Arrange + var requestMessages = new ReadOnlyCollection([new(ChatRole.User, "Hello")]); + var exception = new InvalidOperationException("Test exception"); + var context = new AIContextProvider.InvokedContext(requestMessages, aiContextProviderMessages: null); + + // Act + context.InvokeException = exception; + + // Assert + Assert.Same(exception, context.InvokeException); + } + + #endregion + private sealed class TestAIContextProvider : AIContextProvider { public override ValueTask InvokingAsync(InvokingContext context, CancellationToken cancellationToken = default) diff --git a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentResponseTests.cs b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentResponseTests.cs index 75bc90ca8e..87cdbf4f20 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentResponseTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentResponseTests.cs @@ -346,4 +346,91 @@ public void TryParseAsStructuredOutputFailsWithIncorrectTypedJson() // Act & Assert. Assert.False(response.TryDeserialize(TestJsonSerializerContext.Default.Options, out _)); } + + [Fact] + public void UserInputRequests_ReturnsEmptyWhenNoMessages() + { + // Arrange + AgentResponse response = new(); + + // Act + IEnumerable requests = response.UserInputRequests; + + // Assert + Assert.Empty(requests); + } + + [Fact] + public void UserInputRequests_ReturnsEmptyWhenNoUserInputRequestContent() + { + // Arrange + AgentResponse response = new(new ChatMessage(ChatRole.Assistant, "Hello")); + + // Act + IEnumerable requests = response.UserInputRequests; + + // Assert + Assert.Empty(requests); + } + + [Fact] + public void ToAgentResponseUpdatesWithNoMessagesProducesEmptyArray() + { + // Arrange + AgentResponse response = new(); + + // Act + AgentResponseUpdate[] updates = response.ToAgentResponseUpdates(); + + // Assert + Assert.Empty(updates); + } + + [Fact] + public void ToAgentResponseUpdatesWithUsageOnlyProducesSingleUpdate() + { + // Arrange + AgentResponse response = new() + { + Usage = new UsageDetails { TotalTokenCount = 100 } + }; + + // Act + AgentResponseUpdate[] updates = response.ToAgentResponseUpdates(); + + // Assert + AgentResponseUpdate update = Assert.Single(updates); + UsageContent usageContent = Assert.IsType(update.Contents[0]); + Assert.Equal(100, usageContent.Details.TotalTokenCount); + } + + [Fact] + public void ToAgentResponseUpdatesWithAdditionalPropertiesOnlyProducesSingleUpdate() + { + // Arrange + AgentResponse response = new() + { + AdditionalProperties = new() { ["key"] = "value" } + }; + + // Act + AgentResponseUpdate[] updates = response.ToAgentResponseUpdates(); + + // Assert + AgentResponseUpdate update = Assert.Single(updates); + Assert.NotNull(update.AdditionalProperties); + Assert.Equal("value", update.AdditionalProperties!["key"]); + } + + [Fact] + public void Deserialize_ThrowsWhenDeserializationReturnsNull() + { + // Arrange + AgentResponse response = new(new ChatMessage(ChatRole.Assistant, "null")); + + // Act & Assert + InvalidOperationException exception = Assert.Throws( + () => response.Deserialize(TestJsonSerializerContext.Default.Options)); + Assert.Equal("The deserialized response is null.", exception.Message); + } } diff --git a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentResponseUpdateExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentResponseUpdateExtensionsTests.cs index 2723ed081a..790298ddf9 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentResponseUpdateExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentResponseUpdateExtensionsTests.cs @@ -299,6 +299,161 @@ public async Task ToAgentResponse_TimestampFoldingAsync(bool useAsync, string? t Assert.Equal(expected, response.CreatedAt); } + #region AsChatResponse Tests + + [Fact] + public void AsChatResponse_WithNullArgument_ThrowsArgumentNullException() + { + // Arrange & Act & Assert + Assert.Throws("response", () => ((AgentResponse)null!).AsChatResponse()); + } + + [Fact] + public void AsChatResponse_WithRawRepresentationAsChatResponse_ReturnsSameInstance() + { + // Arrange + ChatResponse originalChatResponse = new() + { + ResponseId = "original-response", + Messages = [new ChatMessage(ChatRole.Assistant, "Hello")] + }; + AgentResponse agentResponse = new(originalChatResponse); + + // Act + ChatResponse result = agentResponse.AsChatResponse(); + + // Assert + Assert.Same(originalChatResponse, result); + } + + [Fact] + public void AsChatResponse_WithoutRawRepresentation_CreatesNewChatResponse() + { + // Arrange + AgentResponse agentResponse = new(new ChatMessage(ChatRole.Assistant, "Test message")) + { + ResponseId = "test-response-id", + CreatedAt = new DateTimeOffset(2024, 1, 1, 12, 0, 0, TimeSpan.Zero), + Usage = new UsageDetails { TotalTokenCount = 50 }, + AdditionalProperties = new() { ["key"] = "value" }, + ContinuationToken = ResponseContinuationToken.FromBytes(new byte[] { 1, 2, 3 }), + }; + + // Act + ChatResponse result = agentResponse.AsChatResponse(); + + // Assert + Assert.NotNull(result); + Assert.Equal("test-response-id", result.ResponseId); + Assert.Equal(new DateTimeOffset(2024, 1, 1, 12, 0, 0, TimeSpan.Zero), result.CreatedAt); + Assert.Same(agentResponse.Messages, result.Messages); + Assert.Same(agentResponse, result.RawRepresentation); + Assert.Same(agentResponse.Usage, result.Usage); + Assert.Same(agentResponse.AdditionalProperties, result.AdditionalProperties); + Assert.Equal(agentResponse.ContinuationToken, result.ContinuationToken); + } + + #endregion + + #region AsChatResponseUpdate Tests + + [Fact] + public void AsChatResponseUpdate_WithNullArgument_ThrowsArgumentNullException() + { + // Arrange & Act & Assert + Assert.Throws("responseUpdate", () => ((AgentResponseUpdate)null!).AsChatResponseUpdate()); + } + + [Fact] + public void AsChatResponseUpdate_WithRawRepresentationAsChatResponseUpdate_ReturnsSameInstance() + { + // Arrange + ChatResponseUpdate originalChatResponseUpdate = new() + { + ResponseId = "original-update", + Contents = [new TextContent("Hello")] + }; + AgentResponseUpdate agentResponseUpdate = new(originalChatResponseUpdate); + + // Act + ChatResponseUpdate result = agentResponseUpdate.AsChatResponseUpdate(); + + // Assert + Assert.Same(originalChatResponseUpdate, result); + } + + [Fact] + public void AsChatResponseUpdate_WithoutRawRepresentation_CreatesNewChatResponseUpdate() + { + // Arrange + AgentResponseUpdate agentResponseUpdate = new(ChatRole.Assistant, "Test") + { + AuthorName = "TestAuthor", + ResponseId = "update-id", + MessageId = "message-id", + CreatedAt = new DateTimeOffset(2024, 1, 1, 12, 0, 0, TimeSpan.Zero), + AdditionalProperties = new() { ["key"] = "value" }, + ContinuationToken = ResponseContinuationToken.FromBytes(new byte[] { 1, 2, 3 }), + }; + + // Act + ChatResponseUpdate result = agentResponseUpdate.AsChatResponseUpdate(); + + // Assert + Assert.NotNull(result); + Assert.Equal("TestAuthor", result.AuthorName); + Assert.Equal("update-id", result.ResponseId); + Assert.Equal("message-id", result.MessageId); + Assert.Equal(new DateTimeOffset(2024, 1, 1, 12, 0, 0, TimeSpan.Zero), result.CreatedAt); + Assert.Equal(ChatRole.Assistant, result.Role); + Assert.Same(agentResponseUpdate.Contents, result.Contents); + Assert.Same(agentResponseUpdate, result.RawRepresentation); + Assert.Same(agentResponseUpdate.AdditionalProperties, result.AdditionalProperties); + Assert.Equal(agentResponseUpdate.ContinuationToken, result.ContinuationToken); + } + + #endregion + + #region AsChatResponseUpdatesAsync Tests + + [Fact] + public async Task AsChatResponseUpdatesAsync_WithNullArgument_ThrowsArgumentNullExceptionAsync() + { + // Arrange & Act & Assert + await Assert.ThrowsAsync("responseUpdates", async () => + { + await foreach (ChatResponseUpdate _ in ((IAsyncEnumerable)null!).AsChatResponseUpdatesAsync()) + { + // Do nothing + } + }); + } + + [Fact] + public async Task AsChatResponseUpdatesAsync_ConvertsUpdatesAsync() + { + // Arrange + AgentResponseUpdate[] updates = + [ + new(ChatRole.Assistant, "First"), + new(ChatRole.Assistant, "Second"), + ]; + + // Act + List results = []; + await foreach (ChatResponseUpdate update in YieldAsync(updates).AsChatResponseUpdatesAsync()) + { + results.Add(update); + } + + // Assert + Assert.Equal(2, results.Count); + Assert.Equal("First", Assert.IsType(results[0].Contents[0]).Text); + Assert.Equal("Second", Assert.IsType(results[1].Contents[0]).Text); + } + + #endregion + private static async IAsyncEnumerable YieldAsync(IEnumerable updates) { foreach (AgentResponseUpdate update in updates) diff --git a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentResponseUpdateTests.cs b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentResponseUpdateTests.cs index 7fda5f680b..1b42188c92 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentResponseUpdateTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentResponseUpdateTests.cs @@ -199,4 +199,30 @@ public void JsonSerializationRoundtrips() Assert.NotNull(result.ContinuationToken); Assert.Equivalent(ResponseContinuationToken.FromBytes(new byte[] { 1, 2, 3 }), result.ContinuationToken); } + + [Fact] + public void UserInputRequests_ReturnsEmptyWhenNoContents() + { + // Arrange + AgentResponseUpdate update = new(); + + // Act + IEnumerable requests = update.UserInputRequests; + + // Assert + Assert.Empty(requests); + } + + [Fact] + public void UserInputRequests_ReturnsEmptyWhenNoUserInputRequestContent() + { + // Arrange + AgentResponseUpdate update = new(ChatRole.Assistant, "Hello"); + + // Act + IEnumerable requests = update.UserInputRequests; + + // Assert + Assert.Empty(requests); + } } diff --git a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/ChatHistoryProviderTests.cs b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/ChatHistoryProviderTests.cs index 02955f4a25..a26ef199d9 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/ChatHistoryProviderTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/ChatHistoryProviderTests.cs @@ -76,6 +76,140 @@ public void GetService_Generic_ReturnsNullForUnrelatedType() #endregion + #region InvokingContext Tests + + [Fact] + public void InvokingContext_Constructor_ThrowsForNullMessages() + { + // Arrange & Act & Assert + Assert.Throws(() => new ChatHistoryProvider.InvokingContext(null!)); + } + + [Fact] + public void InvokingContext_RequestMessages_SetterThrowsForNull() + { + // Arrange + var messages = new List { new(ChatRole.User, "Hello") }; + var context = new ChatHistoryProvider.InvokingContext(messages); + + // Act & Assert + Assert.Throws(() => context.RequestMessages = null!); + } + + [Fact] + public void InvokingContext_RequestMessages_SetterRoundtrips() + { + // Arrange + var initialMessages = new List { new(ChatRole.User, "Hello") }; + var newMessages = new List { new(ChatRole.User, "New message") }; + var context = new ChatHistoryProvider.InvokingContext(initialMessages); + + // Act + context.RequestMessages = newMessages; + + // Assert + Assert.Same(newMessages, context.RequestMessages); + } + + #endregion + + #region InvokedContext Tests + + [Fact] + public void InvokedContext_Constructor_ThrowsForNullRequestMessages() + { + // Arrange & Act & Assert + Assert.Throws(() => new ChatHistoryProvider.InvokedContext(null!, [])); + } + + [Fact] + public void InvokedContext_RequestMessages_SetterThrowsForNull() + { + // Arrange + var requestMessages = new List { new(ChatRole.User, "Hello") }; + var context = new ChatHistoryProvider.InvokedContext(requestMessages, []); + + // Act & Assert + Assert.Throws(() => context.RequestMessages = null!); + } + + [Fact] + public void InvokedContext_RequestMessages_SetterRoundtrips() + { + // Arrange + var initialMessages = new List { new(ChatRole.User, "Hello") }; + var newMessages = new List { new(ChatRole.User, "New message") }; + var context = new ChatHistoryProvider.InvokedContext(initialMessages, []); + + // Act + context.RequestMessages = newMessages; + + // Assert + Assert.Same(newMessages, context.RequestMessages); + } + + [Fact] + public void InvokedContext_ChatHistoryProviderMessages_SetterRoundtrips() + { + // Arrange + var requestMessages = new List { new(ChatRole.User, "Hello") }; + var newProviderMessages = new List { new(ChatRole.System, "System message") }; + var context = new ChatHistoryProvider.InvokedContext(requestMessages, []); + + // Act + context.ChatHistoryProviderMessages = newProviderMessages; + + // Assert + Assert.Same(newProviderMessages, context.ChatHistoryProviderMessages); + } + + [Fact] + public void InvokedContext_AIContextProviderMessages_Roundtrips() + { + // Arrange + var requestMessages = new List { new(ChatRole.User, "Hello") }; + var aiContextMessages = new List { new(ChatRole.System, "AI context message") }; + var context = new ChatHistoryProvider.InvokedContext(requestMessages, []); + + // Act + context.AIContextProviderMessages = aiContextMessages; + + // Assert + Assert.Same(aiContextMessages, context.AIContextProviderMessages); + } + + [Fact] + public void InvokedContext_ResponseMessages_Roundtrips() + { + // Arrange + var requestMessages = new List { new(ChatRole.User, "Hello") }; + var responseMessages = new List { new(ChatRole.Assistant, "Response message") }; + var context = new ChatHistoryProvider.InvokedContext(requestMessages, []); + + // Act + context.ResponseMessages = responseMessages; + + // Assert + Assert.Same(responseMessages, context.ResponseMessages); + } + + [Fact] + public void InvokedContext_InvokeException_Roundtrips() + { + // Arrange + var requestMessages = new List { new(ChatRole.User, "Hello") }; + var exception = new InvalidOperationException("Test exception"); + var context = new ChatHistoryProvider.InvokedContext(requestMessages, []); + + // Act + context.InvokeException = exception; + + // Assert + Assert.Same(exception, context.InvokeException); + } + + #endregion + private sealed class TestChatHistoryProvider : ChatHistoryProvider { public override ValueTask> InvokingAsync(InvokingContext context, CancellationToken cancellationToken = default) diff --git a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/DelegatingAIAgentTests.cs b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/DelegatingAIAgentTests.cs index 6320d9c900..3c49f6f178 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/DelegatingAIAgentTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/DelegatingAIAgentTests.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.AI; @@ -145,6 +146,26 @@ public async Task CreateSessionAsync_DelegatesToInnerAgentAsync() this._innerAgentMock.Verify(x => x.CreateSessionAsync(), Times.Once); } + /// + /// Verify that DeserializeSessionAsync delegates to inner agent. + /// + [Fact] + public async Task DeserializeSessionAsync_DelegatesToInnerAgentAsync() + { + // Arrange + var serializedSession = JsonSerializer.SerializeToElement("test-session-id", TestJsonSerializerContext.Default.String); + this._innerAgentMock + .Setup(x => x.DeserializeSessionAsync(It.IsAny(), null, It.IsAny())) + .ReturnsAsync(this._testSession); + + // Act + var session = await this._delegatingAgent.DeserializeSessionAsync(serializedSession); + + // Assert + Assert.Same(this._testSession, session); + this._innerAgentMock.Verify(x => x.DeserializeSessionAsync(It.IsAny(), null, It.IsAny()), Times.Once); + } + /// /// Verify that RunAsync delegates to inner agent with correct parameters. /// diff --git a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/InMemoryChatHistoryProviderTests.cs b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/InMemoryChatHistoryProviderTests.cs index debaff73ef..ff31d0afc9 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/InMemoryChatHistoryProviderTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/InMemoryChatHistoryProviderTests.cs @@ -614,6 +614,42 @@ public async Task GetMessagesAsync_WithReducer_ButWrongTrigger_DoesNotInvokeRedu reducerMock.Verify(r => r.ReduceAsync(It.IsAny>(), It.IsAny()), Times.Never); } + [Fact] + public async Task InvokedAsync_WithException_DoesNotAddMessagesAsync() + { + // Arrange + var provider = new InMemoryChatHistoryProvider(); + var requestMessages = new List + { + new(ChatRole.User, "Hello") + }; + var responseMessages = new List + { + new(ChatRole.Assistant, "Hi there!") + }; + var context = new ChatHistoryProvider.InvokedContext(requestMessages, []) + { + ResponseMessages = responseMessages, + InvokeException = new InvalidOperationException("Test exception") + }; + + // Act + await provider.InvokedAsync(context, CancellationToken.None); + + // Assert + Assert.Empty(provider); + } + + [Fact] + public async Task InvokingAsync_WithNullContext_ThrowsArgumentNullExceptionAsync() + { + // Arrange + var provider = new InMemoryChatHistoryProvider(); + + // Act & Assert + await Assert.ThrowsAsync(() => provider.InvokingAsync(null!, CancellationToken.None).AsTask()); + } + public class TestAIContent(string testData) : AIContent { public string TestData => testData;