feat(controlplane): deploy-gate TagConfig strictness — Warn default, Error opt-in at the draft gate (R2-11, 05/CONV-2)
This commit is contained in:
+131
@@ -0,0 +1,131 @@
|
||||
using Akka.Actor;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.OtOpcUa.Commons.Messages.Admin;
|
||||
using ZB.MOM.WW.OtOpcUa.Commons.Messages.Deploy;
|
||||
using ZB.MOM.WW.OtOpcUa.Commons.Types;
|
||||
using ZB.MOM.WW.OtOpcUa.Configuration;
|
||||
using ZB.MOM.WW.OtOpcUa.Configuration.Entities;
|
||||
using ZB.MOM.WW.OtOpcUa.Configuration.Enums;
|
||||
using ZB.MOM.WW.OtOpcUa.Configuration.Validation;
|
||||
using ZB.MOM.WW.OtOpcUa.ControlPlane.AdminOperations;
|
||||
using ZB.MOM.WW.OtOpcUa.ControlPlane.Tests.Harness;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.ControlPlane.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// R2-11 (05/CONV-2) — the deploy gate's TagConfig strictness inspection, exercised THROUGH the actor
|
||||
/// (the F10b/PR#423 lesson: an inspector built but never called is inert). Warn mode (default) accepts
|
||||
/// the deploy with the warning surfaced in the result Message; Error mode rejects the same draft; a
|
||||
/// clean config and an unmapped (Galaxy) tag produce no TagConfig noise.
|
||||
/// </summary>
|
||||
public sealed class AdminOperationsActorTagConfigGateTests : ControlPlaneActorTestBase
|
||||
{
|
||||
private static void Seed(IDbContextFactory<OtOpcUaConfigDbContext> dbFactory, string driverType, string tagConfig,
|
||||
string tagName = "flow")
|
||||
{
|
||||
var uuid = Guid.NewGuid();
|
||||
var equipmentId = DraftValidator.DeriveEquipmentId(uuid);
|
||||
using var db = dbFactory.CreateDbContext();
|
||||
db.Namespaces.Add(new Namespace
|
||||
{
|
||||
NamespaceId = "ns-1", ClusterId = "", Kind = NamespaceKind.Equipment, NamespaceUri = "urn:eq",
|
||||
});
|
||||
db.DriverInstances.Add(new DriverInstance
|
||||
{
|
||||
DriverInstanceId = "d-1", ClusterId = "", NamespaceId = "ns-1",
|
||||
Name = "drv", DriverType = driverType, DriverConfig = "{}",
|
||||
});
|
||||
db.Equipment.Add(new Equipment
|
||||
{
|
||||
EquipmentUuid = uuid, EquipmentId = equipmentId, Name = "pump-01",
|
||||
DriverInstanceId = "d-1", UnsLineId = "line-a", MachineCode = "PUMP01",
|
||||
});
|
||||
db.Tags.Add(new Tag
|
||||
{
|
||||
TagId = "tag-1", DriverInstanceId = "d-1", EquipmentId = equipmentId,
|
||||
Name = tagName, DataType = "Int16", AccessLevel = TagAccessLevel.Read, TagConfig = tagConfig,
|
||||
});
|
||||
db.SaveChanges();
|
||||
}
|
||||
|
||||
private const string TypoModbus = "{\"region\":\"HoldingRegisters\",\"address\":1,\"dataType\":\"Intt16\"}";
|
||||
private const string CleanModbus = "{\"region\":\"HoldingRegisters\",\"address\":1,\"dataType\":\"Int16\"}";
|
||||
|
||||
[Fact]
|
||||
public void Warn_mode_accepts_and_surfaces_the_warning_in_the_message()
|
||||
{
|
||||
var dbFactory = NewInMemoryDbFactory();
|
||||
Seed(dbFactory, "Modbus", TypoModbus);
|
||||
var coordinator = CreateTestProbe("coord");
|
||||
var actor = Sys.ActorOf(AdminOperationsActor.Props(
|
||||
dbFactory, coordinator.Ref, Enumerable.Empty<IDriverProbe>(), TagConfigValidationMode.Warn));
|
||||
|
||||
actor.Tell(new StartDeployment("joe", CorrelationId.NewId()));
|
||||
|
||||
coordinator.ExpectMsg<DispatchDeployment>(TimeSpan.FromSeconds(3));
|
||||
var reply = ExpectMsg<StartDeploymentResult>(TimeSpan.FromSeconds(3));
|
||||
reply.Outcome.ShouldBe(StartDeploymentOutcome.Accepted);
|
||||
reply.Message.ShouldNotBeNull();
|
||||
reply.Message.ShouldContain("Intt16");
|
||||
reply.Message.ShouldContain("tag-1");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Error_mode_rejects_the_same_draft()
|
||||
{
|
||||
var dbFactory = NewInMemoryDbFactory();
|
||||
Seed(dbFactory, "Modbus", TypoModbus);
|
||||
var coordinator = CreateTestProbe("coord");
|
||||
var actor = Sys.ActorOf(AdminOperationsActor.Props(
|
||||
dbFactory, coordinator.Ref, Enumerable.Empty<IDriverProbe>(), TagConfigValidationMode.Error));
|
||||
|
||||
actor.Tell(new StartDeployment("joe", CorrelationId.NewId()));
|
||||
|
||||
coordinator.ExpectNoMsg(TimeSpan.FromMilliseconds(500));
|
||||
var reply = ExpectMsg<StartDeploymentResult>(TimeSpan.FromSeconds(3));
|
||||
reply.Outcome.ShouldBe(StartDeploymentOutcome.Rejected);
|
||||
reply.Message.ShouldNotBeNull();
|
||||
reply.Message.ShouldContain("TagConfigInvalid");
|
||||
reply.Message.ShouldContain("Intt16");
|
||||
|
||||
using var verify = dbFactory.CreateDbContext();
|
||||
verify.Deployments.Count().ShouldBe(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Clean_config_accepts_with_no_tagconfig_noise()
|
||||
{
|
||||
var dbFactory = NewInMemoryDbFactory();
|
||||
Seed(dbFactory, "Modbus", CleanModbus);
|
||||
var coordinator = CreateTestProbe("coord");
|
||||
var actor = Sys.ActorOf(AdminOperationsActor.Props(
|
||||
dbFactory, coordinator.Ref, Enumerable.Empty<IDriverProbe>(), TagConfigValidationMode.Warn));
|
||||
|
||||
actor.Tell(new StartDeployment("joe", CorrelationId.NewId()));
|
||||
|
||||
coordinator.ExpectMsg<DispatchDeployment>(TimeSpan.FromSeconds(3));
|
||||
var reply = ExpectMsg<StartDeploymentResult>(TimeSpan.FromSeconds(3));
|
||||
reply.Outcome.ShouldBe(StartDeploymentOutcome.Accepted);
|
||||
(reply.Message is null || !reply.Message.Contains("Intt16")).ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Unmapped_galaxy_tag_is_skipped_even_in_error_mode()
|
||||
{
|
||||
var dbFactory = NewInMemoryDbFactory();
|
||||
// Galaxy tag carrying a FullName (passes the DraftValidator Galaxy rule); the inspector has no
|
||||
// Galaxy mapping, so it is skipped regardless of mode.
|
||||
Seed(dbFactory, "GalaxyMxGateway", "{\"FullName\":\"Obj.Attr\",\"dataType\":\"nonsense\"}");
|
||||
var coordinator = CreateTestProbe("coord");
|
||||
var actor = Sys.ActorOf(AdminOperationsActor.Props(
|
||||
dbFactory, coordinator.Ref, Enumerable.Empty<IDriverProbe>(), TagConfigValidationMode.Error));
|
||||
|
||||
actor.Tell(new StartDeployment("joe", CorrelationId.NewId()));
|
||||
|
||||
coordinator.ExpectMsg<DispatchDeployment>(TimeSpan.FromSeconds(3));
|
||||
ExpectMsg<StartDeploymentResult>(TimeSpan.FromSeconds(3)).Outcome.ShouldBe(StartDeploymentOutcome.Accepted);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user