mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
.NET: Improve unit test coverage for Microsoft.Agents.AI.Abstractions (#3381)
* Initial plan * Add unit tests to improve coverage for Microsoft.Agents.AI.Abstractions Co-authored-by: rogerbarreto <19890735+rogerbarreto@users.noreply.github.com> * Fix file encoding and naming rule violation in new test files Co-authored-by: rogerbarreto <19890735+rogerbarreto@users.noreply.github.com> * Remove ChatMessageStoreExtensionsTests.cs to avoid duplication with Wesley's work Co-authored-by: rogerbarreto <19890735+rogerbarreto@users.noreply.github.com> * Fix AgentThread to AgentSession rename in unit tests Update MockAgentWithName in AIAgentTests.cs and DelegatingAIAgentTests.cs to use the renamed AgentSession class and corresponding methods: - AgentThread -> AgentSession - GetNewThreadAsync -> GetNewSessionAsync - DeserializeThreadAsync -> DeserializeSessionAsync - thread parameter -> session parameter * Fix: Rename GetNewSessionAsync to CreateSessionAsync to match API changes * Fix: Add SerializeSession override and remove async from DeserializeSessionAsync --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: rogerbarreto <19890735+rogerbarreto@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
6255abd687
commit
e8902c0d11
@@ -0,0 +1,42 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
namespace Microsoft.Agents.AI.Abstractions.UnitTests;
|
||||
|
||||
/// <summary>
|
||||
/// Unit tests for the <see cref="AIAgentMetadata"/> class.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -364,6 +364,74 @@ public class AIAgentTests
|
||||
|
||||
#endregion
|
||||
|
||||
#region Name and Description Property Tests
|
||||
|
||||
/// <summary>
|
||||
/// Verify that Name property returns the value from the derived class.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Name_ReturnsValueFromDerivedClass()
|
||||
{
|
||||
// Arrange
|
||||
var agent = new MockAgentWithName("TestAgentName", "TestAgentDescription");
|
||||
|
||||
// Act
|
||||
string? name = agent.Name;
|
||||
|
||||
// Assert
|
||||
Assert.Equal("TestAgentName", name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that Description property returns the value from the derived class.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Description_ReturnsValueFromDerivedClass()
|
||||
{
|
||||
// Arrange
|
||||
var agent = new MockAgentWithName("TestAgentName", "TestAgentDescription");
|
||||
|
||||
// Act
|
||||
string? description = agent.Description;
|
||||
|
||||
// Assert
|
||||
Assert.Equal("TestAgentDescription", description);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that Name property returns null when not overridden.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Name_ReturnsNullByDefault()
|
||||
{
|
||||
// Arrange
|
||||
var agent = new MockAgent();
|
||||
|
||||
// Act
|
||||
string? name = agent.Name;
|
||||
|
||||
// Assert
|
||||
Assert.Null(name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that Description property returns null when not overridden.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Description_ReturnsNullByDefault()
|
||||
{
|
||||
// Arrange
|
||||
var agent = new MockAgent();
|
||||
|
||||
// Act
|
||||
string? description = agent.Description;
|
||||
|
||||
// Assert
|
||||
Assert.Null(description);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Typed mock session.
|
||||
/// </summary>
|
||||
@@ -402,6 +470,44 @@ public class AIAgentTests
|
||||
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<AgentSession> CreateSessionAsync(CancellationToken cancellationToken = default)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
public override ValueTask<AgentSession> 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<AgentResponse> RunCoreAsync(
|
||||
IEnumerable<ChatMessage> messages,
|
||||
AgentSession? session = null,
|
||||
AgentRunOptions? options = null,
|
||||
CancellationToken cancellationToken = default) =>
|
||||
throw new NotImplementedException();
|
||||
|
||||
protected override IAsyncEnumerable<AgentResponseUpdate> RunCoreStreamingAsync(
|
||||
IEnumerable<ChatMessage> messages,
|
||||
AgentSession? session = null,
|
||||
AgentRunOptions? options = null,
|
||||
CancellationToken cancellationToken = default) =>
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private static async IAsyncEnumerable<T> ToAsyncEnumerableAsync<T>(IEnumerable<T> values)
|
||||
{
|
||||
await Task.Yield();
|
||||
|
||||
@@ -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 class AIContextProviderTests
|
||||
|
||||
#endregion
|
||||
|
||||
#region InvokingContext Tests
|
||||
|
||||
[Fact]
|
||||
public void InvokingContext_RequestMessages_SetterThrowsForNull()
|
||||
{
|
||||
// Arrange
|
||||
var messages = new ReadOnlyCollection<ChatMessage>([new(ChatRole.User, "Hello")]);
|
||||
var context = new AIContextProvider.InvokingContext(messages);
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentNullException>(() => context.RequestMessages = null!);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvokingContext_RequestMessages_SetterRoundtrips()
|
||||
{
|
||||
// Arrange
|
||||
var initialMessages = new ReadOnlyCollection<ChatMessage>([new(ChatRole.User, "Hello")]);
|
||||
var newMessages = new List<ChatMessage> { 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<ChatMessage>([new(ChatRole.User, "Hello")]);
|
||||
var context = new AIContextProvider.InvokedContext(messages, aiContextProviderMessages: null);
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentNullException>(() => context.RequestMessages = null!);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvokedContext_RequestMessages_SetterRoundtrips()
|
||||
{
|
||||
// Arrange
|
||||
var initialMessages = new ReadOnlyCollection<ChatMessage>([new(ChatRole.User, "Hello")]);
|
||||
var newMessages = new List<ChatMessage> { 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<ChatMessage>([new(ChatRole.User, "Hello")]);
|
||||
var aiContextMessages = new List<ChatMessage> { 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<ChatMessage>([new(ChatRole.User, "Hello")]);
|
||||
var responseMessages = new List<ChatMessage> { 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<ChatMessage>([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<AIContext> InvokingAsync(InvokingContext context, CancellationToken cancellationToken = default)
|
||||
|
||||
@@ -346,4 +346,91 @@ public class AgentResponseTests
|
||||
// Act & Assert.
|
||||
Assert.False(response.TryDeserialize<Animal>(TestJsonSerializerContext.Default.Options, out _));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UserInputRequests_ReturnsEmptyWhenNoMessages()
|
||||
{
|
||||
// Arrange
|
||||
AgentResponse response = new();
|
||||
|
||||
// Act
|
||||
IEnumerable<UserInputRequestContent> requests = response.UserInputRequests;
|
||||
|
||||
// Assert
|
||||
Assert.Empty(requests);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UserInputRequests_ReturnsEmptyWhenNoUserInputRequestContent()
|
||||
{
|
||||
// Arrange
|
||||
AgentResponse response = new(new ChatMessage(ChatRole.Assistant, "Hello"));
|
||||
|
||||
// Act
|
||||
IEnumerable<UserInputRequestContent> 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<UsageContent>(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<InvalidOperationException>(
|
||||
() => response.Deserialize<Animal>(TestJsonSerializerContext.Default.Options));
|
||||
Assert.Equal("The deserialized response is null.", exception.Message);
|
||||
}
|
||||
}
|
||||
|
||||
+155
@@ -299,6 +299,161 @@ public class AgentResponseUpdateExtensionsTests
|
||||
Assert.Equal(expected, response.CreatedAt);
|
||||
}
|
||||
|
||||
#region AsChatResponse Tests
|
||||
|
||||
[Fact]
|
||||
public void AsChatResponse_WithNullArgument_ThrowsArgumentNullException()
|
||||
{
|
||||
// Arrange & Act & Assert
|
||||
Assert.Throws<ArgumentNullException>("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<ArgumentNullException>("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<ArgumentNullException>("responseUpdates", async () =>
|
||||
{
|
||||
await foreach (ChatResponseUpdate _ in ((IAsyncEnumerable<AgentResponseUpdate>)null!).AsChatResponseUpdatesAsync())
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AsChatResponseUpdatesAsync_ConvertsUpdatesAsync()
|
||||
{
|
||||
// Arrange
|
||||
AgentResponseUpdate[] updates =
|
||||
[
|
||||
new(ChatRole.Assistant, "First"),
|
||||
new(ChatRole.Assistant, "Second"),
|
||||
];
|
||||
|
||||
// Act
|
||||
List<ChatResponseUpdate> results = [];
|
||||
await foreach (ChatResponseUpdate update in YieldAsync(updates).AsChatResponseUpdatesAsync())
|
||||
{
|
||||
results.Add(update);
|
||||
}
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, results.Count);
|
||||
Assert.Equal("First", Assert.IsType<TextContent>(results[0].Contents[0]).Text);
|
||||
Assert.Equal("Second", Assert.IsType<TextContent>(results[1].Contents[0]).Text);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private static async IAsyncEnumerable<AgentResponseUpdate> YieldAsync(IEnumerable<AgentResponseUpdate> updates)
|
||||
{
|
||||
foreach (AgentResponseUpdate update in updates)
|
||||
|
||||
@@ -199,4 +199,30 @@ public class AgentResponseUpdateTests
|
||||
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<UserInputRequestContent> requests = update.UserInputRequests;
|
||||
|
||||
// Assert
|
||||
Assert.Empty(requests);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UserInputRequests_ReturnsEmptyWhenNoUserInputRequestContent()
|
||||
{
|
||||
// Arrange
|
||||
AgentResponseUpdate update = new(ChatRole.Assistant, "Hello");
|
||||
|
||||
// Act
|
||||
IEnumerable<UserInputRequestContent> requests = update.UserInputRequests;
|
||||
|
||||
// Assert
|
||||
Assert.Empty(requests);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,6 +76,140 @@ public class ChatHistoryProviderTests
|
||||
|
||||
#endregion
|
||||
|
||||
#region InvokingContext Tests
|
||||
|
||||
[Fact]
|
||||
public void InvokingContext_Constructor_ThrowsForNullMessages()
|
||||
{
|
||||
// Arrange & Act & Assert
|
||||
Assert.Throws<ArgumentNullException>(() => new ChatHistoryProvider.InvokingContext(null!));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvokingContext_RequestMessages_SetterThrowsForNull()
|
||||
{
|
||||
// Arrange
|
||||
var messages = new List<ChatMessage> { new(ChatRole.User, "Hello") };
|
||||
var context = new ChatHistoryProvider.InvokingContext(messages);
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentNullException>(() => context.RequestMessages = null!);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvokingContext_RequestMessages_SetterRoundtrips()
|
||||
{
|
||||
// Arrange
|
||||
var initialMessages = new List<ChatMessage> { new(ChatRole.User, "Hello") };
|
||||
var newMessages = new List<ChatMessage> { 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<ArgumentNullException>(() => new ChatHistoryProvider.InvokedContext(null!, []));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvokedContext_RequestMessages_SetterThrowsForNull()
|
||||
{
|
||||
// Arrange
|
||||
var requestMessages = new List<ChatMessage> { new(ChatRole.User, "Hello") };
|
||||
var context = new ChatHistoryProvider.InvokedContext(requestMessages, []);
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentNullException>(() => context.RequestMessages = null!);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvokedContext_RequestMessages_SetterRoundtrips()
|
||||
{
|
||||
// Arrange
|
||||
var initialMessages = new List<ChatMessage> { new(ChatRole.User, "Hello") };
|
||||
var newMessages = new List<ChatMessage> { 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<ChatMessage> { new(ChatRole.User, "Hello") };
|
||||
var newProviderMessages = new List<ChatMessage> { 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<ChatMessage> { new(ChatRole.User, "Hello") };
|
||||
var aiContextMessages = new List<ChatMessage> { 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<ChatMessage> { new(ChatRole.User, "Hello") };
|
||||
var responseMessages = new List<ChatMessage> { 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<ChatMessage> { 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<IEnumerable<ChatMessage>> InvokingAsync(InvokingContext context, CancellationToken cancellationToken = default)
|
||||
|
||||
@@ -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 class DelegatingAIAgentTests
|
||||
this._innerAgentMock.Verify(x => x.CreateSessionAsync(), Times.Once);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that DeserializeSessionAsync delegates to inner agent.
|
||||
/// </summary>
|
||||
[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<JsonElement>(), null, It.IsAny<CancellationToken>()))
|
||||
.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<JsonElement>(), null, It.IsAny<CancellationToken>()), Times.Once);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that RunAsync delegates to inner agent with correct parameters.
|
||||
/// </summary>
|
||||
|
||||
+36
@@ -614,6 +614,42 @@ public class InMemoryChatHistoryProviderTests
|
||||
reducerMock.Verify(r => r.ReduceAsync(It.IsAny<IEnumerable<ChatMessage>>(), It.IsAny<CancellationToken>()), Times.Never);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokedAsync_WithException_DoesNotAddMessagesAsync()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new InMemoryChatHistoryProvider();
|
||||
var requestMessages = new List<ChatMessage>
|
||||
{
|
||||
new(ChatRole.User, "Hello")
|
||||
};
|
||||
var responseMessages = new List<ChatMessage>
|
||||
{
|
||||
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<ArgumentNullException>(() => provider.InvokingAsync(null!, CancellationToken.None).AsTask());
|
||||
}
|
||||
|
||||
public class TestAIContent(string testData) : AIContent
|
||||
{
|
||||
public string TestData => testData;
|
||||
|
||||
Reference in New Issue
Block a user