| File: Backchannel\AppHostBackchannelTests.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.Net.Sockets; using Aspire.Hosting.Tests.Utils; using Aspire.Hosting.Utils; using Microsoft.VisualStudio.Threading; using StreamJsonRpc; namespace Aspire.Hosting.Backchannel; [Trait("Partition", "4")] public class AppHostBackchannelTests(ITestOutputHelper outputHelper) { [Fact] public async Task CanConnectToBackchannel() { var root = Directory.CreateTempSubdirectory(); try { await CanConnectToBackchannelAsync(Path.Combine(root.FullName, "custom", "cli.sock")); } finally { root.Delete(recursive: true); } } private async Task CanConnectToBackchannelAsync(string socketPath) { using var builder = TestDistributedApplicationBuilder.CreateWithTestContainerRegistry(outputHelper); builder.Configuration[KnownConfigNames.UnixSocketPath] = socketPath; var backchannelReadyTaskCompletionSource = new TaskCompletionSource<BackchannelReadyEvent>(); builder.Eventing.Subscribe<BackchannelReadyEvent>((e, ct) => { backchannelReadyTaskCompletionSource.SetResult(e); return Task.CompletedTask; }); var backchannelConnectedTaskCompletionSource = new TaskCompletionSource<BackchannelConnectedEvent>(); builder.Eventing.Subscribe<BackchannelConnectedEvent>((e, ct) => { backchannelConnectedTaskCompletionSource.SetResult(e); return Task.CompletedTask; }); using var app = builder.Build(); await app.StartAsync().WaitAsync(TimeSpan.FromSeconds(60)); var backchannelReadyEvent = await backchannelReadyTaskCompletionSource.Task.WaitAsync(TimeSpan.FromSeconds(60)); if (!OperatingSystem.IsWindows()) { Assert.Equal(UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute, File.GetUnixFileMode(Path.GetDirectoryName(backchannelReadyEvent.SocketPath)!)); Assert.Equal(UnixFileMode.UserRead | UnixFileMode.UserWrite, File.GetUnixFileMode(backchannelReadyEvent.SocketPath)); } var socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified); var endpoint = new UnixDomainSocketEndPoint(backchannelReadyEvent.SocketPath); await socket.ConnectAsync(endpoint).WaitAsync(TimeSpan.FromSeconds(60)); _ = await backchannelConnectedTaskCompletionSource.Task.WaitAsync(TimeSpan.FromSeconds(60)); using var stream = new NetworkStream(socket, true); using var rpc = JsonRpc.Attach(stream); await app.StopAsync().WaitAsync(TimeSpan.FromSeconds(60)); } [Fact] public async Task CanStreamResourceStates() { using var builder = TestDistributedApplicationBuilder.CreateWithTestContainerRegistry(outputHelper); builder.Configuration[KnownConfigNames.UnixSocketPath] = UnixSocketHelper.GetBackchannelSocketPath(); builder.AddResource(new TestResource("test")) .WithInitialState(new () { ResourceType = "TestResource", State = new ("Running", null), Properties = [new("A", "B"), new("c", "d")], EnvironmentVariables = [new("e", "f", true), new("g", "h", false)] }); var backchannelReadyTaskCompletionSource = new TaskCompletionSource<BackchannelReadyEvent>(); builder.Eventing.Subscribe<BackchannelReadyEvent>((e, ct) => { backchannelReadyTaskCompletionSource.SetResult(e); return Task.CompletedTask; }); using var app = builder.Build(); await app.StartAsync().WaitAsync(TimeSpan.FromSeconds(60)); var backchannelReadyEvent = await backchannelReadyTaskCompletionSource.Task.WaitAsync(TimeSpan.FromSeconds(60)); var socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified); var endpoint = new UnixDomainSocketEndPoint(backchannelReadyEvent.SocketPath); await socket.ConnectAsync(endpoint).WaitAsync(TimeSpan.FromSeconds(60)); using var stream = new NetworkStream(socket, true); using var rpc = JsonRpc.Attach(stream); var resourceEvents = await rpc.InvokeAsync<IAsyncEnumerable<RpcResourceState>>( "GetResourceStatesAsync", Array.Empty<object>() ).WaitAsync(TimeSpan.FromSeconds(60)); await foreach (var resourceEvent in resourceEvents) { Assert.Equal("test", resourceEvent.Resource); Assert.Equal("TestResource", resourceEvent.Type); Assert.Equal("Running", resourceEvent.State); Assert.Empty(resourceEvent.Endpoints); break; } await app.StopAsync().WaitAsync(TimeSpan.FromSeconds(60)); } } file sealed class TestResource(string name) : Resource(name) { }