using ColaFlow.Modules.ProjectManagement.Domain.Aggregates.ProjectAggregate; using ColaFlow.Modules.ProjectManagement.Domain.ValueObjects; using ColaFlow.Modules.ProjectManagement.Domain.Exceptions; using FluentAssertions; namespace ColaFlow.Domain.Tests.Aggregates; /// /// Unit tests for Epic entity /// public class EpicTests { #region Create Tests [Fact] public void Create_WithValidData_ShouldCreateEpic() { // Arrange var name = "Epic 1"; var description = "Epic Description"; var projectId = ProjectId.Create(); var createdBy = UserId.Create(); // Act var epic = Epic.Create(name, description, projectId, createdBy); // Assert epic.Should().NotBeNull(); epic.Name.Should().Be(name); epic.Description.Should().Be(description); epic.ProjectId.Should().Be(projectId); epic.Status.Should().Be(WorkItemStatus.ToDo); epic.Priority.Should().Be(TaskPriority.Medium); epic.CreatedBy.Should().Be(createdBy); epic.CreatedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(5)); epic.UpdatedAt.Should().BeNull(); epic.Stories.Should().BeEmpty(); } [Fact] public void Create_WithNullDescription_ShouldCreateEpicWithEmptyDescription() { // Arrange var name = "Epic 1"; string? description = null; var projectId = ProjectId.Create(); var createdBy = UserId.Create(); // Act var epic = Epic.Create(name, description!, projectId, createdBy); // Assert epic.Should().NotBeNull(); epic.Description.Should().Be(string.Empty); } [Theory] [InlineData("")] [InlineData(" ")] [InlineData(null)] public void Create_WithEmptyName_ShouldThrowDomainException(string invalidName) { // Arrange var projectId = ProjectId.Create(); var createdBy = UserId.Create(); // Act Action act = () => Epic.Create(invalidName, "Description", projectId, createdBy); // Assert act.Should().Throw() .WithMessage("Epic name cannot be empty"); } [Fact] public void Create_WithNameExceeding200Characters_ShouldThrowDomainException() { // Arrange var name = new string('A', 201); var projectId = ProjectId.Create(); var createdBy = UserId.Create(); // Act Action act = () => Epic.Create(name, "Description", projectId, createdBy); // Assert act.Should().Throw() .WithMessage("Epic name cannot exceed 200 characters"); } [Fact] public void Create_WithNameExactly200Characters_ShouldSucceed() { // Arrange var name = new string('A', 200); var projectId = ProjectId.Create(); var createdBy = UserId.Create(); // Act var epic = Epic.Create(name, "Description", projectId, createdBy); // Assert epic.Should().NotBeNull(); epic.Name.Should().Be(name); } #endregion #region CreateStory Tests [Fact] public void CreateStory_WithValidData_ShouldCreateStory() { // Arrange var epic = Epic.Create("Epic 1", "Description", ProjectId.Create(), UserId.Create()); var storyTitle = "Story 1"; var storyDescription = "Story Description"; var priority = TaskPriority.High; var createdBy = UserId.Create(); // Act var story = epic.CreateStory(storyTitle, storyDescription, priority, createdBy); // Assert story.Should().NotBeNull(); story.Title.Should().Be(storyTitle); story.Description.Should().Be(storyDescription); story.EpicId.Should().Be(epic.Id); story.Priority.Should().Be(priority); story.CreatedBy.Should().Be(createdBy); epic.Stories.Should().ContainSingle(); epic.Stories.Should().Contain(story); } [Fact] public void CreateStory_MultipleStories_ShouldAddToCollection() { // Arrange var epic = Epic.Create("Epic 1", "Description", ProjectId.Create(), UserId.Create()); var createdBy = UserId.Create(); // Act var story1 = epic.CreateStory("Story 1", "Desc 1", TaskPriority.Low, createdBy); var story2 = epic.CreateStory("Story 2", "Desc 2", TaskPriority.Medium, createdBy); var story3 = epic.CreateStory("Story 3", "Desc 3", TaskPriority.High, createdBy); // Assert epic.Stories.Should().HaveCount(3); epic.Stories.Should().Contain(new[] { story1, story2, story3 }); } #endregion #region UpdateDetails Tests [Fact] public void UpdateDetails_WithValidData_ShouldUpdateEpic() { // Arrange var epic = Epic.Create("Original Name", "Original Description", ProjectId.Create(), UserId.Create()); var originalCreatedAt = epic.CreatedAt; var newName = "Updated Name"; var newDescription = "Updated Description"; // Act epic.UpdateDetails(newName, newDescription); // Assert epic.Name.Should().Be(newName); epic.Description.Should().Be(newDescription); epic.CreatedAt.Should().Be(originalCreatedAt); epic.UpdatedAt.Should().NotBeNull(); epic.UpdatedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(5)); } [Fact] public void UpdateDetails_WithNullDescription_ShouldSetEmptyDescription() { // Arrange var epic = Epic.Create("Original Name", "Original Description", ProjectId.Create(), UserId.Create()); // Act epic.UpdateDetails("Updated Name", null!); // Assert epic.Description.Should().Be(string.Empty); } [Theory] [InlineData("")] [InlineData(" ")] [InlineData(null)] public void UpdateDetails_WithEmptyName_ShouldThrowDomainException(string invalidName) { // Arrange var epic = Epic.Create("Original Name", "Original Description", ProjectId.Create(), UserId.Create()); // Act Action act = () => epic.UpdateDetails(invalidName, "Updated Description"); // Assert act.Should().Throw() .WithMessage("Epic name cannot be empty"); } [Fact] public void UpdateDetails_WithNameExceeding200Characters_ShouldThrowDomainException() { // Arrange var epic = Epic.Create("Original Name", "Original Description", ProjectId.Create(), UserId.Create()); var name = new string('A', 201); // Act Action act = () => epic.UpdateDetails(name, "Updated Description"); // Assert act.Should().Throw() .WithMessage("Epic name cannot exceed 200 characters"); } #endregion #region UpdateStatus Tests [Fact] public void UpdateStatus_WithValidStatus_ShouldUpdateStatus() { // Arrange var epic = Epic.Create("Epic 1", "Description", ProjectId.Create(), UserId.Create()); var newStatus = WorkItemStatus.InProgress; // Act epic.UpdateStatus(newStatus); // Assert epic.Status.Should().Be(newStatus); epic.UpdatedAt.Should().NotBeNull(); epic.UpdatedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(5)); } [Fact] public void UpdateStatus_ToAllStatuses_ShouldSucceed() { // Arrange var epic = Epic.Create("Epic 1", "Description", ProjectId.Create(), UserId.Create()); // Act & Assert epic.UpdateStatus(WorkItemStatus.InProgress); epic.Status.Should().Be(WorkItemStatus.InProgress); epic.UpdateStatus(WorkItemStatus.InReview); epic.Status.Should().Be(WorkItemStatus.InReview); epic.UpdateStatus(WorkItemStatus.Done); epic.Status.Should().Be(WorkItemStatus.Done); epic.UpdateStatus(WorkItemStatus.Blocked); epic.Status.Should().Be(WorkItemStatus.Blocked); epic.UpdateStatus(WorkItemStatus.ToDo); epic.Status.Should().Be(WorkItemStatus.ToDo); } #endregion #region UpdatePriority Tests [Fact] public void UpdatePriority_WithValidPriority_ShouldUpdatePriority() { // Arrange var epic = Epic.Create("Epic 1", "Description", ProjectId.Create(), UserId.Create()); var newPriority = TaskPriority.Urgent; // Act epic.UpdatePriority(newPriority); // Assert epic.Priority.Should().Be(newPriority); epic.UpdatedAt.Should().NotBeNull(); epic.UpdatedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(5)); } [Fact] public void UpdatePriority_ToAllPriorities_ShouldSucceed() { // Arrange var epic = Epic.Create("Epic 1", "Description", ProjectId.Create(), UserId.Create()); // Act & Assert epic.UpdatePriority(TaskPriority.Low); epic.Priority.Should().Be(TaskPriority.Low); epic.UpdatePriority(TaskPriority.Medium); epic.Priority.Should().Be(TaskPriority.Medium); epic.UpdatePriority(TaskPriority.High); epic.Priority.Should().Be(TaskPriority.High); epic.UpdatePriority(TaskPriority.Urgent); epic.Priority.Should().Be(TaskPriority.Urgent); } #endregion #region Entity Characteristics Tests [Fact] public void Stories_Collection_ShouldBeReadOnly() { // Arrange var epic = Epic.Create("Epic 1", "Description", ProjectId.Create(), UserId.Create()); // Act & Assert epic.Stories.Should().BeAssignableTo>(); } [Fact] public void Epic_ShouldHaveUniqueId() { // Arrange & Act var projectId = ProjectId.Create(); var createdBy = UserId.Create(); var epic1 = Epic.Create("Epic 1", "Description", projectId, createdBy); var epic2 = Epic.Create("Epic 2", "Description", projectId, createdBy); // Assert epic1.Id.Should().NotBe(epic2.Id); } #endregion }