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 = "Invemory" }; var channel = new EmailChannel { Id = channelId, ServiceId = Guid.NewGuid(), Priority = 1, EmailChannelType = EmailChannelType.Smtp, Settings = settings, IsActive = true }; var template = new EmailTemplate { ServiceName = "Invemory-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(channel.ServiceId, Arg.Any()) .Returns([channel]); var services = Substitute.For(); services.GetByNameAsync("Invemory-Prod", Arg.Any()) .Returns(new NotificationServiceInfo { Id = channel.ServiceId, Name = "Invemory-Prod" }); IEmailChannelUsageRepository usage = Substitute.For(); IEmailTemplateService templates = Substitute.For(); templates.GetAsync("Invemory-Prod", "EmailVerification", "uk", Arg.Any()) .Returns(template); var renderer = new EmailTemplateRenderingService(); var smtp = new RecordingSmtpEmailSender(); INotificationResultPublisher resultPublisher = Substitute.For(); var service = new SendEmailService( channels, services, usage, templates, renderer, smtp, resultPublisher, NullLogger.Instance); var message = new SendEmailMessage { CorrelationContext = new CorrelationContext { CorrelationId = Guid.NewGuid().ToString("D") }, Data = new SendEmailMessageData { ServiceName = "Invemory-Prod", TemplateKey = "EmailVerification", RecipientEmail = "owner@example.com", RecipientName = "Owner", LanguageCode = "uk", Variables = new Dictionary { ["AppName"] = "Invemory" } } }; await service.ProcessAsync(message, CancellationToken.None); Assert.Equal(1, smtp.SendCount); Assert.Same(settings, smtp.Settings); Assert.Equal("Verify Invemory", 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; } } }