File: AIChatWebExecutionTests.cs
Web Access
Project: src\test\ProjectTemplates\Microsoft.Extensions.AI.Templates.IntegrationTests\Microsoft.Extensions.AI.Templates.Tests.csproj (Microsoft.Extensions.AI.Templates.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.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Shared.ProjectTemplates.Tests;
using Microsoft.TestUtilities;
using Xunit;
using Xunit.Abstractions;
using static Microsoft.Shared.ProjectTemplates.Tests.TemplateTestUtilities;
 
namespace Microsoft.Extensions.AI.Templates.Tests;
 
/// <summary>
/// Contains execution tests for the "AI Chat Web" template.
/// </summary>
/// <remarks>
/// In addition to validating that the templates build and restore correctly,
/// these tests are also responsible for template component governance reporting.
/// This is because the generated output is left on disk after tests complete,
/// most importantly the project.assets.json file that gets created during restore.
/// Therefore, it's *critical* that these tests remain in a working state,
/// as disabling them will also disable CG reporting.
/// </remarks>
public class AIChatWebExecutionTests : TemplateExecutionTestBase<AIChatWebExecutionTests>, ITemplateExecutionTestConfigurationProvider
{
    public AIChatWebExecutionTests(TemplateExecutionTestFixture fixture, ITestOutputHelper outputHelper)
        : base(fixture, outputHelper)
    {
    }
 
    public static TemplateExecutionTestConfiguration Configuration { get; } = new()
    {
        TemplatePackageName = "Microsoft.Extensions.AI.Templates",
        TemplateName = "aichatweb"
    };
 
    public static IEnumerable<object[]> GetSupportedProjectConfigurations()
    {
        (string name, string[] values)[] allOptionValues = [
            ("--provider",          ["azureopenai", "githubmodels", "ollama", "openai" /*, "azureaifoundry" */]),
            ("--vector-store",      ["azureaisearch", "local", "qdrant"]),
            ("--aspire",            ["true", "false"]),
            ("--managed-identity",  ["true", "false"]),
            ("--Framework",         ["net9.0", "net10.0"])
        ];
 
        foreach (var args in GetPossibleOptions(allOptionValues))
        {
            // Managed Identity only applies when using an Azure service and not using aspire
            if (HasOption(args, "--managed-identity"))
            {
                if (HasOption(args, "--aspire"))
                {
                    continue;
                }
 
                if (!HasOption(args, "--provider", "azureopenai") &&
                    !HasOption(args, "--provider", "azureaifoundry") &&
                    !HasOption(args, "--vector-store", "azureaisearch"))
                {
                    continue;
                }
            }
 
            // Qdrant requires using Aspire orchestration
            if (HasOption(args, "--vector-store", "qdrant") && !HasOption(args, "--aspire"))
            {
                continue;
            }
 
            yield return args;
        }
    }
 
    // Do not skip. See XML docs for this test class.
    [Theory]
    [MemberData(nameof(GetSupportedProjectConfigurations))]
    public async Task TestAllSupportedConfigurations(params string[] args)
    {
        string projectName = GetProjectNameForArgs(args, prefix: "AIChatWeb");
        string? startupProjectRelativePath = HasOption(args, "--aspire") ? $"{projectName}.AppHost" : null;
 
        await CreateRestoreAndBuild(projectName, args, startupProjectRelativePath);
    }
 
    /// <summary>
    /// Runs a single test with --aspire and a project name that will trigger the class name
    /// normalization bug reported in https://github.com/dotnet/extensions/issues/6811.
    /// </summary>
    [Fact]
    public async Task CreateRestoreAndBuild_AspireProjectName()
    {
        await CreateRestoreAndBuild("mix.ed-dash_name 123", ["--aspire", "--provider", "azureopenai"]);
    }
 
    /// <summary>
    /// Tests build for various project name formats, including dots and other
    /// separators, to trigger the class name normalization bug described
    /// in https://github.com/dotnet/extensions/issues/6811
    /// </summary>
    /// <remarks>
    /// Because this test takes a few minutes to run and is only needed for regression
    /// testing of project name handing integration with Aspire, it is skipped by default.
    /// Set the environment variable <c>AI_TEMPLATES_TEST_PROJECT_NAMES</c> to "true" or "1"
    /// to enable it.
    /// </remarks>
    [ConditionalTheory]
    [EnvironmentVariableCondition("AI_TEMPLATES_TEST_PROJECT_NAMES", "true", "1")]
    [InlineData("dot.name")]
    [InlineData("project.123")]
    [InlineData("space name")]
    [InlineData(".1My.Projec-")]
    [InlineData("1Project123")]
    [InlineData("11double")]
    [InlineData("1")]
    [InlineData("nomatch")]
    public async Task CreateRestoreAndBuild_AspireProjectName_Variants(string projectName)
    {
        await CreateRestoreAndBuild(projectName, ["--aspire", "--provider", "azureopenai"]);
    }
}