forked from admin/VolcanicAgenticCore
110 lines
4.7 KiB
C#
110 lines
4.7 KiB
C#
using Volcanic.AgenticCore.Extensions.ClaudeCode;
|
|
|
|
namespace Volcanic.AgenticCore.Extensions.Tests.ClaudeCode;
|
|
|
|
public class ClaudeInstallationTests
|
|
{
|
|
[Fact]
|
|
public void Captured_help_has_every_required_flag()
|
|
{
|
|
Assert.Empty(ClaudeInstallation.MissingFlags(Fixtures.HelpText));
|
|
Assert.Contains("--print", ClaudeInstallation.RequiredFlags);
|
|
Assert.Contains("--system-prompt", ClaudeInstallation.RequiredFlags);
|
|
}
|
|
|
|
[Fact]
|
|
public void Truncated_help_reports_the_missing_flags()
|
|
{
|
|
var lines = Fixtures.HelpText.Split('\n');
|
|
var truncated = string.Join('\n', lines.Take(60));
|
|
|
|
var missing = ClaudeInstallation.MissingFlags(truncated);
|
|
|
|
Assert.NotEmpty(missing);
|
|
Assert.Contains("--verbose", missing);
|
|
Assert.Contains("--tools", missing);
|
|
Assert.DoesNotContain("--add-dir", missing);
|
|
Assert.Empty(ClaudeInstallation.MissingFlags(" -p, --print x\n" + string.Join('\n', ClaudeInstallation.RequiredFlags.Select(f => $" {f} <v> help"))));
|
|
// A longer flag with the same prefix does not satisfy the check.
|
|
Assert.Contains("--tools", ClaudeInstallation.MissingFlags(" --toolset <x>"));
|
|
}
|
|
|
|
[Fact]
|
|
public void Locate_prefers_the_environment_override_then_searches_path()
|
|
{
|
|
Assert.Equal("/opt/claude/bin/claude", ClaudeInstallation.Locate(name => name == ClaudeInstallation.PathVariable ? " /opt/claude/bin/claude " : null));
|
|
Assert.Null(ClaudeInstallation.Locate(_ => null));
|
|
|
|
var directory = Directory.CreateTempSubdirectory("locate-test-").FullName;
|
|
try
|
|
{
|
|
var name = OperatingSystem.IsWindows() ? "claude.cmd" : "claude";
|
|
var executable = Path.Combine(directory, name);
|
|
File.WriteAllText(executable, "#!/bin/sh\nexit 0\n");
|
|
if (!OperatingSystem.IsWindows())
|
|
{
|
|
File.SetUnixFileMode(executable, UnixFileMode.UserRead | UnixFileMode.UserWrite);
|
|
Assert.Null(ClaudeInstallation.Locate(n => n == "PATH" ? directory : null));
|
|
File.SetUnixFileMode(executable, UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute);
|
|
}
|
|
Assert.Equal(executable, ClaudeInstallation.Locate(n => n switch
|
|
{
|
|
"PATH" => $"{Path.Combine(directory, "missing")}{Path.PathSeparator}{directory}",
|
|
"PATHEXT" => ".COM;.EXE;.BAT;.CMD",
|
|
_ => null,
|
|
}));
|
|
}
|
|
finally
|
|
{
|
|
Directory.Delete(directory, recursive: true);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Inspect_reads_version_auth_and_help_from_the_cli()
|
|
{
|
|
if (!Fixtures.IsPosix) return;
|
|
var directory = Directory.CreateTempSubdirectory("inspect-test-").FullName;
|
|
try
|
|
{
|
|
var script = Path.Combine(directory, "claude");
|
|
File.WriteAllText(script, $$"""
|
|
#!/bin/sh
|
|
case "$1" in
|
|
--version) echo "2.1.280 (Claude Code)";;
|
|
auth) echo '{"loggedIn":true,"authMethod":"claude.ai","apiProvider":"firstParty","subscriptionType":"Pro"}';;
|
|
--help) cat "{{Fixtures.FixturePath("claude-2.1.281-help.txt")}}";;
|
|
*) echo "unexpected: $*" >&2; exit 3;;
|
|
esac
|
|
|
|
""");
|
|
if (!OperatingSystem.IsWindows()) File.SetUnixFileMode(script, UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute);
|
|
|
|
var info = await ClaudeInstallation.InspectAsync(script, CancellationToken.None);
|
|
|
|
Assert.Equal(script, info.Path);
|
|
Assert.Equal("2.1.280", info.Version);
|
|
Assert.True(info.LoggedIn);
|
|
Assert.Equal("claude.ai", info.AuthMethod);
|
|
Assert.Equal("firstParty", info.ApiProvider);
|
|
Assert.Equal("pro", info.SubscriptionType);
|
|
Assert.Empty(info.MissingFlags);
|
|
Assert.DoesNotContain(info.Warnings, w => w.Contains("version"));
|
|
|
|
File.WriteAllText(script, "#!/bin/sh\necho 'claude: not logged in'\nexit 1\n");
|
|
var broken = await ClaudeInstallation.InspectAsync(script, CancellationToken.None);
|
|
Assert.Null(broken.Version);
|
|
Assert.False(broken.LoggedIn);
|
|
Assert.Equal(ClaudeInstallation.RequiredFlags.Count, broken.MissingFlags.Count);
|
|
Assert.NotEmpty(broken.Warnings);
|
|
}
|
|
finally
|
|
{
|
|
Directory.Delete(directory, recursive: true);
|
|
}
|
|
|
|
var missing = await Assert.ThrowsAsync<ClaudeCodeException>(() => ClaudeInstallation.InspectAsync(Path.Combine(directory, "nope"), CancellationToken.None));
|
|
Assert.Equal("executable_missing", missing.Code);
|
|
}
|
|
}
|