// Copyright (c) Microsoft. All rights reserved. using System; using System.ClientModel; using System.ClientModel.Primitives; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Azure.AI.Projects; using Azure.AI.Projects.OpenAI; using Microsoft.Extensions.AI; using Moq; using OpenAI.Responses; namespace Microsoft.Agents.AI.AzureAI.UnitTests; /// /// Unit tests for the class. /// public sealed class AzureAIProjectChatClientExtensionsTests { #region GetAIAgent(AIProjectClient, AgentRecord) Tests /// /// Verify that GetAIAgent throws ArgumentNullException when AIProjectClient is null. /// [Fact] public void GetAIAgent_WithAgentRecord_WithNullClient_ThrowsArgumentNullException() { // Arrange AIProjectClient? client = null; AgentRecord agentRecord = this.CreateTestAgentRecord(); // Act & Assert var exception = Assert.Throws(() => client!.GetAIAgent(agentRecord)); Assert.Equal("aiProjectClient", exception.ParamName); } /// /// Verify that GetAIAgent throws ArgumentNullException when agentRecord is null. /// [Fact] public void GetAIAgent_WithAgentRecord_WithNullAgentRecord_ThrowsArgumentNullException() { // Arrange var mockClient = new Mock(); // Act & Assert var exception = Assert.Throws(() => mockClient.Object.GetAIAgent((AgentRecord)null!)); Assert.Equal("agentRecord", exception.ParamName); } /// /// Verify that GetAIAgent with AgentRecord creates a valid agent. /// [Fact] public void GetAIAgent_WithAgentRecord_CreatesValidAgent() { // Arrange AIProjectClient client = this.CreateTestAgentClient(); AgentRecord agentRecord = this.CreateTestAgentRecord(); // Act var agent = client.GetAIAgent(agentRecord); // Assert Assert.NotNull(agent); Assert.Equal("agent_abc123", agent.Name); } /// /// Verify that GetAIAgent with AgentRecord and clientFactory applies the factory. /// [Fact] public void GetAIAgent_WithAgentRecord_WithClientFactory_AppliesFactoryCorrectly() { // Arrange AIProjectClient client = this.CreateTestAgentClient(); AgentRecord agentRecord = this.CreateTestAgentRecord(); TestChatClient? testChatClient = null; // Act var agent = client.GetAIAgent( agentRecord, clientFactory: (innerClient) => testChatClient = new TestChatClient(innerClient)); // Assert Assert.NotNull(agent); var retrievedTestClient = agent.GetService(); Assert.NotNull(retrievedTestClient); Assert.Same(testChatClient, retrievedTestClient); } #endregion #region GetAIAgent(AIProjectClient, AgentVersion) Tests /// /// Verify that GetAIAgent throws ArgumentNullException when AIProjectClient is null. /// [Fact] public void GetAIAgent_WithAgentVersion_WithNullClient_ThrowsArgumentNullException() { // Arrange AIProjectClient? client = null; AgentVersion agentVersion = this.CreateTestAgentVersion(); // Act & Assert var exception = Assert.Throws(() => client!.GetAIAgent(agentVersion)); Assert.Equal("aiProjectClient", exception.ParamName); } /// /// Verify that GetAIAgent throws ArgumentNullException when agentVersion is null. /// [Fact] public void GetAIAgent_WithAgentVersion_WithNullAgentVersion_ThrowsArgumentNullException() { // Arrange var mockClient = new Mock(); // Act & Assert var exception = Assert.Throws(() => mockClient.Object.GetAIAgent((AgentVersion)null!)); Assert.Equal("agentVersion", exception.ParamName); } /// /// Verify that GetAIAgent with AgentVersion creates a valid agent. /// [Fact] public void GetAIAgent_WithAgentVersion_CreatesValidAgent() { // Arrange AIProjectClient client = this.CreateTestAgentClient(); AgentVersion agentVersion = this.CreateTestAgentVersion(); // Act var agent = client.GetAIAgent(agentVersion); // Assert Assert.NotNull(agent); Assert.Equal("agent_abc123", agent.Name); } /// /// Verify that GetAIAgent with AgentVersion and clientFactory applies the factory. /// [Fact] public void GetAIAgent_WithAgentVersion_WithClientFactory_AppliesFactoryCorrectly() { // Arrange AIProjectClient client = this.CreateTestAgentClient(); AgentVersion agentVersion = this.CreateTestAgentVersion(); TestChatClient? testChatClient = null; // Act var agent = client.GetAIAgent( agentVersion, clientFactory: (innerClient) => testChatClient = new TestChatClient(innerClient)); // Assert Assert.NotNull(agent); var retrievedTestClient = agent.GetService(); Assert.NotNull(retrievedTestClient); Assert.Same(testChatClient, retrievedTestClient); } /// /// Verify that GetAIAgent with requireInvocableTools=true enforces invocable tools. /// [Fact] public void GetAIAgent_WithAgentVersion_WithRequireInvocableToolsTrue_EnforcesInvocableTools() { // Arrange AIProjectClient client = this.CreateTestAgentClient(); AgentVersion agentVersion = this.CreateTestAgentVersion(); var tools = new List { AIFunctionFactory.Create(() => "test", "test_function", "A test function") }; // Act var agent = client.GetAIAgent(agentVersion, tools: tools); // Assert Assert.NotNull(agent); Assert.IsType(agent); } /// /// Verify that GetAIAgent with requireInvocableTools=false allows declarative functions. /// [Fact] public void GetAIAgent_WithAgentVersion_WithRequireInvocableToolsFalse_AllowsDeclarativeFunctions() { // Arrange AIProjectClient client = this.CreateTestAgentClient(); AgentVersion agentVersion = this.CreateTestAgentVersion(); // Act - should not throw even without tools when requireInvocableTools is false var agent = client.GetAIAgent(agentVersion); // Assert Assert.NotNull(agent); Assert.IsType(agent); } #endregion #region GetAIAgent(AIProjectClient, ChatClientAgentOptions) Tests /// /// Verify that GetAIAgent with ChatClientAgentOptions throws ArgumentNullException when client is null. /// [Fact] public void GetAIAgent_WithOptions_WithNullClient_ThrowsArgumentNullException() { // Arrange AIProjectClient? client = null; var options = new ChatClientAgentOptions { Name = "test-agent" }; // Act & Assert var exception = Assert.Throws(() => client!.GetAIAgent(options)); Assert.Equal("aiProjectClient", exception.ParamName); } /// /// Verify that GetAIAgent with ChatClientAgentOptions throws ArgumentNullException when options is null. /// [Fact] public void GetAIAgent_WithOptions_WithNullOptions_ThrowsArgumentNullException() { // Arrange var mockClient = new Mock(); // Act & Assert var exception = Assert.Throws(() => mockClient.Object.GetAIAgent((ChatClientAgentOptions)null!)); Assert.Equal("options", exception.ParamName); } /// /// Verify that GetAIAgent with ChatClientAgentOptions throws ArgumentException when options.Name is null. /// [Fact] public void GetAIAgent_WithOptions_WithoutName_ThrowsArgumentException() { // Arrange AIProjectClient client = this.CreateTestAgentClient(); var options = new ChatClientAgentOptions(); // Act & Assert var exception = Assert.Throws(() => client.GetAIAgent(options)); Assert.Contains("Agent name must be provided", exception.Message); } /// /// Verify that GetAIAgent with ChatClientAgentOptions creates a valid agent. /// [Fact] public void GetAIAgent_WithOptions_CreatesValidAgent() { // Arrange AIProjectClient client = this.CreateTestAgentClient(agentName: "test-agent"); var options = new ChatClientAgentOptions { Name = "test-agent" }; // Act var agent = client.GetAIAgent(options); // Assert Assert.NotNull(agent); Assert.Equal("test-agent", agent.Name); } /// /// Verify that GetAIAgent with ChatClientAgentOptions and clientFactory applies the factory. /// [Fact] public void GetAIAgent_WithOptions_WithClientFactory_AppliesFactoryCorrectly() { // Arrange AIProjectClient client = this.CreateTestAgentClient(agentName: "test-agent"); var options = new ChatClientAgentOptions { Name = "test-agent" }; TestChatClient? testChatClient = null; // Act var agent = client.GetAIAgent( options, clientFactory: (innerClient) => testChatClient = new TestChatClient(innerClient)); // Assert Assert.NotNull(agent); var retrievedTestClient = agent.GetService(); Assert.NotNull(retrievedTestClient); Assert.Same(testChatClient, retrievedTestClient); } #endregion #region GetAIAgentAsync(AIProjectClient, ChatClientAgentOptions) Tests /// /// Verify that GetAIAgentAsync with ChatClientAgentOptions throws ArgumentNullException when client is null. /// [Fact] public async Task GetAIAgentAsync_WithOptions_WithNullClient_ThrowsArgumentNullExceptionAsync() { // Arrange AIProjectClient? client = null; var options = new ChatClientAgentOptions { Name = "test-agent" }; // Act & Assert var exception = await Assert.ThrowsAsync(() => client!.GetAIAgentAsync(options)); Assert.Equal("aiProjectClient", exception.ParamName); } /// /// Verify that GetAIAgentAsync with ChatClientAgentOptions throws ArgumentNullException when options is null. /// [Fact] public async Task GetAIAgentAsync_WithOptions_WithNullOptions_ThrowsArgumentNullExceptionAsync() { // Arrange var mockClient = new Mock(); // Act & Assert var exception = await Assert.ThrowsAsync(() => mockClient.Object.GetAIAgentAsync((ChatClientAgentOptions)null!)); Assert.Equal("options", exception.ParamName); } /// /// Verify that GetAIAgentAsync with ChatClientAgentOptions creates a valid agent. /// [Fact] public async Task GetAIAgentAsync_WithOptions_CreatesValidAgentAsync() { // Arrange AIProjectClient client = this.CreateTestAgentClient(agentName: "test-agent"); var options = new ChatClientAgentOptions { Name = "test-agent" }; // Act var agent = await client.GetAIAgentAsync(options); // Assert Assert.NotNull(agent); Assert.Equal("test-agent", agent.Name); } #endregion #region GetAIAgent(AIProjectClient, string) Tests /// /// Verify that GetAIAgent throws ArgumentNullException when AIProjectClient is null. /// [Fact] public void GetAIAgent_ByName_WithNullClient_ThrowsArgumentNullException() { // Arrange AIProjectClient? client = null; // Act & Assert var exception = Assert.Throws(() => client!.GetAIAgent("test-agent")); Assert.Equal("aiProjectClient", exception.ParamName); } /// /// Verify that GetAIAgent throws ArgumentNullException when name is null. /// [Fact] public void GetAIAgent_ByName_WithNullName_ThrowsArgumentNullException() { // Arrange var mockClient = new Mock(); // Act & Assert var exception = Assert.Throws(() => mockClient.Object.GetAIAgent((string)null!)); Assert.Equal("name", exception.ParamName); } /// /// Verify that GetAIAgent throws ArgumentException when name is empty. /// [Fact] public void GetAIAgent_ByName_WithEmptyName_ThrowsArgumentException() { // Arrange var mockClient = new Mock(); // Act & Assert var exception = Assert.Throws(() => mockClient.Object.GetAIAgent(string.Empty)); Assert.Equal("name", exception.ParamName); } /// /// Verify that GetAIAgent throws InvalidOperationException when agent is not found. /// [Fact] public void GetAIAgent_ByName_WithNonExistentAgent_ThrowsInvalidOperationException() { // Arrange var mockAgentOperations = new Mock(); mockAgentOperations .Setup(c => c.GetAgent(It.IsAny(), It.IsAny())) .Returns(ClientResult.FromOptionalValue((AgentRecord)null!, new MockPipelineResponse(200, BinaryData.FromString("null")))); var mockClient = new Mock(); mockClient.SetupGet(x => x.Agents).Returns(mockAgentOperations.Object); mockClient.Setup(x => x.GetConnection(It.IsAny())).Returns(new ClientConnection("fake-connection-id", "http://localhost", ClientPipeline.Create(), CredentialKind.None)); // Act & Assert var exception = Assert.Throws(() => mockClient.Object.GetAIAgent("non-existent-agent")); Assert.Contains("not found", exception.Message); } #endregion #region GetAIAgentAsync(AIProjectClient, string) Tests /// /// Verify that GetAIAgentAsync throws ArgumentNullException when AIProjectClient is null. /// [Fact] public async Task GetAIAgentAsync_ByName_WithNullClient_ThrowsArgumentNullExceptionAsync() { // Arrange AIProjectClient? client = null; // Act & Assert var exception = await Assert.ThrowsAsync(() => client!.GetAIAgentAsync("test-agent")); Assert.Equal("aiProjectClient", exception.ParamName); } /// /// Verify that GetAIAgentAsync throws ArgumentNullException when name is null. /// [Fact] public async Task GetAIAgentAsync_ByName_WithNullName_ThrowsArgumentNullExceptionAsync() { // Arrange var mockClient = new Mock(); // Act & Assert var exception = await Assert.ThrowsAsync(() => mockClient.Object.GetAIAgentAsync(name: null!)); Assert.Equal("name", exception.ParamName); } /// /// Verify that GetAIAgentAsync throws InvalidOperationException when agent is not found. /// [Fact] public async Task GetAIAgentAsync_ByName_WithNonExistentAgent_ThrowsInvalidOperationExceptionAsync() { // Arrange var mockAgentOperations = new Mock(); mockAgentOperations .Setup(c => c.GetAgentAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(ClientResult.FromOptionalValue((AgentRecord)null!, new MockPipelineResponse(200, BinaryData.FromString("null")))); var mockClient = new Mock(); mockClient.SetupGet(c => c.Agents).Returns(mockAgentOperations.Object); mockClient.Setup(x => x.GetConnection(It.IsAny())).Returns(new ClientConnection("fake-connection-id", "http://localhost", ClientPipeline.Create(), CredentialKind.None)); // Act & Assert var exception = await Assert.ThrowsAsync(() => mockClient.Object.GetAIAgentAsync("non-existent-agent")); Assert.Contains("not found", exception.Message); } #endregion #region GetAIAgent(AIProjectClient, AgentRecord) with tools Tests /// /// Verify that GetAIAgent with additional tools when the definition has no tools does not throw and results in an agent with no tools. /// [Fact] public void GetAIAgent_WithAgentRecordAndAdditionalTools_WhenDefinitionHasNoTools_ShouldNotThrow() { // Arrange AIProjectClient client = this.CreateTestAgentClient(); AgentRecord agentRecord = this.CreateTestAgentRecord(); var tools = new List { AIFunctionFactory.Create(() => "test", "test_function", "A test function") }; // Act var agent = client.GetAIAgent(agentRecord, tools: tools); // Assert Assert.NotNull(agent); Assert.IsType(agent); var chatClient = agent.GetService(); Assert.NotNull(chatClient); var agentVersion = chatClient.GetService(); Assert.NotNull(agentVersion); var definition = Assert.IsType(agentVersion.Definition); Assert.Empty(definition.Tools); } /// /// Verify that GetAIAgent with null tools works correctly. /// [Fact] public void GetAIAgent_WithAgentRecordAndNullTools_WorksCorrectly() { // Arrange AIProjectClient client = this.CreateTestAgentClient(); AgentRecord agentRecord = this.CreateTestAgentRecord(); // Act var agent = client.GetAIAgent(agentRecord, tools: null); // Assert Assert.NotNull(agent); Assert.Equal("agent_abc123", agent.Name); } #endregion #region GetAIAgentAsync(AIProjectClient, string) with tools Tests /// /// Verify that GetAIAgentAsync with tools parameter creates an agent. /// [Fact] public async Task GetAIAgentAsync_WithNameAndTools_CreatesAgentAsync() { // Arrange AIProjectClient client = this.CreateTestAgentClient(); var tools = new List { AIFunctionFactory.Create(() => "test", "test_function", "A test function") }; // Act var agent = await client.GetAIAgentAsync("test-agent", tools: tools); // Assert Assert.NotNull(agent); Assert.IsType(agent); } #endregion #region CreateAIAgent(AIProjectClient, string, string) Tests /// /// Verify that CreateAIAgent throws ArgumentNullException when AIProjectClient is null. /// [Fact] public void CreateAIAgent_WithBasicParams_WithNullClient_ThrowsArgumentNullException() { // Arrange AIProjectClient? client = null; // Act & Assert var exception = Assert.Throws(() => client!.CreateAIAgent("test-agent", "model", "instructions")); Assert.Equal("aiProjectClient", exception.ParamName); } /// /// Verify that CreateAIAgent throws ArgumentNullException when name is null. /// [Fact] public void CreateAIAgent_WithBasicParams_WithNullName_ThrowsArgumentNullException() { // Arrange var mockClient = new Mock(); // Act & Assert var exception = Assert.Throws(() => mockClient.Object.CreateAIAgent(null!, "model", "instructions")); Assert.Equal("name", exception.ParamName); } #endregion #region CreateAIAgent(AIProjectClient, string, AgentDefinition) Tests /// /// Verify that CreateAIAgent throws ArgumentNullException when AIProjectClient is null. /// [Fact] public void CreateAIAgent_WithAgentDefinition_WithNullClient_ThrowsArgumentNullException() { // Arrange AIProjectClient? client = null; var definition = new PromptAgentDefinition("test-model"); var options = new AgentVersionCreationOptions(definition); // Act & Assert var exception = Assert.Throws(() => client!.CreateAIAgent("test-agent", options)); Assert.Equal("aiProjectClient", exception.ParamName); } /// /// Verify that CreateAIAgent throws ArgumentNullException when name is null. /// [Fact] public void CreateAIAgent_WithAgentDefinition_WithNullName_ThrowsArgumentNullException() { // Arrange var mockClient = new Mock(); var definition = new PromptAgentDefinition("test-model"); var options = new AgentVersionCreationOptions(definition); // Act & Assert var exception = Assert.Throws(() => mockClient.Object.CreateAIAgent(null!, options)); Assert.Equal("name", exception.ParamName); } /// /// Verify that CreateAIAgent throws ArgumentNullException when creationOptions is null. /// [Fact] public void CreateAIAgent_WithAgentDefinition_WithNullDefinition_ThrowsArgumentNullException() { // Arrange var mockClient = new Mock(); // Act & Assert var exception = Assert.Throws(() => mockClient.Object.CreateAIAgent("test-agent", (AgentVersionCreationOptions)null!)); Assert.Equal("creationOptions", exception.ParamName); } #endregion #region CreateAIAgent(AIProjectClient, ChatClientAgentOptions, string) Tests /// /// Verify that CreateAIAgent throws ArgumentNullException when AIProjectClient is null. /// [Fact] public void CreateAIAgent_WithOptions_WithNullClient_ThrowsArgumentNullException() { // Arrange AIProjectClient? client = null; var options = new ChatClientAgentOptions { Name = "test-agent" }; // Act & Assert var exception = Assert.Throws(() => client!.CreateAIAgent("model", options)); Assert.Equal("aiProjectClient", exception.ParamName); } /// /// Verify that CreateAIAgent throws ArgumentNullException when options is null. /// [Fact] public void CreateAIAgent_WithOptions_WithNullOptions_ThrowsArgumentNullException() { // Arrange var mockClient = new Mock(); // Act & Assert var exception = Assert.Throws(() => mockClient.Object.CreateAIAgent("model", (ChatClientAgentOptions)null!)); Assert.Equal("options", exception.ParamName); } /// /// Verify that CreateAIAgent throws ArgumentNullException when model is null. /// [Fact] public void CreateAIAgent_WithOptions_WithNullModel_ThrowsArgumentNullException() { // Arrange var mockClient = new Mock(); var options = new ChatClientAgentOptions { Name = "test-agent" }; // Act & Assert var exception = Assert.Throws(() => mockClient.Object.CreateAIAgent(null!, options)); Assert.Equal("model", exception.ParamName); } /// /// Verify that CreateAIAgent throws ArgumentNullException when options.Name is null. /// [Fact] public void CreateAIAgent_WithOptions_WithoutName_ThrowsException() { // Arrange AIProjectClient client = this.CreateTestAgentClient(); var options = new ChatClientAgentOptions(); // Act & Assert var exception = Assert.Throws(() => client.CreateAIAgent("test-model", options)); Assert.Contains("Agent name must be provided", exception.Message); } /// /// Verify that CreateAIAgent with model and options creates a valid agent. /// [Fact] public void CreateAIAgent_WithModelAndOptions_CreatesValidAgent() { // Arrange AIProjectClient client = this.CreateTestAgentClient(agentName: "test-agent", instructions: "Test instructions"); var options = new ChatClientAgentOptions { Name = "test-agent", ChatOptions = new() { Instructions = "Test instructions" } }; // Act var agent = client.CreateAIAgent("test-model", options); // Assert Assert.NotNull(agent); Assert.Equal("test-agent", agent.Name); Assert.Equal("Test instructions", agent.Instructions); } /// /// Verify that CreateAIAgent with model and options and clientFactory applies the factory. /// [Fact] public void CreateAIAgent_WithModelAndOptions_WithClientFactory_AppliesFactoryCorrectly() { // Arrange AIProjectClient client = this.CreateTestAgentClient(agentName: "test-agent", instructions: "Test instructions"); var options = new ChatClientAgentOptions { Name = "test-agent", ChatOptions = new() { Instructions = "Test instructions" } }; TestChatClient? testChatClient = null; // Act var agent = client.CreateAIAgent( "test-model", options, clientFactory: (innerClient) => testChatClient = new TestChatClient(innerClient)); // Assert Assert.NotNull(agent); var retrievedTestClient = agent.GetService(); Assert.NotNull(retrievedTestClient); Assert.Same(testChatClient, retrievedTestClient); } /// /// Verify that CreateAIAgentAsync with model and options creates a valid agent. /// [Fact] public async Task CreateAIAgentAsync_WithModelAndOptions_CreatesValidAgentAsync() { // Arrange AIProjectClient client = this.CreateTestAgentClient(agentName: "test-agent", instructions: "Test instructions"); var options = new ChatClientAgentOptions { Name = "test-agent", ChatOptions = new() { Instructions = "Test instructions" } }; // Act var agent = await client.CreateAIAgentAsync("test-model", options); // Assert Assert.NotNull(agent); Assert.Equal("test-agent", agent.Name); Assert.Equal("Test instructions", agent.Instructions); } /// /// Verify that CreateAIAgentAsync with model and options and clientFactory applies the factory. /// [Fact] public async Task CreateAIAgentAsync_WithModelAndOptions_WithClientFactory_AppliesFactoryCorrectlyAsync() { // Arrange AIProjectClient client = this.CreateTestAgentClient(agentName: "test-agent", instructions: "Test instructions"); var options = new ChatClientAgentOptions { Name = "test-agent", ChatOptions = new() { Instructions = "Test instructions" } }; TestChatClient? testChatClient = null; // Act var agent = await client.CreateAIAgentAsync( "test-model", options, clientFactory: (innerClient) => testChatClient = new TestChatClient(innerClient)); // Assert Assert.NotNull(agent); var retrievedTestClient = agent.GetService(); Assert.NotNull(retrievedTestClient); Assert.Same(testChatClient, retrievedTestClient); } #endregion #region CreateAIAgentAsync(AIProjectClient, string, AgentDefinition) Tests /// /// Verify that CreateAIAgentAsync throws ArgumentNullException when AIProjectClient is null. /// [Fact] public async Task CreateAIAgentAsync_WithAgentDefinition_WithNullClient_ThrowsArgumentNullExceptionAsync() { // Arrange AIProjectClient? client = null; var definition = new PromptAgentDefinition("test-model"); var options = new AgentVersionCreationOptions(definition); // Act & Assert var exception = await Assert.ThrowsAsync(() => client!.CreateAIAgentAsync("agent-name", options)); Assert.Equal("aiProjectClient", exception.ParamName); } /// /// Verify that CreateAIAgentAsync throws ArgumentNullException when creationOptions is null. /// [Fact] public async Task CreateAIAgentAsync_WithAgentDefinition_WithNullDefinition_ThrowsArgumentNullExceptionAsync() { // Arrange var mockClient = new Mock(); // Act & Assert var exception = await Assert.ThrowsAsync(() => mockClient.Object.CreateAIAgentAsync(name: "agent-name", null!)); Assert.Equal("creationOptions", exception.ParamName); } #endregion #region Tool Validation Tests /// /// Verify that CreateAIAgent creates an agent successfully. /// [Fact] public void CreateAIAgent_WithDefinition_CreatesAgentSuccessfully() { // Arrange AIProjectClient client = this.CreateTestAgentClient(); var definition = new PromptAgentDefinition("test-model") { Instructions = "Test" }; var options = new AgentVersionCreationOptions(definition); // Act var agent = client.CreateAIAgent("test-agent", options); // Assert Assert.NotNull(agent); Assert.IsType(agent); } /// /// Verify that CreateAIAgent without tools parameter creates an agent successfully. /// [Fact] public void CreateAIAgent_WithoutToolsParameter_CreatesAgentSuccessfully() { // Arrange var definition = new PromptAgentDefinition("test-model") { Instructions = "Test" }; var definitionResponse = GeneratePromptDefinitionResponse(definition, null); AIProjectClient client = this.CreateTestAgentClient(agentName: "test-agent", agentDefinitionResponse: definitionResponse); var options = new AgentVersionCreationOptions(definition); // Act var agent = client.CreateAIAgent("test-agent", options); // Assert Assert.NotNull(agent); Assert.IsType(agent); } /// /// Verify that CreateAIAgent without tools in definition creates an agent successfully. /// [Fact] public void CreateAIAgent_WithoutToolsInDefinition_CreatesAgentSuccessfully() { // Arrange var definition = new PromptAgentDefinition("test-model") { Instructions = "Test" }; AIProjectClient client = this.CreateTestAgentClient(agentName: "test-agent", agentDefinitionResponse: definition); var options = new AgentVersionCreationOptions(definition); // Act var agent = client.CreateAIAgent("test-agent", options); // Assert Assert.NotNull(agent); Assert.IsType(agent); } /// /// Verify that CreateAIAgent uses tools from the definition when no separate tools parameter is provided. /// [Fact] public void CreateAIAgent_WithDefinitionTools_UsesDefinitionTools() { // Arrange var definition = new PromptAgentDefinition("test-model") { Instructions = "Test" }; // Add a function tool to the definition definition.Tools.Add(ResponseTool.CreateFunctionTool("required_tool", BinaryData.FromString("{}"), strictModeEnabled: false)); // Create a response definition with the same tool var definitionResponse = GeneratePromptDefinitionResponse(definition, definition.Tools.Select(t => t.AsAITool()).ToList()); AIProjectClient client = this.CreateTestAgentClient(agentName: "test-agent", agentDefinitionResponse: definitionResponse); var options = new AgentVersionCreationOptions(definition); // Act var agent = client.CreateAIAgent("test-agent", options); // Assert Assert.NotNull(agent); Assert.IsType(agent); var agentVersion = agent.GetService(); Assert.NotNull(agentVersion); if (agentVersion.Definition is PromptAgentDefinition promptDef) { Assert.NotEmpty(promptDef.Tools); Assert.Single(promptDef.Tools); Assert.Equal("required_tool", (promptDef.Tools.First() as FunctionTool)?.FunctionName); } } /// /// Verify that CreateAIAgentAsync when AI Tools are provided, uses them for the definition via http request. /// [Fact] public async Task CreateAIAgentAsync_WithNameAndAITools_SendsToolDefinitionViaHttpAsync() { // Arrange using var httpHandler = new HttpHandlerAssert(async (request) => { if (request.Content is not null) { var requestBody = await request.Content.ReadAsStringAsync().ConfigureAwait(false); Assert.Contains("required_tool", requestBody); } return new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(TestDataUtil.GetAgentVersionResponseJson(), Encoding.UTF8, "application/json") }; }); #pragma warning disable CA5399 using var httpClient = new HttpClient(httpHandler); #pragma warning restore CA5399 var client = new AIProjectClient(new Uri("https://test.openai.azure.com/"), new FakeAuthenticationTokenProvider(), new() { Transport = new HttpClientPipelineTransport(httpClient) }); // Act var agent = await client.CreateAIAgentAsync( name: "test-agent", model: "test-model", instructions: "Test", tools: [AIFunctionFactory.Create(() => true, "required_tool")]); // Assert Assert.NotNull(agent); Assert.IsType(agent); var agentVersion = agent.GetService(); Assert.NotNull(agentVersion); Assert.IsType(agentVersion.Definition); } /// /// Verify that CreateAIAgent when AI Tools are provided, uses them for the definition via http request. /// [Fact] public void CreateAIAgent_WithNameAndAITools_SendsToolDefinitionViaHttp() { // Arrange using var httpHandler = new HttpHandlerAssert((request) => { if (request.Content is not null) { #pragma warning disable VSTHRD002 // Avoid problematic synchronous waits var requestBody = request.Content.ReadAsStringAsync().GetAwaiter().GetResult(); #pragma warning restore VSTHRD002 // Avoid problematic synchronous waits Assert.Contains("required_tool", requestBody); } return new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(TestDataUtil.GetAgentVersionResponseJson(), Encoding.UTF8, "application/json") }; }); #pragma warning disable CA5399 using var httpClient = new HttpClient(httpHandler); #pragma warning restore CA5399 var client = new AIProjectClient(new Uri("https://test.openai.azure.com/"), new FakeAuthenticationTokenProvider(), new() { Transport = new HttpClientPipelineTransport(httpClient) }); // Act var agent = client.CreateAIAgent( name: "test-agent", model: "test-model", instructions: "Test", tools: [AIFunctionFactory.Create(() => true, "required_tool")]); // Assert Assert.NotNull(agent); Assert.IsType(agent); var agentVersion = agent.GetService(); Assert.NotNull(agentVersion); Assert.IsType(agentVersion.Definition); } /// /// Verify that CreateAIAgent without tools creates an agent successfully. /// [Fact] public void CreateAIAgent_WithoutTools_CreatesAgentSuccessfully() { // Arrange var definition = new PromptAgentDefinition("test-model"); var agentDefinitionResponse = GeneratePromptDefinitionResponse(definition, null); AIProjectClient client = this.CreateTestAgentClient(agentName: "test-agent", agentDefinitionResponse: agentDefinitionResponse); var options = new AgentVersionCreationOptions(definition); // Act var agent = client.CreateAIAgent("test-agent", options); // Assert Assert.NotNull(agent); Assert.IsType(agent); } /// /// Verify that when providing AITools with GetAIAgent, any additional tool that doesn't match the tools in agent definition are ignored. /// [Fact] public void GetAIAgent_AdditionalAITools_WhenNotInTheDefinitionAreIgnored() { // Arrange AIProjectClient client = this.CreateTestAgentClient(); var agentVersion = this.CreateTestAgentVersion(); // Manually add tools to the definition to simulate inline tools if (agentVersion.Definition is PromptAgentDefinition promptDef) { promptDef.Tools.Add(ResponseTool.CreateFunctionTool("inline_tool", BinaryData.FromString("{}"), strictModeEnabled: false)); } var invocableInlineAITool = AIFunctionFactory.Create(() => "test", "inline_tool", "An invocable AIFunction for the inline function"); var shouldBeIgnoredTool = AIFunctionFactory.Create(() => "test", "additional_tool", "An additional test function that should be ignored"); // Act & Assert var agent = client.GetAIAgent(agentVersion, tools: [invocableInlineAITool, shouldBeIgnoredTool]); Assert.NotNull(agent); var version = agent.GetService(); Assert.NotNull(version); var definition = Assert.IsType(version.Definition); Assert.NotEmpty(definition.Tools); Assert.NotNull(GetAgentChatOptions(agent)); Assert.NotNull(GetAgentChatOptions(agent)!.Tools); Assert.Single(GetAgentChatOptions(agent)!.Tools!); Assert.Equal("inline_tool", (definition.Tools.First() as FunctionTool)?.FunctionName); } #endregion #region Inline Tools vs Parameter Tools Tests /// /// Verify that tools passed as parameters are accepted by GetAIAgent. /// [Fact] public void GetAIAgent_WithParameterTools_AcceptsTools() { // Arrange AIProjectClient client = this.CreateTestAgentClient(); AgentRecord agentRecord = this.CreateTestAgentRecord(); var tools = new List { AIFunctionFactory.Create(() => "tool1", "param_tool_1", "First parameter tool"), AIFunctionFactory.Create(() => "tool2", "param_tool_2", "Second parameter tool") }; // Act var agent = client.GetAIAgent(agentRecord, tools: tools); // Assert Assert.NotNull(agent); Assert.IsType(agent); var chatClient = agent.GetService(); Assert.NotNull(chatClient); var agentVersion = chatClient.GetService(); Assert.NotNull(agentVersion); } /// /// Verify that CreateAIAgent with tools in definition creates an agent successfully. /// [Fact] public void CreateAIAgent_WithDefinitionTools_CreatesAgentSuccessfully() { // Arrange var definition = new PromptAgentDefinition("test-model") { Instructions = "Test instructions" }; definition.Tools.Add(ResponseTool.CreateFunctionTool("create_tool", BinaryData.FromString("{}"), strictModeEnabled: false)); // Simulate agent definition response with the tools var definitionResponse = GeneratePromptDefinitionResponse(definition, definition.Tools.Select(t => t.AsAITool()).ToList()); AIProjectClient client = this.CreateTestAgentClient(agentDefinitionResponse: definitionResponse); var options = new AgentVersionCreationOptions(definition); // Act var agent = client.CreateAIAgent("test-agent", options); // Assert Assert.NotNull(agent); Assert.IsType(agent); var agentVersion = agent.GetService(); Assert.NotNull(agentVersion); if (agentVersion.Definition is PromptAgentDefinition promptDef) { Assert.NotEmpty(promptDef.Tools); Assert.Single(promptDef.Tools); } } /// /// Verify that CreateAIAgent creates an agent successfully when definition has a mix of custom and hosted tools. /// [Fact] public void CreateAIAgent_WithMixedToolsInDefinition_CreatesAgentSuccessfully() { // Arrange var definition = new PromptAgentDefinition("test-model") { Instructions = "Test instructions" }; definition.Tools.Add(ResponseTool.CreateFunctionTool("create_tool", BinaryData.FromString("{}"), strictModeEnabled: false)); definition.Tools.Add(new HostedWebSearchTool().GetService() ?? new HostedWebSearchTool().AsOpenAIResponseTool()); definition.Tools.Add(new HostedFileSearchTool().GetService() ?? new HostedFileSearchTool().AsOpenAIResponseTool()); // Simulate agent definition response with the tools var definitionResponse = new PromptAgentDefinition("test-model") { Instructions = "Test instructions" }; foreach (var tool in definition.Tools) { definitionResponse.Tools.Add(tool); } AIProjectClient client = this.CreateTestAgentClient(agentDefinitionResponse: definitionResponse); var options = new AgentVersionCreationOptions(definition); // Act var agent = client.CreateAIAgent("test-agent", options); // Assert Assert.NotNull(agent); Assert.IsType(agent); var agentVersion = agent.GetService(); Assert.NotNull(agentVersion); if (agentVersion.Definition is PromptAgentDefinition promptDef) { Assert.NotEmpty(promptDef.Tools); Assert.Equal(3, promptDef.Tools.Count); } } /// /// Verifies that CreateAIAgent uses tools from definition when they are ResponseTool instances, resulting in successful agent creation. /// [Fact] public void CreateAIAgent_WithResponseToolsInDefinition_CreatesAgentSuccessfully() { // Arrange var definition = new PromptAgentDefinition("test-model") { Instructions = "Test instructions" }; var fabricToolOptions = new FabricDataAgentToolOptions(); fabricToolOptions.ProjectConnections.Add(new ToolProjectConnection("connection-id")); var sharepointOptions = new SharePointGroundingToolOptions(); sharepointOptions.ProjectConnections.Add(new ToolProjectConnection("connection-id")); var structuredOutputs = new StructuredOutputDefinition("name", "description", BinaryData.FromString(AIJsonUtilities.CreateJsonSchema(new { id = "test" }.GetType()).ToString()), false); // Add tools to the definition definition.Tools.Add(ResponseTool.CreateFunctionTool("create_tool", BinaryData.FromString("{}"), strictModeEnabled: false)); definition.Tools.Add((ResponseTool)AgentTool.CreateBingCustomSearchTool(new BingCustomSearchToolParameters([new BingCustomSearchConfiguration("connection-id", "instance-name")]))); definition.Tools.Add((ResponseTool)AgentTool.CreateBrowserAutomationTool(new BrowserAutomationToolParameters(new BrowserAutomationToolConnectionParameters("id")))); definition.Tools.Add(AgentTool.CreateA2ATool(new Uri("https://test-uri.microsoft.com"))); definition.Tools.Add((ResponseTool)AgentTool.CreateBingGroundingTool(new BingGroundingSearchToolOptions([new BingGroundingSearchConfiguration("connection-id")]))); definition.Tools.Add((ResponseTool)AgentTool.CreateMicrosoftFabricTool(fabricToolOptions)); definition.Tools.Add((ResponseTool)AgentTool.CreateOpenApiTool(new OpenAPIFunctionDefinition("name", BinaryData.FromString(OpenAPISpec), new OpenAPIAnonymousAuthenticationDetails()))); definition.Tools.Add((ResponseTool)AgentTool.CreateSharepointTool(sharepointOptions)); definition.Tools.Add((ResponseTool)AgentTool.CreateStructuredOutputsTool(structuredOutputs)); definition.Tools.Add((ResponseTool)AgentTool.CreateAzureAISearchTool(new AzureAISearchToolOptions([new AzureAISearchToolIndex() { IndexName = "name" }]))); // Generate agent definition response with the tools var definitionResponse = GeneratePromptDefinitionResponse(definition, definition.Tools.Select(t => t.AsAITool()).ToList()); AIProjectClient client = this.CreateTestAgentClient(agentDefinitionResponse: definitionResponse); var options = new AgentVersionCreationOptions(definition); // Act var agent = client.CreateAIAgent("test-agent", options); // Assert Assert.NotNull(agent); Assert.IsType(agent); var agentVersion = agent.GetService(); Assert.NotNull(agentVersion); if (agentVersion.Definition is PromptAgentDefinition promptDef) { Assert.NotEmpty(promptDef.Tools); Assert.Equal(10, promptDef.Tools.Count); } } /// /// Verify that CreateAIAgent with string parameters and tools creates an agent. /// [Fact] public void CreateAIAgent_WithStringParamsAndTools_CreatesAgent() { // Arrange var tools = new List { AIFunctionFactory.Create(() => "weather", "string_param_tool", "Tool from string params") }; var definitionResponse = GeneratePromptDefinitionResponse(new PromptAgentDefinition("test-model") { Instructions = "Test instructions" }, tools); AIProjectClient client = this.CreateTestAgentClient(agentName: "test-agent", agentDefinitionResponse: definitionResponse); // Act var agent = client.CreateAIAgent( "test-agent", "test-model", "Test instructions", tools: tools); // Assert Assert.NotNull(agent); Assert.IsType(agent); var agentVersion = agent.GetService(); Assert.NotNull(agentVersion); if (agentVersion.Definition is PromptAgentDefinition promptDef) { Assert.NotEmpty(promptDef.Tools); Assert.Single(promptDef.Tools); } } /// /// Verify that CreateAIAgentAsync with tools in definition creates an agent. /// [Fact] public async Task CreateAIAgentAsync_WithDefinitionTools_CreatesAgentAsync() { // Arrange AIProjectClient client = this.CreateTestAgentClient(); var definition = new PromptAgentDefinition("test-model") { Instructions = "Test instructions" }; definition.Tools.Add(ResponseTool.CreateFunctionTool("async_tool", BinaryData.FromString("{}"), strictModeEnabled: false)); var options = new AgentVersionCreationOptions(definition); // Act var agent = await client.CreateAIAgentAsync("test-agent", options); // Assert Assert.NotNull(agent); Assert.IsType(agent); } /// /// Verify that GetAIAgentAsync with tools parameter creates an agent. /// [Fact] public async Task GetAIAgentAsync_WithToolsParameter_CreatesAgentAsync() { // Arrange AIProjectClient client = this.CreateTestAgentClient(); var tools = new List { AIFunctionFactory.Create(() => "async_get_result", "async_get_tool", "An async get tool") }; // Act var agent = await client.GetAIAgentAsync("test-agent", tools: tools); // Assert Assert.NotNull(agent); Assert.IsType(agent); } #endregion #region Declarative Function Handling Tests /// /// Verify that CreateAIAgent accepts declarative functions from definition. /// [Fact] public void CreateAIAgent_WithDeclarativeFunctionInDefinition_AcceptsDeclarativeFunction() { // Arrange AIProjectClient client = this.CreateTestAgentClient(); var definition = new PromptAgentDefinition("test-model") { Instructions = "Test" }; // Create a declarative function (not invocable) using AIFunctionFactory.CreateDeclaration using var doc = JsonDocument.Parse("{}"); var declarativeFunction = AIFunctionFactory.CreateDeclaration("test_function", "A test function", doc.RootElement); // Add to definition definition.Tools.Add(declarativeFunction.AsOpenAIResponseTool() ?? throw new InvalidOperationException()); var options = new AgentVersionCreationOptions(definition); // Act var agent = client.CreateAIAgent("test-agent", options); // Assert Assert.NotNull(agent); Assert.IsType(agent); } /// /// Verify that CreateAIAgent accepts declarative functions from definition. /// [Fact] public void CreateAIAgent_WithDeclarativeFunctionFromDefinition_AcceptsDeclarativeFunction() { // Arrange var definition = new PromptAgentDefinition("test-model") { Instructions = "Test" }; // Create a declarative function (not invocable) using AIFunctionFactory.CreateDeclaration using var doc = JsonDocument.Parse("{}"); var declarativeFunction = AIFunctionFactory.CreateDeclaration("test_function", "A test function", doc.RootElement); // Add to definition definition.Tools.Add(declarativeFunction.AsOpenAIResponseTool() ?? throw new InvalidOperationException()); // Generate response with the declarative function var definitionResponse = new PromptAgentDefinition("test-model") { Instructions = "Test" }; definitionResponse.Tools.Add(declarativeFunction.AsOpenAIResponseTool() ?? throw new InvalidOperationException()); AIProjectClient client = this.CreateTestAgentClient(agentName: "test-agent", agentDefinitionResponse: definitionResponse); var options = new AgentVersionCreationOptions(definition); // Act var agent = client.CreateAIAgent("test-agent", options); // Assert Assert.NotNull(agent); Assert.IsType(agent); } /// /// Verify that CreateAIAgent accepts FunctionTools from definition. /// [Fact] public void CreateAIAgent_WithFunctionToolsInDefinition_AcceptsDeclarativeFunction() { // Arrange var functionTool = ResponseTool.CreateFunctionTool( functionName: "get_user_name", functionParameters: BinaryData.FromString("{}"), strictModeEnabled: false, functionDescription: "Gets the user's name, as used for friendly address." ); var definition = new PromptAgentDefinition("test-model") { Instructions = "Test" }; definition.Tools.Add(functionTool); // Generate response with the declarative function var definitionResponse = new PromptAgentDefinition("test-model") { Instructions = "Test" }; definitionResponse.Tools.Add(functionTool); AIProjectClient client = this.CreateTestAgentClient(agentName: "test-agent", agentDefinitionResponse: definitionResponse); var options = new AgentVersionCreationOptions(definition); // Act var agent = client.CreateAIAgent("test-agent", options); // Assert Assert.NotNull(agent); Assert.IsType(agent); var definitionFromAgent = Assert.IsType(agent.GetService()?.Definition); Assert.Single(definitionFromAgent.Tools); } /// /// Verify that CreateAIAgentAsync accepts FunctionTools from definition. /// [Fact] public async Task CreateAIAgentAsync_WithFunctionToolsInDefinition_AcceptsDeclarativeFunctionAsync() { // Arrange var functionTool = ResponseTool.CreateFunctionTool( functionName: "get_user_name", functionParameters: BinaryData.FromString("{}"), strictModeEnabled: false, functionDescription: "Gets the user's name, as used for friendly address." ); var definition = new PromptAgentDefinition("test-model") { Instructions = "Test" }; definition.Tools.Add(functionTool); // Generate response with the declarative function var definitionResponse = new PromptAgentDefinition("test-model") { Instructions = "Test" }; definitionResponse.Tools.Add(functionTool); AIProjectClient client = this.CreateTestAgentClient(agentName: "test-agent", agentDefinitionResponse: definitionResponse); var options = new AgentVersionCreationOptions(definition); // Act var agent = await client.CreateAIAgentAsync("test-agent", options); // Assert Assert.NotNull(agent); Assert.IsType(agent); } /// /// Verify that CreateAIAgentAsync accepts declarative functions from definition. /// [Fact] public async Task CreateAIAgentAsync_WithDeclarativeFunctionFromDefinition_AcceptsDeclarativeFunctionAsync() { // Arrange AIProjectClient client = this.CreateTestAgentClient(); var definition = new PromptAgentDefinition("test-model") { Instructions = "Test" }; // Create a declarative function (not invocable) using AIFunctionFactory.CreateDeclaration using var doc = JsonDocument.Parse("{}"); var declarativeFunction = AIFunctionFactory.CreateDeclaration("test_function", "A test function", doc.RootElement); // Add to definition definition.Tools.Add(declarativeFunction.AsOpenAIResponseTool() ?? throw new InvalidOperationException()); var options = new AgentVersionCreationOptions(definition); // Act var agent = await client.CreateAIAgentAsync("test-agent", options); // Assert Assert.NotNull(agent); Assert.IsType(agent); } /// /// Verify that CreateAIAgentAsync accepts declarative functions from definition. /// [Fact] public async Task CreateAIAgentAsync_WithDeclarativeFunctionInDefinition_AcceptsDeclarativeFunctionAsync() { // Arrange var definition = new PromptAgentDefinition("test-model") { Instructions = "Test" }; // Create a declarative function (not invocable) using AIFunctionFactory.CreateDeclaration using var doc = JsonDocument.Parse("{}"); var declarativeFunction = AIFunctionFactory.CreateDeclaration("test_function", "A test function", doc.RootElement); // Add to definition definition.Tools.Add(declarativeFunction.AsOpenAIResponseTool() ?? throw new InvalidOperationException()); // Generate response with the declarative function var definitionResponse = new PromptAgentDefinition("test-model") { Instructions = "Test" }; definitionResponse.Tools.Add(declarativeFunction.AsOpenAIResponseTool() ?? throw new InvalidOperationException()); AIProjectClient client = this.CreateTestAgentClient(agentName: "test-agent", agentDefinitionResponse: definitionResponse); var options = new AgentVersionCreationOptions(definition); // Act var agent = await client.CreateAIAgentAsync("test-agent", options); // Assert Assert.NotNull(agent); Assert.IsType(agent); } #endregion #region Options Generation Validation Tests /// /// Verify that ChatClientAgentOptions are generated correctly without tools. /// [Fact] public void CreateAIAgent_GeneratesCorrectChatClientAgentOptions() { // Arrange var definition = new PromptAgentDefinition("test-model") { Instructions = "Test instructions" }; var definitionResponse = GeneratePromptDefinitionResponse(definition, null); AIProjectClient client = this.CreateTestAgentClient(agentName: "test-agent", agentDefinitionResponse: definitionResponse); var options = new AgentVersionCreationOptions(definition); // Act var agent = client.CreateAIAgent("test-agent", options); // Assert Assert.NotNull(agent); var agentVersion = agent.GetService(); Assert.NotNull(agentVersion); Assert.Equal("test-agent", agentVersion.Name); Assert.Equal("Test instructions", (agentVersion.Definition as PromptAgentDefinition)?.Instructions); } /// /// Verify that ChatClientAgentOptions preserve custom properties from input options. /// [Fact] public void GetAIAgent_WithOptions_PreservesCustomProperties() { // Arrange AIProjectClient client = this.CreateTestAgentClient(agentName: "test-agent", instructions: "Custom instructions", description: "Custom description"); var options = new ChatClientAgentOptions { Name = "test-agent", Description = "Custom description", ChatOptions = new ChatOptions { Instructions = "Custom instructions" } }; // Act var agent = client.GetAIAgent(options); // Assert Assert.NotNull(agent); Assert.Equal("test-agent", agent.Name); Assert.Equal("Custom instructions", agent.Instructions); Assert.Equal("Custom description", agent.Description); } /// /// Verify that CreateAIAgent with options generates correct ChatClientAgentOptions with tools. /// [Fact] public void CreateAIAgent_WithOptionsAndTools_GeneratesCorrectOptions() { // Arrange var tools = new List { AIFunctionFactory.Create(() => "result", "option_tool", "A tool from options") }; var definitionResponse = GeneratePromptDefinitionResponse( new PromptAgentDefinition("test-model") { Instructions = "Test" }, tools); AIProjectClient client = this.CreateTestAgentClient(agentName: "test-agent", agentDefinitionResponse: definitionResponse); var options = new ChatClientAgentOptions { Name = "test-agent", ChatOptions = new ChatOptions { Instructions = "Test", Tools = tools } }; // Act var agent = client.CreateAIAgent("test-model", options); // Assert Assert.NotNull(agent); var agentVersion = agent.GetService(); Assert.NotNull(agentVersion); if (agentVersion.Definition is PromptAgentDefinition promptDef) { Assert.NotEmpty(promptDef.Tools); Assert.Single(promptDef.Tools); } } #endregion #region AgentName Validation Tests /// /// Verify that GetAIAgent throws ArgumentException when agent name is invalid. /// [Theory] [MemberData(nameof(InvalidAgentNameTestData.GetInvalidAgentNames), MemberType = typeof(InvalidAgentNameTestData))] public void GetAIAgent_ByName_WithInvalidAgentName_ThrowsArgumentException(string invalidName) { // Arrange var mockClient = new Mock(); // Act & Assert var exception = Assert.Throws(() => mockClient.Object.GetAIAgent(invalidName)); Assert.Equal("name", exception.ParamName); Assert.Contains("Agent name must be 1-63 characters long", exception.Message); } /// /// Verify that GetAIAgentAsync throws ArgumentException when agent name is invalid. /// [Theory] [MemberData(nameof(InvalidAgentNameTestData.GetInvalidAgentNames), MemberType = typeof(InvalidAgentNameTestData))] public async Task GetAIAgentAsync_ByName_WithInvalidAgentName_ThrowsArgumentExceptionAsync(string invalidName) { // Arrange var mockClient = new Mock(); // Act & Assert var exception = await Assert.ThrowsAsync(() => mockClient.Object.GetAIAgentAsync(invalidName)); Assert.Equal("name", exception.ParamName); Assert.Contains("Agent name must be 1-63 characters long", exception.Message); } /// /// Verify that GetAIAgent with ChatClientAgentOptions throws ArgumentException when agent name is invalid. /// [Theory] [MemberData(nameof(InvalidAgentNameTestData.GetInvalidAgentNames), MemberType = typeof(InvalidAgentNameTestData))] public void GetAIAgent_WithOptions_WithInvalidAgentName_ThrowsArgumentException(string invalidName) { // Arrange AIProjectClient client = this.CreateTestAgentClient(); var options = new ChatClientAgentOptions { Name = invalidName }; // Act & Assert var exception = Assert.Throws(() => client.GetAIAgent(options)); Assert.Equal("name", exception.ParamName); Assert.Contains("Agent name must be 1-63 characters long", exception.Message); } /// /// Verify that GetAIAgentAsync with ChatClientAgentOptions throws ArgumentException when agent name is invalid. /// [Theory] [MemberData(nameof(InvalidAgentNameTestData.GetInvalidAgentNames), MemberType = typeof(InvalidAgentNameTestData))] public async Task GetAIAgentAsync_WithOptions_WithInvalidAgentName_ThrowsArgumentExceptionAsync(string invalidName) { // Arrange AIProjectClient client = this.CreateTestAgentClient(); var options = new ChatClientAgentOptions { Name = invalidName }; // Act & Assert var exception = await Assert.ThrowsAsync(() => client.GetAIAgentAsync(options)); Assert.Equal("name", exception.ParamName); Assert.Contains("Agent name must be 1-63 characters long", exception.Message); } /// /// Verify that CreateAIAgent throws ArgumentException when agent name is invalid. /// [Theory] [MemberData(nameof(InvalidAgentNameTestData.GetInvalidAgentNames), MemberType = typeof(InvalidAgentNameTestData))] public void CreateAIAgent_WithBasicParams_WithInvalidAgentName_ThrowsArgumentException(string invalidName) { // Arrange var mockClient = new Mock(); // Act & Assert var exception = Assert.Throws(() => mockClient.Object.CreateAIAgent(invalidName, "model", "instructions")); Assert.Equal("name", exception.ParamName); Assert.Contains("Agent name must be 1-63 characters long", exception.Message); } /// /// Verify that CreateAIAgentAsync throws ArgumentException when agent name is invalid. /// [Theory] [MemberData(nameof(InvalidAgentNameTestData.GetInvalidAgentNames), MemberType = typeof(InvalidAgentNameTestData))] public async Task CreateAIAgentAsync_WithBasicParams_WithInvalidAgentName_ThrowsArgumentExceptionAsync(string invalidName) { // Arrange var mockClient = new Mock(); // Act & Assert var exception = await Assert.ThrowsAsync(() => mockClient.Object.CreateAIAgentAsync(invalidName, "model", "instructions")); Assert.Equal("name", exception.ParamName); Assert.Contains("Agent name must be 1-63 characters long", exception.Message); } /// /// Verify that CreateAIAgent with AgentVersionCreationOptions throws ArgumentException when agent name is invalid. /// [Theory] [MemberData(nameof(InvalidAgentNameTestData.GetInvalidAgentNames), MemberType = typeof(InvalidAgentNameTestData))] public void CreateAIAgent_WithAgentDefinition_WithInvalidAgentName_ThrowsArgumentException(string invalidName) { // Arrange var mockClient = new Mock(); var definition = new PromptAgentDefinition("test-model"); var options = new AgentVersionCreationOptions(definition); // Act & Assert var exception = Assert.Throws(() => mockClient.Object.CreateAIAgent(invalidName, options)); Assert.Equal("name", exception.ParamName); Assert.Contains("Agent name must be 1-63 characters long", exception.Message); } /// /// Verify that CreateAIAgentAsync with AgentVersionCreationOptions throws ArgumentException when agent name is invalid. /// [Theory] [MemberData(nameof(InvalidAgentNameTestData.GetInvalidAgentNames), MemberType = typeof(InvalidAgentNameTestData))] public async Task CreateAIAgentAsync_WithAgentDefinition_WithInvalidAgentName_ThrowsArgumentExceptionAsync(string invalidName) { // Arrange var mockClient = new Mock(); var definition = new PromptAgentDefinition("test-model"); var options = new AgentVersionCreationOptions(definition); // Act & Assert var exception = await Assert.ThrowsAsync(() => mockClient.Object.CreateAIAgentAsync(invalidName, options)); Assert.Equal("name", exception.ParamName); Assert.Contains("Agent name must be 1-63 characters long", exception.Message); } /// /// Verify that CreateAIAgent with ChatClientAgentOptions throws ArgumentException when agent name is invalid. /// [Theory] [MemberData(nameof(InvalidAgentNameTestData.GetInvalidAgentNames), MemberType = typeof(InvalidAgentNameTestData))] public void CreateAIAgent_WithOptions_WithInvalidAgentName_ThrowsArgumentException(string invalidName) { // Arrange AIProjectClient client = this.CreateTestAgentClient(); var options = new ChatClientAgentOptions { Name = invalidName }; // Act & Assert var exception = Assert.Throws(() => client.CreateAIAgent("test-model", options)); Assert.Equal("name", exception.ParamName); Assert.Contains("Agent name must be 1-63 characters long", exception.Message); } /// /// Verify that CreateAIAgentAsync with ChatClientAgentOptions throws ArgumentException when agent name is invalid. /// [Theory] [MemberData(nameof(InvalidAgentNameTestData.GetInvalidAgentNames), MemberType = typeof(InvalidAgentNameTestData))] public async Task CreateAIAgentAsync_WithOptions_WithInvalidAgentName_ThrowsArgumentExceptionAsync(string invalidName) { // Arrange AIProjectClient client = this.CreateTestAgentClient(); var options = new ChatClientAgentOptions { Name = invalidName }; // Act & Assert var exception = await Assert.ThrowsAsync(() => client.CreateAIAgentAsync("test-model", options)); Assert.Equal("name", exception.ParamName); Assert.Contains("Agent name must be 1-63 characters long", exception.Message); } /// /// Verify that GetAIAgent with AgentReference throws ArgumentException when agent name is invalid. /// [Theory] [MemberData(nameof(InvalidAgentNameTestData.GetInvalidAgentNames), MemberType = typeof(InvalidAgentNameTestData))] public void GetAIAgent_WithAgentReference_WithInvalidAgentName_ThrowsArgumentException(string invalidName) { // Arrange var mockClient = new Mock(); var agentReference = new AgentReference(invalidName, "1"); // Act & Assert var exception = Assert.Throws(() => mockClient.Object.GetAIAgent(agentReference)); Assert.Equal("name", exception.ParamName); Assert.Contains("Agent name must be 1-63 characters long", exception.Message); } #endregion #region AzureAIChatClient Behavior Tests /// /// Verify that the underlying chat client created by extension methods can be wrapped with clientFactory. /// [Fact] public void GetAIAgent_WithClientFactory_WrapsUnderlyingChatClient() { // Arrange AIProjectClient client = this.CreateTestAgentClient(); AgentRecord agentRecord = this.CreateTestAgentRecord(); int factoryCallCount = 0; // Act var agent = client.GetAIAgent( agentRecord, clientFactory: (innerClient) => { factoryCallCount++; return new TestChatClient(innerClient); }); // Assert Assert.NotNull(agent); Assert.Equal(1, factoryCallCount); var wrappedClient = agent.GetService(); Assert.NotNull(wrappedClient); } /// /// Verify that clientFactory is called with the correct underlying chat client. /// [Fact] public void CreateAIAgent_WithClientFactory_ReceivesCorrectUnderlyingClient() { // Arrange AIProjectClient client = this.CreateTestAgentClient(); var definition = new PromptAgentDefinition("test-model") { Instructions = "Test" }; IChatClient? receivedClient = null; var options = new AgentVersionCreationOptions(definition); // Act var agent = client.CreateAIAgent( "test-agent", options, clientFactory: (innerClient) => { receivedClient = innerClient; return new TestChatClient(innerClient); }); // Assert Assert.NotNull(agent); Assert.NotNull(receivedClient); var wrappedClient = agent.GetService(); Assert.NotNull(wrappedClient); } /// /// Verify that multiple clientFactory calls create independent wrapped clients. /// [Fact] public void GetAIAgent_MultipleCallsWithClientFactory_CreatesIndependentClients() { // Arrange AIProjectClient client = this.CreateTestAgentClient(); AgentRecord agentRecord = this.CreateTestAgentRecord(); // Act var agent1 = client.GetAIAgent( agentRecord, clientFactory: (innerClient) => new TestChatClient(innerClient)); var agent2 = client.GetAIAgent( agentRecord, clientFactory: (innerClient) => new TestChatClient(innerClient)); // Assert Assert.NotNull(agent1); Assert.NotNull(agent2); var client1 = agent1.GetService(); var client2 = agent2.GetService(); Assert.NotNull(client1); Assert.NotNull(client2); Assert.NotSame(client1, client2); } /// /// Verify that agent created with clientFactory maintains agent properties. /// [Fact] public void CreateAIAgent_WithClientFactory_PreservesAgentProperties() { // Arrange const string AgentName = "test-agent"; const string Model = "test-model"; const string Instructions = "Test instructions"; AIProjectClient client = this.CreateTestAgentClient(AgentName, Instructions); // Act var agent = client.CreateAIAgent( AgentName, Model, Instructions, clientFactory: (innerClient) => new TestChatClient(innerClient)); // Assert Assert.NotNull(agent); Assert.Equal(AgentName, agent.Name); Assert.Equal(Instructions, agent.Instructions); var wrappedClient = agent.GetService(); Assert.NotNull(wrappedClient); } /// /// Verify that agent created with clientFactory is created successfully. /// [Fact] public void CreateAIAgent_WithClientFactory_CreatesAgentSuccessfully() { // Arrange var definition = new PromptAgentDefinition("test-model") { Instructions = "Test" }; var agentDefinitionResponse = GeneratePromptDefinitionResponse(definition, null); AIProjectClient client = this.CreateTestAgentClient(agentName: "test-agent", agentDefinitionResponse: agentDefinitionResponse); var options = new AgentVersionCreationOptions(definition); // Act var agent = client.CreateAIAgent( "test-agent", options, clientFactory: (innerClient) => new TestChatClient(innerClient)); // Assert Assert.NotNull(agent); var wrappedClient = agent.GetService(); Assert.NotNull(wrappedClient); var agentVersion = agent.GetService(); Assert.NotNull(agentVersion); } #endregion #region User-Agent Header Tests /// /// Verify that GetAIAgent(string name) passes RequestOptions to the Protocol method. /// [Fact] public void GetAIAgent_WithStringName_PassesRequestOptionsToProtocol() { // Arrange RequestOptions? capturedRequestOptions = null; var mockAgentOperations = new Mock(); mockAgentOperations .Setup(x => x.GetAgent(It.IsAny(), It.IsAny())) .Callback((name, options) => capturedRequestOptions = options) .Returns(ClientResult.FromResponse(new MockPipelineResponse(200, BinaryData.FromString(TestDataUtil.GetAgentResponseJson())))); var mockAgentClient = new Mock(new Uri("https://test.openai.azure.com/"), new FakeAuthenticationTokenProvider()); mockAgentClient.SetupGet(x => x.Agents).Returns(mockAgentOperations.Object); mockAgentClient.Setup(x => x.GetConnection(It.IsAny())).Returns(new ClientConnection("fake-connection-id", "http://localhost", ClientPipeline.Create(), CredentialKind.None)); // Act var agent = mockAgentClient.Object.GetAIAgent("test-agent"); // Assert Assert.NotNull(agent); Assert.NotNull(capturedRequestOptions); } /// /// Verify that GetAIAgentAsync(string name) passes RequestOptions to the Protocol method. /// [Fact] public async Task GetAIAgentAsync_WithStringName_PassesRequestOptionsToProtocolAsync() { // Arrange RequestOptions? capturedRequestOptions = null; var mockAgentOperations = new Mock(); mockAgentOperations .Setup(x => x.GetAgentAsync(It.IsAny(), It.IsAny())) .Callback((name, options) => capturedRequestOptions = options) .Returns(Task.FromResult(ClientResult.FromResponse(new MockPipelineResponse(200, BinaryData.FromString(TestDataUtil.GetAgentResponseJson()))))); var mockAgentClient = new Mock(new Uri("https://test.openai.azure.com/"), new FakeAuthenticationTokenProvider()); mockAgentClient.SetupGet(x => x.Agents).Returns(mockAgentOperations.Object); mockAgentClient.Setup(x => x.GetConnection(It.IsAny())).Returns(new ClientConnection("fake-connection-id", "http://localhost", ClientPipeline.Create(), CredentialKind.None)); // Act var agent = await mockAgentClient.Object.GetAIAgentAsync("test-agent"); // Assert Assert.NotNull(agent); Assert.NotNull(capturedRequestOptions); } /// /// Verify that CreateAIAgent(string model, ChatClientAgentOptions options) passes RequestOptions to the Protocol method. /// [Fact] public void CreateAIAgent_WithChatClientAgentOptions_PassesRequestOptionsToProtocol() { // Arrange RequestOptions? capturedRequestOptions = null; var mockAgentOperations = new Mock(); mockAgentOperations .Setup(x => x.CreateAgentVersion(It.IsAny(), It.IsAny(), It.IsAny())) .Callback((name, content, options) => capturedRequestOptions = options) .Returns(ClientResult.FromResponse(new MockPipelineResponse(200, BinaryData.FromString(TestDataUtil.GetAgentVersionResponseJson())))); var mockAgentClient = new Mock(new Uri("https://test.openai.azure.com/"), new FakeAuthenticationTokenProvider()); mockAgentClient.SetupGet(x => x.Agents).Returns(mockAgentOperations.Object); mockAgentClient.Setup(x => x.GetConnection(It.IsAny())).Returns(new ClientConnection("fake-connection-id", "http://localhost", ClientPipeline.Create(), CredentialKind.None)); var agentOptions = new ChatClientAgentOptions { Name = "test-agent" }; // Act var agent = mockAgentClient.Object.CreateAIAgent("gpt-4", agentOptions); // Assert Assert.NotNull(agent); Assert.NotNull(capturedRequestOptions); } /// /// Verify that CreateAIAgentAsync(string model, ChatClientAgentOptions options) passes RequestOptions to the Protocol method. /// [Fact] public async Task CreateAIAgentAsync_WithChatClientAgentOptions_PassesRequestOptionsToProtocolAsync() { // Arrange RequestOptions? capturedRequestOptions = null; var mockAgentOperations = new Mock(); mockAgentOperations .Setup(x => x.CreateAgentVersionAsync(It.IsAny(), It.IsAny(), It.IsAny())) .Callback((name, content, options) => capturedRequestOptions = options) .Returns(Task.FromResult(ClientResult.FromResponse(new MockPipelineResponse(200, BinaryData.FromString(TestDataUtil.GetAgentVersionResponseJson()))))); var mockAgentClient = new Mock(new Uri("https://test.openai.azure.com/"), new FakeAuthenticationTokenProvider()); mockAgentClient.SetupGet(x => x.Agents).Returns(mockAgentOperations.Object); mockAgentClient.Setup(x => x.GetConnection(It.IsAny())).Returns(new ClientConnection("fake-connection-id", "http://localhost", ClientPipeline.Create(), CredentialKind.None)); var agentOptions = new ChatClientAgentOptions { Name = "test-agent" }; // Act var agent = await mockAgentClient.Object.CreateAIAgentAsync("gpt-4", agentOptions); // Assert Assert.NotNull(agent); Assert.NotNull(capturedRequestOptions); } /// /// Verifies that the user-agent header is added to both synchronous and asynchronous requests made by agent creation methods. /// [Fact] public async Task CreateAIAgent_UserAgentHeaderAddedToRequestsAsync() { using var httpHandler = new HttpHandlerAssert(request => { Assert.Equal("POST", request.Method.Method); Assert.Contains("MEAI", request.Headers.UserAgent.ToString()); return new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(TestDataUtil.GetAgentResponseJson(), Encoding.UTF8, "application/json") }; }); #pragma warning disable CA5399 using var httpClient = new HttpClient(httpHandler); #pragma warning restore CA5399 // Arrange var aiProjectClient = new AIProjectClient(new Uri("https://test.openai.azure.com/"), new FakeAuthenticationTokenProvider(), new() { Transport = new HttpClientPipelineTransport(httpClient) }); var agentOptions = new ChatClientAgentOptions { Name = "test-agent" }; // Act var agent1 = aiProjectClient.CreateAIAgent("test", agentOptions); var agent2 = await aiProjectClient.CreateAIAgentAsync("test", agentOptions); // Assert Assert.NotNull(agent1); Assert.NotNull(agent2); } /// /// Verifies that the user-agent header is added to both synchronous and asynchronous GetAIAgent requests. /// [Fact] public async Task GetAIAgent_UserAgentHeaderAddedToRequestsAsync() { using var httpHandler = new HttpHandlerAssert(request => { Assert.Equal("GET", request.Method.Method); Assert.Contains("MEAI", request.Headers.UserAgent.ToString()); return new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(TestDataUtil.GetAgentResponseJson(), Encoding.UTF8, "application/json") }; }); #pragma warning disable CA5399 using var httpClient = new HttpClient(httpHandler); #pragma warning restore CA5399 // Arrange var aiProjectClient = new AIProjectClient(new Uri("https://test.openai.azure.com/"), new FakeAuthenticationTokenProvider(), new() { Transport = new HttpClientPipelineTransport(httpClient) }); // Act var agent1 = aiProjectClient.GetAIAgent("test"); var agent2 = await aiProjectClient.GetAIAgentAsync("test"); // Assert Assert.NotNull(agent1); Assert.NotNull(agent2); } #endregion #region GetAIAgent(AIProjectClient, AgentReference) Tests /// /// Verify that GetAIAgent throws ArgumentNullException when AIProjectClient is null. /// [Fact] public void GetAIAgent_WithAgentReference_WithNullClient_ThrowsArgumentNullException() { // Arrange AIProjectClient? client = null; var agentReference = new AgentReference("test-name", "1"); // Act & Assert var exception = Assert.Throws(() => client!.GetAIAgent(agentReference)); Assert.Equal("aiProjectClient", exception.ParamName); } /// /// Verify that GetAIAgent throws ArgumentNullException when agentReference is null. /// [Fact] public void GetAIAgent_WithAgentReference_WithNullAgentReference_ThrowsArgumentNullException() { // Arrange var mockClient = new Mock(); // Act & Assert var exception = Assert.Throws(() => mockClient.Object.GetAIAgent((AgentReference)null!)); Assert.Equal("agentReference", exception.ParamName); } /// /// Verify that GetAIAgent with AgentReference creates a valid agent. /// [Fact] public void GetAIAgent_WithAgentReference_CreatesValidAgent() { // Arrange AIProjectClient client = this.CreateTestAgentClient(); var agentReference = new AgentReference("test-name", "1"); // Act var agent = client.GetAIAgent(agentReference); // Assert Assert.NotNull(agent); Assert.Equal("test-name", agent.Name); Assert.Equal("test-name:1", agent.Id); } /// /// Verify that GetAIAgent with AgentReference and clientFactory applies the factory. /// [Fact] public void GetAIAgent_WithAgentReference_WithClientFactory_AppliesFactoryCorrectly() { // Arrange AIProjectClient client = this.CreateTestAgentClient(); var agentReference = new AgentReference("test-name", "1"); TestChatClient? testChatClient = null; // Act var agent = client.GetAIAgent( agentReference, clientFactory: (innerClient) => testChatClient = new TestChatClient(innerClient)); // Assert Assert.NotNull(agent); var retrievedTestClient = agent.GetService(); Assert.NotNull(retrievedTestClient); Assert.Same(testChatClient, retrievedTestClient); } /// /// Verify that GetAIAgent with AgentReference sets the agent ID correctly. /// [Fact] public void GetAIAgent_WithAgentReference_SetsAgentIdCorrectly() { // Arrange AIProjectClient client = this.CreateTestAgentClient(); var agentReference = new AgentReference("test-name", "2"); // Act var agent = client.GetAIAgent(agentReference); // Assert Assert.NotNull(agent); Assert.Equal("test-name:2", agent.Id); } /// /// Verify that GetAIAgent with AgentReference and tools includes the tools in ChatOptions. /// [Fact] public void GetAIAgent_WithAgentReference_WithTools_IncludesToolsInChatOptions() { // Arrange AIProjectClient client = this.CreateTestAgentClient(); var agentReference = new AgentReference("test-name", "1"); var tools = new List { AIFunctionFactory.Create(() => "test", "test_function", "A test function") }; // Act var agent = client.GetAIAgent(agentReference, tools: tools); // Assert Assert.NotNull(agent); var chatOptions = GetAgentChatOptions(agent); Assert.NotNull(chatOptions); Assert.NotNull(chatOptions.Tools); Assert.Single(chatOptions.Tools); } #endregion #region GetService Tests /// /// Verify that GetService returns AgentRecord for agents created from AgentRecord. /// [Fact] public void GetService_WithAgentRecord_ReturnsAgentRecord() { // Arrange AIProjectClient client = this.CreateTestAgentClient(); AgentRecord agentRecord = this.CreateTestAgentRecord(); // Act var agent = client.GetAIAgent(agentRecord); var retrievedRecord = agent.GetService(); // Assert Assert.NotNull(retrievedRecord); Assert.Equal(agentRecord.Id, retrievedRecord.Id); } /// /// Verify that GetService returns null for AgentRecord when agent is created from AgentReference. /// [Fact] public void GetService_WithAgentReference_ReturnsNullForAgentRecord() { // Arrange AIProjectClient client = this.CreateTestAgentClient(); var agentReference = new AgentReference("test-name", "1"); // Act var agent = client.GetAIAgent(agentReference); var retrievedRecord = agent.GetService(); // Assert Assert.Null(retrievedRecord); } #endregion #region GetService Tests /// /// Verify that GetService returns AgentVersion for agents created from AgentVersion. /// [Fact] public void GetService_WithAgentVersion_ReturnsAgentVersion() { // Arrange AIProjectClient client = this.CreateTestAgentClient(); AgentVersion agentVersion = this.CreateTestAgentVersion(); // Act var agent = client.GetAIAgent(agentVersion); var retrievedVersion = agent.GetService(); // Assert Assert.NotNull(retrievedVersion); Assert.Equal(agentVersion.Id, retrievedVersion.Id); } /// /// Verify that GetService returns null for AgentVersion when agent is created from AgentReference. /// [Fact] public void GetService_WithAgentReference_ReturnsNullForAgentVersion() { // Arrange AIProjectClient client = this.CreateTestAgentClient(); var agentReference = new AgentReference("test-name", "1"); // Act var agent = client.GetAIAgent(agentReference); var retrievedVersion = agent.GetService(); // Assert Assert.Null(retrievedVersion); } #endregion #region ChatClientMetadata Tests /// /// Verify that ChatClientMetadata is properly populated for agents created from AgentRecord. /// [Fact] public void ChatClientMetadata_WithAgentRecord_IsPopulatedCorrectly() { // Arrange AIProjectClient client = this.CreateTestAgentClient(); AgentRecord agentRecord = this.CreateTestAgentRecord(); // Act var agent = client.GetAIAgent(agentRecord); var metadata = agent.GetService(); // Assert Assert.NotNull(metadata); Assert.NotNull(metadata.DefaultModelId); } /// /// Verify that ChatClientMetadata.DefaultModelId is set from PromptAgentDefinition model property. /// [Fact] public void ChatClientMetadata_WithPromptAgentDefinition_SetsDefaultModelIdFromModel() { // Arrange AIProjectClient client = this.CreateTestAgentClient(); var definition = new PromptAgentDefinition("gpt-4-turbo") { Instructions = "Test instructions" }; AgentRecord agentRecord = this.CreateTestAgentRecord(definition); // Act var agent = client.GetAIAgent(agentRecord); var metadata = agent.GetService(); // Assert Assert.NotNull(metadata); // The metadata should contain the model information from the agent definition Assert.NotNull(metadata.DefaultModelId); Assert.Equal("gpt-4-turbo", metadata.DefaultModelId); } /// /// Verify that ChatClientMetadata is properly populated for agents created from AgentVersion. /// [Fact] public void ChatClientMetadata_WithAgentVersion_IsPopulatedCorrectly() { // Arrange AIProjectClient client = this.CreateTestAgentClient(); AgentVersion agentVersion = this.CreateTestAgentVersion(); // Act var agent = client.GetAIAgent(agentVersion); var metadata = agent.GetService(); // Assert Assert.NotNull(metadata); Assert.NotNull(metadata.DefaultModelId); Assert.Equal((agentVersion.Definition as PromptAgentDefinition)!.Model, metadata.DefaultModelId); } #endregion #region AgentReference Availability Tests /// /// Verify that GetService returns AgentReference for agents created from AgentReference. /// [Fact] public void GetService_WithAgentReference_ReturnsAgentReference() { // Arrange AIProjectClient client = this.CreateTestAgentClient(); var agentReference = new AgentReference("test-agent", "1.0"); // Act var agent = client.GetAIAgent(agentReference); var retrievedReference = agent.GetService(); // Assert Assert.NotNull(retrievedReference); Assert.Equal("test-agent", retrievedReference.Name); Assert.Equal("1.0", retrievedReference.Version); } /// /// Verify that GetService returns null for AgentReference when agent is created from AgentRecord. /// [Fact] public void GetService_WithAgentRecord_ReturnsAlsoAgentReference() { // Arrange AIProjectClient client = this.CreateTestAgentClient(); AgentRecord agentRecord = this.CreateTestAgentRecord(); // Act var agent = client.GetAIAgent(agentRecord); var retrievedReference = agent.GetService(); // Assert Assert.NotNull(retrievedReference); Assert.Equal(agentRecord.Name, retrievedReference.Name); } /// /// Verify that GetService returns null for AgentReference when agent is created from AgentVersion. /// [Fact] public void GetService_WithAgentVersion_ReturnsAlsoAgentReference() { // Arrange AIProjectClient client = this.CreateTestAgentClient(); AgentVersion agentVersion = this.CreateTestAgentVersion(); // Act var agent = client.GetAIAgent(agentVersion); var retrievedReference = agent.GetService(); // Assert Assert.NotNull(retrievedReference); Assert.Equal(agentVersion.Name, retrievedReference.Name); } /// /// Verify that GetService returns AgentReference with correct version information. /// [Fact] public void GetService_WithAgentReference_ReturnsCorrectVersionInformation() { // Arrange AIProjectClient client = this.CreateTestAgentClient(); var agentReference = new AgentReference("versioned-agent", "3.5"); // Act var agent = client.GetAIAgent(agentReference); var retrievedReference = agent.GetService(); // Assert Assert.NotNull(retrievedReference); Assert.Equal("versioned-agent", retrievedReference.Name); Assert.Equal("3.5", retrievedReference.Version); } #endregion #region Helper Methods /// /// Creates a test AIProjectClient with fake behavior. /// private FakeAgentClient CreateTestAgentClient(string? agentName = null, string? instructions = null, string? description = null, AgentDefinition? agentDefinitionResponse = null) { return new FakeAgentClient(agentName, instructions, description, agentDefinitionResponse); } /// /// Creates a test AgentRecord for testing. /// private AgentRecord CreateTestAgentRecord(AgentDefinition? agentDefinition = null) { return ModelReaderWriter.Read(BinaryData.FromString(TestDataUtil.GetAgentResponseJson(agentDefinition: agentDefinition)))!; } private const string OpenAPISpec = """ { "openapi": "3.0.3", "info": { "title": "Tiny Test API", "version": "1.0.0" }, "paths": { "/ping": { "get": { "summary": "Health check", "operationId": "getPing", "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object", "properties": { "message": { "type": "string" } }, "required": ["message"] }, "example": { "message": "pong" } } } } } } } } } """; /// /// Creates a test AgentVersion for testing. /// private AgentVersion CreateTestAgentVersion() { return ModelReaderWriter.Read(BinaryData.FromString(TestDataUtil.GetAgentVersionResponseJson()))!; } /// /// Fake AIProjectClient for testing. /// private sealed class FakeAgentClient : AIProjectClient { public FakeAgentClient(string? agentName = null, string? instructions = null, string? description = null, AgentDefinition? agentDefinitionResponse = null) { this.Agents = new FakeAIProjectAgentsOperations(agentName, instructions, description, agentDefinitionResponse); } public override ClientConnection GetConnection(string connectionId) { return new ClientConnection("fake-connection-id", "http://localhost", ClientPipeline.Create(), CredentialKind.None); } public override AIProjectAgentsOperations Agents { get; } private sealed class FakeAIProjectAgentsOperations : AIProjectAgentsOperations { private readonly string? _agentName; private readonly string? _instructions; private readonly string? _description; private readonly AgentDefinition? _agentDefinition; public FakeAIProjectAgentsOperations(string? agentName = null, string? instructions = null, string? description = null, AgentDefinition? agentDefinitionResponse = null) { this._agentName = agentName; this._instructions = instructions; this._description = description; this._agentDefinition = agentDefinitionResponse; } public override ClientResult GetAgent(string agentName, RequestOptions options) { var responseJson = TestDataUtil.GetAgentResponseJson(this._agentName, this._agentDefinition, this._instructions, this._description); return ClientResult.FromValue(ModelReaderWriter.Read(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200, BinaryData.FromString(responseJson))); } public override ClientResult GetAgent(string agentName, CancellationToken cancellationToken = default) { var responseJson = TestDataUtil.GetAgentResponseJson(this._agentName, this._agentDefinition, this._instructions, this._description); return ClientResult.FromValue(ModelReaderWriter.Read(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200)); } public override Task GetAgentAsync(string agentName, RequestOptions options) { var responseJson = TestDataUtil.GetAgentResponseJson(this._agentName, this._agentDefinition, this._instructions, this._description); return Task.FromResult(ClientResult.FromValue(ModelReaderWriter.Read(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200, BinaryData.FromString(responseJson)))); } public override Task> GetAgentAsync(string agentName, CancellationToken cancellationToken = default) { var responseJson = TestDataUtil.GetAgentResponseJson(this._agentName, this._agentDefinition, this._instructions, this._description); return Task.FromResult(ClientResult.FromValue(ModelReaderWriter.Read(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200))); } public override ClientResult CreateAgentVersion(string agentName, BinaryContent content, RequestOptions? options = null) { var responseJson = TestDataUtil.GetAgentVersionResponseJson(this._agentName, this._agentDefinition, this._instructions, this._description); return ClientResult.FromValue(ModelReaderWriter.Read(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200, BinaryData.FromString(responseJson))); } public override ClientResult CreateAgentVersion(string agentName, AgentVersionCreationOptions? options = null, CancellationToken cancellationToken = default) { var responseJson = TestDataUtil.GetAgentVersionResponseJson(this._agentName, this._agentDefinition, this._instructions, this._description); return ClientResult.FromValue(ModelReaderWriter.Read(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200)); } public override Task CreateAgentVersionAsync(string agentName, BinaryContent content, RequestOptions? options = null) { var responseJson = TestDataUtil.GetAgentVersionResponseJson(this._agentName, this._agentDefinition, this._instructions, this._description); return Task.FromResult(ClientResult.FromValue(ModelReaderWriter.Read(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200, BinaryData.FromString(responseJson)))); } public override Task> CreateAgentVersionAsync(string agentName, AgentVersionCreationOptions? options = null, CancellationToken cancellationToken = default) { var responseJson = TestDataUtil.GetAgentVersionResponseJson(this._agentName, this._agentDefinition, this._instructions, this._description); return Task.FromResult(ClientResult.FromValue(ModelReaderWriter.Read(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200))); } } } private static PromptAgentDefinition GeneratePromptDefinitionResponse(PromptAgentDefinition inputDefinition, List? tools) { var definitionResponse = new PromptAgentDefinition(inputDefinition.Model) { Instructions = inputDefinition.Instructions }; if (tools is not null) { foreach (var tool in tools) { definitionResponse.Tools.Add(tool.GetService() ?? tool.AsOpenAIResponseTool()); } } return definitionResponse; } /// /// Test custom chat client that can be used to verify clientFactory functionality. /// private sealed class TestChatClient : DelegatingChatClient { public TestChatClient(IChatClient innerClient) : base(innerClient) { } } /// /// Mock pipeline response for testing ClientResult wrapping. /// private sealed class MockPipelineResponse : PipelineResponse { private readonly int _status; private readonly MockPipelineResponseHeaders _headers; public MockPipelineResponse(int status, BinaryData? content = null) { this._status = status; this.Content = content ?? BinaryData.Empty; this._headers = new MockPipelineResponseHeaders(); } public override int Status => this._status; public override string ReasonPhrase => "OK"; public override Stream? ContentStream { get => null; set { } } public override BinaryData Content { get; } protected override PipelineResponseHeaders HeadersCore => this._headers; public override BinaryData BufferContent(CancellationToken cancellationToken = default) => throw new NotSupportedException("Buffering content is not supported for mock responses."); public override ValueTask BufferContentAsync(CancellationToken cancellationToken = default) => throw new NotSupportedException("Buffering content asynchronously is not supported for mock responses."); public override void Dispose() { } private sealed class MockPipelineResponseHeaders : PipelineResponseHeaders { private readonly Dictionary _headers = new(StringComparer.OrdinalIgnoreCase) { { "Content-Type", "application/json" }, { "x-ms-request-id", "test-request-id" } }; public override bool TryGetValue(string name, out string? value) { return this._headers.TryGetValue(name, out value); } public override bool TryGetValues(string name, out IEnumerable? values) { if (this._headers.TryGetValue(name, out var value)) { values = [value]; return true; } values = null; return false; } public override IEnumerator> GetEnumerator() { return this._headers.GetEnumerator(); } } } #endregion /// /// Helper method to access internal ChatOptions property via reflection. /// private static ChatOptions? GetAgentChatOptions(ChatClientAgent agent) { if (agent is null) { return null; } var chatOptionsProperty = typeof(ChatClientAgent).GetProperty( "ChatOptions", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); return chatOptionsProperty?.GetValue(agent) as ChatOptions; } } /// /// Provides test data for invalid agent name validation tests. /// internal static class InvalidAgentNameTestData { /// /// Gets a collection of invalid agent names for theory-based testing. /// /// Collection of invalid agent name test cases. public static IEnumerable GetInvalidAgentNames() { yield return new object[] { "-agent" }; yield return new object[] { "agent-" }; yield return new object[] { "agent_name" }; yield return new object[] { "agent name" }; yield return new object[] { "agent@name" }; yield return new object[] { "agent#name" }; yield return new object[] { "agent$name" }; yield return new object[] { "agent%name" }; yield return new object[] { "agent&name" }; yield return new object[] { "agent*name" }; yield return new object[] { "agent.name" }; yield return new object[] { "agent/name" }; yield return new object[] { "agent\\name" }; yield return new object[] { "agent:name" }; yield return new object[] { "agent;name" }; yield return new object[] { "agent,name" }; yield return new object[] { "agentname" }; yield return new object[] { "agent?name" }; yield return new object[] { "agent!name" }; yield return new object[] { "agent~name" }; yield return new object[] { "agent`name" }; yield return new object[] { "agent^name" }; yield return new object[] { "agent|name" }; yield return new object[] { "agent[name" }; yield return new object[] { "agent]name" }; yield return new object[] { "agent{name" }; yield return new object[] { "agent}name" }; yield return new object[] { "agent(name" }; yield return new object[] { "agent)name" }; yield return new object[] { "agent+name" }; yield return new object[] { "agent=name" }; yield return new object[] { "a" + new string('b', 63) }; } }