using FluentAssertions; using Moq; using ColaFlow.Modules.ProjectManagement.Application.Commands.UpdateTaskStatus; using ColaFlow.Modules.ProjectManagement.Domain.Aggregates.ProjectAggregate; using ColaFlow.Modules.ProjectManagement.Domain.Repositories; using ColaFlow.Modules.ProjectManagement.Domain.ValueObjects; using ColaFlow.Modules.ProjectManagement.Domain.Exceptions; namespace ColaFlow.Application.Tests.Commands.UpdateTaskStatus; /// /// Tests for UpdateTaskStatusCommandHandler /// public class UpdateTaskStatusCommandHandlerTests { private readonly Mock _projectRepositoryMock; private readonly Mock _unitOfWorkMock; private readonly UpdateTaskStatusCommandHandler _handler; public UpdateTaskStatusCommandHandlerTests() { _projectRepositoryMock = new Mock(); _unitOfWorkMock = new Mock(); _handler = new UpdateTaskStatusCommandHandler(_projectRepositoryMock.Object, _unitOfWorkMock.Object); } [Fact] public async Task Should_Update_Task_Status_To_InProgress_Successfully() { // Arrange var userId = UserId.Create(); var project = Project.Create("Test Project", "Test Description", "TST", userId); var epic = project.CreateEpic("Test Epic", "Epic Description", userId); var story = epic.CreateStory("Test Story", "Story Description", TaskPriority.Medium, userId); var task = story.CreateTask("Test Task", "Task Description", TaskPriority.Medium, userId); var taskId = task.Id; _projectRepositoryMock .Setup(x => x.GetProjectWithTaskAsync(taskId, It.IsAny())) .ReturnsAsync(project); var command = new UpdateTaskStatusCommand { TaskId = taskId.Value, NewStatus = "In Progress" // Display name with space }; // Act var result = await _handler.Handle(command, CancellationToken.None); // Assert result.Should().NotBeNull(); result.Id.Should().Be(taskId.Value); result.Status.Should().Be("In Progress"); task.Status.Should().Be(WorkItemStatus.InProgress); _projectRepositoryMock.Verify(x => x.Update(project), Times.Once); _unitOfWorkMock.Verify(x => x.SaveChangesAsync(It.IsAny()), Times.Once); } [Fact] public async Task Should_Update_Task_Status_To_InProgress_With_CodeName_Successfully() { // Arrange - This tests the bug fix for accepting "InProgress" (without space) var userId = UserId.Create(); var project = Project.Create("Test Project", "Test Description", "TST", userId); var epic = project.CreateEpic("Test Epic", "Epic Description", userId); var story = epic.CreateStory("Test Story", "Story Description", TaskPriority.Medium, userId); var task = story.CreateTask("Test Task", "Task Description", TaskPriority.Medium, userId); var taskId = task.Id; _projectRepositoryMock .Setup(x => x.GetProjectWithTaskAsync(taskId, It.IsAny())) .ReturnsAsync(project); var command = new UpdateTaskStatusCommand { TaskId = taskId.Value, NewStatus = "InProgress" // Code name without space (this was causing the bug) }; // Act var result = await _handler.Handle(command, CancellationToken.None); // Assert result.Should().NotBeNull(); result.Id.Should().Be(taskId.Value); result.Status.Should().Be("In Progress"); task.Status.Should().Be(WorkItemStatus.InProgress); } [Fact] public async Task Should_Update_Task_Status_To_Done_Successfully() { // Arrange var userId = UserId.Create(); var project = Project.Create("Test Project", "Test Description", "TST", userId); var epic = project.CreateEpic("Test Epic", "Epic Description", userId); var story = epic.CreateStory("Test Story", "Story Description", TaskPriority.Medium, userId); var task = story.CreateTask("Test Task", "Task Description", TaskPriority.Medium, userId); var taskId = task.Id; _projectRepositoryMock .Setup(x => x.GetProjectWithTaskAsync(taskId, It.IsAny())) .ReturnsAsync(project); var command = new UpdateTaskStatusCommand { TaskId = taskId.Value, NewStatus = "Done" }; // Act var result = await _handler.Handle(command, CancellationToken.None); // Assert result.Should().NotBeNull(); result.Status.Should().Be("Done"); task.Status.Should().Be(WorkItemStatus.Done); } [Fact] public async Task Should_Update_Task_Status_To_Blocked_Successfully() { // Arrange var userId = UserId.Create(); var project = Project.Create("Test Project", "Test Description", "TST", userId); var epic = project.CreateEpic("Test Epic", "Epic Description", userId); var story = epic.CreateStory("Test Story", "Story Description", TaskPriority.Medium, userId); var task = story.CreateTask("Test Task", "Task Description", TaskPriority.Medium, userId); var taskId = task.Id; _projectRepositoryMock .Setup(x => x.GetProjectWithTaskAsync(taskId, It.IsAny())) .ReturnsAsync(project); var command = new UpdateTaskStatusCommand { TaskId = taskId.Value, NewStatus = "Blocked" }; // Act var result = await _handler.Handle(command, CancellationToken.None); // Assert result.Should().NotBeNull(); result.Status.Should().Be("Blocked"); task.Status.Should().Be(WorkItemStatus.Blocked); } [Fact] public async Task Should_Update_Task_Status_To_InReview_Successfully() { // Arrange var userId = UserId.Create(); var project = Project.Create("Test Project", "Test Description", "TST", userId); var epic = project.CreateEpic("Test Epic", "Epic Description", userId); var story = epic.CreateStory("Test Story", "Story Description", TaskPriority.Medium, userId); var task = story.CreateTask("Test Task", "Task Description", TaskPriority.Medium, userId); var taskId = task.Id; _projectRepositoryMock .Setup(x => x.GetProjectWithTaskAsync(taskId, It.IsAny())) .ReturnsAsync(project); var command = new UpdateTaskStatusCommand { TaskId = taskId.Value, NewStatus = "In Review" }; // Act var result = await _handler.Handle(command, CancellationToken.None); // Assert result.Should().NotBeNull(); result.Status.Should().Be("In Review"); task.Status.Should().Be(WorkItemStatus.InReview); } [Fact] public async Task Should_Throw_NotFoundException_When_Task_Not_Found() { // Arrange var taskId = TaskId.Create(); _projectRepositoryMock .Setup(x => x.GetProjectWithTaskAsync(taskId, It.IsAny())) .ReturnsAsync((Project?)null); var command = new UpdateTaskStatusCommand { TaskId = taskId.Value, NewStatus = "In Progress" }; // Act Func act = async () => await _handler.Handle(command, CancellationToken.None); // Assert await act.Should().ThrowAsync() .WithMessage("*Task*"); } [Fact] public async Task Should_Throw_Exception_When_Moving_Done_To_ToDo() { // Arrange var userId = UserId.Create(); var project = Project.Create("Test Project", "Test Description", "TST", userId); var epic = project.CreateEpic("Test Epic", "Epic Description", userId); var story = epic.CreateStory("Test Story", "Story Description", TaskPriority.Medium, userId); var task = story.CreateTask("Test Task", "Task Description", TaskPriority.Medium, userId); var taskId = task.Id; // First set task to Done task.UpdateStatus(WorkItemStatus.Done); _projectRepositoryMock .Setup(x => x.GetProjectWithTaskAsync(taskId, It.IsAny())) .ReturnsAsync(project); var command = new UpdateTaskStatusCommand { TaskId = taskId.Value, NewStatus = "To Do" }; // Act Func act = async () => await _handler.Handle(command, CancellationToken.None); // Assert await act.Should().ThrowAsync() .WithMessage("*Cannot move a completed task back to ToDo*"); } [Fact] public async Task Should_Allow_Blocked_To_Any_Status() { // Arrange var userId = UserId.Create(); var project = Project.Create("Test Project", "Test Description", "TST", userId); var epic = project.CreateEpic("Test Epic", "Epic Description", userId); var story = epic.CreateStory("Test Story", "Story Description", TaskPriority.Medium, userId); var task = story.CreateTask("Test Task", "Task Description", TaskPriority.Medium, userId); var taskId = task.Id; // Set task to Blocked task.UpdateStatus(WorkItemStatus.Blocked); _projectRepositoryMock .Setup(x => x.GetProjectWithTaskAsync(taskId, It.IsAny())) .ReturnsAsync(project); var command = new UpdateTaskStatusCommand { TaskId = taskId.Value, NewStatus = "In Progress" }; // Act var result = await _handler.Handle(command, CancellationToken.None); // Assert result.Should().NotBeNull(); result.Status.Should().Be("In Progress"); task.Status.Should().Be(WorkItemStatus.InProgress); } [Fact] public async Task Should_Allow_Any_Status_To_Blocked() { // Arrange var userId = UserId.Create(); var project = Project.Create("Test Project", "Test Description", "TST", userId); var epic = project.CreateEpic("Test Epic", "Epic Description", userId); var story = epic.CreateStory("Test Story", "Story Description", TaskPriority.Medium, userId); var task = story.CreateTask("Test Task", "Task Description", TaskPriority.Medium, userId); var taskId = task.Id; // Task starts as ToDo task.Status.Should().Be(WorkItemStatus.ToDo); _projectRepositoryMock .Setup(x => x.GetProjectWithTaskAsync(taskId, It.IsAny())) .ReturnsAsync(project); var command = new UpdateTaskStatusCommand { TaskId = taskId.Value, NewStatus = "Blocked" }; // Act var result = await _handler.Handle(command, CancellationToken.None); // Assert result.Should().NotBeNull(); result.Status.Should().Be("Blocked"); task.Status.Should().Be(WorkItemStatus.Blocked); } [Fact] public async Task Should_Throw_Exception_For_Invalid_Status() { // Arrange var userId = UserId.Create(); var project = Project.Create("Test Project", "Test Description", "TST", userId); var epic = project.CreateEpic("Test Epic", "Epic Description", userId); var story = epic.CreateStory("Test Story", "Story Description", TaskPriority.Medium, userId); var task = story.CreateTask("Test Task", "Task Description", TaskPriority.Medium, userId); var taskId = task.Id; _projectRepositoryMock .Setup(x => x.GetProjectWithTaskAsync(taskId, It.IsAny())) .ReturnsAsync(project); var command = new UpdateTaskStatusCommand { TaskId = taskId.Value, NewStatus = "InvalidStatus" }; // Act Func act = async () => await _handler.Handle(command, CancellationToken.None); // Assert await act.Should().ThrowAsync() .WithMessage("*InvalidStatus*"); } }