using Akka.Actor;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Commons.Interfaces;
using ZB.MOM.WW.OtOpcUa.Commons.Messages.Fleet;
using ZB.MOM.WW.OtOpcUa.Commons.Types;
using ZB.MOM.WW.OtOpcUa.Runtime.Tests.Harness;
namespace ZB.MOM.WW.OtOpcUa.Runtime.Tests.Drivers;
///
/// Review 02 U1: GetDiagnostics answered a placeholder — Guid.Empty, "Spawned", zero devices and
/// "now" for every child, in every behaviour — so the snapshot looked live and said nothing. It now carries
/// each child's row id, authored name, last-published health state, device counts and last change time.
///
public sealed class DriverHostActorDiagnosticsTests : RuntimeActorTestBase
{
private static readonly RevisionHash RevA = RevisionHash.Parse(new string('a', 64));
private static readonly NodeId TestNode = NodeId.Parse("node-1:4053");
private const string DriverId = "DI-1";
[Fact]
public async Task A_connected_child_reports_its_row_id_name_health_state_and_device_count()
{
var db = NewInMemoryDbFactory();
var rowId = Guid.NewGuid();
var before = DateTime.UtcNow.AddSeconds(-1);
var factory = new RecordingSubscribableDriverFactory("Modbus");
var deploymentId = V3ArtifactSeeder.SeedV3Deployment(
db, RevA, new[] { (DriverId, "Line1-Modbus", "dev1", "speed", "Float", false) },
Array.Empty<(string, string, string, string, string?)>(),
enabled: true, driverRowIds: new Dictionary { [DriverId] = rowId });
var (host, _, _) = V3ArtifactSeeder.SpawnHostAndApply(this, db, TestNode, deploymentId, RevA, factory);
var snapshot = await AwaitDiagnosticsAsync(host, s => s.Drivers.Any(d => d.State == "Healthy"));
snapshot.NodeId.ShouldBe(TestNode);
snapshot.CurrentRevision.ShouldBe(RevA);
snapshot.RunningFromCache.ShouldBeFalse();
var driver = snapshot.Drivers.ShouldHaveSingleItem();
driver.DriverInstanceId.ShouldBe(rowId, "the real row id, not Guid.Empty");
driver.Name.ShouldBe("Line1-Modbus", "the authored name, not the instance id");
driver.State.ShouldBe("Healthy");
driver.ConnectedDevices.ShouldBe(1);
driver.FaultedDevices.ShouldBe(0);
driver.LastChangeUtc.ShouldBeGreaterThan(before);
driver.LastChangeUtc.ShouldBeLessThanOrEqualTo(DateTime.UtcNow);
}
[Fact]
public async Task A_stubbed_child_reports_Stubbed_with_no_devices()
{
var db = NewInMemoryDbFactory();
// The factory answers for a type the artifact does not use, so the host stubs the child.
var factory = new RecordingSubscribableDriverFactory("NotModbus");
var deploymentId = V3ArtifactSeeder.SeedV3Deployment(
db, RevA, new[] { (DriverId, "Modbus", "dev1", "speed", "Float", false) },
Array.Empty<(string, string, string, string, string?)>(), enabled: true);
var (host, _, _) = V3ArtifactSeeder.SpawnHostAndApply(this, db, TestNode, deploymentId, RevA, factory);
var snapshot = await AwaitDiagnosticsAsync(host, s => s.Drivers.Count == 1);
var driver = snapshot.Drivers.Single();
driver.State.ShouldBe("Stubbed");
driver.ConnectedDevices.ShouldBe(0);
driver.FaultedDevices.ShouldBe(0);
}
private async Task AwaitDiagnosticsAsync(IActorRef host, Func ready)
{
NodeDiagnosticsSnapshot? snapshot = null;
await AwaitAssertAsync(async () =>
{
snapshot = await host.Ask(new GetDiagnostics(CorrelationId.NewId()), PresenceBudget);
ready(snapshot).ShouldBeTrue();
}, PresenceBudget);
return snapshot!;
}
}