diff --git a/src/ZB.MOM.WW.ScadaBridge.TemplateEngine/Services/InstanceService.cs b/src/ZB.MOM.WW.ScadaBridge.TemplateEngine/Services/InstanceService.cs index 1c69a847..1f859bca 100644 --- a/src/ZB.MOM.WW.ScadaBridge.TemplateEngine/Services/InstanceService.cs +++ b/src/ZB.MOM.WW.ScadaBridge.TemplateEngine/Services/InstanceService.cs @@ -172,6 +172,7 @@ public class InstanceService if (existingOverride != null) { existingOverride.OverrideValue = overrideValue; + existingOverride.ElementDataType = templateAttr.ElementDataType; await _repository.UpdateInstanceAttributeOverrideAsync(existingOverride, cancellationToken); await _repository.SaveChangesAsync(cancellationToken); @@ -185,7 +186,8 @@ public class InstanceService var newOverride = new InstanceAttributeOverride(attributeName) { InstanceId = instanceId, - OverrideValue = overrideValue + OverrideValue = overrideValue, + ElementDataType = templateAttr.ElementDataType }; await _repository.AddInstanceAttributeOverrideAsync(newOverride, cancellationToken); diff --git a/tests/ZB.MOM.WW.ScadaBridge.TemplateEngine.Tests/Services/InstanceServiceTests.cs b/tests/ZB.MOM.WW.ScadaBridge.TemplateEngine.Tests/Services/InstanceServiceTests.cs index b1cbc66b..b7e0a7fc 100644 --- a/tests/ZB.MOM.WW.ScadaBridge.TemplateEngine.Tests/Services/InstanceServiceTests.cs +++ b/tests/ZB.MOM.WW.ScadaBridge.TemplateEngine.Tests/Services/InstanceServiceTests.cs @@ -269,6 +269,106 @@ public class InstanceServiceTests _repoMock.Verify(r => r.AddInstanceConnectionBindingAsync(It.IsAny(), It.IsAny()), Times.Exactly(2)); } + // --- NJ-2: ElementDataType stamping on SetAttributeOverrideAsync --- + + [Fact] + public async Task SetAttributeOverride_CreatePath_ListAttribute_StampsElementDataType() + { + var instance = new Instance("Inst1") { Id = 1, TemplateId = 1 }; + _repoMock.Setup(r => r.GetInstanceByIdAsync(1, It.IsAny())) + .ReturnsAsync(instance); + _repoMock.Setup(r => r.GetAttributesByTemplateIdAsync(1, It.IsAny())) + .ReturnsAsync(new List + { + new("Tags") + { + IsLocked = false, + DataType = DataType.List, + ElementDataType = DataType.String + } + }); + _repoMock.Setup(r => r.GetOverridesByInstanceIdAsync(1, It.IsAny())) + .ReturnsAsync(new List()); // no existing override → create path + _repoMock.Setup(r => r.SaveChangesAsync(It.IsAny())) + .ReturnsAsync(1); + + InstanceAttributeOverride? captured = null; + _repoMock.Setup(r => r.AddInstanceAttributeOverrideAsync(It.IsAny(), It.IsAny())) + .Callback((o, _) => captured = o); + + var result = await _sut.SetAttributeOverrideAsync(1, "Tags", "[\"a\"]", "admin"); + + Assert.True(result.IsSuccess); + Assert.NotNull(captured); + Assert.Equal(DataType.String, captured.ElementDataType); + } + + [Fact] + public async Task SetAttributeOverride_UpdatePath_ListAttribute_StampsElementDataType() + { + var instance = new Instance("Inst1") { Id = 1, TemplateId = 1 }; + _repoMock.Setup(r => r.GetInstanceByIdAsync(1, It.IsAny())) + .ReturnsAsync(instance); + _repoMock.Setup(r => r.GetAttributesByTemplateIdAsync(1, It.IsAny())) + .ReturnsAsync(new List + { + new("Tags") + { + IsLocked = false, + DataType = DataType.List, + ElementDataType = DataType.String + } + }); + + var existingOverride = new InstanceAttributeOverride("Tags") { Id = 42, InstanceId = 1, OverrideValue = "[\"old\"]" }; + _repoMock.Setup(r => r.GetOverridesByInstanceIdAsync(1, It.IsAny())) + .ReturnsAsync(new List { existingOverride }); // pre-existing → update path + _repoMock.Setup(r => r.SaveChangesAsync(It.IsAny())) + .ReturnsAsync(1); + + InstanceAttributeOverride? captured = null; + _repoMock.Setup(r => r.UpdateInstanceAttributeOverrideAsync(It.IsAny(), It.IsAny())) + .Callback((o, _) => captured = o); + + var result = await _sut.SetAttributeOverrideAsync(1, "Tags", "[\"new\"]", "admin"); + + Assert.True(result.IsSuccess); + Assert.NotNull(captured); + Assert.Equal(DataType.String, captured.ElementDataType); + } + + [Fact] + public async Task SetAttributeOverride_CreatePath_ScalarAttribute_ElementDataTypeIsNull() + { + var instance = new Instance("Inst1") { Id = 1, TemplateId = 1 }; + _repoMock.Setup(r => r.GetInstanceByIdAsync(1, It.IsAny())) + .ReturnsAsync(instance); + _repoMock.Setup(r => r.GetAttributesByTemplateIdAsync(1, It.IsAny())) + .ReturnsAsync(new List + { + new("Threshold") + { + IsLocked = false, + DataType = DataType.Float, + ElementDataType = null + } + }); + _repoMock.Setup(r => r.GetOverridesByInstanceIdAsync(1, It.IsAny())) + .ReturnsAsync(new List()); + _repoMock.Setup(r => r.SaveChangesAsync(It.IsAny())) + .ReturnsAsync(1); + + InstanceAttributeOverride? captured = null; + _repoMock.Setup(r => r.AddInstanceAttributeOverrideAsync(It.IsAny(), It.IsAny())) + .Callback((o, _) => captured = o); + + var result = await _sut.SetAttributeOverrideAsync(1, "Threshold", "3.14", "admin"); + + Assert.True(result.IsSuccess); + Assert.NotNull(captured); + Assert.Null(captured.ElementDataType); + } + [Fact] public async Task AssignToArea_AreaInDifferentSite_ReturnsFailure() {