using System.Collections.Immutable; using Akka.Actor; using Akka.Cluster.Tools.Client; using Akka.Configuration; using Akka.TestKit.Xunit2; using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.ControlPlane.Communication; namespace ZB.MOM.WW.OtOpcUa.ControlPlane.Tests.Communication; /// /// Guards the one non-obvious part of client creation: recreating a client with the same name /// inside a single message handler throws, because Context.Stop is asynchronous and the /// name stays reserved until termination completes. /// public class MeshClusterClientFactoryTests : TestKit { private static readonly Config TestConfig = ConfigurationFactory.ParseString(@" akka.actor.provider = cluster akka.remote.dot-netty.tcp.port = 0 akka.remote.dot-netty.tcp.hostname = 127.0.0.1") .WithFallback(ClusterClientReceptionist.DefaultConfig()); public MeshClusterClientFactoryTests() : base(TestConfig) { } // A real ClusterClient pointed at an unreachable contact is safe in TestKit — it simply retries // the initial-contact rotation and never fails the test. private static ImmutableHashSet UnreachableContact => ImmutableHashSet.Create( ActorPath.Parse("akka.tcp://otopcua@127.0.0.1:65501/system/receptionist")); [Fact] public void Recreating_a_client_in_one_pass_does_not_collide_on_the_actor_name() { var factory = new DefaultMeshClusterClientFactory(); var first = factory.Create(Sys, UnreachableContact); Sys.Stop(first); // Stop is asynchronous: `first`'s name is still reserved right here. Without the generation // suffix this throws InvalidActorNameException — which is precisely what a contact-set // change would do in production, where stop-then-recreate happens in one handler. var second = factory.Create(Sys, UnreachableContact); second.Path.Name.ShouldNotBe(first.Path.Name); } [Fact] public void Every_incarnation_gets_a_distinct_name() { var factory = new DefaultMeshClusterClientFactory(); var names = Enumerable.Range(0, 5) .Select(_ => factory.Create(Sys, UnreachableContact).Path.Name) .ToList(); names.Distinct().Count().ShouldBe(5); } // Contact-set propagation is deliberately NOT asserted here: reading initial contacts back // off a created ClusterClient is not possible, and asserting ClusterClientSettings directly // would test Akka rather than this factory. It is covered where it can actually fail — the // real two-ActorSystem boundary test, which only delivers if the contacts reached the client. }