using System.Diagnostics; using SQLInsight.Servers.Synthetic; namespace SQLInsight.Servers.Tests; /// The process the AX logon runs in: its output is bounded, and it never outlives the agent. public class ProcessRunnerTests { [Fact] public async Task Process_output_is_capped_but_read_to_the_end() { using var reader = new StringReader(new string('x', ProcessRunner.MaxOutputChars * 3) + "end"); var text = await ProcessRunner.ReadBoundedAsync(reader, ProcessRunner.MaxOutputChars); Assert.Equal(ProcessRunner.MaxOutputChars, text.Length); Assert.Equal(-1, reader.Peek()); // everything was read, so a process never blocks on a full pipe } [Fact] public async Task A_process_is_killed_when_the_agent_stops() { if (!OperatingSystem.IsWindows()) { return; } var marker = Path.Combine(Path.GetTempPath(), "sqlinsight-kill-" + Guid.NewGuid().ToString("N") + ".txt"); var powershell = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), @"WindowsPowerShell\v1.0\powershell.exe"); using var stop = new CancellationTokenSource(TimeSpan.FromMinutes(2)); var started = Stopwatch.StartNew(); try { // Writes its process id, then would wait for a minute; the agent stops as soon as it has started. var stopWhenStarted = Task.Run(async () => { while (!File.Exists(marker) && started.Elapsed < TimeSpan.FromMinutes(1)) { await Task.Delay(100); } await Task.Delay(500); await stop.CancelAsync(); }); await Assert.ThrowsAnyAsync(() => new ProcessRunner().RunAsync(powershell, ["-NoProfile", "-NonInteractive", "-Command", $"Set-Content -LiteralPath '{marker}' -Value $PID; Start-Sleep -Seconds 60"], new Dictionary(), TimeSpan.FromMinutes(5), stop.Token)); await stopWhenStarted; Assert.True(started.Elapsed < TimeSpan.FromSeconds(55), $"The process was waited for ({started.Elapsed})."); var pid = int.Parse((await File.ReadAllTextAsync(marker)).Trim(), System.Globalization.CultureInfo.InvariantCulture); var gone = Stopwatch.StartNew(); while (IsRunning(pid)) { Assert.True(gone.Elapsed < TimeSpan.FromSeconds(10), $"PowerShell ({pid}) was left running."); await Task.Delay(100); } } finally { File.Delete(marker); } static bool IsRunning(int pid) { try { using var process = Process.GetProcessById(pid); return !process.HasExited; } catch (ArgumentException) { return false; } } } }