test(alarms): assert reconnect-dropped native alarm does not dead-letter; tighten severity doc
v2-ci / build (push) Failing after 38s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Has been skipped
v2-ci / build (push) Failing after 38s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Has been skipped
Add AllDeadLetters probe to Native_alarm_during_reconnect_is_dropped_not_forwarded so the test genuinely guards the Reconnecting state's Receive<NativeAlarmRaised> drop handler — removing that handler would now cause a dead-letter and fail the assertion (false-negative gap closed). Reword the ScriptedAlarms.md severity-mapping note: "snaps on the first transition" → "every transition maps … overriding the authored seed from the first transition onward", clarifying that MapSeverity runs on every event, not just the first.
This commit is contained in:
+29
-4
@@ -1,4 +1,5 @@
|
||||
using Akka.Actor;
|
||||
using Akka.Event;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.OtOpcUa.Commons.OpcUa;
|
||||
@@ -94,8 +95,10 @@ public sealed class DriverInstanceActorNativeAlarmTests : RuntimeActorTestBase
|
||||
/// <summary>
|
||||
/// A native alarm transition that races in while the actor is in <c>Reconnecting</c> is silently
|
||||
/// dropped (debug log only) — it NEVER reaches the parent as
|
||||
/// <see cref="DriverInstanceActor.AttributeAlarmPublished"/> and does NOT dead-letter. The feed
|
||||
/// re-delivers active alarms once the actor re-enters <c>Connected</c>, so dropping here is safe.
|
||||
/// <see cref="DriverInstanceActor.AttributeAlarmPublished"/> AND is NOT dead-lettered (the
|
||||
/// <c>Reconnecting</c> state's explicit <c>Receive<NativeAlarmRaised></c> handler consumes and
|
||||
/// discards it, so it never becomes unhandled). The feed re-delivers active alarms once the actor
|
||||
/// re-enters <c>Connected</c>, so dropping here is safe.
|
||||
/// (<see cref="DriverInstanceActor"/> line ~345: the <c>Reconnecting</c> state's
|
||||
/// <c>Receive<NativeAlarmRaised></c> logs a debug message and discards.)
|
||||
///
|
||||
@@ -107,10 +110,27 @@ public sealed class DriverInstanceActorNativeAlarmTests : RuntimeActorTestBase
|
||||
/// (detaches handler), then processes the queued <c>NativeAlarmRaised</c> in Reconnecting
|
||||
/// → drops it. The default 10 s reconnect interval ensures no retry fires during the check.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Both properties are actively asserted: (a) the parent probe receives no
|
||||
/// <see cref="DriverInstanceActor.AttributeAlarmPublished"/>; (b) a dead-letter probe subscribed
|
||||
/// to <see cref="AllDeadLetters"/> on the <see cref="ActorSystem.EventStream"/> also receives
|
||||
/// nothing — proving the drop handler is present (removing it would cause a dead-letter and fail
|
||||
/// this assertion). <c>NativeAlarmRaised</c> is private to the actor, so the dead-letter probe
|
||||
/// subscribes to the unfiltered <see cref="AllDeadLetters"/> channel; this is safe because
|
||||
/// exactly one message is injected and the reconnect timer (10 s) cannot fire in the window.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Native_alarm_during_reconnect_is_dropped_not_forwarded()
|
||||
{
|
||||
// Subscribe a dead-letter probe BEFORE injecting the alarm so we don't miss any early publish.
|
||||
// NativeAlarmRaised is private, so we subscribe to the unfiltered AllDeadLetters channel.
|
||||
// Only one message is injected and the 10 s reconnect timer can't fire in this window, so
|
||||
// a plain "no dead letters at all" assertion is safe and non-flaky.
|
||||
var deadLetters = CreateTestProbe();
|
||||
Sys.EventStream.Subscribe(deadLetters.Ref, typeof(AllDeadLetters));
|
||||
|
||||
// Long reconnect interval (default 10 s) so the retry doesn't fire during the assertion window.
|
||||
var driver = new AlarmSourceStubDriver();
|
||||
var parent = CreateTestProbe();
|
||||
@@ -134,13 +154,18 @@ public sealed class DriverInstanceActorNativeAlarmTests : RuntimeActorTestBase
|
||||
SourceTimestampUtc: DateTime.UtcNow,
|
||||
Kind: AlarmTransitionKind.Raise));
|
||||
|
||||
// (a) The parent must NOT receive AttributeAlarmPublished from that alarm.
|
||||
// The actor processes: (1) ForceReconnect → Reconnecting (handler detached);
|
||||
// (2) NativeAlarmRaised → dropped (debug log, no forward).
|
||||
// The parent must NOT receive AttributeAlarmPublished from that alarm.
|
||||
// Wait generously — the default reconnect interval of 10 s means no retry fires here.
|
||||
parent.ExpectNoMsg(TimeSpan.FromMilliseconds(500));
|
||||
|
||||
// The actor must still be alive — Watch + no Terminated (not crashed or dead-lettered).
|
||||
// (b) The alarm must also NOT have dead-lettered — the Reconnecting state's explicit
|
||||
// Receive<NativeAlarmRaised> handler must have consumed it. If that handler were removed the
|
||||
// message would become unhandled → AllDeadLetters, and this assertion would catch the regression.
|
||||
deadLetters.ExpectNoMsg(TimeSpan.FromMilliseconds(200));
|
||||
|
||||
// The actor must still be alive — Watch + no Terminated.
|
||||
Watch(actor);
|
||||
parent.ExpectNoMsg(TimeSpan.FromMilliseconds(100));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user