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( async () => await gate.EnterAsync(stop.Token)); } [Fact] public void SoftwareCatalogKeepsThePublishedRuntimeIdentifiers() { Assert.Equal(["origin", "ansys", "blender"], 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); } [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); } }