using System.Runtime.CompilerServices; using Shouldly; using Xunit; namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests.HardwareGate; /// /// Offline guard for the hardware-gate folder — deliberately NOT env-gated, so it runs in every build. The folder sits /// outside the read-only Differential guard because one test in it writes; this pins how far that goes: /// only may name a write path, its allow-list is exactly the two approved /// (Z-35162, tag) pairs, and its explicit opt-in and differential-run refusal are present. /// public sealed class HardwareGateGuardTests { [Fact] public void Only_the_string_write_gate_names_a_write_path() { // Built by concatenation so this file's own text never matches itself. string[] forbidden = [ "Write" + "Async", "Encode" + "Value", "WriteBit" + "InDInt", "Write" + "Tag", "Write" + "Request", "AbCip" + "Driver", ]; var sources = Directory.GetFiles(Path.GetDirectoryName(ThisFile())!, "*.cs"); sources.ShouldContain(f => Path.GetFileName(f) == nameof(StringWriteGateTests) + ".cs"); sources.ShouldContain(f => Path.GetFileName(f) == nameof(IdleConnectionEvidenceTests) + ".cs"); var hits = (from f in sources where Path.GetFileName(f) != nameof(StringWriteGateTests) + ".cs" let text = File.ReadAllText(f) from token in forbidden where text.Contains(token, StringComparison.Ordinal) select $"{Path.GetFileName(f)} names {token}").ToList(); hits.ShouldBeEmpty(); // Positive control: the scan does see the one file that is allowed to write. var writer = File.ReadAllText(sources.Single(f => Path.GetFileName(f) == nameof(StringWriteGateTests) + ".cs")); writer.ShouldContain(forbidden[0]); } [Fact] public void The_write_allow_list_is_exactly_the_two_approved_pairs_on_Z35162() { StringWriteGateTests.Approved.ShouldBe( [ ("10.209.70.160", "Program:ES03_TemperatureControl._StrTmp"), ("10.209.70.160", "Program:ES03_TemperatureControl._Prefix"), ]); StringWriteGateTests.IsApproved("ab://10.209.70.160/1,0", "Program:ES03_TemperatureControl._StrTmp").ShouldBeTrue(); StringWriteGateTests.IsApproved("ab://10.209.70.160:44818/1,0", "Program:ES03_TemperatureControl._Prefix").ShouldBeTrue(); // Same tag on any other controller, another tag on Z-35162, or a malformed gateway: refused. StringWriteGateTests.IsApproved("ab://10.209.70.161/1,0", "Program:ES03_TemperatureControl._StrTmp").ShouldBeFalse(); StringWriteGateTests.IsApproved("ab://10.100.0.35/1,0", "Program:ES03_TemperatureControl._StrTmp").ShouldBeFalse(); StringWriteGateTests.IsApproved("ab://10.209.70.160/1,0", "test2").ShouldBeFalse(); StringWriteGateTests.IsApproved("ab://10.209.70.160/1,0", "Program:UN01_Press._Prefix").ShouldBeFalse(); StringWriteGateTests.IsApproved("10.209.70.160", "Program:ES03_TemperatureControl._StrTmp").ShouldBeFalse(); } [Fact] public void The_string_write_gate_keeps_its_opt_in_and_refuses_during_a_differential_run() { var text = File.ReadAllText(Path.Combine(Path.GetDirectoryName(ThisFile())!, nameof(StringWriteGateTests) + ".cs")); text.ShouldContain("""GetEnvironmentVariable("AB_HW_WRITE_APPROVED") != "1") Assert.Skip("""); text.ShouldContain("GetEnvironmentVariable(IntegrationWriteGate.DifferentialEndpointVar) is { Length: > 0 })"); text.ShouldContain("if (!IsApproved(gateway, tagPath))"); IntegrationWriteGate.DifferentialEndpointVar.ShouldBe("AB_DIFF_ENDPOINT"); } private static string ThisFile([CallerFilePath] string path = "") => path; }