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!;
}
/// Verifies that a traced change is stamped on the wire and parsed back.
[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);
}
/// Verifies that an unstamped change round-trips to null and false.
[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);
}
/// Verifies that an older site frame without the trace fields parses to null and false.
[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);
}
}