using Shouldly; using Xunit; namespace ZB.MOM.WW.OtOpcUa.Driver.S7.Tests; /// /// Unit tests for 's IReadable/IWritable surface /// that don't require a live PLC — covers error paths (not-initialized, unknown tag, /// read-only write rejection, unsupported data types). Wire-level round-trip tests /// against a live S7 or a mock-server land in a follow-up PR since S7.Net doesn't ship /// an in-process fake and an adequate mock is non-trivial. /// [Trait("Category", "Unit")] public sealed class S7DriverReadWriteTests { [Fact] public async Task Initialize_rejects_invalid_tag_address_and_fails_fast() { // Bad address at init time must throw; the alternative (deferring the parse to the // first read) would surface the config bug as BadInternalError on every subsequent // Read which is impossible for an operator to diagnose from the OPC UA client. var opts = new S7DriverOptions { Host = "192.0.2.1", // reserved — will never complete TCP handshake Timeout = TimeSpan.FromMilliseconds(250), Tags = [new S7TagDefinition("BadTag", "NOT-AN-S7-ADDRESS", S7DataType.Int16)], }; using var drv = new S7Driver(opts, "s7-bad-tag"); // Either the TCP connect fails first (Exception) or the parser fails (FormatException) // — both are acceptable since both are init-time fail-fast. What matters is that we // don't return a "healthy" driver with a latent bad tag. await Should.ThrowAsync(async () => await drv.InitializeAsync("{}", TestContext.Current.CancellationToken)); } [Fact] public async Task ReadAsync_without_initialize_throws_InvalidOperationException() { using var drv = new S7Driver(new S7DriverOptions { Host = "192.0.2.1" }, "s7-uninit"); await Should.ThrowAsync(async () => await drv.ReadAsync(["Any"], TestContext.Current.CancellationToken)); } [Fact] public async Task WriteAsync_without_initialize_throws_InvalidOperationException() { using var drv = new S7Driver(new S7DriverOptions { Host = "192.0.2.1" }, "s7-uninit"); await Should.ThrowAsync(async () => await drv.WriteAsync( [new(FullReference: "Any", Value: (short)0)], TestContext.Current.CancellationToken)); } }