using Akka.Actor; using Akka.Cluster.Tools.PublishSubscribe; using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Commons.Messages.Redundancy; using ZB.MOM.WW.OtOpcUa.ControlPlane.Redundancy; using ZB.MOM.WW.OtOpcUa.ControlPlane.Tests.Harness; namespace ZB.MOM.WW.OtOpcUa.ControlPlane.Tests; public sealed class RedundancyStateActorTests : ControlPlaneActorTestBase { [Fact(Skip = "Single-node DistributedPubSub bootstrap is flaky in TestKit; tracked as F6.")] public void Self_join_triggers_RedundancyStateChanged_on_pubsub_topic() { // Subscribe a probe to the redundancy-state topic. var probe = CreateTestProbe("redundancy-listener"); var mediator = DistributedPubSub.Get(Sys).Mediator; mediator.Tell(new Subscribe(RedundancyStateActor.Topic, probe.Ref)); probe.ExpectMsg(TimeSpan.FromSeconds(3)); // Start the actor — its PreStart subscribes to cluster events, which immediately fires // a CurrentClusterState replay (InitialStateAsEvents). After the 250ms debounce window, // a RedundancyStateChanged should land on the topic. Sys.ActorOf(RedundancyStateActor.Props(), "redundancy-actor"); var msg = probe.ExpectMsg(TimeSpan.FromSeconds(3)); msg.Nodes.ShouldNotBeNull(); msg.CorrelationId.Value.ShouldNotBe(Guid.Empty); } [Fact(Skip = "Same root cause as the prior test; tracked as F6.")] public void Multiple_back_to_back_events_debounce_to_single_publish() { var probe = CreateTestProbe("dedup-listener"); var mediator = DistributedPubSub.Get(Sys).Mediator; mediator.Tell(new Subscribe(RedundancyStateActor.Topic, probe.Ref)); probe.ExpectMsg(TimeSpan.FromSeconds(3)); Sys.ActorOf(RedundancyStateActor.Props(), "redundancy-debounce"); // First publish should arrive within the debounce window. probe.ExpectMsg(TimeSpan.FromSeconds(3)); // After debounce settles, no more events are fired by a quiescent cluster. probe.ExpectNoMsg(TimeSpan.FromMilliseconds(500)); } }