using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared; namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests; /// /// #504 — , the null/short-safe replacement for the /// @value[..N] range-operator slices the Admin UI used to abbreviate hashes and thumbprints. /// /// /// The bug being pinned: a range operator over an unvalidated DB string throws /// out of BuildRenderTree, which is unhandled /// in Blazor and takes the WHOLE page to an HTTP 500 — not just the offending row. Nothing enforces a /// minimum length at the schema, entity, or render layer, so the short-input cases below are the ones /// that matter; the happy path is almost incidental. /// [Trait("Category", "Unit")] public sealed class DisplayTextTests { /// A value longer than the budget is truncated and marked with an ellipsis — the normal /// case for a 64-char SHA-256. [Fact] public void A_long_value_is_truncated_and_marked() { DisplayText.Abbreviate(new string('a', 64), 12).ShouldBe(new string('a', 12) + "…"); } /// The regression: a value SHORTER than the budget must render, not throw. This is exactly /// what a hand-inserted / API-authored SourceHash like h1 hits. [Theory] [InlineData("h1", 12)] [InlineData("h-abs-hr200", 12)] // 11 chars — one short of the old slice, the original crash [InlineData("abc", 16)] [InlineData("x", 64)] public void A_value_shorter_than_the_budget_renders_verbatim(string value, int maxLength) { DisplayText.Abbreviate(value, maxLength).ShouldBe(value); } /// No ellipsis when nothing was dropped — the display must never imply text that is not /// there. (The old markup appended "…" unconditionally, outside the slice.) [Fact] public void A_short_value_gets_no_ellipsis() { DisplayText.Abbreviate("h1", 12).ShouldNotContain("…"); } /// A value exactly at the budget is the boundary — it fits, so it is neither truncated nor /// marked. [Fact] public void A_value_exactly_at_the_budget_is_untouched() { DisplayText.Abbreviate("123456789012", 12).ShouldBe("123456789012"); } /// Null / blank render as the Admin UI's em-dash placeholder rather than throwing or /// producing a stray ellipsis. [Theory] [InlineData(null)] [InlineData("")] [InlineData(" ")] public void A_null_or_blank_value_renders_the_placeholder(string? value) { DisplayText.Abbreviate(value, 12).ShouldBe(DisplayText.Empty); } /// A nonsensical budget is clamped rather than allowed to throw — a display bug must not /// become a page crash, which is the whole point of this helper. [Theory] [InlineData(0)] [InlineData(-1)] [InlineData(int.MinValue)] public void A_non_positive_budget_is_clamped_not_thrown(int maxLength) { DisplayText.Abbreviate("abcdef", maxLength).ShouldBe("a…"); } /// Whatever the inputs, it never throws — the property that keeps a bad row from killing a /// page. Includes the shapes that broke the old code. [Fact] public void It_never_throws_for_any_combination() { string?[] values = [null, "", " ", "h1", "h-abs-hr200", new string('f', 64), "…"]; int[] budgets = [int.MinValue, -1, 0, 1, 12, 16, 64, int.MaxValue]; foreach (var value in values) { foreach (var budget in budgets) { Should.NotThrow(() => DisplayText.Abbreviate(value, budget)); } } } }