Git/GitIdentity/Port/WriteAccess and Claude CLI/Version/Auth/PermissionModeAuto checks plus the ExecutableResolver they depend on were built in two sibling task branches that hadn't landed on main yet. Vendored the finished files in from those branches (same content, verified building + tests green) so the SystemCheckPage task has something to consume.
85 lines
3.0 KiB
C#
85 lines
3.0 KiB
C#
using ClaudeDo.Installer.Checks;
|
|
using ClaudeDo.Installer.Core;
|
|
|
|
namespace ClaudeDo.Installer.Tests.Checks;
|
|
|
|
public sealed class ClaudeCliLookupTests : IDisposable
|
|
{
|
|
private readonly string _dir;
|
|
|
|
public ClaudeCliLookupTests()
|
|
{
|
|
_dir = Path.Combine(Path.GetTempPath(), $"cdclaudelookup_{Guid.NewGuid():N}");
|
|
Directory.CreateDirectory(_dir);
|
|
File.WriteAllText(Path.Combine(_dir, "claude.exe"), "");
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
try { Directory.Delete(_dir, recursive: true); } catch { }
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Version_is_executed_only_once_across_repeated_calls()
|
|
{
|
|
var runner = new FakeProcessRunner((0, "2.1.230 (Claude Code)"));
|
|
var lookup = new ClaudeCliLookup(runner, pathOverride: _dir, pathExtOverride: ".exe");
|
|
|
|
await lookup.ResolveAsync(new InstallContext(), CancellationToken.None);
|
|
await lookup.ResolveAsync(new InstallContext(), CancellationToken.None);
|
|
await lookup.ResolveAsync(new InstallContext(), CancellationToken.None);
|
|
|
|
Assert.Single(runner.Calls);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Concurrent_calls_still_run_version_only_once()
|
|
{
|
|
var runner = new FakeProcessRunner((0, "2.1.230 (Claude Code)"));
|
|
var lookup = new ClaudeCliLookup(runner, pathOverride: _dir, pathExtOverride: ".exe");
|
|
|
|
await Task.WhenAll(Enumerable.Range(0, 8)
|
|
.Select(_ => lookup.ResolveAsync(new InstallContext(), CancellationToken.None)));
|
|
|
|
Assert.Single(runner.Calls);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Version_is_executed_only_once_when_shared_across_all_four_checks()
|
|
{
|
|
var runner = new FakeProcessRunner((0, "2.1.230 (Claude Code)"));
|
|
var lookup = new ClaudeCliLookup(runner, pathOverride: _dir, pathExtOverride: ".exe");
|
|
var ctx = new InstallContext();
|
|
|
|
await new ClaudeCliCheck(lookup).RunAsync(ctx, CancellationToken.None);
|
|
await new ClaudeVersionCheck(lookup).RunAsync(ctx, CancellationToken.None);
|
|
await new ClaudeAuthCheck(lookup).RunAsync(ctx, CancellationToken.None);
|
|
await new PermissionModeAutoCheck(lookup).RunAsync(ctx, CancellationToken.None);
|
|
|
|
var versionCalls = runner.Calls.Count(c => c.Arguments.Contains("--version"));
|
|
Assert.Equal(1, versionCalls);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Cli_not_found_returns_null_resolved_without_running_a_process()
|
|
{
|
|
var emptyDir = Path.Combine(Path.GetTempPath(), $"cdclaudelookup_empty_{Guid.NewGuid():N}");
|
|
Directory.CreateDirectory(emptyDir);
|
|
try
|
|
{
|
|
var runner = new FakeProcessRunner();
|
|
var lookup = new ClaudeCliLookup(runner, pathOverride: emptyDir, pathExtOverride: ".exe");
|
|
var ctx = new InstallContext { ClaudeBin = "claudedo_totally_missing_cmd_9f3a1" };
|
|
|
|
var result = await lookup.ResolveAsync(ctx, CancellationToken.None);
|
|
|
|
Assert.Null(result.Resolved);
|
|
Assert.Empty(runner.Calls);
|
|
}
|
|
finally
|
|
{
|
|
Directory.Delete(emptyDir, recursive: true);
|
|
}
|
|
}
|
|
}
|