File: AzureFoundryToolboxAdministrationTests.cs
Web Access
Project: src\tests\Aspire.Hosting.Foundry.Tests\Aspire.Hosting.Foundry.Tests.csproj (Aspire.Hosting.Foundry.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
#pragma warning disable AAIP001 // Toolbox APIs are experimental.
 
using System.ClientModel.Primitives;
using System.Net;
using System.Text;
using System.Text.Json;
using Aspire.Hosting.Utils;
using Azure.AI.Projects;
using Azure.AI.Projects.Agents;
 
namespace Aspire.Hosting.Foundry.Tests;
 
public class AzureFoundryToolboxAdministrationTests
{
    [Fact]
    public async Task CreateVersionAsync_PreservesSearchToolInHttpRequestAndReturnedConfiguration()
    {
        using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
        var project = builder.AddFoundry("account").AddProject("my-project");
        var search = builder.AddAzureSearch("search");
        var toolbox = project.AddToolbox("field-tools")
            .WithDescription("Tools for field technicians.")
            .WithAISearchTool("knowledge-base", search, "docs", "Search the internal knowledge base.");
        var searchDefinition = Assert.IsType<FoundryToolboxAzureAISearchToolDefinition>(
            Assert.Single(toolbox.Resource.Tools));
        const string connectionId = "/subscriptions/sub/resourceGroups/rg/providers/Microsoft.CognitiveServices/accounts/account/projects/my-project/connections/search";
        searchDefinition.Connection.Outputs["id"] = connectionId;
        var tool = await searchDefinition.ResolveAsync(CancellationToken.None);
        var definition = FoundryToolboxDeploymentDefinition.Create(
            "field-tools", "Tools for field technicians.", [tool], new Dictionary<string, string>());
        var responseJson = $$"""
            {
              "object": "toolbox.version",
              "id": "field-tools:1",
              "name": "field-tools",
              "version": "1",
              "created_at": 0,
              "description": "Tools for field technicians.",
              "metadata": {{JsonSerializer.Serialize(definition.CreateDeploymentMetadata())}},
              "tools": [
                {
                  "type": "azure_ai_search",
                  "name": "knowledge-base",
                  "description": "Search the internal knowledge base.",
                  "azure_ai_search": {
                    "indexes": [{"project_connection_id": "{{connectionId}}", "index_name": "docs"}]
                  }
                }
              ]
            }
            """;
        using var handler = new SequenceHttpMessageHandler(
            new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = new StringContent(responseJson, Encoding.UTF8, "application/json")
            },
            new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = new StringContent(responseJson, Encoding.UTF8, "application/json")
            });
        using var httpClient = new HttpClient(handler);
        var options = new AIProjectClientOptions { Transport = new HttpClientPipelineTransport(httpClient) };
        options.AddPolicy(new FoundryToolboxFeaturesPolicy(), PipelinePosition.PerCall);
        var projectClient = new AIProjectClient(
            new Uri("https://example.invalid/api/projects/my-project"), new TestTokenCredential(), options);
        var toolboxes = projectClient.AgentAdministrationClient.GetAgentToolboxes();
        var administration = new AzureFoundryToolboxAdministration(toolboxes, _ => Assert.Fail("Unexpected retry."));
 
        var version = await administration.CreateVersionAsync(definition, CancellationToken.None);
        var returned = (await toolboxes.GetVersionAsync("field-tools", version, CancellationToken.None)).Value;
 
        Assert.Equal("1", version);
        Assert.Equal(version, returned.Version);
        Assert.Equal(definition.Description, returned.Description);
        Assert.Equal(definition.ConfigurationHash, returned.Metadata["aspire-configuration-hash"]);
        var returnedTool = Assert.IsType<AzureAISearchToolboxTool>(Assert.Single(returned.Tools));
        Assert.Equal("knowledge-base", returnedTool.Name);
        Assert.Equal("Search the internal knowledge base.", returnedTool.Description);
        var index = Assert.Single(returnedTool.AzureAiSearch.Indexes);
        Assert.Equal(connectionId, index.ProjectConnectionId);
        Assert.Equal("docs", index.IndexName);
        Assert.Equal(2, handler.Requests.Count);
        using var request = JsonDocument.Parse(handler.Requests[0].Content);
        using var returnedConfiguration = JsonDocument.Parse(ModelReaderWriter.Write(
            returned, ModelReaderWriterOptions.Json, AzureAIProjectsAgentsContext.Default));
        await Verify(JsonSerializer.Serialize(new
        {
            Request = request.RootElement,
            Returned = returnedConfiguration.RootElement
        }, new JsonSerializerOptions { WriteIndented = true }), "json")
            .AddScrubber(text => text.Replace(definition.ConfigurationHash, "{configurationHash}"));
    }
}