using Moq; using ScadaLink.Commons.Entities.Instances; using ScadaLink.Commons.Interfaces.Repositories; using ScadaLink.Commons.Interfaces.Services; using ScadaLink.TemplateEngine.Services; namespace ScadaLink.TemplateEngine.Tests.Services; public class AreaServiceTests { private readonly Mock _repoMock = new(); private readonly Mock _auditMock = new(); private readonly AreaService _sut; public AreaServiceTests() { _sut = new AreaService(_repoMock.Object, _auditMock.Object); } [Fact] public async Task CreateArea_ValidInput_ReturnsSuccess() { _repoMock.Setup(r => r.GetAreasBySiteIdAsync(1, It.IsAny())) .ReturnsAsync(new List()); _repoMock.Setup(r => r.SaveChangesAsync(It.IsAny())) .ReturnsAsync(1); var result = await _sut.CreateAreaAsync("Building A", 1, null, "admin"); Assert.True(result.IsSuccess); Assert.Equal("Building A", result.Value.Name); Assert.Equal(1, result.Value.SiteId); } [Fact] public async Task CreateArea_DuplicateName_ReturnsFailure() { _repoMock.Setup(r => r.GetAreasBySiteIdAsync(1, It.IsAny())) .ReturnsAsync(new List { new("Building A") { Id = 1, SiteId = 1, ParentAreaId = null } }); var result = await _sut.CreateAreaAsync("Building A", 1, null, "admin"); Assert.True(result.IsFailure); Assert.Contains("already exists", result.Error); } [Fact] public async Task CreateArea_WithParent_ValidatesParentBelongsToSite() { _repoMock.Setup(r => r.GetAreaByIdAsync(5, It.IsAny())) .ReturnsAsync(new Area("Parent") { Id = 5, SiteId = 99 }); // Different site! var result = await _sut.CreateAreaAsync("Child", 1, 5, "admin"); Assert.True(result.IsFailure); Assert.Contains("does not belong", result.Error); } [Fact] public async Task DeleteArea_WithAssignedInstances_ReturnsFailure() { _repoMock.Setup(r => r.GetAreaByIdAsync(1, It.IsAny())) .ReturnsAsync(new Area("Building A") { Id = 1, SiteId = 1 }); _repoMock.Setup(r => r.GetInstancesBySiteIdAsync(1, It.IsAny())) .ReturnsAsync(new List { new("Inst1") { Id = 1, AreaId = 1, SiteId = 1 } }); _repoMock.Setup(r => r.GetAreasBySiteIdAsync(1, It.IsAny())) .ReturnsAsync(new List { new("Building A") { Id = 1, SiteId = 1 } }); var result = await _sut.DeleteAreaAsync(1, "admin"); Assert.True(result.IsFailure); Assert.Contains("instance(s) are assigned", result.Error); } [Fact] public async Task DeleteArea_WithChildAreas_ReturnsFailure() { _repoMock.Setup(r => r.GetAreaByIdAsync(1, It.IsAny())) .ReturnsAsync(new Area("Parent") { Id = 1, SiteId = 1 }); _repoMock.Setup(r => r.GetInstancesBySiteIdAsync(1, It.IsAny())) .ReturnsAsync(new List()); _repoMock.Setup(r => r.GetAreasBySiteIdAsync(1, It.IsAny())) .ReturnsAsync(new List { new("Parent") { Id = 1, SiteId = 1 }, new("Child") { Id = 2, SiteId = 1, ParentAreaId = 1 } }); var result = await _sut.DeleteAreaAsync(1, "admin"); Assert.True(result.IsFailure); Assert.Contains("child areas", result.Error); } [Fact] public async Task DeleteArea_NoConstraints_Success() { _repoMock.Setup(r => r.GetAreaByIdAsync(1, It.IsAny())) .ReturnsAsync(new Area("Empty") { Id = 1, SiteId = 1 }); _repoMock.Setup(r => r.GetInstancesBySiteIdAsync(1, It.IsAny())) .ReturnsAsync(new List()); _repoMock.Setup(r => r.GetAreasBySiteIdAsync(1, It.IsAny())) .ReturnsAsync(new List { new("Empty") { Id = 1, SiteId = 1 } }); _repoMock.Setup(r => r.SaveChangesAsync(It.IsAny())) .ReturnsAsync(1); var result = await _sut.DeleteAreaAsync(1, "admin"); Assert.True(result.IsSuccess); _repoMock.Verify(r => r.DeleteAreaAsync(1, It.IsAny()), Times.Once); } [Fact] public async Task DeleteArea_InstancesInDescendants_Blocked() { // Area hierarchy: Area1 -> Area2 -> Area3 // Instance assigned to Area3, trying to delete Area1 should be blocked _repoMock.Setup(r => r.GetAreaByIdAsync(1, It.IsAny())) .ReturnsAsync(new Area("Root") { Id = 1, SiteId = 1 }); _repoMock.Setup(r => r.GetAreasBySiteIdAsync(1, It.IsAny())) .ReturnsAsync(new List { new("Root") { Id = 1, SiteId = 1 }, new("Mid") { Id = 2, SiteId = 1, ParentAreaId = 1 }, new("Leaf") { Id = 3, SiteId = 1, ParentAreaId = 2 } }); _repoMock.Setup(r => r.GetInstancesBySiteIdAsync(1, It.IsAny())) .ReturnsAsync(new List { new("DeepInstance") { Id = 10, AreaId = 3, SiteId = 1 } }); var result = await _sut.DeleteAreaAsync(1, "admin"); Assert.True(result.IsFailure); Assert.Contains("instance(s) are assigned", result.Error); } }