fix(cluster): gossiped-role predicates compare case-insensitively — a miscased peer no longer un-elects the Primary

This commit is contained in:
Joseph Doherty
2026-08-20 10:04:16 -04:00
parent bcec88ef70
commit e46c1db5e5
12 changed files with 370 additions and 11 deletions
@@ -0,0 +1,54 @@
using Shouldly;
using Xunit;
namespace ZB.MOM.WW.OtOpcUa.Cluster.Tests;
/// <summary>
/// R3-04 (01/C-9, choice 6): <see cref="RoleParser.GossipedRolesHave"/> is defence in depth for a
/// PEER running an older build that still gossips a miscased plain role — a locally-authored node
/// can no longer gossip one at all once <see cref="EffectiveRoles"/> (T1) is wired in, but Akka
/// compares gossiped role names ordinally, so a rolling-upgrade window can still show a
/// mixed-case peer.
/// </summary>
public sealed class GossipedRoleMatchTests
{
[Fact]
public void Matches_the_canonical_spelling()
{
RoleParser.GossipedRolesHave(["driver"], "driver").ShouldBeTrue();
}
[Theory]
[InlineData("Driver")]
[InlineData("DRIVER")]
[InlineData("driVer")]
public void Matches_a_miscased_gossiped_role(string gossiped)
{
RoleParser.GossipedRolesHave([gossiped], "driver").ShouldBeTrue();
}
[Fact]
public void Matches_a_miscased_cluster_role()
{
RoleParser.GossipedRolesHave(["cluster-SITE-A"], "cluster-site-a").ShouldBeTrue();
RoleParser.GossipedRolesHave(["Cluster-Site-A"], "cluster-SITE-A").ShouldBeTrue();
}
[Fact]
public void Does_not_match_a_different_role()
{
RoleParser.GossipedRolesHave(["admin"], "driver").ShouldBeFalse();
}
[Fact]
public void Null_gossiped_roles_is_false()
{
RoleParser.GossipedRolesHave(null, "driver").ShouldBeFalse();
}
[Fact]
public void Empty_gossiped_roles_is_false()
{
RoleParser.GossipedRolesHave([], "driver").ShouldBeFalse();
}
}