File: Scripts\LocalHiveScriptFunctionTests.cs
Web Access
Project: src\tests\Aspire.Acquisition.Tests\Aspire.Acquisition.Tests.csproj (Aspire.Acquisition.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using Xunit;
 
namespace Aspire.Acquisition.Tests.Scripts;
 
/// <summary>
/// Source-level contract tests for localhive installer scripts.
/// </summary>
public class LocalHiveScriptFunctionTests
{
    [Theory]
    [InlineData(nameof(ScriptPaths.LocalHiveShell))]
    [InlineData(nameof(ScriptPaths.LocalHivePowerShell))]
    public void Source_DoesNotReferencePersistentShellProfileFiles(string scriptPathName)
    {
        var source = File.ReadAllText(GetRepoPath(GetScriptPath(scriptPathName)));
 
        Assert.DoesNotContain(".bashrc", source, StringComparison.Ordinal);
        Assert.DoesNotContain(".zshrc", source, StringComparison.Ordinal);
        Assert.DoesNotContain(".profile", source, StringComparison.Ordinal);
        Assert.DoesNotContain(".bash_profile", source, StringComparison.Ordinal);
    }
 
    [Theory]
    [InlineData(nameof(ScriptPaths.LocalHivePowerShell))]
    public void PowerShellSource_DoesNotWriteUserPathEnvironment(string scriptPathName)
    {
        var source = File.ReadAllText(GetRepoPath(GetScriptPath(scriptPathName)));
        var userEnvironmentWrite = new System.Text.RegularExpressions.Regex(
            @"\[Environment\]::SetEnvironmentVariable\([^)]*(['""`]User['""`]|EnvironmentVariableTarget\]::User)",
            System.Text.RegularExpressions.RegexOptions.IgnoreCase | System.Text.RegularExpressions.RegexOptions.Singleline);
 
        Assert.False(
            userEnvironmentWrite.IsMatch(source),
            $"{GetScriptPath(scriptPathName)} must not call [Environment]::SetEnvironmentVariable(..., 'User') or EnvironmentVariableTarget.User.");
    }
 
    [Fact]
    public void ShellSource_PrintsDirectBinaryPathActivationHint()
    {
        var source = File.ReadAllText(GetRepoPath(ScriptPaths.LocalHiveShell));
 
        Assert.Contains("Run Aspire directly with: $CLI_BIN_DIR/$CLI_EXE_NAME", source, StringComparison.Ordinal);
        Assert.Contains("For this shell only, run: export PATH=", source, StringComparison.Ordinal);
        Assert.DoesNotContain("Add this to your shell profile", source, StringComparison.OrdinalIgnoreCase);
        Assert.DoesNotContain("Add the following to ~/.bashrc", source, StringComparison.OrdinalIgnoreCase);
        Assert.DoesNotContain("Add to your shell profile", source, StringComparison.OrdinalIgnoreCase);
    }
 
    [Fact]
    public void PowerShellSource_PrintsDirectBinaryPathActivationHint()
    {
        var source = File.ReadAllText(GetRepoPath(ScriptPaths.LocalHivePowerShell));
 
        Assert.Contains("Run Aspire directly with: $installedCliPath", source, StringComparison.Ordinal);
        Assert.Contains("to PATH for this PowerShell session", source, StringComparison.Ordinal);
        Assert.DoesNotContain("Add this to your shell profile", source, StringComparison.OrdinalIgnoreCase);
        Assert.DoesNotContain("Add the following to ~/.bashrc", source, StringComparison.OrdinalIgnoreCase);
        Assert.DoesNotContain("Add to your shell profile", source, StringComparison.OrdinalIgnoreCase);
    }
 
    [Theory]
    [InlineData(nameof(ScriptPaths.LocalHiveShell))]
    [InlineData(nameof(ScriptPaths.LocalHivePowerShell))]
    public void Source_DoesNotWriteGlobalChannel(string scriptPathName)
    {
        var source = File.ReadAllText(GetRepoPath(GetScriptPath(scriptPathName)));
 
        Assert.DoesNotContain("config set channel", source, StringComparison.OrdinalIgnoreCase);
        Assert.DoesNotContain("aspire config set", source, StringComparison.OrdinalIgnoreCase);
    }
 
    [Fact]
    public void PowerShellSource_WindowsArchivePathDoesNotUseCompressArchive()
    {
        // Compress-Archive enumerates inputs via the PowerShell provider, which on
        // non-Windows hosts hides dotfiles from `<dir>/*` wildcard expansion. The
        // portable layout includes bin/.aspire-install.json — the localhive route
        // sidecar — and silently dropping it from a win-* zip built on Linux/macOS
        // would produce sidecar-less installs on the target. Use
        // [System.IO.Compression.ZipFile]::CreateFromDirectory instead.
        var source = File.ReadAllText(GetRepoPath(ScriptPaths.LocalHivePowerShell));
 
        // Strip PowerShell line comments so the contract check matches actual
        // cmdlet invocations, not mentions of the cmdlet in explanatory comments.
        var sourceWithoutComments = string.Join('\n', source.Split('\n').Select(line =>
        {
            var hashIdx = line.IndexOf('#');
            return hashIdx >= 0 ? line[..hashIdx] : line;
        }));
 
        Assert.DoesNotContain("Compress-Archive", sourceWithoutComments, StringComparison.Ordinal);
        Assert.Contains("[System.IO.Compression.ZipFile]::CreateFromDirectory", source, StringComparison.Ordinal);
    }
 
    private static string GetScriptPath(string scriptPathName) => scriptPathName switch
    {
        nameof(ScriptPaths.LocalHiveShell) => ScriptPaths.LocalHiveShell,
        nameof(ScriptPaths.LocalHivePowerShell) => ScriptPaths.LocalHivePowerShell,
        _ => throw new ArgumentOutOfRangeException(nameof(scriptPathName), scriptPathName, null)
    };
 
    private static string GetRepoPath(string relativePath)
    {
        var repoRoot = Aspire.Templates.Tests.TestUtils.FindRepoRoot()?.FullName
            ?? throw new InvalidOperationException("Could not find repository root");
 
        return Path.Combine(repoRoot, relativePath);
    }
}