using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Driver.AbCip; namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests; /// /// Pure-unit tests for the profile → CLI arg composition. Runs without ab_server /// on PATH so CI without the binary still exercises these contracts + catches any /// profile-definition drift (e.g. a typo in --plc mapping would silently make the /// simulator boot with the wrong family). /// [Trait("Category", "Unit")] public sealed class AbServerProfileTests { [Fact] public void BuildCliArgs_Emits_Port_And_Plc_And_TagFlags() { var profile = new AbServerProfile( Family: AbCipPlcFamily.ControlLogix, AbServerPlcArg: "controllogix", SeedTags: new AbServerSeedTag[] { new("A", "DINT"), new("B", "REAL"), }, Notes: "test"); profile.BuildCliArgs(44818).ShouldBe("--port 44818 --plc controllogix --tag A:DINT --tag B:REAL"); } [Fact] public void BuildCliArgs_NoSeedTags_Emits_Just_Port_And_Plc() { var profile = new AbServerProfile( AbCipPlcFamily.ControlLogix, "controllogix", [], "empty"); profile.BuildCliArgs(5000).ShouldBe("--port 5000 --plc controllogix"); } [Fact] public void AbServerSeedTag_ArraySize_FormatsAsThirdSegment() { new AbServerSeedTag("TestArray", "DINT", ArraySize: 16) .ToCliSpec().ShouldBe("TestArray:DINT:16"); } [Fact] public void AbServerSeedTag_NoArraySize_TwoSegments() { new AbServerSeedTag("TestScalar", "REAL") .ToCliSpec().ShouldBe("TestScalar:REAL"); } [Theory] [InlineData(AbCipPlcFamily.ControlLogix, "controllogix")] [InlineData(AbCipPlcFamily.CompactLogix, "compactlogix")] [InlineData(AbCipPlcFamily.Micro800, "controllogix")] // falls back — ab_server lacks dedicated mode [InlineData(AbCipPlcFamily.GuardLogix, "controllogix")] // falls back — ab_server lacks safety subsystem public void KnownProfiles_ForFamily_Returns_Expected_AbServerPlcArg(AbCipPlcFamily family, string expected) { KnownProfiles.ForFamily(family).AbServerPlcArg.ShouldBe(expected); } [Fact] public void KnownProfiles_All_Covers_Every_Family() { var covered = KnownProfiles.All.Select(p => p.Family).ToHashSet(); foreach (var family in Enum.GetValues()) covered.ShouldContain(family, $"Family {family} is missing a KnownProfiles entry."); } [Fact] public void KnownProfiles_ControlLogix_Includes_AllAtomicTypes() { var tags = KnownProfiles.ControlLogix.SeedTags.Select(t => t.AbServerType).ToHashSet(); tags.ShouldContain("DINT"); tags.ShouldContain("REAL"); tags.ShouldContain("BOOL"); tags.ShouldContain("SINT"); tags.ShouldContain("STRING"); } [Fact] public void KnownProfiles_GuardLogix_SeedsSafetySuffixedTag() { KnownProfiles.GuardLogix.SeedTags .ShouldContain(t => t.Name.EndsWith("_S"), "GuardLogix profile must seed at least one _S-suffixed tag for safety-classification coverage."); } }