b211911f98
v2-ci / unit-tests (push) Failing after 13m22s
Akka.TestKit.Xunit2 -> Akka.TestKit.Xunit 1.5.62 (xunit.v3 TestKit; needs xunit.v3 3.2.2, set via VersionOverride on the four projects). Removes the xunit 2.9.3 pin. IAsyncLifetime returns ValueTask, SqlServerFactAttribute gains the source-info ctor, and xUnit1051 call sites pass TestContext.Current.CancellationToken. Test counts unchanged. Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
67 lines
3.5 KiB
C#
67 lines
3.5 KiB
C#
using Akka.Actor;
|
|
using Akka.Event;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Shouldly;
|
|
using Xunit;
|
|
using ZB.MOM.WW.OtOpcUa.Commons.OpcUa;
|
|
using ZB.MOM.WW.OtOpcUa.Commons.Types;
|
|
using ZB.MOM.WW.OtOpcUa.Configuration;
|
|
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
|
using ZB.MOM.WW.OtOpcUa.Runtime.Drivers;
|
|
using ZB.MOM.WW.OtOpcUa.Runtime.Tests.Harness;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Runtime.Tests.Drivers;
|
|
|
|
/// <summary>
|
|
/// Review 02 H5: the <c>Stale</c> behaviour registered no receive for a driver child's value/alarm bursts (nor
|
|
/// for a late <c>ArtifactForApply</c> / <c>ApplyWedged</c> pipe-back), so any that landed there hit
|
|
/// <c>Unhandled</c> — dead-letter noise and a dropped message with no log naming why. They are now explicit
|
|
/// drops. The two private pipe-back records cannot be constructed from a test; the two public child
|
|
/// messages are pinned here, and the private pair by the same registration pattern (compile-time).
|
|
/// </summary>
|
|
public sealed class DriverHostActorStaleDropTests : RuntimeActorTestBase
|
|
{
|
|
private static readonly NodeId TestNode = NodeId.Parse("stale-drop-test");
|
|
|
|
[Fact]
|
|
public void Child_value_and_alarm_bursts_landing_in_Stale_are_dropped_not_unhandled()
|
|
{
|
|
var unhandled = CreateTestProbe();
|
|
Sys.EventStream.Subscribe(unhandled.Ref, typeof(UnhandledMessage));
|
|
|
|
// ThrowingDbFactory ⇒ Bootstrap's catch ⇒ Become(Stale).
|
|
var host = Sys.ActorOf(DriverHostActor.Props(
|
|
new ThrowingDbFactory(), TestNode, CreateTestProbe().Ref,
|
|
localRoles: new HashSet<string> { "driver" }));
|
|
// Barrier: Stale answers RouteNodeWrite synchronously, so a reply proves the host is in Stale.
|
|
var asker = CreateTestProbe();
|
|
host.Tell(new DriverHostActor.RouteNodeWrite("eq-x/speed", 0.0, AddressSpaceRealm.Uns), asker.Ref);
|
|
asker.ExpectMsg<DriverHostActor.NodeWriteResult>(PresenceBudget, cancellationToken: TestContext.Current.CancellationToken).Reason!.ShouldContain("stale");
|
|
|
|
// Positive control: an unknown message IS reported as unhandled by this host.
|
|
host.Tell("not a message the host knows");
|
|
unhandled.ExpectMsg<UnhandledMessage>(m => ReferenceEquals(m.Recipient, host) || m.Recipient.Equals(host), PresenceBudget, cancellationToken: TestContext.Current.CancellationToken);
|
|
|
|
host.Tell(new DriverInstanceActor.AttributeValueBatchPublished(
|
|
"DI-1", new[] { new DriverInstanceActor.AttributeValueItem("Plant/x/y", 1.0, OpcUaStatus.Good, DateTime.UtcNow) }));
|
|
host.Tell(new DriverInstanceActor.AttributeAlarmPublished(
|
|
"DI-1", new AlarmEventArgs(new Handle(), "Plant/x/alarm", "Plant/x/alarm", "AlarmCondition", "test",
|
|
AlarmSeverity.High, DateTime.UtcNow)));
|
|
// Barrier again, then the absence window.
|
|
host.Tell(new DriverHostActor.RouteNodeWrite("eq-x/speed", 0.0, AddressSpaceRealm.Uns), asker.Ref);
|
|
asker.ExpectMsg<DriverHostActor.NodeWriteResult>(PresenceBudget, cancellationToken: TestContext.Current.CancellationToken);
|
|
|
|
unhandled.ExpectNoMsg(TimeSpan.FromMilliseconds(500), TestContext.Current.CancellationToken);
|
|
}
|
|
|
|
private sealed class Handle : IAlarmSubscriptionHandle
|
|
{
|
|
public string DiagnosticId => "stale-test";
|
|
}
|
|
|
|
private sealed class ThrowingDbFactory : IDbContextFactory<OtOpcUaConfigDbContext>
|
|
{
|
|
public OtOpcUaConfigDbContext CreateDbContext() => throw new InvalidOperationException("db unreachable");
|
|
}
|
|
}
|