// Copyright (c) Microsoft. All rights reserved. using System; using System.Reflection; using Azure.AI.Extensions.OpenAI; using Microsoft.Extensions.AI; using OpenAI.Responses; namespace Microsoft.Agents.AI.Foundry.UnitTests; /// /// Unit tests for the class. /// public sealed class ProjectResponsesClientExtensionsTests { private static ProjectResponsesClient CreateTestClient() { return new ProjectResponsesClient(new FakeAuthenticationTokenProvider()); } /// /// Verify that AsIChatClientWithStoredOutputDisabled throws ArgumentNullException when client is null. /// [Fact] public void AsIChatClientWithStoredOutputDisabled_WithNullClient_ThrowsArgumentNullException() { // Act & Assert var exception = Assert.Throws(() => ((ProjectResponsesClient)null!).AsIChatClientWithStoredOutputDisabled()); Assert.Equal("responseClient", exception.ParamName); } /// /// Verify that AsIChatClientWithStoredOutputDisabled wraps the original ProjectResponsesClient, /// which remains accessible via the service chain. /// [Fact] public void AsIChatClientWithStoredOutputDisabled_InnerResponsesClientIsAccessible() { // Arrange var responseClient = CreateTestClient(); // Act var chatClient = responseClient.AsIChatClientWithStoredOutputDisabled(); // Assert - the inner ProjectResponsesClient should be accessible via GetService var innerClient = chatClient.GetService(); Assert.NotNull(innerClient); Assert.Same(responseClient, innerClient); } /// /// Verify that AsIChatClientWithStoredOutputDisabled with includeReasoningEncryptedContent false /// wraps the original ProjectResponsesClient, which remains accessible via the service chain. /// [Fact] public void AsIChatClientWithStoredOutputDisabled_WithIncludeReasoningFalse_InnerResponsesClientIsAccessible() { // Arrange var responseClient = CreateTestClient(); // Act var chatClient = responseClient.AsIChatClientWithStoredOutputDisabled(includeReasoningEncryptedContent: false); // Assert - the inner ProjectResponsesClient should be accessible via GetService var innerClient = chatClient.GetService(); Assert.NotNull(innerClient); Assert.Same(responseClient, innerClient); } /// /// Verify that AsIChatClientWithStoredOutputDisabled with default parameter (includeReasoningEncryptedContent = true) /// configures StoredOutputEnabled to false and includes ReasoningEncryptedContent in IncludedProperties. /// [Fact] public void AsIChatClientWithStoredOutputDisabled_Default_ConfiguresStoredOutputDisabledWithReasoningEncryptedContent() { // Arrange var responseClient = CreateTestClient(); // Act var chatClient = responseClient.AsIChatClientWithStoredOutputDisabled(); // Assert var createResponseOptions = GetCreateResponseOptionsFromPipeline(chatClient); Assert.NotNull(createResponseOptions); Assert.False(createResponseOptions.StoredOutputEnabled); Assert.Contains(IncludedResponseProperty.ReasoningEncryptedContent, createResponseOptions.IncludedProperties); } /// /// Verify that AsIChatClientWithStoredOutputDisabled with includeReasoningEncryptedContent explicitly set to true /// configures StoredOutputEnabled to false and includes ReasoningEncryptedContent in IncludedProperties. /// [Fact] public void AsIChatClientWithStoredOutputDisabled_WithIncludeReasoningTrue_ConfiguresStoredOutputDisabledWithReasoningEncryptedContent() { // Arrange var responseClient = CreateTestClient(); // Act var chatClient = responseClient.AsIChatClientWithStoredOutputDisabled(includeReasoningEncryptedContent: true); // Assert var createResponseOptions = GetCreateResponseOptionsFromPipeline(chatClient); Assert.NotNull(createResponseOptions); Assert.False(createResponseOptions.StoredOutputEnabled); Assert.Contains(IncludedResponseProperty.ReasoningEncryptedContent, createResponseOptions.IncludedProperties); } /// /// Verify that AsIChatClientWithStoredOutputDisabled with includeReasoningEncryptedContent set to false /// configures StoredOutputEnabled to false and does not include ReasoningEncryptedContent in IncludedProperties. /// [Fact] public void AsIChatClientWithStoredOutputDisabled_WithIncludeReasoningFalse_ConfiguresStoredOutputDisabledWithoutReasoningEncryptedContent() { // Arrange var responseClient = CreateTestClient(); // Act var chatClient = responseClient.AsIChatClientWithStoredOutputDisabled(includeReasoningEncryptedContent: false); // Assert var createResponseOptions = GetCreateResponseOptionsFromPipeline(chatClient); Assert.NotNull(createResponseOptions); Assert.False(createResponseOptions.StoredOutputEnabled); Assert.DoesNotContain(IncludedResponseProperty.ReasoningEncryptedContent, createResponseOptions.IncludedProperties); } /// /// Verify that AsIChatClientWithStoredOutputDisabled preserves an existing RawRepresentationFactory /// set on ChatOptions, augmenting it with StoredOutputEnabled and ReasoningEncryptedContent /// rather than replacing it. /// [Fact] public void AsIChatClientWithStoredOutputDisabled_PreservesExistingRawRepresentationFactory() { // Arrange var responseClient = CreateTestClient(); var chatClient = responseClient.AsIChatClientWithStoredOutputDisabled(); // Simulate a caller setting their own RawRepresentationFactory on ChatOptions // (e.g., to add WebSearchCallActionSources). var options = new ChatOptions { RawRepresentationFactory = _ => new CreateResponseOptions { IncludedProperties = { IncludedResponseProperty.WebSearchCallActionSources }, }, }; // Act var createResponseOptions = GetCreateResponseOptionsFromPipeline(chatClient, options); // Assert Assert.NotNull(createResponseOptions); Assert.False(createResponseOptions.StoredOutputEnabled); Assert.Contains(IncludedResponseProperty.ReasoningEncryptedContent, createResponseOptions.IncludedProperties); Assert.Contains(IncludedResponseProperty.WebSearchCallActionSources, createResponseOptions.IncludedProperties); } /// /// Verify that AsIChatClientWithStoredOutputDisabled does not duplicate ReasoningEncryptedContent /// when the existing factory already includes it. /// [Fact] public void AsIChatClientWithStoredOutputDisabled_DoesNotDuplicateReasoningEncryptedContent() { // Arrange var responseClient = CreateTestClient(); var chatClient = responseClient.AsIChatClientWithStoredOutputDisabled(); // Simulate a caller that already includes ReasoningEncryptedContent var options = new ChatOptions { RawRepresentationFactory = _ => new CreateResponseOptions { IncludedProperties = { IncludedResponseProperty.ReasoningEncryptedContent }, }, }; // Act var createResponseOptions = GetCreateResponseOptionsFromPipeline(chatClient, options); // Assert - ReasoningEncryptedContent should appear exactly once Assert.NotNull(createResponseOptions); int count = 0; foreach (var prop in createResponseOptions.IncludedProperties) { if (prop == IncludedResponseProperty.ReasoningEncryptedContent) { count++; } } Assert.Equal(1, count); } /// /// Verify that AsIChatClientWithStoredOutputDisabled works with an optional deployment name. /// [Fact] public void AsIChatClientWithStoredOutputDisabled_WithDeploymentName_ConfiguresStoredOutputDisabled() { // Arrange var responseClient = CreateTestClient(); // Act var chatClient = responseClient.AsIChatClientWithStoredOutputDisabled(deploymentName: "my-deployment"); // Assert var createResponseOptions = GetCreateResponseOptionsFromPipeline(chatClient); Assert.NotNull(createResponseOptions); Assert.False(createResponseOptions.StoredOutputEnabled); Assert.Contains(IncludedResponseProperty.ReasoningEncryptedContent, createResponseOptions.IncludedProperties); } /// /// Extracts the produced by the ConfigureOptions pipeline /// by using reflection to access the configure action and invoking it on a test . /// private static CreateResponseOptions? GetCreateResponseOptionsFromPipeline(IChatClient chatClient) { return GetCreateResponseOptionsFromPipeline(chatClient, new ChatOptions()); } /// /// Overload that runs the configure action on caller-supplied , /// useful for testing that existing factories are preserved. /// private static CreateResponseOptions? GetCreateResponseOptionsFromPipeline(IChatClient chatClient, ChatOptions options) { var configureField = chatClient.GetType().GetField("_configureOptions", BindingFlags.NonPublic | BindingFlags.Instance); Assert.NotNull(configureField); var configureAction = configureField.GetValue(chatClient) as Action; Assert.NotNull(configureAction); configureAction(options); Assert.NotNull(options.RawRepresentationFactory); return options.RawRepresentationFactory(chatClient) as CreateResponseOptions; } }