using System.Diagnostics; using System.Text.Json; namespace AgentWikiUi.Tests; /// Il doppio di test di claude (docs/TESTING.md → FakeClaude, FK-01…FK-05). [Trait("Category", "Process")] public sealed class FakeClaudeTests : IDisposable { readonly string dir = Directory.CreateTempSubdirectory("awui-fake-").FullName; public void Dispose() => Directory.Delete(dir, true); string LogPath => Path.Combine(dir, "log.jsonl"); Process Start(IEnumerable args, Dictionary? env = null, string? cwd = null) { var psi = new ProcessStartInfo(FakeClaudePath.Exe) { UseShellExecute = false, RedirectStandardInput = true, RedirectStandardOutput = true, RedirectStandardError = true, WorkingDirectory = cwd ?? dir, }; foreach (var a in args) psi.ArgumentList.Add(a); psi.Environment["FAKE_CLAUDE_LOG"] = LogPath; foreach (var (k, v) in env ?? []) psi.Environment[k] = v; return Process.Start(psi)!; } [Fact] public void Registra_argomenti_cwd_e_stdin() { var cwd = Directory.CreateDirectory(Path.Combine(dir, "lavoro")).FullName; using var p = Start(["-p", "dì \"ciao\" a tutti", "--model", "m"], cwd: cwd); p.StandardInput.Write("dati da stdin"); p.StandardInput.Close(); Assert.True(p.WaitForExit(10_000)); using var line = JsonDocument.Parse(File.ReadAllLines(LogPath).Single()); var r = line.RootElement; Assert.Equal(["-p", "dì \"ciao\" a tutti", "--model", "m"], r.GetProperty("args").EnumerateArray().Select(a => a.GetString())); Assert.Equal(cwd, r.GetProperty("cwd").GetString()); Assert.Equal("dati da stdin", r.GetProperty("stdin").GetString()); } [Fact] public void Output_e_stderr_configurati() { using var p = Start([], new() { ["FAKE_CLAUDE_OUT"] = "risposta", ["FAKE_CLAUDE_ERR"] = "avviso" }); p.StandardInput.Close(); Assert.Equal("risposta", p.StandardOutput.ReadToEnd()); Assert.Equal("avviso", p.StandardError.ReadToEnd()); Assert.True(p.WaitForExit(10_000)); } [Theory] [InlineData(null, 0)] [InlineData("3", 3)] [InlineData("127", 127)] public void ExitCode_configurato(string? configured, int expected) { using var p = Start([], configured is null ? null : new() { ["FAKE_CLAUDE_EXIT"] = configured }); p.StandardInput.Close(); Assert.True(p.WaitForExit(10_000)); Assert.Equal(expected, p.ExitCode); } [Fact] public void Ritardo_configurato() { using var p = Start([], new() { ["FAKE_CLAUDE_DELAY_MS"] = "600" }); var sw = Stopwatch.StartNew(); p.StandardInput.Close(); Assert.True(p.WaitForExit(10_000)); Assert.True(sw.ElapsedMilliseconds >= 500, $"terminato dopo {sw.ElapsedMilliseconds} ms"); } [Fact] public void Attende_la_chiusura_di_stdin() { using var p = Start([]); try { Assert.False(p.WaitForExit(700), "il processo è terminato con stdin ancora aperto"); p.StandardInput.Close(); Assert.True(p.WaitForExit(10_000)); } finally { if (!p.HasExited) p.Kill(); } } }