diff --git a/tests/Imprink.Application.Tests/UpdateCategoryHandlerUnitTest.cs b/tests/Imprink.Application.Tests/UpdateCategoryHandlerUnitTest.cs new file mode 100644 index 0000000..3336fe8 --- /dev/null +++ b/tests/Imprink.Application.Tests/UpdateCategoryHandlerUnitTest.cs @@ -0,0 +1,244 @@ +using Imprink.Application.Commands.Categories; +using Imprink.Application.Exceptions; +using Imprink.Domain.Entities; +using Imprink.Domain.Repositories; +using Moq; + +namespace Imprink.Application.Tests; + +public class UpdateCategoryHandlerTests +{ + private readonly Mock _unitOfWorkMock; + private readonly Mock _categoryRepositoryMock; + private readonly UpdateCategoryHandler _handler; + + public UpdateCategoryHandlerTests() + { + _unitOfWorkMock = new Mock(); + _categoryRepositoryMock = new Mock(); + + _unitOfWorkMock.Setup(x => x.CategoryRepository) + .Returns(_categoryRepositoryMock.Object); + + _handler = new UpdateCategoryHandler(_unitOfWorkMock.Object); + } + + [Fact] + public async Task Handle_ValidRequest_ShouldUpdateCategoryAndReturnDto() + { + var categoryId = Guid.NewGuid(); + var parentCategoryId = Guid.NewGuid(); + var createdAt = DateTime.UtcNow.AddDays(-10); + var modifiedAt = DateTime.UtcNow; + + var existingCategory = new Category + { + Id = categoryId, + Name = "Old Name", + Description = "Old Description", + ImageUrl = "old-image.jpg", + SortOrder = 1, + IsActive = true, + ParentCategoryId = null, + CreatedAt = createdAt, + ModifiedAt = createdAt + }; + + var updatedCategory = new Category + { + Id = categoryId, + Name = "New Name", + Description = "New Description", + ImageUrl = "new-image.jpg", + SortOrder = 2, + IsActive = false, + ParentCategoryId = parentCategoryId, + CreatedAt = createdAt, + ModifiedAt = modifiedAt + }; + + var command = new UpdateCategoryCommand + { + Id = categoryId, + Name = "New Name", + Description = "New Description", + ImageUrl = "new-image.jpg", + SortOrder = 2, + IsActive = false, + ParentCategoryId = parentCategoryId + }; + + _categoryRepositoryMock + .Setup(x => x.GetByIdAsync(categoryId, It.IsAny())) + .ReturnsAsync(existingCategory); + + _categoryRepositoryMock + .Setup(x => x.UpdateAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(updatedCategory); + + var result = await _handler.Handle(command, CancellationToken.None); + + Assert.NotNull(result); + Assert.Equal(categoryId, result.Id); + Assert.Equal("New Name", result.Name); + Assert.Equal("New Description", result.Description); + Assert.Equal("new-image.jpg", result.ImageUrl); + Assert.Equal(2, result.SortOrder); + Assert.False(result.IsActive); + Assert.Equal(parentCategoryId, result.ParentCategoryId); + Assert.Equal(createdAt, result.CreatedAt); + Assert.Equal(modifiedAt, result.ModifiedAt); + + _unitOfWorkMock.Verify(x => x.BeginTransactionAsync(It.IsAny()), Times.Once); + _unitOfWorkMock.Verify(x => x.CommitTransactionAsync(It.IsAny()), Times.Once); + _unitOfWorkMock.Verify(x => x.RollbackTransactionAsync(It.IsAny()), Times.Never); + + _categoryRepositoryMock.Verify(x => x.GetByIdAsync(categoryId, It.IsAny()), Times.Once); + _categoryRepositoryMock.Verify(x => x.UpdateAsync(It.IsAny(), It.IsAny()), Times.Once); + } + + [Fact] + public async Task Handle_CategoryNotFound_ShouldThrowNotFoundException() + { + // Arrange + var categoryId = Guid.NewGuid(); + var command = new UpdateCategoryCommand + { + Id = categoryId, + Name = "Test Name", + Description = "Test Description", + SortOrder = 1, + IsActive = true + }; + + _categoryRepositoryMock + .Setup(x => x.GetByIdAsync(categoryId, It.IsAny())) + .ReturnsAsync((Category?)null); + + var exception = await Assert.ThrowsAsync( + () => _handler.Handle(command, CancellationToken.None)); + + Assert.Equal($"Category with ID {categoryId} not found.", exception.Message); + + _unitOfWorkMock.Verify(x => x.BeginTransactionAsync(It.IsAny()), Times.Once); + _unitOfWorkMock.Verify(x => x.RollbackTransactionAsync(It.IsAny()), Times.Once); + _unitOfWorkMock.Verify(x => x.CommitTransactionAsync(It.IsAny()), Times.Never); + + _categoryRepositoryMock.Verify(x => x.UpdateAsync(It.IsAny(), It.IsAny()), Times.Never); + } + + [Fact] + public async Task Handle_UpdateThrowsException_ShouldRollbackTransactionAndRethrow() + { + var categoryId = Guid.NewGuid(); + var existingCategory = new Category + { + Id = categoryId, + Name = "Existing Name", + Description = "Existing Description", + SortOrder = 1, + IsActive = true + }; + + var command = new UpdateCategoryCommand + { + Id = categoryId, + Name = "New Name", + Description = "New Description", + SortOrder = 2, + IsActive = false + }; + + var expectedException = new InvalidOperationException("Database error"); + + _categoryRepositoryMock + .Setup(x => x.GetByIdAsync(categoryId, It.IsAny())) + .ReturnsAsync(existingCategory); + + _categoryRepositoryMock + .Setup(x => x.UpdateAsync(It.IsAny(), It.IsAny())) + .ThrowsAsync(expectedException); + + var thrownException = await Assert.ThrowsAsync( + () => _handler.Handle(command, CancellationToken.None)); + + Assert.Same(expectedException, thrownException); + + _unitOfWorkMock.Verify(x => x.BeginTransactionAsync(It.IsAny()), Times.Once); + _unitOfWorkMock.Verify(x => x.RollbackTransactionAsync(It.IsAny()), Times.Once); + _unitOfWorkMock.Verify(x => x.CommitTransactionAsync(It.IsAny()), Times.Never); + } + + [Fact] + public async Task Handle_CategoryWithNullableFields_ShouldHandleNullValues() + { + // Arrange + var categoryId = Guid.NewGuid(); + var createdAt = DateTime.UtcNow.AddDays(-5); + var modifiedAt = DateTime.UtcNow; + + var existingCategory = new Category + { + Id = categoryId, + Name = "Test Category", + Description = "Test Description", + ImageUrl = "test-image.jpg", + SortOrder = 1, + IsActive = true, + ParentCategoryId = Guid.NewGuid(), + CreatedAt = createdAt, + ModifiedAt = createdAt + }; + + var updatedCategory = new Category + { + Id = categoryId, + Name = "Updated Category", + Description = "Updated Description", + ImageUrl = null, + SortOrder = 5, + IsActive = false, + ParentCategoryId = null, + CreatedAt = createdAt, + ModifiedAt = modifiedAt + }; + + var command = new UpdateCategoryCommand + { + Id = categoryId, + Name = "Updated Category", + Description = "Updated Description", + ImageUrl = null, + SortOrder = 5, + IsActive = false, + ParentCategoryId = null + }; + + _categoryRepositoryMock + .Setup(x => x.GetByIdAsync(categoryId, It.IsAny())) + .ReturnsAsync(existingCategory); + + _categoryRepositoryMock + .Setup(x => x.UpdateAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(updatedCategory); + + // Act + var result = await _handler.Handle(command, CancellationToken.None); + + // Assert + Assert.NotNull(result); + Assert.Equal(categoryId, result.Id); + Assert.Equal("Updated Category", result.Name); + Assert.Equal("Updated Description", result.Description); + Assert.Null(result.ImageUrl); + Assert.Equal(5, result.SortOrder); + Assert.False(result.IsActive); + Assert.Null(result.ParentCategoryId); + Assert.Equal(createdAt, result.CreatedAt); + Assert.Equal(modifiedAt, result.ModifiedAt); + + _unitOfWorkMock.Verify(x => x.BeginTransactionAsync(It.IsAny()), Times.Once); + _unitOfWorkMock.Verify(x => x.CommitTransactionAsync(It.IsAny()), Times.Once); + _unitOfWorkMock.Verify(x => x.RollbackTransactionAsync(It.IsAny()), Times.Never); + } +} \ No newline at end of file