Some checks failed
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been cancelled
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been cancelled
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been cancelled
v2-ci / build (push) Has been cancelled
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been cancelled
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been cancelled
v2-ci / integration (push) Has been cancelled
Reshapes the placeholder buffered-counter actor into a thin fire-and-forget bridge over the existing IAlarmHistorianSink contract. Default sink is NullAlarmHistorianSink; production deployments override the DI binding to SqliteStoreAndForwardSink wrapping WonderwareHistorianClient (the v1 components in src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware* are reused verbatim — actor is just a mailbox-friendly entry point). - HistorianAdapterActor.Props(IAlarmHistorianSink?) — null defaults to NullAlarmHistorianSink - Receive<AlarmHistorianEvent>: fire-and-forget sink.EnqueueAsync - Receive<GetStatus>: returns sink.GetStatus() (queue depth + drain state) - ServiceCollectionExtensions.AddOtOpcUaRuntime registers the default sink - WithOtOpcUaRuntimeActors spawns the actor + registers HistorianAdapterActorKey - Program.cs calls AddOtOpcUaRuntime when hasDriver Tests: 2 new (forward-to-sink + GetStatus). Runtime suite 17 → 18.
89 lines
3.6 KiB
C#
89 lines
3.6 KiB
C#
using Akka.Hosting;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Hosting;
|
|
using Shouldly;
|
|
using Xunit;
|
|
using ZB.MOM.WW.OtOpcUa.Commons.Interfaces;
|
|
using ZB.MOM.WW.OtOpcUa.Commons.Types;
|
|
using ZB.MOM.WW.OtOpcUa.Configuration;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Runtime.Tests;
|
|
|
|
/// <summary>
|
|
/// Verifies <c>WithOtOpcUaRuntimeActors</c> spawns <c>DriverHostActor</c> + <c>DbHealthProbeActor</c>
|
|
/// on the host's <c>ActorSystem</c> and registers both under their marker keys. This is the
|
|
/// driver-role mirror of the admin-role <c>WithOtOpcUaControlPlaneSingletons</c> bootstrap.
|
|
/// </summary>
|
|
public sealed class ServiceCollectionExtensionsTests
|
|
{
|
|
[Fact]
|
|
public async Task WithOtOpcUaRuntimeActors_spawns_driver_host_and_db_health_probe()
|
|
{
|
|
using var host = Host.CreateDefaultBuilder()
|
|
.ConfigureServices((_, services) =>
|
|
{
|
|
services.AddSingleton<IDbContextFactory<OtOpcUaConfigDbContext>>(
|
|
new InMemoryConfigDbFactory(Guid.NewGuid().ToString("N")));
|
|
services.AddSingleton<IClusterRoleInfo>(new FakeClusterRoleInfo());
|
|
|
|
services.AddAkka("otopcua-test", (ab, _) =>
|
|
{
|
|
ab.AddHocon(@"
|
|
akka.actor.provider = ""Akka.Cluster.ClusterActorRefProvider, Akka.Cluster""
|
|
akka.remote.dot-netty.tcp.hostname = ""127.0.0.1""
|
|
akka.remote.dot-netty.tcp.port = 0
|
|
akka.cluster.seed-nodes = []
|
|
akka.cluster.roles = [""driver""]
|
|
", HoconAddMode.Prepend);
|
|
ab.WithOtOpcUaRuntimeActors();
|
|
});
|
|
})
|
|
.Build();
|
|
|
|
await host.StartAsync();
|
|
try
|
|
{
|
|
var driverHost = host.Services.GetRequiredService<IRequiredActor<DriverHostActorKey>>();
|
|
var dbHealth = host.Services.GetRequiredService<IRequiredActor<DbHealthProbeActorKey>>();
|
|
var historian = host.Services.GetRequiredService<IRequiredActor<HistorianAdapterActorKey>>();
|
|
|
|
driverHost.ActorRef.ShouldNotBeNull();
|
|
dbHealth.ActorRef.ShouldNotBeNull();
|
|
historian.ActorRef.ShouldNotBeNull();
|
|
driverHost.ActorRef.Path.Name.ShouldBe(ServiceCollectionExtensions.DriverHostActorName);
|
|
dbHealth.ActorRef.Path.Name.ShouldBe(ServiceCollectionExtensions.DbHealthProbeActorName);
|
|
historian.ActorRef.Path.Name.ShouldBe(ServiceCollectionExtensions.HistorianAdapterActorName);
|
|
}
|
|
finally
|
|
{
|
|
await host.StopAsync();
|
|
}
|
|
}
|
|
|
|
private sealed class InMemoryConfigDbFactory(string dbName) : IDbContextFactory<OtOpcUaConfigDbContext>
|
|
{
|
|
public OtOpcUaConfigDbContext CreateDbContext()
|
|
{
|
|
var opts = new DbContextOptionsBuilder<OtOpcUaConfigDbContext>()
|
|
.UseInMemoryDatabase(dbName)
|
|
.Options;
|
|
return new OtOpcUaConfigDbContext(opts);
|
|
}
|
|
}
|
|
|
|
private sealed class FakeClusterRoleInfo : IClusterRoleInfo
|
|
{
|
|
public NodeId LocalNode { get; } = NodeId.Parse("test-node");
|
|
public IReadOnlySet<string> LocalRoles { get; } = new HashSet<string>(["driver"]);
|
|
public bool HasRole(string role) => LocalRoles.Contains(role);
|
|
public IReadOnlyList<NodeId> MembersWithRole(string role) => Array.Empty<NodeId>();
|
|
public NodeId? RoleLeader(string role) => null;
|
|
public event EventHandler<RoleLeaderChangedEventArgs>? RoleLeaderChanged
|
|
{
|
|
add { _ = value; }
|
|
remove { _ = value; }
|
|
}
|
|
}
|
|
}
|