| File: Dcp\ConfigureDefaultDcpOptionsTests.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 Aspire.Hosting.Dcp; using Microsoft.Extensions.Configuration; namespace Aspire.Hosting.Tests.Dcp; public class ConfigureDefaultDcpOptionsTests { [Fact] public void TerminalHostFallsBackToAspireManagedDashboardPath() { // The CLI bundle launcher (PrebuiltAppHostServer/DotNetAppHostProject) sets // ASPIRE_DASHBOARD_PATH to point at the multi-mode aspire-managed exe but doesn't // set ASPIRE_TERMINAL_HOST_PATH. This regression test pins the fallback that // reuses the aspire-managed binary as the terminal host with "terminalhost" as // its dispatcher arg so .WithTerminal() works in TS-based and other prebuilt // AppHost scenarios. var managedExe = OperatingSystem.IsWindows() ? "aspire-managed.exe" : "aspire-managed"; var managedPath = Path.Combine(Path.GetTempPath(), "aspire-fake-bundle", "managed", managedExe); var options = ConfigureWithDcpPublisher(new() { ["DcpPublisher:DashboardPath"] = managedPath, }); Assert.Equal(managedPath, options.DashboardPath); Assert.Equal(managedPath, options.TerminalHostPath); Assert.Equal("terminalhost", options.TerminalHostInvocationArgs); } [Fact] public void TerminalHostFallbackDoesNotApplyWhenDashboardPathIsNotAspireManaged() { // Standalone NuGet-package scenario: DashboardPath points at a per-RID dashboard // binary. The fallback must not hijack TerminalHostPath in that case (the standalone // terminal host nupkg supplies its own aspireterminalhostpath assembly metadata). var dashboardDll = Path.Combine(Path.GetTempPath(), "fake-dashboard", "Aspire.Dashboard.dll"); var options = ConfigureWithDcpPublisher(new() { ["DcpPublisher:DashboardPath"] = dashboardDll, // Neutralize any ambient aspireterminalhostpath assembly metadata in the test // assembly so we can observe the fallback's null behaviour cleanly. ["DcpPublisher:TerminalHostPath"] = " ", }); Assert.Equal(dashboardDll, options.DashboardPath); // Either the explicit whitespace value falls through to assembly metadata (dev-only, // varies by environment) or stays empty. The point is the fallback didn't fire — i.e. // TerminalHostPath !== DashboardPath and InvocationArgs wasn't auto-set to "terminalhost". Assert.NotEqual(dashboardDll, options.TerminalHostPath); Assert.NotEqual("terminalhost", options.TerminalHostInvocationArgs); } [Fact] public void TerminalHostFallbackDoesNotOverrideExplicitTerminalHostPath() { // If both paths are explicitly set (e.g. an integrator points at a separate // terminal host binary while still using bundled aspire-managed for the dashboard), // honour the explicit TerminalHostPath and don't auto-rewrite it. var managedExe = OperatingSystem.IsWindows() ? "aspire-managed.exe" : "aspire-managed"; var managedPath = Path.Combine(Path.GetTempPath(), "aspire-fake-bundle", "managed", managedExe); var explicitTerminalHostPath = Path.Combine(Path.GetTempPath(), "custom-terminalhost", "Aspire.TerminalHost.exe"); var options = ConfigureWithDcpPublisher(new() { ["DcpPublisher:DashboardPath"] = managedPath, ["DcpPublisher:TerminalHostPath"] = explicitTerminalHostPath, }); Assert.Equal(managedPath, options.DashboardPath); Assert.Equal(explicitTerminalHostPath, options.TerminalHostPath); // InvocationArgs left untouched (the explicit path may or may not be aspire-managed; // the consumer is responsible for supplying its own dispatcher arg if needed). Assert.NotEqual("terminalhost", options.TerminalHostInvocationArgs); } [Fact] public void TerminalHostFallbackHonoursExplicitInvocationArgs() { // If the consumer explicitly sets the InvocationArgs, the bundle fallback must NOT // clobber it — the explicit value wins even when we synthesise the path from the // dashboard. var managedExe = OperatingSystem.IsWindows() ? "aspire-managed.exe" : "aspire-managed"; var managedPath = Path.Combine(Path.GetTempPath(), "aspire-fake-bundle", "managed", managedExe); var options = ConfigureWithDcpPublisher(new() { ["DcpPublisher:DashboardPath"] = managedPath, ["DcpPublisher:TerminalHostInvocationArgs"] = "custom-subcommand", }); Assert.Equal(managedPath, options.TerminalHostPath); Assert.Equal("custom-subcommand", options.TerminalHostInvocationArgs); } private static DcpOptions ConfigureWithDcpPublisher(Dictionary<string, string?> dcpPublisherSettings) { var configuration = new ConfigurationBuilder() .AddInMemoryCollection(dcpPublisherSettings) .Build(); // Point AssemblyName at Aspire.Hosting itself so the resolver picks up an assembly // that has no aspire{dashboard,terminalhost}path metadata — without this, the test // runner's own assembly metadata (added by the build for inner-loop dev) leaks in // and short-circuits the explicit configuration. var appOptions = new DistributedApplicationOptions { AssemblyName = typeof(DcpOptions).Assembly.GetName().Name, }; var configurer = new ConfigureDefaultDcpOptions(appOptions, configuration); var options = new DcpOptions(); configurer.Configure(options); return options; } }