namespace HrynCo.NotificationService.Services.Tests.EmailProcessing; using HrynCo.NotificationService.Contracts.Messages; using HrynCo.NotificationService.Worker.Services.EmailProcessing; using Hrynco.RabbitMq; using Microsoft.Extensions.Logging.Abstractions; using NSubstitute; public sealed class NotificationResultPublisherTests { [Fact] public async Task PublishAsync_TerminalFailure_PublishesNeutralFailureResultToReplyQueue() { IRabbitMqPublisher rabbitMqPublisher = Substitute.For(); var publisher = new NotificationResultPublisher( rabbitMqPublisher, NullLogger.Instance); SendEmailMessage message = CreateMessage(); NotificationResultMessage? publishedResult = null; rabbitMqPublisher .When(x => x.PublishAsync( "item-tracker.notifications.result", Arg.Any(), Arg.Any())) .Do(call => publishedResult = call.ArgAt(1)); await publisher.PublishAsync( message, "Email provider could not deliver the notification.", CancellationToken.None); await rabbitMqPublisher.Received(1).PublishAsync( "item-tracker.notifications.result", Arg.Any(), CancellationToken.None); Assert.NotNull(publishedResult); Assert.Equal(message.CorrelationContext.CorrelationId, publishedResult.CorrelationContext.CorrelationId); Assert.Null(publishedResult.CorrelationContext.ReplyTo); Assert.Equal(message.Data.ServiceName, publishedResult.Data.ServiceName); Assert.Equal(message.Data.TemplateKey, publishedResult.Data.TemplateKey); Assert.Equal(message.Data.RecipientEmail, publishedResult.Data.RecipientEmail); Assert.Equal("Email provider could not deliver the notification.", publishedResult.Data.ErrorMessage); Assert.False(publishedResult.Data.IsSuccess); } [Fact] public async Task PublishAsync_NoReplyQueue_DoesNotPublish() { IRabbitMqPublisher rabbitMqPublisher = Substitute.For(); var publisher = new NotificationResultPublisher( rabbitMqPublisher, NullLogger.Instance); SendEmailMessage message = CreateMessage(); message.CorrelationContext = message.CorrelationContext with { ReplyTo = null }; await publisher.PublishAsync(message, "delivery failed", CancellationToken.None); await rabbitMqPublisher.DidNotReceive() .PublishAsync( Arg.Any(), Arg.Any(), Arg.Any()); } [Fact] public async Task PublishAsync_ResultBrokerFailure_IsBestEffort() { IRabbitMqPublisher rabbitMqPublisher = Substitute.For(); rabbitMqPublisher .PublishAsync( Arg.Any(), Arg.Any(), Arg.Any()) .Returns(_ => throw new InvalidOperationException("reply broker unavailable")); var publisher = new NotificationResultPublisher( rabbitMqPublisher, NullLogger.Instance); Exception? exception = await Record.ExceptionAsync(() => publisher.PublishAsync( CreateMessage(), "delivery failed", CancellationToken.None)); Assert.Null(exception); } private static SendEmailMessage CreateMessage() { return new SendEmailMessage { CorrelationContext = new CorrelationContext { CorrelationId = "correlation-id", ReplyTo = "item-tracker.notifications.result" }, Data = new SendEmailMessageData { ServiceName = "TestService", TemplateKey = "TestEmail", RecipientEmail = "test.user@itemtracker.local", RecipientName = "Test User", LanguageCode = "en", Variables = new Dictionary() } }; } }