fix(alarms): historize the real operator for shelve/unshelve/enable/disable transitions
This commit is contained in:
@@ -512,7 +512,13 @@ public sealed class ScriptedAlarmHostActor : ReceiveActor
|
||||
{
|
||||
EmissionKind.Acknowledged => e.Condition.LastAckUser ?? "system",
|
||||
EmissionKind.Confirmed => e.Condition.LastConfirmUser ?? "system",
|
||||
EmissionKind.CommentAdded => e.Condition.Comments.Count > 0 ? e.Condition.Comments[^1].User : "system",
|
||||
// Shelve / unshelve / enable / disable / comment ops each append the acting user as the LAST audit
|
||||
// entry on the emitted condition (engine auto-unshelve appends "system"); read it from there.
|
||||
EmissionKind.CommentAdded
|
||||
or EmissionKind.Shelved
|
||||
or EmissionKind.Unshelved
|
||||
or EmissionKind.Enabled
|
||||
or EmissionKind.Disabled => e.Condition.Comments.Count > 0 ? e.Condition.Comments[^1].User : "system",
|
||||
_ => "system",
|
||||
};
|
||||
|
||||
|
||||
+32
@@ -612,6 +612,38 @@ public sealed class ScriptedAlarmHostActorTests : RuntimeActorTestBase
|
||||
evtFalse.HistorizeToAveva.ShouldBe(false);
|
||||
}
|
||||
|
||||
/// <summary>OneShotShelve transition carries the operator's identity: an operator-driven OneShotShelve
|
||||
/// drives <c>OneShotShelveAsync</c> — the resulting <see cref="AlarmTransitionEvent"/>(<c>"Shelved"</c>)
|
||||
/// on the alerts topic must carry <c>User == cmd.User</c> (the acting operator), NOT the generic
|
||||
/// <c>"system"</c> sentinel. This verifies the <see cref="ScriptedAlarmHostActor"/>
|
||||
/// <c>TransitionUser</c> arm for <see cref="EmissionKind.Shelved"/>.</summary>
|
||||
[Fact]
|
||||
public void Shelved_transition_carries_operator_user()
|
||||
{
|
||||
var publish = CreateTestProbe();
|
||||
var mux = CreateTestProbe();
|
||||
var alerts = CreateTestProbe();
|
||||
SubscribeToAlerts(alerts);
|
||||
|
||||
var (host, _) = Spawn(publish, mux);
|
||||
host.Tell(new ScriptedAlarmHostActor.ApplyScriptedAlarms(new[] { Plan(id: "alm-1", depRef: "M.T") }));
|
||||
mux.ExpectMsg<DependencyMuxActor.RegisterInterest>(Timeout); // load completed
|
||||
|
||||
// Activate so there is something to shelve.
|
||||
host.Tell(new VirtualTagActor.DependencyValueChanged("M.T", 99, DateTime.UtcNow));
|
||||
publish.FishForMessage<OpcUaPublishActor.AlarmStateUpdate>(m => m.State.Active, Timeout);
|
||||
alerts.FishForMessage<AlarmTransitionEvent>(e => e.TransitionKind == "Activated", Timeout);
|
||||
|
||||
// Shelve via a one-shot shelve command — the host owns alm-1, so OneShotShelveAsync runs.
|
||||
host.Tell(new AlarmCommand(
|
||||
AlarmId: "alm-1", Operation: "OneShotShelve", User: "carol", Comment: null, UnshelveAtUtc: null));
|
||||
|
||||
// The Shelved AlarmTransitionEvent must carry the operator's identity, not "system".
|
||||
var evt = alerts.FishForMessage<AlarmTransitionEvent>(e => e.TransitionKind == "Shelved", Timeout);
|
||||
evt.AlarmId.ShouldBe("alm-1");
|
||||
evt.User.ShouldBe("carol"); // operator — NOT "system"
|
||||
}
|
||||
|
||||
/// <summary>Absent-node default-emit (A1): a <see cref="RedundancyStateChanged"/> snapshot that
|
||||
/// contains ONLY other nodes (the host's own <see cref="LocalNode"/> is absent) must leave the
|
||||
/// cached local role unchanged (null/unknown) — the host therefore defaults to emit, publishing
|
||||
|
||||
Reference in New Issue
Block a user