using Shouldly; using Xunit; namespace ZB.MOM.WW.OtOpcUa.Cluster.Tests; public sealed class RoleParserTests { /// Verifies that empty input yields an empty array. /// The raw input string to test (null, empty, or whitespace). [Theory] [InlineData(null)] [InlineData("")] [InlineData(" ")] public void Empty_input_yields_empty_array(string? raw) { RoleParser.Parse(raw).ShouldBeEmpty(); } /// Verifies that a single role 'admin' is parsed correctly. [Fact] public void Single_role_admin() { RoleParser.Parse("admin").ShouldBe(new[] { "admin" }); } /// Verifies that two roles separated by comma are parsed correctly. [Fact] public void Two_roles_csv() { RoleParser.Parse("admin,driver").ShouldBe(new[] { "admin", "driver" }); } /// Verifies that parser tolerates surrounding whitespace. [Fact] public void Whitespace_tolerant() { RoleParser.Parse(" admin , driver ").ShouldBe(new[] { "admin", "driver" }); } /// Verifies that parser is case-insensitive and normalizes to lowercase. [Fact] public void Case_insensitive_normalizes_to_lower() { RoleParser.Parse("ADMIN,Driver").ShouldBe(new[] { "admin", "driver" }); } /// Verifies that duplicate roles are removed. [Fact] public void Duplicate_roles_deduped() { RoleParser.Parse("admin,admin,driver").ShouldBe(new[] { "admin", "driver" }); } /// Verifies that parser throws for unknown roles. [Fact] public void Unknown_role_throws() { Should.Throw(() => RoleParser.Parse("admin,master")); } /// Verifies that a single cluster-scoped role is accepted. [Theory] [InlineData("cluster-MAIN")] [InlineData("cluster-SITE-A")] public void Cluster_role_accepted(string role) { RoleParser.Parse(role).ShouldBe(new[] { role.ToLowerInvariant() }); } /// Verifies that cluster roles mix freely with the fixed roles. [Fact] public void Fixed_and_cluster_roles_mix() { RoleParser.Parse("driver,cluster-SITE-A").ShouldBe(new[] { "driver", "cluster-site-a" }); } /// Verifies that a cluster role with an empty ClusterId ("cluster-") is rejected. [Fact] public void Empty_cluster_id_throws() { Should.Throw(() => RoleParser.Parse("cluster-")); } /// Verifies IsClusterRole returns true for valid cluster roles and false otherwise. [Theory] [InlineData("cluster-MAIN", true)] [InlineData("cluster-SITE-A", true)] [InlineData("cluster-", false)] [InlineData("driver", false)] [InlineData("admin", false)] [InlineData("clusterx", false)] public void IsClusterRole_classifies_correctly(string role, bool expected) { RoleParser.IsClusterRole(role).ShouldBe(expected); } /// Verifies ClusterIdFromRole extracts the ClusterId suffix. [Fact] public void ClusterIdFromRole_extracts_suffix() { RoleParser.ClusterIdFromRole("cluster-SITE-A").ShouldBe("SITE-A"); } /// Verifies the three well-known role constants match the historical literal values. [Fact] public void Well_known_role_constants() { RoleParser.Admin.ShouldBe("admin"); RoleParser.Driver.ShouldBe("driver"); RoleParser.Dev.ShouldBe("dev"); RoleParser.ClusterRolePrefix.ShouldBe("cluster-"); } }