File: Commands\DoCommandTests.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.Commands;
using Aspire.Cli.Tests.Utils;
using Aspire.Cli.Tests.TestServices;
using Microsoft.Extensions.DependencyInjection;
using Aspire.Cli.Utils;
 
namespace Aspire.Cli.Tests.Commands;
 
public class DoCommandTests(ITestOutputHelper outputHelper)
{
    [Fact]
    public async Task DoCommandWithHelpArgumentReturnsZero()
    {
        using var tempRepo = TemporaryWorkspace.Create(outputHelper);
 
        var services = CliTestHelper.CreateServiceCollection(tempRepo, outputHelper);
        var provider = services.BuildServiceProvider();
 
        var command = provider.GetRequiredService<RootCommand>();
        var result = command.Parse("do --help");
 
        var exitCode = await result.InvokeAsync().WaitAsync(CliTestConstants.DefaultTimeout);
        Assert.Equal(0, exitCode);
    }
 
    [Fact]
    public async Task DoCommandWithStepArgumentSucceeds()
    {
        using var tempRepo = TemporaryWorkspace.Create(outputHelper);
 
        // Arrange
        var services = CliTestHelper.CreateServiceCollection(tempRepo, outputHelper, options =>
        {
            options.ProjectLocatorFactory = (sp) => new TestProjectLocator();
 
            options.DotNetCliRunnerFactory = (sp) =>
            {
                var runner = new TestDotNetCliRunner
                {
                    // Simulate a successful build
                    BuildAsyncCallback = (projectFile, options, cancellationToken) => 0,
 
                    // Simulate a successful app host information retrieval
                    GetAppHostInformationAsyncCallback = (projectFile, options, cancellationToken) =>
                    {
                        return (0, true, VersionHelper.GetDefaultTemplateVersion());
                    },
 
                    // Simulate apphost running successfully and establishing a backchannel
                    RunAsyncCallback = async (projectFile, watch, noBuild, args, env, backchannelCompletionSource, options, cancellationToken) =>
                    {
                        Assert.True(options.NoLaunchProfile);
 
                        // Verify that the custom step is passed
                        Assert.Contains("--step", args);
                        Assert.Contains("my-custom-step", args);
 
                        var completed = new TaskCompletionSource();
                        var backchannel = new TestAppHostBackchannel
                        {
                            RequestStopAsyncCalled = completed
                        };
                        backchannelCompletionSource?.SetResult(backchannel);
                        await completed.Task;
                        return 0;
                    }
                };
 
                return runner;
            };
        });
 
        var provider = services.BuildServiceProvider();
        var command = provider.GetRequiredService<RootCommand>();
 
        // Act
        var result = command.Parse("do my-custom-step");
        var exitCode = await result.InvokeAsync().WaitAsync(CliTestConstants.DefaultTimeout);
 
        // Assert
        Assert.Equal(0, exitCode);
    }
 
    [Fact]
    public async Task DoCommandWithDeployStepSucceeds()
    {
        using var tempRepo = TemporaryWorkspace.Create(outputHelper);
 
        // Arrange
        var services = CliTestHelper.CreateServiceCollection(tempRepo, outputHelper, options =>
        {
            options.ProjectLocatorFactory = (sp) => new TestProjectLocator();
 
            options.DotNetCliRunnerFactory = (sp) =>
            {
                var runner = new TestDotNetCliRunner
                {
                    BuildAsyncCallback = (projectFile, options, cancellationToken) => 0,
 
                    GetAppHostInformationAsyncCallback = (projectFile, options, cancellationToken) =>
                    {
                        return (0, true, VersionHelper.GetDefaultTemplateVersion());
                    },
 
                    RunAsyncCallback = async (projectFile, watch, noBuild, args, env, backchannelCompletionSource, options, cancellationToken) =>
                    {
                        // Verify that --step deploy is passed
                        Assert.Contains("--step", args);
                        Assert.Contains("deploy", args);
 
                        var completed = new TaskCompletionSource();
                        var backchannel = new TestAppHostBackchannel
                        {
                            RequestStopAsyncCalled = completed
                        };
                        backchannelCompletionSource?.SetResult(backchannel);
                        await completed.Task;
                        return 0;
                    }
                };
 
                return runner;
            };
        });
 
        var provider = services.BuildServiceProvider();
        var command = provider.GetRequiredService<RootCommand>();
 
        // Act
        var result = command.Parse("do deploy");
        var exitCode = await result.InvokeAsync().WaitAsync(CliTestConstants.DefaultTimeout);
 
        // Assert
        Assert.Equal(0, exitCode);
    }
 
    [Fact]
    public async Task DoCommandWithPublishStepSucceeds()
    {
        using var tempRepo = TemporaryWorkspace.Create(outputHelper);
 
        // Arrange
        var services = CliTestHelper.CreateServiceCollection(tempRepo, outputHelper, options =>
        {
            options.ProjectLocatorFactory = (sp) => new TestProjectLocator();
 
            options.DotNetCliRunnerFactory = (sp) =>
            {
                var runner = new TestDotNetCliRunner
                {
                    BuildAsyncCallback = (projectFile, options, cancellationToken) => 0,
 
                    GetAppHostInformationAsyncCallback = (projectFile, options, cancellationToken) =>
                    {
                        return (0, true, VersionHelper.GetDefaultTemplateVersion());
                    },
 
                    RunAsyncCallback = async (projectFile, watch, noBuild, args, env, backchannelCompletionSource, options, cancellationToken) =>
                    {
                        // Verify that --step publish is passed
                        Assert.Contains("--step", args);
                        Assert.Contains("publish", args);
 
                        var completed = new TaskCompletionSource();
                        var backchannel = new TestAppHostBackchannel
                        {
                            RequestStopAsyncCalled = completed
                        };
                        backchannelCompletionSource?.SetResult(backchannel);
                        await completed.Task;
                        return 0;
                    }
                };
 
                return runner;
            };
        });
 
        var provider = services.BuildServiceProvider();
        var command = provider.GetRequiredService<RootCommand>();
 
        // Act
        var result = command.Parse("do publish");
        var exitCode = await result.InvokeAsync().WaitAsync(CliTestConstants.DefaultTimeout);
 
        // Assert
        Assert.Equal(0, exitCode);
    }
 
    [Fact]
    public async Task DoCommandPassesOutputPathWhenSpecified()
    {
        using var tempRepo = TemporaryWorkspace.Create(outputHelper);
 
        // Arrange
        var services = CliTestHelper.CreateServiceCollection(tempRepo, outputHelper, options =>
        {
            options.ProjectLocatorFactory = (sp) => new TestProjectLocator();
 
            options.DotNetCliRunnerFactory = (sp) =>
            {
                var runner = new TestDotNetCliRunner
                {
                    BuildAsyncCallback = (projectFile, options, cancellationToken) => 0,
 
                    GetAppHostInformationAsyncCallback = (projectFile, options, cancellationToken) =>
                    {
                        return (0, true, VersionHelper.GetDefaultTemplateVersion());
                    },
 
                    RunAsyncCallback = async (projectFile, watch, noBuild, args, env, backchannelCompletionSource, options, cancellationToken) =>
                    {
                        // Verify output path is included
                        Assert.Contains("--output-path", args);
                        
                        // Find the output path argument value
                        var outputPathIndex = Array.IndexOf(args, "--output-path");
                        Assert.True(outputPathIndex >= 0 && outputPathIndex < args.Length - 1);
                        var outputPath = args[outputPathIndex + 1];
                        Assert.EndsWith("test-output", outputPath);
 
                        var completed = new TaskCompletionSource();
                        var backchannel = new TestAppHostBackchannel
                        {
                            RequestStopAsyncCalled = completed
                        };
                        backchannelCompletionSource?.SetResult(backchannel);
                        await completed.Task;
                        return 0;
                    }
                };
 
                return runner;
            };
        });
 
        var provider = services.BuildServiceProvider();
        var command = provider.GetRequiredService<RootCommand>();
 
        // Act
        var result = command.Parse("do my-step --output-path test-output");
        var exitCode = await result.InvokeAsync().WaitAsync(CliTestConstants.DefaultTimeout);
 
        // Assert
        Assert.Equal(0, exitCode);
    }
 
    [Fact]
    public async Task DoCommandFailsWithInvalidProjectFile()
    {
        using var tempRepo = TemporaryWorkspace.Create(outputHelper);
 
        // Arrange
        var services = CliTestHelper.CreateServiceCollection(tempRepo, outputHelper, options =>
        {
            options.DotNetCliRunnerFactory = (sp) =>
            {
                var runner = new TestDotNetCliRunner
                {
                    GetAppHostInformationAsyncCallback = (projectFile, options, cancellationToken) =>
                    {
                        return (1, false, null);
                    }
                };
                return runner;
            };
        });
 
        var provider = services.BuildServiceProvider();
        var command = provider.GetRequiredService<RootCommand>();
 
        // Act
        var result = command.Parse("do my-step --project invalid.csproj");
        var exitCode = await result.InvokeAsync().WaitAsync(CliTestConstants.DefaultTimeout);
 
        // Assert
        Assert.Equal(ExitCodeConstants.FailedToFindProject, exitCode);
    }
}