File: Terminals\AspireTerminalTests.cs
Web Access
Project: src\tests\Aspire.Hosting.Tests\Aspire.Hosting.Tests.csproj (Aspire.Hosting.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.Reflection;
using Aspire.Hosting.Utils;
 
#pragma warning disable ASPIRETERMINAL001 // Test consumer of the experimental AppHost terminal API.
 
namespace Aspire.Hosting.Tests.Terminals;
 
[Trait("Partition", "2")]
public class AspireTerminalTests
{
    [Theory]
    [InlineData(false)]
    [InlineData(true)]
    public async Task SendKeyAsync_UninitializedKey_DoesNotInvokeBackend(bool canceled)
    {
        var invoked = false;
        var backend = new TestTerminalBackend("invalid-key")
        {
            OnSendKey = (_, _) =>
            {
                invoked = true;
                return Task.CompletedTask;
            }
        };
        await using var terminal = new AspireTerminal(backend);
        using var cts = new CancellationTokenSource();
        if (canceled)
        {
            cts.Cancel();
        }
 
        var exception = Assert.Throws<ArgumentException>(() =>
        {
            _ = terminal.SendKeyAsync(default, cts.Token);
        });
 
        Assert.Equal("key", exception.ParamName);
        Assert.False(invoked);
    }
 
    [Fact]
    public async Task SendKeyAsync_DeclaredKeys_ForwardKeyAndCancellationToken()
    {
        List<(AspireTerminalKey Key, CancellationToken Token)> calls = [];
        var backend = new TestTerminalBackend("valid-key")
        {
            OnSendKey = (key, token) =>
            {
                calls.Add((key, token));
                return Task.CompletedTask;
            }
        };
        await using var terminal = new AspireTerminal(backend);
        using var cts = new CancellationTokenSource();
        var keys = typeof(AspireTerminalKey).GetProperties(BindingFlags.Public | BindingFlags.Static)
            .Where(property => property.PropertyType == typeof(AspireTerminalKey))
            .Select(property => Assert.IsType<AspireTerminalKey>(property.GetValue(null)))
            .SelectMany(key => new[]
            {
                key,
                AspireTerminalKey.Ctrl(key),
                AspireTerminalKey.Shift(key),
                AspireTerminalKey.Alt(key),
                AspireTerminalKey.Ctrl(AspireTerminalKey.Shift(AspireTerminalKey.Alt(key)))
            })
            .ToArray();
 
        foreach (var key in keys)
        {
            await terminal.SendKeyAsync(key, cts.Token);
        }
 
        Assert.Equal(keys.Select(key => (key, cts.Token)), calls);
    }
 
    [Fact]
    public void PublicHandleIsSealedWithOnlyAnInternalConstructor()
    {
        Assert.True(typeof(AspireTerminal).IsPublic);
        Assert.True(typeof(AspireTerminal).IsSealed);
        Assert.Empty(typeof(AspireTerminal).GetConstructors());
        var constructor = Assert.Single(typeof(AspireTerminal).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic));
        Assert.True(constructor.IsAssembly);
        Assert.False(typeof(ITerminalBackend).IsVisible);
        Assert.Equal([typeof(IAsyncDisposable)], typeof(AspireTerminal).GetInterfaces());
    }
 
    [Fact]
    public async Task HandlePreservesIdentityAndLiveMetadata()
    {
        await using var service = TestTerminalService.Create();
        await using var terminal = service.CreateTerminal(new TerminalLaunchOptions
        {
            Title = "Before",
            Placement = TerminalPlacement.Dialog,
            Executable = "bash"
        });
        var backend = Assert.IsType<Hex1bAspireTerminal>(terminal.Backend);
        backend.Retitle("After");
 
        Assert.Same(terminal, backend.Handle);
        Assert.True(service.TryGetTerminal(terminal.Id, out var found));
        Assert.Same(terminal, found);
        Assert.Equal("After", terminal.Title);
        Assert.Equal(TerminalOwner.AppHost, terminal.Owner);
        Assert.Equal(TerminalPlacement.Dialog, terminal.Placement);
 
        await terminal.DisposeAsync();
        Assert.False(service.TryGetTerminal(terminal.Id, out _));
    }
}