fix(transport): transport LockedInDerived on template attributes/alarms/scripts (additive DTO fields)
Adds a trailing optional `bool LockedInDerived = false` to TemplateAttributeDto, TemplateAlarmDto and TemplateScriptDto (mirrors the ExecutionTimeoutSeconds additive precedent — no schemaVersion bump). Populated at export (ToBundleContent), consumed on import (FromBundleContent, BuildTemplate, and all three SyncTemplate*Async changed-predicate + copy + new-entity paths, incl. audit payloads). Old-form bundles that lack the property still deserialize to false. Claude-Session: https://claude.ai/code/session_01MtdgwpEeCUn6cUA5f1LMPj
This commit is contained in:
+57
@@ -197,4 +197,61 @@ public sealed class TemplateScriptFidelityTests : IDisposable
|
||||
a.Action == "TemplateScriptUpdated" && a.EntityName == "Pump.cadence"));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Add_preserves_LockedInDerived_on_attribute_alarm_script()
|
||||
{
|
||||
// #05-T4: LockedInDerived must survive export→import on all three child kinds.
|
||||
await using (var scope = _provider.CreateAsyncScope())
|
||||
{
|
||||
var ctx = scope.ServiceProvider.GetRequiredService<ScadaBridgeDbContext>();
|
||||
var t = new Template("Pump") { Description = "fresh" };
|
||||
t.Attributes.Add(new TemplateAttribute("Pressure")
|
||||
{
|
||||
DataType = DataType.Double,
|
||||
LockedInDerived = true,
|
||||
});
|
||||
t.Alarms.Add(new TemplateAlarm("High")
|
||||
{
|
||||
TriggerType = AlarmTriggerType.RangeViolation,
|
||||
LockedInDerived = true,
|
||||
});
|
||||
t.Scripts.Add(new TemplateScript("cadence", "return 1;")
|
||||
{
|
||||
LockedInDerived = true,
|
||||
});
|
||||
ctx.Templates.Add(t);
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
var sessionId = await ExportAndLoadAsync();
|
||||
|
||||
await using (var scope = _provider.CreateAsyncScope())
|
||||
{
|
||||
var ctx = scope.ServiceProvider.GetRequiredService<ScadaBridgeDbContext>();
|
||||
ctx.Templates.RemoveRange(ctx.Templates);
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
|
||||
await using (var scope = _provider.CreateAsyncScope())
|
||||
{
|
||||
var importer = scope.ServiceProvider.GetRequiredService<IBundleImporter>();
|
||||
await importer.ApplyAsync(sessionId,
|
||||
new List<ImportResolution> { new("Template", "Pump", ResolutionAction.Add, null) },
|
||||
user: "bob");
|
||||
}
|
||||
|
||||
await using (var scope = _provider.CreateAsyncScope())
|
||||
{
|
||||
var ctx = scope.ServiceProvider.GetRequiredService<ScadaBridgeDbContext>();
|
||||
var t = await ctx.Templates
|
||||
.Where(t => t.Name == "Pump")
|
||||
.Include(t => t.Attributes)
|
||||
.Include(t => t.Alarms)
|
||||
.Include(t => t.Scripts)
|
||||
.SingleAsync();
|
||||
Assert.True(t.Attributes.Single(a => a.Name == "Pressure").LockedInDerived);
|
||||
Assert.True(t.Alarms.Single(a => a.Name == "High").LockedInDerived);
|
||||
Assert.True(t.Scripts.Single(s => s.Name == "cadence").LockedInDerived);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user