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;
}
}
}
}