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:
2026-08-04 12:32:28 +03:00
parent b8435ac07b
commit 2757869176
37 changed files with 1465 additions and 122 deletions
@@ -0,0 +1,67 @@
namespace HrynCo.NotificationService.Services.Tests.EmailProcessing;
using HrynCo.NotificationService.Contracts.Messages;
using HrynCo.NotificationService.DAL.Abstract.Templates;
using HrynCo.NotificationService.Worker.Services.EmailProcessing;
public sealed class EmailTemplateRenderingServiceTests
{
private readonly EmailTemplateRenderingService _service = new();
[Fact]
public void Render_InterpolatesItemTrackerVariables()
{
EmailTemplate template = CreateTemplate();
SendEmailMessageData data = CreateData(new Dictionary<string, string>
{
["AppName"] = "StoreMate",
["VerificationUrl"] = "https://example.invalid/verify"
});
RenderedEmail result = _service.Render(template, data);
Assert.Equal("Verify StoreMate", result.Subject);
Assert.Equal("<a href=\"https://example.invalid/verify\">Verify</a>", result.HtmlBody);
Assert.Equal("Verify at https://example.invalid/verify", result.TextBody);
}
[Fact]
public void Render_WhenRequiredVariableIsMissing_ThrowsObservableFailure()
{
EmailTemplate template = CreateTemplate();
SendEmailMessageData data = CreateData(new Dictionary<string, string>
{
["AppName"] = "StoreMate"
});
InvalidDataException exception = Assert.Throws<InvalidDataException>(
() => _service.Render(template, data));
Assert.Equal("Required template variables are missing: VerificationUrl.", exception.Message);
}
private static EmailTemplate CreateTemplate() => new()
{
ServiceName = "StoreMate-Prod",
Key = "EmailVerification",
LanguageCode = "uk",
Subject = "Verify {{AppName}}",
HtmlBody = "<a href=\"{{VerificationUrl}}\">Verify</a>",
TextBody = "Verify at {{VerificationUrl}}",
Variables =
[
new EmailTemplateVariable { Name = "AppName", Required = true },
new EmailTemplateVariable { Name = "VerificationUrl", Required = true }
]
};
private static SendEmailMessageData CreateData(IReadOnlyDictionary<string, string> variables) => new()
{
ServiceName = "StoreMate-Prod",
TemplateKey = "EmailVerification",
RecipientEmail = "owner@example.com",
RecipientName = "Owner",
LanguageCode = "uk",
Variables = variables
};
}