Auto: s7-c5 — pre-flight PUT/GET enablement test

Closes #298
This commit is contained in:
Joseph Doherty
2026-04-26 01:31:48 -04:00
parent 4bc8aa2478
commit 64a11ef285
9 changed files with 625 additions and 3 deletions

View File

@@ -0,0 +1,84 @@
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
namespace ZB.MOM.WW.OtOpcUa.Driver.S7.IntegrationTests.S7_1500;
/// <summary>
/// PR-S7-C5 — integration coverage for the post-<c>OpenAsync</c> pre-flight
/// PUT/GET enablement probe. Snap7 always allows reads (no PUT/GET gating
/// in the simulator), so the integration scope is limited to the happy
/// path: pre-flight succeeds against the seeded MW0/DBW0 fingerprint and
/// the driver reaches <see cref="DriverState.Healthy"/>. The "PUT/GET
/// disabled" failure path is unit-tested via
/// <c>S7PreflightClassifier</c> and documented as a follow-up live-firmware
/// test in <c>docs/drivers/S7-Test-Fixture.md</c>.
/// </summary>
[Collection(Snap7ServerCollection.Name)]
[Trait("Category", "Integration")]
[Trait("Device", "S7_1500")]
public sealed class S7_1500PreflightTests(Snap7ServerFixture sim)
{
[Fact]
public async Task Driver_preflight_passes_when_probe_address_seeded()
{
if (sim.SkipReason is not null) Assert.Skip(sim.SkipReason);
// Use the standard S7-1500 profile — DB1.DBW0 / DB1.DBW10 / etc are seeded
// by the snap7 profile so the default MW0 probe (or DB1.DBW0 fallback)
// exists at read time.
var options = S7_1500Profile.BuildOptions(sim.Host, sim.Port);
// Override the probe loop knob set by S7_1500Profile: the loop stays
// disabled, but Probe.ProbeAddress + SkipPreflight are what RunPreflightAsync
// consults. ProbeAddress defaults to MW0 which python-snap7 zeros at startup;
// any successful read (zeros included) satisfies the pre-flight.
var preflightOptions = new S7DriverOptions
{
Host = options.Host,
Port = options.Port,
CpuType = options.CpuType,
Rack = options.Rack,
Slot = options.Slot,
Timeout = options.Timeout,
Tags = options.Tags,
// ProbeAddress = "MW0" (default); SkipPreflight = false (default).
// Background probe loop disabled to avoid mailbox contention with the test.
Probe = new S7ProbeOptions { Enabled = false },
};
await using var drv = new S7Driver(preflightOptions, driverInstanceId: "s7-preflight-pass");
await drv.InitializeAsync("{}", TestContext.Current.CancellationToken);
// If pre-flight tripped the typed exception, InitializeAsync would have thrown
// before reaching this line. Healthy state proves the probe succeeded.
drv.GetHealth().State.ShouldBe(DriverState.Healthy,
"pre-flight probe must succeed against the seeded snap7 fingerprint");
}
[Fact]
public async Task Driver_preflight_skipped_when_SkipPreflight_set()
{
if (sim.SkipReason is not null) Assert.Skip(sim.SkipReason);
// Skipping the pre-flight is opt-in. The driver must still reach Healthy state
// because the connect path itself succeeds; the only thing different is that no
// probe read fires before _health flips.
var options = S7_1500Profile.BuildOptions(sim.Host, sim.Port);
var skipOptions = new S7DriverOptions
{
Host = options.Host,
Port = options.Port,
CpuType = options.CpuType,
Rack = options.Rack,
Slot = options.Slot,
Timeout = options.Timeout,
Tags = options.Tags,
Probe = new S7ProbeOptions { Enabled = false, SkipPreflight = true },
};
await using var drv = new S7Driver(skipOptions, driverInstanceId: "s7-preflight-skipped");
await drv.InitializeAsync("{}", TestContext.Current.CancellationToken);
drv.GetHealth().State.ShouldBe(DriverState.Healthy);
}
}