fix(template-engine): resolve TemplateEngine-015,016 — cascade-rename nested derived templates, correct composed-script ParentPath
This commit is contained in:
@@ -466,6 +466,67 @@ public class TemplateServiceTests
|
||||
Assert.Equal("Pump.NewSlot", derived.Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RenameComposition_CascadesRenameToNestedDerivedTemplates()
|
||||
{
|
||||
// Pump.TempSensor is the slot-owned derived; Pump.TempSensor.Probe1 is a
|
||||
// cascaded inner derived under it. Renaming the TempSensor slot to
|
||||
// MainSensor must rename BOTH derived templates so the dotted-path
|
||||
// naming invariant holds: Pump.MainSensor and Pump.MainSensor.Probe1.
|
||||
var innerComp = new TemplateComposition("Probe1") { Id = 51, TemplateId = 77, ComposedTemplateId = 78 };
|
||||
var innerDerived = new Template("Pump.TempSensor.Probe1") { Id = 78, IsDerived = true, OwnerCompositionId = 51, ParentTemplateId = 11 };
|
||||
var composition = new TemplateComposition("TempSensor") { Id = 50, TemplateId = 1, ComposedTemplateId = 77 };
|
||||
var owner = new Template("Pump") { Id = 1 };
|
||||
owner.Compositions.Add(composition);
|
||||
var derived = new Template("Pump.TempSensor") { Id = 77, IsDerived = true, OwnerCompositionId = 50, ParentTemplateId = 2 };
|
||||
derived.Compositions.Add(innerComp);
|
||||
|
||||
_repoMock.Setup(r => r.GetTemplateCompositionByIdAsync(50, It.IsAny<CancellationToken>())).ReturnsAsync(composition);
|
||||
_repoMock.Setup(r => r.GetTemplateByIdAsync(1, It.IsAny<CancellationToken>())).ReturnsAsync(owner);
|
||||
_repoMock.Setup(r => r.GetTemplateByIdAsync(77, It.IsAny<CancellationToken>())).ReturnsAsync(derived);
|
||||
_repoMock.Setup(r => r.GetTemplateByIdAsync(78, It.IsAny<CancellationToken>())).ReturnsAsync(innerDerived);
|
||||
_repoMock.Setup(r => r.GetAllTemplatesAsync(It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new List<Template> { owner, derived, innerDerived });
|
||||
|
||||
var result = await _service.RenameCompositionAsync(50, "MainSensor", "admin");
|
||||
|
||||
Assert.True(result.IsSuccess);
|
||||
Assert.Equal("MainSensor", result.Value.InstanceName);
|
||||
Assert.Equal("Pump.MainSensor", derived.Name);
|
||||
Assert.Equal("Pump.MainSensor.Probe1", innerDerived.Name);
|
||||
_repoMock.Verify(r => r.UpdateTemplateAsync(
|
||||
It.Is<Template>(t => t.Id == 78), It.IsAny<CancellationToken>()), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RenameComposition_NestedCascadeNameCollision_Fails()
|
||||
{
|
||||
// A pre-existing template occupies the name the nested cascade would
|
||||
// produce (Pump.MainSensor.Probe1). The rename must abort before any
|
||||
// row mutates, so the full cascade name set must be pre-checked.
|
||||
var innerComp = new TemplateComposition("Probe1") { Id = 51, TemplateId = 77, ComposedTemplateId = 78 };
|
||||
var innerDerived = new Template("Pump.TempSensor.Probe1") { Id = 78, IsDerived = true, OwnerCompositionId = 51, ParentTemplateId = 11 };
|
||||
var composition = new TemplateComposition("TempSensor") { Id = 50, TemplateId = 1, ComposedTemplateId = 77 };
|
||||
var owner = new Template("Pump") { Id = 1 };
|
||||
owner.Compositions.Add(composition);
|
||||
var derived = new Template("Pump.TempSensor") { Id = 77, IsDerived = true, OwnerCompositionId = 50, ParentTemplateId = 2 };
|
||||
derived.Compositions.Add(innerComp);
|
||||
var collider = new Template("Pump.MainSensor.Probe1") { Id = 99 };
|
||||
|
||||
_repoMock.Setup(r => r.GetTemplateCompositionByIdAsync(50, It.IsAny<CancellationToken>())).ReturnsAsync(composition);
|
||||
_repoMock.Setup(r => r.GetTemplateByIdAsync(1, It.IsAny<CancellationToken>())).ReturnsAsync(owner);
|
||||
_repoMock.Setup(r => r.GetTemplateByIdAsync(77, It.IsAny<CancellationToken>())).ReturnsAsync(derived);
|
||||
_repoMock.Setup(r => r.GetTemplateByIdAsync(78, It.IsAny<CancellationToken>())).ReturnsAsync(innerDerived);
|
||||
_repoMock.Setup(r => r.GetAllTemplatesAsync(It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new List<Template> { owner, derived, innerDerived, collider });
|
||||
|
||||
var result = await _service.RenameCompositionAsync(50, "MainSensor", "admin");
|
||||
|
||||
Assert.True(result.IsFailure);
|
||||
Assert.Contains("already exists", result.Error);
|
||||
_repoMock.Verify(r => r.UpdateTemplateAsync(It.IsAny<Template>(), It.IsAny<CancellationToken>()), Times.Never);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RenameComposition_DuplicateName_Fails()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user