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:
InstallerOrchestratorSingleton — a true singleton with a private constructor that performs real downloads and file extraction. No interface exists.
Spectre.Console.AnsiConsole (static) — all output and interactive prompts (SelectionPrompt, MarkupLine, Live) go through the static AnsiConsole. InteractiveOptionSelector reads Console.ReadKey directly.
- File system / config —
DotnetupConfig.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: Small — IDotnetInstallManager 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.
Enable E2E testing of WalkthroughCommand without real I/O or downloads
Problem
WalkthroughCommandcannot be unit/integration tested today because it has hard dependencies on:InstallerOrchestratorSingleton— a true singleton with a private constructor that performs real downloads and file extraction. No interface exists.Spectre.Console.AnsiConsole(static) — all output and interactive prompts (SelectionPrompt,MarkupLine,Live) go through the staticAnsiConsole.InteractiveOptionSelectorreadsConsole.ReadKeydirectly.DotnetupConfig.EnsurePathPreferencereads/writes a real config file.GetInstalledAdminInstallsreads real disk paths viaHostFxrWrapper.This blocks testing:
Proposed Changes
1. Extract
IInstallerOrchestratorfromInstallerOrchestratorSingletonEffort: Medium — this is the critical blocker.
InstallerOrchestratorSingletonimplementsIInstallerOrchestrator.InstallExecutor,InstallWorkflow, command classes.MockInstallerOrchestratorthat returns fakeInstallResultvalues without downloading.2. Create
MockDotnetInstallManagerEffort: Small —
IDotnetInstallManageralready exists.This enables testing:
GetInstalledAdminInstalls()returns empty → admin migration prompt is skippedGetInstalledAdminInstalls()returns items → admin migration prompt firesGetConfiguredInstallType()returns specific install types3. Thread
IAnsiConsolethrough display methodsEffort: Medium — many
SpectreAnsiConsole.*call sites.Spectre.Console supports
IAnsiConsoleinjection. The pattern already exists inDotnetBotBannerTests:Changes:
IAnsiConsoleparameter toWalkthroughCommand,InstallExecutordisplay methods,InstallWalkthrough,InteractiveOptionSelector.AnsiConsole.Consolein production.TestConsoleorAnsiConsole.Create(...)with aStringWriterto capture output.Console.IsInputRedirectedalready causes fallback to defaults inInteractiveOptionSelectorandPromptChannel. Tests redirect stdin.4. Write walkthrough tests
Effort: Small once 1–3 are done.
Implementation Order
MockDotnetInstallManagerIDotnetInstallManagerexistsIInstallerOrchestratorextractionIAnsiConsolethreadingInterim: Process-Level E2E
Before the full refactoring, we can test some ordering via the existing
DotnetupTestUtilities.RunDotnetupProcesspattern with--interactive false --no-progressand stdout capture. This doesn't test interactive prompts but verifies the non-interactive flow and output structure.