| File: ChatCompletion\ChatClientBuilderTest.cs | Web Access |
| Project: src\test\Libraries\Microsoft.Extensions.AI.Tests\Microsoft.Extensions.AI.Tests.csproj (Microsoft.Extensions.AI.Tests) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using Microsoft.Extensions.DependencyInjection; using Xunit; namespace Microsoft.Extensions.AI; public class ChatClientBuilderTest { [Fact] public void PassesServiceProviderToFactories() { var expectedServiceProvider = new ServiceCollection().BuildServiceProvider(); using TestChatClient expectedInnerClient = new(); using TestChatClient expectedOuterClient = new(); var builder = new ChatClientBuilder(services => { Assert.Same(expectedServiceProvider, services); return expectedInnerClient; }); builder.Use((innerClient, serviceProvider) => { Assert.Same(expectedServiceProvider, serviceProvider); Assert.Same(expectedInnerClient, innerClient); return expectedOuterClient; }); Assert.Same(expectedOuterClient, builder.Build(expectedServiceProvider)); } [Fact] public void BuildsPipelineInOrderAdded() { // Arrange using TestChatClient expectedInnerClient = new(); var builder = new ChatClientBuilder(expectedInnerClient); builder.Use(next => new InnerClientCapturingChatClient("First", next)); builder.Use(next => new InnerClientCapturingChatClient("Second", next)); builder.Use(next => new InnerClientCapturingChatClient("Third", next)); // Act var first = (InnerClientCapturingChatClient)builder.Build(); // Assert Assert.Equal("First", first.Name); var second = (InnerClientCapturingChatClient)first.InnerClient; Assert.Equal("Second", second.Name); var third = (InnerClientCapturingChatClient)second.InnerClient; Assert.Equal("Third", third.Name); Assert.Same(expectedInnerClient, third.InnerClient); } [Fact] public void DoesNotAcceptNullInnerService() { Assert.Throws<ArgumentNullException>("innerClient", () => new ChatClientBuilder((IChatClient)null!)); Assert.Throws<ArgumentNullException>("innerClient", () => ((IChatClient)null!).AsBuilder()); } [Fact] public void DoesNotAcceptNullFactories() { Assert.Throws<ArgumentNullException>("innerClientFactory", () => new ChatClientBuilder((Func<IServiceProvider, IChatClient>)null!)); } [Fact] public void DoesNotAllowFactoriesToReturnNull() { using var innerClient = new TestChatClient(); ChatClientBuilder builder = new(innerClient); builder.Use(_ => null!); var ex = Assert.Throws<InvalidOperationException>(() => builder.Build()); Assert.Contains("entry at index 0", ex.Message); } [Fact] public void UsesEmptyServiceProviderWhenNoServicesProvided() { using var innerClient = new TestChatClient(); ChatClientBuilder builder = new(innerClient); builder.Use((innerClient, serviceProvider) => { Assert.Null(serviceProvider.GetService(typeof(object))); var keyedServiceProvider = Assert.IsAssignableFrom<IKeyedServiceProvider>(serviceProvider); Assert.Null(keyedServiceProvider.GetKeyedService(typeof(object), "key")); Assert.Throws<InvalidOperationException>(() => keyedServiceProvider.GetRequiredKeyedService(typeof(object), "key")); return innerClient; }); builder.Build(); } private sealed class InnerClientCapturingChatClient(string name, IChatClient innerClient) : DelegatingChatClient(innerClient) { #pragma warning disable S3604 // False positive: Member initializer values should not be redundant public string Name { get; } = name; #pragma warning restore S3604 public new IChatClient InnerClient => base.InnerClient; } }