Files
lmxopcua/tests/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.IntegrationTests/AbLegacyPerDeviceTimeoutTests.cs
2026-04-26 03:32:45 -04:00

69 lines
3.3 KiB
C#

using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
using ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.PlcFamilies;
namespace ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.IntegrationTests;
/// <summary>
/// PR 9 — per-device timeout integration scaffold. Build-only at PR 9 time: the ab_server
/// PCCC simulator answers in &lt;100 ms locally so a 500 ms per-device timeout doesn't
/// normally trip. Either an <c>iptables --delay</c> sidecar or a <c>tc qdisc</c> netem
/// filter must be wired up first; until then the test asserts that a <i>generous</i>
/// per-device timeout still completes successfully (the precedence path itself is
/// exercised), with the slow-path failure case expressed in unit tests via
/// <see cref="FakeAbLegacyTag"/>.
/// </summary>
[Collection(AbLegacyServerCollection.Name)]
[Trait("Category", "Integration")]
[Trait("Simulator", "ab_server-PCCC")]
public sealed class AbLegacyPerDeviceTimeoutTests(AbLegacyServerFixture sim)
{
[AbLegacyFact]
public async Task Per_device_Timeout_override_flows_into_runtime_against_ab_server()
{
if (sim.SkipReason is not null) Assert.Skip(sim.SkipReason);
var deviceUri = $"ab://{sim.Host}:{sim.Port}/{sim.CipPath}";
await using var drv = new AbLegacyDriver(new AbLegacyDriverOptions
{
// Driver-wide tight 500 ms; per-device override gives the simulator 5 s headroom
// to demonstrate the precedence rule in a wire-level setting.
Timeout = TimeSpan.FromMilliseconds(500),
Devices = [new AbLegacyDeviceOptions(
deviceUri,
AbLegacyPlcFamily.Slc500,
Timeout: TimeSpan.FromSeconds(5))],
Tags = [new AbLegacyTagDefinition(
Name: "IntCounter",
DeviceHostAddress: deviceUri,
Address: "N7:0",
DataType: AbLegacyDataType.Int)],
Probe = new AbLegacyProbeOptions { Enabled = false },
}, driverInstanceId: "ablegacy-pr9-timeout");
await drv.InitializeAsync("{}", TestContext.Current.CancellationToken);
var snapshots = await drv.ReadAsync(["IntCounter"], TestContext.Current.CancellationToken);
// Per-device override picked up; the read against the simulator succeeds because the
// 5 s per-device cap supersedes the otherwise-too-tight 500 ms driver-wide default.
snapshots.Single().StatusCode.ShouldBe(AbLegacyStatusMapper.Good);
}
/// <summary>
/// Skeleton for the inverse — slow-link (<c>tc qdisc</c> / <c>iptables --delay</c>) +
/// tight per-device timeout. Skipped pending the netem sidecar work tracked in
/// <see href="../Docker/README.md#per-device-timeout-fixture">Docker/README.md</see>.
/// </summary>
[AbLegacyFact(Skip = "Pending netem / iptables-delay sidecar — see Docker/README.md")]
public Task Per_device_Timeout_below_simulated_delay_surfaces_BadCommunicationError()
{
// Future shape:
// docker compose --profile slc500-slow up -d (adds netem qdisc on the egress)
// override Timeout: TimeSpan.FromMilliseconds(100)
// ReadAsync ⇒ snapshots.Single().StatusCode == BadCommunicationError
// while a sibling device (no override → 5 s) keeps reading Good.
return Task.CompletedTask;
}
}