feat(template-folder): rename folder with sibling uniqueness check
This commit is contained in:
@@ -42,4 +42,29 @@ public class TemplateFolderService
|
|||||||
|
|
||||||
return Result<TemplateFolder>.Success(folder);
|
return Result<TemplateFolder>.Success(folder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<Result<TemplateFolder>> RenameFolderAsync(
|
||||||
|
int folderId, string newName, string user,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(newName))
|
||||||
|
return Result<TemplateFolder>.Failure("Folder name is required.");
|
||||||
|
|
||||||
|
var folder = await _repository.GetFolderByIdAsync(folderId, cancellationToken);
|
||||||
|
if (folder == null)
|
||||||
|
return Result<TemplateFolder>.Failure($"Folder with ID {folderId} not found.");
|
||||||
|
|
||||||
|
var all = await _repository.GetAllFoldersAsync(cancellationToken);
|
||||||
|
if (all.Any(f => f.Id != folderId
|
||||||
|
&& f.ParentFolderId == folder.ParentFolderId
|
||||||
|
&& string.Equals(f.Name, newName, StringComparison.OrdinalIgnoreCase)))
|
||||||
|
return Result<TemplateFolder>.Failure($"A folder named '{newName}' already exists at this level.");
|
||||||
|
|
||||||
|
folder.Name = newName;
|
||||||
|
await _repository.UpdateFolderAsync(folder, cancellationToken);
|
||||||
|
await _repository.SaveChangesAsync(cancellationToken);
|
||||||
|
await _auditService.LogAsync(user, "Update", "TemplateFolder", folder.Id.ToString(), newName, folder, cancellationToken);
|
||||||
|
|
||||||
|
return Result<TemplateFolder>.Success(folder);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,4 +67,44 @@ public class TemplateFolderServiceTests
|
|||||||
Assert.True(result.IsFailure);
|
Assert.True(result.IsFailure);
|
||||||
Assert.Contains("not found", result.Error);
|
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<CancellationToken>())).ReturnsAsync(folder);
|
||||||
|
_repoMock.Setup(r => r.GetAllFoldersAsync(It.IsAny<CancellationToken>()))
|
||||||
|
.ReturnsAsync(new List<TemplateFolder> { 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<CancellationToken>()))
|
||||||
|
.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<CancellationToken>())).ReturnsAsync(folder);
|
||||||
|
_repoMock.Setup(r => r.GetAllFoldersAsync(It.IsAny<CancellationToken>()))
|
||||||
|
.ReturnsAsync(new List<TemplateFolder> { folder, sibling });
|
||||||
|
|
||||||
|
var result = await _sut.RenameFolderAsync(1, "Other", "admin");
|
||||||
|
|
||||||
|
Assert.True(result.IsFailure);
|
||||||
|
Assert.Contains("already exists", result.Error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user