Skip to content

Add more dotnetup walkthrough tests #53519

@nagilson

Description

@nagilson

Enable E2E testing of WalkthroughCommand without real I/O or downloads

Problem

WalkthroughCommand cannot be unit/integration tested today because it has hard dependencies on:

  1. InstallerOrchestratorSingleton — a true singleton with a private constructor that performs real downloads and file extraction. No interface exists.
  2. Spectre.Console.AnsiConsole (static) — all output and interactive prompts (SelectionPrompt, MarkupLine, Live) go through the static AnsiConsole. InteractiveOptionSelector reads Console.ReadKey directly.
  3. File system / configDotnetupConfig.EnsurePathPreference reads/writes a real config file. GetInstalledAdminInstalls reads real disk paths via HostFxrWrapper.

This blocks testing:

  • Output ordering (banner → channel prompt → path preference → install → config → "Setup complete!")
  • Admin migration prompt suppression when no admin installs exist
  • Deferred install flow when admin installs are present
  • Channel prompt default selection with redirected input

Proposed Changes

1. Extract IInstallerOrchestrator from InstallerOrchestratorSingleton

Effort: Medium — this is the critical blocker.

internal interface IInstallerOrchestrator
{
    InstallResult Install(DotnetInstallRequest request, bool noProgress = false);
    IReadOnlyList<InstallResult> InstallMany(IEnumerable<DotnetInstallRequest> requests, IProgressReporter reporter);
    // Static helpers like PredownloadToCacheAsync can remain static or move to a separate service.
}
  • InstallerOrchestratorSingleton implements IInstallerOrchestrator.
  • Inject via constructor into InstallExecutor, InstallWorkflow, command classes.
  • Tests provide a MockInstallerOrchestrator that returns fake InstallResult values without downloading.

2. Create MockDotnetInstallManager

Effort: SmallIDotnetInstallManager already exists.

internal class MockDotnetInstallManager : IDotnetInstallManager
{
    public List<DotnetInstall> AdminInstalls { get; set; } = [];
    public string DefaultInstallPath { get; set; } = "/tmp/dotnet";
    // ... implement all interface members with configurable return values
}

This enables testing:

  • GetInstalledAdminInstalls() returns empty → admin migration prompt is skipped
  • GetInstalledAdminInstalls() returns items → admin migration prompt fires
  • GetConfiguredInstallType() returns specific install types

3. Thread IAnsiConsole through display methods

Effort: Medium — many SpectreAnsiConsole.* call sites.

Spectre.Console supports IAnsiConsole injection. The pattern already exists in DotnetBotBannerTests:

var writer = new StringWriter();
var console = AnsiConsole.Create(new AnsiConsoleSettings
{
    Out = new AnsiConsoleOutput(writer),
    Ansi = AnsiSupport.No,
});

Changes:

  • Add IAnsiConsole parameter to WalkthroughCommand, InstallExecutor display methods, InstallWalkthrough, InteractiveOptionSelector.
  • Default to AnsiConsole.Console in production.
  • Tests pass a TestConsole or AnsiConsole.Create(...) with a StringWriter to capture output.
  • For input simulation: Console.IsInputRedirected already causes fallback to defaults in InteractiveOptionSelector and PromptChannel. Tests redirect stdin.

4. Write walkthrough tests

Effort: Small once 1–3 are done.

public class WalkthroughCommandTests
{
    [Fact]
    public void Walkthrough_OutputAppearsInCorrectOrder()
    {
        // Verify: banner → channel → path preference → install → config → "Setup complete!"
    }

    [Fact]
    public void Walkthrough_NoAdminInstalls_SkipsAdminMigrationPrompt()
    {
        // MockDotnetInstallManager returns empty admin installs
        // Assert output does not contain migration prompt text
    }

    [Fact]
    public void Walkthrough_WithAdminInstalls_ShowsMigrationPrompt()
    {
        // MockDotnetInstallManager returns admin installs
        // Assert output contains migration prompt and deferred install messages
    }

    [Fact]
    public void Walkthrough_DotnetupDotnetMode_SkipsAdminMigration()
    {
        // PathPreference.DotnetupDotnet should never trigger admin migration
    }
}

Implementation Order

Step What Blocks
1 MockDotnetInstallManager Nothing — IDotnetInstallManager exists
2 IInstallerOrchestrator extraction Walkthrough tests (can't avoid real downloads without it)
3 IAnsiConsole threading Output-order assertions (can use process-level capture as interim)
4 Walkthrough tests Steps 1–2 minimum; step 3 for output assertions

Interim: Process-Level E2E

Before the full refactoring, we can test some ordering via the existing DotnetupTestUtilities.RunDotnetupProcess pattern with --interactive false --no-progress and stdout capture. This doesn't test interactive prompts but verifies the non-interactive flow and output structure.

Metadata

Metadata

Assignees

No one assigned

    Labels

    dotnetupWork items around the proposed `dotnetup` bootstrapper/toolchain management tool and library

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions