using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.AdminUI.Uns.TagEditors; using ZB.MOM.WW.OtOpcUa.Core.Abstractions; namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Uns; /// /// Parity guards for the per-driver CSV column dispatch map (deferment.md G-5). It was /// already a testable structure — unlike the two Razor @switch blocks that needed extracting /// first — so nothing but the tests was missing. /// /// /// The browse-commit seam (G-6) is guarded next door, not here (Gitea #558). This class used to /// also hold Browse_commit_falls_back_to_the_generic_address_key_for_exactly_the_non_browsable_drivers, /// over the same RawBrowseCommitMapper.BuildTagConfig seam /// now enumerates. Two guards over one seam means two /// hand-authored lists of "drivers that legitimately reach the generic key", and the pair can only /// drift — this one's was additionally MISNAMED (NonBrowsableDriverTypes really meant "types /// that produce the generic key": AbLegacy is genuinely non-browsable yet correctly absent, /// because it has a typed branch). The surviving list is /// RawBrowseCommitMapperParityTests.GenericKeyExemptions, which is named for what it holds, /// carries a written reason per entry, and is checked against a derived browsable set rather /// than a second hand-authored one. /// [Trait("Category", "Unit")] public sealed class DriverDispatchMapParityTests { /// /// Driver types with a typed tag editor but no CSV map, so import/export silently degrades them to /// the raw TagConfigJson fallback. Empty — every editor-backed driver now has typed columns. /// Deliberately expressed as "has a typed editor ⇒ has typed CSV columns" rather than /// "every driver type", because a driver with no typed editor has nothing to project into columns. /// Tying the two maps together is what makes this catch the next omission. /// [Fact] public void Every_driver_with_a_typed_tag_editor_has_typed_csv_columns() { var missing = DriverTypeNames.All .Where(t => TagConfigEditorMap.Resolve(t) is not null && CsvColumnMap.Resolve(t) is null) .OrderBy(t => t, StringComparer.Ordinal) .ToList(); missing.ShouldBeEmpty( $"these driver types have a typed tag editor but no CsvColumnMap entry, so CSV import/export " + $"drops them to the raw TagConfigJson fallback: {string.Join(", ", missing)}"); } /// A CSV map for a driver type that no longer exists is a stale entry. [Fact] public void Every_csv_map_targets_a_declared_driver_type() { var declared = DriverTypeNames.All.ToHashSet(StringComparer.OrdinalIgnoreCase); var stale = CsvColumnMap.All .Select(m => m.DriverType) .Where(t => !declared.Contains(t)) .OrderBy(t => t, StringComparer.Ordinal) .ToList(); stale.ShouldBeEmpty($"CsvColumnMap has entries for undeclared driver types: {string.Join(", ", stale)}"); } }