fix(template-engine): resolve TemplateEngine-001/003/004/005, re-triage 002 — recursive composed flattening, fixed-field guard, alarm script refs, dead collision query
This commit is contained in:
@@ -60,6 +60,23 @@ public class TemplateServiceTests
|
||||
Assert.Equal(1, result.Value.ParentTemplateId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateTemplate_WithParent_DoesNotRunDeadCollisionQuery()
|
||||
{
|
||||
// A freshly created child has no members of its own, and the parent's
|
||||
// members were already collision-validated when they were added — so
|
||||
// create-time collision detection on a child is a guaranteed no-op.
|
||||
// The previous code allocated an unused full-table read; the fix
|
||||
// removes it. This guards against the dead query being reintroduced.
|
||||
var parent = new Template("Base") { Id = 1 };
|
||||
_repoMock.Setup(r => r.GetTemplateByIdAsync(1, It.IsAny<CancellationToken>())).ReturnsAsync(parent);
|
||||
|
||||
var result = await _service.CreateTemplateAsync("Child", null, 1, "admin");
|
||||
|
||||
Assert.True(result.IsSuccess);
|
||||
_repoMock.Verify(r => r.GetAllTemplatesAsync(It.IsAny<CancellationToken>()), Times.Never);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateTemplate_NonexistentParent_Fails()
|
||||
{
|
||||
@@ -668,6 +685,54 @@ public class TemplateServiceTests
|
||||
Assert.True(result.Value.IsLocked);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateAttribute_UnlockedAttribute_DataTypeChangeRejected()
|
||||
{
|
||||
// An unlocked attribute must still not be able to change its fixed DataType.
|
||||
var existing = new TemplateAttribute("Temperature")
|
||||
{
|
||||
Id = 1, TemplateId = 1, DataType = DataType.Float, IsLocked = false
|
||||
};
|
||||
_repoMock.Setup(r => r.GetTemplateAttributeByIdAsync(1, It.IsAny<CancellationToken>())).ReturnsAsync(existing);
|
||||
var template = new Template("Pump") { Id = 1 };
|
||||
_repoMock.Setup(r => r.GetTemplateByIdAsync(1, It.IsAny<CancellationToken>())).ReturnsAsync(template);
|
||||
|
||||
var proposed = new TemplateAttribute("Temperature")
|
||||
{
|
||||
DataType = DataType.Int32, IsLocked = false, Value = "42"
|
||||
};
|
||||
var result = await _service.UpdateAttributeAsync(1, proposed, "admin");
|
||||
|
||||
Assert.True(result.IsFailure);
|
||||
Assert.Contains("DataType", result.Error);
|
||||
// The fixed field must not have been mutated.
|
||||
Assert.Equal(DataType.Float, existing.DataType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateAttribute_UnlockedAttribute_DataSourceReferenceChangeRejected()
|
||||
{
|
||||
var existing = new TemplateAttribute("Temperature")
|
||||
{
|
||||
Id = 1, TemplateId = 1, DataType = DataType.Float, IsLocked = false,
|
||||
DataSourceReference = "/Motor/Temp"
|
||||
};
|
||||
_repoMock.Setup(r => r.GetTemplateAttributeByIdAsync(1, It.IsAny<CancellationToken>())).ReturnsAsync(existing);
|
||||
var template = new Template("Pump") { Id = 1 };
|
||||
_repoMock.Setup(r => r.GetTemplateByIdAsync(1, It.IsAny<CancellationToken>())).ReturnsAsync(template);
|
||||
|
||||
var proposed = new TemplateAttribute("Temperature")
|
||||
{
|
||||
DataType = DataType.Float, IsLocked = false, Value = "42",
|
||||
DataSourceReference = "/Motor/Other"
|
||||
};
|
||||
var result = await _service.UpdateAttributeAsync(1, proposed, "admin");
|
||||
|
||||
Assert.True(result.IsFailure);
|
||||
Assert.Contains("DataSourceReference", result.Error);
|
||||
Assert.Equal("/Motor/Temp", existing.DataSourceReference);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateAttribute_ParentLocked_CannotOverride()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user