using JournalBot.Shared; using Xunit; public class BotRunnerTests { // Locate the venv python; skip if absent (CI without venv). private static string? VenvPython() { var root = FindRepoRoot(); if (root is null) return null; var py = Path.Combine(root, ".venv", "Scripts", "python.exe"); return File.Exists(py) ? py : null; } private static string? FindRepoRoot() { var dir = new DirectoryInfo(AppContext.BaseDirectory); while (dir is not null && !File.Exists(Path.Combine(dir.FullName, "pyproject.toml"))) dir = dir.Parent; return dir?.FullName; } [Fact] public async Task Run_capturesStdoutAndExitCode() { var py = VenvPython(); if (py is null) return; // environment without venv; nothing to assert var runner = new BotRunner(py, FindRepoRoot()!); var result = await runner.RunRawAsync(new[] { "-c", "print('hi')" }); Assert.Equal(0, result.ExitCode); Assert.Contains("hi", result.Output); } [Fact] public async Task Run_cancellationThrowsAndDoesNotHang() { var py = VenvPython(); if (py is null) return; // no venv in this environment var runner = new BotRunner(py, FindRepoRoot()!); using var cts = new System.Threading.CancellationTokenSource(); cts.CancelAfter(TimeSpan.FromMilliseconds(200)); await Assert.ThrowsAsync(async () => await runner.RunRawAsync(new[] { "-c", "import time; time.sleep(30)" }, cts.Token)); } }