feat: consume transactional email notifications
Add contract validation, SMTP delivery results, terminal failure context, neutral development seeding, and local Docker setup. Ref: IT-1033
This commit is contained in:
+107
@@ -0,0 +1,107 @@
|
||||
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<IRabbitMqPublisher>();
|
||||
var publisher = new NotificationResultPublisher(
|
||||
rabbitMqPublisher,
|
||||
NullLogger<NotificationResultPublisher>.Instance);
|
||||
SendEmailMessage message = CreateMessage();
|
||||
NotificationResultMessage? publishedResult = null;
|
||||
rabbitMqPublisher
|
||||
.When(x => x.PublishAsync(
|
||||
"item-tracker.notifications.result",
|
||||
Arg.Any<NotificationResultMessage>(),
|
||||
Arg.Any<CancellationToken>()))
|
||||
.Do(call => publishedResult = call.ArgAt<NotificationResultMessage>(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<NotificationResultMessage>(),
|
||||
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<IRabbitMqPublisher>();
|
||||
var publisher = new NotificationResultPublisher(
|
||||
rabbitMqPublisher,
|
||||
NullLogger<NotificationResultPublisher>.Instance);
|
||||
SendEmailMessage message = CreateMessage();
|
||||
message.CorrelationContext = message.CorrelationContext with { ReplyTo = null };
|
||||
|
||||
await publisher.PublishAsync(message, "delivery failed", CancellationToken.None);
|
||||
|
||||
await rabbitMqPublisher.DidNotReceive()
|
||||
.PublishAsync<NotificationResultData>(
|
||||
Arg.Any<string>(),
|
||||
Arg.Any<NotificationResultMessage>(),
|
||||
Arg.Any<CancellationToken>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PublishAsync_ResultBrokerFailure_IsBestEffort()
|
||||
{
|
||||
IRabbitMqPublisher rabbitMqPublisher = Substitute.For<IRabbitMqPublisher>();
|
||||
rabbitMqPublisher
|
||||
.PublishAsync(
|
||||
Arg.Any<string>(),
|
||||
Arg.Any<NotificationResultMessage>(),
|
||||
Arg.Any<CancellationToken>())
|
||||
.Returns<Task>(_ => throw new InvalidOperationException("reply broker unavailable"));
|
||||
var publisher = new NotificationResultPublisher(
|
||||
rabbitMqPublisher,
|
||||
NullLogger<NotificationResultPublisher>.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<string, string>()
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user