zcbot/windows-node/Zcbot.WindowsNode.Tests/AdapterArchitectureTests.cs

116 lines
4.0 KiB
C#

using System.Diagnostics;
namespace Zcbot.WindowsNode.Tests;
public sealed class AdapterArchitectureTests
{
[Fact]
public void ContractLoaderReadsEveryPackagedAdapterWithUniqueCapabilities()
{
var descriptors = Directory.EnumerateDirectories(AdapterCatalog.AdapterRoot)
.Where(path => File.Exists(Path.Combine(path, "adapter.json")))
.Select(AdapterContractLoader.Load)
.ToArray();
Assert.NotEmpty(descriptors);
Assert.Equal(
descriptors.Length,
descriptors.Select(item => item.Contract.Capability).Distinct().Count());
Assert.All(descriptors, descriptor =>
{
Assert.StartsWith(descriptor.DirectoryPath, descriptor.EntrypointPath);
Assert.StartsWith(descriptor.DirectoryPath, descriptor.ContractPath);
Assert.Matches("^[0-9a-f]{64}$", descriptor.ContractSha256);
});
}
[Fact]
public async Task ExecutionGateSerializesAllAdapterCapabilities()
{
using var gate = new JobExecutionGate();
using var first = await gate.EnterAsync(CancellationToken.None);
var secondEntered = false;
var second = Task.Run(async () =>
{
using var lease = await gate.EnterAsync(CancellationToken.None);
secondEntered = true;
});
await Task.Delay(30);
Assert.False(secondEntered);
Assert.True(gate.IsActive);
first.Dispose();
await second;
Assert.True(secondEntered);
Assert.False(gate.IsActive);
}
[Fact]
public async Task ExecutionGateHonorsCancellationWhileWaiting()
{
using var gate = new JobExecutionGate();
using var first = await gate.EnterAsync(CancellationToken.None);
using var stop = new CancellationTokenSource();
stop.Cancel();
await Assert.ThrowsAnyAsync<OperationCanceledException>(
async () => await gate.EnterAsync(stop.Token));
}
[Fact]
public async Task ProcessSupervisorTerminatesAWorkerProcessTree()
{
var supervisor = new ProcessSupervisor();
using var process = supervisor.Start(new ProcessStartInfo(
"cmd.exe", "/d /c ping 127.0.0.1 -t")
{
UseShellExecute = false,
CreateNoWindow = true,
});
await supervisor.TerminateTreeAsync(process);
Assert.True(process.HasExited);
}
[Fact]
public void SoftwareCatalogKeepsThePublishedRuntimeIdentifiers()
{
Assert.Equal(["origin", "ansys", "blender", "freecad", "cad"],
SoftwareCatalog.Definitions.Select(item => item.RuntimeId));
Assert.Equal("ZCBOT_ORIGIN_EXE",
SoftwareCatalog.ByRuntimeId("origin").EnvironmentVariable);
Assert.Equal("AWP_ROOT242",
SoftwareCatalog.ByRuntimeId("ansys").EnvironmentVariable);
Assert.Equal("ZCBOT_BLENDER_EXE",
SoftwareCatalog.ByRuntimeId("blender").EnvironmentVariable);
var freecad = SoftwareCatalog.ByRuntimeId("freecad");
Assert.Equal("ZCBOT_FREECAD_CMD", freecad.EnvironmentVariable);
Assert.Equal("FreeCADCmd.exe", freecad.RequiredRelativePath);
Assert.Equal(SoftwarePathKind.Executable, freecad.PathKind);
var cad = SoftwareCatalog.ByRuntimeId("cad");
Assert.Equal(SoftwarePathKind.None, cad.PathKind);
Assert.Null(cad.EnvironmentVariable);
Assert.Equal("https://pypi.org/simple/", cad.DefaultIndexUrl);
}
[Fact]
public void ProbeServiceCachesAndExplicitlyInvalidatesAProbe()
{
var calls = 0;
var service = new SoftwareProbeService(() =>
{
calls++;
return new AdapterRuntimeStatus(
"Test", "1.0", "1.0.0", "ready", $"probe-{calls}");
});
Assert.Equal("probe-1", service.Detect().Detail);
Assert.Equal("probe-1", service.Detect().Detail);
Assert.Equal(1, calls);
service.Invalidate();
Assert.Equal("probe-2", service.Detect().Detail);
Assert.Equal(2, calls);
}
}