using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Core.Abstractions; namespace ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests.Series; /// /// Issue #271, plan PR F4-d — series-level (would-be integration) coverage of /// cnc_wrunlockparam. Hardware-gated: the FOCAS driver has no public /// simulator (task #222) so the live-controller cases require a real CNC with /// parameter-protect on. The CI lane for this assembly runs the unit-test fakes /// under ; this file is a scaffold that runs /// against a simulator + matching mock_set_password admin endpoint when /// FOCAS_TRUST_WIRE=1. /// /// /// Build-only today: the simulator gate (FOCAS_TRUST_WIRE) skips at runtime so /// CI doesn't need the simulator binary. When the simulator's /// cnc_wrunlockparam + mock_set_password endpoints land /// (docs/v2/implementation/focas-simulator-plan.md) the gated test /// becomes a real round-trip. /// [Trait("Category", "Series")] public sealed class PasswordUnlockTests { [Fact] public void Single_unlock_retry_path_is_documented() { // Build-only scaffold — see FocasUnlockTests for the actual fake-backed // assertion. The integration version of this test (gated on a FOCAS // simulator with mock_set_password) will: // 1. Configure the simulator with password "1234". // 2. Spin up FocasDriver with FocasDeviceOptions.Password = "1234". // 3. Issue a cnc_wrparam against PARAM:1815 — expect Good (unlock applied // on connect). // 4. Use the simulator's admin endpoint to flip the password to "5678" // mid-session (forces EW_PASSWD on the next write). // 5. Issue another write — expect EW_PASSWD on attempt 1, then BadUserAccessDenied // surfaced because the new password doesn't match the cached one. // 6. Reconfigure FocasDeviceOptions.Password = "5678" on a new instance, // issue write — expect Good (unlock applied on first connect). // For now this test merely asserts the type contract; the simulator is // tracked under task #222 + the focas-simulator-plan.md document. typeof(IFocasClient).GetMethod(nameof(IFocasClient.UnlockAsync)) .ShouldNotBeNull(); // Driver-side records the password redaction invariant. var dev = new FocasDeviceOptions("focas://1.2.3.4:8193", Password: "1234"); dev.ToString().ShouldNotContain("1234"); } [Fact(Skip = "Hardware-gated — requires the FOCAS simulator with cnc_wrunlockparam + mock_set_password endpoints (task #222 / focas-simulator-plan.md).")] public Task Live_simulator_unlock_retry_round_trip() { // Body deliberately empty — the [Skip] attribute keeps this off the CI lane. // When the simulator lands, this test materialises a FocasDriver pointed at // the simulator + drives the EW_PASSWD -> unlock -> retry path through real // wire calls. See docs/v2/implementation/focas-simulator-plan.md // § "FOCAS password unlock" for the simulator-side endpoints. return Task.CompletedTask; } }