File: Deployment\RadCliDetectionTests.cs
Web Access
Project: src\tests\Aspire.Hosting.Radius.Tests\Aspire.Hosting.Radius.Tests.csproj (Aspire.Hosting.Radius.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.Radius.Publishing;
using Aspire.TestUtilities;
 
namespace Aspire.Hosting.Radius.Tests.Deployment;
 
public class RadCliDetectionTests
{
    [Fact]
    [OuterloopTest("Spawns the real `rad` CLI process to verify detection")]
    public async Task DetectRadCliAsync_ReturnsBoolean()
    {
        // DetectRadCliAsync should not throw regardless of whether rad is installed.
        var result = await RadiusDeploymentPipelineStep.DetectRadCliAsync();
        Assert.IsType<bool>(result);
    }
 
    [Fact]
    [OuterloopTest("Spawns the real `rad` CLI process to verify detection")]
    public async Task DetectRadCliAsync_ReturnsTrueOrFalse_BasedOnEnvironment()
    {
        // Calling detection twice yields the same result — there is no caching, the result
        // is determined by the presence of `rad` on PATH for the current process.
        var result1 = await RadiusDeploymentPipelineStep.DetectRadCliAsync();
        var result2 = await RadiusDeploymentPipelineStep.DetectRadCliAsync();
 
        Assert.Equal(result1, result2);
    }
 
    [Fact]
    public async Task DetectRadCliAsync_SupportsCancellation()
    {
        using var cts = new CancellationTokenSource();
        await cts.CancelAsync();
 
        // Pre-cancelled tokens should propagate as OperationCanceledException — they must not
        // be swallowed by the catch-all in DetectRadCliAsync. Honouring cancellation is what
        // lets CTRL-C abort a pending `rad version` probe.
        await Assert.ThrowsAsync<OperationCanceledException>(
            () => RadiusDeploymentPipelineStep.DetectRadCliAsync(cts.Token));
    }
 
    [Fact]
    public void RadCliNotFoundException_ContainsInstallLinkAndRemediation()
    {
        // Exercises the real production factory both throw sites use (deploy step and
        // credential-register step). Asserting on the actual message means a refactor that
        // drops the install link or PATH remediation fails this test — unlike asserting on a
        // hand-written literal, which cannot observe production changes.
        var ex = RadiusDeploymentPipelineStep.CreateRadCliNotFoundException();
 
        Assert.Contains(RadiusDeploymentPipelineStep.RadInstallUrl, ex.Message, StringComparison.Ordinal);
        Assert.Contains("rad", ex.Message, StringComparison.Ordinal);
        Assert.Contains("install", ex.Message, StringComparison.OrdinalIgnoreCase);
        Assert.Contains("PATH", ex.Message, StringComparison.Ordinal);
    }
 
    [Fact]
    public void DeployStep_HasCorrectName()
    {
        var environment = new RadiusEnvironmentResource("myenv");
        var step = new RadiusDeploymentPipelineStep(environment);
 
        var pipelineStep = step.CreatePipelineStep();
 
        Assert.Equal("deploy-radius-myenv", pipelineStep.Name);
    }
 
    [Fact]
    public void DeployStep_DependsOnPublishStep()
    {
        var environment = new RadiusEnvironmentResource("myenv");
        var step = new RadiusDeploymentPipelineStep(environment);
 
        var pipelineStep = step.CreatePipelineStep();
 
        // Step should depend on publish, not push — Radius supports kind clusters without
        // a container registry, so the deploy step intentionally skips the push prerequisite.
        Assert.Contains("publish-radius-myenv", pipelineStep.DependsOnSteps);
        Assert.DoesNotContain("push", pipelineStep.DependsOnSteps, StringComparer.OrdinalIgnoreCase);
    }
 
    [Fact]
    public void DeployStep_RequiredByDeployWellKnownStep()
    {
        var environment = new RadiusEnvironmentResource("testenv");
        var step = new RadiusDeploymentPipelineStep(environment);
 
        var pipelineStep = step.CreatePipelineStep();
 
        Assert.Contains("deploy", pipelineStep.RequiredBySteps);
    }
}