dotnet / dotnet/sdk

Add more `dotnetup walkthrough` tests

Open
#53,519 0 comments 0 reactions 0 assignees View on GitHub
Area-dotnetup
Dominant language
C#
Stars
3.2k
Forks
1.3k
PR merge metrics
PR metrics pending

Description

# 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 / 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.

```csharp
internal interface IInstallerOrchestrator
{
InstallResult Install(DotnetInstallRequest request, bool noProgress = false);
IReadOnlyList InstallMany(IEnumerable 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.

```csharp
internal class MockDotnetInstallManager : IDotnetInstallManager
{
public List 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`:

```csharp
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.

```csharp
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.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.