namespace HrynCo.NotificationService.Services.Tests.EmailProcessing; using HrynCo.NotificationService.Contracts.Messages; using HrynCo.NotificationService.DAL.Abstract.Providers; using HrynCo.NotificationService.DAL.Abstract.Repositories; using HrynCo.NotificationService.DAL.Abstract.Templates; using HrynCo.NotificationService.Worker.Services.EmailProcessing; using Hrynco.RabbitMq; using Microsoft.Extensions.Logging.Abstractions; using NSubstitute; public sealed class SendEmailServiceTests { [Fact] public async Task ProcessAsync_ValidItemTrackerMessage_SendsOnceAndUpdatesUsage() { Guid channelId = Guid.NewGuid(); var settings = new SmtpChannelSettings { Host = "smtp.example.invalid", Port = 587, Username = "smtp-user", Password = "not-a-real-secret", UseSsl = true, FromEmail = "notifications@example.com", FromName = "StoreMate" }; var channel = new EmailChannel { Id = channelId, ServiceName = "StoreMate-Prod", Priority = 1, EmailChannelType = EmailChannelType.Smtp, Settings = settings, IsActive = true }; var template = new EmailTemplate { ServiceName = "StoreMate-Prod", Key = "EmailVerification", LanguageCode = "uk", Subject = "Verify {{AppName}}", HtmlBody = "

{{AppName}}

", TextBody = "{{AppName}}", Variables = [new EmailTemplateVariable { Name = "AppName", Required = true }] }; IEmailChannelRepository channels = Substitute.For(); channels.GetByServiceAsync("StoreMate-Prod", Arg.Any()) .Returns([channel]); IEmailChannelUsageRepository usage = Substitute.For(); IEmailTemplateService templates = Substitute.For(); templates.GetAsync("StoreMate-Prod", "EmailVerification", "uk", Arg.Any()) .Returns(template); var renderer = new EmailTemplateRenderingService(); var smtp = new RecordingSmtpEmailSender(); INotificationResultPublisher resultPublisher = Substitute.For(); var service = new SendEmailService( channels, usage, templates, renderer, smtp, resultPublisher, NullLogger.Instance); var message = new SendEmailMessage { CorrelationContext = new CorrelationContext { CorrelationId = Guid.NewGuid().ToString("D") }, Data = new SendEmailMessageData { ServiceName = "StoreMate-Prod", TemplateKey = "EmailVerification", RecipientEmail = "owner@example.com", RecipientName = "Owner", LanguageCode = "uk", Variables = new Dictionary { ["AppName"] = "StoreMate" } } }; await service.ProcessAsync(message, CancellationToken.None); Assert.Equal(1, smtp.SendCount); Assert.Same(settings, smtp.Settings); Assert.Equal("Verify StoreMate", smtp.Email?.Subject); Assert.Equal("owner@example.com", smtp.RecipientEmail); Assert.Equal("Owner", smtp.RecipientName); await usage.Received(1).IncrementUsageAsync( channelId, Arg.Any(), CancellationToken.None); await resultPublisher.Received(1).PublishAsync( message, null, CancellationToken.None); } private sealed class RecordingSmtpEmailSender : ISmtpEmailSender { public int SendCount { get; private set; } public SmtpChannelSettings? Settings { get; private set; } public RenderedEmail? Email { get; private set; } public string? RecipientEmail { get; private set; } public string? RecipientName { get; private set; } public Task SendAsync( SmtpChannelSettings settings, RenderedEmail email, string recipientEmail, string recipientName, CancellationToken cancellationToken) { SendCount++; Settings = settings; Email = email; RecipientEmail = recipientEmail; RecipientName = recipientName; return Task.CompletedTask; } } }