🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
333 lines
9.9 KiB
C#
333 lines
9.9 KiB
C#
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;
|
|
|
|
/// <summary>
|
|
/// Unit tests for Epic entity
|
|
/// </summary>
|
|
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<DomainException>()
|
|
.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<DomainException>()
|
|
.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<DomainException>()
|
|
.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<DomainException>()
|
|
.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<IReadOnlyCollection<Story>>();
|
|
}
|
|
|
|
[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
|
|
}
|