using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Config;
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Runtime;
namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests;
///
/// Pins that the GalaxyDriver populates the extended AlarmEventArgs fields
/// (OperatorComment, OriginalRaiseTimestampUtc, AlarmCategory) when the gateway
/// alarm feed delivers a transition with the rich payload, and leaves them null on
/// transitions that don't carry them.
///
public sealed class GalaxyDriverAlarmEventArgsExtensionTests
{
[Fact]
public async Task Acknowledge_transition_with_full_payload_populates_extended_fields()
{
var feed = new FakeAlarmFeed();
using var driver = NewDriver(feed);
await driver.SubscribeAlarmsAsync(["Tank01"], CancellationToken.None);
var observed = new List();
driver.OnAlarmEvent += (_, args) => observed.Add(args);
var raise = new DateTime(2026, 5, 1, 12, 0, 0, DateTimeKind.Utc);
var ack = raise.AddSeconds(45);
feed.Emit(new GalaxyAlarmTransition(
AlarmFullReference: "Tank01.Level.HiHi",
SourceObjectReference: "Tank01",
AlarmTypeName: "AnalogLimitAlarm.HiHi",
TransitionKind: GalaxyAlarmTransitionKind.Acknowledge,
SeverityBucket: AlarmSeverity.Critical,
OpcUaSeverity: 800,
RawMxAccessSeverity: 750,
OriginalRaiseTimestampUtc: raise,
TransitionTimestampUtc: ack,
OperatorUser: "alice",
OperatorComment: "investigating",
Category: "Process",
Description: "Tank 01 high-high level"));
observed.ShouldHaveSingleItem();
observed[0].OperatorComment.ShouldBe("investigating");
observed[0].OriginalRaiseTimestampUtc.ShouldBe(raise);
observed[0].AlarmCategory.ShouldBe("Process");
}
[Fact]
public async Task Raise_transition_without_optional_fields_leaves_them_null()
{
var feed = new FakeAlarmFeed();
using var driver = NewDriver(feed);
await driver.SubscribeAlarmsAsync(["Tank01"], CancellationToken.None);
var observed = new List();
driver.OnAlarmEvent += (_, args) => observed.Add(args);
feed.Emit(new GalaxyAlarmTransition(
AlarmFullReference: "Tank01.Level.HiHi",
SourceObjectReference: string.Empty,
AlarmTypeName: "AnalogLimitAlarm.HiHi",
TransitionKind: GalaxyAlarmTransitionKind.Raise,
SeverityBucket: AlarmSeverity.Critical,
OpcUaSeverity: 800,
RawMxAccessSeverity: 750,
OriginalRaiseTimestampUtc: null,
TransitionTimestampUtc: DateTime.UtcNow,
OperatorUser: string.Empty,
OperatorComment: string.Empty,
Category: string.Empty,
Description: string.Empty));
observed.ShouldHaveSingleItem();
observed[0].OperatorComment.ShouldBeNull();
observed[0].OriginalRaiseTimestampUtc.ShouldBeNull();
observed[0].AlarmCategory.ShouldBeNull();
}
private static GalaxyDriver NewDriver(IGalaxyAlarmFeed feed)
{
var options = new GalaxyDriverOptions(
new GalaxyGatewayOptions("http://localhost:5000", "literal-api-key"),
new GalaxyMxAccessOptions("AlarmExtensionTest"),
new GalaxyRepositoryOptions(),
new GalaxyReconnectOptions());
return new GalaxyDriver(
driverInstanceId: "drv-1",
options: options,
hierarchySource: null,
alarmFeed: feed);
}
/// In-memory the test drives directly.
private sealed class FakeAlarmFeed : IGalaxyAlarmFeed
{
public event EventHandler? OnAlarmTransition;
public void Start() { }
public void Emit(GalaxyAlarmTransition transition)
=> OnAlarmTransition?.Invoke(this, transition);
public ValueTask DisposeAsync() => ValueTask.CompletedTask;
}
}