using Akka.Actor; using Akka.TestKit.Xunit2; using Microsoft.Extensions.DependencyInjection; using NSubstitute; using ZB.MOM.WW.ScadaBridge.Commons.Entities.Sites; using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories; using ZB.MOM.WW.ScadaBridge.Commons.Messages.Health; using ZB.MOM.WW.ScadaBridge.Commons.Types; using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums; using ZB.MOM.WW.ScadaBridge.Communication.Actors; using ZB.MOM.WW.ScadaBridge.HealthMonitoring; namespace ZB.MOM.WW.ScadaBridge.Communication.Tests; /// /// Review 01 [Medium]: site→central health reports were fire-and-forget, so a lost /// report was invisible to the sender. These tests pin the end-to-end ack: the site actor /// always replies to the sender (even when the transport cannot forward — a fail-loud failure, /// not silence), and the central actor processes + acks a report it receives. /// public class HealthReportAckTests : TestKit { private readonly CommunicationOptions _options = new(); private static SiteHealthReport SampleReport(long seq, string siteId = "site-a") => new(siteId, seq, DateTimeOffset.UtcNow, new Dictionary(), new Dictionary(), 0, 0, new Dictionary(), 0, 0, 0, 0); [Fact] public void SiteCommunicationActor_NoTransport_RepliesFailure_NotSilence() { // With no transport injected the actor falls back to the fail-loud // NoOpCentralTransport, which answers a Status.Failure so the health transport's // Ask sees a transient failure rather than hanging. (Production always injects the // gRPC GrpcCentralTransport; this pins the wiring-guard fallback.) var dmProbe = CreateTestProbe(); var siteComm = Sys.ActorOf(Props.Create(() => new SiteCommunicationActor("site-a", _options, dmProbe.Ref))); siteComm.Tell(SampleReport(seq: 7)); ExpectMsg(); } [Fact] public void CentralCommunicationActor_OnReport_ProcessesAndAcks() { var mockRepo = Substitute.For(); mockRepo.GetAllSitesAsync(Arg.Any()).Returns(new List()); var aggregator = Substitute.For(); var services = new ServiceCollection(); services.AddScoped(_ => mockRepo); services.AddSingleton(aggregator); var sp = services.BuildServiceProvider(); var transport = Substitute.For(); var actor = Sys.ActorOf(Props.Create(() => new CentralCommunicationActor(sp, transport, (TimeSpan?)null))); actor.Tell(SampleReport(seq: 3)); var ack = ExpectMsg(); Assert.True(ack.Accepted); Assert.Equal(3, ack.SequenceNumber); AwaitAssert(() => aggregator.Received().ProcessReport(Arg.Is(r => r.SequenceNumber == 3))); } }