fix(abcip): translate decode/encode-time libplctag exceptions; doc and probe-test cleanups

This commit is contained in:
Joseph Doherty
2026-10-07 15:44:02 -04:00
parent a57d61c484
commit 10c909cbc2
8 changed files with 89 additions and 20 deletions
@@ -181,4 +181,25 @@ public sealed class AbCipDriverProbeTests
public void AnUnrecognisedExceptionMessageIsNotTreatedAsReachable()
=> AbCipDriverProbe.IsReachableException(
new AbTransportException(AbStatus.BadConnection, "something the wrapper has never said")).ShouldBeFalse();
/// <summary>A fake runtime reporting NotFound is reachable ("probe tag not found").</summary>
[Fact]
public async Task Injected_factory_NotFound_status_is_reported_reachable()
{
using var listener = new TcpListener(IPAddress.Loopback, 0);
listener.Start();
var port = ((IPEndPoint)listener.LocalEndpoint).Port;
var factory = new FakeAbCipTagFactory
{
Customise = p => new FakeAbCipTag(p) { Status = (int)AbStatus.NotFound },
};
var result = await new AbCipDriverProbe(factory).ProbeAsync(
$"{{\"devices\":[{{\"hostAddress\":\"ab://127.0.0.1:{port}/1,0\"}}]}}",
TimeSpan.FromSeconds(3),
TestContext.Current.CancellationToken);
result.Ok.ShouldBeTrue();
result.Message!.ShouldContain("probe tag not found");
}
}
@@ -224,4 +224,24 @@ public sealed class AbCipDriverReadTests
snapshots.Single().StatusCode.ShouldBe(AbCipStatusMapper.BadCommunicationError);
factory.Tags["Nope"].Disposed.ShouldBeTrue();
}
/// <summary>A decode that throws OutOfBounds reads BadOutOfRange through the driver read path.</summary>
[Fact]
public async Task Decode_OutOfBounds_exception_maps_to_BadOutOfRange()
{
var (drv, factory) = NewDriver(
new AbCipTagDefinition("Over", "ab://10.0.0.5/1,0", "Over", AbCipDataType.DInt));
await drv.InitializeAsync("{}", CancellationToken.None);
factory.Customise = p => new ThrowingDecodeTag(p);
var snapshots = await drv.ReadAsync(["Over"], CancellationToken.None);
snapshots.Single().StatusCode.ShouldBe(AbCipStatusMapper.BadOutOfRange);
}
private sealed class ThrowingDecodeTag(AbCipTagCreateParams p) : FakeAbCipTag(p)
{
public override object? DecodeValue(AbCipDataType type, int? bitIndex) =>
throw new AbTransportException(AbStatus.OutOfBounds, "decode past buffer");
}
}
@@ -104,4 +104,14 @@ public sealed class AbCipStatusMapperTests
Status.ErrorUnsupported or Status.ErrorNotImplemented => AbCipStatusMapper.BadNotSupported,
_ => AbCipStatusMapper.BadCommunicationError, // incl. BadConnection/BadGateway/BadReply/Partial/Abort/...
};
/// <summary>A decode past the buffer end surfaces as BadOutOfRange once the adapter wraps it.</summary>
[Fact]
public void Wrapped_decode_OutOfBounds_maps_to_BadOutOfRange()
{
var ex = Should.Throw<AbTransportException>(() =>
LibplctagStatusTranslation.WrapSync<int>(() => throw new LibPlcTagException("ErrorOutOfBounds")));
AbCipStatusMapper.MapException(ex).ShouldBe(AbCipStatusMapper.BadOutOfRange);
}
}