File: TestServices\NativeCliTestHost.cs
Web Access
Project: src\tests\Aspire.Hosting.Native.Core.Tests\Aspire.Hosting.Native.Core.Tests.csproj (Aspire.Hosting.Native.Core.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.Net.Sockets;
using System.Text.Json;
using Aspire.Hosting.Native.Api;
using Aspire.Hosting.Native.Cli;
using Aspire.Hosting.Native.Server;
using Aspire.Hosting.Native.Rpc;
using Aspire.Shared;
using Microsoft.Extensions.Logging.Abstractions;
using StreamJsonRpc;
 
namespace Aspire.Hosting.Native.Core.Tests.TestServices;
 
/// <summary>Connects the real StreamJsonRpc client to the native server's existing CLI protocol.</summary>
internal sealed class NativeCliTestHost : IAsyncDisposable
{
    private readonly DirectoryInfo _directory = Directory.CreateTempSubdirectory("native-cli-test-");
    private readonly CancellationTokenSource _lifetime = new();
    private readonly NativeCliProtocol _protocol;
    private readonly Task _run;
    public NativeCliLogBuffer Logs { get; }
    public bool IsReady => _protocol.IsAppHostReady;
    public string SocketPath => Path.Combine(_directory.FullName, "cli.sock");
    public JsonRpc Rpc { get; }
 
    private NativeCliTestHost(NativeApplicationServer server, NativeServerOptions options,
        Func<NativeCliDashboardUrls> dashboard, INativeBackchannelRpc rpc)
    {
        Logs = new NativeCliLogBuffer(options.RetainedAppHostLogEntries);
        SocketPermissionHelper.CreateDirectory(_directory.FullName, repairExisting: true);
        var path = SocketPath;
        _protocol = new(path, server, _lifetime.Cancel, _ => Task.FromResult(dashboard()), Logs, options, rpc);
        _run = _protocol.RunAsync(_lifetime.Token);
        var socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified);
        socket.Connect(new UnixDomainSocketEndPoint(path));
        var stream = new NetworkStream(socket, ownsSocket: true);
        var formatter = new SystemTextJsonFormatter
        {
            JsonSerializerOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }
        };
        Rpc = new JsonRpc(new HeaderDelimitedMessageHandler(stream, stream, formatter));
        Rpc.StartListening();
    }
 
    public static NativeCliTestHost Start(NativeApplicationServer server, NativeServerOptions options,
        Func<NativeCliDashboardUrls> dashboard) => new(server, options, dashboard,
            new NativeBackchannelRpc(options, NullLogger.Instance));
 
    public static NativeCliTestHost Start(NativeApplicationServer server, NativeServerOptions options,
        Func<NativeCliDashboardUrls> dashboard, INativeBackchannelRpc rpc) => new(server, options, dashboard, rpc);
 
    public async ValueTask DisposeAsync()
    {
        Rpc.Dispose();
        await _lifetime.CancelAsync();
        try
        {
            await _run.WaitAsync(TimeSpan.FromSeconds(10));
        }
        finally
        {
            _protocol.Dispose();
            _lifetime.Dispose();
            _directory.Delete(recursive: true);
        }
    }
}