using ClaudeDo.Worker.Lifecycle; namespace ClaudeDo.Worker.Tests.Services; public class VerifyCommandRunnerTests { private readonly VerifyCommandRunner _runner = new(); [Fact] public async Task RunAsync_ExitsZero_ReportsSuccessNotTimedOut() { var result = await _runner.RunAsync( Path.GetTempPath(), "exit 0", TimeSpan.FromSeconds(30), CancellationToken.None); Assert.Equal(0, result.ExitCode); Assert.False(result.TimedOut); } [Fact] public async Task RunAsync_NonZeroExit_ReportsExitCode() { var result = await _runner.RunAsync( Path.GetTempPath(), "exit 7", TimeSpan.FromSeconds(30), CancellationToken.None); Assert.Equal(7, result.ExitCode); Assert.False(result.TimedOut); } [Fact] public async Task RunAsync_CapturesStdoutAndStderr() { var result = await _runner.RunAsync( Path.GetTempPath(), "echo hello-out & echo hello-err 1>&2", TimeSpan.FromSeconds(30), CancellationToken.None); Assert.Contains("hello-out", result.Output); Assert.Contains("hello-err", result.Output); } [Fact] public async Task RunAsync_RunsInSpecifiedWorkingDirectory() { var dir = Path.Combine(Path.GetTempPath(), $"verify_wd_{Guid.NewGuid():N}"); Directory.CreateDirectory(dir); try { var result = await _runner.RunAsync(dir, "cd", TimeSpan.FromSeconds(30), CancellationToken.None); Assert.Contains(new DirectoryInfo(dir).Name, result.Output); } finally { Directory.Delete(dir, recursive: true); } } [Fact] public async Task RunAsync_ExceedsTimeout_KillsProcessAndReportsTimedOut() { var result = await _runner.RunAsync( Path.GetTempPath(), "ping -n 60 127.0.0.1", TimeSpan.FromMilliseconds(300), CancellationToken.None); Assert.True(result.TimedOut); } }