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 expectedResult = new();
        var builder = new ChatClientBuilder(expectedServiceProvider);
 
        builder.Use((serviceProvider, innerClient) =>
        {
            Assert.Same(expectedServiceProvider, serviceProvider);
            return expectedResult;
        });
 
        using TestChatClient innerClient = new();
        Assert.Equal(expectedResult, builder.Use(innerClient: innerClient));
    }
 
    [Fact]
    public void BuildsPipelineInOrderAdded()
    {
        // Arrange
        using TestChatClient expectedInnerClient = new();
        var builder = new ChatClientBuilder();
 
        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.Use(expectedInnerClient);
 
        // 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>(() => new ChatClientBuilder().Use((IChatClient)null!));
    }
 
    [Fact]
    public void DoesNotAcceptNullFactories()
    {
        ChatClientBuilder builder = new();
        Assert.Throws<ArgumentNullException>(() => builder.Use((Func<IChatClient, IChatClient>)null!));
        Assert.Throws<ArgumentNullException>(() => builder.Use((Func<IServiceProvider, IChatClient, IChatClient>)null!));
    }
 
    [Fact]
    public void DoesNotAllowFactoriesToReturnNull()
    {
        ChatClientBuilder builder = new();
        builder.Use(_ => null!);
        var ex = Assert.Throws<InvalidOperationException>(() => builder.Use(new TestChatClient()));
        Assert.Contains("entry at index 0", ex.Message);
    }
 
    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;
    }
}