using Moq; using ScadaLink.Commons.Entities.Templates; using ScadaLink.Commons.Interfaces.Repositories; using ScadaLink.Commons.Interfaces.Services; using ScadaLink.TemplateEngine.Services; namespace ScadaLink.TemplateEngine.Tests.Services; public class TemplateFolderServiceTests { private readonly Mock _repoMock = new(); private readonly Mock _auditMock = new(); private readonly TemplateFolderService _sut; public TemplateFolderServiceTests() { _sut = new TemplateFolderService(_repoMock.Object, _auditMock.Object); } [Fact] public async Task CreateFolder_ValidInput_ReturnsSuccess() { _repoMock.Setup(r => r.GetAllFoldersAsync(It.IsAny())) .ReturnsAsync(new List()); var result = await _sut.CreateFolderAsync("Dev", null, "admin"); Assert.True(result.IsSuccess); Assert.Equal("Dev", result.Value.Name); Assert.Null(result.Value.ParentFolderId); _repoMock.Verify(r => r.AddFolderAsync(It.IsAny(), It.IsAny()), Times.Once); _repoMock.Verify(r => r.SaveChangesAsync(It.IsAny()), Times.Once); } [Fact] public async Task CreateFolder_EmptyName_ReturnsFailure() { var result = await _sut.CreateFolderAsync(" ", null, "admin"); Assert.True(result.IsFailure); Assert.Contains("required", result.Error, StringComparison.OrdinalIgnoreCase); } [Fact] public async Task CreateFolder_DuplicateSiblingName_CaseInsensitive_ReturnsFailure() { _repoMock.Setup(r => r.GetAllFoldersAsync(It.IsAny())) .ReturnsAsync(new List { new("Dev") { Id = 1, ParentFolderId = null } }); var result = await _sut.CreateFolderAsync("dev", null, "admin"); Assert.True(result.IsFailure); Assert.Contains("already exists", result.Error); } [Fact] public async Task CreateFolder_ParentNotFound_ReturnsFailure() { _repoMock.Setup(r => r.GetFolderByIdAsync(99, It.IsAny())) .ReturnsAsync((TemplateFolder?)null); var result = await _sut.CreateFolderAsync("Sub", 99, "admin"); Assert.True(result.IsFailure); Assert.Contains("not found", result.Error); } [Fact] public async Task RenameFolder_ValidInput_ReturnsSuccess() { var folder = new TemplateFolder("Old") { Id = 1, ParentFolderId = null }; _repoMock.Setup(r => r.GetFolderByIdAsync(1, It.IsAny())).ReturnsAsync(folder); _repoMock.Setup(r => r.GetAllFoldersAsync(It.IsAny())) .ReturnsAsync(new List { folder }); var result = await _sut.RenameFolderAsync(1, "New", "admin"); Assert.True(result.IsSuccess); Assert.Equal("New", result.Value.Name); } [Fact] public async Task RenameFolder_NotFound_ReturnsFailure() { _repoMock.Setup(r => r.GetFolderByIdAsync(99, It.IsAny())) .ReturnsAsync((TemplateFolder?)null); var result = await _sut.RenameFolderAsync(99, "New", "admin"); Assert.True(result.IsFailure); } [Fact] public async Task RenameFolder_DuplicateSibling_ReturnsFailure() { var folder = new TemplateFolder("Old") { Id = 1, ParentFolderId = null }; var sibling = new TemplateFolder("Other") { Id = 2, ParentFolderId = null }; _repoMock.Setup(r => r.GetFolderByIdAsync(1, It.IsAny())).ReturnsAsync(folder); _repoMock.Setup(r => r.GetAllFoldersAsync(It.IsAny())) .ReturnsAsync(new List { folder, sibling }); var result = await _sut.RenameFolderAsync(1, "Other", "admin"); Assert.True(result.IsFailure); Assert.Contains("already exists", result.Error); } [Fact] public async Task MoveFolder_ValidParent_ReturnsSuccess() { var f1 = new TemplateFolder("A") { Id = 1, ParentFolderId = null }; var f2 = new TemplateFolder("B") { Id = 2, ParentFolderId = null }; _repoMock.Setup(r => r.GetFolderByIdAsync(1, It.IsAny())).ReturnsAsync(f1); _repoMock.Setup(r => r.GetFolderByIdAsync(2, It.IsAny())).ReturnsAsync(f2); _repoMock.Setup(r => r.GetAllFoldersAsync(It.IsAny())) .ReturnsAsync(new List { f1, f2 }); var result = await _sut.MoveFolderAsync(1, 2, "admin"); Assert.True(result.IsSuccess); Assert.Equal(2, result.Value.ParentFolderId); } [Fact] public async Task MoveFolder_OntoSelf_ReturnsFailure() { var f1 = new TemplateFolder("A") { Id = 1 }; _repoMock.Setup(r => r.GetFolderByIdAsync(1, It.IsAny())).ReturnsAsync(f1); var result = await _sut.MoveFolderAsync(1, 1, "admin"); Assert.True(result.IsFailure); Assert.Contains("cycle", result.Error, StringComparison.OrdinalIgnoreCase); } [Fact] public async Task MoveFolder_OntoDescendant_ReturnsFailure() { // A -> B -> C; attempting to move A under C must fail. var fa = new TemplateFolder("A") { Id = 1, ParentFolderId = null }; var fb = new TemplateFolder("B") { Id = 2, ParentFolderId = 1 }; var fc = new TemplateFolder("C") { Id = 3, ParentFolderId = 2 }; _repoMock.Setup(r => r.GetFolderByIdAsync(1, It.IsAny())).ReturnsAsync(fa); _repoMock.Setup(r => r.GetFolderByIdAsync(3, It.IsAny())).ReturnsAsync(fc); _repoMock.Setup(r => r.GetAllFoldersAsync(It.IsAny())) .ReturnsAsync(new List { fa, fb, fc }); var result = await _sut.MoveFolderAsync(1, 3, "admin"); Assert.True(result.IsFailure); Assert.Contains("cycle", result.Error, StringComparison.OrdinalIgnoreCase); } [Fact] public async Task MoveFolder_ToRoot_ReturnsSuccess() { var f = new TemplateFolder("Sub") { Id = 1, ParentFolderId = 99 }; _repoMock.Setup(r => r.GetFolderByIdAsync(1, It.IsAny())).ReturnsAsync(f); _repoMock.Setup(r => r.GetAllFoldersAsync(It.IsAny())) .ReturnsAsync(new List { f }); var result = await _sut.MoveFolderAsync(1, null, "admin"); Assert.True(result.IsSuccess); Assert.Null(result.Value.ParentFolderId); } }