using Akka.Actor; using Akka.TestKit.Xunit2; using ScadaLink.Commons.Messages.Communication; using ScadaLink.Commons.Messages.Deployment; using ScadaLink.Commons.Messages.DebugView; using ScadaLink.Commons.Messages.Health; using ScadaLink.Communication.Actors; namespace ScadaLink.Communication.Tests; /// /// WP-4: Tests for CentralCommunicationActor message routing. /// WP-5: Tests for connection failure and failover handling. /// public class CentralCommunicationActorTests : TestKit { public CentralCommunicationActorTests() : base(@"akka.loglevel = DEBUG") { } [Fact] public void RegisterSite_AllowsMessageRouting() { var centralActor = Sys.ActorOf(Props.Create(() => new CentralCommunicationActor())); // Register a site pointing to the test probe var probe = CreateTestProbe(); centralActor.Tell(new RegisterSite("site1", probe.Ref.Path.ToString())); // Send a message to the site var command = new DeployInstanceCommand( "dep1", "inst1", "hash1", "{}", "admin", DateTimeOffset.UtcNow); centralActor.Tell(new SiteEnvelope("site1", command)); // The probe should receive the inner message (not the envelope) probe.ExpectMsg(msg => msg.DeploymentId == "dep1"); } [Fact] public void UnregisteredSite_MessageIsDropped() { var centralActor = Sys.ActorOf(Props.Create(() => new CentralCommunicationActor())); var command = new DeployInstanceCommand( "dep1", "inst1", "hash1", "{}", "admin", DateTimeOffset.UtcNow); centralActor.Tell(new SiteEnvelope("unknown-site", command)); // No crash, no response — the ask will timeout on the caller side ExpectNoMsg(TimeSpan.FromMilliseconds(200)); } [Fact] public void ConnectionLost_DebugStreamsKilled() { var centralActor = Sys.ActorOf(Props.Create(() => new CentralCommunicationActor())); var siteProbe = CreateTestProbe(); // Register site centralActor.Tell(new RegisterSite("site1", siteProbe.Ref.Path.ToString())); // Subscribe to debug view (this tracks the subscription) var subscriberProbe = CreateTestProbe(); var subRequest = new SubscribeDebugViewRequest("inst1", "corr-123"); centralActor.Tell(new SiteEnvelope("site1", subRequest), subscriberProbe.Ref); // Simulate site disconnection centralActor.Tell(new ConnectionStateChanged("site1", false, DateTimeOffset.UtcNow)); // The subscriber should receive a DebugStreamTerminated notification subscriberProbe.ExpectMsg( msg => msg.SiteId == "site1" && msg.CorrelationId == "corr-123"); } [Fact] public void ConnectionLost_SiteSelectionRemoved() { var centralActor = Sys.ActorOf(Props.Create(() => new CentralCommunicationActor())); var siteProbe = CreateTestProbe(); centralActor.Tell(new RegisterSite("site1", siteProbe.Ref.Path.ToString())); // Disconnect centralActor.Tell(new ConnectionStateChanged("site1", false, DateTimeOffset.UtcNow)); // Sending a message to the disconnected site should be dropped centralActor.Tell(new SiteEnvelope("site1", new DeployInstanceCommand("dep2", "inst2", "hash2", "{}", "admin", DateTimeOffset.UtcNow))); siteProbe.ExpectNoMsg(TimeSpan.FromMilliseconds(200)); } [Fact] public void Heartbeat_ForwardedToParent() { var parentProbe = CreateTestProbe(); var centralActor = parentProbe.ChildActorOf( Props.Create(() => new CentralCommunicationActor())); var heartbeat = new HeartbeatMessage("site1", "host1", true, DateTimeOffset.UtcNow); centralActor.Tell(heartbeat); parentProbe.ExpectMsg(msg => msg.SiteId == "site1"); } }