// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Aspire.TestUtilities;
using Xunit;
namespace Aspire.Acquisition.Tests.Scripts;
/// <summary>
/// Unit tests for individual functions in the PowerShell PR script (get-aspire-cli-pr.ps1).
/// Tests RID computation and version suffix extraction.
/// </summary>
[RequiresTools(["pwsh"])]
public class PRScriptPSFunctionTests(ITestOutputHelper testOutput)
{
private static readonly string s_prScript = ScriptPaths.PRPowerShell;
private readonly ITestOutputHelper _testOutput = testOutput;
#region Get-RuntimeIdentifier
[Theory]
[InlineData("linux", "x64", "linux-x64")]
[InlineData("linux", "arm64", "linux-arm64")]
[InlineData("osx", "arm64", "osx-arm64")]
[InlineData("osx", "x64", "osx-x64")]
[InlineData("win", "x64", "win-x64")]
[InlineData("win", "arm64", "win-arm64")]
public async Task GetRuntimeIdentifier_ExplicitOsAndArch_ReturnsExpectedRid(
string os, string arch, string expectedRid)
{
using var env = new TestEnvironment();
using var cmd = new ScriptFunctionCommand(
s_prScript,
$"Get-RuntimeIdentifier -_OS '{os}' -_Architecture '{arch}'",
env,
_testOutput);
var result = await cmd.ExecuteAsync();
result.EnsureSuccessful();
Assert.Equal(expectedRid, result.Output.Trim());
}
[Fact]
public async Task GetRuntimeIdentifier_UnsupportedArch_Fails()
{
using var env = new TestEnvironment();
using var cmd = new ScriptFunctionCommand(
s_prScript,
"Get-RuntimeIdentifier -_OS 'linux' -_Architecture 'mips'",
env,
_testOutput);
var result = await cmd.ExecuteAsync();
Assert.NotEqual(0, result.ExitCode);
Assert.Contains("not supported", result.Output, StringComparison.OrdinalIgnoreCase);
}
[Theory]
[InlineData("amd64", "x64")]
[InlineData("x64", "x64")]
[InlineData("arm64", "arm64")]
public async Task GetCliArchitectureFromArchitecture_NormalizesArchNames(string input, string expected)
{
using var env = new TestEnvironment();
using var cmd = new ScriptFunctionCommand(
s_prScript,
$"Get-CLIArchitectureFromArchitecture -Architecture '{input}'",
env,
_testOutput);
var result = await cmd.ExecuteAsync();
result.EnsureSuccessful();
Assert.Equal(expected, result.Output.Trim());
}
[Fact]
public async Task UpdatePathEnvironment_WhenCurrentPathAlreadyContainsDirectory_StillUpdatesGitHubPath()
{
using var env = new TestEnvironment();
var cliBinDir = Path.Combine(env.TempDirectory, "cli-bin");
var existingPath = $"{cliBinDir}{Path.PathSeparator}{Path.Combine(env.TempDirectory, "other-bin")}";
var githubPath = Path.Combine(env.TempDirectory, "github-path.txt");
using var cmd = new ScriptFunctionCommand(
s_prScript,
$"""
$Script:HostOS = 'linux'
$env:PATH = '{existingPath}'
$env:GITHUB_ACTIONS = 'true'
$env:GITHUB_PATH = '{githubPath}'
Update-PathEnvironment -CliBinDir '{cliBinDir}'
""",
env,
_testOutput);
var result = await cmd.ExecuteAsync();
result.EnsureSuccessful();
Assert.Contains("already exists in PATH", result.Output);
Assert.Contains(cliBinDir, await File.ReadAllLinesAsync(githubPath));
}
#endregion
#region Get-VersionSuffixFromPackages
[Fact]
public async Task GetVersionSuffixFromPackages_ValidNupkg_ReturnsVersionSuffix()
{
using var env = new TestEnvironment();
var pkgDir = Path.Combine(env.TempDirectory, "packages");
await FakeArchiveHelper.CreateFakeNupkgAsync(
pkgDir,
"Aspire.Hosting",
"13.2.0-pr.12345.a1b2c3d4");
using var cmd = new ScriptFunctionCommand(
s_prScript,
$"Get-VersionSuffixFromPackages -DownloadDir '{pkgDir}'",
env,
_testOutput);
var result = await cmd.ExecuteAsync();
result.EnsureSuccessful();
Assert.Equal("pr.12345.a1b2c3d4", result.Output.Trim());
}
[Fact]
public async Task GetVersionSuffixFromPackages_NoNupkgFiles_Fails()
{
using var env = new TestEnvironment();
var emptyDir = Path.Combine(env.TempDirectory, "empty-packages");
Directory.CreateDirectory(emptyDir);
using var cmd = new ScriptFunctionCommand(
s_prScript,
$"Get-VersionSuffixFromPackages -DownloadDir '{emptyDir}'",
env,
_testOutput);
var result = await cmd.ExecuteAsync();
Assert.NotEqual(0, result.ExitCode);
}
[Fact]
public async Task GetVersionSuffixFromPackages_NupkgWithoutPrSuffix_Fails()
{
using var env = new TestEnvironment();
var pkgDir = Path.Combine(env.TempDirectory, "packages");
await FakeArchiveHelper.CreateFakeNupkgAsync(
pkgDir,
"Aspire.Hosting",
"13.2.0-release");
using var cmd = new ScriptFunctionCommand(
s_prScript,
$"Get-VersionSuffixFromPackages -DownloadDir '{pkgDir}'",
env,
_testOutput);
var result = await cmd.ExecuteAsync();
Assert.NotEqual(0, result.ExitCode);
}
[Fact]
public async Task GetVersionSuffixFromPackages_MultipleNupkgs_UsesFirst()
{
using var env = new TestEnvironment();
var pkgDir = Path.Combine(env.TempDirectory, "packages");
await FakeArchiveHelper.CreateFakeNupkgAsync(
pkgDir,
"Aspire.Hosting",
"13.2.0-pr.99999.deadbeef");
await FakeArchiveHelper.CreateFakeNupkgAsync(
pkgDir,
"Aspire.Dashboard",
"13.2.0-pr.99999.deadbeef");
using var cmd = new ScriptFunctionCommand(
s_prScript,
$"Get-VersionSuffixFromPackages -DownloadDir '{pkgDir}'",
env,
_testOutput);
var result = await cmd.ExecuteAsync();
result.EnsureSuccessful();
Assert.Equal("pr.99999.deadbeef", result.Output.Trim());
}
#endregion
#region Get-OperatingSystem
[Fact]
public async Task GetOperatingSystem_ReturnsKnownPlatform()
{
using var env = new TestEnvironment();
using var cmd = new ScriptFunctionCommand(
s_prScript,
"Get-OperatingSystem",
env,
_testOutput);
var result = await cmd.ExecuteAsync();
result.EnsureSuccessful();
var os = result.Output.Trim();
Assert.True(
os is "osx" or "linux" or "linux-musl" or "win",
$"Expected a recognized OS, got: '{os}'");
}
#endregion
#region Get-MachineArchitecture
[Fact]
public async Task GetMachineArchitecture_ReturnsKnownArch()
{
using var env = new TestEnvironment();
using var cmd = new ScriptFunctionCommand(
s_prScript,
"Get-MachineArchitecture",
env,
_testOutput);
var result = await cmd.ExecuteAsync();
result.EnsureSuccessful();
var arch = result.Output.Trim();
// Get-MachineArchitecture returns raw OS values:
// Windows: AMD64/amd64/arm64, Unix (modern PS): x64/arm64
Assert.True(
arch.Equals("x64", StringComparison.OrdinalIgnoreCase)
|| arch.Equals("amd64", StringComparison.OrdinalIgnoreCase)
|| arch.Equals("arm64", StringComparison.OrdinalIgnoreCase),
$"Expected x64, amd64, or arm64, got: '{arch}'");
}
#endregion
#region Remove-TempDirectory
[Fact]
public async Task RemoveTempDirectory_KeepArchive_RetainsDirectory()
{
using var env = new TestEnvironment();
var tempDir = Path.Combine(env.TempDirectory, "download-temp");
Directory.CreateDirectory(tempDir);
File.WriteAllText(Path.Combine(tempDir, "artifact.tar.gz"), "fake");
using var cmd = new ScriptFunctionCommand(
s_prScript,
$"$KeepArchive = $true; Remove-TempDirectory -TempDir '{tempDir}'",
env,
_testOutput);
var result = await cmd.ExecuteAsync();
result.EnsureSuccessful();
Assert.True(Directory.Exists(tempDir), "Directory should be retained when KeepArchive is true");
}
[Fact]
public async Task RemoveTempDirectory_NoKeepArchive_DeletesDirectory()
{
using var env = new TestEnvironment();
var tempDir = Path.Combine(env.TempDirectory, "download-temp");
Directory.CreateDirectory(tempDir);
File.WriteAllText(Path.Combine(tempDir, "artifact.tar.gz"), "fake");
using var cmd = new ScriptFunctionCommand(
s_prScript,
$"$KeepArchive = $false; Remove-TempDirectory -TempDir '{tempDir}'",
env,
_testOutput);
var result = await cmd.ExecuteAsync();
result.EnsureSuccessful();
Assert.False(Directory.Exists(tempDir), "Directory should be deleted when KeepArchive is false");
}
[Fact]
public async Task RemoveTempDirectory_WhatIf_RetainsDirectory()
{
using var env = new TestEnvironment();
var tempDir = Path.Combine(env.TempDirectory, "download-temp");
Directory.CreateDirectory(tempDir);
File.WriteAllText(Path.Combine(tempDir, "artifact.tar.gz"), "fake");
using var cmd = new ScriptFunctionCommand(
s_prScript,
@"$KeepArchive = $false; $WhatIfPreference = $true; Remove-TempDirectory -TempDir '" + tempDir + "'",
env,
_testOutput);
var result = await cmd.ExecuteAsync();
result.EnsureSuccessful();
Assert.True(Directory.Exists(tempDir), "Directory should be retained during WhatIf");
}
#endregion
#region Install-AspireCliFromDownload archive selection
[Fact]
public async Task InstallAspireCliFromDownload_ZeroArchives_Fails()
{
using var env = new TestEnvironment();
var downloadDir = Path.Combine(env.TempDirectory, "cli-download");
Directory.CreateDirectory(downloadDir);
await File.WriteAllTextAsync(Path.Combine(downloadDir, "README.txt"), "no archive here");
var binDir = Path.Combine(env.TempDirectory, "bin");
using var cmd = new ScriptFunctionCommand(
s_prScript,
$"Install-AspireCliFromDownload -DownloadDir '{downloadDir}' -CliBinDir '{binDir}'",
env,
_testOutput);
var result = await cmd.ExecuteAsync();
Assert.NotEqual(0, result.ExitCode);
Assert.Contains("No CLI archive found", result.Output);
}
[Fact]
public async Task InstallAspireCliFromDownload_MultipleArchives_Fails()
{
using var env = new TestEnvironment();
var downloadDir = Path.Combine(env.TempDirectory, "cli-download");
Directory.CreateDirectory(downloadDir);
await File.WriteAllTextAsync(Path.Combine(downloadDir, "aspire-cli-linux-x64.tar.gz"), "fake1");
await File.WriteAllTextAsync(Path.Combine(downloadDir, "aspire-cli-osx-arm64.tar.gz"), "fake2");
var binDir = Path.Combine(env.TempDirectory, "bin");
using var cmd = new ScriptFunctionCommand(
s_prScript,
$"Install-AspireCliFromDownload -DownloadDir '{downloadDir}' -CliBinDir '{binDir}'",
env,
_testOutput);
var result = await cmd.ExecuteAsync();
Assert.NotEqual(0, result.ExitCode);
Assert.Contains("Multiple CLI archives found", result.Output);
}
#endregion
}