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.OpenAI (#3349)
* Initial plan * Add unit tests for Microsoft.Agents.AI.OpenAI to improve code coverage Co-authored-by: rogerbarreto <19890735+rogerbarreto@users.noreply.github.com> * Address code review feedback: remove unused using directives Co-authored-by: rogerbarreto <19890735+rogerbarreto@users.noreply.github.com> * Fix format issues: file encoding and remove unused using directives Co-authored-by: rogerbarreto <19890735+rogerbarreto@users.noreply.github.com> * Fix redundant cast error by using named parameter Co-authored-by: rogerbarreto <19890735+rogerbarreto@users.noreply.github.com> * Remove excessive inline comments per PR review feedback --------- 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
9a37411dc1
commit
958e6d27ce
@@ -17,6 +17,10 @@
|
||||
<ProjectReference Include="..\Microsoft.Agents.AI\Microsoft.Agents.AI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<InternalsVisibleTo Include="Microsoft.Agents.AI.OpenAI.UnitTests" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- NuGet Package Settings -->
|
||||
<Title>Microsoft Agent Framework OpenAI</Title>
|
||||
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.ClientModel;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.AI;
|
||||
using OpenAI.Chat;
|
||||
|
||||
namespace Microsoft.Agents.AI.OpenAI.UnitTests.ChatClient;
|
||||
|
||||
/// <summary>
|
||||
/// Unit tests for the <see cref="AsyncStreamingChatCompletionUpdateCollectionResult"/> class.
|
||||
/// </summary>
|
||||
public sealed class AsyncStreamingChatCompletionUpdateCollectionResultTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Verify that GetContinuationToken returns null.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void GetContinuationToken_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
IAsyncEnumerable<AgentResponseUpdate> updates = CreateTestUpdatesAsync();
|
||||
AsyncCollectionResult<StreamingChatCompletionUpdate> collectionResult = new AsyncStreamingChatCompletionUpdateCollectionResult(updates);
|
||||
|
||||
// Act
|
||||
ContinuationToken? token = collectionResult.GetContinuationToken(null!);
|
||||
|
||||
// Assert
|
||||
Assert.Null(token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that GetRawPagesAsync returns a single page.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task GetRawPagesAsync_ReturnsSinglePageAsync()
|
||||
{
|
||||
// Arrange
|
||||
IAsyncEnumerable<AgentResponseUpdate> updates = CreateTestUpdatesAsync();
|
||||
AsyncCollectionResult<StreamingChatCompletionUpdate> collectionResult = new AsyncStreamingChatCompletionUpdateCollectionResult(updates);
|
||||
|
||||
// Act
|
||||
List<ClientResult> pages = [];
|
||||
await foreach (ClientResult page in collectionResult.GetRawPagesAsync())
|
||||
{
|
||||
pages.Add(page);
|
||||
}
|
||||
|
||||
// Assert
|
||||
Assert.Single(pages);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that iterating through the collection yields streaming updates.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task IterateCollection_YieldsUpdatesAsync()
|
||||
{
|
||||
// Arrange
|
||||
IAsyncEnumerable<AgentResponseUpdate> updates = CreateTestUpdatesAsync();
|
||||
AsyncCollectionResult<StreamingChatCompletionUpdate> collectionResult = new AsyncStreamingChatCompletionUpdateCollectionResult(updates);
|
||||
|
||||
// Act
|
||||
List<StreamingChatCompletionUpdate> results = [];
|
||||
await foreach (StreamingChatCompletionUpdate update in collectionResult)
|
||||
{
|
||||
results.Add(update);
|
||||
}
|
||||
|
||||
// Assert
|
||||
Assert.Single(results);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that iterating through the collection with multiple updates yields all updates.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task IterateCollection_WithMultipleUpdates_YieldsAllUpdatesAsync()
|
||||
{
|
||||
// Arrange
|
||||
IAsyncEnumerable<AgentResponseUpdate> updates = CreateMultipleTestUpdatesAsync();
|
||||
AsyncCollectionResult<StreamingChatCompletionUpdate> collectionResult = new AsyncStreamingChatCompletionUpdateCollectionResult(updates);
|
||||
|
||||
// Act
|
||||
List<StreamingChatCompletionUpdate> results = [];
|
||||
await foreach (StreamingChatCompletionUpdate update in collectionResult)
|
||||
{
|
||||
results.Add(update);
|
||||
}
|
||||
|
||||
// Assert
|
||||
Assert.Equal(3, results.Count);
|
||||
}
|
||||
|
||||
private static async IAsyncEnumerable<AgentResponseUpdate> CreateTestUpdatesAsync()
|
||||
{
|
||||
yield return new AgentResponseUpdate(ChatRole.Assistant, "test");
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
private static async IAsyncEnumerable<AgentResponseUpdate> CreateMultipleTestUpdatesAsync()
|
||||
{
|
||||
yield return new AgentResponseUpdate(ChatRole.Assistant, "first");
|
||||
yield return new AgentResponseUpdate(ChatRole.Assistant, "second");
|
||||
yield return new AgentResponseUpdate(ChatRole.Assistant, "third");
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.ClientModel;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.AI;
|
||||
using OpenAI.Responses;
|
||||
|
||||
namespace Microsoft.Agents.AI.OpenAI.UnitTests.ChatClient;
|
||||
|
||||
/// <summary>
|
||||
/// Unit tests for the <see cref="AsyncStreamingResponseUpdateCollectionResult"/> class.
|
||||
/// </summary>
|
||||
public sealed class AsyncStreamingResponseUpdateCollectionResultTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Verify that GetContinuationToken returns null.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void GetContinuationToken_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
IAsyncEnumerable<AgentResponseUpdate> updates = CreateTestUpdatesAsync();
|
||||
AsyncCollectionResult<StreamingResponseUpdate> collectionResult = new AsyncStreamingResponseUpdateCollectionResult(updates);
|
||||
|
||||
// Act
|
||||
ContinuationToken? token = collectionResult.GetContinuationToken(null!);
|
||||
|
||||
// Assert
|
||||
Assert.Null(token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that GetRawPagesAsync returns a single page.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task GetRawPagesAsync_ReturnsSinglePageAsync()
|
||||
{
|
||||
// Arrange
|
||||
IAsyncEnumerable<AgentResponseUpdate> updates = CreateTestUpdatesAsync();
|
||||
AsyncCollectionResult<StreamingResponseUpdate> collectionResult = new AsyncStreamingResponseUpdateCollectionResult(updates);
|
||||
|
||||
// Act
|
||||
List<ClientResult> pages = [];
|
||||
await foreach (ClientResult page in collectionResult.GetRawPagesAsync())
|
||||
{
|
||||
pages.Add(page);
|
||||
}
|
||||
|
||||
// Assert
|
||||
Assert.Single(pages);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that iterating through the collection yields streaming updates when RawRepresentation is a StreamingResponseUpdate.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task IterateCollection_WithStreamingResponseUpdateRawRepresentation_YieldsUpdatesAsync()
|
||||
{
|
||||
// Arrange
|
||||
StreamingResponseUpdate rawUpdate = CreateStreamingResponseUpdate();
|
||||
IAsyncEnumerable<AgentResponseUpdate> updates = CreateTestUpdatesWithRawRepresentationAsync(rawUpdate);
|
||||
AsyncCollectionResult<StreamingResponseUpdate> collectionResult = new AsyncStreamingResponseUpdateCollectionResult(updates);
|
||||
|
||||
// Act
|
||||
List<StreamingResponseUpdate> results = [];
|
||||
await foreach (StreamingResponseUpdate update in collectionResult)
|
||||
{
|
||||
results.Add(update);
|
||||
}
|
||||
|
||||
// Assert
|
||||
Assert.Single(results);
|
||||
Assert.Same(rawUpdate, results[0]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that iterating through the collection yields updates when RawRepresentation is a ChatResponseUpdate containing a StreamingResponseUpdate.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task IterateCollection_WithChatResponseUpdateContainingStreamingResponseUpdate_YieldsUpdatesAsync()
|
||||
{
|
||||
// Arrange
|
||||
StreamingResponseUpdate rawUpdate = CreateStreamingResponseUpdate();
|
||||
ChatResponseUpdate chatResponseUpdate = new() { RawRepresentation = rawUpdate };
|
||||
IAsyncEnumerable<AgentResponseUpdate> updates = CreateTestUpdatesWithChatResponseUpdateAsync(chatResponseUpdate);
|
||||
AsyncCollectionResult<StreamingResponseUpdate> collectionResult = new AsyncStreamingResponseUpdateCollectionResult(updates);
|
||||
|
||||
// Act
|
||||
List<StreamingResponseUpdate> results = [];
|
||||
await foreach (StreamingResponseUpdate update in collectionResult)
|
||||
{
|
||||
results.Add(update);
|
||||
}
|
||||
|
||||
// Assert
|
||||
Assert.Single(results);
|
||||
Assert.Same(rawUpdate, results[0]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that iterating through the collection skips updates when RawRepresentation is not a StreamingResponseUpdate.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task IterateCollection_WithNonStreamingResponseUpdateRawRepresentation_SkipsUpdateAsync()
|
||||
{
|
||||
// Arrange
|
||||
IAsyncEnumerable<AgentResponseUpdate> updates = CreateTestUpdatesAsync();
|
||||
AsyncCollectionResult<StreamingResponseUpdate> collectionResult = new AsyncStreamingResponseUpdateCollectionResult(updates);
|
||||
|
||||
// Act
|
||||
List<StreamingResponseUpdate> results = [];
|
||||
await foreach (StreamingResponseUpdate update in collectionResult)
|
||||
{
|
||||
results.Add(update);
|
||||
}
|
||||
|
||||
// Assert
|
||||
Assert.Empty(results);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that iterating through the collection skips updates when RawRepresentation is a ChatResponseUpdate without StreamingResponseUpdate.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task IterateCollection_WithChatResponseUpdateWithoutStreamingResponseUpdate_SkipsUpdateAsync()
|
||||
{
|
||||
// Arrange
|
||||
ChatResponseUpdate chatResponseUpdate = new() { RawRepresentation = "not a streaming update" };
|
||||
IAsyncEnumerable<AgentResponseUpdate> updates = CreateTestUpdatesWithChatResponseUpdateAsync(chatResponseUpdate);
|
||||
AsyncCollectionResult<StreamingResponseUpdate> collectionResult = new AsyncStreamingResponseUpdateCollectionResult(updates);
|
||||
|
||||
// Act
|
||||
List<StreamingResponseUpdate> results = [];
|
||||
await foreach (StreamingResponseUpdate update in collectionResult)
|
||||
{
|
||||
results.Add(update);
|
||||
}
|
||||
|
||||
// Assert
|
||||
Assert.Empty(results);
|
||||
}
|
||||
|
||||
private static async IAsyncEnumerable<AgentResponseUpdate> CreateTestUpdatesAsync()
|
||||
{
|
||||
yield return new AgentResponseUpdate(ChatRole.Assistant, "test");
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
private static async IAsyncEnumerable<AgentResponseUpdate> CreateTestUpdatesWithRawRepresentationAsync(object rawRepresentation)
|
||||
{
|
||||
AgentResponseUpdate update = new(ChatRole.Assistant, "test")
|
||||
{
|
||||
RawRepresentation = rawRepresentation
|
||||
};
|
||||
yield return update;
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
private static async IAsyncEnumerable<AgentResponseUpdate> CreateTestUpdatesWithChatResponseUpdateAsync(ChatResponseUpdate chatResponseUpdate)
|
||||
{
|
||||
AgentResponseUpdate update = new(ChatRole.Assistant, "test")
|
||||
{
|
||||
RawRepresentation = chatResponseUpdate
|
||||
};
|
||||
yield return update;
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
private static StreamingResponseUpdate CreateStreamingResponseUpdate()
|
||||
{
|
||||
const string Json = """
|
||||
{
|
||||
"type": "response.output_item.added",
|
||||
"sequence_number": 1,
|
||||
"output_index": 0,
|
||||
"item": {
|
||||
"id": "item_abc123",
|
||||
"type": "message",
|
||||
"status": "in_progress",
|
||||
"role": "assistant",
|
||||
"content": []
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
return System.ClientModel.Primitives.ModelReaderWriter.Read<StreamingResponseUpdate>(BinaryData.FromString(Json))!;
|
||||
}
|
||||
}
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.ClientModel.Primitives;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.Agents.AI.OpenAI.UnitTests.ChatClient;
|
||||
|
||||
/// <summary>
|
||||
/// Unit tests for the <see cref="StreamingUpdatePipelineResponse"/> class.
|
||||
/// </summary>
|
||||
public sealed class StreamingUpdatePipelineResponseTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Verify that Status property returns 200.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Status_ReturnsOkStatus()
|
||||
{
|
||||
// Arrange
|
||||
IAsyncEnumerable<AgentResponseUpdate> updates = CreateTestUpdatesAsync();
|
||||
PipelineResponse response = new StreamingUpdatePipelineResponse(updates);
|
||||
|
||||
// Act
|
||||
int status = response.Status;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(200, status);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that ReasonPhrase property returns "OK".
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ReasonPhrase_ReturnsOk()
|
||||
{
|
||||
// Arrange
|
||||
IAsyncEnumerable<AgentResponseUpdate> updates = CreateTestUpdatesAsync();
|
||||
PipelineResponse response = new StreamingUpdatePipelineResponse(updates);
|
||||
|
||||
// Act
|
||||
string reasonPhrase = response.ReasonPhrase;
|
||||
|
||||
// Assert
|
||||
Assert.Equal("OK", reasonPhrase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that ContentStream getter returns null.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ContentStream_Get_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
IAsyncEnumerable<AgentResponseUpdate> updates = CreateTestUpdatesAsync();
|
||||
PipelineResponse response = new StreamingUpdatePipelineResponse(updates);
|
||||
|
||||
// Act
|
||||
System.IO.Stream? contentStream = response.ContentStream;
|
||||
|
||||
// Assert
|
||||
Assert.Null(contentStream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that ContentStream setter is a no-op.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ContentStream_Set_IsNoOp()
|
||||
{
|
||||
// Arrange
|
||||
IAsyncEnumerable<AgentResponseUpdate> updates = CreateTestUpdatesAsync();
|
||||
PipelineResponse response = new StreamingUpdatePipelineResponse(updates);
|
||||
var testStream = new System.IO.MemoryStream();
|
||||
|
||||
// Act
|
||||
response.ContentStream = testStream;
|
||||
|
||||
// Assert
|
||||
Assert.Null(response.ContentStream);
|
||||
|
||||
testStream.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that Content property returns empty BinaryData.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Content_ReturnsEmptyBinaryData()
|
||||
{
|
||||
// Arrange
|
||||
IAsyncEnumerable<AgentResponseUpdate> updates = CreateTestUpdatesAsync();
|
||||
PipelineResponse response = new StreamingUpdatePipelineResponse(updates);
|
||||
|
||||
// Act
|
||||
BinaryData content = response.Content;
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(content);
|
||||
Assert.Equal(string.Empty, content.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that BufferContent throws NotSupportedException.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void BufferContent_ThrowsNotSupportedException()
|
||||
{
|
||||
// Arrange
|
||||
IAsyncEnumerable<AgentResponseUpdate> updates = CreateTestUpdatesAsync();
|
||||
PipelineResponse response = new StreamingUpdatePipelineResponse(updates);
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<NotSupportedException>(() => response.BufferContent());
|
||||
Assert.Contains("Buffering content is not supported", exception.Message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that BufferContentAsync throws NotSupportedException.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task BufferContentAsync_ThrowsNotSupportedExceptionAsync()
|
||||
{
|
||||
// Arrange
|
||||
IAsyncEnumerable<AgentResponseUpdate> updates = CreateTestUpdatesAsync();
|
||||
PipelineResponse response = new StreamingUpdatePipelineResponse(updates);
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<NotSupportedException>(
|
||||
async () => await response.BufferContentAsync());
|
||||
Assert.Contains("Buffering content asynchronously is not supported", exception.Message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that Dispose does not throw.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Dispose_DoesNotThrow()
|
||||
{
|
||||
// Arrange
|
||||
IAsyncEnumerable<AgentResponseUpdate> updates = CreateTestUpdatesAsync();
|
||||
PipelineResponse response = new StreamingUpdatePipelineResponse(updates);
|
||||
|
||||
// Act & Assert
|
||||
response.Dispose();
|
||||
}
|
||||
|
||||
private static async IAsyncEnumerable<AgentResponseUpdate> CreateTestUpdatesAsync()
|
||||
{
|
||||
yield return new AgentResponseUpdate(Microsoft.Extensions.AI.ChatRole.Assistant, "test");
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
+164
@@ -7,6 +7,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Moq;
|
||||
using Moq.Protected;
|
||||
using OpenAI.Responses;
|
||||
using ChatMessage = Microsoft.Extensions.AI.ChatMessage;
|
||||
using ChatRole = Microsoft.Extensions.AI.ChatRole;
|
||||
using OpenAIChatMessage = OpenAI.Chat.ChatMessage;
|
||||
@@ -208,4 +209,167 @@ public sealed class AIAgentWithOpenAIExtensionsTests
|
||||
yield return await Task.FromResult(update);
|
||||
}
|
||||
}
|
||||
|
||||
#region ResponseItem overload tests
|
||||
|
||||
/// <summary>
|
||||
/// Verify that RunAsync with ResponseItem throws ArgumentNullException when agent is null.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task RunAsync_ResponseItem_WithNullAgent_ThrowsArgumentNullExceptionAsync()
|
||||
{
|
||||
// Arrange
|
||||
AIAgent? agent = null;
|
||||
IEnumerable<ResponseItem> messages = [ResponseItem.CreateUserMessageItem("Test message")];
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
|
||||
() => agent!.RunAsync(messages));
|
||||
|
||||
Assert.Equal("agent", exception.ParamName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that RunAsync with ResponseItem throws ArgumentNullException when messages is null.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task RunAsync_ResponseItem_WithNullMessages_ThrowsArgumentNullExceptionAsync()
|
||||
{
|
||||
// Arrange
|
||||
var mockAgent = new Mock<AIAgent>();
|
||||
IEnumerable<ResponseItem>? messages = null;
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
|
||||
() => mockAgent.Object.RunAsync(messages!));
|
||||
|
||||
Assert.Equal("messages", exception.ParamName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that the RunAsync with ResponseItem extension method calls the underlying agent's RunAsync with converted messages and parameters.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task RunAsync_ResponseItem_CallsUnderlyingAgentAsync()
|
||||
{
|
||||
// Arrange
|
||||
var mockAgent = new Mock<AIAgent>();
|
||||
var mockThread = new Mock<AgentThread>();
|
||||
var options = new AgentRunOptions();
|
||||
var cancellationToken = new CancellationToken(false);
|
||||
const string TestMessageText = "Hello, assistant!";
|
||||
const string ResponseText = "This is the assistant's response.";
|
||||
IEnumerable<ResponseItem> responseItemMessages = [ResponseItem.CreateUserMessageItem(TestMessageText)];
|
||||
|
||||
var responseMessage = new ChatMessage(ChatRole.Assistant, [new TextContent(ResponseText)]);
|
||||
|
||||
mockAgent
|
||||
.Protected()
|
||||
.Setup<Task<AgentResponse>>("RunCoreAsync",
|
||||
ItExpr.IsAny<IEnumerable<ChatMessage>>(),
|
||||
ItExpr.IsAny<AgentThread?>(),
|
||||
ItExpr.IsAny<AgentRunOptions?>(),
|
||||
ItExpr.IsAny<CancellationToken>())
|
||||
.ReturnsAsync(new AgentResponse([responseMessage]));
|
||||
|
||||
// Act
|
||||
ResponseResult result = await mockAgent.Object.RunAsync(responseItemMessages, mockThread.Object, options, cancellationToken);
|
||||
|
||||
// Assert
|
||||
mockAgent.Protected()
|
||||
.Verify("RunCoreAsync",
|
||||
Times.Once(),
|
||||
ItExpr.IsAny<IEnumerable<ChatMessage>>(),
|
||||
mockThread.Object,
|
||||
options,
|
||||
cancellationToken
|
||||
);
|
||||
|
||||
Assert.NotNull(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that RunStreamingAsync with ResponseItem throws ArgumentNullException when agent is null.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void RunStreamingAsync_ResponseItem_WithNullAgent_ThrowsArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
AIAgent? agent = null;
|
||||
IEnumerable<ResponseItem> messages = [ResponseItem.CreateUserMessageItem("Test message")];
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentNullException>(
|
||||
"agent",
|
||||
() => agent!.RunStreamingAsync(messages));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that RunStreamingAsync with ResponseItem throws ArgumentNullException when messages is null.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void RunStreamingAsync_ResponseItem_WithNullMessages_ThrowsArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
var mockAgent = new Mock<AIAgent>();
|
||||
IEnumerable<ResponseItem>? messages = null;
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<ArgumentNullException>(
|
||||
() => mockAgent.Object.RunStreamingAsync(messages!));
|
||||
|
||||
Assert.Equal("messages", exception.ParamName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that the RunStreamingAsync with ResponseItem extension method calls the underlying agent's RunStreamingAsync with converted messages and parameters.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task RunStreamingAsync_ResponseItem_CallsUnderlyingAgentAsync()
|
||||
{
|
||||
// Arrange
|
||||
var mockAgent = new Mock<AIAgent>();
|
||||
var mockThread = new Mock<AgentThread>();
|
||||
var options = new AgentRunOptions();
|
||||
var cancellationToken = new CancellationToken(false);
|
||||
const string TestMessageText = "Hello, assistant!";
|
||||
const string ResponseText1 = "This is ";
|
||||
const string ResponseText2 = "the assistant's response.";
|
||||
IEnumerable<ResponseItem> responseItemMessages = [ResponseItem.CreateUserMessageItem(TestMessageText)];
|
||||
|
||||
var responseUpdates = new List<AgentResponseUpdate>
|
||||
{
|
||||
new(ChatRole.Assistant, ResponseText1),
|
||||
new(ChatRole.Assistant, ResponseText2)
|
||||
};
|
||||
|
||||
mockAgent
|
||||
.Protected()
|
||||
.Setup<IAsyncEnumerable<AgentResponseUpdate>>("RunCoreStreamingAsync",
|
||||
ItExpr.IsAny<IEnumerable<ChatMessage>>(),
|
||||
ItExpr.IsAny<AgentThread?>(),
|
||||
ItExpr.IsAny<AgentRunOptions?>(),
|
||||
ItExpr.IsAny<CancellationToken>())
|
||||
.Returns(ToAsyncEnumerableAsync(responseUpdates));
|
||||
|
||||
// Act
|
||||
var result = mockAgent.Object.RunStreamingAsync(responseItemMessages, mockThread.Object, options, cancellationToken);
|
||||
var updateCount = 0;
|
||||
await foreach (var update in result)
|
||||
{
|
||||
updateCount++;
|
||||
}
|
||||
|
||||
// Assert
|
||||
mockAgent.Protected()
|
||||
.Verify("RunCoreStreamingAsync",
|
||||
Times.Once(),
|
||||
ItExpr.IsAny<IEnumerable<ChatMessage>>(),
|
||||
mockThread.Object,
|
||||
options,
|
||||
cancellationToken
|
||||
);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using OpenAI.Chat;
|
||||
using ChatMessage = Microsoft.Extensions.AI.ChatMessage;
|
||||
using ChatRole = Microsoft.Extensions.AI.ChatRole;
|
||||
using TextContent = Microsoft.Extensions.AI.TextContent;
|
||||
|
||||
namespace Microsoft.Agents.AI.OpenAI.UnitTests.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Unit tests for the AgentResponseExtensions class that provides OpenAI extension methods.
|
||||
/// </summary>
|
||||
public sealed class AgentResponseExtensionsTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Verify that AsOpenAIChatCompletion throws ArgumentNullException when response is null.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void AsOpenAIChatCompletion_WithNullResponse_ThrowsArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
AgentResponse? response = null;
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<ArgumentNullException>(
|
||||
() => response!.AsOpenAIChatCompletion());
|
||||
|
||||
Assert.Equal("response", exception.ParamName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that AsOpenAIChatCompletion returns the RawRepresentation when it is a ChatCompletion.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void AsOpenAIChatCompletion_WithChatCompletionRawRepresentation_ReturnsChatCompletion()
|
||||
{
|
||||
// Arrange
|
||||
ChatCompletion chatCompletion = ModelReaderWriterHelper.CreateChatCompletion("assistant_id", "Hello");
|
||||
var responseMessage = new ChatMessage(ChatRole.Assistant, [new TextContent("Hello")]);
|
||||
var agentResponse = new AgentResponse([responseMessage])
|
||||
{
|
||||
RawRepresentation = chatCompletion
|
||||
};
|
||||
|
||||
// Act
|
||||
ChatCompletion result = agentResponse.AsOpenAIChatCompletion();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Same(chatCompletion, result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that AsOpenAIChatCompletion converts a ChatResponse when RawRepresentation is not a ChatCompletion.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void AsOpenAIChatCompletion_WithNonChatCompletionRawRepresentation_ConvertsChatResponse()
|
||||
{
|
||||
// Arrange
|
||||
const string ResponseText = "This is a test response.";
|
||||
var responseMessage = new ChatMessage(ChatRole.Assistant, [new TextContent(ResponseText)]);
|
||||
var agentResponse = new AgentResponse([responseMessage]);
|
||||
|
||||
// Act
|
||||
ChatCompletion result = agentResponse.AsOpenAIChatCompletion();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Single(result.Content);
|
||||
Assert.Equal(ResponseText, result.Content[0].Text);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that AsOpenAIResponse throws ArgumentNullException when response is null.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void AsOpenAIResponse_WithNullResponse_ThrowsArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
AgentResponse? response = null;
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<ArgumentNullException>(
|
||||
() => response!.AsOpenAIResponse());
|
||||
|
||||
Assert.Equal("response", exception.ParamName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that AsOpenAIResponse converts a ChatResponse when RawRepresentation is not a ResponseResult.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void AsOpenAIResponse_WithNonResponseResultRawRepresentation_ConvertsChatResponse()
|
||||
{
|
||||
// Arrange
|
||||
const string ResponseText = "This is a test response.";
|
||||
var responseMessage = new ChatMessage(ChatRole.Assistant, [new TextContent(ResponseText)]);
|
||||
var agentResponse = new AgentResponse([responseMessage]);
|
||||
|
||||
// Act
|
||||
var result = agentResponse.AsOpenAIResponse();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper class for creating OpenAI model objects using ModelReaderWriter.
|
||||
/// </summary>
|
||||
internal static class ModelReaderWriterHelper
|
||||
{
|
||||
public static ChatCompletion CreateChatCompletion(string id, string contentText)
|
||||
{
|
||||
string json = $$"""
|
||||
{
|
||||
"id": "{{id}}",
|
||||
"object": "chat.completion",
|
||||
"created": 1700000000,
|
||||
"model": "gpt-4",
|
||||
"choices": [
|
||||
{
|
||||
"index": 0,
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": "{{contentText}}"
|
||||
},
|
||||
"finish_reason": "stop"
|
||||
}
|
||||
],
|
||||
"usage": {
|
||||
"prompt_tokens": 10,
|
||||
"completion_tokens": 10,
|
||||
"total_tokens": 20
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
return System.ClientModel.Primitives.ModelReaderWriter.Read<ChatCompletion>(BinaryData.FromString(json))!;
|
||||
}
|
||||
}
|
||||
+381
@@ -569,6 +569,387 @@ public sealed class OpenAIAssistantClientExtensionsTests
|
||||
return property?.GetValue(client) as IServiceProvider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that CreateAIAgentAsync with HostedCodeInterpreterTool properly adds CodeInterpreter tool definition.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task CreateAIAgentAsync_WithHostedCodeInterpreterTool_CreatesAgentWithToolAsync()
|
||||
{
|
||||
// Arrange
|
||||
var assistantClient = new TestAssistantClient();
|
||||
const string ModelId = "test-model";
|
||||
var options = new ChatClientAgentOptions
|
||||
{
|
||||
Name = "Test Agent",
|
||||
ChatOptions = new ChatOptions
|
||||
{
|
||||
Instructions = "Test instructions",
|
||||
Tools = [new HostedCodeInterpreterTool()]
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var agent = await assistantClient.CreateAIAgentAsync(ModelId, options);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(agent);
|
||||
Assert.Equal("Test Agent", agent.Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that CreateAIAgentAsync with HostedCodeInterpreterTool with HostedFileContent input properly creates agent.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task CreateAIAgentAsync_WithHostedCodeInterpreterToolAndHostedFileContent_CreatesAgentWithToolResourcesAsync()
|
||||
{
|
||||
// Arrange
|
||||
var assistantClient = new TestAssistantClient();
|
||||
const string ModelId = "test-model";
|
||||
var codeInterpreterTool = new HostedCodeInterpreterTool
|
||||
{
|
||||
Inputs = [new HostedFileContent("test-file-id")]
|
||||
};
|
||||
var options = new ChatClientAgentOptions
|
||||
{
|
||||
Name = "Test Agent",
|
||||
ChatOptions = new ChatOptions
|
||||
{
|
||||
Instructions = "Test instructions",
|
||||
Tools = [codeInterpreterTool]
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var agent = await assistantClient.CreateAIAgentAsync(ModelId, options);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(agent);
|
||||
Assert.Equal("Test Agent", agent.Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that CreateAIAgentAsync with HostedFileSearchTool properly adds FileSearch tool definition.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task CreateAIAgentAsync_WithHostedFileSearchTool_CreatesAgentWithToolAsync()
|
||||
{
|
||||
// Arrange
|
||||
var assistantClient = new TestAssistantClient();
|
||||
const string ModelId = "test-model";
|
||||
var options = new ChatClientAgentOptions
|
||||
{
|
||||
Name = "Test Agent",
|
||||
ChatOptions = new ChatOptions
|
||||
{
|
||||
Instructions = "Test instructions",
|
||||
Tools = [new HostedFileSearchTool()]
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var agent = await assistantClient.CreateAIAgentAsync(ModelId, options);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(agent);
|
||||
Assert.Equal("Test Agent", agent.Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that CreateAIAgentAsync with HostedFileSearchTool with HostedVectorStoreContent input properly creates agent.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task CreateAIAgentAsync_WithHostedFileSearchToolAndHostedVectorStoreContent_CreatesAgentWithToolResourcesAsync()
|
||||
{
|
||||
// Arrange
|
||||
var assistantClient = new TestAssistantClient();
|
||||
const string ModelId = "test-model";
|
||||
var fileSearchTool = new HostedFileSearchTool
|
||||
{
|
||||
MaximumResultCount = 10,
|
||||
Inputs = [new HostedVectorStoreContent("test-vector-store-id")]
|
||||
};
|
||||
var options = new ChatClientAgentOptions
|
||||
{
|
||||
Name = "Test Agent",
|
||||
ChatOptions = new ChatOptions
|
||||
{
|
||||
Instructions = "Test instructions",
|
||||
Tools = [fileSearchTool]
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var agent = await assistantClient.CreateAIAgentAsync(ModelId, options);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(agent);
|
||||
Assert.Equal("Test Agent", agent.Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that CreateAIAgentAsync with multiple tools including functions properly creates agent.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task CreateAIAgentAsync_WithMixedTools_CreatesAgentWithAllToolsAsync()
|
||||
{
|
||||
// Arrange
|
||||
var assistantClient = new TestAssistantClient();
|
||||
const string ModelId = "test-model";
|
||||
var testFunction = AIFunctionFactory.Create(() => "test", "TestFunction", "A test function");
|
||||
var options = new ChatClientAgentOptions
|
||||
{
|
||||
Name = "Test Agent",
|
||||
ChatOptions = new ChatOptions
|
||||
{
|
||||
Instructions = "Test instructions",
|
||||
Tools = [new HostedCodeInterpreterTool(), new HostedFileSearchTool(), testFunction]
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var agent = await assistantClient.CreateAIAgentAsync(ModelId, options);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(agent);
|
||||
Assert.Equal("Test Agent", agent.Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that CreateAIAgentAsync with function tools properly categorizes them as other tools.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task CreateAIAgentAsync_WithFunctionTools_CategorizesAsOtherToolsAsync()
|
||||
{
|
||||
// Arrange
|
||||
var assistantClient = new TestAssistantClient();
|
||||
const string ModelId = "test-model";
|
||||
var testFunction = AIFunctionFactory.Create(() => "test", "TestFunction", "A test function");
|
||||
var options = new ChatClientAgentOptions
|
||||
{
|
||||
Name = "Test Agent",
|
||||
ChatOptions = new ChatOptions
|
||||
{
|
||||
Instructions = "Test instructions",
|
||||
Tools = [testFunction]
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var agent = await assistantClient.CreateAIAgentAsync(ModelId, options);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(agent);
|
||||
Assert.Equal("Test Agent", agent.Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that AsAIAgent with legacy overload works correctly when assistant instructions are set.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void AsAIAgent_LegacyOverload_WithAssistantInstructions_SetsInstructions()
|
||||
{
|
||||
// Arrange
|
||||
var assistantClient = new TestAssistantClient();
|
||||
var assistant = ModelReaderWriter.Read<Assistant>(BinaryData.FromString("""{"id": "asst_abc123", "name": "Test Agent", "instructions": "Original Instructions"}"""))!;
|
||||
|
||||
// Act
|
||||
var agent = assistantClient.AsAIAgent(assistant);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(agent);
|
||||
Assert.Equal("Test Agent", agent.Name);
|
||||
Assert.Equal("Original Instructions", agent.Instructions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that AsAIAgent with legacy overload works correctly when chatOptions with instructions is provided.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void AsAIAgent_LegacyOverload_WithChatOptionsInstructions_UsesChatOptionsInstructions()
|
||||
{
|
||||
// Arrange
|
||||
var assistantClient = new TestAssistantClient();
|
||||
var assistant = ModelReaderWriter.Read<Assistant>(BinaryData.FromString("""{"id": "asst_abc123", "name": "Test Agent", "instructions": "Original Instructions"}"""))!;
|
||||
var chatOptions = new ChatOptions { Instructions = "Override Instructions" };
|
||||
|
||||
// Act
|
||||
var agent = assistantClient.AsAIAgent(assistant, chatOptions);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(agent);
|
||||
Assert.Equal("Test Agent", agent.Name);
|
||||
Assert.Equal("Override Instructions", agent.Instructions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that AsAIAgent with legacy overload and ClientResult works correctly.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void AsAIAgent_LegacyOverload_WithClientResult_WorksCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var assistantClient = new TestAssistantClient();
|
||||
var assistant = ModelReaderWriter.Read<Assistant>(BinaryData.FromString("""{"id": "asst_abc123", "name": "Test Agent", "instructions": "Original Instructions"}"""))!;
|
||||
var clientResult = ClientResult.FromValue(assistant, new FakePipelineResponse());
|
||||
|
||||
// Act
|
||||
var agent = assistantClient.AsAIAgent(clientResult);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(agent);
|
||||
Assert.Equal("Test Agent", agent.Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that AsAIAgent with legacy overload throws ArgumentNullException when assistant client is null.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void AsAIAgent_LegacyOverload_WithNullAssistantClient_ThrowsArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
AssistantClient? assistantClient = null;
|
||||
var assistant = ModelReaderWriter.Read<Assistant>(BinaryData.FromString("""{"id": "asst_abc123"}"""))!;
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<ArgumentNullException>(() =>
|
||||
assistantClient!.AsAIAgent(assistant));
|
||||
|
||||
Assert.Equal("assistantClient", exception.ParamName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that AsAIAgent with legacy overload throws ArgumentNullException when assistantMetadata is null.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void AsAIAgent_LegacyOverload_WithNullAssistantMetadata_ThrowsArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
var assistantClient = new TestAssistantClient();
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<ArgumentNullException>(() =>
|
||||
assistantClient.AsAIAgent((Assistant)null!));
|
||||
|
||||
Assert.Equal("assistantMetadata", exception.ParamName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that AsAIAgent with legacy overload throws ArgumentNullException when clientResult is null.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void AsAIAgent_LegacyOverload_WithNullClientResult_ThrowsArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
var assistantClient = new TestAssistantClient();
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<ArgumentNullException>(() =>
|
||||
assistantClient.AsAIAgent(null!, chatOptions: null));
|
||||
|
||||
Assert.Equal("assistantClientResult", exception.ParamName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that GetAIAgentAsync with legacy overload works correctly.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task GetAIAgentAsync_LegacyOverload_WorksCorrectlyAsync()
|
||||
{
|
||||
// Arrange
|
||||
var assistantClient = new TestAssistantClient();
|
||||
const string AgentId = "asst_abc123";
|
||||
|
||||
// Act
|
||||
var agent = await assistantClient.GetAIAgentAsync(AgentId);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(agent);
|
||||
Assert.Equal("Original Name", agent.Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that GetAIAgentAsync with legacy overload throws ArgumentNullException when assistantClient is null.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task GetAIAgentAsync_LegacyOverload_WithNullAssistantClient_ThrowsArgumentNullExceptionAsync()
|
||||
{
|
||||
// Arrange
|
||||
AssistantClient? assistantClient = null;
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<ArgumentNullException>(() =>
|
||||
assistantClient!.GetAIAgentAsync("asst_abc123"));
|
||||
|
||||
Assert.Equal("assistantClient", exception.ParamName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that GetAIAgentAsync with legacy overload throws ArgumentException when agentId is empty.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task GetAIAgentAsync_LegacyOverload_WithEmptyAgentId_ThrowsArgumentExceptionAsync()
|
||||
{
|
||||
// Arrange
|
||||
var assistantClient = new TestAssistantClient();
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<ArgumentException>(() =>
|
||||
assistantClient.GetAIAgentAsync(string.Empty));
|
||||
|
||||
Assert.Equal("agentId", exception.ParamName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that GetAIAgentAsync with options throws ArgumentNullException when assistantClient is null.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task GetAIAgentAsync_WithOptions_WithNullAssistantClient_ThrowsArgumentNullExceptionAsync()
|
||||
{
|
||||
// Arrange
|
||||
AssistantClient? assistantClient = null;
|
||||
var options = new ChatClientAgentOptions();
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<ArgumentNullException>(() =>
|
||||
assistantClient!.GetAIAgentAsync("asst_abc123", options));
|
||||
|
||||
Assert.Equal("assistantClient", exception.ParamName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that GetAIAgentAsync with options throws ArgumentNullException when options is null.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task GetAIAgentAsync_WithOptions_WithNullOptions_ThrowsArgumentNullExceptionAsync()
|
||||
{
|
||||
// Arrange
|
||||
var assistantClient = new TestAssistantClient();
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<ArgumentNullException>(() =>
|
||||
assistantClient.GetAIAgentAsync("asst_abc123", (ChatClientAgentOptions)null!));
|
||||
|
||||
Assert.Equal("options", exception.ParamName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that AsAIAgent with options throws ArgumentNullException when assistantClient is null.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void AsAIAgent_WithOptions_WithNullAssistantClient_ThrowsArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
AssistantClient? assistantClient = null;
|
||||
var assistant = ModelReaderWriter.Read<Assistant>(BinaryData.FromString("""{"id": "asst_abc123"}"""))!;
|
||||
var options = new ChatClientAgentOptions();
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<ArgumentNullException>(() =>
|
||||
assistantClient!.AsAIAgent(assistant, options));
|
||||
|
||||
Assert.Equal("assistantClient", exception.ParamName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a test AssistantClient implementation for testing.
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user