using Cleanuparr.Domain.Enums; using Cleanuparr.Infrastructure.Features.Notifications; using Cleanuparr.Infrastructure.Features.Notifications.Consumers; using Cleanuparr.Infrastructure.Features.Notifications.Models; using Microsoft.Extensions.Logging; using NSubstitute; using NSubstitute.ExceptionExtensions; using Shouldly; using Xunit; namespace Cleanuparr.Infrastructure.Tests.Features.Notifications; public class NotificationConsumerTests { private readonly INotificationConfigurationService _configurationService; private readonly INotificationProviderFactory _providerFactory; private readonly NotificationConsumer _consumer; public NotificationConsumerTests() { _configurationService = Substitute.For(); _providerFactory = Substitute.For(); NotificationService notificationService = new( Substitute.For>(), _configurationService, _providerFactory, TimeProvider.System); _consumer = new NotificationConsumer(notificationService); } private static NotificationContext CreateContext(NotificationEventType eventType) { return new NotificationContext { EventType = eventType, Title = "Test title", Description = "Test description", }; } private static NotificationProviderDto CreateProviderDto(string name = "TestProvider") { return new NotificationProviderDto { Id = Guid.NewGuid(), Name = name, Type = NotificationProviderType.Notifiarr, IsEnabled = true, Events = new NotificationEventFlags(), Configuration = new { ApiKey = "test" } }; } [Fact] public async Task HandleAsync_WithConfiguredProviders_SendsToAll() { NotificationProviderDto providerDto1 = CreateProviderDto("Provider1"); NotificationProviderDto providerDto2 = CreateProviderDto("Provider2"); INotificationProvider provider1 = Substitute.For(); INotificationProvider provider2 = Substitute.For(); NotificationMessage message = new(NotificationEventType.FailedImportStrike, CreateContext(NotificationEventType.FailedImportStrike)); _configurationService.GetProvidersForEventAsync(NotificationEventType.FailedImportStrike) .Returns(new List { providerDto1, providerDto2 }); _providerFactory.CreateProvider(providerDto1).Returns(provider1); _providerFactory.CreateProvider(providerDto2).Returns(provider2); await _consumer.HandleAsync(message); await provider1.Received(1).SendNotificationAsync(message.Context); await provider2.Received(1).SendNotificationAsync(message.Context); } [Fact] public async Task HandleAsync_WhenOneProviderThrows_OthersStillReceiveNotification() { NotificationProviderDto providerDto1 = CreateProviderDto("Provider1"); NotificationProviderDto providerDto2 = CreateProviderDto("Provider2"); INotificationProvider provider1 = Substitute.For(); INotificationProvider provider2 = Substitute.For(); provider1.SendNotificationAsync(Arg.Any()) .ThrowsAsync(new Exception("Provider1 failed")); NotificationMessage message = new(NotificationEventType.StalledStrike, CreateContext(NotificationEventType.StalledStrike)); _configurationService.GetProvidersForEventAsync(NotificationEventType.StalledStrike) .Returns(new List { providerDto1, providerDto2 }); _providerFactory.CreateProvider(providerDto1).Returns(provider1); _providerFactory.CreateProvider(providerDto2).Returns(provider2); await _consumer.HandleAsync(message); await provider2.Received(1).SendNotificationAsync(message.Context); } [Fact] public async Task HandleAsync_WhenNoProvidersConfigured_DoesNotCreateAnyProvider() { NotificationMessage message = new(NotificationEventType.QueueItemDeleted, CreateContext(NotificationEventType.QueueItemDeleted)); _configurationService.GetProvidersForEventAsync(NotificationEventType.QueueItemDeleted) .Returns(new List()); await _consumer.HandleAsync(message); _providerFactory.DidNotReceive().CreateProvider(Arg.Any()); } [Fact] public async Task HandleAsync_WhenProviderLookupThrows_DoesNotThrow() { NotificationMessage message = new(NotificationEventType.QueueItemDeleted, CreateContext(NotificationEventType.QueueItemDeleted)); _configurationService.GetProvidersForEventAsync(Arg.Any()) .ThrowsAsync(new InvalidOperationException("db locked")); await Should.NotThrowAsync(() => _consumer.HandleAsync(message)); _providerFactory.DidNotReceive().CreateProvider(Arg.Any()); } }