Files
lmxopcua/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverHostActorReprovisionStampTests.cs
T
Joseph Doherty b211911f98
v2-ci / unit-tests (push) Failing after 13m22s
test: migrate AdminUI/ControlPlane/Runtime/Host tests to xunit.v3 (#756)
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
2026-11-10 08:57:33 -05:00

64 lines
3.9 KiB
C#

using Akka.Actor;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Commons.Types;
using ZB.MOM.WW.OtOpcUa.Runtime.Tests.Harness;
namespace ZB.MOM.WW.OtOpcUa.Runtime.Tests.Drivers;
/// <summary>
/// Review 02 L2: <c>DriverHostActor._lastReprovisionUtc</c> was never pruned — a driver removed by a deploy
/// after a supervision crash kept its stamp, so the map grew with every id ever reprovisioned-then-removed,
/// and (the observable half) a driver removed and later re-added inherited the OLD stamp: its first crash
/// after the re-add was deferred by the backoff as if it were crash-looping, although the new incarnation had
/// never crashed. <c>StopChild</c> now drops the stamp.
/// </summary>
public sealed class DriverHostActorReprovisionStampTests : RuntimeActorTestBase
{
private static readonly RevisionHash RevA = RevisionHash.Parse(new string('a', 64));
private static readonly RevisionHash RevB = RevisionHash.Parse(new string('b', 64));
private static readonly RevisionHash RevC = RevisionHash.Parse(new string('c', 64));
private static readonly NodeId TestNode = NodeId.Parse("node-1:4053");
private const string DriverId = "DI-1";
private const string SpeedPath = "Plant/Modbus/dev1/speed";
[Fact]
public void A_driver_removed_and_re_added_does_not_inherit_the_old_incarnations_reprovision_stamp()
{
var db = NewInMemoryDbFactory();
// Far longer than the presence budget below: an inherited stamp would defer the re-added driver's
// reprovision past it, which is the failure this test reports.
var backoff = TimeSpan.FromSeconds(60);
var factory = new RecordingSubscribableDriverFactory("Modbus");
var withDriver = new[] { (DriverId, "Modbus", "dev1", "speed", "Float", false) };
var none = Array.Empty<(string, string, string, string, string?)>();
var revA = V3ArtifactSeeder.SeedV3Deployment(db, RevA, withDriver, none, enabled: true);
var (host, _, _, coordinator) = V3ArtifactSeeder.SpawnHostAndApplyWithCoordinator(
this, db, TestNode, revA, RevA, factory, reprovisionBackoff: backoff);
AwaitAssert(() => factory.Created.Count.ShouldBe(1), PresenceBudget, cancellationToken: TestContext.Current.CancellationToken);
AwaitAssert(() => factory.Created[0].SubscribeCount.ShouldBe(1), PresenceBudget, cancellationToken: TestContext.Current.CancellationToken);
// Crash 1 on the first incarnation: reprovisioned immediately, which stamps the id.
factory.Created[0].FireMalformedDataChange(SpeedPath);
AwaitAssert(() => factory.Created[0].InitializeCount.ShouldBe(2), PresenceBudget, cancellationToken: TestContext.Current.CancellationToken);
// Deploy B removes the driver (StopChild), deploy C re-adds it: a brand-new driver object.
var revB = V3ArtifactSeeder.SeedV3Deployment(
db, RevB, Array.Empty<(string, string, string, string, string, bool)>(), none, enabled: true);
V3ArtifactSeeder.Dispatch(host, coordinator, revB, RevB);
var revC = V3ArtifactSeeder.SeedV3Deployment(db, RevC, withDriver, none, enabled: true);
V3ArtifactSeeder.Dispatch(host, coordinator, revC, RevC);
AwaitAssert(() => factory.Created.Count.ShouldBe(2), PresenceBudget, cancellationToken: TestContext.Current.CancellationToken);
var reAdded = factory.Created[1];
AwaitAssert(() => reAdded.SubscribeCount.ShouldBe(1), PresenceBudget, cancellationToken: TestContext.Current.CancellationToken);
// Its FIRST crash must reprovision immediately — it has no history of its own.
reAdded.FireMalformedDataChange(SpeedPath);
AwaitAssert(
() => reAdded.InitializeCount.ShouldBe(
2, "the re-added driver's first reprovision was deferred by the REMOVED incarnation's stamp"),
duration: TimeSpan.FromSeconds(5), cancellationToken: TestContext.Current.CancellationToken);
}
}