using Akka.Cluster; using Akka.TestKit.Xunit2; using Microsoft.EntityFrameworkCore; using ZB.MOM.WW.OtOpcUa.Configuration; namespace ZB.MOM.WW.OtOpcUa.ControlPlane.Tests.Harness; /// /// Akka TestKit fixture for ControlPlane actor tests. Provides: /// - A test ActorSystem (xunit2 TestKit) wired with PubSub/Cluster extensions. /// - A fresh in-memory per harness instance. /// - An the actors can hold. /// /// One harness per test fact — InMemory provider gives strong isolation when the database /// name is unique (random Guid). /// public abstract class ControlPlaneActorTestBase : TestKit { protected static string AkkaTestHocon => @" akka { loglevel = ""WARNING"" extensions = [ ""Akka.Cluster.Tools.PublishSubscribe.DistributedPubSubExtensionProvider, Akka.Cluster.Tools"" ] actor { provider = ""Akka.Cluster.ClusterActorRefProvider, Akka.Cluster"" } remote.dot-netty.tcp { hostname = ""127.0.0.1"" port = 0 } cluster { seed-nodes = [] roles = [""admin""] min-nr-of-members = 1 run-coordinated-shutdown-when-down = off pub-sub.role = """" } }"; protected ControlPlaneActorTestBase() : base(AkkaTestHocon) { // Self-join so the cluster transitions to Up and DistributedPubSub forms. var cluster = Akka.Cluster.Cluster.Get(Sys); cluster.Join(cluster.SelfAddress); AwaitCondition(() => cluster.State.Members.Any(m => m.Status == MemberStatus.Up), TimeSpan.FromSeconds(5)); } protected static IDbContextFactory NewInMemoryDbFactory(string? dbName = null) { dbName ??= Guid.NewGuid().ToString("N"); return new InMemoryConfigDbFactory(dbName); } private sealed class InMemoryConfigDbFactory(string dbName) : IDbContextFactory { public OtOpcUaConfigDbContext CreateDbContext() { var opts = new DbContextOptionsBuilder() .UseInMemoryDatabase(dbName) .Options; return new OtOpcUaConfigDbContext(opts); } } }