File: AtsExportsTests.cs
Web Access
Project: src\tests\Aspire.Hosting.RemoteHost.Tests\Aspire.Hosting.RemoteHost.Tests.csproj (Aspire.Hosting.RemoteHost.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Aspire.Tests.Utils;
using Aspire.Hosting.Ats;
using Aspire.Hosting.Tests;
using Xunit;
 
namespace Aspire.Hosting.RemoteHost.Tests;
 
[Trait("Partition", "4")]
public class AtsExportsTests(ITestOutputHelper outputHelper)
{
    [Fact]
    public void GetConnectionString_ReturnsConfiguredValue()
    {
        var configuration = new ConfigurationBuilder()
            .AddInMemoryCollection(new Dictionary<string, string?>
            {
                ["ConnectionStrings:cache"] = "UseDevelopmentStorage=true"
            })
            .Build();
 
        var value = BuilderExports.GetConnectionString(configuration, "cache");
 
        Assert.Equal("UseDevelopmentStorage=true", value);
    }
 
    [Fact]
    public void IsDevelopment_ReturnsTrueForDevelopmentEnvironment()
    {
        var environment = new TestHostEnvironment
        {
            EnvironmentName = Environments.Development
        };
 
        var result = BuilderExports.IsDevelopment(environment);
 
        Assert.True(result);
    }
 
    [Fact]
    public void IsEnvironment_ReturnsTrueForMatchingEnvironmentName()
    {
        var environment = new TestHostEnvironment
        {
            EnvironmentName = "Custom"
        };
 
        var result = BuilderExports.IsEnvironment(environment, "Custom");
 
        Assert.True(result);
    }
 
    [Fact]
    public void ParseLogLevel_UnknownLevelDefaultsToInformation()
    {
        var result = LoggingExports.ParseLogLevel("verbose");
 
        Assert.Equal(LogLevel.Information, result);
    }
 
    [Fact]
    public void ParseLogLevel_StrictModeThrowsForUnknownLevel()
    {
        var exception = Assert.Throws<ArgumentOutOfRangeException>(() => LoggingExports.ParseLogLevel("verbose", throwOnUnknown: true));
 
        Assert.Equal("level", exception.ParamName);
    }
 
    // Polyglot callers cannot mutate the input handles they pass to PromptInputs (those handles live on the
    // server and only the data fields cross the wire), so submitted values must travel back through the result.
    // This round-trips a prompt to prove the result carries the values keyed by input name.
#pragma warning disable ASPIREINTERACTION001 // Type is for evaluation purposes only and is subject to change or removal in future updates.
    [Fact]
    public async Task PromptInputs_ResultCarriesSubmittedValuesByName()
    {
        var interactionService = new TestInteractionService();
 
        var promptTask = InteractionExports.PromptInputs(
            interactionService,
            "Configure",
            "Fill out the form.",
            [
                InteractionExports.CreateTextInput(interactionService, "region"),
                InteractionExports.CreateTextInput(interactionService, "zone"),
            ]);
 
        var data = await interactionService.Interactions.Reader.ReadAsync();
        data.Inputs["region"].Value = "westus";
        data.Inputs["zone"].Value = "a";
        data.CompletionTcs.SetResult(InteractionResult.Ok(data.Inputs));
 
        var result = await promptTask;
 
        Assert.False(result.Canceled);
        // Inputs is now the InteractionInputCollection handle, so callers can read submitted values by name
        // (mirroring the .NET indexer) in addition to enumerating them.
        Assert.Equal("westus", result.Inputs["region"].Value);
        Assert.Equal("a", result.Inputs["zone"].Value);
        Assert.Collection(
            result.Inputs,
            input =>
            {
                Assert.Equal("region", input.Name);
                Assert.Equal("westus", input.Value);
            },
            input =>
            {
                Assert.Equal("zone", input.Name);
                Assert.Equal("a", input.Value);
            });
    }
 
    [Fact]
    public async Task PromptInputs_ResultCarriesSubmittedFileMetadataByName()
    {
        var interactionService = new TestInteractionService();
        var input = InteractionExports.CreateFileInput(interactionService, "artifact", new CreateInteractionInputOptions
        {
            Label = "Artifact",
            MaxFileSize = 1024
        });
 
        var promptTask = InteractionExports.PromptInputs(
            interactionService,
            "Upload",
            "Select a file.",
            [input]);
 
        var data = await interactionService.Interactions.Reader.ReadAsync();
        data.Inputs["artifact"].Value = "/repo/artifact.zip";
 
        using var workspace = TemporaryWorkspace.Create(outputHelper);
        var tempFile = Path.Combine(workspace.WorkspaceRoot.FullName, "artifact.zip");
        await File.WriteAllTextAsync(tempFile, "test content");
        var releaseCount = 0;
        data.Inputs["artifact"].SetFiles(new InteractionFileCollection(
            [new InteractionFile("file-1", "artifact.zip", tempFile)],
            () => releaseCount++));
 
        data.CompletionTcs.SetResult(InteractionResult.Ok(data.Inputs));
 
        var result = await promptTask;
 
        Assert.False(result.Canceled);
        Assert.Equal("/repo/artifact.zip", result.Inputs["artifact"].Value);
        var files = result.Inputs["artifact"].GetFiles();
        var file = Assert.Single(files);
        Assert.Equal("artifact.zip", file.Name);
        Assert.Equal(1024, result.Inputs["artifact"].MaxFileSize);
 
        input.ReleaseFiles();
        input.ReleaseFiles();
 
        Assert.Equal(1, releaseCount);
        Assert.Equal("artifact.zip", file.Name);
    }
 
    [Fact]
    public void CreateFileInput_CreatesFileWithOptions()
    {
        var interactionService = new TestInteractionService();
 
        var input = InteractionExports.CreateFileInput(interactionService, "artifact", new CreateInteractionInputOptions
        {
            Label = "Artifact",
            Placeholder = "Choose artifact",
            MaxFileSize = 2048
        }).Input;
 
        Assert.Equal("artifact", input.Name);
        Assert.Equal("Artifact", input.Label);
        Assert.Equal("Choose artifact", input.Placeholder);
        Assert.Equal(InputType.File, input.InputType);
        Assert.Equal(2048, input.MaxFileSize);
    }
#pragma warning restore ASPIREINTERACTION001
 
    private sealed class TestHostEnvironment : IHostEnvironment
    {
        public string EnvironmentName { get; set; } = Environments.Production;
        public string ApplicationName { get; set; } = nameof(TestHostEnvironment);
        public string ContentRootPath { get; set; } = "/";
        public IFileProvider ContentRootFileProvider { get; set; } = new NullFileProvider();
    }
}