using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Core.Abstractions; namespace ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests.Series; /// /// Issue #272, plan PR F5-a — series-level (would-be integration) coverage of /// Production/LastCycleSeconds + Production/LastCycleStartUtc. /// The derivation is pure (no new wire calls — see /// docs/v2/focas-deployment.md § "Derived telemetry") so the /// real-simulator test asserts the existing cnc_rdparam(6711) + /// cycle-timer poll that F1-b already emits drives both fields end-to-end. /// /// /// Build-only today: focas-mock has not yet shipped (tracked under /// docs/v2/implementation/focas-simulator-plan.md § "Cycle-time per /// part / last cycle delta — F5-a"). The unit-test coverage in /// exercises every same-process invariant /// of the derivation. The gated test below materialises the /// SimulateCycleCompletionAsync + admin-endpoint contract once the /// simulator binary lands. /// [Trait("Category", "Series")] public sealed class CycleDeltaTests { [Fact] public void Derivation_contract_is_documented() { // Build-only scaffold — see FocasCycleDeltaTests for the actual fake-backed // assertion. The integration version of this test (gated on a focas-mock // simulator with a SimulateCycleCompletionAsync admin endpoint) will: // 1. Spin up FocasDriver pointed at the simulator with parts=5, timer=10s. // 2. Wait for the first probe tick (baseline established). // 3. Call simulator.SimulateCycleCompletionAsync(profile, parts: 6, timer: 18s). // 4. Wait for the next probe tick to refresh the production cache. // 5. Read Production/LastCycleSeconds + Production/LastCycleStartUtc through // the OPC UA surface; assert delta == 8.0 and the timestamp is now - 8s. // The driver-side derivation already locks the contract in unit tests; this // scaffold pins the simulator-side contract for the focas-mock implementor. var info = typeof(FocasDriver).GetMethod( nameof(FocasDriver.UpdateCycleDerivation), System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); info.ShouldNotBeNull(); } [Fact(Skip = "Hardware-gated — requires focas-mock with the SimulateCycleCompletionAsync admin endpoint (focas-simulator-plan.md § 'Cycle-time per part / last cycle delta — F5-a').")] public Task Live_simulator_cycle_completion_round_trip() { // Body deliberately empty — the [Skip] attribute keeps this off the CI // lane. When focas-mock lands the SimulateCycleCompletionAsync helper + // matching admin endpoint, this test materialises a FocasDriver pointed // at the simulator + drives the parts-count 5 -> 6 transition through real // wire calls. return Task.CompletedTask; } }