using Akka.Cluster.Hosting; using Akka.Cluster.Hosting.SBR; using Shouldly; using Xunit; namespace ZB.MOM.WW.OtOpcUa.Cluster.Tests; /// /// Guards the split-brain-resolver ACTIVATION (arch-review 03/S1). The split-brain-resolver /// HOCON block is inert on its own; the resolver only runs when the typed /// is set. These tests assert /// registers it, so a future refactor /// cannot silently drop back to Akka's default NoDowning (the exact "built-but-never-wired" failure /// mode this finding described). /// public sealed class SplitBrainResolverActivationTests { private static AkkaClusterOptions SampleOptions() => new() { SeedNodes = new[] { "akka.tcp://otopcua@127.0.0.1:4053" }, Roles = new[] { "admin", "driver" }, }; /// Verifies the cluster options carry a split-brain resolver (provider gets activated). [Fact] public void BuildClusterOptions_activates_a_split_brain_resolver() { var options = ServiceCollectionExtensions.BuildClusterOptions(SampleOptions()); options.SplitBrainResolver.ShouldNotBeNull( "SplitBrainResolver must be set — without it the cluster runs default NoDowning and never fails over on a hard crash."); } /// Verifies the resolver is keep-oldest with down-if-alone (correct for a 2-node pair). [Fact] public void BuildClusterOptions_uses_keep_oldest_with_down_if_alone() { var options = ServiceCollectionExtensions.BuildClusterOptions(SampleOptions()); var keepOldest = options.SplitBrainResolver.ShouldBeOfType(); keepOldest.DownIfAlone.ShouldBe(true); } /// Verifies seed nodes and roles are still threaded through unchanged. [Fact] public void BuildClusterOptions_preserves_seed_nodes_and_roles() { var input = SampleOptions(); var options = ServiceCollectionExtensions.BuildClusterOptions(input); options.SeedNodes.ShouldBe(input.SeedNodes); options.Roles.ShouldBe(input.Roles); } }