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