diff --git a/Directory.Packages.props b/Directory.Packages.props
index 8e4674e5743..097be0ed6d4 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -175,17 +175,18 @@
-
+
-
+
+
-
+
@@ -195,6 +196,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -218,18 +236,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
@@ -254,26 +260,8 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
@@ -295,17 +283,5 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/playground/AzureAIFoundryEndToEnd/AzureAIFoundryEndToEnd.WebStory/AzureAIFoundryEndToEnd.WebStory.csproj b/playground/AzureAIFoundryEndToEnd/AzureAIFoundryEndToEnd.WebStory/AzureAIFoundryEndToEnd.WebStory.csproj
index cd25e05445f..aa19fad6805 100644
--- a/playground/AzureAIFoundryEndToEnd/AzureAIFoundryEndToEnd.WebStory/AzureAIFoundryEndToEnd.WebStory.csproj
+++ b/playground/AzureAIFoundryEndToEnd/AzureAIFoundryEndToEnd.WebStory/AzureAIFoundryEndToEnd.WebStory.csproj
@@ -4,9 +4,6 @@
$(DefaultTargetFramework)
enable
enable
-
-
- true
diff --git a/playground/AzureOpenAIEndToEnd/AzureOpenAIEndToEnd.WebStory/AzureOpenAIEndToEnd.WebStory.csproj b/playground/AzureOpenAIEndToEnd/AzureOpenAIEndToEnd.WebStory/AzureOpenAIEndToEnd.WebStory.csproj
index cefd34325a6..ede52a85213 100644
--- a/playground/AzureOpenAIEndToEnd/AzureOpenAIEndToEnd.WebStory/AzureOpenAIEndToEnd.WebStory.csproj
+++ b/playground/AzureOpenAIEndToEnd/AzureOpenAIEndToEnd.WebStory/AzureOpenAIEndToEnd.WebStory.csproj
@@ -4,9 +4,6 @@
$(DefaultTargetFramework)
enable
enable
-
-
- true
diff --git a/playground/GitHubModelsEndToEnd/GitHubModelsEndToEnd.WebStory/GitHubModelsEndToEnd.WebStory.csproj b/playground/GitHubModelsEndToEnd/GitHubModelsEndToEnd.WebStory/GitHubModelsEndToEnd.WebStory.csproj
index cd25e05445f..aa19fad6805 100644
--- a/playground/GitHubModelsEndToEnd/GitHubModelsEndToEnd.WebStory/GitHubModelsEndToEnd.WebStory.csproj
+++ b/playground/GitHubModelsEndToEnd/GitHubModelsEndToEnd.WebStory/GitHubModelsEndToEnd.WebStory.csproj
@@ -4,9 +4,6 @@
$(DefaultTargetFramework)
enable
enable
-
-
- true
diff --git a/playground/OpenAIEndToEnd/OpenAIEndToEnd.WebStory/OpenAIEndToEnd.WebStory.csproj b/playground/OpenAIEndToEnd/OpenAIEndToEnd.WebStory/OpenAIEndToEnd.WebStory.csproj
index 2df50b9502b..60f4356d1a3 100644
--- a/playground/OpenAIEndToEnd/OpenAIEndToEnd.WebStory/OpenAIEndToEnd.WebStory.csproj
+++ b/playground/OpenAIEndToEnd/OpenAIEndToEnd.WebStory/OpenAIEndToEnd.WebStory.csproj
@@ -4,9 +4,6 @@
$(DefaultTargetFramework)
enable
enable
-
-
- true
diff --git a/src/Aspire.Hosting/Backchannel/AuxiliaryBackchannelService.cs b/src/Aspire.Hosting/Backchannel/AuxiliaryBackchannelService.cs
index 6bf750cff6a..2ef434dd1af 100644
--- a/src/Aspire.Hosting/Backchannel/AuxiliaryBackchannelService.cs
+++ b/src/Aspire.Hosting/Backchannel/AuxiliaryBackchannelService.cs
@@ -22,12 +22,21 @@ internal sealed class AuxiliaryBackchannelService(
: BackgroundService
{
private Socket? _serverSocket;
+ private readonly TaskCompletionSource _listeningTcs = new(TaskCreationOptions.RunContinuationsAsynchronously);
///
/// Gets the Unix socket path where the auxiliary backchannel is listening.
///
public string? SocketPath { get; private set; }
+ ///
+ /// Gets a task that completes when the server socket is bound and listening for connections.
+ ///
+ ///
+ /// Used by tests to wait until the backchannel is ready before attempting to connect.
+ ///
+ internal Task ListeningTask => _listeningTcs.Task;
+
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
@@ -72,6 +81,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
_serverSocket.Listen(backlog: 10); // Allow multiple pending connections
logger.LogDebug("Auxiliary backchannel listening on {SocketPath}", SocketPath);
+ _listeningTcs.TrySetResult();
// Accept connections in a loop (supporting multiple concurrent connections)
while (!stoppingToken.IsCancellationRequested)
diff --git a/src/Components/Aspire.Azure.AI.Inference/Aspire.Azure.AI.Inference.csproj b/src/Components/Aspire.Azure.AI.Inference/Aspire.Azure.AI.Inference.csproj
index 7370e834905..7926621c583 100644
--- a/src/Components/Aspire.Azure.AI.Inference/Aspire.Azure.AI.Inference.csproj
+++ b/src/Components/Aspire.Azure.AI.Inference/Aspire.Azure.AI.Inference.csproj
@@ -9,8 +9,6 @@
$(NoWarn);SYSLIB1100;SYSLIB1101
true
-
- true
diff --git a/src/Components/Aspire.Azure.AI.OpenAI/Aspire.Azure.AI.OpenAI.csproj b/src/Components/Aspire.Azure.AI.OpenAI/Aspire.Azure.AI.OpenAI.csproj
index 99d48574b2e..db2deffe6bc 100644
--- a/src/Components/Aspire.Azure.AI.OpenAI/Aspire.Azure.AI.OpenAI.csproj
+++ b/src/Components/Aspire.Azure.AI.OpenAI/Aspire.Azure.AI.OpenAI.csproj
@@ -10,8 +10,6 @@
$(NoWarn);SYSLIB1100;SYSLIB1101;AOAI001
true
-
- true
diff --git a/src/Components/Aspire.OpenAI/Aspire.OpenAI.csproj b/src/Components/Aspire.OpenAI/Aspire.OpenAI.csproj
index 836c1d9d11c..6b76d148c20 100644
--- a/src/Components/Aspire.OpenAI/Aspire.OpenAI.csproj
+++ b/src/Components/Aspire.OpenAI/Aspire.OpenAI.csproj
@@ -8,8 +8,6 @@
$(NoWarn);SYSLIB1100;SYSLIB1101
true
-
- true
diff --git a/tests/Aspire.Azure.AI.Inference.Tests/Aspire.Azure.AI.Inference.Tests.csproj b/tests/Aspire.Azure.AI.Inference.Tests/Aspire.Azure.AI.Inference.Tests.csproj
index cca72f73ac9..948e92b0096 100644
--- a/tests/Aspire.Azure.AI.Inference.Tests/Aspire.Azure.AI.Inference.Tests.csproj
+++ b/tests/Aspire.Azure.AI.Inference.Tests/Aspire.Azure.AI.Inference.Tests.csproj
@@ -2,9 +2,6 @@
$(AllTargetFrameworks)
-
-
- true
diff --git a/tests/Aspire.Azure.AI.OpenAI.Tests/Aspire.Azure.AI.OpenAI.Tests.csproj b/tests/Aspire.Azure.AI.OpenAI.Tests/Aspire.Azure.AI.OpenAI.Tests.csproj
index a27e07a3a63..2f45183b667 100644
--- a/tests/Aspire.Azure.AI.OpenAI.Tests/Aspire.Azure.AI.OpenAI.Tests.csproj
+++ b/tests/Aspire.Azure.AI.OpenAI.Tests/Aspire.Azure.AI.OpenAI.Tests.csproj
@@ -2,9 +2,6 @@
$(AllTargetFrameworks)
-
-
- true
diff --git a/tests/Aspire.Hosting.Azure.Tests/AzureDeployerTests.cs b/tests/Aspire.Hosting.Azure.Tests/AzureDeployerTests.cs
index 0c9ff5c0492..09460f25c8d 100644
--- a/tests/Aspire.Hosting.Azure.Tests/AzureDeployerTests.cs
+++ b/tests/Aspire.Hosting.Azure.Tests/AzureDeployerTests.cs
@@ -1072,7 +1072,7 @@ public async Task DeployAsync_WithAzureResourceDependencies_DoesNotHang(string s
// Act
using var app = builder.Build();
await app.StartAsync();
- await app.StopAsync();
+ await app.WaitForShutdownAsync();
if (step == "diagnostics")
{
@@ -1159,7 +1159,7 @@ public async Task DeployAsync_WithRedisAccessKeyAuthentication_CreatesCorrectDep
// Act
using var app = builder.Build();
await app.StartAsync();
- await app.StopAsync();
+ await app.WaitForShutdownAsync();
// In diagnostics mode, verify the deployment graph shows correct dependencies
var logs = mockActivityReporter.LoggedMessages
diff --git a/tests/Aspire.Hosting.Testing.Tests/ResourceLoggerForwarderServiceTests.cs b/tests/Aspire.Hosting.Testing.Tests/ResourceLoggerForwarderServiceTests.cs
index 2e1084139c8..d10d3e2775f 100644
--- a/tests/Aspire.Hosting.Testing.Tests/ResourceLoggerForwarderServiceTests.cs
+++ b/tests/Aspire.Hosting.Testing.Tests/ResourceLoggerForwarderServiceTests.cs
@@ -35,11 +35,29 @@ public async Task ExecuteDoesNotThrowOperationCanceledWhenAppStoppingTokenSignal
var loggerFactory = new NullLoggerFactory();
var resourceLogForwarder = new ResourceLoggerForwarderService(resourceNotificationService, resourceLoggerService, hostEnvironment, loggerFactory);
+ // use a task to signal when the resourceLogForwarder has started executing
+ var subscribedTcs = new TaskCompletionSource();
+ var subscriberLoop = Task.Run(async () =>
+ {
+ await foreach (var sub in resourceLoggerService.WatchAnySubscribersAsync(hostApplicationLifetime.ApplicationStopping))
+ {
+ subscribedTcs.TrySetResult();
+ return;
+ }
+ });
+
await resourceLogForwarder.StartAsync(hostApplicationLifetime.ApplicationStopping);
Assert.NotNull(resourceLogForwarder.ExecuteTask);
Assert.Equal(TaskStatus.WaitingForActivation, resourceLogForwarder.ExecuteTask.Status);
+ // Publish an update to the resource to kickstart the notification service loop
+ var myresource = new CustomResource("myresource");
+ await resourceNotificationService.PublishUpdateAsync(myresource, snapshot => snapshot with { State = "Running" });
+
+ // Wait for the log stream to begin
+ await subscribedTcs.Task.WaitAsync(TimeSpan.FromSeconds(15));
+
// Signal the stopping token
hostApplicationLifetime.StopApplication();
diff --git a/tests/Aspire.Hosting.Tests/Backchannel/AuxiliaryBackchannelTests.cs b/tests/Aspire.Hosting.Tests/Backchannel/AuxiliaryBackchannelTests.cs
index 86f7622be67..1c06ea8f482 100644
--- a/tests/Aspire.Hosting.Tests/Backchannel/AuxiliaryBackchannelTests.cs
+++ b/tests/Aspire.Hosting.Tests/Backchannel/AuxiliaryBackchannelTests.cs
@@ -28,16 +28,13 @@ public async Task CanStartAuxiliaryBackchannelService()
return Task.CompletedTask;
});
- // Register the auxiliary backchannel service
- builder.Services.AddSingleton();
- builder.Services.AddSingleton(sp => sp.GetRequiredService());
-
using var app = builder.Build();
await app.StartAsync().WaitAsync(TimeSpan.FromSeconds(60));
// Get the service and verify it started
var service = app.Services.GetRequiredService();
+ await service.ListeningTask.WaitAsync(TimeSpan.FromSeconds(60));
Assert.NotNull(service.SocketPath);
Assert.True(File.Exists(service.SocketPath));
@@ -71,25 +68,22 @@ public async Task CanConnectMultipleClientsToAuxiliaryBackchannel()
return Task.CompletedTask;
});
- // Register the auxiliary backchannel service
- builder.Services.AddSingleton();
- builder.Services.AddSingleton(sp => sp.GetRequiredService());
-
using var app = builder.Build();
await app.StartAsync().WaitAsync(TimeSpan.FromSeconds(60));
// Get the service
var service = app.Services.GetRequiredService();
+ await service.ListeningTask.WaitAsync(TimeSpan.FromSeconds(60));
Assert.NotNull(service.SocketPath);
// Connect multiple clients concurrently
var client1Socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified);
var client2Socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified);
var client3Socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified);
-
+
var endpoint = new UnixDomainSocketEndPoint(service.SocketPath);
-
+
await client1Socket.ConnectAsync(endpoint).WaitAsync(TimeSpan.FromSeconds(60));
await client2Socket.ConnectAsync(endpoint).WaitAsync(TimeSpan.FromSeconds(60));
await client3Socket.ConnectAsync(endpoint).WaitAsync(TimeSpan.FromSeconds(60));
@@ -116,16 +110,13 @@ public async Task CanInvokeRpcMethodOnAuxiliaryBackchannel()
// When the Dashboard is not part of the app model, null should be returned
using var builder = TestDistributedApplicationBuilder.CreateWithTestContainerRegistry(outputHelper);
- // Register the auxiliary backchannel service
- builder.Services.AddSingleton();
- builder.Services.AddSingleton(sp => sp.GetRequiredService());
-
using var app = builder.Build();
await app.StartAsync().WaitAsync(TimeSpan.FromSeconds(60));
// Get the service
var service = app.Services.GetRequiredService();
+ await service.ListeningTask.WaitAsync(TimeSpan.FromSeconds(60));
Assert.NotNull(service.SocketPath);
// Connect a client
@@ -154,16 +145,13 @@ public async Task GetAppHostInformationAsyncReturnsAppHostPath()
// This test verifies that GetAppHostInformationAsync returns the AppHost path
using var builder = TestDistributedApplicationBuilder.CreateWithTestContainerRegistry(outputHelper);
- // Register the auxiliary backchannel service
- builder.Services.AddSingleton();
- builder.Services.AddSingleton(sp => sp.GetRequiredService());
-
using var app = builder.Build();
await app.StartAsync().WaitAsync(TimeSpan.FromSeconds(60));
// Get the service
var service = app.Services.GetRequiredService();
+ await service.ListeningTask.WaitAsync(TimeSpan.FromSeconds(60));
Assert.NotNull(service.SocketPath);
// Connect a client
@@ -198,16 +186,13 @@ public async Task MultipleClientsCanInvokeRpcMethodsConcurrently()
// When the Dashboard is not part of the app model, null should be returned
using var builder = TestDistributedApplicationBuilder.CreateWithTestContainerRegistry(outputHelper);
- // Register the auxiliary backchannel service
- builder.Services.AddSingleton();
- builder.Services.AddSingleton(sp => sp.GetRequiredService());
-
using var app = builder.Build();
await app.StartAsync().WaitAsync(TimeSpan.FromSeconds(60));
// Get the service
var service = app.Services.GetRequiredService();
+ await service.ListeningTask.WaitAsync(TimeSpan.FromSeconds(60));
Assert.NotNull(service.SocketPath);
// Create multiple clients and invoke RPC methods concurrently
@@ -245,16 +230,13 @@ public async Task GetAppHostInformationAsyncReturnsFilePathWithExtension()
// For .csproj-based AppHosts, it should include the .csproj extension
using var builder = TestDistributedApplicationBuilder.CreateWithTestContainerRegistry(outputHelper);
- // Register the auxiliary backchannel service
- builder.Services.AddSingleton();
- builder.Services.AddSingleton(sp => sp.GetRequiredService());
-
using var app = builder.Build();
await app.StartAsync().WaitAsync(TimeSpan.FromSeconds(60));
// Get the service
var service = app.Services.GetRequiredService();
+ await service.ListeningTask.WaitAsync(TimeSpan.FromSeconds(60));
Assert.NotNull(service.SocketPath);
// Connect a client
@@ -275,10 +257,10 @@ public async Task GetAppHostInformationAsyncReturnsFilePathWithExtension()
Assert.NotNull(appHostInfo);
Assert.NotNull(appHostInfo.AppHostPath);
Assert.NotEmpty(appHostInfo.AppHostPath);
-
+
// The path should be an absolute path
Assert.True(Path.IsPathRooted(appHostInfo.AppHostPath), $"Expected absolute path but got: {appHostInfo.AppHostPath}");
-
+
// In test scenarios where assembly metadata is not available, we may get a path without extension
// (falling back to AppHost:Path). In real scenarios with proper metadata, we should get .csproj or .cs
// So we just verify the path is non-empty and rooted
@@ -294,22 +276,19 @@ public async Task SocketPathUsesAuxiPrefix()
// to avoid Windows reserved device name issues (AUX is reserved on Windows < 11)
using var builder = TestDistributedApplicationBuilder.CreateWithTestContainerRegistry(outputHelper);
- // Register the auxiliary backchannel service
- builder.Services.AddSingleton();
- builder.Services.AddSingleton(sp => sp.GetRequiredService());
-
using var app = builder.Build();
await app.StartAsync().WaitAsync(TestConstants.DefaultTimeoutTimeSpan);
// Get the service
var service = app.Services.GetRequiredService();
+ await service.ListeningTask.WaitAsync(TimeSpan.FromSeconds(60));
Assert.NotNull(service.SocketPath);
// Verify that the socket path uses "auxi.sock." prefix
var fileName = Path.GetFileName(service.SocketPath);
Assert.StartsWith("auxi.sock.", fileName);
-
+
// Verify that the socket file can be created (not blocked by Windows reserved names)
Assert.True(File.Exists(service.SocketPath), $"Socket file should exist at: {service.SocketPath}");
@@ -328,16 +307,13 @@ public async Task CallResourceMcpToolAsyncThrowsWhenResourceNotFound()
// Add a simple container resource (without MCP)
builder.AddContainer("mycontainer", "nginx");
- // Register the auxiliary backchannel service
- builder.Services.AddSingleton();
- builder.Services.AddSingleton(sp => sp.GetRequiredService());
-
using var app = builder.Build();
await app.StartAsync().WaitAsync(TestConstants.DefaultTimeoutTimeSpan);
// Get the service
var service = app.Services.GetRequiredService();
+ await service.ListeningTask.WaitAsync(TimeSpan.FromSeconds(60));
Assert.NotNull(service.SocketPath);
// Connect a client
@@ -372,16 +348,13 @@ public async Task CallResourceMcpToolAsyncThrowsWhenResourceHasNoMcpAnnotation()
// Add a simple container resource (without MCP)
builder.AddContainer("mycontainer", "nginx");
- // Register the auxiliary backchannel service
- builder.Services.AddSingleton();
- builder.Services.AddSingleton(sp => sp.GetRequiredService());
-
using var app = builder.Build();
await app.StartAsync().WaitAsync(TestConstants.DefaultTimeoutTimeSpan);
// Get the service
var service = app.Services.GetRequiredService();
+ await service.ListeningTask.WaitAsync(TimeSpan.FromSeconds(60));
Assert.NotNull(service.SocketPath);
// Connect a client
@@ -412,16 +385,13 @@ public async Task StopAppHostAsyncInitiatesShutdown()
// This test verifies that StopAppHostAsync initiates AppHost shutdown
using var builder = TestDistributedApplicationBuilder.CreateWithTestContainerRegistry(outputHelper);
- // Register the auxiliary backchannel service
- builder.Services.AddSingleton();
- builder.Services.AddSingleton(sp => sp.GetRequiredService());
-
using var app = builder.Build();
await app.StartAsync().WaitAsync(TestConstants.DefaultTimeoutTimeSpan);
// Get the service
var service = app.Services.GetRequiredService();
+ await service.ListeningTask.WaitAsync(TimeSpan.FromSeconds(60));
Assert.NotNull(service.SocketPath);
// Connect a client
@@ -464,16 +434,13 @@ public async Task GetCapabilitiesAsyncReturnsV1AndV2()
// This test verifies that GetCapabilitiesAsync returns both v1 and v2 capabilities
using var builder = TestDistributedApplicationBuilder.CreateWithTestContainerRegistry(outputHelper);
- // Register the auxiliary backchannel service
- builder.Services.AddSingleton();
- builder.Services.AddSingleton(sp => sp.GetRequiredService());
-
using var app = builder.Build();
await app.StartAsync().WaitAsync(TestConstants.DefaultTimeoutTimeSpan);
// Get the service
var service = app.Services.GetRequiredService();
+ await service.ListeningTask.WaitAsync(TimeSpan.FromSeconds(60));
Assert.NotNull(service.SocketPath);
// Connect a client
@@ -505,16 +472,13 @@ public async Task GetAppHostInfoAsyncV2ReturnsAppHostInfo()
// This test verifies that the v2 GetAppHostInfoAsync returns AppHost info
using var builder = TestDistributedApplicationBuilder.CreateWithTestContainerRegistry(outputHelper);
- // Register the auxiliary backchannel service
- builder.Services.AddSingleton();
- builder.Services.AddSingleton(sp => sp.GetRequiredService());
-
using var app = builder.Build();
await app.StartAsync().WaitAsync(TestConstants.DefaultTimeoutTimeSpan);
// Get the service
var service = app.Services.GetRequiredService();
+ await service.ListeningTask.WaitAsync(TimeSpan.FromSeconds(60));
Assert.NotNull(service.SocketPath);
// Connect a client
@@ -551,16 +515,13 @@ public async Task GetResourcesAsyncV2ReturnsResources()
// Add a simple parameter resource
builder.AddParameter("myparam");
- // Register the auxiliary backchannel service
- builder.Services.AddSingleton();
- builder.Services.AddSingleton(sp => sp.GetRequiredService());
-
using var app = builder.Build();
await app.StartAsync().WaitAsync(TestConstants.DefaultTimeoutTimeSpan);
// Get the service
var service = app.Services.GetRequiredService();
+ await service.ListeningTask.WaitAsync(TimeSpan.FromSeconds(60));
Assert.NotNull(service.SocketPath);
// Connect a client
diff --git a/tests/Aspire.OpenAI.Tests/Aspire.OpenAI.Tests.csproj b/tests/Aspire.OpenAI.Tests/Aspire.OpenAI.Tests.csproj
index 62892924e61..dec5f627481 100644
--- a/tests/Aspire.OpenAI.Tests/Aspire.OpenAI.Tests.csproj
+++ b/tests/Aspire.OpenAI.Tests/Aspire.OpenAI.Tests.csproj
@@ -2,9 +2,6 @@
$(AllTargetFrameworks)
-
-
- true