fix(mgmt): pass sharedScripts to design-time template $ref validation (#259)
This commit is contained in:
@@ -590,9 +590,23 @@ public class ManagementActor : ReceiveActor
|
|||||||
var schemaLibrary = sharedSchemas.ToDictionary(s => s.Name, s => s.SchemaJson, StringComparer.Ordinal);
|
var schemaLibrary = sharedSchemas.ToDictionary(s => s.Name, s => s.SchemaJson, StringComparer.Ordinal);
|
||||||
Func<string, string?> resolveSchemaRef = name => schemaLibrary.GetValueOrDefault(name);
|
Func<string, string?> resolveSchemaRef = name => schemaLibrary.GetValueOrDefault(name);
|
||||||
|
|
||||||
|
// #259: also pass shared scripts so a dangling $ref in a SHARED script's schema is
|
||||||
|
// caught at design-time (not only on the deploy path). Mirror the deploy-path mapping
|
||||||
|
// in FlatteningPipeline: SharedScript entity → ResolvedScript (name + schema fields).
|
||||||
|
var sharedScriptEntities = await repo.GetAllSharedScriptsAsync();
|
||||||
|
var resolvedSharedScripts = sharedScriptEntities
|
||||||
|
.Select(s => new Commons.Types.Flattening.ResolvedScript
|
||||||
|
{
|
||||||
|
CanonicalName = s.Name,
|
||||||
|
Code = s.Code,
|
||||||
|
ParameterDefinitions = s.ParameterDefinitions,
|
||||||
|
ReturnDefinition = s.ReturnDefinition
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
|
||||||
// Run full validation pipeline (collisions, script compilation, trigger refs, bindings)
|
// Run full validation pipeline (collisions, script compilation, trigger refs, bindings)
|
||||||
var validationService = new TemplateEngine.Validation.ValidationService();
|
var validationService = new TemplateEngine.Validation.ValidationService();
|
||||||
var validationResult = validationService.Validate(flatConfig, resolveSchemaRef: resolveSchemaRef);
|
var validationResult = validationService.Validate(flatConfig, resolvedSharedScripts, resolveSchemaRef: resolveSchemaRef);
|
||||||
|
|
||||||
// Also detect naming collisions across the inheritance/composition graph
|
// Also detect naming collisions across the inheritance/composition graph
|
||||||
var svc = sp.GetRequiredService<TemplateService>();
|
var svc = sp.GetRequiredService<TemplateService>();
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ using Microsoft.Extensions.Logging.Abstractions;
|
|||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
using NSubstitute.ExceptionExtensions;
|
using NSubstitute.ExceptionExtensions;
|
||||||
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Instances;
|
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Instances;
|
||||||
|
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Schemas;
|
||||||
|
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Scripts;
|
||||||
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Templates;
|
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Templates;
|
||||||
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories;
|
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories;
|
||||||
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Services;
|
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Services;
|
||||||
@@ -2378,4 +2380,66 @@ public class ManagementActorTests : TestKit, IDisposable
|
|||||||
// The off-site template (42) must never be scanned.
|
// The off-site template (42) must never be scanned.
|
||||||
_templateRepo.DidNotReceive().GetTemplateByIdAsync(42, Arg.Any<CancellationToken>());
|
_templateRepo.DidNotReceive().GetTemplateByIdAsync(42, Arg.Any<CancellationToken>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// #259: design-time ValidateTemplateCommand passes sharedScripts so a
|
||||||
|
// dangling $ref in a SHARED script's schema is caught at design time, not
|
||||||
|
// only on the deploy path.
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ValidateTemplate_DanglingRefInSharedScript_ReturnsInvalidResult()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var template = new Template("T1") { Id = 1 };
|
||||||
|
|
||||||
|
_templateRepo.GetTemplateWithChildrenAsync(1, Arg.Any<CancellationToken>())
|
||||||
|
.Returns(template);
|
||||||
|
_templateRepo.GetAttributesByTemplateIdAsync(1, Arg.Any<CancellationToken>())
|
||||||
|
.Returns(new List<TemplateAttribute>());
|
||||||
|
_templateRepo.GetAlarmsByTemplateIdAsync(1, Arg.Any<CancellationToken>())
|
||||||
|
.Returns(new List<TemplateAlarm>());
|
||||||
|
_templateRepo.GetScriptsByTemplateIdAsync(1, Arg.Any<CancellationToken>())
|
||||||
|
.Returns(new List<TemplateScript>());
|
||||||
|
|
||||||
|
// Shared script whose parameter schema contains a dangling $ref.
|
||||||
|
_templateRepo.GetAllSharedScriptsAsync(Arg.Any<CancellationToken>())
|
||||||
|
.Returns(new List<SharedScript>
|
||||||
|
{
|
||||||
|
new SharedScript("ComputeOrder", "var x = 1;")
|
||||||
|
{
|
||||||
|
ParameterDefinitions = """{"$ref":"lib:MissingSharedSchema"}"""
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Schema library is empty → the $ref cannot be resolved and dangles.
|
||||||
|
var schemaRepo = Substitute.For<ISharedSchemaRepository>();
|
||||||
|
schemaRepo.ListAsync(Arg.Any<CancellationToken>())
|
||||||
|
.Returns(new List<SharedSchema>());
|
||||||
|
_services.AddScoped(_ => schemaRepo);
|
||||||
|
|
||||||
|
// TemplateService is required by the collision-detection step inside HandleValidateTemplate.
|
||||||
|
_templateRepo.GetTemplateByIdAsync(1, Arg.Any<CancellationToken>())
|
||||||
|
.Returns(template);
|
||||||
|
_templateRepo.GetAllTemplatesAsync(Arg.Any<CancellationToken>())
|
||||||
|
.Returns(new List<Template> { template });
|
||||||
|
_services.AddScoped<TemplateService>();
|
||||||
|
|
||||||
|
var actor = CreateActor();
|
||||||
|
var envelope = Envelope(new ValidateTemplateCommand(1), "Designer");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
actor.Tell(envelope);
|
||||||
|
|
||||||
|
// Assert: the handler returns ManagementSuccess whose JSON payload signals an invalid
|
||||||
|
// ValidationResult with a SchemaReference error naming the missing library entry.
|
||||||
|
var msg = ExpectMsg<ManagementSuccess>(TimeSpan.FromSeconds(10));
|
||||||
|
Assert.Equal(envelope.CorrelationId, msg.CorrelationId);
|
||||||
|
// The ValidationResult is JSON-serialised into JsonData; check key fields in the raw JSON
|
||||||
|
// (consistent with how other ManagementActorTests verify structured payloads).
|
||||||
|
// ValidationCategory is serialised as an integer; SchemaReference is ordinal 15.
|
||||||
|
Assert.Contains("\"isValid\":false", msg.JsonData, StringComparison.Ordinal);
|
||||||
|
Assert.Contains("MissingSharedSchema", msg.JsonData, StringComparison.Ordinal);
|
||||||
|
Assert.Contains("\"category\":15", msg.JsonData, StringComparison.Ordinal);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user