test(ablegacy): bit write surfaces device rejection status (review)

Adds `Bit_write_surfaces_device_rejection_status` to AbLegacyBitRmwTests,
verifying that a non-zero libplctag status returned by the parent-word write
in WriteBitInWordAsync propagates as a non-Good OPC UA StatusCode rather than
being silently swallowed. Added a minimal `WriteStatusOverride` hook to
FakeAbLegacyTag (test-project-only) so the read half of the RMW still
returns 0/Good while the write half returns the seeded error code.
This commit is contained in:
Joseph Doherty
2026-06-17 12:02:10 -04:00
parent 340c145e87
commit c48f299994
2 changed files with 44 additions and 2 deletions
@@ -177,4 +177,38 @@ public sealed class AbLegacyBitRmwTests
Convert.ToInt32(factory.Tags["B3:0"].Value).ShouldBe(0xFF);
}
/// <summary>
/// A non-zero libplctag status returned by the parent-word write is surfaced as a non-Good
/// OPC UA StatusCode — the PCCC device rejection propagates to the caller.
/// </summary>
[Fact]
public async Task Bit_write_surfaces_device_rejection_status()
{
// Arrange: the parent tag reads OK (Status = 0) but write returns a timeout error.
const int errorTimeout = (int)libplctag.Status.ErrorTimeout;
var factory = new FakeAbLegacyTagFactory
{
Customise = p => new FakeAbLegacyTag(p)
{
Value = (short)0b0001,
WriteStatusOverride = errorTimeout,
},
};
var drv = new AbLegacyDriver(new AbLegacyDriverOptions
{
Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/1,0")],
Tags = [new AbLegacyTagDefinition("F", "ab://10.0.0.5/1,0", "I:0/3", AbLegacyDataType.Bit)],
Probe = new AbLegacyProbeOptions { Enabled = false },
}, "drv-1", factory);
await drv.InitializeAsync("{}", CancellationToken.None);
// Act
var results = await drv.WriteAsync([new WriteRequest("F", true)], CancellationToken.None);
// Assert: the write rejection must NOT be silently swallowed as Good.
var statusCode = results.Single().StatusCode;
statusCode.ShouldNotBe(AbLegacyStatusMapper.Good);
statusCode.ShouldBe(AbLegacyStatusMapper.MapLibplctagStatus(errorTimeout));
}
}