180 lines
6.0 KiB
C#

using System.Diagnostics;
using System.Text.Json;
using Xunit;
namespace DevTool.Tests;
public sealed class ScriptSmokeTests
{
[Fact]
public async Task DiagProbe_JsonContract_IsValid()
{
var python = ResolvePython();
var result = await RunAsync(
python,
["scripts/diag.py", "probe", "--tool", "python", "--json"]);
Assert.Equal(0, result.ExitCode);
using var doc = JsonDocument.Parse(result.StdOut);
Assert.True(doc.RootElement.TryGetProperty("tool", out _));
Assert.True(doc.RootElement.TryGetProperty("available", out _));
}
[Fact]
public async Task BuildAction_InvalidRequirements_PropagatesNonZeroAndJson()
{
var python = ResolvePython();
var missingReq = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".txt");
var result = await RunAsync(
python,
[
"scripts/build.py",
"python-pip-install",
"--project-root",
RepoRoot(),
"--requirements",
missingReq,
"--json"
]);
Assert.NotEqual(0, result.ExitCode);
var jsonText = ExtractLastJsonObject(result.StdOut);
using var doc = JsonDocument.Parse(jsonText);
Assert.True(doc.RootElement.TryGetProperty("exit_code", out var code));
Assert.NotEqual(0, code.GetInt32());
Assert.True(doc.RootElement.TryGetProperty("failure_reason", out _));
}
[Fact]
public async Task BuildAction_DotnetRestore_CommandNotFoundStillReturnsJson()
{
var python = ResolvePython();
var result = await RunAsync(
python,
[
"scripts/build.py",
"dotnet-restore",
"--project-root",
RepoRoot(),
"--json"
]);
var jsonText = ExtractLastJsonObject(result.StdOut);
using var doc = JsonDocument.Parse(jsonText);
Assert.True(doc.RootElement.TryGetProperty("exit_code", out _));
Assert.True(doc.RootElement.TryGetProperty("status", out _));
}
[Fact]
public async Task BuildAction_DotnetBuild_AutoSelectsSlnTarget_WhenSingleSlnFound()
{
var python = ResolvePython();
var tempRoot = Path.Combine(Path.GetTempPath(), "sdt-dotnet-target-" + Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(tempRoot);
var sln = Path.Combine(tempRoot, "sample.sln");
await File.WriteAllTextAsync(sln, "Microsoft Visual Studio Solution File, Format Version 12.00");
var result = await RunAsync(
python,
[
"scripts/build.py",
"dotnet-build",
"--project-root",
tempRoot,
"--working-dir",
".",
"--json"
]);
var jsonText = ExtractLastJsonObject(result.StdOut);
using var doc = JsonDocument.Parse(jsonText);
Assert.True(doc.RootElement.TryGetProperty("args", out var args));
Assert.Contains(args.EnumerateArray().Select(x => x.GetString()), x => string.Equals(x, sln, StringComparison.OrdinalIgnoreCase));
}
private static string ResolvePython()
{
var candidates = OperatingSystem.IsWindows()
? new[] { "python", "py" }
: new[] { "python3", "python" };
foreach (var candidate in candidates)
{
try
{
using var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = candidate,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true,
}
};
process.StartInfo.ArgumentList.Add("--version");
process.Start();
process.WaitForExit(2000);
if (process.ExitCode == 0)
return candidate;
}
catch
{
}
}
throw new InvalidOperationException("Python executable not found for script smoke tests.");
}
private static async Task<(int ExitCode, string StdOut, string StdErr)> RunAsync(string command, IReadOnlyList<string> args)
{
var psi = new ProcessStartInfo
{
FileName = command,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true,
WorkingDirectory = RepoRoot(),
};
foreach (var arg in args)
psi.ArgumentList.Add(arg);
using var process = new Process { StartInfo = psi };
process.Start();
var stdout = await process.StandardOutput.ReadToEndAsync();
var stderr = await process.StandardError.ReadToEndAsync();
await process.WaitForExitAsync();
return (process.ExitCode, stdout, stderr);
}
private static string RepoRoot()
{
var dir = new DirectoryInfo(AppContext.BaseDirectory);
while (dir is not null)
{
if (File.Exists(Path.Combine(dir.FullName, "devtool.json")))
return dir.FullName;
dir = dir.Parent;
}
throw new InvalidOperationException("Could not locate repo root (devtool.json not found).");
}
private static string ExtractLastJsonObject(string text)
{
var lines = text.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
for (var i = lines.Length - 1; i >= 0; i--)
{
var line = lines[i];
if (line.StartsWith("{", StringComparison.Ordinal) && line.EndsWith("}", StringComparison.Ordinal))
return line;
}
throw new InvalidOperationException("No JSON object line found in script output.");
}
}