feat(deploy): reject Sql Query tags naming undeclared queries; warn on Writable/writable disagreement (lmxopcua#781)
v2-ci / unit-tests (push) Has been cancelled
v2-ci / unit-tests (push) Has been cancelled
Item 5: DraftValidator now emits SqlQueryNotDeclared (always blocking) for a Query tag whose query is absent from the merged driver config's queries map, via the driver's own tag parse. Item 3: StartDeployment appends a non-blocking warning to the accepted message when the Tag entity's AccessLevel and the Sql blob's writable flag disagree. Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
This commit is contained in:
+45
@@ -124,4 +124,49 @@ public sealed class AdminOperationsActorTagConfigGateTests : ControlPlaneActorTe
|
||||
coordinator.ExpectMsg<DispatchDeployment>(PresenceBudget);
|
||||
ExpectMsg<StartDeploymentResult>(PresenceBudget).Outcome.ShouldBe(StartDeploymentOutcome.Accepted);
|
||||
}
|
||||
|
||||
// lmxopcua#781 item 3 — through the actor, in Error mode too: the writable mismatch is a warning, never a reject.
|
||||
private const string SqlQueries = "{\"queries\":{\"line\":{\"sql\":\"SELECT 1 AS v\",\"keyColumns\":[]}}}";
|
||||
|
||||
private StartDeploymentResult DeploySql(bool writableInBlob, TagAccessLevel access)
|
||||
{
|
||||
var dbFactory = NewInMemoryDbFactory();
|
||||
using (var db = dbFactory.CreateDbContext())
|
||||
{
|
||||
db.DriverInstances.Add(new DriverInstance
|
||||
{ DriverInstanceId = "d-1", ClusterId = "", Name = "drv", DriverType = "Sql", DriverConfig = SqlQueries });
|
||||
db.Devices.Add(new Device { DeviceId = "dev-1", DriverInstanceId = "d-1", Name = "dev", DeviceConfig = "{}" });
|
||||
db.Tags.Add(new Tag
|
||||
{
|
||||
TagId = "tag-1", DeviceId = "dev-1", Name = "flow", DataType = "Int32", AccessLevel = access,
|
||||
TagConfig = "{\"model\":\"Query\",\"query\":\"line\",\"column\":\"v\",\"writable\":"
|
||||
+ (writableInBlob ? "true" : "false") + "}",
|
||||
});
|
||||
db.SaveChanges();
|
||||
}
|
||||
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>(PresenceBudget);
|
||||
return ExpectMsg<StartDeploymentResult>(PresenceBudget);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Sql_writable_mismatch_is_accepted_with_a_warning_even_in_error_mode()
|
||||
{
|
||||
var reply = DeploySql(writableInBlob: true, TagAccessLevel.Read);
|
||||
reply.Outcome.ShouldBe(StartDeploymentOutcome.Accepted);
|
||||
reply.Message.ShouldNotBeNull();
|
||||
reply.Message.ShouldContain("tag-1");
|
||||
reply.Message.ShouldContain("writable");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Sql_writable_agreement_adds_no_warning()
|
||||
{
|
||||
var reply = DeploySql(writableInBlob: true, TagAccessLevel.ReadWrite);
|
||||
reply.Outcome.ShouldBe(StartDeploymentOutcome.Accepted);
|
||||
reply.Message.ShouldBeNull();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user