File: Utils\SdkInstallHelperTests.cs
Web Access
Project: src\tests\Aspire.Cli.Tests\Aspire.Cli.Tests.csproj (Aspire.Cli.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.Cli.Configuration;
using Aspire.Cli.Telemetry;
using Aspire.Cli.Tests.Telemetry;
using Aspire.Cli.Tests.TestServices;
using Aspire.Cli.Utils;
 
namespace Aspire.Cli.Tests.Utils;
 
public class SdkInstallHelperTests
{
    private sealed class TestCliHostEnvironment(bool supportsInteractiveInput) : ICliHostEnvironment
    {
        public bool SupportsInteractiveInput { get; } = supportsInteractiveInput;
        public bool SupportsInteractiveOutput => true;
        public bool SupportsAnsi => true;
    }
 
    private sealed class TestFeatures : IFeatures
    {
        private readonly Dictionary<string, bool> _features = new();
 
        public TestFeatures SetFeature(string featureName, bool value)
        {
            _features[featureName] = value;
            return this;
        }
 
        public bool IsFeatureEnabled(string featureName, bool defaultValue = false)
        {
            return _features.TryGetValue(featureName, out var value) ? value : defaultValue;
        }
    }
 
    [Fact]
    public async Task EnsureSdkInstalledAsync_WhenSdkAlreadyInstalled_RecordsAlreadyInstalledTelemetry()
    {
        using var fixture = new TelemetryFixture();
 
        var sdkInstaller = new TestDotNetSdkInstaller
        {
            CheckAsyncCallback = _ => (true, "9.0.302", "9.0.302", false)
        };
 
        var interactionService = new TestConsoleInteractionService();
        var features = new TestFeatures();
 
        var result = await SdkInstallHelper.EnsureSdkInstalledAsync(
            sdkInstaller,
            interactionService,
            features,
            fixture.Telemetry);
 
        Assert.True(result);
        Assert.NotNull(fixture.CapturedActivity);
 
        var tags = fixture.CapturedActivity.Tags.ToDictionary(t => t.Key, t => t.Value);
        Assert.Equal("9.0.302", tags[TelemetryConstants.Tags.SdkDetectedVersion]);
        Assert.Equal("9.0.302", tags[TelemetryConstants.Tags.SdkMinimumRequiredVersion]);
        Assert.Equal("already_installed", tags[TelemetryConstants.Tags.SdkCheckResult]);
        Assert.Equal("already_installed", tags[TelemetryConstants.Tags.SdkInstallResult]);
    }
 
    [Fact]
    public async Task EnsureSdkInstalledAsync_WhenSdkMissingAndFeatureDisabled_RecordsFeatureNotEnabledTelemetry()
    {
        using var fixture = new TelemetryFixture();
 
        var sdkInstaller = new TestDotNetSdkInstaller
        {
            CheckAsyncCallback = _ => (false, null, "9.0.302", false)
        };
 
        var interactionService = new TestConsoleInteractionService();
        var features = new TestFeatures()
            .SetFeature(KnownFeatures.DotNetSdkInstallationEnabled, false);
 
        var result = await SdkInstallHelper.EnsureSdkInstalledAsync(
            sdkInstaller,
            interactionService,
            features,
            fixture.Telemetry);
 
        Assert.False(result);
        Assert.NotNull(fixture.CapturedActivity);
 
        var tags = fixture.CapturedActivity.Tags.ToDictionary(t => t.Key, t => t.Value);
        Assert.Equal("(not found)", tags[TelemetryConstants.Tags.SdkDetectedVersion]);
        Assert.Equal("9.0.302", tags[TelemetryConstants.Tags.SdkMinimumRequiredVersion]);
        Assert.Equal("not_installed", tags[TelemetryConstants.Tags.SdkCheckResult]);
        Assert.Equal("feature_not_enabled", tags[TelemetryConstants.Tags.SdkInstallResult]);
    }
 
    [Fact]
    public async Task EnsureSdkInstalledAsync_WhenSdkMissingAndNotInteractive_RecordsNotInteractiveTelemetry()
    {
        using var fixture = new TelemetryFixture();
 
        var sdkInstaller = new TestDotNetSdkInstaller
        {
            CheckAsyncCallback = _ => (false, "8.0.100", "9.0.302", false)
        };
 
        var interactionService = new TestConsoleInteractionService();
        var features = new TestFeatures()
            .SetFeature(KnownFeatures.DotNetSdkInstallationEnabled, true);
        var hostEnvironment = new TestCliHostEnvironment(supportsInteractiveInput: false);
 
        var result = await SdkInstallHelper.EnsureSdkInstalledAsync(
            sdkInstaller,
            interactionService,
            features,
            fixture.Telemetry,
            hostEnvironment);
 
        Assert.False(result);
        Assert.NotNull(fixture.CapturedActivity);
 
        var tags = fixture.CapturedActivity.Tags.ToDictionary(t => t.Key, t => t.Value);
        Assert.Equal("8.0.100", tags[TelemetryConstants.Tags.SdkDetectedVersion]);
        Assert.Equal("9.0.302", tags[TelemetryConstants.Tags.SdkMinimumRequiredVersion]);
        Assert.Equal("not_installed", tags[TelemetryConstants.Tags.SdkCheckResult]);
        Assert.Equal("not_interactive", tags[TelemetryConstants.Tags.SdkInstallResult]);
    }
 
    [Fact]
    public async Task EnsureSdkInstalledAsync_WhenUserDeclinesInstallation_RecordsUserDeclinedTelemetry()
    {
        using var fixture = new TelemetryFixture();
 
        var sdkInstaller = new TestDotNetSdkInstaller
        {
            CheckAsyncCallback = _ => (false, "8.0.100", "9.0.302", false)
        };
 
        var interactionService = new TestConsoleInteractionService
        {
            ConfirmCallback = (_, _) => false // User declines
        };
        var features = new TestFeatures()
            .SetFeature(KnownFeatures.DotNetSdkInstallationEnabled, true);
        var hostEnvironment = new TestCliHostEnvironment(supportsInteractiveInput: true);
 
        var result = await SdkInstallHelper.EnsureSdkInstalledAsync(
            sdkInstaller,
            interactionService,
            features,
            fixture.Telemetry,
            hostEnvironment);
 
        Assert.False(result);
        Assert.NotNull(fixture.CapturedActivity);
 
        var tags = fixture.CapturedActivity.Tags.ToDictionary(t => t.Key, t => t.Value);
        Assert.Equal("not_installed", tags[TelemetryConstants.Tags.SdkCheckResult]);
        Assert.Equal("user_declined", tags[TelemetryConstants.Tags.SdkInstallResult]);
    }
 
    [Fact]
    public async Task EnsureSdkInstalledAsync_WhenInstallationSucceeds_RecordsInstalledTelemetry()
    {
        using var fixture = new TelemetryFixture();
 
        var sdkInstaller = new TestDotNetSdkInstaller
        {
            CheckAsyncCallback = _ => (false, "8.0.100", "9.0.302", false),
            InstallAsyncCallback = _ => Task.CompletedTask
        };
 
        var interactionService = new TestConsoleInteractionService
        {
            ConfirmCallback = (_, _) => true // User accepts
        };
        var features = new TestFeatures()
            .SetFeature(KnownFeatures.DotNetSdkInstallationEnabled, true);
        var hostEnvironment = new TestCliHostEnvironment(supportsInteractiveInput: true);
 
        var result = await SdkInstallHelper.EnsureSdkInstalledAsync(
            sdkInstaller,
            interactionService,
            features,
            fixture.Telemetry,
            hostEnvironment);
 
        Assert.True(result);
        Assert.NotNull(fixture.CapturedActivity);
 
        var tags = fixture.CapturedActivity.Tags.ToDictionary(t => t.Key, t => t.Value);
        Assert.Equal("not_installed", tags[TelemetryConstants.Tags.SdkCheckResult]);
        Assert.Equal("installed", tags[TelemetryConstants.Tags.SdkInstallResult]);
    }
 
    [Fact]
    public async Task EnsureSdkInstalledAsync_WhenInstallationFails_RecordsInstallErrorTelemetry()
    {
        using var fixture = new TelemetryFixture();
 
        var sdkInstaller = new TestDotNetSdkInstaller
        {
            CheckAsyncCallback = _ => (false, "8.0.100", "9.0.302", false),
            InstallAsyncCallback = _ => throw new InvalidOperationException("Installation failed")
        };
 
        var interactionService = new TestConsoleInteractionService
        {
            ConfirmCallback = (_, _) => true // User accepts
        };
        var features = new TestFeatures()
            .SetFeature(KnownFeatures.DotNetSdkInstallationEnabled, true);
        var hostEnvironment = new TestCliHostEnvironment(supportsInteractiveInput: true);
 
        var result = await SdkInstallHelper.EnsureSdkInstalledAsync(
            sdkInstaller,
            interactionService,
            features,
            fixture.Telemetry,
            hostEnvironment);
 
        Assert.False(result);
        Assert.NotNull(fixture.CapturedActivity);
 
        var tags = fixture.CapturedActivity.Tags.ToDictionary(t => t.Key, t => t.Value);
        Assert.Equal("not_installed", tags[TelemetryConstants.Tags.SdkCheckResult]);
        Assert.Equal("install_error", tags[TelemetryConstants.Tags.SdkInstallResult]);
    }
 
    [Fact]
    public async Task EnsureSdkInstalledAsync_WhenForceInstall_RecordsForceInstalledTelemetry()
    {
        using var fixture = new TelemetryFixture();
 
        var sdkInstaller = new TestDotNetSdkInstaller
        {
            CheckAsyncCallback = _ => (true, "9.0.302", "9.0.302", true), // forceInstall = true
            InstallAsyncCallback = _ => Task.CompletedTask
        };
 
        var interactionService = new TestConsoleInteractionService();
        var features = new TestFeatures()
            .SetFeature(KnownFeatures.DotNetSdkInstallationEnabled, true);
 
        var result = await SdkInstallHelper.EnsureSdkInstalledAsync(
            sdkInstaller,
            interactionService,
            features,
            fixture.Telemetry);
 
        Assert.True(result);
        Assert.NotNull(fixture.CapturedActivity);
 
        var tags = fixture.CapturedActivity.Tags.ToDictionary(t => t.Key, t => t.Value);
        Assert.Equal("force_installed", tags[TelemetryConstants.Tags.SdkCheckResult]);
        Assert.Equal("installed", tags[TelemetryConstants.Tags.SdkInstallResult]);
    }
}