using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Junction.Domain; using Junction.Domain.Models; using Junction.Domain.Protocols; using Moq; using Xunit; namespace Junction.Tests.Unit { /// /// Contract/shape tests for the plugin protocol contract. Pure compile + behavior /// against Moq doubles; no real IO. /// public class ProtocolContractTests { private static Machine SampleMachine(IReadOnlyDictionary? config = null) => new Machine( Guid.NewGuid(), "M1", "mtconnect", config, TimeSpan.FromSeconds(1)); private static MachineSnapshot SampleSnapshot(Guid machineId) => new MachineSnapshot( machineId, DateTimeOffset.UtcNow, ConnectionState.Connected, Array.Empty()); [Fact] public void Driver_ProtocolId_ReturnsConfiguredValue() { var mock = new Mock(); mock.SetupGet(d => d.ProtocolId).Returns("mtconnect"); Assert.Equal("mtconnect", mock.Object.ProtocolId); } [Fact] public async Task Driver_ReadCurrentAsync_Ok_ReturnsSnapshotResult() { var machineId = Guid.NewGuid(); var snapshot = SampleSnapshot(machineId); var mock = new Mock(); mock.Setup(d => d.ReadCurrentAsync(It.IsAny())) .ReturnsAsync(Result.Ok(snapshot)); Result result = await mock.Object.ReadCurrentAsync(CancellationToken.None); Assert.True(result.IsSuccess); Assert.Same(snapshot, result.Value); } [Fact] public async Task Driver_ReadCurrentAsync_Fail_ReturnsFailedResult() { var error = OperationError.Of("driver", "read failed"); var mock = new Mock(); mock.Setup(d => d.ReadCurrentAsync(It.IsAny())) .ReturnsAsync(Result.Fail(error)); Result result = await mock.Object.ReadCurrentAsync(CancellationToken.None); Assert.False(result.IsSuccess); Assert.NotEmpty(result.Errors); } [Fact] public async Task Driver_ReadCurrentAsync_ForwardsCancellationToken() { using var cts = new CancellationTokenSource(); var mock = new Mock(); mock.Setup(d => d.ReadCurrentAsync(cts.Token)) .ReturnsAsync(Result.Cancelled()); Result result = await mock.Object.ReadCurrentAsync(cts.Token); Assert.True(result.WasCancelled); mock.Verify(d => d.ReadCurrentAsync(cts.Token), Times.Once); } [Fact] public void Factory_ProtocolId_ReturnsConfiguredValue() { var mock = new Mock(); mock.SetupGet(f => f.ProtocolId).Returns("mtconnect"); Assert.Equal("mtconnect", mock.Object.ProtocolId); } [Fact] public void Factory_Create_Ok_WrapsDriver() { var driver = new Mock().Object; var factory = new Mock(); factory.Setup(f => f.Create(It.IsAny())) .Returns(Result.Ok(driver)); Result result = factory.Object.Create(SampleMachine()); Assert.True(result.IsSuccess); Assert.Same(driver, result.Value); } [Fact] public void Factory_Create_InvalidConfig_ReturnsFail() { // Simulate protocol-specific validation: missing required key => Fail. var factory = new Mock(); factory.Setup(f => f.Create(It.Is(m => !m.ConnectionConfig.ContainsKey("endpoint")))) .Returns(Result.Fail( OperationError.Of("factory", "missing 'endpoint'"))); Result result = factory.Object.Create(SampleMachine()); Assert.False(result.IsSuccess); Assert.NotEmpty(result.Errors); } [Fact] public void Factory_Create_ValidConfig_ReturnsOk() { var driver = new Mock().Object; var factory = new Mock(); factory.Setup(f => f.Create(It.Is(m => m.ConnectionConfig.ContainsKey("endpoint")))) .Returns(Result.Ok(driver)); var machine = SampleMachine(new Dictionary { ["endpoint"] = "http://x" }); Result result = factory.Object.Create(machine); Assert.True(result.IsSuccess); Assert.Same(driver, result.Value); } } }