Extends SignalR notification system to cover all ProjectManagement CRUD operations:
Domain Events Created:
- EpicUpdatedEvent, EpicDeletedEvent
- StoryCreatedEvent, StoryUpdatedEvent, StoryDeletedEvent
- TaskCreatedEvent, TaskUpdatedEvent, TaskDeletedEvent, TaskAssignedEvent
Event Handlers Added (10 handlers):
- EpicCreatedEventHandler, EpicUpdatedEventHandler, EpicDeletedEventHandler
- StoryCreatedEventHandler, StoryUpdatedEventHandler, StoryDeletedEventHandler
- TaskCreatedEventHandler, TaskUpdatedEventHandler, TaskDeletedEventHandler
- TaskAssignedEventHandler
Infrastructure Extensions:
- Extended IProjectNotificationService with Epic/Story/Task methods
- Extended IRealtimeNotificationService with Epic/Story/Task methods
- Extended RealtimeNotificationService with implementations
- Extended ProjectNotificationServiceAdapter for delegation
Domain Changes:
- Updated EpicCreatedEvent to include TenantId (consistency with other events)
- Added Epic/Story/Task CRUD methods to Project aggregate root
- All operations raise appropriate domain events
Broadcasting Strategy:
- Created events: Broadcast to both project-{projectId} and tenant-{tenantId} groups
- Updated events: Broadcast to project-{projectId} group only
- Deleted events: Broadcast to project-{projectId} group only
- Assigned events: Broadcast to project-{projectId} group with assignment details
Test Results:
- All 192 domain tests passing
- Domain and Application layers compile successfully
- Event handlers auto-registered by MediatR
Files Changed:
- 9 new domain event files
- 10 new event handler files
- 3 service interfaces extended
- 2 service implementations extended
- 1 aggregate updated with event raising logic
- 1 test file updated for new event signature
Status: Complete real-time collaboration for ProjectManagement module
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
232 lines
7.3 KiB
C#
232 lines
7.3 KiB
C#
using ColaFlow.Modules.ProjectManagement.Domain.Events;
|
|
using ColaFlow.Modules.ProjectManagement.Domain.ValueObjects;
|
|
using FluentAssertions;
|
|
|
|
namespace ColaFlow.Domain.Tests.Events;
|
|
|
|
/// <summary>
|
|
/// Unit tests for Domain Events
|
|
/// </summary>
|
|
public class DomainEventsTests
|
|
{
|
|
#region ProjectCreatedEvent Tests
|
|
|
|
[Fact]
|
|
public void ProjectCreatedEvent_Constructor_ShouldSetProperties()
|
|
{
|
|
// Arrange
|
|
var projectId = ProjectId.Create();
|
|
var projectName = "Test Project";
|
|
var createdBy = UserId.Create();
|
|
|
|
// Act
|
|
var @event = new ProjectCreatedEvent(projectId, TenantId.Create(Guid.NewGuid()), projectName, createdBy);
|
|
|
|
// Assert
|
|
@event.ProjectId.Should().Be(projectId);
|
|
@event.ProjectName.Should().Be(projectName);
|
|
@event.CreatedBy.Should().Be(createdBy);
|
|
@event.OccurredOn.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(5));
|
|
}
|
|
|
|
[Fact]
|
|
public void ProjectCreatedEvent_ShouldBeRecord()
|
|
{
|
|
// Arrange
|
|
var projectId = ProjectId.Create();
|
|
var projectName = "Test Project";
|
|
var createdBy = UserId.Create();
|
|
|
|
// Act
|
|
var tenantId = TenantId.Create(Guid.NewGuid());
|
|
var event1 = new ProjectCreatedEvent(projectId, tenantId, projectName, createdBy);
|
|
var event2 = new ProjectCreatedEvent(projectId, tenantId, projectName, createdBy);
|
|
|
|
// Assert - Records with same values should be equal
|
|
event1.ProjectId.Should().Be(event2.ProjectId);
|
|
event1.ProjectName.Should().Be(event2.ProjectName);
|
|
event1.CreatedBy.Should().Be(event2.CreatedBy);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ProjectUpdatedEvent Tests
|
|
|
|
[Fact]
|
|
public void ProjectUpdatedEvent_Constructor_ShouldSetProperties()
|
|
{
|
|
// Arrange
|
|
var projectId = ProjectId.Create();
|
|
var name = "Updated Project";
|
|
var description = "Updated Description";
|
|
|
|
// Act
|
|
var @event = new ProjectUpdatedEvent(projectId, name, description);
|
|
|
|
// Assert
|
|
@event.ProjectId.Should().Be(projectId);
|
|
@event.Name.Should().Be(name);
|
|
@event.Description.Should().Be(description);
|
|
@event.OccurredOn.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(5));
|
|
}
|
|
|
|
[Fact]
|
|
public void ProjectUpdatedEvent_WithNullDescription_ShouldAcceptNull()
|
|
{
|
|
// Arrange
|
|
var projectId = ProjectId.Create();
|
|
var name = "Updated Project";
|
|
|
|
// Act
|
|
var @event = new ProjectUpdatedEvent(projectId, name, null!);
|
|
|
|
// Assert
|
|
@event.Description.Should().BeNull();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ProjectArchivedEvent Tests
|
|
|
|
[Fact]
|
|
public void ProjectArchivedEvent_Constructor_ShouldSetProperties()
|
|
{
|
|
// Arrange
|
|
var projectId = ProjectId.Create();
|
|
|
|
// Act
|
|
var @event = new ProjectArchivedEvent(projectId);
|
|
|
|
// Assert
|
|
@event.ProjectId.Should().Be(projectId);
|
|
@event.OccurredOn.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(5));
|
|
}
|
|
|
|
[Fact]
|
|
public void ProjectArchivedEvent_ShouldBeRecord()
|
|
{
|
|
// Arrange
|
|
var projectId = ProjectId.Create();
|
|
|
|
// Act
|
|
var event1 = new ProjectArchivedEvent(projectId);
|
|
var event2 = new ProjectArchivedEvent(projectId);
|
|
|
|
// Assert - Records with same values should be equal
|
|
event1.ProjectId.Should().Be(event2.ProjectId);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region EpicCreatedEvent Tests
|
|
|
|
[Fact]
|
|
public void EpicCreatedEvent_Constructor_ShouldSetProperties()
|
|
{
|
|
// Arrange
|
|
var epicId = EpicId.Create();
|
|
var tenantId = TenantId.Create(Guid.NewGuid());
|
|
var projectId = ProjectId.Create();
|
|
var epicName = "Epic 1";
|
|
|
|
// Act
|
|
var @event = new EpicCreatedEvent(epicId, tenantId, projectId, epicName);
|
|
|
|
// Assert
|
|
@event.EpicId.Should().Be(epicId);
|
|
@event.TenantId.Should().Be(tenantId);
|
|
@event.ProjectId.Should().Be(projectId);
|
|
@event.EpicName.Should().Be(epicName);
|
|
@event.OccurredOn.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(5));
|
|
}
|
|
|
|
[Fact]
|
|
public void EpicCreatedEvent_ShouldBeRecord()
|
|
{
|
|
// Arrange
|
|
var epicId = EpicId.Create();
|
|
var tenantId = TenantId.Create(Guid.NewGuid());
|
|
var projectId = ProjectId.Create();
|
|
var epicName = "Epic 1";
|
|
|
|
// Act
|
|
var event1 = new EpicCreatedEvent(epicId, tenantId, projectId, epicName);
|
|
var event2 = new EpicCreatedEvent(epicId, tenantId, projectId, epicName);
|
|
|
|
// Assert - Records with same values should be equal
|
|
event1.EpicId.Should().Be(event2.EpicId);
|
|
event1.TenantId.Should().Be(event2.TenantId);
|
|
event1.ProjectId.Should().Be(event2.ProjectId);
|
|
event1.EpicName.Should().Be(event2.EpicName);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Event Timing Tests
|
|
|
|
[Fact]
|
|
public void DomainEvents_OccurredOn_ShouldBeUtcTime()
|
|
{
|
|
// Arrange & Act
|
|
var projectCreatedEvent = new ProjectCreatedEvent(ProjectId.Create(), TenantId.Create(Guid.NewGuid()), "Test", UserId.Create());
|
|
var projectUpdatedEvent = new ProjectUpdatedEvent(ProjectId.Create(), "Test", "Desc");
|
|
var projectArchivedEvent = new ProjectArchivedEvent(ProjectId.Create());
|
|
var epicCreatedEvent = new EpicCreatedEvent(EpicId.Create(), TenantId.Create(Guid.NewGuid()), ProjectId.Create(), "Epic");
|
|
|
|
// Assert
|
|
projectCreatedEvent.OccurredOn.Kind.Should().Be(DateTimeKind.Utc);
|
|
projectUpdatedEvent.OccurredOn.Kind.Should().Be(DateTimeKind.Utc);
|
|
projectArchivedEvent.OccurredOn.Kind.Should().Be(DateTimeKind.Utc);
|
|
epicCreatedEvent.OccurredOn.Kind.Should().Be(DateTimeKind.Utc);
|
|
}
|
|
|
|
[Fact]
|
|
public void DomainEvents_OccurredOn_ShouldBeSetAutomatically()
|
|
{
|
|
// Arrange
|
|
var beforeCreation = DateTime.UtcNow;
|
|
|
|
// Act
|
|
var @event = new ProjectCreatedEvent(ProjectId.Create(), TenantId.Create(Guid.NewGuid()), "Test", UserId.Create());
|
|
|
|
// Assert
|
|
var afterCreation = DateTime.UtcNow;
|
|
@event.OccurredOn.Should().BeOnOrAfter(beforeCreation);
|
|
@event.OccurredOn.Should().BeOnOrBefore(afterCreation);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Event Immutability Tests
|
|
|
|
[Fact]
|
|
public void DomainEvents_ShouldBeImmutable()
|
|
{
|
|
// Arrange
|
|
var projectId = ProjectId.Create();
|
|
var projectName = "Test Project";
|
|
var createdBy = UserId.Create();
|
|
|
|
// Act
|
|
var @event = new ProjectCreatedEvent(projectId, TenantId.Create(Guid.NewGuid()), projectName, createdBy);
|
|
var originalProjectId = @event.ProjectId;
|
|
var originalProjectName = @event.ProjectName;
|
|
var originalCreatedBy = @event.CreatedBy;
|
|
var originalOccurredOn = @event.OccurredOn;
|
|
|
|
// Try to access properties multiple times
|
|
var projectId1 = @event.ProjectId;
|
|
var projectName1 = @event.ProjectName;
|
|
var createdBy1 = @event.CreatedBy;
|
|
var occurredOn1 = @event.OccurredOn;
|
|
|
|
// Assert - Properties should not change
|
|
projectId1.Should().Be(originalProjectId);
|
|
projectName1.Should().Be(originalProjectName);
|
|
createdBy1.Should().Be(originalCreatedBy);
|
|
occurredOn1.Should().Be(originalOccurredOn);
|
|
}
|
|
|
|
#endregion
|
|
}
|