192 lines
8.0 KiB
C#
192 lines
8.0 KiB
C#
using Moq;
|
|
using ScadaLink.Commons.Entities.Instances;
|
|
using ScadaLink.Commons.Entities.Templates;
|
|
using ScadaLink.Commons.Interfaces.Repositories;
|
|
using ScadaLink.TemplateEngine.Services;
|
|
|
|
namespace ScadaLink.TemplateEngine.Tests.Services;
|
|
|
|
public class TemplateDeletionServiceTests
|
|
{
|
|
private readonly Mock<ITemplateEngineRepository> _repoMock = new();
|
|
private readonly TemplateDeletionService _sut;
|
|
|
|
public TemplateDeletionServiceTests()
|
|
{
|
|
_sut = new TemplateDeletionService(_repoMock.Object);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task CanDeleteTemplate_NoReferences_ReturnsSuccess()
|
|
{
|
|
_repoMock.Setup(r => r.GetTemplateByIdAsync(1, It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new Template("Orphan") { Id = 1 });
|
|
_repoMock.Setup(r => r.GetInstancesByTemplateIdAsync(1, It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new List<Instance>());
|
|
_repoMock.Setup(r => r.GetAllTemplatesAsync(It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new List<Template> { new("Orphan") { Id = 1 } });
|
|
_repoMock.Setup(r => r.GetCompositionsByTemplateIdAsync(It.IsAny<int>(), It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new List<TemplateComposition>());
|
|
|
|
var result = await _sut.CanDeleteTemplateAsync(1);
|
|
|
|
Assert.True(result.IsSuccess);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task CanDeleteTemplate_WithInstances_ReturnsFailure()
|
|
{
|
|
_repoMock.Setup(r => r.GetTemplateByIdAsync(1, It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new Template("Used") { Id = 1 });
|
|
_repoMock.Setup(r => r.GetInstancesByTemplateIdAsync(1, It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new List<Instance>
|
|
{
|
|
new("Inst1") { Id = 1 },
|
|
new("Inst2") { Id = 2 }
|
|
});
|
|
_repoMock.Setup(r => r.GetAllTemplatesAsync(It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new List<Template> { new("Used") { Id = 1 } });
|
|
_repoMock.Setup(r => r.GetCompositionsByTemplateIdAsync(It.IsAny<int>(), It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new List<TemplateComposition>());
|
|
|
|
var result = await _sut.CanDeleteTemplateAsync(1);
|
|
|
|
Assert.True(result.IsFailure);
|
|
Assert.Contains("2 instance(s)", result.Error);
|
|
Assert.Contains("Inst1", result.Error);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task CanDeleteTemplate_WithChildTemplates_ReturnsFailure()
|
|
{
|
|
_repoMock.Setup(r => r.GetTemplateByIdAsync(1, It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new Template("Base") { Id = 1 });
|
|
_repoMock.Setup(r => r.GetInstancesByTemplateIdAsync(1, It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new List<Instance>());
|
|
_repoMock.Setup(r => r.GetAllTemplatesAsync(It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new List<Template>
|
|
{
|
|
new("Base") { Id = 1 },
|
|
new("Child") { Id = 2, ParentTemplateId = 1 }
|
|
});
|
|
_repoMock.Setup(r => r.GetCompositionsByTemplateIdAsync(It.IsAny<int>(), It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new List<TemplateComposition>());
|
|
|
|
var result = await _sut.CanDeleteTemplateAsync(1);
|
|
|
|
Assert.True(result.IsFailure);
|
|
Assert.Contains("child template(s)", result.Error);
|
|
Assert.Contains("Child", result.Error);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task CanDeleteTemplate_ComposedByOthers_ReturnsFailure()
|
|
{
|
|
var composer = new Template("Composer") { Id = 2 };
|
|
composer.Compositions.Add(new TemplateComposition("PumpModule") { ComposedTemplateId = 1 });
|
|
|
|
_repoMock.Setup(r => r.GetTemplateByIdAsync(1, It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new Template("Module") { Id = 1 });
|
|
_repoMock.Setup(r => r.GetInstancesByTemplateIdAsync(1, It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new List<Instance>());
|
|
_repoMock.Setup(r => r.GetAllTemplatesAsync(It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new List<Template>
|
|
{
|
|
new("Module") { Id = 1 },
|
|
composer
|
|
});
|
|
|
|
var result = await _sut.CanDeleteTemplateAsync(1);
|
|
|
|
Assert.True(result.IsFailure);
|
|
Assert.Contains("compose it", result.Error);
|
|
Assert.Contains("Composer", result.Error);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task CanDeleteTemplate_DoesNotIssuePerTemplateCompositionQuery()
|
|
{
|
|
// TemplateEngine-009: Check 3 must read the Compositions navigation
|
|
// already loaded by GetAllTemplatesAsync rather than issuing one
|
|
// GetCompositionsByTemplateIdAsync round-trip per template.
|
|
var templates = new List<Template>
|
|
{
|
|
new("Module") { Id = 1 },
|
|
new("A") { Id = 2 },
|
|
new("B") { Id = 3 },
|
|
new("C") { Id = 4 },
|
|
};
|
|
|
|
_repoMock.Setup(r => r.GetTemplateByIdAsync(1, It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new Template("Module") { Id = 1 });
|
|
_repoMock.Setup(r => r.GetInstancesByTemplateIdAsync(1, It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new List<Instance>());
|
|
_repoMock.Setup(r => r.GetAllTemplatesAsync(It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(templates);
|
|
|
|
var result = await _sut.CanDeleteTemplateAsync(1);
|
|
|
|
Assert.True(result.IsSuccess);
|
|
_repoMock.Verify(r => r.GetCompositionsByTemplateIdAsync(
|
|
It.IsAny<int>(), It.IsAny<CancellationToken>()), Times.Never);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task CanDeleteTemplate_NotFound_ReturnsFailure()
|
|
{
|
|
_repoMock.Setup(r => r.GetTemplateByIdAsync(999, It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync((Template?)null);
|
|
|
|
var result = await _sut.CanDeleteTemplateAsync(999);
|
|
|
|
Assert.True(result.IsFailure);
|
|
Assert.Contains("not found", result.Error);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DeleteTemplate_AllConstraintsMet_Deletes()
|
|
{
|
|
_repoMock.Setup(r => r.GetTemplateByIdAsync(1, It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new Template("Safe") { Id = 1 });
|
|
_repoMock.Setup(r => r.GetInstancesByTemplateIdAsync(1, It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new List<Instance>());
|
|
_repoMock.Setup(r => r.GetAllTemplatesAsync(It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new List<Template> { new("Safe") { Id = 1 } });
|
|
_repoMock.Setup(r => r.GetCompositionsByTemplateIdAsync(It.IsAny<int>(), It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new List<TemplateComposition>());
|
|
_repoMock.Setup(r => r.SaveChangesAsync(It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(1);
|
|
|
|
var result = await _sut.DeleteTemplateAsync(1);
|
|
|
|
Assert.True(result.IsSuccess);
|
|
_repoMock.Verify(r => r.DeleteTemplateAsync(1, It.IsAny<CancellationToken>()), Times.Once);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task CanDeleteTemplate_MultipleConstraints_AllErrorsReported()
|
|
{
|
|
_repoMock.Setup(r => r.GetTemplateByIdAsync(1, It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new Template("Busy") { Id = 1 });
|
|
_repoMock.Setup(r => r.GetInstancesByTemplateIdAsync(1, It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new List<Instance> { new("Inst1") { Id = 1 } });
|
|
var composer = new Template("Composer") { Id = 3 };
|
|
composer.Compositions.Add(new TemplateComposition("Module") { ComposedTemplateId = 1 });
|
|
_repoMock.Setup(r => r.GetAllTemplatesAsync(It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new List<Template>
|
|
{
|
|
new("Busy") { Id = 1 },
|
|
new("Child") { Id = 2, ParentTemplateId = 1 },
|
|
composer
|
|
});
|
|
|
|
var result = await _sut.CanDeleteTemplateAsync(1);
|
|
|
|
Assert.True(result.IsFailure);
|
|
// All three constraint types should be mentioned
|
|
Assert.Contains("instance(s)", result.Error);
|
|
Assert.Contains("child template(s)", result.Error);
|
|
Assert.Contains("compose it", result.Error);
|
|
}
|
|
}
|