using System.Threading.Channels; using Akka.Actor; using Akka.TestKit.Xunit2; using Google.Protobuf.WellKnownTypes; using ZB.MOM.WW.ScadaBridge.Commons.Messages.Streaming; using ZB.MOM.WW.ScadaBridge.Communication.Actors; using ZB.MOM.WW.ScadaBridge.Communication.Grpc; namespace ZB.MOM.WW.ScadaBridge.Communication.Tests; /// /// Flow Debugger (design 2026-09-05 §5.3/§5.4): AttributeValueUpdate fields 7/8 — /// stamps source_execution_id/trace on the way /// out and parses them back; an unstamped change (and an /// older site's frame) round-trips to null/false. /// public class StreamRelayTraceStampTests : TestKit { private static SiteStreamEvent Relay(AttributeValueChanged change, ActorSystem sys) { var channel = Channel.CreateUnbounded(); var actor = sys.ActorOf(Props.Create(() => new StreamRelayActor("corr", channel.Writer))); actor.Tell(change); SiteStreamEvent? evt = null; for (var i = 0; i < 40 && !channel.Reader.TryRead(out evt); i++) Thread.Sleep(50); Assert.NotNull(evt); return evt!; } [Fact] public void TracedChange_IsStampedOnTheWire_AndParsedBack() { var source = Guid.NewGuid(); var now = new DateTimeOffset(2026, 9, 5, 12, 0, 0, TimeSpan.Zero); var change = new AttributeValueChanged("P1", "Status", "Status", "Running", "Good", now, SourceExecutionId: source, Trace: true); var evt = Relay(change, Sys); Assert.Equal(source.ToString("D"), evt.AttributeChanged.SourceExecutionId); Assert.True(evt.AttributeChanged.Trace); var back = Assert.IsType(SiteStreamGrpcClient.ConvertToDomainEvent(evt)); Assert.Equal(source, back.SourceExecutionId); Assert.True(back.Trace); } [Fact] public void UnstampedChange_RoundTripsToNullAndFalse() { var change = new AttributeValueChanged("P1", "Status", "Status", "Idle", "Good", DateTimeOffset.UtcNow); var evt = Relay(change, Sys); Assert.Equal(string.Empty, evt.AttributeChanged.SourceExecutionId); Assert.False(evt.AttributeChanged.Trace); var back = Assert.IsType(SiteStreamGrpcClient.ConvertToDomainEvent(evt)); Assert.Null(back.SourceExecutionId); Assert.False(back.Trace); } [Fact] public void OlderSiteFrame_WithoutTheFields_ParsesToNullAndFalse() { var evt = new SiteStreamEvent { CorrelationId = "c", AttributeChanged = new AttributeValueUpdate { InstanceUniqueName = "P1", AttributePath = "A", AttributeName = "A", Value = "1", Quality = Quality.Good, Timestamp = Timestamp.FromDateTimeOffset(DateTimeOffset.UtcNow), }, }; var back = Assert.IsType(SiteStreamGrpcClient.ConvertToDomainEvent(evt)); Assert.Null(back.SourceExecutionId); Assert.False(back.Trace); } }