Merge subtask

This commit is contained in:
mika kuns
2026-08-06 07:54:08 +02:00
4 changed files with 194 additions and 0 deletions
@@ -0,0 +1,122 @@
using ClaudeDo.Installer.Checks;
using ClaudeDo.Installer.Core;
namespace ClaudeDo.Installer.Tests;
public sealed class EnvironmentCheckServiceTests
{
private sealed class FakeCheck : IEnvironmentCheck
{
private readonly Func<InstallContext, CancellationToken, Task<CheckResult>>? _run;
public FakeCheck(string id, CheckSeverity severity, Func<InstallContext, CancellationToken, Task<CheckResult>>? run = null)
{
Id = id;
Severity = severity;
_run = run;
}
public string Id { get; }
public CheckSeverity Severity { get; }
public Task<CheckResult> RunAsync(InstallContext ctx, CancellationToken ct) =>
_run is null
? Task.FromResult(CheckResult.Ok(Id, Severity, "title", "ok"))
: _run(ctx, ct);
}
private sealed class ThrowingCheck : IEnvironmentCheck
{
public string Id => "throwing";
public CheckSeverity Severity => CheckSeverity.Error;
public Task<CheckResult> RunAsync(InstallContext ctx, CancellationToken ct) =>
throw new InvalidOperationException("boom");
}
[Fact]
public async Task RunAllAsync_PreservesInputOrder_RegardlessOfCompletionOrder()
{
var slow = new FakeCheck("slow", CheckSeverity.Warning, async (_, ct) =>
{
await Task.Delay(50, ct);
return CheckResult.Ok("slow", CheckSeverity.Warning, "title", "ok");
});
var fast = new FakeCheck("fast", CheckSeverity.Warning);
var service = new EnvironmentCheckService(new IEnvironmentCheck[] { slow, fast });
var report = await service.RunAllAsync(new InstallContext(), CancellationToken.None);
Assert.Equal(new[] { "slow", "fast" }, report.Results.Select(r => r.Id));
}
[Fact]
public async Task HasBlockingError_True_WhenErrorAndFailed()
{
var check = new FakeCheck("id", CheckSeverity.Error, (_, _) =>
Task.FromResult(CheckResult.Fail("id", CheckSeverity.Error, "title", "bad")));
var service = new EnvironmentCheckService(new IEnvironmentCheck[] { check });
var report = await service.RunAllAsync(new InstallContext(), CancellationToken.None);
Assert.True(report.HasBlockingError);
}
[Fact]
public async Task HasBlockingError_False_WhenWarningAndFailed()
{
var check = new FakeCheck("id", CheckSeverity.Warning, (_, _) =>
Task.FromResult(CheckResult.Fail("id", CheckSeverity.Warning, "title", "bad")));
var service = new EnvironmentCheckService(new IEnvironmentCheck[] { check });
var report = await service.RunAllAsync(new InstallContext(), CancellationToken.None);
Assert.False(report.HasBlockingError);
}
[Fact]
public async Task HasBlockingError_False_WhenErrorAndUnknown()
{
var check = new FakeCheck("id", CheckSeverity.Error, (_, _) =>
Task.FromResult(CheckResult.Unknown("id", CheckSeverity.Error, "title", "dunno")));
var service = new EnvironmentCheckService(new IEnvironmentCheck[] { check });
var report = await service.RunAllAsync(new InstallContext(), CancellationToken.None);
Assert.False(report.HasBlockingError);
}
[Fact]
public async Task RunAllAsync_ThrowingCheck_BecomesUnknown_OthersStillRun_ServiceDoesNotThrow()
{
var throwing = new ThrowingCheck();
var ok = new FakeCheck("ok", CheckSeverity.Warning);
var service = new EnvironmentCheckService(new IEnvironmentCheck[] { throwing, ok });
var report = await service.RunAllAsync(new InstallContext(), CancellationToken.None);
var throwingResult = report.Results[0];
Assert.Equal(CheckStatus.Unknown, throwingResult.Status);
Assert.Contains("boom", throwingResult.Detail);
var okResult = report.Results[1];
Assert.Equal(CheckStatus.Ok, okResult.Status);
}
[Fact]
public async Task RunAllAsync_PassesCancellationToken_ToChecks()
{
CancellationToken? seen = null;
var check = new FakeCheck("id", CheckSeverity.Warning, (_, ct) =>
{
seen = ct;
return Task.FromResult(CheckResult.Ok("id", CheckSeverity.Warning, "title", "ok"));
});
var service = new EnvironmentCheckService(new IEnvironmentCheck[] { check });
using var cts = new CancellationTokenSource();
await service.RunAllAsync(new InstallContext(), cts.Token);
Assert.Equal(cts.Token, seen);
}
}