using ColaFlow.Modules.Mcp.Application.DTOs.Notifications; using ColaFlow.Modules.Mcp.Application.EventHandlers; using ColaFlow.Modules.Mcp.Application.Services; using ColaFlow.Modules.Mcp.Domain.Entities; using ColaFlow.Modules.Mcp.Domain.Events; using ColaFlow.Modules.Mcp.Domain.Repositories; using ColaFlow.Modules.Mcp.Domain.ValueObjects; using Microsoft.Extensions.Logging; using Moq; using Xunit; namespace ColaFlow.Modules.Mcp.Tests.EventHandlers; public class PendingChangeCreatedNotificationHandlerTests { private readonly Mock _mockNotificationService; private readonly Mock _mockRepository; private readonly Mock> _mockLogger; private readonly PendingChangeCreatedNotificationHandler _handler; public PendingChangeCreatedNotificationHandlerTests() { _mockNotificationService = new Mock(); _mockRepository = new Mock(); _mockLogger = new Mock>(); _handler = new PendingChangeCreatedNotificationHandler( _mockNotificationService.Object, _mockRepository.Object, _mockLogger.Object); } [Fact] public async Task Handle_SendsNotification_WhenPendingChangeExists() { // Arrange var pendingChangeId = Guid.NewGuid(); var tenantId = Guid.NewGuid(); var apiKeyId = Guid.NewGuid(); var domainEvent = new PendingChangeCreatedEvent( pendingChangeId, "create_epic", "Epic", "CREATE", tenantId); var diff = new DiffPreview( "CREATE", "Epic", null, null, null, "{\"name\":\"Test Epic\"}", new List()); var pendingChange = PendingChange.Create( "create_epic", diff, tenantId, apiKeyId, 12); _mockRepository.Setup(r => r.GetByIdAsync(pendingChangeId, It.IsAny())) .ReturnsAsync(pendingChange); // Act await _handler.Handle(domainEvent, CancellationToken.None); // Assert _mockNotificationService.Verify( s => s.NotifyPendingChangeCreatedAsync( It.Is(n => n.PendingChangeId == pendingChangeId && n.ToolName == "create_epic" && n.EntityType == "Epic" && n.Operation == "CREATE" && n.TenantId == tenantId), It.IsAny()), Times.Once); } [Fact] public async Task Handle_DoesNotSendNotification_WhenPendingChangeNotFound() { // Arrange var pendingChangeId = Guid.NewGuid(); var tenantId = Guid.NewGuid(); var domainEvent = new PendingChangeCreatedEvent( pendingChangeId, "create_epic", "Epic", "CREATE", tenantId); _mockRepository.Setup(r => r.GetByIdAsync(pendingChangeId, It.IsAny())) .ReturnsAsync((PendingChange?)null); // Act await _handler.Handle(domainEvent, CancellationToken.None); // Assert _mockNotificationService.Verify( s => s.NotifyPendingChangeCreatedAsync( It.IsAny(), It.IsAny()), Times.Never); } [Fact] public async Task Handle_DoesNotThrow_WhenNotificationServiceFails() { // Arrange var pendingChangeId = Guid.NewGuid(); var tenantId = Guid.NewGuid(); var apiKeyId = Guid.NewGuid(); var domainEvent = new PendingChangeCreatedEvent( pendingChangeId, "create_epic", "Epic", "CREATE", tenantId); var diff = new DiffPreview( "CREATE", "Epic", null, null, null, "{\"name\":\"Test Epic\"}", new List()); var pendingChange = PendingChange.Create( "create_epic", diff, tenantId, apiKeyId, 12); _mockRepository.Setup(r => r.GetByIdAsync(pendingChangeId, It.IsAny())) .ReturnsAsync(pendingChange); _mockNotificationService.Setup(s => s.NotifyPendingChangeCreatedAsync( It.IsAny(), It.IsAny())) .ThrowsAsync(new Exception("SignalR connection failed")); // Act & Assert - Should not throw await _handler.Handle(domainEvent, CancellationToken.None); } }